mirror of https://github.com/msgbyte/tailchat
refactor: rebuild project struction
parent
bd441a45eb
commit
26fd3c6cdb
@ -0,0 +1,7 @@
|
||||
import { EffectCallback, useEffect } from 'react';
|
||||
|
||||
// Reference: https://github.com/streamich/react-use/blob/master/src/useEffectOnce.ts
|
||||
|
||||
export const useEffectOnce = (effect: EffectCallback) => {
|
||||
useEffect(effect, []);
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
import { Dispatch, SetStateAction, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { useUnmount } from './useUnmount';
|
||||
|
||||
// Reference: https://github.com/streamich/react-use/blob/master/src/useRafState.ts
|
||||
|
||||
export const useRafState = <S>(
|
||||
initialState: S | (() => S)
|
||||
): [S, Dispatch<SetStateAction<S>>] => {
|
||||
const frame = useRef(0);
|
||||
const [state, setState] = useState(initialState);
|
||||
|
||||
const setRafState = useCallback((value: S | ((prevState: S) => S)) => {
|
||||
cancelAnimationFrame(frame.current);
|
||||
|
||||
frame.current = requestAnimationFrame(() => {
|
||||
setState(value);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useUnmount(() => {
|
||||
cancelAnimationFrame(frame.current);
|
||||
});
|
||||
|
||||
return [state, setRafState];
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import { useRef } from 'react';
|
||||
import { useEffectOnce } from './useEffectOnce';
|
||||
|
||||
// Reference: https://github.com/streamich/react-use/blob/master/src/useUnmount.ts
|
||||
|
||||
export const useUnmount = (fn: () => any): void => {
|
||||
const fnRef = useRef(fn);
|
||||
|
||||
// update the ref each render so if it change the newest callback will be invoked
|
||||
fnRef.current = fn;
|
||||
|
||||
useEffectOnce(() => () => fnRef.current());
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
export const isNavigator = typeof navigator !== 'undefined';
|
@ -0,0 +1,10 @@
|
||||
import { useWindowSize } from './useWindowSize';
|
||||
|
||||
/**
|
||||
* 判定是否为移动版网页
|
||||
*/
|
||||
export function useIsMobile(): boolean {
|
||||
const { width } = useWindowSize();
|
||||
|
||||
return width < 768;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import { isBrowser, useRafState } from 'pawchat-shared';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
// Reference: https://github.com/streamich/react-use/blob/master/src/useWindowSize.ts
|
||||
|
||||
export const useWindowSize = (
|
||||
initialWidth = Infinity,
|
||||
initialHeight = Infinity
|
||||
) => {
|
||||
const [state, setState] = useRafState<{ width: number; height: number }>({
|
||||
width: isBrowser ? window.innerWidth : initialWidth,
|
||||
height: isBrowser ? window.innerHeight : initialHeight,
|
||||
});
|
||||
|
||||
useEffect((): (() => void) | void => {
|
||||
if (isBrowser) {
|
||||
const handler = () => {
|
||||
setState({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handler);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
return state;
|
||||
};
|
@ -1,52 +0,0 @@
|
||||
import React from 'react';
|
||||
import { PillTabs, PillTabPane } from '../../components/PillTabs';
|
||||
import { useAppSelector } from '../../hooks/useAppSelector';
|
||||
|
||||
/**
|
||||
* 主要内容组件
|
||||
*/
|
||||
export const Content: React.FC = React.memo(() => {
|
||||
const friends = useAppSelector((state) => state.user.friends);
|
||||
const friendRequests = useAppSelector((state) => state.user.friendRequests);
|
||||
const userId = useAppSelector((state) => state.user.info?._id);
|
||||
|
||||
return (
|
||||
<div className="flex-auto bg-gray-700">
|
||||
<PillTabs>
|
||||
<PillTabPane tab={'全部'} key="1">
|
||||
<div className="py-2.5 px-5">
|
||||
<div>好友列表</div>
|
||||
<div>{JSON.stringify(friends)}</div>
|
||||
</div>
|
||||
</PillTabPane>
|
||||
<PillTabPane tab={'已发送'} key="2">
|
||||
<div className="py-2.5 px-5">
|
||||
<div>发送的好友请求</div>
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
friendRequests.filter((item) => item.from === userId)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PillTabPane>
|
||||
<PillTabPane tab={'待处理'} key="3">
|
||||
<div className="py-2.5 px-5">
|
||||
<div>接受的好友请求</div>
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
friendRequests.filter((item) => item.to === userId)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PillTabPane>
|
||||
<PillTabPane
|
||||
tab={<span className="text-green-400">添加好友</span>}
|
||||
key="4"
|
||||
>
|
||||
添加好友
|
||||
</PillTabPane>
|
||||
</PillTabs>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Content.displayName = 'Content';
|
@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { PillTabPane, PillTabs } from '@/components/PillTabs';
|
||||
|
||||
/**
|
||||
* 主要内容组件
|
||||
*/
|
||||
export const FriendPanel: React.FC = React.memo(() => {
|
||||
const friends = useAppSelector((state) => state.user.friends);
|
||||
const friendRequests = useAppSelector((state) => state.user.friendRequests);
|
||||
const userId = useAppSelector((state) => state.user.info?._id);
|
||||
|
||||
return (
|
||||
<PillTabs>
|
||||
<PillTabPane tab={'全部'} key="1">
|
||||
<div className="py-2.5 px-5">
|
||||
<div>好友列表</div>
|
||||
<div>{JSON.stringify(friends)}</div>
|
||||
</div>
|
||||
</PillTabPane>
|
||||
<PillTabPane tab={'已发送'} key="2">
|
||||
<div className="py-2.5 px-5">
|
||||
<div>发送的好友请求</div>
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
friendRequests.filter((item) => item.from === userId)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PillTabPane>
|
||||
<PillTabPane tab={'待处理'} key="3">
|
||||
<div className="py-2.5 px-5">
|
||||
<div>接受的好友请求</div>
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
friendRequests.filter((item) => item.to === userId)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PillTabPane>
|
||||
<PillTabPane
|
||||
tab={<span className="text-green-400">添加好友</span>}
|
||||
key="4"
|
||||
>
|
||||
添加好友
|
||||
</PillTabPane>
|
||||
</PillTabs>
|
||||
);
|
||||
});
|
||||
FriendPanel.displayName = 'FriendPanel';
|
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { PageContent } from '../PageContent';
|
||||
import { FriendPanel } from './Friends';
|
||||
import { Sidebar } from './Sidebar';
|
||||
|
||||
export const Personal: React.FC = React.memo(() => {
|
||||
return (
|
||||
<PageContent sidebar={<Sidebar />}>
|
||||
<Switch>
|
||||
<Route path="/main/personal/friends" component={FriendPanel} />
|
||||
|
||||
<Redirect to="/main/personal/friends" />
|
||||
</Switch>
|
||||
</PageContent>
|
||||
);
|
||||
});
|
||||
Personal.displayName = 'Personal';
|
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Personal } from './Personal';
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
|
||||
export const MainContent: React.FC = React.memo(() => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path="/main/personal" component={Personal} />
|
||||
{/* <Route path="/main/group/add">
|
||||
<AddGroup />
|
||||
</Route>
|
||||
<Route path="/main/group/:groupUUID">
|
||||
<Group />
|
||||
</Route> */}
|
||||
<Redirect to="/main/personal" />
|
||||
</Switch>
|
||||
);
|
||||
});
|
||||
MainContent.displayName = 'MainContent';
|
@ -0,0 +1,38 @@
|
||||
import React, { useContext, useState, useCallback } from 'react';
|
||||
import _noop from 'lodash/noop';
|
||||
|
||||
interface SidebarContextProps {
|
||||
showSidebar: boolean;
|
||||
switchSidebar: () => void;
|
||||
setShowSidebar: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
const SidebarContext = React.createContext<SidebarContextProps>({
|
||||
showSidebar: true,
|
||||
switchSidebar: _noop,
|
||||
setShowSidebar: _noop,
|
||||
});
|
||||
SidebarContext.displayName = 'SidebarContext';
|
||||
|
||||
export const SidebarContextProvider: React.FC = React.memo((props) => {
|
||||
const [showSidebar, setShowSidebar] = useState(true);
|
||||
|
||||
// 切换
|
||||
const switchSidebar = useCallback(() => {
|
||||
setShowSidebar(!showSidebar);
|
||||
}, [showSidebar]);
|
||||
|
||||
return (
|
||||
<SidebarContext.Provider
|
||||
value={{ showSidebar, switchSidebar, setShowSidebar }}
|
||||
>
|
||||
{props.children}
|
||||
</SidebarContext.Provider>
|
||||
);
|
||||
});
|
||||
SidebarContextProvider.displayName = 'SidebarContextProvider';
|
||||
|
||||
export function useSidebarContext(): SidebarContextProps {
|
||||
const context = useContext(SidebarContext);
|
||||
|
||||
return context;
|
||||
}
|
@ -1,3 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.json"
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue