diff --git a/android/app/build.gradle b/android/app/build.gradle
index 1a663b5fa08a..d78c4b107e84 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -107,8 +107,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001047606
- versionName "1.4.76-6"
+ versionCode 1001047700
+ versionName "1.4.77-0"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/assets/images/comment-bubbles.svg b/assets/images/comment-bubbles.svg
new file mode 100644
index 000000000000..748134b71dfb
--- /dev/null
+++ b/assets/images/comment-bubbles.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/assets/images/stopwatch.svg b/assets/images/stopwatch.svg
index 0f26af219e04..7ea3a0bca261 100644
--- a/assets/images/stopwatch.svg
+++ b/assets/images/stopwatch.svg
@@ -1 +1,13 @@
-
\ No newline at end of file
+
+
+
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 2884e1b4318d..9c95f45f3ddd 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.4.76
+ 1.4.77
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.76.6
+ 1.4.77.0
FullStory
OrgId
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 628d11fb7896..e88964613a93 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.4.76
+ 1.4.77
CFBundleSignature
????
CFBundleVersion
- 1.4.76.6
+ 1.4.77.0
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 48f43e75badf..311360dd247f 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName
$(PRODUCT_NAME)
CFBundleShortVersionString
- 1.4.76
+ 1.4.77
CFBundleVersion
- 1.4.76.6
+ 1.4.77.0
NSExtension
NSExtensionPointIdentifier
diff --git a/package-lock.json b/package-lock.json
index 5d02069bbf04..3275ee64d44f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.76-6",
+ "version": "1.4.77-0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.76-6",
+ "version": "1.4.77-0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -59,7 +59,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#1713f28214f0e7176c4fd13433fb0ea15491ebf9",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#18fa764be9d68f72b48d238dcc20f2b0ca8f1147",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
@@ -103,7 +103,7 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "2.0.41",
+ "react-native-onyx": "2.0.32",
"react-native-pager-view": "6.2.3",
"react-native-pdf": "6.7.3",
"react-native-performance": "^5.1.0",
@@ -241,7 +241,7 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"type-fest": "^4.10.2",
- "typescript": "^5.4.5",
+ "typescript": "^5.3.2",
"wait-port": "^0.2.9",
"webpack": "^5.76.0",
"webpack-bundle-analyzer": "^4.5.0",
@@ -20342,8 +20342,8 @@
},
"node_modules/expensify-common": {
"version": "1.0.0",
- "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#1713f28214f0e7176c4fd13433fb0ea15491ebf9",
- "integrity": "sha512-uy1+axUTTuPKwAR06xNG/tGIJ+uaavmSQgKiNU7pQVR94ibNzDD2WESn2E7OEP9/QrHa61lfFlluTjFvvz5I8Q==",
+ "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#18fa764be9d68f72b48d238dcc20f2b0ca8f1147",
+ "integrity": "sha512-AbeXop0pAVnkOJ7uVShqF7q9xwOYADW1mit0kK73ADkNuuQuHCYTqQSsQDuLaG80c5N96h+NZF/9LvcrhU2aFw==",
"license": "MIT",
"dependencies": {
"classnames": "2.5.0",
@@ -31472,9 +31472,9 @@
}
},
"node_modules/react-native-onyx": {
- "version": "2.0.41",
- "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.41.tgz",
- "integrity": "sha512-33r0sVBq7MV/GZwRneRt81uxgW8x3YG75VNJvThycB/dkCnGCfbxoVkZADVH3ET3jzfFXy9wnS06sZnZp78zMQ==",
+ "version": "2.0.32",
+ "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.32.tgz",
+ "integrity": "sha512-tB9wqMJGTLOYfrfplRP+9aq5JdD8w/hV/OZsMAVH+ewbE1zLY8OymUsAsIFdF1v+cB8HhehP569JVLZmhm6bsg==",
"dependencies": {
"ascii-table": "0.0.9",
"fast-equals": "^4.0.3",
@@ -36075,10 +36075,9 @@
}
},
"node_modules/typescript": {
- "version": "5.4.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
- "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "version": "5.3.3",
"devOptional": true,
+ "license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index 88b47e803558..b3c7267abe73 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.76-6",
+ "version": "1.4.77-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -111,7 +111,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#1713f28214f0e7176c4fd13433fb0ea15491ebf9",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#18fa764be9d68f72b48d238dcc20f2b0ca8f1147",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
@@ -155,7 +155,7 @@
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
- "react-native-onyx": "2.0.41",
+ "react-native-onyx": "2.0.32",
"react-native-pager-view": "6.2.3",
"react-native-pdf": "6.7.3",
"react-native-performance": "^5.1.0",
@@ -293,7 +293,7 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"type-fest": "^4.10.2",
- "typescript": "^5.4.5",
+ "typescript": "^5.3.2",
"wait-port": "^0.2.9",
"webpack": "^5.76.0",
"webpack-bundle-analyzer": "^4.5.0",
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 8d11c889abd9..862c577c8a26 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -98,6 +98,7 @@ const ROUTES = {
SETTINGS_TIMEZONE_SELECT: 'settings/profile/timezone/select',
SETTINGS_PRONOUNS: 'settings/profile/pronouns',
SETTINGS_PREFERENCES: 'settings/preferences',
+ SETTINGS_SUBSCRIPTION: 'settings/subscription',
SETTINGS_PRIORITY_MODE: 'settings/preferences/priority-mode',
SETTINGS_LANGUAGE: 'settings/preferences/language',
SETTINGS_THEME: 'settings/preferences/theme',
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index f74002312623..4e7243d0eb2c 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -103,6 +103,10 @@ const SCREENS = {
RESPONSE: 'Settings_ExitSurvey_Response',
CONFIRM: 'Settings_ExitSurvey_Confirm',
},
+
+ SUBSCRIPTION: {
+ ROOT: 'Settings_Subscription',
+ },
},
SAVE_THE_WORLD: {
ROOT: 'SaveTheWorld_Root',
diff --git a/src/components/AddressForm.tsx b/src/components/AddressForm.tsx
index 296ecce7d092..9ad4643e834a 100644
--- a/src/components/AddressForm.tsx
+++ b/src/components/AddressForm.tsx
@@ -4,6 +4,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import type {MaybePhraseKey} from '@libs/Localize';
+import Navigation from '@libs/Navigation/Navigation';
import * as ValidationUtils from '@libs/ValidationUtils';
import CONST from '@src/CONST';
import type {Country} from '@src/CONST';
@@ -148,6 +149,8 @@ function AddressForm({
label={translate('common.addressLine', {lineNumber: 1})}
onValueChange={(data: unknown, key: unknown) => {
onAddressChanged(data, key);
+ // This enforces the country selector to use the country from address instead of the country from URL
+ Navigation.setParams({country: undefined});
}}
defaultValue={street1}
renamedInputKeys={{
diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx
index de98ba79d23c..fb6a8e911e87 100644
--- a/src/components/AttachmentModal.tsx
+++ b/src/components/AttachmentModal.tsx
@@ -607,6 +607,7 @@ export default withOnyx({
const transactionID = parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction?.originalMessage.IOUTransactionID ?? '0' : '0';
return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
},
+ initWithStoredValues: false,
},
})(memo(AttachmentModal));
diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx
index 156c27abbc9d..8942bf97a7dd 100644
--- a/src/components/AvatarWithDisplayName.tsx
+++ b/src/components/AvatarWithDisplayName.tsx
@@ -1,6 +1,6 @@
import React, {useCallback, useEffect, useRef} from 'react';
import {View} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import useStyleUtils from '@hooks/useStyleUtils';
@@ -12,7 +12,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import type {PersonalDetails, PersonalDetailsList, Policy, Report, ReportActions} from '@src/types/onyx';
+import type {PersonalDetails, Policy, Report, ReportActions} from '@src/types/onyx';
import DisplayNames from './DisplayNames';
import MultipleAvatars from './MultipleAvatars';
import ParentNavigationSubtitle from './ParentNavigationSubtitle';
@@ -25,7 +25,7 @@ type AvatarWithDisplayNamePropsWithOnyx = {
parentReportActions: OnyxEntry;
/** Personal details of all users */
- personalDetails: OnyxEntry;
+ personalDetails: OnyxCollection;
};
type AvatarWithDisplayNameProps = AvatarWithDisplayNamePropsWithOnyx & {
diff --git a/src/components/ChatDetailsQuickActionsBar.tsx b/src/components/ChatDetailsQuickActionsBar.tsx
deleted file mode 100644
index f15fc31aec45..000000000000
--- a/src/components/ChatDetailsQuickActionsBar.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React, {useState} from 'react';
-import {View} from 'react-native';
-import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@hooks/useThemeStyles';
-import * as Report from '@userActions/Report';
-import type {Report as OnyxReportType} from '@src/types/onyx';
-import Button from './Button';
-import ConfirmModal from './ConfirmModal';
-import * as Expensicons from './Icon/Expensicons';
-
-type ChatDetailsQuickActionsBarProps = {
- report: OnyxReportType;
-};
-
-function ChatDetailsQuickActionsBar({report}: ChatDetailsQuickActionsBarProps) {
- const styles = useThemeStyles();
- const [isLastMemberLeavingGroupModalVisible, setIsLastMemberLeavingGroupModalVisible] = useState(false);
- const {translate} = useLocalize();
- const isPinned = !!report.isPinned;
- return (
-
-
- {
- setIsLastMemberLeavingGroupModalVisible(false);
- Report.leaveGroupChat(report.reportID);
- }}
- onCancel={() => setIsLastMemberLeavingGroupModalVisible(false)}
- prompt={translate('groupChat.lastMemberWarning')}
- confirmText={translate('common.leave')}
- cancelText={translate('common.cancel')}
- />
-
-
-
-
- );
-}
-
-ChatDetailsQuickActionsBar.displayName = 'ChatDetailsQuickActionsBar';
-
-export default ChatDetailsQuickActionsBar;
diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts
index f5211993987b..bcc40947a83a 100644
--- a/src/components/Icon/Expensicons.ts
+++ b/src/components/Icon/Expensicons.ts
@@ -42,6 +42,7 @@ import Close from '@assets/images/close.svg';
import ClosedSign from '@assets/images/closed-sign.svg';
import Coins from '@assets/images/coins.svg';
import Collapse from '@assets/images/collapse.svg';
+import CommentBubbles from '@assets/images/comment-bubbles.svg';
import Concierge from '@assets/images/concierge.svg';
import Connect from '@assets/images/connect.svg';
import ConnectionComplete from '@assets/images/connection-complete.svg';
@@ -207,6 +208,7 @@ export {
Close,
ClosedSign,
Collapse,
+ CommentBubbles,
Concierge,
ConciergeAvatar,
Connect,
diff --git a/src/components/KeyboardAvoidingView/index.ios.tsx b/src/components/KeyboardAvoidingView/index.ios.tsx
index 171210eab7ac..a7cd767377ef 100644
--- a/src/components/KeyboardAvoidingView/index.ios.tsx
+++ b/src/components/KeyboardAvoidingView/index.ios.tsx
@@ -3,7 +3,7 @@
*/
import React from 'react';
import {KeyboardAvoidingView as KeyboardAvoidingViewComponent} from 'react-native';
-import type {KeyboardAvoidingViewProps} from './types';
+import type KeyboardAvoidingViewProps from './types';
function KeyboardAvoidingView(props: KeyboardAvoidingViewProps) {
// eslint-disable-next-line react/jsx-props-no-spreading
diff --git a/src/components/KeyboardAvoidingView/index.tsx b/src/components/KeyboardAvoidingView/index.tsx
index c0882ae1e9cc..09ec21e5b219 100644
--- a/src/components/KeyboardAvoidingView/index.tsx
+++ b/src/components/KeyboardAvoidingView/index.tsx
@@ -3,7 +3,7 @@
*/
import React from 'react';
import {View} from 'react-native';
-import type {KeyboardAvoidingViewProps} from './types';
+import type KeyboardAvoidingViewProps from './types';
function KeyboardAvoidingView(props: KeyboardAvoidingViewProps) {
const {behavior, contentContainerStyle, enabled, keyboardVerticalOffset, ...rest} = props;
diff --git a/src/components/KeyboardAvoidingView/types.ts b/src/components/KeyboardAvoidingView/types.ts
index 2c1ef64ced8f..48d354e8b53f 100644
--- a/src/components/KeyboardAvoidingView/types.ts
+++ b/src/components/KeyboardAvoidingView/types.ts
@@ -1,4 +1,3 @@
-import type {KeyboardAvoidingViewProps} from 'react-native';
+import {KeyboardAvoidingViewProps} from 'react-native';
-// eslint-disable-next-line import/prefer-default-export
-export type {KeyboardAvoidingViewProps};
+export default KeyboardAvoidingViewProps;
diff --git a/src/components/PromotedActionsBar.tsx b/src/components/PromotedActionsBar.tsx
new file mode 100644
index 000000000000..0a83a9b94d5f
--- /dev/null
+++ b/src/components/PromotedActionsBar.tsx
@@ -0,0 +1,118 @@
+import React, {useState} from 'react';
+import type {StyleProp, ViewStyle} from 'react-native';
+import {View} from 'react-native';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as HeaderUtils from '@libs/HeaderUtils';
+import * as ReportActions from '@userActions/Report';
+import type OnyxReport from '@src/types/onyx/Report';
+import Button from './Button';
+import ConfirmModal from './ConfirmModal';
+import type {ThreeDotsMenuItem} from './HeaderWithBackButton/types';
+import * as Expensicons from './Icon/Expensicons';
+
+type PromotedAction = {
+ key: string;
+} & ThreeDotsMenuItem;
+
+type ReportPromotedAction = (report: OnyxReport) => PromotedAction;
+
+type PromotedActionsType = {
+ pin: ReportPromotedAction;
+};
+
+const PromotedActions = {
+ pin: (report) => ({
+ key: 'pin',
+ ...HeaderUtils.getPinMenuItem(report),
+ }),
+} satisfies PromotedActionsType;
+
+type PromotedActionsBarProps = {
+ /** The report of actions */
+ report?: OnyxReport;
+
+ /** The list of actions to show */
+ promotedActions: PromotedAction[];
+
+ /** The style of the container */
+ containerStyle?: StyleProp;
+
+ /**
+ * Whether to show the `Leave` button.
+ * @deprecated Remove this prop when @src/pages/ReportDetailsPage.tsx is updated
+ */
+ shouldShowLeaveButton?: boolean;
+};
+
+function PromotedActionsBar({report, promotedActions, containerStyle, shouldShowLeaveButton}: PromotedActionsBarProps) {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+ const [isLastMemberLeavingGroupModalVisible, setIsLastMemberLeavingGroupModalVisible] = useState(false);
+ const {translate} = useLocalize();
+
+ if (promotedActions.length === 0) {
+ return null;
+ }
+
+ return (
+
+ {/* TODO: Remove the `Leave` button when @src/pages/ReportDetailsPage.tsx is updated */}
+ {shouldShowLeaveButton && report && (
+ // The `Leave` button is left to make the component backward compatible with the existing code.
+ // After the `Leave` button is moved to the `MenuItem` list, this block can be removed.
+
+ {
+ setIsLastMemberLeavingGroupModalVisible(false);
+ ReportActions.leaveGroupChat(report.reportID);
+ }}
+ onCancel={() => setIsLastMemberLeavingGroupModalVisible(false)}
+ prompt={translate('groupChat.lastMemberWarning')}
+ confirmText={translate('common.leave')}
+ cancelText={translate('common.cancel')}
+ />
+
+ )}
+ {promotedActions.map(({key, onSelected, ...props}) => (
+
+
+
+ ))}
+
+ );
+}
+
+PromotedActionsBar.displayName = 'PromotedActionsBar';
+
+export default PromotedActionsBar;
+
+export {PromotedActions};
+export type {PromotedActionsBarProps, PromotedAction};
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 0f5822b9f411..af674bc048ba 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -1924,6 +1924,7 @@ export default {
distanceRates: 'Distance rates',
welcomeNote: ({workspaceName}: WelcomeNoteParams) =>
`You have been invited to ${workspaceName || 'a workspace'}! Download the Expensify mobile app at use.expensify.com/download to start tracking your expenses.`,
+ subscription: 'Subscription',
},
qbo: {
importDescription: 'Choose which coding configurations are imported from QuickBooks Online to Expensify.',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 4c636fa1ddf1..f3fb29eb0060 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1948,6 +1948,7 @@ export default {
distanceRates: 'Tasas de distancia',
welcomeNote: ({workspaceName}: WelcomeNoteParams) =>
`¡Has sido invitado a ${workspaceName}! Descargue la aplicación móvil Expensify en use.expensify.com/download para comenzar a rastrear sus gastos.`,
+ subscription: 'Suscripción',
},
qbo: {
importDescription: 'Elige que configuraciónes de codificación son importadas desde QuickBooks Online a Expensify.',
diff --git a/src/languages/types.ts b/src/languages/types.ts
index ef984b9e640a..e2e7e26e696b 100644
--- a/src/languages/types.ts
+++ b/src/languages/types.ts
@@ -1,4 +1,3 @@
-import type {OnyxEntry} from 'react-native-onyx';
import type {ReportAction} from '@src/types/onyx';
import type {Unit} from '@src/types/onyx/Policy';
import type en from './en';
@@ -41,15 +40,15 @@ type LocalTimeParams = {
};
type EditActionParams = {
- action: OnyxEntry;
+ action: ReportAction | null;
};
type DeleteActionParams = {
- action: OnyxEntry;
+ action: ReportAction | null;
};
type DeleteConfirmationParams = {
- action: OnyxEntry;
+ action: ReportAction | null;
};
type BeginningOfChatHistoryDomainRoomPartOneParams = {
@@ -300,10 +299,11 @@ type DistanceRateOperationsParams = {count: number};
type ReimbursementRateParams = {unit: Unit};
export type {
- AddressLineParams,
AdminCanceledRequestParams,
- AlreadySignedInParams,
ApprovedAmountParams,
+ AddressLineParams,
+ AlreadySignedInParams,
+ UserSplitParams,
BeginningOfChatHistoryAdminRoomPartOneParams,
BeginningOfChatHistoryAnnounceRoomPartOneParams,
BeginningOfChatHistoryAnnounceRoomPartTwo,
@@ -324,10 +324,8 @@ export type {
FormattedMaxLengthParams,
GoBackMessageParams,
GoToRoomParams,
- HeldRequestParams,
InstantSummaryParams,
LocalTimeParams,
- LogSizeParams,
LoggedInAsParams,
ManagerApprovedAmountParams,
ManagerApprovedParams,
@@ -341,13 +339,11 @@ export type {
PaidElsewhereWithAmountParams,
PaidWithExpensifyWithAmountParams,
ParentNavigationSummaryParams,
- PaySomeoneParams,
PayerOwesAmountParams,
PayerOwesParams,
PayerPaidAmountParams,
PayerPaidParams,
PayerSettledParams,
- ReimbursementRateParams,
RemovedTheRequestParams,
RenamedRoomActionParams,
ReportArchiveReasonsClosedParams,
@@ -379,9 +375,7 @@ export type {
UntilTimeParams,
UpdatedTheDistanceParams,
UpdatedTheRequestParams,
- UsePlusButtonParams,
UserIsAlreadyMemberParams,
- UserSplitParams,
ViolationsAutoReportedRejectedExpenseParams,
ViolationsCashExpenseWithNoReceiptParams,
ViolationsConversionSurchargeParams,
@@ -398,9 +392,14 @@ export type {
ViolationsTaxOutOfPolicyParams,
WaitingOnBankAccountParams,
WalletProgramParams,
+ UsePlusButtonParams,
WeSentYouMagicSignInLinkParams,
WelcomeEnterMagicCodeParams,
WelcomeNoteParams,
WelcomeToRoomParams,
ZipCodeExampleFormatParams,
+ LogSizeParams,
+ HeldRequestParams,
+ PaySomeoneParams,
+ ReimbursementRateParams,
};
diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts
index 9d82cefe831c..0be7e76a0aa9 100644
--- a/src/libs/EmojiUtils.ts
+++ b/src/libs/EmojiUtils.ts
@@ -433,7 +433,7 @@ function suggestEmojis(text: string, lang: Locale, limit: number = CONST.AUTO_CO
/**
* Retrieve preferredSkinTone as Number to prevent legacy 'default' String value
*/
-const getPreferredSkinToneIndex = (value: OnyxEntry): number => {
+const getPreferredSkinToneIndex = (value: string | number | null): number => {
if (value !== null && Number.isInteger(Number(value))) {
return Number(value);
}
diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts
index ecaf563d59d1..3487f05b9c05 100644
--- a/src/libs/ErrorUtils.ts
+++ b/src/libs/ErrorUtils.ts
@@ -1,5 +1,4 @@
import mapValues from 'lodash/mapValues';
-import type {OnyxEntry} from 'react-native-onyx';
import CONST from '@src/CONST';
import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types';
import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon';
@@ -62,7 +61,7 @@ type OnyxDataWithErrors = {
errors?: Errors | null;
};
-function getLatestErrorMessage(onyxData: OnyxEntry): Localize.MaybePhraseKey {
+function getLatestErrorMessage(onyxData: TOnyxData | null): Localize.MaybePhraseKey {
const errors = onyxData?.errors ?? {};
if (Object.keys(errors).length === 0) {
@@ -73,8 +72,8 @@ function getLatestErrorMessage(onyxData: O
return getErrorMessageWithTranslationData(errors[key]);
}
-function getLatestErrorMessageField(onyxData: OnyxEntry): Errors {
- const errors = onyxData?.errors ?? {};
+function getLatestErrorMessageField(onyxData: TOnyxData): Errors {
+ const errors = onyxData.errors ?? {};
if (Object.keys(errors).length === 0) {
return {};
@@ -89,8 +88,8 @@ type OnyxDataWithErrorFields = {
errorFields?: ErrorFields;
};
-function getLatestErrorField(onyxData: OnyxEntry, fieldName: string): Errors {
- const errorsForField = onyxData?.errorFields?.[fieldName] ?? {};
+function getLatestErrorField(onyxData: TOnyxData, fieldName: string): Errors {
+ const errorsForField = onyxData.errorFields?.[fieldName] ?? {};
if (Object.keys(errorsForField).length === 0) {
return {};
@@ -100,8 +99,8 @@ function getLatestErrorField(onyxData
return {[key]: getErrorMessageWithTranslationData(errorsForField[key])};
}
-function getEarliestErrorField(onyxData: OnyxEntry, fieldName: string): Errors {
- const errorsForField = onyxData?.errorFields?.[fieldName] ?? {};
+function getEarliestErrorField(onyxData: TOnyxData, fieldName: string): Errors {
+ const errorsForField = onyxData.errorFields?.[fieldName] ?? {};
if (Object.keys(errorsForField).length === 0) {
return {};
@@ -114,8 +113,8 @@ function getEarliestErrorField(onyxDa
/**
* Method used to get the latest error field for any field
*/
-function getLatestErrorFieldForAnyField(onyxData: OnyxEntry): Errors {
- const errorFields = onyxData?.errorFields ?? {};
+function getLatestErrorFieldForAnyField(onyxData: TOnyxData): Errors {
+ const errorFields = onyxData.errorFields ?? {};
if (Object.keys(errorFields).length === 0) {
return {};
@@ -190,9 +189,9 @@ export {
getErrorMessageWithTranslationData,
getErrorsWithTranslationData,
getLatestErrorField,
- getLatestErrorFieldForAnyField,
getLatestErrorMessage,
getLatestErrorMessageField,
+ getLatestErrorFieldForAnyField,
getMicroSecondOnyxError,
getMicroSecondOnyxErrorObject,
isReceiptError,
diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx
index 1e257732cd91..22e6c6807ee8 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx
@@ -24,6 +24,7 @@ const settingsScreens = {
[SCREENS.SETTINGS.ABOUT]: () => require('../../../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType,
[SCREENS.SETTINGS.TROUBLESHOOT]: () => require('../../../../../pages/settings/Troubleshoot/TroubleshootPage').default as React.ComponentType,
[SCREENS.SETTINGS.SAVE_THE_WORLD]: () => require('../../../../../pages/TeachersUnite/SaveTheWorldPage').default as React.ComponentType,
+ [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: () => require('../../../../../pages/settings/Subscription/SubscriptionSettingsPage').default as React.ComponentType,
} satisfies Screens;
function BaseCentralPaneNavigator() {
@@ -35,6 +36,7 @@ function BaseCentralPaneNavigator() {
// Prevent unnecessary scrolling
cardStyle: styles.cardStyleNavigator,
};
+
return (
> =
[SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER],
[SCREENS.SETTINGS.TROUBLESHOOT]: [SCREENS.SETTINGS.CONSOLE],
[SCREENS.SEARCH.CENTRAL_PANE]: [SCREENS.SEARCH.REPORT_RHP],
+ [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [],
};
export default CENTRAL_PANE_TO_RHP_MAPPING;
diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts
index 9e40a3dc0d4c..15b83314e84e 100755
--- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts
@@ -13,6 +13,7 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = {
SCREENS.SETTINGS.WORKSPACES,
SCREENS.SETTINGS.SAVE_THE_WORLD,
SCREENS.SETTINGS.TROUBLESHOOT,
+ SCREENS.SETTINGS.SUBSCRIPTION.ROOT,
],
};
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index a093b778360e..e9b86993ad43 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -68,6 +68,7 @@ const config: LinkingOptions['config'] = {
[SCREENS.SETTINGS.WORKSPACES]: ROUTES.SETTINGS_WORKSPACES,
[SCREENS.SEARCH.CENTRAL_PANE]: ROUTES.SEARCH.route,
[SCREENS.SETTINGS.SAVE_THE_WORLD]: ROUTES.SETTINGS_SAVE_THE_WORLD,
+ [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: ROUTES.SETTINGS_SUBSCRIPTION,
},
},
[SCREENS.NOT_FOUND]: '*',
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index 7112ddca607c..7f597e4d2210 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -71,6 +71,7 @@ type CentralPaneNavigatorParamList = {
sortOrder?: SortOrder;
};
[SCREENS.SETTINGS.SAVE_THE_WORLD]: undefined;
+ [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined;
};
type BackToParams = {
@@ -103,6 +104,7 @@ type SettingsNavigatorParamList = {
backTo: Routes;
};
[SCREENS.SETTINGS.PREFERENCES.ROOT]: undefined;
+ [SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: undefined;
[SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: undefined;
[SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: undefined;
[SCREENS.SETTINGS.PREFERENCES.THEME]: undefined;
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index a8180ab60f70..2be960225a97 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -30,7 +30,7 @@ Onyx.connect({
*/
function getActivePolicies(policies: OnyxCollection): Policy[] {
return Object.values(policies ?? {}).filter(
- (policy): policy is Policy => !!policy && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !!policy.name && !!policy.id,
+ (policy): policy is Policy => policy !== null && policy && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !!policy.name && !!policy.id,
);
}
diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts
index 2c5dda801359..d35d6122aff7 100644
--- a/src/libs/Pusher/pusher.ts
+++ b/src/libs/Pusher/pusher.ts
@@ -170,7 +170,7 @@ function bindEventToChannel(channel: Channel
let data: EventData;
try {
- data = isObject(eventData) ? eventData : JSON.parse(eventData as string);
+ data = isObject(eventData) ? eventData : JSON.parse(eventData);
} catch (err) {
Log.alert('[Pusher] Unable to parse single JSON event data from Pusher', {error: err, eventData});
return;
diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts
index 0c02cf436e37..bda96e01fbe6 100644
--- a/src/libs/ReportActionsUtils.ts
+++ b/src/libs/ReportActionsUtils.ts
@@ -667,7 +667,7 @@ function getLastVisibleMessage(reportID: string, actionsToMerge: OnyxCollection<
/**
* A helper method to filter out report actions keyed by sequenceNumbers.
*/
-function filterOutDeprecatedReportActions(reportActions: OnyxEntry): ReportAction[] {
+function filterOutDeprecatedReportActions(reportActions: ReportActions | null): ReportAction[] {
return Object.entries(reportActions ?? {})
.filter(([key, reportAction]) => !isReportActionDeprecated(reportAction, key))
.map((entry) => entry[1]);
@@ -679,7 +679,7 @@ function filterOutDeprecatedReportActions(reportActions: OnyxEntry | ReportAction[], shouldIncludeInvisibleActions = false): ReportAction[] {
+function getSortedReportActionsForDisplay(reportActions: ReportActions | null | ReportAction[], shouldIncludeInvisibleActions = false): ReportAction[] {
let filteredReportActions: ReportAction[] = [];
if (!reportActions) {
return [];
@@ -703,7 +703,7 @@ function getSortedReportActionsForDisplay(reportActions: OnyxEntry): OnyxEntry {
+function getLastClosedReportAction(reportActions: ReportActions | null): OnyxEntry {
// If closed report action is not present, return early
if (!Object.values(reportActions ?? {}).some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED)) {
return null;
@@ -756,7 +756,7 @@ function getLinkedTransactionID(reportActionOrID: string | OnyxEntry {
return allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]?.[reportActionID] ?? null;
}
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index df2b0b9d703e..7939ef876042 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -507,7 +507,7 @@ Onyx.connect({
},
});
-let allPersonalDetails: OnyxEntry;
+let allPersonalDetails: OnyxCollection;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry;
Onyx.connect({
@@ -1651,7 +1651,7 @@ function getReportRecipientAccountIDs(report: OnyxEntry, currentLoginAcc
/**
* Whether the time row should be shown for a report.
*/
-function canShowReportRecipientLocalTime(personalDetails: OnyxEntry, report: OnyxEntry, accountID: number): boolean {
+function canShowReportRecipientLocalTime(personalDetails: OnyxCollection, report: OnyxEntry, accountID: number): boolean {
const reportRecipientAccountIDs = getReportRecipientAccountIDs(report, accountID);
const hasMultipleParticipants = reportRecipientAccountIDs.length > 1;
const reportRecipient = personalDetails?.[reportRecipientAccountIDs[0]];
@@ -1735,7 +1735,7 @@ function getDefaultGroupAvatar(reportID?: string): IconAsset {
* Returns the appropriate icons for the given chat report using the stored personalDetails.
* The Avatar sources can be URLs or Icon components according to the chat type.
*/
-function getIconsForParticipants(participants: number[], personalDetails: OnyxEntry): Icon[] {
+function getIconsForParticipants(participants: number[], personalDetails: OnyxCollection): Icon[] {
const participantDetails: ParticipantDetails[] = [];
const participantsList = participants || [];
@@ -1920,7 +1920,7 @@ function getParticipants(reportID: string) {
*/
function getIcons(
report: OnyxEntry,
- personalDetails: OnyxEntry,
+ personalDetails: OnyxCollection,
defaultIcon: UserUtils.AvatarSource | null = null,
defaultName = '',
defaultAccountID = -1,
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index 46d37c54f087..79e73f1585d2 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -366,7 +366,7 @@ function getMerchant(transaction: OnyxEntry): string {
return transaction?.modifiedMerchant ? transaction.modifiedMerchant : transaction?.merchant ?? '';
}
-function getDistance(transaction: OnyxEntry): number {
+function getDistance(transaction: Transaction | null): number {
return transaction?.comment?.customUnit?.quantity ?? 0;
}
diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts
index fdb21eedbf84..cddb2c371a60 100644
--- a/src/libs/actions/BankAccounts.ts
+++ b/src/libs/actions/BankAccounts.ts
@@ -1,3 +1,4 @@
+import type {OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
import type {OnfidoDataWithApplicantID} from '@components/Onfido/types';
@@ -69,7 +70,7 @@ function openPlaidView() {
clearPlaid().then(() => ReimbursementAccount.setBankAccountSubStep(CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID));
}
-function setPlaidEvent(eventName: string | null) {
+function setPlaidEvent(eventName: OnyxEntry) {
Onyx.set(ONYXKEYS.PLAID_CURRENT_EVENT, eventName);
}
diff --git a/src/libs/actions/FormActions.ts b/src/libs/actions/FormActions.ts
index 5fe1705d8db3..1fcf9bed6a55 100644
--- a/src/libs/actions/FormActions.ts
+++ b/src/libs/actions/FormActions.ts
@@ -24,7 +24,7 @@ function clearErrorFields(formID: OnyxFormKey) {
}
function setDraftValues(formID: OnyxFormKey, draftValues: NullishDeep>) {
- Onyx.merge(`${formID}Draft`, draftValues ?? null);
+ Onyx.merge(`${formID}Draft`, draftValues);
}
function clearDraftValues(formID: OnyxFormKey) {
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index a08be2ab3d4b..41e684a5c579 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -1369,7 +1369,7 @@ function buildOnyxDataForTrackExpense(
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
- value: quickAction ?? null,
+ value: quickAction,
});
if (iouReport) {
@@ -1606,7 +1606,7 @@ function getDeleteTrackExpenseInformation(
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
- value: transaction ?? null,
+ value: transaction,
});
}
@@ -1614,7 +1614,7 @@ function getDeleteTrackExpenseInformation(
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`,
- value: transactionViolations ?? null,
+ value: transactionViolations,
});
}
@@ -1689,7 +1689,7 @@ function getSendInvoiceInformation(
let chatReport = !isEmptyObject(invoiceChatReport) && invoiceChatReport?.reportID ? invoiceChatReport : null;
if (!chatReport) {
- chatReport = ReportUtils.getInvoiceChatByParticipants(senderWorkspaceID, receiverAccountID) ?? null;
+ chatReport = ReportUtils.getInvoiceChatByParticipants(senderWorkspaceID, receiverAccountID);
}
if (!chatReport) {
@@ -1842,7 +1842,7 @@ function getMoneyRequestInformation(
}
if (!chatReport) {
- chatReport = ReportUtils.getChatByParticipants([payerAccountID, payeeAccountID]) ?? null;
+ chatReport = ReportUtils.getChatByParticipants([payerAccountID, payeeAccountID]);
}
// If we still don't have a report, it likely doens't exist and we need to build an optimistic one
@@ -5354,7 +5354,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
{
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
- value: transaction ?? null,
+ value: transaction,
},
];
@@ -5362,7 +5362,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor
failureData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`,
- value: transactionViolations ?? null,
+ value: transactionViolations,
});
}
@@ -6460,7 +6460,7 @@ function detachReceipt(transactionID: string) {
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
value: {
- ...(transaction ?? null),
+ ...transaction,
errors: ErrorUtils.getMicroSecondOnyxError('iou.error.receiptDeleteFailureError'),
},
},
diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts
index fc651b0599b5..04656f1adfec 100644
--- a/src/libs/actions/OnyxUpdates.ts
+++ b/src/libs/actions/OnyxUpdates.ts
@@ -1,4 +1,4 @@
-import type {OnyxUpdate} from 'react-native-onyx';
+import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {Merge} from 'type-fest';
import Log from '@libs/Log';
@@ -13,7 +13,7 @@ import * as QueuedOnyxUpdates from './QueuedOnyxUpdates';
// This key needs to be separate from ONYXKEYS.ONYX_UPDATES_FROM_SERVER so that it can be updated without triggering the callback when the server IDs are updated. If that
// callback were triggered it would lead to duplicate processing of server updates.
-let lastUpdateIDAppliedToClient: number | null = 0;
+let lastUpdateIDAppliedToClient: OnyxEntry = 0;
Onyx.connect({
key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT,
callback: (val) => (lastUpdateIDAppliedToClient = val),
@@ -151,4 +151,4 @@ function doesClientNeedToBeUpdated(previousUpdateID = 0, clientLastUpdateID = 0)
}
// eslint-disable-next-line import/prefer-default-export
-export {apply, doesClientNeedToBeUpdated, saveUpdateInformation};
+export {saveUpdateInformation, doesClientNeedToBeUpdated, apply};
diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts
index 4ba6ba497944..68814cbd78c0 100644
--- a/src/libs/actions/Policy/Policy.ts
+++ b/src/libs/actions/Policy/Policy.ts
@@ -235,7 +235,7 @@ Onyx.connect({
* Stores in Onyx the policy ID of the last workspace that was accessed by the user
*/
function updateLastAccessedWorkspace(policyID: OnyxEntry) {
- Onyx.set(ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, policyID ?? null);
+ Onyx.set(ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, policyID);
}
/**
@@ -712,6 +712,7 @@ function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueO
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
reimbursementChoice,
+ isLoadingWorkspaceReimbursement: true,
achAccount: {reimburser: reimburserEmail},
errorFields: {reimbursementChoice: null},
pendingFields: {reimbursementChoice: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
@@ -724,6 +725,7 @@ function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueO
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
+ isLoadingWorkspaceReimbursement: false,
errorFields: {reimbursementChoice: null},
pendingFields: {reimbursementChoice: null},
},
@@ -735,6 +737,7 @@ function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueO
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
+ isLoadingWorkspaceReimbursement: false,
reimbursementChoice: policy.reimbursementChoice ?? null,
achAccount: {reimburser: policy.achAccount?.reimburser ?? null},
errorFields: {reimbursementChoice: ErrorUtils.getMicroSecondOnyxError('common.genericErrorMessage')},
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 7a6e2570462a..aa134d47cf77 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -84,6 +84,7 @@ import INPUT_IDS from '@src/types/form/NewRoomForm';
import type {
InvitedEmailsToAccountIDs,
NewGroupChatDraft,
+ PersonalDetails,
PersonalDetailsList,
PolicyReportField,
QuickAction,
@@ -871,8 +872,8 @@ function openReport(
});
// Add optimistic personal details for new participants
- const optimisticPersonalDetails: OnyxEntry = {};
- const settledPersonalDetails: OnyxEntry = {};
+ const optimisticPersonalDetails: OnyxCollection = {};
+ const settledPersonalDetails: OnyxCollection = {};
const redundantParticipants: Record = {};
const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(participantLoginList);
participantLoginList.forEach((login, index) => {
@@ -2568,6 +2569,13 @@ function navigateToMostRecentReport(currentReport: OnyxEntry) {
}
}
+function joinRoom(report: OnyxEntry) {
+ if (!report) {
+ return;
+ }
+ updateNotificationPreference(report.reportID, report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, report.parentReportID, report.parentReportActionID);
+}
+
function leaveGroupChat(reportID: string) {
const report = ReportUtils.getReport(reportID);
if (!report) {
@@ -3814,6 +3822,7 @@ export {
showReportActionNotification,
toggleEmojiReaction,
shouldShowReportActionNotification,
+ joinRoom,
leaveRoom,
inviteToRoom,
inviteToGroupChat,
diff --git a/src/libs/actions/ReportActions.ts b/src/libs/actions/ReportActions.ts
index 4fc72bae1e7d..217dd0b12100 100644
--- a/src/libs/actions/ReportActions.ts
+++ b/src/libs/actions/ReportActions.ts
@@ -71,11 +71,11 @@ Onyx.connect({
});
/**
- *
+ *
ignore: `undefined` means we want to check both parent and children report actions
ignore: `parent` or `child` means we want to ignore checking parent or child report actions because they've been previously checked
*/
-function clearAllRelatedReportActionErrors(reportID: string, reportAction: ReportAction | null | undefined, ignore?: IgnoreDirection, keys?: string[]) {
+function clearAllRelatedReportActionErrors(reportID: string, reportAction: ReportAction | null, ignore?: IgnoreDirection, keys?: string[]) {
const errorKeys = keys ?? Object.keys(reportAction?.errors ?? {});
if (!reportAction || errorKeys.length === 0) {
return;
diff --git a/src/libs/migrations/RenameReceiptFilename.ts b/src/libs/migrations/RenameReceiptFilename.ts
index 100b279c950f..b867024fc74e 100644
--- a/src/libs/migrations/RenameReceiptFilename.ts
+++ b/src/libs/migrations/RenameReceiptFilename.ts
@@ -1,5 +1,5 @@
import Onyx from 'react-native-onyx';
-import type {NullishDeep, OnyxCollection, OnyxEntry} from 'react-native-onyx';
+import type {NullishDeep, OnyxCollection} from 'react-native-onyx';
import Log from '@libs/Log';
import ONYXKEYS from '@src/ONYXKEYS';
import type Transaction from '@src/types/onyx/Transaction';
@@ -24,7 +24,7 @@ export default function () {
return resolve();
}
- const transactionsWithReceipt: Array> = Object.values(transactions).filter((transaction) => transaction?.receiptFilename);
+ const transactionsWithReceipt: Array = Object.values(transactions).filter((transaction) => transaction?.receiptFilename);
if (!transactionsWithReceipt?.length) {
Log.info('[Migrate Onyx] Skipped migration RenameReceiptFilename because there were no transactions with the receiptFilename property');
return resolve();
diff --git a/src/libs/migrations/TransactionBackupsToCollection.ts b/src/libs/migrations/TransactionBackupsToCollection.ts
index 8b963d7fa0c2..a7167492007a 100644
--- a/src/libs/migrations/TransactionBackupsToCollection.ts
+++ b/src/libs/migrations/TransactionBackupsToCollection.ts
@@ -1,4 +1,4 @@
-import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import Log from '@libs/Log';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -30,7 +30,7 @@ export default function (): Promise {
// Find all the transaction backups available
Object.keys(transactions).forEach((transactionOnyxKey: string) => {
- const transaction: OnyxEntry = transactions[transactionOnyxKey];
+ const transaction: Transaction | null = transactions[transactionOnyxKey];
// Determine whether or not the transaction is a backup
if (transactionOnyxKey.endsWith('-backup') && transaction) {
diff --git a/src/pages/GroupChatNameEditPage.tsx b/src/pages/GroupChatNameEditPage.tsx
index cb604a1e1722..87218fbb89cd 100644
--- a/src/pages/GroupChatNameEditPage.tsx
+++ b/src/pages/GroupChatNameEditPage.tsx
@@ -1,6 +1,5 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useMemo} from 'react';
-import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
@@ -25,7 +24,7 @@ import type NewGroupChatDraft from '@src/types/onyx/NewGroupChatDraft';
import type {Errors} from '@src/types/onyx/OnyxCommon';
type GroupChatNameEditPageOnyxProps = {
- groupChatDraft: OnyxEntry;
+ groupChatDraft: NewGroupChatDraft | null;
};
type GroupChatNameEditPageProps = StackScreenProps & GroupChatNameEditPageOnyxProps;
diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx
index d62333be4888..b2008ecc2cca 100755
--- a/src/pages/NewChatPage.tsx
+++ b/src/pages/NewChatPage.tsx
@@ -1,7 +1,6 @@
import isEmpty from 'lodash/isEmpty';
import reject from 'lodash/reject';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
-import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import KeyboardAvoidingView from '@components/KeyboardAvoidingView';
@@ -32,7 +31,6 @@ import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import type {Beta} from '@src/types/onyx';
import type {SelectedParticipant} from '@src/types/onyx/NewGroupChatDraft';
type NewChatPageProps = {
@@ -56,7 +54,7 @@ function useOptions({isGroupChat}: NewChatPageProps) {
const filteredOptions = OptionsListUtils.getFilteredOptions(
listOptions.reports ?? [],
listOptions.personalDetails ?? [],
- (betas ?? []) as OnyxEntry,
+ betas ?? [],
debouncedSearchTerm,
selectedOptions,
isGroupChat ? excludedGroupEmails : [],
diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx
index c3d8239bdf99..19cbe0bbea42 100644
--- a/src/pages/PrivateNotes/PrivateNotesEditPage.tsx
+++ b/src/pages/PrivateNotes/PrivateNotesEditPage.tsx
@@ -5,7 +5,7 @@ import Str from 'expensify-common/lib/str';
import lodashDebounce from 'lodash/debounce';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import {Keyboard} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
@@ -31,12 +31,12 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/PrivateNotesForm';
-import type {PersonalDetailsList, Report} from '@src/types/onyx';
+import type {PersonalDetails, Report} from '@src/types/onyx';
import type {Note} from '@src/types/onyx/Report';
type PrivateNotesEditPageOnyxProps = {
/** All of the personal details for everyone */
- personalDetailsList: OnyxEntry;
+ personalDetailsList: OnyxCollection;
};
type PrivateNotesEditPageProps = WithReportAndPrivateNotesOrNotFoundProps &
diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.tsx b/src/pages/PrivateNotes/PrivateNotesListPage.tsx
index cf3ce4c36b53..1893f81da2fe 100644
--- a/src/pages/PrivateNotes/PrivateNotesListPage.tsx
+++ b/src/pages/PrivateNotes/PrivateNotesListPage.tsx
@@ -1,6 +1,6 @@
import React, {useMemo} from 'react';
-import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
+import type {OnyxCollection} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
@@ -15,11 +15,11 @@ import withReportAndPrivateNotesOrNotFound from '@pages/home/report/withReportAn
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import type {PersonalDetailsList, Report} from '@src/types/onyx';
+import type {PersonalDetails, Report} from '@src/types/onyx';
type PrivateNotesListPageOnyxProps = {
/** All of the personal details for everyone */
- personalDetailsList: OnyxEntry;
+ personalDetailsList: OnyxCollection;
};
type PrivateNotesListPageProps = WithReportAndPrivateNotesOrNotFoundProps &
diff --git a/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx b/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx
index 97a8caaef692..a83e18d119c9 100644
--- a/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx
+++ b/src/pages/ReimbursementAccount/ConnectBankAccount/components/BankAccountValidationForm.tsx
@@ -1,7 +1,6 @@
import Str from 'expensify-common/lib/str';
import React, {useCallback} from 'react';
import {View} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
@@ -27,7 +26,7 @@ type BankAccountValidationFormProps = {
requiresTwoFactorAuth: boolean;
/** The policy which the user has access to and which the report is tied to */
- policy: OnyxEntry;
+ policy: Policy | null;
};
type AmountValues = {
diff --git a/src/pages/ReimbursementAccount/utils/getValuesForBeneficialOwner.ts b/src/pages/ReimbursementAccount/utils/getValuesForBeneficialOwner.ts
index b564b8f325ee..3733a6727c54 100644
--- a/src/pages/ReimbursementAccount/utils/getValuesForBeneficialOwner.ts
+++ b/src/pages/ReimbursementAccount/utils/getValuesForBeneficialOwner.ts
@@ -1,4 +1,3 @@
-import type {OnyxEntry} from 'react-native-onyx';
import CONST from '@src/CONST';
import type {ReimbursementAccountForm} from '@src/types/form';
@@ -13,7 +12,7 @@ type BeneficialOwnerValues = {
zipCode: string;
};
-function getValuesForBeneficialOwner(beneficialOwnerBeingModifiedID: string, reimbursementAccountDraft: OnyxEntry): BeneficialOwnerValues {
+function getValuesForBeneficialOwner(beneficialOwnerBeingModifiedID: string, reimbursementAccountDraft: ReimbursementAccountForm | null): BeneficialOwnerValues {
if (!reimbursementAccountDraft) {
return {
firstName: '',
diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx
index adb1daa1e82f..dc51955e212c 100644
--- a/src/pages/ReportDetailsPage.tsx
+++ b/src/pages/ReportDetailsPage.tsx
@@ -2,12 +2,11 @@ import {useRoute} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useEffect, useMemo} from 'react';
import {View} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
-import ChatDetailsQuickActionsBar from '@components/ChatDetailsQuickActionsBar';
import DisplayNames from '@components/DisplayNames';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
@@ -17,6 +16,7 @@ import MultipleAvatars from '@components/MultipleAvatars';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
+import PromotedActionsBar, {PromotedActions} from '@components/PromotedActionsBar';
import RoomHeaderAvatars from '@components/RoomHeaderAvatars';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
@@ -53,7 +53,7 @@ type ReportDetailsPageMenuItem = {
type ReportDetailsPageOnyxProps = {
/** Personal details of all the users */
- personalDetails: OnyxEntry;
+ personalDetails: OnyxCollection;
/** Session info for the currently logged in user. */
session: OnyxEntry;
@@ -333,7 +333,13 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
/>
)}
- {isGroupChat && }
+ {isGroupChat && (
+
+ )}
{menuItems.map((item) => {
const brickRoadIndicator =
ReportUtils.hasReportNameError(report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined;
diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx
index 9f600179e58a..b16fb86f9b21 100644
--- a/src/pages/home/HeaderView.tsx
+++ b/src/pages/home/HeaderView.tsx
@@ -155,9 +155,7 @@ function HeaderView({
}
}
- const join = Session.checkIfActionIsAllowed(() =>
- Report.updateNotificationPreference(reportID, report.notificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, report.parentReportID, report.parentReportActionID),
- );
+ const join = Session.checkIfActionIsAllowed(() => Report.joinRoom(report));
const canJoin = ReportUtils.canJoinChat(report, parentReportAction, policy);
if (canJoin) {
diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx
index b29506ea8e8f..b0287efb8990 100644
--- a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx
+++ b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx
@@ -15,7 +15,8 @@ type ProfileAvatarWithIndicatorProps = {
function ProfileAvatarWithIndicator({isSelected = false}: ProfileAvatarWithIndicatorProps) {
const styles = useThemeStyles();
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
- const [isLoading = true] = useOnyx(ONYXKEYS.IS_LOADING_APP);
+ const [isLoadingOnyxValue] = useOnyx(ONYXKEYS.IS_LOADING_APP);
+ const isLoading = isLoadingOnyxValue ?? true;
return (
diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx
index 91a8b94537ab..fcb018348b72 100644
--- a/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx
+++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.tsx
@@ -70,31 +70,31 @@ function AddressPage({privatePersonalDetails, route, isLoadingApp = true}: Addre
}, [address]);
const handleAddressChange = useCallback((value: unknown, key: unknown) => {
- const addressPart = value as string;
- const addressPartKey = key as keyof Address;
+ const countryValue = value as Country | '';
+ const addressKey = key as keyof Address;
- if (addressPartKey !== 'country' && addressPartKey !== 'state' && addressPartKey !== 'city' && addressPartKey !== 'zipPostCode') {
+ if (addressKey !== 'country' && addressKey !== 'state' && addressKey !== 'city' && addressKey !== 'zipPostCode') {
return;
}
- if (addressPartKey === 'country') {
- setCurrentCountry(addressPart as Country | '');
+ if (addressKey === 'country') {
+ setCurrentCountry(countryValue);
setState('');
setCity('');
setZipcode('');
return;
}
- if (addressPartKey === 'state') {
- setState(addressPart);
+ if (addressKey === 'state') {
+ setState(countryValue);
setCity('');
setZipcode('');
return;
}
- if (addressPartKey === 'city') {
- setCity(addressPart);
+ if (addressKey === 'city') {
+ setCity(countryValue);
setZipcode('');
return;
}
- setZipcode(addressPart);
+ setZipcode(countryValue);
}, []);
useEffect(() => {
diff --git a/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx b/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx
new file mode 100644
index 000000000000..83ede1532efc
--- /dev/null
+++ b/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
+import * as Illustrations from '@components/Icon/Illustrations';
+import ScreenWrapper from '@components/ScreenWrapper';
+import useLocalize from '@hooks/useLocalize';
+import useWindowDimensions from '@hooks/useWindowDimensions';
+import Navigation from '@libs/Navigation/Navigation';
+
+function SubscriptionSettingsPage() {
+ const {isSmallScreenWidth} = useWindowDimensions();
+ const {translate} = useLocalize();
+
+ return (
+
+ Navigation.goBack()}
+ shouldShowBackButton={isSmallScreenWidth}
+ icon={Illustrations.CreditCardsNew}
+ />
+
+ );
+}
+
+SubscriptionSettingsPage.displayName = 'SubscriptionSettingsPage';
+
+export default SubscriptionSettingsPage;
diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
index 016a1eb9e8fa..5b17a4e26051 100644
--- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
+++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx
@@ -1,7 +1,7 @@
import {useFocusEffect} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useMemo, useState} from 'react';
-import {View} from 'react-native';
+import {ActivityIndicator, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import ConfirmModal from '@components/ConfirmModal';
@@ -12,6 +12,7 @@ import Section from '@components/Section';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as ErrorUtils from '@libs/ErrorUtils';
@@ -44,6 +45,7 @@ type WorkspaceWorkflowsPageProps = WithPolicyProps & WorkspaceWorkflowsPageOnyxP
function WorkspaceWorkflowsPage({policy, betas, route}: WorkspaceWorkflowsPageProps) {
const {translate, preferredLocale} = useLocalize();
+ const theme = useTheme();
const styles = useThemeStyles();
const {isSmallScreenWidth} = useWindowDimensions();
@@ -83,8 +85,9 @@ function WorkspaceWorkflowsPage({policy, betas, route}: WorkspaceWorkflowsPagePr
);
const optionItems: ToggleSettingOptionRowProps[] = useMemo(() => {
- const {accountNumber, addressName, bankName} = policy?.achAccount ?? {};
- const hasVBA = !!policy?.achAccount;
+ const {accountNumber, addressName, bankName, bankAccountID} = policy?.achAccount ?? {};
+ const shouldShowBankAccount = !!bankAccountID && policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES;
+
let bankDisplayName = bankName ?? addressName;
if (accountNumber && bankDisplayName !== accountNumber) {
bankDisplayName += ` ${accountNumber.slice(-5)}`;
@@ -168,60 +171,65 @@ function WorkspaceWorkflowsPage({policy, betas, route}: WorkspaceWorkflowsPagePr
let newReimbursementChoice;
if (!isEnabled) {
newReimbursementChoice = CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO;
- } else if (hasVBA && !Policy.isCurrencySupportedForDirectReimbursement(policy?.outputCurrency ?? '')) {
+ } else if (!!policy?.achAccount && !Policy.isCurrencySupportedForDirectReimbursement(policy?.outputCurrency ?? '')) {
newReimbursementChoice = CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL;
} else {
- newReimbursementChoice = hasVBA ? CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES : CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL;
+ newReimbursementChoice = CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES;
}
const newReimburserEmail = policy?.achAccount?.reimburser ?? policy?.owner;
Policy.setWorkspaceReimbursement(policy?.id ?? '', newReimbursementChoice, newReimburserEmail ?? '');
},
- subMenuItems: (
- <>
-