diff --git a/cspell.json b/cspell.json index 82a172987070..a898e4068f47 100644 --- a/cspell.json +++ b/cspell.json @@ -50,6 +50,7 @@ "asar", "ASPAC", "assetlinks", + "accountid", "attributes.accountid", "attributes.reportid", "authorised", diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 9917965054fb..796b04afef44 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -145,6 +145,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim 'mention-report': HTMLElementModel.fromCustomModel({tagName: 'mention-report', contentModel: HTMLContentModel.textual}), 'mention-here': HTMLElementModel.fromCustomModel({tagName: 'mention-here', contentModel: HTMLContentModel.textual}), 'mention-short': HTMLElementModel.fromCustomModel({tagName: 'mention-short', contentModel: HTMLContentModel.textual}), + 'user-details': HTMLElementModel.fromCustomModel({tagName: 'user-details', contentModel: HTMLContentModel.textual}), 'copy-text': HTMLElementModel.fromCustomModel({tagName: 'copy-text', contentModel: HTMLContentModel.textual}), 'concierge-link': HTMLElementModel.fromCustomModel({tagName: 'concierge-link', contentModel: HTMLContentModel.textual}), 'account-manager-link': HTMLElementModel.fromCustomModel({tagName: 'account-manager-link', contentModel: HTMLContentModel.textual}), diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx new file mode 100644 index 000000000000..fe30cc678525 --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/UserDetailsRenderer.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; +import {TNodeChildrenRenderer} from 'react-native-render-html'; +import Text from '@components/Text'; +import UserDetailsTooltip from '@components/UserDetailsTooltip'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import {isOptimisticPersonalDetail} from '@libs/ReportUtils'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type UserDetailsRendererProps = CustomRendererProps; + +function UserDetailsRenderer({tnode, ...defaultRendererProps}: UserDetailsRendererProps) { + const styles = useThemeStyles(); + const accountID = tnode.attributes.accountid ? parseInt(tnode.attributes.accountid, 10) : undefined; + + if (!accountID) { + // Fallback: render without tooltip if no accountID + return ; + } + + const isOptimistic = isOptimisticPersonalDetail(accountID); + + return ( + + {isOptimistic ? ( + + + + ) : ( + Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} + suppressHighlighting + role={CONST.ROLE.LINK} + > + + + )} + + ); +} + +UserDetailsRenderer.displayName = 'UserDetailsRenderer'; + +export default UserDetailsRenderer; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts index 027cca76fdef..b6b2e1192664 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts +++ b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts @@ -16,6 +16,7 @@ import PreRenderer from './PreRenderer'; import RBRRenderer from './RBRRenderer'; import ShortMentionRenderer from './ShortMentionRenderer'; import TaskTitleRenderer from './TaskTitleRenderer'; +import UserDetailsRenderer from './UserDetailsRenderer'; import VideoRenderer from './VideoRenderer'; /** @@ -38,6 +39,7 @@ const HTMLEngineProviderComponentList: CustomTagRendererRecord = { 'mention-report': MentionReportRenderer, 'mention-here': MentionHereRenderer, 'mention-short': ShortMentionRenderer, + 'user-details': UserDetailsRenderer, 'copy-text': CopyTextRenderer, emoji: EmojiRenderer, 'next-step-email': NextStepEmailRenderer, diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index 47138ed0f59e..64d24970e59c 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -11,14 +11,11 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import { - getDisplayNamesWithTooltips, getParticipantsAccountIDsForDisplay, getPolicyName, getReportName, isChatRoom as isChatRoomReportUtils, - isConciergeChatReport, isInvoiceRoom as isInvoiceRoomReportUtils, - isOptimisticPersonalDetail, isPolicyExpenseChat as isPolicyExpenseChatReportUtils, isSelfDM as isSelfDMReportUtils, isSystemChat as isSystemChatReportUtils, @@ -32,7 +29,6 @@ import ROUTES from '@src/ROUTES'; import type {OnyxInputOrEntry, PersonalDetails, PersonalDetailsList, Policy, Report} from '@src/types/onyx'; import RenderHTML from './RenderHTML'; import Text from './Text'; -import UserDetailsTooltip from './UserDetailsTooltip'; type ReportWelcomeTextProps = { /** The report currently being looked at */ @@ -68,12 +64,6 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { const isSystemChat = isSystemChatReportUtils(report); const isDefault = !(isChatRoom || isPolicyExpenseChat || isSelfDM || isSystemChat); const participantAccountIDs = getParticipantsAccountIDsForDisplay(report, undefined, true, true, reportMetadata); - const isMultipleParticipant = participantAccountIDs.length > 1; - const displayNamesWithTooltips = getDisplayNamesWithTooltips( - getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails as OnyxInputOrEntry), - isMultipleParticipant, - localeCompare, - ); const moneyRequestOptions = temporary_getMoneyRequestOptions(report, policy, participantAccountIDs, isReportArchived, isRestrictedToPreferredPolicy); const policyName = getPolicyName({report}); @@ -135,7 +125,16 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { const participantPersonalDetailListExcludeCurrentUser = Object.values( getPersonalDetailsForAccountIDs(participantAccountIDsExcludeCurrentUser, personalDetails as OnyxInputOrEntry), ); - const welcomeMessage = SidebarUtils.getWelcomeMessage(report, policy, participantPersonalDetailListExcludeCurrentUser, localeCompare, isReportArchived, reportDetailsLink); + const welcomeMessage = SidebarUtils.getWelcomeMessage( + report, + policy, + participantPersonalDetailListExcludeCurrentUser, + localeCompare, + isReportArchived, + reportDetailsLink, + shouldShowUsePlusButtonText, + additionalText, + ); return ( <> @@ -159,34 +158,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) { {welcomeMessage.messageText} )} - {isDefault && displayNamesWithTooltips.length > 0 && ( - - {welcomeMessage.phrase1} - {displayNamesWithTooltips.map(({displayName, accountID}, index) => ( - // eslint-disable-next-line react/no-array-index-key - - - {isOptimisticPersonalDetail(accountID) ? ( - {displayName} - ) : ( - Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()))} - suppressHighlighting - > - {displayName} - - )} - - {index === displayNamesWithTooltips.length - 1 && .} - {index === displayNamesWithTooltips.length - 2 && {`${displayNamesWithTooltips.length > 2 ? ',' : ''} ${translate('common.and')} `}} - {index < displayNamesWithTooltips.length - 2 && , } - - ))} - {shouldShowUsePlusButtonText && {translate('reportActionsView.usePlusButton', {additionalText})}} - {isConciergeChatReport(report) && {translate('reportActionsView.askConcierge')}} - - )} + {isDefault && !!welcomeMessage.messageHtml && } ); diff --git a/src/languages/de.ts b/src/languages/de.ts index 2f782f98916c..d6e9f610dcb1 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -955,7 +956,7 @@ const translations: TranslationDeepObject = { `Dieser Chatraum ist für alles, was mit ${reportName} zu tun hat.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `Dieser Chat ist für Rechnungen zwischen ${invoicePayer} und ${invoiceReceiver}. Verwenden Sie die Schaltfläche +, um eine Rechnung zu senden.`, - beginningOfChatHistory: 'Dieser Chat ist mit', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Dieser Chat ist mit ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `Hier wird ${submitterDisplayName} die Ausgaben an ${workspaceName} übermitteln. Verwenden Sie einfach die Schaltfläche +.`, beginningOfChatHistorySelfDM: 'Dies ist Ihr persönlicher Bereich. Nutzen Sie ihn für Notizen, Aufgaben, Entwürfe und Erinnerungen.', diff --git a/src/languages/en.ts b/src/languages/en.ts index d40bb790730c..1c32c56f59e3 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -29,6 +29,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -944,7 +945,7 @@ const translations = { `This chat room is for anything ${reportName} related.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `This chat is for invoices between ${invoicePayer} and ${invoiceReceiver}. Use the + button to send an invoice.`, - beginningOfChatHistory: 'This chat is with ', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `This chat is with ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `This is where ${submitterDisplayName} will submit expenses to ${workspaceName}. Just use the + button.`, beginningOfChatHistorySelfDM: 'This is your personal space. Use it for notes, tasks, drafts, and reminders.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 66f21edb3f2d..ca44a0d5b3bf 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2,7 +2,7 @@ import {CONST as COMMON_CONST} from 'expensify-common'; import dedent from '@libs/StringUtils/dedent'; import CONST from '@src/CONST'; import type en from './en'; -import type {TagSelectionParams, ViolationsRterParams} from './params'; +import type {BeginningOfChatHistoryParams, TagSelectionParams, ViolationsRterParams} from './params'; import type {TranslationDeepObject} from './types'; /* eslint-disable max-len */ @@ -608,7 +608,7 @@ const translations: TranslationDeepObject = { `Esta sala de chat es para cualquier cosa relacionada con ${reportName}.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}) => `Este chat es para facturas entre ${invoicePayer} y ${invoiceReceiver}. Usa el botón + para enviar una factura.`, - beginningOfChatHistory: 'Este chat es con ', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Este chat es con ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}) => `Aquí es donde ${submitterDisplayName} enviará los gastos al espacio de trabajo ${workspaceName}. Solo usa el botón +.`, beginningOfChatHistorySelfDM: 'Este es tu espacio personal. Úsalo para notas, tareas, borradores y recordatorios.', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 71af0fe255d2..4fa666a97c2a 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -957,7 +958,7 @@ const translations: TranslationDeepObject = { `Ce salon de discussion est destiné à tout ce qui concerne ${reportName}.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `Ce chat concerne les factures entre ${invoicePayer} et ${invoiceReceiver}. Utilisez le bouton + pour envoyer une facture.`, - beginningOfChatHistory: 'Ce chat est avec', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Ce chat est avec ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `C'est ici que ${submitterDisplayName} soumettra ses dépenses à ${workspaceName}. Il suffit d'utiliser le bouton +.`, beginningOfChatHistorySelfDM: "C'est votre espace personnel. Utilisez-le pour des notes, des tâches, des brouillons et des rappels.", diff --git a/src/languages/it.ts b/src/languages/it.ts index fd529cf3f383..fa2a8ae21e75 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -954,7 +955,7 @@ const translations: TranslationDeepObject = { `Questa chat è per tutto ciò che riguarda ${reportName}.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `Questa chat è per le fatture tra ${invoicePayer} e ${invoiceReceiver}. Utilizzare il pulsante + per inviare una fattura.`, - beginningOfChatHistory: 'Questa chat è con', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Questa chat è con ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `È qui che ${submitterDisplayName} presenterà le spese a ${workspaceName}. Basta usare il pulsante +.`, beginningOfChatHistorySelfDM: 'Questo è il tuo spazio personale. Usalo per appunti, compiti, bozze e promemoria.', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 1761af92a476..820ab28d068c 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -955,7 +956,7 @@ const translations: TranslationDeepObject = { `このチャットルームは、${reportName}に関することなら何でもどうぞ。`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `このチャットは、${invoicePayer}${invoiceReceiver}間の請求書用です。請求書を送信するには、+ ボタンを使用してください。`, - beginningOfChatHistory: 'このチャットは', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `このチャットは${users}とのチャットです。`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `ここで${submitterDisplayName}${workspaceName}に経費を提出します。+ボタンをクリックしてください。`, beginningOfChatHistorySelfDM: 'これはあなたの個人スペースです。メモ、タスク、下書き、リマインダーに使用してください。', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index db08d1f1b209..4f1d569ce93c 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -953,7 +954,7 @@ const translations: TranslationDeepObject = { `Deze chatroom is voor alles wat met ${reportName} te maken heeft.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `Deze chat is voor facturen tussen ${invoicePayer} en ${invoiceReceiver}. Gebruik de + knop om een factuur te sturen.`, - beginningOfChatHistory: 'Deze chat is met', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Deze chat is met ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `Dit is waar ${submitterDisplayName} kosten zal indienen bij ${workspaceName}. Gebruik gewoon de + knop.`, beginningOfChatHistorySelfDM: 'Dit is je persoonlijke ruimte. Gebruik het voor notities, taken, concepten en herinneringen.', diff --git a/src/languages/params.ts b/src/languages/params.ts index 03d15acf7b86..b4c7af96b624 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -89,6 +89,10 @@ type BeginningOfChatHistoryInvoiceRoomParams = { invoiceReceiver: string; }; +type BeginningOfChatHistoryParams = { + users: string; +}; + type LearnMoreRouteParams = { learnMoreMethodsRoute: string; formattedPrice: string; @@ -1051,6 +1055,7 @@ export type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfArchivedRoomParams, BeginningOfChatHistoryUserRoomParams, BeginningOfChatHistoryAnnounceRoomPartTwo, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index a0dfa5ecfad9..fab43816ff49 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -954,7 +955,7 @@ const translations: TranslationDeepObject = { `Ten czat jest przeznaczony do wszystkiego, co związane z ${reportName}.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `Ten czat służy do wystawiania faktur między ${invoicePayer} i ${invoiceReceiver}. Użyj przycisku +, aby wysłać fakturę.`, - beginningOfChatHistory: 'Ta rozmowa jest z', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Ta rozmowa jest z ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `W tym miejscu ${submitterDisplayName} będzie przesyłać wydatki do ${workspaceName}. Wystarczy użyć przycisku +.`, beginningOfChatHistorySelfDM: 'To jest Twoja przestrzeń osobista. Używaj jej do notatek, zadań, szkiców i przypomnień.', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 28b8fe1d7596..e38c27611e06 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -952,7 +953,7 @@ const translations: TranslationDeepObject = { `Esta sala de bate-papo é para qualquer coisa relacionada ao ${reportName}.`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `Este bate-papo é para faturas entre ${invoicePayer} e a ${invoiceReceiver}. Use o botão + para enviar uma fatura.`, - beginningOfChatHistory: 'Este chat é com', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `Este chat é com ${users}.`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `É aqui que ${submitterDisplayName} enviará as despesas para a ${workspaceName}. Basta usar o botão +.`, beginningOfChatHistorySelfDM: 'Este é o seu espaço pessoal. Use-o para anotações, tarefas, rascunhos e lembretes.', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 02552e1d57af..c2463fac08e0 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -40,6 +40,7 @@ import type { BeginningOfChatHistoryAnnounceRoomParams, BeginningOfChatHistoryDomainRoomParams, BeginningOfChatHistoryInvoiceRoomParams, + BeginningOfChatHistoryParams, BeginningOfChatHistoryPolicyExpenseChatParams, BeginningOfChatHistoryUserRoomParams, BillableDefaultDescriptionParams, @@ -946,7 +947,7 @@ const translations: TranslationDeepObject = { `本聊天室用于与 ${reportName} 有关的任何内容。`, beginningOfChatHistoryInvoiceRoom: ({invoicePayer, invoiceReceiver}: BeginningOfChatHistoryInvoiceRoomParams) => `该聊天用于 ${invoicePayer}${invoiceReceiver} 之间的发票。使用 + 按钮发送发票。`, - beginningOfChatHistory: '此聊天是与', + beginningOfChatHistory: ({users}: BeginningOfChatHistoryParams) => `此聊天是与${users}的聊天。`, beginningOfChatHistoryPolicyExpenseChat: ({workspaceName, submitterDisplayName}: BeginningOfChatHistoryPolicyExpenseChatParams) => `这是${submitterDisplayName}${workspaceName} 提交费用的地方。使用 + 按钮即可。`, beginningOfChatHistorySelfDM: '这是您的个人空间。用于记录笔记、任务、草稿和提醒。', diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 3ba24b41e9c9..e78b6d9b37a3 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -163,10 +163,6 @@ type MiniReport = { lastVisibleActionCreated?: string; }; -function ensureSingleSpacing(text: string) { - return text.replaceAll(CONST.REGEX.WHITESPACE, ' ').trim(); -} - function shouldDisplayReportInLHN( report: Report, reports: OnyxCollection, @@ -1016,6 +1012,8 @@ function getWelcomeMessage( localeCompare: LocaleContextProps['localeCompare'], isReportArchived = false, reportDetailsLink = '', + shouldShowUsePlusButtonText = false, + additionalText = '', ): WelcomeMessage { const welcomeMessage: WelcomeMessage = {}; if (isChatThread(report) || isTaskReport(report)) { @@ -1052,31 +1050,46 @@ function getWelcomeMessage( welcomeMessage.messageText = translateLocal('reportActionsView.beginningOfChatHistorySystemDM'); return welcomeMessage; } - // eslint-disable-next-line @typescript-eslint/no-deprecated - welcomeMessage.phrase1 = translateLocal('reportActionsView.beginningOfChatHistory'); const isMultipleParticipant = participantPersonalDetailList.length > 1; const displayNamesWithTooltips = getDisplayNamesWithTooltips(participantPersonalDetailList, isMultipleParticipant, localeCompare); - const displayNamesWithTooltipsText = displayNamesWithTooltips - .map(({displayName}, index) => { - if (index === displayNamesWithTooltips.length - 1) { - return `${displayName}.`; + + // Build HTML string with user-details tags and proper grammar + const usersHtml = displayNamesWithTooltips + .map(({displayName, accountID}, index) => { + const participantCount = displayNamesWithTooltips.length; + const userTag = `${displayName ?? ''}`; + + // Add grammar (commas, "and", period) + if (index === participantCount - 1) { + return userTag; } - if (index === displayNamesWithTooltips.length - 2) { - if (displayNamesWithTooltips.length > 2) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - return `${displayName}, ${translateLocal('common.and')}`; - } + if (index === participantCount - 2) { // eslint-disable-next-line @typescript-eslint/no-deprecated - return `${displayName} ${translateLocal('common.and')}`; - } - if (index < displayNamesWithTooltips.length - 2) { - return `${displayName},`; + return `${userTag}${participantCount > 2 ? ',' : ''} ${translateLocal('common.and')} `; } - return ''; + return `${userTag}, `; }) - .join(' '); + .join(''); + + if (!displayNamesWithTooltips.length) { + return welcomeMessage; + } + + // eslint-disable-next-line @typescript-eslint/no-deprecated + let messageHtml = translateLocal('reportActionsView.beginningOfChatHistory', {users: usersHtml}); - welcomeMessage.messageText = displayNamesWithTooltips.length ? ensureSingleSpacing(`${welcomeMessage.phrase1} ${displayNamesWithTooltipsText}`) : ''; + // Append additional text for plus button or Concierge + if (shouldShowUsePlusButtonText) { + // eslint-disable-next-line @typescript-eslint/no-deprecated + messageHtml += translateLocal('reportActionsView.usePlusButton', {additionalText}); + } + if (isConciergeChatReport(report)) { + // eslint-disable-next-line @typescript-eslint/no-deprecated + messageHtml += translateLocal('reportActionsView.askConcierge'); + } + + welcomeMessage.messageHtml = messageHtml; + welcomeMessage.messageText = Parser.htmlToText(welcomeMessage.messageHtml); return welcomeMessage; } diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index d2eec57e4b05..2cc28a51b533 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -874,11 +874,85 @@ describe('SidebarUtils', () => { ) .then(() => { const result = SidebarUtils.getWelcomeMessage(MOCK_REPORT, undefined, participantPersonalDetailList, localeCompare); - expect(result.messageText).toBe('This chat is with One and Two.'); + expect(result.messageHtml).toContain('This chat is with'); + expect(result.messageHtml).toContain(''); + expect(result.messageHtml).toContain(''); + expect(result.messageHtml).toContain(' and'); }) ); }); + it('returns correct messageText for a single user DM chat', async () => { + const MOCK_REPORT: Report = { + ...LHNTestUtils.getFakeReport(), + chatType: undefined, + type: 'chat', + }; + const participantPersonalDetailList: PersonalDetails[] = [ + {accountID: 1, displayName: 'Email One', avatar: 'https://example.com/one.png', login: 'email1@test.com'} as unknown as PersonalDetails, + ]; + + await waitForBatchedUpdates(); + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + }); + }); + + const result = SidebarUtils.getWelcomeMessage(MOCK_REPORT, undefined, participantPersonalDetailList, localeCompare); + expect(result.messageText).toBe('This chat is with Email One.'); + expect(result.messageHtml).toContain('Email One'); + }); + + it('returns correct messageText for two users in a group chat', async () => { + const MOCK_REPORT: Report = { + ...LHNTestUtils.getFakeReport(), + chatType: 'group', + type: 'chat', + }; + const participantPersonalDetailList: PersonalDetails[] = [ + {accountID: 1, displayName: 'Email One', avatar: 'https://example.com/one.png', login: 'email1@test.com'} as unknown as PersonalDetails, + {accountID: 2, displayName: 'Email Two', avatar: 'https://example.com/two.png', login: 'email2@test.com'} as unknown as PersonalDetails, + ]; + + await waitForBatchedUpdates(); + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + }); + }); + + const result = SidebarUtils.getWelcomeMessage(MOCK_REPORT, undefined, participantPersonalDetailList, localeCompare); + expect(result.messageText).toMatch(/^This chat is with .+ and .+\.$/); + expect(result.messageText).toContain(' and '); + expect(result.messageText).not.toContain(' { + const MOCK_REPORT: Report = { + ...LHNTestUtils.getFakeReport(), + chatType: 'group', + type: 'chat', + }; + const participantPersonalDetailList: PersonalDetails[] = [ + {accountID: 1, displayName: 'Email One', avatar: 'https://example.com/one.png', login: 'email1@test.com'} as unknown as PersonalDetails, + {accountID: 2, displayName: 'Email Two', avatar: 'https://example.com/two.png', login: 'email2@test.com'} as unknown as PersonalDetails, + {accountID: 3, displayName: 'Email Three', avatar: 'https://example.com/three.png', login: 'email3@test.com'} as unknown as PersonalDetails, + ]; + + await waitForBatchedUpdates(); + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + }); + }); + + const result = SidebarUtils.getWelcomeMessage(MOCK_REPORT, undefined, participantPersonalDetailList, localeCompare); + expect(result.messageText).toMatch(/^This chat is with .+, .+, and .+\.$/); + expect(result.messageText).toContain(', and '); + expect(result.messageText).not.toContain(' { const MOCK_REPORT: Report = { ...LHNTestUtils.getFakeReport(),