From 97807c245cb9fd7a45dd2258847019339f04fa8a Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 10 Aug 2023 10:48:11 +0700 Subject: [PATCH 001/117] Create is leaving client pusher event --- src/ONYXKEYS.js | 1 + src/libs/Pusher/EventType.js | 1 + src/libs/actions/Report.js | 101 +++++++++++++++++++++++++++++++++ src/pages/home/ReportScreen.js | 49 ++++++++++++++++ 4 files changed, 152 insertions(+) diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index 89e6aa9be419..75e22dbbf0d9 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -127,6 +127,7 @@ export default { REPORT_DRAFT_COMMENT_NUMBER_OF_LINES: 'reportDraftCommentNumberOfLines_', REPORT_IS_COMPOSER_FULL_SIZE: 'reportIsComposerFullSize_', REPORT_USER_IS_TYPING: 'reportUserIsTyping_', + REPORT_USER_IS_LEAVING_ROOM: 'reportUserIsLeavingRoom_', SECURITY_GROUP: 'securityGroup_', TRANSACTION: 'transactions_', diff --git a/src/libs/Pusher/EventType.js b/src/libs/Pusher/EventType.js index 639e10020fc7..85ccc5e17242 100644 --- a/src/libs/Pusher/EventType.js +++ b/src/libs/Pusher/EventType.js @@ -5,6 +5,7 @@ export default { REPORT_COMMENT: 'reportComment', ONYX_API_UPDATE: 'onyxApiUpdate', + USER_IS_LEAVING_ROOM: 'client-userIsLeavingRoom', USER_IS_TYPING: 'client-userIsTyping', MULTIPLE_EVENTS: 'multipleEvents', MULTIPLE_EVENT_TYPE: { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2ac014e22a13..f241c3b5a657 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -79,6 +79,7 @@ Onyx.connect({ const allReports = {}; let conciergeChatReportID; const typingWatchTimers = {}; +const leavingWatchTimers = {}; /** * Get the private pusher channel name for a Report. @@ -111,6 +112,28 @@ function getNormalizedTypingStatus(typingStatus) { return normalizedTypingStatus; } +/** + * There are 2 possibilities that we can receive via pusher for a user's leaving status: + * 1. The "new" way from New Expensify is passed as {[login]: Boolean} (e.g. {yuwen@expensify.com: true}), where the value + * is whether the user with that login is leaving on the report or not. + * 2. The "old" way from e.com which is passed as {userLogin: login} (e.g. {userLogin: bstites@expensify.com}) + * + * This method makes sure that no matter which we get, we return the "new" format + * + * @param {Object} leavingStatus + * @returns {Object} + */ +function getNormalizedLeavingStatus(leavingStatus) { + let normalizedLeavingStatus = leavingStatus; + + if (_.first(_.keys(leavingStatus)) === 'userLogin') { + normalizedLeavingStatus = {[leavingStatus.userLogin]: true}; + } + + return normalizedLeavingStatus; +} + + /** * Initialize our pusher subscriptions to listen for someone typing in a report. * @@ -158,6 +181,53 @@ function subscribeToReportTypingEvents(reportID) { }); } +/** + * Initialize our pusher subscriptions to listen for someone typing in a report. + * + * @param {String} reportID + */ +function subscribeToReportLeavingEvents(reportID) { + if (!reportID) { + return; + } + + // Make sure we have a clean Leaving indicator before subscribing to leaving events + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, {}); + + const pusherChannelName = getReportChannelName(reportID); + Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus) => { + // If the pusher message comes from OldDot, we expect the leaving status to be keyed by user + // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID + // since personal details are keyed by accountID. + const normalizedLeavingStatus = getNormalizedLeavingStatus(leavingStatus); + const accountIDOrLogin = _.first(_.keys(normalizedLeavingStatus)); + + if (!accountIDOrLogin) { + return; + } + + // Don't show the leaving indicator if the user is leaving on another platform + if (Number(accountIDOrLogin) === currentUserAccountID) { + return; + } + + // Use a combo of the reportID and the accountID or login as a key for holding our timers. + const reportUserIdentifier = `${reportID}-${accountIDOrLogin}`; + clearTimeout(leavingWatchTimers[reportUserIdentifier]); + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, normalizedLeavingStatus); + + // Wait for 1.5s of no additional leaving events before setting the status back to false. + leavingWatchTimers[reportUserIdentifier] = setTimeout(() => { + const leavingStoppedStatus = {}; + leavingStoppedStatus[accountIDOrLogin] = false; + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, leavingStoppedStatus); + delete leavingWatchTimers[reportUserIdentifier]; + }, 1500); + }).catch((error) => { + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', false, {errorType: error.type, pusherChannelName}); + }); +} + /** * Remove our pusher subscriptions to listen for someone typing in a report. * @@ -173,6 +243,22 @@ function unsubscribeFromReportChannel(reportID) { Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING); } +/** + * Remove our pusher subscriptions to listen for someone typing in a report. + * + * @param {String} reportID + */ +function unsubscribeFromLeavingRoomReportChannel(reportID) { + if (!reportID) { + return; + } + + const pusherChannelName = getReportChannelName(reportID); + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, {}); + Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM); +} + + // New action subscriber array for report pages let newActionSubscribers = []; @@ -838,6 +924,17 @@ function broadcastUserIsTyping(reportID) { typingStatus[currentUserAccountID] = true; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } +/** + * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. + * + * @param {String} reportID + */ +function broadcastUserIsLeavingRoom(reportID) { + const privateReportChannelName = getReportChannelName(reportID); + const leavingStatus = {}; + leavingStatus[currentUserAccountID] = true; + Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); +} /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name @@ -1763,6 +1860,7 @@ function leaveRoom(reportID) { ], }, ); + broadcastUserIsLeavingRoom() navigateToConciergeChat(); } @@ -1878,10 +1976,13 @@ export { updateWriteCapabilityAndNavigate, updateNotificationPreferenceAndNavigate, subscribeToReportTypingEvents, + subscribeToReportLeavingEvents, unsubscribeFromReportChannel, + unsubscribeFromLeavingRoomReportChannel, saveReportComment, saveReportCommentNumberOfLines, broadcastUserIsTyping, + broadcastUserIsLeavingRoom, togglePinnedState, editReportComment, handleUserDeletedLinksInHtml, diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index c859bc6b8f05..c4d5a61bfbe7 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -83,6 +83,8 @@ const propTypes = { /** All of the personal details for everyone */ personalDetails: PropTypes.objectOf(personalDetailsPropType), + userLeavingStatuses: PropTypes.bool, + ...windowDimensionsPropTypes, ...viewportOffsetTopPropTypes, }; @@ -98,6 +100,7 @@ const defaultProps = { betas: [], policies: {}, accountManagerReportID: null, + userLeavingStatuses: false, personalDetails: {}, }; @@ -125,6 +128,7 @@ class ReportScreen extends React.Component { this.onSubmitComment = this.onSubmitComment.bind(this); this.chatWithAccountManager = this.chatWithAccountManager.bind(this); this.dismissBanner = this.dismissBanner.bind(this); + this.checkAndSubscribe = this.checkAndSubscribe.bind(this); this.state = { skeletonViewContainerHeight: reportActionsListViewHeight, @@ -135,6 +139,8 @@ class ReportScreen extends React.Component { this.flatListRef = React.createRef(); this.reactionListRef = React.createRef(); + + this.didSubscribeToReportLeavingEvents = React.createRef(); } componentDidMount() { @@ -152,14 +158,39 @@ class ReportScreen extends React.Component { this.fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); + this.checkAndSubscribe(); } componentDidUpdate(prevProps) { + + console.log('[debug] userLeavingStatuses', this.props.userLeavingStatuses) // If composer should be hidden, hide emoji picker as well if (ReportUtils.shouldHideComposer(this.props.report)) { EmojiPickerAction.hideEmojiPicker(true); } + // const onyxReportID = this.props.report.reportID; + // const prevOnyxReportID = prevProps.report.reportID; + // const routeReportID = getReportID(this.props.route); + + // // navigate to concierge when the room removed from another device (e.g. user leaving a room) + // // the report will not really null when removed, it will have defaultProps properties and values + // if ( + // prevOnyxReportID && + // prevOnyxReportID === routeReportID && + // !onyxReportID && + // // non-optimistic case + // (_.isEqual(this.props.report, defaultProps.report) || + // // optimistic case + // (prevProps.report.statusNum === CONST.REPORT.STATUS.OPEN && this.props.report.statusNum === CONST.REPORT.STATUS.CLOSED)) + // ) { + // // Navigation.goBack(); + // // Report.navigateToConciergeChat(); + // // // isReportRemoved will prevent showing when navigating + // // this.setState({isReportRemoved: true}); + // return; + // } + // If you already have a report open and are deeplinking to a new report on native, // the ReportScreen never actually unmounts and the reportID in the route also doesn't change. // Therefore, we need to compare if the existing reportID is the same as the one in the route @@ -172,9 +203,13 @@ class ReportScreen extends React.Component { this.fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); + this.checkAndSubscribe(); } componentWillUnmount() { + if(this.didSubscribeToReportLeavingEvents){ + Report.unsubscribeFromLeavingRoomReportChannel(this.props.report.reportID); + } if (!this.unsubscribeVisibilityListener) { return; } @@ -228,6 +263,17 @@ class ReportScreen extends React.Component { Navigation.navigate(ROUTES.getReportRoute(this.props.accountManagerReportID)); } + checkAndSubscribe() { + const { report, reportID } = this.props; + + const didCreateReportSuccessfully = !report.pendingFields || (!report.pendingFields.addWorkspaceRoom && !report.pendingFields.createChat); + + if (!this.didSubscribeToReportLeavingEvents.current && didCreateReportSuccessfully) { + Report.subscribeToReportLeavingEvents(reportID); + this.didSubscribeToReportLeavingEvents.current = true; + } + } + render() { const reportID = getReportID(this.props.route); const {addWorkspaceRoomOrChatPendingAction, addWorkspaceRoomOrChatErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(this.props.report); @@ -426,5 +472,8 @@ export default compose( personalDetails: { key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, + userLeavingStatuses: { + key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, + }, }), )(ReportScreen); From b769dc838a9e04eac975955c1bd69704c70f5e22 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 10 Aug 2023 14:07:10 +0700 Subject: [PATCH 002/117] implement subscribeToReportLeavingEvents --- src/libs/Pusher/pusher.js | 13 +++++++ src/libs/actions/Report.js | 37 +++++++++++--------- src/pages/home/ReportScreen.js | 63 +++++++++++++++++----------------- 3 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.js index 60587a68e173..5921d8448f51 100644 --- a/src/libs/Pusher/pusher.js +++ b/src/libs/Pusher/pusher.js @@ -195,8 +195,12 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}) { */ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscribe = () => {}) { return new Promise((resolve, reject) => { + console.log('[debug] jiasjdawd', channelName) + // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { + console.log('[debug] ohjaihwa', channelName) + throw new Error(`[Pusher] instance not found. Pusher.subscribe() most likely has been called before Pusher.init()`); } @@ -205,11 +209,17 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri let channel = getChannel(channelName); if (!channel || !channel.subscribed) { + console.log('[debug] noahdwawwad', channelName) + channel = socket.subscribe(channelName); let isBound = false; channel.bind('pusher:subscription_succeeded', () => { + console.log('[debug] ahusyodiasd', channelName) + // Check so that we do not bind another event with each reconnect attempt if (!isBound) { + console.log('[debug] pjaisdh08q', channelName) + bindEventToChannel(channel, eventName, eventCallback); resolve(); isBound = true; @@ -224,6 +234,8 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri }); channel.bind('pusher:subscription_error', (data = {}) => { + console.log('[debug] jh98c20chc0asc', channelName) + const {type, error, status} = data; Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', { channelName, @@ -234,6 +246,7 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri reject(error); }); } else { + console.log('[debug] jouahsdiasd', channelName) bindEventToChannel(channel, eventName, eventCallback); resolve(); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index f241c3b5a657..4cc5ed0eba50 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -187,42 +187,47 @@ function subscribeToReportTypingEvents(reportID) { * @param {String} reportID */ function subscribeToReportLeavingEvents(reportID) { + console.log('[debug] subscribeToReportLeavingEvents(reportID)', reportID) if (!reportID) { return; } // Make sure we have a clean Leaving indicator before subscribing to leaving events - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, {}); + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, false); const pusherChannelName = getReportChannelName(reportID); Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus) => { + console.log('[debug] leavingStatus', leavingStatus) // If the pusher message comes from OldDot, we expect the leaving status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. const normalizedLeavingStatus = getNormalizedLeavingStatus(leavingStatus); const accountIDOrLogin = _.first(_.keys(normalizedLeavingStatus)); + console.log('[debug] normalizedLeavingStatus', normalizedLeavingStatus) + console.log('[debug] accountIDOrLogin', accountIDOrLogin) if (!accountIDOrLogin) { return; } - // Don't show the leaving indicator if the user is leaving on another platform - if (Number(accountIDOrLogin) === currentUserAccountID) { + if (Number(accountIDOrLogin) !== currentUserAccountID) { return; } // Use a combo of the reportID and the accountID or login as a key for holding our timers. - const reportUserIdentifier = `${reportID}-${accountIDOrLogin}`; - clearTimeout(leavingWatchTimers[reportUserIdentifier]); - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, normalizedLeavingStatus); - - // Wait for 1.5s of no additional leaving events before setting the status back to false. - leavingWatchTimers[reportUserIdentifier] = setTimeout(() => { - const leavingStoppedStatus = {}; - leavingStoppedStatus[accountIDOrLogin] = false; - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, leavingStoppedStatus); - delete leavingWatchTimers[reportUserIdentifier]; - }, 1500); + // const reportUserIdentifier = `${reportID}-${accountIDOrLogin}`; + // clearTimeout(leavingWatchTimers[reportUserIdentifier]); + console.log('[debug] Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true)'); + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true); + // // Wait for 1.5s of no additional leaving events before setting the status back to false. + // leavingWatchTimers[reportUserIdentifier] = setTimeout(() => { + // const leavingStoppedStatus = {}; + // leavingStoppedStatus[accountIDOrLogin] = false; + // console.log('[debug] leavingStoppedStatus', leavingStoppedStatus) + + // Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, leavingStoppedStatus); + // delete leavingWatchTimers[reportUserIdentifier]; + // }, 1500); }).catch((error) => { Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', false, {errorType: error.type, pusherChannelName}); }); @@ -254,7 +259,7 @@ function unsubscribeFromLeavingRoomReportChannel(reportID) { } const pusherChannelName = getReportChannelName(reportID); - Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, {}); + Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, false); Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM); } @@ -1860,7 +1865,7 @@ function leaveRoom(reportID) { ], }, ); - broadcastUserIsLeavingRoom() + broadcastUserIsLeavingRoom(reportID) navigateToConciergeChat(); } diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index c4d5a61bfbe7..e86df04d54f0 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -37,6 +37,7 @@ import * as ComposerActions from '../../libs/actions/Composer'; import ReportScreenContext from './ReportScreenContext'; import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; import DragAndDropProvider from '../../components/DragAndDrop/Provider'; +import CONST from '../../CONST'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -83,7 +84,7 @@ const propTypes = { /** All of the personal details for everyone */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - userLeavingStatuses: PropTypes.bool, + userLeavingStatus: PropTypes.bool, ...windowDimensionsPropTypes, ...viewportOffsetTopPropTypes, @@ -100,7 +101,7 @@ const defaultProps = { betas: [], policies: {}, accountManagerReportID: null, - userLeavingStatuses: false, + userLeavingStatus: false, personalDetails: {}, }; @@ -163,40 +164,39 @@ class ReportScreen extends React.Component { componentDidUpdate(prevProps) { - console.log('[debug] userLeavingStatuses', this.props.userLeavingStatuses) // If composer should be hidden, hide emoji picker as well if (ReportUtils.shouldHideComposer(this.props.report)) { EmojiPickerAction.hideEmojiPicker(true); } - // const onyxReportID = this.props.report.reportID; - // const prevOnyxReportID = prevProps.report.reportID; - // const routeReportID = getReportID(this.props.route); - - // // navigate to concierge when the room removed from another device (e.g. user leaving a room) - // // the report will not really null when removed, it will have defaultProps properties and values - // if ( - // prevOnyxReportID && - // prevOnyxReportID === routeReportID && - // !onyxReportID && - // // non-optimistic case - // (_.isEqual(this.props.report, defaultProps.report) || - // // optimistic case - // (prevProps.report.statusNum === CONST.REPORT.STATUS.OPEN && this.props.report.statusNum === CONST.REPORT.STATUS.CLOSED)) - // ) { - // // Navigation.goBack(); - // // Report.navigateToConciergeChat(); - // // // isReportRemoved will prevent showing when navigating - // // this.setState({isReportRemoved: true}); - // return; - // } + const onyxReportID = this.props.report.reportID; + const prevOnyxReportID = prevProps.report.reportID; + const routeReportID = getReportID(this.props.route); + + console.log('[debug] userLeavingStatus', this.props.userLeavingStatus) + console.log('[debug] prevOnyxReportID', prevOnyxReportID) + console.log('[debug] routeReportID', routeReportID) + console.log('[debug] onyxReportID', onyxReportID) + + // navigate to concierge when the room removed from another device (e.g. user leaving a room) + if ( + // non-optimistic case + (!prevProps.userLeavingStatus && this.props.userLeavingStatus) || + // optimistic case + (prevOnyxReportID && + prevOnyxReportID === routeReportID && + !onyxReportID && + (prevProps.report.statusNum === CONST.REPORT.STATUS.OPEN && this.props.report.statusNum === CONST.REPORT.STATUS.CLOSED)) + ) { + Navigation.goBack(); + Report.navigateToConciergeChat(); + return; + } // If you already have a report open and are deeplinking to a new report on native, // the ReportScreen never actually unmounts and the reportID in the route also doesn't change. // Therefore, we need to compare if the existing reportID is the same as the one in the route // before deciding that we shouldn't call OpenReport. - const onyxReportID = this.props.report.reportID; - const routeReportID = getReportID(this.props.route); if (onyxReportID === prevProps.report.reportID && (!onyxReportID || onyxReportID === routeReportID)) { return; } @@ -264,12 +264,13 @@ class ReportScreen extends React.Component { } checkAndSubscribe() { - const { report, reportID } = this.props; + const {report} = this.props; const didCreateReportSuccessfully = !report.pendingFields || (!report.pendingFields.addWorkspaceRoom && !report.pendingFields.createChat); if (!this.didSubscribeToReportLeavingEvents.current && didCreateReportSuccessfully) { - Report.subscribeToReportLeavingEvents(reportID); + console.log('[debug] Report.subscribeToReportLeavingEvents(reportID);') + Report.subscribeToReportLeavingEvents(report.reportID); this.didSubscribeToReportLeavingEvents.current = true; } } @@ -340,7 +341,7 @@ class ReportScreen extends React.Component { shouldEnableKeyboardAvoidingView={isTopMostReportId} > `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, + userLeavingStatus: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, }, }), )(ReportScreen); From c87ceb934bd0f4b2f5610ac137f1421ba3df7216 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 10 Aug 2023 14:29:09 +0700 Subject: [PATCH 003/117] Remove unnecessary code, run prettier --- src/libs/Pusher/pusher.js | 13 ------------- src/libs/actions/Report.js | 21 +-------------------- src/pages/home/ReportScreen.js | 14 +++----------- 3 files changed, 4 insertions(+), 44 deletions(-) diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.js index 5921d8448f51..60587a68e173 100644 --- a/src/libs/Pusher/pusher.js +++ b/src/libs/Pusher/pusher.js @@ -195,12 +195,8 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}) { */ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscribe = () => {}) { return new Promise((resolve, reject) => { - console.log('[debug] jiasjdawd', channelName) - // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { - console.log('[debug] ohjaihwa', channelName) - throw new Error(`[Pusher] instance not found. Pusher.subscribe() most likely has been called before Pusher.init()`); } @@ -209,17 +205,11 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri let channel = getChannel(channelName); if (!channel || !channel.subscribed) { - console.log('[debug] noahdwawwad', channelName) - channel = socket.subscribe(channelName); let isBound = false; channel.bind('pusher:subscription_succeeded', () => { - console.log('[debug] ahusyodiasd', channelName) - // Check so that we do not bind another event with each reconnect attempt if (!isBound) { - console.log('[debug] pjaisdh08q', channelName) - bindEventToChannel(channel, eventName, eventCallback); resolve(); isBound = true; @@ -234,8 +224,6 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri }); channel.bind('pusher:subscription_error', (data = {}) => { - console.log('[debug] jh98c20chc0asc', channelName) - const {type, error, status} = data; Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', { channelName, @@ -246,7 +234,6 @@ function subscribe(channelName, eventName, eventCallback = () => {}, onResubscri reject(error); }); } else { - console.log('[debug] jouahsdiasd', channelName) bindEventToChannel(channel, eventName, eventCallback); resolve(); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 4cc5ed0eba50..a3a77667bf43 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -133,7 +133,6 @@ function getNormalizedLeavingStatus(leavingStatus) { return normalizedLeavingStatus; } - /** * Initialize our pusher subscriptions to listen for someone typing in a report. * @@ -187,7 +186,6 @@ function subscribeToReportTypingEvents(reportID) { * @param {String} reportID */ function subscribeToReportLeavingEvents(reportID) { - console.log('[debug] subscribeToReportLeavingEvents(reportID)', reportID) if (!reportID) { return; } @@ -197,14 +195,11 @@ function subscribeToReportLeavingEvents(reportID) { const pusherChannelName = getReportChannelName(reportID); Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus) => { - console.log('[debug] leavingStatus', leavingStatus) // If the pusher message comes from OldDot, we expect the leaving status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. const normalizedLeavingStatus = getNormalizedLeavingStatus(leavingStatus); const accountIDOrLogin = _.first(_.keys(normalizedLeavingStatus)); - console.log('[debug] normalizedLeavingStatus', normalizedLeavingStatus) - console.log('[debug] accountIDOrLogin', accountIDOrLogin) if (!accountIDOrLogin) { return; @@ -214,20 +209,7 @@ function subscribeToReportLeavingEvents(reportID) { return; } - // Use a combo of the reportID and the accountID or login as a key for holding our timers. - // const reportUserIdentifier = `${reportID}-${accountIDOrLogin}`; - // clearTimeout(leavingWatchTimers[reportUserIdentifier]); - console.log('[debug] Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true)'); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true); - // // Wait for 1.5s of no additional leaving events before setting the status back to false. - // leavingWatchTimers[reportUserIdentifier] = setTimeout(() => { - // const leavingStoppedStatus = {}; - // leavingStoppedStatus[accountIDOrLogin] = false; - // console.log('[debug] leavingStoppedStatus', leavingStoppedStatus) - - // Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, leavingStoppedStatus); - // delete leavingWatchTimers[reportUserIdentifier]; - // }, 1500); }).catch((error) => { Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', false, {errorType: error.type, pusherChannelName}); }); @@ -263,7 +245,6 @@ function unsubscribeFromLeavingRoomReportChannel(reportID) { Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM); } - // New action subscriber array for report pages let newActionSubscribers = []; @@ -1865,7 +1846,7 @@ function leaveRoom(reportID) { ], }, ); - broadcastUserIsLeavingRoom(reportID) + broadcastUserIsLeavingRoom(reportID); navigateToConciergeChat(); } diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index e86df04d54f0..7d817af71524 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -163,7 +163,6 @@ class ReportScreen extends React.Component { } componentDidUpdate(prevProps) { - // If composer should be hidden, hide emoji picker as well if (ReportUtils.shouldHideComposer(this.props.report)) { EmojiPickerAction.hideEmojiPicker(true); @@ -173,11 +172,6 @@ class ReportScreen extends React.Component { const prevOnyxReportID = prevProps.report.reportID; const routeReportID = getReportID(this.props.route); - console.log('[debug] userLeavingStatus', this.props.userLeavingStatus) - console.log('[debug] prevOnyxReportID', prevOnyxReportID) - console.log('[debug] routeReportID', routeReportID) - console.log('[debug] onyxReportID', onyxReportID) - // navigate to concierge when the room removed from another device (e.g. user leaving a room) if ( // non-optimistic case @@ -186,7 +180,8 @@ class ReportScreen extends React.Component { (prevOnyxReportID && prevOnyxReportID === routeReportID && !onyxReportID && - (prevProps.report.statusNum === CONST.REPORT.STATUS.OPEN && this.props.report.statusNum === CONST.REPORT.STATUS.CLOSED)) + prevProps.report.statusNum === CONST.REPORT.STATUS.OPEN && + this.props.report.statusNum === CONST.REPORT.STATUS.CLOSED) ) { Navigation.goBack(); Report.navigateToConciergeChat(); @@ -207,7 +202,7 @@ class ReportScreen extends React.Component { } componentWillUnmount() { - if(this.didSubscribeToReportLeavingEvents){ + if (this.didSubscribeToReportLeavingEvents) { Report.unsubscribeFromLeavingRoomReportChannel(this.props.report.reportID); } if (!this.unsubscribeVisibilityListener) { @@ -265,11 +260,8 @@ class ReportScreen extends React.Component { checkAndSubscribe() { const {report} = this.props; - const didCreateReportSuccessfully = !report.pendingFields || (!report.pendingFields.addWorkspaceRoom && !report.pendingFields.createChat); - if (!this.didSubscribeToReportLeavingEvents.current && didCreateReportSuccessfully) { - console.log('[debug] Report.subscribeToReportLeavingEvents(reportID);') Report.subscribeToReportLeavingEvents(report.reportID); this.didSubscribeToReportLeavingEvents.current = true; } From b57b046624adb8dac585064e016af2956d41cfe3 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 10 Aug 2023 14:42:01 +0700 Subject: [PATCH 004/117] refine comment, remove leavingWatchTimers variable, refine checkAndSubscribe naming --- src/libs/actions/Report.js | 5 ++--- src/pages/home/ReportScreen.js | 9 +++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a3a77667bf43..de1fe0eb4213 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -79,7 +79,6 @@ Onyx.connect({ const allReports = {}; let conciergeChatReportID; const typingWatchTimers = {}; -const leavingWatchTimers = {}; /** * Get the private pusher channel name for a Report. @@ -231,7 +230,7 @@ function unsubscribeFromReportChannel(reportID) { } /** - * Remove our pusher subscriptions to listen for someone typing in a report. + * Remove our pusher subscriptions to listen for someone leaving a report. * * @param {String} reportID */ @@ -911,7 +910,7 @@ function broadcastUserIsTyping(reportID) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } /** - * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. + * Broadcasts whether or not a user is leaving on a report over the report's private pusher channel. * * @param {String} reportID */ diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 7d817af71524..dca4a74daf8a 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -84,6 +84,7 @@ const propTypes = { /** All of the personal details for everyone */ personalDetails: PropTypes.objectOf(personalDetailsPropType), + /** Whether user leaving current report that listen to another device leaveRoom trigger */ userLeavingStatus: PropTypes.bool, ...windowDimensionsPropTypes, @@ -129,7 +130,7 @@ class ReportScreen extends React.Component { this.onSubmitComment = this.onSubmitComment.bind(this); this.chatWithAccountManager = this.chatWithAccountManager.bind(this); this.dismissBanner = this.dismissBanner.bind(this); - this.checkAndSubscribe = this.checkAndSubscribe.bind(this); + this.checkAndSubscribeReportLeavingEvents = this.checkAndSubscribeReportLeavingEvents.bind(this); this.state = { skeletonViewContainerHeight: reportActionsListViewHeight, @@ -159,7 +160,7 @@ class ReportScreen extends React.Component { this.fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); - this.checkAndSubscribe(); + this.checkAndSubscribeReportLeavingEvents(); } componentDidUpdate(prevProps) { @@ -198,7 +199,7 @@ class ReportScreen extends React.Component { this.fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); - this.checkAndSubscribe(); + this.checkAndSubscribeReportLeavingEvents(); } componentWillUnmount() { @@ -258,7 +259,7 @@ class ReportScreen extends React.Component { Navigation.navigate(ROUTES.getReportRoute(this.props.accountManagerReportID)); } - checkAndSubscribe() { + checkAndSubscribeReportLeavingEvents() { const {report} = this.props; const didCreateReportSuccessfully = !report.pendingFields || (!report.pendingFields.addWorkspaceRoom && !report.pendingFields.createChat); if (!this.didSubscribeToReportLeavingEvents.current && didCreateReportSuccessfully) { From 6baddfe4f3ec43f9e97fe3ff934828c310dcf1b4 Mon Sep 17 00:00:00 2001 From: Edu Date: Wed, 16 Aug 2023 10:19:45 -0300 Subject: [PATCH 005/117] adding script to find unused keys --- package.json | 3 +- scripts/find-unused-keys.sh | 91 +++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100755 scripts/find-unused-keys.sh diff --git a/package.json b/package.json index 00d8c2f027fe..9454f8e982aa 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,8 @@ "analyze-packages": "ANALYZE_BUNDLE=true webpack --config config/webpack/webpack.common.js --env envFile=.env.production", "symbolicate:android": "npx metro-symbolicate android/app/build/generated/sourcemaps/react/release/index.android.bundle.map", "symbolicate:ios": "npx metro-symbolicate main.jsbundle.map", - "test:e2e": "node tests/e2e/testRunner.js --development" + "test:e2e": "node tests/e2e/testRunner.js --development", + "find-missing-keys": "scripts/find-unused-keys.sh" }, "dependencies": { "@expensify/react-native-web": "0.18.15", diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh new file mode 100755 index 000000000000..acfd9be1a191 --- /dev/null +++ b/scripts/find-unused-keys.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Configurations +SRC_DIR="src" +STYLES_FILE="src/styles/styles.js" +TRANSLATION_FILES=("src/languages/es.js" "src/languages/en.js") +KEYS_LIST_FILE="keys_list.txt" + +# Function to find and store keys from a file +find_and_store_keys() { + local file="$1" + local file_keys=($(grep -Eo "([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:" "$file" | sed -E "s/[:]//g")) + + for key in "${file_keys[@]}"; do + local line_numbers=($(grep -n "$key" "$file" | cut -d':' -f1)) + for line_number in "${line_numbers[@]}"; do + echo "$key:$file:$line_number" + done + done +} + +# Function to remove keys from the list +remove_keys() { + local file="$1" + local list_file="$2" + + while IFS= read -r key_info; do + local key=$(echo "$key_info" | cut -d':' -f1) + local key_file=$(echo "$key_info" | cut -d':' -f2) + if [[ "$key_file" != "$file" ]]; then + echo "$key_info" + fi + done < "$list_file" +} + +# Function to find unused keys in a file +find_unused_keys_in_file() { + local file="$1" + local list_file="$2" + local unused_keys=() + + while IFS= read -r key_info; do + local key=$(echo "$key_info" | cut -d':' -f1) + local key_file=$(echo "$key_info" | cut -d':' -f2) + local line_number=$(echo "$key_info" | cut -d':' -f3) + if [[ "$key_file" != "$file" ]]; then + continue + fi + + if ! grep -q "$key" "$file"; then + # Check if the line number contains a numeric value + if [[ "$line_number" =~ ^[0-9]+$ ]]; then + unused_keys+=("$key_info") + fi + fi + done < "$list_file" + + for unused_key_info in "${unused_keys[@]}"; do + echo "Error: Unused key '$(echo "$unused_key_info" | cut -d':' -f1)' found in '$file' at line: $(echo "$unused_key_info" | cut -d':' -f3)" + done +} + +# Find and store keys from styles.js (only top-level keys) +grep -Eo "^[[:space:]]*[a-zA-Z0-9_-]+:" "$STYLES_FILE" | sed -E "s/[:]//g" | while IFS= read -r key; do + echo "$key:$STYLES_FILE:0" +done > "$KEYS_LIST_FILE" + +# Find and store keys from translation files +for translation_file in "${TRANSLATION_FILES[@]}"; do + find_and_store_keys "$translation_file" >> "$KEYS_LIST_FILE" +done + +# Find and remove used keys from the list +while IFS= read -r file; do + remove_keys "$file" "$KEYS_LIST_FILE" > keys_list_temp.txt + mv keys_list_temp.txt "$KEYS_LIST_FILE" +done < <(find "$SRC_DIR" -type f) + +# Find unused keys in all files +unused_keys_found=false +while IFS= read -r file; do + unused_keys_in_file=$(find_unused_keys_in_file "$file" "$KEYS_LIST_FILE") + if [[ -n "$unused_keys_in_file" ]]; then + unused_keys_found=true + echo "$unused_keys_in_file" + fi +done < <(find "$SRC_DIR" -type f) + +if [[ "$unused_keys_found" = false ]]; then + echo "No unused keys found." +fi \ No newline at end of file From 990932d49607b97105ea7873674d57f5032b1e1f Mon Sep 17 00:00:00 2001 From: honnamkuan Date: Thu, 17 Aug 2023 21:37:38 +0800 Subject: [PATCH 006/117] sort group member avatar icons correctly --- src/libs/ReportUtils.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 1baf6eacb9da..a2663c0cf30d 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -874,8 +874,14 @@ function getIconsForParticipants(participants, personalDetails) { ]); } - // Sort all logins by first name (which is the second element in the array) - const sortedParticipantDetails = participantDetails.sort((a, b) => a[2] - b[2]); + // Sort all logins by first name (position 2 element in array) + // if multiple participants have the same first name, sub-sort them by login/displayName (position 1 element in array) + // if that still clashes, sub-sort by accountID (position 0 element in array) + const sortedParticipantDetails = _.chain(participantDetails) + .sortBy((detail) => detail[0]) + .sortBy((detail) => detail[1]) + .sortBy((detail) => detail[2]) + .value(); // Now that things are sorted, gather only the avatars (third element in the array) and return those const avatars = []; From d798ea6a0a53eb548469a1b6d4a568136de2a6a6 Mon Sep 17 00:00:00 2001 From: Edu Date: Fri, 18 Aug 2023 17:31:12 -0300 Subject: [PATCH 007/117] Separated into 2 functions to find and parse the object names styles and en/es files --- scripts/find-unused-keys.sh | 143 ++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 65 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index acfd9be1a191..de13dccba812 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -4,88 +4,101 @@ SRC_DIR="src" STYLES_FILE="src/styles/styles.js" TRANSLATION_FILES=("src/languages/es.js" "src/languages/en.js") -KEYS_LIST_FILE="keys_list.txt" +STYLES_KEYS_FILE="src/languages/style_keys_list_temp.txt" +TRANSLATION_KEYS_FILE="src/languages/translations_keys_list_temp.txt" # Function to find and store keys from a file -find_and_store_keys() { +find_styles_and_store_keys() { local file="$1" - local file_keys=($(grep -Eo "([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:" "$file" | sed -E "s/[:]//g")) - - for key in "${file_keys[@]}"; do - local line_numbers=($(grep -n "$key" "$file" | cut -d':' -f1)) - for line_number in "${line_numbers[@]}"; do - echo "$key:$file:$line_number" - done - done -} + local parent_keys=() + local root_key="" + local line_number=0 # Initialize the line number -# Function to remove keys from the list -remove_keys() { - local file="$1" - local list_file="$2" - - while IFS= read -r key_info; do - local key=$(echo "$key_info" | cut -d':' -f1) - local key_file=$(echo "$key_info" | cut -d':' -f2) - if [[ "$key_file" != "$file" ]]; then - echo "$key_info" + while IFS= read -r line; do + ((line_number++)) # Increment the line number + + # Skip lines that are not key-related + if [[ ! "$line" =~ ^[[:space:]]*const[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} ]]; then + continue fi - done < "$list_file" + + if [[ "$line" =~ ^[[:space:]]*const[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then + root_key=$(echo "${BASH_REMATCH[1]}" | sed -E "s/[:[:space:]]*\{.*//") + elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{ ]]; then + local key=$(echo "$line" | sed -E "s/[:[:space:]]*\{.*//") + # local line_number=$(echo "$line" | grep -n "$key:" | cut -d':' -f1) + + if [[ ${#parent_keys[@]} -gt 0 ]]; then + parent_key_trimmed="${parent_keys[${#parent_keys[@]}-1]// /}" # Trim spaces + key_trimmed="${key// /}" # Trim spaces + key="$parent_key_trimmed.$key_trimmed" + elif [[ -n "$root_key" ]]; then + parent_key_trimmed="${root_key// /}" # Trim spaces + key_trimmed="${key// /}" # Trim spaces + key="$parent_key_trimmed.$key_trimmed" + fi + + echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + parent_keys+=("$key") + elif [[ "$line" =~ ^[[:space:]]*\} ]]; then + # unset "parent_keys[${#parent_keys[@]}-1]" + parent_keys=("${parent_keys[@]:0:${#parent_keys[@]}-1}") + fi + # done < <(grep -E "^[[:space:]]*const[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\}" "$file") + done < "$file" } -# Function to find unused keys in a file -find_unused_keys_in_file() { +find_translations_and_store_keys() { local file="$1" - local list_file="$2" - local unused_keys=() - - while IFS= read -r key_info; do - local key=$(echo "$key_info" | cut -d':' -f1) - local key_file=$(echo "$key_info" | cut -d':' -f2) - local line_number=$(echo "$key_info" | cut -d':' -f3) - if [[ "$key_file" != "$file" ]]; then + local parent_key=() + local current_key="" + local line_number=0 # Initialize the line number + + while IFS= read -r line; do + ((line_number++)) # Increment the line number + + # Skip lines that are not key-related + if [[ ! "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*(\'[^\']*\'|\{)|^[[:space:]]*\} ]]; then continue fi + - if ! grep -q "$key" "$file"; then - # Check if the line number contains a numeric value - if [[ "$line_number" =~ ^[0-9]+$ ]]; then - unused_keys+=("$key_info") + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*\{ ]]; then + local key="${BASH_REMATCH[1]}" + current_key="$key" + + if [[ ${#parent_keys[@]} -gt 0 ]]; then + local parent_key="${parent_keys[*]}" + current_key="$parent_key.$key" + fi + + parent_keys=("${parent_keys[@]}" "$current_key") + elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*(\'[^\']*\'|\{) ]]; then + local key="${BASH_REMATCH[1]}" + # local line_number=$(echo "$line" | grep -n "${BASH_REMATCH[1]}" | cut -d':' -f1) + + if [[ ${#parent_keys[@]} -gt 0 ]]; then + local lastItem="${#parent_keys[@]}-1" + local parent_key="${parent_keys[$lastItem]}" + + echo "${parent_key}.${key}:${file}:${line_number}" >> "$TRANSLATION_KEYS_FILE" + else + echo "$key:${file}:${line_number}" >> "$TRANSLATION_KEYS_FILE" fi + elif [[ "$line" =~ ^[[:space:]]*\} ]]; then + parent_keys=("${parent_keys[@]:0:${#parent_keys[@]}-1}") + current_key="${parent_keys[*]}" fi - done < "$list_file" - - for unused_key_info in "${unused_keys[@]}"; do - echo "Error: Unused key '$(echo "$unused_key_info" | cut -d':' -f1)' found in '$file' at line: $(echo "$unused_key_info" | cut -d':' -f3)" - done + # done < <(grep -E "^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*(\'[^\']*\'|\{)|^[[:space:]]*\}" "$file") + done < "$file" } -# Find and store keys from styles.js (only top-level keys) -grep -Eo "^[[:space:]]*[a-zA-Z0-9_-]+:" "$STYLES_FILE" | sed -E "s/[:]//g" | while IFS= read -r key; do - echo "$key:$STYLES_FILE:0" -done > "$KEYS_LIST_FILE" +# Find and store keys from styles.js +find_styles_and_store_keys "$STYLES_FILE" # Find and store keys from translation files for translation_file in "${TRANSLATION_FILES[@]}"; do - find_and_store_keys "$translation_file" >> "$KEYS_LIST_FILE" + find_translations_and_store_keys "$translation_file" done -# Find and remove used keys from the list -while IFS= read -r file; do - remove_keys "$file" "$KEYS_LIST_FILE" > keys_list_temp.txt - mv keys_list_temp.txt "$KEYS_LIST_FILE" -done < <(find "$SRC_DIR" -type f) - -# Find unused keys in all files -unused_keys_found=false -while IFS= read -r file; do - unused_keys_in_file=$(find_unused_keys_in_file "$file" "$KEYS_LIST_FILE") - if [[ -n "$unused_keys_in_file" ]]; then - unused_keys_found=true - echo "$unused_keys_in_file" - fi -done < <(find "$SRC_DIR" -type f) - -if [[ "$unused_keys_found" = false ]]; then - echo "No unused keys found." -fi \ No newline at end of file +echo "Keys saved to $KEYS_FILE" \ No newline at end of file From ea6c6fe36cc6138485c1ebb6b1297aba7b7b23db Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 23 Aug 2023 09:13:36 +0700 Subject: [PATCH 008/117] Move REPORT_USER_IS_LEAVING_ROOM to ONYXKEYS.ts --- src/ONYXKEYS.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 3c0b3ee9a6d6..686d2546bf59 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -233,6 +233,7 @@ const ONYXKEYS = { REPORT_DRAFT_COMMENT_NUMBER_OF_LINES: 'reportDraftCommentNumberOfLines_', REPORT_IS_COMPOSER_FULL_SIZE: 'reportIsComposerFullSize_', REPORT_USER_IS_TYPING: 'reportUserIsTyping_', + REPORT_USER_IS_LEAVING_ROOM: 'reportUserIsLeavingRoom_', SECURITY_GROUP: 'securityGroup_', TRANSACTION: 'transactions_', @@ -361,6 +362,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number; [ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean; [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: boolean; + [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; From fd1f6595d9032c8d506d7958195548b8472e5842 Mon Sep 17 00:00:00 2001 From: honnamkuan Date: Wed, 23 Aug 2023 23:18:47 +0800 Subject: [PATCH 009/117] sort avatar icons by user displayName/login followed by accountID efficiently add unit tests --- src/libs/ReportUtils.js | 27 ++++++------- tests/unit/ReportUtilsTest.js | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f64221f74e6b..028d88d4260c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -884,21 +884,22 @@ function getIconsForParticipants(participants, personalDetails) { for (let i = 0; i < participantsList.length; i++) { const accountID = participantsList[i]; const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [accountID, 'avatar'], ''), accountID); - participantDetails.push([ - accountID, - lodashGet(personalDetails, [accountID, 'displayName']) || lodashGet(personalDetails, [accountID, 'login'], ''), - lodashGet(personalDetails, [accountID, 'firstName'], ''), - avatarSource, - ]); + participantDetails.push([accountID, lodashGet(personalDetails, [accountID, 'displayName']) || lodashGet(personalDetails, [accountID, 'login'], ''), avatarSource]); } - // Sort all logins by first name (position 2 element in array) - // if multiple participants have the same first name, sub-sort them by login/displayName (position 1 element in array) - // if that still clashes, sub-sort by accountID (position 0 element in array) const sortedParticipantDetails = _.chain(participantDetails) - .sortBy((detail) => detail[0]) - .sortBy((detail) => detail[1]) - .sortBy((detail) => detail[2]) + .sort((first, second) => { + // First sort by displayName/login + const displayNameLoginOrder = first[1].localeCompare(second[1]); + if (displayNameLoginOrder !== 0) { + return displayNameLoginOrder; + } + + // Then fallback on accountID as the final sorting criteria. + // This will ensure that the order of avatars with same login/displayName + // stay consistent across all users and devices + return first[0] > second[0]; + }) .value(); // Now that things are sorted, gather only the avatars (third element in the array) and return those @@ -906,7 +907,7 @@ function getIconsForParticipants(participants, personalDetails) { for (let i = 0; i < sortedParticipantDetails.length; i++) { const userIcon = { id: sortedParticipantDetails[i][0], - source: sortedParticipantDetails[i][3], + source: sortedParticipantDetails[i][2], type: CONST.ICON_TYPE_AVATAR, name: sortedParticipantDetails[i][1], }; diff --git a/tests/unit/ReportUtilsTest.js b/tests/unit/ReportUtilsTest.js index e97e9147c328..015121a203a0 100644 --- a/tests/unit/ReportUtilsTest.js +++ b/tests/unit/ReportUtilsTest.js @@ -35,6 +35,13 @@ const participantsPersonalDetails = { login: '+18332403627@expensify.sms', displayName: '(833) 240-3627', }, + 5: { + accountID: 5, + displayName: 'Lagertha Lothbrok', + firstName: 'Lagertha', + login: 'lagertha2@vikings.net', + pronouns: 'She/her', + }, }; const policy = { policyID: 1, @@ -55,6 +62,53 @@ describe('ReportUtils', () => { }); beforeEach(() => Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.DEFAULT).then(waitForPromisesToResolve)); + describe('getIconsForParticipants', () => { + it('returns sorted avatar source by name, then accountID', () => { + expect(ReportUtils.getIconsForParticipants([1, 2, 3, 4, 5], participantsPersonalDetails)).toEqual([ + { + id: 4, + name: '(833) 240-3627', + source: { + testUri: '../../../assets/images/avatars/user/default-avatar_5.svg', + }, + type: 'avatar', + }, + { + id: 2, + name: 'floki@vikings.net', + source: { + testUri: '../../../assets/images/avatars/user/default-avatar_3.svg', + }, + type: 'avatar', + }, + { + id: 3, + name: 'Lagertha Lothbrok', + source: { + testUri: '../../../assets/images/avatars/user/default-avatar_4.svg', + }, + type: 'avatar', + }, + { + id: 5, + name: 'Lagertha Lothbrok', + source: { + testUri: '../../../assets/images/avatars/user/default-avatar_6.svg', + }, + type: 'avatar', + }, + { + id: 1, + name: 'Ragnar Lothbrok', + source: { + testUri: '../../../assets/images/avatars/user/default-avatar_2.svg', + }, + type: 'avatar', + }, + ]); + }); + }); + describe('getDisplayNamesWithTooltips', () => { test('withSingleParticipantReport', () => { expect(ReportUtils.getDisplayNamesWithTooltips(participantsPersonalDetails, false)).toStrictEqual([ @@ -94,6 +148,15 @@ describe('ReportUtils', () => { accountID: 4, pronouns: undefined, }, + { + displayName: 'Lagertha Lothbrok', + avatar: { + testUri: '../../../assets/images/avatars/user/default-avatar_6.svg', + }, + login: 'lagertha2@vikings.net', + accountID: 5, + pronouns: 'She/her', + }, ]); }); @@ -135,6 +198,15 @@ describe('ReportUtils', () => { accountID: 4, pronouns: undefined, }, + { + displayName: 'Lagertha', + avatar: { + testUri: '../../../assets/images/avatars/user/default-avatar_6.svg', + }, + login: 'lagertha2@vikings.net', + accountID: 5, + pronouns: 'She/her', + }, ]); }); }); From e70c1e9d723ec348bf86dcbd2b99dbd25bb8629b Mon Sep 17 00:00:00 2001 From: honnamkuan Date: Wed, 23 Aug 2023 23:29:10 +0800 Subject: [PATCH 010/117] update comment --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 028d88d4260c..53a0b69b54f2 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -902,7 +902,7 @@ function getIconsForParticipants(participants, personalDetails) { }) .value(); - // Now that things are sorted, gather only the avatars (third element in the array) and return those + // Now that things are sorted, gather only the avatars (second element in the array) and return those const avatars = []; for (let i = 0; i < sortedParticipantDetails.length; i++) { const userIcon = { From e4d1363e973158876dc1c7c5d9c758ad8a5e6882 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 25 Aug 2023 14:35:39 +0700 Subject: [PATCH 011/117] Remove unnecessary code --- src/pages/home/ReportScreen.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 598d13ab9f58..d42886aeeb2b 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -163,10 +163,6 @@ class ReportScreen extends React.Component { } componentDidUpdate(prevProps) { - if (ReportUtils.shouldDisableWriteActions(this.props.report)) { - EmojiPickerAction.hideEmojiPicker(true); - } - const onyxReportID = this.props.report.reportID; const prevOnyxReportID = prevProps.report.reportID; const routeReportID = getReportID(this.props.route); From 4ff465faced849a23da24f9929793602dc9226b2 Mon Sep 17 00:00:00 2001 From: Edu Date: Mon, 28 Aug 2023 17:12:07 +0200 Subject: [PATCH 012/117] Going through the codebase and get keys from the files --- scripts/find-unused-keys.sh | 50 ++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index de13dccba812..f8515ff3e17f 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -4,8 +4,34 @@ SRC_DIR="src" STYLES_FILE="src/styles/styles.js" TRANSLATION_FILES=("src/languages/es.js" "src/languages/en.js") -STYLES_KEYS_FILE="src/languages/style_keys_list_temp.txt" -TRANSLATION_KEYS_FILE="src/languages/translations_keys_list_temp.txt" +STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" +TRANSLATION_KEYS_FILE="scripts/translations_keys_list_temp.txt" +REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" + # Create an empty temp file if it doesn't exist + if [ ! -f "$REMOVAL_KEYS_FILE" ]; then + touch "$REMOVAL_KEYS_FILE" + fi +# Function to remove a keyword from the temp file +remove_keyword() { + + keyword="$1" + # echo "Removing $keyword" + grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" + mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" +} + +lookfor_unused_keywords() { + # Loop through all files in the src folder + find src -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + # echo "Checking $file" + # Search for keywords starting with "styles" + grep -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | while IFS= read -r keyword; do + # Remove any [ ] characters from the keyword + clean_keyword="${keyword//[\[\]]/}" + remove_keyword "$clean_keyword" + done + done +} # Function to find and store keys from a file find_styles_and_store_keys() { @@ -97,8 +123,20 @@ find_translations_and_store_keys() { find_styles_and_store_keys "$STYLES_FILE" # Find and store keys from translation files -for translation_file in "${TRANSLATION_FILES[@]}"; do - find_translations_and_store_keys "$translation_file" -done +# for translation_file in "${TRANSLATION_FILES[@]}"; do +# find_translations_and_store_keys "$translation_file" +# done + +echo "Keys saved to $KEYS_FILE" +echo "Now go through the list and remove the keys that are used." + +line_count=$(wc -l < $STYLES_KEYS_FILE) +echo "Number of lines in the file: $line_count" + +lookfor_unused_keywords + +echo "Unused keys are into to $STYLES_KEYS_FILE" -echo "Keys saved to $KEYS_FILE" \ No newline at end of file +line_count2=$(wc -l < $STYLES_KEYS_FILE) +echo "Number of lines in the file: $line_count2" +# cat "$STYLES_KEYS_FILE" \ No newline at end of file From fd39606656ba36778e293292ecbfd49a5feaa893 Mon Sep 17 00:00:00 2001 From: Edu Date: Tue, 29 Aug 2023 16:30:12 +0200 Subject: [PATCH 013/117] showing unused style keys --- scripts/find-unused-keys.sh | 51 +++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index f8515ff3e17f..38ec0c8b6ed6 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -11,24 +11,58 @@ REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" if [ ! -f "$REMOVAL_KEYS_FILE" ]; then touch "$REMOVAL_KEYS_FILE" fi + +# Read the style file with unused keys +show_unused_style_keywords() { + while IFS=: read -r key file line_number; do + line_count=$(wc -l < $STYLES_KEYS_FILE) + echo "Unused keys: $line_count" + echo "File: $file" + + # Get lines before and after the error line + lines_before=$((line_number - 3)) + lines_after=$((line_number + 3)) + + # Print context of the error line + echo "Context around line $line_number:" + sed -n "$lines_before,$lines_after p" "$file" | awk -v key="$key" '{gsub(key, "\033[1;31m"key"\033[0m"); print}' + + echo "Unused key: $key" + echo "--------------------------------" + done < "$STYLES_KEYS_FILE" +} + # Function to remove a keyword from the temp file remove_keyword() { keyword="$1" # echo "Removing $keyword" grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" + line_count=$(wc -l < $REMOVAL_KEYS_FILE) + # echo "$REMOVAL_KEYS_FILE lines in the file: $line_count" mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" + # echo "$STYLES_KEYS_FILE UPDATED lines in the file: $line_count" } lookfor_unused_keywords() { # Loop through all files in the src folder - find src -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + find 'src' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do # echo "Checking $file" # Search for keywords starting with "styles" - grep -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | while IFS= read -r keyword; do + # grep -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | while IFS= read -r keyword; do + grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do + # Remove any [ ] characters from the keyword + # echo "File: $file" clean_keyword="${keyword//[\[\]]/}" - remove_keyword "$clean_keyword" + # skip styles. keyword that might be used in comments + if [[ "$clean_keyword" == "styles." ]]; then + continue + fi + # echo "Found $clean_keyword" + # Remove the keyword from the temp file + remove_keyword "$clean_keyword" + done done } @@ -64,7 +98,12 @@ find_styles_and_store_keys() { key="$parent_key_trimmed.$key_trimmed" fi - echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + # echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + if [[ "$key" == "styles."* ]]; then + echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + else + echo "styles.$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + fi parent_keys+=("$key") elif [[ "$line" =~ ^[[:space:]]*\} ]]; then # unset "parent_keys[${#parent_keys[@]}-1]" @@ -139,4 +178,6 @@ echo "Unused keys are into to $STYLES_KEYS_FILE" line_count2=$(wc -l < $STYLES_KEYS_FILE) echo "Number of lines in the file: $line_count2" -# cat "$STYLES_KEYS_FILE" \ No newline at end of file +# cat "$STYLES_KEYS_FILE" + +show_unused_style_keywords \ No newline at end of file From 96d1c4509e023f117974390bf10277cff7a0cfca Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 30 Aug 2023 06:24:53 +0700 Subject: [PATCH 014/117] Add prevReport.statusNum to dependency --- src/pages/home/ReportScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ac96ed39bf4f..a1c280a498dd 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -308,7 +308,7 @@ function ReportScreen({ fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); - }, [route, report, errors, fetchReportIfNeeded, prevReport.reportID, userLeavingStatus]); + }, [route, report, errors, fetchReportIfNeeded, prevReport.reportID, userLeavingStatus, prevReport.statusNum]); useEffect(() => { // Ensures subscription event succeeds when the report/workspace room is created optimistically. From d779eecd92326ab69a08bcaee194536412ba7b90 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 1 Sep 2023 08:05:43 +0200 Subject: [PATCH 015/117] removed translation code --- scripts/find-unused-keys.sh | 84 +++++++------------------------------ 1 file changed, 16 insertions(+), 68 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index 38ec0c8b6ed6..b99e28d3fe8a 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -3,14 +3,14 @@ # Configurations SRC_DIR="src" STYLES_FILE="src/styles/styles.js" -TRANSLATION_FILES=("src/languages/es.js" "src/languages/en.js") STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" TRANSLATION_KEYS_FILE="scripts/translations_keys_list_temp.txt" REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" - # Create an empty temp file if it doesn't exist - if [ ! -f "$REMOVAL_KEYS_FILE" ]; then - touch "$REMOVAL_KEYS_FILE" - fi + +# Create an empty temp file if it doesn't exist +if [ ! -f "$REMOVAL_KEYS_FILE" ]; then + touch "$REMOVAL_KEYS_FILE" +fi # Read the style file with unused keys show_unused_style_keywords() { @@ -34,35 +34,28 @@ show_unused_style_keywords() { # Function to remove a keyword from the temp file remove_keyword() { - keyword="$1" - # echo "Removing $keyword" + grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" line_count=$(wc -l < $REMOVAL_KEYS_FILE) - # echo "$REMOVAL_KEYS_FILE lines in the file: $line_count" mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" - # echo "$STYLES_KEYS_FILE UPDATED lines in the file: $line_count" } lookfor_unused_keywords() { # Loop through all files in the src folder find 'src' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do - # echo "Checking $file" + # Search for keywords starting with "styles" - # grep -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | while IFS= read -r keyword; do grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do # Remove any [ ] characters from the keyword - # echo "File: $file" clean_keyword="${keyword//[\[\]]/}" # skip styles. keyword that might be used in comments if [[ "$clean_keyword" == "styles." ]]; then continue fi - # echo "Found $clean_keyword" - # Remove the keyword from the temp file - remove_keyword "$clean_keyword" + remove_keyword "$clean_keyword" done done } @@ -87,6 +80,12 @@ find_styles_and_store_keys() { elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{ ]]; then local key=$(echo "$line" | sed -E "s/[:[:space:]]*\{.*//") # local line_number=$(echo "$line" | grep -n "$key:" | cut -d':' -f1) + echo "line $line" + # Handle keys defined in functions within objects + function_key_pattern="[a-zA-Z0-9_-]+:[[:space:]]*\\(.*\\)[[:space:]]*=>[[:space:]]*\\{" + if [[ "$line" =~ $function_key_pattern ]]; then + key="${BASH_REMATCH[0]%%:*}" + fi if [[ ${#parent_keys[@]} -gt 0 ]]; then parent_key_trimmed="${parent_keys[${#parent_keys[@]}-1]// /}" # Trim spaces @@ -113,71 +112,20 @@ find_styles_and_store_keys() { done < "$file" } -find_translations_and_store_keys() { - local file="$1" - local parent_key=() - local current_key="" - local line_number=0 # Initialize the line number - - while IFS= read -r line; do - ((line_number++)) # Increment the line number - - # Skip lines that are not key-related - if [[ ! "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*(\'[^\']*\'|\{)|^[[:space:]]*\} ]]; then - continue - fi - - - if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*\{ ]]; then - local key="${BASH_REMATCH[1]}" - current_key="$key" - - if [[ ${#parent_keys[@]} -gt 0 ]]; then - local parent_key="${parent_keys[*]}" - current_key="$parent_key.$key" - fi - - parent_keys=("${parent_keys[@]}" "$current_key") - elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*(\'[^\']*\'|\{) ]]; then - local key="${BASH_REMATCH[1]}" - # local line_number=$(echo "$line" | grep -n "${BASH_REMATCH[1]}" | cut -d':' -f1) - - if [[ ${#parent_keys[@]} -gt 0 ]]; then - local lastItem="${#parent_keys[@]}-1" - local parent_key="${parent_keys[$lastItem]}" - - echo "${parent_key}.${key}:${file}:${line_number}" >> "$TRANSLATION_KEYS_FILE" - else - echo "$key:${file}:${line_number}" >> "$TRANSLATION_KEYS_FILE" - fi - elif [[ "$line" =~ ^[[:space:]]*\} ]]; then - parent_keys=("${parent_keys[@]:0:${#parent_keys[@]}-1}") - current_key="${parent_keys[*]}" - fi - # done < <(grep -E "^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+)[[:space:]]*:[[:space:]]*(\'[^\']*\'|\{)|^[[:space:]]*\}" "$file") - done < "$file" -} - # Find and store keys from styles.js find_styles_and_store_keys "$STYLES_FILE" -# Find and store keys from translation files -# for translation_file in "${TRANSLATION_FILES[@]}"; do -# find_translations_and_store_keys "$translation_file" -# done - echo "Keys saved to $KEYS_FILE" echo "Now go through the list and remove the keys that are used." line_count=$(wc -l < $STYLES_KEYS_FILE) echo "Number of lines in the file: $line_count" -lookfor_unused_keywords +# lookfor_unused_keywords echo "Unused keys are into to $STYLES_KEYS_FILE" line_count2=$(wc -l < $STYLES_KEYS_FILE) echo "Number of lines in the file: $line_count2" -# cat "$STYLES_KEYS_FILE" -show_unused_style_keywords \ No newline at end of file +# show_unused_style_keywords \ No newline at end of file From c04114bf72a44cc795e6b537f9d4b2035c8572e7 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 6 Sep 2023 17:28:19 +0200 Subject: [PATCH 016/117] getting keys from utilities folder to find unused styles --- scripts/find-unused-keys.sh | 131 +++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 32 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index b99e28d3fe8a..8e00c19dbeef 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -3,8 +3,9 @@ # Configurations SRC_DIR="src" STYLES_FILE="src/styles/styles.js" +UTILITIES_STYLES_FILE="src/styles/utilities" STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" -TRANSLATION_KEYS_FILE="scripts/translations_keys_list_temp.txt" +UTILITY_STYLES_KEYS_FILE="scripts/utility_keys_list_temp.txt" REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" # Create an empty temp file if it doesn't exist @@ -35,19 +36,22 @@ show_unused_style_keywords() { # Function to remove a keyword from the temp file remove_keyword() { keyword="$1" - - grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" - line_count=$(wc -l < $REMOVAL_KEYS_FILE) - mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" + if grep -q "$keyword" "$STYLES_KEYS_FILE"; then + grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" + mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" + return 0 # Keyword was removed + else + return 1 # Keyword was not found + fi } lookfor_unused_keywords() { # Loop through all files in the src folder find 'src' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do - # Search for keywords starting with "styles" - grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do - + # Search for keywords starting with "styles" + grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do + # Remove any [ ] characters from the keyword clean_keyword="${keyword//[\[\]]/}" # skip styles. keyword that might be used in comments @@ -55,8 +59,17 @@ lookfor_unused_keywords() { continue fi - remove_keyword "$clean_keyword" + if ! remove_keyword "$clean_keyword" ; then + # In case of a leaf of the styles object is being used, it meas the parent objects is being used + # we need to mark it as used. + if [[ "$clean_keyword" =~ ^styles\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$ ]]; then + # Keyword has more than two words, remove words after the second word + keyword_prefix="$(echo "$clean_keyword" | sed -E 's/(styles\.[a-zA-Z0-9_-]+)\..*/\1/')" + remove_keyword "$keyword_prefix" + fi + fi done + done } @@ -70,34 +83,30 @@ find_styles_and_store_keys() { while IFS= read -r line; do ((line_number++)) # Increment the line number - # Skip lines that are not key-related - if [[ ! "$line" =~ ^[[:space:]]*const[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} ]]; then + # Skip lines that are not key-related + if [[ ! "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} && ! "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then continue fi - - if [[ "$line" =~ ^[[:space:]]*const[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then - root_key=$(echo "${BASH_REMATCH[1]}" | sed -E "s/[:[:space:]]*\{.*//") - elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{ ]]; then + # Handle keys defined in functions within objects + function_key_pattern="^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then + root_key=$(echo "${BASH_REMATCH[2]}" | sed -E "s/[:[:space:]]*\{.*//") + elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{ || "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then local key=$(echo "$line" | sed -E "s/[:[:space:]]*\{.*//") - # local line_number=$(echo "$line" | grep -n "$key:" | cut -d':' -f1) - echo "line $line" - # Handle keys defined in functions within objects - function_key_pattern="[a-zA-Z0-9_-]+:[[:space:]]*\\(.*\\)[[:space:]]*=>[[:space:]]*\\{" - if [[ "$line" =~ $function_key_pattern ]]; then + + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then key="${BASH_REMATCH[0]%%:*}" fi + key="${key// /}" # Trim spaces if [[ ${#parent_keys[@]} -gt 0 ]]; then parent_key_trimmed="${parent_keys[${#parent_keys[@]}-1]// /}" # Trim spaces - key_trimmed="${key// /}" # Trim spaces - key="$parent_key_trimmed.$key_trimmed" + key="$parent_key_trimmed.$key" elif [[ -n "$root_key" ]]; then parent_key_trimmed="${root_key// /}" # Trim spaces - key_trimmed="${key// /}" # Trim spaces - key="$parent_key_trimmed.$key_trimmed" + key="$parent_key_trimmed.$key" fi - # echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" if [[ "$key" == "styles."* ]]; then echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" else @@ -105,27 +114,85 @@ find_styles_and_store_keys() { fi parent_keys+=("$key") elif [[ "$line" =~ ^[[:space:]]*\} ]]; then - # unset "parent_keys[${#parent_keys[@]}-1]" parent_keys=("${parent_keys[@]:0:${#parent_keys[@]}-1}") fi - # done < <(grep -E "^[[:space:]]*const[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\}" "$file") done < "$file" } +find_utility_styles_store_prefix() { + # Loop through all files in the src folder + find 'src/styles' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + + # Search for keywords starting with "styles" + grep -E -o './utilities/[a-zA-Z0-9_-]+' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do + variable=$(echo "$keyword" | sed 's/.*\///') + variable_trimmed="${variable// /}" # Trim spaces + + echo "$variable_trimmed" >> "$UTILITY_STYLES_KEYS_FILE" + done + done + + # Sort and remove duplicates from the temporary file + sort -u -o "$UTILITY_STYLES_KEYS_FILE" "$UTILITY_STYLES_KEYS_FILE" +} + +find_utility_usage_as_styles() { + find $UTILITIES_STYLES_FILE -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + if [ -d "$path" ]; then + # Use the folder name as the root key + root_key=$(basename "$path") + echo "styles.$root_key:$path:0" >> "$STYLES_KEYS_FILE" + continue + fi + find_styles_and_store_keys $file + done +} + +lookfor_unused_utilities() { + # Read each utility keyword from the file + while read -r keyword; do + # Creating a copy so later the replacement can reference it + original_keyword="$keyword" + + # Iterate through all files in "src/styles" + find 'src/styles' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + # Find all words that match "$keyword.[a-zA-Z0-9_-]+" + grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r match; do + # Replace the utility prefix with "styles" + variable=$(echo "$match" | sed "s/^$original_keyword/styles/") + + # Call the remove_keyword function with the variable + remove_keyword "$variable" + done + done + done < "$UTILITY_STYLES_KEYS_FILE" +} + +# Find and store the name of the utility files as keys +find_utility_styles_store_prefix + # Find and store keys from styles.js find_styles_and_store_keys "$STYLES_FILE" +find_utility_usage_as_styles + +# Look for usages of utilities into src/styles +lookfor_unused_utilities + echo "Keys saved to $KEYS_FILE" -echo "Now go through the list and remove the keys that are used." +echo "Now going through the list and removing the keys that are being used." line_count=$(wc -l < $STYLES_KEYS_FILE) -echo "Number of lines in the file: $line_count" +echo "Number of styles found: $line_count" -# lookfor_unused_keywords +lookfor_unused_keywords echo "Unused keys are into to $STYLES_KEYS_FILE" line_count2=$(wc -l < $STYLES_KEYS_FILE) -echo "Number of lines in the file: $line_count2" +echo "Number of styles not being used: $line_count2" + +show_unused_style_keywords -# show_unused_style_keywords \ No newline at end of file +# Delete all files containing a specific pattern in their name +find /scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; \ No newline at end of file From c2ea419b30a93e0a1fa28442dcb6717ed1816744 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 6 Sep 2023 18:29:24 +0200 Subject: [PATCH 017/117] Code clean up + improvements to the output --- scripts/find-unused-keys.sh | 42 ++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index 8e00c19dbeef..0f713a312197 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -7,30 +7,43 @@ UTILITIES_STYLES_FILE="src/styles/utilities" STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" UTILITY_STYLES_KEYS_FILE="scripts/utility_keys_list_temp.txt" REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" + +# FILE_EXTENSIONS="-name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx'" +FILE_EXTENSIONS=('-name' '*.js' '-o' '-name' '*.jsx' '-o' '-name' '*.ts' '-o' '-name' '*.tsx') + +# trap ctrl-c and call ctrl_c() +trap ctrl_c INT + +function ctrl_c() { + find scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; + exit 1 +} +source scripts/shellUtils.sh + # Create an empty temp file if it doesn't exist -if [ ! -f "$REMOVAL_KEYS_FILE" ]; then - touch "$REMOVAL_KEYS_FILE" -fi +# if [ ! -f "$REMOVAL_KEYS_FILE" ]; then +# touch "$REMOVAL_KEYS_FILE" +# fi # Read the style file with unused keys show_unused_style_keywords() { while IFS=: read -r key file line_number; do - line_count=$(wc -l < $STYLES_KEYS_FILE) - echo "Unused keys: $line_count" - echo "File: $file" + title "File: $file:$line_number" # Get lines before and after the error line lines_before=$((line_number - 3)) lines_after=$((line_number + 3)) # Print context of the error line - echo "Context around line $line_number:" sed -n "$lines_before,$lines_after p" "$file" | awk -v key="$key" '{gsub(key, "\033[1;31m"key"\033[0m"); print}' - echo "Unused key: $key" + error "Unused key: $key" echo "--------------------------------" done < "$STYLES_KEYS_FILE" + + line_count=$(wc -l < $STYLES_KEYS_FILE) + error "Unused keys: $line_count" } # Function to remove a keyword from the temp file @@ -39,6 +52,7 @@ remove_keyword() { if grep -q "$keyword" "$STYLES_KEYS_FILE"; then grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" + return 0 # Keyword was removed else return 1 # Keyword was not found @@ -47,7 +61,7 @@ remove_keyword() { lookfor_unused_keywords() { # Loop through all files in the src folder - find 'src' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + find 'src' -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do # Search for keywords starting with "styles" grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do @@ -121,7 +135,7 @@ find_styles_and_store_keys() { find_utility_styles_store_prefix() { # Loop through all files in the src folder - find 'src/styles' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do # Search for keywords starting with "styles" grep -E -o './utilities/[a-zA-Z0-9_-]+' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do @@ -137,7 +151,7 @@ find_utility_styles_store_prefix() { } find_utility_usage_as_styles() { - find $UTILITIES_STYLES_FILE -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + find $UTILITIES_STYLES_FILE -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do if [ -d "$path" ]; then # Use the folder name as the root key root_key=$(basename "$path") @@ -155,7 +169,7 @@ lookfor_unused_utilities() { original_keyword="$keyword" # Iterate through all files in "src/styles" - find 'src/styles' -type f -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" | while read -r file; do + find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do # Find all words that match "$keyword.[a-zA-Z0-9_-]+" grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r match; do # Replace the utility prefix with "styles" @@ -194,5 +208,5 @@ echo "Number of styles not being used: $line_count2" show_unused_style_keywords -# Delete all files containing a specific pattern in their name -find /scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; \ No newline at end of file +# Delete all temo files +find scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; \ No newline at end of file From 3042c9abe0fa3f3bab4bb0fb97a1c8c039f748c7 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 7 Sep 2023 16:15:53 +0200 Subject: [PATCH 018/117] Clean up, used builtins and improved code readability --- scripts/find-unused-keys.sh | 165 +++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 77 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index 0f713a312197..b24b4c1150e2 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -1,30 +1,41 @@ #!/bin/bash # Configurations -SRC_DIR="src" -STYLES_FILE="src/styles/styles.js" -UTILITIES_STYLES_FILE="src/styles/utilities" -STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" -UTILITY_STYLES_KEYS_FILE="scripts/utility_keys_list_temp.txt" -REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" +readonly SRC_DIR="src" +readonly STYLES_FILE="src/styles/styles.js" +readonly UTILITIES_STYLES_FILE="src/styles/utilities" +readonly STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" +readonly UTILITY_STYLES_KEYS_FILE="scripts/utility_keys_list_temp.txt" +readonly REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" +readonly AMOUNT_LINES_TO_SHOW=3 + +readonly FILE_EXTENSIONS=('-name' '*.js' '-o' '-name' '*.jsx' '-o' '-name' '*.ts' '-o' '-name' '*.tsx') + +# Regex +readonly OBJ_PROP_DECLARATION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\}" +readonly OBJ_PROP_FUNC_DEFINITION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} && ! [[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" +readonly OBJ_DEFINITION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{" +readonly CAPTURE_ARROW_FUNC_REGEX='^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{' +readonly CAPTURE_OBJ_ARROW_FUNC_REGEX='^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{' -# FILE_EXTENSIONS="-name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx'" -FILE_EXTENSIONS=('-name' '*.js' '-o' '-name' '*.jsx' '-o' '-name' '*.ts' '-o' '-name' '*.tsx') +source scripts/shellUtils.sh # trap ctrl-c and call ctrl_c() trap ctrl_c INT -function ctrl_c() { +delete_temp_files() { find scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; +} + +ctrl_c() { + delete_temp_files exit 1 } -source scripts/shellUtils.sh - -# Create an empty temp file if it doesn't exist -# if [ ! -f "$REMOVAL_KEYS_FILE" ]; then -# touch "$REMOVAL_KEYS_FILE" -# fi +count_lines() { + local file=$1 + wc -l < $file +} # Read the style file with unused keys show_unused_style_keywords() { @@ -32,23 +43,29 @@ show_unused_style_keywords() { title "File: $file:$line_number" # Get lines before and after the error line - lines_before=$((line_number - 3)) - lines_after=$((line_number + 3)) - - # Print context of the error line - sed -n "$lines_before,$lines_after p" "$file" | awk -v key="$key" '{gsub(key, "\033[1;31m"key"\033[0m"); print}' + local lines_before=$((line_number - AMOUNT_LINES_TO_SHOW)) + local lines_after=$((line_number + AMOUNT_LINES_TO_SHOW)) + # Read the lines into an array + local lines=() + while IFS= read -r line; do + lines+=("$line") + done < "$file" + + # Loop through the lines + for ((i = lines_before; i <= lines_after; i++)); do + local line="${lines[i]}" + # Print context of the error line + echo "$line" + done error "Unused key: $key" echo "--------------------------------" done < "$STYLES_KEYS_FILE" - - line_count=$(wc -l < $STYLES_KEYS_FILE) - error "Unused keys: $line_count" } # Function to remove a keyword from the temp file remove_keyword() { - keyword="$1" + local keyword="$1" if grep -q "$keyword" "$STYLES_KEYS_FILE"; then grep -v "$keyword" "$STYLES_KEYS_FILE" > "$REMOVAL_KEYS_FILE" mv "$REMOVAL_KEYS_FILE" "$STYLES_KEYS_FILE" @@ -61,13 +78,13 @@ remove_keyword() { lookfor_unused_keywords() { # Loop through all files in the src folder - find 'src' -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do + while read -r file; do # Search for keywords starting with "styles" - grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do + while IFS= read -r keyword; do # Remove any [ ] characters from the keyword - clean_keyword="${keyword//[\[\]]/}" + local clean_keyword="${keyword//[\[\]]/}" # skip styles. keyword that might be used in comments if [[ "$clean_keyword" == "styles." ]]; then continue @@ -78,46 +95,41 @@ lookfor_unused_keywords() { # we need to mark it as used. if [[ "$clean_keyword" =~ ^styles\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$ ]]; then # Keyword has more than two words, remove words after the second word - keyword_prefix="$(echo "$clean_keyword" | sed -E 's/(styles\.[a-zA-Z0-9_-]+)\..*/\1/')" + local keyword_prefix="${clean_keyword%.*}" remove_keyword "$keyword_prefix" fi fi - done - - done + done < <(grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') + done < <(find 'src' -type f \( "${FILE_EXTENSIONS[@]}" \)) } + # Function to find and store keys from a file find_styles_and_store_keys() { local file="$1" local parent_keys=() local root_key="" - local line_number=0 # Initialize the line number + local line_number=0 while IFS= read -r line; do - ((line_number++)) # Increment the line number + ((line_number++)) # Skip lines that are not key-related - if [[ ! "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} && ! "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then + if [[ ! "$line" =~ $OBJ_PROP_DECLARATION_REGEX && ! "$line" =~ $CAPTURE_ARROW_FUNC_REGEX ]]; then continue fi - # Handle keys defined in functions within objects - function_key_pattern="^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" - if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then - root_key=$(echo "${BASH_REMATCH[2]}" | sed -E "s/[:[:space:]]*\{.*//") - elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{ || "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then - local key=$(echo "$line" | sed -E "s/[:[:space:]]*\{.*//") - - if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then - key="${BASH_REMATCH[0]%%:*}" - fi - + + if [[ "$line" =~ $OBJ_DEFINITION_REGEX ]]; then + root_key="${BASH_REMATCH[2]%%:*{*)}" + elif [[ "$line" =~ $CAPTURE_OBJ_ARROW_FUNC_REGEX ]]; then + # Removing all the extra lines after the ":" + local key="${line%%:*}" key="${key// /}" # Trim spaces if [[ ${#parent_keys[@]} -gt 0 ]]; then - parent_key_trimmed="${parent_keys[${#parent_keys[@]}-1]// /}" # Trim spaces + local parent_key_trimmed="${parent_keys[${#parent_keys[@]}-1]// /}" # Trim spaces key="$parent_key_trimmed.$key" elif [[ -n "$root_key" ]]; then - parent_key_trimmed="${root_key// /}" # Trim spaces + local parent_key_trimmed="${root_key// /}" # Trim spaces key="$parent_key_trimmed.$key" fi @@ -135,50 +147,49 @@ find_styles_and_store_keys() { find_utility_styles_store_prefix() { # Loop through all files in the src folder - find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do - + while read -r file; do # Search for keywords starting with "styles" - grep -E -o './utilities/[a-zA-Z0-9_-]+' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r keyword; do - variable=$(echo "$keyword" | sed 's/.*\///') - variable_trimmed="${variable// /}" # Trim spaces + while IFS= read -r keyword; do + local variable="${keyword##*/}" + local variable_trimmed="${variable// /}" # Trim spaces echo "$variable_trimmed" >> "$UTILITY_STYLES_KEYS_FILE" - done - done + done < <(grep -E -o './utilities/[a-zA-Z0-9_-]+' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') + done < <(find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \)) # Sort and remove duplicates from the temporary file sort -u -o "$UTILITY_STYLES_KEYS_FILE" "$UTILITY_STYLES_KEYS_FILE" } find_utility_usage_as_styles() { - find $UTILITIES_STYLES_FILE -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do + while read -r file; do if [ -d "$path" ]; then # Use the folder name as the root key - root_key=$(basename "$path") + local root_key=$(basename "$path") echo "styles.$root_key:$path:0" >> "$STYLES_KEYS_FILE" continue fi find_styles_and_store_keys $file - done + done < <(find $UTILITIES_STYLES_FILE -type f \( "${FILE_EXTENSIONS[@]}" \)) } lookfor_unused_utilities() { # Read each utility keyword from the file while read -r keyword; do # Creating a copy so later the replacement can reference it - original_keyword="$keyword" + local original_keyword="$keyword" # Iterate through all files in "src/styles" - find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \) | while read -r file; do + while read -r file; do # Find all words that match "$keyword.[a-zA-Z0-9_-]+" - grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/' | while IFS= read -r match; do + while IFS= read -r match; do # Replace the utility prefix with "styles" - variable=$(echo "$match" | sed "s/^$original_keyword/styles/") + local variable="${match/#$original_keyword/styles}" # Call the remove_keyword function with the variable remove_keyword "$variable" - done - done + done < <(grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') + done < <(find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \)) done < "$UTILITY_STYLES_KEYS_FILE" } @@ -193,20 +204,20 @@ find_utility_usage_as_styles # Look for usages of utilities into src/styles lookfor_unused_utilities -echo "Keys saved to $KEYS_FILE" -echo "Now going through the list and removing the keys that are being used." - -line_count=$(wc -l < $STYLES_KEYS_FILE) -echo "Number of styles found: $line_count" +echo "⏱️ Now going through the list and looking for unused keys." lookfor_unused_keywords -echo "Unused keys are into to $STYLES_KEYS_FILE" - -line_count2=$(wc -l < $STYLES_KEYS_FILE) -echo "Number of styles not being used: $line_count2" - -show_unused_style_keywords - -# Delete all temo files -find scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; \ No newline at end of file +final_styles_line_count=$(count_lines "$STYLES_KEYS_FILE") + +if [[ $final_styles_line_count -eq 0 ]]; then + # Exit successfully (status code 0) + delete_temp_files + success "Styles are in a good shape" + exit 0 +else + show_unused_style_keywords + delete_temp_files + error "Unused keys: $final_styles_line_count" + exit 1 +fi \ No newline at end of file From 6fa51d0bbbfe9b244a7c7732b8b9ac3f1c8d835e Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 7 Sep 2023 18:09:34 +0200 Subject: [PATCH 019/117] Fixed lint issues --- scripts/find-unused-keys.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index b24b4c1150e2..568e3f7045d2 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -13,10 +13,9 @@ readonly FILE_EXTENSIONS=('-name' '*.js' '-o' '-name' '*.jsx' '-o' '-name' '*.ts # Regex readonly OBJ_PROP_DECLARATION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\}" -readonly OBJ_PROP_FUNC_DEFINITION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} && ! [[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" readonly OBJ_DEFINITION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{" -readonly CAPTURE_ARROW_FUNC_REGEX='^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{' -readonly CAPTURE_OBJ_ARROW_FUNC_REGEX='^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{' +readonly CAPTURE_ARROW_FUNC_REGEX="^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" +readonly CAPTURE_OBJ_ARROW_FUNC_REGEX="^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" source scripts/shellUtils.sh @@ -27,6 +26,7 @@ delete_temp_files() { find scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; } +# shellcheck disable=SC2317 # Don't warn about unreachable commands in this function ctrl_c() { delete_temp_files exit 1 @@ -34,7 +34,7 @@ ctrl_c() { count_lines() { local file=$1 - wc -l < $file + wc -l < "$file" } # Read the style file with unused keys @@ -100,7 +100,7 @@ lookfor_unused_keywords() { fi fi done < <(grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') - done < <(find 'src' -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find $SRC_DIR -type f \( "${FILE_EXTENSIONS[@]}" \)) } @@ -163,13 +163,13 @@ find_utility_styles_store_prefix() { find_utility_usage_as_styles() { while read -r file; do - if [ -d "$path" ]; then + if [ -d "$file" ]; then # Use the folder name as the root key - local root_key=$(basename "$path") + local root_key=$(basename "$file") echo "styles.$root_key:$path:0" >> "$STYLES_KEYS_FILE" continue fi - find_styles_and_store_keys $file + find_styles_and_store_keys "$file" done < <(find $UTILITIES_STYLES_FILE -type f \( "${FILE_EXTENSIONS[@]}" \)) } From 3424140f511f65020540ce5d7ca88b5ca49ce50f Mon Sep 17 00:00:00 2001 From: Eduardo Date: Thu, 7 Sep 2023 18:12:11 +0200 Subject: [PATCH 020/117] typo fixed --- scripts/find-unused-keys.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index 568e3f7045d2..f492f43a5233 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -91,7 +91,7 @@ lookfor_unused_keywords() { fi if ! remove_keyword "$clean_keyword" ; then - # In case of a leaf of the styles object is being used, it meas the parent objects is being used + # In case of a leaf of the styles object is being used, it means the parent objects is being used # we need to mark it as used. if [[ "$clean_keyword" =~ ^styles\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$ ]]; then # Keyword has more than two words, remove words after the second word From 656beff46a4b2f42bd8b2cf88603f6fbd9156aac Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Thu, 7 Sep 2023 12:32:13 -0700 Subject: [PATCH 021/117] update policy type definitions --- src/types/onyx/Policy.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index cacbb5d15199..87382090f294 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -32,6 +32,10 @@ type Policy = { isFromFullPolicy?: boolean; lastModified?: string; customUnits?: Record; + + areChatRoomsEnabled?: boolean; + + isPolicyExpenseChatEnabled?: boolean; }; export default Policy; From b43deaf73b193d3c57cc92c76698eda54821567a Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Thu, 7 Sep 2023 12:43:02 -0700 Subject: [PATCH 022/117] add and use areChatRoomsEnabled --- src/libs/PolicyUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 164f284a4ef5..743dfba34382 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -10,7 +10,7 @@ import ONYXKEYS from '../ONYXKEYS'; * @returns {Array} */ function getActivePolicies(policies) { - return _.filter(policies, (policy) => policy && policy.isPolicyExpenseChatEnabled && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); + return _.filter(policies, (policy) => policy && (policy.isPolicyExpenseChatEnabled || policy.areChatRoomsEnabled) && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); } /** From ac8aea27d87efac1766ac5fc552d9c798736942a Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 8 Sep 2023 11:59:01 +0200 Subject: [PATCH 023/117] fixed some lint errors + clean ups --- scripts/find-unused-keys.sh | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index f492f43a5233..2bb0c8200aa0 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -107,6 +107,7 @@ lookfor_unused_keywords() { # Function to find and store keys from a file find_styles_and_store_keys() { local file="$1" + local base_name="${2:-styles}" # Set styles as default local parent_keys=() local root_key="" local line_number=0 @@ -134,9 +135,9 @@ find_styles_and_store_keys() { fi if [[ "$key" == "styles."* ]]; then - echo "$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + echo "${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" else - echo "styles.$key:$file:$line_number" >> "$STYLES_KEYS_FILE" + echo "styles.${key}|${base_name}.${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" fi parent_keys+=("$key") elif [[ "$line" =~ ^[[:space:]]*\} ]]; then @@ -158,18 +159,24 @@ find_utility_styles_store_prefix() { done < <(find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \)) # Sort and remove duplicates from the temporary file - sort -u -o "$UTILITY_STYLES_KEYS_FILE" "$UTILITY_STYLES_KEYS_FILE" + sort -u -o "${UTILITY_STYLES_KEYS_FILE}" "${UTILITY_STYLES_KEYS_FILE}" } find_utility_usage_as_styles() { while read -r file; do - if [ -d "$file" ]; then - # Use the folder name as the root key - local root_key=$(basename "$file") - echo "styles.$root_key:$path:0" >> "$STYLES_KEYS_FILE" + local folder_name + local root_key + local parent_dir + + # Get the folder name, given this utility files are index.js + parent_dir=$(dirname "$file") + root_key=$(basename "${parent_dir}") + + if [[ "${root_key}" == "utilities" ]]; then continue fi - find_styles_and_store_keys "$file" + + find_styles_and_store_keys "${file}" "${root_key}" done < <(find $UTILITIES_STYLES_FILE -type f \( "${FILE_EXTENSIONS[@]}" \)) } @@ -177,7 +184,7 @@ lookfor_unused_utilities() { # Read each utility keyword from the file while read -r keyword; do # Creating a copy so later the replacement can reference it - local original_keyword="$keyword" + local original_keyword="${keyword}" # Iterate through all files in "src/styles" while read -r file; do @@ -185,9 +192,9 @@ lookfor_unused_utilities() { while IFS= read -r match; do # Replace the utility prefix with "styles" local variable="${match/#$original_keyword/styles}" - # Call the remove_keyword function with the variable - remove_keyword "$variable" + remove_keyword "${variable}" + remove_keyword "${match}" done < <(grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') done < <(find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \)) done < "$UTILITY_STYLES_KEYS_FILE" From ff2174b06790a1f974d14b1037b35005403152d0 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 8 Sep 2023 15:25:29 +0200 Subject: [PATCH 024/117] removed unused variable --- scripts/find-unused-keys.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/find-unused-keys.sh b/scripts/find-unused-keys.sh index 2bb0c8200aa0..5dff65571d54 100755 --- a/scripts/find-unused-keys.sh +++ b/scripts/find-unused-keys.sh @@ -164,7 +164,6 @@ find_utility_styles_store_prefix() { find_utility_usage_as_styles() { while read -r file; do - local folder_name local root_key local parent_dir From 261adb0db8db5ea80b67f78b060738380b72d5eb Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 11 Sep 2023 16:18:36 +0200 Subject: [PATCH 025/117] moved unused style script into workflow actions --- .../scripts/findUnusedKeys.sh | 22 +++++++++++-------- .github/workflows/findUnusedStyles.yml | 22 +++++++++++++++++++ package.json | 2 +- 3 files changed, 36 insertions(+), 10 deletions(-) rename scripts/find-unused-keys.sh => .github/scripts/findUnusedKeys.sh (90%) create mode 100644 .github/workflows/findUnusedStyles.yml diff --git a/scripts/find-unused-keys.sh b/.github/scripts/findUnusedKeys.sh similarity index 90% rename from scripts/find-unused-keys.sh rename to .github/scripts/findUnusedKeys.sh index 5dff65571d54..1ab00662dfe7 100755 --- a/scripts/find-unused-keys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -1,12 +1,16 @@ #!/bin/bash # Configurations -readonly SRC_DIR="src" -readonly STYLES_FILE="src/styles/styles.js" -readonly UTILITIES_STYLES_FILE="src/styles/utilities" -readonly STYLES_KEYS_FILE="scripts/style_keys_list_temp.txt" -readonly UTILITY_STYLES_KEYS_FILE="scripts/utility_keys_list_temp.txt" -readonly REMOVAL_KEYS_FILE="scripts/removal_keys_list_temp.txt" +declare LIB_PATH +LIB_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../../ && pwd)" + +readonly SRC_DIR="${LIB_PATH}/src" +readonly STYLES_DIR="${LIB_PATH}/src/styles" +readonly STYLES_FILE="${LIB_PATH}/src/styles/styles.js" +readonly UTILITIES_STYLES_FILE="${LIB_PATH}/src/styles/utilities" +readonly STYLES_KEYS_FILE="${LIB_PATH}/scripts/style_keys_list_temp.txt" +readonly UTILITY_STYLES_KEYS_FILE="${LIB_PATH}/scripts/utility_keys_list_temp.txt" +readonly REMOVAL_KEYS_FILE="${LIB_PATH}/scripts/removal_keys_list_temp.txt" readonly AMOUNT_LINES_TO_SHOW=3 readonly FILE_EXTENSIONS=('-name' '*.js' '-o' '-name' '*.jsx' '-o' '-name' '*.ts' '-o' '-name' '*.tsx') @@ -23,7 +27,7 @@ source scripts/shellUtils.sh trap ctrl_c INT delete_temp_files() { - find scripts -name "*keys_list_temp*" -type f -exec rm -f {} \; + find "${LIB_PATH}/scripts" -name "*keys_list_temp*" -type f -exec rm -f {} \; } # shellcheck disable=SC2317 # Don't warn about unreachable commands in this function @@ -156,7 +160,7 @@ find_utility_styles_store_prefix() { echo "$variable_trimmed" >> "$UTILITY_STYLES_KEYS_FILE" done < <(grep -E -o './utilities/[a-zA-Z0-9_-]+' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') - done < <(find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find $STYLES_DIR -type f \( "${FILE_EXTENSIONS[@]}" \)) # Sort and remove duplicates from the temporary file sort -u -o "${UTILITY_STYLES_KEYS_FILE}" "${UTILITY_STYLES_KEYS_FILE}" @@ -195,7 +199,7 @@ lookfor_unused_utilities() { remove_keyword "${variable}" remove_keyword "${match}" done < <(grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') - done < <(find 'src/styles' -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find $STYLES_DIR -type f \( "${FILE_EXTENSIONS[@]}" \)) done < "$UTILITY_STYLES_KEYS_FILE" } diff --git a/.github/workflows/findUnusedStyles.yml b/.github/workflows/findUnusedStyles.yml new file mode 100644 index 000000000000..b832084dcc7e --- /dev/null +++ b/.github/workflows/findUnusedStyles.yml @@ -0,0 +1,22 @@ +name: Find Unused styles + +on: + pull_request: + types: [opened, synchronize] + branches-ignore: [staging, production] + +jobs: + perf-tests: + if: ${{ github.actor != 'OSBotify' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup NodeJS + uses: Expensify/App/.github/actions/composite/setupNode@main + + - name: Run unused style searcher + shell: bash + run: ./.github/scripts/findUnusedKeys.sh + diff --git a/package.json b/package.json index 9baff5b8f0b1..391e32f40029 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "symbolicate:android": "npx metro-symbolicate android/app/build/generated/sourcemaps/react/release/index.android.bundle.map", "symbolicate:ios": "npx metro-symbolicate main.jsbundle.map", "test:e2e": "node tests/e2e/testRunner.js --development", - "find-missing-keys": "scripts/find-unused-keys.sh" + "gh-actions-unused-styles": "./.github/scripts/findUnusedKeys.sh" }, "dependencies": { "@expensify/react-native-web": "0.18.15", From 82cf6262896e84c37ee761e47e25b94c6632373a Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 11 Sep 2023 16:30:37 +0200 Subject: [PATCH 026/117] added find unused styles into lint.yml workflow --- .github/workflows/findUnusedStyles.yml | 22 ---------------------- .github/workflows/lint.yml | 4 ++++ 2 files changed, 4 insertions(+), 22 deletions(-) delete mode 100644 .github/workflows/findUnusedStyles.yml diff --git a/.github/workflows/findUnusedStyles.yml b/.github/workflows/findUnusedStyles.yml deleted file mode 100644 index b832084dcc7e..000000000000 --- a/.github/workflows/findUnusedStyles.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Find Unused styles - -on: - pull_request: - types: [opened, synchronize] - branches-ignore: [staging, production] - -jobs: - perf-tests: - if: ${{ github.actor != 'OSBotify' }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup NodeJS - uses: Expensify/App/.github/actions/composite/setupNode@main - - - name: Run unused style searcher - shell: bash - run: ./.github/scripts/findUnusedKeys.sh - diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 795271cab60a..7158d4f67e7b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,3 +31,7 @@ jobs: echo 'Error: Prettier diff detected! Please run `npm run prettier` and commit the changes.' exit 1 fi + + - name: Run unused style searcher + shell: bash + run: ./.github/scripts/findUnusedKeys.sh From d47feeee66e09807971ccd055333bb97e0de19ea Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 11 Sep 2023 16:39:04 +0200 Subject: [PATCH 027/117] fixed some lint issues --- .github/scripts/findUnusedKeys.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index 1ab00662dfe7..0dbd5c033d5f 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -104,7 +104,7 @@ lookfor_unused_keywords() { fi fi done < <(grep -E -o '\bstyles\.[a-zA-Z0-9_.]*' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') - done < <(find $SRC_DIR -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find "${SRC_DIR}" -type f \( "${FILE_EXTENSIONS[@]}" \)) } @@ -160,7 +160,7 @@ find_utility_styles_store_prefix() { echo "$variable_trimmed" >> "$UTILITY_STYLES_KEYS_FILE" done < <(grep -E -o './utilities/[a-zA-Z0-9_-]+' "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') - done < <(find $STYLES_DIR -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find "${STYLES_DIR}" -type f \( "${FILE_EXTENSIONS[@]}" \)) # Sort and remove duplicates from the temporary file sort -u -o "${UTILITY_STYLES_KEYS_FILE}" "${UTILITY_STYLES_KEYS_FILE}" @@ -180,7 +180,7 @@ find_utility_usage_as_styles() { fi find_styles_and_store_keys "${file}" "${root_key}" - done < <(find $UTILITIES_STYLES_FILE -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find "${UTILITIES_STYLES_FILE}" -type f \( "${FILE_EXTENSIONS[@]}" \)) } lookfor_unused_utilities() { @@ -199,7 +199,7 @@ lookfor_unused_utilities() { remove_keyword "${variable}" remove_keyword "${match}" done < <(grep -E -o "$original_keyword\.[a-zA-Z0-9_-]+" "$file" | grep -v '\/\/' | grep -vE '\/\*.*\*\/') - done < <(find $STYLES_DIR -type f \( "${FILE_EXTENSIONS[@]}" \)) + done < <(find "${STYLES_DIR}" -type f \( "${FILE_EXTENSIONS[@]}" \)) done < "$UTILITY_STYLES_KEYS_FILE" } From 3af3896cf768f7919a4bdf59bca46875a9396d53 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 11 Sep 2023 11:25:17 -0700 Subject: [PATCH 028/117] update type definitions --- src/types/onyx/Policy.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 87382090f294..4df45e9d1146 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -4,22 +4,22 @@ import * as OnyxCommon from './OnyxCommon'; type Policy = { /** The ID of the policy */ - id?: string; + id: string; /** The name of the policy */ - name?: string; + name: string; /** The current user's role in the policy */ - role?: ValueOf; + role: ValueOf; /** The policy type */ - type?: ValueOf; + type: ValueOf; /** The email of the policy owner */ - owner?: string; + owner: string; /** The output currency for the policy */ - outputCurrency?: string; + outputCurrency: string; /** The URL for the policy avatar */ avatar?: string; @@ -33,9 +33,9 @@ type Policy = { lastModified?: string; customUnits?: Record; - areChatRoomsEnabled?: boolean; + areChatRoomsEnabled: boolean; - isPolicyExpenseChatEnabled?: boolean; + isPolicyExpenseChatEnabled: boolean; }; export default Policy; From 4ab225844f4807cd41e819974057ac874d396a73 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 11 Sep 2023 11:26:48 -0700 Subject: [PATCH 029/117] update comment --- src/libs/PolicyUtils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 743dfba34382..5847452537ef 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -6,6 +6,7 @@ import ONYXKEYS from '../ONYXKEYS'; /** * Filter out the active policies, which will exclude policies with pending deletion + * These are policies that we can use to create reports with in NewDot. * @param {Object} policies * @returns {Array} */ From 9f3e7997b97b47a60b767d369c90ebe8342df63c Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Tue, 12 Sep 2023 18:42:43 +0200 Subject: [PATCH 030/117] Init device capabilities migration --- .../{index.native.js => index.native.ts} | 0 .../canUseTouchScreen/{index.js => index.ts} | 10 +++++----- src/libs/DeviceCapabilities/canUseTouchScreen/types.ts | 3 +++ .../{index.native.js => index.native.ts} | 0 .../hasHoverSupport/{index.js => index.ts} | 0 src/libs/DeviceCapabilities/hasHoverSupport/types.ts | 0 src/libs/DeviceCapabilities/{index.js => index.ts} | 0 7 files changed, 8 insertions(+), 5 deletions(-) rename src/libs/DeviceCapabilities/canUseTouchScreen/{index.native.js => index.native.ts} (100%) rename src/libs/DeviceCapabilities/canUseTouchScreen/{index.js => index.ts} (85%) create mode 100644 src/libs/DeviceCapabilities/canUseTouchScreen/types.ts rename src/libs/DeviceCapabilities/hasHoverSupport/{index.native.js => index.native.ts} (100%) rename src/libs/DeviceCapabilities/hasHoverSupport/{index.js => index.ts} (100%) create mode 100644 src/libs/DeviceCapabilities/hasHoverSupport/types.ts rename src/libs/DeviceCapabilities/{index.js => index.ts} (100%) diff --git a/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.js b/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.ts similarity index 100% rename from src/libs/DeviceCapabilities/canUseTouchScreen/index.native.js rename to src/libs/DeviceCapabilities/canUseTouchScreen/index.native.ts diff --git a/src/libs/DeviceCapabilities/canUseTouchScreen/index.js b/src/libs/DeviceCapabilities/canUseTouchScreen/index.ts similarity index 85% rename from src/libs/DeviceCapabilities/canUseTouchScreen/index.js rename to src/libs/DeviceCapabilities/canUseTouchScreen/index.ts index 17dcc9dffd73..e35dadf312be 100644 --- a/src/libs/DeviceCapabilities/canUseTouchScreen/index.js +++ b/src/libs/DeviceCapabilities/canUseTouchScreen/index.ts @@ -1,16 +1,16 @@ +import CanUseTouchScreen from './types'; + /** * Allows us to identify whether the platform has a touchscreen. * * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent - * - * @returns {Boolean} */ -function canUseTouchScreen() { +const canUseTouchScreen: CanUseTouchScreen = () => { let hasTouchScreen = false; if ('maxTouchPoints' in navigator) { hasTouchScreen = navigator.maxTouchPoints > 0; } else if ('msMaxTouchPoints' in navigator) { - hasTouchScreen = navigator.msMaxTouchPoints > 0; + hasTouchScreen = (navigator as Navigator)?.msMaxTouchPoints > 0; } else { const mQ = window.matchMedia && matchMedia('(pointer:coarse)'); if (mQ && mQ.media === '(pointer:coarse)') { @@ -24,6 +24,6 @@ function canUseTouchScreen() { } } return hasTouchScreen; -} +}; export default canUseTouchScreen; diff --git a/src/libs/DeviceCapabilities/canUseTouchScreen/types.ts b/src/libs/DeviceCapabilities/canUseTouchScreen/types.ts new file mode 100644 index 000000000000..6b71ecffeb05 --- /dev/null +++ b/src/libs/DeviceCapabilities/canUseTouchScreen/types.ts @@ -0,0 +1,3 @@ +type CanUseTouchScreen = () => boolean; + +export default CanUseTouchScreen; diff --git a/src/libs/DeviceCapabilities/hasHoverSupport/index.native.js b/src/libs/DeviceCapabilities/hasHoverSupport/index.native.ts similarity index 100% rename from src/libs/DeviceCapabilities/hasHoverSupport/index.native.js rename to src/libs/DeviceCapabilities/hasHoverSupport/index.native.ts diff --git a/src/libs/DeviceCapabilities/hasHoverSupport/index.js b/src/libs/DeviceCapabilities/hasHoverSupport/index.ts similarity index 100% rename from src/libs/DeviceCapabilities/hasHoverSupport/index.js rename to src/libs/DeviceCapabilities/hasHoverSupport/index.ts diff --git a/src/libs/DeviceCapabilities/hasHoverSupport/types.ts b/src/libs/DeviceCapabilities/hasHoverSupport/types.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/libs/DeviceCapabilities/index.js b/src/libs/DeviceCapabilities/index.ts similarity index 100% rename from src/libs/DeviceCapabilities/index.js rename to src/libs/DeviceCapabilities/index.ts From d0a1cba2c8fa06893896d018f5b48c692482c42b Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Tue, 12 Sep 2023 11:01:43 -0700 Subject: [PATCH 031/117] add comments --- src/types/onyx/Policy.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 4df45e9d1146..35855402807c 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -27,14 +27,25 @@ type Policy = { /** Error objects keyed by field name containing errors keyed by microtime */ errorFields?: OnyxCommon.ErrorFields; + /** Indicates the type of change made to the policy that hasn't been synced with the server yet */ pendingAction?: OnyxCommon.PendingAction; + + /** A list of errors keyed by microtime */ errors: OnyxCommon.Errors; + + /** Whether this policy was loaded from a policy summary, or loaded completely with all of its values */ isFromFullPolicy?: boolean; + + /** When this policy was last modified */ lastModified?: string; + + /** The custom units data for this policy */ customUnits?: Record; + /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. */ areChatRoomsEnabled: boolean; + /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. */ isPolicyExpenseChatEnabled: boolean; }; From b64ece2f38679e7841b7b2ae7e6f90d7b8ebb18e Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Tue, 12 Sep 2023 11:02:39 -0700 Subject: [PATCH 032/117] update comments --- src/types/onyx/Policy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 35855402807c..df4a1364a894 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -42,10 +42,10 @@ type Policy = { /** The custom units data for this policy */ customUnits?: Record; - /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. */ + /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ areChatRoomsEnabled: boolean; - /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. */ + /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; }; From ebf2e6683d3f019a8549984c72befd534c553401 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Tue, 12 Sep 2023 20:24:16 +0200 Subject: [PATCH 033/117] Improved script find objects and fields with arrow function on different moments, removed unused styles --- .github/scripts/findUnusedKeys.sh | 83 ++++++-- src/styles/styles.js | 336 +----------------------------- 2 files changed, 67 insertions(+), 352 deletions(-) diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index 0dbd5c033d5f..85d8112e8f13 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -15,12 +15,6 @@ readonly AMOUNT_LINES_TO_SHOW=3 readonly FILE_EXTENSIONS=('-name' '*.js' '-o' '-name' '*.jsx' '-o' '-name' '*.ts' '-o' '-name' '*.tsx') -# Regex -readonly OBJ_PROP_DECLARATION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\}" -readonly OBJ_DEFINITION_REGEX="^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{" -readonly CAPTURE_ARROW_FUNC_REGEX="^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" -readonly CAPTURE_OBJ_ARROW_FUNC_REGEX="^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{" - source scripts/shellUtils.sh # trap ctrl-c and call ctrl_c() @@ -38,7 +32,11 @@ ctrl_c() { count_lines() { local file=$1 - wc -l < "$file" + if [[ -e "$file" ]]; then + wc -l < "$file" + else + echo "File not found: $file" + fi } # Read the style file with unused keys @@ -109,24 +107,39 @@ lookfor_unused_keywords() { # Function to find and store keys from a file -find_styles_and_store_keys() { +find_styles_object_and_store_keys() { local file="$1" local base_name="${2:-styles}" # Set styles as default local parent_keys=() local root_key="" local line_number=0 + local inside_arrow_function=false while IFS= read -r line; do ((line_number++)) + # Check if we are inside an arrow function and we find a closing curly brace + if [[ "$inside_arrow_function" == true ]]; then + if [[ "$line" =~ ^[[:space:]]*\}\) ]]; then + inside_arrow_function=false + fi + continue + fi + + # Check if we are inside an arrow function + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then + inside_arrow_function=true + continue + fi + # Skip lines that are not key-related - if [[ ! "$line" =~ $OBJ_PROP_DECLARATION_REGEX && ! "$line" =~ $CAPTURE_ARROW_FUNC_REGEX ]]; then + if [[ ! "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} ]]; then continue fi - if [[ "$line" =~ $OBJ_DEFINITION_REGEX ]]; then + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then root_key="${BASH_REMATCH[2]%%:*{*)}" - elif [[ "$line" =~ $CAPTURE_OBJ_ARROW_FUNC_REGEX ]]; then + elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then # Removing all the extra lines after the ":" local key="${line%%:*}" key="${key// /}" # Trim spaces @@ -150,6 +163,42 @@ find_styles_and_store_keys() { done < "$file" } +find_styles_functions_and_store_keys() { + local file="$1" + local line_number=0 + local inside_object=false + local inside_arrow_function=false + local key="" + + while IFS= read -r line; do + ((line_number++)) + + # Check if we are inside an arrow function + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then + inside_arrow_function=true + key="${line%%:*}" + key="${key// /}" # Trim spaces + echo "styles.${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" + fi + + # If we are inside an arrow function and we find an opening curly brace, + # then set inside_object to true, indicating we are inside an object. + if [[ "$inside_arrow_function" == true && "$line" =~ ^[[:space:]]*\{ ]]; then + inside_object=true + fi + + # If we are inside an object, continue to the next line. + if [[ "$inside_object" == true ]]; then + continue + fi + + # If we find a closing curly brace, reset the inside_object flag. + if [[ "$line" =~ ^[[:space:]]*\},?$ ]]; then + inside_object=false + fi + done < "$file" +} + find_utility_styles_store_prefix() { # Loop through all files in the src folder while read -r file; do @@ -179,7 +228,7 @@ find_utility_usage_as_styles() { continue fi - find_styles_and_store_keys "${file}" "${root_key}" + find_styles_object_and_store_keys "${file}" "${root_key}" done < <(find "${UTILITIES_STYLES_FILE}" -type f \( "${FILE_EXTENSIONS[@]}" \)) } @@ -203,19 +252,19 @@ lookfor_unused_utilities() { done < "$UTILITY_STYLES_KEYS_FILE" } +echo "🔍 Looking for styles." # Find and store the name of the utility files as keys find_utility_styles_store_prefix +find_utility_usage_as_styles # Find and store keys from styles.js -find_styles_and_store_keys "$STYLES_FILE" +find_styles_object_and_store_keys "$STYLES_FILE" +find_styles_functions_and_store_keys "$STYLES_FILE" -find_utility_usage_as_styles +echo "🗄️ Now going through the codebase and looking for unused keys." # Look for usages of utilities into src/styles lookfor_unused_utilities - -echo "⏱️ Now going through the list and looking for unused keys." - lookfor_unused_keywords final_styles_line_count=$(count_lines "$STYLES_KEYS_FILE") diff --git a/src/styles/styles.js b/src/styles/styles.js index 1c1340600a51..2fd346bed64c 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -174,12 +174,6 @@ const styles = { ...themeColors, ...textUnderline, - rateCol: { - margin: 0, - padding: 0, - flexBasis: '48%', - }, - autoCompleteSuggestionsContainer: { backgroundColor: themeColors.appBG, borderRadius: 8, @@ -236,13 +230,6 @@ const styles = { borderRadius: 12, }, - unitCol: { - margin: 0, - padding: 0, - marginLeft: '4%', - flexBasis: '48%', - }, - webViewStyles, link, @@ -265,19 +252,6 @@ const styles = { backgroundColor: themeColors.appBG, }, - h1: { - color: themeColors.heading, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeh1, - fontWeight: fontWeightBold, - }, - - h3: { - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontSize: variables.fontSizeNormal, - fontWeight: fontWeightBold, - }, - h4: { fontFamily: fontFamily.EXP_NEUE_BOLD, fontSize: variables.fontSizeLabel, @@ -428,10 +402,6 @@ const styles = { color: themeColors.textSupporting, }, - colorHeading: { - color: themeColors.heading, - }, - bgTransparent: { backgroundColor: 'transparent', }, @@ -632,10 +602,6 @@ const styles = { backgroundColor: themeColors.activeComponentBG, }, - fontWeightBold: { - fontWeight: fontWeightBold, - }, - touchableButtonImage: { alignItems: 'center', height: variables.componentSizeNormal, @@ -829,10 +795,6 @@ const styles = { height: CONST.DESKTOP_HEADER_PADDING, }, - pushTextRight: { - left: 100000, - }, - reportOptions: { marginLeft: 8, }, @@ -1104,10 +1066,6 @@ const styles = { noOutline: addOutlineWidth({}, 0), - errorOutline: { - borderColor: themeColors.danger, - }, - textLabelSupporting: { fontFamily: fontFamily.EXP_NEUE, fontSize: variables.fontSizeLabel, @@ -1173,13 +1131,6 @@ const styles = { marginBottom: 4, }, - desktopRedirectPage: { - backgroundColor: themeColors.appBG, - minHeight: '100%', - flex: 1, - alignItems: 'center', - }, - signInPage: { backgroundColor: themeColors.highlightBG, minHeight: '100%', @@ -1559,11 +1510,6 @@ const styles = { width: 18, }, - chatContent: { - flex: 4, - justifyContent: 'flex-end', - }, - chatContentScrollView: { flexGrow: 1, justifyContent: 'flex-start', @@ -1718,11 +1664,6 @@ const styles = { textAlignVertical: 'top', }, - editInputComposeSpacing: { - backgroundColor: themeColors.transparent, - marginVertical: 8, - }, - // composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669 textInputComposeSpacing: { paddingVertical: 5, @@ -1844,23 +1785,6 @@ const styles = { width: 200, }, - chatSwticherPillWrapper: { - marginTop: 5, - marginRight: 4, - }, - - navigationModalOverlay: { - ...userSelect.userSelectNone, - position: 'absolute', - width: '100%', - height: '100%', - transform: [ - { - translateX: -variables.sideBarWidth, - }, - ], - }, - sidebarVisible: { borderRightWidth: 1, }, @@ -1885,14 +1809,6 @@ const styles = { borderRadius: 24, }, - singleSubscript: { - height: variables.iconSizeNormal, - width: variables.iconSizeNormal, - backgroundColor: themeColors.icon, - borderRadius: 20, - zIndex: 1, - }, - singleAvatarSmall: { height: 18, width: 18, @@ -1936,17 +1852,6 @@ const styles = { right: 0, }, - leftSideLargeAvatar: { - left: 15, - }, - - rightSideLargeAvatar: { - right: 15, - zIndex: 2, - borderWidth: 4, - borderRadius: 100, - }, - secondAvatarInline: { bottom: -3, right: -25, @@ -1961,18 +1866,6 @@ const styles = { height: variables.avatarSizeLarge, }, - avatarNormal: { - height: variables.componentSizeNormal, - width: variables.componentSizeNormal, - borderRadius: variables.componentSizeNormal, - }, - - avatarSmall: { - height: variables.avatarSizeSmall, - width: variables.avatarSizeSmall, - borderRadius: variables.avatarSizeSmall, - }, - avatarInnerText: { color: themeColors.textLight, fontSize: variables.fontSizeSmall, @@ -1989,21 +1882,6 @@ const styles = { textAlign: 'center', }, - avatarSpace: { - top: 3, - left: 3, - }, - - avatar: { - backgroundColor: themeColors.sidebar, - borderColor: themeColors.sidebar, - }, - - focusedAvatar: { - backgroundColor: themeColors.border, - borderColor: themeColors.border, - }, - emptyAvatar: { height: variables.avatarSizeNormal, width: variables.avatarSizeNormal, @@ -2050,11 +1928,6 @@ const styles = { marginRight: variables.avatarChatSpacing - 4, }, - modalViewContainer: { - alignItems: 'center', - flex: 1, - }, - borderTop: { borderTopWidth: variables.borderTopWidth, borderColor: themeColors.border, @@ -2144,14 +2017,6 @@ const styles = { ...(isSmallScreenWidth && flex.flex1), }), - modalCenterContentContainer: { - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: themeColors.modalBackdrop, - }, - centeredModalStyles: (isSmallScreenWidth, isFullScreenWhenSmall) => ({ borderWidth: isSmallScreenWidth && !isFullScreenWhenSmall ? 1 : 0, marginHorizontal: isSmallScreenWidth ? 0 : 20, @@ -2174,28 +2039,6 @@ const styles = { alignItems: 'center', }, - notFoundSafeArea: { - flex: 1, - backgroundColor: themeColors.heading, - }, - - notFoundView: { - flex: 1, - alignItems: 'center', - paddingTop: 40, - paddingBottom: 40, - justifyContent: 'space-between', - }, - - notFoundLogo: { - width: 202, - height: 63, - }, - - notFoundContent: { - alignItems: 'center', - }, - notFoundTextHeader: { ...headlineFont, color: themeColors.heading, @@ -2206,20 +2049,6 @@ const styles = { textAlign: 'center', }, - notFoundTextBody: { - color: themeColors.componentBG, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 15, - }, - - notFoundButtonText: { - color: themeColors.link, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - fontSize: 15, - }, - blockingViewContainer: { paddingBottom: variables.contentHeaderHeight, }, @@ -2270,18 +2099,6 @@ const styles = { justifyContent: 'space-around', }, - settingsPageColumn: { - width: '100%', - alignItems: 'center', - justifyContent: 'space-around', - }, - - settingsPageContainer: { - justifyContent: 'space-between', - alignItems: 'center', - width: '100%', - }, - twoFactorAuthSection: { backgroundColor: themeColors.appBG, padding: 0, @@ -2419,18 +2236,6 @@ const styles = { left: -16, }, - svgAvatarBorder: { - borderRadius: 100, - overflow: 'hidden', - }, - - displayName: { - fontSize: variables.fontSizeLarge, - fontFamily: fontFamily.EXP_NEUE_BOLD, - fontWeight: fontWeightBold, - color: themeColors.heading, - }, - pageWrapper: { width: '100%', alignItems: 'center', @@ -2502,21 +2307,11 @@ const styles = { transform: [{rotate: '180deg'}], }, - navigationSceneContainer: { - backgroundColor: themeColors.appBG, - }, - navigationScreenCardStyle: { backgroundColor: themeColors.appBG, height: '100%', }, - navigationSceneFullScreenWrapper: { - borderRadius: variables.componentBorderRadiusCard, - overflow: 'hidden', - height: '100%', - }, - invisible: { position: 'absolute', opacity: 0, @@ -2554,14 +2349,6 @@ const styles = { paddingBottom: 0, }, - detailsPageSectionVersion: { - alignSelf: 'center', - color: themeColors.textSupporting, - fontSize: variables.fontSizeSmall, - height: 24, - lineHeight: 20, - }, - switchTrack: { width: 50, height: 28, @@ -2711,12 +2498,6 @@ const styles = { alignSelf: 'center', }, - iouDetailsContainer: { - flexGrow: 1, - paddingStart: 20, - paddingEnd: 20, - }, - codeWordWrapper: { ...codeStyles.codeWordWrapper, }, @@ -2756,11 +2537,6 @@ const styles = { zIndex: 10, }, - navigatorFullScreenLoading: { - backgroundColor: themeColors.highlightBG, - opacity: 1, - }, - reimbursementAccountFullScreenLoading: { backgroundColor: themeColors.componentBG, opacity: 0.8, @@ -2845,40 +2621,6 @@ const styles = { height: '100%', }, - fullscreenCard: { - position: 'absolute', - left: 0, - top: 0, - width: '100%', - height: '100%', - }, - - fullscreenCardWeb: { - left: 'auto', - right: '-24%', - top: '-18%', - height: '120%', - }, - - fullscreenCardWebCentered: { - left: '0', - right: '0', - top: '0', - height: '60%', - }, - - fullscreenCardMobile: { - left: '-20%', - top: '-30%', - width: '150%', - }, - - fullscreenCardMediumScreen: { - left: '-15%', - top: '-30%', - width: '145%', - }, - smallEditIcon: { alignItems: 'center', backgroundColor: themeColors.buttonHoveredBG, @@ -2897,41 +2639,6 @@ const styles = { bottom: -4, }, - workspaceCard: { - width: '100%', - height: 400, - borderRadius: variables.componentBorderRadiusCard, - overflow: 'hidden', - backgroundColor: themeColors.heroCard, - }, - - workspaceCardMobile: { - height: 475, - }, - - workspaceCardMediumScreen: { - height: 540, - }, - - workspaceCardMainText: { - fontSize: variables.fontSizeXXXLarge, - fontWeight: 'bold', - lineHeight: variables.fontSizeXXXLarge, - }, - - workspaceCardContent: { - zIndex: 1, - padding: 50, - }, - - workspaceCardContentMediumScreen: { - padding: 25, - }, - - workspaceCardCTA: { - width: 250, - }, - autoGrowHeightMultilineInput: { maxHeight: 115, }, @@ -3011,12 +2718,6 @@ const styles = { opacity: variables.overlayOpacity, }, - communicationsLinkIcon: { - right: -36, - top: 0, - bottom: 0, - }, - shortTermsBorder: { borderWidth: 1, borderColor: themeColors.border, @@ -3202,10 +2903,6 @@ const styles = { fontSize: 48, }, - closeAccountMessageInput: { - height: 153, - }, - imageCropContainer: { overflow: 'hidden', alignItems: 'center', @@ -3302,24 +2999,11 @@ const styles = { alignItems: 'center', }, - callRequestSection: { - backgroundColor: themeColors.appBG, - paddingHorizontal: 0, - paddingBottom: 0, - marginHorizontal: 0, - marginBottom: 0, - }, - archivedReportFooter: { borderRadius: variables.componentBorderRadius, ...wordBreak.breakWord, }, - saveButtonPadding: { - paddingLeft: 18, - paddingRight: 18, - }, - deeplinkWrapperContainer: { padding: 20, flex: 1, @@ -3365,11 +3049,7 @@ const styles = { alignSelf: 'flex-start', marginRight: 4, }, - reactionListItem: { - flexDirection: 'row', - paddingVertical: 12, - paddingHorizontal: 20, - }, + reactionListHeaderText: { color: themeColors.textSupporting, marginLeft: 8, @@ -3461,11 +3141,6 @@ const styles = { width: '100%', }, - listPickerSeparator: { - height: 1, - backgroundColor: themeColors.buttonDefaultBG, - }, - datePickerRoot: { position: 'relative', zIndex: 99, @@ -3518,15 +3193,6 @@ const styles = { width: 16, }, - validateCodeMessage: { - width: variables.modalContentMaxWidth, - textAlign: 'center', - }, - - whisper: { - backgroundColor: themeColors.cardBG, - }, - contextMenuItemPopoverMaxWidth: { maxWidth: 375, }, From 9f47e6a2fcf95df8a4cb0fef16819483090f3f02 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 13 Sep 2023 11:14:39 +0700 Subject: [PATCH 034/117] Update src/libs/actions/Report.js Co-authored-by: Carlos Martins --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2de649163079..cd3ca192d9a4 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -192,7 +192,7 @@ function subscribeToReportTypingEvents(reportID) { } /** - * Initialize our pusher subscriptions to listen for someone typing in a report. + * Initialize our pusher subscriptions to listen for someone leaving a room. * * @param {String} reportID */ From 6a1f0806474e48c60c4e05edc85ab3f8ef8a4823 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 13 Sep 2023 11:21:02 +0700 Subject: [PATCH 035/117] Update src/libs/actions/Report.js Co-authored-by: Carlos Martins --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index cd3ca192d9a4..65ec101aeb6d 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -937,7 +937,7 @@ function broadcastUserIsTyping(reportID) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } /** - * Broadcasts whether or not a user is leaving on a report over the report's private pusher channel. + * Broadcasts to the report's private pusher channel whether a user is leaving a report * * @param {String} reportID */ From 39c079b8d43ab38e0b3ae829f127957d15ebf7b0 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 13 Sep 2023 11:23:20 +0700 Subject: [PATCH 036/117] Update src/pages/home/ReportScreen.js Co-authored-by: Carlos Martins --- src/pages/home/ReportScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 74182d8db49a..0f824db5860c 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -88,7 +88,7 @@ const propTypes = { /** All of the personal details for everyone */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - /** Whether user leaving current report that listen to another device leaveRoom trigger */ + /** Whether user is leaving the current report */ userLeavingStatus: PropTypes.bool, ...windowDimensionsPropTypes, From 70c6b304f3dd0edef9c9f606b876467a962298f2 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 13 Sep 2023 11:24:09 +0700 Subject: [PATCH 037/117] Update src/pages/home/ReportScreen.js Co-authored-by: Carlos Martins --- src/pages/home/ReportScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 0f824db5860c..ba4322d08393 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -290,7 +290,7 @@ function ReportScreen({ const prevOnyxReportID = prevReport.reportID; const routeReportID = getReportID(route); - // navigate to concierge when the room removed from another device (e.g. user leaving a room) + // Navigate to the Concierge chat if the room was removed from another device (e.g. user leaving a room) if ( // non-optimistic case userLeavingStatus || From f91b792e26b924ea6f37ab2e39d2131d2743ed7a Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 13 Sep 2023 11:28:49 +0700 Subject: [PATCH 038/117] make getNormalizedTypingStatus become generic getNormalizedStatus --- src/libs/actions/Report.js | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index cd3ca192d9a4..d61670aacc13 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -113,35 +113,14 @@ function getReportChannelName(reportID) { * @param {Object} typingStatus * @returns {Object} */ -function getNormalizedTypingStatus(typingStatus) { - let normalizedTypingStatus = typingStatus; +function getNormalizedStatus(status) { + let normalizedStatus = status; - if (_.first(_.keys(typingStatus)) === 'userLogin') { - normalizedTypingStatus = {[typingStatus.userLogin]: true}; + if (_.first(_.keys(status)) === 'userLogin') { + normalizedStatus = {[status.userLogin]: true}; } - return normalizedTypingStatus; -} - -/** - * There are 2 possibilities that we can receive via pusher for a user's leaving status: - * 1. The "new" way from New Expensify is passed as {[login]: Boolean} (e.g. {yuwen@expensify.com: true}), where the value - * is whether the user with that login is leaving on the report or not. - * 2. The "old" way from e.com which is passed as {userLogin: login} (e.g. {userLogin: bstites@expensify.com}) - * - * This method makes sure that no matter which we get, we return the "new" format - * - * @param {Object} leavingStatus - * @returns {Object} - */ -function getNormalizedLeavingStatus(leavingStatus) { - let normalizedLeavingStatus = leavingStatus; - - if (_.first(_.keys(leavingStatus)) === 'userLogin') { - normalizedLeavingStatus = {[leavingStatus.userLogin]: true}; - } - - return normalizedLeavingStatus; + return normalizedStatus; } /** @@ -162,7 +141,7 @@ function subscribeToReportTypingEvents(reportID) { // If the pusher message comes from OldDot, we expect the typing status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. - const normalizedTypingStatus = getNormalizedTypingStatus(typingStatus); + const normalizedTypingStatus = getNormalizedStatus(typingStatus); const accountIDOrLogin = _.first(_.keys(normalizedTypingStatus)); if (!accountIDOrLogin) { @@ -209,7 +188,7 @@ function subscribeToReportLeavingEvents(reportID) { // If the pusher message comes from OldDot, we expect the leaving status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. - const normalizedLeavingStatus = getNormalizedLeavingStatus(leavingStatus); + const normalizedLeavingStatus = getNormalizedStatus(leavingStatus); const accountIDOrLogin = _.first(_.keys(normalizedLeavingStatus)); if (!accountIDOrLogin) { From a658b186749b240e04a340fb59949e5cabdcfaaa Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 13 Sep 2023 11:39:12 +0700 Subject: [PATCH 039/117] fix lint --- src/libs/actions/Report.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index b74e532b1c39..f0ac66923002 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -103,14 +103,14 @@ function getReportChannelName(reportID) { } /** - * There are 2 possibilities that we can receive via pusher for a user's typing status: + * There are 2 possibilities that we can receive via pusher for a user's typing/leaving status: * 1. The "new" way from New Expensify is passed as {[login]: Boolean} (e.g. {yuwen@expensify.com: true}), where the value - * is whether the user with that login is typing on the report or not. + * is whether the user with that login is typing/leaving on the report or not. * 2. The "old" way from e.com which is passed as {userLogin: login} (e.g. {userLogin: bstites@expensify.com}) * * This method makes sure that no matter which we get, we return the "new" format * - * @param {Object} typingStatus + * @param {Object} status * @returns {Object} */ function getNormalizedStatus(status) { From a842d93b5d3b1a00942146455202aa4e5662ecbc Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 13 Sep 2023 11:02:31 +0200 Subject: [PATCH 040/117] [TS migration] Migrate 'DeviceCapabilities' lib to TypeScript --- .../canUseTouchScreen/index.native.ts | 6 +++--- .../DeviceCapabilities/canUseTouchScreen/index.ts | 15 ++++++++++++--- .../hasHoverSupport/index.native.ts | 7 +++---- .../DeviceCapabilities/hasHoverSupport/index.ts | 8 +++----- .../DeviceCapabilities/hasHoverSupport/types.ts | 3 +++ 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.ts b/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.ts index 4306b0cff3f6..60980801e73c 100644 --- a/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.ts +++ b/src/libs/DeviceCapabilities/canUseTouchScreen/index.native.ts @@ -1,5 +1,5 @@ -function canUseTouchScreen() { - return true; -} +import CanUseTouchScreen from './types'; + +const canUseTouchScreen: CanUseTouchScreen = () => true; export default canUseTouchScreen; diff --git a/src/libs/DeviceCapabilities/canUseTouchScreen/index.ts b/src/libs/DeviceCapabilities/canUseTouchScreen/index.ts index e35dadf312be..72798159e36c 100644 --- a/src/libs/DeviceCapabilities/canUseTouchScreen/index.ts +++ b/src/libs/DeviceCapabilities/canUseTouchScreen/index.ts @@ -1,5 +1,8 @@ +import {Merge} from 'type-fest'; import CanUseTouchScreen from './types'; +type ExtendedNavigator = Merge; + /** * Allows us to identify whether the platform has a touchscreen. * @@ -7,19 +10,25 @@ import CanUseTouchScreen from './types'; */ const canUseTouchScreen: CanUseTouchScreen = () => { let hasTouchScreen = false; + + // TypeScript removed support for msMaxTouchPoints, this doesn't mean however that + // this property doesn't exist - hence the use of ExtendedNavigator to ensure + // that the functionality doesn't change + // https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1029 if ('maxTouchPoints' in navigator) { hasTouchScreen = navigator.maxTouchPoints > 0; } else if ('msMaxTouchPoints' in navigator) { - hasTouchScreen = (navigator as Navigator)?.msMaxTouchPoints > 0; + hasTouchScreen = (navigator as ExtendedNavigator).msMaxTouchPoints > 0; } else { - const mQ = window.matchMedia && matchMedia('(pointer:coarse)'); + // Same case as for Navigator - TypeScript thinks that matchMedia is obligatory property of window although it may not be + const mQ = (window as Partial).matchMedia && matchMedia('(pointer:coarse)'); if (mQ && mQ.media === '(pointer:coarse)') { hasTouchScreen = !!mQ.matches; } else if ('orientation' in window) { hasTouchScreen = true; // deprecated, but good fallback } else { // Only as a last resort, fall back to user agent sniffing - const UA = navigator.userAgent; + const UA = (navigator as ExtendedNavigator).userAgent; hasTouchScreen = /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA); } } diff --git a/src/libs/DeviceCapabilities/hasHoverSupport/index.native.ts b/src/libs/DeviceCapabilities/hasHoverSupport/index.native.ts index d77fcc17448a..097b3b0cbba1 100644 --- a/src/libs/DeviceCapabilities/hasHoverSupport/index.native.ts +++ b/src/libs/DeviceCapabilities/hasHoverSupport/index.native.ts @@ -1,9 +1,8 @@ +import HasHoverSupport from './types'; + /** * Allows us to identify whether the platform is hoverable. - * - * @returns {Boolean} */ - -const hasHoverSupport = () => false; +const hasHoverSupport: HasHoverSupport = () => false; export default hasHoverSupport; diff --git a/src/libs/DeviceCapabilities/hasHoverSupport/index.ts b/src/libs/DeviceCapabilities/hasHoverSupport/index.ts index 84a3fbbc5ed1..df62e6681548 100644 --- a/src/libs/DeviceCapabilities/hasHoverSupport/index.ts +++ b/src/libs/DeviceCapabilities/hasHoverSupport/index.ts @@ -1,10 +1,8 @@ +import HasHoverSupport from './types'; + /** * Allows us to identify whether the platform is hoverable. - * - * @returns {Boolean} */ -function hasHoverSupport() { - return window.matchMedia('(hover: hover) and (pointer: fine)').matches; -} +const hasHoverSupport: HasHoverSupport = () => window.matchMedia('(hover: hover) and (pointer: fine)').matches; export default hasHoverSupport; diff --git a/src/libs/DeviceCapabilities/hasHoverSupport/types.ts b/src/libs/DeviceCapabilities/hasHoverSupport/types.ts index e69de29bb2d1..b8fe944cf88e 100644 --- a/src/libs/DeviceCapabilities/hasHoverSupport/types.ts +++ b/src/libs/DeviceCapabilities/hasHoverSupport/types.ts @@ -0,0 +1,3 @@ +type HasHoverSupport = () => boolean; + +export default HasHoverSupport; From 30c7499a5004cb5247ff16566cf7abce32beb258 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Sep 2023 00:15:56 +0700 Subject: [PATCH 041/117] Ensure pusher's leavingStatus be sent earlier --- src/libs/actions/Report.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index f0ac66923002..92dbb0e5e0f6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1846,6 +1846,12 @@ function getCurrentUserAccountID() { function leaveRoom(reportID) { const report = lodashGet(allReports, [reportID], {}); const reportKeys = _.keys(report); + + // Pusher's leavingStatus should be sent earlier. + // Place the broadcast before calling the LeaveRoom API to prevent a race condition + // between Onyx report being null and Pusher's leavingStatus becoming true. + broadcastUserIsLeavingRoom(reportID); + API.write( 'LeaveRoom', { @@ -1887,7 +1893,6 @@ function leaveRoom(reportID) { if (Navigation.getTopmostReportId() === reportID) { Navigation.goBack(); } - broadcastUserIsLeavingRoom(reportID); navigateToConciergeChat(); } From b9a001eb84adf3a75be054f07d30889a4bc3910e Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Sep 2023 00:44:57 +0700 Subject: [PATCH 042/117] Add prevUserLeavingStatus to the check --- src/pages/home/ReportScreen.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ed29ccbb5954..b784c91b23db 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -146,6 +146,7 @@ function ReportScreen({ const flatListRef = useRef(); const reactionListRef = useRef(); const prevReport = usePrevious(report); + const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [skeletonViewContainerHeight, setSkeletonViewContainerHeight] = useState(0); const [isBannerVisible, setIsBannerVisible] = useState(true); @@ -293,7 +294,7 @@ function ReportScreen({ // Navigate to the Concierge chat if the room was removed from another device (e.g. user leaving a room) if ( // non-optimistic case - userLeavingStatus || + (!prevUserLeavingStatus && userLeavingStatus) || // optimistic case (prevOnyxReportID && prevOnyxReportID === routeReportID && !onyxReportID && prevReport.statusNum === CONST.REPORT.STATUS.OPEN && report.statusNum === CONST.REPORT.STATUS.CLOSED) ) { @@ -312,7 +313,7 @@ function ReportScreen({ fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); - }, [route, report, errors, fetchReportIfNeeded, prevReport.reportID, userLeavingStatus, prevReport.statusNum]); + }, [route, report, errors, fetchReportIfNeeded, prevReport.reportID, prevUserLeavingStatus, userLeavingStatus, prevReport.statusNum]); useEffect(() => { // Ensures subscription event succeeds when the report/workspace room is created optimistically. From d9595b03823b72ea492ac1d0c5ac09f9c2284b1d Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 18 Sep 2023 15:14:16 +0530 Subject: [PATCH 043/117] fix: Refocus issue on address field on delete and cancel Signed-off-by: Krishna Gupta --- src/components/HeaderWithBackButton/index.js | 2 ++ src/components/PopoverMenu/index.js | 5 +++++ src/components/ThreeDotsMenu/index.js | 12 ++++++++++-- src/pages/iou/WaypointEditor.js | 11 ++++++++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index bbf905cc1ac2..b99290918e13 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -47,6 +47,7 @@ function HeaderWithBackButton({ }, threeDotsMenuItems = [], children = null, + onPopoverHide = () => {}, }) { const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState(); const {translate} = useLocalize(); @@ -137,6 +138,7 @@ function HeaderWithBackButton({ menuItems={threeDotsMenuItems} onIconPress={onThreeDotsButtonPress} anchorPosition={threeDotsAnchorPosition} + onPopoverHide={onPopoverHide} /> )} {shouldShowCloseButton && ( diff --git a/src/components/PopoverMenu/index.js b/src/components/PopoverMenu/index.js index 5fabf73547ea..1533f329ad4a 100644 --- a/src/components/PopoverMenu/index.js +++ b/src/components/PopoverMenu/index.js @@ -34,6 +34,9 @@ const propTypes = { }), withoutOverlay: PropTypes.bool, + + /** Function to call on popover hide */ + onPopoverHide: PropTypes.func, }; const defaultProps = { @@ -44,6 +47,7 @@ const defaultProps = { }, anchorRef: () => {}, withoutOverlay: false, + onPopoverHide: () => {}, }; function PopoverMenu(props) { @@ -78,6 +82,7 @@ function PopoverMenu(props) { isVisible={props.isVisible} onModalHide={() => { setFocusedIndex(-1); + props.onPopoverHide(); if (selectedItemIndex.current !== null) { props.menuItems[selectedItemIndex.current].onSelected(); selectedItemIndex.current = null; diff --git a/src/components/ThreeDotsMenu/index.js b/src/components/ThreeDotsMenu/index.js index b5637a4f3879..5daeb9669933 100644 --- a/src/components/ThreeDotsMenu/index.js +++ b/src/components/ThreeDotsMenu/index.js @@ -1,5 +1,5 @@ import React, {useState, useRef} from 'react'; -import {View} from 'react-native'; +import {InteractionManager, View} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import Icon from '../Icon'; @@ -45,6 +45,9 @@ const propTypes = { horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), }), + + /** Function to call on popover hide */ + onPopoverClose: PropTypes.func, }; const defaultProps = { @@ -57,9 +60,10 @@ const defaultProps = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP }, + onPopoverHide: () => {}, }; -function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment}) { +function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, menuItems, anchorPosition, anchorAlignment, onPopoverHide}) { const [isPopupMenuVisible, setPopupMenuVisible] = useState(false); const buttonRef = useRef(null); const {translate} = useLocalize(); @@ -69,6 +73,9 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me }; const hidePopoverMenu = () => { + InteractionManager.runAfterInteractions(() => { + onPopoverHide(); + }); setPopupMenuVisible(false); }; @@ -101,6 +108,7 @@ function ThreeDotsMenu({iconTooltip, icon, iconFill, iconStyles, onIconPress, me { + InteractionManager.runAfterInteractions(() => { + if (!textInput.current) return; + textInput.current.focus(); + }); + }; + return ( setIsDeleteStopModalOpen(true), }, ]} + onPopoverHide={focus} /> setIsDeleteStopModalOpen(false)} + onModalHide={focus} prompt={translate('distance.deleteWaypointConfirmation')} confirmText={translate('common.delete')} cancelText={translate('common.cancel')} From 6756074fa9163d0367e8e60d4cc21c2dbc73eff2 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 18 Sep 2023 13:18:39 +0200 Subject: [PATCH 044/117] Updated script to handle styles with theme function, and obj spreading in style.js file --- .github/scripts/findUnusedKeys.sh | 154 ++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 31 deletions(-) diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index 85d8112e8f13..e34d8e0d78ea 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -110,6 +110,85 @@ lookfor_unused_keywords() { find_styles_object_and_store_keys() { local file="$1" local base_name="${2:-styles}" # Set styles as default + local line_number=0 + local inside_arrow_function=false + + while IFS= read -r line; do + ((line_number++)) + + # Check if we are inside an arrow function and we find a closing curly brace + if [[ "$inside_arrow_function" == true ]]; then + if [[ "$line" =~ ^[[:space:]]*\}\) ]]; then + inside_arrow_function=false + fi + continue + fi + + # Check if we are inside an arrow function + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ || "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + inside_arrow_function=true + continue + fi + + # Skip lines that are not key-related + if [[ ! "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} ]]; then + continue + fi + + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then + key="${BASH_REMATCH[2]%%:*{*)}" + echo "styles.${key}|...${key}|${base_name}.${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" + fi + done < "$file" +} + +find_styles_functions_and_store_keys() { + local file="$1" + local line_number=0 + local inside_object=false + local inside_arrow_function=false + local key="" + + while IFS= read -r line; do + ((line_number++)) + + # Skip lines that are not key-related + if [[ "${line}" == *styles* ]]; then + continue + fi + + # Check if we are inside an arrow function and we find a closing curly brace + if [[ "$inside_arrow_function" == true ]]; then + if [[ "$line" =~ ^[[:space:]]*\}\) ]]; then + inside_arrow_function=false + fi + continue + fi + + # Check if we are inside an arrow function + if [[ "${line}" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\( ]]; then + inside_arrow_function=true + key="${line%%:*}" + key="${key// /}" # Trim spaces + echo "styles.${key}|...${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" + continue + fi + + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + inside_arrow_function=true + key="${BASH_REMATCH[2]}" + key="${key// /}" # Trim spaces + echo "styles.${key}|...${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" + continue + fi + + done < "$file" +} + +find_theme_style_and_store_keys() { + local file="$1" + local start_line_number="$2" + local base_name="${3:-styles}" # Set styles as default local parent_keys=() local root_key="" local line_number=0 @@ -118,6 +197,10 @@ find_styles_object_and_store_keys() { while IFS= read -r line; do ((line_number++)) + if [ ! "$line_number" -ge "$start_line_number" ]; then + continue + fi + # Check if we are inside an arrow function and we find a closing curly brace if [[ "$inside_arrow_function" == true ]]; then if [[ "$line" =~ ^[[:space:]]*\}\) ]]; then @@ -127,22 +210,22 @@ find_styles_object_and_store_keys() { fi # Check if we are inside an arrow function - if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ || "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then inside_arrow_function=true continue fi # Skip lines that are not key-related if [[ ! "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*\} ]]; then + continue fi - if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\{ ]]; then - root_key="${BASH_REMATCH[2]%%:*{*)}" - elif [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{|^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then # Removing all the extra lines after the ":" local key="${line%%:*}" key="${key// /}" # Trim spaces + if [[ ${#parent_keys[@]} -gt 0 ]]; then local parent_key_trimmed="${parent_keys[${#parent_keys[@]}-1]// /}" # Trim spaces key="$parent_key_trimmed.$key" @@ -150,12 +233,8 @@ find_styles_object_and_store_keys() { local parent_key_trimmed="${root_key// /}" # Trim spaces key="$parent_key_trimmed.$key" fi - - if [[ "$key" == "styles."* ]]; then - echo "${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" - else - echo "styles.${key}|${base_name}.${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" - fi + + echo "styles.${key}|...${key}|${base_name}.${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" parent_keys+=("$key") elif [[ "$line" =~ ^[[:space:]]*\} ]]; then parent_keys=("${parent_keys[@]:0:${#parent_keys[@]}-1}") @@ -163,40 +242,51 @@ find_styles_object_and_store_keys() { done < "$file" } -find_styles_functions_and_store_keys() { +# Given that all the styles are inside of a function, we need to find the function and then look for the styles +collect_theme_keys_from_styles() { local file="$1" local line_number=0 - local inside_object=false - local inside_arrow_function=false - local key="" + local inside_styles=false while IFS= read -r line; do ((line_number++)) - # Check if we are inside an arrow function - if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-])+:[[:space:]]*\(.*\)[[:space:]]*'=>'[[:space:]]*\(\{ ]]; then - inside_arrow_function=true - key="${line%%:*}" - key="${key// /}" # Trim spaces - echo "styles.${key}:${file}:${line_number}" >> "$STYLES_KEYS_FILE" + if [[ "$inside_styles" == false ]]; then + if [[ "$line" =~ ^[[:space:]]*(const|let|var)[[:space:]]+([a-zA-Z0-9_-]+)[[:space:]]*=[[:space:]]*\(.*\)[[:space:]]*'=>' ]]; then + key="${BASH_REMATCH[2]}" + key="${key// /}" # Trim spaces + if [[ "$key" == "styles"* ]]; then + inside_styles=true + # Need to start within the style function + ((line_number++)) + find_theme_style_and_store_keys "$file" "$line_number" + fi + continue + fi fi + done < "$file" +} - # If we are inside an arrow function and we find an opening curly brace, - # then set inside_object to true, indicating we are inside an object. - if [[ "$inside_arrow_function" == true && "$line" =~ ^[[:space:]]*\{ ]]; then - inside_object=true - fi +lookfor_unused_spread_keywords() { + local inside_object=false - # If we are inside an object, continue to the next line. - if [[ "$inside_object" == true ]]; then - continue + while IFS= read -r line; do + # Detect the start of an object + if [[ "$line" =~ ^[[:space:]]*([a-zA-Z0-9_-]+\.)?[a-zA-Z0-9_-]+:[[:space:]]*\{ ]]; then + inside_object=true fi - # If we find a closing curly brace, reset the inside_object flag. + # Detect the end of an object if [[ "$line" =~ ^[[:space:]]*\},?$ ]]; then inside_object=false fi - done < "$file" + + # If we're inside an object and the line starts with '...', capture the key + if [[ "$inside_object" == true && "$line" =~ ^[[:space:]]*\.\.\.([a-zA-Z0-9_]+)(\(.+\))?,?$ ]]; then + local key=("${BASH_REMATCH[1]}") + remove_keyword "...${key}" + fi + done < "$STYLES_FILE" } find_utility_styles_store_prefix() { @@ -228,7 +318,7 @@ find_utility_usage_as_styles() { continue fi - find_styles_object_and_store_keys "${file}" "${root_key}" + find_theme_style_and_store_keys "${file}" 0 "${root_key}" done < <(find "${UTILITIES_STYLES_FILE}" -type f \( "${FILE_EXTENSIONS[@]}" \)) } @@ -260,11 +350,13 @@ find_utility_usage_as_styles # Find and store keys from styles.js find_styles_object_and_store_keys "$STYLES_FILE" find_styles_functions_and_store_keys "$STYLES_FILE" +collect_theme_keys_from_styles "$STYLES_FILE" echo "🗄️ Now going through the codebase and looking for unused keys." # Look for usages of utilities into src/styles lookfor_unused_utilities +lookfor_unused_spread_keywords lookfor_unused_keywords final_styles_line_count=$(count_lines "$STYLES_KEYS_FILE") From 54e786c1593e5a53ba9c58827c345908be6420af Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 18 Sep 2023 13:36:08 +0200 Subject: [PATCH 045/117] fixed some lint issues --- .github/scripts/findUnusedKeys.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/scripts/findUnusedKeys.sh b/.github/scripts/findUnusedKeys.sh index e34d8e0d78ea..6668d3257546 100755 --- a/.github/scripts/findUnusedKeys.sh +++ b/.github/scripts/findUnusedKeys.sh @@ -259,7 +259,7 @@ collect_theme_keys_from_styles() { inside_styles=true # Need to start within the style function ((line_number++)) - find_theme_style_and_store_keys "$file" "$line_number" + find_theme_style_and_store_keys "$STYLES_FILE" "$line_number" fi continue fi @@ -269,6 +269,7 @@ collect_theme_keys_from_styles() { lookfor_unused_spread_keywords() { local inside_object=false + local key="" while IFS= read -r line; do # Detect the start of an object @@ -283,7 +284,7 @@ lookfor_unused_spread_keywords() { # If we're inside an object and the line starts with '...', capture the key if [[ "$inside_object" == true && "$line" =~ ^[[:space:]]*\.\.\.([a-zA-Z0-9_]+)(\(.+\))?,?$ ]]; then - local key=("${BASH_REMATCH[1]}") + key="${BASH_REMATCH[1]}" remove_keyword "...${key}" fi done < "$STYLES_FILE" From 24e4db5626db5a0e29238565240fa7f5de913e1d Mon Sep 17 00:00:00 2001 From: Eduardo Date: Mon, 18 Sep 2023 16:03:35 +0200 Subject: [PATCH 046/117] removed unused style --- src/styles/styles.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index a8b764dc0e39..81f3c22c0dae 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -225,11 +225,6 @@ const styles = (theme) => ({ color: theme.textSupporting, }, - appIconBorderRadius: { - overflow: 'hidden', - borderRadius: 12, - }, - webViewStyles: webViewStyles(theme), link: link(theme), From 5ba5ee73d2d13401cec7d1424050a9dec37ea3a8 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Mon, 18 Sep 2023 22:34:55 +0530 Subject: [PATCH 047/117] multiline focus logic updated --- ...tRange.js => UpdateMultilineInputRange.js} | 3 +- src/pages/EditRequestDescriptionPage.js | 81 +++++++---- .../PrivateNotes/PrivateNotesEditPage.js | 130 ++++++++++-------- src/pages/ReportWelcomeMessagePage.js | 13 +- src/pages/iou/MoneyRequestDescriptionPage.js | 80 +++++++---- src/pages/tasks/NewTaskDescriptionPage.js | 80 +++++++---- src/pages/tasks/TaskDescriptionPage.js | 18 ++- 7 files changed, 259 insertions(+), 146 deletions(-) rename src/libs/{focusAndUpdateMultilineInputRange.js => UpdateMultilineInputRange.js} (91%) diff --git a/src/libs/focusAndUpdateMultilineInputRange.js b/src/libs/UpdateMultilineInputRange.js similarity index 91% rename from src/libs/focusAndUpdateMultilineInputRange.js rename to src/libs/UpdateMultilineInputRange.js index b5e438899d3d..018ba8d98bf1 100644 --- a/src/libs/focusAndUpdateMultilineInputRange.js +++ b/src/libs/UpdateMultilineInputRange.js @@ -9,12 +9,11 @@ * * @param {Object} input the input element */ -export default function focusAndUpdateMultilineInputRange(input) { +export default function UpdateMultilineInputRange(input) { if (!input) { return; } - input.focus(); if (input.value && input.setSelectionRange) { const length = input.value.length; input.setSelectionRange(length, length); diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index 3916daecd33c..a726409364b9 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -11,6 +11,8 @@ import Navigation from '../libs/Navigation/Navigation'; import CONST from '../CONST'; import useLocalize from '../hooks/useLocalize'; import * as Browser from '../libs/Browser'; +import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { /** Transaction default description value */ @@ -27,36 +29,59 @@ function EditRequestDescriptionPage({defaultDescription, onSubmit}) { descriptionInputRef.current && descriptionInputRef.current.focus()} + onEntryTransitionEnd={() => { + if (!descriptionInputRef.current) { + return; + } + UpdateMultilineInputRange(descriptionInputRef.current); + descriptionInputRef.current.focus(); + }} > - Navigation.goBack()} - /> -
- - (descriptionInputRef.current = e)} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} + {(didScreenTransitionEnd) => ( + <> + Navigation.goBack()} /> - -
+
+ + { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!descriptionInputRef.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + descriptionInputRef.current = el; + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+ + )}
); } diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 4cada83941ac..96d9cd370dbe 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -23,7 +23,8 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** All of the personal details for everyone */ @@ -78,62 +79,83 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { return ( focusAndUpdateMultilineInputRange(privateNotesInput.current)} + onEntryTransitionEnd={() => { + if (!privateNotesInput.current) { + return; + } + UpdateMultilineInputRange(privateNotesInput.current); + privateNotesInput.current.focus(); + }} > - Navigation.goBack()} - > - Navigation.dismissModal()} + {(didScreenTransitionEnd) => ( + Navigation.goBack()} - /> - - - - {translate( - Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN - ? 'privateNotes.sharedNoteMessage' - : 'privateNotes.personalNoteMessage', - )} - - -
- Report.clearPrivateNotesError(report.reportID, route.params.accountID)} - style={[styles.mb3]} + > + Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack()} + /> + + + + {translate( + Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN + ? 'privateNotes.sharedNoteMessage' + : 'privateNotes.personalNoteMessage', + )} + + + - setPrivateNote(text)} - ref={(el) => (privateNotesInput.current = el)} - /> - -
-
-
+ Report.clearPrivateNotesError(report.reportID, route.params.accountID)} + style={[styles.mb3]} + > + setPrivateNote(text)} + ref={(el) => { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!privateNotesInput.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + privateNotesInput.current = el; + }} + /> + + + +
+ )}
); } diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index f14b6c48b9dd..2f2db8819019 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -19,7 +19,8 @@ import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundVi import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; -import focusAndUpdateMultilineInputRange from '../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { ...withLocalizePropTypes, @@ -60,7 +61,8 @@ function ReportWelcomeMessagePage(props) { if (!welcomeMessageInputRef.current) { return; } - focusAndUpdateMultilineInputRange(welcomeMessageInputRef.current); + UpdateMultilineInputRange(welcomeMessageInputRef.current); + welcomeMessageInputRef.current.focus(); }} > {({didScreenTransitionEnd}) => ( @@ -88,8 +90,13 @@ function ReportWelcomeMessagePage(props) { if (!el) { return; } + UpdateMultilineInputRange(el); if (!welcomeMessageInputRef.current && didScreenTransitionEnd) { - focusAndUpdateMultilineInputRange(el); + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); } welcomeMessageInputRef.current = el; }} diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index 63aea67ce598..c7f4113b7be7 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -17,7 +17,8 @@ import * as IOU from '../../libs/actions/IOU'; import * as MoneyRequestUtils from '../../libs/MoneyRequestUtils'; import CONST from '../../CONST'; import useLocalize from '../../hooks/useLocalize'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -89,35 +90,58 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { focusAndUpdateMultilineInputRange(inputRef.current)} + onEntryTransitionEnd={() => { + if (!inputRef.current) { + return; + } + UpdateMultilineInputRange(inputRef.current); + inputRef.current.focus(); + }} > - navigateBack()} - /> -
updateComment(value)} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - (inputRef.current = el)} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} + {({didScreenTransitionEnd}) => ( + <> + navigateBack()} /> - -
+
updateComment(value)} + submitButtonText={translate('common.save')} + enabledWhenOffline + > + + { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!inputRef.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + inputRef.current = el; + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+ + )}
); } diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index dacf368a574e..3f3c764f6417 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -14,8 +14,9 @@ import TextInput from '../../components/TextInput'; import Permissions from '../../libs/Permissions'; import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import CONST from '../../CONST'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -55,36 +56,59 @@ function NewTaskDescriptionPage(props) { return ( focusAndUpdateMultilineInputRange(inputRef.current)} + onEntryTransitionEnd={() => { + if (!inputRef.current) { + return; + } + UpdateMultilineInputRange(inputRef.current); + inputRef.current.focus(); + }} shouldEnableMaxHeight > - Task.dismissModalAndClearOutTaskInfo()} - onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} - /> -
onSubmit(values)} - enabledWhenOffline - > - - (inputRef.current = el)} - autoGrowHeight - submitOnEnter={!Browser.isMobile()} - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" + {({didScreenTransitionEnd}) => ( + <> + Task.dismissModalAndClearOutTaskInfo()} + onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} /> - -
+
onSubmit(values)} + enabledWhenOffline + > + + { + if (!el) { + return; + } + UpdateMultilineInputRange(el); + if (!inputRef.current && didScreenTransitionEnd) { + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); + } + inputRef.current = el; + }} + autoGrowHeight + submitOnEnter={!Browser.isMobile()} + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + /> + +
+ + )}
); } diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 382e8d80d4ad..14641cf4397d 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -14,12 +14,13 @@ import compose from '../../libs/compose'; import * as Task from '../../libs/actions/Task'; import * as ReportUtils from '../../libs/ReportUtils'; import CONST from '../../CONST'; -import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; +import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import * as Browser from '../../libs/Browser'; import Navigation from '../../libs/Navigation/Navigation'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; import withReportOrNotFound from '../home/report/withReportOrNotFound'; +import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** Current user session */ @@ -65,7 +66,13 @@ function TaskDescriptionPage(props) { return ( focusAndUpdateMultilineInputRange(inputRef.current)} + onEntryTransitionEnd={() => { + if (!inputRef.current) { + return; + } + UpdateMultilineInputRange(inputRef.current); + inputRef.current.focus(); + }} shouldEnableMaxHeight > {({didScreenTransitionEnd}) => ( @@ -92,8 +99,13 @@ function TaskDescriptionPage(props) { if (!el) { return; } + UpdateMultilineInputRange(el); if (!inputRef.current && didScreenTransitionEnd) { - focusAndUpdateMultilineInputRange(el); + if (shouldDelayFocus) { + setTimeout(() => { + el.focus(); + }, CONST.ANIMATED_TRANSITION); + } else el.focus(); } inputRef.current = el; }} From 32168a280c3357cc8c0bf97d4a466dd0d7cbba16 Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Tue, 19 Sep 2023 01:02:04 +0530 Subject: [PATCH 048/117] using focus hook --- src/pages/ReportWelcomeMessagePage.js | 44 ++++++++++++--------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 2f2db8819019..7f29d012326c 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import {useFocusEffect} from '@react-navigation/native'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; import ScreenWrapper from '../components/ScreenWrapper'; @@ -20,7 +21,6 @@ import Form from '../components/Form'; import * as PolicyUtils from '../libs/PolicyUtils'; import {policyPropTypes, policyDefaultProps} from './workspace/withPolicy'; import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { ...withLocalizePropTypes, @@ -46,6 +46,7 @@ function ReportWelcomeMessagePage(props) { const parser = new ExpensiMark(); const [welcomeMessage, setWelcomeMessage] = useState(parser.htmlToMarkdown(props.report.welcomeMessage)); const welcomeMessageInputRef = useRef(null); + const focusTimeoutRef = useRef(null); const handleWelcomeMessageChange = useCallback((value) => { setWelcomeMessage(value); @@ -55,18 +56,25 @@ function ReportWelcomeMessagePage(props) { Report.updateWelcomeMessage(props.report.reportID, props.report.welcomeMessage, welcomeMessage.trim()); }, [props.report.reportID, props.report.welcomeMessage, welcomeMessage]); + useFocusEffect( + useCallback(() => { + + focusTimeoutRef.current = setTimeout(() => { + welcomeMessageInputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, [welcomeMessageInputRef]) + ) + return ( { - if (!welcomeMessageInputRef.current) { - return; - } - UpdateMultilineInputRange(welcomeMessageInputRef.current); - welcomeMessageInputRef.current.focus(); - }} > - {({didScreenTransitionEnd}) => ( - +
{ - // Before updating the DOM, React sets the affected ref.current values to null. After updating the DOM, React immediately sets them to the corresponding DOM nodes - // to avoid focus multiple time, we should early return if el is null. - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!welcomeMessageInputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } + if(!el) return; welcomeMessageInputRef.current = el; + UpdateMultilineInputRange(welcomeMessageInputRef.current); }} value={welcomeMessage} onChangeText={handleWelcomeMessageChange} @@ -109,7 +106,6 @@ function ReportWelcomeMessagePage(props) {
- )}
); } From cadf08232889f77fd5945ef8519dd5e3e390159b Mon Sep 17 00:00:00 2001 From: Chirag Arora Date: Tue, 19 Sep 2023 01:24:35 +0530 Subject: [PATCH 049/117] using hook in other multiline inputs --- src/pages/EditRequestDescriptionPage.js | 99 +++++++------ .../PrivateNotes/PrivateNotesEditPage.js | 140 +++++++++--------- src/pages/ReportWelcomeMessagePage.js | 72 +++++---- src/pages/iou/MoneyRequestDescriptionPage.js | 96 ++++++------ src/pages/tasks/NewTaskDescriptionPage.js | 105 +++++++------ src/pages/tasks/TaskDescriptionPage.js | 98 ++++++------ 6 files changed, 307 insertions(+), 303 deletions(-) diff --git a/src/pages/EditRequestDescriptionPage.js b/src/pages/EditRequestDescriptionPage.js index a726409364b9..850272c089cf 100644 --- a/src/pages/EditRequestDescriptionPage.js +++ b/src/pages/EditRequestDescriptionPage.js @@ -1,6 +1,7 @@ -import React, {useRef} from 'react'; +import React, {useRef, useCallback} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; +import {useFocusEffect} from '@react-navigation/native'; import TextInput from '../components/TextInput'; import ScreenWrapper from '../components/ScreenWrapper'; import HeaderWithBackButton from '../components/HeaderWithBackButton'; @@ -12,7 +13,6 @@ import CONST from '../CONST'; import useLocalize from '../hooks/useLocalize'; import * as Browser from '../libs/Browser'; import UpdateMultilineInputRange from '../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../libs/shouldDelayFocus'; const propTypes = { /** Transaction default description value */ @@ -25,6 +25,22 @@ const propTypes = { function EditRequestDescriptionPage({defaultDescription, onSubmit}) { const {translate} = useLocalize(); const descriptionInputRef = useRef(null); + const focusTimeoutRef = useRef(null); + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (descriptionInputRef.current) descriptionInputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + return ( - {(didScreenTransitionEnd) => ( - <> - Navigation.goBack()} - /> -
- - { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!descriptionInputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - descriptionInputRef.current = el; - }} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} - /> - -
- - )} + <> + Navigation.goBack()} + /> +
+ + { + if (!el) return; + descriptionInputRef.current = el; + UpdateMultilineInputRange(descriptionInputRef.current); + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+
); } diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 96d9cd370dbe..f84dc41ea9a5 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -1,7 +1,8 @@ -import React, {useState, useRef} from 'react'; +import React, {useState, useRef, useCallback} from 'react'; import PropTypes from 'prop-types'; import {View, Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -24,7 +25,6 @@ import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** All of the personal details for everyone */ @@ -66,6 +66,21 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { // To focus on the input field when the page loads const privateNotesInput = useRef(null); + const focusTimeoutRef = useRef(null); + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (privateNotesInput.current) privateNotesInput.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); const savePrivateNote = () => { const editedNote = parser.replace(privateNote); @@ -87,75 +102,64 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { privateNotesInput.current.focus(); }} > - {(didScreenTransitionEnd) => ( - Navigation.goBack()} + > + Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} - > - Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack()} - /> - - - - {translate( - Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN - ? 'privateNotes.sharedNoteMessage' - : 'privateNotes.personalNoteMessage', - )} - - -
+ + + + {translate( + Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN + ? 'privateNotes.sharedNoteMessage' + : 'privateNotes.personalNoteMessage', + )} + + + + Report.clearPrivateNotesError(report.reportID, route.params.accountID)} + style={[styles.mb3]} > - setPrivateNote(text)} + ref={(el) => { + if (!el) return; + privateNotesInput.current = el; + UpdateMultilineInputRange(privateNotesInput.current); }} - onClose={() => Report.clearPrivateNotesError(report.reportID, route.params.accountID)} - style={[styles.mb3]} - > - setPrivateNote(text)} - ref={(el) => { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!privateNotesInput.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - privateNotesInput.current = el; - }} - /> - - -
-
- )} + /> + + + +
); } diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index 7f29d012326c..fc05b4ae5d56 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -58,9 +58,8 @@ function ReportWelcomeMessagePage(props) { useFocusEffect( useCallback(() => { - focusTimeoutRef.current = setTimeout(() => { - welcomeMessageInputRef.current.focus(); + if (welcomeMessageInputRef.current) welcomeMessageInputRef.current.focus(); return () => { if (!focusTimeoutRef.current) { return; @@ -68,44 +67,43 @@ function ReportWelcomeMessagePage(props) { clearTimeout(focusTimeoutRef.current); }; }, CONST.ANIMATED_TRANSITION); - }, [welcomeMessageInputRef]) - ) + }, []), + ); return ( - + - -
- {props.translate('welcomeMessagePage.explainerText')} - - { - if(!el) return; - welcomeMessageInputRef.current = el; - UpdateMultilineInputRange(welcomeMessageInputRef.current); - }} - value={welcomeMessage} - onChangeText={handleWelcomeMessageChange} - autoCapitalize="none" - textAlignVertical="top" - containerStyles={[styles.autoGrowHeightMultilineInput]} - /> - -
-
+ +
+ {props.translate('welcomeMessagePage.explainerText')} + + { + if (!el) return; + welcomeMessageInputRef.current = el; + UpdateMultilineInputRange(welcomeMessageInputRef.current); + }} + value={welcomeMessage} + onChangeText={handleWelcomeMessageChange} + autoCapitalize="none" + textAlignVertical="top" + containerStyles={[styles.autoGrowHeightMultilineInput]} + /> + +
+
); } diff --git a/src/pages/iou/MoneyRequestDescriptionPage.js b/src/pages/iou/MoneyRequestDescriptionPage.js index c7f4113b7be7..692e5f0ccac3 100644 --- a/src/pages/iou/MoneyRequestDescriptionPage.js +++ b/src/pages/iou/MoneyRequestDescriptionPage.js @@ -1,6 +1,7 @@ -import React, {useEffect, useRef} from 'react'; +import React, {useEffect, useRef, useCallback} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -18,7 +19,6 @@ import * as MoneyRequestUtils from '../../libs/MoneyRequestUtils'; import CONST from '../../CONST'; import useLocalize from '../../hooks/useLocalize'; import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -55,10 +55,25 @@ const defaultProps = { function MoneyRequestDescriptionPage({iou, route, selectedTab}) { const {translate} = useLocalize(); const inputRef = useRef(null); + const focusTimeoutRef = useRef(null); const iouType = lodashGet(route, 'params.iouType', ''); const reportID = lodashGet(route, 'params.reportID', ''); const isDistanceRequest = MoneyRequestUtils.isDistanceRequest(iouType, selectedTab); + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (inputRef.current) inputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + useEffect(() => { const moneyRequestId = `${iouType}${reportID}`; const shouldReset = iou.id !== moneyRequestId; @@ -98,50 +113,39 @@ function MoneyRequestDescriptionPage({iou, route, selectedTab}) { inputRef.current.focus(); }} > - {({didScreenTransitionEnd}) => ( - <> - navigateBack()} - /> -
updateComment(value)} - submitButtonText={translate('common.save')} - enabledWhenOffline - > - - { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!inputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - inputRef.current = el; - }} - autoGrowHeight - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - submitOnEnter={!Browser.isMobile()} - /> - -
- - )} + <> + navigateBack()} + /> +
updateComment(value)} + submitButtonText={translate('common.save')} + enabledWhenOffline + > + + { + if (!el) return; + inputRef.current = el; + UpdateMultilineInputRange(inputRef.current); + }} + autoGrowHeight + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + submitOnEnter={!Browser.isMobile()} + /> + +
+ ); } diff --git a/src/pages/tasks/NewTaskDescriptionPage.js b/src/pages/tasks/NewTaskDescriptionPage.js index 3f3c764f6417..d3cdc52553d1 100644 --- a/src/pages/tasks/NewTaskDescriptionPage.js +++ b/src/pages/tasks/NewTaskDescriptionPage.js @@ -1,6 +1,7 @@ -import React, {useRef} from 'react'; +import React, {useRef, useCallback} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; @@ -16,7 +17,6 @@ import ROUTES from '../../ROUTES'; import * as Task from '../../libs/actions/Task'; import UpdateMultilineInputRange from '../../libs/UpdateMultilineInputRange'; import CONST from '../../CONST'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; import * as Browser from '../../libs/Browser'; const propTypes = { @@ -41,9 +41,24 @@ const defaultProps = { function NewTaskDescriptionPage(props) { const inputRef = useRef(null); - + const focusTimeoutRef = useRef(null); // On submit, we want to call the assignTask function and wait to validate // the response + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (inputRef.current) inputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + const onSubmit = (values) => { Task.setDescriptionValue(values.taskDescription); Navigation.goBack(ROUTES.NEW_TASK); @@ -56,59 +71,41 @@ function NewTaskDescriptionPage(props) { return ( { - if (!inputRef.current) { - return; - } - UpdateMultilineInputRange(inputRef.current); - inputRef.current.focus(); - }} shouldEnableMaxHeight > - {({didScreenTransitionEnd}) => ( - <> - Task.dismissModalAndClearOutTaskInfo()} - onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} - /> -
onSubmit(values)} - enabledWhenOffline - > - - { - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!inputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - inputRef.current = el; - }} - autoGrowHeight - submitOnEnter={!Browser.isMobile()} - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - /> - -
- - )} + <> + Task.dismissModalAndClearOutTaskInfo()} + onBackButtonPress={() => Navigation.goBack(ROUTES.NEW_TASK)} + /> +
onSubmit(values)} + enabledWhenOffline + > + + { + if (!el) return; + inputRef.current = el; + UpdateMultilineInputRange(inputRef.current); + }} + autoGrowHeight + submitOnEnter={!Browser.isMobile()} + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + /> + +
+
); } diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 14641cf4397d..0d16f8b1e5bc 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -1,6 +1,7 @@ import React, {useCallback, useRef} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; +import {useFocusEffect} from '@react-navigation/native'; import {withOnyx} from 'react-native-onyx'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -20,7 +21,6 @@ import Navigation from '../../libs/Navigation/Navigation'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; import withReportOrNotFound from '../home/report/withReportOrNotFound'; -import shouldDelayFocus from '../../libs/shouldDelayFocus'; const propTypes = { /** Current user session */ @@ -58,66 +58,62 @@ function TaskDescriptionPage(props) { }); } const inputRef = useRef(null); + const focusTimeoutRef = useRef(null); const isOpen = ReportUtils.isOpenTaskReport(props.report); const canModifyTask = Task.canModifyTask(props.report, props.currentUserPersonalDetails.accountID); const isTaskNonEditable = ReportUtils.isTaskReport(props.report) && (!canModifyTask || !isOpen); + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (inputRef.current) inputRef.current.focus(); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + return ( { - if (!inputRef.current) { - return; - } - UpdateMultilineInputRange(inputRef.current); - inputRef.current.focus(); - }} shouldEnableMaxHeight > - {({didScreenTransitionEnd}) => ( - - -
- - { - // if we wrap the page with FullPageNotFoundView we need to explicitly handle focusing on text input - if (!el) { - return; - } - UpdateMultilineInputRange(el); - if (!inputRef.current && didScreenTransitionEnd) { - if (shouldDelayFocus) { - setTimeout(() => { - el.focus(); - }, CONST.ANIMATED_TRANSITION); - } else el.focus(); - } - inputRef.current = el; - }} - autoGrowHeight - submitOnEnter={!Browser.isMobile()} - containerStyles={[styles.autoGrowHeightMultilineInput]} - textAlignVertical="top" - /> - -
-
- )} + + +
+ + { + if (!el) return; + inputRef.current = el; + UpdateMultilineInputRange(inputRef.current); + }} + autoGrowHeight + submitOnEnter={!Browser.isMobile()} + containerStyles={[styles.autoGrowHeightMultilineInput]} + textAlignVertical="top" + /> + +
+
); } From 6a8b7d86779d8aa98442474944e78f9c29fb4a2c Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 19 Sep 2023 11:43:57 +0700 Subject: [PATCH 050/117] fix unread marker of modified messsage --- src/libs/actions/IOU.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 587b2392deba..79b1ecdad855 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1087,6 +1087,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC } // STEP 4: Compose the optimistic data + const currentTime = DateUtils.getDBTime(); const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1110,6 +1111,14 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`, value: updatedChatReport, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, + value: { + lastReadTime: currentTime, + lastVisibleActionCreated: currentTime, + }, + }, ]; const successData = [ @@ -1163,6 +1172,14 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`, value: chatReport, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, + value: { + lastReadTime: transactionThread.lastReadTime, + lastVisibleActionCreated: transactionThread.lastVisibleActionCreated, + }, + }, ]; // STEP 6: Call the API endpoint From feeac721700ba5a95d8ea05b64d03ad8e1226da2 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Tue, 19 Sep 2023 12:16:30 +0700 Subject: [PATCH 051/117] fix offline deleted workspace allow to open reimburse page --- src/libs/PolicyUtils.js | 9 +++++++++ src/pages/workspace/WorkspaceInitialPage.js | 2 +- src/pages/workspace/WorkspaceInviteMessagePage.js | 2 +- src/pages/workspace/WorkspaceInvitePage.js | 2 +- src/pages/workspace/WorkspaceMembersPage.js | 2 +- src/pages/workspace/WorkspacePageWithSections.js | 2 +- 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 164f284a4ef5..6ec0c11a31d6 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -166,6 +166,14 @@ function getIneligibleInvitees(policyMembers, personalDetails) { return memberEmailsToExclude; } +/** + * @param {Object} policy + * @returns {Boolean} + */ +function isPendingDeletePolicy(policy) { + return policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; +} + export { getActivePolicies, hasPolicyMemberError, @@ -179,4 +187,5 @@ export { isPolicyAdmin, getMemberAccountIDsForWorkspace, getIneligibleInvitees, + isPendingDeletePolicy, }; diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 8f93434e6845..2b7fc924d85c 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -186,7 +186,7 @@ function WorkspaceInitialPage(props) { {({safeAreaPaddingBottomStyle}) => ( Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} - shouldShow={_.isEmpty(props.policy) || !PolicyUtils.isPolicyAdmin(props.policy)} + shouldShow={_.isEmpty(props.policy) || !PolicyUtils.isPolicyAdmin(props.policy) || PolicyUtils.isPendingDeletePolicy(props.policy)} subtitleKey={_.isEmpty(props.policy) ? undefined : 'workspace.common.notAuthorized'} > Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} > diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index e37588d2d401..fdb2f817fd1b 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -203,7 +203,7 @@ function WorkspaceInvitePage(props) { return ( Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} > diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 7bb9a91c130e..6051bf73de10 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -346,7 +346,7 @@ function WorkspaceMembersPage(props) { style={[styles.defaultModalContainer]} > Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} > diff --git a/src/pages/workspace/WorkspacePageWithSections.js b/src/pages/workspace/WorkspacePageWithSections.js index fa04f5cfc49e..f7907e56f1e5 100644 --- a/src/pages/workspace/WorkspacePageWithSections.js +++ b/src/pages/workspace/WorkspacePageWithSections.js @@ -105,7 +105,7 @@ function WorkspacePageWithSections({backButtonRoute, children, footer, guidesCal > Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} - shouldShow={_.isEmpty(policy) || !PolicyUtils.isPolicyAdmin(policy)} + shouldShow={_.isEmpty(policy) || !PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPendingDeletePolicy(policy)} subtitleKey={_.isEmpty(policy) ? undefined : 'workspace.common.notAuthorized'} > Date: Tue, 19 Sep 2023 13:16:53 +0700 Subject: [PATCH 052/117] fix: 26505 Losing focus on the amount input field when switching between apps --- src/components/TextInput/BaseTextInput.js | 20 +--------- src/components/TextInput/index.native.js | 45 +++++++++++++++++------ 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index ec22b09dde04..244d6a8d8490 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import React, {useState, useRef, useEffect, useCallback} from 'react'; -import {Animated, View, AppState, Keyboard, StyleSheet} from 'react-native'; +import {Animated, View, StyleSheet} from 'react-native'; import Str from 'expensify-common/lib/str'; import RNTextInput from '../RNTextInput'; import TextInputLabel from './TextInputLabel'; @@ -38,24 +38,6 @@ function BaseTextInput(props) { const input = useRef(null); const isLabelActive = useRef(initialActiveLabel); - useEffect(() => { - if (!props.disableKeyboard) { - return; - } - - const appStateSubscription = AppState.addEventListener('change', (nextAppState) => { - if (!nextAppState.match(/inactive|background/)) { - return; - } - - Keyboard.dismiss(); - }); - - return () => { - appStateSubscription.remove(); - }; - }, [props.disableKeyboard]); - // AutoFocus which only works on mount: useEffect(() => { // We are manually managing focus to prevent this issue: https://github.com/Expensify/App/issues/4514 diff --git a/src/components/TextInput/index.native.js b/src/components/TextInput/index.native.js index eb9970f2261f..059550855c0a 100644 --- a/src/components/TextInput/index.native.js +++ b/src/components/TextInput/index.native.js @@ -1,19 +1,40 @@ -import React, {forwardRef} from 'react'; +import React, {forwardRef, useEffect} from 'react'; +import {AppState, Keyboard} from 'react-native'; import styles from '../../styles/styles'; import BaseTextInput from './BaseTextInput'; import * as baseTextInputPropTypes from './baseTextInputPropTypes'; -const TextInput = forwardRef((props, ref) => ( - -)); +const TextInput = forwardRef((props, ref) => { + useEffect(() => { + if (!props.disableKeyboard) { + return; + } + + const appStateSubscription = AppState.addEventListener('change', (nextAppState) => { + if (!nextAppState.match(/inactive|background/)) { + return; + } + + Keyboard.dismiss(); + }); + + return () => { + appStateSubscription.remove(); + }; + }, [props.disableKeyboard]); + + return ( + + ); +}); TextInput.propTypes = baseTextInputPropTypes.propTypes; TextInput.defaultProps = baseTextInputPropTypes.defaultProps; From da5635cb42d6982ad82a5fb6b12fb9213b4829d3 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Tue, 19 Sep 2023 07:49:15 +0100 Subject: [PATCH 053/117] consolidate 'I am a Teacher' and 'Intro to School Principal' pages --- src/ROUTES.ts | 2 +- .../AppNavigator/ModalStackNavigators.js | 2 +- src/pages/TeachersUnite/ImTeacherPage.js | 70 +++++++------------ .../TeachersUnite/ImTeacherUpdateEmailPage.js | 54 ++++++++++++++ .../TeachersUnite/IntroSchoolPrincipalPage.js | 1 + src/pages/TeachersUnite/SaveTheWorldPage.js | 23 +----- 6 files changed, 84 insertions(+), 68 deletions(-) create mode 100644 src/pages/TeachersUnite/ImTeacherUpdateEmailPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 2c37116db395..feead4890114 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -139,8 +139,8 @@ export default { SEARCH: 'search', TEACHERS_UNITE: 'teachersunite', I_KNOW_A_TEACHER: 'teachersunite/i-know-a-teacher', - INTRO_SCHOOL_PRINCIPAL: 'teachersunite/intro-school-principal', I_AM_A_TEACHER: 'teachersunite/i-am-a-teacher', + INTRO_SCHOOL_PRINCIPAL: 'teachersunite/intro-school-principal', DETAILS: 'details', getDetailsRoute: (login: string) => `details?login=${encodeURIComponent(login)}`, PROFILE: 'a/:accountID', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 392781a777db..16d2e0733224 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -345,7 +345,7 @@ const NewTeachersUniteNavigator = createModalStackNavigator([ }, { getComponent: () => { - const IntroSchoolPrincipalPage = require('../../../pages/TeachersUnite/IntroSchoolPrincipalPage').default; + const IntroSchoolPrincipalPage = require('../../../pages/TeachersUnite/ImTeacherPage').default; return IntroSchoolPrincipalPage; }, name: 'Intro_School_Principal', diff --git a/src/pages/TeachersUnite/ImTeacherPage.js b/src/pages/TeachersUnite/ImTeacherPage.js index dbeba700d208..a938abf81b79 100644 --- a/src/pages/TeachersUnite/ImTeacherPage.js +++ b/src/pages/TeachersUnite/ImTeacherPage.js @@ -1,54 +1,36 @@ import React from 'react'; -import ScreenWrapper from '../../components/ScreenWrapper'; -import HeaderWithBackButton from '../../components/HeaderWithBackButton'; -import ROUTES from '../../ROUTES'; -import Navigation from '../../libs/Navigation/Navigation'; -import FixedFooter from '../../components/FixedFooter'; -import styles from '../../styles/styles'; -import Button from '../../components/Button'; -import * as Illustrations from '../../components/Icon/Illustrations'; -import variables from '../../styles/variables'; -import useLocalize from '../../hooks/useLocalize'; -import BlockingView from '../../components/BlockingViews/BlockingView'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import * as LoginUtils from '../../libs/LoginUtils'; +import ImTeacherUpdateEmailPage from './ImTeacherUpdateEmailPage'; +import IntroSchoolPrincipalPage from './IntroSchoolPrincipalPage'; -const propTypes = {}; +const propTypes = { + /** Current user session */ + session: PropTypes.shape({ + /** Current user primary login */ + email: PropTypes.string.isRequired, + }), +}; -const defaultProps = {}; +const defaultProps = { + session: { + email: null, + }, +}; -function ImTeacherPage() { - const {translate} = useLocalize(); - - return ( - - Navigation.goBack(ROUTES.TEACHERS_UNITE)} - /> - Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHODS)} - iconWidth={variables.signInLogoWidthLargeScreen} - iconHeight={variables.lhnLogoWidth} - /> - -