feat: introduce new categories field for UI Action Buttons (#33066)

Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com>
pull/33479/head
デワンシュ 1 year ago committed by GitHub
parent 760ae5c01a
commit debd3ffa22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      .changeset/red-crews-behave.md
  2. 16
      apps/meteor/client/components/message/toolbar/MessageToolbar.tsx
  3. 89
      apps/meteor/client/components/message/toolbar/MessageToolbarStarsActionMenu.tsx
  4. 13
      apps/meteor/client/hooks/useAppActionButtons.ts
  5. 24
      apps/meteor/client/hooks/useFilterActions.ts
  6. 1
      packages/i18n/src/locales/en.i18n.json

@ -0,0 +1,7 @@
---
'@rocket.chat/ui-kit': minor
'@rocket.chat/i18n': minor
'@rocket.chat/meteor': minor
---
Introduces new property `category` for Rocket.Chat Apps to register UI action buttons. This property is used to group buttons in the UI.

@ -19,6 +19,7 @@ import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoT
import { useChat } from '../../../views/room/contexts/ChatContext';
import { useRoomToolbox } from '../../../views/room/contexts/RoomToolboxContext';
import MessageActionMenu from './MessageActionMenu';
import MessageToolbarStarsActionMenu from './MessageToolbarStarsActionMenu';
import { useWebDAVMessageAction } from './useWebDAVMessageAction';
const getMessageContext = (message: IMessage, room: IRoom, context?: MessageActionContext): MessageActionContext => {
@ -78,6 +79,8 @@ const MessageToolbar = ({
const actionButtonApps = useMessageActionAppsActionButtons(context);
const starsAction = useMessageActionAppsActionButtons(context, 'ai');
const { messageToolbox: hiddenActions } = useLayoutHiddenActions();
// TODO: move this to another place
@ -135,6 +138,19 @@ const MessageToolbar = ({
disabled={action?.disabled?.({ message, room, user, subscription, settings: mapSettings, chat, context })}
/>
))}
{starsAction.data && starsAction.data.length > 0 && (
<MessageToolbarStarsActionMenu
options={starsAction.data.map((action) => ({
...action,
action: (e) => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }),
}))}
onChangeMenuVisibility={onChangeMenuVisibility}
data-qa-type='message-action-stars-menu-options'
context={{ message, room, user, subscription, settings: mapSettings, chat, context }}
isMessageEncrypted={isE2EEMessage(message)}
/>
)}
{actionsQueryResult.isSuccess && actionsQueryResult.data.menu.length > 0 && (
<MessageActionMenu
options={[...actionsQueryResult.data?.menu, ...(actionButtonApps.data ?? [])].filter(Boolean).map((action) => ({

@ -0,0 +1,89 @@
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { GenericMenu, type GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { MouseEvent, ReactElement } from 'react';
import React from 'react';
import type { MessageActionConditionProps, MessageActionConfig } from '../../../../app/ui-utils/client/lib/MessageAction';
type MessageActionConfigOption = Omit<MessageActionConfig, 'condition' | 'context' | 'order' | 'action'> & {
action: (e?: MouseEvent<HTMLElement>) => void;
};
type MessageActionSection = {
id: string;
title: string;
items: GenericMenuItemProps[];
};
type MessageActionMenuProps = {
onChangeMenuVisibility: (visible: boolean) => void;
options: MessageActionConfigOption[];
context: MessageActionConditionProps;
isMessageEncrypted: boolean;
};
const MessageToolbarStarsActionMenu = ({
options,
onChangeMenuVisibility,
context,
isMessageEncrypted,
}: MessageActionMenuProps): ReactElement => {
const t = useTranslation();
const id = useUniqueId();
const groupOptions = options.reduce((acc, option) => {
const transformedOption = {
variant: option.color === 'alert' ? 'danger' : '',
id: option.id,
icon: option.icon,
content: t(option.label),
onClick: option.action,
type: option.type,
...(option.disabled && { disabled: option?.disabled?.(context) }),
...(option.disabled &&
option?.disabled?.(context) && { tooltip: t('Action_not_available_encrypted_content', { action: t(option.label) }) }),
};
const group = option.type || '';
let section = acc.find((section: { id: string }) => section.id === group);
if (!section) {
section = { id: group, title: '', items: [] };
acc.push(section);
}
// Add option to the appropriate section
section.items.push(transformedOption);
// Handle the "apps" section if message is encrypted
if (group === 'apps' && isMessageEncrypted) {
section.items = [
{
content: t('Unavailable'),
id,
disabled: true,
gap: false,
tooltip: t('Action_not_available_encrypted_content', { action: t('Apps') }),
},
];
}
return acc;
}, [] as MessageActionSection[]);
return (
<GenericMenu
onOpenChange={onChangeMenuVisibility}
detached
icon='stars'
title={t('AI_Actions')}
data-qa-id='menu'
data-qa-type='message-action-menu'
sections={groupOptions}
placement='bottom-end'
/>
);
};
export default MessageToolbarStarsActionMenu;

@ -1,4 +1,4 @@
import type { IUIActionButton, UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui';
import { type IUIActionButton, type UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui';
import { useDebouncedCallback } from '@rocket.chat/fuselage-hooks';
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useEndpoint, useStream, useToastMessageDispatch, useUserId } from '@rocket.chat/ui-contexts';
@ -13,6 +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';
const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`;
@ -160,20 +161,18 @@ export const useUserDropdownAppsActionButtons = () => {
} as UseQueryResult<GenericMenuItemProps[]>;
};
export const useMessageActionAppsActionButtons = (context?: MessageActionContext) => {
export const useMessageActionAppsActionButtons = (context?: MessageActionContext, category?: string) => {
const result = useAppActionButtons('messageAction');
const actionManager = useUiKitActionManager();
const applyButtonFilters = useApplyButtonFilters();
const dispatchToastMessage = useToastMessageDispatch();
const { t } = useTranslation();
const filterActionsByContextAndCategory = useFilterActionsByContextAndCategory(context, category);
const data = useMemo(
() =>
result.data
?.filter((action) => {
if (
context &&
!(action.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred']).includes(context as any)
) {
if (!filterActionsByContextAndCategory(action)) {
return false;
}
return applyButtonFilters(action);
@ -212,7 +211,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext
return item;
}),
[actionManager, applyButtonFilters, context, dispatchToastMessage, result.data, t],
[actionManager, applyButtonFilters, dispatchToastMessage, filterActionsByContextAndCategory, result.data, t],
);
return {
...result,

@ -0,0 +1,24 @@
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') => {
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;
},
[context, category],
);
};

@ -392,6 +392,7 @@
"Agent_Without_Extensions": "Agent Without Extensions",
"Agents": "Agents",
"Agree": "Agree",
"AI_Actions": "AI Actions",
"Alerts": "Alerts",
"Alias": "Alias",
"Alias_Format": "Alias Format",

Loading…
Cancel
Save