diff --git a/.changeset/four-experts-compare.md b/.changeset/four-experts-compare.md new file mode 100644 index 000000000000..b9ce498e4a4b --- /dev/null +++ b/.changeset/four-experts-compare.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Introduces a new featured action on the room header for action buttons using the non-default category to enhance user accessibility. diff --git a/apps/meteor/client/hooks/roomActions/useAppsRoomStarActions.tsx b/apps/meteor/client/hooks/roomActions/useAppsRoomStarActions.tsx new file mode 100644 index 000000000000..a03affabfe1e --- /dev/null +++ b/apps/meteor/client/hooks/roomActions/useAppsRoomStarActions.tsx @@ -0,0 +1,82 @@ +import { Box } from '@rocket.chat/fuselage'; +import { GenericMenu, HeaderToolbarAction } from '@rocket.chat/ui-client'; +import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { UiKitTriggerTimeoutError } from '../../../app/ui-message/client/UiKitTriggerTimeoutError'; +import { Utilities } from '../../../ee/lib/misc/Utilities'; +import { useUiKitActionManager } from '../../uikit/hooks/useUiKitActionManager'; +import { useRoom } from '../../views/room/contexts/RoomContext'; +import type { RoomToolboxActionConfig } from '../../views/room/contexts/RoomToolboxContext'; +import { useAppActionButtons } from '../useAppActionButtons'; +import { useApplyButtonFilters } from '../useApplyButtonFilters'; + +export const useAppsRoomStarActions = () => { + const result = useAppActionButtons('roomAction'); + const actionManager = useUiKitActionManager(); + const applyButtonFilters = useApplyButtonFilters('ai'); + const room = useRoom(); + const { t } = useTranslation(); + const dispatchToastMessage = useToastMessageDispatch(); + + return useMemo((): RoomToolboxActionConfig | undefined => { + if (!result.data) { + return undefined; + } + + const filteredActions = result.data.filter(applyButtonFilters); + + if (filteredActions.length === 0) { + return undefined; + } + + return { + id: 'ai-actions', + title: 'AI_Actions', + icon: 'stars', + groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], + featured: true, + renderToolboxItem: ({ id, icon, title, disabled, className }) => ( + } + key={id} + title={title} + disabled={disabled} + items={filteredActions.map((action) => ({ + id: action.actionId, + icon: undefined, + title: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + content: {t(`${Utilities.getI18nKeyForApp(action.labelI18n, action.appId)}`)}, + variant: action.variant, + groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], + onClick: () => { + void actionManager + .emitInteraction(action.appId, { + type: 'actionButton', + actionId: action.actionId, + rid: room._id, + payload: { context: action.context }, + }) + .catch(async (reason) => { + if (reason instanceof UiKitTriggerTimeoutError) { + dispatchToastMessage({ + type: 'error', + message: t('UIKit_Interaction_Timeout'), + }); + return; + } + + return reason; + }); + }, + type: 'apps', + }))} + className={className} + placement='bottom-start' + icon={icon} + /> + ), + }; + }, [actionManager, applyButtonFilters, dispatchToastMessage, result.data, room._id, t]); +}; diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts index 3e21ca6a668d..2a074fcc3be1 100644 --- a/apps/meteor/client/hooks/useAppActionButtons.ts +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -13,7 +13,7 @@ import type { MessageBoxAction } from '../../app/ui-utils/client/lib/messageBox' import { Utilities } from '../../ee/lib/misc/Utilities'; import { useUiKitActionManager } from '../uikit/hooks/useUiKitActionManager'; import { useApplyButtonFilters, useApplyButtonAuthFilter } from './useApplyButtonFilters'; -import { useFilterActionsByContextAndCategory } from './useFilterActions'; +import { useFilterActionsByContext } from './useFilterActions'; const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; @@ -164,15 +164,15 @@ export const useUserDropdownAppsActionButtons = () => { export const useMessageActionAppsActionButtons = (context?: MessageActionContext, category?: string) => { const result = useAppActionButtons('messageAction'); const actionManager = useUiKitActionManager(); - const applyButtonFilters = useApplyButtonFilters(); + const applyButtonFilters = useApplyButtonFilters(category); const dispatchToastMessage = useToastMessageDispatch(); const { t } = useTranslation(); - const filterActionsByContextAndCategory = useFilterActionsByContextAndCategory(context, category); + const filterActionsByContext = useFilterActionsByContext(context); const data = useMemo( () => result.data ?.filter((action) => { - if (!filterActionsByContextAndCategory(action)) { + if (!filterActionsByContext(action)) { return false; } return applyButtonFilters(action); @@ -211,7 +211,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext return item; }), - [actionManager, applyButtonFilters, dispatchToastMessage, filterActionsByContextAndCategory, result.data, t], + [actionManager, applyButtonFilters, dispatchToastMessage, filterActionsByContext, result.data, t], ); return { ...result, diff --git a/apps/meteor/client/hooks/useApplyButtonFilters.ts b/apps/meteor/client/hooks/useApplyButtonFilters.ts index 742f33489deb..63542c5189c6 100644 --- a/apps/meteor/client/hooks/useApplyButtonFilters.ts +++ b/apps/meteor/client/hooks/useApplyButtonFilters.ts @@ -32,15 +32,26 @@ const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => { return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room)); }; -export const useApplyButtonFilters = (): ((button: IUIActionButton) => boolean) => { +const applyCategoryFilter = (button: IUIActionButton, category: string): boolean => { + const { category: buttonCategory } = button; + + if (category === 'default') { + return !buttonCategory || buttonCategory === 'default'; + } + + return buttonCategory === category; +}; + +export const useApplyButtonFilters = (category = 'default'): ((button: IUIActionButton) => boolean) => { const room = useRoom(); if (!room) { throw new Error('useApplyButtonFilters must be used inside a room context'); } const applyAuthFilter = useApplyButtonAuthFilter(); return useCallback( - (button: IUIActionButton) => applyAuthFilter(button) && (!room || applyRoomFilter(button, room)), - [applyAuthFilter, room], + (button: IUIActionButton) => + applyAuthFilter(button) && (!room || applyRoomFilter(button, room)) && applyCategoryFilter(button, category), + [applyAuthFilter, category, room], ); }; diff --git a/apps/meteor/client/hooks/useFilterActions.ts b/apps/meteor/client/hooks/useFilterActions.ts index 5eab0b795a59..4dda122c3bb3 100644 --- a/apps/meteor/client/hooks/useFilterActions.ts +++ b/apps/meteor/client/hooks/useFilterActions.ts @@ -2,23 +2,18 @@ import { MessageActionContext } from '@rocket.chat/apps-engine/definition/ui'; import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; import { useCallback } from 'react'; -const DEFAULT_CATEGORY = 'default'; - -export const useFilterActionsByContextAndCategory = (context: string | undefined, category = 'default') => { +export const useFilterActionsByContext = (context: string | undefined) => { return useCallback( (action: IUIActionButton) => { if (!context) { return true; } - const actionCategory = action?.category ?? DEFAULT_CATEGORY; const messageActionContext = action.when?.messageActionContext || Object.values(MessageActionContext); const isContextMatch = messageActionContext.includes(context as MessageActionContext); - const isCategoryMatch = category === DEFAULT_CATEGORY ? actionCategory === DEFAULT_CATEGORY : actionCategory === category; - - return isContextMatch && isCategoryMatch; + return isContextMatch; }, - [context, category], + [context], ); }; diff --git a/apps/meteor/client/ui.ts b/apps/meteor/client/ui.ts index 6c7971a8cca0..922b605a2b40 100644 --- a/apps/meteor/client/ui.ts +++ b/apps/meteor/client/ui.ts @@ -3,6 +3,7 @@ import { useCloseChatQuickAction } from './hooks/quickActions/useCloseChatQuickA import { useMoveQueueQuickAction } from './hooks/quickActions/useMoveQueueQuickAction'; import { useOnHoldChatQuickAction } from './hooks/quickActions/useOnHoldChatQuickAction'; import { useTranscriptQuickAction } from './hooks/quickActions/useTranscriptQuickAction'; +import { useAppsRoomStarActions } from './hooks/roomActions/useAppsRoomStarActions'; import { useAutotranslateRoomAction } from './hooks/roomActions/useAutotranslateRoomAction'; import { useCallsRoomAction } from './hooks/roomActions/useCallsRoomAction'; import { useCannedResponsesRoomAction } from './hooks/roomActions/useCannedResponsesRoomAction'; @@ -69,6 +70,7 @@ export const roomActionHooks = [ useUploadedFilesListRoomAction, useVoIPRoomInfoRoomAction, useWebRTCVideoRoomAction, + useAppsRoomStarActions, ] satisfies (() => RoomToolboxActionConfig | undefined)[]; export const quickActionHooks = [