feat: add group badge for current room participants

pull/105/merge
moonrailgun 2 years ago
parent 80347f9c41
commit d99e556c79

@ -34,11 +34,11 @@ export type GroupPanelFeature =
export interface GroupPanel {
/**
*
* , objectId
*/
id: string;
/**
*
* : `com.msgbyte.xxx/panel`
*/
name: string;
parentId?: string;

@ -7,7 +7,6 @@ import {
regSocketEventListener,
PermissionItemType,
GroupPanelFeature,
InboxItem,
buildRegMap,
BasicInboxItem,
} from 'tailchat-shared';
@ -255,7 +254,8 @@ export const [pluginPermission, regPluginPermission] =
*/
export const [pluginGroupPanelBadges, regGroupPanelBadge] = buildRegList<{
name: string;
render: (groupId: string, panelId: string) => React.ReactNode;
panelType: string;
render: React.ComponentType<{ groupId: string; panelId: string }>;
}>();
/**

@ -21,7 +21,11 @@ export const GroupAckPanelItem: React.FC<GroupTextPanelItemProps> = React.memo(
const { groupId, panel } = props;
const panelId = panel.id;
const hasUnread = useGroupTextPanelUnread(panelId);
const extraBadge = useGroupPanelExtraBadge(groupId, panelId);
const extraBadge = useGroupPanelExtraBadge(
groupId,
panelId,
panel.pluginPanelName ?? ''
);
const { checkIsMuted } = useUserNotifyMute();
const isMuted = checkIsMuted(panelId, groupId);

@ -38,7 +38,11 @@ export const SidebarItem: React.FC<{
const dispatch = useAppDispatch();
const { markConverseAllAck } = useConverseAck(panelId);
const extraMenuItems = useExtraMenuItems(panel);
const extraBadge = useGroupPanelExtraBadge(groupId, panelId);
const extraBadge = useGroupPanelExtraBadge(
groupId,
panelId,
panel.pluginPanelName ?? ''
);
const { checkIsMuted, toggleMute } = useUserNotifyMute();
if (!groupInfo) {

@ -66,7 +66,8 @@ export function useExtraMenuItems(panel: GroupPanel): ItemType[] {
*/
export function useGroupPanelExtraBadge(
groupId: string,
panelId: string
panelId: string,
panelType: string
): React.ReactNode[] {
const update = useUpdate();
@ -74,11 +75,17 @@ export function useGroupPanelExtraBadge(
update();
});
const extraBadge = pluginGroupPanelBadges.map((item) => (
<React.Fragment key={panelId + item.name}>
{item.render(groupId, panelId)}
</React.Fragment>
));
const extraBadge = pluginGroupPanelBadges
.filter((item) => item.panelType === panelType)
.map((item) => {
const Component = item.render;
return (
<React.Fragment key={panelId + item.name}>
<Component groupId={groupId} panelId={panelId} />
</React.Fragment>
);
});
return extraBadge;
}

