diff --git a/client/shared/index.tsx b/client/shared/index.tsx index 7b406a04..0a4ba35e 100644 --- a/client/shared/index.tsx +++ b/client/shared/index.tsx @@ -108,6 +108,8 @@ export { setAlert, showGlobalLoading, setGlobalLoading, + showNotification, + setNotification, } from './manager/ui'; // model diff --git a/client/shared/manager/ui.ts b/client/shared/manager/ui.ts index 9675d993..aa05f6ae 100644 --- a/client/shared/manager/ui.ts +++ b/client/shared/manager/ui.ts @@ -47,3 +47,11 @@ export const [showGlobalLoading, setGlobalLoading] = buildRegFn< >('global-loading', () => { return () => {}; }); + +/** + * 提示通知,返回关闭函数 + */ +export const [showNotification, setNotification] = + buildRegFn<(message: React.ReactNode, duration?: number) => () => void>( + 'notification' + ); diff --git a/client/web/assets/audio/telephone.mp3 b/client/web/assets/audio/telephone.mp3 new file mode 100644 index 00000000..61deeaf4 Binary files /dev/null and b/client/web/assets/audio/telephone.mp3 differ diff --git a/client/web/build/webpack.config.ts b/client/web/build/webpack.config.ts index 9a363874..c6b69e31 100644 --- a/client/web/build/webpack.config.ts +++ b/client/web/build/webpack.config.ts @@ -92,6 +92,10 @@ const plugins: Configuration['plugins'] = [ from: path.resolve(ROOT_PATH, './assets/images/avatar/'), to: 'images/avatar/', }, + { + from: path.resolve(ROOT_PATH, './assets/audio/'), + to: 'audio/', + }, { from: path.resolve(ROOT_PATH, '../../vercel.json'), to: 'vercel.json', diff --git a/client/web/src/init.tsx b/client/web/src/init.tsx index 62118286..0928967d 100644 --- a/client/web/src/init.tsx +++ b/client/web/src/init.tsx @@ -1,4 +1,4 @@ -import { message, Modal } from 'antd'; +import { message, Modal, notification } from 'antd'; import React from 'react'; import { buildStorage, @@ -18,10 +18,12 @@ import { parseUrlStr, onLanguageLoaded, version, + setNotification, } from 'tailchat-shared'; import { getPopupContainer } from './utils/dom-helper'; import { getUserJWT } from './utils/jwt-helper'; import _get from 'lodash/get'; +import _uniqueId from 'lodash/uniqueId'; import { recordMeasure } from './utils/measure-helper'; import { postMessageEvent } from './utils/event-helper'; import { setImageUrlParser, setWebMetaFormConfig } from 'tailchat-design'; @@ -73,6 +75,19 @@ setGlobalLoading((text) => { return hide; }); +setNotification((message, duration) => { + const key = _uniqueId('notification'); + notification.open({ + key, + message, + duration, + }); + + return () => { + notification.close(key); + }; +}); + setImageUrlParser(parseUrlStr); onLanguageLoaded(() => { diff --git a/client/web/src/plugin/common/index.ts b/client/web/src/plugin/common/index.ts index 0de504bb..00c5493c 100644 --- a/client/web/src/plugin/common/index.ts +++ b/client/web/src/plugin/common/index.ts @@ -62,6 +62,7 @@ export { showToasts, showSuccessToasts, showErrorToasts, + showNotification, fetchAvailableServices, isValidStr, useGroupPanelInfo, diff --git a/client/web/src/plugin/component/index.tsx b/client/web/src/plugin/component/index.tsx index b9975ef8..072660f8 100644 --- a/client/web/src/plugin/component/index.tsx +++ b/client/web/src/plugin/component/index.tsx @@ -64,7 +64,7 @@ export { PortalAdd, PortalRemove } from '@/components/Portal'; export { ErrorBoundary } from '@/components/ErrorBoundary'; export { ErrorView } from '@/components/ErrorView'; export { UserAvatar } from '@/components/UserAvatar'; -export { UserName } from '@/components/UserName'; +export { UserName, UserNamePure } from '@/components/UserName'; export { UserListItem } from '@/components/UserListItem'; export { Markdown, MarkdownEditor } from '@/components/Markdown'; export { Webview, WebviewKeepAlive } from '@/components/Webview'; diff --git a/client/web/tailchat.d.ts b/client/web/tailchat.d.ts index 19a2d1bf..c436cc6e 100644 --- a/client/web/tailchat.d.ts +++ b/client/web/tailchat.d.ts @@ -183,6 +183,11 @@ declare module '@capital/common' { export const showErrorToasts: (error: any) => void; + export const showNotification: ( + message: React.ReactNode, + duration?: number + ) => () => void; + export const fetchAvailableServices: any; export const isValidStr: (str: any) => str is string; @@ -632,6 +637,8 @@ declare module '@capital/component' { style?: React.CSSProperties; }>; + export const UserNamePure: any; + export const UserListItem: any; export const Markdown: any; diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/InviteCallNotification.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/InviteCallNotification.tsx new file mode 100644 index 00000000..1d136fad --- /dev/null +++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/components/InviteCallNotification.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Translate } from '../translate'; +import { IconBtn, UserNamePure } from '@capital/component'; + +const Root = styled.div` + .actions { + display: flex; + gap: 8px; + justify-content: flex-end; + margin-top: 20px; + } +`; + +interface InviteCallNotificationProps { + senderUserId: string; + roomName: string; + onJoin?: () => void; +} +const InviteCallNotification: React.FC = + React.memo((props) => { + return ( + + + ); + }); +InviteCallNotification.displayName = 'InviteCallNotification'; + +export default InviteCallNotification; 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 index 699aef0d..4c0b1b47 100644 --- 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 @@ -26,7 +26,7 @@ export const Member: React.FC = React.memo(() => { const getAction = useEvent((participant: Participant) => { return [ - !participant.isSpeaking && ( + participant.isSpeaking && ( ({Translate.isSpeaking}) ),
diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/index.tsx b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/index.tsx index 25de6960..093ab732 100644 --- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/index.tsx +++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/src/index.tsx @@ -7,10 +7,12 @@ import { panelWindowManager, regSocketEventListener, getGlobalState, + showNotification, } from '@capital/common'; import { Loadable } from '@capital/component'; import { useIconIsShow } from './navbar/useIconIsShow'; import { Translate } from './translate'; +import React from 'react'; const PLUGIN_ID = 'com.msgbyte.livekit'; @@ -27,6 +29,13 @@ const LivekitMeetingPanel = Loadable( } ); +const InviteCallNotification = Loadable( + () => import('./components/InviteCallNotification'), + { + componentName: `${PLUGIN_ID}:InviteCallNotification`, + } +); + regGroupPanel({ name: `${PLUGIN_ID}/livekitPanel`, label: Translate.voiceChannel, @@ -88,3 +97,27 @@ regPluginPanelAction({ (win.window as any).autoInviteIds = shouldInviteUserIds; }, }); + +regSocketEventListener({ + eventName: `plugin:${PLUGIN_ID}.inviteCall`, + eventFn: (data) => { + const { senderUserId, roomName } = data; + + const close = showNotification( + { + panelWindowManager.open( + `/panel/plugin/${PLUGIN_ID}/meeting/${roomName}`, + { + width: 1280, + height: 768, + } + ); + close(); + }} + />, + 0 + ); + }, +}); 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 aebe7c24..285ebe6a 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 @@ -85,4 +85,8 @@ export const Translate = { 'zh-CN': '用户呼叫失败,该用户离线', 'en-US': 'The user call failed because of offline', }), + inviteJoinCall: localTrans({ + 'zh-CN': '邀请你加入会话', + 'en-US': 'invite you to join conversation', + }), }; diff --git a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/types/tailchat.d.ts b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/types/tailchat.d.ts index 4fc5fdf9..c436cc6e 100644 --- a/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/types/tailchat.d.ts +++ b/server/plugins/com.msgbyte.livekit/web/plugins/com.msgbyte.livekit/types/tailchat.d.ts @@ -183,6 +183,11 @@ declare module '@capital/common' { export const showErrorToasts: (error: any) => void; + export const showNotification: ( + message: React.ReactNode, + duration?: number + ) => () => void; + export const fetchAvailableServices: any; export const isValidStr: (str: any) => str is string; @@ -237,8 +242,29 @@ declare module '@capital/common' { }; export const createPluginRequest: (pluginName: string) => { - get: (actionName: string, config?: any) => Promise; - post: (actionName: string, data?: any, config?: any) => Promise; + get: ( + actionName: string, + config?: any + ) => Promise<{ + data: any; + headers: Record; + status: number; + statusText: string; + config: Record; + request: any; + }>; + post: ( + actionName: string, + data?: any, + config?: any + ) => Promise<{ + data: any; + headers: Record; + status: number; + statusText: string; + config: Record; + request: any; + }>; }; export const postRequest: any; @@ -611,6 +637,8 @@ declare module '@capital/component' { style?: React.CSSProperties; }>; + export const UserNamePure: any; + export const UserListItem: any; export const Markdown: any;