stream-chat-react 12.3.0 → 12.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/components/Channel/Channel.d.ts +1 -1
  2. package/dist/components/Channel/Channel.js +2 -0
  3. package/dist/components/ChatAutoComplete/ChatAutoComplete.d.ts +1 -1
  4. package/dist/components/Message/MessageOptions.js +1 -1
  5. package/dist/components/Message/MessageSimple.js +5 -2
  6. package/dist/components/Message/utils.d.ts +1 -1
  7. package/dist/components/MessageActions/MessageActions.d.ts +2 -1
  8. package/dist/components/MessageActions/MessageActions.js +1 -1
  9. package/dist/components/Reactions/ReactionSelectorWithButton.d.ts +1 -2
  10. package/dist/components/Reactions/ReactionSelectorWithButton.js +2 -2
  11. package/dist/components/Threads/hooks/useThreadManagerState.js +1 -1
  12. package/dist/context/ComponentContext.d.ts +7 -1
  13. package/dist/experimental/MessageActions/MessageActions.d.ts +17 -0
  14. package/dist/experimental/MessageActions/MessageActions.js +48 -0
  15. package/dist/experimental/MessageActions/defaults.d.ts +5 -0
  16. package/dist/experimental/MessageActions/defaults.js +93 -0
  17. package/dist/experimental/MessageActions/hooks/index.d.ts +2 -0
  18. package/dist/experimental/MessageActions/hooks/index.js +2 -0
  19. package/dist/experimental/MessageActions/hooks/useBaseMessageActionSetFilter.d.ts +8 -0
  20. package/dist/experimental/MessageActions/hooks/useBaseMessageActionSetFilter.js +57 -0
  21. package/dist/experimental/MessageActions/hooks/useSplitMessageActionSet.d.ts +5 -0
  22. package/dist/experimental/MessageActions/hooks/useSplitMessageActionSet.js +12 -0
  23. package/dist/experimental/MessageActions/index.d.ts +3 -0
  24. package/dist/experimental/MessageActions/index.js +3 -0
  25. package/dist/experimental/index.browser.cjs +1091 -0
  26. package/dist/experimental/index.browser.cjs.map +7 -0
  27. package/dist/experimental/index.d.ts +1 -0
  28. package/dist/experimental/index.js +1 -0
  29. package/dist/experimental/index.node.cjs +1099 -0
  30. package/dist/experimental/index.node.cjs.map +7 -0
  31. package/dist/i18n/Streami18n.d.ts +1 -0
  32. package/dist/i18n/de.json +1 -0
  33. package/dist/i18n/en.json +1 -0
  34. package/dist/i18n/es.json +1 -0
  35. package/dist/i18n/fr.json +1 -0
  36. package/dist/i18n/hi.json +1 -0
  37. package/dist/i18n/it.json +1 -0
  38. package/dist/i18n/ja.json +1 -0
  39. package/dist/i18n/ko.json +1 -0
  40. package/dist/i18n/nl.json +1 -0
  41. package/dist/i18n/pt.json +1 -0
  42. package/dist/i18n/ru.json +1 -0
  43. package/dist/i18n/tr.json +1 -0
  44. package/dist/index.browser.cjs +23 -6
  45. package/dist/index.browser.cjs.map +3 -3
  46. package/dist/index.node.cjs +24 -6
  47. package/dist/index.node.cjs.map +3 -3
  48. package/package.json +16 -1
@@ -6,7 +6,7 @@ import { ComponentContextValue, StreamMessage } from '../../context';
6
6
  import type { MessageInputProps } from '../MessageInput';
7
7
  import type { ChannelUnreadUiState, CustomTrigger, DefaultStreamChatGenerics, GiphyVersions, ImageAttachmentSizeHandler, SendMessageOptions, UpdateMessageOptions, VideoAttachmentSizeHandler } from '../../types/types';
8
8
  import type { URLEnrichmentConfig } from '../MessageInput/hooks/useLinkPreviews';
