Skip to content

Commit 39e604d

Browse files
author
themoonalsofall
committed
Merge branch 'main' into update-2FA-page
2 parents c585d10 + a50d4bd commit 39e604d

345 files changed

Lines changed: 4324 additions & 2332 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/scripts/verifyRedirect.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ while read -r line; do
5555
exit 1
5656
fi
5757

58-
if ! [[ $DEST_URL =~ ^https://(help|use|integrations)\.expensify\.com|^https://www\.expensify\.org ]]; then
58+
if ! [[ $DEST_URL =~ ^https://(www|help|use|integrations)\.expensify\.com|^https://www\.expensify\.org ]]; then
5959
error "Found destination URL that is not a supported URL: $DEST_URL"
6060
exit 1
6161
fi

.github/workflows/deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,10 @@ jobs:
595595

596596
- name: 🚀 Create prerelease 🚀
597597
run: |
598-
gh release create ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --title ${{ needs.prep.outputs.APP_VERSION }} --generate-notes --prerelease --target staging
598+
gh release create ${{ needs.prep.outputs.APP_VERSION }}-staging --repo ${{ github.repository }} --title ${{ needs.prep.outputs.APP_VERSION }} --generate-notes --prerelease --verify-tag --target staging
599599
RETRIES=0
600600
MAX_RETRIES=10
601-
until [[ $(gh release view ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }}) || $RETRIES -ge $MAX_RETRIES ]]; do
601+
until [[ $(gh release view ${{ needs.prep.outputs.APP_VERSION }}-staging --repo ${{ github.repository }}) || $RETRIES -ge $MAX_RETRIES ]]; do
602602
echo "release not found, retrying $((MAX_RETRIES - RETRIES++)) times"
603603
sleep 1
604604
done

.github/workflows/postDeployComments.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ jobs:
100100
id: getPullRequestList
101101
uses: ./.github/actions/javascript/getDeployPullRequestList
102102
with:
103-
TAG: ${{ inputs.version }}
103+
# Staging tags use the `-staging` suffix, production tags use the version only
104+
TAG: ${{ inputs.version }}${{ inputs.env == 'staging' && '-staging' || '' }}
104105
GITHUB_TOKEN: ${{ github.token }}
105106
IS_PRODUCTION_DEPLOY: ${{ inputs.env == 'production' }}
106107

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Remote Build Android
2+
3+
on:
4+
workflow_dispatch:
5+
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.ref }}
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest-xl
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
include:
16+
- project_root: ./
17+
variant: 'developmentDebug'
18+
19+
- project_root: Mobile-Expensify/
20+
is_hybrid_build: true
21+
variant: 'Debug'
22+
steps:
23+
- name: Checkout
24+
# v4
25+
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
26+
with:
27+
submodules: ${{ matrix.is_hybrid_build || false }}
28+
token: ${{ secrets.OS_BOTIFY_TOKEN }}
29+
30+
- name: Setup Node
31+
uses: ./.github/actions/composite/setupNode
32+
with:
33+
IS_HYBRID_BUILD: ${{ matrix.is_hybrid_build && 'true' || 'false' }}
34+
35+
- name: RNEF Remote Build - Android
36+
uses: callstackincubator/android@a49920aadab9f41951944ea52bb7983fa6b20b03
37+
env:
38+
PROJECT_ROOT_PATH: ${{ matrix.project_root }}
39+
with:
40+
variant: ${{ matrix.variant }}
41+
github-token: ${{ github.token }}
42+
comment-bot: false
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Remote Build iOS
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches-ignore: [staging, production]
7+
8+
concurrency:
9+
group: ${{ github.workflow }}-${{ github.ref }}
10+
11+
jobs:
12+
build:
13+
runs-on: macos-15-xlarge
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
include:
18+
- project_root: ./
19+
scheme: 'New Expensify Dev'
20+
configuration: 'DebugDevelopment'
21+
22+
- project_root: Mobile-Expensify/
23+
is_hybrid_build: true
24+
scheme: 'Expensify Dev'
25+
configuration: 'Debug'
26+
steps:
27+
- name: Checkout
28+
# v4
29+
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
30+
with:
31+
submodules: ${{ matrix.is_hybrid_build || false }}
32+
token: ${{ secrets.OS_BOTIFY_TOKEN }}
33+
34+
- name: Setup Node
35+
uses: ./.github/actions/composite/setupNode
36+
with:
37+
IS_HYBRID_BUILD: ${{ matrix.is_hybrid_build && 'true' || 'false' }}
38+
39+
- name: RNEF Remote Build - iOS
40+
uses: callstackincubator/ios@4c2dcc0d6b9c35407cf294e17386b65258a3b6af
41+
env:
42+
PROJECT_ROOT_PATH: ${{ matrix.project_root }}
43+
with:
44+
destination: simulator
45+
scheme: ${{ matrix.scheme }}
46+
configuration: ${{ matrix.configuration }}
47+
github-token: ${{ github.token }}
48+
comment-bot: false

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 1009012700
118-
versionName "9.1.27-0"
117+
versionCode 1009012704
118+
versionName "9.1.27-4"
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"
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
title: Free-Features-in-Expensify.md
3+
description: Learn what features are available for free in Expensify and common use cases like tracking receipts for taxes, budgeting, and freelance expense management.
4+
keywords: free features, SmartScan, receipt tracking, tax documentation, freelance expenses, personal finance
5+
---
6+
7+
<div id="expensify-classic" markdown="1">
8+
9+
You don’t need a paid Workspace to use Expensify! This guide covers the free features available to individual users who want to manage personal, freelance, or household expenses.
10+
11+
# What’s available for free in Expensify
12+
13+
You can access these features without joining a paid Workspace:
14+
15+
- **SmartScan** receipts (limited free scans each month)
16+
- Email receipts to [receipts@expensify.com](mailto:receipts@expensify.com) for automatic upload
17+
- **Create and organize expenses** using categories and tags
18+
- **Export expense reports** as spreadsheet or CSV files
19+
- **Manually enter and edit expenses** in the mobile or web app
20+
21+
**Note:** Features like automatic reimbursement, Expensify Cards, integrations, and team collaboration require a paid Workspace.
22+
23+
---
24+
25+
# Common use cases for free users
26+
27+
## Tracking receipts for tax purposes
28+
29+
Stay organized throughout the year and simplify tax filing:
30+
- Create reports like **“2024 Tax-Deductible Expenses”**
31+
- Tag expenses for business or personal use
32+
- Export reports to share with your tax software or accountant
33+
34+
[Learn how to export expenses](https://help.expensify.com/classic/for-individuals/exporting-expenses)
35+
36+
## Managing freelance or gig work expenses
37+
38+
Freelancers often use Expensify to:
39+
- **Scan receipts** for client-related purchases
40+
- Generate reports to attach to invoices
41+
- **Track mileage manually** for tax deductions
42+
43+
[How to add expenses manually](https://help.expensify.com/classic/for-individuals/adding-expenses-manually)
44+
45+
## Personal budgeting and finance tracking
46+
47+
Expensify is also great for everyday spending:
48+
- Monitor spending in categories like groceries, utilities, or entertainment
49+
- Save digital receipts for warranties or large purchases
50+
- Use tags to track **shared expenses** with family or roommates
51+
52+
[Organize expenses with categories and tags](https://help.expensify.com/classic/for-individuals/categorizing-and-tagging-expenses)
53+
54+
---
55+
56+
# FAQ
57+
58+
## Is SmartScan free?
59+
60+
Yes, each account includes a limited number of free SmartScans per month. After that, you can still upload receipts manually.
61+
62+
## Can I use Expensify without a business?
63+
64+
Absolutely. Expensify is designed for anyone—individuals, freelancers, or households—who want to track expenses.
65+
66+
## Do I need to upgrade to export expenses?
67+
68+
No upgrade is required. You can export your expenses anytime as a spreadsheet or CSV.
69+
70+
## What if I need advanced features later?
71+
72+
You can always **create or join a Workspace** to unlock more tools like bank connections, accounting integrations, and company card support.
73+

docs/redirects.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,3 +710,4 @@ https://help.expensify.com/articles/new-expensify/expenses-&-payments/Unlock-a-B
710710
https://help.expensify.com/articles/new-expensify/expenses-&-payments/Validate-a-Business-Bank-Account,https://help.expensify.com/articles/new-expensify/expenses-and-payments/Validate-a-Business-Bank-Account
711711
https://help.expensify.com/articles/new-expensify/expenses-and-payments/Export-download-expenses,https://help.expensify.com/articles/new-expensify/expenses-and-payments/Search-and-Download-Expenses
712712
https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/bank-accounts/Enable-Global-Reimbursements,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Enable-Global-Reimbursement
713+
https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Plan-types-and-pricing,https://www.expensify.com/pricing

ios/AppDelegate.swift

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// AppDelegate.swift
3+
// NewExpensify
4+
//
5+
// Created by Marcin Warchoł on 08/04/2025.
6+
//
7+
8+
import UIKit
9+
import React
10+
import React_RCTAppDelegate
11+
import ReactAppDependencyProvider
12+
import ExpoModulesCore
13+
import Firebase
14+
15+
16+
@main
17+
class AppDelegate: ExpoAppDelegate, UNUserNotificationCenterDelegate {
18+
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
19+
self.moduleName = "NewExpensify"
20+
self.dependencyProvider = RCTAppDependencyProvider()
21+
self.initialProps = [:]
22+
23+
// Configure firebase
24+
FirebaseApp.configure()
25+
26+
// Force the app to LTR mode.
27+
RCTI18nUtil.sharedInstance().allowRTL(false)
28+
RCTI18nUtil.sharedInstance().forceRTL(false)
29+
30+
_ = super.application(application, didFinishLaunchingWithOptions: launchOptions)
31+
32+
if let rootView = self.window.rootViewController?.view as? RCTRootView {
33+
RCTBootSplash.initWithStoryboard("BootSplash", rootView: rootView) // <- initialization using the storyboard file name
34+
}
35+
36+
// Define UNUserNotificationCenter
37+
let center = UNUserNotificationCenter.current()
38+
center.delegate = self
39+
40+
// Start the "js_load" custom performance tracing metric. This timer is
41+
// stopped by a native module in the JS so we can measure total time starting
42+
// in the native layer and ending in the JS layer.
43+
RCTStartupTimer.start()
44+
45+
if !UserDefaults.standard.bool(forKey: "isFirstRunComplete") {
46+
UIApplication.shared.applicationIconBadgeNumber = 0
47+
UserDefaults.standard.set(true, forKey: "isFirstRunComplete")
48+
}
49+
50+
RNBackgroundTaskManager.setup()
51+
52+
return true
53+
}
54+
55+
56+
override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
57+
return RCTLinkingManager.application(application, open: url, options: options)
58+
}
59+
60+
override func application(_ application: UIApplication,
61+
continue userActivity: NSUserActivity,
62+
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
63+
return RCTLinkingManager.application(application,
64+
continue: userActivity,
65+
restorationHandler: restorationHandler)
66+
}
67+
68+
override func sourceURL(for bridge: RCTBridge) -> URL? {
69+
return self.bundleURL()
70+
}
71+
72+
override func bundleURL() -> URL? {
73+
#if DEBUG
74+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
75+
#else
76+
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
77+
#endif
78+
}
79+
80+
// This methods is needed to support the hardware keyboard shortcuts
81+
func keyCommands() -> [Any]? {
82+
return HardwareShortcuts.sharedInstance().keyCommands()
83+
}
84+
85+
func handleKeyCommand(_ keyCommand: UIKeyCommand) {
86+
HardwareShortcuts.sharedInstance().handleKeyCommand(keyCommand)
87+
}
88+
}

0 commit comments

Comments
 (0)