From 3bc046b425a39e3b88c18cb2d48f509ea4043a94 Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:56:31 +0200 Subject: [PATCH 001/183] fix: update length exceeded indicator --- src/components/ExceededCommentLength.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js index c403aa63c172..72cee78777d0 100644 --- a/src/components/ExceededCommentLength.js +++ b/src/components/ExceededCommentLength.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import {debounce} from 'lodash'; import CONST from '../CONST'; import * as ReportUtils from '../libs/ReportUtils'; +import useLocalize from '../hooks/useLocalize'; import Text from './Text'; import styles from '../styles/styles'; @@ -15,6 +16,7 @@ const propTypes = { }; function ExceededCommentLength(props) { + const {numberFormat} = useLocalize(); const [commentLength, setCommentLength] = useState(0); const updateCommentLength = useMemo( () => @@ -34,7 +36,7 @@ function ExceededCommentLength(props) { return null; } - return {`${commentLength}/${CONST.MAX_COMMENT_LENGTH}`}; + return {numberFormat(CONST.MAX_COMMENT_LENGTH)}+; } ExceededCommentLength.propTypes = propTypes; From 49b14a77863b86cd8d770145807d4a8fc26a255a Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:58:55 +0200 Subject: [PATCH 002/183] fix: make identical to max markup length --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index 56102c7641f7..b248791c2bb0 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1272,7 +1272,7 @@ const CONST = { }, // Auth limit is 60k for the column but we store edits and other metadata along the html so let's use a lower limit to accommodate for it. - MAX_COMMENT_LENGTH: 15000, + MAX_COMMENT_LENGTH: 10000, // Furthermore, applying markup is very resource-consuming, so let's set a slightly lower limit for that MAX_MARKUP_LENGTH: 10000, From 588b04d30da5f9644f9ca2635189f1975068bd8a Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:03:06 +0200 Subject: [PATCH 003/183] docs: update comment for MAX_MARKUP_LENGTH --- src/CONST.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CONST.js b/src/CONST.js index b248791c2bb0..f12132116914 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1274,7 +1274,7 @@ const CONST = { // Auth limit is 60k for the column but we store edits and other metadata along the html so let's use a lower limit to accommodate for it. MAX_COMMENT_LENGTH: 10000, - // Furthermore, applying markup is very resource-consuming, so let's set a slightly lower limit for that + // Use the same value as MAX_COMMENT_LENGTH to ensure the entire comment is parsed. Note that applying markup is very resource-consuming. MAX_MARKUP_LENGTH: 10000, MAX_THREAD_REPLIES_PREVIEW: 99, From b43b1b95eac6599a7b39eea05e943a1014de403f Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Wed, 9 Aug 2023 14:49:52 +0200 Subject: [PATCH 004/183] fix: inclusive comparison for parsing --- src/libs/ReportUtils.js | 4 ++-- src/libs/actions/Report.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e46f1b39971e..603c154bea3b 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1485,7 +1485,7 @@ function hasReportNameError(report) { } /** - * For comments shorter than 10k chars, convert the comment from MD into HTML because that's how it is stored in the database + * For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database * For longer comments, skip parsing, but still escape the text, and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!! * * @param {String} text @@ -1493,7 +1493,7 @@ function hasReportNameError(report) { */ function getParsedComment(text) { const parser = new ExpensiMark(); - return text.length < CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : _.escape(text); + return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : _.escape(text); } /** diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 216d71b3389e..2180bd56a959 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1016,7 +1016,7 @@ const removeLinksFromHtml = (html, links) => { */ const handleUserDeletedLinksInHtml = (newCommentText, originalHtml) => { const parser = new ExpensiMark(); - if (newCommentText.length >= CONST.MAX_MARKUP_LENGTH) { + if (newCommentText.length > CONST.MAX_MARKUP_LENGTH) { return newCommentText; } const markdownOriginalComment = parser.htmlToMarkdown(originalHtml).trim(); @@ -1043,10 +1043,10 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { const htmlForNewComment = handleUserDeletedLinksInHtml(textForNewComment, originalCommentHTML); const reportComment = parser.htmlToText(htmlForNewComment); - // For comments shorter than 10k chars, convert the comment from MD into HTML because that's how it is stored in the database + // For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database // For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!! let parsedOriginalCommentHTML = originalCommentHTML; - if (textForNewComment.length < CONST.MAX_MARKUP_LENGTH) { + if (textForNewComment.length <= CONST.MAX_MARKUP_LENGTH) { const autolinkFilter = {filterRules: _.filter(_.pluck(parser.rules, 'name'), (name) => name !== 'autolink')}; parsedOriginalCommentHTML = parser.replace(parser.htmlToMarkdown(originalCommentHTML).trim(), autolinkFilter); } From 97807c245cb9fd7a45dd2258847019339f04fa8a Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Thu, 10 Aug 2023 10:48:11 +0700 Subject: [PATCH 005/183] 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 006/183] 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 007/183] 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 008/183] 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 009/183] 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 211e92d2704f817974dbac2f764a7ca876895d54 Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Fri, 18 Aug 2023 16:24:14 +0200 Subject: [PATCH 010/183] feat(lang): updated limit exceeded error --- src/components/ExceededCommentLength.js | 8 ++++++-- src/languages/en.js | 1 + src/languages/es.js | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js index 72cee78777d0..7c0024313bd1 100644 --- a/src/components/ExceededCommentLength.js +++ b/src/components/ExceededCommentLength.js @@ -16,7 +16,7 @@ const propTypes = { }; function ExceededCommentLength(props) { - const {numberFormat} = useLocalize(); + const {numberFormat, translate} = useLocalize(); const [commentLength, setCommentLength] = useState(0); const updateCommentLength = useMemo( () => @@ -36,7 +36,11 @@ function ExceededCommentLength(props) { return null; } - return {numberFormat(CONST.MAX_COMMENT_LENGTH)}+; + return ( + + {translate('composer.commentExceededMaxLength', {formattedMaxLength: numberFormat(CONST.MAX_COMMENT_LENGTH)})} + + ); } ExceededCommentLength.propTypes = propTypes; diff --git a/src/languages/en.js b/src/languages/en.js index bbf2762cc92f..fac41c6bb9e3 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -179,6 +179,7 @@ export default { composer: { noExtensionFoundForMimeType: 'No extension found for mime type', problemGettingImageYouPasted: 'There was a problem getting the image you pasted', + commentExceededMaxLength: ({formattedMaxLength}) => `The maximum comment length is ${formattedMaxLength} characters.`, }, baseUpdateAppModal: { updateApp: 'Update app', diff --git a/src/languages/es.js b/src/languages/es.js index c9ba02840238..281b75b2f161 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -178,6 +178,7 @@ export default { composer: { noExtensionFoundForMimeType: 'No se encontró una extension para este tipo de contenido', problemGettingImageYouPasted: 'Ha ocurrido un problema al obtener la imagen que has pegado', + commentExceededMaxLength: ({formattedMaxLength}) => `El comentario debe tener máximo ${formattedMaxLength} caracteres.`, }, baseUpdateAppModal: { updateApp: 'Actualizar app', From d798ea6a0a53eb548469a1b6d4a568136de2a6a6 Mon Sep 17 00:00:00 2001 From: Edu Date: Fri, 18 Aug 2023 17:31:12 -0300 Subject: [PATCH 011/183] 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 6dca5cda990a12e7b945edf37430a4d50ead73b9 Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Mon, 21 Aug 2023 11:31:50 +0200 Subject: [PATCH 012/183] fix: ensure single line with ellipsis on overflow --- src/components/ExceededCommentLength.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js index 7c0024313bd1..8b3f4f0adc1e 100644 --- a/src/components/ExceededCommentLength.js +++ b/src/components/ExceededCommentLength.js @@ -37,7 +37,10 @@ function ExceededCommentLength(props) { } return ( - + {translate('composer.commentExceededMaxLength', {formattedMaxLength: numberFormat(CONST.MAX_COMMENT_LENGTH)})} ); From ea6c6fe36cc6138485c1ebb6b1297aba7b7b23db Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Wed, 23 Aug 2023 09:13:36 +0700 Subject: [PATCH 013/183] 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 e4d1363e973158876dc1c7c5d9c758ad8a5e6882 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 25 Aug 2023 14:35:39 +0700 Subject: [PATCH 014/183] 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 015/183] 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 016/183] 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 017/183] 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 018/183] 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 73be910420842d4c1fb8bc8434cbc735c39a7a7a Mon Sep 17 00:00:00 2001 From: Tam Dao Date: Sat, 2 Sep 2023 06:26:35 +0700 Subject: [PATCH 019/183] Fix image loads on reesize window --- .../AttachmentCarouselCellRenderer.js | 23 +++++++++++++++ .../Attachments/AttachmentCarousel/index.js | 29 ++----------------- src/components/withWindowDimensions.js | 22 +++++++------- 3 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js diff --git a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js new file mode 100644 index 000000000000..be59f6a834f5 --- /dev/null +++ b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {View, PixelRatio} from 'react-native'; +import useWindowDimensions from '../../../hooks/useWindowDimensions'; +import styles from '../../../styles/styles'; + +function AttachmentCarouselCellRenderer(props) { + const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); + const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true); + // eslint-disable-next-line react/prop-types + const style = [props.style, styles.h100, {width: PixelRatio.roundToNearestPixel(windowWidth - (modalStyles.marginHorizontal + modalStyles.borderWidth) * 2)}]; + + return ( + + ); +} + +AttachmentCarouselCellRenderer.displayName = 'AttachmentCarouselCellRenderer'; + +export default React.memo(AttachmentCarouselCellRenderer); diff --git a/src/components/Attachments/AttachmentCarousel/index.js b/src/components/Attachments/AttachmentCarousel/index.js index 53c2c840d95d..974fb5060f8f 100644 --- a/src/components/Attachments/AttachmentCarousel/index.js +++ b/src/components/Attachments/AttachmentCarousel/index.js @@ -14,11 +14,11 @@ import ONYXKEYS from '../../../ONYXKEYS'; import withLocalize from '../../withLocalize'; import compose from '../../../libs/compose'; import useCarouselArrows from './useCarouselArrows'; -import useWindowDimensions from '../../../hooks/useWindowDimensions'; import Navigation from '../../../libs/Navigation/Navigation'; import BlockingView from '../../BlockingViews/BlockingView'; import * as Illustrations from '../../Icon/Illustrations'; import variables from '../../../styles/variables'; +import AttachmentCarouselCellRenderer from './AttachmentCarouselCellRenderer'; const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); const viewabilityConfig = { @@ -30,8 +30,6 @@ const viewabilityConfig = { function AttachmentCarousel({report, reportActions, source, onNavigate, setDownloadButtonVisibility, translate}) { const scrollRef = useRef(null); - const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); - const [containerWidth, setContainerWidth] = useState(0); const [page, setPage] = useState(0); const [attachments, setAttachments] = useState([]); @@ -117,29 +115,6 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl [containerWidth], ); - /** - * Defines how a container for a single attachment should be rendered - * @param {Object} cellRendererProps - * @returns {JSX.Element} - */ - const renderCell = useCallback( - (cellProps) => { - // Use window width instead of layout width to address the issue in https://github.com/Expensify/App/issues/17760 - // considering horizontal margin and border width in centered modal - const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true); - const style = [cellProps.style, styles.h100, {width: PixelRatio.roundToNearestPixel(windowWidth - (modalStyles.marginHorizontal + modalStyles.borderWidth) * 2)}]; - - return ( - - ); - }, - [isSmallScreenWidth, windowWidth], - ); - /** * Defines how a single attachment should be rendered * @param {Object} item @@ -212,7 +187,7 @@ function AttachmentCarousel({report, reportActions, source, onNavigate, setDownl windowSize={5} maxToRenderPerBatch={3} data={attachments} - CellRendererComponent={renderCell} + CellRendererComponent={AttachmentCarouselCellRenderer} renderItem={renderItem} getItemLayout={getItemLayout} keyExtractor={(item) => item.source} diff --git a/src/components/withWindowDimensions.js b/src/components/withWindowDimensions.js index 9ec9c5d4acbd..2290aff0f304 100644 --- a/src/components/withWindowDimensions.js +++ b/src/components/withWindowDimensions.js @@ -1,5 +1,6 @@ -import React, {forwardRef, createContext, useState, useEffect} from 'react'; +import React, {forwardRef, createContext, useState, useEffect, useCallback} from 'react'; import PropTypes from 'prop-types'; +import lodashDebounce from 'lodash/debounce'; import {Dimensions} from 'react-native'; import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; import getComponentDisplayName from '../libs/getComponentDisplayName'; @@ -41,17 +42,18 @@ function WindowDimensionsProvider(props) { }; }); - useEffect(() => { - const onDimensionChange = (newDimensions) => { - const {window} = newDimensions; + const onDimensionChange = useCallback((newDimensions) => { + const {window} = newDimensions; + setWindowDimension({ + windowHeight: window.height, + windowWidth: window.width, + }); + }, []); - setWindowDimension({ - windowHeight: window.height, - windowWidth: window.width, - }); - }; + const onDimensionChangeDebounce = useCallback(lodashDebounce(onDimensionChange, 300), []); - const dimensionsEventListener = Dimensions.addEventListener('change', onDimensionChange); + useEffect(() => { + const dimensionsEventListener = Dimensions.addEventListener('change', onDimensionChangeDebounce); return () => { if (!dimensionsEventListener) { From 74a2652cb5b8fddce8b6b6c7a89d7a88c3cb214d Mon Sep 17 00:00:00 2001 From: Tam Dao Date: Sat, 2 Sep 2023 07:17:09 +0700 Subject: [PATCH 020/183] Add prop types into AttachmentCarouselCellRenderer component --- .../AttachmentCarouselCellRenderer.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js index be59f6a834f5..2c698d5c8a61 100644 --- a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js +++ b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js @@ -1,12 +1,21 @@ import React from 'react'; +import PropTypes from 'prop-types'; import {View, PixelRatio} from 'react-native'; import useWindowDimensions from '../../../hooks/useWindowDimensions'; import styles from '../../../styles/styles'; +const propTypes = { + /** Cell Container styles */ + style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), +}; + +const defaultProps = { + style: [], +}; + function AttachmentCarouselCellRenderer(props) { const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true); - // eslint-disable-next-line react/prop-types const style = [props.style, styles.h100, {width: PixelRatio.roundToNearestPixel(windowWidth - (modalStyles.marginHorizontal + modalStyles.borderWidth) * 2)}]; return ( @@ -18,6 +27,8 @@ function AttachmentCarouselCellRenderer(props) { ); } +AttachmentCarouselCellRenderer.propTypes = propTypes; +AttachmentCarouselCellRenderer.defaultProps = defaultProps; AttachmentCarouselCellRenderer.displayName = 'AttachmentCarouselCellRenderer'; export default React.memo(AttachmentCarouselCellRenderer); From bcc644c4140321453190497707a7e11d197ff122 Mon Sep 17 00:00:00 2001 From: Tam Dao Date: Tue, 5 Sep 2023 00:08:03 +0700 Subject: [PATCH 021/183] fix eslint warning --- src/components/withWindowDimensions.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/withWindowDimensions.js b/src/components/withWindowDimensions.js index 2290aff0f304..6feb81b957a8 100644 --- a/src/components/withWindowDimensions.js +++ b/src/components/withWindowDimensions.js @@ -42,17 +42,17 @@ function WindowDimensionsProvider(props) { }; }); - const onDimensionChange = useCallback((newDimensions) => { - const {window} = newDimensions; - setWindowDimension({ - windowHeight: window.height, - windowWidth: window.width, - }); - }, []); + useEffect(() => { + const onDimensionChange = (newDimensions) => { + const {window} = newDimensions; + setWindowDimension({ + windowHeight: window.height, + windowWidth: window.width, + }); + }; - const onDimensionChangeDebounce = useCallback(lodashDebounce(onDimensionChange, 300), []); + const onDimensionChangeDebounce = lodashDebounce(onDimensionChange, 300); - useEffect(() => { const dimensionsEventListener = Dimensions.addEventListener('change', onDimensionChangeDebounce); return () => { From c04114bf72a44cc795e6b537f9d4b2035c8572e7 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 6 Sep 2023 17:28:19 +0200 Subject: [PATCH 022/183] 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 023/183] 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 024/183] 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 025/183] 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 026/183] 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 027/183] 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 028/183] 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 029/183] 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 030/183] 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 031/183] 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 032/183] 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 033/183] 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 034/183] 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 035/183] 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 83cc790e96532b8a00ab4c6ec21288eb49c12313 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 11 Sep 2023 14:04:08 -0600 Subject: [PATCH 036/183] Add type for filename property --- src/types/onyx/Transaction.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 4326920ab51f..cc47632ea1fb 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -19,25 +19,27 @@ type Route = { type Routes = Record; type Transaction = { - transactionID: string; amount: number; category?: string; - currency: string; - reportID: string; comment: Comment; - merchant: string; created: string; - pendingAction: OnyxCommon.PendingAction; + currency: string; errors: OnyxCommon.Errors; + // The name of the file used for a receipt (formerly receiptFilename) + filename: string; + merchant: string; modifiedAmount?: number; modifiedCreated?: string; modifiedCurrency?: string; + pendingAction: OnyxCommon.PendingAction; receipt: { receiptID?: number; source?: string; state?: ValueOf; }; + reportID: string; routes?: Routes; + transactionID: string; }; export default Transaction; From 3b410aab208aa40da98e17f2898c5565280babc1 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 11 Sep 2023 14:04:20 -0600 Subject: [PATCH 037/183] Remove fallback to old property name --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 +- src/components/ReportActionItem/ReportPreview.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 02da03225062..1fe819a7edc7 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -170,7 +170,7 @@ function MoneyRequestPreview(props) { !_.isEmpty(requestMerchant) && !props.isBillSplit && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT; const shouldShowDescription = !_.isEmpty(description) && !shouldShowMerchant; - const receiptImages = hasReceipt ? [ReceiptUtils.getThumbnailAndImageURIs(props.transaction.receipt.source, props.transaction.filename || props.transaction.receiptFilename || '')] : []; + const receiptImages = hasReceipt ? [ReceiptUtils.getThumbnailAndImageURIs(props.transaction.receipt.source, props.transaction.filename || '')] : []; const getSettledMessage = () => { switch (lodashGet(props.action, 'originalMessage.paymentType', '')) { diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index b95fdaad1025..f75f35481aed 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -120,9 +120,7 @@ function ReportPreview(props) { const isScanning = hasReceipts && ReportUtils.areAllRequestsBeingSmartScanned(props.iouReportID, props.action); const hasErrors = hasReceipts && ReportUtils.hasMissingSmartscanFields(props.iouReportID); const lastThreeTransactionsWithReceipts = ReportUtils.getReportPreviewDisplayTransactions(props.action); - const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, ({receipt, filename, receiptFilename}) => - ReceiptUtils.getThumbnailAndImageURIs(receipt.source, filename || receiptFilename || ''), - ); + const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, ({receipt, filename}) => ReceiptUtils.getThumbnailAndImageURIs(receipt.source, filename || '')); const hasOnlyOneReceiptRequest = numberOfRequests === 1 && hasReceipts; const previewSubtitle = hasOnlyOneReceiptRequest From 8521bfbc3a55580e1f3f55f5c76284ce7ca5c02e Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 11 Sep 2023 14:17:58 -0600 Subject: [PATCH 038/183] Write initial migration --- src/libs/migrations/RenameReceiptFilename.js | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/libs/migrations/RenameReceiptFilename.js diff --git a/src/libs/migrations/RenameReceiptFilename.js b/src/libs/migrations/RenameReceiptFilename.js new file mode 100644 index 000000000000..f7e3d8a92acb --- /dev/null +++ b/src/libs/migrations/RenameReceiptFilename.js @@ -0,0 +1,49 @@ +import Onyx from 'react-native-onyx'; +import _ from 'underscore'; +import lodashHas from 'lodash/has'; +import ONYXKEYS from '../../ONYXKEYS'; +import Log from '../Log'; + +// This migration changes the property name on a transaction from receiptFilename to filename so that it matches what is stored in the database +export default function () { + return new Promise((resolve) => { + // Connect to the TRANSACTION collection key in Onyx to get all of the stored transactions. + // Go through each transaction and change the property name + const connectionID = Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + callback: (transactions) => { + Onyx.disconnect(connectionID); + + if (!transactions || transactions.length === 0) { + Log.info('[Migrate Onyx] Skipped migration RenameReceiptFilename because there are no transactions'); + return resolve(); + } + + const dataToSave = _.reduce( + transactions, + (dataToSaveToOnyx, transaction) => { + // Do nothing if there is no receiptFilename property + if (!lodashHas(transaction, 'receiptFilename')) { + return dataToSaveToOnyx; + } + Log.info(`[Migrate Onyx] Renaming receiptFilename ${transaction.receiptFilename} to filename`); + return { + ...dataToSaveToOnyx, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transctionID}`]: { + ..._.omit(transaction, 'receiptFilename'), + filename: transaction.receiptFilename, + }, + }; + }, + {}, + ); + + // eslint-disable-next-line rulesdir/prefer-actions-set-data + Onyx.multiSet(dataToSave).then(() => { + Log.info('[Migrate Onyx] Ran migration RenameReceiptFilename'); + resolve(); + }); + }, + }); + }); +} From b9a72f43356a40352ebc1d6d8c0b50007dc2c4c7 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 11 Sep 2023 14:21:54 -0600 Subject: [PATCH 039/183] Add an early return --- src/libs/migrations/RenameReceiptFilename.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/migrations/RenameReceiptFilename.js b/src/libs/migrations/RenameReceiptFilename.js index f7e3d8a92acb..095987b186ce 100644 --- a/src/libs/migrations/RenameReceiptFilename.js +++ b/src/libs/migrations/RenameReceiptFilename.js @@ -19,6 +19,11 @@ export default function () { return resolve(); } + if (!_.pluck(transactions, 'receiptFilename').length) { + Log.info('[Migrate Onyx] Skipped migration RenameReceiptFilename because there were no transactions with the receiptFilename property'); + return resolve(); + } + const dataToSave = _.reduce( transactions, (dataToSaveToOnyx, transaction) => { @@ -40,7 +45,7 @@ export default function () { // eslint-disable-next-line rulesdir/prefer-actions-set-data Onyx.multiSet(dataToSave).then(() => { - Log.info('[Migrate Onyx] Ran migration RenameReceiptFilename'); + Log.info(`[Migrate Onyx] Ran migration RenameReceiptFilename and renamed ${_.size(dataToSave)} properties`); resolve(); }); }, From 78b8b5452be52425d48745d227642f921574be82 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 11 Sep 2023 14:48:04 -0600 Subject: [PATCH 040/183] Clean up final migration --- src/libs/migrateOnyx.js | 2 ++ src/libs/migrations/RenameReceiptFilename.js | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libs/migrateOnyx.js b/src/libs/migrateOnyx.js index 9389a9b66fbc..7832b368a918 100644 --- a/src/libs/migrateOnyx.js +++ b/src/libs/migrateOnyx.js @@ -8,6 +8,7 @@ import RenameExpensifyNewsStatus from './migrations/RenameExpensifyNewsStatus'; import AddLastVisibleActionCreated from './migrations/AddLastVisibleActionCreated'; import KeyReportActionsByReportActionID from './migrations/KeyReportActionsByReportActionID'; import PersonalDetailsByAccountID from './migrations/PersonalDetailsByAccountID'; +import RenameReceiptFilename from './migrations/RenameReceiptFilename'; export default function () { const startTime = Date.now(); @@ -24,6 +25,7 @@ export default function () { AddLastVisibleActionCreated, KeyReportActionsByReportActionID, PersonalDetailsByAccountID, + RenameReceiptFilename, ]; // Reduce all promises down to a single promise. All promises run in a linear fashion, waiting for the diff --git a/src/libs/migrations/RenameReceiptFilename.js b/src/libs/migrations/RenameReceiptFilename.js index 095987b186ce..b8df705fd7d1 100644 --- a/src/libs/migrations/RenameReceiptFilename.js +++ b/src/libs/migrations/RenameReceiptFilename.js @@ -11,6 +11,7 @@ export default function () { // Go through each transaction and change the property name const connectionID = Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, callback: (transactions) => { Onyx.disconnect(connectionID); @@ -19,24 +20,26 @@ export default function () { return resolve(); } - if (!_.pluck(transactions, 'receiptFilename').length) { + if (!_.compact(_.pluck(transactions, 'receiptFilename')).length) { Log.info('[Migrate Onyx] Skipped migration RenameReceiptFilename because there were no transactions with the receiptFilename property'); return resolve(); } + Log.info('[Migrate Onyx] Running RenameReceiptFilename migration'); + const dataToSave = _.reduce( transactions, - (dataToSaveToOnyx, transaction) => { + (result, transaction) => { // Do nothing if there is no receiptFilename property if (!lodashHas(transaction, 'receiptFilename')) { - return dataToSaveToOnyx; + return result; } Log.info(`[Migrate Onyx] Renaming receiptFilename ${transaction.receiptFilename} to filename`); return { - ...dataToSaveToOnyx, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transctionID}`]: { - ..._.omit(transaction, 'receiptFilename'), + ...result, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`]: { filename: transaction.receiptFilename, + receiptFilename: null, }, }; }, @@ -44,7 +47,7 @@ export default function () { ); // eslint-disable-next-line rulesdir/prefer-actions-set-data - Onyx.multiSet(dataToSave).then(() => { + Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, dataToSave).then(() => { Log.info(`[Migrate Onyx] Ran migration RenameReceiptFilename and renamed ${_.size(dataToSave)} properties`); resolve(); }); From 62de1ba8d46d23bf4c8adc84e131cec887460039 Mon Sep 17 00:00:00 2001 From: Sam Hariri <137707942+samh-nl@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:56:06 +0200 Subject: [PATCH 041/183] fix: added type --- src/languages/en.ts | 3 ++- src/languages/es.ts | 3 ++- src/languages/types.ts | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 0e2b19ff4dd2..b34653966c52 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -71,6 +71,7 @@ import type { SetTheRequestParams, UpdatedTheRequestParams, RemovedTheRequestParams, + FormattedMaxLengthParams, } from './types'; import * as ReportActionsUtils from '../libs/ReportActionsUtils'; @@ -270,7 +271,7 @@ export default { composer: { noExtensionFoundForMimeType: 'No extension found for mime type', problemGettingImageYouPasted: 'There was a problem getting the image you pasted', - commentExceededMaxLength: ({formattedMaxLength}) => `The maximum comment length is ${formattedMaxLength} characters.`, + commentExceededMaxLength: ({formattedMaxLength}: FormattedMaxLengthParams) => `The maximum comment length is ${formattedMaxLength} characters.`, }, baseUpdateAppModal: { updateApp: 'Update app', diff --git a/src/languages/es.ts b/src/languages/es.ts index b6a01c8d04ca..d6a1d7096ee6 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -71,6 +71,7 @@ import type { SetTheRequestParams, UpdatedTheRequestParams, RemovedTheRequestParams, + FormattedMaxLengthParams, } from './types'; /* eslint-disable max-len */ @@ -269,7 +270,7 @@ export default { composer: { noExtensionFoundForMimeType: 'No se encontró una extension para este tipo de contenido', problemGettingImageYouPasted: 'Ha ocurrido un problema al obtener la imagen que has pegado', - commentExceededMaxLength: ({formattedMaxLength}) => `El comentario debe tener máximo ${formattedMaxLength} caracteres.`, + commentExceededMaxLength: ({formattedMaxLength}: FormattedMaxLengthParams) => `El comentario debe tener máximo ${formattedMaxLength} caracteres.`, }, baseUpdateAppModal: { updateApp: 'Actualizar app', diff --git a/src/languages/types.ts b/src/languages/types.ts index 7a698f912b14..0103425dec1d 100644 --- a/src/languages/types.ts +++ b/src/languages/types.ts @@ -190,6 +190,8 @@ type RemovedTheRequestParams = {valueName: string; oldValueToDisplay: string}; type UpdatedTheRequestParams = {valueName: string; newValueToDisplay: string; oldValueToDisplay: string}; +type FormattedMaxLengthParams = {formattedMaxLength: string}; + export type { AddressLineParams, CharacterLimitParams, @@ -261,4 +263,5 @@ export type { SetTheRequestParams, UpdatedTheRequestParams, RemovedTheRequestParams, + FormattedMaxLengthParams, }; From e15c420419bf6de00a28e6bb419a30092eac531f Mon Sep 17 00:00:00 2001 From: Tam Dao Date: Tue, 12 Sep 2023 09:05:50 +0900 Subject: [PATCH 042/183] remove unused func --- src/components/withWindowDimensions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/withWindowDimensions.js b/src/components/withWindowDimensions.js index 6feb81b957a8..5ce39bbf2b42 100644 --- a/src/components/withWindowDimensions.js +++ b/src/components/withWindowDimensions.js @@ -1,4 +1,4 @@ -import React, {forwardRef, createContext, useState, useEffect, useCallback} from 'react'; +import React, {forwardRef, createContext, useState, useEffect} from 'react'; import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; import {Dimensions} from 'react-native'; From 9f3e7997b97b47a60b767d369c90ebe8342df63c Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Tue, 12 Sep 2023 18:42:43 +0200 Subject: [PATCH 043/183] 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 044/183] 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 045/183] 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 046/183] 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 0ab4031e761fedf0cfa7e37c00b01c1c4b3fa287 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 12 Sep 2023 12:39:52 -0600 Subject: [PATCH 047/183] Let property be optional --- src/types/onyx/Transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index cc47632ea1fb..904a68413d2d 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -26,7 +26,7 @@ type Transaction = { currency: string; errors: OnyxCommon.Errors; // The name of the file used for a receipt (formerly receiptFilename) - filename: string; + filename?: string; merchant: string; modifiedAmount?: number; modifiedCreated?: string; From 9f47e6a2fcf95df8a4cb0fef16819483090f3f02 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 13 Sep 2023 11:14:39 +0700 Subject: [PATCH 048/183] 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 049/183] 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 050/183] 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 051/183] 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 052/183] 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 053/183] 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 054/183] [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 cd0804b89612bfb6a802cfeed704aff9750a95b5 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Thu, 14 Sep 2023 09:54:35 +0700 Subject: [PATCH 055/183] fix: 26882 --- src/components/ImageView/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index c92bd7738253..5d6d1b609ab3 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -225,14 +225,14 @@ function ImageView({isAuthTokenRequired, url, fileName}) { source={{uri: url}} isAuthTokenRequired={isAuthTokenRequired} // Hide image until finished loading to prevent showing preview with wrong dimensions. - style={isLoading ? undefined : [styles.w100, styles.h100]} + style={(isLoading || zoomScale === 0) ? undefined : [styles.w100, styles.h100]} // When Image dimensions are lower than the container boundary(zoomscale <= 1), use `contain` to render the image with natural dimensions. // Both `center` and `contain` keeps the image centered on both x and y axis. resizeMode={zoomScale > 1 ? Image.resizeMode.center : Image.resizeMode.contain} onLoadStart={imageLoadingStart} onLoad={imageLoad} /> - {isLoading && } + {(isLoading || zoomScale === 0) && } ); } From 32d135582c6c20e3fa5ab5f0e88ff9a47c9c8532 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 14 Sep 2023 16:43:09 +0200 Subject: [PATCH 056/183] delegate category selection to parent --- .../CategoryPicker/categoryPickerPropTypes.js | 16 ++++------ src/components/CategoryPicker/index.js | 30 ++++--------------- src/pages/iou/MoneyRequestCategoryPage.js | 27 +++++++++++++++-- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/src/components/CategoryPicker/categoryPickerPropTypes.js b/src/components/CategoryPicker/categoryPickerPropTypes.js index b8e24c199a73..6f2800a5d98f 100644 --- a/src/components/CategoryPicker/categoryPickerPropTypes.js +++ b/src/components/CategoryPicker/categoryPickerPropTypes.js @@ -2,14 +2,11 @@ import PropTypes from 'prop-types'; import categoryPropTypes from '../categoryPropTypes'; const propTypes = { - /** The report ID of the IOU */ - reportID: PropTypes.string.isRequired, - /** The policyID we are getting categories for */ policyID: PropTypes.string, - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string.isRequired, + /** The selected category of an expense */ + selectedCategory: PropTypes.string, /* Onyx Props */ /** Collection of categories attached to a policy */ @@ -19,18 +16,15 @@ const propTypes = { /** Collection of recently used categories attached to a policy */ policyRecentlyUsedCategories: PropTypes.arrayOf(PropTypes.string), - /* Onyx Props */ - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: PropTypes.shape({ - category: PropTypes.string.isRequired, - }), + /** Callback to fire when a category is pressed */ + onSubmit: PropTypes.func.isRequired, }; const defaultProps = { policyID: '', + selectedCategory: '', policyCategories: {}, policyRecentlyUsedCategories: [], - iou: {}, }; export {propTypes, defaultProps}; diff --git a/src/components/CategoryPicker/index.js b/src/components/CategoryPicker/index.js index 91c7e82e7887..66ac0bf14d4a 100644 --- a/src/components/CategoryPicker/index.js +++ b/src/components/CategoryPicker/index.js @@ -5,15 +5,12 @@ import lodashGet from 'lodash/get'; import ONYXKEYS from '../../ONYXKEYS'; import {propTypes, defaultProps} from './categoryPickerPropTypes'; import styles from '../../styles/styles'; -import Navigation from '../../libs/Navigation/Navigation'; -import ROUTES from '../../ROUTES'; import CONST from '../../CONST'; -import * as IOU from '../../libs/actions/IOU'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; import OptionsSelector from '../OptionsSelector'; import useLocalize from '../../hooks/useLocalize'; -function CategoryPicker({policyCategories, reportID, iouType, iou, policyRecentlyUsedCategories}) { +function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedCategories, onSubmit}) { const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); @@ -21,18 +18,18 @@ function CategoryPicker({policyCategories, reportID, iouType, iou, policyRecentl const isCategoriesCountBelowThreshold = policyCategoriesCount < CONST.CATEGORY_LIST_THRESHOLD; const selectedOptions = useMemo(() => { - if (!iou.category) { + if (!selectedCategory) { return []; } return [ { - name: iou.category, + name: selectedCategory, enabled: true, accountID: null, }, ]; - }, [iou.category]); + }, [selectedCategory]); const initialFocusedIndex = useMemo(() => { if (isCategoriesCountBelowThreshold && selectedOptions.length > 0) { @@ -53,20 +50,6 @@ function CategoryPicker({policyCategories, reportID, iouType, iou, policyRecentl const headerMessage = OptionsListUtils.getHeaderMessage(lodashGet(sections, '[0].data.length', 0) > 0, false, searchValue); const shouldShowTextInput = !isCategoriesCountBelowThreshold; - const navigateBack = () => { - Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID)); - }; - - const updateCategory = (category) => { - if (category.searchText === iou.category) { - IOU.resetMoneyRequestCategory(); - } else { - IOU.setMoneyRequestCategory(category.searchText); - } - - navigateBack(); - }; - return ( ); } @@ -97,7 +80,4 @@ export default withOnyx({ policyRecentlyUsedCategories: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, }, - iou: { - key: ONYXKEYS.IOU, - }, })(CategoryPicker); diff --git a/src/pages/iou/MoneyRequestCategoryPage.js b/src/pages/iou/MoneyRequestCategoryPage.js index 80b88a762609..adba5755496d 100644 --- a/src/pages/iou/MoneyRequestCategoryPage.js +++ b/src/pages/iou/MoneyRequestCategoryPage.js @@ -10,6 +10,7 @@ import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import CategoryPicker from '../../components/CategoryPicker'; import ONYXKEYS from '../../ONYXKEYS'; import reportPropTypes from '../reportPropTypes'; +import * as IOU from '../../libs/actions/IOU'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -26,13 +27,20 @@ const propTypes = { /** The report currently being used */ report: reportPropTypes, + + /* Onyx Props */ + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: PropTypes.shape({ + category: PropTypes.string.isRequired, + }), }; const defaultProps = { report: {}, + iou: {}, }; -function MoneyRequestCategoryPage({route, report}) { +function MoneyRequestCategoryPage({route, report, iou}) { const {translate} = useLocalize(); const reportID = lodashGet(route, 'params.reportID', ''); @@ -42,6 +50,16 @@ function MoneyRequestCategoryPage({route, report}) { Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID)); }; + const updateCategory = (category) => { + if (category.searchText === iou.category) { + IOU.resetMoneyRequestCategory(); + } else { + IOU.setMoneyRequestCategory(category.searchText); + } + + Navigation.goBack(ROUTES.getMoneyRequestConfirmationRoute(iouType, reportID)); + }; + return ( ); @@ -69,4 +87,7 @@ export default withOnyx({ report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`, }, + iou: { + key: ONYXKEYS.IOU, + }, })(MoneyRequestCategoryPage); From d7996869bf22815900c87a80f9ac9e68a1f27626 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 14 Sep 2023 16:44:05 +0200 Subject: [PATCH 057/183] implement edit request category page --- src/CONST.ts | 1 + src/pages/EditRequestCategoryPage.js | 49 ++++++++++++++++++++++++++++ src/pages/EditRequestPage.js | 26 ++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/pages/EditRequestCategoryPage.js diff --git a/src/CONST.ts b/src/CONST.ts index 1ef2f3e83246..7b6d1d1d36fb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1349,6 +1349,7 @@ const CONST = { DATE: 'date', DESCRIPTION: 'description', MERCHANT: 'merchant', + CATEGORY: 'category', }, FOOTER: { EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`, diff --git a/src/pages/EditRequestCategoryPage.js b/src/pages/EditRequestCategoryPage.js new file mode 100644 index 000000000000..ba94c2b40800 --- /dev/null +++ b/src/pages/EditRequestCategoryPage.js @@ -0,0 +1,49 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ScreenWrapper from '../components/ScreenWrapper'; +import HeaderWithBackButton from '../components/HeaderWithBackButton'; +import Navigation from '../libs/Navigation/Navigation'; +import useLocalize from '../hooks/useLocalize'; +import CategoryPicker from '../components/CategoryPicker'; + +const propTypes = { + /** Transaction default category value */ + defaultCategory: PropTypes.string.isRequired, + + /** The policyID we are getting categories for */ + policyID: PropTypes.string.isRequired, + + /** Callback to fire when the Save button is pressed */ + onSubmit: PropTypes.func.isRequired, +}; + +function EditRequestCategoryPage({defaultCategory, policyID, onSubmit}) { + const {translate} = useLocalize(); + + return ( + + + + + onSubmit({ + category: category.searchText, + }) + } + /> + + ); +} + +EditRequestCategoryPage.propTypes = propTypes; +EditRequestCategoryPage.displayName = 'EditRequestCategoryPage'; + +export default EditRequestCategoryPage; diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 266515e29c2c..d4fc92c894d0 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -19,6 +19,7 @@ import reportPropTypes from './reportPropTypes'; import * as IOU from '../libs/actions/IOU'; import * as CurrencyUtils from '../libs/CurrencyUtils'; import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; +import EditRequestCategoryPage from './EditRequestCategoryPage'; const propTypes = { /** Route from navigation */ @@ -69,7 +70,13 @@ const defaultProps = { function EditRequestPage({report, route, parentReport, policy, session}) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); const transaction = TransactionUtils.getLinkedTransaction(parentReportAction); - const {amount: transactionAmount, currency: transactionCurrency, comment: transactionDescription, merchant: transactionMerchant} = ReportUtils.getTransactionDetails(transaction); + const { + amount: transactionAmount, + currency: transactionCurrency, + comment: transactionDescription, + merchant: transactionMerchant, + category: transactionCategory, + } = ReportUtils.getTransactionDetails(transaction); const defaultCurrency = lodashGet(route, 'params.currency', '') || transactionCurrency; @@ -169,6 +176,23 @@ function EditRequestPage({report, route, parentReport, policy, session}) { ); } + if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.CATEGORY) { + return ( + { + let updatedCategory = transactionChanges.category; + // In case the same category has been selected, do reset of the category. + if (transactionCategory === updatedCategory) { + updatedCategory = ''; + } + editMoneyRequest({category: updatedCategory}); + }} + /> + ); + } + return ; } From da95354c9ad2a170f224a080e4651ef0a0ec2207 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 14 Sep 2023 16:46:25 +0200 Subject: [PATCH 058/183] preview and navigate to edit a category --- .../ReportActionItem/MoneyRequestView.js | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index a9264812b99d..7ad7dc0ce356 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -1,6 +1,7 @@ import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import reportPropTypes from '../../pages/reportPropTypes'; @@ -27,6 +28,7 @@ import Image from '../Image'; import ReportActionItemImage from './ReportActionItemImage'; import * as TransactionUtils from '../../libs/TransactionUtils'; import OfflineWithFeedback from '../OfflineWithFeedback'; +import categoryPropTypes from '../categoryPropTypes'; const propTypes = { /** The report currently being looked at */ @@ -35,6 +37,10 @@ const propTypes = { /** The expense report or iou report (only will have a value if this is a transaction thread) */ parentReport: iouReportPropTypes, + /* Onyx Props */ + /** Collection of categories attached to a policy */ + policyCategories: PropTypes.objectOf(categoryPropTypes), + /** The transaction associated with the transactionThread */ transaction: transactionPropTypes, @@ -46,6 +52,7 @@ const propTypes = { const defaultProps = { parentReport: {}, + policyCategories: {}, transaction: { amount: 0, currency: CONST.CURRENCY.USD, @@ -53,7 +60,7 @@ const defaultProps = { }, }; -function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, transaction}) { +function MoneyRequestView({report, parentReport, policyCategories, shouldShowHorizontalRule, transaction}) { const {isSmallScreenWidth} = useWindowDimensions(); const {translate} = useLocalize(); @@ -65,6 +72,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans currency: transactionCurrency, comment: transactionDescription, merchant: transactionMerchant, + category: transactionCategory, } = ReportUtils.getTransactionDetails(transaction); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; @@ -72,6 +80,7 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction); + const shouldShowCategory = !_.isEmpty(policyCategories) || !_.isEmpty(transactionCategory); let description = `${translate('iou.amount')} • ${translate('iou.cash')}`; if (isSettled) { @@ -165,6 +174,18 @@ function MoneyRequestView({report, parentReport, shouldShowHorizontalRule, trans subtitleTextStyle={styles.textLabelError} /> + {shouldShowCategory && ( + + Navigation.navigate(ROUTES.getEditRequestRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.CATEGORY))} + /> + + )} {shouldShowHorizontalRule && } ); @@ -183,6 +204,9 @@ export default compose( policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, + policyCategories: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report.policyID}`, + }, session: { key: ONYXKEYS.SESSION, }, From bfc59ba9dcf7275eb7cad57dac477d0576577190 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 14 Sep 2023 17:35:52 +0200 Subject: [PATCH 059/183] prepare api --- src/libs/TransactionUtils.js | 14 +++++++++++++- src/libs/actions/IOU.js | 13 +++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 4ca8b48d284e..73ba6a8116ef 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -141,6 +141,11 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep shouldStopSmartscan = true; } + if (_.has(transactionChanges, 'category')) { + updatedTransaction.modifiedCategory = transactionChanges.category; + shouldStopSmartscan = true; + } + if (shouldStopSmartscan && _.has(transaction, 'receipt') && !_.isEmpty(transaction.receipt) && lodashGet(transaction, 'receipt.state') !== CONST.IOU.RECEIPT_STATE.OPEN) { updatedTransaction.receipt.state = CONST.IOU.RECEIPT_STATE.OPEN; } @@ -151,6 +156,7 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep ...(_.has(transactionChanges, 'amount') && {amount: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), ...(_.has(transactionChanges, 'currency') && {currency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), ...(_.has(transactionChanges, 'merchant') && {merchant: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), + ...(_.has(transactionChanges, 'category') && {category: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), }; return updatedTransaction; @@ -232,12 +238,18 @@ function getMerchant(transaction) { } /** - * Return the category from the transaction. This "category" field has no "modified" complement. + * Return the category from the transaction, return the modifiedCategory if present. * * @param {Object} transaction * @return {String} */ function getCategory(transaction) { + const modifiedCategory = lodashGet(transaction, 'modifiedCategory', null); + + if (!_.isNull(modifiedCategory)) { + return modifiedCategory; + } + return lodashGet(transaction, 'category', ''); } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 8f18119203be..3e23bb596eee 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1121,6 +1121,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC created: null, currency: null, merchant: null, + category: null, }, }, }, @@ -1156,6 +1157,18 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC }, ]; + // STEP 5: Use the modifiedCategory as a category on success + if (!_.isUndefined(updatedTransaction.modifiedCategory)) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + category: updatedTransaction.modifiedCategory, + modifiedCategory: null, + }, + }); + } + // STEP 6: Call the API endpoint const {created, amount, currency, comment, merchant, category} = ReportUtils.getTransactionDetails(updatedTransaction); API.write( From 30c7499a5004cb5247ff16566cf7abce32beb258 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 15 Sep 2023 00:15:56 +0700 Subject: [PATCH 060/183] 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 061/183] 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 7edcbbeff0207a614188090ef812cbfca1050938 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Fri, 15 Sep 2023 03:25:02 +0530 Subject: [PATCH 062/183] fix: autoFocus for payment input in safari. Signed-off-by: Krishna Gupta --- src/libs/Navigation/OnyxTabNavigator.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libs/Navigation/OnyxTabNavigator.js b/src/libs/Navigation/OnyxTabNavigator.js index dc68021bf515..56b820f5e05b 100644 --- a/src/libs/Navigation/OnyxTabNavigator.js +++ b/src/libs/Navigation/OnyxTabNavigator.js @@ -4,6 +4,8 @@ import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import Tab from '../actions/Tab'; import ONYXKEYS from '../../ONYXKEYS'; +import * as Browser from '../Browser'; +import CONST from '../../CONST'; const propTypes = { /* ID of the tab component to be saved in onyx */ @@ -23,6 +25,10 @@ const defaultProps = { // eslint-disable-next-line rulesdir/no-inline-named-export export const TopTab = createMaterialTopTabNavigator(); +// Will set this for all platforms once issue below is fixed for native devices. +// https://github.com/Expensify/App/issues/27117 +const keyboardDismissModeProp = Browser.getBrowser() === CONST.BROWSER.SAFARI ? {keyboardDismissMode: 'none'} : {}; + // This takes all the same props as MaterialTopTabsNavigator: https://reactnavigation.org/docs/material-top-tab-navigator/#props, // except ID is now required, and it gets a `selectedTab` from Onyx function OnyxTabNavigator({id, selectedTab, children, ...rest}) { @@ -30,6 +36,8 @@ function OnyxTabNavigator({id, selectedTab, children, ...rest}) { Date: Fri, 15 Sep 2023 11:15:41 +0200 Subject: [PATCH 063/183] Refactor migrations file --- ...ctiveClientsKey.js => RenameActiveClientsKey.ts} | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) rename src/libs/migrations/{RenameActiveClientsKey.js => RenameActiveClientsKey.ts} (70%) diff --git a/src/libs/migrations/RenameActiveClientsKey.js b/src/libs/migrations/RenameActiveClientsKey.ts similarity index 70% rename from src/libs/migrations/RenameActiveClientsKey.js rename to src/libs/migrations/RenameActiveClientsKey.ts index 54b36e13cff5..cb73e64ca12c 100644 --- a/src/libs/migrations/RenameActiveClientsKey.js +++ b/src/libs/migrations/RenameActiveClientsKey.ts @@ -1,28 +1,31 @@ -import Onyx from 'react-native-onyx'; -import _ from 'underscore'; +import Onyx, {OnyxEntry} from 'react-native-onyx'; import Log from '../Log'; import ONYXKEYS from '../../ONYXKEYS'; // This migration changes the name of the Onyx key ACTIVE_CLIENTS from activeClients2 to activeClients -export default function () { +export default function (): Promise { return new Promise((resolve) => { // Connect to the old key in Onyx to get the old value of activeClients2 // then set the new key activeClients to hold the old data // finally remove the old key by setting the value to null const connectionID = Onyx.connect({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error key: 'activeClients2', callback: (oldActiveClients) => { Onyx.disconnect(connectionID); // Fail early here because there is nothing to migrate - if (_.isEmpty(oldActiveClients)) { + if (oldActiveClients) { Log.info('[Migrate Onyx] Skipped migration RenameActiveClientsKey'); return resolve(); } Onyx.multiSet({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error activeClients2: null, - [ONYXKEYS.ACTIVE_CLIENTS]: oldActiveClients, + [ONYXKEYS.ACTIVE_CLIENTS]: oldActiveClients as OnyxEntry, }).then(() => { Log.info('[Migrate Onyx] Ran migration RenameActiveClientsKey'); resolve(); From 05f020c371399f9d4871287853b61c4bb973da19 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 15 Sep 2023 17:35:06 +0200 Subject: [PATCH 064/183] do not stop smart scan --- src/libs/TransactionUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 73ba6a8116ef..5fc6294aca91 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -143,7 +143,7 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep if (_.has(transactionChanges, 'category')) { updatedTransaction.modifiedCategory = transactionChanges.category; - shouldStopSmartscan = true; + shouldStopSmartscan = false; } if (shouldStopSmartscan && _.has(transaction, 'receipt') && !_.isEmpty(transaction.receipt) && lodashGet(transaction, 'receipt.state') !== CONST.IOU.RECEIPT_STATE.OPEN) { From 636924f0928a253fd6a7efb4d271eba125afdcbc Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 15 Sep 2023 19:07:05 +0200 Subject: [PATCH 065/183] handle a selected category alone --- src/libs/OptionsListUtils.js | 12 ++++++++++++ tests/unit/OptionsListUtilsTest.js | 23 ++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7629a1acc0a6..8027a30b34a1 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -666,6 +666,18 @@ function getCategoryListSections(categories, recentlyUsedCategories, selectedOpt const numberOfCategories = _.size(categoriesValues); let indexOffset = 0; + if (numberOfCategories === 0 && selectedOptions.length > 0) { + categorySections.push({ + // "Selected" section + title: '', + shouldShow: false, + indexOffset, + data: getCategoryOptionTree(selectedOptions, true), + }); + + return categorySections; + } + if (!_.isEmpty(searchInputValue)) { const searchCategories = _.filter(categoriesValues, (category) => category.name.toLowerCase().includes(searchInputValue.toLowerCase())); diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index f1a251e4e433..ef90c767175e 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -660,6 +660,7 @@ describe('OptionsListUtils', () => { const selectedOptions = [ { name: 'Medical', + enabled: true, }, ]; const smallCategoriesList = { @@ -817,7 +818,7 @@ describe('OptionsListUtils', () => { keyForList: 'Medical', searchText: 'Medical', tooltipText: 'Medical', - isDisabled: true, + isDisabled: false, }, ], }, @@ -1000,6 +1001,23 @@ describe('OptionsListUtils', () => { data: [], }, ]; + const emptyCategoriesList = {}; + const emptySelectedResultList = [ + { + title: '', + shouldShow: false, + indexOffset: 0, + data: [ + { + text: 'Medical', + keyForList: 'Medical', + searchText: 'Medical', + tooltipText: 'Medical', + isDisabled: false, + }, + ], + }, + ]; const smallResult = OptionsListUtils.getNewChatOptions(REPORTS, PERSONAL_DETAILS, [], emptySearch, [], [], false, false, true, smallCategoriesList); expect(smallResult.categoryOptions).toStrictEqual(smallResultList); @@ -1054,6 +1072,9 @@ describe('OptionsListUtils', () => { recentlyUsedCategories, ); expect(largeWrongSearchResult.categoryOptions).toStrictEqual(largeWrongSearchResultList); + + const emptyResult = OptionsListUtils.getNewChatOptions(REPORTS, PERSONAL_DETAILS, [], search, selectedOptions, [], false, false, true, emptyCategoriesList); + expect(emptyResult.categoryOptions).toStrictEqual(emptySelectedResultList); }); it('getCategoryOptionTree()', () => { From ad08c251b2a62e4ce7566fcbf832860044d47609 Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Sat, 16 Sep 2023 13:28:55 +0530 Subject: [PATCH 066/183] fix: removed check for safari only. Signed-off-by: Krishna Gupta --- src/libs/Navigation/OnyxTabNavigator.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libs/Navigation/OnyxTabNavigator.js b/src/libs/Navigation/OnyxTabNavigator.js index 56b820f5e05b..2782054497b0 100644 --- a/src/libs/Navigation/OnyxTabNavigator.js +++ b/src/libs/Navigation/OnyxTabNavigator.js @@ -4,8 +4,6 @@ import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import Tab from '../actions/Tab'; import ONYXKEYS from '../../ONYXKEYS'; -import * as Browser from '../Browser'; -import CONST from '../../CONST'; const propTypes = { /* ID of the tab component to be saved in onyx */ @@ -25,10 +23,6 @@ const defaultProps = { // eslint-disable-next-line rulesdir/no-inline-named-export export const TopTab = createMaterialTopTabNavigator(); -// Will set this for all platforms once issue below is fixed for native devices. -// https://github.com/Expensify/App/issues/27117 -const keyboardDismissModeProp = Browser.getBrowser() === CONST.BROWSER.SAFARI ? {keyboardDismissMode: 'none'} : {}; - // This takes all the same props as MaterialTopTabsNavigator: https://reactnavigation.org/docs/material-top-tab-navigator/#props, // except ID is now required, and it gets a `selectedTab` from Onyx function OnyxTabNavigator({id, selectedTab, children, ...rest}) { @@ -36,11 +30,10 @@ function OnyxTabNavigator({id, selectedTab, children, ...rest}) { { const state = event.data.state; From 812ea0568c93ef608a43f35013a0a9bef6eff8f5 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 18 Sep 2023 11:05:03 +0200 Subject: [PATCH 067/183] do not use modified category --- src/libs/TransactionUtils.js | 11 ++--------- src/libs/actions/IOU.js | 12 ------------ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/libs/TransactionUtils.js b/src/libs/TransactionUtils.js index 5fc6294aca91..7e768e97b837 100644 --- a/src/libs/TransactionUtils.js +++ b/src/libs/TransactionUtils.js @@ -142,8 +142,7 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep } if (_.has(transactionChanges, 'category')) { - updatedTransaction.modifiedCategory = transactionChanges.category; - shouldStopSmartscan = false; + updatedTransaction.category = transactionChanges.category; } if (shouldStopSmartscan && _.has(transaction, 'receipt') && !_.isEmpty(transaction.receipt) && lodashGet(transaction, 'receipt.state') !== CONST.IOU.RECEIPT_STATE.OPEN) { @@ -238,18 +237,12 @@ function getMerchant(transaction) { } /** - * Return the category from the transaction, return the modifiedCategory if present. + * Return the category from the transaction. This "category" field has no "modified" complement. * * @param {Object} transaction * @return {String} */ function getCategory(transaction) { - const modifiedCategory = lodashGet(transaction, 'modifiedCategory', null); - - if (!_.isNull(modifiedCategory)) { - return modifiedCategory; - } - return lodashGet(transaction, 'category', ''); } diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 16722d4dc70b..9f1d67d1db1d 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1160,18 +1160,6 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC }, ]; - // STEP 5: Use the modifiedCategory as a category on success - if (!_.isUndefined(updatedTransaction.modifiedCategory)) { - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - category: updatedTransaction.modifiedCategory, - modifiedCategory: null, - }, - }); - } - // STEP 6: Call the API endpoint const {created, amount, currency, comment, merchant, category} = ReportUtils.getTransactionDetails(updatedTransaction); API.write( From d9595b03823b72ea492ac1d0c5ac09f9c2284b1d Mon Sep 17 00:00:00 2001 From: Krishna Gupta Date: Mon, 18 Sep 2023 15:14:16 +0530 Subject: [PATCH 068/183] 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 530d25e8e12babe41c1b730ccadc8af2c27d8ef9 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 18 Sep 2023 17:33:33 +0700 Subject: [PATCH 069/183] new design when adding stop waypoint --- src/components/DistanceRequest.js | 6 +++++- src/pages/iou/WaypointEditor.js | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/components/DistanceRequest.js b/src/components/DistanceRequest.js index bf5a4cb9548b..151227d4ad38 100644 --- a/src/components/DistanceRequest.js +++ b/src/components/DistanceRequest.js @@ -260,7 +260,11 @@ function DistanceRequest({iou, iouType, report, transaction, mapboxAccessToken,