9
- type ChannelPropsForwardedToComponentContext<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Pick<ComponentContextValue<StreamChatGenerics>, 'Attachment' | 'AttachmentPreviewList' | 'AudioRecorder' | 'AutocompleteSuggestionItem' | 'AutocompleteSuggestionList' | 'Avatar' | 'BaseImage' | 'CooldownTimer' | 'CustomMessageActionsList' | 'DateSeparator' | 'EditMessageInput' | 'EmojiPicker' | 'emojiSearchIndex' | 'EmptyStateIndicator' | 'FileUploadIcon' | 'GiphyPreviewMessage' | 'HeaderComponent' | 'Input' | 'LinkPreviewList' | 'LoadingIndicator' | 'Message' | 'MessageBouncePrompt' | 'MessageDeleted' | 'MessageListNotifications' | 'MessageListMainPanel' | 'MessageNotification' | 'MessageOptions' | 'MessageRepliesCountButton' | 'MessageStatus' | 'MessageSystem' | 'MessageTimestamp' | 'ModalGallery' | 'PinIndicator' | 'QuotedMessage' | 'QuotedMessagePreview' | 'reactionOptions' | 'ReactionSelector' | 'ReactionsList' | 'SendButton' | 'StartRecordingAudioButton' | 'ThreadHead' | 'ThreadHeader' | 'ThreadStart' | 'Timestamp' | 'TriggerProvider' | 'TypingIndicator' | 'UnreadMessagesNotification' | 'UnreadMessagesSeparator' | 'VirtualMessage'>;
9
+ type ChannelPropsForwardedToComponentContext<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = Pick<ComponentContextValue<StreamChatGenerics>, 'Attachment' | 'AttachmentPreviewList' | 'AudioRecorder' | 'AutocompleteSuggestionItem' | 'AutocompleteSuggestionList' | 'Avatar' | 'BaseImage' | 'CooldownTimer' | 'CustomMessageActionsList' | 'DateSeparator' | 'EditMessageInput' | 'EmojiPicker' | 'emojiSearchIndex' | 'EmptyStateIndicator' | 'FileUploadIcon' | 'GiphyPreviewMessage' | 'HeaderComponent' | 'Input' | 'LinkPreviewList' | 'LoadingIndicator' | 'Message' | 'MessageActions' | 'MessageBouncePrompt' | 'MessageDeleted' | 'MessageListNotifications' | 'MessageListMainPanel' | 'MessageNotification' | 'MessageOptions' | 'MessageRepliesCountButton' | 'MessageStatus' | 'MessageSystem' | 'MessageTimestamp' | 'ModalGallery' | 'PinIndicator' | 'QuotedMessage' | 'QuotedMessagePreview' | 'reactionOptions' | 'ReactionSelector' | 'ReactionsList' | 'SendButton' | 'StartRecordingAudioButton' | 'ThreadHead' | 'ThreadHeader' | 'ThreadStart' | 'Timestamp' | 'TriggerProvider' | 'TypingIndicator' | 'UnreadMessagesNotification' | 'UnreadMessagesSeparator' | 'VirtualMessage'>;
10
10
  export type ChannelProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger> = ChannelPropsForwardedToComponentContext<StreamChatGenerics> & {
11
11
  /** List of accepted file types */
12
12
  acceptedFiles?: string[];
@@ -749,6 +749,7 @@ const ChannelInner = (props) => {
749
749
  LinkPreviewList: props.LinkPreviewList,
750
750
  LoadingIndicator: props.LoadingIndicator,
751
751
  Message: props.Message,
752
+ MessageActions: props.MessageActions,
752
753
  MessageBouncePrompt: props.MessageBouncePrompt,
753
754
  MessageDeleted: props.MessageDeleted,
754
755
  MessageListNotifications: props.MessageListNotifications,
@@ -797,6 +798,7 @@ const ChannelInner = (props) => {
797
798
  props.LinkPreviewList,
798
799
  props.LoadingIndicator,
799
800
  props.Message,
801
+ props.MessageActions,
800
802
  props.MessageBouncePrompt,
801
803
  props.MessageDeleted,
802
804
  props.MessageListNotifications,
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import type { CommandResponse, UserResponse } from 'stream-chat';
3
3
  import type { TriggerSettings } from '../MessageInput/DefaultTriggerProvider';
4
4
  import type { CustomTrigger, DefaultStreamChatGenerics, UnknownType } from '../../types/types';
5
- import { EmojiSearchIndex } from 'components/MessageInput';
5
+ import type { EmojiSearchIndex } from '../MessageInput';
6
6
  type ObjectUnion<T> = T[keyof T];
7
7
  export type SuggestionCommand<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = CommandResponse<StreamChatGenerics>;
8
8
  export type SuggestionUser<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = UserResponse<StreamChatGenerics>;
@@ -31,6 +31,6 @@ const UnMemoizedMessageOptions = (props) => {
31
31
  React.createElement(MessageActions, { ActionsIcon: ActionsIcon }),
32
32
  shouldShowReplies && (React.createElement("button", { "aria-label": t('aria/Open Thread'), className: `str-chat__message-${theme}__actions__action str-chat__message-${theme}__actions__action--thread str-chat__message-reply-in-thread-button`, "data-testid": 'thread-action', onClick: handleOpenThread },
33
33
  React.createElement(ThreadIcon, { className: 'str-chat__message-action-icon' }))),
34
- shouldShowReactions && (React.createElement(ReactionSelectorWithButton, { ReactionIcon: ReactionIcon, theme: theme }))));
34
+ shouldShowReactions && React.createElement(ReactionSelectorWithButton, { ReactionIcon: ReactionIcon })));
35
35
  };
