From 4312d03db082ec0b3cc552e60609684807a7479f Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:33:01 -0800 Subject: [PATCH] Fix self-mention in groups --- ts/components/CompositionArea.tsx | 4 ++++ ts/components/CompositionInput.stories.tsx | 1 + ts/components/CompositionInput.tsx | 6 +++--- ts/components/CompositionTextArea.tsx | 3 +++ ts/components/ForwardMessagesModal.stories.tsx | 1 + ts/components/MediaEditor.tsx | 3 +++ ts/components/StoryCreator.tsx | 1 + ts/components/StoryViewer.tsx | 3 +++ ts/components/StoryViewsNRepliesModal.tsx | 3 +++ ts/quill/memberRepository.ts | 15 ++++++--------- ts/quill/mentions/completion.tsx | 12 ++++++++---- ts/state/smart/CompositionArea.tsx | 1 + ts/state/smart/CompositionTextArea.tsx | 4 +++- ts/state/smart/StoryViewer.tsx | 4 +++- .../quill/mentions/completion_test.tsx | 2 +- 15 files changed, 44 insertions(+), 19 deletions(-) diff --git a/ts/components/CompositionArea.tsx b/ts/components/CompositionArea.tsx index 2755d587c..c71c3985d 100644 --- a/ts/components/CompositionArea.tsx +++ b/ts/components/CompositionArea.tsx @@ -198,6 +198,7 @@ export type Props = Pick< | 'getPreferredBadge' | 'onEditorStateChange' | 'onTextTooLong' + | 'ourConversationId' | 'quotedMessageId' | 'sendCounter' | 'sortedGroupMembers' @@ -280,6 +281,7 @@ export const CompositionArea = memo(function CompositionArea({ isFormattingEnabled, onEditorStateChange, onTextTooLong, + ourConversationId, sendCounter, sortedGroupMembers, // EmojiButton @@ -947,6 +949,7 @@ export const CompositionArea = memo(function CompositionArea({ }} onPickEmoji={onPickEmoji} onTextTooLong={onTextTooLong} + ourConversationId={ourConversationId} platform={platform} recentStickers={recentStickers} skinTone={skinTone} @@ -1042,6 +1045,7 @@ export const CompositionArea = memo(function CompositionArea({ onPickEmoji={onPickEmoji} onSubmit={handleSubmit} onTextTooLong={onTextTooLong} + ourConversationId={ourConversationId} platform={platform} quotedMessageId={quotedMessageId} sendCounter={sendCounter} diff --git a/ts/components/CompositionInput.stories.tsx b/ts/components/CompositionInput.stories.tsx index 003380592..3a76f2f7c 100644 --- a/ts/components/CompositionInput.stories.tsx +++ b/ts/components/CompositionInput.stories.tsx @@ -43,6 +43,7 @@ const useProps = (overrideProps: Partial = {}): Props => { onPickEmoji: action('onPickEmoji'), onSubmit: action('onSubmit'), onTextTooLong: action('onTextTooLong'), + ourConversationId: 'me', platform: 'darwin', quotedMessageId: null, sendCounter: 0, diff --git a/ts/components/CompositionInput.tsx b/ts/components/CompositionInput.tsx index 00da41beb..f89d6e061 100644 --- a/ts/components/CompositionInput.tsx +++ b/ts/components/CompositionInput.tsx @@ -139,6 +139,7 @@ export type Props = Readonly<{ timestamp: number ): unknown; onScroll?: (ev: React.UIEvent) => void; + ourConversationId: string | undefined; platform: string; quotedMessageId: string | null; shouldHidePopovers: boolean | null; @@ -173,6 +174,7 @@ export function CompositionInput(props: Props): React.ReactElement { onPickEmoji, onScroll, onSubmit, + ourConversationId, placeholder, platform, quotedMessageId, @@ -781,11 +783,9 @@ export function CompositionInput(props: Props): React.ReactElement { }, mentionCompletion: { getPreferredBadge, - me: sortedGroupMembers - ? sortedGroupMembers.find(foo => foo.isMe) - : undefined, memberRepositoryRef, setMentionPickerElement: setMentionCompletionElement, + ourConversationId, i18n, theme, }, diff --git a/ts/components/CompositionTextArea.tsx b/ts/components/CompositionTextArea.tsx index bdd968c90..87e4d3ca4 100644 --- a/ts/components/CompositionTextArea.tsx +++ b/ts/components/CompositionTextArea.tsx @@ -39,6 +39,7 @@ export type CompositionTextAreaProps = { timestamp: number ) => void; onTextTooLong: () => void; + ourConversationId: string | undefined; platform: string; getPreferredBadge: PreferredBadgeSelectorType; draftText: string; @@ -66,6 +67,7 @@ export function CompositionTextArea({ onSetSkinTone, onSubmit, onTextTooLong, + ourConversationId, placeholder, platform, recentEmojis, @@ -147,6 +149,7 @@ export function CompositionTextArea({ onScroll={onScroll} onSubmit={onSubmit} onTextTooLong={onTextTooLong} + ourConversationId={ourConversationId} placeholder={placeholder} platform={platform} quotedMessageId={null} diff --git a/ts/components/ForwardMessagesModal.stories.tsx b/ts/components/ForwardMessagesModal.stories.tsx index 72870b427..66600aa58 100644 --- a/ts/components/ForwardMessagesModal.stories.tsx +++ b/ts/components/ForwardMessagesModal.stories.tsx @@ -67,6 +67,7 @@ const useProps = (overrideProps: Partial = {}): PropsType => ({ onPickEmoji={action('onPickEmoji')} onSetSkinTone={action('onSetSkinTone')} onTextTooLong={action('onTextTooLong')} + ourConversationId="me" platform="darwin" skinTone={0} /> diff --git a/ts/components/MediaEditor.tsx b/ts/components/MediaEditor.tsx index 5803b0a73..588e4b9ac 100644 --- a/ts/components/MediaEditor.tsx +++ b/ts/components/MediaEditor.tsx @@ -89,6 +89,7 @@ export type PropsType = { | 'isFormattingEnabled' | 'onPickEmoji' | 'onTextTooLong' + | 'ourConversationId' | 'platform' | 'sortedGroupMembers' > & @@ -157,6 +158,7 @@ export function MediaEditor({ isFormattingEnabled, onPickEmoji, onTextTooLong, + ourConversationId, platform, sortedGroupMembers, @@ -1315,6 +1317,7 @@ export function MediaEditor({ onPickEmoji={onPickEmoji} onSubmit={noop} onTextTooLong={onTextTooLong} + ourConversationId={ourConversationId} placeholder={i18n('icu:MediaEditor__input-placeholder')} platform={platform} quotedMessageId={null} diff --git a/ts/components/StoryCreator.tsx b/ts/components/StoryCreator.tsx index 4df76fdb6..0486dec6b 100644 --- a/ts/components/StoryCreator.tsx +++ b/ts/components/StoryCreator.tsx @@ -274,6 +274,7 @@ export function StoryCreator({ }} onPickEmoji={onPickEmoji} onTextTooLong={onTextTooLong} + ourConversationId={ourConversationId} platform={platform} recentStickers={recentStickers} skinTone={skinTone} diff --git a/ts/components/StoryViewer.tsx b/ts/components/StoryViewer.tsx index ebb34454b..cd5b5fce4 100644 --- a/ts/components/StoryViewer.tsx +++ b/ts/components/StoryViewer.tsx @@ -103,6 +103,7 @@ export type PropsType = { ) => unknown; onUseEmoji: (_: EmojiPickDataType) => unknown; onMediaPlaybackStart: () => void; + ourConversationId: string | undefined; platform: string; preferredReactionEmoji: ReadonlyArray; queueStoryDownload: (storyId: string) => unknown; @@ -159,6 +160,7 @@ export function StoryViewer({ onTextTooLong, onUseEmoji, onMediaPlaybackStart, + ourConversationId, platform, preferredReactionEmoji, queueStoryDownload, @@ -978,6 +980,7 @@ export function StoryViewer({ onSetSkinTone={onSetSkinTone} onTextTooLong={onTextTooLong} onUseEmoji={onUseEmoji} + ourConversationId={ourConversationId} preferredReactionEmoji={preferredReactionEmoji} recentEmojis={recentEmojis} renderEmojiPicker={renderEmojiPicker} diff --git a/ts/components/StoryViewsNRepliesModal.tsx b/ts/components/StoryViewsNRepliesModal.tsx index d2acd1d42..f04cddd4c 100644 --- a/ts/components/StoryViewsNRepliesModal.tsx +++ b/ts/components/StoryViewsNRepliesModal.tsx @@ -107,6 +107,7 @@ export type PropsType = { onSetSkinTone: (tone: number) => unknown; onTextTooLong: () => unknown; onUseEmoji: (_: EmojiPickDataType) => unknown; + ourConversationId: string | undefined; preferredReactionEmoji: ReadonlyArray; recentEmojis?: ReadonlyArray; renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element; @@ -138,6 +139,7 @@ export function StoryViewsNRepliesModal({ onSetSkinTone, onTextTooLong, onUseEmoji, + ourConversationId, preferredReactionEmoji, recentEmojis, renderEmojiPicker, @@ -254,6 +256,7 @@ export function StoryViewsNRepliesModal({ onReply(...args); }} onTextTooLong={onTextTooLong} + ourConversationId={ourConversationId} placeholder={ group ? i18n('icu:StoryViewer__reply-group') diff --git a/ts/quill/memberRepository.ts b/ts/quill/memberRepository.ts index e7d0ab4f0..d59306939 100644 --- a/ts/quill/memberRepository.ts +++ b/ts/quill/memberRepository.ts @@ -82,9 +82,9 @@ export class MemberRepository { this.isFuseReady = false; } - getMembers(omit?: Pick): ReadonlyArray { - if (omit) { - return this.members.filter(({ id }) => id !== omit.id); + getMembers(omitId?: string): ReadonlyArray { + if (omitId) { + return this.members.filter(({ id }) => id !== omitId); } return this.members; @@ -102,10 +102,7 @@ export class MemberRepository { : undefined; } - search( - pattern: string, - omit?: Pick - ): ReadonlyArray { + search(pattern: string, omitId?: string): ReadonlyArray { if (!this.isFuseReady) { this.fuse.setCollection(this.members); this.isFuseReady = true; @@ -115,8 +112,8 @@ export class MemberRepository { .search(removeDiacritics(pattern)) .map(result => result.item); - if (omit) { - return results.filter(({ id }) => id !== omit.id); + if (omitId) { + return results.filter(({ id }) => id !== omitId); } return results; diff --git a/ts/quill/mentions/completion.tsx b/ts/quill/mentions/completion.tsx index fab91e78d..3dc502a33 100644 --- a/ts/quill/mentions/completion.tsx +++ b/ts/quill/mentions/completion.tsx @@ -10,7 +10,6 @@ import React from 'react'; import { Popper } from 'react-popper'; import classNames from 'classnames'; import { createPortal } from 'react-dom'; -import type { ConversationType } from '../../state/ducks/conversations'; import { Avatar, AvatarSize } from '../../components/Avatar'; import type { LocalizerType, ThemeType } from '../../types/Util'; import type { MemberType, MemberRepository } from '../memberRepository'; @@ -26,7 +25,7 @@ export type MentionCompletionOptions = { i18n: LocalizerType; memberRepositoryRef: RefObject; setMentionPickerElement: (element: JSX.Element | null) => void; - me?: ConversationType; + ourConversationId: string | undefined; theme: ThemeType; }; @@ -128,10 +127,15 @@ export class MentionCompletion { if (memberRepository) { if (leftTokenText === '') { - results = memberRepository.getMembers(this.options.me); + results = memberRepository.getMembers( + this.options.ourConversationId + ); } else { const fullMentionText = leftTokenText; - results = memberRepository.search(fullMentionText, this.options.me); + results = memberRepository.search( + fullMentionText, + this.options.ourConversationId + ); } } diff --git a/ts/state/smart/CompositionArea.tsx b/ts/state/smart/CompositionArea.tsx index a2a0dccf9..2d48373cd 100644 --- a/ts/state/smart/CompositionArea.tsx +++ b/ts/state/smart/CompositionArea.tsx @@ -255,6 +255,7 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({ lastEditableMessageId={lastEditableMessageId ?? null} messageCompositionId={messageCompositionId} platform={platform} + ourConversationId={ourConversationId} sendCounter={sendCounter} shouldHidePopovers={shouldHidePopovers} theme={theme} diff --git a/ts/state/smart/CompositionTextArea.tsx b/ts/state/smart/CompositionTextArea.tsx index 36fcd2598..08d5560eb 100644 --- a/ts/state/smart/CompositionTextArea.tsx +++ b/ts/state/smart/CompositionTextArea.tsx @@ -4,7 +4,7 @@ import React, { memo } from 'react'; import { useSelector } from 'react-redux'; import type { CompositionTextAreaProps } from '../../components/CompositionTextArea'; import { CompositionTextArea } from '../../components/CompositionTextArea'; -import { getIntl, getPlatform } from '../selectors/user'; +import { getIntl, getPlatform, getUserConversationId } from '../selectors/user'; import { useEmojisActions as useEmojiActions } from '../ducks/emojis'; import { useItemsActions } from '../ducks/items'; import { getPreferredBadgeSelector } from '../selectors/badges'; @@ -31,6 +31,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea( ) { const i18n = useSelector(getIntl); const platform = useSelector(getPlatform); + const ourConversationId = useSelector(getUserConversationId); const { onUseEmoji: onPickEmoji } = useEmojiActions(); const { onSetSkinTone } = useItemsActions(); @@ -50,6 +51,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea( onSetSkinTone={onSetSkinTone} onTextTooLong={onTextTooLong} platform={platform} + ourConversationId={ourConversationId} /> ); }); diff --git a/ts/state/smart/StoryViewer.tsx b/ts/state/smart/StoryViewer.tsx index 3b4d61fc5..4b64d061b 100644 --- a/ts/state/smart/StoryViewer.tsx +++ b/ts/state/smart/StoryViewer.tsx @@ -14,7 +14,7 @@ import { getTextFormattingEnabled, isInternalUser, } from '../selectors/items'; -import { getIntl, getPlatform } from '../selectors/user'; +import { getIntl, getPlatform, getUserConversationId } from '../selectors/user'; import { getPreferredBadgeSelector } from '../selectors/badges'; import { getSelectedStoryData, @@ -64,6 +64,7 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() { const i18n = useSelector(getIntl); const platform = useSelector(getPlatform); + const ourConversationId = useSelector(getUserConversationId); const getPreferredBadge = useSelector(getPreferredBadgeSelector); const preferredReactionEmoji = useSelector(getPreferredReactionEmoji); const selectedStoryData = useSelector(getSelectedStoryData); @@ -154,6 +155,7 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() { onSetSkinTone={onSetSkinTone} onTextTooLong={handleTextTooLong} onUseEmoji={onUseEmoji} + ourConversationId={ourConversationId} platform={platform} preferredReactionEmoji={preferredReactionEmoji} queueStoryDownload={queueStoryDownload} diff --git a/ts/test-electron/quill/mentions/completion_test.tsx b/ts/test-electron/quill/mentions/completion_test.tsx index 94097ee0f..e82ca91f3 100644 --- a/ts/test-electron/quill/mentions/completion_test.tsx +++ b/ts/test-electron/quill/mentions/completion_test.tsx @@ -82,7 +82,7 @@ describe('MentionCompletion', () => { const options: MentionCompletionOptions = { getPreferredBadge: () => undefined, i18n: setupI18n('en', {}), - me, + ourConversationId: me.id, memberRepositoryRef, setMentionPickerElement: sinon.stub(), theme: ThemeType.dark,