diff --git a/.github/workflows/deployExpensifyHelp.yml b/.github/workflows/deployExpensifyHelp.yml
index 831ec0c0b95e..d4577e112d59 100644
--- a/.github/workflows/deployExpensifyHelp.yml
+++ b/.github/workflows/deployExpensifyHelp.yml
@@ -46,6 +46,7 @@ jobs:
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca
id: deploy
+ if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork)
with:
apiToken: ${{ secrets.CLOUDFLARE_PAGES_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml
index 4d6597334447..91e244a0ed7c 100644
--- a/.github/workflows/platformDeploy.yml
+++ b/.github/workflows/platformDeploy.yml
@@ -157,6 +157,8 @@ jobs:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_PRODUCTION }}
+
- name: Build staging desktop app
if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
@@ -168,6 +170,7 @@ jobs:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_STAGING }}
iOS:
name: Build and deploy iOS
diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml
index 3f02430f3c1f..fc9e75e626d3 100644
--- a/.github/workflows/testBuild.yml
+++ b/.github/workflows/testBuild.yml
@@ -265,6 +265,7 @@ jobs:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_STAGING }}
web:
name: Build and deploy Web
diff --git a/android/app/build.gradle b/android/app/build.gradle
index d687ccfb0cc3..74830269ad45 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001045500
- versionName "1.4.55-0"
+ versionCode 1001045501
+ versionName "1.4.55-1"
}
flavorDimensions "default"
diff --git a/desktop/main.ts b/desktop/main.ts
index cbc12d9d2608..6e14d661b345 100644
--- a/desktop/main.ts
+++ b/desktop/main.ts
@@ -21,7 +21,7 @@ const {DESKTOP_SHORTCUT_ACCELERATOR, LOCALES} = CONST;
// Setup google api key in process environment, we are setting it this way intentionally. It is required by the
// geolocation api (window.navigator.geolocation.getCurrentPosition) to work on desktop.
// Source: https://github.com/electron/electron/blob/98cd16d336f512406eee3565be1cead86514db7b/docs/api/environment-variables.md#google_api_key
-process.env.GOOGLE_API_KEY = CONFIG.GOOGLE_GEOLOCATION_API_KEY;
+process.env.GOOGLE_API_KEY = CONFIG.GCP_GEOLOCATION_API_KEY;
app.setName('New Expensify');
diff --git a/docs/articles/new-expensify/chat/Introducing-Expensify-Chat.md b/docs/articles/new-expensify/chat/Introducing-Expensify-Chat.md
index c7ae49e02292..096a3d1527be 100644
--- a/docs/articles/new-expensify/chat/Introducing-Expensify-Chat.md
+++ b/docs/articles/new-expensify/chat/Introducing-Expensify-Chat.md
@@ -12,7 +12,7 @@ For a quick snapshot of how Expensify Chat works, and New Expensify in general,
# What’s Expensify Chat?
-Expensify Chat is an instant messaging and payment platform. You can manage all your payments, wether for business or personal, and discuss the transactions themselves.
+Expensify Chat is an instant messaging and payment platform. You can manage all your payments, whether for business or personal, and discuss the transactions themselves.
With Expensify Chat, you can start a conversation about that missing receipt your employee forgot to submit or chat about splitting that electric bill with your roommates. Expensify makes sending and receiving money as easy as sending and receiving messages. Chat with anyone directly, in groups, or in rooms.
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 1ea2673b92ee..22de50aee5a3 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.55.0
+ 1.4.55.1
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index cc6c5cf4c86a..61c240540779 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 1.4.55.0
+ 1.4.55.1
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 7c90acab4958..a30258647997 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
1.4.55
CFBundleVersion
- 1.4.55.0
+ 1.4.55.1
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index dd2084b238fb..310003ee8adc 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -39,7 +39,7 @@ PODS:
- React-Core
- Expo (50.0.4):
- ExpoModulesCore
- - ExpoImage (1.10.1):
+ - ExpoImage (1.11.0):
- ExpoModulesCore
- SDWebImage (~> 5.17.0)
- SDWebImageAVIFCoder (~> 0.10.1)
@@ -1790,7 +1790,7 @@ SPEC CHECKSUMS:
EXAV: 09a4d87fa6b113fbb0ada3aade6799f78271cb44
EXImageLoader: 55080616b2fe9da19ef8c7f706afd9814e279b6b
Expo: 1e3bcf9dd99de57a636127057f6b488f0609681a
- ExpoImage: 1cdaa65a6a70bb01067e21ad1347ff2d973885f5
+ ExpoImage: 390c524542b258f8173f475c1cc71f016444a7be
ExpoImageManipulator: c1d7cb865eacd620a35659f3da34c70531f10b59
ExpoModulesCore: 96d1751929ad10622773bb729ab28a8423f0dd0c
FBLazyVector: fbc4957d9aa695250b55d879c1d86f79d7e69ab4
@@ -1921,7 +1921,7 @@ SPEC CHECKSUMS:
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2
VisionCamera: 0a6794d1974aed5d653d0d0cb900493e2583e35a
- Yoga: 13c8ef87792450193e117976337b8527b49e8c03
+ Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047
PODFILE CHECKSUM: a431c146e1501391834a2f299a74093bac53b530
diff --git a/package-lock.json b/package-lock.json
index 61d6a27821cd..0d0b11fff3f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.55-0",
+ "version": "1.4.55-1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.55-0",
+ "version": "1.4.55-1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -54,7 +54,7 @@
"expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#615f4a8662cd1abea9fdeee4d04847197c5e36ae",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
- "expo-image": "1.10.1",
+ "expo-image": "1.11.0",
"expo-image-manipulator": "11.8.0",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
@@ -27514,8 +27514,9 @@
}
},
"node_modules/expo-image": {
- "version": "1.10.1",
- "license": "MIT",
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-1.11.0.tgz",
+ "integrity": "sha512-CfkSGWIGidxxqzErke16bCS9aefS04qvgjk+T9Nc34LAb3ysBAqCv5hoCnvylHOvi/7jTCC/ouLm5oLLqkDSRQ==",
"dependencies": {
"@react-native/assets-registry": "~0.73.1"
},
diff --git a/package.json b/package.json
index 92a6b9cde5e1..5001e6f0dd1a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.55-0",
+ "version": "1.4.55-1",
"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.",
@@ -105,7 +105,7 @@
"expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#615f4a8662cd1abea9fdeee4d04847197c5e36ae",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
- "expo-image": "1.10.1",
+ "expo-image": "1.11.0",
"expo-image-manipulator": "11.8.0",
"htmlparser2": "^7.2.0",
"idb-keyval": "^6.2.1",
diff --git a/patches/expo-image+1.10.1+001+applyFill.patch b/patches/expo-image+1.10.1+001+applyFill.patch
deleted file mode 100644
index 5f168040d04d..000000000000
--- a/patches/expo-image+1.10.1+001+applyFill.patch
+++ /dev/null
@@ -1,112 +0,0 @@
-diff --git a/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt b/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt
-index 619daf2..b58a0df 100644
---- a/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt
-+++ b/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt
-@@ -1,5 +1,9 @@
- package com.caverock.androidsvg
-
-+import com.caverock.androidsvg.SVG.SPECIFIED_COLOR
-+import com.caverock.androidsvg.SVG.SPECIFIED_FILL
-+import com.caverock.androidsvg.SVG.SvgElementBase
-+
- internal fun replaceColor(paint: SVG.SvgPaint?, newColor: Int) {
- if (paint is SVG.Colour && paint !== SVG.Colour.TRANSPARENT) {
- paint.colour = newColor
-@@ -19,15 +23,83 @@ internal fun replaceStyles(style: SVG.Style?, newColor: Int) {
- replaceColor(style.viewportFill, newColor)
- }
-
--internal fun applyTintColor(element: SVG.SvgObject, newColor: Int) {
-- if (element is SVG.SvgElementBase) {
-+internal fun hasStyle(element: SvgElementBase): Boolean {
-+ if (element.style == null && element.baseStyle == null) {
-+ return false
-+ }
-+
-+ val style = element.style
-+ val hasColorInStyle = style != null &&
-+ (
-+ style.color != null || style.fill != null || style.stroke != null ||
-+ style.stroke != null || style.stopColor != null || style.solidColor != null
-+ )
-+
-+ if (hasColorInStyle) {
-+ return true
-+ }
-+
-+ val baseStyle = element.baseStyle ?: return false
-+ return baseStyle.color != null || baseStyle.fill != null || baseStyle.stroke != null ||
-+ baseStyle.viewportFill != null || baseStyle.stopColor != null || baseStyle.solidColor != null
-+}
-+
-+internal fun defineStyles(element: SvgElementBase, newColor: Int, hasStyle: Boolean) {
-+ if (hasStyle) {
-+ return
-+ }
-+
-+ val style = if (element.style != null) {
-+ element.style
-+ } else {
-+ SVG.Style().also {
-+ element.style = it
-+ }
-+ }
-+
-+ val color = SVG.Colour(newColor)
-+ when (element) {
-+ is SVG.Path,
-+ is SVG.Circle,
-+ is SVG.Ellipse,
-+ is SVG.Rect,
-+ is SVG.SolidColor,
-+ is SVG.Line,
-+ is SVG.Polygon,
-+ is SVG.PolyLine -> {
-+ style.apply {
-+ fill = color
-+
-+ specifiedFlags = SPECIFIED_FILL
-+ }
-+ }
-+
-+ is SVG.TextPath -> {
-+ style.apply {
-+ this.color = color
-+
-+ specifiedFlags = SPECIFIED_COLOR
-+ }
-+ }
-+ }
-+}
-+
-+internal fun applyTintColor(element: SVG.SvgObject, newColor: Int, parentDefinesStyle: Boolean) {
-+ val definesStyle = if (element is SvgElementBase) {
-+ val hasStyle = parentDefinesStyle || hasStyle(element)
-+
- replaceStyles(element.baseStyle, newColor)
- replaceStyles(element.style, newColor)
-+ defineStyles(element, newColor, hasStyle)
-+
-+ hasStyle
-+ } else {
-+ parentDefinesStyle
- }
-
- if (element is SVG.SvgContainer) {
- for (child in element.children) {
-- applyTintColor(child, newColor)
-+ applyTintColor(child, newColor, definesStyle)
- }
- }
- }
-@@ -36,8 +108,9 @@ fun applyTintColor(svg: SVG, newColor: Int) {
- val root = svg.rootElement
-
- replaceStyles(root.style, newColor)
-+ val hasStyle = hasStyle(root)
-
- for (child in root.children) {
-- applyTintColor(child, newColor)
-+ applyTintColor(child, newColor, hasStyle)
- }
- }
diff --git a/patches/expo-image+1.10.1+002+TintFix.patch b/patches/expo-image+1.10.1+002+TintFix.patch
deleted file mode 100644
index 92b56c039b43..000000000000
--- a/patches/expo-image+1.10.1+002+TintFix.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-diff --git a/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt b/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt
-index b58a0df..6b8da3c 100644
---- a/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt
-+++ b/node_modules/expo-image/android/src/main/java/com/caverock/androidsvg/SVGStyler.kt
-@@ -107,6 +107,7 @@ internal fun applyTintColor(element: SVG.SvgObject, newColor: Int, parentDefines
- fun applyTintColor(svg: SVG, newColor: Int) {
- val root = svg.rootElement
-
-+ replaceStyles(root.baseStyle, newColor)
- replaceStyles(root.style, newColor)
- val hasStyle = hasStyle(root)
-
-diff --git a/node_modules/expo-image/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt b/node_modules/expo-image/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt
-index 602b570..8becf72 100644
---- a/node_modules/expo-image/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt
-+++ b/node_modules/expo-image/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt
-@@ -31,6 +31,7 @@ import expo.modules.image.records.ImageLoadEvent
- import expo.modules.image.records.ImageProgressEvent
- import expo.modules.image.records.ImageTransition
- import expo.modules.image.records.SourceMap
-+import expo.modules.image.svg.SVGPictureDrawable
- import expo.modules.kotlin.AppContext
- import expo.modules.kotlin.tracing.beginAsyncTraceBlock
- import expo.modules.kotlin.tracing.trace
-@@ -127,7 +128,12 @@ class ExpoImageViewWrapper(context: Context, appContext: AppContext) : ExpoView(
- internal var tintColor: Int? = null
- set(value) {
- field = value
-- activeView.setTintColor(value)
-+ // To apply the tint color to the SVG, we need to recreate the drawable.
-+ if (activeView.drawable is SVGPictureDrawable) {
-+ shouldRerender = true
-+ } else {
-+ activeView.setTintColor(value)
-+ }
- }
-
- internal var isFocusableProp: Boolean = false
diff --git a/src/CONFIG.ts b/src/CONFIG.ts
index 37da65f0c305..76ea18d37d5f 100644
--- a/src/CONFIG.ts
+++ b/src/CONFIG.ts
@@ -21,7 +21,7 @@ const secureExpensifyUrl = Url.addTrailingForwardSlash(get(Config, 'SECURE_EXPEN
const useNgrok = get(Config, 'USE_NGROK', 'false') === 'true';
const useWebProxy = get(Config, 'USE_WEB_PROXY', 'true') === 'true';
const expensifyComWithProxy = getPlatform() === 'web' && useWebProxy ? '/' : expensifyURL;
-const googleGeolocationAPIKey = get(Config, 'GOOGLE_GEOLOCATION_API_KEY', 'AIzaSyBqg6bMvQU7cPWDKhhzpYqJrTEnSorpiLI');
+const googleGeolocationAPIKey = get(Config, 'GCP_GEOLOCATION_API_KEY', '');
// Throw errors on dev if config variables are not set correctly
if (ENVIRONMENT === CONST.ENVIRONMENT.DEV) {
@@ -94,5 +94,5 @@ export default {
WEB_CLIENT_ID: '921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com',
IOS_CLIENT_ID: '921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com',
},
- GOOGLE_GEOLOCATION_API_KEY: googleGeolocationAPIKey,
+ GCP_GEOLOCATION_API_KEY: googleGeolocationAPIKey,
} as const;
diff --git a/src/CONST.ts b/src/CONST.ts
index aece53666327..3c53f083abac 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -359,6 +359,7 @@ const CONST = {
NOT_INSTALLED: 'not-installed',
},
TAX_RATES: {
+ CUSTOM_NAME_MAX_LENGTH: 8,
NAME_MAX_LENGTH: 50,
},
PLATFORM: {
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 9ce32835c8d7..1f802c5036e3 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -332,9 +332,9 @@ const ROUTES = {
getUrlWithBackToParam(`create/${iouType}/taxAmount/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_CATEGORY: {
- route: ':action/:iouType/category/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/category/${transactionID}/${reportID}`, backTo),
+ route: ':action/:iouType/category/:transactionID/:reportID/:reportActionID?',
+ getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
+ getUrlWithBackToParam(`${action}/${iouType}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_CURRENCY: {
route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?',
@@ -347,9 +347,9 @@ const ROUTES = {
getUrlWithBackToParam(`${action}/${iouType}/date/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_DESCRIPTION: {
- route: ':action/:iouType/description/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/description/${transactionID}/${reportID}`, backTo),
+ route: ':action/:iouType/description/:transactionID/:reportID/:reportActionID?',
+ getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
+ getUrlWithBackToParam(`${action}/${iouType}/description/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_DISTANCE: {
route: 'create/:iouType/distance/:transactionID/:reportID',
diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx
index 8bcda759d26c..e39e940ebf5c 100644
--- a/src/components/AvatarWithImagePicker.tsx
+++ b/src/components/AvatarWithImagePicker.tsx
@@ -287,11 +287,12 @@ function AvatarWithImagePicker({
return (
-
+
diff --git a/src/components/Form/FormWrapper.tsx b/src/components/Form/FormWrapper.tsx
index 5c2488ca144a..902a96b1bcaf 100644
--- a/src/components/Form/FormWrapper.tsx
+++ b/src/components/Form/FormWrapper.tsx
@@ -110,7 +110,7 @@ function FormWrapper({
buttonText={submitButtonText}
isAlertVisible={((!isEmptyObject(errors) || !isEmptyObject(formState?.errorFields)) && !shouldHideFixErrorsAlert) || !!errorMessage}
isLoading={!!formState?.isLoading}
- message={typeof errorMessage === 'string' && isEmptyObject(formState?.errorFields) ? errorMessage : undefined}
+ message={isEmptyObject(formState?.errorFields) ? errorMessage : undefined}
onSubmit={onSubmit}
footerContent={footerContent}
onFixTheErrorsLinkPressed={onFixTheErrorsLinkPressed}
diff --git a/src/components/HoldMenuSectionList.tsx b/src/components/HoldMenuSectionList.tsx
index aa5dd75ce159..4ffdfa1bd60e 100644
--- a/src/components/HoldMenuSectionList.tsx
+++ b/src/components/HoldMenuSectionList.tsx
@@ -59,12 +59,7 @@ function HoldMenuSectionList() {
/>
{translate(section.titleTranslationKey)}
-
- {translate(section.descriptionTranslationKey)}
-
+ {translate(section.descriptionTranslationKey)}
))}
diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx
index 923337ba9ada..5065d1cc7c13 100644
--- a/src/components/LHNOptionsList/OptionRowLHN.tsx
+++ b/src/components/LHNOptionsList/OptionRowLHN.tsx
@@ -54,14 +54,6 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
const hasBrickError = optionItem.brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
const shouldShowGreenDotIndicator = !hasBrickError && ReportUtils.requiresAttentionFromCurrentUser(optionItem, optionItem.parentReportAction);
-
- const isHidden = optionItem.notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN;
-
- const shouldOverrideHidden = hasBrickError || isFocused || optionItem.isPinned;
- if (isHidden && !shouldOverrideHidden) {
- return null;
- }
-
const isInFocusMode = viewMode === CONST.OPTION_MODE.COMPACT;
const textStyle = isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText;
const textUnreadStyle = optionItem?.isUnread && optionItem.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE ? [textStyle, styles.sidebarLinkTextBold] : [textStyle];
diff --git a/src/components/Lightbox/index.tsx b/src/components/Lightbox/index.tsx
index 56852a8e2ea1..86a52c2baf6c 100644
--- a/src/components/Lightbox/index.tsx
+++ b/src/components/Lightbox/index.tsx
@@ -112,7 +112,7 @@ function Lightbox({isAuthTokenRequired = false, uri, onScaleChanged: onScaleChan
return;
}
- setContentSize({width: width * PixelRatio.get(), height: height * PixelRatio.get()});
+ setContentSize({width, height});
},
[contentSize, setContentSize],
);
diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx
index 2e8f80175b56..a4da7e551515 100755
--- a/src/components/MoneyRequestConfirmationList.tsx
+++ b/src/components/MoneyRequestConfirmationList.tsx
@@ -665,7 +665,14 @@ function MoneyRequestConfirmationList({
description={translate('common.description')}
onPress={() => {
Navigation.navigate(
- ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()),
+ ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(
+ CONST.IOU.ACTION.EDIT,
+ iouType,
+ transaction?.transactionID ?? '',
+ reportID,
+ Navigation.getActiveRouteWithoutParams(),
+ reportActionID,
+ ),
);
}}
style={styles.moneyRequestMenuItem}
@@ -757,6 +764,7 @@ function MoneyRequestConfirmationList({
transaction?.transactionID ?? '',
reportID,
Navigation.getActiveRouteWithoutParams(),
+ reportActionID,
),
);
}}
diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx
index dad7117bef67..61a13d271e7d 100644
--- a/src/components/TaxPicker.tsx
+++ b/src/components/TaxPicker.tsx
@@ -53,10 +53,10 @@ function TaxPicker({selectedTaxRate = '', taxRates, insets, onSubmit}: TaxPicker
];
}, [selectedTaxRate, getTaxName]);
- const sections = useMemo(() => {
- const taxRatesOptions = OptionsListUtils.getTaxRatesSection(taxRates, selectedOptions as OptionsListUtils.Category[], searchValue, selectedTaxRate);
- return taxRatesOptions;
- }, [taxRates, searchValue, selectedOptions, selectedTaxRate]);
+ const sections = useMemo(
+ () => OptionsListUtils.getTaxRatesSection(taxRates, selectedOptions as OptionsListUtils.Category[], searchValue, selectedTaxRate),
+ [taxRates, searchValue, selectedOptions, selectedTaxRate],
+ );
const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList(sections[0].data.length > 0, searchValue);
diff --git a/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.js b/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx
similarity index 68%
rename from src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.js
rename to src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx
index c6eb1a179726..f9dd09db59f4 100644
--- a/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.js
+++ b/src/components/VideoPlayer/VideoPlayerControls/ProgressBar/index.tsx
@@ -1,25 +1,27 @@
-import PropTypes from 'prop-types';
import React, {useEffect, useState} from 'react';
+import type {LayoutChangeEvent, ViewStyle} from 'react-native';
+import type {GestureStateChangeEvent, GestureUpdateEvent, PanGestureChangeEventPayload, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import Animated, {runOnJS, useAnimatedStyle, useSharedValue} from 'react-native-reanimated';
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
import useThemeStyles from '@hooks/useThemeStyles';
-const propTypes = {
- duration: PropTypes.number.isRequired,
+type ProgressBarProps = {
+ /** Total duration of a video. */
+ duration: number;
- position: PropTypes.number.isRequired,
+ /** Position of progress pointer on the bar. */
+ position: number;
- seekPosition: PropTypes.func.isRequired,
+ /** Function to seek to a specific position in the video. */
+ seekPosition: (newPosition: number) => void;
};
-const defaultProps = {};
-
-function getProgress(currentPosition, maxPosition) {
+function getProgress(currentPosition: number, maxPosition: number): number {
return Math.min(Math.max((currentPosition / maxPosition) * 100, 0), 100);
}
-function ProgressBar({duration, position, seekPosition}) {
+function ProgressBar({duration, position, seekPosition}: ProgressBarProps) {
const styles = useThemeStyles();
const {pauseVideo, playVideo, checkVideoPlaying} = usePlaybackContext();
const [sliderWidth, setSliderWidth] = useState(1);
@@ -27,18 +29,18 @@ function ProgressBar({duration, position, seekPosition}) {
const progressWidth = useSharedValue(0);
const wasVideoPlayingOnCheck = useSharedValue(false);
- const onCheckVideoPlaying = (isPlaying) => {
+ const onCheckVideoPlaying = (isPlaying: boolean) => {
wasVideoPlayingOnCheck.value = isPlaying;
};
- const progressBarInteraction = (event) => {
+ const progressBarInteraction = (event: GestureUpdateEvent | GestureStateChangeEvent) => {
const progress = getProgress(event.x, sliderWidth);
progressWidth.value = progress;
runOnJS(seekPosition)((progress * duration) / 100);
};
- const onSliderLayout = (e) => {
- setSliderWidth(e.nativeEvent.layout.width);
+ const onSliderLayout = (event: LayoutChangeEvent) => {
+ setSliderWidth(event.nativeEvent.layout.width);
};
const pan = Gesture.Pan()
@@ -66,7 +68,7 @@ function ProgressBar({duration, position, seekPosition}) {
progressWidth.value = getProgress(position, duration);
}, [duration, isSliderPressed, position, progressWidth]);
- const progressBarStyle = useAnimatedStyle(() => ({width: `${progressWidth.value}%`}));
+ const progressBarStyle: ViewStyle = useAnimatedStyle(() => ({width: `${progressWidth.value}%`}));
return (
@@ -85,8 +87,6 @@ function ProgressBar({duration, position, seekPosition}) {
);
}
-ProgressBar.propTypes = propTypes;
-ProgressBar.defaultProps = defaultProps;
ProgressBar.displayName = 'ProgressBar';
export default ProgressBar;
diff --git a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx
similarity index 81%
rename from src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js
rename to src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx
index 45f47eb87c36..011391ed4c71 100644
--- a/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.js
+++ b/src/components/VideoPlayer/VideoPlayerControls/VolumeButton/index.tsx
@@ -1,6 +1,7 @@
-import PropTypes from 'prop-types';
import React, {memo, useCallback, useState} from 'react';
+import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
+import type {GestureStateChangeEvent, GestureUpdateEvent, PanGestureChangeEventPayload, PanGestureHandlerEventPayload} from 'react-native-gesture-handler';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import Animated, {runOnJS, useAnimatedStyle, useDerivedValue} from 'react-native-reanimated';
import Hoverable from '@components/Hoverable';
@@ -10,18 +11,16 @@ import {useVolumeContext} from '@components/VideoPlayerContexts/VolumeContext';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as NumberUtils from '@libs/NumberUtils';
-import stylePropTypes from '@styles/stylePropTypes';
-const propTypes = {
- style: stylePropTypes.isRequired,
- small: PropTypes.bool,
-};
+type VolumeButtonProps = {
+ /** Style for the volume button. */
+ style?: StyleProp;
-const defaultProps = {
- small: false,
+ /** Is button icon small. */
+ small?: boolean;
};
-const getVolumeIcon = (volume) => {
+const getVolumeIcon = (volume: number) => {
if (volume === 0) {
return Expensicons.Mute;
}
@@ -31,7 +30,7 @@ const getVolumeIcon = (volume) => {
return Expensicons.VolumeHigh;
};
-function VolumeButton({style, small}) {
+function VolumeButton({style, small = false}: VolumeButtonProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {updateVolume, volume} = useVolumeContext();
@@ -39,12 +38,12 @@ function VolumeButton({style, small}) {
const [volumeIcon, setVolumeIcon] = useState({icon: getVolumeIcon(volume.value)});
const [isSliderBeingUsed, setIsSliderBeingUsed] = useState(false);
- const onSliderLayout = useCallback((e) => {
- setSliderHeight(e.nativeEvent.layout.height);
+ const onSliderLayout = useCallback((event: LayoutChangeEvent) => {
+ setSliderHeight(event.nativeEvent.layout.height);
}, []);
const changeVolumeOnPan = useCallback(
- (event) => {
+ (event: GestureStateChangeEvent | GestureUpdateEvent) => {
const val = NumberUtils.roundToTwoDecimalPlaces(1 - event.y / sliderHeight);
volume.value = NumberUtils.clamp(val, 0, 1);
},
@@ -65,7 +64,7 @@ function VolumeButton({style, small}) {
const progressBarStyle = useAnimatedStyle(() => ({height: `${volume.value * 100}%`}));
- const updateIcon = useCallback((vol) => {
+ const updateIcon = useCallback((vol: number) => {
setVolumeIcon({icon: getVolumeIcon(vol)});
}, []);
@@ -98,7 +97,6 @@ function VolumeButton({style, small}) {
tooltipText={volume.value === 0 ? translate('videoPlayer.unmute') : translate('videoPlayer.mute')}
onPress={() => updateVolume(volume.value === 0 ? 1 : 0)}
src={volumeIcon.icon}
- fill={styles.white}
small={small}
shouldForceRenderingTooltipBelow
/>
@@ -108,8 +106,6 @@ function VolumeButton({style, small}) {
);
}
-VolumeButton.propTypes = propTypes;
-VolumeButton.defaultProps = defaultProps;
VolumeButton.displayName = 'VolumeButton';
export default memo(VolumeButton);
diff --git a/src/components/VideoPlayer/VideoPlayerControls/index.js b/src/components/VideoPlayer/VideoPlayerControls/index.tsx
similarity index 75%
rename from src/components/VideoPlayer/VideoPlayerControls/index.js
rename to src/components/VideoPlayer/VideoPlayerControls/index.tsx
index 262613ce0797..7c61721b67b7 100644
--- a/src/components/VideoPlayer/VideoPlayerControls/index.js
+++ b/src/components/VideoPlayer/VideoPlayerControls/index.tsx
@@ -1,55 +1,58 @@
-import PropTypes from 'prop-types';
+import type {Video} from 'expo-av';
+import type {MutableRefObject} from 'react';
import React, {useCallback, useMemo, useState} from 'react';
+import type {GestureResponderEvent, LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import Animated from 'react-native-reanimated';
import * as Expensicons from '@components/Icon/Expensicons';
-import refPropTypes from '@components/refPropTypes';
import Text from '@components/Text';
import IconButton from '@components/VideoPlayer/IconButton';
import {convertMillisecondsToTime} from '@components/VideoPlayer/utils';
import {usePlaybackContext} from '@components/VideoPlayerContexts/PlaybackContext';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import stylePropTypes from '@styles/stylePropTypes';
import CONST from '@src/CONST';
import ProgressBar from './ProgressBar';
import VolumeButton from './VolumeButton';
-const propTypes = {
- duration: PropTypes.number.isRequired,
+type VideoPlayerControlsProps = {
+ /** Duration of a video. */
+ duration: number;
- position: PropTypes.number.isRequired,
+ /** Position of progress pointer. */
+ position: number;
- url: PropTypes.string.isRequired,
+ /** Url of a video. */
+ url: string;
- videoPlayerRef: refPropTypes.isRequired,
+ /** Ref for video player. */
+ videoPlayerRef: MutableRefObject