refactor(web): 群组面板与群组分组

pull/13/head
moonrailgun 4 years ago
parent 517c8950f6
commit 44fbcb941a

@ -68,6 +68,7 @@ export {
export { useAppSelector, useAppDispatch } from './redux/hooks/useAppSelector';
export { useDMConverseList } from './redux/hooks/useConverse';
export { useConverseMessage } from './redux/hooks/useConverseMessage';
export { useGroupInfo } from './redux/hooks/useGroup';
export { useUserId } from './redux/hooks/useUserInfo';
export { userActions } from './redux/slices';
export type { ChatConverseState } from './redux/slices/chat';

@ -0,0 +1,9 @@
import type { GroupInfo } from '../../model/group';
import { useAppSelector } from './useAppSelector';
/**
*
*/
export function useGroupInfo(groupId: string): GroupInfo | undefined {
return useAppSelector((state) => state.group.groups[groupId]);
}

@ -26,7 +26,7 @@ export const App: React.FC = React.memo(() => {
return (
<div
className={clsx('h-screen w-screen min-h-screen', {
className={clsx('h-screen w-screen min-h-screen select-none', {
dark: darkMode,
})}
>

@ -39,7 +39,7 @@ export const ChatBox: React.FC<{
}
return (
<div className="w-full h-screen flex flex-col">
<div className="w-full h-screen flex flex-col select-text">
<ChatMessageList messages={messages} />
<ChatInputBox

@ -0,0 +1,113 @@
import React, { useReducer } from 'react';
import { Icon } from '@iconify/react';
import { GroupPanelType, useGroupInfo } from 'tailchat-shared';
import { useLocation, useParams } from 'react-router';
import { Badge, Typography } from 'antd';
import { Link } from 'react-router-dom';
import clsx from 'clsx';
interface GroupParams {
groupId: string;
}
const GroupSection: React.FC<{
header: string;
}> = React.memo((props) => {
const [isShow, switchShow] = useReducer((v) => !v, true);
return (
<div>
<div
className="flex items-center cursor-pointer py-1"
onClick={switchShow}
>
<Icon
className="mr-1"
icon="mdi-chevron-right"
rotate={isShow ? 45 : 0}
/>
<div>{props.header}</div>
</div>
<div
className="transition-all overflow-hidden"
style={{
maxHeight: isShow ? 'var(--max-height)' : 0,
}}
ref={(ref) =>
ref?.style.setProperty('--max-height', `${ref.scrollHeight}px`)
}
>
{props.children}
</div>
</div>
);
});
GroupSection.displayName = 'GroupSection';
const GroupPanelItem: React.FC<{
name: string;
icon: React.ReactNode;
to: string;
badge?: boolean;
}> = React.memo((props) => {
const { icon, name, to, badge } = props;
const location = useLocation();
const isActive = location.pathname.startsWith(to);
return (
<Link to={to}>
<div
className={clsx(
'w-full hover:bg-white hover:bg-opacity-20 cursor-pointer text-white rounded px-1 h-8 flex items-center text-base group',
{
'bg-opacity-20': isActive,
}
)}
>
<div className="flex items-center justify-center px-1 mr-1">{icon}</div>
<Typography.Text className="flex-1 text-white" ellipsis={true}>
{name}
</Typography.Text>
{badge === true ? (
<Badge status="error" />
) : (
<Badge count={Number(badge) || 0} />
)}
</div>
</Link>
);
});
GroupPanelItem.displayName = 'GroupPanelItem';
/**
*
*/
export const Sidebar: React.FC = React.memo(() => {
const { groupId } = useParams<GroupParams>();
const groupInfo = useGroupInfo(groupId);
const groupPanels = groupInfo?.panels ?? [];
return (
<div>
{groupPanels
.filter((panel) => panel.type === GroupPanelType.GROUP)
.map((group) => (
<GroupSection key={group.id} header={group.name}>
{groupPanels
.filter((panel) => panel.parentId === group.id)
.map((panel) => (
<GroupPanelItem
key={panel.id}
name={panel.name}
icon={<div>#</div>}
to={`/main/group/${groupId}/${panel.id}`}
/>
))}
</GroupSection>
))}
</div>
);
});
Sidebar.displayName = 'Sidebar';

@ -0,0 +1,12 @@
import React from 'react';
import { PageContent } from '../PageContent';
import { Sidebar } from './Sidebar';
export const Group: React.FC = React.memo(() => {
return (
<PageContent sidebar={<Sidebar />}>
<div>TODO</div>
</PageContent>
);
});
Group.displayName = 'Group';

@ -1,17 +1,13 @@
import React from 'react';
import { Personal } from './Personal';
import { Route, Switch, Redirect } from 'react-router-dom';
import { Group } from './Group';
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> */}
<Route path="/main/group/:groupId" component={Group} />
<Redirect to="/main/personal" />
</Switch>
);

@ -3,6 +3,7 @@ import { openModal } from '@/components/Modal';
import { ModalCreateGroup } from '@/components/modals/CreateGroup';
import { Icon } from '@iconify/react';
import React, { useCallback, useMemo } from 'react';
import { useHistory } from 'react-router';
import { GroupInfo, useAppSelector } from 'tailchat-shared';
import { NavbarNavItem } from './NavItem';
@ -16,6 +17,11 @@ function useGroups(): GroupInfo[] {
export const GroupNav: React.FC = React.memo(() => {
const groups = useGroups();
const history = useHistory();
const handleToGroup = useCallback((groupId: string) => {
history.push(`/main/group/${groupId}`);
}, []);
const handleCreateGroup = useCallback(() => {
openModal(<ModalCreateGroup />);
@ -25,7 +31,10 @@ export const GroupNav: React.FC = React.memo(() => {
<div className="space-y-2">
{Array.isArray(groups) &&
groups.map((group) => (
<NavbarNavItem key={group._id}>
<NavbarNavItem
key={group._id}
onClick={() => handleToGroup(group._id)}
>
<Avatar
shape="square"
size={48}

@ -4,18 +4,20 @@ import { Icon } from '@iconify/react';
import { Avatar } from '@/components/Avatar';
import { NavbarNavItem } from './NavItem';
import { GroupNav } from './GroupNav';
import { useHistory } from 'react-router';
/**
*
*/
export const Navbar: React.FC = React.memo(() => {
const userInfo = useAppSelector((state) => state.user.info);
const history = useHistory();
return (
<div className="w-18 bg-gray-900 flex flex-col justify-start items-center pt-4 pb-4 p-1">
{/* Navbar */}
<div className="flex-1">
<NavbarNavItem>
<NavbarNavItem onClick={() => history.push('/main/personal')}>
<Avatar
shape="square"
size={48}

Loading…
Cancel
Save