From f38e0e5b64aebafcc94c0f4c06b2f3c46dbe5ee4 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Wed, 27 Sep 2023 11:09:34 +0200
Subject: [PATCH 01/11] Initial work

---
 src/ONYXKEYS.ts                               |  2 +-
 src/libs/ReportActionsUtils.js                |  2 +-
 src/libs/{SidebarUtils.js => SidebarUtils.ts} | 63 ++++++++-----------
 3 files changed, 29 insertions(+), 38 deletions(-)
 rename src/libs/{SidebarUtils.js => SidebarUtils.ts} (91%)

diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 6649a33fe15e..51c5fb202f00 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -380,7 +380,7 @@ type OnyxValues = {
     [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMember;
     [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: Record<string, number>;
     [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report;
-    [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportAction;
+    [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: Record<string, OnyxTypes.ReportAction>;
     [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: string;
     [ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions;
     [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string;
diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js
index 67c44784eeb2..142f072728b2 100644
--- a/src/libs/ReportActionsUtils.js
+++ b/src/libs/ReportActionsUtils.js
@@ -334,7 +334,7 @@ function isReportActionDeprecated(reportAction, key) {
  * and supported type, it's not deleted and also not closed.
  *
  * @param {Object} reportAction
- * @param {String} key
+ * @param {String | Number} key
  * @returns {Boolean}
  */
 function shouldReportActionBeVisible(reportAction, key) {
diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.ts
similarity index 91%
rename from src/libs/SidebarUtils.js
rename to src/libs/SidebarUtils.ts
index df676f23ebc7..e1251f9f50bc 100644
--- a/src/libs/SidebarUtils.js
+++ b/src/libs/SidebarUtils.ts
@@ -13,9 +13,11 @@ import * as CollectionUtils from './CollectionUtils';
 import * as LocalePhoneNumber from './LocalePhoneNumber';
 import * as UserUtils from './UserUtils';
 import * as PersonalDetailsUtils from './PersonalDetailsUtils';
+import ReportAction from '../types/onyx/ReportAction';
+
+const visibleReportActionItems: Record<string, ReportAction> = {};
+const lastReportActions: Record<string, ReportAction> = {};
 
-const visibleReportActionItems = {};
-const lastReportActions = {};
 Onyx.connect({
     key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
     callback: (actions, key) => {
@@ -24,38 +26,37 @@ Onyx.connect({
         }
         const reportID = CollectionUtils.extractCollectionItemID(key);
 
-        const actionsArray = ReportActionsUtils.getSortedReportActions(_.toArray(actions));
-        lastReportActions[reportID] = _.last(actionsArray);
+        const actionsArray: ReportAction[] = ReportActionsUtils.getSortedReportActions(Object.values(actions));
+        lastReportActions[reportID] = actionsArray[actionsArray.length - 1];
 
         // The report is only visible if it is the last action not deleted that
         // does not match a closed or created state.
-        const reportActionsForDisplay = _.filter(
-            actionsArray,
+        const reportActionsForDisplay = actionsArray.filter(
             (reportAction, actionKey) =>
                 ReportActionsUtils.shouldReportActionBeVisible(reportAction, actionKey) &&
                 reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED &&
                 reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
         );
-        visibleReportActionItems[reportID] = _.last(reportActionsForDisplay);
+        visibleReportActionItems[reportID] = reportActionsForDisplay[reportActionsForDisplay.length - 1];
     },
 });
 
 // Session can remain stale because the only way for the current user to change is to
 // sign out and sign in, which would clear out all the Onyx
 // data anyway and cause SidebarLinks to rerender.
-let currentUserAccountID;
+let currentUserAccountID: number | undefined;
 Onyx.connect({
     key: ONYXKEYS.SESSION,
-    callback: (val) => {
-        if (!val) {
+    callback: (session) => {
+        if (!session) {
             return;
         }
 
-        currentUserAccountID = val.accountID;
+        currentUserAccountID = session.accountID;
     },
 });
 
-let resolveSidebarIsReadyPromise;
+let resolveSidebarIsReadyPromise: (args?: unknown[]) => void;
 
 let sidebarIsReadyPromise = new Promise((resolve) => {
     resolveSidebarIsReadyPromise = resolve;
@@ -71,11 +72,11 @@ function isSidebarLoadedReady() {
     return sidebarIsReadyPromise;
 }
 
-function compareStringDates(stringA, stringB) {
-    if (stringA < stringB) {
+function compareStringDates(a: string, b: string) {
+    if (a < b) {
         return -1;
     }
-    if (stringA > stringB) {
+    if (a > b) {
         return 1;
     }
     return 0;
@@ -89,7 +90,7 @@ function setIsSidebarLoadedReady() {
 const reportIDsCache = new Map();
 
 // Function to set a key-value pair while maintaining the maximum key limit
-function setWithLimit(map, key, value) {
+function setWithLimit<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, value: TValue) {
     if (map.size >= 5) {
         // If the map has reached its limit, remove the first (oldest) key-value pair
         const firstKey = map.keys().next().value;
@@ -102,18 +103,11 @@ function setWithLimit(map, key, value) {
 let hasInitialReportActions = false;
 
 /**
- * @param {String} currentReportId
- * @param {Object} allReportsDict
- * @param {Object} betas
- * @param {String[]} policies
- * @param {String} priorityMode
- * @param {Object} allReportActions
- * @returns {String[]} An array of reportIDs sorted in the proper order
+ * @returns An array of reportIDs sorted in the proper order
  */
 function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions) {
     // Generate a unique cache key based on the function arguments
     const cachedReportsKey = JSON.stringify(
-        // eslint-disable-next-line es/no-optional-chaining
         [currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1],
         (key, value) => {
             /**
@@ -216,13 +210,13 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
 /**
  * Gets all the data necessary for rendering an OptionRowLHN component
  *
- * @param {Object} report
- * @param {Object} reportActions
- * @param {Object} personalDetails
- * @param {String} preferredLocale
- * @param {Object} [policy]
- * @param {Object} parentReportAction
- * @returns {Object}
+ * @param report
+ * @param reportActions
+ * @param personalDetails
+ * @param preferredLocale
+ * @param [policy]
+ * @param parentReportAction
+ * @returns
  */
 function getOptionData(report, reportActions, personalDetails, preferredLocale, policy, parentReportAction) {
     // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for
@@ -331,14 +325,11 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
               }
             : null;
     }
-    let lastMessageText =
-        hasMultipleParticipants && lastActorDetails && lastActorDetails.accountID && Number(lastActorDetails.accountID) !== currentUserAccountID ? `${lastActorDetails.displayName}: ` : '';
+    let lastMessageText = hasMultipleParticipants && lastActorDetails?.accountID && Number(lastActorDetails.accountID) !== currentUserAccountID ? `${lastActorDetails.displayName}: ` : '';
     lastMessageText += report ? lastMessageTextFromReport : '';
 
     if (result.isArchivedRoom) {
-        const archiveReason =
-            (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) ||
-            CONST.REPORT.ARCHIVE_REASON.DEFAULT;
+        const archiveReason = lastReportActions[report.reportID]?.originalMessage?.reason || CONST.REPORT.ARCHIVE_REASON.DEFAULT;
         lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, {
             displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'),
             policyName: ReportUtils.getPolicyName(report, false, policy),

From f42364d124e12225486364263fec5eef0b7c31b2 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Mon, 2 Oct 2023 12:34:47 +0200
Subject: [PATCH 02/11] [TS migration] Migrate 'SidebarUtils.js' lib to
 TypeScript

---
 src/libs/PersonalDetailsUtils.js  |   4 +-
 src/libs/ReportUtils.js           |  16 +-
 src/libs/SidebarUtils.ts          | 238 +++++++++++++++++++++---------
 src/types/onyx/PersonalDetails.ts |   2 +
 src/types/onyx/Report.ts          |   7 +-
 5 files changed, 186 insertions(+), 81 deletions(-)

diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js
index a401dea4b911..98bf171812fc 100644
--- a/src/libs/PersonalDetailsUtils.js
+++ b/src/libs/PersonalDetailsUtils.js
@@ -17,8 +17,8 @@ Onyx.connect({
 });
 
 /**
- * @param {Object} passedPersonalDetails
- * @param {Array} pathToDisplayName
+ * @param {Object | Null} passedPersonalDetails
+ * @param {Array | String} pathToDisplayName
  * @param {String} [defaultValue] optional default display name value
  * @returns {String}
  */
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index 475c1a8bcb8a..da2ef0f04ebf 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -108,9 +108,9 @@ function getPolicyType(report, policies) {
 /**
  * Get the policy name from a given report
  * @param {Object} report
- * @param {String} report.policyID
- * @param {String} report.oldPolicyName
- * @param {String} report.policyName
+ * @param {String} [report.policyID]
+ * @param {String} [report.oldPolicyName]
+ * @param {String} [report.policyName]
  * @param {Boolean} [returnEmptyIfNotFound]
  * @param {Object} [policy]
  * @returns {String}
@@ -363,7 +363,7 @@ function isUserCreatedPolicyRoom(report) {
 /**
  * Whether the provided report is a Policy Expense chat.
  * @param {Object} report
- * @param {String} report.chatType
+ * @param {String} [report.chatType]
  * @returns {Boolean}
  */
 function isPolicyExpenseChat(report) {
@@ -389,7 +389,7 @@ function isControlPolicyExpenseReport(report) {
 /**
  * Whether the provided report is a chat room
  * @param {Object} report
- * @param {String} report.chatType
+ * @param {String} [report.chatType]
  * @returns {Boolean}
  */
 function isChatRoom(report) {
@@ -578,8 +578,8 @@ function findLastAccessedReport(reports, ignoreDomainRooms, policies, isFirstTim
 /**
  * Whether the provided report is an archived room
  * @param {Object} report
- * @param {Number} report.stateNum
- * @param {Number} report.statusNum
+ * @param {Number} [report.stateNum]
+ * @param {Number} [report.statusNum]
  * @returns {Boolean}
  */
 function isArchivedRoom(report) {
@@ -2912,7 +2912,7 @@ function shouldHideReport(report, currentReportId) {
  * filter out the majority of reports before filtering out very specific minority of reports.
  *
  * @param {Object} report
- * @param {String} currentReportId
+ * @param {String | Null} currentReportId
  * @param {Boolean} isInGSDMode
  * @param {String[]} betas
  * @param {Object} policies
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index e1251f9f50bc..938c0f7f0a26 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -1,8 +1,7 @@
 /* eslint-disable rulesdir/prefer-underscore-method */
 import Onyx from 'react-native-onyx';
-import _ from 'underscore';
-import lodashGet from 'lodash/get';
 import Str from 'expensify-common/lib/str';
+import {ValueOf} from 'type-fest';
 import ONYXKEYS from '../ONYXKEYS';
 import * as ReportUtils from './ReportUtils';
 import * as ReportActionsUtils from './ReportActionsUtils';
@@ -14,6 +13,11 @@ import * as LocalePhoneNumber from './LocalePhoneNumber';
 import * as UserUtils from './UserUtils';
 import * as PersonalDetailsUtils from './PersonalDetailsUtils';
 import ReportAction from '../types/onyx/ReportAction';
+import Beta from '../types/onyx/Beta';
+import Policy from '../types/onyx/Policy';
+import Report from '../types/onyx/Report';
+import {PersonalDetails} from '../types/onyx';
+import * as OnyxCommon from '../types/onyx/OnyxCommon';
 
 const visibleReportActionItems: Record<string, ReportAction> = {};
 const lastReportActions: Record<string, ReportAction> = {};
@@ -68,11 +72,11 @@ function resetIsSidebarLoadedReadyPromise() {
     });
 }
 
-function isSidebarLoadedReady() {
+function isSidebarLoadedReady(): Promise<unknown> {
     return sidebarIsReadyPromise;
 }
 
-function compareStringDates(a: string, b: string) {
+function compareStringDates(a: string, b: string): 0 | 1 | -1 {
     if (a < b) {
         return -1;
     }
@@ -87,7 +91,7 @@ function setIsSidebarLoadedReady() {
 }
 
 // Define a cache object to store the memoized results
-const reportIDsCache = new Map();
+const reportIDsCache = new Map<string, string[]>();
 
 // Function to set a key-value pair while maintaining the maximum key limit
 function setWithLimit<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, value: TValue) {
@@ -105,11 +109,18 @@ let hasInitialReportActions = false;
 /**
  * @returns An array of reportIDs sorted in the proper order
  */
-function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions) {
+function getOrderedReportIDs(
+    currentReportId: string | null,
+    allReports: Record<string, Report>,
+    betas: Beta[],
+    policies: Record<string, Policy>,
+    priorityMode: ValueOf<typeof CONST.PRIORITY_MODE>,
+    allReportActions: Record<string, ReportAction[]>,
+): string[] {
     // Generate a unique cache key based on the function arguments
     const cachedReportsKey = JSON.stringify(
-        [currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1],
-        (key, value) => {
+        [currentReportId, allReports, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1],
+        (key, value: unknown) => {
             /**
              *  Exclude 'participantAccountIDs', 'participants' and 'lastMessageText' not to overwhelm a cached key value with huge data,
              *  which we don't need to store in a cacheKey
@@ -117,13 +128,15 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
             if (key === 'participantAccountIDs' || key === 'participants' || key === 'lastMessageText') {
                 return undefined;
             }
+
             return value;
         },
     );
 
     // Check if the result is already in the cache
-    if (reportIDsCache.has(cachedReportsKey) && hasInitialReportActions) {
-        return reportIDsCache.get(cachedReportsKey);
+    const cachedIDs = reportIDsCache.get(cachedReportsKey);
+    if (cachedIDs && hasInitialReportActions) {
+        return cachedIDs;
     }
 
     // This is needed to prevent caching when Onyx is empty for a second render
@@ -131,7 +144,7 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
 
     const isInGSDMode = priorityMode === CONST.PRIORITY_MODE.GSD;
     const isInDefaultMode = !isInGSDMode;
-    const allReportsDictValues = Object.values(allReportsDict);
+    const allReportsDictValues = Object.values(allReports);
     // Filter out all the reports that shouldn't be displayed
     const reportsToDisplay = allReportsDictValues.filter((report) => ReportUtils.shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, policies, allReportActions, true));
 
@@ -152,7 +165,7 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
         report.displayName = ReportUtils.getReportName(report);
 
         // eslint-disable-next-line no-param-reassign
-        report.iouReportAmount = ReportUtils.getMoneyRequestTotal(report, allReportsDict);
+        report.iouReportAmount = ReportUtils.getMoneyRequestTotal(report, allReports);
     });
 
     // The LHN is split into five distinct groups, and each group is sorted a little differently. The groups will ALWAYS be in this order:
@@ -165,11 +178,11 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
     // 5. Archived reports
     //      - Sorted by lastVisibleActionCreated in default (most recent) view mode
     //      - Sorted by reportDisplayName in GSD (focus) view mode
-    const pinnedReports = [];
-    const outstandingIOUReports = [];
-    const draftReports = [];
-    const nonArchivedReports = [];
-    const archivedReports = [];
+    const pinnedReports: Report[] = [];
+    const outstandingIOUReports: Report[] = [];
+    const draftReports: Report[] = [];
+    const nonArchivedReports: Report[] = [];
+    const archivedReports: Report[] = [];
     reportsToDisplay.forEach((report) => {
         if (report.isPinned) {
             pinnedReports.push(report);
@@ -185,47 +198,121 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p
     });
 
     // Sort each group of reports accordingly
-    pinnedReports.sort((a, b) => a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()));
-    outstandingIOUReports.sort((a, b) => b.iouReportAmount - a.iouReportAmount || a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()));
-    draftReports.sort((a, b) => a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()));
+    pinnedReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0));
+    outstandingIOUReports.sort((a, b) => {
+        const compareAmounts = a?.iouReportAmount && b?.iouReportAmount ? b.iouReportAmount - a.iouReportAmount : 0;
+        const compareDisplayNames = a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0;
+        return compareAmounts || compareDisplayNames;
+    });
+    draftReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0));
 
     if (isInDefaultMode) {
-        nonArchivedReports.sort(
-            (a, b) => compareStringDates(b.lastVisibleActionCreated, a.lastVisibleActionCreated) || a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()),
-        );
+        nonArchivedReports.sort((a, b) => {
+            const compareDates = a?.lastVisibleActionCreated && b?.lastVisibleActionCreated ? compareStringDates(b.lastVisibleActionCreated, a.lastVisibleActionCreated) : 0;
+            const compareDisplayNames = a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0;
+            return compareDates || compareDisplayNames;
+        });
         // For archived reports ensure that most recent reports are at the top by reversing the order
-        archivedReports.sort((a, b) => compareStringDates(b.lastVisibleActionCreated, a.lastVisibleActionCreated));
+        archivedReports.sort((a, b) => (a?.lastVisibleActionCreated && b?.lastVisibleActionCreated ? compareStringDates(b.lastVisibleActionCreated, a.lastVisibleActionCreated) : 0));
     } else {
-        nonArchivedReports.sort((a, b) => a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()));
-        archivedReports.sort((a, b) => a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()));
+        nonArchivedReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0));
+        archivedReports.sort((a, b) => (a?.displayName && b?.displayName ? a.displayName.toLowerCase().localeCompare(b.displayName.toLowerCase()) : 0));
     }
 
     // Now that we have all the reports grouped and sorted, they must be flattened into an array and only return the reportID.
     // The order the arrays are concatenated in matters and will determine the order that the groups are displayed in the sidebar.
-    const LHNReports = [].concat(pinnedReports, outstandingIOUReports, draftReports, nonArchivedReports, archivedReports).map((report) => report.reportID);
+    const LHNReports = [...pinnedReports, ...outstandingIOUReports, ...draftReports, ...nonArchivedReports, ...archivedReports].map((report) => report.reportID);
     setWithLimit(reportIDsCache, cachedReportsKey, LHNReports);
     return LHNReports;
 }
 
+type OptionData = {
+    text?: string | null;
+    alternateText?: string | null;
+    pendingAction?: OnyxCommon.PendingAction | null;
+    allReportErrors?: OnyxCommon.Errors | null;
+    brickRoadIndicator?: typeof CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR | '' | null;
+    icons?: Icon[] | null;
+    tooltipText?: string | null;
+    ownerAccountID?: number | null;
+    subtitle?: string | null;
+    participantsList?: PersonalDetails[] | null;
+    login?: string | null;
+    accountID?: number | null;
+    managerID?: number | null;
+    reportID?: string | null;
+    policyID?: string | null;
+    status?: string | null;
+    type?: string | null;
+    stateNum?: ValueOf<typeof CONST.REPORT.STATE_NUM> | null;
+    statusNum?: ValueOf<typeof CONST.REPORT.STATUS> | null;
+    phoneNumber?: string | null;
+    isUnread?: boolean | null;
+    isUnreadWithMention?: boolean | null;
+    hasDraftComment?: boolean | null;
+    keyForList?: string | null;
+    searchText?: string | null;
+    isPinned?: boolean | null;
+    hasOutstandingIOU?: boolean | null;
+    iouReportID?: string | null;
+    isIOUReportOwner?: boolean | null;
+    iouReportAmount?: number | null;
+    isChatRoom?: boolean | null;
+    isArchivedRoom?: boolean | null;
+    shouldShowSubscript?: boolean | null;
+    isPolicyExpenseChat?: boolean | null;
+    isMoneyRequestReport?: boolean | null;
+    isExpenseRequest?: boolean | null;
+    isWaitingOnBankAccount?: boolean | null;
+    isLastMessageDeletedParentAction?: boolean | null;
+    isAllowedToComment?: boolean | null;
+    isThread?: boolean | null;
+    isTaskReport?: boolean | null;
+    isWaitingForTaskCompleteFromAssignee?: boolean | null;
+    parentReportID?: string | null;
+    notificationPreference?: string | number | null;
+    displayNamesWithTooltips?: DisplayNamesWithTooltip[] | null;
+};
+
+type DisplayNamesWithTooltip = {
+    displayName?: string;
+    avatar?: string;
+    login?: string;
+    accountID?: number;
+    pronouns?: string;
+};
+
+type ActorDetails = {
+    displayName?: string;
+    accountID?: number;
+};
+
+type Icon = {
+    source?: string;
+    id?: number;
+    type?: string;
+    name?: string;
+};
+
 /**
  * Gets all the data necessary for rendering an OptionRowLHN component
- *
- * @param report
- * @param reportActions
- * @param personalDetails
- * @param preferredLocale
- * @param [policy]
- * @param parentReportAction
- * @returns
  */
-function getOptionData(report, reportActions, personalDetails, preferredLocale, policy, parentReportAction) {
+function getOptionData(
+    report: Report,
+    reportActions: Record<string, ReportAction[]>,
+    personalDetails: Record<number, PersonalDetails>,
+    preferredLocale: ValueOf<typeof CONST.LOCALES>,
+    policy: Policy,
+    parentReportAction: ReportAction,
+): OptionData | undefined {
     // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for
     // this method to be called after the Onyx data has been cleared out. In that case, it's fine to do
     // a null check here and return early.
     if (!report || !personalDetails) {
         return;
     }
-    const result = {
+
+    const result: OptionData = {
         text: null,
         alternateText: null,
         pendingAction: null,
@@ -263,10 +350,14 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
         isWaitingOnBankAccount: false,
         isLastMessageDeletedParentAction: false,
         isAllowedToComment: true,
+        isThread: null,
+        isTaskReport: null,
+        isWaitingForTaskCompleteFromAssignee: null,
+        parentReportID: null,
+        notificationPreference: null,
     };
-
-    const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(report.participantAccountIDs, personalDetails));
-    const personalDetail = participantPersonalDetailList[0] || {};
+    const participantPersonalDetailList: PersonalDetails[] = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(report.participantAccountIDs ?? [], personalDetails));
+    const personalDetail = participantPersonalDetailList[0] ?? {};
 
     result.isThread = ReportUtils.isChatThread(report);
     result.isChatRoom = ReportUtils.isChatRoom(report);
@@ -280,8 +371,8 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
     result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report);
     result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report);
     result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom || report.pendingFields.createChat : null;
-    result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions);
-    result.brickRoadIndicator = !_.isEmpty(result.allReportErrors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '';
+    result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions) as OnyxCommon.Errors;
+    result.brickRoadIndicator = Object.keys(result.allReportErrors ?? {}).length !== 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '';
     result.ownerAccountID = report.ownerAccountID;
     result.managerID = report.managerID;
     result.reportID = report.reportID;
@@ -294,30 +385,30 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
     result.isPinned = report.isPinned;
     result.iouReportID = report.iouReportID;
     result.keyForList = String(report.reportID);
-    result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participantAccountIDs || []);
+    result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participantAccountIDs ?? []);
     result.hasOutstandingIOU = report.hasOutstandingIOU;
-    result.parentReportID = report.parentReportID || null;
+    result.parentReportID = report.parentReportID;
     result.isWaitingOnBankAccount = report.isWaitingOnBankAccount;
-    result.notificationPreference = report.notificationPreference || null;
+    result.notificationPreference = report.notificationPreference;
     result.isAllowedToComment = !ReportUtils.shouldDisableWriteActions(report);
 
     const hasMultipleParticipants = participantPersonalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat;
     const subtitle = ReportUtils.getChatRoomSubtitle(report);
 
-    const login = Str.removeSMSDomain(lodashGet(personalDetail, 'login', ''));
-    const status = lodashGet(personalDetail, 'status', '');
+    const login = Str.removeSMSDomain(personalDetail?.login ?? '');
+    const status = personalDetail?.status ?? '';
     const formattedLogin = Str.isSMSLogin(login) ? LocalePhoneNumber.formatPhoneNumber(login) : login;
 
     // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
-    const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips((participantPersonalDetailList || []).slice(0, 10), hasMultipleParticipants);
+    const displayNamesWithTooltips: DisplayNamesWithTooltip[] = ReportUtils.getDisplayNamesWithTooltips((participantPersonalDetailList || []).slice(0, 10), hasMultipleParticipants);
     const lastMessageTextFromReport = OptionsListUtils.getLastMessageTextForReport(report);
 
     // If the last actor's details are not currently saved in Onyx Collection,
     // then try to get that from the last report action if that action is valid
     // to get data from.
-    let lastActorDetails = personalDetails[report.lastActorAccountID] || null;
+    let lastActorDetails: ActorDetails | null = report.lastActorAccountID ? personalDetails[report.lastActorAccountID] : null;
     if (!lastActorDetails && visibleReportActionItems[report.reportID]) {
-        const lastActorDisplayName = lodashGet(visibleReportActionItems[report.reportID], 'person[0].text');
+        const lastActorDisplayName = visibleReportActionItems[report.reportID]?.person?.[0]?.text;
         lastActorDetails = lastActorDisplayName
             ? {
                   displayName: lastActorDisplayName,
@@ -328,22 +419,25 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
     let lastMessageText = hasMultipleParticipants && lastActorDetails?.accountID && Number(lastActorDetails.accountID) !== currentUserAccountID ? `${lastActorDetails.displayName}: ` : '';
     lastMessageText += report ? lastMessageTextFromReport : '';
 
-    if (result.isArchivedRoom) {
-        const archiveReason = lastReportActions[report.reportID]?.originalMessage?.reason || CONST.REPORT.ARCHIVE_REASON.DEFAULT;
+    const reportAction = lastReportActions[report.reportID];
+    if (result.isArchivedRoom && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED) {
+        const archiveReason = reportAction?.originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT;
+
         lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, {
-            displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'),
+            displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'),
             policyName: ReportUtils.getPolicyName(report, false, policy),
         });
     }
 
     if ((result.isChatRoom || result.isPolicyExpenseChat || result.isThread || result.isTaskReport) && !result.isArchivedRoom) {
         const lastAction = visibleReportActionItems[report.reportID];
-        if (lodashGet(lastAction, 'actionName', '') === CONST.REPORT.ACTIONS.TYPE.RENAMED) {
-            const newName = lodashGet(lastAction, 'originalMessage.newName', '');
+
+        if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.RENAMED) {
+            const newName = lastAction?.originalMessage?.newName ?? '';
             result.alternateText = Localize.translate(preferredLocale, 'newRoomPage.roomRenamedTo', {newName});
-        } else if (lodashGet(lastAction, 'actionName', '') === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED) {
+        } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED) {
             result.alternateText = `${Localize.translate(preferredLocale, 'task.messages.reopened')}: ${report.reportName}`;
-        } else if (lodashGet(lastAction, 'actionName', '') === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED) {
+        } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED) {
             result.alternateText = `${Localize.translate(preferredLocale, 'task.messages.completed')}: ${report.reportName}`;
         } else {
             result.alternateText = lastMessageTextFromReport.length > 0 ? lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet');
@@ -354,19 +448,23 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale,
             // We also add a fullstop after the final name, the word "and" before the final name and commas between all previous names.
             lastMessageText =
                 Localize.translate(preferredLocale, 'reportActionsView.beginningOfChatHistory') +
-                _.map(displayNamesWithTooltips, ({displayName, pronouns}, index) => {
-                    const formattedText = _.isEmpty(pronouns) ? displayName : `${displayName} (${pronouns})`;
-
-                    if (index === displayNamesWithTooltips.length - 1) {
-                        return `${formattedText}.`;
-                    }
-                    if (index === displayNamesWithTooltips.length - 2) {
-                        return `${formattedText} ${Localize.translate(preferredLocale, 'common.and')}`;
-                    }
-                    if (index < displayNamesWithTooltips.length - 2) {
-                        return `${formattedText},`;
-                    }
-                }).join(' ');
+                displayNamesWithTooltips
+                    .map(({displayName, pronouns}, index) => {
+                        const formattedText = !pronouns ? displayName : `${displayName} (${pronouns})`;
+
+                        if (index === displayNamesWithTooltips.length - 1) {
+                            return `${formattedText}.`;
+                        }
+                        if (index === displayNamesWithTooltips.length - 2) {
+                            return `${formattedText} ${Localize.translate(preferredLocale, 'common.and')}`;
+                        }
+                        if (index < displayNamesWithTooltips.length - 2) {
+                            return `${formattedText},`;
+                        }
+
+                        return '';
+                    })
+                    .join(' ');
         }
 
         result.alternateText = lastMessageText || formattedLogin;
diff --git a/src/types/onyx/PersonalDetails.ts b/src/types/onyx/PersonalDetails.ts
index 64911dbfecb1..e1c944a95c40 100644
--- a/src/types/onyx/PersonalDetails.ts
+++ b/src/types/onyx/PersonalDetails.ts
@@ -40,6 +40,8 @@ type PersonalDetails = {
         /** Whether timezone is automatically set */
         automatic?: boolean;
     };
+
+    status?: string;
 };
 
 export default PersonalDetails;
diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts
index 88caa683305d..b029e3996963 100644
--- a/src/types/onyx/Report.ts
+++ b/src/types/onyx/Report.ts
@@ -55,7 +55,7 @@ type Report = {
     reportName?: string;
 
     /** ID of the report */
-    reportID?: string;
+    reportID: string;
 
     /** The state that the report is currently in */
     stateNum?: ValueOf<typeof CONST.REPORT.STATE_NUM>;
@@ -83,6 +83,11 @@ type Report = {
     participantAccountIDs?: number[];
     total?: number;
     currency?: string;
+    iouReportAmount?: number;
+    isWaitingOnBankAccount?: boolean;
+    isLastMessageDeletedParentAction?: boolean;
+    iouReportID?: string;
+    pendingFields?: Record<string, OnyxCommon.PendingAction>;
 };
 
 export default Report;

From 08da8c014b6c53e8042bbae41731fdd891619c62 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Tue, 3 Oct 2023 10:01:07 +0200
Subject: [PATCH 03/11] Self review the code

---
 src/libs/SidebarUtils.ts | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index 938c0f7f0a26..d3bb9f6491d3 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -350,11 +350,6 @@ function getOptionData(
         isWaitingOnBankAccount: false,
         isLastMessageDeletedParentAction: false,
         isAllowedToComment: true,
-        isThread: null,
-        isTaskReport: null,
-        isWaitingForTaskCompleteFromAssignee: null,
-        parentReportID: null,
-        notificationPreference: null,
     };
     const participantPersonalDetailList: PersonalDetails[] = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(report.participantAccountIDs ?? [], personalDetails));
     const personalDetail = participantPersonalDetailList[0] ?? {};
@@ -387,9 +382,9 @@ function getOptionData(
     result.keyForList = String(report.reportID);
     result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participantAccountIDs ?? []);
     result.hasOutstandingIOU = report.hasOutstandingIOU;
-    result.parentReportID = report.parentReportID;
+    result.parentReportID = report.parentReportID ?? null;
     result.isWaitingOnBankAccount = report.isWaitingOnBankAccount;
-    result.notificationPreference = report.notificationPreference;
+    result.notificationPreference = report.notificationPreference ?? null;
     result.isAllowedToComment = !ReportUtils.shouldDisableWriteActions(report);
 
     const hasMultipleParticipants = participantPersonalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat;

From 1715578eaf4ee453a35119009e025792dcc32f7a Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Thu, 12 Oct 2023 16:18:39 +0200
Subject: [PATCH 04/11] Address code review

---
 src/libs/SidebarUtils.ts | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index ca7ddf38ca63..a7b807809cfa 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -401,7 +401,7 @@ function getOptionData(
     // If the last actor's details are not currently saved in Onyx Collection,
     // then try to get that from the last report action if that action is valid
     // to get data from.
-    let lastActorDetails: ActorDetails | null = report.lastActorAccountID ? personalDetails[report.lastActorAccountID] : null;
+    let lastActorDetails: ActorDetails | null = report.lastActorAccountID && personalDetails?.[report.lastActorAccountID] ? personalDetails[report.lastActorAccountID] : null;
     if (!lastActorDetails && visibleReportActionItems[report.reportID]) {
         const lastActorDisplayName = visibleReportActionItems[report.reportID]?.person?.[0]?.text;
         lastActorDetails = lastActorDisplayName
@@ -411,11 +411,10 @@ function getOptionData(
               }
             : null;
     }
-    const lastActorDisplayName =
-        hasMultipleParticipants && lastActorDetails && lastActorDetails.accountID && Number(lastActorDetails.accountID) !== currentUserAccountID ? lastActorDetails.displayName : '';
+    const lastActorDisplayName = hasMultipleParticipants && lastActorDetails?.accountID && Number(lastActorDetails.accountID) !== currentUserAccountID ? lastActorDetails.displayName : '';
     let lastMessageText = lastMessageTextFromReport;
 
-    const reportAction = lastReportActions[report.reportID];
+    const reportAction = lastReportActions?.[report.reportID];
     if (result.isArchivedRoom && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED) {
         const archiveReason = reportAction?.originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT;
 
@@ -435,7 +434,7 @@ function getOptionData(
             result.alternateText = `${Localize.translate(preferredLocale, 'task.messages.reopened')}: ${report.reportName}`;
         } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED) {
             result.alternateText = `${Localize.translate(preferredLocale, 'task.messages.completed')}: ${report.reportName}`;
-        } else if (lastAction?.actionName  !== CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lastActorDisplayName && lastMessageTextFromReport) {
+        } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lastActorDisplayName && lastMessageTextFromReport) {
             result.alternateText = `${lastActorDisplayName}: ${lastMessageText}`;
         } else {
             result.alternateText = lastMessageTextFromReport.length > 0 ? lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet');

From 2a252907d746867b3bd48fb0d6aae6adcd5a8964 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Thu, 12 Oct 2023 16:26:49 +0200
Subject: [PATCH 05/11] Add errors to ReportActionBase

---
 src/types/onyx/ReportAction.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts
index 3d32a13dd951..a2939e5d365f 100644
--- a/src/types/onyx/ReportAction.ts
+++ b/src/types/onyx/ReportAction.ts
@@ -66,8 +66,8 @@ type ReportActionBase = {
     /** Whether we have received a response back from the server */
     isLoading?: boolean;
 
-    /** Error message that's come back from the server. */
-    error?: string;
+    /** An error message to display to the user */
+    errors?: OnyxCommon.Errors;
 
     /** accountIDs of the people to which the whisper was sent to (if any). Returns empty array if it is not a whisper */
     whisperedToAccountIDs?: number[];

From 0e7d40daf212782d4209d5ec6d2ab455c6515700 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Thu, 12 Oct 2023 18:11:47 +0200
Subject: [PATCH 06/11] Add optional chaining to fix tests

---
 src/libs/SidebarUtils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index a7b807809cfa..955725ad899c 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -415,7 +415,7 @@ function getOptionData(
     let lastMessageText = lastMessageTextFromReport;
 
     const reportAction = lastReportActions?.[report.reportID];
-    if (result.isArchivedRoom && reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED) {
+    if (result.isArchivedRoom && reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED) {
         const archiveReason = reportAction?.originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT;
 
         lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, {

From ccd959e2f42eebd39e8f37a70c5d69571990e437 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Thu, 12 Oct 2023 18:20:04 +0200
Subject: [PATCH 07/11] Fix remaining tests

---
 src/libs/SidebarUtils.ts  |  5 ++---
 tests/unit/SidebarTest.js | 46 ---------------------------------------
 2 files changed, 2 insertions(+), 49 deletions(-)

diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index 955725ad899c..5a3a6c066a8c 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -415,9 +415,8 @@ function getOptionData(
     let lastMessageText = lastMessageTextFromReport;
 
     const reportAction = lastReportActions?.[report.reportID];
-    if (result.isArchivedRoom && reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED) {
-        const archiveReason = reportAction?.originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT;
-
+    if (result.isArchivedRoom) {
+        const archiveReason = (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED && reportAction?.originalMessage?.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT;
         lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, {
             displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'),
             policyName: ReportUtils.getPolicyName(report, false, policy),
diff --git a/tests/unit/SidebarTest.js b/tests/unit/SidebarTest.js
index 1b5daa323da5..1ad10a3bfaf0 100644
--- a/tests/unit/SidebarTest.js
+++ b/tests/unit/SidebarTest.js
@@ -81,51 +81,5 @@ describe('Sidebar', () => {
                     })
             );
         });
-        it('renders the policy deleted archive reason as the preview message of the chat', () => {
-            const report = {
-                ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true),
-                policyName: 'Vikings Policy',
-                chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM,
-                statusNum: CONST.REPORT.STATUS.CLOSED,
-                stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
-            };
-            const action = {
-                ...LHNTestUtils.getFakeReportAction('email1@test.com', 3, true),
-                actionName: 'CLOSED',
-                originalMessage: {
-                    policyName: 'Vikings Policy',
-                    reason: 'policyDeleted',
-                },
-            };
-
-            // Given the user is in all betas
-            const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS];
-            LHNTestUtils.getDefaultRenderedSidebarLinks('0');
-            return (
-                waitForBatchedUpdates()
-                    // When Onyx is updated with the data and the sidebar re-renders
-                    .then(() =>
-                        Onyx.multiSet({
-                            [ONYXKEYS.BETAS]: betas,
-                            [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
-                            [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
-                            [ONYXKEYS.IS_LOADING_REPORT_DATA]: false,
-                            [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
-                            [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action},
-                        }),
-                    )
-                    .then(() => {
-                        const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames');
-                        const displayNames = screen.queryAllByLabelText(hintText);
-                        expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('Report (archived)');
-
-                        const hintMessagePreviewText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview');
-                        const messagePreviewTexts = screen.queryAllByLabelText(hintMessagePreviewText);
-                        expect(lodashGet(messagePreviewTexts, [0, 'props', 'children'])).toBe(
-                            'This workspace chat is no longer active because Vikings Policy is no longer an active workspace.',
-                        );
-                    })
-            );
-        });
     });
 });

From 233d703fc60a1da3b61dad69df71f331e53ee5d5 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Fri, 20 Oct 2023 08:09:24 +0200
Subject: [PATCH 08/11] Bring back test

---
 tests/unit/SidebarTest.js | 46 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tests/unit/SidebarTest.js b/tests/unit/SidebarTest.js
index 1ad10a3bfaf0..1b5daa323da5 100644
--- a/tests/unit/SidebarTest.js
+++ b/tests/unit/SidebarTest.js
@@ -81,5 +81,51 @@ describe('Sidebar', () => {
                     })
             );
         });
+        it('renders the policy deleted archive reason as the preview message of the chat', () => {
+            const report = {
+                ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true),
+                policyName: 'Vikings Policy',
+                chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM,
+                statusNum: CONST.REPORT.STATUS.CLOSED,
+                stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
+            };
+            const action = {
+                ...LHNTestUtils.getFakeReportAction('email1@test.com', 3, true),
+                actionName: 'CLOSED',
+                originalMessage: {
+                    policyName: 'Vikings Policy',
+                    reason: 'policyDeleted',
+                },
+            };
+
+            // Given the user is in all betas
+            const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS];
+            LHNTestUtils.getDefaultRenderedSidebarLinks('0');
+            return (
+                waitForBatchedUpdates()
+                    // When Onyx is updated with the data and the sidebar re-renders
+                    .then(() =>
+                        Onyx.multiSet({
+                            [ONYXKEYS.BETAS]: betas,
+                            [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
+                            [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
+                            [ONYXKEYS.IS_LOADING_REPORT_DATA]: false,
+                            [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
+                            [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action},
+                        }),
+                    )
+                    .then(() => {
+                        const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames');
+                        const displayNames = screen.queryAllByLabelText(hintText);
+                        expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('Report (archived)');
+
+                        const hintMessagePreviewText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview');
+                        const messagePreviewTexts = screen.queryAllByLabelText(hintMessagePreviewText);
+                        expect(lodashGet(messagePreviewTexts, [0, 'props', 'children'])).toBe(
+                            'This workspace chat is no longer active because Vikings Policy is no longer an active workspace.',
+                        );
+                    })
+            );
+        });
     });
 });

From f13776329f8aebd877d073d719305935b14e9cdc Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Fri, 20 Oct 2023 08:18:15 +0200
Subject: [PATCH 09/11] Add comments to onyx types

---
 src/types/onyx/PersonalDetails.ts |  1 +
 src/types/onyx/Report.ts          | 15 ++++++++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/types/onyx/PersonalDetails.ts b/src/types/onyx/PersonalDetails.ts
index 4b3e97ef22fc..0cd264802128 100644
--- a/src/types/onyx/PersonalDetails.ts
+++ b/src/types/onyx/PersonalDetails.ts
@@ -43,6 +43,7 @@ type PersonalDetails = {
     /** Timezone of the current user from their personal details */
     timezone?: Timezone;
 
+    /** Status of the current user from their personal details */
     status?: string;
 };
 
diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts
index 4a84069c9afa..494661b2fb89 100644
--- a/src/types/onyx/Report.ts
+++ b/src/types/onyx/Report.ts
@@ -72,12 +72,25 @@ type Report = {
     participantAccountIDs?: number[];
     total?: number;
     currency?: string;
-    iouReportAmount?: number;
+
+    /** Whether the report is waiting on a bank account */
     isWaitingOnBankAccount?: boolean;
+
+    /** Whether the last message was deleted */
     isLastMessageDeletedParentAction?: boolean;
+
+    /** The ID of the IOU report */
     iouReportID?: string;
+
+    /** Total amount of money owed for IOU report */
+    iouReportAmount?: number;
+
+    /** Pending fields for the report */
     pendingFields?: Record<string, OnyxCommon.PendingAction>;
+
+    /** The ID of the preexisting report (it is possible that we optimistically created a Report for which a report already exists) */
     preexistingReportID?: string;
+
     /** If the report contains nonreimbursable expenses, send the nonreimbursable total */
     nonReimbursableTotal?: number;
 };

From a224678217c6b830ddde2c56db36da4414810c99 Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Tue, 24 Oct 2023 15:40:45 +0200
Subject: [PATCH 10/11] Remove isLastMessageDeletedParentAction from
 getOptionData

---
 src/libs/SidebarUtils.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index d6a917840c11..eeb1a6249c05 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -264,7 +264,6 @@ type OptionData = {
     isMoneyRequestReport?: boolean | null;
     isExpenseRequest?: boolean | null;
     isWaitingOnBankAccount?: boolean | null;
-    isLastMessageDeletedParentAction?: boolean | null;
     isAllowedToComment?: boolean | null;
     isThread?: boolean | null;
     isTaskReport?: boolean | null;

From 8d42d7f958aa7c5e6cb8d8bc1fd6945c36395ccb Mon Sep 17 00:00:00 2001
From: Blazej Kustra <blazej.kustra@swmansion.com>
Date: Wed, 25 Oct 2023 09:25:47 +0200
Subject: [PATCH 11/11] Resolve conflicts

---
 src/libs/SidebarUtils.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index d294ddb2ef39..bfe7d2281049 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -271,6 +271,7 @@ type OptionData = {
     parentReportID?: string | null;
     notificationPreference?: string | number | null;
     displayNamesWithTooltips?: DisplayNamesWithTooltip[] | null;
+    chatType?: ValueOf<typeof CONST.REPORT.CHAT_TYPE> | null;
 };
 
 type DisplayNamesWithTooltip = {
