diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx
index e241f65bc646..8c43ae542932 100644
--- a/src/components/LHNOptionsList/LHNOptionsList.tsx
+++ b/src/components/LHNOptionsList/LHNOptionsList.tsx
@@ -17,6 +17,7 @@ import usePrevious from '@hooks/usePrevious';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
+import * as DraftCommentUtils from '@libs/DraftCommentUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
@@ -112,7 +113,7 @@ function LHNOptionsList({
const itemPolicy = policy?.[`${ONYXKEYS.COLLECTION.POLICY}${itemFullReport?.policyID}`] ?? null;
const transactionID = itemParentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? itemParentReportAction.originalMessage.IOUTransactionID ?? '' : '';
const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? null;
- const itemComment = draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] ?? '';
+ const hasDraftComment = DraftCommentUtils.isValidDraftComment(draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`]);
const sortedReportActions = ReportActionsUtils.getSortedReportActionsForDisplay(itemReportActions);
const lastReportAction = sortedReportActions[0];
@@ -139,7 +140,7 @@ function LHNOptionsList({
isFocused={!shouldDisableFocusOptions}
onSelectRow={onSelectRow}
preferredLocale={preferredLocale}
- comment={itemComment}
+ hasDraftComment={hasDraftComment}
transactionViolations={transactionViolations}
canUseViolations={canUseViolations}
onLayout={onLayoutItem}
@@ -163,7 +164,10 @@ function LHNOptionsList({
],
);
- const extraData = useMemo(() => [reportActions, reports, policy, personalDetails, data.length], [reportActions, reports, policy, personalDetails, data.length]);
+ const extraData = useMemo(
+ () => [reportActions, reports, policy, personalDetails, data.length, draftComments],
+ [reportActions, reports, policy, personalDetails, data.length, draftComments],
+ );
const previousOptionMode = usePrevious(optionMode);
diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx
index b1abaf3f0b5b..fbcb7d4b373d 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.tsx
+++ b/src/components/LHNOptionsList/OptionRowLHN.tsx
@@ -27,7 +27,7 @@ import CONST from '@src/CONST';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {OptionRowLHNProps} from './types';
-function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, optionItem, viewMode = 'default', style, onLayout = () => {}}: OptionRowLHNProps) {
+function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, optionItem, viewMode = 'default', style, onLayout = () => {}, hasDraftComment}: OptionRowLHNProps) {
const theme = useTheme();
const styles = useThemeStyles();
const popoverAnchor = useRef(null);
@@ -248,7 +248,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
/>
)}
- {optionItem.hasDraftComment && optionItem.isAllowedToComment && (
+ {hasDraftComment && optionItem.isAllowedToComment && (
{
- if (!optionItem || !!optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) {
- return;
- }
- Report.setReportWithDraft(reportID, true);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
return (
;
- /** Comment added to report */
- comment: string;
+ /** Whether a report contains a draft */
+ hasDraftComment: boolean;
/** The receipt transaction from the parent report action */
receiptTransactions: OnyxCollection;
@@ -133,6 +133,9 @@ type OptionRowLHNProps = {
/** The item that should be rendered */
optionItem?: OptionData;
+ /** Whether a report contains a draft */
+ hasDraftComment: boolean;
+
onLayout?: (event: LayoutChangeEvent) => void;
};
diff --git a/src/components/optionPropTypes.js b/src/components/optionPropTypes.js
index 4cfadea33d60..53d8e86b02d6 100644
--- a/src/components/optionPropTypes.js
+++ b/src/components/optionPropTypes.js
@@ -25,9 +25,6 @@ export default PropTypes.shape({
// reportID (only present when there is a matching report)
reportID: PropTypes.string,
- // Whether the report has a draft comment or not
- hasDraftComment: PropTypes.bool,
-
// Key used internally by React
keyForList: PropTypes.string,
diff --git a/src/libs/ComposerUtils/getDraftComment.ts b/src/libs/ComposerUtils/getDraftComment.ts
deleted file mode 100644
index 7f11825004a1..000000000000
--- a/src/libs/ComposerUtils/getDraftComment.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import type {OnyxEntry} from 'react-native-onyx';
-import Onyx from 'react-native-onyx';
-import ONYXKEYS from '@src/ONYXKEYS';
-
-const draftCommentMap: Record> = {};
-Onyx.connect({
- key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT,
- callback: (value, key) => {
- if (!key) {
- return;
- }
-
- const reportID = key.replace(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, '');
- draftCommentMap[reportID] = value;
- },
-});
-
-/**
- * Returns a draft comment from the onyx collection.
- * Note: You should use the HOCs/hooks to get onyx data, instead of using this directly.
- * A valid use case to use this is if the value is only needed once for an initial value.
- */
-export default function getDraftComment(reportID: string): OnyxEntry {
- return draftCommentMap[reportID];
-}
diff --git a/src/libs/DraftCommentUtils.ts b/src/libs/DraftCommentUtils.ts
new file mode 100644
index 000000000000..325da93d235e
--- /dev/null
+++ b/src/libs/DraftCommentUtils.ts
@@ -0,0 +1,47 @@
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
+import Onyx from 'react-native-onyx';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+let draftCommentCollection: OnyxCollection = {};
+Onyx.connect({
+ key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT,
+ callback: (nextVal) => {
+ draftCommentCollection = nextVal;
+ },
+ waitForCollectionCallback: true,
+});
+
+/**
+ * Returns a draft comment from the onyx collection for given reportID.
+ * Note: You should use the HOCs/hooks to get onyx data, instead of using this directly.
+ * A valid use-case of this function is outside React components, like in utility functions.
+ */
+function getDraftComment(reportID: string): OnyxEntry | null | undefined {
+ return draftCommentCollection?.[ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT + reportID];
+}
+
+/**
+ * Returns true if the report has a valid draft comment.
+ * A valid draft comment is a non-empty string.
+ */
+function isValidDraftComment(comment?: string | null): boolean {
+ return !!comment?.trim();
+}
+
+/**
+ * Returns true if the report has a valid draft comment.
+ */
+function hasValidDraftComment(reportID: string): boolean {
+ return isValidDraftComment(getDraftComment(reportID));
+}
+
+/**
+ * Prepares a draft comment by trimming it and returning null if it's empty.
+ */
+function prepareDraftComment(comment: string | null) {
+ // logical OR is used to convert empty string to null
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ return comment?.trim() || null;
+}
+
+export {getDraftComment, isValidDraftComment, hasValidDraftComment, prepareDraftComment};
diff --git a/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts b/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts
deleted file mode 100644
index 8305fa217f79..000000000000
--- a/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import type {OnyxEntry} from 'react-native-onyx';
-import type {Report} from '@src/types/onyx';
-
-type ReportWithoutHasDraft = Omit;
-
-export default function reportWithoutHasDraftSelector(report: OnyxEntry): OnyxEntry {
- if (!report) {
- return null;
- }
- const {hasDraft, ...reportWithoutHasDraft} = report;
- return reportWithoutHasDraft;
-}
-
-export type {ReportWithoutHasDraft};
diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts
index 21c178684bfe..bd8b799bdc52 100644
--- a/src/libs/OptionsListUtils.ts
+++ b/src/libs/OptionsListUtils.ts
@@ -654,7 +654,6 @@ function createOption(
login: null,
reportID: '',
phoneNumber: null,
- hasDraftComment: false,
keyForList: null,
searchText: null,
isDefaultRoom: false,
@@ -699,7 +698,6 @@ function createOption(
result.ownerAccountID = report.ownerAccountID;
result.reportID = report.reportID;
result.isUnread = ReportUtils.isUnread(report);
- result.hasDraftComment = report.hasDraft;
result.isPinned = report.isPinned;
result.iouReportID = report.iouReportID;
result.keyForList = String(report.reportID);
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 23d42efef01c..9d7b6b1d6549 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -56,6 +56,7 @@ import * as store from './actions/ReimbursementAccount/store';
import * as CollectionUtils from './CollectionUtils';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
+import {hasValidDraftComment} from './DraftCommentUtils';
import originalGetReportPolicyID from './getReportPolicyID';
import isReportMessageAttachment from './isReportMessageAttachment';
import localeCompare from './LocaleCompare';
@@ -391,7 +392,6 @@ type OptionData = {
phoneNumber?: string | null;
isUnread?: boolean | null;
isUnreadWithMention?: boolean | null;
- hasDraftComment?: boolean | null;
keyForList?: string | null;
searchText?: string | null;
isIOUReportOwner?: boolean | null;
@@ -4469,9 +4469,12 @@ function shouldReportBeInOptionList({
return true;
}
+ // Retrieve the draft comment for the report and convert it to a boolean
+ const hasDraftComment = hasValidDraftComment(report.reportID);
+
// Include reports that are relevant to the user in any view mode. Criteria include having a draft or having a GBR showing.
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- if (report.hasDraft || requiresAttentionFromCurrentUser(report)) {
+ if (hasDraftComment || requiresAttentionFromCurrentUser(report)) {
return true;
}
const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID);
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index 2c628f397390..789029779b55 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -13,6 +13,7 @@ import type {ReportActions} from '@src/types/onyx/ReportAction';
import type ReportAction from '@src/types/onyx/ReportAction';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import * as CollectionUtils from './CollectionUtils';
+import {hasValidDraftComment} from './DraftCommentUtils';
import localeCompare from './LocaleCompare';
import * as LocalePhoneNumber from './LocalePhoneNumber';
import * as Localize from './Localize';
@@ -139,7 +140,7 @@ function getOrderedReportIDs(
const reportAction = ReportActionsUtils.getReportAction(report.parentReportID ?? '', report.parentReportActionID ?? '');
if (isPinned || ReportUtils.requiresAttentionFromCurrentUser(report, reportAction)) {
pinnedAndGBRReports.push(report);
- } else if (report.hasDraft) {
+ } else if (hasValidDraftComment(report.reportID)) {
draftReports.push(report);
} else if (ReportUtils.isArchivedRoom(report)) {
archivedReports.push(report);
@@ -214,7 +215,6 @@ function getOptionData({
phoneNumber: null,
isUnread: null,
isUnreadWithMention: null,
- hasDraftComment: false,
keyForList: null,
searchText: null,
isPinned: false,
@@ -263,7 +263,6 @@ function getOptionData({
// setting it Unread so we add additional condition here to avoid empty chat LHN from being bold.
result.isUnread = ReportUtils.isUnread(report) && !!report.lastActorAccountID;
result.isUnreadWithMention = ReportUtils.isUnreadWithMention(report);
- result.hasDraftComment = report.hasDraft;
result.isPinned = report.isPinned;
result.iouReportID = report.iouReportID;
result.keyForList = String(report.reportID);
diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts
index 778b519b9180..42a2852e42a6 100644
--- a/src/libs/actions/Policy.ts
+++ b/src/libs/actions/Policy.ts
@@ -160,7 +160,6 @@ Onyx.connect({
return;
}
const {reportID} = policyReport;
- cleanUpMergeQueries[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] = {hasDraft: false};
cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null;
cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null;
});
@@ -362,7 +361,6 @@ function deleteWorkspace(policyID: string, policyName: string) {
value: {
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
- hasDraft: false,
oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '',
policyName: '',
},
@@ -412,14 +410,13 @@ function deleteWorkspace(policyID: string, policyName: string) {
];
reportsToArchive.forEach((report) => {
- const {reportID, stateNum, statusNum, hasDraft, oldPolicyName} = report ?? {};
+ const {reportID, stateNum, statusNum, oldPolicyName} = report ?? {};
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
stateNum,
statusNum,
- hasDraft,
oldPolicyName,
policyName: report?.policyName,
},
@@ -881,7 +878,6 @@ function removeMembers(accountIDs: number[], policyID: string) {
statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
oldPolicyName: policy.name,
- hasDraft: false,
pendingChatMembers,
},
});
@@ -936,14 +932,13 @@ function removeMembers(accountIDs: number[], policyID: string) {
const filteredWorkspaceChats = workspaceChats.filter((report): report is Report => report !== null);
- filteredWorkspaceChats.forEach(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => {
+ filteredWorkspaceChats.forEach(({reportID, stateNum, statusNum, oldPolicyName = null}) => {
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
value: {
stateNum,
statusNum,
- hasDraft,
oldPolicyName,
},
});
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 3605f8c39962..179ee87862ff 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -44,6 +44,7 @@ import type UpdateRoomVisibilityParams from '@libs/API/parameters/UpdateRoomVisi
import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import * as CollectionUtils from '@libs/CollectionUtils';
import DateUtils from '@libs/DateUtils';
+import {prepareDraftComment} from '@libs/DraftCommentUtils';
import * as EmojiUtils from '@libs/EmojiUtils';
import * as Environment from '@libs/Environment/Environment';
import * as ErrorUtils from '@libs/ErrorUtils';
@@ -1073,9 +1074,10 @@ function togglePinnedState(reportID: string, isPinnedChat: boolean) {
/**
* Saves the comment left by the user as they are typing. By saving this data the user can switch between chats, close
* tab, refresh etc without worrying about loosing what they typed out.
+ * When empty string or null is passed, it will delete the draft comment from Onyx store.
*/
-function saveReportComment(reportID: string, comment: string) {
- Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, comment);
+function saveReportDraftComment(reportID: string, comment: string | null) {
+ Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, prepareDraftComment(comment));
}
/** Saves the number of lines for the comment */
@@ -1083,11 +1085,6 @@ function saveReportCommentNumberOfLines(reportID: string, numberOfLines: number)
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines);
}
-/** Immediate indication whether the report has a draft comment. */
-function setReportWithDraft(reportID: string, hasDraft: boolean): Promise {
- return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft});
-}
-
/** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */
function broadcastUserIsTyping(reportID: string) {
const privateReportChannelName = getReportChannelName(reportID);
@@ -2908,17 +2905,6 @@ function clearNewRoomFormError() {
});
}
-function getReportDraftStatus(reportID: string) {
- if (!allReports) {
- return false;
- }
-
- if (!allReports[reportID]) {
- return false;
- }
- return allReports[reportID]?.hasDraft;
-}
-
function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEntry, resolution: ValueOf) {
const message = reportAction?.message?.[0];
if (!message) {
@@ -2973,7 +2959,6 @@ function setGroupDraft(participants: Array<{login: string; accountID: number}>,
}
export {
- getReportDraftStatus,
searchInServer,
addComment,
addAttachment,
@@ -2984,7 +2969,7 @@ export {
subscribeToReportLeavingEvents,
unsubscribeFromReportChannel,
unsubscribeFromLeavingRoomReportChannel,
- saveReportComment,
+ saveReportDraftComment,
saveReportCommentNumberOfLines,
broadcastUserIsTyping,
broadcastUserIsLeavingRoom,
@@ -2996,7 +2981,6 @@ export {
saveReportActionDraftNumberOfLines,
deleteReportComment,
navigateToConciergeChat,
- setReportWithDraft,
addPolicyReport,
deleteReport,
navigateToConciergeChatAndDeleteReport,
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 515a63aa5265..1b8957b833b0 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -23,8 +23,6 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as HeaderUtils from '@libs/HeaderUtils';
import Navigation from '@libs/Navigation/Navigation';
-import type {ReportWithoutHasDraft} from '@libs/OnyxSelectors/reportWithoutHasDraftSelector';
-import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
@@ -49,7 +47,7 @@ type HeaderViewOnyxProps = {
personalDetails: OnyxEntry;
/** Parent report */
- parentReport: OnyxEntry;
+ parentReport: OnyxEntry;
/** The current policy of the report */
policy: OnyxEntry;
@@ -377,8 +375,7 @@ export default memo(
initialValue: null,
},
parentReport: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? report?.reportID}`,
- selector: reportWithoutHasDraftSelector,
+ key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID ?? report?.reportID}`,
},
session: {
key: ONYXKEYS.SESSION,
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index ef277984b4e9..940cba181db7 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -30,8 +30,6 @@ import Timing from '@libs/actions/Timing';
import * as Browser from '@libs/Browser';
import Navigation from '@libs/Navigation/Navigation';
import clearReportNotifications from '@libs/Notification/clearReportNotifications';
-import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector';
-import type {ReportWithoutHasDraft} from '@libs/OnyxSelectors/reportWithoutHasDraftSelector';
import Performance from '@libs/Performance';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
@@ -79,7 +77,7 @@ type ReportScreenOnyxPropsWithoutParentReportAction = {
sortedAllReportActions: OnyxTypes.ReportAction[];
/** The report currently being looked at */
- report: OnyxEntry;
+ report: OnyxEntry;
/** The report metadata loading states */
reportMetadata: OnyxEntry;
@@ -711,7 +709,6 @@ export default withCurrentReportID(
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`,
allowStaleData: true,
- selector: reportWithoutHasDraftSelector,
},
reportMetadata: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${getReportID(route)}`,
diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
index ab3ed32f5ee8..bfcef66e7c54 100644
--- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
+++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx
@@ -29,8 +29,8 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import * as Browser from '@libs/Browser';
import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
import * as ComposerUtils from '@libs/ComposerUtils';
-import getDraftComment from '@libs/ComposerUtils/getDraftComment';
import convertToLTRForComposer from '@libs/convertToLTRForComposer';
+import {getDraftComment} from '@libs/DraftCommentUtils';
import * as EmojiUtils from '@libs/EmojiUtils';
import focusComposerWithDelay from '@libs/focusComposerWithDelay';
import getPlatform from '@libs/getPlatform';
@@ -333,7 +333,7 @@ function ComposerWithSuggestions(
const debouncedSaveReportComment = useMemo(
() =>
lodashDebounce((selectedReportID, newComment) => {
- Report.saveReportComment(selectedReportID, newComment || '');
+ Report.saveReportDraftComment(selectedReportID, newComment);
isCommentPendingSaved.current = false;
}, 1000),
[],
@@ -426,29 +426,12 @@ function ComposerWithSuggestions(
});
}
- // Indicate that draft has been created.
- if (commentRef.current.length === 0 && newCommentConverted.length !== 0) {
- Report.setReportWithDraft(reportID, true);
- }
-
- const hasDraftStatus = Report.getReportDraftStatus(reportID);
-
- /**
- * The extra `!hasDraftStatus` check is to prevent the draft being set
- * when the user navigates to the ReportScreen. This doesn't alter anything
- * in terms of functionality.
- */
- // The draft has been deleted.
- if (newCommentConverted.length === 0 && hasDraftStatus) {
- Report.setReportWithDraft(reportID, false);
- }
-
commentRef.current = newCommentConverted;
if (shouldDebounceSaveComment) {
isCommentPendingSaved.current = true;
debouncedSaveReportComment(reportID, newCommentConverted);
} else {
- Report.saveReportComment(reportID, newCommentConverted || '');
+ Report.saveReportDraftComment(reportID, newCommentConverted);
}
if (newCommentConverted) {
debouncedBroadcastUserIsTyping(reportID);
diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
index 70340e9e1fec..5f7f5adebfc9 100644
--- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
+++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx
@@ -24,8 +24,8 @@ import useNetwork from '@hooks/useNetwork';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus';
-import getDraftComment from '@libs/ComposerUtils/getDraftComment';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
+import {getDraftComment} from '@libs/DraftCommentUtils';
import getModalState from '@libs/getModalState';
import * as ReportUtils from '@libs/ReportUtils';
import playSound, {SOUNDS} from '@libs/Sound';
diff --git a/src/pages/home/report/ReportActionItemCreated.tsx b/src/pages/home/report/ReportActionItemCreated.tsx
index 4fe52f6adf41..899575a9aa3a 100644
--- a/src/pages/home/report/ReportActionItemCreated.tsx
+++ b/src/pages/home/report/ReportActionItemCreated.tsx
@@ -10,7 +10,6 @@ import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
-import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector';
import * as ReportUtils from '@libs/ReportUtils';
import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report';
import CONST from '@src/CONST';
@@ -97,7 +96,6 @@ ReportActionItemCreated.displayName = 'ReportActionItemCreated';
export default withOnyx({
report: {
key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
- selector: reportWithoutHasDraftSelector,
},
policy: {
diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js
index 0c1f8b8c5972..34af75308ac4 100644
--- a/src/pages/home/sidebar/SidebarLinksData.js
+++ b/src/pages/home/sidebar/SidebarLinksData.js
@@ -93,6 +93,8 @@ const propTypes = {
),
}),
+ reportsDrafts: PropTypes.objectOf(PropTypes.string),
+
...withCurrentUserPersonalDetailsPropTypes,
};
@@ -105,6 +107,7 @@ const defaultProps = {
policyMembers: {},
transactionViolations: {},
allReportActions: {},
+ reportsDrafts: {},
...withCurrentUserPersonalDetailsDefaultProps,
};
@@ -123,6 +126,7 @@ function SidebarLinksData({
policyMembers,
transactionViolations,
currentUserPersonalDetails,
+ reportsDrafts,
}) {
const styles = useThemeStyles();
const {activeWorkspaceID} = useActiveWorkspace();
@@ -138,8 +142,20 @@ function SidebarLinksData({
const isLoading = isLoadingApp;
const optionItemsMemoized = useMemo(
- () => SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions, transactionViolations, activeWorkspaceID, policyMemberAccountIDs),
- [chatReports, betas, policies, priorityMode, allReportActions, transactionViolations, activeWorkspaceID, policyMemberAccountIDs],
+ () =>
+ SidebarUtils.getOrderedReportIDs(
+ null,
+ chatReports,
+ betas,
+ policies,
+ priorityMode,
+ allReportActions,
+ transactionViolations,
+ activeWorkspaceID,
+ policyMemberAccountIDs,
+ reportsDrafts,
+ ),
+ [chatReports, betas, policies, priorityMode, allReportActions, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, reportsDrafts],
);
const optionListItems = useMemo(() => {
@@ -219,7 +235,6 @@ const chatReportSelector = (report) =>
report && {
reportID: report.reportID,
participantAccountIDs: report.participantAccountIDs,
- hasDraft: report.hasDraft,
isPinned: report.isPinned,
isHidden: report.isHidden,
notificationPreference: report.notificationPreference,
@@ -329,6 +344,10 @@ export default compose(
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
initialValue: {},
},
+ reportsDrafts: {
+ key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT,
+ initialValue: {},
+ },
}),
)(
/*
@@ -351,6 +370,7 @@ export default compose(
_.isEqual(prevProps.policyMembers, nextProps.policyMembers) &&
_.isEqual(prevProps.transactionViolations, nextProps.transactionViolations) &&
_.isEqual(prevProps.currentUserPersonalDetails, nextProps.currentUserPersonalDetails) &&
- prevProps.currentReportID === nextProps.currentReportID,
+ prevProps.currentReportID === nextProps.currentReportID &&
+ _.isEqual(prevProps.reportsDrafts, nextProps.reportsDrafts),
),
);
diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts
index ce20462df372..7e39e7f65a22 100644
--- a/src/types/onyx/Report.ts
+++ b/src/types/onyx/Report.ts
@@ -132,7 +132,6 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback<
parentReportID?: string;
parentReportActionID?: string;
isOptimisticReport?: boolean;
- hasDraft?: boolean;
managerID?: number;
lastVisibleActionLastModified?: string;
displayName?: string;
diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js
index f95fe3e484e9..d590236e5256 100644
--- a/tests/unit/OptionsListUtilsTest.js
+++ b/tests/unit/OptionsListUtilsTest.js
@@ -17,7 +17,6 @@ describe('OptionsListUtils', () => {
participantAccountIDs: [2, 1],
visibleChatMemberAccountIDs: [2, 1],
reportName: 'Iron Man, Mister Fantastic',
- hasDraft: true,
type: CONST.REPORT.TYPE.CHAT,
},
2: {
diff --git a/tests/unit/SidebarFilterTest.ts b/tests/unit/SidebarFilterTest.ts
index 05771980dcf3..3f8678fa8cf9 100644
--- a/tests/unit/SidebarFilterTest.ts
+++ b/tests/unit/SidebarFilterTest.ts
@@ -24,6 +24,7 @@ const ONYXKEYS = {
REPORT: 'report_',
REPORT_ACTIONS: 'reportActions_',
POLICY: 'policy_',
+ REPORT_DRAFT_COMMENT: 'reportDraftComment_',
},
NETWORK: 'network',
} as const;
@@ -110,7 +111,6 @@ xdescribe('Sidebar', () => {
// Given a new report with a draft text
const report: Report = {
...LHNTestUtils.getFakeReport([1, 2], 0),
- hasDraft: true,
};
const reportCollectionDataSet: ReportCollectionDataSet = {
@@ -124,6 +124,7 @@ xdescribe('Sidebar', () => {
Onyx.multiSet({
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`]: 'This is a draft message',
...reportCollectionDataSet,
}),
)
@@ -330,9 +331,9 @@ xdescribe('Sidebar', () => {
// const boolArr = [false, false, false, false, false];
it(`the booleans ${JSON.stringify(boolArr)}`, () => {
- const [isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft] = boolArr;
+ const [isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned] = boolArr;
const report2: Report = {
- ...LHNTestUtils.getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft),
+ ...LHNTestUtils.getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned),
policyID: policy.policyID,
};
LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID);
@@ -453,7 +454,6 @@ xdescribe('Sidebar', () => {
// Given a draft report and a pinned report
const draftReport = {
...LHNTestUtils.getFakeReport([1, 2]),
- hasDraft: true,
};
const pinnedReport = {
...LHNTestUtils.getFakeReport([3, 4]),
@@ -474,6 +474,7 @@ xdescribe('Sidebar', () => {
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${draftReport.reportID}`]: 'draft report message',
...reportCollectionDataSet,
}),
)
@@ -676,7 +677,7 @@ xdescribe('Sidebar', () => {
it(`the booleans ${JSON.stringify(boolArr)}`, () => {
const [isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft] = boolArr;
const report2 = {
- ...LHNTestUtils.getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft),
+ ...LHNTestUtils.getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned),
policyID: policy.policyID,
};
LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID);
@@ -696,6 +697,7 @@ xdescribe('Sidebar', () => {
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
[`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy,
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report2.reportID}`]: hasDraft ? 'report2 draft' : null,
...reportCollectionDataSet,
}),
)
diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts
index 27da8348f43d..2758d43fb81e 100644
--- a/tests/unit/SidebarOrderTest.ts
+++ b/tests/unit/SidebarOrderTest.ts
@@ -25,6 +25,7 @@ const ONYXKEYS = {
REPORT: 'report_',
REPORT_ACTIONS: 'reportActions_',
POLICY: 'policy_',
+ REPORT_DRAFT_COMMENT: 'reportDraftComment_',
},
NETWORK: 'network',
IS_LOADING_REPORT_DATA: 'isLoadingReportData',
@@ -163,7 +164,6 @@ describe('Sidebar', () => {
// And the currently viewed report is the first report
const report1 = {
...LHNTestUtils.getFakeReport([1, 2], 3),
- hasDraft: true,
};
const report2 = LHNTestUtils.getFakeReport([3, 4], 2);
const report3 = LHNTestUtils.getFakeReport([5, 6], 1);
@@ -190,6 +190,7 @@ describe('Sidebar', () => {
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report1.reportID}`]: 'report1 draft',
...reportCollectionDataSet,
}),
)
@@ -455,7 +456,6 @@ describe('Sidebar', () => {
const report1 = LHNTestUtils.getFakeReport([1, 2], 3);
const report2: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([3, 4], 2),
- hasDraft: true,
};
const report3 = LHNTestUtils.getFakeReport([5, 6], 1);
@@ -481,6 +481,7 @@ describe('Sidebar', () => {
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
+ [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT + report2.reportID]: 'This is a draft',
...reportCollectionDataSet,
}),
)
@@ -513,7 +514,6 @@ describe('Sidebar', () => {
// And the report has a draft
const report: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([1, 2]),
- hasDraft: true,
};
const reportCollectionDataSet: ReportCollectionDataSet = {
@@ -528,6 +528,7 @@ describe('Sidebar', () => {
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`]: 'This is a draft',
...reportCollectionDataSet,
}),
)
@@ -538,7 +539,7 @@ describe('Sidebar', () => {
})
// When the draft is removed
- .then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, {hasDraft: null}))
+ .then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`, null))
// Then the pencil icon goes away
.then(() => {
@@ -599,7 +600,6 @@ describe('Sidebar', () => {
};
const report2: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([3, 4], 2),
- hasDraft: true,
};
const report3: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([5, 6], 1),
@@ -643,6 +643,7 @@ describe('Sidebar', () => {
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
[ONYXKEYS.SESSION]: {accountID: currentlyLoggedInUserAccountID},
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report2.reportID}`]: 'Report2 draft comment',
...reportCollectionDataSet,
}),
)
@@ -734,19 +735,15 @@ describe('Sidebar', () => {
// and they all have drafts
const report1: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([1, 2], 3),
- hasDraft: true,
};
const report2: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([3, 4], 2),
- hasDraft: true,
};
const report3: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([5, 6], 1),
- hasDraft: true,
};
const report4: OnyxTypes.Report = {
...LHNTestUtils.getFakeReport([7, 8], 0),
- hasDraft: true,
};
LHNTestUtils.getDefaultRenderedSidebarLinks('0');
@@ -757,6 +754,12 @@ describe('Sidebar', () => {
[`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3,
};
+ const reportDraftCommentCollectionDataSet = {
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report1.reportID}`]: 'report1 draft',
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report2.reportID}`]: 'report2 draft',
+ [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report3.reportID}`]: 'report3 draft',
+ };
+
return (
waitForBatchedUpdates()
// When Onyx is updated with the data and the sidebar re-renders
@@ -765,6 +768,7 @@ describe('Sidebar', () => {
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
[ONYXKEYS.IS_LOADING_APP]: false,
+ ...reportDraftCommentCollectionDataSet,
...reportCollectionDataSet,
}),
)
@@ -780,7 +784,12 @@ describe('Sidebar', () => {
})
// When a new report is added
- .then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report4.reportID}`, report4))
+ .then(() =>
+ Promise.all([
+ Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report4.reportID}`, report4),
+ Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report4.reportID}`, 'report4 draft'),
+ ]),
+ )
// Then they are still in alphabetical order
.then(() => {
diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx
index f6eee590313b..2dfb31f7cfe4 100644
--- a/tests/utils/LHNTestUtils.tsx
+++ b/tests/utils/LHNTestUtils.tsx
@@ -217,7 +217,7 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast =
};
}
-function getAdvancedFakeReport(isArchived: boolean, isUserCreatedPolicyRoom: boolean, hasAddWorkspaceError: boolean, isUnread: boolean, isPinned: boolean, hasDraft: boolean): Report {
+function getAdvancedFakeReport(isArchived: boolean, isUserCreatedPolicyRoom: boolean, hasAddWorkspaceError: boolean, isUnread: boolean, isPinned: boolean): Report {
return {
...getFakeReport([1, 2], 0, isUnread),
type: CONST.REPORT.TYPE.CHAT,
@@ -226,7 +226,6 @@ function getAdvancedFakeReport(isArchived: boolean, isUserCreatedPolicyRoom: boo
stateNum: isArchived ? CONST.REPORT.STATE_NUM.APPROVED : 0,
errorFields: hasAddWorkspaceError ? {1708946640843000: {addWorkspaceRoom: 'blah'}} : undefined,
isPinned,
- hasDraft,
};
}
diff --git a/tests/utils/collections/reports.ts b/tests/utils/collections/reports.ts
index 34ce66ab495d..60908e72c2de 100644
--- a/tests/utils/collections/reports.ts
+++ b/tests/utils/collections/reports.ts
@@ -8,7 +8,6 @@ export default function createRandomReport(index: number): Report {
chatType: rand(Object.values(CONST.REPORT.CHAT_TYPE)),
currency: randCurrencyCode(),
displayName: randWord(),
- hasDraft: randBoolean(),
ownerAccountID: index,
isPinned: randBoolean(),
isOptimisticReport: randBoolean(),