Skip to content

Commit e3a06e7

Browse files
committed
Merge branch 'main' into refactor/remove-connect-muted-platform
2 parents d88e933 + bb3a5ba commit e3a06e7

179 files changed

Lines changed: 5189 additions & 1177 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deployBlocker.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ jobs:
99
updateChecklist:
1010
if: github.event.label.name == 'DeployBlockerCash'
1111
uses: ./.github/workflows/createDeployChecklist.yml
12+
secrets: inherit
1213

1314
deployBlocker:
1415
if: github.event.label.name == 'DeployBlockerCash'

Mobile-Expensify

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ android {
114114
minSdkVersion rootProject.ext.minSdkVersion
115115
targetSdkVersion rootProject.ext.targetSdkVersion
116116
multiDexEnabled rootProject.ext.multiDexEnabled
117-
versionCode 1009019300
118-
versionName "9.1.93-0"
117+
versionCode 1009019303
118+
versionName "9.1.93-3"
119119
// Supported language variants must be declared here to avoid from being removed during the compilation.
120120
// This also helps us to not include unnecessary language variants in the APK.
121121
resConfigs "en", "es"

android/app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@
155155
</intent-filter>
156156
</activity>
157157

158+
<activity
159+
android:name="com.plaid.internal.link.LinkActivity"
160+
android:launchMode="singleTask"
161+
tools:replace="android:launchMode"/>
162+
158163
<meta-data
159164
android:name="com.urbanairship.reactnative.AIRSHIP_EXTENDER"
160165
android:value="com.expensify.chat.customairshipextender.CustomAirshipExtender" />

android/app/src/main/java/com/expensify/chat/AppStateTrackerModule.java

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.expensify.chat
2+
3+
import com.facebook.react.bridge.Promise
4+
import com.facebook.react.bridge.ReactApplicationContext
5+
import com.facebook.react.bridge.ReactContextBaseJavaModule
6+
import com.facebook.react.bridge.ReactMethod
7+
8+
import com.expensify.chat.MainActivity
9+
10+
/**
11+
* Checks whether the app was relaunched from the app icon in app drawer, not from recent apps.
12+
*/
13+
class AppStateTrackerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(
14+
reactContext
15+
) {
16+
override fun getName(): String = "AppStateTracker"
17+
18+
@ReactMethod
19+
fun getWasAppRelaunchedFromIcon(promise: Promise) {
20+
val activity = currentActivity
21+
if (activity is MainActivity) {
22+
promise.resolve(activity.wasAppRelaunchedFromIcon)
23+
activity.wasAppRelaunchedFromIcon = false
24+
} else {
25+
promise.resolve(false)
26+
}
27+
}
28+
}

android/app/src/main/java/com/expensify/chat/MainActivity.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class MainActivity : ReactActivity() {
2525
*/
2626
override fun getMainComponentName() = "NewExpensify"
2727

28+
var wasAppRelaunchedFromIcon: Boolean = false
29+
2830
/**
2931
* Returns the instance of the [ReactActivityDelegate]. Here we use a util class [ ] which allows you to easily enable Fabric and Concurrent React
3032
* (aka React 18) with two boolean flags.
@@ -62,6 +64,13 @@ class MainActivity : ReactActivity() {
6264

6365
override fun onNewIntent(intent: Intent) {
6466
super.onNewIntent(intent)
67+
68+
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER)
69+
&& intent.getAction() != null
70+
&& intent.getAction().equals(Intent.ACTION_MAIN)) {
71+
wasAppRelaunchedFromIcon = true
72+
}
73+
6574
setIntent(intent) // Must store the new intent unless getIntent() will return the old one
6675
handleIntent(intent)
6776
}
@@ -108,6 +117,6 @@ class MainActivity : ReactActivity() {
108117

109118
override fun onStart() {
110119
super.onStart()
111-
RNPerformance.getInstance().mark("appCreationEnd", false);
120+
RNPerformance.getInstance().mark("appCreationEnd", false)
112121
}
113122
}

android/app/src/main/java/com/expensify/chat/MainApplication.kt

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
package com.expensify.chat
22

3-
import android.app.Activity
43
import com.facebook.react.common.assets.ReactFontManager
54

65
import android.app.ActivityManager
7-
import android.app.ActivityManager.RunningAppProcessInfo
8-
import android.content.Context
96
import android.content.res.Configuration
107
import android.database.CursorWindow
11-
import android.os.Bundle
128
import android.os.Process
139
import androidx.multidex.MultiDexApplication
1410
import com.expensify.chat.bootsplash.BootSplashPackage
@@ -31,15 +27,10 @@ import expo.modules.ApplicationLifecycleDispatcher
3127
import expo.modules.ReactNativeHostWrapper
3228

3329
class MainApplication : MultiDexApplication(), ReactApplication {
34-
var currentState: String = "active"
35-
private set
36-
var prevState: String = "inactive"
37-
private set
38-
3930
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(this, object : DefaultReactNativeHost(this) {
4031
override fun getUseDeveloperSupport() = BuildConfig.DEBUG
4132

42-
override fun getPackages(): List<ReactPackage> =
33+
override fun getPackages(): List<ReactPackage> =
4334
PackageList(this).packages.apply {
4435
// Packages that cannot be autolinked yet can be added manually here, for example:
4536
// add(MyReactNativePackage());
@@ -73,30 +64,6 @@ class MainApplication : MultiDexApplication(), ReactApplication {
7364
return
7465
}
7566

76-
registerActivityLifecycleCallbacks(object: ActivityLifecycleCallbacks {
77-
override fun onActivityStarted(p0: Activity) {
78-
prevState = currentState
79-
currentState = "active"
80-
}
81-
82-
override fun onActivityStopped(p0: Activity) {
83-
val isOnForeground = isAppOnForeground()
84-
prevState = currentState
85-
currentState = if (isOnForeground) "active" else "background"
86-
}
87-
88-
override fun onActivityDestroyed(p0: Activity) {
89-
val isOnForeground = isAppOnForeground()
90-
prevState = currentState
91-
currentState = if (isOnForeground) "active" else "background"
92-
}
93-
94-
override fun onActivityCreated(p0: Activity, p1: Bundle?) {}
95-
override fun onActivityResumed(p0: Activity) {}
96-
override fun onActivityPaused(p0: Activity) {}
97-
override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}
98-
})
99-
10067
SoLoader.init(this, OpenSourceMergedSoMapping)
10168
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
10269
// If you opted-in for the New Architecture, we load the native entry point for this app.
@@ -139,22 +106,4 @@ class MainApplication : MultiDexApplication(), ReactApplication {
139106
it.pid == pid && it.processName.endsWith(":onfido_process")
140107
}
141108
}
142-
143-
/**
144-
* Checks if the application is currently running in the foreground.
145-
* https://stackoverflow.com/a/8490088/8398300
146-
*/
147-
private fun isAppOnForeground(): Boolean {
148-
val activityManager = applicationContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
149-
val appProcesses = activityManager.runningAppProcesses ?: return false
150-
val packageName: String = applicationContext.getPackageName()
151-
for (appProcess in appProcesses) {
152-
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
153-
appProcess.processName == packageName
154-
) {
155-
return true
156-
}
157-
}
158-
return false
159-
}
160109
}

