mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-27 03:43:27 +01:00
Fix self-mention in groups
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
@@ -198,6 +198,7 @@ export type Props = Pick<
|
|||||||
| 'getPreferredBadge'
|
| 'getPreferredBadge'
|
||||||
| 'onEditorStateChange'
|
| 'onEditorStateChange'
|
||||||
| 'onTextTooLong'
|
| 'onTextTooLong'
|
||||||
|
| 'ourConversationId'
|
||||||
| 'quotedMessageId'
|
| 'quotedMessageId'
|
||||||
| 'sendCounter'
|
| 'sendCounter'
|
||||||
| 'sortedGroupMembers'
|
| 'sortedGroupMembers'
|
||||||
@@ -280,6 +281,7 @@ export const CompositionArea = memo(function CompositionArea({
|
|||||||
isFormattingEnabled,
|
isFormattingEnabled,
|
||||||
onEditorStateChange,
|
onEditorStateChange,
|
||||||
onTextTooLong,
|
onTextTooLong,
|
||||||
|
ourConversationId,
|
||||||
sendCounter,
|
sendCounter,
|
||||||
sortedGroupMembers,
|
sortedGroupMembers,
|
||||||
// EmojiButton
|
// EmojiButton
|
||||||
@@ -947,6 +949,7 @@ export const CompositionArea = memo(function CompositionArea({
|
|||||||
}}
|
}}
|
||||||
onPickEmoji={onPickEmoji}
|
onPickEmoji={onPickEmoji}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
recentStickers={recentStickers}
|
recentStickers={recentStickers}
|
||||||
skinTone={skinTone}
|
skinTone={skinTone}
|
||||||
@@ -1042,6 +1045,7 @@ export const CompositionArea = memo(function CompositionArea({
|
|||||||
onPickEmoji={onPickEmoji}
|
onPickEmoji={onPickEmoji}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
quotedMessageId={quotedMessageId}
|
quotedMessageId={quotedMessageId}
|
||||||
sendCounter={sendCounter}
|
sendCounter={sendCounter}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => {
|
|||||||
onPickEmoji: action('onPickEmoji'),
|
onPickEmoji: action('onPickEmoji'),
|
||||||
onSubmit: action('onSubmit'),
|
onSubmit: action('onSubmit'),
|
||||||
onTextTooLong: action('onTextTooLong'),
|
onTextTooLong: action('onTextTooLong'),
|
||||||
|
ourConversationId: 'me',
|
||||||
platform: 'darwin',
|
platform: 'darwin',
|
||||||
quotedMessageId: null,
|
quotedMessageId: null,
|
||||||
sendCounter: 0,
|
sendCounter: 0,
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ export type Props = Readonly<{
|
|||||||
timestamp: number
|
timestamp: number
|
||||||
): unknown;
|
): unknown;
|
||||||
onScroll?: (ev: React.UIEvent<HTMLElement>) => void;
|
onScroll?: (ev: React.UIEvent<HTMLElement>) => void;
|
||||||
|
ourConversationId: string | undefined;
|
||||||
platform: string;
|
platform: string;
|
||||||
quotedMessageId: string | null;
|
quotedMessageId: string | null;
|
||||||
shouldHidePopovers: boolean | null;
|
shouldHidePopovers: boolean | null;
|
||||||
@@ -173,6 +174,7 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||||||
onPickEmoji,
|
onPickEmoji,
|
||||||
onScroll,
|
onScroll,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
ourConversationId,
|
||||||
placeholder,
|
placeholder,
|
||||||
platform,
|
platform,
|
||||||
quotedMessageId,
|
quotedMessageId,
|
||||||
@@ -781,11 +783,9 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||||||
},
|
},
|
||||||
mentionCompletion: {
|
mentionCompletion: {
|
||||||
getPreferredBadge,
|
getPreferredBadge,
|
||||||
me: sortedGroupMembers
|
|
||||||
? sortedGroupMembers.find(foo => foo.isMe)
|
|
||||||
: undefined,
|
|
||||||
memberRepositoryRef,
|
memberRepositoryRef,
|
||||||
setMentionPickerElement: setMentionCompletionElement,
|
setMentionPickerElement: setMentionCompletionElement,
|
||||||
|
ourConversationId,
|
||||||
i18n,
|
i18n,
|
||||||
theme,
|
theme,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export type CompositionTextAreaProps = {
|
|||||||
timestamp: number
|
timestamp: number
|
||||||
) => void;
|
) => void;
|
||||||
onTextTooLong: () => void;
|
onTextTooLong: () => void;
|
||||||
|
ourConversationId: string | undefined;
|
||||||
platform: string;
|
platform: string;
|
||||||
getPreferredBadge: PreferredBadgeSelectorType;
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
draftText: string;
|
draftText: string;
|
||||||
@@ -66,6 +67,7 @@ export function CompositionTextArea({
|
|||||||
onSetSkinTone,
|
onSetSkinTone,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onTextTooLong,
|
onTextTooLong,
|
||||||
|
ourConversationId,
|
||||||
placeholder,
|
placeholder,
|
||||||
platform,
|
platform,
|
||||||
recentEmojis,
|
recentEmojis,
|
||||||
@@ -147,6 +149,7 @@ export function CompositionTextArea({
|
|||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
quotedMessageId={null}
|
quotedMessageId={null}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||||||
onPickEmoji={action('onPickEmoji')}
|
onPickEmoji={action('onPickEmoji')}
|
||||||
onSetSkinTone={action('onSetSkinTone')}
|
onSetSkinTone={action('onSetSkinTone')}
|
||||||
onTextTooLong={action('onTextTooLong')}
|
onTextTooLong={action('onTextTooLong')}
|
||||||
|
ourConversationId="me"
|
||||||
platform="darwin"
|
platform="darwin"
|
||||||
skinTone={0}
|
skinTone={0}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export type PropsType = {
|
|||||||
| 'isFormattingEnabled'
|
| 'isFormattingEnabled'
|
||||||
| 'onPickEmoji'
|
| 'onPickEmoji'
|
||||||
| 'onTextTooLong'
|
| 'onTextTooLong'
|
||||||
|
| 'ourConversationId'
|
||||||
| 'platform'
|
| 'platform'
|
||||||
| 'sortedGroupMembers'
|
| 'sortedGroupMembers'
|
||||||
> &
|
> &
|
||||||
@@ -157,6 +158,7 @@ export function MediaEditor({
|
|||||||
isFormattingEnabled,
|
isFormattingEnabled,
|
||||||
onPickEmoji,
|
onPickEmoji,
|
||||||
onTextTooLong,
|
onTextTooLong,
|
||||||
|
ourConversationId,
|
||||||
platform,
|
platform,
|
||||||
sortedGroupMembers,
|
sortedGroupMembers,
|
||||||
|
|
||||||
@@ -1315,6 +1317,7 @@ export function MediaEditor({
|
|||||||
onPickEmoji={onPickEmoji}
|
onPickEmoji={onPickEmoji}
|
||||||
onSubmit={noop}
|
onSubmit={noop}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
placeholder={i18n('icu:MediaEditor__input-placeholder')}
|
placeholder={i18n('icu:MediaEditor__input-placeholder')}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
quotedMessageId={null}
|
quotedMessageId={null}
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ export function StoryCreator({
|
|||||||
}}
|
}}
|
||||||
onPickEmoji={onPickEmoji}
|
onPickEmoji={onPickEmoji}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
recentStickers={recentStickers}
|
recentStickers={recentStickers}
|
||||||
skinTone={skinTone}
|
skinTone={skinTone}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export type PropsType = {
|
|||||||
) => unknown;
|
) => unknown;
|
||||||
onUseEmoji: (_: EmojiPickDataType) => unknown;
|
onUseEmoji: (_: EmojiPickDataType) => unknown;
|
||||||
onMediaPlaybackStart: () => void;
|
onMediaPlaybackStart: () => void;
|
||||||
|
ourConversationId: string | undefined;
|
||||||
platform: string;
|
platform: string;
|
||||||
preferredReactionEmoji: ReadonlyArray<string>;
|
preferredReactionEmoji: ReadonlyArray<string>;
|
||||||
queueStoryDownload: (storyId: string) => unknown;
|
queueStoryDownload: (storyId: string) => unknown;
|
||||||
@@ -159,6 +160,7 @@ export function StoryViewer({
|
|||||||
onTextTooLong,
|
onTextTooLong,
|
||||||
onUseEmoji,
|
onUseEmoji,
|
||||||
onMediaPlaybackStart,
|
onMediaPlaybackStart,
|
||||||
|
ourConversationId,
|
||||||
platform,
|
platform,
|
||||||
preferredReactionEmoji,
|
preferredReactionEmoji,
|
||||||
queueStoryDownload,
|
queueStoryDownload,
|
||||||
@@ -978,6 +980,7 @@ export function StoryViewer({
|
|||||||
onSetSkinTone={onSetSkinTone}
|
onSetSkinTone={onSetSkinTone}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
onUseEmoji={onUseEmoji}
|
onUseEmoji={onUseEmoji}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
preferredReactionEmoji={preferredReactionEmoji}
|
preferredReactionEmoji={preferredReactionEmoji}
|
||||||
recentEmojis={recentEmojis}
|
recentEmojis={recentEmojis}
|
||||||
renderEmojiPicker={renderEmojiPicker}
|
renderEmojiPicker={renderEmojiPicker}
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export type PropsType = {
|
|||||||
onSetSkinTone: (tone: number) => unknown;
|
onSetSkinTone: (tone: number) => unknown;
|
||||||
onTextTooLong: () => unknown;
|
onTextTooLong: () => unknown;
|
||||||
onUseEmoji: (_: EmojiPickDataType) => unknown;
|
onUseEmoji: (_: EmojiPickDataType) => unknown;
|
||||||
|
ourConversationId: string | undefined;
|
||||||
preferredReactionEmoji: ReadonlyArray<string>;
|
preferredReactionEmoji: ReadonlyArray<string>;
|
||||||
recentEmojis?: ReadonlyArray<string>;
|
recentEmojis?: ReadonlyArray<string>;
|
||||||
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
|
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
|
||||||
@@ -138,6 +139,7 @@ export function StoryViewsNRepliesModal({
|
|||||||
onSetSkinTone,
|
onSetSkinTone,
|
||||||
onTextTooLong,
|
onTextTooLong,
|
||||||
onUseEmoji,
|
onUseEmoji,
|
||||||
|
ourConversationId,
|
||||||
preferredReactionEmoji,
|
preferredReactionEmoji,
|
||||||
recentEmojis,
|
recentEmojis,
|
||||||
renderEmojiPicker,
|
renderEmojiPicker,
|
||||||
@@ -254,6 +256,7 @@ export function StoryViewsNRepliesModal({
|
|||||||
onReply(...args);
|
onReply(...args);
|
||||||
}}
|
}}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
placeholder={
|
placeholder={
|
||||||
group
|
group
|
||||||
? i18n('icu:StoryViewer__reply-group')
|
? i18n('icu:StoryViewer__reply-group')
|
||||||
|
|||||||
@@ -82,9 +82,9 @@ export class MemberRepository {
|
|||||||
this.isFuseReady = false;
|
this.isFuseReady = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMembers(omit?: Pick<MemberType, 'id'>): ReadonlyArray<MemberType> {
|
getMembers(omitId?: string): ReadonlyArray<MemberType> {
|
||||||
if (omit) {
|
if (omitId) {
|
||||||
return this.members.filter(({ id }) => id !== omit.id);
|
return this.members.filter(({ id }) => id !== omitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.members;
|
return this.members;
|
||||||
@@ -102,10 +102,7 @@ export class MemberRepository {
|
|||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
search(
|
search(pattern: string, omitId?: string): ReadonlyArray<MemberType> {
|
||||||
pattern: string,
|
|
||||||
omit?: Pick<MemberType, 'id'>
|
|
||||||
): ReadonlyArray<MemberType> {
|
|
||||||
if (!this.isFuseReady) {
|
if (!this.isFuseReady) {
|
||||||
this.fuse.setCollection(this.members);
|
this.fuse.setCollection(this.members);
|
||||||
this.isFuseReady = true;
|
this.isFuseReady = true;
|
||||||
@@ -115,8 +112,8 @@ export class MemberRepository {
|
|||||||
.search(removeDiacritics(pattern))
|
.search(removeDiacritics(pattern))
|
||||||
.map(result => result.item);
|
.map(result => result.item);
|
||||||
|
|
||||||
if (omit) {
|
if (omitId) {
|
||||||
return results.filter(({ id }) => id !== omit.id);
|
return results.filter(({ id }) => id !== omitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import React from 'react';
|
|||||||
import { Popper } from 'react-popper';
|
import { Popper } from 'react-popper';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
|
||||||
import { Avatar, AvatarSize } from '../../components/Avatar';
|
import { Avatar, AvatarSize } from '../../components/Avatar';
|
||||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
import type { MemberType, MemberRepository } from '../memberRepository';
|
import type { MemberType, MemberRepository } from '../memberRepository';
|
||||||
@@ -26,7 +25,7 @@ export type MentionCompletionOptions = {
|
|||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
memberRepositoryRef: RefObject<MemberRepository>;
|
memberRepositoryRef: RefObject<MemberRepository>;
|
||||||
setMentionPickerElement: (element: JSX.Element | null) => void;
|
setMentionPickerElement: (element: JSX.Element | null) => void;
|
||||||
me?: ConversationType;
|
ourConversationId: string | undefined;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,10 +127,15 @@ export class MentionCompletion {
|
|||||||
|
|
||||||
if (memberRepository) {
|
if (memberRepository) {
|
||||||
if (leftTokenText === '') {
|
if (leftTokenText === '') {
|
||||||
results = memberRepository.getMembers(this.options.me);
|
results = memberRepository.getMembers(
|
||||||
|
this.options.ourConversationId
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const fullMentionText = leftTokenText;
|
const fullMentionText = leftTokenText;
|
||||||
results = memberRepository.search(fullMentionText, this.options.me);
|
results = memberRepository.search(
|
||||||
|
fullMentionText,
|
||||||
|
this.options.ourConversationId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,7 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
|
|||||||
lastEditableMessageId={lastEditableMessageId ?? null}
|
lastEditableMessageId={lastEditableMessageId ?? null}
|
||||||
messageCompositionId={messageCompositionId}
|
messageCompositionId={messageCompositionId}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
sendCounter={sendCounter}
|
sendCounter={sendCounter}
|
||||||
shouldHidePopovers={shouldHidePopovers}
|
shouldHidePopovers={shouldHidePopovers}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, { memo } from 'react';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import type { CompositionTextAreaProps } from '../../components/CompositionTextArea';
|
import type { CompositionTextAreaProps } from '../../components/CompositionTextArea';
|
||||||
import { CompositionTextArea } 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 { useEmojisActions as useEmojiActions } from '../ducks/emojis';
|
||||||
import { useItemsActions } from '../ducks/items';
|
import { useItemsActions } from '../ducks/items';
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
@@ -31,6 +31,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
|
|||||||
) {
|
) {
|
||||||
const i18n = useSelector(getIntl);
|
const i18n = useSelector(getIntl);
|
||||||
const platform = useSelector(getPlatform);
|
const platform = useSelector(getPlatform);
|
||||||
|
const ourConversationId = useSelector(getUserConversationId);
|
||||||
|
|
||||||
const { onUseEmoji: onPickEmoji } = useEmojiActions();
|
const { onUseEmoji: onPickEmoji } = useEmojiActions();
|
||||||
const { onSetSkinTone } = useItemsActions();
|
const { onSetSkinTone } = useItemsActions();
|
||||||
@@ -50,6 +51,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
|
|||||||
onSetSkinTone={onSetSkinTone}
|
onSetSkinTone={onSetSkinTone}
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
getTextFormattingEnabled,
|
getTextFormattingEnabled,
|
||||||
isInternalUser,
|
isInternalUser,
|
||||||
} from '../selectors/items';
|
} from '../selectors/items';
|
||||||
import { getIntl, getPlatform } from '../selectors/user';
|
import { getIntl, getPlatform, getUserConversationId } from '../selectors/user';
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
import {
|
import {
|
||||||
getSelectedStoryData,
|
getSelectedStoryData,
|
||||||
@@ -64,6 +64,7 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
|||||||
|
|
||||||
const i18n = useSelector(getIntl);
|
const i18n = useSelector(getIntl);
|
||||||
const platform = useSelector(getPlatform);
|
const platform = useSelector(getPlatform);
|
||||||
|
const ourConversationId = useSelector(getUserConversationId);
|
||||||
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
||||||
const preferredReactionEmoji = useSelector(getPreferredReactionEmoji);
|
const preferredReactionEmoji = useSelector(getPreferredReactionEmoji);
|
||||||
const selectedStoryData = useSelector(getSelectedStoryData);
|
const selectedStoryData = useSelector(getSelectedStoryData);
|
||||||
@@ -154,6 +155,7 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
|||||||
onSetSkinTone={onSetSkinTone}
|
onSetSkinTone={onSetSkinTone}
|
||||||
onTextTooLong={handleTextTooLong}
|
onTextTooLong={handleTextTooLong}
|
||||||
onUseEmoji={onUseEmoji}
|
onUseEmoji={onUseEmoji}
|
||||||
|
ourConversationId={ourConversationId}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
preferredReactionEmoji={preferredReactionEmoji}
|
preferredReactionEmoji={preferredReactionEmoji}
|
||||||
queueStoryDownload={queueStoryDownload}
|
queueStoryDownload={queueStoryDownload}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ describe('MentionCompletion', () => {
|
|||||||
const options: MentionCompletionOptions = {
|
const options: MentionCompletionOptions = {
|
||||||
getPreferredBadge: () => undefined,
|
getPreferredBadge: () => undefined,
|
||||||
i18n: setupI18n('en', {}),
|
i18n: setupI18n('en', {}),
|
||||||
me,
|
ourConversationId: me.id,
|
||||||
memberRepositoryRef,
|
memberRepositoryRef,
|
||||||
setMentionPickerElement: sinon.stub(),
|
setMentionPickerElement: sinon.stub(),
|
||||||
theme: ThemeType.dark,
|
theme: ThemeType.dark,
|
||||||
|
|||||||
Reference in New Issue
Block a user