diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 8c48cbad561f..5f89bde40167 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -304,6 +304,7 @@ const ONYXKEYS = { REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_', REPORT_ACTIONS_REACTIONS: 'reportActionsReactions_', REPORT_DRAFT_COMMENT: 'reportDraftComment_', + LAST_SELECTED_MENTION_SUGGESTION: 'lastSelectedMentionSuggestion_', REPORT_DRAFT_COMMENT_NUMBER_OF_LINES: 'reportDraftCommentNumberOfLines_', REPORT_IS_COMPOSER_FULL_SIZE: 'reportIsComposerFullSize_', REPORT_USER_IS_TYPING: 'reportUserIsTyping_', @@ -481,6 +482,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: OnyxTypes.ReportActionsDrafts; [ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions; [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; + [ONYXKEYS.COLLECTION.LAST_SELECTED_MENTION_SUGGESTION]: string; [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number; [ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean; [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: OnyxTypes.ReportUserIsTyping; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c999a137df1d..4870d01b62e7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1091,6 +1091,14 @@ function setReportWithDraft(reportID: string, hasDraft: boolean): Promise return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft}); } +/** + * Saves the last chosen mention from mention suggestion list for the report. + * This is used to determine if the mention suggestion list should be opened/remain opened. + */ +function saveReportDraftLastMention(reportID: string, comment: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.LAST_SELECTED_MENTION_SUGGESTION}${reportID}`, comment); +} + /** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */ function broadcastUserIsTyping(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); @@ -3022,4 +3030,5 @@ export { updateReportName, resolveActionableMentionWhisper, updateRoomVisibility, + saveReportDraftLastMention, }; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx index 6bbe5b10802f..1fc1d6d9d7bc 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx @@ -768,6 +768,7 @@ function ComposerWithSuggestions( composerHeight={composerHeight} measureParentContainer={measureParentContainer} isAutoSuggestionPickerLarge={isAutoSuggestionPickerLarge} + reportID={reportID} // Input value={value} setValue={setValue} diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index 0ae45d2d705d..ba2a8eccde21 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -25,7 +25,7 @@ type SuggestionEmojiOnyxProps = { preferredSkinTone: number; }; -type SuggestionEmojiProps = SuggestionProps & +type SuggestionEmojiProps = Omit & SuggestionEmojiOnyxProps & { /** Function to clear the input */ resetKeyboardInput?: () => void; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 5f07dc66ea4d..bd5434bf8171 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -1,7 +1,9 @@ import Str from 'expensify-common/lib/str'; import lodashSortBy from 'lodash/sortBy'; -import type {ForwardedRef} from 'react'; +import type {ForwardedRef, RefAttributes} from 'react'; import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import type {Mention} from '@components/MentionSuggestions'; import MentionSuggestions from '@components/MentionSuggestions'; @@ -10,11 +12,13 @@ import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; +import * as Report from '@libs/actions/Report'; import * as LoginUtils from '@libs/LoginUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList} from '@src/types/onyx'; import type {SuggestionsRef} from './ReportActionCompose'; import type {SuggestionProps} from './Suggestions'; @@ -38,8 +42,15 @@ const defaultSuggestionsValues: SuggestionValues = { mentionPrefix: '', }; +type SuggestionMentionOnyxProps = { + /** The last selected mention suggestion */ + lastSelectedMentionSuggestion: OnyxEntry; +}; + +type SuggestionMentionProps = SuggestionProps & SuggestionMentionOnyxProps; + function SuggestionMention( - {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused}: SuggestionProps, + {value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused, lastSelectedMentionSuggestion, reportID}: SuggestionMentionProps, ref: ForwardedRef, ) { const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; @@ -97,8 +108,9 @@ function SuggestionMention( ...prevState, suggestedMentions: [], })); + Report.saveReportDraftLastMention(reportID, mentionCode); }, - [value, suggestionValues.atSignIndex, suggestionValues.suggestedMentions, suggestionValues.mentionPrefix, updateComment, setSelection, formatLoginPrivateDomain], + [value, suggestionValues.atSignIndex, suggestionValues.suggestedMentions, suggestionValues.mentionPrefix.length, formatLoginPrivateDomain, updateComment, setSelection, reportID], ); /** @@ -222,6 +234,11 @@ function SuggestionMention( let suggestionWord = ''; let prefix: string; + if (lastSelectedMentionSuggestion && (lastWord === lastSelectedMentionSuggestion || (!lastWord && secondToLastWord === lastSelectedMentionSuggestion))) { + resetSuggestions(); + return; + } + // Detect if the last two words contain a mention (two words are needed to detect a mention with a space in it) if (lastWord.startsWith('@')) { atSignIndex = leftString.lastIndexOf(lastWord) + afterLastBreakLineIndex; @@ -258,10 +275,14 @@ function SuggestionMention( })); setHighlightedMentionIndex(0); }, - [getMentionOptions, personalDetails, resetSuggestions, setHighlightedMentionIndex, value, isComposerFocused], + [isComposerFocused, value, lastSelectedMentionSuggestion, reportID, setHighlightedMentionIndex, resetSuggestions, getMentionOptions, personalDetails], ); useEffect(() => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if ((!value && lastSelectedMentionSuggestion) || (lastSelectedMentionSuggestion && !value.includes(lastSelectedMentionSuggestion))) { + Report.saveReportDraftLastMention(reportID, ''); + } if (value.length < previousValue.length) { // A workaround to not show the suggestions list when the user deletes a character before the mention. // It is caused by a buggy behavior of the TextInput on iOS. Should be fixed after migration to Fabric. @@ -270,7 +291,7 @@ function SuggestionMention( } calculateMentionSuggestion(selection.end); - }, [selection, value, previousValue, calculateMentionSuggestion]); + }, [selection, value, previousValue, calculateMentionSuggestion, lastSelectedMentionSuggestion, reportID]); const updateShouldShowSuggestionMenuToFalse = useCallback(() => { setSuggestionValues((prevState) => { @@ -320,4 +341,8 @@ function SuggestionMention( SuggestionMention.displayName = 'SuggestionMention'; -export default forwardRef(SuggestionMention); +export default withOnyx, SuggestionMentionOnyxProps>({ + lastSelectedMentionSuggestion: { + key: ({reportID}) => `${ONYXKEYS.COLLECTION.LAST_SELECTED_MENTION_SUGGESTION}${reportID}`, + }, +})(forwardRef(SuggestionMention)); diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.tsx b/src/pages/home/report/ReportActionCompose/Suggestions.tsx index 61026a792919..89f755b49f1f 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.tsx +++ b/src/pages/home/report/ReportActionCompose/Suggestions.tsx @@ -46,6 +46,9 @@ type SuggestionProps = { /** The height of the composer */ composerHeight?: number; + + /** The report ID */ + reportID: string; }; /** @@ -66,6 +69,7 @@ function Suggestions( measureParentContainer, isAutoSuggestionPickerLarge = true, isComposerFocused, + reportID, }: SuggestionProps, ref: ForwardedRef, ) { @@ -167,6 +171,7 @@ function Suggestions( />