babel.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ if (!process.env.ELECTRON_ENV && process.env.npm_lifecycle_event !== 'desktop')
5555
defaultPluginsForWebpack.push([
5656
'@fullstory/babel-plugin-annotate-react',
5757
{
58-
'react-native-web': true,
5958
native: true,
6059
},
6160
]);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
title: Merge Expenses
3+
description: Learn how to merge two expenses to eliminate duplicates and simplify your reports.
4+
keywords: [New Expensify, merge expenses, duplicate expenses, SmartScan, manual merge, combine expenses]
5+
---
6+
7+
<div id="new-expensify" markdown="1">
8+
9+
Merging expenses helps you quickly resolve duplicates when automatic SmartScan merges don’t catch everything. This guide walks you through manually merging two expenses from the expense view or Reports page.
10+
11+
# Merge expenses
12+
13+
You can merge two expenses if at least one is a cash expense. You cannot merge two card expenses. Merging is only available for editable expenses (such as those that are unsubmitted or awaiting first-level approval).
14+
15+
## Merging from an individual expense
16+
17+
1. In the left-hand menu, select **Reports > Expenses**
18+
2. Tap the expense you'd like to merge from.
19+
3. Tap the **More** button, then select **Merge**.
20+
4. Choose another eligible expense to merge with.
21+
- You can only select one.
22+
- Expenses are sorted by date (newest first).
23+
- If there are no eligible matches, you'll see:
24+
*"You don’t have any expenses that can be merged with this one. [Learn more](#eligible-expenses)."*
25+
5. If only one expense has a receipt, it’s selected automatically. If both do, you’ll be asked to choose.
26+
6. Select the details you want to keep from each expense—like merchant name, amount, or category.
27+
7. Tap **Continue**, then **Confirm** to complete the merge.
28+
29+
## Eligible expenses for merging
30+
31+
To merge two expenses, they must meet all of the following:
32+
33+
- **By type**:
34+
- You can merge a cash expense with another cash expense, or a cash expense with a card expense.
35+
- You cannot merge two card expenses.
36+
37+
- **By permission**:
38+
- **Submitters** can merge their own expenses (including unreported and in-progress ones).
39+
- **Approvers and Admins** can merge expenses on the same report
40+
41+
- **By status**:
42+
- Both expenses must be editable. That includes unreported expenses, or ones on a Draft or Processing report that hasn’t yet been approved.
43+
44+
## What happens when you merge?
45+
46+
- One expense is kept, and the details you choose will be used for the final version.
47+
- The other expense is **merged into the final one** and no longer shown separately.
48+
- If one expense is a card transaction, it will always be the one that’s kept.
49+
- If both are cash expenses, the one you started the merge from will be kept.
50+
51+
# FAQ
52+
53+
## Can I merge two card expenses?
54+
55+
No. Only one card expense can be part of a merge. You can merge:
56+
- Two cash expenses
57+
- One cash and one card expense
58+
59+
## What happens to the other expense?
60+
61+
It’s merged into the final expense and no longer appears separately. Its details are combined based on what you selected.
62+
63+
## What if the expenses have the same details?
64+
65+
If there are no differences to resolve, the system automatically keeps the shared values without prompting you.
66+
67+
## Why don’t I see the Merge option?
68+
69+
Check these conditions:
70+
- You must have permission to edit both expenses.
71+
- At least one expense must be a cash transaction.
72+
- The expenses must be editable (not submitted or fully approved).
73+
74+
</div>

0 commit comments

Comments
 (0)