@ -9,7 +9,7 @@ export function isGroupAckPanel(panel: GroupPanel) {
return true;
}
if (panel.type === GroupPanelType.GROUP) {
if (panel.type === GroupPanelType.PLUGIN) {
const pluginPanelInfo = findPluginPanelInfoByName(panel.name);
return pluginPanelInfo?.feature?.includes('ack') ?? false;
}

@ -1,24 +1,21 @@
import React, { useEffect, useRef } from 'react';
import { Avatar, Tooltip, UserAvatar, UserName } from '@capital/component';
import { useAsyncFn, useEvent } from '@capital/common';
import { request } from '../request';
import {
Avatar,
LoadingSpinner,
Tooltip,
UserAvatar,
UserName,
} from '@capital/component';
import { Translate } from '../translate';
import { useRoomParticipants } from '../utils/useRoomParticipants';
interface Props {
roomName: string;
}
export const ParticipantAvatars: React.FC<Props> = React.memo((props) => {
const containerEl = useRef<HTMLDivElement>(null);
const [{ value: participants = [] }, _handleFetchParticipants] =
useAsyncFn(async () => {
const { data } = await request.post('roomMembers', {
roomName: props.roomName,
});
return data ?? [];
}, [props.roomName]);
const handleFetchParticipants = useEvent(_handleFetchParticipants);
const { participants, fetchParticipants, isFirstLoading } =
useRoomParticipants(props.roomName);
useEffect(() => {
let timer: number;
@ -26,13 +23,13 @@ export const ParticipantAvatars: React.FC<Props> = React.memo((props) => {
const fn = async () => {
if (containerEl.current && containerEl.current.offsetWidth !== 0) {
// 该元素可见
await handleFetchParticipants();
await fetchParticipants();
}
timer = window.setTimeout(fn, 3000);
};
timer = window.setTimeout(fn, 3000);
fn();
return () => {
if (timer) {
@ -43,36 +40,33 @@ export const ParticipantAvatars: React.FC<Props> = React.memo((props) => {
let inner: React.ReactNode;
if (participants.length === 0) {
inner = Translate.nobodyInMeeting;
if (isFirstLoading) {
inner = <LoadingSpinner />;
} else {
inner = (
<>
<div>{Translate.peopleInMeeting}</div>
<Avatar.Group
maxCount={4}
maxPopoverTrigger="click"
maxStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}
>
{[
...participants,
...participants,
...participants,
...participants,
...participants,
...participants,
].map((info, i) => (
<Tooltip
key={`${info.sid}#${i}`}
title={<UserName userId={info.identity} />}
placement="top"
>
<UserAvatar userId={info.identity} />
</Tooltip>
))}
</Avatar.Group>
</>
);
if (participants.length === 0) {
inner = Translate.nobodyInMeeting;
} else {
inner = (
<>
<div>{Translate.peopleInMeeting}</div>
<Avatar.Group
maxCount={4}
maxPopoverTrigger="click"
maxStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}
>
{participants.map((info, i) => (
<Tooltip
key={`${info.sid}#${i}`}
title={<UserName userId={info.identity} />}
placement="top"
>
<UserAvatar userId={info.identity} />
</Tooltip>
))}
</Avatar.Group>
</>
);
}
}
return (

@ -0,0 +1,37 @@
import { Avatar, Tooltip, UserAvatar, UserName } from '@capital/component';
import React, { useEffect } from 'react';
import { useRoomParticipants } from '../utils/useRoomParticipants';
export const LivekitPanelBadge: React.FC<{
groupId: string;
panelId: string;
}> = React.memo((props) => {
const roomName = `${props.groupId}#${props.panelId}`;
const { participants, fetchParticipants } = useRoomParticipants(roomName);
useEffect(() => {
fetchParticipants();
}, []);
return (
<Avatar.Group
maxCount={4}
maxPopoverTrigger="click"
style={{ verticalAlign: 'middle' }}
maxStyle={{ color: '#f56a00', backgroundColor: '#fde3cf' }}
>
{participants.map((info, i) => (
<Tooltip
key={`${info.sid}#${i}`}
title={<UserName userId={info.identity} />}
placement="top"
>
<UserAvatar userId={info.identity} size={24} />
</Tooltip>
))}
</Avatar.Group>
);
});
LivekitPanelBadge.displayName = 'LivekitPanelBadge';
export default LivekitPanelBadge;

@ -1,4 +1,8 @@
import { regCustomPanel, regGroupPanel } from '@capital/common';
import {
regCustomPanel,
regGroupPanel,
regGroupPanelBadge,
} from '@capital/common';
import { Loadable } from '@capital/component';
import { useIconIsShow } from './navbar/useIconIsShow';
import { Translate } from './translate';
@ -16,6 +20,15 @@ regGroupPanel({
}),
});
regGroupPanelBadge({
name: `${PLUGIN_ID}/livekitPanelBadge`,
panelType: `${PLUGIN_ID}/livekitPanel`,
render: Loadable(() => import('./group/LivekitPanelBadge'), {
componentName: `${PLUGIN_ID}:LivekitPanelBadge`,
fallback: null,
}),
});
regCustomPanel({
position: 'navbar-more',
icon: 'mingcute:voice-line',

@ -0,0 +1,34 @@
import { useAsyncFn, useEvent } from '@capital/common';
import { useMemo, useRef } from 'react';
import { request } from '../request';
export function useRoomParticipants(roomName: string) {
const [{ value: participants = [], loading }, _handleFetchParticipants] =
useAsyncFn(async () => {
const { data } = await request.post('roomMembers', {
roomName,
});
return data ?? [];
}, [roomName]);
const fetchParticipants = useEvent(_handleFetchParticipants);
const lockRef = useRef(false);
const isFirstLoading = useMemo(() => {
if (loading && lockRef.current === false) {
lockRef.current = true;
return true;
}
return false;
}, [loading]);
return {
loading,
isFirstLoading,
participants,
fetchParticipants,
};
}
Loading…
Cancel
Save