diff --git a/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/component.tsx b/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/component.tsx index 48037108..74289ad2 100644 --- a/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/component.tsx +++ b/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/component.tsx @@ -1,8 +1,13 @@ import * as React from 'react'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; -import { BbbPluginSdk, PluginApi } from 'bigbluebutton-html-plugin-sdk'; -import { SampleUiCommandsPluginProps } from './types'; +import { + BbbPluginSdk, + PluginApi, + ActionButtonDropdownOption, +} from 'bigbluebutton-html-plugin-sdk'; +import { PrivateChatSubscriptionResult, SampleUiCommandsPluginProps } from './types'; +import { GET_CHATS_SUBSCRIPTION } from './query'; function SampleUiCommandsPlugin( { pluginUuid: uuid }: SampleUiCommandsPluginProps, @@ -10,12 +15,82 @@ function SampleUiCommandsPlugin( BbbPluginSdk.initialize(uuid); const pluginApi: PluginApi = BbbPluginSdk.getPluginApi(uuid); + const [privateChatId, setPrivateChatId] = useState(null); + const [targetUserId, setTargetUserId] = useState(null); + + // Get all users in the meeting + const { data: usersData } = pluginApi.useUsersBasicInfo(); + + // Get current user to exclude from random selection + const { data: currentUser } = pluginApi.useCurrentUser(); + + // Subscribe to private chats to get chatId after creating + const { data: privateChatsData } = pluginApi.useCustomSubscription?.< + PrivateChatSubscriptionResult + >(GET_CHATS_SUBSCRIPTION) || { data: undefined }; + + // Monitor for the chatId of the private chat we created + useEffect(() => { + if (targetUserId && privateChatsData?.chat) { + const targetChat = privateChatsData.chat.find( + (chat) => chat.participant.userId === targetUserId, + ); + if (targetChat && targetChat.chatId !== privateChatId) { + setPrivateChatId(targetChat.chatId); + } + } + }, [privateChatsData, targetUserId, privateChatId]); + useEffect(() => { - pluginApi.uiCommands.chat.form.open(); - pluginApi.uiCommands.chat.form.fill({ - text: 'Just an example message filled by the plugin', - }); - }, []); + if (privateChatId) { + // Open the private chat panel + pluginApi.uiCommands?.chat.form.open({ + chatId: privateChatId, + }); + + // Fill the private chat form with a hello world message + setTimeout(() => { + pluginApi.uiCommands?.chat.form.fill({ + text: 'Hello World! This message was filled by the plugin.', + }); + }, 500); + } + }, [privateChatId]); + + useEffect(() => { + // Set up the action button dropdown + pluginApi.setActionButtonDropdownItems([ + new ActionButtonDropdownOption({ + label: 'Create Private Chat & Say Hello', + icon: 'user', + tooltip: 'Creates a private chat with a random user and sends Hello World', + dataTest: 'createPrivateChatButton', + allowed: true, + onClick: () => { + // Get a random user (excluding current user) + if (usersData?.user && currentUser) { + const otherUsers = usersData.user.filter( + (user) => user.userId !== currentUser.userId, + ); + + if (otherUsers.length > 0) { + const randomUser = otherUsers[ + Math.floor(Math.random() * otherUsers.length) + ]; + + // Store the target user ID to track the chat creation + setTargetUserId(randomUser.userId); + + // Create private chat with the random user + pluginApi.serverCommands?.chat.createPrivateChat({ + userId: randomUser.userId, + }); + } + } + }, + }), + ]); + }, [usersData, currentUser]); return null; } diff --git a/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/query.ts b/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/query.ts new file mode 100644 index 00000000..d79ce71f --- /dev/null +++ b/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/query.ts @@ -0,0 +1,11 @@ +export const GET_CHATS_SUBSCRIPTION = ` + subscription PrivateChats { + chat(where: {public: {_eq: false}}) { + chatId + participant { + userId + name + } + } + } +`; diff --git a/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/types.ts b/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/types.ts index 93ff5b78..e2b2b8a3 100644 --- a/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/types.ts +++ b/samples/sample-ui-commands-plugin/src/sample-ui-commands-plugin-item/types.ts @@ -1,6 +1,14 @@ -interface SampleUiCommandsPluginProps { +export interface SampleUiCommandsPluginProps { pluginName: string, pluginUuid: string, } -export { SampleUiCommandsPluginProps }; +export interface PrivateChatSubscriptionResult { + chat: Array<{ + chatId: string; + participant: { + userId: string; + name: string; + }; + }>; +} diff --git a/src/server-commands/chat/commands.ts b/src/server-commands/chat/commands.ts index c7f107b8..09463e88 100644 --- a/src/server-commands/chat/commands.ts +++ b/src/server-commands/chat/commands.ts @@ -3,9 +3,35 @@ import { ChatCommandsEnum } from './enum'; import { ChatSendMessageCommandArguments, ChatSendMessageEventArguments, + SendChatMessageArguments, + CreatePrivateChatCommandArguments, } from './types'; export const chat = (pluginName: string) => ({ + /** + * Sends chat message to specific chat. + * + * @param SendChatMessageArguments the text, custom metadata(optional), optional flag + * to tell whether or not the message will be custom, and the chatId; + * Refer to {@link SendChatMessageArguments} to understand the argument + * structure. + */ + sendChatMessage: ( + chatMessageArguments: SendChatMessageArguments, + ) => { + window.dispatchEvent( + new CustomEvent< + ChatSendMessageEventArguments + >(ChatCommandsEnum.SEND_MESSAGE, { + detail: { + pluginName, + ...chatMessageArguments, + custom: chatMessageArguments?.custom || false, + }, + }), + ); + }, + /** * Sends chat message to the public chat. * @@ -56,4 +82,25 @@ export const chat = (pluginName: string) => ({ }), ); }, + + /** + * Creates a private chat with a specific user. + * + * @param createPrivateChatCommandArguments the userId of the user to create a private chat with. + * Refer to {@link CreatePrivateChatCommandArguments} to understand the argument + * structure. + */ + createPrivateChat: ( + createPrivateChatCommandArguments: CreatePrivateChatCommandArguments, + ) => { + window.dispatchEvent( + new CustomEvent< + CreatePrivateChatCommandArguments + >(ChatCommandsEnum.CREATE_PRIVATE_CHAT, { + detail: { + ...createPrivateChatCommandArguments, + }, + }), + ); + }, }); diff --git a/src/server-commands/chat/enum.ts b/src/server-commands/chat/enum.ts index e238ac51..01e088d3 100644 --- a/src/server-commands/chat/enum.ts +++ b/src/server-commands/chat/enum.ts @@ -1,3 +1,4 @@ export enum ChatCommandsEnum { SEND_MESSAGE = 'CHAT_SEND_MESSAGE', + CREATE_PRIVATE_CHAT = 'CHAT_CREATE_PRIVATE_CHAT', } diff --git a/src/server-commands/chat/types.ts b/src/server-commands/chat/types.ts index 1f491784..a9ff5c1f 100644 --- a/src/server-commands/chat/types.ts +++ b/src/server-commands/chat/types.ts @@ -4,17 +4,34 @@ export interface ChatSendMessageCommandArguments { } export interface ChatSendMessageEventArguments - extends ChatSendMessageCommandArguments { + extends ChatSendMessageCommandArguments { pluginName: string; chatId: string; custom: boolean; } +export interface SendChatMessageArguments { + textMessageInMarkdownFormat: string; + chatId: string; + custom?: boolean; + pluginCustomMetadata?: string; +} + +export interface CreatePrivateChatCommandArguments { + userId: string; +} + export interface ServerCommandsChatObject { + sendChatMessage: ( + chatMessageArguments: SendChatMessageArguments + ) => void; sendCustomPublicChatMessage: ( chatSendCustomPublicChatMessageCommandArguments: ChatSendMessageCommandArguments ) => void; sendPublicChatMessage: ( chatSendPublicChatMessageCommandArguments: ChatSendMessageCommandArguments ) => void; + createPrivateChat: ( + createPrivateChatCommandArguments: CreatePrivateChatCommandArguments + ) => void; } diff --git a/src/ui-commands/chat/enums.ts b/src/ui-commands/chat/enums.ts new file mode 100644 index 00000000..209e9a84 --- /dev/null +++ b/src/ui-commands/chat/enums.ts @@ -0,0 +1,3 @@ +export enum ChatUiCommandsEnum { + OPEN_PRIVATE_CHAT = 'OPEN_PRIVATE_CHAT_COMMAND', +} diff --git a/src/ui-commands/chat/form/commands.ts b/src/ui-commands/chat/form/commands.ts index eb2b817a..066a6fe1 100644 --- a/src/ui-commands/chat/form/commands.ts +++ b/src/ui-commands/chat/form/commands.ts @@ -1,12 +1,26 @@ import { ChatFormCommandsEnum } from './enums'; -import { FillChatFormCommandArguments } from './types'; +import { + FillChatFormCommandArguments, + OpenChatFormCommandArguments, +} from './types'; export const form = { /** - * Opens the public chat panel automatically. + * Opens the chat panel automatically. If chatId is provided, opens that specific chat. + * + * @param openChatCommandArgument Optional chatId to open a specific chat panel. + * Refer to {@link OpenChatFormCommandArguments} to understand the argument structure. */ - open: () => { - window.dispatchEvent(new Event(ChatFormCommandsEnum.OPEN)); + open: (openChatCommandArgument?: OpenChatFormCommandArguments) => { + if (openChatCommandArgument) { + window.dispatchEvent( + new CustomEvent(ChatFormCommandsEnum.OPEN, { + detail: openChatCommandArgument, + }), + ); + } else { + window.dispatchEvent(new Event(ChatFormCommandsEnum.OPEN)); + } }, /** diff --git a/src/ui-commands/chat/form/types.ts b/src/ui-commands/chat/form/types.ts index bf49806e..4a8aa1c0 100644 --- a/src/ui-commands/chat/form/types.ts +++ b/src/ui-commands/chat/form/types.ts @@ -2,7 +2,11 @@ export interface FillChatFormCommandArguments { text: string; } +export interface OpenChatFormCommandArguments { + chatId: string; +} + export interface UiCommandsChatFormObject { - open: () => void; + open: (openChatCommandArgument?: OpenChatFormCommandArguments) => void; fill: (FillChatFormCommandArguments: FillChatFormCommandArguments) => void; } diff --git a/src/ui-commands/index.ts b/src/ui-commands/index.ts index 634b656f..486b7fbd 100644 --- a/src/ui-commands/index.ts +++ b/src/ui-commands/index.ts @@ -1,2 +1,3 @@ export { NotificationTypeUiCommand } from './notification/enums'; export { ChangeEnforcedLayoutTypeEnum, EnforcedLayoutTypeEnum } from './layout/enums'; +export { ChatUiCommandsEnum } from './chat/enums';