36
36
  export const MessageOptions = React.memo(UnMemoizedMessageOptions);
@@ -26,7 +26,10 @@ const MessageSimpleWithContext = (props) => {
26
26
  const { t } = useTranslationContext('MessageSimple');
27
27
  const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false);
28
28
  const [isEditedTimestampOpen, setEditedTimestampOpen] = useState(false);
29
- const { Attachment = DefaultAttachment, Avatar = DefaultAvatar, EditMessageInput = DefaultEditMessageForm, MessageDeleted = DefaultMessageDeleted, MessageBouncePrompt = DefaultMessageBouncePrompt, MessageOptions = DefaultMessageOptions, MessageRepliesCountButton = DefaultMessageRepliesCountButton, MessageStatus = DefaultMessageStatus, MessageTimestamp = DefaultMessageTimestamp, ReactionsList = DefaultReactionList, PinIndicator, } = useComponentContext('MessageSimple');
29
+ const { Attachment = DefaultAttachment, Avatar = DefaultAvatar, EditMessageInput = DefaultEditMessageForm, MessageOptions = DefaultMessageOptions,
30
+ // TODO: remove this "passthrough" in the next
31
+ // major release and use the new default instead
32
+ MessageActions = MessageOptions, MessageDeleted = DefaultMessageDeleted, MessageBouncePrompt = DefaultMessageBouncePrompt, MessageRepliesCountButton = DefaultMessageRepliesCountButton, MessageStatus = DefaultMessageStatus, MessageTimestamp = DefaultMessageTimestamp, ReactionsList = DefaultReactionList, PinIndicator, } = useComponentContext('MessageSimple');
30
33
  const hasAttachment = messageHasAttachments(message);
31
34
  const hasReactions = messageHasReactions(message);
