@@ -1084,10 +1084,38 @@ const supportedActionTypes = new Set<ReportActionName>([...Object.values(otherAc
10841084 * Checks whether an action is actionable track expense and resolved.
10851085 *
10861086 */
1087- function isResolvedActionableWhisper ( reportAction : OnyxEntry < ReportAction > ) : boolean {
1087+ function isResolvedActionableWhisper ( reportAction : OnyxEntry < ReportAction > , allActionsForReport ?: OnyxEntry < ReportActions > ) : boolean {
10881088 const originalMessage = getOriginalMessage ( reportAction ) ;
1089- const resolution = originalMessage && typeof originalMessage === 'object' && 'resolution' in originalMessage ? originalMessage ?. resolution : null ;
1090- return ! ! resolution ;
1089+ if ( ! originalMessage || typeof originalMessage !== 'object' ) {
1090+ return false ;
1091+ }
1092+ const resolution = 'resolution' in originalMessage ? originalMessage ?. resolution : null ;
1093+ if ( resolution ) {
1094+ return true ;
1095+ }
1096+
1097+ const deleted = 'deleted' in originalMessage ? originalMessage ?. deleted : null ;
1098+ if ( ! deleted ) {
1099+ return false ;
1100+ }
1101+
1102+ // For mention whispers, only treat as deleted if the parent comment is also deleted.
1103+ // This distinguishes cascade deletion (parent deleted -> whisper should hide) from
1104+ // the backend's one-per-user cleanup (parent still exists -> whisper should stay visible).
1105+ if ( reportAction ?. reportActionID && ( isActionableMentionWhisper ( reportAction ) || isActionableReportMentionWhisper ( reportAction ) ) ) {
1106+ const reportID = reportAction . reportID ;
1107+ const actions = allActionsForReport ?? ( reportID ? allReportActions ?. [ `${ ONYXKEYS . COLLECTION . REPORT_ACTIONS } ${ reportID } ` ] : undefined ) ;
1108+ if ( actions ) {
1109+ const parentOffset = isActionableReportMentionWhisper ( reportAction ) ? 2n : 1n ;
1110+ const parentActionID = String ( BigInt ( reportAction . reportActionID ) - parentOffset ) ;
1111+ const parentAction = actions [ parentActionID ] ;
1112+ if ( parentAction && ! isDeletedAction ( parentAction ) ) {
1113+ return false ;
1114+ }
1115+ }
1116+ }
1117+
1118+ return true ;
10911119}
10921120
10931121/**
@@ -1112,7 +1140,13 @@ function isResolvedConciergeDescriptionOptions(reportAction: OnyxEntry<ReportAct
11121140 * Checks if a reportAction is fit for display, meaning that it's not deprecated, is of a valid
11131141 * and supported type, it's not deleted and also not closed.
11141142 */
1115- function shouldReportActionBeVisible ( reportAction : OnyxEntry < ReportAction > , key : string | number , canUserPerformWriteAction ?: boolean , reportsParam ?: OnyxCollection < Report > ) : boolean {
1143+ function shouldReportActionBeVisible (
1144+ reportAction : OnyxEntry < ReportAction > ,
1145+ key : string | number ,
1146+ canUserPerformWriteAction ?: boolean ,
1147+ reportsParam ?: OnyxCollection < Report > ,
1148+ allActionsForReport ?: OnyxEntry < ReportActions > ,
1149+ ) : boolean {
11161150 if ( ! reportAction ) {
11171151 return false ;
11181152 }
@@ -1189,7 +1223,7 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry<ReportAction>, key:
11891223 }
11901224
11911225 // If action is actionable whisper and resolved by user, then we don't want to render anything
1192- if ( isActionableWhisper ( reportAction ) && isResolvedActionableWhisper ( reportAction ) ) {
1226+ if ( isActionableWhisper ( reportAction ) && isResolvedActionableWhisper ( reportAction , allActionsForReport ) ) {
11931227 return false ;
11941228 }
11951229
@@ -1225,22 +1259,27 @@ function isReportActionVisible(
12251259 return false ;
12261260 }
12271261
1262+ // Look up all actions for this report so the parent-check in isResolvedActionableWhisper
1263+ // can determine whether a whisper's `deleted` flag reflects a real cascade deletion
1264+ // (parent comment deleted) vs. the backend's one-per-user cleanup (parent still exists).
1265+ const reportActionsForReport = allReportActions ?. [ `${ ONYXKEYS . COLLECTION . REPORT_ACTIONS } ${ reportID } ` ] ;
1266+
12281267 // Actions with pendingAction are optimistic or in-flight, so their visibility may differ
12291268 // from what's cached in visibleReportActions (which reflects persisted Onyx data).
12301269 // We must recalculate visibility at runtime to ensure accuracy for these transient states.
12311270 if ( reportAction . pendingAction ) {
1232- return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction ) ;
1271+ return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction , undefined , reportActionsForReport ) ;
12331272 }
12341273
12351274 if ( visibleReportActions ) {
12361275 const reportCache = visibleReportActions [ reportID ] ;
12371276 if ( ! reportCache ) {
1238- return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction ) ;
1277+ return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction , undefined , reportActionsForReport ) ;
12391278 }
12401279 const staticVisibility = reportCache [ reportAction . reportActionID ] ;
12411280 // If action is not in derived value cache, fall back to runtime calculation
12421281 if ( staticVisibility === undefined ) {
1243- return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction ) ;
1282+ return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction , undefined , reportActionsForReport ) ;
12441283 }
12451284 if ( ! staticVisibility ) {
12461285 return false ;
@@ -1250,7 +1289,7 @@ function isReportActionVisible(
12501289 }
12511290 return true ;
12521291 }
1253- return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction ) ;
1292+ return shouldReportActionBeVisible ( reportAction , reportAction . reportActionID , canUserPerformWriteAction , undefined , reportActionsForReport ) ;
12541293}
12551294
12561295/**
0 commit comments