From 2da64c96f217b7090765a57161a224ed5d7b7644 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 5 Aug 2023 12:52:15 +0200 Subject: [PATCH 001/218] add ts type for theme --- src/styles/themes/ThemeColors.ts | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/styles/themes/ThemeColors.ts diff --git a/src/styles/themes/ThemeColors.ts b/src/styles/themes/ThemeColors.ts new file mode 100644 index 000000000000..19fcf76819b0 --- /dev/null +++ b/src/styles/themes/ThemeColors.ts @@ -0,0 +1,85 @@ +type HexColor = `#${string}`; +type Color = HexColor | `rgba(${number}, ${number}, ${number})` | `rgba(${number}, ${number}, ${number}, ${number})` | 'transparent'; + +type ThemeColors = { + appBG: Color; + splashBG: Color; + highlightBG: Color; + border: Color; + borderLighter: Color; + borderFocus: Color; + icon: Color; + iconMenu: Color; + iconHovered: Color; + iconSuccessFill: Color; + iconReversed: Color; + iconColorfulBackground: Color; + textSupporting: Color; + text: Color; + textColorfulBackground: Color; + link: Color; + linkHover: Color; + buttonDefaultBG: Color; + buttonHoveredBG: Color; + buttonPressedBG: Color; + danger: Color; + dangerHover: Color; + dangerPressed: Color; + warning: Color; + success: Color; + successHover: Color; + successPressed: Color; + transparent: Color; + signInPage: Color; + + // Additional keys + overlay: Color; + inverse: Color; + shadow: Color; + componentBG: Color; + hoverComponentBG: Color; + activeComponentBG: Color; + signInSidebar: Color; + sidebar: Color; + sidebarHover: Color; + heading: Color; + textLight: Color; + textDark: Color; + textReversed: Color; + textBackground: Color; + textMutedReversed: Color; + textError: Color; + offline: Color; + modalBackdrop: Color; + modalBackground: Color; + cardBG: Color; + cardBorder: Color; + spinner: Color; + unreadIndicator: Color; + placeholderText: Color; + heroCard: Color; + uploadPreviewActivityIndicator: Color; + dropUIBG: Color; + receiptDropUIBG: Color; + checkBox: Color; + pickerOptionsTextColor: Color; + imageCropBackgroundColor: Color; + fallbackIconColor: Color; + reactionActiveBackground: Color; + reactionActiveText: Color; + badgeAdHoc: Color; + badgeAdHocHover: Color; + mentionText: Color; + mentionBG: Color; + ourMentionText: Color; + ourMentionBG: Color; + tooltipSupportingText: Color; + tooltipPrimaryText: Color; + skeletonLHNIn: Color; + skeletonLHNOut: Color; + QRLogo: Color; + + PAGE_BACKGROUND_COLORS: Record; +}; + +export {type HexColor, type Color, type ThemeColors}; From 899d38e7e6d8c72bda02919a9d0d795c7ded3f44 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 5 Aug 2023 12:52:36 +0200 Subject: [PATCH 002/218] adapt light and dark theme to ts type --- src/styles/themes/{default.js => dark.ts} | 17 +++++++++-------- src/styles/themes/{light.js => light.ts} | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) rename src/styles/themes/{default.js => dark.ts} (88%) rename src/styles/themes/{light.js => light.ts} (95%) diff --git a/src/styles/themes/default.js b/src/styles/themes/dark.ts similarity index 88% rename from src/styles/themes/default.js rename to src/styles/themes/dark.ts index 8347a7b74cc8..c816693d41e5 100644 --- a/src/styles/themes/default.js +++ b/src/styles/themes/dark.ts @@ -1,8 +1,9 @@ /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; +import {HexColor, ThemeColors} from './ThemeColors'; -const darkTheme = { +const darkTheme: ThemeColors = { // Figma keys appBG: colors.darkAppBackground, splashBG: colors.green400, @@ -15,7 +16,7 @@ const darkTheme = { iconHovered: colors.darkPrimaryText, iconSuccessFill: colors.green400, iconReversed: colors.darkAppBackground, - iconColorfulBackground: `${colors.ivory}cc`, + iconColorfulBackground: `${colors.ivory as HexColor}cc`, textSupporting: colors.darkSupportingText, text: colors.darkPrimaryText, textColorfulBackground: colors.ivory, @@ -61,7 +62,7 @@ const darkTheme = { placeholderText: colors.darkIcons, heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.darkHighlightBackground, - dropUIBG: 'rgba(6,27,9,0.92)', + dropUIBG: 'rgba(6, 27, 9, 0.92)', receiptDropUIBG: 'rgba(3, 212, 124, 0.84)', checkBox: colors.green400, pickerOptionsTextColor: colors.darkPrimaryText, @@ -80,12 +81,12 @@ const darkTheme = { skeletonLHNIn: colors.darkBorders, skeletonLHNOut: colors.darkDefaultButton, QRLogo: colors.green400, -}; -darkTheme.PAGE_BACKGROUND_COLORS = { - [SCREENS.HOME]: darkTheme.sidebar, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + PAGE_BACKGROUND_COLORS: { + [SCREENS.HOME]: colors.darkHighlightBackground, + [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + }, }; export default darkTheme; diff --git a/src/styles/themes/light.js b/src/styles/themes/light.ts similarity index 95% rename from src/styles/themes/light.js rename to src/styles/themes/light.ts index 82717f35f124..c320c14ac6b8 100644 --- a/src/styles/themes/light.js +++ b/src/styles/themes/light.ts @@ -79,11 +79,11 @@ const lightTheme = { skeletonLHNIn: colors.lightBorders, skeletonLHNOut: colors.lightDefaultButtonPressed, QRLogo: colors.green400, -}; -lightTheme.PAGE_BACKGROUND_COLORS = { - [SCREENS.HOME]: lightTheme.sidebar, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + PAGE_BACKGROUND_COLORS: { + [SCREENS.HOME]: colors.lightHighlightBackground, + [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + }, }; export default lightTheme; From e089833582ba9dbca8b50f99fe175b4769ec0981 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 5 Aug 2023 12:53:10 +0200 Subject: [PATCH 003/218] use light theme in ThemeProvider --- src/styles/themes/ThemeContext.js | 4 ++-- src/styles/themes/ThemeProvider.js | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/styles/themes/ThemeContext.js b/src/styles/themes/ThemeContext.js index 30d476c22d9c..e75c48bcab1e 100644 --- a/src/styles/themes/ThemeContext.js +++ b/src/styles/themes/ThemeContext.js @@ -1,6 +1,6 @@ import React from 'react'; -import defaultColors from './default'; +import darkTheme from './dark'; -const ThemeContext = React.createContext(defaultColors); +const ThemeContext = React.createContext(darkTheme); export default ThemeContext; diff --git a/src/styles/themes/ThemeProvider.js b/src/styles/themes/ThemeProvider.js index f4601712a0bc..2dd92e8be729 100644 --- a/src/styles/themes/ThemeProvider.js +++ b/src/styles/themes/ThemeProvider.js @@ -4,9 +4,8 @@ import PropTypes from 'prop-types'; import ThemeContext from './ThemeContext'; import useThemePreference from './useThemePreference'; import CONST from '../../CONST'; - -// Going to eventually import the light theme here too -import darkTheme from './default'; +import darkTheme from './dark'; +import lightTheme from './light'; const propTypes = { /** Rendered child component */ @@ -16,7 +15,7 @@ const propTypes = { function ThemeProvider(props) { const themePreference = useThemePreference(); - const theme = useMemo(() => (themePreference === CONST.THEME.LIGHT ? /* TODO: replace with light theme */ darkTheme : darkTheme), [themePreference]); + const theme = useMemo(() => (themePreference === CONST.THEME.LIGHT ? lightTheme : darkTheme), [themePreference]); return {props.children}; } From b32f538a84fe6b4a9101d39cb05ff274f5c2d623 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 5 Aug 2023 12:53:40 +0200 Subject: [PATCH 004/218] adapt colors to new ts type --- src/styles/{colors.js => colors.ts} | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) rename src/styles/{colors.js => colors.ts} (95%) diff --git a/src/styles/colors.js b/src/styles/colors.ts similarity index 95% rename from src/styles/colors.js rename to src/styles/colors.ts index 9ac3226a1b80..b17f7fb4cb96 100644 --- a/src/styles/colors.js +++ b/src/styles/colors.ts @@ -1,7 +1,9 @@ +import {Color} from './themes/ThemeColors'; + /** * DO NOT import colors.js into files. Use ../themes/default.js instead. */ -export default { +const colors: Record = { black: '#000000', white: '#FFFFFF', ivory: '#fffaf0', @@ -91,3 +93,5 @@ export default { ice700: '#28736D', ice800: '#134038', }; + +export default colors; From 21787ba652659da893e3d2b3267aaf986bfc86e3 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 5 Aug 2023 12:53:54 +0200 Subject: [PATCH 005/218] change usage of "default.js" to "dark.js" --- src/components/AddPlaidBankAccount.js | 2 +- src/components/AddressSearch/index.js | 2 +- src/components/AttachmentCarousel/index.js | 2 +- src/components/AttachmentModal.js | 2 +- src/components/AttachmentView.js | 2 +- src/components/Avatar.js | 2 +- src/components/AvatarCropModal/AvatarCropModal.js | 2 +- src/components/AvatarWithDisplayName.js | 2 +- src/components/AvatarWithImagePicker.js | 2 +- src/components/BlockingViews/BlockingView.js | 2 +- src/components/Button/index.js | 2 +- src/components/ButtonWithDropdownMenu.js | 2 +- src/components/Checkbox.js | 2 +- src/components/Composer/index.android.js | 2 +- src/components/Composer/index.ios.js | 2 +- src/components/Composer/index.js | 2 +- src/components/CurrentUserPersonalDetailsSkeletonView/index.js | 2 +- src/components/CustomStatusBar/index.js | 2 +- src/components/DatePicker/index.ios.js | 2 +- src/components/EmojiPicker/CategoryShortcutButton.js | 2 +- src/components/ExpensifyWordmark.js | 2 +- src/components/FloatingActionButton.js | 2 +- src/components/FullscreenLoadingIndicator.js | 2 +- src/components/GrowlNotification/index.js | 2 +- .../HTMLEngineProvider/HTMLRenderers/EditedRenderer.js | 2 +- src/components/Icon/index.js | 2 +- src/components/IllustratedHeaderPageLayout.js | 2 +- src/components/Indicator.js | 2 +- src/components/InlineSystemMessage.js | 2 +- src/components/LHNOptionsList/OptionRowLHN.js | 2 +- src/components/LocalePicker.js | 2 +- src/components/MenuItem.js | 2 +- src/components/Modal/BaseModal.js | 2 +- src/components/Modal/index.web.js | 2 +- src/components/MoneyRequestConfirmationList.js | 2 +- src/components/MoneyRequestDetails.js | 2 +- src/components/MultipleAvatars.js | 2 +- src/components/Onfido/BaseOnfidoWeb.js | 2 +- src/components/OptionRow.js | 2 +- src/components/OptionsListSkeletonView.js | 2 +- src/components/Picker/BasePicker.js | 2 +- src/components/PinButton.js | 2 +- src/components/QRCode/index.js | 2 +- src/components/QRShare/index.js | 2 +- src/components/ReportActionItem/IOUPreview.js | 2 +- src/components/ReportActionItem/ReportPreview.js | 2 +- src/components/ReportActionsSkeletonView/SkeletonViewLines.js | 2 +- src/components/ReportHeaderSkeletonView.js | 2 +- src/components/RoomHeaderAvatars.js | 2 +- src/components/SelectCircle.js | 2 +- src/components/SelectionListRadio/RadioListItem.js | 2 +- src/components/SubscriptAvatar.js | 2 +- src/components/TabSelector/TabSelectorItem.js | 2 +- src/components/Text.js | 2 +- src/components/TextInput/BaseTextInput.js | 2 +- .../VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js | 2 +- .../createResponsiveStackNavigator/ThreePaneView.js | 2 +- src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js | 2 +- src/libs/Navigation/NavigationRoot.js | 2 +- src/pages/ErrorPage/GenericErrorPage.js | 2 +- src/pages/home/report/FloatingMessageCounter/index.js | 2 +- src/pages/home/report/LinkPreviewer.js | 2 +- src/pages/home/report/ReportActionCompose.js | 2 +- src/pages/home/report/ReportActionItemFragment.js | 2 +- src/pages/home/report/ReportActionItemMessageEdit.js | 2 +- src/pages/home/sidebar/SidebarLinks.js | 2 +- src/pages/home/sidebar/SidebarScreen/index.js | 2 +- src/pages/iou/IOUCurrencySelection.js | 2 +- src/pages/iou/ReceiptSelector/index.native.js | 2 +- src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js | 2 +- src/pages/settings/Preferences/PreferencesPage.js | 2 +- src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js | 2 +- .../Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- src/pages/settings/Report/NotificationPreferencePage.js | 2 +- src/pages/settings/Report/WriteCapabilityPage.js | 2 +- src/pages/settings/Security/TwoFactorAuth/CodesPage.js | 2 +- src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js | 2 +- src/pages/signin/SignInPageLayout/Footer.js | 2 +- src/pages/signin/SignInPageLayout/index.js | 2 +- src/pages/signin/Socials.js | 2 +- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- src/pages/workspace/WorkspacesListPage.js | 2 +- src/pages/workspace/reimburse/WorkspaceReimburseSection.js | 2 +- src/stories/Composer.stories.js | 2 +- src/stories/PopoverMenu.stories.js | 2 +- src/styles/StyleUtils.js | 2 +- src/styles/addOutlineWidth/index.js | 2 +- src/styles/getModalStyles/getBaseModalStyles.js | 2 +- src/styles/getReportActionContextMenuStyles.js | 2 +- src/styles/getTooltipStyles.js | 2 +- src/styles/styles.js | 2 +- 91 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index ff97c9be24a6..d1d8c42abffa 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -9,7 +9,7 @@ import PlaidLink from './PlaidLink'; import * as BankAccounts from '../libs/actions/BankAccounts'; import ONYXKEYS from '../ONYXKEYS'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import Picker from './Picker'; diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index e8a41ec35435..a29fd4002077 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -6,7 +6,7 @@ import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete' import lodashGet from 'lodash/get'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import TextInput from '../TextInput'; import * as ApiUtils from '../../libs/ApiUtils'; import * as GooglePlacesUtils from '../../libs/GooglePlacesUtils'; diff --git a/src/components/AttachmentCarousel/index.js b/src/components/AttachmentCarousel/index.js index 3f2524a2992e..b15c8a0b7c7e 100644 --- a/src/components/AttachmentCarousel/index.js +++ b/src/components/AttachmentCarousel/index.js @@ -4,7 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import * as Expensicons from '../Icon/Expensicons'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import CarouselActions from './CarouselActions'; import Button from '../Button'; import AttachmentView from '../AttachmentView'; diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 1b87799d4f5b..1e768d2eefd3 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -12,7 +12,7 @@ import AttachmentCarousel from './AttachmentCarousel'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; import * as FileUtils from '../libs/fileDownload/FileUtils'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import compose from '../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import Button from './Button'; diff --git a/src/components/AttachmentView.js b/src/components/AttachmentView.js index d880ac9b9076..7cf77c08e5e2 100755 --- a/src/components/AttachmentView.js +++ b/src/components/AttachmentView.js @@ -12,7 +12,7 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize'; import compose from '../libs/compose'; import Text from './Text'; import Tooltip from './Tooltip'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import variables from '../styles/variables'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 8dcf7f08c1d1..f4502ddb6a03 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import _ from 'underscore'; import stylePropTypes from '../styles/stylePropTypes'; import Icon from './Icon'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import CONST from '../CONST'; import * as StyleUtils from '../styles/StyleUtils'; import * as Expensicons from './Icon/Expensicons'; diff --git a/src/components/AvatarCropModal/AvatarCropModal.js b/src/components/AvatarCropModal/AvatarCropModal.js index 99262bf12938..f0e7b4ed74be 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.js +++ b/src/components/AvatarCropModal/AvatarCropModal.js @@ -6,7 +6,7 @@ import {runOnUI, interpolate, useAnimatedGestureHandler, useSharedValue, useWork import CONST from '../../CONST'; import compose from '../../libs/compose'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import Button from '../Button'; import HeaderWithBackButton from '../HeaderWithBackButton'; import Icon from '../Icon'; diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 0f1300ebf03d..52ae459bca7a 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -8,7 +8,7 @@ import participantPropTypes from './participantPropTypes'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import SubscriptAvatar from './SubscriptAvatar'; import * as ReportUtils from '../libs/ReportUtils'; import MultipleAvatars from './MultipleAvatars'; diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index fcbfe4f4c4c4..8452afcc0616 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -8,7 +8,7 @@ import Icon from './Icon'; import PopoverMenu from './PopoverMenu'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import AttachmentPicker from './AttachmentPicker'; import AvatarCropModal from './AvatarCropModal/AvatarCropModal'; import OfflineWithFeedback from './OfflineWithFeedback'; diff --git a/src/components/BlockingViews/BlockingView.js b/src/components/BlockingViews/BlockingView.js index d02fa55a6434..a4af9754bbe5 100644 --- a/src/components/BlockingViews/BlockingView.js +++ b/src/components/BlockingViews/BlockingView.js @@ -5,7 +5,7 @@ import styles from '../../styles/styles'; import variables from '../../styles/variables'; import Icon from '../Icon'; import Text from '../Text'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import TextLink from '../TextLink'; import Navigation from '../../libs/Navigation/Navigation'; import AutoEmailLink from '../AutoEmailLink'; diff --git a/src/components/Button/index.js b/src/components/Button/index.js index a850a43d2fb0..03e98b56d5c6 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import Text from '../Text'; import KeyboardShortcut from '../../libs/KeyboardShortcut'; import Icon from '../Icon'; diff --git a/src/components/ButtonWithDropdownMenu.js b/src/components/ButtonWithDropdownMenu.js index 1396ab601330..eb1729a45614 100644 --- a/src/components/ButtonWithDropdownMenu.js +++ b/src/components/ButtonWithDropdownMenu.js @@ -8,7 +8,7 @@ import Button from './Button'; import PopoverMenu from './PopoverMenu'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import CONST from '../CONST'; const propTypes = { diff --git a/src/components/Checkbox.js b/src/components/Checkbox.js index 86b6e05d5ed7..6150e5134e19 100644 --- a/src/components/Checkbox.js +++ b/src/components/Checkbox.js @@ -2,7 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import stylePropTypes from '../styles/stylePropTypes'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; diff --git a/src/components/Composer/index.android.js b/src/components/Composer/index.android.js index d0805cbcc7c3..c252d3dcfa62 100644 --- a/src/components/Composer/index.android.js +++ b/src/components/Composer/index.android.js @@ -3,7 +3,7 @@ import {StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import RNTextInput from '../RNTextInput'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import * as ComposerUtils from '../../libs/ComposerUtils'; const propTypes = { diff --git a/src/components/Composer/index.ios.js b/src/components/Composer/index.ios.js index c0a3859e6d01..a0e372403310 100644 --- a/src/components/Composer/index.ios.js +++ b/src/components/Composer/index.ios.js @@ -3,7 +3,7 @@ import {StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import RNTextInput from '../RNTextInput'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import * as ComposerUtils from '../../libs/ComposerUtils'; const propTypes = { diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index d32246529b6c..4b8144745119 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -6,7 +6,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import RNTextInput from '../RNTextInput'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Growl from '../../libs/Growl'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import updateIsFullComposerAvailable from '../../libs/ComposerUtils/updateIsFullComposerAvailable'; import * as ComposerUtils from '../../libs/ComposerUtils'; import * as Browser from '../../libs/Browser'; diff --git a/src/components/CurrentUserPersonalDetailsSkeletonView/index.js b/src/components/CurrentUserPersonalDetailsSkeletonView/index.js index 6e6c46e971c0..8e63be848e91 100644 --- a/src/components/CurrentUserPersonalDetailsSkeletonView/index.js +++ b/src/components/CurrentUserPersonalDetailsSkeletonView/index.js @@ -5,7 +5,7 @@ import {Circle, Rect} from 'react-native-svg'; import {View} from 'react-native'; import * as StyleUtils from '../../styles/StyleUtils'; import CONST from '../../CONST'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import variables from '../../styles/variables'; import styles from '../../styles/styles'; diff --git a/src/components/CustomStatusBar/index.js b/src/components/CustomStatusBar/index.js index 76752cb549e1..4f77691046bd 100644 --- a/src/components/CustomStatusBar/index.js +++ b/src/components/CustomStatusBar/index.js @@ -1,6 +1,6 @@ import React, {useEffect} from 'react'; import StatusBar from '../../libs/StatusBar'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; function CustomStatusBar() { useEffect(() => { diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index 5d87636a9365..410376a387dd 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -10,7 +10,7 @@ import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Popover from '../Popover'; import CONST from '../../CONST'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import {propTypes, defaultProps} from './datepickerPropTypes'; import withKeyboardState, {keyboardStatePropTypes} from '../withKeyboardState'; diff --git a/src/components/EmojiPicker/CategoryShortcutButton.js b/src/components/EmojiPicker/CategoryShortcutButton.js index a7658ae0542d..ac7f1991ec6a 100644 --- a/src/components/EmojiPicker/CategoryShortcutButton.js +++ b/src/components/EmojiPicker/CategoryShortcutButton.js @@ -7,7 +7,7 @@ import variables from '../../styles/variables'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import getButtonState from '../../libs/getButtonState'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; import CONST from '../../CONST'; diff --git a/src/components/ExpensifyWordmark.js b/src/components/ExpensifyWordmark.js index dde792e87e22..25ff571a7c01 100644 --- a/src/components/ExpensifyWordmark.js +++ b/src/components/ExpensifyWordmark.js @@ -8,7 +8,7 @@ import StagingLogo from '../../assets/images/expensify-logo--staging.svg'; import AdHocLogo from '../../assets/images/expensify-logo--adhoc.svg'; import CONST from '../CONST'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; import variables from '../styles/variables'; diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js index 706bad59f7b7..4a2db6e2bcfb 100644 --- a/src/components/FloatingActionButton.js +++ b/src/components/FloatingActionButton.js @@ -5,7 +5,7 @@ import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import Tooltip from './Tooltip'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; diff --git a/src/components/FullscreenLoadingIndicator.js b/src/components/FullscreenLoadingIndicator.js index 5c212b6dc29e..96c1246f33c5 100644 --- a/src/components/FullscreenLoadingIndicator.js +++ b/src/components/FullscreenLoadingIndicator.js @@ -2,7 +2,7 @@ import _ from 'underscore'; import React from 'react'; import {ActivityIndicator, StyleSheet, View} from 'react-native'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import stylePropTypes from '../styles/stylePropTypes'; const propTypes = { diff --git a/src/components/GrowlNotification/index.js b/src/components/GrowlNotification/index.js index 70cadd5efd8e..750063847ead 100644 --- a/src/components/GrowlNotification/index.js +++ b/src/components/GrowlNotification/index.js @@ -1,7 +1,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {Directions, FlingGestureHandler, State} from 'react-native-gesture-handler'; import {View, Animated} from 'react-native'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import Text from '../Text'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js index d91510c3ec6a..3f5ff9a72dc2 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js @@ -4,7 +4,7 @@ import htmlRendererPropTypes from './htmlRendererPropTypes'; import withLocalize, {withLocalizePropTypes} from '../../withLocalize'; import Text from '../../Text'; import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import styles from '../../../styles/styles'; import editedLabelStyles from '../../../styles/editedLabelStyles'; diff --git a/src/components/Icon/index.js b/src/components/Icon/index.js index 8c6559451215..044baee944b3 100644 --- a/src/components/Icon/index.js +++ b/src/components/Icon/index.js @@ -2,7 +2,7 @@ import React, {PureComponent} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import variables from '../../styles/variables'; import * as StyleUtils from '../../styles/StyleUtils'; import IconWrapperStyles from './IconWrapperStyles'; diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js index 7fc340426d69..d1563c500671 100644 --- a/src/components/IllustratedHeaderPageLayout.js +++ b/src/components/IllustratedHeaderPageLayout.js @@ -7,7 +7,7 @@ import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBack import HeaderWithBackButton from './HeaderWithBackButton'; import ScreenWrapper from './ScreenWrapper'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import * as StyleUtils from '../styles/StyleUtils'; import useWindowDimensions from '../hooks/useWindowDimensions'; import FixedFooter from './FixedFooter'; diff --git a/src/components/Indicator.js b/src/components/Indicator.js index 765d79e156af..cd66de22f57d 100644 --- a/src/components/Indicator.js +++ b/src/components/Indicator.js @@ -15,7 +15,7 @@ import * as PolicyUtils from '../libs/PolicyUtils'; import * as PaymentMethods from '../libs/actions/PaymentMethods'; import * as ReimbursementAccountProps from '../pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as UserUtils from '../libs/UserUtils'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; const propTypes = { /* Onyx Props */ diff --git a/src/components/InlineSystemMessage.js b/src/components/InlineSystemMessage.js index a6866fb5a887..ea21c5f65352 100644 --- a/src/components/InlineSystemMessage.js +++ b/src/components/InlineSystemMessage.js @@ -2,7 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; -import theme from '../styles/themes/default'; +import theme from '../styles/themes/dark'; import Text from './Text'; import * as Expensicons from './Icon/Expensicons'; import Icon from './Icon'; diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index e17cf71e5d06..51045cd8876f 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -14,7 +14,7 @@ import colors from '../../styles/colors'; import Text from '../Text'; import SubscriptAvatar from '../SubscriptAvatar'; import CONST from '../../CONST'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import OfflineWithFeedback from '../OfflineWithFeedback'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; import * as ReportActionContextMenu from '../../pages/home/report/ContextMenu/ReportActionContextMenu'; diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js index 532c29535e50..af191080a4ca 100644 --- a/src/components/LocalePicker.js +++ b/src/components/LocalePicker.js @@ -9,7 +9,7 @@ import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import Picker from './Picker'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; const propTypes = { /** Indicates which locale the user currently has selected */ diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index c280a75a8ef3..b470b11171f8 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -3,7 +3,7 @@ import React from 'react'; import {View} from 'react-native'; import Text from './Text'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import * as StyleUtils from '../styles/StyleUtils'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 6d5bd5390416..a9116faa3460 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -5,7 +5,7 @@ import ReactNativeModal from 'react-native-modal'; import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './modalPropTypes'; import * as Modal from '../../libs/actions/Modal'; import getModalStyles from '../../styles/getModalStyles'; diff --git a/src/components/Modal/index.web.js b/src/components/Modal/index.web.js index 065b3a9f210f..8fc5a4ee22fe 100644 --- a/src/components/Modal/index.web.js +++ b/src/components/Modal/index.web.js @@ -3,7 +3,7 @@ import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; import {propTypes, defaultProps} from './modalPropTypes'; import * as StyleUtils from '../../styles/StyleUtils'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import StatusBar from '../../libs/StatusBar'; import CONST from '../../CONST'; diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index e421dae4e37e..76620c7a0863 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -23,7 +23,7 @@ import optionPropTypes from './optionPropTypes'; import * as CurrencyUtils from '../libs/CurrencyUtils'; import Button from './Button'; import * as Expensicons from './Icon/Expensicons'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import Image from './Image'; import ReceiptHTML from '../../assets/images/receipt-html.png'; import ReceiptDoc from '../../assets/images/receipt-doc.png'; diff --git a/src/components/MoneyRequestDetails.js b/src/components/MoneyRequestDetails.js index a690c31c000c..fe6d7afdfe7a 100644 --- a/src/components/MoneyRequestDetails.js +++ b/src/components/MoneyRequestDetails.js @@ -11,7 +11,7 @@ import Text from './Text'; import participantPropTypes from './participantPropTypes'; import Avatar from './Avatar'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import CONST from '../CONST'; import withWindowDimensions from './withWindowDimensions'; import compose from '../libs/compose'; diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index ceb1c5371c7e..c89613c90e19 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -6,7 +6,7 @@ import styles from '../styles/styles'; import Avatar from './Avatar'; import Tooltip from './Tooltip'; import Text from './Text'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import * as StyleUtils from '../styles/StyleUtils'; import CONST from '../CONST'; import variables from '../styles/variables'; diff --git a/src/components/Onfido/BaseOnfidoWeb.js b/src/components/Onfido/BaseOnfidoWeb.js index 394996331d5e..54fb2abba2bf 100644 --- a/src/components/Onfido/BaseOnfidoWeb.js +++ b/src/components/Onfido/BaseOnfidoWeb.js @@ -6,7 +6,7 @@ import * as OnfidoSDK from 'onfido-sdk-ui'; import onfidoPropTypes from './onfidoPropTypes'; import CONST from '../../CONST'; import variables from '../../styles/variables'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import fontWeightBold from '../../styles/fontWeight/bold'; import fontFamily from '../../styles/fontFamily'; import Log from '../../libs/Log'; diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js index adaa4457bbd9..d341710945d7 100644 --- a/src/components/OptionRow.js +++ b/src/components/OptionRow.js @@ -11,7 +11,7 @@ import * as Expensicons from './Icon/Expensicons'; import MultipleAvatars from './MultipleAvatars'; import Hoverable from './Hoverable'; import DisplayNames from './DisplayNames'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import Text from './Text'; import SelectCircle from './SelectCircle'; diff --git a/src/components/OptionsListSkeletonView.js b/src/components/OptionsListSkeletonView.js index 15c66affe84d..1cf6a97f8c97 100644 --- a/src/components/OptionsListSkeletonView.js +++ b/src/components/OptionsListSkeletonView.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import {Rect, Circle} from 'react-native-svg'; import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../CONST'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import styles from '../styles/styles'; const propTypes = { diff --git a/src/components/Picker/BasePicker.js b/src/components/Picker/BasePicker.js index 173b863edfcc..c64aad3d0c1a 100644 --- a/src/components/Picker/BasePicker.js +++ b/src/components/Picker/BasePicker.js @@ -8,7 +8,7 @@ import * as Expensicons from '../Icon/Expensicons'; import FormHelpMessage from '../FormHelpMessage'; import Text from '../Text'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import {ScrollContext} from '../ScrollViewWithContext'; const propTypes = { diff --git a/src/components/PinButton.js b/src/components/PinButton.js index 84ad6e22f50b..2074996cfb7b 100644 --- a/src/components/PinButton.js +++ b/src/components/PinButton.js @@ -1,6 +1,6 @@ import React from 'react'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import Icon from './Icon'; import Tooltip from './Tooltip'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; diff --git a/src/components/QRCode/index.js b/src/components/QRCode/index.js index f27cf28066ef..7bc0829d399d 100644 --- a/src/components/QRCode/index.js +++ b/src/components/QRCode/index.js @@ -1,7 +1,7 @@ import React from 'react'; import QRCodeLibrary from 'react-native-qrcode-svg'; import PropTypes from 'prop-types'; -import defaultTheme from '../../styles/themes/default'; +import defaultTheme from '../../styles/themes/dark'; import CONST from '../../CONST'; const propTypes = { diff --git a/src/components/QRShare/index.js b/src/components/QRShare/index.js index d96024ad1046..580151554d63 100644 --- a/src/components/QRShare/index.js +++ b/src/components/QRShare/index.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; -import defaultTheme from '../../styles/themes/default'; +import defaultTheme from '../../styles/themes/dark'; import styles from '../../styles/styles'; import Text from '../Text'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItem/IOUPreview.js index 85a0b22ac327..fe207476627b 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -10,7 +10,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import MultipleAvatars from '../MultipleAvatars'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import * as Report from '../../libs/actions/Report'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import Icon from '../Icon'; import CONST from '../../CONST'; import * as Expensicons from '../Icon/Expensicons'; diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index d5d85df5e7ee..89b7616d081a 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -23,7 +23,7 @@ import SettlementButton from '../SettlementButton'; import * as IOU from '../../libs/actions/IOU'; import refPropTypes from '../refPropTypes'; import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import reportPropTypes from '../../pages/reportPropTypes'; const propTypes = { diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js index ddaa46e0b731..f8dcaf4de34c 100644 --- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js +++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import {Rect, Circle} from 'react-native-svg'; import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../../CONST'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import styles from '../../styles/styles'; const propTypes = { diff --git a/src/components/ReportHeaderSkeletonView.js b/src/components/ReportHeaderSkeletonView.js index 5f2d5379419d..c6e712417577 100644 --- a/src/components/ReportHeaderSkeletonView.js +++ b/src/components/ReportHeaderSkeletonView.js @@ -8,7 +8,7 @@ import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import variables from '../styles/variables'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; diff --git a/src/components/RoomHeaderAvatars.js b/src/components/RoomHeaderAvatars.js index 6f78e6ace66d..cb18973e287c 100644 --- a/src/components/RoomHeaderAvatars.js +++ b/src/components/RoomHeaderAvatars.js @@ -6,7 +6,7 @@ import styles from '../styles/styles'; import Text from './Text'; import CONST from '../CONST'; import Avatar from './Avatar'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import * as StyleUtils from '../styles/StyleUtils'; import avatarPropTypes from './avatarPropTypes'; diff --git a/src/components/SelectCircle.js b/src/components/SelectCircle.js index 93cf285eab59..60e449479d11 100644 --- a/src/components/SelectCircle.js +++ b/src/components/SelectCircle.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import styles from '../styles/styles'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; const propTypes = { /** Should we show the checkmark inside the circle */ diff --git a/src/components/SelectionListRadio/RadioListItem.js b/src/components/SelectionListRadio/RadioListItem.js index c5c4b3aeaf2c..41928c50e05d 100644 --- a/src/components/SelectionListRadio/RadioListItem.js +++ b/src/components/SelectionListRadio/RadioListItem.js @@ -6,7 +6,7 @@ import styles from '../../styles/styles'; import Text from '../Text'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import {radioListItemPropTypes} from './selectionListRadioPropTypes'; const propTypes = { diff --git a/src/components/SubscriptAvatar.js b/src/components/SubscriptAvatar.js index 05202e720bd4..5ca496025f1f 100644 --- a/src/components/SubscriptAvatar.js +++ b/src/components/SubscriptAvatar.js @@ -4,7 +4,7 @@ import {View} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import Avatar from './Avatar'; import CONST from '../CONST'; import * as StyleUtils from '../styles/StyleUtils'; diff --git a/src/components/TabSelector/TabSelectorItem.js b/src/components/TabSelector/TabSelectorItem.js index cea59bc2ee65..f96d9f2d621e 100644 --- a/src/components/TabSelector/TabSelectorItem.js +++ b/src/components/TabSelector/TabSelectorItem.js @@ -2,7 +2,7 @@ import {Text} from 'react-native'; import React from 'react'; import PropTypes from 'prop-types'; import Icon from '../Icon'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import styles from '../../styles/styles'; import PressableWithFeedback from '../Pressable/PressableWithFeedback'; diff --git a/src/components/Text.js b/src/components/Text.js index 83b6be8fffb0..3c44183cd17b 100644 --- a/src/components/Text.js +++ b/src/components/Text.js @@ -4,7 +4,7 @@ import _ from 'underscore'; // eslint-disable-next-line no-restricted-imports import {Text as RNText} from 'react-native'; import fontFamily from '../styles/fontFamily'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import variables from '../styles/variables'; const propTypes = { diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 68c09e3a7f82..b075672d492e 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -5,7 +5,7 @@ import Str from 'expensify-common/lib/str'; import RNTextInput from '../RNTextInput'; import TextInputLabel from './TextInputLabel'; import * as baseTextInputPropTypes from './baseTextInputPropTypes'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import styles from '../../styles/styles'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; diff --git a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js index 4c0e2b551382..23ad19f53d37 100755 --- a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js +++ b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js @@ -10,7 +10,7 @@ import ZoomIcon from '../../../assets/images/zoom-icon.svg'; import GoogleMeetIcon from '../../../assets/images/google-meet.svg'; import CONST from '../../CONST'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import compose from '../../libs/compose'; diff --git a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js index 2f9a899191bf..bc6ba2cfb3cd 100644 --- a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js +++ b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js @@ -3,7 +3,7 @@ import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import SCREENS from '../../../../SCREENS'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; import NAVIGATORS from '../../../../NAVIGATORS'; import * as StyleUtils from '../../../../styles/StyleUtils'; import {withNavigationPropTypes} from '../../../../components/withNavigation'; diff --git a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js index d2de1ba23a01..63aca125dc27 100644 --- a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js +++ b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js @@ -1,7 +1,7 @@ import {Animated} from 'react-native'; import variables from '../../../styles/variables'; import getCardStyles from '../../../styles/cardStyles'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; export default (isSmallScreenWidth, isFullScreenModal, {current: {progress}, inverted, layouts: {screen}}) => { const translateX = Animated.multiply( diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 23c320eb991c..c22c459b54ab 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -6,7 +6,7 @@ import {useSharedValue, useAnimatedReaction, interpolateColor, withTiming, withD import Navigation, {navigationRef} from './Navigation'; import linkingConfig from './linkingConfig'; import AppNavigator from './AppNavigator'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import Log from '../Log'; import StatusBar from '../StatusBar'; import useCurrentReportID from '../../hooks/useCurrentReportID'; diff --git a/src/pages/ErrorPage/GenericErrorPage.js b/src/pages/ErrorPage/GenericErrorPage.js index 3ff3bc686419..05dddf273547 100644 --- a/src/pages/ErrorPage/GenericErrorPage.js +++ b/src/pages/ErrorPage/GenericErrorPage.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import Icon from '../../components/Icon'; -import defaultTheme from '../../styles/themes/default'; +import defaultTheme from '../../styles/themes/dark'; import * as Expensicons from '../../components/Icon/Expensicons'; import Text from '../../components/Text'; import Button from '../../components/Button'; diff --git a/src/pages/home/report/FloatingMessageCounter/index.js b/src/pages/home/report/FloatingMessageCounter/index.js index 73fe02df129b..4178502a1368 100644 --- a/src/pages/home/report/FloatingMessageCounter/index.js +++ b/src/pages/home/report/FloatingMessageCounter/index.js @@ -6,7 +6,7 @@ import Button from '../../../../components/Button'; import Text from '../../../../components/Text'; import Icon from '../../../../components/Icon'; import * as Expensicons from '../../../../components/Icon/Expensicons'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; import useLocalize from '../../../../hooks/useLocalize'; import FloatingMessageCounterContainer from './FloatingMessageCounterContainer'; diff --git a/src/pages/home/report/LinkPreviewer.js b/src/pages/home/report/LinkPreviewer.js index 4fcbb0dc0569..3859299ea1a7 100644 --- a/src/pages/home/report/LinkPreviewer.js +++ b/src/pages/home/report/LinkPreviewer.js @@ -8,7 +8,7 @@ import TextLink from '../../../components/TextLink'; import * as StyleUtils from '../../../styles/StyleUtils'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; const MAX_IMAGE_HEIGHT = 180; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 1146e3b382f5..e8d26edb40e5 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -7,7 +7,7 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import Composer from '../../../components/Composer'; import ONYXKEYS from '../../../ONYXKEYS'; import Icon from '../../../components/Icon'; diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 009c1118400b..6863e75ab894 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -5,7 +5,7 @@ import Str from 'expensify-common/lib/str'; import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import RenderHTML from '../../../components/RenderHTML'; import Text from '../../../components/Text'; import * as EmojiUtils from '../../../libs/EmojiUtils'; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 54c5fec4533e..5c7e71d0a877 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -7,7 +7,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import reportActionPropTypes from './reportActionPropTypes'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import * as StyleUtils from '../../../styles/StyleUtils'; import containerComposeStyles from '../../../styles/containerComposeStyles'; import Composer from '../../../components/Composer'; diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index 132b767a7f70..dc917b1f6c6a 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -24,7 +24,7 @@ import LHNOptionsList from '../../../components/LHNOptionsList/LHNOptionsList'; import SidebarUtils from '../../../libs/SidebarUtils'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; import Header from '../../../components/Header'; -import defaultTheme from '../../../styles/themes/default'; +import defaultTheme from '../../../styles/themes/dark'; import OptionsListSkeletonView from '../../../components/OptionsListSkeletonView'; import variables from '../../../styles/variables'; import LogoComponent from '../../../../assets/images/expensify-wordmark.svg'; diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 705d9d1e2d08..20f7e7098f1c 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -7,7 +7,7 @@ import FloatingActionButtonAndPopover from './FloatingActionButtonAndPopover'; import FreezeWrapper from '../../../../libs/Navigation/FreezeWrapper'; import withWindowDimensions from '../../../../components/withWindowDimensions'; import StatusBar from '../../../../libs/StatusBar'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; function SidebarScreen(props) { const popoverModal = useRef(null); diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index 44a7fba5d487..b46919127fb3 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -15,7 +15,7 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize import {withNetwork} from '../../components/OnyxProvider'; import * as CurrencyUtils from '../../libs/CurrencyUtils'; import ROUTES from '../../ROUTES'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import * as Expensicons from '../../components/Icon/Expensicons'; import reportPropTypes from '../reportPropTypes'; import * as ReportUtils from '../../libs/ReportUtils'; diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 7eeab6e493bd..06f692fa1b33 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -13,7 +13,7 @@ import styles from '../../../styles/styles'; import Shutter from '../../../../assets/images/shutter.svg'; import Hand from '../../../../assets/images/hand.svg'; import * as IOU from '../../../libs/actions/IOU'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import reportPropTypes from '../../reportPropTypes'; import CONST from '../../../CONST'; import Button from '../../../components/Button'; diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js index 346738574da3..c2690e27dd55 100644 --- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -28,7 +28,7 @@ import * as PaymentUtils from '../../../../libs/PaymentUtils'; import OfflineWithFeedback from '../../../../components/OfflineWithFeedback'; import ConfirmContent from '../../../../components/ConfirmContent'; import Button from '../../../../components/Button'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; import variables from '../../../../styles/variables'; import useLocalize from '../../../../hooks/useLocalize'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index b8bb74295567..e91fcdacaf0e 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -7,7 +7,7 @@ import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import ONYXKEYS from '../../../ONYXKEYS'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import Text from '../../../components/Text'; import CONST from '../../../CONST'; import * as User from '../../../libs/actions/User'; diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js index 2289e68e7bd1..dd6f5cf90c4a 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js @@ -21,7 +21,7 @@ import ConfirmModal from '../../../../components/ConfirmModal'; import * as User from '../../../../libs/actions/User'; import CONST from '../../../../CONST'; import * as ErrorUtils from '../../../../libs/ErrorUtils'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; import NotFoundPage from '../../../ErrorPage/NotFoundPage'; import ValidateCodeForm from './ValidateCodeForm'; import ROUTES from '../../../../ROUTES'; diff --git a/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js index ea81413fcbb5..6cf47d37e3af 100644 --- a/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js @@ -20,7 +20,7 @@ import shouldDelayFocus from '../../../../../libs/shouldDelayFocus'; import Text from '../../../../../components/Text'; import {withNetwork} from '../../../../../components/OnyxProvider'; import PressableWithFeedback from '../../../../../components/Pressable/PressableWithFeedback'; -import themeColors from '../../../../../styles/themes/default'; +import themeColors from '../../../../../styles/themes/dark'; import * as StyleUtils from '../../../../../styles/StyleUtils'; import CONST from '../../../../../CONST'; diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 9765cf1ae0b4..6b625f3e13b9 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -14,7 +14,7 @@ import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Expensicons from '../../../components/Icon/Expensicons'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; const propTypes = { ...withLocalizePropTypes, diff --git a/src/pages/settings/Report/WriteCapabilityPage.js b/src/pages/settings/Report/WriteCapabilityPage.js index 59ad90a2cd1f..5e1d1192824b 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.js +++ b/src/pages/settings/Report/WriteCapabilityPage.js @@ -15,7 +15,7 @@ import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; import * as Expensicons from '../../../components/Icon/Expensicons'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import * as ReportUtils from '../../../libs/ReportUtils'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; import * as PolicyUtils from '../../../libs/PolicyUtils'; diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index 6780080ff382..8e2b0cff5b69 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -22,7 +22,7 @@ import Text from '../../../../components/Text'; import Section from '../../../../components/Section'; import ONYXKEYS from '../../../../ONYXKEYS'; import Clipboard from '../../../../libs/Clipboard'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; import localFileDownload from '../../../../libs/localFileDownload'; import * as TwoFactorAuthActions from '../../../../libs/actions/TwoFactorAuthActions'; diff --git a/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js b/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js index 4d49edac1fe4..b5bc98005170 100644 --- a/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js @@ -9,7 +9,7 @@ import ROUTES from '../../../../ROUTES'; import Section from '../../../../components/Section'; import * as Illustrations from '../../../../components/Icon/Illustrations'; import * as Expensicons from '../../../../components/Icon/Expensicons'; -import themeColors from '../../../../styles/themes/default'; +import themeColors from '../../../../styles/themes/dark'; import styles from '../../../../styles/styles'; import ConfirmModal from '../../../../components/ConfirmModal'; import FullPageOfflineBlockingView from '../../../../components/BlockingViews/FullPageOfflineBlockingView'; diff --git a/src/pages/signin/SignInPageLayout/Footer.js b/src/pages/signin/SignInPageLayout/Footer.js index 35e63b0699b3..0cb5a8dd45b3 100644 --- a/src/pages/signin/SignInPageLayout/Footer.js +++ b/src/pages/signin/SignInPageLayout/Footer.js @@ -5,7 +5,7 @@ import _ from 'underscore'; import Text from '../../../components/Text'; import styles from '../../../styles/styles'; import * as StyleUtils from '../../../styles/StyleUtils'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import variables from '../../../styles/variables'; import * as Expensicons from '../../../components/Icon/Expensicons'; import TextLink from '../../../components/TextLink'; diff --git a/src/pages/signin/SignInPageLayout/index.js b/src/pages/signin/SignInPageLayout/index.js index 7acd08a6c693..9f73b828c5e9 100644 --- a/src/pages/signin/SignInPageLayout/index.js +++ b/src/pages/signin/SignInPageLayout/index.js @@ -11,7 +11,7 @@ import styles from '../../../styles/styles'; import SignInPageHero from '../SignInPageHero'; import * as StyleUtils from '../../../styles/StyleUtils'; import scrollViewContentContainerStyles from './signInPageStyles'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import BackgroundImage from './BackgroundImage'; import SignInGradient from '../../../../assets/images/home-fade-gradient.svg'; import variables from '../../../styles/variables'; diff --git a/src/pages/signin/Socials.js b/src/pages/signin/Socials.js index f7a866d2023b..be6305cf1bc6 100644 --- a/src/pages/signin/Socials.js +++ b/src/pages/signin/Socials.js @@ -5,7 +5,7 @@ import * as Link from '../../libs/actions/Link'; import Icon from '../../components/Icon'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import * as Expensicons from '../../components/Icon/Expensicons'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import styles from '../../styles/styles'; import variables from '../../styles/variables'; import CONST from '../../CONST'; diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index a68f99df6d24..e0ccdca636ab 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -7,7 +7,7 @@ import lodashGet from 'lodash/get'; import styles from '../../../styles/styles'; import Button from '../../../components/Button'; import Text from '../../../components/Text'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import * as Session from '../../../libs/actions/Session'; import ONYXKEYS from '../../../ONYXKEYS'; import CONST from '../../../CONST'; diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js index 98ef04836f76..6073836c6e9a 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -10,7 +10,7 @@ import styles from '../../styles/styles'; import compose from '../../libs/compose'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; import * as Expensicons from '../../components/Icon/Expensicons'; -import themeColors from '../../styles/themes/default'; +import themeColors from '../../styles/themes/dark'; import * as PolicyUtils from '../../libs/PolicyUtils'; import MenuItem from '../../components/MenuItem'; import * as Policy from '../../libs/actions/Policy'; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js index eb8305f23140..8f48fc216bfd 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js @@ -5,7 +5,7 @@ import lodashGet from 'lodash/get'; import _ from 'underscore'; import Text from '../../../components/Text'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/default'; +import themeColors from '../../../styles/themes/dark'; import * as Expensicons from '../../../components/Icon/Expensicons'; import * as Illustrations from '../../../components/Icon/Illustrations'; import Section from '../../../components/Section'; diff --git a/src/stories/Composer.stories.js b/src/stories/Composer.stories.js index 3dfc5b0e3ead..632901df582f 100644 --- a/src/stories/Composer.stories.js +++ b/src/stories/Composer.stories.js @@ -5,7 +5,7 @@ import Composer from '../components/Composer'; import RenderHTML from '../components/RenderHTML'; import Text from '../components/Text'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; import * as StyleUtils from '../styles/StyleUtils'; import CONST from '../CONST'; diff --git a/src/stories/PopoverMenu.stories.js b/src/stories/PopoverMenu.stories.js index 1098fa9ce226..198c7ef28e83 100644 --- a/src/stories/PopoverMenu.stories.js +++ b/src/stories/PopoverMenu.stories.js @@ -3,7 +3,7 @@ import {SafeAreaProvider} from 'react-native-safe-area-context'; import PopoverMenu from '../components/PopoverMenu'; import * as Expensicons from '../components/Icon/Expensicons'; import MenuItem from '../components/MenuItem'; -import themeColors from '../styles/themes/default'; +import themeColors from '../styles/themes/dark'; /** * We use the Component Story Format for writing stories. Follow the docs here: diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js index f8db7272e93f..676796751dc3 100644 --- a/src/styles/StyleUtils.js +++ b/src/styles/StyleUtils.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import CONST from '../CONST'; import fontFamily from './fontFamily'; -import themeColors from './themes/default'; +import themeColors from './themes/dark'; import variables from './variables'; import colors from './colors'; import positioning from './utilities/positioning'; diff --git a/src/styles/addOutlineWidth/index.js b/src/styles/addOutlineWidth/index.js index 2a2657b24910..cdb7dce7f926 100644 --- a/src/styles/addOutlineWidth/index.js +++ b/src/styles/addOutlineWidth/index.js @@ -3,7 +3,7 @@ * can be added to the object */ -import themeDefault from '../themes/default'; +import themeDefault from '../themes/dark'; /** * Adds the addOutlineWidth property to an object to be used when styling diff --git a/src/styles/getModalStyles/getBaseModalStyles.js b/src/styles/getModalStyles/getBaseModalStyles.js index b7a3317963ca..7daf944225c5 100644 --- a/src/styles/getModalStyles/getBaseModalStyles.js +++ b/src/styles/getModalStyles/getBaseModalStyles.js @@ -1,6 +1,6 @@ import CONST from '../../CONST'; import variables from '../variables'; -import themeColors from '../themes/default'; +import themeColors from '../themes/dark'; import styles from '../styles'; const getCenteredModalStyles = (windowWidth, isSmallScreenWidth, isFullScreenWhenSmall = false) => ({ diff --git a/src/styles/getReportActionContextMenuStyles.js b/src/styles/getReportActionContextMenuStyles.js index 026306084ce4..b8084abad976 100644 --- a/src/styles/getReportActionContextMenuStyles.js +++ b/src/styles/getReportActionContextMenuStyles.js @@ -1,6 +1,6 @@ import styles from './styles'; import variables from './variables'; -import themeColors from './themes/default'; +import themeColors from './themes/dark'; const defaultWrapperStyle = { backgroundColor: themeColors.componentBG, diff --git a/src/styles/getTooltipStyles.js b/src/styles/getTooltipStyles.js index bc5fcfe807aa..58ec3f200b2d 100644 --- a/src/styles/getTooltipStyles.js +++ b/src/styles/getTooltipStyles.js @@ -1,7 +1,7 @@ import spacing from './utilities/spacing'; import styles from './styles'; import colors from './colors'; -import themeColors from './themes/default'; +import themeColors from './themes/dark'; import fontFamily from './fontFamily'; import variables from './variables'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; diff --git a/src/styles/styles.js b/src/styles/styles.js index 835082ba98c9..fe47cc70c66a 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2,7 +2,7 @@ import {defaultStyles as defaultPickerStyles} from 'react-native-picker-select/s import lodashClamp from 'lodash/clamp'; import fontFamily from './fontFamily'; import addOutlineWidth from './addOutlineWidth'; -import themeColors from './themes/default'; +import themeColors from './themes/dark'; import fontWeightBold from './fontWeight/bold'; import variables from './variables'; import spacing from './utilities/spacing'; From 0e02b13875976b5bc64bc69da4cc6a302408f5a1 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 16 Sep 2023 11:33:56 +0200 Subject: [PATCH 006/218] fix: rename back dark.ts --- src/components/AddPlaidBankAccount.js | 2 +- src/components/AddressSearch/index.js | 2 +- src/components/AttachmentCarousel/index.js | 2 +- src/components/AttachmentModal.js | 2 +- src/components/AttachmentView.js | 2 +- src/components/Avatar.js | 2 +- src/components/AvatarCropModal/AvatarCropModal.js | 2 +- src/components/AvatarWithDisplayName.js | 2 +- src/components/AvatarWithImagePicker.js | 2 +- src/components/BlockingViews/BlockingView.js | 2 +- src/components/Button/index.js | 2 +- src/components/ButtonWithDropdownMenu.js | 2 +- src/components/Checkbox.js | 2 +- src/components/Composer/index.android.js | 2 +- src/components/Composer/index.ios.js | 2 +- src/components/Composer/index.js | 2 +- src/components/CurrentUserPersonalDetailsSkeletonView/index.js | 2 +- src/components/CustomStatusBar/index.js | 2 +- src/components/DatePicker/index.ios.js | 2 +- src/components/EmojiPicker/CategoryShortcutButton.js | 2 +- src/components/ExpensifyWordmark.js | 2 +- src/components/FloatingActionButton.js | 2 +- src/components/FullscreenLoadingIndicator.js | 2 +- src/components/GrowlNotification/index.js | 2 +- .../HTMLEngineProvider/HTMLRenderers/EditedRenderer.js | 2 +- src/components/Icon/index.js | 2 +- src/components/IllustratedHeaderPageLayout.js | 2 +- src/components/Indicator.js | 2 +- src/components/InlineSystemMessage.js | 2 +- src/components/LHNOptionsList/OptionRowLHN.js | 2 +- src/components/LocalePicker.js | 2 +- src/components/MenuItem.js | 2 +- src/components/Modal/BaseModal.js | 2 +- src/components/Modal/index.web.js | 2 +- src/components/MoneyRequestConfirmationList.js | 2 +- src/components/MoneyRequestDetails.js | 2 +- src/components/MultipleAvatars.js | 2 +- src/components/Onfido/BaseOnfidoWeb.js | 2 +- src/components/OptionRow.js | 2 +- src/components/OptionsListSkeletonView.js | 2 +- src/components/Picker/BasePicker.js | 2 +- src/components/PinButton.js | 2 +- src/components/QRCode/index.js | 2 +- src/components/QRShare/index.js | 2 +- src/components/ReportActionItem/IOUPreview.js | 2 +- src/components/ReportActionItem/ReportPreview.js | 2 +- src/components/ReportActionsSkeletonView/SkeletonViewLines.js | 2 +- src/components/ReportHeaderSkeletonView.js | 2 +- src/components/RoomHeaderAvatars.js | 2 +- src/components/SelectCircle.js | 2 +- src/components/SelectionListRadio/RadioListItem.js | 2 +- src/components/SubscriptAvatar.js | 2 +- src/components/TabSelector/TabSelectorItem.js | 2 +- src/components/Text.js | 2 +- src/components/TextInput/BaseTextInput.js | 2 +- .../VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js | 2 +- .../createResponsiveStackNavigator/ThreePaneView.js | 2 +- src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js | 2 +- src/libs/Navigation/NavigationRoot.js | 2 +- src/pages/ErrorPage/GenericErrorPage.js | 2 +- src/pages/home/report/FloatingMessageCounter/index.js | 2 +- src/pages/home/report/LinkPreviewer.js | 2 +- src/pages/home/report/ReportActionCompose.js | 2 +- src/pages/home/report/ReportActionItemFragment.js | 2 +- src/pages/home/report/ReportActionItemMessageEdit.js | 2 +- src/pages/home/sidebar/SidebarLinks.js | 2 +- src/pages/home/sidebar/SidebarScreen/index.js | 2 +- src/pages/iou/IOUCurrencySelection.js | 2 +- src/pages/iou/ReceiptSelector/index.native.js | 2 +- src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js | 2 +- src/pages/settings/Preferences/PreferencesPage.js | 2 +- src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js | 2 +- .../Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- src/pages/settings/Report/NotificationPreferencePage.js | 2 +- src/pages/settings/Report/WriteCapabilityPage.js | 2 +- src/pages/settings/Security/TwoFactorAuth/CodesPage.js | 2 +- src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js | 2 +- src/pages/signin/SignInPageLayout/Footer.js | 2 +- src/pages/signin/SignInPageLayout/index.js | 2 +- src/pages/signin/Socials.js | 2 +- src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- src/pages/workspace/WorkspacesListPage.js | 2 +- src/pages/workspace/reimburse/WorkspaceReimburseSection.js | 2 +- src/stories/Composer.stories.js | 2 +- src/stories/PopoverMenu.stories.js | 2 +- src/styles/StyleUtils.js | 2 +- src/styles/addOutlineWidth/index.js | 2 +- src/styles/getModalStyles/getBaseModalStyles.js | 2 +- src/styles/getReportActionContextMenuStyles.js | 2 +- src/styles/getTooltipStyles.js | 2 +- src/styles/styles.js | 2 +- src/styles/themes/ThemeContext.js | 2 +- src/styles/themes/ThemeProvider.js | 2 +- src/styles/themes/{dark.ts => default.ts} | 0 94 files changed, 93 insertions(+), 93 deletions(-) rename src/styles/themes/{dark.ts => default.ts} (100%) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index d1d8c42abffa..ff97c9be24a6 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -9,7 +9,7 @@ import PlaidLink from './PlaidLink'; import * as BankAccounts from '../libs/actions/BankAccounts'; import ONYXKEYS from '../ONYXKEYS'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import Picker from './Picker'; diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js index a29fd4002077..e8a41ec35435 100644 --- a/src/components/AddressSearch/index.js +++ b/src/components/AddressSearch/index.js @@ -6,7 +6,7 @@ import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete' import lodashGet from 'lodash/get'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import TextInput from '../TextInput'; import * as ApiUtils from '../../libs/ApiUtils'; import * as GooglePlacesUtils from '../../libs/GooglePlacesUtils'; diff --git a/src/components/AttachmentCarousel/index.js b/src/components/AttachmentCarousel/index.js index b15c8a0b7c7e..3f2524a2992e 100644 --- a/src/components/AttachmentCarousel/index.js +++ b/src/components/AttachmentCarousel/index.js @@ -4,7 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import * as Expensicons from '../Icon/Expensicons'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import CarouselActions from './CarouselActions'; import Button from '../Button'; import AttachmentView from '../AttachmentView'; diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 1e768d2eefd3..1b87799d4f5b 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -12,7 +12,7 @@ import AttachmentCarousel from './AttachmentCarousel'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; import * as FileUtils from '../libs/fileDownload/FileUtils'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import compose from '../libs/compose'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import Button from './Button'; diff --git a/src/components/AttachmentView.js b/src/components/AttachmentView.js index 7cf77c08e5e2..d880ac9b9076 100755 --- a/src/components/AttachmentView.js +++ b/src/components/AttachmentView.js @@ -12,7 +12,7 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize'; import compose from '../libs/compose'; import Text from './Text'; import Tooltip from './Tooltip'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import variables from '../styles/variables'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback'; diff --git a/src/components/Avatar.js b/src/components/Avatar.js index f4502ddb6a03..8dcf7f08c1d1 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import _ from 'underscore'; import stylePropTypes from '../styles/stylePropTypes'; import Icon from './Icon'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import CONST from '../CONST'; import * as StyleUtils from '../styles/StyleUtils'; import * as Expensicons from './Icon/Expensicons'; diff --git a/src/components/AvatarCropModal/AvatarCropModal.js b/src/components/AvatarCropModal/AvatarCropModal.js index f0e7b4ed74be..99262bf12938 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.js +++ b/src/components/AvatarCropModal/AvatarCropModal.js @@ -6,7 +6,7 @@ import {runOnUI, interpolate, useAnimatedGestureHandler, useSharedValue, useWork import CONST from '../../CONST'; import compose from '../../libs/compose'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import Button from '../Button'; import HeaderWithBackButton from '../HeaderWithBackButton'; import Icon from '../Icon'; diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 52ae459bca7a..0f1300ebf03d 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -8,7 +8,7 @@ import participantPropTypes from './participantPropTypes'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import SubscriptAvatar from './SubscriptAvatar'; import * as ReportUtils from '../libs/ReportUtils'; import MultipleAvatars from './MultipleAvatars'; diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 8452afcc0616..fcbfe4f4c4c4 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -8,7 +8,7 @@ import Icon from './Icon'; import PopoverMenu from './PopoverMenu'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import AttachmentPicker from './AttachmentPicker'; import AvatarCropModal from './AvatarCropModal/AvatarCropModal'; import OfflineWithFeedback from './OfflineWithFeedback'; diff --git a/src/components/BlockingViews/BlockingView.js b/src/components/BlockingViews/BlockingView.js index a4af9754bbe5..d02fa55a6434 100644 --- a/src/components/BlockingViews/BlockingView.js +++ b/src/components/BlockingViews/BlockingView.js @@ -5,7 +5,7 @@ import styles from '../../styles/styles'; import variables from '../../styles/variables'; import Icon from '../Icon'; import Text from '../Text'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import TextLink from '../TextLink'; import Navigation from '../../libs/Navigation/Navigation'; import AutoEmailLink from '../AutoEmailLink'; diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 03e98b56d5c6..a850a43d2fb0 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import Text from '../Text'; import KeyboardShortcut from '../../libs/KeyboardShortcut'; import Icon from '../Icon'; diff --git a/src/components/ButtonWithDropdownMenu.js b/src/components/ButtonWithDropdownMenu.js index eb1729a45614..1396ab601330 100644 --- a/src/components/ButtonWithDropdownMenu.js +++ b/src/components/ButtonWithDropdownMenu.js @@ -8,7 +8,7 @@ import Button from './Button'; import PopoverMenu from './PopoverMenu'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import CONST from '../CONST'; const propTypes = { diff --git a/src/components/Checkbox.js b/src/components/Checkbox.js index 6150e5134e19..86b6e05d5ed7 100644 --- a/src/components/Checkbox.js +++ b/src/components/Checkbox.js @@ -2,7 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import stylePropTypes from '../styles/stylePropTypes'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; diff --git a/src/components/Composer/index.android.js b/src/components/Composer/index.android.js index c252d3dcfa62..d0805cbcc7c3 100644 --- a/src/components/Composer/index.android.js +++ b/src/components/Composer/index.android.js @@ -3,7 +3,7 @@ import {StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import RNTextInput from '../RNTextInput'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import * as ComposerUtils from '../../libs/ComposerUtils'; const propTypes = { diff --git a/src/components/Composer/index.ios.js b/src/components/Composer/index.ios.js index a0e372403310..c0a3859e6d01 100644 --- a/src/components/Composer/index.ios.js +++ b/src/components/Composer/index.ios.js @@ -3,7 +3,7 @@ import {StyleSheet} from 'react-native'; import PropTypes from 'prop-types'; import _ from 'underscore'; import RNTextInput from '../RNTextInput'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import * as ComposerUtils from '../../libs/ComposerUtils'; const propTypes = { diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index 4b8144745119..d32246529b6c 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -6,7 +6,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import RNTextInput from '../RNTextInput'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Growl from '../../libs/Growl'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import updateIsFullComposerAvailable from '../../libs/ComposerUtils/updateIsFullComposerAvailable'; import * as ComposerUtils from '../../libs/ComposerUtils'; import * as Browser from '../../libs/Browser'; diff --git a/src/components/CurrentUserPersonalDetailsSkeletonView/index.js b/src/components/CurrentUserPersonalDetailsSkeletonView/index.js index 8e63be848e91..6e6c46e971c0 100644 --- a/src/components/CurrentUserPersonalDetailsSkeletonView/index.js +++ b/src/components/CurrentUserPersonalDetailsSkeletonView/index.js @@ -5,7 +5,7 @@ import {Circle, Rect} from 'react-native-svg'; import {View} from 'react-native'; import * as StyleUtils from '../../styles/StyleUtils'; import CONST from '../../CONST'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import variables from '../../styles/variables'; import styles from '../../styles/styles'; diff --git a/src/components/CustomStatusBar/index.js b/src/components/CustomStatusBar/index.js index 4f77691046bd..76752cb549e1 100644 --- a/src/components/CustomStatusBar/index.js +++ b/src/components/CustomStatusBar/index.js @@ -1,6 +1,6 @@ import React, {useEffect} from 'react'; import StatusBar from '../../libs/StatusBar'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; function CustomStatusBar() { useEffect(() => { diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index 410376a387dd..5d87636a9365 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -10,7 +10,7 @@ import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Popover from '../Popover'; import CONST from '../../CONST'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import {propTypes, defaultProps} from './datepickerPropTypes'; import withKeyboardState, {keyboardStatePropTypes} from '../withKeyboardState'; diff --git a/src/components/EmojiPicker/CategoryShortcutButton.js b/src/components/EmojiPicker/CategoryShortcutButton.js index ac7f1991ec6a..a7658ae0542d 100644 --- a/src/components/EmojiPicker/CategoryShortcutButton.js +++ b/src/components/EmojiPicker/CategoryShortcutButton.js @@ -7,7 +7,7 @@ import variables from '../../styles/variables'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import getButtonState from '../../libs/getButtonState'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; import CONST from '../../CONST'; diff --git a/src/components/ExpensifyWordmark.js b/src/components/ExpensifyWordmark.js index 25ff571a7c01..dde792e87e22 100644 --- a/src/components/ExpensifyWordmark.js +++ b/src/components/ExpensifyWordmark.js @@ -8,7 +8,7 @@ import StagingLogo from '../../assets/images/expensify-logo--staging.svg'; import AdHocLogo from '../../assets/images/expensify-logo--adhoc.svg'; import CONST from '../CONST'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; import variables from '../styles/variables'; diff --git a/src/components/FloatingActionButton.js b/src/components/FloatingActionButton.js index 4a2db6e2bcfb..706bad59f7b7 100644 --- a/src/components/FloatingActionButton.js +++ b/src/components/FloatingActionButton.js @@ -5,7 +5,7 @@ import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import styles from '../styles/styles'; import * as StyleUtils from '../styles/StyleUtils'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import Tooltip from './Tooltip'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; diff --git a/src/components/FullscreenLoadingIndicator.js b/src/components/FullscreenLoadingIndicator.js index 96c1246f33c5..5c212b6dc29e 100644 --- a/src/components/FullscreenLoadingIndicator.js +++ b/src/components/FullscreenLoadingIndicator.js @@ -2,7 +2,7 @@ import _ from 'underscore'; import React from 'react'; import {ActivityIndicator, StyleSheet, View} from 'react-native'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import stylePropTypes from '../styles/stylePropTypes'; const propTypes = { diff --git a/src/components/GrowlNotification/index.js b/src/components/GrowlNotification/index.js index 750063847ead..70cadd5efd8e 100644 --- a/src/components/GrowlNotification/index.js +++ b/src/components/GrowlNotification/index.js @@ -1,7 +1,7 @@ import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {Directions, FlingGestureHandler, State} from 'react-native-gesture-handler'; import {View, Animated} from 'react-native'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import Text from '../Text'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js index 3f5ff9a72dc2..d91510c3ec6a 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js @@ -4,7 +4,7 @@ import htmlRendererPropTypes from './htmlRendererPropTypes'; import withLocalize, {withLocalizePropTypes} from '../../withLocalize'; import Text from '../../Text'; import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import styles from '../../../styles/styles'; import editedLabelStyles from '../../../styles/editedLabelStyles'; diff --git a/src/components/Icon/index.js b/src/components/Icon/index.js index 044baee944b3..8c6559451215 100644 --- a/src/components/Icon/index.js +++ b/src/components/Icon/index.js @@ -2,7 +2,7 @@ import React, {PureComponent} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import variables from '../../styles/variables'; import * as StyleUtils from '../../styles/StyleUtils'; import IconWrapperStyles from './IconWrapperStyles'; diff --git a/src/components/IllustratedHeaderPageLayout.js b/src/components/IllustratedHeaderPageLayout.js index d1563c500671..7fc340426d69 100644 --- a/src/components/IllustratedHeaderPageLayout.js +++ b/src/components/IllustratedHeaderPageLayout.js @@ -7,7 +7,7 @@ import headerWithBackButtonPropTypes from './HeaderWithBackButton/headerWithBack import HeaderWithBackButton from './HeaderWithBackButton'; import ScreenWrapper from './ScreenWrapper'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import * as StyleUtils from '../styles/StyleUtils'; import useWindowDimensions from '../hooks/useWindowDimensions'; import FixedFooter from './FixedFooter'; diff --git a/src/components/Indicator.js b/src/components/Indicator.js index cd66de22f57d..765d79e156af 100644 --- a/src/components/Indicator.js +++ b/src/components/Indicator.js @@ -15,7 +15,7 @@ import * as PolicyUtils from '../libs/PolicyUtils'; import * as PaymentMethods from '../libs/actions/PaymentMethods'; import * as ReimbursementAccountProps from '../pages/ReimbursementAccount/reimbursementAccountPropTypes'; import * as UserUtils from '../libs/UserUtils'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; const propTypes = { /* Onyx Props */ diff --git a/src/components/InlineSystemMessage.js b/src/components/InlineSystemMessage.js index ea21c5f65352..a6866fb5a887 100644 --- a/src/components/InlineSystemMessage.js +++ b/src/components/InlineSystemMessage.js @@ -2,7 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import styles from '../styles/styles'; -import theme from '../styles/themes/dark'; +import theme from '../styles/themes/default'; import Text from './Text'; import * as Expensicons from './Icon/Expensicons'; import Icon from './Icon'; diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index 51045cd8876f..e17cf71e5d06 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -14,7 +14,7 @@ import colors from '../../styles/colors'; import Text from '../Text'; import SubscriptAvatar from '../SubscriptAvatar'; import CONST from '../../CONST'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import OfflineWithFeedback from '../OfflineWithFeedback'; import PressableWithSecondaryInteraction from '../PressableWithSecondaryInteraction'; import * as ReportActionContextMenu from '../../pages/home/report/ContextMenu/ReportActionContextMenu'; diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js index af191080a4ca..532c29535e50 100644 --- a/src/components/LocalePicker.js +++ b/src/components/LocalePicker.js @@ -9,7 +9,7 @@ import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import Picker from './Picker'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; const propTypes = { /** Indicates which locale the user currently has selected */ diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index b470b11171f8..c280a75a8ef3 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -3,7 +3,7 @@ import React from 'react'; import {View} from 'react-native'; import Text from './Text'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import * as StyleUtils from '../styles/StyleUtils'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index a9116faa3460..6d5bd5390416 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -5,7 +5,7 @@ import ReactNativeModal from 'react-native-modal'; import {SafeAreaInsetsContext} from 'react-native-safe-area-context'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './modalPropTypes'; import * as Modal from '../../libs/actions/Modal'; import getModalStyles from '../../styles/getModalStyles'; diff --git a/src/components/Modal/index.web.js b/src/components/Modal/index.web.js index 8fc5a4ee22fe..065b3a9f210f 100644 --- a/src/components/Modal/index.web.js +++ b/src/components/Modal/index.web.js @@ -3,7 +3,7 @@ import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; import {propTypes, defaultProps} from './modalPropTypes'; import * as StyleUtils from '../../styles/StyleUtils'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import StatusBar from '../../libs/StatusBar'; import CONST from '../../CONST'; diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 76620c7a0863..e421dae4e37e 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -23,7 +23,7 @@ import optionPropTypes from './optionPropTypes'; import * as CurrencyUtils from '../libs/CurrencyUtils'; import Button from './Button'; import * as Expensicons from './Icon/Expensicons'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import Image from './Image'; import ReceiptHTML from '../../assets/images/receipt-html.png'; import ReceiptDoc from '../../assets/images/receipt-doc.png'; diff --git a/src/components/MoneyRequestDetails.js b/src/components/MoneyRequestDetails.js index fe6d7afdfe7a..a690c31c000c 100644 --- a/src/components/MoneyRequestDetails.js +++ b/src/components/MoneyRequestDetails.js @@ -11,7 +11,7 @@ import Text from './Text'; import participantPropTypes from './participantPropTypes'; import Avatar from './Avatar'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import CONST from '../CONST'; import withWindowDimensions from './withWindowDimensions'; import compose from '../libs/compose'; diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index c89613c90e19..ceb1c5371c7e 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -6,7 +6,7 @@ import styles from '../styles/styles'; import Avatar from './Avatar'; import Tooltip from './Tooltip'; import Text from './Text'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import * as StyleUtils from '../styles/StyleUtils'; import CONST from '../CONST'; import variables from '../styles/variables'; diff --git a/src/components/Onfido/BaseOnfidoWeb.js b/src/components/Onfido/BaseOnfidoWeb.js index 54fb2abba2bf..394996331d5e 100644 --- a/src/components/Onfido/BaseOnfidoWeb.js +++ b/src/components/Onfido/BaseOnfidoWeb.js @@ -6,7 +6,7 @@ import * as OnfidoSDK from 'onfido-sdk-ui'; import onfidoPropTypes from './onfidoPropTypes'; import CONST from '../../CONST'; import variables from '../../styles/variables'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import fontWeightBold from '../../styles/fontWeight/bold'; import fontFamily from '../../styles/fontFamily'; import Log from '../../libs/Log'; diff --git a/src/components/OptionRow.js b/src/components/OptionRow.js index d341710945d7..adaa4457bbd9 100644 --- a/src/components/OptionRow.js +++ b/src/components/OptionRow.js @@ -11,7 +11,7 @@ import * as Expensicons from './Icon/Expensicons'; import MultipleAvatars from './MultipleAvatars'; import Hoverable from './Hoverable'; import DisplayNames from './DisplayNames'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import Text from './Text'; import SelectCircle from './SelectCircle'; diff --git a/src/components/OptionsListSkeletonView.js b/src/components/OptionsListSkeletonView.js index 1cf6a97f8c97..15c66affe84d 100644 --- a/src/components/OptionsListSkeletonView.js +++ b/src/components/OptionsListSkeletonView.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import {Rect, Circle} from 'react-native-svg'; import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../CONST'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import styles from '../styles/styles'; const propTypes = { diff --git a/src/components/Picker/BasePicker.js b/src/components/Picker/BasePicker.js index c64aad3d0c1a..173b863edfcc 100644 --- a/src/components/Picker/BasePicker.js +++ b/src/components/Picker/BasePicker.js @@ -8,7 +8,7 @@ import * as Expensicons from '../Icon/Expensicons'; import FormHelpMessage from '../FormHelpMessage'; import Text from '../Text'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import {ScrollContext} from '../ScrollViewWithContext'; const propTypes = { diff --git a/src/components/PinButton.js b/src/components/PinButton.js index 2074996cfb7b..84ad6e22f50b 100644 --- a/src/components/PinButton.js +++ b/src/components/PinButton.js @@ -1,6 +1,6 @@ import React from 'react'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import Icon from './Icon'; import Tooltip from './Tooltip'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; diff --git a/src/components/QRCode/index.js b/src/components/QRCode/index.js index 7bc0829d399d..f27cf28066ef 100644 --- a/src/components/QRCode/index.js +++ b/src/components/QRCode/index.js @@ -1,7 +1,7 @@ import React from 'react'; import QRCodeLibrary from 'react-native-qrcode-svg'; import PropTypes from 'prop-types'; -import defaultTheme from '../../styles/themes/dark'; +import defaultTheme from '../../styles/themes/default'; import CONST from '../../CONST'; const propTypes = { diff --git a/src/components/QRShare/index.js b/src/components/QRShare/index.js index 580151554d63..d96024ad1046 100644 --- a/src/components/QRShare/index.js +++ b/src/components/QRShare/index.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; -import defaultTheme from '../../styles/themes/dark'; +import defaultTheme from '../../styles/themes/default'; import styles from '../../styles/styles'; import Text from '../Text'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItem/IOUPreview.js index fe207476627b..85a0b22ac327 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -10,7 +10,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import MultipleAvatars from '../MultipleAvatars'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import * as Report from '../../libs/actions/Report'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import Icon from '../Icon'; import CONST from '../../CONST'; import * as Expensicons from '../Icon/Expensicons'; diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 89b7616d081a..d5d85df5e7ee 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -23,7 +23,7 @@ import SettlementButton from '../SettlementButton'; import * as IOU from '../../libs/actions/IOU'; import refPropTypes from '../refPropTypes'; import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import reportPropTypes from '../../pages/reportPropTypes'; const propTypes = { diff --git a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js index f8dcaf4de34c..ddaa46e0b731 100644 --- a/src/components/ReportActionsSkeletonView/SkeletonViewLines.js +++ b/src/components/ReportActionsSkeletonView/SkeletonViewLines.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import {Rect, Circle} from 'react-native-svg'; import SkeletonViewContentLoader from 'react-content-loader/native'; import CONST from '../../CONST'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import styles from '../../styles/styles'; const propTypes = { diff --git a/src/components/ReportHeaderSkeletonView.js b/src/components/ReportHeaderSkeletonView.js index c6e712417577..5f2d5379419d 100644 --- a/src/components/ReportHeaderSkeletonView.js +++ b/src/components/ReportHeaderSkeletonView.js @@ -8,7 +8,7 @@ import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import variables from '../styles/variables'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import PressableWithFeedback from './Pressable/PressableWithFeedback'; import compose from '../libs/compose'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; diff --git a/src/components/RoomHeaderAvatars.js b/src/components/RoomHeaderAvatars.js index cb18973e287c..6f78e6ace66d 100644 --- a/src/components/RoomHeaderAvatars.js +++ b/src/components/RoomHeaderAvatars.js @@ -6,7 +6,7 @@ import styles from '../styles/styles'; import Text from './Text'; import CONST from '../CONST'; import Avatar from './Avatar'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import * as StyleUtils from '../styles/StyleUtils'; import avatarPropTypes from './avatarPropTypes'; diff --git a/src/components/SelectCircle.js b/src/components/SelectCircle.js index 60e449479d11..93cf285eab59 100644 --- a/src/components/SelectCircle.js +++ b/src/components/SelectCircle.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import styles from '../styles/styles'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; const propTypes = { /** Should we show the checkmark inside the circle */ diff --git a/src/components/SelectionListRadio/RadioListItem.js b/src/components/SelectionListRadio/RadioListItem.js index 41928c50e05d..c5c4b3aeaf2c 100644 --- a/src/components/SelectionListRadio/RadioListItem.js +++ b/src/components/SelectionListRadio/RadioListItem.js @@ -6,7 +6,7 @@ import styles from '../../styles/styles'; import Text from '../Text'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import {radioListItemPropTypes} from './selectionListRadioPropTypes'; const propTypes = { diff --git a/src/components/SubscriptAvatar.js b/src/components/SubscriptAvatar.js index 5ca496025f1f..05202e720bd4 100644 --- a/src/components/SubscriptAvatar.js +++ b/src/components/SubscriptAvatar.js @@ -4,7 +4,7 @@ import {View} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import Avatar from './Avatar'; import CONST from '../CONST'; import * as StyleUtils from '../styles/StyleUtils'; diff --git a/src/components/TabSelector/TabSelectorItem.js b/src/components/TabSelector/TabSelectorItem.js index f96d9f2d621e..cea59bc2ee65 100644 --- a/src/components/TabSelector/TabSelectorItem.js +++ b/src/components/TabSelector/TabSelectorItem.js @@ -2,7 +2,7 @@ import {Text} from 'react-native'; import React from 'react'; import PropTypes from 'prop-types'; import Icon from '../Icon'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import styles from '../../styles/styles'; import PressableWithFeedback from '../Pressable/PressableWithFeedback'; diff --git a/src/components/Text.js b/src/components/Text.js index 3c44183cd17b..83b6be8fffb0 100644 --- a/src/components/Text.js +++ b/src/components/Text.js @@ -4,7 +4,7 @@ import _ from 'underscore'; // eslint-disable-next-line no-restricted-imports import {Text as RNText} from 'react-native'; import fontFamily from '../styles/fontFamily'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import variables from '../styles/variables'; const propTypes = { diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index b075672d492e..68c09e3a7f82 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -5,7 +5,7 @@ import Str from 'expensify-common/lib/str'; import RNTextInput from '../RNTextInput'; import TextInputLabel from './TextInputLabel'; import * as baseTextInputPropTypes from './baseTextInputPropTypes'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import styles from '../../styles/styles'; import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; diff --git a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js index 23ad19f53d37..4c0e2b551382 100755 --- a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js +++ b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js @@ -10,7 +10,7 @@ import ZoomIcon from '../../../assets/images/zoom-icon.svg'; import GoogleMeetIcon from '../../../assets/images/google-meet.svg'; import CONST from '../../CONST'; import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import compose from '../../libs/compose'; diff --git a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js index bc6ba2cfb3cd..2f9a899191bf 100644 --- a/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js +++ b/src/libs/Navigation/AppNavigator/createResponsiveStackNavigator/ThreePaneView.js @@ -3,7 +3,7 @@ import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import SCREENS from '../../../../SCREENS'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; import NAVIGATORS from '../../../../NAVIGATORS'; import * as StyleUtils from '../../../../styles/StyleUtils'; import {withNavigationPropTypes} from '../../../../components/withNavigation'; diff --git a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js index 63aca125dc27..d2de1ba23a01 100644 --- a/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js +++ b/src/libs/Navigation/AppNavigator/modalCardStyleInterpolator.js @@ -1,7 +1,7 @@ import {Animated} from 'react-native'; import variables from '../../../styles/variables'; import getCardStyles from '../../../styles/cardStyles'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; export default (isSmallScreenWidth, isFullScreenModal, {current: {progress}, inverted, layouts: {screen}}) => { const translateX = Animated.multiply( diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index c22c459b54ab..23c320eb991c 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -6,7 +6,7 @@ import {useSharedValue, useAnimatedReaction, interpolateColor, withTiming, withD import Navigation, {navigationRef} from './Navigation'; import linkingConfig from './linkingConfig'; import AppNavigator from './AppNavigator'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import Log from '../Log'; import StatusBar from '../StatusBar'; import useCurrentReportID from '../../hooks/useCurrentReportID'; diff --git a/src/pages/ErrorPage/GenericErrorPage.js b/src/pages/ErrorPage/GenericErrorPage.js index 05dddf273547..3ff3bc686419 100644 --- a/src/pages/ErrorPage/GenericErrorPage.js +++ b/src/pages/ErrorPage/GenericErrorPage.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import Icon from '../../components/Icon'; -import defaultTheme from '../../styles/themes/dark'; +import defaultTheme from '../../styles/themes/default'; import * as Expensicons from '../../components/Icon/Expensicons'; import Text from '../../components/Text'; import Button from '../../components/Button'; diff --git a/src/pages/home/report/FloatingMessageCounter/index.js b/src/pages/home/report/FloatingMessageCounter/index.js index 4178502a1368..73fe02df129b 100644 --- a/src/pages/home/report/FloatingMessageCounter/index.js +++ b/src/pages/home/report/FloatingMessageCounter/index.js @@ -6,7 +6,7 @@ import Button from '../../../../components/Button'; import Text from '../../../../components/Text'; import Icon from '../../../../components/Icon'; import * as Expensicons from '../../../../components/Icon/Expensicons'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; import useLocalize from '../../../../hooks/useLocalize'; import FloatingMessageCounterContainer from './FloatingMessageCounterContainer'; diff --git a/src/pages/home/report/LinkPreviewer.js b/src/pages/home/report/LinkPreviewer.js index 3859299ea1a7..4fcbb0dc0569 100644 --- a/src/pages/home/report/LinkPreviewer.js +++ b/src/pages/home/report/LinkPreviewer.js @@ -8,7 +8,7 @@ import TextLink from '../../../components/TextLink'; import * as StyleUtils from '../../../styles/StyleUtils'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; const IMAGE_TYPES = ['jpg', 'jpeg', 'png']; const MAX_IMAGE_HEIGHT = 180; diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index e8d26edb40e5..1146e3b382f5 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -7,7 +7,7 @@ import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import Composer from '../../../components/Composer'; import ONYXKEYS from '../../../ONYXKEYS'; import Icon from '../../../components/Icon'; diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 6863e75ab894..009c1118400b 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -5,7 +5,7 @@ import Str from 'expensify-common/lib/str'; import reportActionFragmentPropTypes from './reportActionFragmentPropTypes'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import RenderHTML from '../../../components/RenderHTML'; import Text from '../../../components/Text'; import * as EmojiUtils from '../../../libs/EmojiUtils'; diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 5c7e71d0a877..54c5fec4533e 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -7,7 +7,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import reportActionPropTypes from './reportActionPropTypes'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import * as StyleUtils from '../../../styles/StyleUtils'; import containerComposeStyles from '../../../styles/containerComposeStyles'; import Composer from '../../../components/Composer'; diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index dc917b1f6c6a..132b767a7f70 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -24,7 +24,7 @@ import LHNOptionsList from '../../../components/LHNOptionsList/LHNOptionsList'; import SidebarUtils from '../../../libs/SidebarUtils'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; import Header from '../../../components/Header'; -import defaultTheme from '../../../styles/themes/dark'; +import defaultTheme from '../../../styles/themes/default'; import OptionsListSkeletonView from '../../../components/OptionsListSkeletonView'; import variables from '../../../styles/variables'; import LogoComponent from '../../../../assets/images/expensify-wordmark.svg'; diff --git a/src/pages/home/sidebar/SidebarScreen/index.js b/src/pages/home/sidebar/SidebarScreen/index.js index 20f7e7098f1c..705d9d1e2d08 100755 --- a/src/pages/home/sidebar/SidebarScreen/index.js +++ b/src/pages/home/sidebar/SidebarScreen/index.js @@ -7,7 +7,7 @@ import FloatingActionButtonAndPopover from './FloatingActionButtonAndPopover'; import FreezeWrapper from '../../../../libs/Navigation/FreezeWrapper'; import withWindowDimensions from '../../../../components/withWindowDimensions'; import StatusBar from '../../../../libs/StatusBar'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; function SidebarScreen(props) { const popoverModal = useRef(null); diff --git a/src/pages/iou/IOUCurrencySelection.js b/src/pages/iou/IOUCurrencySelection.js index b46919127fb3..44a7fba5d487 100644 --- a/src/pages/iou/IOUCurrencySelection.js +++ b/src/pages/iou/IOUCurrencySelection.js @@ -15,7 +15,7 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize import {withNetwork} from '../../components/OnyxProvider'; import * as CurrencyUtils from '../../libs/CurrencyUtils'; import ROUTES from '../../ROUTES'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import * as Expensicons from '../../components/Icon/Expensicons'; import reportPropTypes from '../reportPropTypes'; import * as ReportUtils from '../../libs/ReportUtils'; diff --git a/src/pages/iou/ReceiptSelector/index.native.js b/src/pages/iou/ReceiptSelector/index.native.js index 06f692fa1b33..7eeab6e493bd 100644 --- a/src/pages/iou/ReceiptSelector/index.native.js +++ b/src/pages/iou/ReceiptSelector/index.native.js @@ -13,7 +13,7 @@ import styles from '../../../styles/styles'; import Shutter from '../../../../assets/images/shutter.svg'; import Hand from '../../../../assets/images/hand.svg'; import * as IOU from '../../../libs/actions/IOU'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import reportPropTypes from '../../reportPropTypes'; import CONST from '../../../CONST'; import Button from '../../../components/Button'; diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js index c2690e27dd55..346738574da3 100644 --- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -28,7 +28,7 @@ import * as PaymentUtils from '../../../../libs/PaymentUtils'; import OfflineWithFeedback from '../../../../components/OfflineWithFeedback'; import ConfirmContent from '../../../../components/ConfirmContent'; import Button from '../../../../components/Button'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; import variables from '../../../../styles/variables'; import useLocalize from '../../../../hooks/useLocalize'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index e91fcdacaf0e..b8bb74295567 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -7,7 +7,7 @@ import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; import ONYXKEYS from '../../../ONYXKEYS'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import Text from '../../../components/Text'; import CONST from '../../../CONST'; import * as User from '../../../libs/actions/User'; diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js index dd6f5cf90c4a..2289e68e7bd1 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.js @@ -21,7 +21,7 @@ import ConfirmModal from '../../../../components/ConfirmModal'; import * as User from '../../../../libs/actions/User'; import CONST from '../../../../CONST'; import * as ErrorUtils from '../../../../libs/ErrorUtils'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; import NotFoundPage from '../../../ErrorPage/NotFoundPage'; import ValidateCodeForm from './ValidateCodeForm'; import ROUTES from '../../../../ROUTES'; diff --git a/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js index 6cf47d37e3af..ea81413fcbb5 100644 --- a/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.js @@ -20,7 +20,7 @@ import shouldDelayFocus from '../../../../../libs/shouldDelayFocus'; import Text from '../../../../../components/Text'; import {withNetwork} from '../../../../../components/OnyxProvider'; import PressableWithFeedback from '../../../../../components/Pressable/PressableWithFeedback'; -import themeColors from '../../../../../styles/themes/dark'; +import themeColors from '../../../../../styles/themes/default'; import * as StyleUtils from '../../../../../styles/StyleUtils'; import CONST from '../../../../../CONST'; diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 6b625f3e13b9..9765cf1ae0b4 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -14,7 +14,7 @@ import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Expensicons from '../../../components/Icon/Expensicons'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; const propTypes = { ...withLocalizePropTypes, diff --git a/src/pages/settings/Report/WriteCapabilityPage.js b/src/pages/settings/Report/WriteCapabilityPage.js index 5e1d1192824b..59ad90a2cd1f 100644 --- a/src/pages/settings/Report/WriteCapabilityPage.js +++ b/src/pages/settings/Report/WriteCapabilityPage.js @@ -15,7 +15,7 @@ import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; import * as Expensicons from '../../../components/Icon/Expensicons'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import * as ReportUtils from '../../../libs/ReportUtils'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; import * as PolicyUtils from '../../../libs/PolicyUtils'; diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index 8e2b0cff5b69..6780080ff382 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -22,7 +22,7 @@ import Text from '../../../../components/Text'; import Section from '../../../../components/Section'; import ONYXKEYS from '../../../../ONYXKEYS'; import Clipboard from '../../../../libs/Clipboard'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; import localFileDownload from '../../../../libs/localFileDownload'; import * as TwoFactorAuthActions from '../../../../libs/actions/TwoFactorAuthActions'; diff --git a/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js b/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js index b5bc98005170..4d49edac1fe4 100644 --- a/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/IsEnabledPage.js @@ -9,7 +9,7 @@ import ROUTES from '../../../../ROUTES'; import Section from '../../../../components/Section'; import * as Illustrations from '../../../../components/Icon/Illustrations'; import * as Expensicons from '../../../../components/Icon/Expensicons'; -import themeColors from '../../../../styles/themes/dark'; +import themeColors from '../../../../styles/themes/default'; import styles from '../../../../styles/styles'; import ConfirmModal from '../../../../components/ConfirmModal'; import FullPageOfflineBlockingView from '../../../../components/BlockingViews/FullPageOfflineBlockingView'; diff --git a/src/pages/signin/SignInPageLayout/Footer.js b/src/pages/signin/SignInPageLayout/Footer.js index 0cb5a8dd45b3..35e63b0699b3 100644 --- a/src/pages/signin/SignInPageLayout/Footer.js +++ b/src/pages/signin/SignInPageLayout/Footer.js @@ -5,7 +5,7 @@ import _ from 'underscore'; import Text from '../../../components/Text'; import styles from '../../../styles/styles'; import * as StyleUtils from '../../../styles/StyleUtils'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import variables from '../../../styles/variables'; import * as Expensicons from '../../../components/Icon/Expensicons'; import TextLink from '../../../components/TextLink'; diff --git a/src/pages/signin/SignInPageLayout/index.js b/src/pages/signin/SignInPageLayout/index.js index 9f73b828c5e9..7acd08a6c693 100644 --- a/src/pages/signin/SignInPageLayout/index.js +++ b/src/pages/signin/SignInPageLayout/index.js @@ -11,7 +11,7 @@ import styles from '../../../styles/styles'; import SignInPageHero from '../SignInPageHero'; import * as StyleUtils from '../../../styles/StyleUtils'; import scrollViewContentContainerStyles from './signInPageStyles'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import BackgroundImage from './BackgroundImage'; import SignInGradient from '../../../../assets/images/home-fade-gradient.svg'; import variables from '../../../styles/variables'; diff --git a/src/pages/signin/Socials.js b/src/pages/signin/Socials.js index be6305cf1bc6..f7a866d2023b 100644 --- a/src/pages/signin/Socials.js +++ b/src/pages/signin/Socials.js @@ -5,7 +5,7 @@ import * as Link from '../../libs/actions/Link'; import Icon from '../../components/Icon'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import * as Expensicons from '../../components/Icon/Expensicons'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import styles from '../../styles/styles'; import variables from '../../styles/variables'; import CONST from '../../CONST'; diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index e0ccdca636ab..a68f99df6d24 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -7,7 +7,7 @@ import lodashGet from 'lodash/get'; import styles from '../../../styles/styles'; import Button from '../../../components/Button'; import Text from '../../../components/Text'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import * as Session from '../../../libs/actions/Session'; import ONYXKEYS from '../../../ONYXKEYS'; import CONST from '../../../CONST'; diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js index 6073836c6e9a..98ef04836f76 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -10,7 +10,7 @@ import styles from '../../styles/styles'; import compose from '../../libs/compose'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; import * as Expensicons from '../../components/Icon/Expensicons'; -import themeColors from '../../styles/themes/dark'; +import themeColors from '../../styles/themes/default'; import * as PolicyUtils from '../../libs/PolicyUtils'; import MenuItem from '../../components/MenuItem'; import * as Policy from '../../libs/actions/Policy'; diff --git a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js index 8f48fc216bfd..eb8305f23140 100644 --- a/src/pages/workspace/reimburse/WorkspaceReimburseSection.js +++ b/src/pages/workspace/reimburse/WorkspaceReimburseSection.js @@ -5,7 +5,7 @@ import lodashGet from 'lodash/get'; import _ from 'underscore'; import Text from '../../../components/Text'; import styles from '../../../styles/styles'; -import themeColors from '../../../styles/themes/dark'; +import themeColors from '../../../styles/themes/default'; import * as Expensicons from '../../../components/Icon/Expensicons'; import * as Illustrations from '../../../components/Icon/Illustrations'; import Section from '../../../components/Section'; diff --git a/src/stories/Composer.stories.js b/src/stories/Composer.stories.js index 632901df582f..3dfc5b0e3ead 100644 --- a/src/stories/Composer.stories.js +++ b/src/stories/Composer.stories.js @@ -5,7 +5,7 @@ import Composer from '../components/Composer'; import RenderHTML from '../components/RenderHTML'; import Text from '../components/Text'; import styles from '../styles/styles'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; import * as StyleUtils from '../styles/StyleUtils'; import CONST from '../CONST'; diff --git a/src/stories/PopoverMenu.stories.js b/src/stories/PopoverMenu.stories.js index 198c7ef28e83..1098fa9ce226 100644 --- a/src/stories/PopoverMenu.stories.js +++ b/src/stories/PopoverMenu.stories.js @@ -3,7 +3,7 @@ import {SafeAreaProvider} from 'react-native-safe-area-context'; import PopoverMenu from '../components/PopoverMenu'; import * as Expensicons from '../components/Icon/Expensicons'; import MenuItem from '../components/MenuItem'; -import themeColors from '../styles/themes/dark'; +import themeColors from '../styles/themes/default'; /** * We use the Component Story Format for writing stories. Follow the docs here: diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js index 676796751dc3..f8db7272e93f 100644 --- a/src/styles/StyleUtils.js +++ b/src/styles/StyleUtils.js @@ -1,7 +1,7 @@ import _ from 'underscore'; import CONST from '../CONST'; import fontFamily from './fontFamily'; -import themeColors from './themes/dark'; +import themeColors from './themes/default'; import variables from './variables'; import colors from './colors'; import positioning from './utilities/positioning'; diff --git a/src/styles/addOutlineWidth/index.js b/src/styles/addOutlineWidth/index.js index cdb7dce7f926..2a2657b24910 100644 --- a/src/styles/addOutlineWidth/index.js +++ b/src/styles/addOutlineWidth/index.js @@ -3,7 +3,7 @@ * can be added to the object */ -import themeDefault from '../themes/dark'; +import themeDefault from '../themes/default'; /** * Adds the addOutlineWidth property to an object to be used when styling diff --git a/src/styles/getModalStyles/getBaseModalStyles.js b/src/styles/getModalStyles/getBaseModalStyles.js index 7daf944225c5..b7a3317963ca 100644 --- a/src/styles/getModalStyles/getBaseModalStyles.js +++ b/src/styles/getModalStyles/getBaseModalStyles.js @@ -1,6 +1,6 @@ import CONST from '../../CONST'; import variables from '../variables'; -import themeColors from '../themes/dark'; +import themeColors from '../themes/default'; import styles from '../styles'; const getCenteredModalStyles = (windowWidth, isSmallScreenWidth, isFullScreenWhenSmall = false) => ({ diff --git a/src/styles/getReportActionContextMenuStyles.js b/src/styles/getReportActionContextMenuStyles.js index b8084abad976..026306084ce4 100644 --- a/src/styles/getReportActionContextMenuStyles.js +++ b/src/styles/getReportActionContextMenuStyles.js @@ -1,6 +1,6 @@ import styles from './styles'; import variables from './variables'; -import themeColors from './themes/dark'; +import themeColors from './themes/default'; const defaultWrapperStyle = { backgroundColor: themeColors.componentBG, diff --git a/src/styles/getTooltipStyles.js b/src/styles/getTooltipStyles.js index 58ec3f200b2d..bc5fcfe807aa 100644 --- a/src/styles/getTooltipStyles.js +++ b/src/styles/getTooltipStyles.js @@ -1,7 +1,7 @@ import spacing from './utilities/spacing'; import styles from './styles'; import colors from './colors'; -import themeColors from './themes/dark'; +import themeColors from './themes/default'; import fontFamily from './fontFamily'; import variables from './variables'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; diff --git a/src/styles/styles.js b/src/styles/styles.js index fe47cc70c66a..835082ba98c9 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2,7 +2,7 @@ import {defaultStyles as defaultPickerStyles} from 'react-native-picker-select/s import lodashClamp from 'lodash/clamp'; import fontFamily from './fontFamily'; import addOutlineWidth from './addOutlineWidth'; -import themeColors from './themes/dark'; +import themeColors from './themes/default'; import fontWeightBold from './fontWeight/bold'; import variables from './variables'; import spacing from './utilities/spacing'; diff --git a/src/styles/themes/ThemeContext.js b/src/styles/themes/ThemeContext.js index e75c48bcab1e..12877a40ab96 100644 --- a/src/styles/themes/ThemeContext.js +++ b/src/styles/themes/ThemeContext.js @@ -1,5 +1,5 @@ import React from 'react'; -import darkTheme from './dark'; +import darkTheme from './default'; const ThemeContext = React.createContext(darkTheme); diff --git a/src/styles/themes/ThemeProvider.js b/src/styles/themes/ThemeProvider.js index 2dd92e8be729..d32a6d203cc5 100644 --- a/src/styles/themes/ThemeProvider.js +++ b/src/styles/themes/ThemeProvider.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import ThemeContext from './ThemeContext'; import useThemePreference from './useThemePreference'; import CONST from '../../CONST'; -import darkTheme from './dark'; +import darkTheme from './default'; import lightTheme from './light'; const propTypes = { diff --git a/src/styles/themes/dark.ts b/src/styles/themes/default.ts similarity index 100% rename from src/styles/themes/dark.ts rename to src/styles/themes/default.ts From 2ba934cdcf327b814af931971348f1c55d0d36d1 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 16 Sep 2023 11:56:37 +0200 Subject: [PATCH 007/218] add TODO comment about renaming default.ts --- src/styles/themes/default.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index 70f8a9f77b8f..f7087ad558b5 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -1,3 +1,5 @@ +// TODO: For consistency reasons, rename this file to "dark.ts" after theme switching migration is done (GH issue:) + /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; From 4b10b95afb44061921d9a1482d540dbbf45cf4f2 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 16 Sep 2023 12:00:20 +0200 Subject: [PATCH 008/218] fix: update outdated comment --- src/styles/colors.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/styles/colors.ts b/src/styles/colors.ts index b17f7fb4cb96..2d13f930ff0e 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -1,7 +1,9 @@ import {Color} from './themes/ThemeColors'; /** - * DO NOT import colors.js into files. Use ../themes/default.js instead. + * DO NOT import colors.js into files. Use the theme switching hooks and HOCs instead. + * For functional components, you can use the `useTheme` and `useThemeStyles` hooks + * For class components, you can use the `withTheme` and `withThemeStyles` HOCs */ const colors: Record = { black: '#000000', From 84c47315178f4ea68b1a59438084834e7dfce367 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 16 Sep 2023 12:01:54 +0200 Subject: [PATCH 009/218] fix: add TODO comment --- src/styles/colors.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/colors.ts b/src/styles/colors.ts index 2d13f930ff0e..5b05d16bb9b3 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -6,6 +6,7 @@ import {Color} from './themes/ThemeColors'; * For class components, you can use the `withTheme` and `withThemeStyles` HOCs */ const colors: Record = { + // TODO: Find a good name/description for this block of colors. black: '#000000', white: '#FFFFFF', ivory: '#fffaf0', From 429e2b664ea9a3632ba771f12fd8e1dbd3a51727 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 16 Sep 2023 12:28:03 +0200 Subject: [PATCH 010/218] fix: add types for the rest of theme switching logic --- ...StylesContext.ts => ThemeStylesContext.js} | 0 src/styles/ThemeStylesProvider.tsx | 3 ++- src/styles/themes/ThemeColors.ts | 4 +++- src/styles/themes/ThemeContext.js | 6 ------ src/styles/themes/ThemeContext.ts | 7 +++++++ .../{ThemeProvider.js => ThemeProvider.tsx} | 2 +- src/styles/themes/default.ts | 2 +- src/styles/themes/light.ts | 2 +- src/styles/themes/useTheme.js | 14 ------------- src/styles/themes/useTheme.ts | 15 +++++++++++++ ...emePreference.js => useThemePreference.ts} | 21 ++++++++++++------- 11 files changed, 43 insertions(+), 33 deletions(-) rename src/styles/{ThemeStylesContext.ts => ThemeStylesContext.js} (100%) delete mode 100644 src/styles/themes/ThemeContext.js create mode 100644 src/styles/themes/ThemeContext.ts rename src/styles/themes/{ThemeProvider.js => ThemeProvider.tsx} (93%) delete mode 100644 src/styles/themes/useTheme.js create mode 100644 src/styles/themes/useTheme.ts rename src/styles/themes/{useThemePreference.js => useThemePreference.ts} (53%) diff --git a/src/styles/ThemeStylesContext.ts b/src/styles/ThemeStylesContext.js similarity index 100% rename from src/styles/ThemeStylesContext.ts rename to src/styles/ThemeStylesContext.js diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index d0db784ca8ca..1c5c8cf3437a 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -4,8 +4,9 @@ import useTheme from './themes/useTheme'; import ThemeStylesContext from './ThemeStylesContext'; // TODO: Rename this to "styles" once the app is migrated to theme switching hooks and HOCs import {stylesGenerator as stylesUntyped} from './styles'; +import ThemeColors from './themes/ThemeColors'; -const styles = stylesUntyped as (theme: Record) => Record; +const styles = stylesUntyped as (theme: ThemeColors) => Record; type ThemeStylesProviderProps = { children: React.ReactNode; diff --git a/src/styles/themes/ThemeColors.ts b/src/styles/themes/ThemeColors.ts index 555e7ccb2274..382449ebdf62 100644 --- a/src/styles/themes/ThemeColors.ts +++ b/src/styles/themes/ThemeColors.ts @@ -83,4 +83,6 @@ type ThemeColors = ThemeColorsWithoutPageBackgroundColors & { PAGE_BACKGROUND_COLORS: Record; }; -export {type Color, type ThemeColors, type ThemeColorsWithoutPageBackgroundColors}; +export default ThemeColors; + +export {type Color, type ThemeColorsWithoutPageBackgroundColors}; diff --git a/src/styles/themes/ThemeContext.js b/src/styles/themes/ThemeContext.js deleted file mode 100644 index 12877a40ab96..000000000000 --- a/src/styles/themes/ThemeContext.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import darkTheme from './default'; - -const ThemeContext = React.createContext(darkTheme); - -export default ThemeContext; diff --git a/src/styles/themes/ThemeContext.ts b/src/styles/themes/ThemeContext.ts new file mode 100644 index 000000000000..98bed9ce822c --- /dev/null +++ b/src/styles/themes/ThemeContext.ts @@ -0,0 +1,7 @@ +import React from 'react'; +import darkTheme from './default'; +import ThemeColors from './ThemeColors'; + +const ThemeContext = React.createContext(darkTheme); + +export default ThemeContext; diff --git a/src/styles/themes/ThemeProvider.js b/src/styles/themes/ThemeProvider.tsx similarity index 93% rename from src/styles/themes/ThemeProvider.js rename to src/styles/themes/ThemeProvider.tsx index d32a6d203cc5..e4e316a16af1 100644 --- a/src/styles/themes/ThemeProvider.js +++ b/src/styles/themes/ThemeProvider.tsx @@ -12,7 +12,7 @@ const propTypes = { children: PropTypes.node.isRequired, }; -function ThemeProvider(props) { +function ThemeProvider(props: React.PropsWithChildren) { const themePreference = useThemePreference(); const theme = useMemo(() => (themePreference === CONST.THEME.LIGHT ? lightTheme : darkTheme), [themePreference]); diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index f7087ad558b5..f8b8e7df10b9 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -3,7 +3,7 @@ /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import {ThemeColors, ThemeColorsWithoutPageBackgroundColors} from './ThemeColors'; +import ThemeColors, {ThemeColorsWithoutPageBackgroundColors} from './ThemeColors'; const darkThemeWithoutPageBackgroundColors = { // Figma keys diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index 499e1bbf127c..9b9b897acb1e 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -1,6 +1,6 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import {ThemeColors, ThemeColorsWithoutPageBackgroundColors} from './ThemeColors'; +import ThemeColors, {ThemeColorsWithoutPageBackgroundColors} from './ThemeColors'; const lightThemeWithoutPageBackgroundColors = { // Figma keys diff --git a/src/styles/themes/useTheme.js b/src/styles/themes/useTheme.js deleted file mode 100644 index 8e88b23a7688..000000000000 --- a/src/styles/themes/useTheme.js +++ /dev/null @@ -1,14 +0,0 @@ -import {useContext} from 'react'; -import ThemeContext from './ThemeContext'; - -function useTheme() { - const theme = useContext(ThemeContext); - - if (!theme) { - throw new Error('StylesContext was null! Are you sure that you wrapped the component under a ?'); - } - - return theme; -} - -export default useTheme; diff --git a/src/styles/themes/useTheme.ts b/src/styles/themes/useTheme.ts new file mode 100644 index 000000000000..51ee044d59c1 --- /dev/null +++ b/src/styles/themes/useTheme.ts @@ -0,0 +1,15 @@ +import {useContext} from 'react'; +import ThemeContext from './ThemeContext'; +import ThemeColors from './ThemeColors'; + +function useTheme(): ThemeColors { + const theme = useContext(ThemeContext); + + if (!theme) { + throw new Error('ThemeContext was null! Are you sure that you wrapped the component under a ?'); + } + + return theme; +} + +export default useTheme; diff --git a/src/styles/themes/useThemePreference.js b/src/styles/themes/useThemePreference.ts similarity index 53% rename from src/styles/themes/useThemePreference.js rename to src/styles/themes/useThemePreference.ts index fbb557423f10..8f68d55143a5 100644 --- a/src/styles/themes/useThemePreference.js +++ b/src/styles/themes/useThemePreference.ts @@ -1,26 +1,31 @@ import {useState, useEffect, useContext} from 'react'; -import {Appearance} from 'react-native'; +import {Appearance, ColorSchemeName} from 'react-native'; import CONST from '../../CONST'; import {PreferredThemeContext} from '../../components/OnyxProvider'; +// TODO: Remove this once "OnyxProvider" is typed +type PreferredThemeContextType = React.Context<(typeof CONST.THEME)[keyof typeof CONST.THEME]>; + +type ThemePreference = typeof CONST.THEME.LIGHT | typeof CONST.THEME.DARK; + function useThemePreference() { - const [themePreference, setThemePreference] = useState(CONST.THEME.DEFAULT); - const [systemTheme, setSystemTheme] = useState(); - const preferredThemeContext = useContext(PreferredThemeContext); + const [themePreference, setThemePreference] = useState(CONST.THEME.DEFAULT); + const [systemTheme, setSystemTheme] = useState(); + const preferredThemeFromStorage = useContext(PreferredThemeContext as PreferredThemeContextType); useEffect(() => { // This is used for getting the system theme, that can be set in the OS's theme settings. This will always return either "light" or "dark" and will update automatically if the OS theme changes. const systemThemeSubscription = Appearance.addChangeListener(({colorScheme}) => setSystemTheme(colorScheme)); - return systemThemeSubscription.remove; + return () => systemThemeSubscription.remove(); }, []); useEffect(() => { - const theme = preferredThemeContext || CONST.THEME.DEFAULT; + const theme = preferredThemeFromStorage || CONST.THEME.DEFAULT; // If the user chooses to use the device theme settings, we need to set the theme preference to the system theme - if (theme === CONST.THEME.SYSTEM) setThemePreference(systemTheme); + if (theme === CONST.THEME.SYSTEM) setThemePreference(systemTheme ?? CONST.THEME.DEFAULT); else setThemePreference(theme); - }, [preferredThemeContext, systemTheme]); + }, [preferredThemeFromStorage, systemTheme]); return themePreference; } From b7b1179f4bce3f0ca23ad471f14875d1f424a3a4 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Sat, 16 Sep 2023 12:28:48 +0200 Subject: [PATCH 011/218] fix: type remaining file with TODO --- src/styles/ThemeStylesContext.js | 6 ------ src/styles/ThemeStylesContext.ts | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 src/styles/ThemeStylesContext.js create mode 100644 src/styles/ThemeStylesContext.ts diff --git a/src/styles/ThemeStylesContext.js b/src/styles/ThemeStylesContext.js deleted file mode 100644 index 1c81ab3b39a5..000000000000 --- a/src/styles/ThemeStylesContext.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import styles from './styles'; - -const ThemeStylesContext = React.createContext(styles); - -export default ThemeStylesContext; diff --git a/src/styles/ThemeStylesContext.ts b/src/styles/ThemeStylesContext.ts new file mode 100644 index 000000000000..c32f994e16da --- /dev/null +++ b/src/styles/ThemeStylesContext.ts @@ -0,0 +1,7 @@ +import React from 'react'; +import styles from './styles'; + +// TODO: Change "uknown" once "styles.js" is typed +const ThemeStylesContext = React.createContext(styles); + +export default ThemeStylesContext; From f69116a29911d67ea1257b90392164e70aa32d2e Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 10:23:30 +0200 Subject: [PATCH 012/218] fix: remove theme spreading and abstraction --- src/styles/themes/ThemeColors.ts | 6 ++---- src/styles/themes/default.ts | 9 +++------ src/styles/themes/light.ts | 9 +++------ 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/styles/themes/ThemeColors.ts b/src/styles/themes/ThemeColors.ts index 382449ebdf62..19840a05491b 100644 --- a/src/styles/themes/ThemeColors.ts +++ b/src/styles/themes/ThemeColors.ts @@ -1,6 +1,6 @@ type Color = string; -type ThemeColorsWithoutPageBackgroundColors = { +type ThemeColors = { // Figma keys appBG: Color; splashBG: Color; @@ -77,12 +77,10 @@ type ThemeColorsWithoutPageBackgroundColors = { skeletonLHNIn: Color; skeletonLHNOut: Color; QRLogo: Color; -}; -type ThemeColors = ThemeColorsWithoutPageBackgroundColors & { PAGE_BACKGROUND_COLORS: Record; }; export default ThemeColors; -export {type Color, type ThemeColorsWithoutPageBackgroundColors}; +export {type Color}; diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index f8b8e7df10b9..b8eccb761bfa 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -3,9 +3,9 @@ /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import ThemeColors, {ThemeColorsWithoutPageBackgroundColors} from './ThemeColors'; +import ThemeColors from './ThemeColors'; -const darkThemeWithoutPageBackgroundColors = { +const darkTheme = { // Figma keys appBG: colors.darkAppBackground, splashBG: colors.green400, @@ -82,12 +82,9 @@ const darkThemeWithoutPageBackgroundColors = { skeletonLHNIn: colors.darkBorders, skeletonLHNOut: colors.darkDefaultButton, QRLogo: colors.green400, -} satisfies ThemeColorsWithoutPageBackgroundColors; -const darkTheme = { - ...darkThemeWithoutPageBackgroundColors, PAGE_BACKGROUND_COLORS: { - [SCREENS.HOME]: darkThemeWithoutPageBackgroundColors.sidebar, + [SCREENS.HOME]: colors.darkHighlightBackground, [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, }, } satisfies ThemeColors; diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index 9b9b897acb1e..f5edb41e6094 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -1,8 +1,8 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import ThemeColors, {ThemeColorsWithoutPageBackgroundColors} from './ThemeColors'; +import ThemeColors from './ThemeColors'; -const lightThemeWithoutPageBackgroundColors = { +const lightTheme = { // Figma keys appBG: colors.lightAppBackground, splashBG: colors.green400, @@ -79,12 +79,9 @@ const lightThemeWithoutPageBackgroundColors = { skeletonLHNIn: colors.lightBorders, skeletonLHNOut: colors.lightDefaultButtonPressed, QRLogo: colors.green500, -} satisfies ThemeColorsWithoutPageBackgroundColors; -const lightTheme = { - ...lightThemeWithoutPageBackgroundColors, PAGE_BACKGROUND_COLORS: { - [SCREENS.HOME]: lightThemeWithoutPageBackgroundColors.sidebar, + [SCREENS.HOME]: colors.lightBorders, [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, }, } satisfies ThemeColors; From 08f5b4fd9101053e38db965c839acbe576fa6e17 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 10:37:51 +0200 Subject: [PATCH 013/218] fix: changed colors --- src/styles/themes/ThemeColors.ts | 5 ++++- src/styles/themes/default.ts | 4 +++- src/styles/themes/light.ts | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/styles/themes/ThemeColors.ts b/src/styles/themes/ThemeColors.ts index 19840a05491b..f4cdc3445c1f 100644 --- a/src/styles/themes/ThemeColors.ts +++ b/src/styles/themes/ThemeColors.ts @@ -16,6 +16,7 @@ type ThemeColors = { iconColorfulBackground: Color; textSupporting: Color; text: Color; + textColorfulBackground: Color; link: Color; linkHover: Color; buttonDefaultBG: Color; @@ -59,7 +60,8 @@ type ThemeColors = { heroCard: Color; uploadPreviewActivityIndicator: Color; dropUIBG: Color; - dropTransparentOverlay: Color; + receiptDropUIBG?: Color; + dropTransparentOverlay?: Color; checkBox: Color; pickerOptionsTextColor: Color; imageCropBackgroundColor: Color; @@ -77,6 +79,7 @@ type ThemeColors = { skeletonLHNIn: Color; skeletonLHNOut: Color; QRLogo: Color; + starDefaultBG: Color; PAGE_BACKGROUND_COLORS: Record; }; diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index b8eccb761bfa..f521efba6047 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -21,6 +21,7 @@ const darkTheme = { iconColorfulBackground: `${colors.ivory}cc`, textSupporting: colors.darkSupportingText, text: colors.darkPrimaryText, + textColorfulBackground: colors.ivory, link: colors.blue300, linkHover: colors.blue100, buttonDefaultBG: colors.darkDefaultButton, @@ -64,7 +65,7 @@ const darkTheme = { heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.darkHighlightBackground, dropUIBG: 'rgba(6,27,9,0.92)', - dropTransparentOverlay: 'rgba(255,255,255,0)', + receiptDropUIBG: 'rgba(3, 212, 124, 0.84)', checkBox: colors.green400, pickerOptionsTextColor: colors.darkPrimaryText, imageCropBackgroundColor: colors.darkIcons, @@ -82,6 +83,7 @@ const darkTheme = { skeletonLHNIn: colors.darkBorders, skeletonLHNOut: colors.darkDefaultButton, QRLogo: colors.green400, + starDefaultBG: 'rgb(254, 228, 94)', PAGE_BACKGROUND_COLORS: { [SCREENS.HOME]: colors.darkHighlightBackground, diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index f5edb41e6094..d9c2eef42840 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -18,6 +18,7 @@ const lightTheme = { iconColorfulBackground: `${colors.ivory}cc`, textSupporting: colors.lightSupportingText, text: colors.lightPrimaryText, + textColorfulBackground: colors.ivory, link: colors.blue600, linkHover: colors.blue500, buttonDefaultBG: colors.lightDefaultButton, @@ -78,7 +79,8 @@ const lightTheme = { tooltipPrimaryText: colors.darkPrimaryText, skeletonLHNIn: colors.lightBorders, skeletonLHNOut: colors.lightDefaultButtonPressed, - QRLogo: colors.green500, + QRLogo: colors.green400, + starDefaultBG: 'rgb(254, 228, 94)', PAGE_BACKGROUND_COLORS: { [SCREENS.HOME]: colors.lightBorders, From fd4e5aa701d0400d9e7393fabaa086bea3b46ee3 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 10:41:24 +0200 Subject: [PATCH 014/218] fix: remove more diffs to main --- src/styles/themes/default.ts | 5 +++++ src/styles/themes/light.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index f521efba6047..ce96213a6bd5 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -4,6 +4,7 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; import ThemeColors from './ThemeColors'; +import ROUTES from '../../ROUTES'; const darkTheme = { // Figma keys @@ -88,6 +89,10 @@ const darkTheme = { PAGE_BACKGROUND_COLORS: { [SCREENS.HOME]: colors.darkHighlightBackground, [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + [ROUTES.SETTINGS_STATUS]: colors.green700, + [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, + [ROUTES.SETTINGS_SECURITY]: colors.ice500, }, } satisfies ThemeColors; diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index d9c2eef42840..154c717dee89 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -1,6 +1,7 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; import ThemeColors from './ThemeColors'; +import ROUTES from '../../ROUTES'; const lightTheme = { // Figma keys @@ -85,6 +86,10 @@ const lightTheme = { PAGE_BACKGROUND_COLORS: { [SCREENS.HOME]: colors.lightBorders, [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, + [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, + [ROUTES.SETTINGS_STATUS]: colors.green700, + [ROUTES.I_KNOW_A_TEACHER]: colors.tangerine800, + [ROUTES.SETTINGS_SECURITY]: colors.ice500, }, } satisfies ThemeColors; From c794fd19a17285281137c4b59c02b154e628c2af Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 10:43:49 +0200 Subject: [PATCH 015/218] fix: typo --- src/styles/ThemeStylesContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/ThemeStylesContext.ts b/src/styles/ThemeStylesContext.ts index c32f994e16da..49f9eb127af9 100644 --- a/src/styles/ThemeStylesContext.ts +++ b/src/styles/ThemeStylesContext.ts @@ -1,7 +1,7 @@ import React from 'react'; import styles from './styles'; -// TODO: Change "uknown" once "styles.js" is typed +// TODO: Change "unknown" once "styles.js" is typed const ThemeStylesContext = React.createContext(styles); export default ThemeStylesContext; From 10f4778e1a75132a4864400192dcf8b770219f10 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 10:44:48 +0200 Subject: [PATCH 016/218] update todo comments --- src/styles/ThemeStylesProvider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index 1c5c8cf3437a..37f43c2c4f3f 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -2,10 +2,11 @@ import React, {useMemo} from 'react'; import useTheme from './themes/useTheme'; import ThemeStylesContext from './ThemeStylesContext'; -// TODO: Rename this to "styles" once the app is migrated to theme switching hooks and HOCs +// TODO: Rename this to "styles" once the styles are fully typed import {stylesGenerator as stylesUntyped} from './styles'; import ThemeColors from './themes/ThemeColors'; +// TODO: Remove this once the styles are fully typed const styles = stylesUntyped as (theme: ThemeColors) => Record; type ThemeStylesProviderProps = { From dba25a2a693737b6261cd6e73a46d07cdd9fc253 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 10:45:43 +0200 Subject: [PATCH 017/218] fix: naming --- src/styles/useThemeStyles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/useThemeStyles.ts b/src/styles/useThemeStyles.ts index a5b3baebbaec..69ba43692f49 100644 --- a/src/styles/useThemeStyles.ts +++ b/src/styles/useThemeStyles.ts @@ -5,7 +5,7 @@ function useThemeStyles() { const themeStyles = useContext(ThemeStylesContext); if (!themeStyles) { - throw new Error('StylesContext was null! Are you sure that you wrapped the component under a ?'); + throw new Error('ThemeStylesContext was null! Are you sure that you wrapped the component under a ?'); } // TODO: Remove this "eslint-disable-next" once the theme switching migration is done and styles are fully typed (GH Issue: https://github.com/Expensify/App/issues/27337) From 0bbcd568dced2640225b31ec018dc39e278aa345 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 16:31:53 +0200 Subject: [PATCH 018/218] fix: update styles.ts --- src/styles/styles.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 632c02530e23..aa66d1a51c58 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -18,7 +18,6 @@ import overflowXHidden from './overflowXHidden'; import pointerEventsAuto from './pointerEventsAuto'; import pointerEventsNone from './pointerEventsNone'; import defaultTheme from './themes/default'; -import {ThemeDefault} from './themes/types'; import cursor from './utilities/cursor'; import display from './utilities/display'; import flex from './utilities/flex'; @@ -33,6 +32,7 @@ import whiteSpace from './utilities/whiteSpace'; import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; import variables from './variables'; +import ThemeColors from './themes/ThemeColors'; type AnchorPosition = { horizontal: number; @@ -76,7 +76,7 @@ const touchCalloutNone: Pick = Browser.isMobile // to prevent vertical text offset in Safari for badges, new lineHeight values have been added const lineHeightBadge: Pick = Browser.isSafari() ? {lineHeight: variables.lineHeightXSmall} : {lineHeight: variables.lineHeightNormal}; -const picker = (theme: ThemeDefault) => +const picker = (theme: ThemeColors) => ({ backgroundColor: theme.transparent, color: theme.text, @@ -92,14 +92,14 @@ const picker = (theme: ThemeDefault) => textAlign: 'left', } satisfies TextStyle); -const link = (theme: ThemeDefault) => +const link = (theme: ThemeColors) => ({ color: theme.link, textDecorationColor: theme.link, fontFamily: fontFamily.EXP_NEUE, } satisfies ViewStyle & MixedStyleDeclaration); -const baseCodeTagStyles = (theme: ThemeDefault) => +const baseCodeTagStyles = (theme: ThemeColors) => ({ borderWidth: 1, borderRadius: 5, @@ -112,7 +112,7 @@ const headlineFont = { fontWeight: '500', } satisfies TextStyle; -const webViewStyles = (theme: ThemeDefault) => +const webViewStyles = (theme: ThemeColors) => ({ // As of react-native-render-html v6, don't declare distinct styles for // custom renderers, the API for custom renderers has changed. Declare the @@ -206,7 +206,7 @@ const webViewStyles = (theme: ThemeDefault) => }, } satisfies WebViewStyle); -const styles = (theme: ThemeDefault) => +const styles = (theme: ThemeColors) => ({ // Add all of our utility and helper styles ...spacing, From 2b6f41222ca7190095b2be743558e046c0089c6f Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 19 Sep 2023 16:37:00 +0200 Subject: [PATCH 019/218] feat: update types int ThemeStylesProvider logic --- src/styles/ThemeStylesContext.ts | 5 ++--- src/styles/ThemeStylesProvider.tsx | 3 +-- src/styles/styles.ts | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/styles/ThemeStylesContext.ts b/src/styles/ThemeStylesContext.ts index 49f9eb127af9..3df2b19b31bf 100644 --- a/src/styles/ThemeStylesContext.ts +++ b/src/styles/ThemeStylesContext.ts @@ -1,7 +1,6 @@ import React from 'react'; -import styles from './styles'; +import styles, {type Styles} from './styles'; -// TODO: Change "unknown" once "styles.js" is typed -const ThemeStylesContext = React.createContext(styles); +const ThemeStylesContext = React.createContext(styles); export default ThemeStylesContext; diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index 09b7fd2837be..581edab55f3f 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -2,9 +2,8 @@ import React, {useMemo} from 'react'; import useTheme from './themes/useTheme'; import ThemeStylesContext from './ThemeStylesContext'; -// TODO: Rename this to "styles" once the styles are fully typed +// TODO: Replace this import with "styles" once the static style export from "styles.js" isn't used anymore import {stylesGenerator} from './styles'; -import ThemeColors from './themes/ThemeColors'; type ThemeStylesProviderProps = { children: React.ReactNode; diff --git a/src/styles/styles.ts b/src/styles/styles.ts index aa66d1a51c58..e2db4989661c 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -4054,4 +4054,4 @@ const stylesGenerator = styles; const defaultStyles = styles(defaultTheme); export default defaultStyles; -export {stylesGenerator}; +export {stylesGenerator, type Styles}; From e4d4a107a271619e8746bb0ba82441b95a06b57f Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 20 Sep 2023 08:30:55 +0200 Subject: [PATCH 020/218] fix: merge main --- src/styles/styles.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/styles/styles.ts b/src/styles/styles.ts index e2db4989661c..e15779f78d0d 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -3743,27 +3743,26 @@ const styles = (theme: ThemeColors) => }, loginButtonRow: { - justifyContent: 'center', width: '100%', + gap: 12, ...flex.flexRow, + ...flex.justifyContentCenter, }, loginButtonRowSmallScreen: { - justifyContent: 'center', width: '100%', - marginBottom: 10, + gap: 12, ...flex.flexRow, + ...flex.justifyContentCenter, + marginBottom: 10, }, - appleButtonContainer: { + desktopSignInButtonContainer: { width: 40, height: 40, - marginRight: 20, }, signInIconButton: { - margin: 10, - marginTop: 0, padding: 2, }, @@ -3771,7 +3770,6 @@ const styles = (theme: ThemeColors) => colorScheme: 'light', width: 40, height: 40, - marginLeft: 12, alignItems: 'center', overflow: 'hidden', }, From 3f2830063ab69f0e0aa8ea2bc03e25189215e287 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 20 Sep 2023 08:31:01 +0200 Subject: [PATCH 021/218] enforce consistent themes --- src/styles/themes/ThemeColors.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/themes/ThemeColors.ts b/src/styles/themes/ThemeColors.ts index f4cdc3445c1f..2347cbd3633f 100644 --- a/src/styles/themes/ThemeColors.ts +++ b/src/styles/themes/ThemeColors.ts @@ -61,7 +61,6 @@ type ThemeColors = { uploadPreviewActivityIndicator: Color; dropUIBG: Color; receiptDropUIBG?: Color; - dropTransparentOverlay?: Color; checkBox: Color; pickerOptionsTextColor: Color; imageCropBackgroundColor: Color; From fb20af781e1c2c8425fadd49cad2f248a4e6d0b1 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 20 Sep 2023 08:39:36 +0200 Subject: [PATCH 022/218] fix: move ThemeColors to types.ts --- src/styles/colors.ts | 2 +- src/styles/styles.ts | 2 +- src/styles/themes/ThemeColors.ts | 88 ------------------------------- src/styles/themes/ThemeContext.ts | 2 +- src/styles/themes/default.ts | 2 +- src/styles/themes/light.ts | 2 +- src/styles/themes/types.ts | 88 +++++++++++++++++++++++++++++-- src/styles/themes/useTheme.ts | 2 +- 8 files changed, 89 insertions(+), 99 deletions(-) delete mode 100644 src/styles/themes/ThemeColors.ts diff --git a/src/styles/colors.ts b/src/styles/colors.ts index 5b05d16bb9b3..aa12699ebdea 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -1,4 +1,4 @@ -import {Color} from './themes/ThemeColors'; +import {Color} from './themes/types'; /** * DO NOT import colors.js into files. Use the theme switching hooks and HOCs instead. diff --git a/src/styles/styles.ts b/src/styles/styles.ts index e15779f78d0d..ce3e2938d26d 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -32,7 +32,7 @@ import whiteSpace from './utilities/whiteSpace'; import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; import variables from './variables'; -import ThemeColors from './themes/ThemeColors'; +import {ThemeColors} from './themes/types'; type AnchorPosition = { horizontal: number; diff --git a/src/styles/themes/ThemeColors.ts b/src/styles/themes/ThemeColors.ts deleted file mode 100644 index 2347cbd3633f..000000000000 --- a/src/styles/themes/ThemeColors.ts +++ /dev/null @@ -1,88 +0,0 @@ -type Color = string; - -type ThemeColors = { - // Figma keys - appBG: Color; - splashBG: Color; - highlightBG: Color; - border: Color; - borderLighter: Color; - borderFocus: Color; - icon: Color; - iconMenu: Color; - iconHovered: Color; - iconSuccessFill: Color; - iconReversed: Color; - iconColorfulBackground: Color; - textSupporting: Color; - text: Color; - textColorfulBackground: Color; - link: Color; - linkHover: Color; - buttonDefaultBG: Color; - buttonHoveredBG: Color; - buttonPressedBG: Color; - danger: Color; - dangerHover: Color; - dangerPressed: Color; - warning: Color; - success: Color; - successHover: Color; - successPressed: Color; - transparent: Color; - signInPage: Color; - - // Additional keys - overlay: Color; - inverse: Color; - shadow: Color; - componentBG: Color; - hoverComponentBG: Color; - activeComponentBG: Color; - signInSidebar: Color; - sidebar: Color; - sidebarHover: Color; - heading: Color; - textLight: Color; - textDark: Color; - textReversed: Color; - textBackground: Color; - textMutedReversed: Color; - textError: Color; - offline: Color; - modalBackdrop: Color; - modalBackground: Color; - cardBG: Color; - cardBorder: Color; - spinner: Color; - unreadIndicator: Color; - placeholderText: Color; - heroCard: Color; - uploadPreviewActivityIndicator: Color; - dropUIBG: Color; - receiptDropUIBG?: Color; - checkBox: Color; - pickerOptionsTextColor: Color; - imageCropBackgroundColor: Color; - fallbackIconColor: Color; - reactionActiveBackground: Color; - reactionActiveText: Color; - badgeAdHoc: Color; - badgeAdHocHover: Color; - mentionText: Color; - mentionBG: Color; - ourMentionText: Color; - ourMentionBG: Color; - tooltipSupportingText: Color; - tooltipPrimaryText: Color; - skeletonLHNIn: Color; - skeletonLHNOut: Color; - QRLogo: Color; - starDefaultBG: Color; - - PAGE_BACKGROUND_COLORS: Record; -}; - -export default ThemeColors; - -export {type Color}; diff --git a/src/styles/themes/ThemeContext.ts b/src/styles/themes/ThemeContext.ts index 98bed9ce822c..8c57cc9c7e9f 100644 --- a/src/styles/themes/ThemeContext.ts +++ b/src/styles/themes/ThemeContext.ts @@ -1,6 +1,6 @@ import React from 'react'; import darkTheme from './default'; -import ThemeColors from './ThemeColors'; +import {ThemeColors} from './types'; const ThemeContext = React.createContext(darkTheme); diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index ce96213a6bd5..2fa17b832a72 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -3,7 +3,7 @@ /* eslint-disable no-unused-vars */ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import ThemeColors from './ThemeColors'; +import {ThemeColors} from './types'; import ROUTES from '../../ROUTES'; const darkTheme = { diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index 0a9fda7344b8..9b39a94f02a6 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -1,6 +1,6 @@ import colors from '../colors'; import SCREENS from '../../SCREENS'; -import ThemeColors from './ThemeColors'; +import {ThemeColors} from './types'; import ROUTES from '../../ROUTES'; const lightTheme = { diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts index 40b8da361654..1ffe3e776a7e 100644 --- a/src/styles/themes/types.ts +++ b/src/styles/themes/types.ts @@ -1,8 +1,86 @@ -import DeepRecord from '../../types/utils/DeepRecord'; -import defaultTheme from './default'; +type Color = string; -type ThemeBase = DeepRecord; +type ThemeColors = { + // Figma keys + appBG: Color; + splashBG: Color; + highlightBG: Color; + border: Color; + borderLighter: Color; + borderFocus: Color; + icon: Color; + iconMenu: Color; + iconHovered: Color; + iconSuccessFill: Color; + iconReversed: Color; + iconColorfulBackground: Color; + textSupporting: Color; + text: Color; + textColorfulBackground: Color; + link: Color; + linkHover: Color; + buttonDefaultBG: Color; + buttonHoveredBG: Color; + buttonPressedBG: Color; + danger: Color; + dangerHover: Color; + dangerPressed: Color; + warning: Color; + success: Color; + successHover: Color; + successPressed: Color; + transparent: Color; + signInPage: Color; -type ThemeDefault = typeof defaultTheme; + // Additional keys + overlay: Color; + inverse: Color; + shadow: Color; + componentBG: Color; + hoverComponentBG: Color; + activeComponentBG: Color; + signInSidebar: Color; + sidebar: Color; + sidebarHover: Color; + heading: Color; + textLight: Color; + textDark: Color; + textReversed: Color; + textBackground: Color; + textMutedReversed: Color; + textError: Color; + offline: Color; + modalBackdrop: Color; + modalBackground: Color; + cardBG: Color; + cardBorder: Color; + spinner: Color; + unreadIndicator: Color; + placeholderText: Color; + heroCard: Color; + uploadPreviewActivityIndicator: Color; + dropUIBG: Color; + receiptDropUIBG?: Color; + checkBox: Color; + pickerOptionsTextColor: Color; + imageCropBackgroundColor: Color; + fallbackIconColor: Color; + reactionActiveBackground: Color; + reactionActiveText: Color; + badgeAdHoc: Color; + badgeAdHocHover: Color; + mentionText: Color; + mentionBG: Color; + ourMentionText: Color; + ourMentionBG: Color; + tooltipSupportingText: Color; + tooltipPrimaryText: Color; + skeletonLHNIn: Color; + skeletonLHNOut: Color; + QRLogo: Color; + starDefaultBG: Color; -export type {ThemeBase, ThemeDefault}; + PAGE_BACKGROUND_COLORS: Record; +}; + +export {type ThemeColors, type Color}; diff --git a/src/styles/themes/useTheme.ts b/src/styles/themes/useTheme.ts index 51ee044d59c1..8bb4fe73c106 100644 --- a/src/styles/themes/useTheme.ts +++ b/src/styles/themes/useTheme.ts @@ -1,6 +1,6 @@ import {useContext} from 'react'; import ThemeContext from './ThemeContext'; -import ThemeColors from './ThemeColors'; +import {ThemeColors} from './types'; function useTheme(): ThemeColors { const theme = useContext(ThemeContext); From 33d0bc8e0d69338e1a337a54ed52aa1f953f351d Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 20 Sep 2023 08:40:30 +0200 Subject: [PATCH 023/218] fix: make colors non optional --- src/styles/themes/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts index 1ffe3e776a7e..eb72685bbd80 100644 --- a/src/styles/themes/types.ts +++ b/src/styles/themes/types.ts @@ -60,7 +60,7 @@ type ThemeColors = { heroCard: Color; uploadPreviewActivityIndicator: Color; dropUIBG: Color; - receiptDropUIBG?: Color; + receiptDropUIBG: Color; checkBox: Color; pickerOptionsTextColor: Color; imageCropBackgroundColor: Color; From 0b7a8724bcc2295e00915181e9b868c7770785d3 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 20 Sep 2023 08:45:31 +0200 Subject: [PATCH 024/218] fix: add remaining color in light theme --- src/styles/themes/light.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index 9b39a94f02a6..fb1747b2d77b 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -63,7 +63,7 @@ const lightTheme = { heroCard: colors.blue400, uploadPreviewActivityIndicator: colors.lightHighlightBackground, dropUIBG: 'rgba(252, 251, 249, 0.92)', - receiptDropUIBG: '', // TODO: add color + receiptDropUIBG: 'rgba(3, 212, 124, 0.84)', checkBox: colors.green400, pickerOptionsTextColor: colors.lightPrimaryText, imageCropBackgroundColor: colors.lightIcons, From e14ba5d874daa03a3c244c7e97aa0aa70970a455 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 27 Sep 2023 15:31:00 +0700 Subject: [PATCH 025/218] don't allow go to next step if empty waypoint --- .../MoneyRequestParticipantsPage.js | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 8d745903eb40..c6d0cd541944 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -17,6 +17,7 @@ import * as IOU from '../../../../libs/actions/IOU'; import * as MoneyRequestUtils from '../../../../libs/MoneyRequestUtils'; import {iouPropTypes, iouDefaultProps} from '../../propTypes'; import useLocalize from '../../../../hooks/useLocalize'; +import compose from '../../../../libs/compose'; const propTypes = { /** React Navigation route */ @@ -42,7 +43,7 @@ const defaultProps = { iou: iouDefaultProps, }; -function MoneyRequestParticipantsPage({iou, selectedTab, route}) { +function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { const {translate} = useLocalize(); const prevMoneyRequestId = useRef(iou.id); const isNewReportIDSelectedLocally = useRef(false); @@ -53,7 +54,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) { const isScanRequest = MoneyRequestUtils.isScanRequest(selectedTab); const isSplitRequest = iou.id === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT; const [headerTitle, setHeaderTitle] = useState(); - + const isEmptyWaypoint = _.isEmpty(lodashGet(transaction, 'comment.waypoints.waypoint0', {})); useEffect(() => { if (isDistanceRequest) { setHeaderTitle(translate('common.distance')); @@ -85,10 +86,12 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) { }; useEffect(() => { + const isInvalidDistanceRequest = !isDistanceRequest || isEmptyWaypoint; + // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request if (prevMoneyRequestId.current !== iou.id) { // The ID is cleared on completing a request. In that case, we will do nothing - if (iou.id && !isDistanceRequest && !isSplitRequest && !isNewReportIDSelectedLocally.current) { + if (iou.id && isInvalidDistanceRequest && !isSplitRequest && !isNewReportIDSelectedLocally.current) { navigateBack(true); } return; @@ -100,14 +103,14 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route}) { if (shouldReset) { IOU.resetMoneyRequestInfo(moneyRequestId); } - if (!isDistanceRequest && ((iou.amount === 0 && !iou.receiptPath) || shouldReset)) { + if (isInvalidDistanceRequest && ((iou.amount === 0 && !iou.receiptPath) || shouldReset)) { navigateBack(true); } return () => { prevMoneyRequestId.current = iou.id; }; - }, [iou.amount, iou.id, iou.receiptPath, isDistanceRequest, isSplitRequest]); + }, [iou.amount, iou.id, iou.receiptPath, isDistanceRequest, isSplitRequest, isEmptyWaypoint]); return ( `${ONYXKEYS.COLLECTION.TRANSACTION}${iou.transactionID}`, + }, + }), +)(MoneyRequestParticipantsPage); From 0e23a7b844036cd1e9218d6c8b18c97d6d093326 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 27 Sep 2023 15:45:57 +0700 Subject: [PATCH 026/218] fix lint --- .../MoneyRequestParticipantsPage.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index c6d0cd541944..98e5e0976b7a 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -18,6 +18,7 @@ import * as MoneyRequestUtils from '../../../../libs/MoneyRequestUtils'; import {iouPropTypes, iouDefaultProps} from '../../propTypes'; import useLocalize from '../../../../hooks/useLocalize'; import compose from '../../../../libs/compose'; +import transactionPropTypes from '../../../../components/transactionPropTypes'; const propTypes = { /** React Navigation route */ @@ -37,10 +38,14 @@ const propTypes = { /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ selectedTab: PropTypes.oneOf([CONST.TAB.DISTANCE, CONST.TAB.MANUAL, CONST.TAB.SCAN]).isRequired, + + /** Transaction that stores the distance request data */ + transaction: transactionPropTypes, }; const defaultProps = { iou: iouDefaultProps, + transaction: {}, }; function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { From 537af3d2e4826966db8cc7a1bb46f344ad3901c5 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Wed, 27 Sep 2023 17:56:23 +0700 Subject: [PATCH 027/218] fix lint issue --- .../MoneyRequestParticipantsPage.js | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 98e5e0976b7a..31785081f6ab 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -17,7 +17,6 @@ import * as IOU from '../../../../libs/actions/IOU'; import * as MoneyRequestUtils from '../../../../libs/MoneyRequestUtils'; import {iouPropTypes, iouDefaultProps} from '../../propTypes'; import useLocalize from '../../../../hooks/useLocalize'; -import compose from '../../../../libs/compose'; import transactionPropTypes from '../../../../components/transactionPropTypes'; const propTypes = { @@ -151,18 +150,14 @@ MoneyRequestParticipantsPage.displayName = 'IOUParticipantsPage'; MoneyRequestParticipantsPage.propTypes = propTypes; MoneyRequestParticipantsPage.defaultProps = defaultProps; -export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - selectedTab: { - key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, - }, - }), - withOnyx({ - transaction: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou.transactionID}`, - }, - }), -)(MoneyRequestParticipantsPage); +export default withOnyx({ + iou: { + key: ONYXKEYS.IOU, + }, + selectedTab: { + key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, + }, + transaction: { + key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou.transactionID}`, + }, +})(MoneyRequestParticipantsPage); From add086352c19883fe1b34cad8c2243b31be674d2 Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Thu, 28 Sep 2023 02:58:18 +0700 Subject: [PATCH 028/218] fix cannot read property undefined transactionID --- .../MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 31785081f6ab..87b27b27622a 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -158,6 +158,6 @@ export default withOnyx({ key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, }, transaction: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou.transactionID}`, + key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou?.transactionID}`, }, })(MoneyRequestParticipantsPage); From bab3fc8c1ae4ca870c06eb253870631ad6f3731d Mon Sep 17 00:00:00 2001 From: DylanDylann Date: Thu, 28 Sep 2023 03:10:00 +0700 Subject: [PATCH 029/218] fix optional chaining --- .../MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 87b27b27622a..98682e1af490 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -158,6 +158,6 @@ export default withOnyx({ key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, }, transaction: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou?.transactionID}`, + key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou ? iou.transactionID : ''}`, }, })(MoneyRequestParticipantsPage); From 8c3526d404d96f513fc6c093b97ca467c24af1f2 Mon Sep 17 00:00:00 2001 From: Dylan Date: Thu, 28 Sep 2023 13:30:40 +0700 Subject: [PATCH 030/218] make logic more accurate --- .../MoneyRequestParticipantsPage.js | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js index 98682e1af490..0059a6037fec 100644 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import _ from 'underscore'; +import compose from '../../../../libs/compose'; import CONST from '../../../../CONST'; import ONYXKEYS from '../../../../ONYXKEYS'; import ROUTES from '../../../../ROUTES'; @@ -14,6 +15,7 @@ import Navigation from '../../../../libs/Navigation/Navigation'; import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; import * as IOU from '../../../../libs/actions/IOU'; +import * as TransactionUtils from '../../../../libs/TransactionUtils'; import * as MoneyRequestUtils from '../../../../libs/MoneyRequestUtils'; import {iouPropTypes, iouDefaultProps} from '../../propTypes'; import useLocalize from '../../../../hooks/useLocalize'; @@ -58,7 +60,9 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { const isScanRequest = MoneyRequestUtils.isScanRequest(selectedTab); const isSplitRequest = iou.id === CONST.IOU.MONEY_REQUEST_TYPE.SPLIT; const [headerTitle, setHeaderTitle] = useState(); - const isEmptyWaypoint = _.isEmpty(lodashGet(transaction, 'comment.waypoints.waypoint0', {})); + const waypoints = lodashGet(transaction, 'comment.waypoints', {}); + const validatedWaypoints = TransactionUtils.getValidWaypoints(waypoints); + const isInvalidWaypoint = _.size(validatedWaypoints) < 2; useEffect(() => { if (isDistanceRequest) { setHeaderTitle(translate('common.distance')); @@ -90,7 +94,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { }; useEffect(() => { - const isInvalidDistanceRequest = !isDistanceRequest || isEmptyWaypoint; + const isInvalidDistanceRequest = !isDistanceRequest || isInvalidWaypoint; // ID in Onyx could change by initiating a new request in a separate browser tab or completing a request if (prevMoneyRequestId.current !== iou.id) { @@ -114,7 +118,7 @@ function MoneyRequestParticipantsPage({iou, selectedTab, route, transaction}) { return () => { prevMoneyRequestId.current = iou.id; }; - }, [iou.amount, iou.id, iou.receiptPath, isDistanceRequest, isSplitRequest, isEmptyWaypoint]); + }, [iou.amount, iou.id, iou.receiptPath, isDistanceRequest, isSplitRequest, isInvalidWaypoint]); return ( `${ONYXKEYS.COLLECTION.TRANSACTION}${iou ? iou.transactionID : ''}`, - }, -})(MoneyRequestParticipantsPage); +export default compose( + withOnyx({ + iou: { + key: ONYXKEYS.IOU, + }, + selectedTab: { + key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, + }, + }), + // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file + withOnyx({ + transaction: { + key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${iou.transactionID}`, + }, + }), +)(MoneyRequestParticipantsPage); From 3ecb3ef3587f347c41e4a7651854bab14cc3701d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 28 Sep 2023 13:54:25 +0100 Subject: [PATCH 031/218] update index.js --- .../HTMLRenderers/PreRenderer/index.js | 84 +++++++++---------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/index.js b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/index.js index efc9e432cba8..e4aec1446135 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/index.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/PreRenderer/index.js @@ -1,70 +1,66 @@ -import React from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; import _ from 'underscore'; -import withLocalize from '../../../withLocalize'; + +import ControlSelection from '../../../../libs/ControlSelection'; +import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; import htmlRendererPropTypes from '../htmlRendererPropTypes'; import BasePreRenderer from './BasePreRenderer'; -import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; -import ControlSelection from '../../../../libs/ControlSelection'; - -class PreRenderer extends React.Component { - constructor(props) { - super(props); - this.scrollNode = this.scrollNode.bind(this); - this.debouncedIsScrollingVertically = _.debounce(this.isScrollingVertically.bind(this), 100, true); - } +const isScrollingVertically = (event) => + // Mark as vertical scrolling only when absolute value of deltaY is more than the double of absolute + // value of deltaX, so user can use trackpad scroll on the code block horizontally at a wide angle. + Math.abs(event.deltaY) > Math.abs(event.deltaX) * 2; - componentDidMount() { - if (!this.ref) { - return; - } - this.ref.getScrollableNode().addEventListener('wheel', this.scrollNode); - } +const debouncedIsScrollingVertically = (event) => _.debounce(isScrollingVertically(event), 100, true); - componentWillUnmount() { - this.ref.getScrollableNode().removeEventListener('wheel', this.scrollNode); - } +function PreRenderer(props) { + const scrollViewRef = useRef(); /** - * Check if user is scrolling vertically based on deltaX and deltaY. We debounce this - * method in the constructor to make sure it's called only for the first event. + * Checks if user is scrolling vertically based on deltaX and deltaY. We debounce this + * method in order to make sure it's called only for the first event. * @param {WheelEvent} event Wheel event * @returns {Boolean} true if user is scrolling vertically */ - isScrollingVertically(event) { - // Mark as vertical scrolling only when absolute value of deltaY is more than the double of absolute - // value of deltaX, so user can use trackpad scroll on the code block horizontally at a wide angle. - return Math.abs(event.deltaY) > Math.abs(event.deltaX) * 2; - } /** * Manually scrolls the code block if code block horizontal scrollable, then prevents the event from being passed up to the parent. * @param {Object} event native event */ - scrollNode(event) { - const node = this.ref.getScrollableNode(); + const scrollNode = useCallback((event) => { + const node = scrollViewRef.current.getScrollableNode(); const horizontalOverflow = node.scrollWidth > node.offsetWidth; - const isScrollingVertically = this.debouncedIsScrollingVertically(event); - if (event.currentTarget === node && horizontalOverflow && !isScrollingVertically) { + if (event.currentTarget === node && horizontalOverflow && !debouncedIsScrollingVertically(event)) { node.scrollLeft += event.deltaX; event.preventDefault(); event.stopPropagation(); } - } + }, []); + + useEffect(() => { + const eventListenerRefValue = scrollViewRef.current; + if (!eventListenerRefValue) { + return; + } + eventListenerRefValue.getScrollableNode().addEventListener('wheel', scrollNode); + + return () => { + eventListenerRefValue.getScrollableNode().removeEventListener('wheel', scrollNode); + }; + }, [scrollNode]); - render() { - return ( - (this.ref = el)} - onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} - onPressOut={() => ControlSelection.unblock()} - /> - ); - } + return ( + DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} + onPressOut={ControlSelection.unblock} + /> + ); } PreRenderer.propTypes = htmlRendererPropTypes; +PreRenderer.displayName = 'PreRenderer'; -export default withLocalize(PreRenderer); +export default PreRenderer; \ No newline at end of file From eb52c3e5d55cc62c2a50d4ce067ff3caf3d92a62 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 4 Oct 2023 07:28:32 +0100 Subject: [PATCH 032/218] migrate clipboard to TS --- src/libs/Clipboard/index.native.js | 18 ----- src/libs/Clipboard/index.native.ts | 19 ++++++ src/libs/Clipboard/{index.js => index.ts} | 83 +++++++++++++++-------- src/libs/Clipboard/types.ts | 9 +++ src/types/modules/react-native-web.d.ts | 7 ++ 5 files changed, 88 insertions(+), 48 deletions(-) delete mode 100644 src/libs/Clipboard/index.native.js create mode 100644 src/libs/Clipboard/index.native.ts rename src/libs/Clipboard/{index.js => index.ts} (53%) create mode 100644 src/libs/Clipboard/types.ts create mode 100644 src/types/modules/react-native-web.d.ts diff --git a/src/libs/Clipboard/index.native.js b/src/libs/Clipboard/index.native.js deleted file mode 100644 index d6345ac94a36..000000000000 --- a/src/libs/Clipboard/index.native.js +++ /dev/null @@ -1,18 +0,0 @@ -import Clipboard from '@react-native-community/clipboard'; - -/** - * Sets a string on the Clipboard object via @react-native-community/clipboard - * - * @param {String} text - */ -const setString = (text) => { - Clipboard.setString(text); -}; - -export default { - setString, - - // We don't want to set HTML on native platforms so noop them. - canSetHtml: () => false, - setHtml: () => {}, -}; diff --git a/src/libs/Clipboard/index.native.ts b/src/libs/Clipboard/index.native.ts new file mode 100644 index 000000000000..bc7da7ad9d3f --- /dev/null +++ b/src/libs/Clipboard/index.native.ts @@ -0,0 +1,19 @@ +import RNCClipboard from '@react-native-community/clipboard'; +import {SetString, Clipboard} from './types'; + +/** + * Sets a string on the Clipboard object via @react-native-community/clipboard + */ +const setString: SetString = (text) => { + RNCClipboard.setString(text); +}; + +const clipboard: Clipboard = { + setString, + + // We don't want to set HTML on native platforms so noop them. + canSetHtml: () => false, + setHtml: () => {}, +}; + +export default clipboard; diff --git a/src/libs/Clipboard/index.js b/src/libs/Clipboard/index.ts similarity index 53% rename from src/libs/Clipboard/index.js rename to src/libs/Clipboard/index.ts index b770b2f2c787..fe0515edc585 100644 --- a/src/libs/Clipboard/index.js +++ b/src/libs/Clipboard/index.ts @@ -1,17 +1,37 @@ // on Web/desktop this import will be replaced with `react-native-web` -import {Clipboard} from 'react-native-web'; -import lodashGet from 'lodash/get'; +import {Clipboard as RNWClipboard} from 'react-native-web'; import CONST from '../../CONST'; import * as Browser from '../Browser'; +import {SetString, Clipboard} from './types'; -const canSetHtml = () => lodashGet(navigator, 'clipboard.write'); +type ComposerSelection = { + start: number; + end: number; + direction: 'forward' | 'backward' | 'none'; +}; + +type AnchorSelection = { + anchorOffset: number; + focusOffset: number; + anchorNode: Node; + focusNode: Node; +}; + +type Nullable = {[K in keyof T]: T[K] | null}; +type OriginalSelection = ComposerSelection | Partial>; + +/* +* @param {this: void} object The object to query. +*/ + +const canSetHtml = () => (...args: ClipboardItems) => navigator?.clipboard?.write([...args]); /** * Deprecated method to write the content as HTML to clipboard. - * @param {String} html HTML representation - * @param {String} text Plain text representation + * @param HTML representation + * @param Plain text representation */ -function setHTMLSync(html, text) { +function setHTMLSync(html: string, text: string) { const node = document.createElement('span'); node.textContent = html; node.style.all = 'unset'; @@ -22,16 +42,16 @@ function setHTMLSync(html, text) { node.addEventListener('copy', (e) => { e.stopPropagation(); e.preventDefault(); - e.clipboardData.clearData(); - e.clipboardData.setData('text/html', html); - e.clipboardData.setData('text/plain', text); + e.clipboardData?.clearData(); + e.clipboardData?.setData('text/html', html); + e.clipboardData?.setData('text/plain', text); }); document.body.appendChild(node); - const selection = window.getSelection(); - const firstAnchorChild = selection.anchorNode && selection.anchorNode.firstChild; + const selection = window?.getSelection(); + const firstAnchorChild = selection?.anchorNode?.firstChild; const isComposer = firstAnchorChild instanceof HTMLTextAreaElement; - let originalSelection = null; + let originalSelection: OriginalSelection | null = null; if (isComposer) { originalSelection = { start: firstAnchorChild.selectionStart, @@ -40,17 +60,17 @@ function setHTMLSync(html, text) { }; } else { originalSelection = { - anchorNode: selection.anchorNode, - anchorOffset: selection.anchorOffset, - focusNode: selection.focusNode, - focusOffset: selection.focusOffset, + anchorNode: selection?.anchorNode, + anchorOffset: selection?.anchorOffset, + focusNode: selection?.focusNode, + focusOffset: selection?.focusOffset, }; } - selection.removeAllRanges(); + selection?.removeAllRanges(); const range = document.createRange(); range.selectNodeContents(node); - selection.addRange(range); + selection?.addRange(range); try { document.execCommand('copy'); @@ -59,12 +79,14 @@ function setHTMLSync(html, text) { // See https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#the-copy-command for more details. } - selection.removeAllRanges(); + selection?.removeAllRanges(); if (isComposer) { - firstAnchorChild.setSelectionRange(originalSelection.start, originalSelection.end, originalSelection.direction); + const composerSelection = originalSelection as ComposerSelection; + firstAnchorChild.setSelectionRange(composerSelection.start, composerSelection.end, composerSelection.direction); } else { - selection.setBaseAndExtent(originalSelection.anchorNode, originalSelection.anchorOffset, originalSelection.focusNode, originalSelection.focusOffset); + const anchorSelection = originalSelection as AnchorSelection; + selection?.setBaseAndExtent(anchorSelection.anchorNode, anchorSelection.anchorOffset, anchorSelection.focusNode, anchorSelection.focusOffset); } document.body.removeChild(node); @@ -72,10 +94,10 @@ function setHTMLSync(html, text) { /** * Writes the content as HTML if the web client supports it. - * @param {String} html HTML representation - * @param {String} text Plain text representation + * @param HTML representation + * @param Plain text representation */ -const setHtml = (html, text) => { +const setHtml = (html: string, text: string) => { if (!html || !text) { return; } @@ -92,9 +114,10 @@ const setHtml = (html, text) => { setHTMLSync(html, text); } else { navigator.clipboard.write([ - // eslint-disable-next-line no-undef new ClipboardItem({ + // eslint-disable-next-line @typescript-eslint/naming-convention 'text/html': new Blob([html], {type: 'text/html'}), + // eslint-disable-next-line @typescript-eslint/naming-convention 'text/plain': new Blob([text], {type: 'text/plain'}), }), ]); @@ -103,15 +126,15 @@ const setHtml = (html, text) => { /** * Sets a string on the Clipboard object via react-native-web - * - * @param {String} text */ -const setString = (text) => { - Clipboard.setString(text); +const setString: SetString = (text) => { + RNWClipboard.setString(text); }; -export default { +const clipboard: Clipboard = { setString, canSetHtml, setHtml, }; + +export default clipboard; diff --git a/src/libs/Clipboard/types.ts b/src/libs/Clipboard/types.ts new file mode 100644 index 000000000000..e4b7cb1b5332 --- /dev/null +++ b/src/libs/Clipboard/types.ts @@ -0,0 +1,9 @@ +type SetString = (text: string) => void; + +type Clipboard = { + setString: SetString; + canSetHtml: () => void; + setHtml: (html: string, text: string) => void; +} + +export type {SetString, Clipboard}; \ No newline at end of file diff --git a/src/types/modules/react-native-web.d.ts b/src/types/modules/react-native-web.d.ts new file mode 100644 index 000000000000..067e2f95e07a --- /dev/null +++ b/src/types/modules/react-native-web.d.ts @@ -0,0 +1,7 @@ +declare module 'react-native-web' { + type SetString = (text: string) => void; + + const Clipboard: { + setString: SetString; + } +} \ No newline at end of file From 82abacaa4985587dd730465d75652aa37a12b00d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 4 Oct 2023 07:56:04 +0100 Subject: [PATCH 033/218] fix lint --- src/libs/Clipboard/index.ts | 9 ++++++--- src/libs/Clipboard/types.ts | 4 ++-- src/types/modules/react-native-web.d.ts | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libs/Clipboard/index.ts b/src/libs/Clipboard/index.ts index fe0515edc585..8c2d1be1afd5 100644 --- a/src/libs/Clipboard/index.ts +++ b/src/libs/Clipboard/index.ts @@ -21,10 +21,13 @@ type Nullable = {[K in keyof T]: T[K] | null}; type OriginalSelection = ComposerSelection | Partial>; /* -* @param {this: void} object The object to query. -*/ + * @param {this: void} object The object to query. + */ -const canSetHtml = () => (...args: ClipboardItems) => navigator?.clipboard?.write([...args]); +const canSetHtml = + () => + (...args: ClipboardItems) => + navigator?.clipboard?.write([...args]); /** * Deprecated method to write the content as HTML to clipboard. diff --git a/src/libs/Clipboard/types.ts b/src/libs/Clipboard/types.ts index e4b7cb1b5332..92a90df85e02 100644 --- a/src/libs/Clipboard/types.ts +++ b/src/libs/Clipboard/types.ts @@ -4,6 +4,6 @@ type Clipboard = { setString: SetString; canSetHtml: () => void; setHtml: (html: string, text: string) => void; -} +}; -export type {SetString, Clipboard}; \ No newline at end of file +export type {SetString, Clipboard}; diff --git a/src/types/modules/react-native-web.d.ts b/src/types/modules/react-native-web.d.ts index 067e2f95e07a..da723e9a811d 100644 --- a/src/types/modules/react-native-web.d.ts +++ b/src/types/modules/react-native-web.d.ts @@ -1,7 +1,7 @@ declare module 'react-native-web' { type SetString = (text: string) => void; - + const Clipboard: { setString: SetString; - } -} \ No newline at end of file + }; +} From 8ba2000e210551890e44f576250d50678c6bc9c2 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 4 Oct 2023 10:11:43 +0200 Subject: [PATCH 034/218] create MVCPScrollView --- .../MVCPScrollView/MVCPScrollView.js | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js diff --git a/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js b/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js new file mode 100644 index 000000000000..f0139a4ec39c --- /dev/null +++ b/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js @@ -0,0 +1,128 @@ +import React, {forwardRef, useEffect, useRef} from 'react'; +import {ScrollView, StyleSheet} from 'react-native'; +import PropTypes from 'prop-types'; + + +const MVCPScrollView = forwardRef(({maintainVisibleContentPosition, horizontal, ...props}, ref) => { + const scrollViewRef = useRef(null); + const prevFirstVisibleOffset = useRef(null); + const firstVisibleView = useRef(null); + const mutationObserver = useRef(null); + + const getContentView = () => scrollViewRef.current?.childNodes[0]; + + const prepareForMaintainVisibleContentPosition = () => { + if (maintainVisibleContentPosition == null || scrollViewRef.current == null) { + return; + } + + const contentView = getContentView(); + const minIdx = maintainVisibleContentPosition.minIndexForVisible; + for (let ii = minIdx; ii < contentView.childNodes.length; ii++) { + const subview = contentView.childNodes[ii]; + const hasNewView = horizontal ? subview.offsetLeft > scrollViewRef.current.scrollLeft : subview.offsetTop > scrollViewRef.current.scrollTop; + if (hasNewView || ii === contentView.childNodes.length - 1) { + prevFirstVisibleOffset.current = horizontal ? subview.offsetLeft : subview.offsetTop; + firstVisibleView.current = subview; + break; + } + } + }; + const scrollEventListener = useRef(() => { + prepareForMaintainVisibleContentPosition(); + }); + + const adjustForMaintainVisibleContentPosition = () => { + if (maintainVisibleContentPosition == null || scrollViewRef.current == null || firstVisibleView.current == null || prevFirstVisibleOffset.current == null) { + return; + } + + const autoscrollThreshold = maintainVisibleContentPosition.autoscrollToTopThreshold; + if (horizontal) { + const deltaX = firstVisibleView.current.offsetLeft - prevFirstVisibleOffset.current; + if (Math.abs(deltaX) > 0.5) { + const x = scrollViewRef.current.scrollLeft; + prevFirstVisibleOffset.current = firstVisibleView.current.offsetLeft; + scrollViewRef.current.scrollTo({x: x + deltaX, animated: false}); + if (autoscrollThreshold != null && x <= autoscrollThreshold) { + scrollViewRef.current.scrollTo({x: 0, animated: true}); + } + } + } else { + const deltaY = firstVisibleView.current.offsetTop - prevFirstVisibleOffset.current; + if (Math.abs(deltaY) > 0.5) { + const y = scrollViewRef.current.scrollTop; + prevFirstVisibleOffset.current = firstVisibleView.current.offsetTop; + scrollViewRef.current.scrollTo({y: y + deltaY, animated: false}); + if (autoscrollThreshold != null && y <= autoscrollThreshold) { + scrollViewRef.current.scrollTo({y: 0, animated: true}); + } + } + } + }; + + if (mutationObserver.current == null) { + mutationObserver.current = new MutationObserver(() => { + // This needs to execute after scroll events are dispatched, but + // in the same tick to avoid flickering. rAF provides the right timing. + requestAnimationFrame(adjustForMaintainVisibleContentPosition); + }); + } + + const onRef = (newRef) => { + scrollViewRef.current = newRef; + if (typeof ref === 'function') { + ref(newRef); + } else { + // eslint-disable-next-line no-param-reassign + ref.current = newRef; + } + prepareForMaintainVisibleContentPosition(); + mutationObserver.current.disconnect(); + mutationObserver.current.observe(getContentView(), { + attributes: true, + childList: true, + subtree: true, + }); + newRef.removeEventListener('scroll', scrollEventListener.current); + newRef.addEventListener('scroll', scrollEventListener.current); + }; + + useEffect(() => { + const currentObserver = mutationObserver.current; + const currentScrollEventListener = scrollEventListener.current; + return () => { + currentObserver.disconnect(); + scrollViewRef.current.removeEventListener('scroll', currentScrollEventListener); + }; + }, []); + + return ( + + ); +}); + +const styles = StyleSheet.create({ + inverted: { + transform: [{ scaleY: -1 }], + }, +}); + + +MVCPScrollView.propTypes = { + maintainVisibleContentPosition: PropTypes.shape({ + minIndexForVisible: PropTypes.number.isRequired, + autoscrollToTopThreshold: PropTypes.number, + }), + horizontal: PropTypes.bool, +}; + +export default MVCPScrollView; From ef49188d8f4c82342f51b3b830b289b5c4e682aa Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 4 Oct 2023 10:12:26 +0200 Subject: [PATCH 035/218] use renderScrollComponent --- src/components/InvertedFlatList/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.js index d46cd5801605..caa49eaccf58 100644 --- a/src/components/InvertedFlatList/index.js +++ b/src/components/InvertedFlatList/index.js @@ -2,6 +2,7 @@ import React, {forwardRef, useEffect, useRef} from 'react'; import PropTypes from 'prop-types'; import {DeviceEventEmitter, FlatList, StyleSheet} from 'react-native'; import _ from 'underscore'; +import MVCPScrollView from './MVCPScrollView/MVCPScrollView'; import BaseInvertedFlatList from './BaseInvertedFlatList'; import styles from '../../styles/styles'; import CONST from '../../CONST'; @@ -122,6 +123,10 @@ function InvertedFlatList(props) { // We need to keep batch size to one to workaround a bug in react-native-web. // This can be removed once https://github.com/Expensify/App/pull/24482 is merged. maxToRenderPerBatch={1} + + // We need to use our own scroll component to workaround a maintainVisibleContentPosition for web + // eslint-disable-next-line react/jsx-props-no-spreading + renderScrollComponent={(_props) => } /> ); } From 40226f38242c79e2cf02d535af3713ce9d402d53 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 4 Oct 2023 09:16:21 +0100 Subject: [PATCH 036/218] remove redundant JSDOC and Move Nullable to utils --- src/libs/Clipboard/index.ts | 15 +++------------ src/types/utils/Nullable.ts | 3 +++ 2 files changed, 6 insertions(+), 12 deletions(-) create mode 100644 src/types/utils/Nullable.ts diff --git a/src/libs/Clipboard/index.ts b/src/libs/Clipboard/index.ts index 8c2d1be1afd5..3166cab1bed9 100644 --- a/src/libs/Clipboard/index.ts +++ b/src/libs/Clipboard/index.ts @@ -3,6 +3,7 @@ import {Clipboard as RNWClipboard} from 'react-native-web'; import CONST from '../../CONST'; import * as Browser from '../Browser'; import {SetString, Clipboard} from './types'; +import Nullable from '../../types/utils/Nullable'; type ComposerSelection = { start: number; @@ -17,13 +18,8 @@ type AnchorSelection = { focusNode: Node; }; -type Nullable = {[K in keyof T]: T[K] | null}; type OriginalSelection = ComposerSelection | Partial>; -/* - * @param {this: void} object The object to query. - */ - const canSetHtml = () => (...args: ClipboardItems) => @@ -31,8 +27,6 @@ const canSetHtml = /** * Deprecated method to write the content as HTML to clipboard. - * @param HTML representation - * @param Plain text representation */ function setHTMLSync(html: string, text: string) { const node = document.createElement('span'); @@ -84,9 +78,8 @@ function setHTMLSync(html: string, text: string) { selection?.removeAllRanges(); - if (isComposer) { - const composerSelection = originalSelection as ComposerSelection; - firstAnchorChild.setSelectionRange(composerSelection.start, composerSelection.end, composerSelection.direction); + if (isComposer && 'start' in originalSelection) { + firstAnchorChild.setSelectionRange(originalSelection.start, originalSelection.end, originalSelection.direction); } else { const anchorSelection = originalSelection as AnchorSelection; selection?.setBaseAndExtent(anchorSelection.anchorNode, anchorSelection.anchorOffset, anchorSelection.focusNode, anchorSelection.focusOffset); @@ -97,8 +90,6 @@ function setHTMLSync(html: string, text: string) { /** * Writes the content as HTML if the web client supports it. - * @param HTML representation - * @param Plain text representation */ const setHtml = (html: string, text: string) => { if (!html || !text) { diff --git a/src/types/utils/Nullable.ts b/src/types/utils/Nullable.ts new file mode 100644 index 000000000000..caba46ef5b58 --- /dev/null +++ b/src/types/utils/Nullable.ts @@ -0,0 +1,3 @@ +type Nullable = {[K in keyof T]: T[K] | null}; + +export default Nullable; From dfc7be9bd67acee2ba2253ab4afa41170e065bd9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 4 Oct 2023 09:25:30 +0100 Subject: [PATCH 037/218] Update src/types/modules/react-native-web.d.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fábio Henriques --- src/types/modules/react-native-web.d.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/types/modules/react-native-web.d.ts b/src/types/modules/react-native-web.d.ts index da723e9a811d..f7db951eadad 100644 --- a/src/types/modules/react-native-web.d.ts +++ b/src/types/modules/react-native-web.d.ts @@ -1,7 +1,11 @@ +/* eslint-disable import/prefer-default-export */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ declare module 'react-native-web' { - type SetString = (text: string) => void; + class Clipboard { + static isAvailable(): boolean; + static getString(): Promise; + static setString(text: string): boolean; + } - const Clipboard: { - setString: SetString; - }; + export {Clipboard}; } From 4bf68670f7af143b6c576cc8bf83076d6cd607f4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 4 Oct 2023 10:07:42 +0100 Subject: [PATCH 038/218] fix reviews --- src/libs/Clipboard/index.native.ts | 20 ++++++++++---------- src/libs/Clipboard/index.ts | 19 +++++++++---------- src/libs/Clipboard/types.ts | 10 +++------- src/types/utils/Nullable.ts | 3 --- 4 files changed, 22 insertions(+), 30 deletions(-) delete mode 100644 src/types/utils/Nullable.ts diff --git a/src/libs/Clipboard/index.native.ts b/src/libs/Clipboard/index.native.ts index bc7da7ad9d3f..4a805b466d5b 100644 --- a/src/libs/Clipboard/index.native.ts +++ b/src/libs/Clipboard/index.native.ts @@ -1,19 +1,19 @@ -import RNCClipboard from '@react-native-community/clipboard'; -import {SetString, Clipboard} from './types'; +import Clipboard from '@react-native-community/clipboard'; +import {SetString, CanSetHtml, SetHtml} from './types'; /** * Sets a string on the Clipboard object via @react-native-community/clipboard */ const setString: SetString = (text) => { - RNCClipboard.setString(text); + Clipboard.setString(text); }; -const clipboard: Clipboard = { - setString, +// We don't want to set HTML on native platforms so noop them. +const canSetHtml: CanSetHtml = () => false; +const setHtml: SetHtml = () => {}; - // We don't want to set HTML on native platforms so noop them. - canSetHtml: () => false, - setHtml: () => {}, +export default { + setString, + canSetHtml, + setHtml, }; - -export default clipboard; diff --git a/src/libs/Clipboard/index.ts b/src/libs/Clipboard/index.ts index 3166cab1bed9..975780f561a3 100644 --- a/src/libs/Clipboard/index.ts +++ b/src/libs/Clipboard/index.ts @@ -1,9 +1,8 @@ // on Web/desktop this import will be replaced with `react-native-web` -import {Clipboard as RNWClipboard} from 'react-native-web'; +import {Clipboard} from 'react-native-web'; import CONST from '../../CONST'; import * as Browser from '../Browser'; -import {SetString, Clipboard} from './types'; -import Nullable from '../../types/utils/Nullable'; +import {SetString, SetHtml, CanSetHtml} from './types'; type ComposerSelection = { start: number; @@ -18,9 +17,11 @@ type AnchorSelection = { focusNode: Node; }; -type OriginalSelection = ComposerSelection | Partial>; +type NullableObject = {[K in keyof T]: T[K] | null}; -const canSetHtml = +type OriginalSelection = ComposerSelection | Partial>; + +const canSetHtml: CanSetHtml = () => (...args: ClipboardItems) => navigator?.clipboard?.write([...args]); @@ -91,7 +92,7 @@ function setHTMLSync(html: string, text: string) { /** * Writes the content as HTML if the web client supports it. */ -const setHtml = (html: string, text: string) => { +const setHtml: SetHtml = (html: string, text: string) => { if (!html || !text) { return; } @@ -122,13 +123,11 @@ const setHtml = (html: string, text: string) => { * Sets a string on the Clipboard object via react-native-web */ const setString: SetString = (text) => { - RNWClipboard.setString(text); + Clipboard.setString(text); }; -const clipboard: Clipboard = { +export default { setString, canSetHtml, setHtml, }; - -export default clipboard; diff --git a/src/libs/Clipboard/types.ts b/src/libs/Clipboard/types.ts index 92a90df85e02..1d899144a2ba 100644 --- a/src/libs/Clipboard/types.ts +++ b/src/libs/Clipboard/types.ts @@ -1,9 +1,5 @@ type SetString = (text: string) => void; +type SetHtml = (html: string, text: string) => void; +type CanSetHtml = (() => (...args: ClipboardItems) => Promise) | (() => boolean); -type Clipboard = { - setString: SetString; - canSetHtml: () => void; - setHtml: (html: string, text: string) => void; -}; - -export type {SetString, Clipboard}; +export type {SetString, CanSetHtml, SetHtml}; diff --git a/src/types/utils/Nullable.ts b/src/types/utils/Nullable.ts deleted file mode 100644 index caba46ef5b58..000000000000 --- a/src/types/utils/Nullable.ts +++ /dev/null @@ -1,3 +0,0 @@ -type Nullable = {[K in keyof T]: T[K] | null}; - -export default Nullable; From 444710247dba60d2f42e9d3e39a5d94a853151e4 Mon Sep 17 00:00:00 2001 From: Al Garces Date: Thu, 12 Oct 2023 16:52:06 -0700 Subject: [PATCH 039/218] Close Account on Mobile --- .../ExpensifyHelp_CloseAccount_Mobile.png | Bin 0 -> 132318 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/images/ExpensifyHelp_CloseAccount_Mobile.png diff --git a/docs/assets/images/ExpensifyHelp_CloseAccount_Mobile.png b/docs/assets/images/ExpensifyHelp_CloseAccount_Mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..6853953a5c6b703274dc800883e5d81f38573b29 GIT binary patch literal 132318 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 Ts?MuWcj3rE&jaQAgKqr~HD@(5 literal 0 HcmV?d00001 From 4ef562b24f78f214e7d7e3b830cbed899dc4e694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Fri, 13 Oct 2023 12:54:59 +0200 Subject: [PATCH 040/218] memoize useDebounce return value --- src/hooks/useDebounce.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/useDebounce.js b/src/hooks/useDebounce.js index 8995a0443b85..3435a2699e93 100644 --- a/src/hooks/useDebounce.js +++ b/src/hooks/useDebounce.js @@ -1,4 +1,4 @@ -import {useEffect, useRef} from 'react'; +import {useCallback, useEffect, useRef} from 'react'; import lodashDebounce from 'lodash/debounce'; /** @@ -27,11 +27,13 @@ export default function useDebounce(func, wait, options) { return debouncedFn.cancel; }, [func, wait, leading, maxWait, trailing]); - return (...args) => { + const debounceCallback = useCallback((...args) => { const debouncedFn = debouncedFnRef.current; if (debouncedFn) { debouncedFn(...args); } - }; + }, []); + + return debounceCallback; } From 4a18a1ffe6d075fc3688c9f36948b8f6c2f62ba6 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Fri, 13 Oct 2023 15:04:17 +0200 Subject: [PATCH 041/218] [TS migration] Migrate 'withToggleVisibilityView.js' HOC to TypeScript --- .eslintrc.js | 2 +- src/components/withToggleVisibilityView.js | 47 --------------------- src/components/withToggleVisibilityView.tsx | 27 ++++++++++++ src/libs/getComponentDisplayName.ts | 2 +- src/pages/signin/LoginForm/BaseLoginForm.js | 6 +-- 5 files changed, 32 insertions(+), 52 deletions(-) delete mode 100644 src/components/withToggleVisibilityView.js create mode 100644 src/components/withToggleVisibilityView.tsx diff --git a/.eslintrc.js b/.eslintrc.js index 75a74ed371c4..83e9479ce0c4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -116,7 +116,7 @@ module.exports = { }, { selector: ['parameter', 'method'], - format: ['camelCase'], + format: ['camelCase', 'PascalCase'], }, ], '@typescript-eslint/ban-types': [ diff --git a/src/components/withToggleVisibilityView.js b/src/components/withToggleVisibilityView.js deleted file mode 100644 index eef5135d02b6..000000000000 --- a/src/components/withToggleVisibilityView.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import styles from '../styles/styles'; -import getComponentDisplayName from '../libs/getComponentDisplayName'; -import refPropTypes from './refPropTypes'; - -const toggleVisibilityViewPropTypes = { - /** Whether the content is visible. */ - isVisible: PropTypes.bool, -}; - -export default function (WrappedComponent) { - function WithToggleVisibilityView(props) { - return ( - - - - ); - } - - WithToggleVisibilityView.displayName = `WithToggleVisibilityView(${getComponentDisplayName(WrappedComponent)})`; - WithToggleVisibilityView.propTypes = { - forwardedRef: refPropTypes, - - /** Whether the content is visible. */ - isVisible: PropTypes.bool, - }; - WithToggleVisibilityView.defaultProps = { - forwardedRef: undefined, - isVisible: false, - }; - return React.forwardRef((props, ref) => ( - - )); -} - -export {toggleVisibilityViewPropTypes}; diff --git a/src/components/withToggleVisibilityView.tsx b/src/components/withToggleVisibilityView.tsx new file mode 100644 index 000000000000..bdff40be3deb --- /dev/null +++ b/src/components/withToggleVisibilityView.tsx @@ -0,0 +1,27 @@ +import React, {ComponentType, ForwardedRef, RefAttributes} from 'react'; +import {View} from 'react-native'; +import styles from '../styles/styles'; +import getComponentDisplayName from '../libs/getComponentDisplayName'; + +type ToggleVisibilityViewPropTypes = { + /** Whether the content is visible. */ + isVisible: boolean; +}; + +export default function withToggleVisibilityView(WrappedComponent: ComponentType>): ComponentType { + function WithToggleVisibilityView(props: Omit, ref: ForwardedRef) { + return ( + + + + ); + } + + WithToggleVisibilityView.displayName = `WithToggleVisibilityView(${getComponentDisplayName(WrappedComponent)})`; + return React.forwardRef(WithToggleVisibilityView); +} diff --git a/src/libs/getComponentDisplayName.ts b/src/libs/getComponentDisplayName.ts index fd1bbcaea521..0bf52d543a84 100644 --- a/src/libs/getComponentDisplayName.ts +++ b/src/libs/getComponentDisplayName.ts @@ -1,6 +1,6 @@ import {ComponentType} from 'react'; /** Returns the display name of a component */ -export default function getComponentDisplayName(component: ComponentType): string { +export default function getComponentDisplayName(component: ComponentType): string { return component.displayName ?? component.name ?? 'Component'; } diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 2c65b5ff5d37..14d9ee3f5dfc 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -16,7 +16,7 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal import TextInput from '../../../components/TextInput'; import * as ValidationUtils from '../../../libs/ValidationUtils'; import * as LoginUtils from '../../../libs/LoginUtils'; -import withToggleVisibilityView, {toggleVisibilityViewPropTypes} from '../../../components/withToggleVisibilityView'; +import withToggleVisibilityView from '../../../components/withToggleVisibilityView'; import FormAlertWithSubmitButton from '../../../components/FormAlertWithSubmitButton'; import {withNetwork} from '../../../components/OnyxProvider'; import networkPropTypes from '../../../components/networkPropTypes'; @@ -66,12 +66,12 @@ const propTypes = { /** Whether or not the sign in page is being rendered in the RHP modal */ isInModal: PropTypes.bool, + isVisible: PropTypes.bool.isRequired, + ...windowDimensionsPropTypes, ...withLocalizePropTypes, - ...toggleVisibilityViewPropTypes, - ...withNavigationFocusPropTypes, }; From 659831b526af983f91d76e3393465c2e0f6cf8e6 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Fri, 13 Oct 2023 15:07:41 +0200 Subject: [PATCH 042/218] Add default value for isVisible --- src/components/withToggleVisibilityView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/withToggleVisibilityView.tsx b/src/components/withToggleVisibilityView.tsx index bdff40be3deb..b3c8d9bfb221 100644 --- a/src/components/withToggleVisibilityView.tsx +++ b/src/components/withToggleVisibilityView.tsx @@ -11,12 +11,12 @@ type ToggleVisibilityViewPropTypes = { export default function withToggleVisibilityView(WrappedComponent: ComponentType>): ComponentType { function WithToggleVisibilityView(props: Omit, ref: ForwardedRef) { return ( - + ); From 547ab90addaf9bb3db18b5527ff9a2d8c8d445c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Fri, 13 Oct 2023 15:11:18 +0200 Subject: [PATCH 043/218] reuse personalDetails prop by introducing context instead of using withOnyx --- .../PersonalDetailsContext.js | 7 + .../ReportActionCompose.js | 223 +++++++++--------- .../ReportActionCompose/SuggestionMention.js | 45 +--- 3 files changed, 131 insertions(+), 144 deletions(-) create mode 100644 src/pages/home/report/ReportActionCompose/PersonalDetailsContext.js diff --git a/src/pages/home/report/ReportActionCompose/PersonalDetailsContext.js b/src/pages/home/report/ReportActionCompose/PersonalDetailsContext.js new file mode 100644 index 000000000000..83ce73404e2d --- /dev/null +++ b/src/pages/home/report/ReportActionCompose/PersonalDetailsContext.js @@ -0,0 +1,7 @@ +import {createContext} from 'react'; + +const PersonalDetailsContext = createContext({}); + +PersonalDetailsContext.displayName = 'PersonalDetailsContext'; + +export default PersonalDetailsContext; diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index dd4d51653546..831d03d2871c 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -38,6 +38,7 @@ import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; import getDraftComment from '../../../../libs/ComposerUtils/getDraftComment'; import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; +import PersonalDetailsContext from './PersonalDetailsContext'; const propTypes = { /** A method to call when the form is submitted */ @@ -342,121 +343,123 @@ function ReportActionCompose({ }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); return ( - - - + - {shouldShowReportRecipientLocalTime && hasReportRecipient && } - + - setIsAttachmentPreviewActive(true)} - onModalHide={onAttachmentPreviewClose} + {shouldShowReportRecipientLocalTime && hasReportRecipient && } + - {({displayFileInModal}) => ( - <> - - - { - if (isAttachmentPreviewActive) { - return; - } - const data = lodashGet(e, ['dataTransfer', 'items', 0]); - displayFileInModal(data); - }} - /> - + setIsAttachmentPreviewActive(true)} + onModalHide={onAttachmentPreviewClose} + > + {({displayFileInModal}) => ( + <> + + + { + if (isAttachmentPreviewActive) { + return; + } + const data = lodashGet(e, ['dataTransfer', 'items', 0]); + displayFileInModal(data); + }} + /> + + )} + + {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( + composerRef.current.replaceSelectionWithText(...args)} + emojiPickerID={report.reportID} + /> )} - - {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( - composerRef.current.replaceSelectionWithText(...args)} - emojiPickerID={report.reportID} + - )} - - - - {!isSmallScreenWidth && } - - - - - + + + {!isSmallScreenWidth && } + + + + + + ); } diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 84bee9c80c7f..e8dfab4eb9ac 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -1,7 +1,6 @@ -import React, {useState, useCallback, useRef, useImperativeHandle, useEffect} from 'react'; +import React, {useState, useCallback, useRef, useImperativeHandle, useEffect, useContext} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; import CONST from '../../../../CONST'; import useArrowKeyFocusManager from '../../../../hooks/useArrowKeyFocusManager'; import MentionSuggestions from '../../../../components/MentionSuggestions'; @@ -9,9 +8,8 @@ import * as UserUtils from '../../../../libs/UserUtils'; import * as Expensicons from '../../../../components/Icon/Expensicons'; import * as SuggestionsUtils from '../../../../libs/SuggestionUtils'; import useLocalize from '../../../../hooks/useLocalize'; -import ONYXKEYS from '../../../../ONYXKEYS'; -import personalDetailsPropType from '../../../personalDetailsPropType'; import * as SuggestionProps from './suggestionProps'; +import PersonalDetailsContext from './PersonalDetailsContext'; /** * Check if this piece of string looks like a mention @@ -28,9 +26,6 @@ const defaultSuggestionsValues = { }; const propTypes = { - /** Personal details of all users */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - /** A ref to this component */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), @@ -38,23 +33,11 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, forwardedRef: null, }; -function SuggestionMention({ - value, - setValue, - selection, - setSelection, - isComposerFullSize, - personalDetails, - updateComment, - composerHeight, - forwardedRef, - isAutoSuggestionPickerLarge, - measureParentContainer, -}) { +function SuggestionMention({value, setValue, selection, setSelection, isComposerFullSize, updateComment, composerHeight, forwardedRef, isAutoSuggestionPickerLarge, measureParentContainer}) { + const personalDetails = useContext(PersonalDetailsContext); const {translate} = useLocalize(); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); @@ -296,16 +279,10 @@ SuggestionMention.propTypes = propTypes; SuggestionMention.defaultProps = defaultProps; SuggestionMention.displayName = 'SuggestionMention'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, -})( - React.forwardRef((props, ref) => ( - - )), -); +export default React.forwardRef((props, ref) => ( + +)); From 956d7175a6700caf91bbe7fd86a77ea835e8bf18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Fri, 13 Oct 2023 18:50:14 +0200 Subject: [PATCH 044/218] Remove draft comment dependency from OptionRowLHNData component --- .../LHNOptionsList/OptionRowLHNData.js | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index 3386dbe8c8cd..b2dcffa7b9bd 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -2,14 +2,12 @@ import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import _ from 'underscore'; import PropTypes from 'prop-types'; -import React, {useEffect, useRef, useMemo} from 'react'; +import React, {useRef, useMemo} from 'react'; import {deepEqual} from 'fast-equals'; -import {withReportCommentDrafts} from '../OnyxProvider'; import SidebarUtils from '../../libs/SidebarUtils'; import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import OptionRowLHN, {propTypes as basePropTypes, defaultProps as baseDefaultProps} from './OptionRowLHN'; -import * as Report from '../../libs/actions/Report'; import * as UserUtils from '../../libs/UserUtils'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; import * as TransactionUtils from '../../libs/TransactionUtils'; @@ -70,19 +68,7 @@ const defaultProps = { * The OptionRowLHN component is memoized, so it will only * re-render if the data really changed. */ -function OptionRowLHNData({ - isFocused, - fullReport, - reportActions, - personalDetails, - preferredLocale, - comment, - policy, - receiptTransactions, - parentReportActions, - transaction, - ...propsToForward -}) { +function OptionRowLHNData({isFocused, fullReport, reportActions, personalDetails, preferredLocale, policy, receiptTransactions, parentReportActions, transaction, ...propsToForward}) { const reportID = propsToForward.reportID; const parentReportAction = parentReportActions[fullReport.parentReportActionID]; @@ -109,14 +95,6 @@ function OptionRowLHNData({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction]); - useEffect(() => { - if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) { - return; - } - Report.setReportWithDraft(reportID, true); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return ( */ export default React.memo( compose( - withReportCommentDrafts({ - propName: 'comment', - transformValue: (drafts, props) => { - const draftKey = `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${props.reportID}`; - return lodashGet(drafts, draftKey, ''); - }, - }), withOnyx({ fullReport: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, From 46608d0bd1b9de33d9f592b38df733c751485b82 Mon Sep 17 00:00:00 2001 From: kimkurta Date: Fri, 13 Oct 2023 15:23:46 -0500 Subject: [PATCH 045/218] Update CSV-Import.md Resolving help site migration https://github.com/Expensify/Expensify/issues/309825 --- .../company-cards/CSV-Import.md | 99 ++++++++++++++++++- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md index 6debce6240ff..14e6633d32c1 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md @@ -1,5 +1,98 @@ --- -title: CSV Import -description: CSV Import +title: Import and assign company cards from CSV file +description: uploading a CSV file containing your company card transactions --- -## Resource Coming Soon! + +# Overview +Expensify offers a convenient CSV import feature for managing company card expenses when direct connections or commercial card feeds aren't available. This feature allows you to upload a CSV file containing your company card transactions and assign them to cardholders within your Expensify domain. +This feature is available on Group Workspaces and requires Domain Admin access. + +# How to import company cards via CSV +1. Download a CSV of transactions from your bank by logging into their website and finding the relevant statement. +2. Format the CSV for upload using [this template](https://s3-us-west-1.amazonaws.com/concierge-responses-expensify-com/uploads%2F1594908368712-Best+Example+CSV+for+Domains.csv) as a guide. +- At a minimum, your file must include the following columns: + - **Card Number** - each number in this column should display at least the last four digits, and you can obscure up to 12 characters +(e.g., 543212XXXXXX12334). + - **Posted Date** - use the YYYY-MM-DD format in this column (and any other date column in your spreadsheet). + - **Merchant** - the name of the individual or business that provided goods or services for the transaction. This is a free-text field. + - **Posted Amount** - use the number format in this column, and indicate negative amounts with parentheses (e.g., (335.98) for -$335.98). + - **Posted Currency** - use currency codes (e.g., USD, GBP, EUR) to indicate the currency of the posted transactions. +- You can also add mapping for Categories and Tags, but those parameters are optional. +3. Log into Expensify on your web browser. +4. Head to Settings > Domains > Domain Name > Company Cards +5. Click Manage/Import CSV +6. Create a Company Card Layout Name for your spreadsheet +7. Click Upload CSV +8. Review the mapping of your spreadsheet to ensure that the Card Number, Date, Merchant, Amount, and Currency match your data. +9. Double-check the Output Preview for any errors and, if needed, refer to the common error solutions listed in the FAQ below. +10. Once the mapping is correct, click Submit Spreadsheet to complete the import. +11. After submitting the spreadsheet, click I'll wait a minute. Then, wait about 1-2 minutes for the import to process. The domain page will refresh once the upload is complete. + +# How to assign new cards +If you’re assigning cards via CSV upload for the first time: +1. Head to **Settings > Domains > Domain Name > Company Cards** +2. Find the new CSV feed in the drop-down list underneath **Imported Cards** +3. Click **Assign New Cards** +4. Under **Assign a Card**, enter the relevant info +5. Click **Assign** +From there, transactions will be imported to the cardholder's account, where they can add receipts, code the expenses, and submit them for review and approval. +# How to upload new expenses for existing assigned cards +There's no need to create a new upload layout for subsequent CSV uploads. Instead, add new expenses to the existing CSV: +1. Head to **Settings > Domains > Domain Name > Company Cards** +2. Click **Manage/Import CSV** +3. Select the saved layout from the drop-down list +4. Click **Upload CSV** +5. After uploading the more recent CSV, click **Update All Cards** to retrieve the new expenses for the assigned cards. + +# Deep dive +If the CSV upload isn’t formatted correctly, it will cause issues when you try to import or assign cards. Let’s go over some common issues and how to fix them. + +## Error: “Attribute value mapping is missing” +If you encounter an error that says "Attribute-value mapping is missing,” the spreadsheet likely lacks critical details like Card Number, Date, Merchant, Amount, or Currency. To resolve: +1. Click the **X** at the top of the page to close the mapping window +2. Confirm what’s missing from the spreadsheet +3. Add a new column to your spreadsheet and add the missing detail +4. Upload the revised spreadsheet by clicking **Manage Spreadsheet** +5. Enter a **Company Card Layout Name** for the contents of your spreadsheet +6. Click **Upload CSV** + +## Error: “We’ve detected an error while processing your spreadsheet feed” +This error usually occurs when there’s an upload issue. +To troubleshoot this: +1. Head to **Settings > Domains > Domain Name > Company Cards** and click **Manage/Import CSV** +2. In the **Upload Company Card transactions for** dropdown list, look for the layout name you previously created. +3. If the layout is listed, wait at least one hour and then sync the cards to see if new transactions are imported. +4. If the layout isn’t listed, create a new **Company Card Layout Name** and upload the spreadsheet again. + +## Error: “An unexpected error occurred, and we could not retrieve the list of cards” +This error occurs when there’s an issue uploading the spreadsheet or the upload fails. +To troubleshoot this: +1. Head to **Settings > Domains > Domain Name > Company Cards** and click **Manage/Import CSV** +2. In the **Upload Company Card transactions for** dropdown list, look for the layout name you previously created. +3. If the layout is listed, wait at least one hour and then sync the cards to see if new transactions are imported. +4. If the layout isn’t listed, create a new **Company Card Layout Name** and upload the spreadsheet again. + + +## I added a new parameter to an existing spreadsheet, but the data isn’t showing in Expensify after the upload completes. What’s going on? +If you added a new card to an existing spreadsheet and imported it via a saved layout, but it isn’t showing up for assignment, this suggests that the modification may have caused an issue. +The next step in troubleshooting this issue is to compare the number of rows on the revised spreadsheet to the Output Preview to ensure the row count matches the revised spreadsheet. +To check this: +1. Head to **Settings > Domains > Domain Name > Company Cards** and click **Manage/Import CSV** +2. Select your saved layout in the dropdown list +3. Click **Upload CSV** and select the revised spreadsheet +4. Compare the Output Preview row count to your revised spreadsheet to ensure they match +[insert image here] +If they don’t match, you’ll need to revise the spreadsheet by following the CSV formatting guidelines in step 2 of “How to import company cards via CSV” above. +Once you do that, save the revised spreadsheet with a new layout name. +Then, try to upload the revised spreadsheet again: +1. Click **Upload CSV** +2. Upload the revised file +3. Check the row count again on the Output Preview to confirm it matches the spreadsheet +4. Click **Submit Spreadsheet** +# FAQ +## Why can’t I see my CSV transactions immediately after uploading them? +Don’t worry! You’ll typically need to wait 1-2 minutes after clicking **I understand, I'll wait!** + +## I'm trying to import a credit. Why isn't it uploading? +Negative expenses shouldn’t include a minus sign. Instead, they should just be wrapped in parentheses. For example, to indicate “-335.98,” you’ll want to make sure it’s formatted as “(335.98).” + From b0fc9a48e4871b72de0522194c80cc98bca0b165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Mon, 16 Oct 2023 16:53:48 +0200 Subject: [PATCH 046/218] remove unused variable --- src/components/LHNOptionsList/OptionRowLHNData.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index b2dcffa7b9bd..b00fb65e33c7 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -69,8 +69,6 @@ const defaultProps = { * re-render if the data really changed. */ function OptionRowLHNData({isFocused, fullReport, reportActions, personalDetails, preferredLocale, policy, receiptTransactions, parentReportActions, transaction, ...propsToForward}) { - const reportID = propsToForward.reportID; - const parentReportAction = parentReportActions[fullReport.parentReportActionID]; const optionItemRef = useRef(); From a53c07a5122eafd4c85dc10315ad61d04fcd3da9 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 16 Oct 2023 19:28:46 +0200 Subject: [PATCH 047/218] Fixes --- src/components/withToggleVisibilityView.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/withToggleVisibilityView.tsx b/src/components/withToggleVisibilityView.tsx index b3c8d9bfb221..3f5524913874 100644 --- a/src/components/withToggleVisibilityView.tsx +++ b/src/components/withToggleVisibilityView.tsx @@ -1,22 +1,25 @@ -import React, {ComponentType, ForwardedRef, RefAttributes} from 'react'; +import React, {ComponentType, ForwardedRef, ReactElement, RefAttributes} from 'react'; import {View} from 'react-native'; import styles from '../styles/styles'; import getComponentDisplayName from '../libs/getComponentDisplayName'; +import {SetOptional} from 'type-fest'; type ToggleVisibilityViewPropTypes = { /** Whether the content is visible. */ isVisible: boolean; }; -export default function withToggleVisibilityView(WrappedComponent: ComponentType>): ComponentType { - function WithToggleVisibilityView(props: Omit, ref: ForwardedRef) { +export default function withToggleVisibilityView( + WrappedComponent: ComponentType>, +): (props: TProps & RefAttributes) => ReactElement | null { + function WithToggleVisibilityView({isVisible = false, ...rest}: SetOptional, ref: ForwardedRef) { return ( - + ); From f3dfe0a606237a0af01fd2ee1282d722923c5358 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 16 Oct 2023 19:29:13 +0200 Subject: [PATCH 048/218] Rename propTypes --- src/components/withToggleVisibilityView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/withToggleVisibilityView.tsx b/src/components/withToggleVisibilityView.tsx index 3f5524913874..5cabdc5ffc0e 100644 --- a/src/components/withToggleVisibilityView.tsx +++ b/src/components/withToggleVisibilityView.tsx @@ -4,12 +4,12 @@ import styles from '../styles/styles'; import getComponentDisplayName from '../libs/getComponentDisplayName'; import {SetOptional} from 'type-fest'; -type ToggleVisibilityViewPropTypes = { +type ToggleVisibilityViewProp = { /** Whether the content is visible. */ isVisible: boolean; }; -export default function withToggleVisibilityView( +export default function withToggleVisibilityView( WrappedComponent: ComponentType>, ): (props: TProps & RefAttributes) => ReactElement | null { function WithToggleVisibilityView({isVisible = false, ...rest}: SetOptional, ref: ForwardedRef) { From d36a76282ef0aab476dc4538f8d523102844c963 Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Tue, 17 Oct 2023 21:20:02 +0700 Subject: [PATCH 049/218] remove error when all workspace is deleted --- src/libs/actions/Policy.js | 87 +++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 53753e193fb1..bd234d22a826 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -66,6 +66,12 @@ Onyx.connect({ callback: (val) => (allPersonalDetails = val), }); +let reimbursementAccount; +Onyx.connect({ + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + callback: (val) => (reimbursementAccount = val), +}); + let allRecentlyUsedCategories = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, @@ -81,6 +87,36 @@ function updateLastAccessedWorkspace(policyID) { Onyx.set(ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, policyID); } +/** + * Check if the user has any active free policies (aka workspaces) + * + * @param {Array} policies + * @returns {Boolean} + */ +function hasActiveFreePolicy(policies) { + const adminFreePolicies = _.filter(policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); + + if (adminFreePolicies.length === 0) { + return false; + } + + if (_.some(adminFreePolicies, (policy) => !policy.pendingAction)) { + return true; + } + + if (_.some(adminFreePolicies, (policy) => policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)) { + return true; + } + + if (_.some(adminFreePolicies, (policy) => policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { + return false; + } + + // If there are no add or delete pending actions the only option left is an update + // pendingAction, in which case we should return true. + return true; +} + /** * Delete the workspace * @@ -89,6 +125,10 @@ function updateLastAccessedWorkspace(policyID) { * @param {String} policyName */ function deleteWorkspace(policyID, reports, policyName) { + const filteredPolicies = _.filter(allPolicies, (policy) => policy.id !== policyID); + const hasActivePolicy = hasActiveFreePolicy(filteredPolicies); + const oldReimbursementAccount = reimbursementAccount; + const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -125,6 +165,18 @@ function deleteWorkspace(policyID, reports, policyName) { value: optimisticReportActions, }; }), + + ...(!hasActivePolicy + ? [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + errors: null, + }, + }, + ] + : []), ]; // Restore the old report stateNum and statusNum @@ -139,6 +191,11 @@ function deleteWorkspace(policyID, reports, policyName) { oldPolicyName, }, })), + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: oldReimbursementAccount, + }, ]; // We don't need success data since the push notification will update @@ -162,36 +219,6 @@ function isAdminOfFreePolicy(policies) { return _.some(policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); } -/** - * Check if the user has any active free policies (aka workspaces) - * - * @param {Array} policies - * @returns {Boolean} - */ -function hasActiveFreePolicy(policies) { - const adminFreePolicies = _.filter(policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); - - if (adminFreePolicies.length === 0) { - return false; - } - - if (_.some(adminFreePolicies, (policy) => !policy.pendingAction)) { - return true; - } - - if (_.some(adminFreePolicies, (policy) => policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)) { - return true; - } - - if (_.some(adminFreePolicies, (policy) => policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { - return false; - } - - // If there are no add or delete pending actions the only option left is an update - // pendingAction, in which case we should return true. - return true; -} - /** * Remove the passed members from the policy employeeList * From 2cdea51fd549f0d1dc7ea3b4a94747bb3fc8b897 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 18 Oct 2023 15:01:00 +0200 Subject: [PATCH 050/218] ref: moved InlineErrorText to TS --- src/components/InlineErrorText.js | 31 ------------------------------ src/components/InlineErrorText.tsx | 19 ++++++++++++++++++ 2 files changed, 19 insertions(+), 31 deletions(-) delete mode 100644 src/components/InlineErrorText.js create mode 100644 src/components/InlineErrorText.tsx diff --git a/src/components/InlineErrorText.js b/src/components/InlineErrorText.js deleted file mode 100644 index ea701a3f6e8e..000000000000 --- a/src/components/InlineErrorText.js +++ /dev/null @@ -1,31 +0,0 @@ -import _ from 'underscore'; -import React from 'react'; -import PropTypes from 'prop-types'; -import styles from '../styles/styles'; -import Text from './Text'; - -const propTypes = { - /** Text to display */ - children: PropTypes.string.isRequired, - - /** Styling for inline error text */ - // eslint-disable-next-line react/forbid-prop-types - styles: PropTypes.arrayOf(PropTypes.object), -}; - -const defaultProps = { - styles: [], -}; - -function InlineErrorText(props) { - if (_.isEmpty(props.children)) { - return null; - } - - return {props.children}; -} - -InlineErrorText.propTypes = propTypes; -InlineErrorText.defaultProps = defaultProps; -InlineErrorText.displayName = 'InlineErrorText'; -export default InlineErrorText; diff --git a/src/components/InlineErrorText.tsx b/src/components/InlineErrorText.tsx new file mode 100644 index 000000000000..109acfc1b893 --- /dev/null +++ b/src/components/InlineErrorText.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import {TextStyle} from 'react-native'; +import styles from '../styles/styles'; +import Text from './Text'; + +type InlineErrorTextProps = { + children: React.ReactNode; + styles: TextStyle[]; +}; +function InlineErrorText(props: InlineErrorTextProps) { + if (!props.children) { + return null; + } + + return {props.children}; +} + +InlineErrorText.displayName = 'InlineErrorText'; +export default InlineErrorText; From 806d3442624541a500d1c201054cbbc0db5404dd Mon Sep 17 00:00:00 2001 From: kimkurta Date: Wed, 18 Oct 2023 08:25:44 -0500 Subject: [PATCH 051/218] Update docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md Co-authored-by: Rushat Gabhane --- .../bank-accounts-and-credit-cards/company-cards/CSV-Import.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md index 14e6633d32c1..892cfdde1bde 100644 --- a/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md +++ b/docs/articles/expensify-classic/bank-accounts-and-credit-cards/company-cards/CSV-Import.md @@ -89,6 +89,7 @@ Then, try to upload the revised spreadsheet again: 2. Upload the revised file 3. Check the row count again on the Output Preview to confirm it matches the spreadsheet 4. Click **Submit Spreadsheet** + # FAQ ## Why can’t I see my CSV transactions immediately after uploading them? Don’t worry! You’ll typically need to wait 1-2 minutes after clicking **I understand, I'll wait!** From cb37d5f91bee686598236ffb1eafeb01a7d21fe6 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Wed, 18 Oct 2023 15:27:02 -0500 Subject: [PATCH 052/218] Update Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md --- ...ok-For-Small-To-Medium-Sized-Businesses.md | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md index d933e66cc2d1..c275e05dba7b 100644 --- a/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md +++ b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md @@ -22,23 +22,23 @@ If you don't already have one, go to *[new.expensify.com](https://new.expensify. > **Robyn Gresham** > Senior Accounting Systems Manager at SunCommon -## Step 2: Create a Control Policy -There are three policy types, but for your small business needs we recommend the *Control Plan* for the following reasons: +## Step 2: Create a Control Workspace +There are three workspace types, but for your small business needs we recommend the *Control Plan* for the following reasons: - *The Control Plan* is designed for organizations with a high volume of employee expense submissions, who also rely on compliance controls - The ease of use and mobile-first design of the Control plan can increase employee adoption and participation, leading to better expense tracking and management. - The plan integrates with a variety of tools, including accounting software and payroll systems, providing a seamless and integrated experience - Accounting integrations include QuickBooks Online, Xero, NetSuite, and Sage Intacct, with indirect support from Microsoft Dynamics and any other accounting solution you work with -We recommend creating one single policy for your US entity. This allows you to centrally manage all employees in one “group” while enforcing compliance controls and syncing with your accounting package accordingly. +We recommend creating one single workspace for your US entity. This allows you to centrally manage all employees in one “group” while enforcing compliance controls and syncing with your accounting package accordingly. -To create your Control Policy: +To create your Control Workspace: -1. Go to *Settings > Policies* -2. Select *Group* and click the button that says *New Policy* +1. Go to *Settings > Workspace* +2. Select *Group* and click the button that says *New Workspace* 3. Click *Select* under Control -The Control Plan also gives you access to a dedicated Setup Specialist. You can find yours by looking at your policy's *#admins* room in *[new.expensify.com](https://new.expensify.com)*, and in your company’s policy settings in the *Overview* tab, where you can chat with them and schedule an onboarding call to walk through any setup questions. The Control Plan bundled with the Expensify Card is only *$9 per user per month* (not taking into account cash back your earn) when you commit annually. That’s a 75% discount off the unbundled price point if you choose to use a different Corporate Card (or no) provider. +The Control Plan also gives you access to a dedicated Setup Specialist. You can find yours by looking at your workspace's *#admins* room in *[new.expensify.com](https://new.expensify.com)*, and in your company’s workspace settings in the *Overview* tab, where you can chat with them and schedule an onboarding call to walk through any setup questions. The Control Plan bundled with the Expensify Card is only *$9 per user per month* (not taking into account cash back your earn) when you commit annually. That’s a 75% discount off the unbundled price point if you choose to use a different Corporate Card (or no) provider. ## Step 3: Connect your accounting system As a small to medium-sized business, it's important to maintain proper spend management to ensure the success and stability of your organization. This requires paying close attention to your expenses, streamlining your financial processes, and making sure that your financial information is accurate, compliant, and transparent. Include best practices such as: @@ -49,15 +49,15 @@ As a small to medium-sized business, it's important to maintain proper spend man You do this by synchronizing Expensify and your accounting package as follows: -1. Click *Settings > Policies* +1. Click *Settings > Workspace* 2. Navigate to the *Connections* tab 3. Select your accounting system 4. Follow the prompts to connect your accounting package Check out the links below for more information on how to connect to your accounting solution: -- *[QuickBooks Online](https://community.expensify.com/discussion/4833/how-to-connect-your-policy-to-quickbooks-online)* -- *[Xero](https://community.expensify.com/discussion/5282/how-to-connect-your-policy-to-xero)* -- *[NetSuite](https://community.expensify.com/discussion/5212/how-to-connect-your-policy-to-netsuite-token-based-authentication)* +- *[QuickBooks Online](https://community.expensify.com/discussion/4833/how-to-connect-your-workspace-to-quickbooks-online)* +- *[Xero](https://community.expensify.com/discussion/5282/how-to-connect-your-workspace-to-xero)* +- *[NetSuite](https://community.expensify.com/discussion/5212/how-to-connect-your-workspace-to-netsuite-token-based-authentication)* - *[Sage Intacct](https://community.expensify.com/discussion/4777/how-to-connect-to-sage-intacct-user-based-permissions-expense-reports)* - *[Other Accounting System](https://community.expensify.com/discussion/5271/how-to-set-up-an-indirect-accounting-integration) @@ -82,15 +82,15 @@ Head over to the *Categories* tab to set compliance controls on your newly impor Tags in Expensify often relate to departments, projects/customers, classes, and so on. And in some cases they are *required* to be selected on every transactions. And in others, something like *departments* is a static field, meaning we could set it as an employee default and not enforce the tag selection with each expense. *Make Tags Required* -In the tags tab in your policy settings, you’ll notice the option to enable the “Required” field. This makes it so any time an employee doesn’t assign a tag to an expense, we’ll flag a violation on it and notify both the employee and the approver. +In the tags tab in your workspace settings, you’ll notice the option to enable the “Required” field. This makes it so any time an employee doesn’t assign a tag to an expense, we’ll flag a violation on it and notify both the employee and the approver. - *Note:* In general, we take prior selection into account, so anytime you select a tag in Expensify, we’ll pre-populate that same field for any subsequent expense. It’s completely interchangeable, and there for convenience. *Set Tags as an Employee Default* -Separately, if your policy is connected to NetSuite or Sage Intacct, you can set departments, for example, as an employee default. All that means is we’ll apply the department (for example) that’s assigned to the employee record in your accounting package and apply that to every exported transaction, eliminating the need for the employee to have to manually select a department for each expense. +Separately, if your workspace is connected to NetSuite or Sage Intacct, you can set departments, for example, as an employee default. All that means is we’ll apply the department (for example) that’s assigned to the employee record in your accounting package and apply that to every exported transaction, eliminating the need for the employee to have to manually select a department for each expense. ## Step 6: Set rules for all expenses regardless of categorization -In the Expenses tab in your group Control policy, you’ll notice a *Violations* section designed to enforce top-level compliance controls that apply to every expense, for every employee in your policy. We recommend the following confiuration: +In the Expenses tab in your group Control workspace, you’ll notice a *Violations* section designed to enforce top-level compliance controls that apply to every expense, for every employee in your workspace. We recommend the following confiuration: *Max Expense Age: 90 days (or leave it blank)* This will enable Expensify to catch employee reimbursement requests that are far too outdated for reimbursement, and present them as a violations. If you’d prefer a different time window, you can edit it accordingly @@ -106,17 +106,17 @@ Receipts are important, and in most cases you prefer an itemized receipt. Howeve At this point, you’ve set enough compliance controls around categorical spend and general expenses for all employees, such that you can put trust in our solution to audit all expenses up front so you don’t have to. Next, let’s dive into how we can comfortably take on more automation, while relying on compliance controls to capture bad behavior (or better yet, instill best practices in our employees). ## Step 7: Set up scheduled submit -For an efficient company, we recommend setting up [Scheduled Submit](https://community.expensify.com/discussion/4476/how-to-enable-scheduled-submit-for-a-group-policy) on a *Daily* frequency: +For an efficient company, we recommend setting up [Scheduled Submit](https://community.expensify.com/discussion/4476/how-to-enable-scheduled-submit-for-a-group-workspace) on a *Daily* frequency: -- Click *Settings > Policies* -- From here, select your group collect policy -- Within your policy settings, select the *Reports* tab +- Click *Settings > Workspace* +- From here, select your group collect workspace +- Within your workspace settings, select the *Reports* tab - You’ll notice *Scheduled Submit* is located directly under *Report Basics* - Choose *Daily* Between Expensify's SmartScan technology, automatic categorization, and [DoubleCheck](https://community.expensify.com/discussion/5738/deep-dive-how-does-concierge-receipt-audit-work) features, your employees shouldn't need to do anything more than swipe their Expensify Card or take a photo of their receipt. -Expenses with violations will stay behind for the employee to fix, while expenses that are “in-policy” will move into an approver’s queue to mitigate any potential for delays. Scheduled Submit will ensure all expenses are submitted automatically for approval. +Expenses with violations will stay behind for the employee to fix, while expenses that are “in-workspace” will move into an approver’s queue to mitigate any potential for delays. Scheduled Submit will ensure all expenses are submitted automatically for approval. ![Scheduled submit](https://help.expensify.com/assets/images/playbook-scheduled-submit.png){:width="100%"} @@ -162,8 +162,8 @@ In this case we recommend setting *Manually approve all expenses over: $0* ## Step 10: Configure Auto-Approval Knowing you have all the control you need to review reports, we recommend configuring auto-approval for *all reports*. Why? Because you’ve already put reports through an entire approval workflow, and manually triggering reimbursement is an unnecessary action at this stage. -1. Navigate to *Settings > Policies > Group > [Policy Name] > Reimbursement* -2. Set your *Manual Reimbursement threshold to $20,0000* +1. Navigate to *Settings > Workspace > Group > [Workspace Name] > Reimbursement* +2. Set your *Manual Reimbursement threshold to $20,000* ## Step 11: Enable Domains and set up your corporate card feed for employees Expensify is optimized to work with corporate cards from all banks – or even better, use our own perfectly integrated *[Expensify Card](https://use.expensify.com/company-credit-card)*. The first step for connecting to any bank you use for corporate cards, and the Expensify Card is to validate your company’s domain in Domain settings. @@ -203,7 +203,7 @@ The Expensify Card is recommended as the most efficient way to manage your compa Here’s how to enable it: 1. There are *two ways* you can [apply for the Expensify Card](https://community.expensify.com/discussion/4874/how-to-apply-for-the-expensify-card) - - *Via your Inbox* + - *Via your tasks on the Home page* - *Via Domain Settings* - Go to Settings > Domain > Company Cards > Enable Expensify Card 2. Assign the cards to your employees 3. Set *SmartLimits*: @@ -219,7 +219,7 @@ As a small business, managing bills and invoices can be a complex and time-consu Here are some of the key benefits of using Expensify for bill payments and invoicing: - Flexible payment options: Expensify allows you to pay your bills via ACH, credit card, or check, so you can choose the option that works best for you (US businesses only). -- Free, No Fees: The bill pay and invoicing features come included with every policy and workspace, so you won't need to pay any additional fees. +- Free, No Fees: The bill pay and invoicing features come included with every workspace and workspace, so you won't need to pay any additional fees. - Integration with your business bank account: With your business bank account verified, you can easily link your finances to receive payment from customers when invoices are paid. Let’s first chat through how Bill Pay works @@ -244,7 +244,7 @@ Reports, invoices, and bills are largely the same, in theory, just with differen 2. Add all of the expenses/transactions tied to the Invoice 3. Enter the recipient’s email address, a memo if needed, and a due date for when it needs to get paid, and click *Send* -You’ll notice it’s a slightly different flow from creating a Bill. Here, you are adding the transactions tied to the Invoice, and establishing a due date for when it needs to get paid. If you need to apply any markups, you can do so from your policy settings under the Invoices tab. Your customers can pay their invoice in Expensify via ACH, or Check, or Credit Card. +You’ll notice it’s a slightly different flow from creating a Bill. Here, you are adding the transactions tied to the Invoice, and establishing a due date for when it needs to get paid. If you need to apply any markups, you can do so from your workspace settings under the Invoices tab. Your customers can pay their invoice in Expensify via ACH, or Check, or Credit Card. ## Step 13: Run monthly, quarterly and annual reporting At this stage, reporting is important and given that Expensify is the primary point of entry for all employee spend, we make reporting visually appealing and wildly customizable. @@ -266,7 +266,7 @@ Our pricing model is unique in the sense that you are in full control of your bi To set your subscription, head to: -1. Settings > Policies +1. Settings > Workspace 2. Select *Group* 3. Scroll down to *Subscription* 4. Select *Annual Subscription* @@ -281,4 +281,4 @@ Now that we’ve gone through all of the steps for setting up your account, let 4. Click *Accept Terms* # You’re all set! -Congrats, you are all set up! If you need any assistance with anything mentioned above or would like to understand other features available in Expensify, reach out to your Setup Specialist directly in *[new.expensify.com](https://new.expensify.com)*. Don’t have one yet? Create a Control Policy, and we’ll automatically assign a dedicated Setup Specialist to you. +Congrats, you are all set up! If you need any assistance with anything mentioned above or would like to understand other features available in Expensify, reach out to your Setup Specialist directly in *[new.expensify.com](https://new.expensify.com)*. Don’t have one yet? Create a Control Workspace, and we’ll automatically assign a dedicated Setup Specialist to you. From 58a59de71799c2c9e2208d20bb72a79719ef6f39 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 19 Oct 2023 09:16:41 +0200 Subject: [PATCH 053/218] remove TODO --- src/styles/ThemeStylesProvider.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/ThemeStylesProvider.tsx b/src/styles/ThemeStylesProvider.tsx index 581edab55f3f..2ef9a8521e4d 100644 --- a/src/styles/ThemeStylesProvider.tsx +++ b/src/styles/ThemeStylesProvider.tsx @@ -2,7 +2,6 @@ import React, {useMemo} from 'react'; import useTheme from './themes/useTheme'; import ThemeStylesContext from './ThemeStylesContext'; -// TODO: Replace this import with "styles" once the static style export from "styles.js" isn't used anymore import {stylesGenerator} from './styles'; type ThemeStylesProviderProps = { From 2caad032d15b66b39fe3ca6e21046a0023add5a7 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 19 Oct 2023 09:17:28 +0200 Subject: [PATCH 054/218] replace comment --- src/styles/colors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/colors.ts b/src/styles/colors.ts index aa12699ebdea..fbe694e051ee 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -6,7 +6,7 @@ import {Color} from './themes/types'; * For class components, you can use the `withTheme` and `withThemeStyles` HOCs */ const colors: Record = { - // TODO: Find a good name/description for this block of colors. + // Brand Colors black: '#000000', white: '#FFFFFF', ivory: '#fffaf0', From ef2771c22e51956bb195eb259176b22ffe9d7929 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 19 Oct 2023 09:17:48 +0200 Subject: [PATCH 055/218] remove TODO --- src/styles/themes/default.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index 0edd7f90e1df..f8be30a9d881 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -1,4 +1,3 @@ -// TODO: For consistency reasons, rename this file to "dark.ts" after theme switching migration is done (GH issue:) import colors from '../colors'; import SCREENS from '../../SCREENS'; import {ThemeColors} from './types'; From 23bcb925508d4a2e56d25195f1c2876bb1b5c37d Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 19 Oct 2023 09:25:27 +0200 Subject: [PATCH 056/218] remove TODO block --- src/styles/themes/useThemePreference.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/styles/themes/useThemePreference.ts b/src/styles/themes/useThemePreference.ts index 4a7dd067f0d5..6e07ab35f65c 100644 --- a/src/styles/themes/useThemePreference.ts +++ b/src/styles/themes/useThemePreference.ts @@ -3,15 +3,12 @@ import {Appearance, ColorSchemeName} from 'react-native'; import CONST from '../../CONST'; import {PreferredThemeContext} from '../../components/OnyxProvider'; -// TODO: Remove this once "OnyxProvider" is typed -type PreferredThemeContextType = React.Context<(typeof CONST.THEME)[keyof typeof CONST.THEME]>; - type ThemePreference = typeof CONST.THEME.LIGHT | typeof CONST.THEME.DARK; function useThemePreference() { const [themePreference, setThemePreference] = useState(CONST.THEME.DEFAULT); const [systemTheme, setSystemTheme] = useState(); - const preferredThemeFromStorage = useContext(PreferredThemeContext as PreferredThemeContextType); + const preferredThemeFromStorage = useContext(PreferredThemeContext); useEffect(() => { // This is used for getting the system theme, that can be set in the OS's theme settings. This will always return either "light" or "dark" and will update automatically if the OS theme changes. @@ -20,7 +17,7 @@ function useThemePreference() { }, []); useEffect(() => { - const theme = preferredThemeFromStorage || CONST.THEME.DEFAULT; + const theme = preferredThemeFromStorage ?? CONST.THEME.DEFAULT; // If the user chooses to use the device theme settings, we need to set the theme preference to the system theme if (theme === CONST.THEME.SYSTEM) { From 427f99168a34b73c6a795beb499cbab9b3941b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Miko=C5=82ajczak?= Date: Thu, 19 Oct 2023 10:54:33 +0200 Subject: [PATCH 057/218] wrap ComposerWithSuggestions with context instead of entire ReportActionCompose --- .../ReportActionCompose.js | 172 +++++++++--------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 831d03d2871c..2ca3103bad93 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -343,54 +343,54 @@ function ReportActionCompose({ }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); return ( - - + + - - } + - {shouldShowReportRecipientLocalTime && hasReportRecipient && } - setIsAttachmentPreviewActive(true)} + onModalHide={onAttachmentPreviewClose} > - setIsAttachmentPreviewActive(true)} - onModalHide={onAttachmentPreviewClose} - > - {({displayFileInModal}) => ( - <> - + {({displayFileInModal}) => ( + <> + + - { - if (isAttachmentPreviewActive) { - return; - } - const data = lodashGet(e, ['dataTransfer', 'items', 0]); - displayFileInModal(data); - }} - /> - - )} - - {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( - composerRef.current.replaceSelectionWithText(...args)} - emojiPickerID={report.reportID} - /> + + { + if (isAttachmentPreviewActive) { + return; + } + const data = lodashGet(e, ['dataTransfer', 'items', 0]); + displayFileInModal(data); + }} + /> + )} - + {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( + composerRef.current.replaceSelectionWithText(...args)} + emojiPickerID={report.reportID} /> - - - {!isSmallScreenWidth && } - - - - - - + )} + + + + {!isSmallScreenWidth && } + + + + + ); } From 02d15a8d183eb5370642a0dfb06982475cb375e0 Mon Sep 17 00:00:00 2001 From: John Schuster Date: Thu, 19 Oct 2023 13:36:55 -0500 Subject: [PATCH 058/218] Update Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md --- ...-Playbook-For-Small-To-Medium-Sized-Businesses.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md index c275e05dba7b..9e6ea7ef68e7 100644 --- a/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md +++ b/docs/articles/expensify-classic/getting-started/playbooks/Expensify-Playbook-For-Small-To-Medium-Sized-Businesses.md @@ -55,11 +55,11 @@ You do this by synchronizing Expensify and your accounting package as follows: 4. Follow the prompts to connect your accounting package Check out the links below for more information on how to connect to your accounting solution: -- *[QuickBooks Online](https://community.expensify.com/discussion/4833/how-to-connect-your-workspace-to-quickbooks-online)* -- *[Xero](https://community.expensify.com/discussion/5282/how-to-connect-your-workspace-to-xero)* -- *[NetSuite](https://community.expensify.com/discussion/5212/how-to-connect-your-workspace-to-netsuite-token-based-authentication)* -- *[Sage Intacct](https://community.expensify.com/discussion/4777/how-to-connect-to-sage-intacct-user-based-permissions-expense-reports)* -- *[Other Accounting System](https://community.expensify.com/discussion/5271/how-to-set-up-an-indirect-accounting-integration) +- *[QuickBooks Online](https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/QuickBooks-Online#gsc.tab=0)* +- *[Xero](https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/Xero#gsc.tab=0)* +- *[NetSuite](https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/NetSuite#gsc.tab=0)* +- *[Sage Intacct](https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/Sage-Intacct#gsc.tab=0)* +- *[Other Accounting System](https://help.expensify.com/articles/expensify-classic/integrations/accounting-integrations/Indirect-Accounting-Integrations#gsc.tab=0) *“Employees really appreciate how easy it is to use, and the fact that the reimbursement drops right into their bank account. Since most employees are submitting expenses from their phones, the ease of use of the app is critical.”* @@ -106,7 +106,7 @@ Receipts are important, and in most cases you prefer an itemized receipt. Howeve At this point, you’ve set enough compliance controls around categorical spend and general expenses for all employees, such that you can put trust in our solution to audit all expenses up front so you don’t have to. Next, let’s dive into how we can comfortably take on more automation, while relying on compliance controls to capture bad behavior (or better yet, instill best practices in our employees). ## Step 7: Set up scheduled submit -For an efficient company, we recommend setting up [Scheduled Submit](https://community.expensify.com/discussion/4476/how-to-enable-scheduled-submit-for-a-group-workspace) on a *Daily* frequency: +For an efficient company, we recommend setting up [Scheduled Submit](https://help.expensify.com/articles/expensify-classic/policy-and-domain-settings/reports/Scheduled-Submit#gsc.tab=0) on a *Daily* frequency: - Click *Settings > Workspace* - From here, select your group collect workspace From 1dff6dd3fd3d1582d7eae15f3e9aa668c1c3fe2e Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Fri, 20 Oct 2023 12:20:53 +0700 Subject: [PATCH 059/218] using lodash instead of underscore --- src/libs/actions/Policy.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index bd234d22a826..4417d5f00853 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1,4 +1,5 @@ import _ from 'underscore'; +import filter from 'lodash/filter'; import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; import lodashUnion from 'lodash/union'; @@ -125,8 +126,7 @@ function hasActiveFreePolicy(policies) { * @param {String} policyName */ function deleteWorkspace(policyID, reports, policyName) { - const filteredPolicies = _.filter(allPolicies, (policy) => policy.id !== policyID); - const hasActivePolicy = hasActiveFreePolicy(filteredPolicies); + const filteredPolicies = filter(allPolicies, (policy) => policy.id !== policyID); const oldReimbursementAccount = reimbursementAccount; const optimisticData = [ @@ -166,7 +166,7 @@ function deleteWorkspace(policyID, reports, policyName) { }; }), - ...(!hasActivePolicy + ...(!hasActiveFreePolicy(filteredPolicies) ? [ { onyxMethod: Onyx.METHOD.MERGE, @@ -194,7 +194,9 @@ function deleteWorkspace(policyID, reports, policyName) { { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: oldReimbursementAccount, + value: { + errors: lodashGet(oldReimbursementAccount, 'errors', null), + }, }, ]; From b9316bf5e22eecaf3136ef0f955436d27c8cf478 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Fri, 20 Oct 2023 09:52:35 +0200 Subject: [PATCH 060/218] Fix prettier --- src/components/withToggleVisibilityView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/withToggleVisibilityView.tsx b/src/components/withToggleVisibilityView.tsx index 5cabdc5ffc0e..066cbae223b1 100644 --- a/src/components/withToggleVisibilityView.tsx +++ b/src/components/withToggleVisibilityView.tsx @@ -1,8 +1,8 @@ import React, {ComponentType, ForwardedRef, ReactElement, RefAttributes} from 'react'; +import {SetOptional} from 'type-fest'; import {View} from 'react-native'; import styles from '../styles/styles'; import getComponentDisplayName from '../libs/getComponentDisplayName'; -import {SetOptional} from 'type-fest'; type ToggleVisibilityViewProp = { /** Whether the content is visible. */ From 7af7d79a70d807900423534e3981174fa564b289 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 20 Oct 2023 14:17:47 +0200 Subject: [PATCH 061/218] [TS migration] Migrate 'withNavigationFallback.js' HOC --- src/components/withNavigationFallback.js | 43 ----------------------- src/components/withNavigationFallback.tsx | 43 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) delete mode 100644 src/components/withNavigationFallback.js create mode 100644 src/components/withNavigationFallback.tsx diff --git a/src/components/withNavigationFallback.js b/src/components/withNavigationFallback.js deleted file mode 100644 index e82946c9e049..000000000000 --- a/src/components/withNavigationFallback.js +++ /dev/null @@ -1,43 +0,0 @@ -import React, {forwardRef, useContext, useMemo} from 'react'; -import {NavigationContext} from '@react-navigation/core'; -import getComponentDisplayName from '../libs/getComponentDisplayName'; -import refPropTypes from './refPropTypes'; - -export default function (WrappedComponent) { - function WithNavigationFallback(props) { - const context = useContext(NavigationContext); - - const navigationContextValue = useMemo(() => ({isFocused: () => true, addListener: () => () => {}, removeListener: () => () => {}}), []); - - return context ? ( - - ) : ( - - - - ); - } - WithNavigationFallback.displayName = `WithNavigationFocusWithFallback(${getComponentDisplayName(WrappedComponent)})`; - WithNavigationFallback.propTypes = { - forwardedRef: refPropTypes, - }; - WithNavigationFallback.defaultProps = { - forwardedRef: undefined, - }; - - return forwardRef((props, ref) => ( - - )); -} diff --git a/src/components/withNavigationFallback.tsx b/src/components/withNavigationFallback.tsx new file mode 100644 index 000000000000..63dbdaf02ce9 --- /dev/null +++ b/src/components/withNavigationFallback.tsx @@ -0,0 +1,43 @@ +import React, {ComponentType, RefAttributes, ForwardedRef, ReactElement, forwardRef, useContext, useMemo} from 'react'; +import {NavigationContext} from '@react-navigation/core'; +import {NavigationProp} from '@react-navigation/native'; +import {ParamListBase} from '@react-navigation/routers'; +import getComponentDisplayName from '../libs/getComponentDisplayName'; + +type AddListenerCallback = () => void; + +type RemoveListenerCallback = () => void; + +type NavigationContextValue = { + isFocused: () => boolean; + addListener: () => AddListenerCallback; + removeListener: () => RemoveListenerCallback; +}; + +export default function (WrappedComponent: ComponentType>): (props: TProps & RefAttributes) => ReactElement | null { + function WithNavigationFallback(props: TProps, ref: ForwardedRef) { + const context = useContext(NavigationContext); + + const navigationContextValue: NavigationContextValue = useMemo(() => ({isFocused: () => true, addListener: () => () => {}, removeListener: () => () => {}}), []); + + return context ? ( + + ) : ( + }> + + + ); + } + + WithNavigationFallback.displayName = `WithNavigationFocusWithFallback(${getComponentDisplayName(WrappedComponent)})`; + + return forwardRef(WithNavigationFallback); +} From 3cc63f83d8953d7719429b15814f23847721e777 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 4 Oct 2023 10:11:43 +0200 Subject: [PATCH 062/218] create MVCPScrollView --- .../MVCPScrollView/MVCPScrollView.js | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js diff --git a/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js b/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js new file mode 100644 index 000000000000..f0139a4ec39c --- /dev/null +++ b/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js @@ -0,0 +1,128 @@ +import React, {forwardRef, useEffect, useRef} from 'react'; +import {ScrollView, StyleSheet} from 'react-native'; +import PropTypes from 'prop-types'; + + +const MVCPScrollView = forwardRef(({maintainVisibleContentPosition, horizontal, ...props}, ref) => { + const scrollViewRef = useRef(null); + const prevFirstVisibleOffset = useRef(null); + const firstVisibleView = useRef(null); + const mutationObserver = useRef(null); + + const getContentView = () => scrollViewRef.current?.childNodes[0]; + + const prepareForMaintainVisibleContentPosition = () => { + if (maintainVisibleContentPosition == null || scrollViewRef.current == null) { + return; + } + + const contentView = getContentView(); + const minIdx = maintainVisibleContentPosition.minIndexForVisible; + for (let ii = minIdx; ii < contentView.childNodes.length; ii++) { + const subview = contentView.childNodes[ii]; + const hasNewView = horizontal ? subview.offsetLeft > scrollViewRef.current.scrollLeft : subview.offsetTop > scrollViewRef.current.scrollTop; + if (hasNewView || ii === contentView.childNodes.length - 1) { + prevFirstVisibleOffset.current = horizontal ? subview.offsetLeft : subview.offsetTop; + firstVisibleView.current = subview; + break; + } + } + }; + const scrollEventListener = useRef(() => { + prepareForMaintainVisibleContentPosition(); + }); + + const adjustForMaintainVisibleContentPosition = () => { + if (maintainVisibleContentPosition == null || scrollViewRef.current == null || firstVisibleView.current == null || prevFirstVisibleOffset.current == null) { + return; + } + + const autoscrollThreshold = maintainVisibleContentPosition.autoscrollToTopThreshold; + if (horizontal) { + const deltaX = firstVisibleView.current.offsetLeft - prevFirstVisibleOffset.current; + if (Math.abs(deltaX) > 0.5) { + const x = scrollViewRef.current.scrollLeft; + prevFirstVisibleOffset.current = firstVisibleView.current.offsetLeft; + scrollViewRef.current.scrollTo({x: x + deltaX, animated: false}); + if (autoscrollThreshold != null && x <= autoscrollThreshold) { + scrollViewRef.current.scrollTo({x: 0, animated: true}); + } + } + } else { + const deltaY = firstVisibleView.current.offsetTop - prevFirstVisibleOffset.current; + if (Math.abs(deltaY) > 0.5) { + const y = scrollViewRef.current.scrollTop; + prevFirstVisibleOffset.current = firstVisibleView.current.offsetTop; + scrollViewRef.current.scrollTo({y: y + deltaY, animated: false}); + if (autoscrollThreshold != null && y <= autoscrollThreshold) { + scrollViewRef.current.scrollTo({y: 0, animated: true}); + } + } + } + }; + + if (mutationObserver.current == null) { + mutationObserver.current = new MutationObserver(() => { + // This needs to execute after scroll events are dispatched, but + // in the same tick to avoid flickering. rAF provides the right timing. + requestAnimationFrame(adjustForMaintainVisibleContentPosition); + }); + } + + const onRef = (newRef) => { + scrollViewRef.current = newRef; + if (typeof ref === 'function') { + ref(newRef); + } else { + // eslint-disable-next-line no-param-reassign + ref.current = newRef; + } + prepareForMaintainVisibleContentPosition(); + mutationObserver.current.disconnect(); + mutationObserver.current.observe(getContentView(), { + attributes: true, + childList: true, + subtree: true, + }); + newRef.removeEventListener('scroll', scrollEventListener.current); + newRef.addEventListener('scroll', scrollEventListener.current); + }; + + useEffect(() => { + const currentObserver = mutationObserver.current; + const currentScrollEventListener = scrollEventListener.current; + return () => { + currentObserver.disconnect(); + scrollViewRef.current.removeEventListener('scroll', currentScrollEventListener); + }; + }, []); + + return ( + + ); +}); + +const styles = StyleSheet.create({ + inverted: { + transform: [{ scaleY: -1 }], + }, +}); + + +MVCPScrollView.propTypes = { + maintainVisibleContentPosition: PropTypes.shape({ + minIndexForVisible: PropTypes.number.isRequired, + autoscrollToTopThreshold: PropTypes.number, + }), + horizontal: PropTypes.bool, +}; + +export default MVCPScrollView; From d82cf206706c0b6c566d54a1bb3fdfaf29693898 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Wed, 4 Oct 2023 10:12:26 +0200 Subject: [PATCH 063/218] use renderScrollComponent --- src/components/InvertedFlatList/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.js index 564db6296c9b..e7f6c14f52d8 100644 --- a/src/components/InvertedFlatList/index.js +++ b/src/components/InvertedFlatList/index.js @@ -2,6 +2,7 @@ import React, {forwardRef, useEffect, useRef} from 'react'; import PropTypes from 'prop-types'; import {DeviceEventEmitter, FlatList, StyleSheet} from 'react-native'; import _ from 'underscore'; +import MVCPScrollView from './MVCPScrollView/MVCPScrollView'; import BaseInvertedFlatList from './BaseInvertedFlatList'; import styles from '../../styles/styles'; import CONST from '../../CONST'; @@ -121,6 +122,10 @@ function InvertedFlatList(props) { // We need to keep batch size to one to workaround a bug in react-native-web. // This can be removed once https://github.com/Expensify/App/pull/24482 is merged. maxToRenderPerBatch={1} + + // We need to use our own scroll component to workaround a maintainVisibleContentPosition for web + // eslint-disable-next-line react/jsx-props-no-spreading + renderScrollComponent={(_props) => } /> ); } From 3c5d2796c9e926cb7accd3396c43cda722f8851a Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 20 Oct 2023 13:08:06 -0400 Subject: [PATCH 064/218] update implementation --- src/components/FlatList/MVCPFlatList.js | 190 ++++++++++++++++++ src/components/FlatList/index.web.js | 3 + .../MVCPScrollView/MVCPScrollView.js | 128 ------------ src/components/InvertedFlatList/index.js | 5 - 4 files changed, 193 insertions(+), 133 deletions(-) create mode 100644 src/components/FlatList/MVCPFlatList.js create mode 100644 src/components/FlatList/index.web.js delete mode 100644 src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js diff --git a/src/components/FlatList/MVCPFlatList.js b/src/components/FlatList/MVCPFlatList.js new file mode 100644 index 000000000000..357db1535e6b --- /dev/null +++ b/src/components/FlatList/MVCPFlatList.js @@ -0,0 +1,190 @@ +/* eslint-disable es/no-optional-chaining, es/no-nullish-coalescing-operators, react/prop-types */ + +import React from 'react'; +import {FlatList} from 'react-native'; + +function mergeRefs(...args) { + return function forwardRef(node) { + args.forEach((ref) => { + if (ref == null) { + return; + } + if (typeof ref === 'function') { + ref(node); + return; + } + if (typeof ref === 'object') { + // eslint-disable-next-line no-param-reassign + ref.current = node; + return; + } + console.error(`mergeRefs cannot handle Refs of type boolean, number or string, received ref ${String(ref)}`); + }); + }; +} + +function useMergeRefs(...args) { + return React.useMemo( + () => mergeRefs(...args), + // eslint-disable-next-line + [...args], + ); +} + +const MVCPFlatList = React.forwardRef(({maintainVisibleContentPosition, horizontal, inverted, onScroll, ...props}, forwardedRef) => { + const {minIndexForVisible: mvcpMinIndexForVisible, autoscrollToTopThreshold: mvcpAutoscrollToTopThreshold} = maintainVisibleContentPosition ?? {}; + const scrollRef = React.useRef(null); + const prevFirstVisibleOffsetRef = React.useRef(null); + const firstVisibleViewRef = React.useRef(null); + const mutationObserverRef = React.useRef(null); + const lastScrollOffsetRef = React.useRef(0); + + const getScrollOffset = React.useCallback(() => { + if (scrollRef.current == null) { + return 0; + } + return horizontal ? scrollRef.current.getScrollableNode().scrollLeft : scrollRef.current.getScrollableNode().scrollTop; + }, [horizontal]); + + const getContentView = React.useCallback(() => scrollRef.current?.getScrollableNode().childNodes[0], []); + + const scrollToOffset = React.useCallback( + (offset, animated) => { + const behavior = animated ? 'smooth' : 'instant'; + scrollRef.current?.getScrollableNode().scroll(horizontal ? {left: offset, behavior} : {top: offset, behavior}); + }, + [horizontal], + ); + + const prepareForMaintainVisibleContentPosition = React.useCallback(() => { + if (mvcpMinIndexForVisible == null) { + return; + } + + const contentView = getContentView(); + if (contentView == null) { + return; + } + + const scrollOffset = getScrollOffset(); + + for (let i = mvcpMinIndexForVisible; i < contentView.childNodes.length; i++) { + const subview = contentView.childNodes[i]; + const subviewOffset = horizontal ? subview.offsetLeft : subview.offsetTop; + if (subviewOffset > scrollOffset || i === contentView.childNodes.length - 1) { + prevFirstVisibleOffsetRef.current = subviewOffset; + firstVisibleViewRef.current = subview; + break; + } + } + }, [getContentView, getScrollOffset, mvcpMinIndexForVisible, horizontal]); + + const adjustForMaintainVisibleContentPosition = React.useCallback(() => { + if (mvcpMinIndexForVisible == null) { + return; + } + + const firstVisibleView = firstVisibleViewRef.current; + const prevFirstVisibleOffset = prevFirstVisibleOffsetRef.current; + if (firstVisibleView == null || prevFirstVisibleOffset == null) { + return; + } + + const firstVisibleViewOffset = horizontal ? firstVisibleView.offsetLeft : firstVisibleView.offsetTop; + const delta = firstVisibleViewOffset - prevFirstVisibleOffset; + if (Math.abs(delta) > 0.5) { + const scrollOffset = getScrollOffset(); + prevFirstVisibleOffsetRef.current = firstVisibleViewOffset; + scrollToOffset(scrollOffset + delta, false); + if (mvcpAutoscrollToTopThreshold != null && scrollOffset <= mvcpAutoscrollToTopThreshold) { + scrollToOffset(0, true); + } + } + }, [getScrollOffset, scrollToOffset, mvcpMinIndexForVisible, mvcpAutoscrollToTopThreshold, horizontal]); + + const setupMutationObserver = React.useCallback(() => { + const contentView = getContentView(); + if (contentView == null) { + return; + } + + mutationObserverRef.current?.disconnect(); + + const mutationObserver = new MutationObserver(() => { + // Chrome adjusts scroll position when elements are added at the top of the + // view. We want to have the same behavior as react-native / Safari so we + // reset the scroll position to the last value we got from an event. + const lastScrollOffset = lastScrollOffsetRef.current; + const scrollOffset = getScrollOffset(); + if (lastScrollOffset !== scrollOffset) { + scrollToOffset(lastScrollOffset, false); + } + + // This needs to execute after scroll events are dispatched, but + // in the same tick to avoid flickering. rAF provides the right timing. + requestAnimationFrame(() => { + adjustForMaintainVisibleContentPosition(); + }); + }); + mutationObserver.observe(contentView, { + attributes: true, + childList: true, + subtree: true, + }); + + mutationObserverRef.current = mutationObserver; + }, [adjustForMaintainVisibleContentPosition, getContentView, getScrollOffset, scrollToOffset]); + + React.useEffect(() => { + prepareForMaintainVisibleContentPosition(); + setupMutationObserver(); + }, [prepareForMaintainVisibleContentPosition, setupMutationObserver]); + + const setMergedRef = useMergeRefs(scrollRef, forwardedRef); + + const onRef = React.useCallback( + (newRef) => { + // Make sure to only call refs and re-attach listeners if the node changed. + if (newRef == null || newRef === scrollRef.current) { + return; + } + + setMergedRef(newRef); + prepareForMaintainVisibleContentPosition(); + setupMutationObserver(); + }, + [prepareForMaintainVisibleContentPosition, setMergedRef, setupMutationObserver], + ); + + React.useEffect(() => { + const mutationObserver = mutationObserverRef.current; + return () => { + mutationObserver?.disconnect(); + }; + }, []); + + const onScrollInternal = React.useCallback( + (ev) => { + lastScrollOffsetRef.current = getScrollOffset(); + + prepareForMaintainVisibleContentPosition(); + + onScroll?.(ev); + }, + [getScrollOffset, prepareForMaintainVisibleContentPosition, onScroll], + ); + + return ( + + ); +}); + +export default MVCPFlatList; diff --git a/src/components/FlatList/index.web.js b/src/components/FlatList/index.web.js new file mode 100644 index 000000000000..7299776db9bc --- /dev/null +++ b/src/components/FlatList/index.web.js @@ -0,0 +1,3 @@ +import MVCPFlatList from './MVCPFlatList'; + +export default MVCPFlatList; diff --git a/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js b/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js deleted file mode 100644 index f0139a4ec39c..000000000000 --- a/src/components/InvertedFlatList/MVCPScrollView/MVCPScrollView.js +++ /dev/null @@ -1,128 +0,0 @@ -import React, {forwardRef, useEffect, useRef} from 'react'; -import {ScrollView, StyleSheet} from 'react-native'; -import PropTypes from 'prop-types'; - - -const MVCPScrollView = forwardRef(({maintainVisibleContentPosition, horizontal, ...props}, ref) => { - const scrollViewRef = useRef(null); - const prevFirstVisibleOffset = useRef(null); - const firstVisibleView = useRef(null); - const mutationObserver = useRef(null); - - const getContentView = () => scrollViewRef.current?.childNodes[0]; - - const prepareForMaintainVisibleContentPosition = () => { - if (maintainVisibleContentPosition == null || scrollViewRef.current == null) { - return; - } - - const contentView = getContentView(); - const minIdx = maintainVisibleContentPosition.minIndexForVisible; - for (let ii = minIdx; ii < contentView.childNodes.length; ii++) { - const subview = contentView.childNodes[ii]; - const hasNewView = horizontal ? subview.offsetLeft > scrollViewRef.current.scrollLeft : subview.offsetTop > scrollViewRef.current.scrollTop; - if (hasNewView || ii === contentView.childNodes.length - 1) { - prevFirstVisibleOffset.current = horizontal ? subview.offsetLeft : subview.offsetTop; - firstVisibleView.current = subview; - break; - } - } - }; - const scrollEventListener = useRef(() => { - prepareForMaintainVisibleContentPosition(); - }); - - const adjustForMaintainVisibleContentPosition = () => { - if (maintainVisibleContentPosition == null || scrollViewRef.current == null || firstVisibleView.current == null || prevFirstVisibleOffset.current == null) { - return; - } - - const autoscrollThreshold = maintainVisibleContentPosition.autoscrollToTopThreshold; - if (horizontal) { - const deltaX = firstVisibleView.current.offsetLeft - prevFirstVisibleOffset.current; - if (Math.abs(deltaX) > 0.5) { - const x = scrollViewRef.current.scrollLeft; - prevFirstVisibleOffset.current = firstVisibleView.current.offsetLeft; - scrollViewRef.current.scrollTo({x: x + deltaX, animated: false}); - if (autoscrollThreshold != null && x <= autoscrollThreshold) { - scrollViewRef.current.scrollTo({x: 0, animated: true}); - } - } - } else { - const deltaY = firstVisibleView.current.offsetTop - prevFirstVisibleOffset.current; - if (Math.abs(deltaY) > 0.5) { - const y = scrollViewRef.current.scrollTop; - prevFirstVisibleOffset.current = firstVisibleView.current.offsetTop; - scrollViewRef.current.scrollTo({y: y + deltaY, animated: false}); - if (autoscrollThreshold != null && y <= autoscrollThreshold) { - scrollViewRef.current.scrollTo({y: 0, animated: true}); - } - } - } - }; - - if (mutationObserver.current == null) { - mutationObserver.current = new MutationObserver(() => { - // This needs to execute after scroll events are dispatched, but - // in the same tick to avoid flickering. rAF provides the right timing. - requestAnimationFrame(adjustForMaintainVisibleContentPosition); - }); - } - - const onRef = (newRef) => { - scrollViewRef.current = newRef; - if (typeof ref === 'function') { - ref(newRef); - } else { - // eslint-disable-next-line no-param-reassign - ref.current = newRef; - } - prepareForMaintainVisibleContentPosition(); - mutationObserver.current.disconnect(); - mutationObserver.current.observe(getContentView(), { - attributes: true, - childList: true, - subtree: true, - }); - newRef.removeEventListener('scroll', scrollEventListener.current); - newRef.addEventListener('scroll', scrollEventListener.current); - }; - - useEffect(() => { - const currentObserver = mutationObserver.current; - const currentScrollEventListener = scrollEventListener.current; - return () => { - currentObserver.disconnect(); - scrollViewRef.current.removeEventListener('scroll', currentScrollEventListener); - }; - }, []); - - return ( - - ); -}); - -const styles = StyleSheet.create({ - inverted: { - transform: [{ scaleY: -1 }], - }, -}); - - -MVCPScrollView.propTypes = { - maintainVisibleContentPosition: PropTypes.shape({ - minIndexForVisible: PropTypes.number.isRequired, - autoscrollToTopThreshold: PropTypes.number, - }), - horizontal: PropTypes.bool, -}; - -export default MVCPScrollView; diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.js index e7f6c14f52d8..564db6296c9b 100644 --- a/src/components/InvertedFlatList/index.js +++ b/src/components/InvertedFlatList/index.js @@ -2,7 +2,6 @@ import React, {forwardRef, useEffect, useRef} from 'react'; import PropTypes from 'prop-types'; import {DeviceEventEmitter, FlatList, StyleSheet} from 'react-native'; import _ from 'underscore'; -import MVCPScrollView from './MVCPScrollView/MVCPScrollView'; import BaseInvertedFlatList from './BaseInvertedFlatList'; import styles from '../../styles/styles'; import CONST from '../../CONST'; @@ -122,10 +121,6 @@ function InvertedFlatList(props) { // We need to keep batch size to one to workaround a bug in react-native-web. // This can be removed once https://github.com/Expensify/App/pull/24482 is merged. maxToRenderPerBatch={1} - - // We need to use our own scroll component to workaround a maintainVisibleContentPosition for web - // eslint-disable-next-line react/jsx-props-no-spreading - renderScrollComponent={(_props) => } /> ); } From e8c96dbb91a53b99b01945526c44064f60e391b0 Mon Sep 17 00:00:00 2001 From: Justin Persaud Date: Fri, 20 Oct 2023 16:07:59 -0400 Subject: [PATCH 065/218] Use OSBotify App Token in cherryPick --- .github/workflows/cherryPick.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index e6da6fff1446..43f3c64554bc 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -41,6 +41,7 @@ jobs: token: ${{ secrets.OS_BOTIFY_TOKEN }} - name: Set up git for OSBotify + id: setupGitForOSBotify uses: Expensify/App/.github/actions/composite/setupGitForOSBotifyApp@8c19d6da4a3d7ce3b15c9cd89a802187d208ecab with: GPG_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} @@ -119,7 +120,7 @@ jobs: **Important:** There may be conflicts that GitHub is not able to detect, so please _carefully_ review this pull request before approving." gh pr edit --add-assignee "${{ github.actor }},${{ steps.getCPMergeCommit.outputs.MERGE_ACTOR }}" env: - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} - name: "Announces a CP failure in the #announce Slack room" uses: 8398a7/action-slack@v3 From c0a57b91e18bf71ba1eddca884f482ca8eb5620f Mon Sep 17 00:00:00 2001 From: Justin Persaud Date: Fri, 20 Oct 2023 16:10:39 -0400 Subject: [PATCH 066/218] use new token in finishReleaseCycle --- .github/workflows/finishReleaseCycle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml index 4fe6249edacc..f8b68786aaab 100644 --- a/.github/workflows/finishReleaseCycle.yml +++ b/.github/workflows/finishReleaseCycle.yml @@ -34,13 +34,13 @@ jobs: echo "IS_DEPLOYER=false" >> "$GITHUB_OUTPUT" fi env: - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} - name: Reopen and comment on issue (not a team member) if: ${{ !fromJSON(steps.isDeployer.outputs.IS_DEPLOYER) }} uses: Expensify/App/.github/actions/javascript/reopenIssueWithComment@main with: - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} ISSUE_NUMBER: ${{ github.event.issue.number }} COMMENT: | Sorry, only members of @Expensify/Mobile-Deployers can close deploy checklists. @@ -51,14 +51,14 @@ jobs: id: checkDeployBlockers uses: Expensify/App/.github/actions/javascript/checkDeployBlockers@main with: - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} ISSUE_NUMBER: ${{ github.event.issue.number }} - name: Reopen and comment on issue (has blockers) if: ${{ fromJSON(steps.isDeployer.outputs.IS_DEPLOYER) && fromJSON(steps.checkDeployBlockers.outputs.HAS_DEPLOY_BLOCKERS || 'false') }} uses: Expensify/App/.github/actions/javascript/reopenIssueWithComment@main with: - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} + GITHUB_TOKEN: ${{ steps.setupGitForOSBotify.outputs.OS_BOTIFY_API_TOKEN }} ISSUE_NUMBER: ${{ github.event.issue.number }} COMMENT: | This issue either has unchecked items or has not yet been marked with the `:shipit:` emoji of approval. From 4a67723154ac01bc139a5a06e5c0ea229a6dae07 Mon Sep 17 00:00:00 2001 From: Aswin S Date: Sun, 22 Oct 2023 05:53:01 +0530 Subject: [PATCH 067/218] fix: append whitespace after emoji --- src/libs/ComposerUtils/index.ts | 5 +- .../ComposerWithSuggestions.js | 59 +++++++++++++------ 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index 5e2a42fc65dd..987615f17695 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -32,7 +32,10 @@ function canSkipTriggerHotkeys(isSmallScreenWidth: boolean, isKeyboardShown: boo */ function getCommonSuffixLength(str1: string, str2: string): number { let i = 0; - while (str1[str1.length - 1 - i] === str2[str2.length - 1 - i]) { + if(str1.length===0||str2.length===0){ + return 0; + } + while (str1[str1.length - 1 - i] === str2[str2.length - 1 - i]) { i++; } return i; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index e194d0870885..a1950ad2a96e 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -116,6 +116,7 @@ function ComposerWithSuggestions({ return draft; }); const commentRef = useRef(value); + const lastTextRef = useRef(value); const {isSmallScreenWidth} = useWindowDimensions(); const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; @@ -194,6 +195,31 @@ function ComposerWithSuggestions({ RNTextInputReset.resetKeyboardInput(findNodeHandle(textInputRef.current)); }, [textInputRef]); + const findNewlyAddedChars = useCallback( + (prevText, newText) => { + const isTextReplace = selection.end - selection.start > 0; + const commonSuffixLength =ComposerUtils.getCommonSuffixLength(prevText, newText); + let startIndex = -1; + let endIndex = -1; + let i = 0; + + while (i < newText.length && prevText.charAt(i) === newText.charAt(i) && selection.start > i) { + i++; + } + + if (i < newText.length) { + startIndex = i; + // if text is getting pasted over find length of common suffix and subtract it from new text length + endIndex = isTextReplace ? newText.length-commonSuffixLength : i + (newText.length - prevText.length); + } + + return {startIndex, endIndex, diff: newText.substring(startIndex, endIndex)}; + }, + [selection.end, selection.start], + ); + + const insertWhiteSpace = (text, index) => `${text.slice(0, index)} ${text.slice(index)}`; + const debouncedSaveReportComment = useMemo( () => _.debounce((selectedReportID, newComment) => { @@ -211,7 +237,13 @@ function ComposerWithSuggestions({ const updateComment = useCallback( (commentValue, shouldDebounceSaveComment) => { raiseIsScrollLikelyLayoutTriggered(); - const {text: newComment, emojis} = EmojiUtils.replaceAndExtractEmojis(commentValue, preferredSkinTone, preferredLocale); + const {startIndex, endIndex, diff} = findNewlyAddedChars(lastTextRef.current, commentValue); + const isEmojiInserted = diff.length && endIndex > startIndex && EmojiUtils.containsOnlyEmojis(diff); + const {text: newComment, emojis} = EmojiUtils.replaceAndExtractEmojis( + isEmojiInserted ? insertWhiteSpace(commentValue, endIndex) : commentValue, + preferredSkinTone, + preferredLocale, + ); if (!_.isEmpty(emojis)) { const newEmojis = EmojiUtils.getAddedEmojis(emojis, emojisPresentBefore.current); @@ -255,16 +287,7 @@ function ComposerWithSuggestions({ debouncedBroadcastUserIsTyping(reportID); } }, - [ - debouncedUpdateFrequentlyUsedEmojis, - preferredLocale, - preferredSkinTone, - reportID, - setIsCommentEmpty, - suggestionsRef, - raiseIsScrollLikelyLayoutTriggered, - debouncedSaveReportComment, - ], + [raiseIsScrollLikelyLayoutTriggered, findNewlyAddedChars, preferredSkinTone, preferredLocale, setIsCommentEmpty, debouncedUpdateFrequentlyUsedEmojis, suggestionsRef, reportID, debouncedSaveReportComment], ); /** @@ -313,14 +336,8 @@ function ComposerWithSuggestions({ * @param {Boolean} shouldAddTrailSpace */ const replaceSelectionWithText = useCallback( - (text, shouldAddTrailSpace = true) => { - const updatedText = shouldAddTrailSpace ? `${text} ` : text; - const selectionSpaceLength = shouldAddTrailSpace ? CONST.SPACE_LENGTH : 0; - updateComment(ComposerUtils.insertText(commentRef.current, selection, updatedText)); - setSelection((prevSelection) => ({ - start: prevSelection.start + text.length + selectionSpaceLength, - end: prevSelection.start + text.length + selectionSpaceLength, - })); + (text) => { + updateComment(ComposerUtils.insertText(commentRef.current, selection, text)); }, [selection, updateComment], ); @@ -508,6 +525,10 @@ function ComposerWithSuggestions({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + lastTextRef.current = value; + }, [value]); + useImperativeHandle( forwardedRef, () => ({ From 6db20146c0d0d11ff350255c0ba8e6e02c5020b3 Mon Sep 17 00:00:00 2001 From: Aswin S Date: Sun, 22 Oct 2023 06:18:40 +0530 Subject: [PATCH 068/218] fix: prevent infinite loop --- src/libs/ComposerUtils/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index 987615f17695..bf22fcb04a49 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -35,7 +35,8 @@ function getCommonSuffixLength(str1: string, str2: string): number { if(str1.length===0||str2.length===0){ return 0; } - while (str1[str1.length - 1 - i] === str2[str2.length - 1 - i]) { + const minLen = Math.min(str1.length, str2.length); + while (i>minLen && str1[str1.length - 1 - i] === str2[str2.length - 1 - i]) { i++; } return i; From a8bd00576b2efad25380615217eb9904203dcda4 Mon Sep 17 00:00:00 2001 From: Aswin S Date: Sun, 22 Oct 2023 06:24:05 +0530 Subject: [PATCH 069/218] fix: clean lint --- src/libs/ComposerUtils/index.ts | 4 ++-- .../ComposerWithSuggestions.js | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libs/ComposerUtils/index.ts b/src/libs/ComposerUtils/index.ts index bf22fcb04a49..3167ce851e60 100644 --- a/src/libs/ComposerUtils/index.ts +++ b/src/libs/ComposerUtils/index.ts @@ -32,11 +32,11 @@ function canSkipTriggerHotkeys(isSmallScreenWidth: boolean, isKeyboardShown: boo */ function getCommonSuffixLength(str1: string, str2: string): number { let i = 0; - if(str1.length===0||str2.length===0){ + if (str1.length === 0 || str2.length === 0) { return 0; } const minLen = Math.min(str1.length, str2.length); - while (i>minLen && str1[str1.length - 1 - i] === str2[str2.length - 1 - i]) { + while (i < minLen && str1[str1.length - 1 - i] === str2[str2.length - 1 - i]) { i++; } return i; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index a1950ad2a96e..d8b3bc8f820a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -198,7 +198,7 @@ function ComposerWithSuggestions({ const findNewlyAddedChars = useCallback( (prevText, newText) => { const isTextReplace = selection.end - selection.start > 0; - const commonSuffixLength =ComposerUtils.getCommonSuffixLength(prevText, newText); + const commonSuffixLength = ComposerUtils.getCommonSuffixLength(prevText, newText); let startIndex = -1; let endIndex = -1; let i = 0; @@ -210,7 +210,7 @@ function ComposerWithSuggestions({ if (i < newText.length) { startIndex = i; // if text is getting pasted over find length of common suffix and subtract it from new text length - endIndex = isTextReplace ? newText.length-commonSuffixLength : i + (newText.length - prevText.length); + endIndex = isTextReplace ? newText.length - commonSuffixLength : i + (newText.length - prevText.length); } return {startIndex, endIndex, diff: newText.substring(startIndex, endIndex)}; @@ -287,7 +287,17 @@ function ComposerWithSuggestions({ debouncedBroadcastUserIsTyping(reportID); } }, - [raiseIsScrollLikelyLayoutTriggered, findNewlyAddedChars, preferredSkinTone, preferredLocale, setIsCommentEmpty, debouncedUpdateFrequentlyUsedEmojis, suggestionsRef, reportID, debouncedSaveReportComment], + [ + raiseIsScrollLikelyLayoutTriggered, + findNewlyAddedChars, + preferredSkinTone, + preferredLocale, + setIsCommentEmpty, + debouncedUpdateFrequentlyUsedEmojis, + suggestionsRef, + reportID, + debouncedSaveReportComment, + ], ); /** From 69272012a274d2c350b165573ca2d24bc29f85cd Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 23 Oct 2023 11:14:29 +0200 Subject: [PATCH 070/218] WIP testing maintainVisibleContentPosition --- src/components/InvertedFlatList/index.js | 325 ++++++++++++++--------- 1 file changed, 194 insertions(+), 131 deletions(-) diff --git a/src/components/InvertedFlatList/index.js b/src/components/InvertedFlatList/index.js index 564db6296c9b..f2414d577222 100644 --- a/src/components/InvertedFlatList/index.js +++ b/src/components/InvertedFlatList/index.js @@ -1,140 +1,203 @@ -import React, {forwardRef, useEffect, useRef} from 'react'; +import React, {forwardRef, useEffect, useRef, useState} from 'react'; import PropTypes from 'prop-types'; -import {DeviceEventEmitter, FlatList, StyleSheet} from 'react-native'; +import {DeviceEventEmitter, StyleSheet, View, Text, Button} from 'react-native'; import _ from 'underscore'; import BaseInvertedFlatList from './BaseInvertedFlatList'; -import styles from '../../styles/styles'; +// import styles from '../../styles/styles'; import CONST from '../../CONST'; +import FlatList from '../FlatList/index.web'; + +// const propTypes = { +// /** Passed via forwardRef so we can access the FlatList ref */ +// innerRef: PropTypes.shape({ +// current: PropTypes.instanceOf(FlatList), +// }).isRequired, + +// /** Any additional styles to apply */ +// // eslint-disable-next-line react/forbid-prop-types +// contentContainerStyle: PropTypes.any, + +// /** Same as for FlatList */ +// onScroll: PropTypes.func, +// }; + +// // This is adapted from https://codesandbox.io/s/react-native-dsyse +// // It's a HACK alert since FlatList has inverted scrolling on web +// function InvertedFlatList(props) { +// const {innerRef, contentContainerStyle} = props; +// const listRef = React.createRef(); + +// const lastScrollEvent = useRef(null); +// const scrollEndTimeout = useRef(null); +// const updateInProgress = useRef(false); +// const eventHandler = useRef(null); + +// useEffect(() => { +// if (!_.isFunction(innerRef)) { +// // eslint-disable-next-line no-param-reassign +// innerRef.current = listRef.current; +// } else { +// innerRef(listRef); +// } + +// return () => { +// if (scrollEndTimeout.current) { +// clearTimeout(scrollEndTimeout.current); +// } + +// if (eventHandler.current) { +// eventHandler.current.remove(); +// } +// }; +// }, [innerRef, listRef]); + +// /** +// * Emits when the scrolling is in progress. Also, +// * invokes the onScroll callback function from props. +// * +// * @param {Event} event - The onScroll event from the FlatList +// */ +// const onScroll = (event) => { +// props.onScroll(event); + +// if (!updateInProgress.current) { +// updateInProgress.current = true; +// eventHandler.current = DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, true); +// } +// }; + +// /** +// * Emits when the scrolling has ended. +// */ +// const onScrollEnd = () => { +// eventHandler.current = DeviceEventEmitter.emit(CONST.EVENTS.SCROLLING, false); +// updateInProgress.current = false; +// }; + +// /** +// * Decides whether the scrolling has ended or not. If it has ended, +// * then it calls the onScrollEnd function. Otherwise, it calls the +// * onScroll function and pass the event to it. +// * +// * This is a temporary work around, since react-native-web doesn't +// * support onScrollBeginDrag and onScrollEndDrag props for FlatList. +// * More info: +// * https://github.com/necolas/react-native-web/pull/1305 +// * +// * This workaround is taken from below and refactored to fit our needs: +// * https://github.com/necolas/react-native-web/issues/1021#issuecomment-984151185 +// * +// * @param {Event} event - The onScroll event from the FlatList +// */ +// const handleScroll = (event) => { +// onScroll(event); +// const timestamp = Date.now(); + +// if (scrollEndTimeout.current) { +// clearTimeout(scrollEndTimeout.current); +// } + +// if (lastScrollEvent.current) { +// scrollEndTimeout.current = setTimeout(() => { +// if (lastScrollEvent.current !== timestamp) { +// return; +// } +// // Scroll has ended +// lastScrollEvent.current = null; +// onScrollEnd(); +// }, 250); +// } + +// lastScrollEvent.current = timestamp; +// }; + +// return ( +// +// ); +// } + +// InvertedFlatList.propTypes = propTypes; +// InvertedFlatList.defaultProps = { +// contentContainerStyle: {}, +// onScroll: () => {}, +// }; + +// export default forwardRef((props, ref) => ( +// +// )); + + + +function ReportScreen() { + const [data, setData] = useState(generatePosts(15)); + + const loadNewerChats = () => { + const lastId = data[0].id - 1; + setData([...generatePosts(5, lastId - 4), ...data]); + }; + + const renderItem = ({ item }) => ; + const keyExtractor = (item) => item.id.toString(); + + return ( + <> + +