feat: group preview component

feat/group-preview
moonrailgun 2 years ago
parent 0d21980e40
commit 030adfa4b3

@ -55,13 +55,13 @@ export class AppSocket {
if (resp.result === true) { if (resp.result === true) {
resolve(resp.data); resolve(resp.data);
} else if (resp.result === false) { } else if (resp.result === false) {
reject( const error = new SocketEventError(
new SocketEventError(
`[${eventName}]: ${resp.message}: \ndata: ${JSON.stringify( `[${eventName}]: ${resp.message}: \ndata: ${JSON.stringify(
eventData eventData
)}` )}`
)
); );
console.error(error);
reject(error);
} }
}); });
}); });

@ -59,6 +59,14 @@ const InviteRoute = Loadable(
) )
); );
const PreviewRoute = Loadable(
() =>
import(
/* webpackChunkName: 'preview' */ /* webpackPreload: true */
'./routes/Preview'
)
);
export const TcAntdProvider: React.FC<PropsWithChildren> = React.memo( export const TcAntdProvider: React.FC<PropsWithChildren> = React.memo(
(props) => { (props) => {
const { value: locale } = useAsync(async (): Promise<Locale> => { const { value: locale } = useAsync(async (): Promise<Locale> => {
@ -168,6 +176,7 @@ export const App: React.FC = React.memo(() => {
<Route path="/main/*" element={<MainRoute />} /> <Route path="/main/*" element={<MainRoute />} />
<Route path="/panel/*" element={<PanelRoute />} /> <Route path="/panel/*" element={<PanelRoute />} />
<Route path="/invite/:inviteCode" element={<InviteRoute />} /> <Route path="/invite/:inviteCode" element={<InviteRoute />} />
<Route path="/preview/*" element={<PreviewRoute />} />
<Route <Route
path="/plugin/*" path="/plugin/*"
element={ element={

@ -0,0 +1,14 @@
import React from 'react';
import { useSocket } from './useSocket';
/**
* A Component for Group Preview Entry
*/
export const GroupPreview: React.FC<{
groupId: string;
}> = React.memo((props) => {
useSocket(props.groupId);
return null;
});
GroupPreview.displayName = 'GroupPreview';

@ -0,0 +1,19 @@
import React from 'react';
import { ChatMessageList } from '../ChatBox/ChatMessageList';
interface GroupPreviewMessageListProps {
groupId: string;
converseId: string;
}
export const GroupPreviewMessageList: React.FC<GroupPreviewMessageListProps> =
React.memo(() => {
return (
<ChatMessageList
messages={[]}
isLoadingMore={false}
hasMoreMessage={false}
onLoadMore={async () => {}}
/>
);
});
GroupPreviewMessageList.displayName = 'GroupPreviewMessageList';

@ -0,0 +1,3 @@
Preview Group Message like Discord.
NOTICE: GroupPreview should has independent context because its should can run in non-main page.

@ -0,0 +1 @@
export { GroupPreview } from './GroupPreview';

@ -0,0 +1,29 @@
import { create } from 'zustand';
import type { model } from 'tailchat-shared';
interface ChatConverseState extends model.converse.ChatConverseInfo {
messages: model.message.LocalChatMessage[];
}
interface GroupPreviewState {
groupInfo: model.group.GroupInfo | null;
converses: Record<string, ChatConverseState>;
}
function getDefaultState() {
return {
groupInfo: null,
converses: {},
};
}
export const useGroupPreviewStore = create<GroupPreviewState>((get) => ({
...getDefaultState(),
}));
/**
*
*/
export function resetGroupPreviewState() {
useGroupPreviewStore.setState(getDefaultState());
}

@ -0,0 +1,41 @@
import { useSocketContext } from '@/context/SocketContext';
import { useEffect } from 'react';
import { GroupInfo, GroupPanelType } from 'tailchat-shared';
import { resetGroupPreviewState, useGroupPreviewStore } from './store';
export function useSocket(groupId: string) {
const socket = useSocketContext();
useEffect(() => {
socket.request('group.preview.joinGroupRooms', {
groupId,
});
socket
.request<GroupInfo>('group.preview.getGroupInfo', {
groupId,
})
.then((groupInfo) => {
console.log('groupInfo', groupInfo);
useGroupPreviewStore.setState({
groupInfo,
});
if (Array.isArray(groupInfo.panels)) {
const textPanels = groupInfo.panels.map(
(p) => p.type === GroupPanelType.TEXT
);
// TODO
}
});
return () => {
socket.request('group.preview.leaveGroupRooms', {
groupId,
});
resetGroupPreviewState();
};
}, [groupId]);
}

@ -0,0 +1,34 @@
import { GroupPreview } from '@/components/GroupPreview';
import { NotFound } from '@/components/NotFound';
import React from 'react';
import { Route, Routes, useParams } from 'react-router';
import { t } from 'tailchat-shared';
import { MainProvider } from '../Main/Provider';
const PreviewRoute: React.FC = React.memo(() => {
return (
<MainProvider>
<Routes>
<Route path="/:groupId" element={<GroupPreviewRoute />} />
<Route path="/*" element={t('未知的页面')} />
</Routes>
</MainProvider>
);
});
PreviewRoute.displayName = 'PreviewRoute';
const GroupPreviewRoute: React.FC = React.memo(() => {
const { groupId } = useParams<{
groupId: string;
}>();
if (!groupId) {
return <NotFound />;
}
return <GroupPreview groupId={groupId} />;
});
GroupPreviewRoute.displayName = 'GroupPreviewRoute';
export default PreviewRoute;

@ -25,6 +25,7 @@ import {
db, db,
} from 'tailchat-server-sdk'; } from 'tailchat-server-sdk';
import moment from 'moment'; import moment from 'moment';
import type { GroupStruct } from 'tailchat-server-sdk';
interface GroupService interface GroupService
extends TcService, extends TcService,
@ -48,6 +49,7 @@ class GroupService extends TcService {
'getJoinedGroupAndPanelIds', 'getJoinedGroupAndPanelIds',
this.getJoinedGroupAndPanelIds this.getJoinedGroupAndPanelIds
); );
this.registerAction('getGroupSocketRooms', this.getGroupSocketRooms);
this.registerAction('getGroupBasicInfo', this.getGroupBasicInfo, { this.registerAction('getGroupBasicInfo', this.getGroupBasicInfo, {
params: { params: {
groupId: 'string', groupId: 'string',
@ -234,7 +236,7 @@ class GroupService extends TcService {
* *
* socket * socket
*/ */
private getSubscribedGroupPanelIds(group: Group): { private getSubscribedGroupPanelIds(group: GroupStruct): {
textPanelIds: string[]; textPanelIds: string[];
subscribeFeaturePanelIds: string[]; subscribeFeaturePanelIds: string[];
} { } {
@ -254,7 +256,7 @@ class GroupService extends TcService {
* id * id
* *
*/ */
private getGroupTextPanelIds(group: Group): string[] { private getGroupTextPanelIds(group: GroupStruct): string[] {
// TODO: 先无视权限, 把所有的信息全部显示 // TODO: 先无视权限, 把所有的信息全部显示
const textPanelIds = group.panels const textPanelIds = group.panels
.filter((p) => p.type === GroupPanelType.TEXT) .filter((p) => p.type === GroupPanelType.TEXT)
@ -268,7 +270,7 @@ class GroupService extends TcService {
* @param group * @param group
*/ */
private getGroupPanelIdsWithFeature( private getGroupPanelIdsWithFeature(
group: Group, group: GroupStruct,
feature: PanelFeature feature: PanelFeature
): string[] { ): string[] {
const featureAllPanelNames = this.getPanelNamesWithFeature(feature); const featureAllPanelNames = this.getPanelNamesWithFeature(feature);
@ -301,12 +303,14 @@ class GroupService extends TcService {
throw new NoPermissionError(t('创建群组功能已被管理员禁用')); throw new NoPermissionError(t('创建群组功能已被管理员禁用'));
} }
const group = await this.adapter.model.createGroup({ const doc = await this.adapter.model.createGroup({
name, name,
panels, panels,
owner: userId, owner: userId,
}); });
const group = await this.transformDocuments(ctx, {}, doc);
const { textPanelIds, subscribeFeaturePanelIds } = const { textPanelIds, subscribeFeaturePanelIds } =
this.getSubscribedGroupPanelIds(group); this.getSubscribedGroupPanelIds(group);
@ -315,10 +319,10 @@ class GroupService extends TcService {
userId userId
); );
return this.transformDocuments(ctx, {}, group); return group;
} }
async getUserGroups(ctx: TcContext): Promise<Group[]> { async getUserGroups(ctx: TcContext): Promise<GroupStruct[]> {
const userId = ctx.meta.userId; const userId = ctx.meta.userId;
const groups = await this.adapter.model.getUserGroups(userId); const groups = await this.adapter.model.getUserGroups(userId);
@ -351,6 +355,20 @@ class GroupService extends TcService {
}; };
} }
/**
*
*/
async getGroupSocketRooms(ctx: TcContext<{ groupId: string }>): Promise<{
textPanelIds: string[];
subscribeFeaturePanelIds: string[];
}> {
const groupId = ctx.params.groupId;
const group = await call(ctx).getGroupInfo(groupId);
return this.getSubscribedGroupPanelIds(group);
}
/** /**
* *
*/ */
@ -550,7 +568,7 @@ class GroupService extends TcService {
) )
.exec(); .exec();
const group: Group = await this.transformDocuments(ctx, {}, doc); const group: GroupStruct = await this.transformDocuments(ctx, {}, doc);
this.notifyGroupInfoUpdate(ctx, group); // 推送变更 this.notifyGroupInfoUpdate(ctx, group); // 推送变更
this.unicastNotify(ctx, userId, 'add', group); this.unicastNotify(ctx, userId, 'add', group);
@ -1241,13 +1259,15 @@ class GroupService extends TcService {
*/ */
private async notifyGroupInfoUpdate( private async notifyGroupInfoUpdate(
ctx: TcContext, ctx: TcContext,
group: Group group: Group | GroupStruct
): Promise<Group> { ): Promise<GroupStruct> {
const groupId = String(group._id); const groupId = String(group._id);
let json = group; let json: GroupStruct;
if (_.isPlainObject(group) === false) { if (_.isPlainObject(group) === false) {
// 当传入的数据为group doc时 // 当传入的数据为group doc时
json = await this.transformDocuments(ctx, {}, group); json = await this.transformDocuments(ctx, {}, group);
} else {
json = group as any;
} }
this.cleanGroupInfoCache(groupId); this.cleanGroupInfoCache(groupId);

@ -0,0 +1,78 @@
import { TcService, TcContext, call } from 'tailchat-server-sdk';
class GroupPreviewService extends TcService {
get serviceName(): string {
return 'group.preview';
}
onInit(): void {
/**
* TODO: action
*/
this.registerAction('joinGroupRooms', this.joinGroupRooms, {
params: {
groupId: 'string',
},
});
this.registerAction('leaveGroupRooms', this.leaveGroupRooms, {
params: {
groupId: 'string',
},
});
this.registerAction('getGroupInfo', this.getGroupInfo, {
params: {
groupId: 'string',
},
});
}
async joinGroupRooms(ctx: TcContext<{ groupId: string }>) {
const groupId = ctx.params.groupId;
const { textPanelIds, subscribeFeaturePanelIds } = await ctx.call<
{
textPanelIds: string[];
subscribeFeaturePanelIds: string[];
},
{ groupId: string }
>('group.getGroupSocketRooms', {
groupId,
});
await call(ctx).joinSocketIORoom([
groupId,
...textPanelIds,
...subscribeFeaturePanelIds,
]);
}
async leaveGroupRooms(ctx: TcContext<{ groupId: string }>) {
const groupId = ctx.params.groupId;
const { textPanelIds, subscribeFeaturePanelIds } = await ctx.call<
{
textPanelIds: string[];
subscribeFeaturePanelIds: string[];
},
{ groupId: string }
>('group.getGroupSocketRooms', {
groupId,
});
await call(ctx).leaveSocketIORoom([
groupId,
...textPanelIds,
...subscribeFeaturePanelIds,
]);
}
async getGroupInfo(ctx: TcContext<{ groupId: string }>) {
const groupId = ctx.params.groupId;
const groupInfo = await call(ctx).getGroupInfo(groupId);
return groupInfo;
}
}
export default GroupPreviewService;
Loading…
Cancel
Save