diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association
index dce724440adf..54fdd5681ae4 100644
--- a/.well-known/apple-app-site-association
+++ b/.well-known/apple-app-site-association
@@ -2,7 +2,7 @@
"applinks": {
"details": [
{
- "appIDs": ["368M544MTT.com.chat.expensify.chat"],
+ "appIDs": ["368M544MTT.com.chat.expensify.chat", "452835FXHF.com.expensify.expensifylite"],
"components": [
{
"/": "/r/*",
@@ -105,6 +105,6 @@
]
},
"webcredentials": {
- "apps": ["368M544MTT.com.chat.expensify.chat"]
+ "apps": ["368M544MTT.com.chat.expensify.chat", "452835FXHF.com.expensify.expensifylite"]
}
}
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 472f8f52b87f..57a1d0fa4a99 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -110,8 +110,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1009005205
- versionName "9.0.52-5"
+ versionCode 1009005301
+ versionName "9.0.53-1"
// 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/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt
index 2cc8b7780253..f476ad89c5b4 100644
--- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt
+++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt
@@ -11,9 +11,11 @@ import com.expensify.chat.bootsplash.BootSplashPackage
import com.expensify.chat.shortcutManagerModule.ShortcutManagerPackage
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
+import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
+import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.modules.i18nmanager.I18nUtil
import com.facebook.soloader.SoLoader
@@ -44,6 +46,9 @@ class MainApplication : MultiDexApplication(), ReactApplication {
get() = BuildConfig.IS_HERMES_ENABLED
})
+ override val reactHost: ReactHost
+ get() = getDefaultReactHost(applicationContext, reactNativeHost)
+
override fun onCreate() {
super.onCreate()
ReactFontManager.getInstance().addCustomFont(this, "Expensify New Kansas", R.font.expensify_new_kansas)
@@ -59,7 +64,7 @@ class MainApplication : MultiDexApplication(), ReactApplication {
SoLoader.init(this, /* native exopackage */false)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
- load(bridgelessEnabled = false)
+ load()
}
if (BuildConfig.DEBUG) {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false)
diff --git a/docs/articles/expensify-classic/connections/quickbooks-desktop/Configure-Quickbooks-Desktop.md b/docs/articles/expensify-classic/connections/quickbooks-desktop/Configure-Quickbooks-Desktop.md
index 917c3c007b28..dd913af1c497 100644
--- a/docs/articles/expensify-classic/connections/quickbooks-desktop/Configure-Quickbooks-Desktop.md
+++ b/docs/articles/expensify-classic/connections/quickbooks-desktop/Configure-Quickbooks-Desktop.md
@@ -7,6 +7,8 @@ Our new QuickBooks Desktop integration allows you to automate the import and exp
# Step 1: Configure export settings
The following steps will determine how data will be exported from Expensify to QuickBooks Desktop.
+![Expensify export settings page for the QuickBooks Desktop integration](https://help.expensify.com/assets/images/quickbooks-desktop-export-settings.png){:width="100%"}
+
1. In Expensify, hover over **Settings** and click **Workspaces**.
2. Select the Workspace you want to connect to QuickBooks Desktop.
3. Click the **Connections** tab.
@@ -28,6 +30,8 @@ The following steps will determine how data will be exported from Expensify to Q
The following steps help you determine how data will be imported from QuickBooks Online to Expensify:
+![Expensify coding settings page for the QuickBooks Desktop integration](https://help.expensify.com/assets/images/quickbooks-desktop-coding-settings.png){:width="100%"}
+
1. Click Import under the QuickBooks Online connection.
2. Review each of the following import settings:
- **Chart of Accounts**: The Chart of Accounts is automatically imported from QuickBooks Desktop as categories. This cannot be amended.
@@ -39,6 +43,8 @@ The following steps help you determine how data will be imported from QuickBooks
The following steps help you determine the advanced settings for your connection, like auto-sync and employee invitation settings.
+![Expensify advanced settings page for the QuickBooks Desktop integration](https://help.expensify.com/assets/images/quickbooks-desktop-advanced-settings.png){:width="100%"}
+
1. Click **Advanced** under the QuickBooks Desktop connection.
2. **Enable or disable Auto-Sync**: If enabled, QuickBooks Desktop automatically communicates changes with Expensify to ensure that the data shared between the two systems is up to date. New report approvals/reimbursements will be synced during the next auto-sync period.
diff --git a/docs/articles/expensify-classic/connections/quickbooks-desktop/Quickbooks-Desktop-Troubleshooting.md b/docs/articles/expensify-classic/connections/quickbooks-desktop/Quickbooks-Desktop-Troubleshooting.md
index 06f894ce7ef6..c832667080d5 100644
--- a/docs/articles/expensify-classic/connections/quickbooks-desktop/Quickbooks-Desktop-Troubleshooting.md
+++ b/docs/articles/expensify-classic/connections/quickbooks-desktop/Quickbooks-Desktop-Troubleshooting.md
@@ -40,7 +40,13 @@ Generally, these errors indicate that there is a credentials issue.
4. Check that you have the correct permissions.
5. Log in to QuickBooks Desktop as an Admin (in single-user mode).
6. Go to **Edit** > **Preferences** > **Integrated Applications** > **Company Preferences**.
-7. Select the Web Connector and click **Properties**.
+
+![Company Preferences page of QuickBooks Desktop](https://help.expensify.com/assets/images/quickbooks-desktop-company-preferences.png){:width="100%"}
+
+7. Select the Web Connector and click **Properties**.
+
+![Web Connector Properties page in QuickBooks Desktop](https://help.expensify.com/assets/images/quickbooks-desktop-access-rights.png){:width="100%"}
+
8. Make sure that the "Allow this application to login automatically" checkbox is selected and click **OK**.
9. Close all windows in QuickBooks.
@@ -98,6 +104,11 @@ Generally, this is the result of not having both the QuickBooks Web Connector an
1. Make sure that the Web Connector and QuickBooks Desktop Company File are both open.
2. In the Web Connector, check that the Last Status is “Ok”.
+
+![QuickBooks Web Connector showing status "OK"](https://help.expensify.com/assets/images/quickbooks-desktop-web-connector.png){:width="100%"}
+
3. Check the Report Comments in Expensify to confirm that the report has been successfully exported to QuickBooks Desktop.
+![Expensify report showing the report was exported](https://help.expensify.com/assets/images/quickbooks-desktop-exported-report-comments.png){:width="100%"}
+
If these general troubleshooting steps don’t work, reach out to Concierge with your Expensify Report ID and a screenshot of your QuickBooks Web Connector.
diff --git a/docs/assets/images/ExpensifyHelp_OldDot_SendInvoice.png b/docs/assets/images/ExpensifyHelp_OldDot_SendInvoice.png
new file mode 100644
index 000000000000..402afb86cc40
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp_OldDot_SendInvoice.png differ
diff --git a/docs/assets/images/ExpensifyHelp_OldDot_SendInvoice_02.png b/docs/assets/images/ExpensifyHelp_OldDot_SendInvoice_02.png
new file mode 100644
index 000000000000..7aeb0fdfb7c5
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp_OldDot_SendInvoice_02.png differ
diff --git a/docs/assets/images/cardfeeds-01.png b/docs/assets/images/cardfeeds-01.png
new file mode 100644
index 000000000000..ddf318fc05e8
Binary files /dev/null and b/docs/assets/images/cardfeeds-01.png differ
diff --git a/docs/assets/images/cardfeeds-02.png b/docs/assets/images/cardfeeds-02.png
new file mode 100644
index 000000000000..b0f047722444
Binary files /dev/null and b/docs/assets/images/cardfeeds-02.png differ
diff --git a/docs/assets/images/compcard-01.png b/docs/assets/images/compcard-01.png
new file mode 100644
index 000000000000..95b577714833
Binary files /dev/null and b/docs/assets/images/compcard-01.png differ
diff --git a/docs/assets/images/compcard-02.png b/docs/assets/images/compcard-02.png
new file mode 100644
index 000000000000..a34cdbfa1603
Binary files /dev/null and b/docs/assets/images/compcard-02.png differ
diff --git a/docs/assets/images/compcard-03.png b/docs/assets/images/compcard-03.png
new file mode 100644
index 000000000000..1e4bb6776e17
Binary files /dev/null and b/docs/assets/images/compcard-03.png differ
diff --git a/docs/assets/images/csv-01.png b/docs/assets/images/csv-01.png
new file mode 100644
index 000000000000..e6cfe9cf36f6
Binary files /dev/null and b/docs/assets/images/csv-01.png differ
diff --git a/docs/assets/images/csv-02.png b/docs/assets/images/csv-02.png
new file mode 100644
index 000000000000..72ba2b5cf583
Binary files /dev/null and b/docs/assets/images/csv-02.png differ
diff --git a/docs/assets/images/csv-03.png b/docs/assets/images/csv-03.png
new file mode 100644
index 000000000000..4aac1f72893c
Binary files /dev/null and b/docs/assets/images/csv-03.png differ
diff --git a/docs/assets/images/expenses-01.png b/docs/assets/images/expenses-01.png
new file mode 100644
index 000000000000..0169a20b2e2b
Binary files /dev/null and b/docs/assets/images/expenses-01.png differ
diff --git a/docs/assets/images/expenses-02.png b/docs/assets/images/expenses-02.png
new file mode 100644
index 000000000000..1164f341b033
Binary files /dev/null and b/docs/assets/images/expenses-02.png differ
diff --git a/docs/assets/images/expenses-03.png b/docs/assets/images/expenses-03.png
new file mode 100644
index 000000000000..75c06639cb81
Binary files /dev/null and b/docs/assets/images/expenses-03.png differ
diff --git a/docs/assets/images/expenses-04.png b/docs/assets/images/expenses-04.png
new file mode 100644
index 000000000000..16e9b9756d47
Binary files /dev/null and b/docs/assets/images/expenses-04.png differ
diff --git a/docs/assets/images/expenses-05.png b/docs/assets/images/expenses-05.png
new file mode 100644
index 000000000000..cf99d05eb1af
Binary files /dev/null and b/docs/assets/images/expenses-05.png differ
diff --git a/docs/assets/images/invoice-bulk-01.png b/docs/assets/images/invoice-bulk-01.png
new file mode 100644
index 000000000000..1dbf7fa5088d
Binary files /dev/null and b/docs/assets/images/invoice-bulk-01.png differ
diff --git a/docs/assets/images/invoice-bulk-02.png b/docs/assets/images/invoice-bulk-02.png
new file mode 100644
index 000000000000..82e388b0125f
Binary files /dev/null and b/docs/assets/images/invoice-bulk-02.png differ
diff --git a/docs/assets/images/invoice-bulk-03.png b/docs/assets/images/invoice-bulk-03.png
new file mode 100644
index 000000000000..f51abec046b7
Binary files /dev/null and b/docs/assets/images/invoice-bulk-03.png differ
diff --git a/docs/assets/images/invoice-bulk-04.png b/docs/assets/images/invoice-bulk-04.png
new file mode 100644
index 000000000000..35e12a095ba6
Binary files /dev/null and b/docs/assets/images/invoice-bulk-04.png differ
diff --git a/docs/assets/images/invoice-bulk-05.png b/docs/assets/images/invoice-bulk-05.png
new file mode 100644
index 000000000000..c7044c259de2
Binary files /dev/null and b/docs/assets/images/invoice-bulk-05.png differ
diff --git a/docs/assets/images/tax_tracking-01.png b/docs/assets/images/tax_tracking-01.png
new file mode 100644
index 000000000000..a35da6c1848a
Binary files /dev/null and b/docs/assets/images/tax_tracking-01.png differ
diff --git a/docs/assets/images/tax_tracking-02.png b/docs/assets/images/tax_tracking-02.png
new file mode 100644
index 000000000000..4d3df9eda60c
Binary files /dev/null and b/docs/assets/images/tax_tracking-02.png differ
diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj
index 96baba0d4e87..b3ec8febb1df 100644
--- a/ios/NewExpensify.xcodeproj/project.pbxproj
+++ b/ios/NewExpensify.xcodeproj/project.pbxproj
@@ -681,7 +681,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/FullStory/tools/FullStoryCommandLine\" \"${CONFIGURATION_BUILD_DIR}/${WRAPPER_NAME}\"\n";
+ shellScript = "if [ \"$CONFIGURATION\" != \"DebugDevelopment\" ]; then\n \"${PODS_ROOT}/FullStory/tools/FullStoryCommandLine\" \"${CONFIGURATION_BUILD_DIR}/${WRAPPER_NAME}\"\nelse\n echo \"Skipping FullStory Asset Uploader phase for DebugDevelopment scheme.\"\nfi\n";
};
5CF45ABA52C0BB0D7B9D139A /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
diff --git a/ios/NewExpensify/AppDelegate.mm b/ios/NewExpensify/AppDelegate.mm
index dc0ef2812031..5608c44823f4 100644
--- a/ios/NewExpensify/AppDelegate.mm
+++ b/ios/NewExpensify/AppDelegate.mm
@@ -88,11 +88,6 @@ - (NSURL *)bundleURL
#endif
}
-- (BOOL)bridgelessEnabled
-{
- return NO;
-}
-
// This methods is needed to support the hardware keyboard shortcuts
- (NSArray *)keyCommands {
return [HardwareShortcuts sharedInstance].keyCommands;
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 35057e6e33c1..686956d696f6 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 9.0.52
+ 9.0.53
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 9.0.52.5
+ 9.0.53.1
FullStory
OrgId
diff --git a/ios/NewExpensify/RCTBootSplash.h b/ios/NewExpensify/RCTBootSplash.h
index 5dc3def635f2..f25f3e28f561 100644
--- a/ios/NewExpensify/RCTBootSplash.h
+++ b/ios/NewExpensify/RCTBootSplash.h
@@ -1,12 +1,4 @@
-//
-// RCTBootSplash.h
-// NewExpensify
-//
-// Created by Mathieu Acthernoene on 07/01/2022.
-//
-
#import
-#import
@interface RCTBootSplash : NSObject
diff --git a/ios/NewExpensify/RCTBootSplash.mm b/ios/NewExpensify/RCTBootSplash.mm
index 3e4a086f07b1..ddb3f2d047ce 100644
--- a/ios/NewExpensify/RCTBootSplash.mm
+++ b/ios/NewExpensify/RCTBootSplash.mm
@@ -2,19 +2,16 @@
#import
-#if RCT_NEW_ARCH_ENABLED
#import
#import
-#else
#import
-#endif
-static NSMutableArray *_resolveQueue = nil;
+static RCTSurfaceHostingProxyRootView *_rootView = nil;
+
static UIView *_loadingView = nil;
-static UIView *_rootView = nil;
-static float _duration = 0;
+static NSMutableArray *_resolveQueue = [[NSMutableArray alloc] init];
+static bool _fade = false;
static bool _nativeHidden = false;
-static bool _transitioning = false;
@implementation RCTBootSplash
@@ -24,14 +21,18 @@ - (dispatch_queue_t)methodQueue {
return dispatch_get_main_queue();
}
++ (BOOL)requiresMainQueueSetup {
+ return NO;
+}
+
+ (void)invalidateBootSplash {
_resolveQueue = nil;
_rootView = nil;
_nativeHidden = false;
}
-+ (bool)isLoadingViewHidden {
- return _loadingView == nil || [_loadingView isHidden];
++ (bool)isLoadingViewVisible {
+ return _loadingView != nil && ![_loadingView isHidden];
}
+ (bool)hasResolveQueue {
@@ -41,7 +42,7 @@ + (bool)hasResolveQueue {
+ (void)clearResolveQueue {
if (![self hasResolveQueue])
return;
-
+
while ([_resolveQueue count] > 0) {
RCTPromiseResolveBlock resolve = [_resolveQueue objectAtIndex:0];
[_resolveQueue removeObjectAtIndex:0];
@@ -49,19 +50,15 @@ + (void)clearResolveQueue {
}
}
-+ (void)hideLoadingView {
- if ([self isLoadingViewHidden])
++ (void)hideAndClearPromiseQueue {
+ if (![self isLoadingViewVisible]) {
return [RCTBootSplash clearResolveQueue];
+ }
- if (_duration > 0) {
+ if (_fade) {
dispatch_async(dispatch_get_main_queue(), ^{
- _transitioning = true;
-
- if (_rootView == nil)
- return;
-
[UIView transitionWithView:_rootView
- duration:_duration / 1000.0
+ duration:0.250
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
_loadingView.hidden = YES;
@@ -70,7 +67,6 @@ + (void)hideLoadingView {
[_loadingView removeFromSuperview];
_loadingView = nil;
- _transitioning = false;
return [RCTBootSplash clearResolveQueue];
}];
});
@@ -85,30 +81,9 @@ + (void)hideLoadingView {
+ (void)initWithStoryboard:(NSString * _Nonnull)storyboardName
rootView:(UIView * _Nullable)rootView {
- if (rootView == nil
-#ifdef RCT_NEW_ARCH_ENABLED
- || ![rootView isKindOfClass:[RCTSurfaceHostingProxyRootView class]]
-#else
- || ![rootView isKindOfClass:[RCTRootView class]]
-#endif
- || _rootView != nil
- || [self hasResolveQueue] // hide has already been called, abort init
- || RCTRunningInAppExtension())
+ if (RCTRunningInAppExtension()) {
return;
-
-#ifdef RCT_NEW_ARCH_ENABLED
- RCTSurfaceHostingProxyRootView *proxy = (RCTSurfaceHostingProxyRootView *)rootView;
- _rootView = (RCTSurfaceHostingView *)proxy.surface.view;
-#else
- _rootView = (RCTRootView *)rootView;
-#endif
-
- UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
-
- _loadingView = [[storyboard instantiateInitialViewController] view];
- _loadingView.hidden = NO;
-
- [_rootView addSubview:_loadingView];
+ }
[NSTimer scheduledTimerWithTimeInterval:0.35
repeats:NO
@@ -117,19 +92,35 @@ + (void)initWithStoryboard:(NSString * _Nonnull)storyboardName
_nativeHidden = true;
// hide has been called before native launch screen fade out
- if ([self hasResolveQueue])
- [self hideLoadingView];
+ if ([_resolveQueue count] > 0) {
+ [self hideAndClearPromiseQueue];
+ }
}];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(onJavaScriptDidLoad)
- name:RCTJavaScriptDidLoadNotification
- object:nil];
+ if (rootView != nil) {
+ _rootView = (RCTSurfaceHostingProxyRootView *)rootView;
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(onJavaScriptDidFailToLoad)
- name:RCTJavaScriptDidFailToLoadNotification
- object:nil];
+ UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
+
+ _loadingView = [[storyboard instantiateInitialViewController] view];
+ _loadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ _loadingView.frame = _rootView.bounds;
+ _loadingView.center = (CGPoint){CGRectGetMidX(_rootView.bounds), CGRectGetMidY(_rootView.bounds)};
+ _loadingView.hidden = NO;
+
+ [_rootView disableActivityIndicatorAutoHide:YES];
+ [_rootView setLoadingView:_loadingView];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onJavaScriptDidLoad)
+ name:RCTJavaScriptDidLoadNotification
+ object:nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(onJavaScriptDidFailToLoad)
+ name:RCTJavaScriptDidFailToLoadNotification
+ object:nil];
+ }
}
+ (void)onJavaScriptDidLoad {
@@ -137,50 +128,51 @@ + (void)onJavaScriptDidLoad {
}
+ (void)onJavaScriptDidFailToLoad {
- [self hideLoadingView];
+ [self hideAndClearPromiseQueue];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-- (void)hide:(double)duration
- resolve:(RCTPromiseResolveBlock)resolve
- reject:(RCTPromiseRejectBlock)reject {
- if (_resolveQueue == nil)
- _resolveQueue = [[NSMutableArray alloc] init];
+- (NSDictionary *)constantsToExport {
+ UIWindow *window = RCTKeyWindow();
+ __block bool darkModeEnabled = false;
- [_resolveQueue addObject:resolve];
+ RCTUnsafeExecuteOnMainQueueSync(^{
+ darkModeEnabled = window != nil && window.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
+ });
- if ([RCTBootSplash isLoadingViewHidden] || RCTRunningInAppExtension())
- return [RCTBootSplash clearResolveQueue];
+ return @{
+ @"darkModeEnabled": @(darkModeEnabled)
+ };
+}
+
+- (void)hideImpl:(BOOL)fade
+ resolve:(RCTPromiseResolveBlock)resolve {
+ if (_resolveQueue == nil)
+ _resolveQueue = [[NSMutableArray alloc] init];
+
+ [_resolveQueue addObject:resolve];
+
+ if (![RCTBootSplash isLoadingViewVisible] || RCTRunningInAppExtension())
+ return [RCTBootSplash clearResolveQueue];
- _duration = lroundf((float)duration);
+ _fade = fade;
- if (_nativeHidden)
- return [RCTBootSplash hideLoadingView];
+ if (_nativeHidden)
+ return [RCTBootSplash hideAndClearPromiseQueue];
}
-- (void)getVisibilityStatus:(RCTPromiseResolveBlock)resolve
- reject:(RCTPromiseRejectBlock)reject {
- if ([RCTBootSplash isLoadingViewHidden])
- return resolve(@"hidden");
- else if (_transitioning)
- return resolve(@"transitioning");
- else
- return resolve(@"visible");
+- (void)isVisibleImpl:(RCTPromiseResolveBlock)resolve {
+ resolve(@([RCTBootSplash isLoadingViewVisible]));
}
-RCT_REMAP_METHOD(hide,
- resolve:(RCTPromiseResolveBlock)resolve
- rejecte:(RCTPromiseRejectBlock)reject) {
- [self hide:0
- resolve:resolve
- reject:reject];
+RCT_EXPORT_METHOD(hide:(RCTPromiseResolveBlock)resolve
+ reject:(RCTPromiseRejectBlock)reject) {
+ [self hideImpl:0 resolve:resolve];
}
-RCT_REMAP_METHOD(getVisibilityStatus,
- getVisibilityStatusWithResolve:(RCTPromiseResolveBlock)resolve
- rejecte:(RCTPromiseRejectBlock)reject) {
- [self getVisibilityStatus:resolve
- reject:reject];
+RCT_EXPORT_METHOD(isVisible:(RCTPromiseResolveBlock)resolve
+ reject:(RCTPromiseRejectBlock)reject) {
+ [self isVisibleImpl:resolve];
}
@end
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index f1d0ef296b54..09b60f4fa387 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 9.0.52
+ 9.0.53
CFBundleSignature
????
CFBundleVersion
- 9.0.52.5
+ 9.0.53.1
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 3efdcd69ae04..45ef64b85188 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName
$(PRODUCT_NAME)
CFBundleShortVersionString
- 9.0.52
+ 9.0.53
CFBundleVersion
- 9.0.52.5
+ 9.0.53.1
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/NotificationServiceExtension/NotificationService.swift b/ios/NotificationServiceExtension/NotificationService.swift
index e489cb368d17..b588c6be1d0f 100644
--- a/ios/NotificationServiceExtension/NotificationService.swift
+++ b/ios/NotificationServiceExtension/NotificationService.swift
@@ -8,12 +8,18 @@
import AirshipServiceExtension
import os.log
import Intents
+import AppLogs
class NotificationService: UANotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
let log = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "com.expensify.chat.dev.NotificationServiceExtension", category: "NotificationService")
+ let appLogs: AppLogs = .init()
+
+ deinit {
+ appLogs.forwardLogsTo(appGroup: "group.com.expensify.new")
+ }
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
os_log("[NotificationService] didReceive() - received notification", log: log)
@@ -42,7 +48,7 @@ class NotificationService: UANotificationServiceExtension {
do {
notificationData = try parsePayload(notificationContent: notificationContent)
} catch ExpError.runtimeError(let errorMessage) {
- os_log("[NotificationService] configureCommunicationNotification() - couldn't parse the payload '%@'", log: log, type: .error, errorMessage)
+ os_log("[NotificationService] configureCommunicationNotification() - couldn't parse the payload '%{public}@'", log: log, type: .error, errorMessage)
contentHandler(notificationContent)
return
} catch {
@@ -212,7 +218,7 @@ class NotificationService: UANotificationServiceExtension {
let data = try Data(contentsOf: url)
return INImage(imageData: data)
} catch {
- os_log("[NotificationService] fetchINImage() - failed to fetch avatar. reportActionID: %@", log: self.log, type: .error, reportActionID)
+ os_log("[NotificationService] fetchINImage() - failed to fetch avatar. reportActionID: %{public}@", log: self.log, type: .error, reportActionID)
return nil
}
}
diff --git a/ios/Podfile b/ios/Podfile
index e807089c26b9..4d139711ef01 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -119,6 +119,7 @@ end
target 'NotificationServiceExtension' do
pod 'AirshipServiceExtension'
+ pod 'AppLogs', :path => '../node_modules/react-native-app-logs/AppLogsPod'
end
pod 'FullStory', :http => 'https://ios-releases.fullstory.com/fullstory-1.52.0-xcframework.tar.gz'
\ No newline at end of file
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 1242ab7a5a39..9a706cc4e8aa 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -26,6 +26,7 @@ PODS:
- AppAuth/Core (1.7.5)
- AppAuth/ExternalUserAgent (1.7.5):
- AppAuth/Core
+ - AppLogs (0.1.0)
- boost (1.84.0)
- DoubleConversion (1.1.6)
- EXAV (14.0.7):
@@ -1564,6 +1565,27 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
+ - react-native-app-logs (0.3.1):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
- react-native-blob-util (0.19.4):
- DoubleConversion
- glog
@@ -2373,7 +2395,7 @@ PODS:
- RNGoogleSignin (10.0.1):
- GoogleSignIn (~> 7.0)
- React-Core
- - RNLiveMarkdown (0.1.164):
+ - RNLiveMarkdown (0.1.176):
- DoubleConversion
- glog
- hermes-engine
@@ -2393,9 +2415,9 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - RNLiveMarkdown/newarch (= 0.1.164)
+ - RNLiveMarkdown/newarch (= 0.1.176)
- Yoga
- - RNLiveMarkdown/newarch (0.1.164):
+ - RNLiveMarkdown/newarch (0.1.176):
- DoubleConversion
- glog
- hermes-engine
@@ -2702,6 +2724,7 @@ PODS:
DEPENDENCIES:
- AirshipServiceExtension
+ - AppLogs (from `../node_modules/react-native-app-logs/AppLogsPod`)
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXAV (from `../node_modules/expo-av/ios`)
@@ -2751,6 +2774,7 @@ DEPENDENCIES:
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- "react-native-airship (from `../node_modules/@ua/react-native-airship`)"
+ - react-native-app-logs (from `../node_modules/react-native-app-logs`)
- react-native-blob-util (from `../node_modules/react-native-blob-util`)
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
- react-native-config (from `../node_modules/react-native-config`)
@@ -2864,6 +2888,8 @@ SPEC REPOS:
- Turf
EXTERNAL SOURCES:
+ AppLogs:
+ :path: "../node_modules/react-native-app-logs/AppLogsPod"
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
DoubleConversion:
@@ -2959,6 +2985,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-airship:
:path: "../node_modules/@ua/react-native-airship"
+ react-native-app-logs:
+ :path: "../node_modules/react-native-app-logs"
react-native-blob-util:
:path: "../node_modules/react-native-blob-util"
react-native-cameraroll:
@@ -3109,6 +3137,7 @@ SPEC CHECKSUMS:
AirshipFrameworkProxy: dbd862dc6fb21b13e8b196458d626123e2a43a50
AirshipServiceExtension: 9c73369f426396d9fb9ff222d86d842fac76ba46
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
+ AppLogs: 3bc4e9b141dbf265b9464409caaa40416a9ee0e0
boost: 26992d1adf73c1c7676360643e687aee6dda994b
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f
@@ -3184,6 +3213,7 @@ SPEC CHECKSUMS:
React-Mapbuffer: 1c08607305558666fd16678b85ef135e455d5c96
React-microtasksnativemodule: f13f03163b6a5ec66665dfe80a0df4468bb766a6
react-native-airship: e10f6823d8da49bbcb2db4bdb16ff954188afccc
+ react-native-app-logs: b8a104816aafc78cd0965e923452de88dcf8ec67
react-native-blob-util: 221c61c98ae507b758472ac4d2d489119d1a6c44
react-native-cameraroll: 478a0c1fcdd39f08f6ac272b7ed06e92b2c7c129
react-native-config: 742a9e0a378a78d0eaff1fb3477d8c0ae222eb51
@@ -3242,7 +3272,7 @@ SPEC CHECKSUMS:
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 8781e2529230a1bc3ea8d75e5c3cd071b6c6aed7
RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0
- RNLiveMarkdown: b2bd97a6f1206be16cf6536c092fe39f986aca34
+ RNLiveMarkdown: 0b8756147a5e8eeea98d3e1187c0c27d5a96d1ff
RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81
rnmapbox-maps: 460d6ff97ae49c7d5708c3212c6521697c36a0c4
RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28
@@ -3261,6 +3291,6 @@ SPEC CHECKSUMS:
VisionCamera: c6c8aa4b028501fc87644550fbc35a537d4da3fb
Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8
-PODFILE CHECKSUM: a07e55247056ec5d84d1af31d694506efff3cfe2
+PODFILE CHECKSUM: 15e2f095b9c80d658459723edf84005a6867debf
COCOAPODS: 1.15.2
diff --git a/jest/setup.ts b/jest/setup.ts
index 6901ad3c66f3..7dbe91c32fda 100644
--- a/jest/setup.ts
+++ b/jest/setup.ts
@@ -1,5 +1,6 @@
/* eslint-disable max-classes-per-file */
import '@shopify/flash-list/jestSetup';
+import type * as RNAppLogs from 'react-native-app-logs';
import 'react-native-gesture-handler/jestSetup';
import type * as RNKeyboardController from 'react-native-keyboard-controller';
import mockStorage from 'react-native-onyx/dist/storage/__mocks__';
@@ -75,6 +76,8 @@ jest.mock('react-native-reanimated', () => ({
jest.mock('react-native-keyboard-controller', () => require('react-native-keyboard-controller/jest'));
+jest.mock('react-native-app-logs', () => require('react-native-app-logs/jest'));
+
jest.mock('@src/libs/actions/Timing', () => ({
start: jest.fn(),
end: jest.fn(),
diff --git a/package-lock.json b/package-lock.json
index 1b8c13af2320..07ef2e48ac6b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,17 +1,17 @@
{
"name": "new.expensify",
- "version": "9.0.52-5",
+ "version": "9.0.53-1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.0.52-5",
+ "version": "9.0.53-1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@dotlottie/react-player": "^1.6.3",
- "@expensify/react-native-live-markdown": "0.1.164",
+ "@expensify/react-native-live-markdown": "0.1.176",
"@expo/metro-runtime": "~3.2.3",
"@firebase/app": "^0.10.10",
"@firebase/performance": "^0.6.8",
@@ -51,7 +51,7 @@
"date-fns-tz": "^3.2.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "2.0.94",
+ "expensify-common": "2.0.100",
"expo": "51.0.31",
"expo-av": "14.0.7",
"expo-image": "1.12.15",
@@ -77,6 +77,7 @@
"react-map-gl": "^7.1.3",
"react-native": "0.75.2",
"react-native-android-location-enabler": "^2.0.1",
+ "react-native-app-logs": "git+https://github.com/margelo/react-native-app-logs#7e9c311bffdc6a9eeb69d90d30ead47e01c3552a",
"react-native-blob-util": "0.19.4",
"react-native-collapsible": "^1.6.2",
"react-native-config": "1.5.3",
@@ -3637,9 +3638,10 @@
}
},
"node_modules/@expensify/react-native-live-markdown": {
- "version": "0.1.164",
- "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.164.tgz",
- "integrity": "sha512-x1/Oa+I1AI82xWEFYd2kSkSj4rZ1q2JG4aEDomUHSqcNjuQetQPw9kVFN5DaLHt0Iu0iKEUrXIhy5LpMSHJQLg==",
+ "version": "0.1.176",
+ "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.176.tgz",
+ "integrity": "sha512-0IS0Rfl0qYqrE2V8jsVX58c4K/zxeNC7o1CAL9Xu+HTbTtD58Yu5gOOwp5AljkS2qdPR86swGRZyLXGkGRKkPg==",
+ "license": "MIT",
"workspaces": [
"parser",
"example",
@@ -24054,9 +24056,9 @@
}
},
"node_modules/expensify-common": {
- "version": "2.0.94",
- "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.94.tgz",
- "integrity": "sha512-Cco5X6u4IL5aQlFqa2IgGgR+vAffYLxpPN2d7bzfptW/pRLY2L2JRJohgvXEswlCcTKFVt4nIJ4bx9YIOvzxBA==",
+ "version": "2.0.100",
+ "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.100.tgz",
+ "integrity": "sha512-mektI+OuTywYU47Valjsn2+kLQ1/Wc9sWCY1/a0Vo8IHTXroQWvbKs5IXlkiqODi4SRonVZwOL3ha/oJD7o7nQ==",
"dependencies": {
"awesome-phonenumber": "^5.4.0",
"classnames": "2.5.0",
@@ -34400,6 +34402,18 @@
"prop-types": "^15.7.2"
}
},
+ "node_modules/react-native-app-logs": {
+ "version": "0.3.1",
+ "resolved": "git+ssh://git@github.com/margelo/react-native-app-logs.git#7e9c311bffdc6a9eeb69d90d30ead47e01c3552a",
+ "integrity": "sha512-GFZFbUe9bUIbuH2zTAS7JAXCAIYnyf4cTnsz6pSzYCl3F+nF+O3fRa5ZM8P7zr+wTG7fZoVs0b6XFfcFUcxY2A==",
+ "workspaces": [
+ "example"
+ ],
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-blob-util": {
"version": "0.19.4",
"license": "MIT",
diff --git a/package.json b/package.json
index f28b5775082b..8867dc529c8a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.0.52-5",
+ "version": "9.0.53-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.",
@@ -67,7 +67,7 @@
},
"dependencies": {
"@dotlottie/react-player": "^1.6.3",
- "@expensify/react-native-live-markdown": "0.1.164",
+ "@expensify/react-native-live-markdown": "0.1.176",
"@expo/metro-runtime": "~3.2.3",
"@firebase/app": "^0.10.10",
"@firebase/performance": "^0.6.8",
@@ -107,7 +107,7 @@
"date-fns-tz": "^3.2.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "2.0.94",
+ "expensify-common": "2.0.100",
"expo": "51.0.31",
"expo-av": "14.0.7",
"expo-image": "1.12.15",
@@ -133,6 +133,7 @@
"react-map-gl": "^7.1.3",
"react-native": "0.75.2",
"react-native-android-location-enabler": "^2.0.1",
+ "react-native-app-logs": "git+https://github.com/margelo/react-native-app-logs#7e9c311bffdc6a9eeb69d90d30ead47e01c3552a",
"react-native-blob-util": "0.19.4",
"react-native-collapsible": "^1.6.2",
"react-native-config": "1.5.3",
diff --git a/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch b/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch
new file mode 100644
index 000000000000..a085cdbcfbe2
--- /dev/null
+++ b/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch
@@ -0,0 +1,13 @@
+diff --git a/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js b/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js
+index 03f001c..23d467d 100644
+--- a/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js
++++ b/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js
+@@ -65,7 +65,7 @@ function nativeModuleWrapped(namespace, NativeModule, argToPrepend) {
+ return NativeModule;
+ }
+
+- const properties = Object.keys(NativeModule);
++ const properties = Object.keys(Object.getPrototypeOf(NativeModule));
+
+ for (let i = 0, len = properties.length; i < len; i++) {
+ const property = properties[i];
diff --git a/patches/@rnmapbox+maps+10.1.30+001+bridgeless.patch b/patches/@rnmapbox+maps+10.1.30+001+bridgeless.patch
new file mode 100644
index 000000000000..b840e3da7b12
--- /dev/null
+++ b/patches/@rnmapbox+maps+10.1.30+001+bridgeless.patch
@@ -0,0 +1,83 @@
+diff --git a/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt b/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt
+index 5bebc1b..80a4be4 100644
+--- a/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt
++++ b/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt
+@@ -5,6 +5,8 @@ import com.facebook.react.bridge.ReactApplicationContext
+ import com.facebook.react.uimanager.ThemedReactContext
+ import com.facebook.react.uimanager.annotations.ReactProp
+ import com.facebook.react.viewmanagers.RNMBXRasterSourceManagerInterface
++import com.rnmapbox.rnmbx.events.constants.EventKeys
++import com.rnmapbox.rnmbx.events.constants.eventMapOf
+ import javax.annotation.Nonnull
+
+ class RNMBXRasterSourceManager(reactApplicationContext: ReactApplicationContext) :
+@@ -26,7 +28,10 @@ class RNMBXRasterSourceManager(reactApplicationContext: ReactApplicationContext)
+ }
+
+ override fun customEvents(): Map? {
+- return null
++ return eventMapOf(
++ EventKeys.RASTER_SOURCE_LAYER_CLICK to "onMapboxRasterSourcePress",
++ EventKeys.MAP_ANDROID_CALLBACK to "onAndroidCallback"
++ )
+ }
+
+ companion object {
+diff --git a/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/events/constants/EventKeys.kt b/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/events/constants/EventKeys.kt
+index d059b2c..3882f1e 100644
+--- a/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/events/constants/EventKeys.kt
++++ b/node_modules/@rnmapbox/maps/android/src/main/java/com/rnmapbox/rnmbx/events/constants/EventKeys.kt
+@@ -4,35 +4,37 @@ private fun ns(name: String): String {
+ val namespace = "rct.mapbox"
+ return String.format("%s.%s", namespace, name)
+ }
++
+ enum class EventKeys(val value: String) {
+ // map events
+- MAP_CLICK(ns("map.press")),
+- MAP_LONG_CLICK(ns("map.longpress")),
+- MAP_ONCHANGE(ns("map.change")),
+- MAP_ON_LOCATION_CHANGE(ns("map.location.change")),
+- MAP_ANDROID_CALLBACK(ns("map.androidcallback")),
+- MAP_USER_TRACKING_MODE_CHANGE(ns("map.usertrackingmodechange")),
++ MAP_CLICK("topPress"),
++ MAP_LONG_CLICK("topLongPress"),
++ MAP_ONCHANGE("topMapChange"),
++ MAP_ON_LOCATION_CHANGE("topLocationChange"),
++ MAP_ANDROID_CALLBACK("topAndroidCallback"),
++ MAP_USER_TRACKING_MODE_CHANGE("topUserTrackingModeChange"),
+
+ // point annotation events
+- POINT_ANNOTATION_SELECTED(ns("pointannotation.selected")),
+- POINT_ANNOTATION_DESELECTED(ns("pointannotation.deselected")),
+- POINT_ANNOTATION_DRAG_START(ns("pointannotation.dragstart")),
+- POINT_ANNOTATION_DRAG(ns("pointannotation.drag")),
+- POINT_ANNOTATION_DRAG_END(ns("pointannotation.dragend")),
++ POINT_ANNOTATION_SELECTED("topMapboxPointAnnotationSelected"),
++ POINT_ANNOTATION_DESELECTED("topMapboxPointAnnotationDeselected"),
++ POINT_ANNOTATION_DRAG_START("topMapboxPointAnnotationDragStart"),
++ POINT_ANNOTATION_DRAG("topMapboxPointAnnotationDrag"),
++ POINT_ANNOTATION_DRAG_END("topMapboxPointAnnotationDragEnd"),
+
+ // source events
+- SHAPE_SOURCE_LAYER_CLICK(ns("shapesource.layer.pressed")),
+- VECTOR_SOURCE_LAYER_CLICK(ns("vectorsource.layer.pressed")),
+- RASTER_SOURCE_LAYER_CLICK(ns("rastersource.layer.pressed")),
++ SHAPE_SOURCE_LAYER_CLICK("topMapboxShapeSourcePress"),
++ VECTOR_SOURCE_LAYER_CLICK("topMapboxVectorSourcePress"),
++ RASTER_SOURCE_LAYER_CLICK("topMapboxRasterSourcePress"),
+
+ // images event
+- IMAGES_MISSING(ns("images.missing")),
++ IMAGES_MISSING("topImageMissing"),
+
+ // location events
++ // TODO: not sure about this one since it is not registered anywhere
+ USER_LOCATION_UPDATE(ns("user.location.update")),
+
+ // viewport events
+- VIEWPORT_STATUS_CHANGE(ns("viewport.statuschange"))
++ VIEWPORT_STATUS_CHANGE("topStatusChanged")
+ }
+
+ fun eventMapOf(vararg values: Pair): Map {
diff --git a/patches/lottie-react-native+6.5.1.patch b/patches/lottie-react-native+6.5.1+001+recycling.patch
similarity index 100%
rename from patches/lottie-react-native+6.5.1.patch
rename to patches/lottie-react-native+6.5.1+001+recycling.patch
diff --git a/patches/lottie-react-native+6.5.1+002+bridgeless.patch b/patches/lottie-react-native+6.5.1+002+bridgeless.patch
new file mode 100644
index 000000000000..854d26f9beb9
--- /dev/null
+++ b/patches/lottie-react-native+6.5.1+002+bridgeless.patch
@@ -0,0 +1,25 @@
+diff --git a/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationFailureEvent.kt b/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationFailureEvent.kt
+index aa538d3..0185eaf 100644
+--- a/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationFailureEvent.kt
++++ b/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationFailureEvent.kt
+@@ -21,6 +21,6 @@ constructor(surfaceId: Int, viewId: Int, private val error: Throwable) :
+ }
+
+ companion object {
+- const val EVENT_NAME = "topAnimationFailureEvent"
++ const val EVENT_NAME = "topAnimationFailure"
+ }
+ }
+\ No newline at end of file
+diff --git a/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationLoadedEvent.kt b/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationLoadedEvent.kt
+index f17cff9..4ebe3ba 100644
+--- a/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationLoadedEvent.kt
++++ b/node_modules/lottie-react-native/android/src/main/java/com/airbnb/android/react/lottie/OnAnimationLoadedEvent.kt
+@@ -16,6 +16,6 @@ class OnAnimationLoadedEvent constructor(surfaceId: Int, viewId: Int) :
+ }
+
+ companion object {
+- const val EVENT_NAME = "topAnimationLoadedEvent"
++ const val EVENT_NAME = "topAnimationLoaded"
+ }
+ }
diff --git a/patches/react-native+0.75.2+011+textinput-clear-command.patch b/patches/react-native+0.75.2+011+textinput-clear-command.patch
index 773dde04ef44..6723d36d6c6c 100644
--- a/patches/react-native+0.75.2+011+textinput-clear-command.patch
+++ b/patches/react-native+0.75.2+011+textinput-clear-command.patch
@@ -1,3 +1,51 @@
+diff --git a/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
+index a77e5b4..6c4bbb2 100644
+--- a/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
++++ b/node_modules/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
+@@ -412,6 +412,13 @@ export type NativeProps = $ReadOnly<{|
+ $ReadOnly<{|target: Int32, text: string|}>,
+ >,
+
++ /**
++ * Invoked when the user performs the clear action.
++ */
++ onClear?: ?BubblingEventHandler<
++ $ReadOnly<{|target: Int32, eventCount: Int32, text: string|}>,
++ >,
++
+ /**
+ * Callback that is called when a key is pressed.
+ * This will be called with `{ nativeEvent: { key: keyValue } }`
+@@ -655,6 +662,9 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
+ },
+ },
+ directEventTypes: {
++ topClear: {
++ registrationName: 'onClear',
++ },
+ topScroll: {
+ registrationName: 'onScroll',
+ },
+@@ -693,6 +703,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
+ textTransform: true,
+ returnKeyType: true,
+ keyboardType: true,
++ onClear: true,
+ multiline: true,
+ color: {process: require('../../StyleSheet/processColor').default},
+ autoComplete: true,
+diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
+index 0aa8965..0b14171 100644
+--- a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
++++ b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
+@@ -146,6 +146,7 @@ const RCTTextInputViewConfig = {
+ lineBreakStrategyIOS: true,
+ smartInsertDelete: true,
+ ...ConditionallyIgnoredEventHandlers({
++ onClear: true,
+ onChange: true,
+ onSelectionChange: true,
+ onContentSizeChange: true,
diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
index 0aa8965..3bfe22c 100644
--- a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
diff --git a/patches/react-native-modal+13.0.1.patch b/patches/react-native-modal+13.0.1.patch
index cc9c8531e3a3..bd65871cf5ac 100644
--- a/patches/react-native-modal+13.0.1.patch
+++ b/patches/react-native-modal+13.0.1.patch
@@ -11,7 +11,7 @@ index b63bcfc..bd6419e 100644
buildPanResponder: () => void;
getAccDistancePerDirection: (gestureState: PanResponderGestureState) => number;
diff --git a/node_modules/react-native-modal/dist/modal.js b/node_modules/react-native-modal/dist/modal.js
-index 80f4e75..5a58eae 100644
+index 80f4e75..46277ea 100644
--- a/node_modules/react-native-modal/dist/modal.js
+++ b/node_modules/react-native-modal/dist/modal.js
@@ -75,6 +75,13 @@ export class ReactNativeModal extends React.Component {
@@ -28,7 +28,18 @@ index 80f4e75..5a58eae 100644
this.shouldPropagateSwipe = (evt, gestureState) => {
return typeof this.props.propagateSwipe === 'function'
? this.props.propagateSwipe(evt, gestureState)
-@@ -453,10 +460,18 @@ export class ReactNativeModal extends React.Component {
+@@ -383,7 +390,9 @@ export class ReactNativeModal extends React.Component {
+ this.setState({
+ isVisible: false,
+ }, () => {
+- this.props.onModalHide();
++ if (Platform.OS !== 'ios') {
++ this.props.onModalHide();
++ }
+ });
+ });
+ }
+@@ -453,10 +462,18 @@ export class ReactNativeModal extends React.Component {
if (this.state.isVisible) {
this.open();
}
@@ -48,7 +59,7 @@ index 80f4e75..5a58eae 100644
if (this.didUpdateDimensionsEmitter) {
this.didUpdateDimensionsEmitter.remove();
}
-@@ -464,6 +479,9 @@ export class ReactNativeModal extends React.Component {
+@@ -464,6 +481,9 @@ export class ReactNativeModal extends React.Component {
InteractionManager.clearInteractionHandle(this.interactionHandle);
this.interactionHandle = null;
}
@@ -58,9 +69,21 @@ index 80f4e75..5a58eae 100644
}
componentDidUpdate(prevProps) {
// If the animations have been changed then rebuild them to make sure we're
-@@ -525,7 +543,7 @@ export class ReactNativeModal extends React.Component {
+@@ -490,7 +510,7 @@ export class ReactNativeModal extends React.Component {
+ }
+ render() {
+ /* eslint-disable @typescript-eslint/no-unused-vars */
+- const { animationIn, animationInTiming, animationOut, animationOutTiming, avoidKeyboard, coverScreen, hasBackdrop, backdropColor, backdropOpacity, backdropTransitionInTiming, backdropTransitionOutTiming, customBackdrop, children, isVisible, onModalShow, onBackButtonPress, useNativeDriver, propagateSwipe, style, ...otherProps } = this.props;
++ const { animationIn, animationInTiming, animationOut, animationOutTiming, avoidKeyboard, coverScreen, hasBackdrop, backdropColor, backdropOpacity, backdropTransitionInTiming, backdropTransitionOutTiming, customBackdrop, children, isVisible, onModalShow, onBackButtonPress, useNativeDriver, propagateSwipe, style, onDismiss, ...otherProps } = this.props;
+ const { testID, ...containerProps } = otherProps;
+ const computedStyle = [
+ { margin: this.getDeviceWidth() * 0.05, transform: [{ translateY: 0 }] },
+@@ -523,9 +543,9 @@ export class ReactNativeModal extends React.Component {
+ this.makeBackdrop(),
+ containerView));
}
- return (React.createElement(Modal, Object.assign({ transparent: true, animationType: 'none', visible: this.state.isVisible, onRequestClose: onBackButtonPress }, otherProps),
+- return (React.createElement(Modal, Object.assign({ transparent: true, animationType: 'none', visible: this.state.isVisible, onRequestClose: onBackButtonPress }, otherProps),
++ return (React.createElement(Modal, Object.assign({ transparent: true, animationType: 'none', visible: this.state.isVisible, onRequestClose: onBackButtonPress, onDismiss: () => {onDismiss();if (Platform.OS === 'ios'){this.props.onModalHide();}} }, otherProps),
this.makeBackdrop(),
- avoidKeyboard ? (React.createElement(KeyboardAvoidingView, { behavior: Platform.OS === 'ios' ? 'padding' : undefined, pointerEvents: "box-none", style: computedStyle.concat([{ margin: 0 }]) }, containerView)) : (containerView)));
+ avoidKeyboard ? (React.createElement(KeyboardAvoidingView, { behavior: 'padding', pointerEvents: "box-none", style: computedStyle.concat([{ margin: 0 }]) }, containerView)) : (containerView)));
diff --git a/patches/react-native-pager-view+6.4.1.patch b/patches/react-native-pager-view+6.4.1.patch
new file mode 100644
index 000000000000..64b2b580ecd3
--- /dev/null
+++ b/patches/react-native-pager-view+6.4.1.patch
@@ -0,0 +1,73 @@
+--- a/node_modules/react-native-pager-view/ios/Fabric/RNCPagerViewComponentView.mm
++++ b/node_modules/react-native-pager-view/ios/Fabric/RNCPagerViewComponentView.mm
+@@ -195,13 +195,10 @@ -(void)scrollViewDidScroll:(UIScrollView *)scrollView {
+
+ strongEventEmitter.onPageScroll(RNCViewPagerEventEmitter::OnPageScroll{.position = static_cast(position), .offset = offset});
+
+- //This is temporary workaround to allow animations based on onPageScroll event
+- //until Fabric implements proper NativeAnimationDriver
+- RCTBridge *bridge = [RCTBridge currentBridge];
+-
+- if (bridge) {
+- [bridge.eventDispatcher sendEvent:[[RCTOnPageScrollEvent alloc] initWithReactTag:[NSNumber numberWithInt:self.tag] position:@(position) offset:@(offset)]];
+- }
++ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[[RCTOnPageScrollEvent alloc] initWithReactTag:[NSNumber numberWithInt:self.tag] position:@(position) offset:@(offset)], @"event", nil];
++ [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED"
++ object:nil
++ userInfo:userInfo];
+ }
+
+ #pragma mark - Internal methods
+diff --git a/node_modules/react-native-pager-view/ios/LEGACY/Fabric/LEGACY_RNCPagerViewComponentView.mm b/node_modules/react-native-pager-view/ios/LEGACY/Fabric/LEGACY_RNCPagerViewComponentView.mm
+index 7608645..84f6f60 100644
+--- a/node_modules/react-native-pager-view/ios/LEGACY/Fabric/LEGACY_RNCPagerViewComponentView.mm
++++ b/node_modules/react-native-pager-view/ios/LEGACY/Fabric/LEGACY_RNCPagerViewComponentView.mm
+@@ -363,14 +363,10 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
+ int eventPosition = (int) position;
+ strongEventEmitter.onPageScroll(LEGACY_RNCViewPagerEventEmitter::OnPageScroll{.position = static_cast(eventPosition), .offset = interpolatedOffset});
+
+- //This is temporary workaround to allow animations based on onPageScroll event
+- //until Fabric implements proper NativeAnimationDriver
+- RCTBridge *bridge = [RCTBridge currentBridge];
+-
+- if (bridge) {
+- [bridge.eventDispatcher sendEvent:[[RCTOnPageScrollEvent alloc] initWithReactTag:[NSNumber numberWithInt:self.tag] position:@(position) offset:@(interpolatedOffset)]];
+- }
+-
++ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[[RCTOnPageScrollEvent alloc] initWithReactTag:[NSNumber numberWithInt:self.tag] position:@(position) offset:@(interpolatedOffset)], @"event", nil];
++ [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED"
++ object:nil
++ userInfo:userInfo];
+ }
+
+
+diff --git a/node_modules/react-native-pager-view/ios/LEGACY/LEGACY_RNCPagerView.m b/node_modules/react-native-pager-view/ios/LEGACY/LEGACY_RNCPagerView.m
+index 5f6c535..fd6c2a1 100644
+--- a/node_modules/react-native-pager-view/ios/LEGACY/LEGACY_RNCPagerView.m
++++ b/node_modules/react-native-pager-view/ios/LEGACY/LEGACY_RNCPagerView.m
+@@ -1,5 +1,5 @@
+ #import "LEGACY_RNCPagerView.h"
+-#import "React/RCTLog.h"
++#import
+ #import
+
+ #import "UIViewController+CreateExtension.h"
+diff --git a/node_modules/react-native-pager-view/ios/RNCPagerView.m b/node_modules/react-native-pager-view/ios/RNCPagerView.m
+index 584aada..978496f 100644
+--- a/node_modules/react-native-pager-view/ios/RNCPagerView.m
++++ b/node_modules/react-native-pager-view/ios/RNCPagerView.m
+@@ -1,12 +1,12 @@
+
+ #import "RNCPagerView.h"
+-#import "React/RCTLog.h"
++#import
+ #import
+
+ #import "UIViewController+CreateExtension.h"
+ #import "RCTOnPageScrollEvent.h"
+ #import "RCTOnPageScrollStateChanged.h"
+-#import "React/RCTUIManagerObserverCoordinator.h"
++#import
+ #import "RCTOnPageSelected.h"
+ #import
+
diff --git a/patches/react-native-performance+5.1.0+001+bridgeless.patch b/patches/react-native-performance+5.1.0+001+bridgeless.patch
new file mode 100644
index 000000000000..7aed8cf57487
--- /dev/null
+++ b/patches/react-native-performance+5.1.0+001+bridgeless.patch
@@ -0,0 +1,30 @@
+diff --git a/node_modules/react-native-performance/android/src/main/java/com/oblador/performance/PerformanceModule.java b/node_modules/react-native-performance/android/src/main/java/com/oblador/performance/PerformanceModule.java
+index 2fa7d5d..10e1ba6 100644
+--- a/node_modules/react-native-performance/android/src/main/java/com/oblador/performance/PerformanceModule.java
++++ b/node_modules/react-native-performance/android/src/main/java/com/oblador/performance/PerformanceModule.java
+@@ -17,7 +17,7 @@ import java.util.Queue;
+ import java.util.concurrent.ConcurrentLinkedQueue;
+
+ // Should extend NativeRNPerformanceManagerSpec when codegen for old architecture is solved
+-public class PerformanceModule extends ReactContextBaseJavaModule implements TurboModule, RNPerformance.MarkerListener {
++public class PerformanceModule extends NativeRNPerformanceManagerSpec implements RNPerformance.MarkerListener {
+ public static final String PERFORMANCE_MODULE = "RNPerformanceManager";
+ public static final String BRIDGE_SETUP_START = "bridgeSetupStart";
+
+@@ -118,6 +118,16 @@ public class PerformanceModule extends ReactContextBaseJavaModule implements Tur
+ return PERFORMANCE_MODULE;
+ }
+
++ @Override
++ public void addListener(String eventName) {
++ // needed for spec
++ }
++
++ @Override
++ public void removeListeners(double count) {
++ // needed for spec
++ }
++
+ private void emitNativeStartupTime() {
+ safelyEmitMark(new PerformanceMark("nativeLaunchStart", StartTimeProvider.getStartTime()));
+ safelyEmitMark(new PerformanceMark("nativeLaunchEnd", StartTimeProvider.getEndTime()));
diff --git a/patches/react-native-quick-sqlite+8.1.0+001+bridgeless.patch b/patches/react-native-quick-sqlite+8.1.0+001+bridgeless.patch
new file mode 100644
index 000000000000..8f8a13d684e5
--- /dev/null
+++ b/patches/react-native-quick-sqlite+8.1.0+001+bridgeless.patch
@@ -0,0 +1,41 @@
+diff --git a/node_modules/react-native-quick-sqlite/ios/QuickSQLite.mm b/node_modules/react-native-quick-sqlite/ios/QuickSQLite.mm
+index 519f31a..308f746 100644
+--- a/node_modules/react-native-quick-sqlite/ios/QuickSQLite.mm
++++ b/node_modules/react-native-quick-sqlite/ios/QuickSQLite.mm
+@@ -12,12 +12,12 @@ @implementation QuickSQLite
+
+ RCT_EXPORT_MODULE(QuickSQLite)
+
++@synthesize bridge = _bridge;
+
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
+ NSLog(@"Installing QuickSQLite module...");
+
+- RCTBridge *bridge = [RCTBridge currentBridge];
+- RCTCxxBridge *cxxBridge = (RCTCxxBridge *)bridge;
++ RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
+ if (cxxBridge == nil) {
+ return @false;
+ }
+@@ -29,7 +29,7 @@ @implementation QuickSQLite
+ return @false;
+ }
+ auto &runtime = *jsiRuntime;
+- auto callInvoker = bridge.jsCallInvoker;
++ auto callInvoker = cxxBridge.jsCallInvoker;
+
+ // Get appGroupID value from Info.plist using key "AppGroup"
+ NSString *appGroupID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"ReactNativeQuickSQLite_AppGroup"];
+diff --git a/node_modules/react-native-quick-sqlite/src/index.ts b/node_modules/react-native-quick-sqlite/src/index.ts
+index b3e7fc7..7d8930a 100644
+--- a/node_modules/react-native-quick-sqlite/src/index.ts
++++ b/node_modules/react-native-quick-sqlite/src/index.ts
+@@ -15,7 +15,7 @@ if (global.__QuickSQLiteProxy == null) {
+ }
+
+ // Check if we are running on-device (JSI)
+- if (global.nativeCallSyncHook == null || QuickSQLiteModule.install == null) {
++ if ((!global.nativeCallSyncHook && !global.RN$Bridgeless) || QuickSQLiteModule.install == null) {
+ throw new Error(
+ 'Failed to install react-native-quick-sqlite: React Native is not running on-device. QuickSQLite can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.'
+ );
diff --git a/patches/react-native-vision-camera+4.0.0-beta.13+001+rn75-compatibility.patch b/patches/react-native-vision-camera+4.0.0-beta.13+001+rn75-compatibility.patch
index 4e0961ec536a..7c585ddf9f27 100644
--- a/patches/react-native-vision-camera+4.0.0-beta.13+001+rn75-compatibility.patch
+++ b/patches/react-native-vision-camera+4.0.0-beta.13+001+rn75-compatibility.patch
@@ -729,10 +729,10 @@ index 25e1f55..33b9dd3 100644
+ }
}
diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/CameraViewManager.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/CameraViewManager.kt
-index f2b284c..e348e5c 100644
+index f2b284c..4bb2ebc 100644
--- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/CameraViewManager.kt
+++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/CameraViewManager.kt
-@@ -4,7 +4,10 @@ import com.facebook.react.bridge.ReadableMap
+@@ -4,8 +4,18 @@ import com.facebook.react.bridge.ReadableMap
import com.facebook.react.common.MapBuilder
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
@@ -740,10 +740,18 @@ index f2b284c..e348e5c 100644
import com.facebook.react.uimanager.annotations.ReactProp
+import com.facebook.react.viewmanagers.CameraViewManagerDelegate
+import com.facebook.react.viewmanagers.CameraViewManagerInterface
++import com.mrousavy.camera.types.CameraCodeScannedEvent
import com.mrousavy.camera.types.CameraDeviceFormat
++import com.mrousavy.camera.types.CameraErrorEvent
++import com.mrousavy.camera.types.CameraInitializedEvent
++import com.mrousavy.camera.types.CameraShutterEvent
++import com.mrousavy.camera.types.CameraStartedEvent
++import com.mrousavy.camera.types.CameraStoppedEvent
++import com.mrousavy.camera.types.CameraViewReadyEvent
import com.mrousavy.camera.types.CodeScannerOptions
import com.mrousavy.camera.types.Orientation
-@@ -16,10 +19,19 @@ import com.mrousavy.camera.types.Torch
+ import com.mrousavy.camera.types.PixelFormat
+@@ -16,10 +26,19 @@ import com.mrousavy.camera.types.Torch
import com.mrousavy.camera.types.VideoStabilizationMode
@Suppress("unused")
@@ -764,7 +772,28 @@ index f2b284c..e348e5c 100644
public override fun createViewInstance(context: ThemedReactContext): CameraView = CameraView(context)
override fun onAfterUpdateTransaction(view: CameraView) {
-@@ -46,37 +58,37 @@ class CameraViewManager : ViewGroupManager() {
+@@ -29,13 +48,13 @@ class CameraViewManager : ViewGroupManager() {
+
+ override fun getExportedCustomDirectEventTypeConstants(): MutableMap? =
+ MapBuilder.builder()
+- .put("cameraViewReady", MapBuilder.of("registrationName", "onViewReady"))
+- .put("cameraInitialized", MapBuilder.of("registrationName", "onInitialized"))
+- .put("cameraStarted", MapBuilder.of("registrationName", "onStarted"))
+- .put("cameraStopped", MapBuilder.of("registrationName", "onStopped"))
+- .put("cameraShutter", MapBuilder.of("registrationName", "onShutter"))
+- .put("cameraError", MapBuilder.of("registrationName", "onError"))
+- .put("cameraCodeScanned", MapBuilder.of("registrationName", "onCodeScanned"))
++ .put(CameraViewReadyEvent.EVENT_NAME, MapBuilder.of("registrationName", "onViewReady"))
++ .put(CameraInitializedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onInitialized"))
++ .put(CameraStartedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onStarted"))
++ .put(CameraStoppedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onStopped"))
++ .put(CameraShutterEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShutter"))
++ .put(CameraErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onError"))
++ .put(CameraCodeScannedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onCodeScanned"))
+ .build()
+
+ override fun getName(): String = TAG
+@@ -46,37 +65,37 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "cameraId")
@@ -809,7 +838,7 @@ index f2b284c..e348e5c 100644
if (pixelFormat != null) {
val newPixelFormat = PixelFormat.fromUnionValue(pixelFormat)
view.pixelFormat = newPixelFormat
-@@ -86,27 +98,27 @@ class CameraViewManager : ViewGroupManager() {
+@@ -86,27 +105,27 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "enableDepthData")
@@ -842,7 +871,7 @@ index f2b284c..e348e5c 100644
if (videoStabilizationMode != null) {
val newMode = VideoStabilizationMode.fromUnionValue(videoStabilizationMode)
view.videoStabilizationMode = newMode
-@@ -116,12 +128,12 @@ class CameraViewManager : ViewGroupManager() {
+@@ -116,12 +135,12 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "enablePortraitEffectsMatteDelivery")
@@ -857,7 +886,7 @@ index f2b284c..e348e5c 100644
if (format != null) {
val newFormat = CameraDeviceFormat.fromJSValue(format)
view.format = newFormat
-@@ -131,7 +143,7 @@ class CameraViewManager : ViewGroupManager() {
+@@ -131,7 +150,7 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "resizeMode")
@@ -866,7 +895,7 @@ index f2b284c..e348e5c 100644
if (resizeMode != null) {
val newMode = ResizeMode.fromUnionValue(resizeMode)
view.resizeMode = newMode
-@@ -141,7 +153,7 @@ class CameraViewManager : ViewGroupManager() {
+@@ -141,7 +160,7 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "androidPreviewViewType")
@@ -875,7 +904,7 @@ index f2b284c..e348e5c 100644
if (androidPreviewViewType != null) {
val newMode = PreviewViewType.fromUnionValue(androidPreviewViewType)
view.androidPreviewViewType = newMode
-@@ -154,17 +166,17 @@ class CameraViewManager : ViewGroupManager() {
+@@ -154,17 +173,17 @@ class CameraViewManager : ViewGroupManager() {
// We're treating -1 as "null" here, because when I make the fps parameter
// of type "Int?" the react bridge throws an error.
@ReactProp(name = "fps", defaultInt = -1)
@@ -896,7 +925,7 @@ index f2b284c..e348e5c 100644
if (photoQualityBalance != null) {
val newMode = QualityBalance.fromUnionValue(photoQualityBalance)
view.photoQualityBalance = newMode
-@@ -174,22 +186,22 @@ class CameraViewManager : ViewGroupManager() {
+@@ -174,22 +193,22 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "videoHdr")
@@ -923,7 +952,7 @@ index f2b284c..e348e5c 100644
if (torch != null) {
val newMode = Torch.fromUnionValue(torch)
view.torch = newMode
-@@ -199,17 +211,17 @@ class CameraViewManager : ViewGroupManager() {
+@@ -199,17 +218,17 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "zoom")
@@ -944,7 +973,7 @@ index f2b284c..e348e5c 100644
if (orientation != null) {
val newMode = Orientation.fromUnionValue(orientation)
view.orientation = newMode
-@@ -219,7 +231,7 @@ class CameraViewManager : ViewGroupManager() {
+@@ -219,7 +238,7 @@ class CameraViewManager : ViewGroupManager() {
}
@ReactProp(name = "codeScannerOptions")
@@ -953,7 +982,7 @@ index f2b284c..e348e5c 100644
if (codeScannerOptions != null) {
val newCodeScannerOptions = CodeScannerOptions.fromJSValue(codeScannerOptions)
view.codeScannerOptions = newCodeScannerOptions
-@@ -227,4 +239,8 @@ class CameraViewManager : ViewGroupManager() {
+@@ -227,4 +246,8 @@ class CameraViewManager : ViewGroupManager() {
view.codeScannerOptions = null
}
}
@@ -981,6 +1010,79 @@ index b9d3f67..cb70963 100644
@Suppress("KotlinJniMissingFunction") // we use fbjni.
class VisionCameraProxy(private val reactContext: ReactApplicationContext) {
companion object {
+diff --git a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/types/Events.kt b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/types/Events.kt
+index 1ed0355..b8ff7cf 100644
+--- a/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/types/Events.kt
++++ b/node_modules/react-native-vision-camera/android/src/main/java/com/mrousavy/camera/types/Events.kt
+@@ -3,39 +3,61 @@ package com.mrousavy.camera.types
+ import com.facebook.react.bridge.Arguments
+ import com.facebook.react.bridge.WritableMap
+ import com.facebook.react.uimanager.events.Event
++import com.mrousavy.camera.types.CameraInitializedEvent.Companion.EVENT_NAME
+
+ class CameraInitializedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraInitialized"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData(): WritableMap = Arguments.createMap()
++ companion object {
++ const val EVENT_NAME = "topInitialized"
++ }
+ }
+
+ class CameraStartedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraStarted"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData(): WritableMap = Arguments.createMap()
++ companion object {
++ const val EVENT_NAME = "topStarted"
++ }
+ }
+
+ class CameraStoppedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraStopped"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData(): WritableMap = Arguments.createMap()
++ companion object {
++ const val EVENT_NAME = "topStopped"
++ }
+ }
+
+ class CameraShutterEvent(surfaceId: Int, viewId: Int, private val data: WritableMap) : Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraShutter"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData() = data
++ companion object {
++ const val EVENT_NAME = "topShutter"
++ }
+ }
+
+ class CameraErrorEvent(surfaceId: Int, viewId: Int, private val data: WritableMap) : Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraError"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData() = data
++ companion object {
++ const val EVENT_NAME = "topError"
++ }
+ }
+
+ class CameraViewReadyEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraViewReady"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData(): WritableMap = Arguments.createMap()
++ companion object {
++ const val EVENT_NAME = "topViewReady"
++ }
+ }
+
+ class CameraCodeScannedEvent(surfaceId: Int, viewId: Int, private val data: WritableMap) :
+ Event(surfaceId, viewId) {
+- override fun getEventName() = "cameraCodeScanned"
++ override fun getEventName() = EVENT_NAME
+ override fun getEventData() = data
++ companion object {
++ const val EVENT_NAME = "topCodeScanned"
++ }
+ }
diff --git a/node_modules/react-native-vision-camera/ios/.swift-version b/node_modules/react-native-vision-camera/ios/.swift-version
new file mode 100644
index 0000000..ef425ca
diff --git a/src/CONST.ts b/src/CONST.ts
index 440f942e1244..a3df980ff5dd 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -297,6 +297,9 @@ const CONST = {
// Regex to get link in href prop inside of component
REGEX_LINK_IN_ANCHOR: /]*?\s+)?href="([^"]*)"/gi,
+ // Regex to read violation value from string given by backend
+ VIOLATION_LIMIT_REGEX: /[^0-9]+/g,
+
MERCHANT_NAME_MAX_LENGTH: 255,
MASKED_PAN_PREFIX: 'XXXXXXXXXXXX',
@@ -1143,6 +1146,9 @@ const CONST = {
SEARCH_OPTION_LIST_DEBOUNCE_TIME: 300,
RESIZE_DEBOUNCE_TIME: 100,
UNREAD_UPDATE_DEBOUNCE_TIME: 300,
+ SEARCH_CONVERT_SEARCH_VALUES: 'search_convert_search_values',
+ SEARCH_MAKE_TREE: 'search_make_tree',
+ SEARCH_BUILD_TREE: 'search_build_tree',
SEARCH_FILTER_OPTIONS: 'search_filter_options',
USE_DEBOUNCED_STATE_DELAY: 300,
},
@@ -2599,6 +2605,7 @@ const CONST = {
MONTHLY: 'monthly',
},
CARD_TITLE_INPUT_LIMIT: 255,
+ MANAGE_EXPENSIFY_CARDS_ARTICLE_LINK: 'https://help.expensify.com/articles/new-expensify/expensify-card/Manage-Expensify-Cards',
},
COMPANY_CARDS: {
CONNECTION_ERROR: 'connectionError',
@@ -2635,6 +2642,7 @@ const CONST = {
},
BANK_CONNECTIONS: {
WELLS_FARGO: 'wellsfargo',
+ BANK_OF_AMERICA: 'bankofamerica',
CHASE: 'chase',
BREX: 'brex',
CAPITAL_ONE: 'capitalone',
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index d083a46d7760..427e05052ae3 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -3,6 +3,7 @@ import type CONST from './CONST';
import type {OnboardingCompanySizeType, OnboardingPurposeType} from './CONST';
import type * as FormTypes from './types/form';
import type * as OnyxTypes from './types/onyx';
+import type {Attendee} from './types/onyx/IOU';
import type Onboarding from './types/onyx/Onboarding';
import type AssertTypesEqual from './types/utils/AssertTypesEqual';
import type DeepValueOf from './types/utils/DeepValueOf';
@@ -112,6 +113,9 @@ const ONYXKEYS = {
/** Boolean flag only true when first set */
NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER: 'nvp_isFirstTimeNewExpensifyUser',
+ /** This NVP contains list of at most 5 recent attendees */
+ NVP_RECENT_ATTENDEES: 'nvp_expensify_recentAttendees',
+
/** This NVP contains information about whether the onboarding flow was completed or not */
NVP_ONBOARDING: 'nvp_onboarding',
@@ -905,6 +909,7 @@ type OnyxValuesMapping = {
// The value of this nvp is a string representation of the date when the block expires, or an empty string if the user is not blocked
[ONYXKEYS.NVP_BLOCKED_FROM_CHAT]: string;
[ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string;
+ [ONYXKEYS.NVP_RECENT_ATTENDEES]: Attendee[];
[ONYXKEYS.NVP_TRY_FOCUS_MODE]: boolean;
[ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION]: boolean;
[ONYXKEYS.FOCUS_MODE_NOTIFICATION]: boolean;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 23ef269d8cfb..e6984d6c757b 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -447,6 +447,11 @@ const ROUTES = {
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
getUrlWithBackToParam(`${action as string}/${iouType as string}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
+ MONEY_REQUEST_ATTENDEE: {
+ route: ':action/:iouType/attendees/:transactionID/:reportID',
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/attendees/${transactionID}/${reportID}`, backTo),
+ },
SETTINGS_TAGS_ROOT: {
route: 'settings/:policyID/tags',
getRoute: (policyID: string, backTo = '') => getUrlWithBackToParam(`settings/${policyID}/tags`, backTo),
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 3a33975b2bbe..7f04b861dec5 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -219,6 +219,7 @@ const SCREENS = {
EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint',
RECEIPT: 'Money_Request_Receipt',
STATE_SELECTOR: 'Money_Request_State_Selector',
+ STEP_ATTENDEES: 'Money_Request_Attendee',
},
TRANSACTION_DUPLICATE: {
diff --git a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx
index 443a553d4689..ae74a11c7e9d 100644
--- a/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx
+++ b/src/components/AnchorForAttachmentsOnly/BaseAnchorForAttachmentsOnly.tsx
@@ -22,7 +22,7 @@ type BaseAnchorForAttachmentsOnlyProps = AnchorForAttachmentsOnlyProps & {
onPressOut?: () => void;
};
-function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', onPressIn, onPressOut}: BaseAnchorForAttachmentsOnlyProps) {
+function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', onPressIn, onPressOut, isDeleted}: BaseAnchorForAttachmentsOnlyProps) {
const sourceURLWithAuth = addEncryptedAuthTokenToURL(source);
const sourceID = (source.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1];
@@ -63,6 +63,7 @@ function BaseAnchorForAttachmentsOnly({style, source = '', displayName = '', onP
shouldShowDownloadIcon={!!sourceID && !isOffline}
shouldShowLoadingSpinnerIcon={isDownloading}
isUsedAsChatAttachment
+ isDeleted={!!isDeleted}
isUploading={!sourceID}
/>
diff --git a/src/components/AnchorForAttachmentsOnly/types.ts b/src/components/AnchorForAttachmentsOnly/types.ts
index a5186d8c0d90..67a5bb532c27 100644
--- a/src/components/AnchorForAttachmentsOnly/types.ts
+++ b/src/components/AnchorForAttachmentsOnly/types.ts
@@ -9,6 +9,9 @@ type AnchorForAttachmentsOnlyProps = {
/** Any additional styles to apply */
style?: StyleProp;
+
+ /** Whether the attachment is deleted */
+ isDeleted?: boolean;
};
export default AnchorForAttachmentsOnlyProps;
diff --git a/src/components/AttachmentDeletedIndicator.tsx b/src/components/AttachmentDeletedIndicator.tsx
new file mode 100644
index 000000000000..06e700c2fd73
--- /dev/null
+++ b/src/components/AttachmentDeletedIndicator.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import {View} from 'react-native';
+import type {StyleProp, ViewStyle} from 'react-native';
+import useNetwork from '@hooks/useNetwork';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+import variables from '@styles/variables';
+import Icon from './Icon';
+import * as Expensicons from './Icon/Expensicons';
+
+type AttachmentDeletedIndicatorProps = {
+ /** Additional styles for container */
+ containerStyles?: StyleProp;
+};
+
+function AttachmentDeletedIndicator({containerStyles}: AttachmentDeletedIndicatorProps) {
+ const theme = useTheme();
+ const styles = useThemeStyles();
+ const {isOffline} = useNetwork();
+
+ if (!isOffline) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+}
+
+AttachmentDeletedIndicator.displayName = 'AttachmentDeletedIndicator';
+
+export default AttachmentDeletedIndicator;
diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx
index 1e3cded92bd5..c6e7984b793f 100644
--- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx
+++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx
@@ -32,7 +32,7 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) {
const Pan = Gesture.Pan()
.manualActivation(true)
.onTouchesMove((evt) => {
- if (offsetX.value !== 0 && offsetY.value !== 0 && isScrollEnabled) {
+ if (offsetX.value !== 0 && offsetY.value !== 0 && isScrollEnabled && scale.value === 1) {
const translateX = Math.abs((evt.allTouches.at(0)?.absoluteX ?? 0) - offsetX.value);
const translateY = Math.abs((evt.allTouches.at(0)?.absoluteY ?? 0) - offsetY.value);
const allowEnablingScroll = !isPanGestureActive.value || isScrollEnabled.value;
@@ -40,7 +40,7 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) {
// if the value of X is greater than Y and the pdf is not zoomed in,
// enable the pager scroll so that the user
// can swipe to the next attachment otherwise disable it.
- if (translateX > translateY && translateX > SCROLL_THRESHOLD && scale.value === 1 && allowEnablingScroll) {
+ if (translateX > translateY && translateX > SCROLL_THRESHOLD && allowEnablingScroll) {
// eslint-disable-next-line react-compiler/react-compiler
isScrollEnabled.value = true;
} else if (translateY > SCROLL_THRESHOLD) {
@@ -57,7 +57,7 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) {
if (!isScrollEnabled) {
return;
}
- isScrollEnabled.value = true;
+ isScrollEnabled.value = scale.value === 1;
});
const Content = useMemo(
diff --git a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx
index e6ac9f9f21c7..23e13833df64 100644
--- a/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx
+++ b/src/components/Attachments/AttachmentView/DefaultAttachmentView/index.tsx
@@ -25,11 +25,14 @@ type DefaultAttachmentViewProps = {
icon?: IconAsset;
+ /** Whether the attachment is deleted */
+ isDeleted?: boolean;
+
/** Flag indicating if the attachment is being uploaded. */
isUploading?: boolean;
};
-function DefaultAttachmentView({fileName = '', shouldShowLoadingSpinnerIcon = false, shouldShowDownloadIcon, containerStyles, icon, isUploading}: DefaultAttachmentViewProps) {
+function DefaultAttachmentView({fileName = '', shouldShowLoadingSpinnerIcon = false, shouldShowDownloadIcon, containerStyles, icon, isUploading, isDeleted}: DefaultAttachmentViewProps) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -43,7 +46,7 @@ function DefaultAttachmentView({fileName = '', shouldShowLoadingSpinnerIcon = fa
/>
- {fileName}
+ {fileName}
{!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && (
diff --git a/src/components/Attachments/AttachmentView/index.tsx b/src/components/Attachments/AttachmentView/index.tsx
index 080e0ec589ec..0af1a86992e7 100644
--- a/src/components/Attachments/AttachmentView/index.tsx
+++ b/src/components/Attachments/AttachmentView/index.tsx
@@ -72,6 +72,9 @@ type AttachmentViewProps = Attachment & {
/* Flag indicating whether the attachment has been uploaded. */
isUploaded?: boolean;
+ /** Whether the attachment is deleted */
+ isDeleted?: boolean;
+
/** Flag indicating if the attachment is being uploaded. */
isUploading?: boolean;
};
@@ -98,14 +101,14 @@ function AttachmentView({
duration,
isUsedAsChatAttachment,
isUploaded = true,
+ isDeleted,
isUploading = false,
}: AttachmentViewProps) {
+ const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`);
const {translate} = useLocalize();
const {updateCurrentlyPlayingURL} = usePlaybackContext();
const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext);
- const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`);
-
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
@@ -292,6 +295,7 @@ function AttachmentView({
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
shouldShowLoadingSpinnerIcon={shouldShowLoadingSpinnerIcon || isUploading}
containerStyles={containerStyles}
+ isDeleted={isDeleted}
isUploading={isUploading}
/>
);
diff --git a/src/components/ConnectBankAccountButton.tsx b/src/components/ConnectBankAccountButton.tsx
deleted file mode 100644
index ee6ad04d727e..000000000000
--- a/src/components/ConnectBankAccountButton.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import type {StyleProp, ViewStyle} from 'react-native';
-import {View} from 'react-native';
-import useLocalize from '@hooks/useLocalize';
-import useNetwork from '@hooks/useNetwork';
-import Navigation from '@libs/Navigation/Navigation';
-import * as ReimbursementAccount from '@userActions/ReimbursementAccount';
-import Button from './Button';
-import * as Expensicons from './Icon/Expensicons';
-import Text from './Text';
-
-type ConnectBankAccountButtonProps = {
- /** PolicyID for navigating to bank account route of that policy */
- policyID: string;
-
- /** Button styles, also applied for offline message wrapper */
- style?: StyleProp;
-};
-
-function ConnectBankAccountButton({style, policyID}: ConnectBankAccountButtonProps) {
- const {isOffline} = useNetwork();
- const {translate} = useLocalize();
- const activeRoute = Navigation.getActiveRouteWithoutParams();
-
- return isOffline ? (
-
- {`${translate('common.youAppearToBeOffline')} ${translate('common.thisFeatureRequiresInternet')}`}
-
- ) : (
-