From 17c4dbe1dad8384505134a9a948e90098d448ea3 Mon Sep 17 00:00:00 2001 From: Uzaifm127 Date: Thu, 5 Mar 2026 20:57:04 +0530 Subject: [PATCH 1/5] fix: Updated task title isn't reflected in Reports > Chats in Offline and infinite loading when click on the time of the task in Reports > Chats --- src/components/ReportActionItem/TaskPreview.tsx | 3 ++- src/components/Search/index.tsx | 4 +++- src/libs/SearchUIUtils.ts | 11 +++++++++-- src/libs/actions/Report/index.ts | 2 +- src/libs/actions/Task.ts | 8 +------- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index 65ac9d320d0c8..1452bbe3466ca 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -86,7 +86,8 @@ function TaskPreview({ const {translate} = useLocalize(); const theme = useTheme(); const taskReportID = taskReport?.reportID ?? action?.childReportID; - const taskTitle = action?.childReportName ?? taskReport?.reportName ?? ''; + // Prefer the live task report name so offline title edits are reflected immediately. + const taskTitle = taskReport?.reportName ?? action?.childReportName ?? ''; const taskTitleWithoutImage = Parser.replace(Parser.htmlToMarkdown(taskTitle), {disabledRules: [...CONST.TASK_TITLE_DISABLED_RULES]}); diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 9d4a16a0a71a3..e6532dc4ee5b5 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -42,6 +42,7 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute'; import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; +import {isCreatedTaskReportAction} from '@libs/ReportActionsUtils'; import {isSplitAction} from '@libs/ReportSecondaryActionUtils'; import {canEditFieldOfMoneyRequest, canHoldUnholdReportAction, canRejectReportAction, isOneTransactionReport, selectFilteredReportActions} from '@libs/ReportUtils'; import {buildCannedSearchQuery, buildSearchQueryString} from '@libs/SearchQueryUtils'; @@ -1047,7 +1048,8 @@ function Search({ } if (isReportActionListItemType(item)) { - const reportActionID = reportActionItem.reportActionID; + // For created-task rows, reportActionID may not exist offline yet; skipping it prevents the report RHP from loading forever. + const reportActionID = isCreatedTaskReportAction(reportActionItem) ? undefined : reportActionItem.reportActionID; Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo})); return; } diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 18b0f424ef6bf..b549b129d799e 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -2114,9 +2114,15 @@ function getReportActionsSections(data: OnyxTypes.SearchResults['data'], visible const reportActions = Object.values(data[key]); n += reportActions.length; for (const reportAction of reportActions) { - const reportID = reportAction.reportID ?? reportIDFromKey; + // Always use the container reportID so "In " rows open the parent chat, not a child task/thread report. + const reportID = reportIDFromKey; const from = reportAction.accountID ? (data.personalDetailsList?.[reportAction.accountID] ?? emptyPersonalDetails) : emptyPersonalDetails; - const report = data[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? {}; + const report = + getReportOrDraftReport(reportID) ?? + getReportOrDraftReport(reportAction.reportID) ?? + data[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? + data[`${ONYXKEYS.COLLECTION.REPORT}${reportAction.reportID}`] ?? + {}; const policy = data[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`] ?? {}; const originalMessage = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction) : undefined; const isSendingMoney = isMoneyRequestAction(reportAction) && originalMessage?.type === CONST.IOU.REPORT_ACTION_TYPE.PAY && originalMessage?.IOUDetails; @@ -2138,6 +2144,7 @@ function getReportActionsSections(data: OnyxTypes.SearchResults['data'], visible reportActionItems.push({ ...reportAction, + reportID, from, // eslint-disable-next-line @typescript-eslint/no-deprecated reportName: getSearchReportName({report, policy, personalDetails: data.personalDetailsList, transactions, invoiceReceiverPolicy, reports, policies, isReportArchived}), diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index a130ad354d6fb..331f7d718e50d 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -823,7 +823,7 @@ function addActions({ failureReportActions[pregeneratedResponseParams.optimisticConciergeReportActionID] = null; } - const failureData: Array> = [ + const failureData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 451286240055e..17c10870eaa8a 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -210,13 +210,7 @@ function createTaskAndNavigate(params: CreateTaskAndNavigateParams) { // task report/action on API failure so the task stays visible until the user dismiss the // error from chat. const failureData: Array< - OnyxUpdate< - | typeof ONYXKEYS.COLLECTION.REPORT - | typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS - | typeof ONYXKEYS.PERSONAL_DETAILS_LIST - | typeof ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE - | typeof ONYXKEYS.COLLECTION.SNAPSHOT - > + OnyxUpdate > = []; if (assigneeChatReport && assigneeChatReportID) { From 99f3bba978c47f68bd32cb460f616cc46ff0b908 Mon Sep 17 00:00:00 2001 From: Uzaifm127 Date: Sat, 7 Mar 2026 19:19:07 +0530 Subject: [PATCH 2/5] omitting reportActionID for optimistic offline created task and keeping it for online ones --- src/components/Search/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 21df6a5aa40d1..2221a3f8a5b63 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1050,8 +1050,12 @@ function Search({ } if (isReportActionListItemType(item)) { - // For created-task rows, reportActionID may not exist offline yet; skipping it prevents the report RHP from loading forever. - const reportActionID = isCreatedTaskReportAction(reportActionItem) ? undefined : reportActionItem.reportActionID; + // Keep deep-linking for persisted actions, but avoid anchoring to optimistic created-task actions that may not be resolvable offline. + const isOptimisticCreatedTaskAction = reportActionItem.isOptimisticAction ?? false; + const shouldSkipReportActionID = + isCreatedTaskReportAction(reportActionItem) && (isOptimisticCreatedTaskAction || reportActionItem.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + + const reportActionID = shouldSkipReportActionID ? undefined : reportActionItem.reportActionID; Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo})); return; } From 5d44370a9ad02d708047045d41814eabc9ab8efe Mon Sep 17 00:00:00 2001 From: Uzaifm127 Date: Thu, 12 Mar 2026 17:04:39 +0530 Subject: [PATCH 3/5] added the optimistic data for parent chat's created task preview action --- src/libs/actions/Task.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index dbee9cce2c41f..cfb46ced1dffa 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -615,6 +615,18 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task }, ]; + if (title && report.parentReportID && report.parentReportActionID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + value: { + [report.parentReportActionID]: { + childReportName: parsedTitle, + }, + }, + }); + } + const successData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -651,6 +663,18 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task }, ]; + if (title && report.parentReportID && report.parentReportActionID) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, + value: { + [report.parentReportActionID]: { + childReportName: report.reportName, + }, + }, + }); + } + const parameters: EditTaskParams = { taskReportID: report.reportID, htmlTitle: parsedTitle, From 3c6c13ded1deb2df4b8ba9dd672a1a2310912476 Mon Sep 17 00:00:00 2001 From: Uzaifm127 Date: Sun, 15 Mar 2026 14:43:48 +0530 Subject: [PATCH 4/5] synched the task status update between Reports page and report screen offline and enable the task checkbox in Reports page --- .../ReportActionItem/TaskPreview.tsx | 30 ++++++++++---- src/libs/actions/Task.ts | 39 ++++++++++++++++--- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index 1452bbe3466ca..bcf4a6bf82e06 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -88,6 +88,20 @@ function TaskPreview({ const taskReportID = taskReport?.reportID ?? action?.childReportID; // Prefer the live task report name so offline title edits are reflected immediately. const taskTitle = taskReport?.reportName ?? action?.childReportName ?? ''; + const taskContextReport = React.useMemo( + () => + taskReport ?? + ({ + reportID: taskReportID, + parentReportID: chatReportID, + parentReportActionID: action?.reportActionID, + ownerAccountID: action?.childOwnerAccountID, + managerID: action?.childManagerAccountID, + stateNum: action?.childStateNum, + statusNum: action?.childStatusNum, + } as Report), + [action?.childManagerAccountID, action?.childOwnerAccountID, action?.childStateNum, action?.childStatusNum, action?.reportActionID, chatReportID, taskReport, taskReportID], + ); const taskTitleWithoutImage = Parser.replace(Parser.htmlToMarkdown(taskTitle), {disabledRules: [...CONST.TASK_TITLE_DISABLED_RULES]}); @@ -97,12 +111,12 @@ function TaskPreview({ const isTaskCompleted = !isEmptyObject(taskReport) ? taskReport?.stateNum === CONST.REPORT.STATE_NUM.APPROVED && taskReport.statusNum === CONST.REPORT.STATUS_NUM.APPROVED : action?.childStateNum === CONST.REPORT.STATE_NUM.APPROVED && action?.childStatusNum === CONST.REPORT.STATUS_NUM.APPROVED; - const parentReportAction = useParentReportAction(taskReport); - const taskAssigneeAccountID = getTaskAssigneeAccountID(taskReport, parentReportAction) ?? action?.childManagerAccountID ?? CONST.DEFAULT_NUMBER_ID; - const parentReport = useParentReport(taskReport?.reportID); + const parentReportAction = useParentReportAction(taskContextReport); + const taskAssigneeAccountID = getTaskAssigneeAccountID(taskContextReport, parentReportAction) ?? action?.childManagerAccountID ?? CONST.DEFAULT_NUMBER_ID; + const parentReport = useParentReport(taskContextReport?.reportID); const isParentReportArchived = useReportIsArchived(parentReport?.reportID); - const hasOutstandingChildTask = useHasOutstandingChildTask(taskReport); - const isTaskActionable = canActionTask(taskReport, parentReportAction, currentUserPersonalDetails.accountID, parentReport, isParentReportArchived); + const hasOutstandingChildTask = useHasOutstandingChildTask(taskContextReport); + const isTaskActionable = canActionTask(taskContextReport, parentReportAction, currentUserPersonalDetails.accountID, parentReport, isParentReportArchived); const hasAssignee = taskAssigneeAccountID > 0; const personalDetails = usePersonalDetails(); const avatar = personalDetails?.[taskAssigneeAccountID]?.avatar ?? icons.FallbackAvatar; @@ -110,7 +124,7 @@ function TaskPreview({ const isDeletedParentAction = isCanceledTaskReport(taskReport, action); const iconWrapperStyle = StyleUtils.getTaskPreviewIconWrapper(hasAssignee ? avatarSize : undefined); - const shouldShowGreenDotIndicator = isOpenTaskReport(taskReport, action) && isReportManager(taskReport); + const shouldShowGreenDotIndicator = isOpenTaskReport(taskContextReport, action) && isReportManager(taskContextReport); if (isDeletedParentAction) { return ${translate('parentReportAction.deletedTask')}`} />; } @@ -151,9 +165,9 @@ function TaskPreview({ disabled={!isTaskActionable} onPress={callFunctionIfActionIsAllowed(() => { if (isTaskCompleted) { - reopenTask(taskReport, parentReport, currentUserPersonalDetails.accountID, taskReportID); + reopenTask(taskContextReport, parentReport, currentUserPersonalDetails.accountID, taskReportID); } else { - completeTask(taskReport, parentReport?.hasOutstandingChildTask ?? false, hasOutstandingChildTask, parentReportAction, taskReportID); + completeTask(taskContextReport, parentReport?.hasOutstandingChildTask ?? false, hasOutstandingChildTask, parentReportAction, taskReportID); } })} accessibilityLabel={translate('task.task')} diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index cfb46ced1dffa..77dd990f452eb 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -418,12 +418,15 @@ function buildTaskData( }, ]; - if (parentReportAction) { + const parentReportID = taskReport?.parentReportID; + const parentReportActionID = parentReportAction?.reportActionID ?? taskReport?.parentReportActionID; + + if (parentReportID && parentReportActionID) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReport?.parentReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, value: { - [parentReportAction.reportActionID]: { + [parentReportActionID]: { childStateNum: CONST.REPORT.STATE_NUM.APPROVED, childStatusNum: CONST.REPORT.STATUS_NUM.APPROVED, }, @@ -432,9 +435,9 @@ function buildTaskData( failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReport?.parentReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, value: { - [parentReportAction.reportActionID]: { + [parentReportActionID]: { childStateNum: CONST.REPORT.STATE_NUM.OPEN, childStatusNum: CONST.REPORT.STATUS_NUM.OPEN, }, @@ -535,6 +538,19 @@ function reopenTask(taskReport: OnyxEntry, parentReport: OnyxE }, ]; + if (taskReport?.parentReportID && taskReport?.parentReportActionID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReport.parentReportID}`, + value: { + [taskReport.parentReportActionID]: { + childStateNum: CONST.REPORT.STATE_NUM.OPEN, + childStatusNum: CONST.REPORT.STATUS_NUM.OPEN, + }, + }, + }); + } + const successData: Array> = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -573,6 +589,19 @@ function reopenTask(taskReport: OnyxEntry, parentReport: OnyxE }, ]; + if (taskReport?.parentReportID && taskReport?.parentReportActionID) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${taskReport.parentReportID}`, + value: { + [taskReport.parentReportActionID]: { + childStateNum: CONST.REPORT.STATE_NUM.APPROVED, + childStatusNum: CONST.REPORT.STATUS_NUM.APPROVED, + }, + }, + }); + } + const parameters: ReopenTaskParams = { taskReportID, reopenedTaskReportActionID: reopenedTaskReportAction.reportActionID, From 593a913034b2bc60e7b3d6567fdaeb7d1ddb5259 Mon Sep 17 00:00:00 2001 From: Uzaifm127 Date: Sun, 15 Mar 2026 20:45:44 +0530 Subject: [PATCH 5/5] Removed useMemo from TaskPreview --- .../ReportActionItem/TaskPreview.tsx | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index bcf4a6bf82e06..0fb9ea9fcce78 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -88,20 +88,17 @@ function TaskPreview({ const taskReportID = taskReport?.reportID ?? action?.childReportID; // Prefer the live task report name so offline title edits are reflected immediately. const taskTitle = taskReport?.reportName ?? action?.childReportName ?? ''; - const taskContextReport = React.useMemo( - () => - taskReport ?? - ({ - reportID: taskReportID, - parentReportID: chatReportID, - parentReportActionID: action?.reportActionID, - ownerAccountID: action?.childOwnerAccountID, - managerID: action?.childManagerAccountID, - stateNum: action?.childStateNum, - statusNum: action?.childStatusNum, - } as Report), - [action?.childManagerAccountID, action?.childOwnerAccountID, action?.childStateNum, action?.childStatusNum, action?.reportActionID, chatReportID, taskReport, taskReportID], - ); + const taskContextReport = + taskReport ?? + ({ + reportID: taskReportID, + parentReportID: chatReportID, + parentReportActionID: action?.reportActionID, + ownerAccountID: action?.childOwnerAccountID, + managerID: action?.childManagerAccountID, + stateNum: action?.childStateNum, + statusNum: action?.childStatusNum, + } as Report); const taskTitleWithoutImage = Parser.replace(Parser.htmlToMarkdown(taskTitle), {disabledRules: [...CONST.TASK_TITLE_DISABLED_RULES]});