Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LHN debugging to Debug Mode #48442

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
852e9dc
feat: add reasons for showing reports in LHN
pac-guerreiro Aug 30, 2024
b2fcff1
refactor(debug): apply chirag suggestions
pac-guerreiro Sep 20, 2024
eb30a1a
refactor(debug): reduce type cast usage
pac-guerreiro Sep 20, 2024
62f9775
fix(debug): missing spanish translations for empty field sections
pac-guerreiro Sep 20, 2024
2336e43
fix(debug): merge conflicts
pac-guerreiro Sep 20, 2024
31a8c94
fix(debug): styles
pac-guerreiro Sep 20, 2024
efd60b6
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Sep 20, 2024
4e0da01
chore(debug): fix linter issues
pac-guerreiro Sep 23, 2024
772c0d1
chore(debug): fix unit tests
pac-guerreiro Sep 23, 2024
3a36d86
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Sep 23, 2024
a90b8c2
fix(debug): missing report action for invoices missing a bank account
pac-guerreiro Sep 24, 2024
76ef287
refactor(debug): apply suggestions
pac-guerreiro Sep 24, 2024
b509c00
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Sep 24, 2024
9d5fbca
refactor(debug): apply linter
pac-guerreiro Sep 25, 2024
0b4d1f0
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Oct 1, 2024
c9ad33e
fix(debug): missing view button for GBR triggered by pending task
pac-guerreiro Oct 1, 2024
e3ed10f
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Oct 2, 2024
85db7dd
refactor(debug): eliminate flakiness from debug mode
pac-guerreiro Oct 3, 2024
d5f274d
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Oct 3, 2024
35004c7
fix(debug): GBR can be true when RBR is true in report debug page
pac-guerreiro Oct 3, 2024
9bc09dc
refactor: apply suggestions
pac-guerreiro Oct 3, 2024
0123142
chore(debug): apply suggestions and fix RBR debugging edge cases
pac-guerreiro Oct 3, 2024
320b72f
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Oct 3, 2024
30b0308
refactor(debug): apply suggestions
pac-guerreiro Oct 4, 2024
8260bb1
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Oct 4, 2024
1fff689
refactor: apply suggestions
pac-guerreiro Oct 4, 2024
8c48c05
Merge branch 'main' into pac-guerreiro/feature/46992-add-lhn-debuggin…
pac-guerreiro Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5798,6 +5798,27 @@ const CONST = {
REPORT_ACTIONS: 'actions',
REPORT_ACTION_PREVIEW: 'preview',
},

REPORT_IN_LHN_REASONS: {
HAS_DRAFT_COMMENT: 'hasDraftComment',
HAS_GBR: 'hasGBR',
PINNED_BY_USER: 'pinnedByUser',
HAS_IOU_VIOLATIONS: 'hasIOUViolations',
HAS_ADD_WORKSPACE_ROOM_ERRORS: 'hasAddWorkspaceRoomErrors',
IS_UNREAD: 'isUnread',
IS_ARCHIVED: 'isArchived',
IS_SELF_DM: 'isSelfDM',
IS_FOCUSED: 'isFocused',
DEFAULT: 'default',
},

REQUIRES_ATTENTION_REASONS: {
HAS_JOIN_REQUEST: 'hasJoinRequest',
IS_UNREAD_WITH_MENTION: 'isUnreadWithMention',
IS_WAITING_FOR_ASSIGNEE_TO_COMPLETE_ACTION: 'isWaitingForAssigneeToCompleteAction',
HAS_CHILD_REPORT_AWAITING_ACTION: 'hasChildReportAwaitingAction',
HAS_MISSING_INVOICE_BANK_ACCOUNT: 'hasMissingInvoiceBankAccount',
},
} as const;

type Country = keyof typeof CONST.ALL_COUNTRIES;
Expand Down
34 changes: 17 additions & 17 deletions src/components/TimePicker/TimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type TimePickerProps = {
/** Whether the time value should be validated */
shouldValidate?: boolean;

/** Whether the picker shows hours, minutes, seconds and miliseconds */
/** Whether the picker shows hours, minutes, seconds and milliseconds */
showFullFormat?: boolean;
};

Expand Down Expand Up @@ -88,7 +88,7 @@ function replaceRangeWithZeros(originalString: string, from: number, to: number,
}

