Skip to content

Commit

Permalink
Merge pull request Expensify#40232 from software-mansion-labs/war-in/…
Browse files Browse the repository at this point in the history
…online-search-for-auto-complete-widget

[Mentions v2] Update auto-complete widget to perform online search when needed
  • Loading branch information
rlinoz authored Apr 23, 2024
2 parents 944751f + 91d22c1 commit f05d306
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 33 deletions.
6 changes: 6 additions & 0 deletions src/libs/API/parameters/SearchForRoomsToMentionParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type SearchForRoomsToMentionParams = {
query: string;
policyID: string;
};

export default SearchForRoomsToMentionParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type {default as RequestAccountValidationLinkParams} from './RequestAccou
export type {default as ResolveActionableMentionWhisperParams} from './ResolveActionableMentionWhisperParams';
export type {default as RevealExpensifyCardDetailsParams} from './RevealExpensifyCardDetailsParams';
export type {default as SearchForReportsParams} from './SearchForReportsParams';
export type {default as SearchForRoomsToMentionParams} from './SearchForRoomsToMentionParams';
export type {default as SendPerformanceTimingParams} from './SendPerformanceTimingParams';
export type {default as SetContactMethodAsDefaultParams} from './SetContactMethodAsDefaultParams';
export type {default as SignInUserWithLinkParams} from './SignInUserWithLinkParams';
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ const READ_COMMANDS = {
GET_REPORT_PRIVATE_NOTE: 'GetReportPrivateNote',
OPEN_ROOM_MEMBERS_PAGE: 'OpenRoomMembersPage',
SEARCH_FOR_REPORTS: 'SearchForReports',
SEARCH_FOR_ROOMS_TO_MENTION: 'SearchForRoomsToMention',
SEND_PERFORMANCE_TIMING: 'SendPerformanceTiming',
GET_ROUTE: 'GetRoute',
GET_ROUTE_FOR_DRAFT: 'GetRouteForDraft',
Expand Down Expand Up @@ -484,6 +485,7 @@ type ReadCommandParameters = {
[READ_COMMANDS.GET_REPORT_PRIVATE_NOTE]: Parameters.GetReportPrivateNoteParams;
[READ_COMMANDS.OPEN_ROOM_MEMBERS_PAGE]: Parameters.OpenRoomMembersPageParams;
[READ_COMMANDS.SEARCH_FOR_REPORTS]: Parameters.SearchForReportsParams;
[READ_COMMANDS.SEARCH_FOR_ROOMS_TO_MENTION]: Parameters.SearchForRoomsToMentionParams;
[READ_COMMANDS.SEND_PERFORMANCE_TIMING]: Parameters.SendPerformanceTimingParams;
[READ_COMMANDS.GET_ROUTE]: Parameters.GetRouteParams;
[READ_COMMANDS.GET_ROUTE_FOR_DRAFT]: Parameters.GetRouteParams;
Expand Down
15 changes: 10 additions & 5 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import type {
RemoveFromRoomParams,
ResolveActionableMentionWhisperParams,
SearchForReportsParams,
SearchForRoomsToMentionParams,
SetNameValuePairParams,
TogglePinnedChatParams,
UpdateCommentParams,
Expand Down Expand Up @@ -3426,7 +3427,7 @@ function savePrivateNotesDraft(reportID: string, note: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT}${reportID}`, note);
}

function searchForReports(searchInput: string) {
function searchForReports(searchInput: string, policyID?: string) {
// We do not try to make this request while offline because it sets a loading indicator optimistically
if (isNetworkOffline) {
Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false);
Expand All @@ -3449,12 +3450,16 @@ function searchForReports(searchInput: string) {
},
];

const parameters: SearchForReportsParams = {searchInput};
const searchForRoomToMentionParams: SearchForRoomsToMentionParams = {query: searchInput, policyID: policyID ?? ''};
const searchForReportsParams: SearchForReportsParams = {searchInput};

API.read(READ_COMMANDS.SEARCH_FOR_REPORTS, parameters, {successData, failureData});
API.read(policyID ? READ_COMMANDS.SEARCH_FOR_ROOMS_TO_MENTION : READ_COMMANDS.SEARCH_FOR_REPORTS, policyID ? searchForRoomToMentionParams : searchForReportsParams, {
successData,
failureData,
});
}

function searchInServer(searchInput: string) {
function searchInServer(searchInput: string, policyID?: string) {
if (isNetworkOffline || !searchInput.trim().length) {
Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false);
return;
Expand All @@ -3464,7 +3469,7 @@ function searchInServer(searchInput: string) {
// we want to show the loading state right away. Otherwise, we will see a flashing UI where the client options are sorted and
// tell the user there are no options, then we start searching, and tell them there are no options again.
Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, true);
searchForReports(searchInput);
searchForReports(searchInput, policyID);
}

function updateLastVisitTime(reportID: string) {
Expand Down
61 changes: 33 additions & 28 deletions src/pages/home/report/ReportActionCompose/SuggestionMention.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import Str from 'expensify-common/lib/str';
import lodashSortBy from 'lodash/sortBy';
import type {ForwardedRef, RefAttributes} from 'react';
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {withOnyx} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import type {OnyxCollection} from 'react-native-onyx';
import * as Expensicons from '@components/Icon/Expensicons';
import type {Mention} from '@components/MentionSuggestions';
import MentionSuggestions from '@components/MentionSuggestions';
import {usePersonalDetails} from '@components/OnyxProvider';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDebounce from '@hooks/useDebounce';
import useLocalize from '@hooks/useLocalize';
import * as LoginUtils from '@libs/LoginUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as SuggestionsUtils from '@libs/SuggestionUtils';
import * as UserUtils from '@libs/UserUtils';
import {isValidRoomName} from '@libs/ValidationUtils';
import * as ReportUserActions from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PersonalDetailsList, Report} from '@src/types/onyx';
Expand All @@ -31,11 +33,6 @@ type SuggestionValues = {
prefixType: string;
};

type RoomMentionOnyxProps = {
/** All reports shared with the user */
reports: OnyxCollection<Report>;
};

/**
* Check if this piece of string looks like a mention
*/
Expand All @@ -50,24 +47,15 @@ const defaultSuggestionsValues: SuggestionValues = {
};

function SuggestionMention(
{
value,
selection,
setSelection,
updateComment,
isAutoSuggestionPickerLarge,
measureParentContainer,
isComposerFocused,
reports,
isGroupPolicyReport,
policyID,
}: SuggestionProps & RoomMentionOnyxProps,
{value, selection, setSelection, updateComment, isAutoSuggestionPickerLarge, measureParentContainer, isComposerFocused, isGroupPolicyReport, policyID}: SuggestionProps,
ref: ForwardedRef<SuggestionsRef>,
) {
const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT;
const {translate, formatPhoneNumber} = useLocalize();
const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues);

const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);

const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const isMentionSuggestionsMenuVisible = !!suggestionValues.suggestedMentions.length && suggestionValues.shouldShowSuggestionMenu;

Expand All @@ -90,6 +78,21 @@ function SuggestionMention(
// Used to decide whether to block the suggestions list from showing to prevent flickering
const shouldBlockCalc = useRef(false);

/**
* Search for reports suggestions in server.
*
* The function is debounced to not perform requests on every keystroke.
*/
const debouncedSearchInServer = useDebounce(
useCallback(() => {
const foundSuggestionsCount = suggestionValues.suggestedMentions.length;
if (suggestionValues.prefixType === '#' && foundSuggestionsCount < 5) {
ReportUserActions.searchInServer(value, policyID);
}
}, [suggestionValues.suggestedMentions.length, suggestionValues.prefixType, policyID, value]),
CONST.TIMING.SEARCH_OPTION_LIST_DEBOUNCE_TIME,
);

const formatLoginPrivateDomain = useCallback(
(displayText = '', userLogin = '') => {
if (userLogin !== displayText) {
Expand Down Expand Up @@ -137,6 +140,7 @@ function SuggestionMention(
setSuggestionValues((prevState) => ({
...prevState,
suggestedMentions: [],
shouldShowSuggestionMenu: false,
}));
},
[
Expand Down Expand Up @@ -323,10 +327,11 @@ function SuggestionMention(

const shouldDisplayRoomMentionsSuggestions = isGroupPolicyReport && (isValidRoomName(suggestionWord.toLowerCase()) || prefix === '');
if (prefixType === '#' && shouldDisplayRoomMentionsSuggestions) {
// filter reports by room name and current policy
const filteredRoomMentions = getRoomMentionOptions(prefix, reports);
nextState.suggestedMentions = filteredRoomMentions;
nextState.shouldShowSuggestionMenu = !!filteredRoomMentions.length;
// Filter reports by room name and current policy
nextState.suggestedMentions = getRoomMentionOptions(prefix, reports);

// Even if there are no reports, we should show the suggestion menu - to perform live search
nextState.shouldShowSuggestionMenu = true;
}

setSuggestionValues((prevState) => ({
Expand All @@ -342,6 +347,10 @@ function SuggestionMention(
calculateMentionSuggestion(selection.end);
}, [selection, calculateMentionSuggestion]);

useEffect(() => {
debouncedSearchInServer();
}, [suggestionValues.suggestedMentions.length, suggestionValues.prefixType, policyID, value, debouncedSearchInServer]);

const updateShouldShowSuggestionMenuToFalse = useCallback(() => {
setSuggestionValues((prevState) => {
if (prevState.shouldShowSuggestionMenu) {
Expand Down Expand Up @@ -390,8 +399,4 @@ function SuggestionMention(

SuggestionMention.displayName = 'SuggestionMention';

export default withOnyx<SuggestionProps & RoomMentionOnyxProps & RefAttributes<SuggestionsRef>, RoomMentionOnyxProps>({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
},
})(forwardRef(SuggestionMention));
export default forwardRef(SuggestionMention);

0 comments on commit f05d306

Please sign in to comment.