From 750ac4886592330f7eef581fc96c15a4f53a47a9 Mon Sep 17 00:00:00 2001
From: Sam Hariri <137707942+samh-nl@users.noreply.github.com>
Date: Tue, 5 Sep 2023 14:14:57 +0200
Subject: [PATCH 1/5] feat: support accountid attribute in user mentions

---
 .../HTMLRenderers/MentionUserRenderer.js      | 58 +++++++++++++------
 1 file changed, 39 insertions(+), 19 deletions(-)

diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 7f7753dfea44..992f8494dc1f 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -1,6 +1,8 @@
 import React from 'react';
 import _ from 'underscore';
 import {TNodeChildrenRenderer} from 'react-native-render-html';
+import {withOnyx} from 'react-native-onyx';
+import lodashGet from 'lodash/get';
 import Navigation from '../../../libs/Navigation/Navigation';
 import ROUTES from '../../../ROUTES';
 import Text from '../../Text';
@@ -10,52 +12,63 @@ import withCurrentUserPersonalDetails from '../../withCurrentUserPersonalDetails
 import personalDetailsPropType from '../../../pages/personalDetailsPropType';
 import * as StyleUtils from '../../../styles/StyleUtils';
 import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils';
+import compose from '../../../libs/compose';
 import TextLink from '../../TextLink';
+import ONYXKEYS from '../../../ONYXKEYS';
+import useLocalize from '../../../hooks/useLocalize';
 
 const propTypes = {
     ...htmlRendererPropTypes,
 
-    /**
-     * Current user personal details
-     */
+    /** Current user personal details */
     currentUserPersonalDetails: personalDetailsPropType.isRequired,
-};
 
