mirror of https://github.com/msgbyte/tailchat
refactor(shared): 群组列表获取与展示
parent
229a037275
commit
fc3d9ae06f
@ -0,0 +1,25 @@
|
||||
export enum GroupPanelType {
|
||||
TEXT = 0,
|
||||
GROUP = 1,
|
||||
}
|
||||
|
||||
export interface GroupMember {
|
||||
role: string; // 角色
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export interface GroupPanel {
|
||||
id: string; // 在群组中唯一
|
||||
name: string;
|
||||
parentId?: string;
|
||||
type: GroupPanelType;
|
||||
}
|
||||
|
||||
export interface GroupInfo {
|
||||
_id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
creator: string;
|
||||
members: GroupMember[];
|
||||
panels: GroupPanel[];
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import type { GroupInfo } from '../../model/group';
|
||||
|
||||
interface GroupState {
|
||||
groups: Record<string, GroupInfo>;
|
||||
}
|
||||
|
||||
const initialState: GroupState = {
|
||||
groups: {},
|
||||
};
|
||||
|
||||
const groupSlice = createSlice({
|
||||
name: 'group',
|
||||
initialState,
|
||||
reducers: {
|
||||
appendGroups(state, action: PayloadAction<GroupInfo[]>) {
|
||||
const groups = action.payload;
|
||||
|
||||
for (const group of groups) {
|
||||
state.groups[group._id] = {
|
||||
...state.groups[group._id],
|
||||
...group,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const groupActions = groupSlice.actions;
|
||||
export const groupReducer = groupSlice.reducer;
|
@ -1,13 +1,16 @@
|
||||
import { combineReducers } from '@reduxjs/toolkit';
|
||||
import { userReducer } from './user';
|
||||
import { chatReducer } from './chat';
|
||||
import { groupReducer } from './group';
|
||||
|
||||
export const appReducer = combineReducers({
|
||||
user: userReducer,
|
||||
chat: chatReducer,
|
||||
group: groupReducer,
|
||||
});
|
||||
|
||||
export type AppState = ReturnType<typeof appReducer>;
|
||||
|
||||
export { userActions } from './user';
|
||||
export { chatActions } from './chat';
|
||||
export { groupActions } from './group';
|
||||
|
@ -0,0 +1,39 @@
|
||||
import { Avatar } from '@/components/Avatar';
|
||||
import { Icon } from '@iconify/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { GroupInfo, useAppSelector } from 'tailchat-shared';
|
||||
import { NavbarNavItem } from './NavItem';
|
||||
|
||||
function useGroups(): GroupInfo[] {
|
||||
const groups = useAppSelector((state) => state.group.groups);
|
||||
return useMemo(
|
||||
() => Object.entries(groups).map(([_, group]) => group),
|
||||
[groups]
|
||||
);
|
||||
}
|
||||
|
||||
export const GroupNav: React.FC = React.memo(() => {
|
||||
const groups = useGroups();
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{Array.isArray(groups) &&
|
||||
groups.map((group) => (
|
||||
<NavbarNavItem key={group._id}>
|
||||
<Avatar
|
||||
shape="square"
|
||||
size={48}
|
||||
name={group.name}
|
||||
src={group.avatar}
|
||||
/>
|
||||
</NavbarNavItem>
|
||||
))}
|
||||
|
||||
{/* 创建群组 */}
|
||||
<NavbarNavItem className="bg-green-500">
|
||||
<Icon className="text-3xl text-white" icon="mdi-plus" />
|
||||
</NavbarNavItem>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
GroupNav.displayName = 'GroupNav';
|
@ -0,0 +1,19 @@
|
||||
import type { ClassValue } from 'clsx';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
export const NavbarNavItem: React.FC<{
|
||||
className?: ClassValue;
|
||||
}> = React.memo((props) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'w-12 h-12 hover:rounded-lg bg-gray-300 transition-all rounded-1/2 cursor-pointer flex items-center justify-center overflow-hidden',
|
||||
props.className
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
NavbarNavItem.displayName = 'NavbarNavItem';
|
Loading…
Reference in New Issue