/**
* Clear the value under selection of an input (either hours, minutes, seconds or miliseconds) by replacing it with zeros
* Clear the value under selection of an input (either hours, minutes, seconds or milliseconds) by replacing it with zeros
*
* @param value - current value of the input
* @param selection - current selection of the input
Expand Down Expand Up @@ -135,7 +135,7 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
const [hours, setHours] = useState(() => DateUtils.get12HourTimeObjectFromDate(value, showFullFormat).hour);
const [minutes, setMinutes] = useState(() => DateUtils.get12HourTimeObjectFromDate(value, showFullFormat).minute);
const [seconds, setSeconds] = useState(() => DateUtils.get12HourTimeObjectFromDate(value, showFullFormat).seconds);
const [miliseconds, setMiliseconds] = useState(() => DateUtils.get12HourTimeObjectFromDate(value, showFullFormat).miliseconds);
const [milliseconds, setMilliseconds] = useState(() => DateUtils.get12HourTimeObjectFromDate(value, showFullFormat).milliseconds);
const [amPmValue, setAmPmValue] = useState(() => DateUtils.get12HourTimeObjectFromDate(value, showFullFormat).period);

const lastPressedKey = useRef('');
Expand Down Expand Up @@ -189,7 +189,7 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
setSelectionSecond({start: 0, end: 0});
};

const resetMiliseconds = () => {
const resetMilliseconds = () => {
setMinutes('000');
setSelectionMilisecond({start: 0, end: 0});
};
Expand Down Expand Up @@ -440,14 +440,14 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
};

/*
This function receives value from the miliseconds input and validates it.
This function receives value from the milliseconds input and validates it.
The valid format is SSS(from 000 to 999). If the user enters 9, it will be prepended to 009. If the user tries to change 999 to 9999, it would skip the character
*/
const handleMilisecondsChange = (text: string) => {
const handleMillisecondsChange = (text: string) => {
// Replace spaces with 0 to implement the following digit removal by pressing space
const trimmedText = text.replace(/ /g, '0');
if (!trimmedText) {
resetMiliseconds();
resetMilliseconds();
return;
}

Expand All @@ -460,7 +460,7 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
let newSelection;

if (selectionMilisecond.start === 0 && selectionMilisecond.end === 0) {
// The cursor is at the start of miliseconds
// The cursor is at the start of milliseconds
const firstDigit = trimmedText[0];
const secondDigit = trimmedText[2] || '0';
const thirdDigit = trimmedText[3] || '0';
Expand Down Expand Up @@ -514,10 +514,10 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
}

if (Number(newMilisecond) > 999) {
newMilisecond = miliseconds;
newMilisecond = milliseconds;
}

setMiliseconds(newMilisecond);
setMilliseconds(newMilisecond);
setSelectionMilisecond({start: newSelection, end: newSelection});
};

Expand Down Expand Up @@ -563,7 +563,7 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
return;
}

clearSelectedValue(miliseconds, selectionMilisecond, setMiliseconds, setSelectionMilisecond, 3);
clearSelectedValue(milliseconds, selectionMilisecond, setMilliseconds, setSelectionMilisecond, 3);
}
return;
}
Expand All @@ -576,11 +576,11 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
} else if (isSecondFocused) {
handleSecondsChange(insertAtPosition(seconds, trimmedKey, selectionSecond.start, selectionSecond.end));
} else if (isMilisecondFocused) {
handleMilisecondsChange(insertAtPosition(miliseconds, trimmedKey, selectionMilisecond.start, selectionMilisecond.end));
handleMillisecondsChange(insertAtPosition(milliseconds, trimmedKey, selectionMilisecond.start, selectionMilisecond.end));
}
},
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
[minutes, hours, seconds, miliseconds, selectionMinute, selectionHour, selectionSecond, selectionMilisecond],
[minutes, hours, seconds, milliseconds, selectionMinute, selectionHour, selectionSecond, selectionMilisecond],
);

useEffect(() => {
Expand Down Expand Up @@ -690,12 +690,12 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
}, [canUseTouchScreen, updateAmountNumberPad]);

