Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/libs/PersonalDetailsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ type FirstAndLastName = {

let allPersonalDetails: OnyxEntry<PersonalDetailsList> = {};
let emailToPersonalDetailsCache: Record<string, PersonalDetails> = {};
let allPersonalDetailLogins: string[] = [];
Onyx.connect({
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (val) => {
const personalDetails = Object.values(val ?? {});
allPersonalDetails = val;
allPersonalDetailLogins = personalDetails.map((detail) => detail?.login ?? '');
emailToPersonalDetailsCache = personalDetails.reduce((acc: Record<string, PersonalDetails>, detail) => {
if (detail?.login) {
acc[detail.login.toLowerCase()] = detail;
Expand Down Expand Up @@ -264,6 +266,10 @@ function getPersonalDetailByEmail(email: string | undefined): PersonalDetails |
return emailToPersonalDetailsCache[email.toLowerCase()];
}

function getAllPersonalDetailLogins(): string[] {
Comment thread
adhorodyski marked this conversation as resolved.
return allPersonalDetailLogins;
}

/**
* Returns the accountID for a login only when it exists in personal details.
* Unlike getAccountIDsByLogins, does not fabricate optimistic account IDs for unknown logins.
Expand Down Expand Up @@ -625,6 +631,7 @@ export {
getParticipantsPersonalDetails,
getPersonalDetailsListByIDs,
getDisplayNameOrYou,
getAllPersonalDetailLogins,
getPersonalDetailByEmail,
getKnownAccountIDByLogin,
getAccountIDsByLogins,
Expand Down
13 changes: 9 additions & 4 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,14 @@ import Parser from './Parser';
import {getParsedMessageWithShortMentions} from './ParsingUtils';
import {getBankAccountLastFourDigits} from './PaymentUtils';
import Permissions from './Permissions';
import {getAccountIDsByLogins, getDisplayNameOrDefault, getLoginByAccountID, getPersonalDetailByEmail, temporaryGetDisplayNameOrDefault} from './PersonalDetailsUtils';
import {
getAccountIDsByLogins,
getAllPersonalDetailLogins,
getDisplayNameOrDefault,
getLoginByAccountID,
getPersonalDetailByEmail,
temporaryGetDisplayNameOrDefault,
} from './PersonalDetailsUtils';
import {
canSendInvoiceFromWorkspace,
getActivePolicies,
Expand Down Expand Up @@ -1041,7 +1048,6 @@ Onyx.connect({
});

let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry<PersonalDetails>;
Onyx.connect({
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
Expand All @@ -1050,7 +1056,6 @@ Onyx.connect({
currentUserPersonalDetails = value?.[deprecatedCurrentUserAccountID] ?? undefined;
}
allPersonalDetails = value ?? {};
allPersonalDetailLogins = Object.values(allPersonalDetails).map((personalDetail) => personalDetail?.login ?? '');
},
});

Expand Down Expand Up @@ -6537,7 +6542,7 @@ function getParsedComment(

return getParsedMessageWithShortMentions({
text,
availableMentionLogins: allPersonalDetailLogins,
availableMentionLogins: getAllPersonalDetailLogins(),
userEmailDomain,
parserOptions: {
disabledRules: isGroupPolicyReport ? [...rules] : ['reportMentions', ...rules],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import {usePersonalDetails} from '@components/OnyxListItemProvider';

import useAncestors from '@hooks/useAncestors';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDelegateAccountID from '@hooks/useDelegateAccountID';
import useIsInSidePanel from '@hooks/useIsInSidePanel';
import useOnyx from '@hooks/useOnyx';
import useShortMentionsList from '@hooks/useShortMentionsList';

import {addAttachmentWithComment, addComment, clearAgentZeroProcessingIndicator} from '@libs/actions/Report';
import {createTaskAndNavigate, setNewOptimisticAssignee} from '@libs/actions/Task';
import {isEmailPublicDomain} from '@libs/LoginUtils';
import {rand64} from '@libs/NumberUtils';
import {addDomainToShortMention} from '@libs/ParsingUtils';
import {getAllPersonalDetailLogins, getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
import {isConciergeChatReport} from '@libs/ReportUtils';
import {startSpan} from '@libs/telemetry/activeSpans';
import getSendMessageSource from '@libs/telemetry/getSendMessageSource';
Expand All @@ -36,8 +34,6 @@ import useSidePanelContext from './useSidePanelContext';

function useComposerSubmit(reportID: string) {
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const personalDetails = usePersonalDetails();
const {availableLoginsList} = useShortMentionsList();
const isInSidePanel = useIsInSidePanel();
const sidePanelContext = useSidePanelContext(reportID);
const route = useRoute();
Expand Down Expand Up @@ -110,15 +106,15 @@ function useComposerSubmit(reportID: string) {
if (taskTitle) {
const mention = taskMatch[1] ? taskMatch[1].trim() : '';
const currentUserPrivateDomain = isEmailPublicDomain(currentUserEmail) ? '' : Str.extractEmailDomain(currentUserEmail);
const mentionWithDomain = addDomainToShortMention(mention, availableLoginsList, currentUserPrivateDomain) ?? mention;
const mentionWithDomain = addDomainToShortMention(mention, getAllPersonalDetailLogins(), currentUserPrivateDomain) ?? mention;
const isValidMention = Str.isValidEmail(mentionWithDomain);

let assignee: OnyxEntry<OnyxTypes.PersonalDetails>;
let assigneeChatReport;
if (mentionWithDomain) {
if (isValidMention) {
assignee = Object.values(personalDetails ?? {}).find((value) => value?.login === mentionWithDomain) ?? undefined;
if (!Object.keys(assignee ?? {}).length) {
assignee = getPersonalDetailByEmail(mentionWithDomain);
if (!assignee) {
const optimisticDataForNewAssignee = setNewOptimisticAssignee(currentUserPersonalDetails.accountID, {
accountID: generateAccountID(mentionWithDomain),
login: mentionWithDomain,
Expand Down
53 changes: 52 additions & 1 deletion tests/ui/ReportActionComposeTest.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {act, fireEvent, render, screen, waitFor} from '@testing-library/react-native';

import ComposeProviders from '@components/ComposeProviders';
import {CurrentUserPersonalDetailsProvider} from '@components/CurrentUserPersonalDetailsProvider';
import {LocaleContextProvider} from '@components/LocaleContextProvider';
import OnyxListItemProvider from '@components/OnyxListItemProvider';
import {KeyboardStateProvider} from '@components/withKeyboardState';

import type * as TaskActions from '@libs/actions/Task';
import {createTaskAndNavigate} from '@libs/actions/Task';

import type {ReportActionComposeProps} from '@pages/inbox/report/ReportActionCompose/ReportActionCompose';
import ReportActionCompose from '@pages/inbox/report/ReportActionCompose/ReportActionCompose';
import {ReportActionEditMessageContextProvider} from '@pages/inbox/report/ReportActionEditMessageContext';
Expand All @@ -26,6 +30,11 @@ jest.mock('@libs/ComponentUtils', () => ({
forceClearInput: jest.fn(),
}));

jest.mock('@libs/actions/Task', () => ({
...jest.requireActual<typeof TaskActions>('@libs/actions/Task'),
createTaskAndNavigate: jest.fn(),
}));

jest.mock('@hooks/useLocalize', () =>
jest.fn(() => ({
translate: jest.fn((key: string) => key),
Expand Down Expand Up @@ -70,7 +79,13 @@ function ReportActionEditMessageContextProviderForReport({children}: PropsWithCh
}

function ReportScreenProviders({children}: PropsWithChildren) {
return <ComposeProviders components={[OnyxListItemProvider, LocaleContextProvider, KeyboardStateProvider, ReportActionEditMessageContextProviderForReport]}>{children}</ComposeProviders>;
return (
<ComposeProviders
components={[OnyxListItemProvider, CurrentUserPersonalDetailsProvider, LocaleContextProvider, KeyboardStateProvider, ReportActionEditMessageContextProviderForReport]}
>
{children}
</ComposeProviders>
);
}

const renderReportActionCompose = (props?: Partial<ReportActionComposeProps>) => {
Expand Down Expand Up @@ -437,4 +452,40 @@ describe('ReportActionCompose Integration Tests', () => {
unmount();
});
});

describe('Task creation with a short mention', () => {
it('assigns the task to the user resolved from a same-private-domain short mention', async () => {
// Given a current user on a private domain and a coworker on the same domain
const coworkerAccountID = 2;
await TestHelper.signInWithTestUser(1, 'user@domain.com');
await act(async () => {
await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, {
[coworkerAccountID]: TestHelper.buildPersonalDetails('mat@domain.com', coworkerAccountID, 'Mat'),
});
});

const {unmount} = renderReportActionCompose();
await waitForBatchedUpdatesWithAct();

// When a task with a short mention of the coworker is typed and submitted
// (the composer submits by clearing the input, which hands the draft to validateAndSubmitDraft)
const composer = screen.getByTestId('composer');
fireEvent.changeText(composer, '[] @mat Buy milk');
fireEvent(composer, 'clear', {nativeEvent: {text: '[] @mat Buy milk'}});

// Then the task is created with the mention resolved to the coworker's full login
await waitFor(() => {
expect(createTaskAndNavigate).toHaveBeenCalledWith(
expect.objectContaining({
title: 'Buy milk',
assigneeEmail: 'mat@domain.com',
assigneeAccountID: coworkerAccountID,
}),
);
});

unmount();
await waitForBatchedUpdatesWithAct();
});
});
});
Loading