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 = [