useEffect(() => {
onInputChange(showFullFormat ? `${hours}:${minutes}:${seconds}.${miliseconds} ${amPmValue}` : `${hours}:${minutes} ${amPmValue}`);
onInputChange(showFullFormat ? `${hours}:${minutes}:${seconds}.${milliseconds} ${amPmValue}` : `${hours}:${minutes} ${amPmValue}`);
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [hours, minutes, amPmValue]);

const handleSubmit = () => {
const time = showFullFormat ? `${hours}:${minutes}:${seconds}.${miliseconds}` : `${hours}:${minutes} ${amPmValue}`;
const time = showFullFormat ? `${hours}:${minutes}:${seconds}.${milliseconds}` : `${hours}:${minutes} ${amPmValue}`;
const isValid = validate(time);

if (isValid) {
Expand Down Expand Up @@ -796,12 +796,12 @@ function TimePicker({defaultValue = '', onSubmit, onInputChange = () => {}, shou
<Text style={[styles.timePickerSemiDot, showFullFormat && [styles.textXXLarge, {height: undefined}]]}>{CONST.COLON}</Text>
<AmountTextInput
placeholder={numberFormat(0)}
formattedAmount={miliseconds}
formattedAmount={milliseconds}
onKeyPress={(e) => {
lastPressedKey.current = e.nativeEvent.key;
handleFocusOnBackspace(e);
}}
onChangeAmount={handleMilisecondsChange}
onChangeAmount={handleMillisecondsChange}
ref={(textInputRef) => {
updateRefs('milisecondRef', textInputRef);
milisecondInputRef.current = textInputRef as TextInput | null;
Expand Down
23 changes: 23 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4915,6 +4915,29 @@ const translations = {
date: 'Date',
time: 'Time',
none: 'None',
visibleInLHN: 'Visible in LHN',
GBR: 'GBR',
RBR: 'RBR',
true: 'true',
false: 'false',
reasonVisibleInLHN: {
hasDraftComment: 'Has draft comment',
hasGBR: 'Has GBR',
pinnedByUser: 'Pinned by user',
hasIOUViolations: 'Has IOU violations',
hasAddWorkspaceRoomErrors: 'Has add workspace room errors',
isUnread: 'Is unread (focus mode)',
isArchived: 'Is archived (most recent mode)',
isSelfDM: 'Is self DM',
isFocused: 'Is temporarily focused',
},
reasonGBR: {
hasJoinRequest: 'Has join request (admin room)',
isUnreadWithMention: 'Is unread with mention',
isWaitingForAssigneeToCompleteAction: 'Is waiting for assignee to complete action',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isWaitingForAssigneeToCompleteAction: 'Is waiting for assignee to complete action',
isWaitingForAssigneeToCompleteAction: 'Is waiting for assignee to complete task',

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

action is more inclusive than task

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but isWaitingForAssigneeToCompleteAction only be used in case the user needs to complete the task.

Anyways, It still is fine if we keep 'action'.

hasChildReportAwaitingAction: 'Has child report awaiting action',
hasMissingInvoiceBankAccount: 'Has missing invoice bank account',
},
},
};

Expand Down
23 changes: 23 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5427,6 +5427,29 @@ const translations = {
date: 'Fecha',
time: 'Hora',
none: 'Ninguno',
visibleInLHN: 'Visible en LHN',
mountiny marked this conversation as resolved.
Show resolved Hide resolved
GBR: 'GBR',
RBR: 'RBR',
true: 'verdadero',
false: 'falso',
reasonVisibleInLHN: {
hasDraftComment: 'Tiene comentario en borrador',
hasGBR: 'Tiene GBR',
pinnedByUser: 'Fijado por el usuario',
hasIOUViolations: 'Tiene violaciones de IOU',
hasAddWorkspaceRoomErrors: 'Tiene errores al agregar sala de espacio de trabajo',
isUnread: 'No leído (modo de enfoque)',
isArchived: 'Archivado (modo más reciente)',
isSelfDM: 'Es un mensaje directo propio',
isFocused: 'Está temporalmente enfocado',
},
reasonGBR: {
hasJoinRequest: 'Tiene solicitud de unión (sala de administrador)',
isUnreadWithMention: 'No leído con mención',
isWaitingForAssigneeToCompleteAction: 'Esperando a que el asignado complete la acción',
hasChildReportAwaitingAction: 'Informe secundario pendiente de acción',
hasMissingInvoiceBankAccount: 'Falta la cuenta bancaria de la factura',
},
},
};

Expand Down
8 changes: 4 additions & 4 deletions src/libs/DateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,15 +616,15 @@ const combineDateAndTime = (updatedTime: string, inputDateTime: string): string
/**
* param {String} dateTime in 'HH:mm:ss.SSS a' format
* returns {Object}
* example {hour: '11', minute: '10', seconds: '10', miliseconds: '123', period: 'AM'}
* example {hour: '11', minute: '10', seconds: '10', milliseconds: '123', period: 'AM'}
*/
function get12HourTimeObjectFromDate(dateTime: string, isFullFormat = false): {hour: string; minute: string; seconds: string; miliseconds: string; period: string} {
function get12HourTimeObjectFromDate(dateTime: string, isFullFormat = false): {hour: string; minute: string; seconds: string; milliseconds: string; period: string} {
if (!dateTime) {
return {
hour: '12',
minute: '00',
seconds: '00',
miliseconds: '000',
milliseconds: '000',
period: 'PM',
};
}
Expand All @@ -633,7 +633,7 @@ function get12HourTimeObjectFromDate(dateTime: string, isFullFormat = false): {h
hour: format(parsedTime, 'hh'),
minute: format(parsedTime, 'mm'),
seconds: isFullFormat ? format(parsedTime, 'ss') : '00',
miliseconds: isFullFormat ? format(parsedTime, 'SSS') : '000',
milliseconds: isFullFormat ? format(parsedTime, 'SSS') : '000',
period: format(parsedTime, 'a').toUpperCase(),
};
}
Expand Down
107 changes: 105 additions & 2 deletions src/libs/DebugUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
/* eslint-disable max-classes-per-file */
import {isMatch} from 'date-fns';
import isValid from 'date-fns/isValid';
import type {OnyxEntry} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import CONST from '@src/CONST';
import type {Report, ReportAction} from '@src/types/onyx';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Beta, Policy, Report, ReportAction, ReportActions, TransactionViolation} from '@src/types/onyx';
import * as OptionsListUtils from './OptionsListUtils';
import * as ReportUtils from './ReportUtils';

class NumberError extends SyntaxError {
constructor() {
Expand Down Expand Up @@ -107,6 +112,40 @@ const REPORT_ACTION_BOOLEAN_PROPERTIES: Array<keyof ReportAction> = [

const REPORT_ACTION_DATE_PROPERTIES: Array<keyof ReportAction> = ['created', 'lastModified'] satisfies Array<keyof ReportAction>;

let isInFocusMode: OnyxEntry<boolean>;
Onyx.connect({
key: ONYXKEYS.NVP_PRIORITY_MODE,
callback: (priorityMode) => {
isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD;
},
});

let policies: OnyxCollection<Policy>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => {
policies = value;
},
});

let transactionViolations: OnyxCollection<TransactionViolation[]>;
Onyx.connect({
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
waitForCollectionCallback: true,
callback: (value) => {
transactionViolations = value;
},
});

let betas: OnyxEntry<Beta[]>;
Onyx.connect({
key: ONYXKEYS.BETAS,
callback: (value) => {
betas = value;
},
});

function stringifyJSON(data: Record<string, unknown>) {
return JSON.stringify(data, null, 6);
}
Expand Down Expand Up @@ -551,6 +590,67 @@ function validateReportActionJSON(json: string) {
});
}

/**
* Gets the reason for showing LHN row
*/
function getReasonForShowingRowInLHN(report: OnyxEntry<Report>): TranslationPaths | null {
if (!report) {
return null;
}

const doesReportHaveViolations = OptionsListUtils.shouldShowViolations(report, transactionViolations);

const reason = ReportUtils.reasonForReportToBeInOptionList({
report,
// We can't pass report.reportID because it will cause reason to always be isFocused
currentReportId: '-1',
isInFocusMode: !!isInFocusMode,
betas,
policies,
excludeEmptyChats: true,
doesReportHaveViolations,
includeSelfDM: true,
});

// When there's no specific reason, we default to isFocused since the report is only showing because we're viewing it
if (reason === null || reason === CONST.REPORT_IN_LHN_REASONS.DEFAULT) {
return 'debug.reasonVisibleInLHN.isFocused';
}

return `debug.reasonVisibleInLHN.${reason}`;
}

type GBRReasonAndReportAction = {
reason: TranslationPaths;
reportAction: OnyxEntry<ReportAction>;
};

/**
* Gets the reason and report action that is causing the GBR to show up in LHN row
*/
function getReasonAndReportActionForGBRInLHNRow(report: OnyxEntry<Report>): GBRReasonAndReportAction | null {
if (!report) {
return null;
}

const {reason, reportAction} = ReportUtils.getReasonAndReportActionThatRequiresAttention(report) ?? {};

if (reason) {
return {reason: `debug.reasonGBR.${reason}`, reportAction};
}

return null;
}

/**
* Gets the report action that is causing the RBR to show up in LHN
*/
function getRBRReportAction(report: OnyxEntry<Report>, reportActions: OnyxEntry<ReportActions>): OnyxEntry<ReportAction> {
const {reportAction} = OptionsListUtils.getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions);

return reportAction;
}

const DebugUtils = {
stringifyJSON,
onyxDataToDraftData,
Expand All @@ -568,6 +668,9 @@ const DebugUtils = {
validateReportDraftProperty,
validateReportActionDraftProperty,
validateReportActionJSON,
getReasonForShowingRowInLHN,
getReasonAndReportActionForGBRInLHNRow,
getRBRReportAction,
REPORT_ACTION_REQUIRED_PROPERTIES,
REPORT_REQUIRED_PROPERTIES,
};
Expand Down
Loading
Loading