-/**
- * Navigates to user details screen based on email
- * @param {String} email
- * @returns {void}
- * */
-const showUserDetails = (email) => Navigation.navigate(ROUTES.getDetailsRoute(email));
+    /** Personal details of all users */
+    personalDetails: personalDetailsPropType.isRequired,
+};
 
 function MentionUserRenderer(props) {
+    const {translate} = useLocalize();
     const defaultRendererProps = _.omit(props, ['TDefaultRenderer', 'style']);
+    const htmlAttribAccountID = lodashGet(props.tnode.attributes, 'accountid');
+
+    let accountID;
+    let displayNameOrLogin;
+    let navigationRoute;
 
-    // We need to remove the leading @ from data as it is not part of the login
-    const loginWithoutLeadingAt = props.tnode.data ? props.tnode.data.slice(1) : '';
+    if (!_.isEmpty(htmlAttribAccountID)) {
+        const user = lodashGet(props.personalDetails, htmlAttribAccountID);
+        accountID = parseInt(htmlAttribAccountID, 10);
+        displayNameOrLogin = lodashGet(user, 'login', '') || lodashGet(user, 'displayName', '') || translate('common.hidden');
+        navigationRoute = ROUTES.getProfileRoute(htmlAttribAccountID);
+    } else {
+        // We need to remove the leading @ from data as it is not part of the login
+        displayNameOrLogin = props.tnode.data ? props.tnode.data.slice(1) : '';
 
-    const accountID = _.first(PersonalDetailsUtils.getAccountIDsByLogins([loginWithoutLeadingAt]));
+        accountID = _.first(PersonalDetailsUtils.getAccountIDsByLogins([displayNameOrLogin]));
+        navigationRoute = ROUTES.getDetailsRoute(displayNameOrLogin);
+    }
 
-    const isOurMention = loginWithoutLeadingAt === props.currentUserPersonalDetails.login;
+    const isOurMention = accountID === props.currentUserPersonalDetails.accountID;
 
     return (
         <Text>
             <UserDetailsTooltip
                 accountID={accountID}
                 fallbackUserDetails={{
-                    displayName: loginWithoutLeadingAt,
+                    displayName: displayNameOrLogin,
                 }}
             >
                 <TextLink
                     // eslint-disable-next-line react/jsx-props-no-spreading
                     {...defaultRendererProps}
-                    href={ROUTES.getDetailsRoute(loginWithoutLeadingAt)}
+                    href={`/${navigationRoute}`}
                     style={[_.omit(props.style, 'color'), StyleUtils.getMentionStyle(isOurMention), {color: StyleUtils.getMentionTextColor(isOurMention)}]}
-                    onPress={() => showUserDetails(loginWithoutLeadingAt)}
+                    onPress={() => Navigation.navigate(navigationRoute)}
                     // Add testID so it is NOT selected as an anchor tag by SelectionScraper
                     testID="span"
                 >
-                    <TNodeChildrenRenderer tnode={props.tnode} />
+                    {!_.isEmpty(htmlAttribAccountID) ? `@${displayNameOrLogin}` : <TNodeChildrenRenderer tnode={props.tnode} />}
                 </TextLink>
             </UserDetailsTooltip>
         </Text>
@@ -65,4 +78,11 @@ function MentionUserRenderer(props) {
 MentionUserRenderer.propTypes = propTypes;
 MentionUserRenderer.displayName = 'MentionUserRenderer';
 
-export default withCurrentUserPersonalDetails(MentionUserRenderer);
+export default compose(
+    withCurrentUserPersonalDetails,
+    withOnyx({
+        personalDetails: {
+            key: ONYXKEYS.PERSONAL_DETAILS_LIST,
+        },
+    }),
+)(MentionUserRenderer);

From 19faf7deb3ed7043108a3ad7162fcbd90bb45901 Mon Sep 17 00:00:00 2001
From: Sam Hariri <137707942+samh-nl@users.noreply.github.com>
Date: Wed, 6 Sep 2023 12:52:57 +0200
Subject: [PATCH 2/5] feat: remove navigation and tooltip when hidden

---
 .../HTMLRenderers/MentionUserRenderer.js      | 29 ++++++++++++++-----
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 992f8494dc1f..087334061fa2 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -10,6 +10,7 @@ import UserDetailsTooltip from '../../UserDetailsTooltip';
 import htmlRendererPropTypes from './htmlRendererPropTypes';
 import withCurrentUserPersonalDetails from '../../withCurrentUserPersonalDetails';
 import personalDetailsPropType from '../../../pages/personalDetailsPropType';
+import styles from '../../../styles/styles';
 import * as StyleUtils from '../../../styles/StyleUtils';
 import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils';
 import compose from '../../../libs/compose';
@@ -39,8 +40,13 @@ function MentionUserRenderer(props) {
     if (!_.isEmpty(htmlAttribAccountID)) {
         const user = lodashGet(props.personalDetails, htmlAttribAccountID);
         accountID = parseInt(htmlAttribAccountID, 10);
-        displayNameOrLogin = lodashGet(user, 'login', '') || lodashGet(user, 'displayName', '') || translate('common.hidden');
-        navigationRoute = ROUTES.getProfileRoute(htmlAttribAccountID);
+        displayNameOrLogin = lodashGet(user, 'login', '') || lodashGet(user, 'displayName', '');
+
+        if (_.isEmpty(displayNameOrLogin)) {
+            displayNameOrLogin = translate('common.hidden');
+        } else {
+            navigationRoute = ROUTES.getProfileRoute(htmlAttribAccountID);
+        }
     } else {
         // We need to remove the leading @ from data as it is not part of the login
         displayNameOrLogin = props.tnode.data ? props.tnode.data.slice(1) : '';
@@ -50,26 +56,33 @@ function MentionUserRenderer(props) {
     }
 
     const isOurMention = accountID === props.currentUserPersonalDetails.accountID;
+    const TextLinkComponent = _.isEmpty(navigationRoute) ? Text : TextLink;
 
     return (
         <Text>
             <UserDetailsTooltip
                 accountID={accountID}
                 fallbackUserDetails={{
-                    displayName: displayNameOrLogin,
+                    // If there is no navigation route (i.e. user is hidden), empty the displayName here to ensure the tooltip is removed
+                    displayName: _.isEmpty(navigationRoute) ? '' : displayNameOrLogin,
                 }}
             >
-                <TextLink
+                <TextLinkComponent
                     // eslint-disable-next-line react/jsx-props-no-spreading
                     {...defaultRendererProps}
-                    href={`/${navigationRoute}`}
-                    style={[_.omit(props.style, 'color'), StyleUtils.getMentionStyle(isOurMention), {color: StyleUtils.getMentionTextColor(isOurMention)}]}
-                    onPress={() => Navigation.navigate(navigationRoute)}
+                    style={[
+                        _.omit(props.style, 'color'),
+                        StyleUtils.getMentionStyle(isOurMention),
+                        {color: StyleUtils.getMentionTextColor(isOurMention)},
+                        _.isEmpty(navigationRoute) && styles.cursorDefault,
+                    ]}
+                    href={_.isEmpty(navigationRoute) ? undefined : `/${navigationRoute}`}
+                    onPress={_.isEmpty(navigationRoute) ? undefined : () => Navigation.navigate(navigationRoute)}
                     // Add testID so it is NOT selected as an anchor tag by SelectionScraper
                     testID="span"
                 >
                     {!_.isEmpty(htmlAttribAccountID) ? `@${displayNameOrLogin}` : <TNodeChildrenRenderer tnode={props.tnode} />}
-                </TextLink>
+                </TextLinkComponent>
             </UserDetailsTooltip>
         </Text>
     );

From 2af817dfd68a3d0b34b50a52de8c34edb58ba515 Mon Sep 17 00:00:00 2001
From: Sam Hariri <137707942+samh-nl@users.noreply.github.com>
Date: Wed, 6 Sep 2023 14:28:07 +0200
Subject: [PATCH 3/5] fix: ensure proptype is adhered to

---
 .../HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 087334061fa2..155596fb23b0 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -74,7 +74,7 @@ function MentionUserRenderer(props) {
                         _.omit(props.style, 'color'),
                         StyleUtils.getMentionStyle(isOurMention),
                         {color: StyleUtils.getMentionTextColor(isOurMention)},
-                        _.isEmpty(navigationRoute) && styles.cursorDefault,
+                        _.isEmpty(navigationRoute) ? styles.cursorDefault : {},
                     ]}
                     href={_.isEmpty(navigationRoute) ? undefined : `/${navigationRoute}`}
                     onPress={_.isEmpty(navigationRoute) ? undefined : () => Navigation.navigate(navigationRoute)}

From 942b3268fca17392e5d71fc73c046c2eb48234ce Mon Sep 17 00:00:00 2001
From: Sam Hariri <137707942+samh-nl@users.noreply.github.com>
Date: Mon, 18 Sep 2023 15:45:57 +0200
Subject: [PATCH 4/5] Revert "feat: remove navigation and tooltip when hidden"

This reverts commit 19faf7deb3ed7043108a3ad7162fcbd90bb45901.
---
 .../HTMLRenderers/MentionUserRenderer.js      | 29 +++++--------------
 1 file changed, 8 insertions(+), 21 deletions(-)

diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 155596fb23b0..992f8494dc1f 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -10,7 +10,6 @@ import UserDetailsTooltip from '../../UserDetailsTooltip';
 import htmlRendererPropTypes from './htmlRendererPropTypes';
 import withCurrentUserPersonalDetails from '../../withCurrentUserPersonalDetails';
 import personalDetailsPropType from '../../../pages/personalDetailsPropType';
-import styles from '../../../styles/styles';
 import * as StyleUtils from '../../../styles/StyleUtils';
 import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils';
 import compose from '../../../libs/compose';
@@ -40,13 +39,8 @@ function MentionUserRenderer(props) {
     if (!_.isEmpty(htmlAttribAccountID)) {
         const user = lodashGet(props.personalDetails, htmlAttribAccountID);
         accountID = parseInt(htmlAttribAccountID, 10);
-        displayNameOrLogin = lodashGet(user, 'login', '') || lodashGet(user, 'displayName', '');
-
-        if (_.isEmpty(displayNameOrLogin)) {
-            displayNameOrLogin = translate('common.hidden');
-        } else {
-            navigationRoute = ROUTES.getProfileRoute(htmlAttribAccountID);
-        }
+        displayNameOrLogin = lodashGet(user, 'login', '') || lodashGet(user, 'displayName', '') || translate('common.hidden');
+        navigationRoute = ROUTES.getProfileRoute(htmlAttribAccountID);
     } else {
         // We need to remove the leading @ from data as it is not part of the login
         displayNameOrLogin = props.tnode.data ? props.tnode.data.slice(1) : '';
@@ -56,33 +50,26 @@ function MentionUserRenderer(props) {
     }
 
     const isOurMention = accountID === props.currentUserPersonalDetails.accountID;
-    const TextLinkComponent = _.isEmpty(navigationRoute) ? Text : TextLink;
 
     return (
         <Text>
             <UserDetailsTooltip
                 accountID={accountID}
                 fallbackUserDetails={{
-                    // If there is no navigation route (i.e. user is hidden), empty the displayName here to ensure the tooltip is removed
-                    displayName: _.isEmpty(navigationRoute) ? '' : displayNameOrLogin,
+                    displayName: displayNameOrLogin,
                 }}
             >
-                <TextLinkComponent
+                <TextLink
                     // eslint-disable-next-line react/jsx-props-no-spreading
                     {...defaultRendererProps}
-                    style={[
-                        _.omit(props.style, 'color'),
-                        StyleUtils.getMentionStyle(isOurMention),
-                        {color: StyleUtils.getMentionTextColor(isOurMention)},
-                        _.isEmpty(navigationRoute) ? styles.cursorDefault : {},
-                    ]}
-                    href={_.isEmpty(navigationRoute) ? undefined : `/${navigationRoute}`}
-                    onPress={_.isEmpty(navigationRoute) ? undefined : () => Navigation.navigate(navigationRoute)}
+                    href={`/${navigationRoute}`}
+                    style={[_.omit(props.style, 'color'), StyleUtils.getMentionStyle(isOurMention), {color: StyleUtils.getMentionTextColor(isOurMention)}]}
+                    onPress={() => Navigation.navigate(navigationRoute)}
                     // Add testID so it is NOT selected as an anchor tag by SelectionScraper
                     testID="span"
                 >
                     {!_.isEmpty(htmlAttribAccountID) ? `@${displayNameOrLogin}` : <TNodeChildrenRenderer tnode={props.tnode} />}
-                </TextLinkComponent>
+                </TextLink>
             </UserDetailsTooltip>
         </Text>
     );

From 8e5aba99f32d40c7db244dec9f1bdd6ce5140dfb Mon Sep 17 00:00:00 2001
From: Sam Hariri <137707942+samh-nl@users.noreply.github.com>
Date: Mon, 18 Sep 2023 16:47:48 +0200
Subject: [PATCH 5/5] feat: don't render when no account id or email

---
 .../HTMLRenderers/MentionUserRenderer.js                   | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
index 992f8494dc1f..ccace94142d5 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js
@@ -41,12 +41,15 @@ function MentionUserRenderer(props) {
         accountID = parseInt(htmlAttribAccountID, 10);
         displayNameOrLogin = lodashGet(user, 'login', '') || lodashGet(user, 'displayName', '') || translate('common.hidden');
         navigationRoute = ROUTES.getProfileRoute(htmlAttribAccountID);
-    } else {
+    } else if (!_.isEmpty(props.tnode.data)) {
         // We need to remove the leading @ from data as it is not part of the login
-        displayNameOrLogin = props.tnode.data ? props.tnode.data.slice(1) : '';
+        displayNameOrLogin = props.tnode.data.slice(1);
 
         accountID = _.first(PersonalDetailsUtils.getAccountIDsByLogins([displayNameOrLogin]));
         navigationRoute = ROUTES.getDetailsRoute(displayNameOrLogin);
+    } else {
+        // If neither an account ID or email is provided, don't render anything
+        return null;
     }
 
     const isOurMention = accountID === props.currentUserPersonalDetails.accountID;
