diff --git a/client/web/src/plugin/component/index.tsx b/client/web/src/plugin/component/index.tsx
index 8efdef4fbec..b9975ef84a1 100644
--- a/client/web/src/plugin/component/index.tsx
+++ b/client/web/src/plugin/component/index.tsx
@@ -65,6 +65,7 @@ export { ErrorBoundary } from '@/components/ErrorBoundary';
export { ErrorView } from '@/components/ErrorView';
export { UserAvatar } from '@/components/UserAvatar';
export { UserName } from '@/components/UserName';
+export { UserListItem } from '@/components/UserListItem';
export { Markdown, MarkdownEditor } from '@/components/Markdown';
export { Webview, WebviewKeepAlive } from '@/components/Webview';
export { Card } from '@/components/Card';
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Chat.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Chat.tsx
index 948005a4b8d..084c9ee75d0 100644
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Chat.tsx
+++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Chat.tsx
@@ -12,11 +12,6 @@ import * as React from 'react';
import { Translate } from '../../translate';
import { cloneSingleChild } from '../../utils/common';
import { useObservableState } from '../../utils/useObservableState';
-// import { useRoomContext } from '../context';
-// import { useObservableState } from '../hooks/internal/useObservableState';
-// import { cloneSingleChild } from '../utils';
-// import type { MessageFormatter } from '../components/ChatEntry';
-// import { ChatEntry } from '../components/ChatEntry';
export type { ChatMessage, ReceivedChatMessage };
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/ControlBar.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/ControlBar.tsx
index 2b5fa1c09f6..e18393c480c 100644
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/ControlBar.tsx
+++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/ControlBar.tsx
@@ -3,7 +3,6 @@ import * as React from 'react';
import { supportsScreenSharing } from '@livekit/components-core';
import {
- ChatToggle,
DisconnectButton,
MediaDeviceMenu,
StartAudio,
@@ -13,15 +12,15 @@ import {
} from '@livekit/components-react';
import { Translate } from '../../translate';
import { useMediaQuery } from '../../utils/useMediaQuery';
-import { ChatIcon } from './icons/ChatIcon';
-import { LeaveIcon } from './icons/LeaveIcon';
import { useMeetingContextState } from '../../context/MeetingContext';
+import { Icon } from '@capital/component';
/** @public */
export type ControlBarControls = {
microphone?: boolean;
camera?: boolean;
chat?: boolean;
+ member?: boolean;
screenShare?: boolean;
leave?: boolean;
};
@@ -71,13 +70,15 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
if (!localPermissions) {
visibleControls.camera = false;
visibleControls.chat = false;
+ visibleControls.member = false;
visibleControls.microphone = false;
visibleControls.screenShare = false;
} else {
visibleControls.camera ??= localPermissions.canPublish;
visibleControls.microphone ??= localPermissions.canPublish;
visibleControls.screenShare ??= localPermissions.canPublish;
- visibleControls.chat ??= localPermissions.canPublishData && controls?.chat;
+ visibleControls.chat ??= localPermissions.canPublishData;
+ visibleControls.member ??= localPermissions.canSubscribe;
}
const showIcon = React.useMemo(
@@ -137,14 +138,21 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
{visibleControls.chat && (
)}
+ {visibleControls.member && (
+
+ )}
+
{visibleControls.leave && (
- {showIcon && }
+ {showIcon && }
{showText && Translate.leave}
)}
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Member.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Member.tsx
new file mode 100644
index 00000000000..699aef0df72
--- /dev/null
+++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/Member.tsx
@@ -0,0 +1,54 @@
+import { useParticipants } from '@livekit/components-react';
+import * as React from 'react';
+import styled from 'styled-components';
+import { Icon, UserListItem } from '@capital/component';
+import { useEvent } from '@capital/common';
+import type { Participant } from 'livekit-client';
+import { Translate } from '../../translate';
+
+const MemberList = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ width: clamp(200px, 55ch, 60ch);
+ background-color: var(--lk-bg2);
+ border-left: 1px solid var(--lk-border-color);
+ padding: 8px;
+`;
+
+const IsSpeakingTip = styled.div`
+ font-size: 12px;
+ opacity: 0.6;
+`;
+
+export const Member: React.FC = React.memo(() => {
+ const participants = useParticipants();
+
+ const getAction = useEvent((participant: Participant) => {
+ return [
+ !participant.isSpeaking && (
+ ({Translate.isSpeaking})
+ ),
+
+ {participant.isMicrophoneEnabled ? (
+
+ ) : (
+
+ )}
+
,
+ ];
+ });
+
+ return (
+
+ {participants.map((member) => (
+
+ ))}
+
+ );
+});
+Member.displayName = 'Member';
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/VideoConference.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/VideoConference.tsx
index 9c603efcc31..87d77540f86 100644
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/VideoConference.tsx
+++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/VideoConference.tsx
@@ -29,6 +29,7 @@ import { ControlBar } from './ControlBar';
import { Chat } from './Chat';
import { FocusLayout } from './FocusLayout';
import { useMeetingContextState } from '../../context/MeetingContext';
+import { Member } from './Member';
/**
* @public
@@ -145,12 +146,14 @@ export const VideoConference: React.FC = React.memo(
)}
-
+
{rightPanel === 'chat' && (
)}
+
+ {rightPanel === 'member' && }
)}
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/icons/ChatIcon.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/icons/ChatIcon.tsx
deleted file mode 100644
index 957f3c24dc8..00000000000
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/icons/ChatIcon.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as React from 'react';
-import type { SVGProps } from 'react';
-
-export const ChatIcon = (props: SVGProps) => (
-
-);
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/icons/LeaveIcon.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/icons/LeaveIcon.tsx
deleted file mode 100644
index eed41533070..00000000000
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/lib/icons/LeaveIcon.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as React from 'react';
-import type { SVGProps } from 'react';
-
-export const LeaveIcon = (props: SVGProps) => (
-
-);
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/translate.ts b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/translate.ts
index 996705f8331..c082d4b9d3f 100644
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/translate.ts
+++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/translate.ts
@@ -41,6 +41,10 @@ export const Translate = {
'zh-CN': '聊天',
'en-US': 'Chat',
}),
+ member: localTrans({
+ 'zh-CN': '成员',
+ 'en-US': 'Member',
+ }),
leave: localTrans({
'zh-CN': '离开',
'en-US': 'Leave',
@@ -53,6 +57,10 @@ export const Translate = {
'zh-CN': '输入消息...',
'en-US': 'Enter a message...',
}),
+ isSpeaking: localTrans({
+ 'zh-CN': '正在发言',
+ 'en-US': 'Is speaking',
+ }),
nobodyInMeeting: localTrans({
'zh-CN': '当前无人在会...',
'en-US': 'Nobody in Meeting...',
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/utils/useObservableState.ts b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/utils/useObservableState.ts
index e174f5503df..9d15592eeb0 100644
--- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/utils/useObservableState.ts
+++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/utils/useObservableState.ts
@@ -10,11 +10,13 @@ export function useObservableState(
startWith: T
) {
const [state, setState] = React.useState(startWith);
+
React.useEffect(() => {
// observable state doesn't run in SSR
if (typeof window === 'undefined' || !observable) return;
const subscription = observable.subscribe(setState);
return () => subscription.unsubscribe();
}, [observable]);
+
return state;
}