32
35
  if (message.customType === CUSTOM_MESSAGE_TYPE.date) {
@@ -73,7 +76,7 @@ const MessageSimpleWithContext = (props) => {
73
76
  React.createElement("div", { className: clsx('str-chat__message-inner', {
74
77
  'str-chat__simple-message--error-failed': allowRetry || isBounced,
75
78
  }), "data-testid": 'message-inner', onClick: handleClick, onKeyUp: handleClick },
76
- React.createElement(MessageOptions, null),
79
+ React.createElement(MessageActions, null),
77
80
  React.createElement("div", { className: 'str-chat__message-reactions-host' }, hasReactions && React.createElement(ReactionsList, { reverse: true })),
78
81
  React.createElement("div", { className: 'str-chat__message-bubble' },
79
82
  message.attachments?.length && !message.quoted_message ? (React.createElement(Attachment, { actionHandler: handleAction, attachments: message.attachments })) : null,
@@ -24,7 +24,7 @@ export declare const MESSAGE_ACTIONS: {
24
24
  react: string;
25
25
  reply: string;
26
26
  };
27
- export type MessageActionsArray<T extends string = string> = Array<'delete' | 'edit' | 'flag' | 'mute' | 'pin' | 'quote' | 'react' | 'reply' | T>;
27
+ export type MessageActionsArray<T extends string = string> = Array<keyof typeof MESSAGE_ACTIONS | T>;
28
28
  export declare const defaultPinPermissions: PinPermissions;
29
29
  export type Capabilities = {
30
30
  canDelete?: boolean;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { PropsWithChildren } from 'react';
2
2
  import { MessageContextValue } from '../../context/MessageContext';
3
3
  import type { DefaultStreamChatGenerics, IconProps } from '../../types/types';
4
4
  type MessageContextPropsToPick = 'getMessageActions' | 'handleDelete' | 'handleFlag' | 'handleMarkUnread' | 'handleMute' | 'handlePin' | 'message';
@@ -14,4 +14,5 @@ export type MessageActionsWrapperProps = {
14
14
  inline?: boolean;
15
15
  toggleOpen?: () => void;
16
16
  };
17
+ export declare const MessageActionsWrapper: (props: PropsWithChildren<MessageActionsWrapperProps>) => React.JSX.Element;
17
18
  export {};
@@ -41,7 +41,7 @@ export const MessageActions = (props) => {
41
41
  React.createElement("button", { "aria-expanded": dialogIsOpen, "aria-haspopup": 'true', "aria-label": t('aria/Open Message Actions Menu'), className: 'str-chat__message-actions-box-button', "data-testid": 'message-actions-toggle-button', ref: actionsBoxButtonRef },
42
42
  React.createElement(ActionsIcon, { className: 'str-chat__message-action-icon' }))));
43
43
  };
44
- const MessageActionsWrapper = (props) => {
44
+ export const MessageActionsWrapper = (props) => {
45
45
  const { children, customWrapperClass, inline, toggleOpen } = props;
46
46
  const defaultWrapperClass = clsx('str-chat__message-simple__actions__action', 'str-chat__message-simple__actions__action--options', 'str-chat__message-actions-container');
47
47
  const wrapperProps = {
@@ -3,11 +3,10 @@ import type { DefaultStreamChatGenerics } from '../../types';
3
3
  import type { IconProps } from '../../types/types';
4
4
  type ReactionSelectorWithButtonProps = {
5
5
  ReactionIcon: React.ComponentType<IconProps>;
6
- theme: string;
7
6
  };
8
7
  /**
9
8
  * Internal convenience component - not to be exported. It just groups the button and the dialog anchor and thus prevents
10
9
  * cluttering the parent component.
11
10
  */
12
- export declare const ReactionSelectorWithButton: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ ReactionIcon, theme, }: ReactionSelectorWithButtonProps) => React.JSX.Element;
11
+ export declare const ReactionSelectorWithButton: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ ReactionIcon, }: ReactionSelectorWithButtonProps) => React.JSX.Element;
13
12
  export {};
@@ -6,7 +6,7 @@ import { useComponentContext, useMessageContext, useTranslationContext } from '.
6
6
  * Internal convenience component - not to be exported. It just groups the button and the dialog anchor and thus prevents
7
7
  * cluttering the parent component.
8
8
  */
9
- export const ReactionSelectorWithButton = ({ ReactionIcon, theme, }) => {
9
+ export const ReactionSelectorWithButton = ({ ReactionIcon, }) => {
10
10
  const { t } = useTranslationContext('ReactionSelectorWithButton');
11
11
  const { isMyMessage, message } = useMessageContext('MessageOptions');
12
12
  const { ReactionSelector = DefaultReactionSelector } = useComponentContext('MessageOptions');
@@ -17,6 +17,6 @@ export const ReactionSelectorWithButton = ({ ReactionIcon, theme, }) => {
17
17
  return (React.createElement(React.Fragment, null,
18
18
  React.createElement(DialogAnchor, { id: dialogId, placement: isMyMessage() ? 'top-end' : 'top-start', referenceElement: buttonRef.current, trapFocus: true },
19
19
  React.createElement(ReactionSelector, null)),
20
- React.createElement("button", { "aria-expanded": dialogIsOpen, "aria-label": t('aria/Open Reaction Selector'), className: `str-chat__message-${theme}__actions__action str-chat__message-${theme}__actions__action--reactions str-chat__message-reactions-button`, "data-testid": 'message-reaction-action', onClick: () => dialog?.toggle(), ref: buttonRef },
20
+ React.createElement("button", { "aria-expanded": dialogIsOpen, "aria-label": t('aria/Open Reaction Selector'), className: 'str-chat__message-reactions-button', "data-testid": 'message-reaction-action', onClick: () => dialog?.toggle(), ref: buttonRef },
21
21
  React.createElement(ReactionIcon, { className: 'str-chat__message-action-icon' }))));
22
22
  };
@@ -1,4 +1,4 @@
1
- import { useChatContext } from 'context';
1
+ import { useChatContext } from '../../../context';
2
2
  import { useStateStore } from '../../../store';
3
3
  export const useThreadManagerState = (selector) => {
4
4
  const { client } = useChatContext();
@@ -47,6 +47,8 @@ export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGe
47
47
  LoadingIndicator?: React.ComponentType<LoadingIndicatorProps>;
48
48
  /** Custom UI component to display a message in the standard `MessageList`, defaults to and accepts the same props as: [MessageSimple](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageSimple.tsx) */
49
49
  Message?: React.ComponentType<MessageUIComponentProps<StreamChatGenerics>>;
50
+ /** Custom UI component for message actions popup, accepts no props, all the defaults are set within [MessageActions (unstable)](https://github.com/GetStream/stream-chat-react/blob/master/src/experimental/MessageActions/MessageActions.tsx) */
51
+ MessageActions?: React.ComponentType;
50
52
  /** Custom UI component to display the contents of a bounced message modal. Usually it allows to retry, edit, or delete the message. Defaults to and accepts the same props as: [MessageBouncePrompt](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageBounce/MessageBouncePrompt.tsx) */
51
53
  MessageBouncePrompt?: React.ComponentType<MessageBouncePromptProps>;
52
54
  /** Custom UI component for a deleted message, defaults to and accepts same props as: [MessageDeleted](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageDeleted.tsx) */
@@ -56,7 +58,11 @@ export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGe
56
58
  MessageListNotifications?: React.ComponentType<MessageListNotificationsProps>;
57
59
  /** Custom UI component to display a notification when scrolled up the list and new messages arrive, defaults to and accepts same props as [MessageNotification](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageList/MessageNotification.tsx) */
58
60
  MessageNotification?: React.ComponentType<MessageNotificationProps>;
59
- /** Custom UI component for message options popup, defaults to and accepts same props as: [MessageOptions](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageOptions.tsx) */
61
+ /**
62
+ * Custom UI component for message options popup, defaults to and accepts same props as: [MessageOptions](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageOptions.tsx)
63
+ *
64
+ * @deprecated Use MessageActions property instead.
65
+ */
60
66
  MessageOptions?: React.ComponentType<MessageOptionsProps<StreamChatGenerics>>;
61
67
  /** Custom UI component to display message replies, defaults to and accepts same props as: [MessageRepliesCountButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageRepliesCountButton.tsx) */
62
68
  MessageRepliesCountButton?: React.ComponentType<MessageRepliesCountButtonProps>;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { MESSAGE_ACTIONS } from '../../components';
3
+ export type MessageActionSetItem = {
4
+ Component: React.ComponentType;
5
+ placement: 'quick' | 'dropdown';
6
+ type: keyof typeof MESSAGE_ACTIONS | (string & {});
7
+ };
8
+ export type MessageActionsProps = {
9
+ disableBaseMessageActionSetFilter?: boolean;
10
+ messageActionSet?: MessageActionSetItem[];
11
+ };
12
+ /**
13
+ * A new actions component to replace current `MessageOptions` component.
14
+ * Exports from `stream-chat-react/experimental` __MIGHT__ change - use with caution
15
+ * and follow release notes in case you notice unexpected behavior.
16
+ */
17
+ export declare const MessageActions: ({ disableBaseMessageActionSetFilter, messageActionSet, }: MessageActionsProps) => React.JSX.Element | null;
@@ -0,0 +1,48 @@
1
+ /* eslint-disable sort-keys */
2
+ import clsx from 'clsx';
3
+ import React, { useState } from 'react';
4
+ import { useChatContext, useMessageContext, useTranslationContext } from '../../context';
5
+ import { ActionsIcon } from '../../components/Message/icons';
6
+ import { DialogAnchor, useDialog, useDialogIsOpen } from '../../components/Dialog';
7
+ import { MessageActionsWrapper } from '../../components/MessageActions/MessageActions';
8
+ import { useBaseMessageActionSetFilter, useSplitMessageActionSet } from './hooks';
9
+ import { defaultMessageActionSet } from './defaults';
10
+ // TODO: allow passing down customWrapperClass
11
+ /**
12
+ * A new actions component to replace current `MessageOptions` component.
13
+ * Exports from `stream-chat-react/experimental` __MIGHT__ change - use with caution
14
+ * and follow release notes in case you notice unexpected behavior.
15
+ */
16
+ export const MessageActions = ({ disableBaseMessageActionSetFilter = false, messageActionSet = defaultMessageActionSet, }) => {
17
+ const { theme } = useChatContext();
18
+ const { isMyMessage, message } = useMessageContext();
19
+ const { t } = useTranslationContext();
20
+ const [actionsBoxButtonElement, setActionsBoxButtonElement] = useState(null);
21
+ const filteredMessageActionSet = useBaseMessageActionSetFilter(messageActionSet, disableBaseMessageActionSetFilter);
22
+ const { dropdownActionSet, quickActionSet } = useSplitMessageActionSet(filteredMessageActionSet);
23
+ const dropdownDialogId = `message-actions--${message.id}`;
24
+ const reactionSelectorDialogId = `reaction-selector--${message.id}`;
25
+ const dialog = useDialog({ id: dropdownDialogId });
26
+ const dropdownDialogIsOpen = useDialogIsOpen(dropdownDialogId);
27
+ const reactionSelectorDialogIsOpen = useDialogIsOpen(reactionSelectorDialogId);
28
+ // do not render anything if total action count is zero
29
+ if (dropdownActionSet.length + quickActionSet.length === 0) {
30
+ return null;
31
+ }
32
+ return (React.createElement("div", { className: clsx(`str-chat__message-${theme}__actions str-chat__message-options`, {
33
+ 'str-chat__message-options--active': dropdownDialogIsOpen || reactionSelectorDialogIsOpen,
34
+ }) },
35
+ dropdownActionSet.length > 0 && (React.createElement(MessageActionsWrapper, { inline: false, toggleOpen: dialog?.toggle },
36
+ React.createElement("button", { "aria-expanded": dropdownDialogIsOpen, "aria-haspopup": 'true', "aria-label": t('aria/Open Message Actions Menu'), className: 'str-chat__message-actions-box-button', "data-testid": 'message-actions-toggle-button', ref: setActionsBoxButtonElement },
37
+ React.createElement(ActionsIcon, { className: 'str-chat__message-action-icon' })),
38
+ React.createElement(DialogAnchor, { id: dropdownDialogId, placement: isMyMessage() ? 'top-end' : 'top-start', referenceElement: actionsBoxButtonElement, trapFocus: true },
39
+ React.createElement(DropdownBox, { open: dropdownDialogIsOpen }, dropdownActionSet.map(({ Component: DropdownActionComponent, type }) => (React.createElement(DropdownActionComponent, { key: type }))))))),
40
+ quickActionSet.map(({ Component: QuickActionComponent, type }) => (React.createElement(QuickActionComponent, { key: type })))));
41
+ };
42
+ const DropdownBox = ({ children, open }) => {
43
+ const { t } = useTranslationContext();
44
+ return (React.createElement("div", { className: clsx('str-chat__message-actions-box', {
45
+ 'str-chat__message-actions-box--open': open,
46
+ }) },
47
+ React.createElement("div", { "aria-label": t('aria/Message Options'), className: 'str-chat__message-actions-list', role: 'listbox' }, children)));
48
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { ComponentPropsWithoutRef } from 'react';
3
+ import type { MessageActionSetItem } from './MessageActions';
4
+ export declare const DefaultDropdownActionButton: ({ "aria-selected": ariaSelected, children, className, role, ...rest }: ComponentPropsWithoutRef<'button'>) => React.JSX.Element;
5
+ export declare const defaultMessageActionSet: MessageActionSetItem[];
@@ -0,0 +1,93 @@
1
+ /* eslint-disable sort-keys */
2
+ import React from 'react';
3
+ import { isUserMuted } from '../../components';
4
+ import { ReactionIcon as DefaultReactionIcon, ThreadIcon } from '../../components/Message/icons';
5
+ import { ReactionSelectorWithButton } from '../../components/Reactions/ReactionSelectorWithButton';
6
+ import { useChannelActionContext, useChatContext, useMessageContext, useTranslationContext, } from '../../context';
7
+ export const DefaultDropdownActionButton = ({ 'aria-selected': ariaSelected = 'false', children, className = 'str-chat__message-actions-list-item-button', role = 'option', ...rest }) => (React.createElement("button", { "aria-selected": ariaSelected, className: className, role: role, ...rest }, children));
8
+ const DefaultMessageActionComponents = {
9
+ dropdown: {
10
+ Quote() {
11
+ const { setQuotedMessage } = useChannelActionContext();
12
+ const { message } = useMessageContext();
13
+ const { t } = useTranslationContext();
14
+ const handleQuote = () => {
15
+ setQuotedMessage(message);
16
+ const elements = message.parent_id
17
+ ? document.querySelectorAll('.str-chat__thread .str-chat__textarea__textarea')
18
+ : document.getElementsByClassName('str-chat__textarea__textarea');
19
+ const textarea = elements.item(0);
20
+ if (textarea instanceof HTMLTextAreaElement) {
21
+ textarea.focus();
22
+ }
23
+ };
24
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handleQuote }, t('Quote')));
25
+ },
26
+ Pin() {
27
+ const { handlePin, message } = useMessageContext();
28
+ const { t } = useTranslationContext();
29
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handlePin }, !message.pinned ? t('Pin') : t('Unpin')));
30
+ },
31
+ MarkUnread() {
32
+ const { handleMarkUnread } = useMessageContext();
33
+ const { t } = useTranslationContext();
34
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handleMarkUnread }, t('Mark as unread')));
35
+ },
36
+ Flag() {
37
+ const { handleFlag } = useMessageContext();
38
+ const { t } = useTranslationContext();
39
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handleFlag }, t('Flag')));
40
+ },
41
+ Mute() {
42
+ const { handleMute, message } = useMessageContext();
43
+ const { mutes } = useChatContext();
44
+ const { t } = useTranslationContext();
45
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handleMute }, isUserMuted(message, mutes) ? t('Unmute') : t('Mute')));
46
+ },
47
+ Edit() {
48
+ const { handleEdit } = useMessageContext();
49
+ const { t } = useTranslationContext();
50
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handleEdit }, t('Edit Message')));
51
+ },
52
+ Delete() {
53
+ const { handleDelete } = useMessageContext();
54
+ const { t } = useTranslationContext();
55
+ return (React.createElement(DefaultDropdownActionButton, { onClick: handleDelete }, t('Delete')));
56
+ },
57
+ },
58
+ quick: {
59
+ React() {
60
+ return React.createElement(ReactionSelectorWithButton, { ReactionIcon: DefaultReactionIcon });
61
+ },
62
+ Reply() {
63
+ const { handleOpenThread } = useMessageContext();
64
+ const { t } = useTranslationContext();
65
+ return (React.createElement("button", { "aria-label": t('aria/Open Thread'), className: 'str-chat__message-reply-in-thread-button', "data-testid": 'thread-action', onClick: handleOpenThread },
66
+ React.createElement(ThreadIcon, { className: 'str-chat__message-action-icon' })));
67
+ },
68
+ },
69
+ };
70
+ export const defaultMessageActionSet = [
71
+ // { placement: 'dropdown', type: 'block' },
72
+ { Component: DefaultMessageActionComponents.quick.Reply, placement: 'quick', type: 'reply' },
73
+ { Component: DefaultMessageActionComponents.quick.React, placement: 'quick', type: 'react' },
74
+ {
75
+ Component: DefaultMessageActionComponents.dropdown.Delete,
76
+ placement: 'dropdown',
77
+ type: 'delete',
78
+ },
79
+ { Component: DefaultMessageActionComponents.dropdown.Edit, placement: 'dropdown', type: 'edit' },
80
+ { Component: DefaultMessageActionComponents.dropdown.Mute, placement: 'dropdown', type: 'mute' },
81
+ { Component: DefaultMessageActionComponents.dropdown.Flag, placement: 'dropdown', type: 'flag' },
82
+ { Component: DefaultMessageActionComponents.dropdown.Pin, placement: 'dropdown', type: 'pin' },
83
+ {
84
+ Component: DefaultMessageActionComponents.dropdown.Quote,
85
+ placement: 'dropdown',
86
+ type: 'quote',
87
+ },
88
+ {
89
+ Component: DefaultMessageActionComponents.dropdown.MarkUnread,
90
+ placement: 'dropdown',
91
+ type: 'markUnread',
92
+ },
93
+ ];
@@ -0,0 +1,2 @@
1
+ export * from './useBaseMessageActionSetFilter';
2
+ export * from './useSplitMessageActionSet';
@@ -0,0 +1,2 @@
1
+ export * from './useBaseMessageActionSetFilter';
2
+ export * from './useSplitMessageActionSet';
@@ -0,0 +1,8 @@
1
+ import type { MessageActionSetItem } from '../MessageActions';
2
+ /**
3
+ * Base filter hook which covers actions of type `delete`, `edit`,
4
+ * `flag`, `markUnread`, `mute`, `quote`, `react` and `reply`, whether
5
+ * the rendered message is a reply (replies are limited to certain actions) and
6
+ * whether the message has appropriate type and status.
7
+ */
8
+ export declare const useBaseMessageActionSetFilter: (messageActionSet: MessageActionSetItem[], disable?: boolean) => MessageActionSetItem[];
@@ -0,0 +1,57 @@
1
+ import { useMemo } from 'react';
2
+ import { ACTIONS_NOT_WORKING_IN_THREAD, useUserRole } from '../../../components';
3
+ import { useMessageContext } from '../../../context';
4
+ /**
5
+ * Base filter hook which covers actions of type `delete`, `edit`,
6
+ * `flag`, `markUnread`, `mute`, `quote`, `react` and `reply`, whether
7
+ * the rendered message is a reply (replies are limited to certain actions) and
8
+ * whether the message has appropriate type and status.
9
+ */
10
+ export const useBaseMessageActionSetFilter = (messageActionSet, disable = false) => {
11
+ const { initialMessage: isInitialMessage, message } = useMessageContext();
12
+ const { canDelete, canEdit, canFlag, canMarkUnread, canMute, canQuote, canReact, canReply, } = useUserRole(message);
13
+ const isMessageThreadReply = typeof message.parent_id === 'string';
14
+ return useMemo(() => {
15
+ if (disable)
16
+ return messageActionSet;
17
+ // filter out all actions if any of these are true
18
+ if (isInitialMessage || // not sure whether this thing even works anymore
19
+ !message.type ||
20
+ message.type === 'error' ||
21
+ message.type === 'system' ||
22
+ message.type === 'ephemeral' ||
23
+ message.status === 'failed' ||
24
+ message.status === 'sending')
25
+ return [];
26
+ return messageActionSet.filter(({ type }) => {
27
+ // filter out actions with types that do not work in thread
28
+ if (ACTIONS_NOT_WORKING_IN_THREAD.includes(type) && isMessageThreadReply)
29
+ return false;
30
+ if ((type === 'delete' && !canDelete) ||
31
+ (type === 'edit' && !canEdit) ||
32
+ (type === 'flag' && !canFlag) ||
33
+ (type === 'markUnread' && !canMarkUnread) ||
34
+ (type === 'mute' && !canMute) ||
35
+ (type === 'quote' && !canQuote) ||
36
+ (type === 'react' && !canReact) ||
37
+ (type === 'reply' && !canReply))
38
+ return false;
39
+ return true;
40
+ });
41
+ }, [
42
+ canDelete,
43
+ canEdit,
44
+ canFlag,
45
+ canMarkUnread,
46
+ canMute,
47
+ canQuote,
48
+ canReact,
49
+ canReply,
50
+ isInitialMessage,
51
+ isMessageThreadReply,
52
+ message.status,
53
+ message.type,
54
+ disable,
55
+ messageActionSet,
56
+ ]);
57
+ };
@@ -0,0 +1,5 @@
1
+ import type { MessageActionSetItem } from '../MessageActions';
2
+ export declare const useSplitMessageActionSet: (messageActionSet: MessageActionSetItem[]) => {
3
+ dropdownActionSet: MessageActionSetItem[];
4
+ quickActionSet: MessageActionSetItem[];
5
+ };
@@ -0,0 +1,12 @@
1
+ import { useMemo } from 'react';
2
+ export const useSplitMessageActionSet = (messageActionSet) => useMemo(() => {
3
+ const quickActionSet = [];
4
+ const dropdownActionSet = [];
5
+ for (const action of messageActionSet) {
6
+ if (action.placement === 'quick')
7
+ quickActionSet.push(action);
8
+ if (action.placement === 'dropdown')
9
+ dropdownActionSet.push(action);
10
+ }
11
+ return { dropdownActionSet, quickActionSet };
12
+ }, [messageActionSet]);
@@ -0,0 +1,3 @@
1
+ export * from './MessageActions';
2
+ export * from './defaults';
3
+ export * from './hooks';
@@ -0,0 +1,3 @@
1
+ export * from './MessageActions';
2
+ export * from './defaults';
3
+ export * from './hooks';