Skip to content

Commit 8788c9e

Browse files
committed
Merge branch 'main' into add-Unicode-custom-emojis
2 parents db9da50 + 4a1c444 commit 8788c9e

27 files changed

Lines changed: 541 additions & 175 deletions

docs/articles/new-expensify/billing-and-subscriptions/Add-a-payment-card-and-view-your-subscription.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,25 @@
22
title: Manage Expensify Subscription
33
description: Learn how to manage your subscription, update billing details, cancel early, or delete a workspace in New Expensify.
44
keywords: [New Expensify, subscription, billing, payment card, cancel subscription, delete workspace, remove workspace, subscription settings]
5-
---
6-
<div id="new-expensify" markdown="1">
7-
8-
Easily manage your subscription in New Expensify—update payment details, adjust your plan, request early cancellation, or delete a workspace if needed.
95

106
---
117

12-
# View Subscription and Add a Payment Card
8+
<div id="new-expensify" markdown="1">
139

14-
To view your subscription and add a payment card:
10+
Easily manage your subscription in New Expensify—update payment details, adjust your plan, request early cancellation, or delete a workspace if needed.
1511

16-
1. Navigate to **Account > Subscription**.
17-
2. Click **Add Payment Card** and enter your card details to ensure uninterrupted billing.
12+
You can view and manage your subscription under **Account > Subscription**.
1813

1914
---
2015

21-
# Manage Subscription Plan
16+
# Manage Subscription
2217

2318
In the Subscription section, you can view and update your plan details, including:
2419

20+
- **Add a payment card**: Connect the credit card you want to use to pay for Expensify each month.
2521
- **Current plan:** View your subscription type and number of seats.
2622
- **Billing info:** See your payment method and next renewal date.
27-
- **Auto-renew:** Check when your subscription will automatically renew (e.g., **Renews on Nov 1, 2027**).
23+
- **Auto-renew:** Check when your subscription will automatically renew (e.g., **Renews on Nov 1, 2026**).
2824
- **Auto-increase annual seats:** See how much you could save by automatically increasing seats if your team grows.
2925

3026
**Note:** Enabling auto-increase will extend your annual subscription end date.
@@ -35,18 +31,19 @@ In the Subscription section, you can view and update your plan details, includin
3531

3632
To request an early cancellation:
3733

38-
1. Go to the **Subscription** section.
39-
2. Click **Request Early Cancellation**.
34+
1. Head to **Account > Subscription**.
35+
2. Click the **Request early cancellation** button.
36+
3. Depending on how your subscription is configured, the early cancellation will happen automatically or be sent to our team for review.
4037

4138
**Note:** Early cancellations aren’t available to all customers.
4239

4340
---
4441

4542
# Delete a Workspace
4643

47-
To permanently delete a workspace:
44+
To permanently delete a workspace and downgrade to a free account:
4845

49-
1. Go to **Settings > Workspaces**.
46+
1. From the left-hand menu, click **Workspaces**.
5047
2. Click the three dots next to the workspace name.
5148
3. Select **Delete workspace** from the dropdown.
5249
4. Confirm the deletion when prompted.

docs/articles/new-expensify/billing-and-subscriptions/Billing-Overview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ For a full breakdown, visit the [Expensify pricing page](https://www.expensify.c
4343
## 🧾 Receipts
4444

4545
- Your billing receipts show: `X Collect members @ $5.00`
46-
- View them in **Workspaces > Subscription**
46+
- View them in **Account > Subscription**
4747

4848
---
4949

@@ -92,7 +92,7 @@ For a full breakdown, visit the [Expensify pricing page](https://www.expensify.c
9292

9393
## 🧾 Receipts
9494

95-
- View billing receipts under **Workspaces > Subscription**
95+
- View billing receipts under **Account > Subscription**
9696
- Each workspace can only have **one billing owner**
9797

9898
---

docs/articles/new-expensify/billing-and-subscriptions/Changing-Your-Workspace-Plan.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Take a look at this resource to learn how to update your Expensify workspace sub
1313

1414
To update your subscription plan:
1515

16-
1. Use the **left-hand navigation bar (LHB)** to go to **Workspaces > Subscription**.
16+
1. Use the **left-hand menu** to go to **Account > Subscription**.
1717
2. Click **Explore all plans** and choose the one that best fits your needs.
1818

1919
---
@@ -22,7 +22,7 @@ To update your subscription plan:
2222

2323
If you're on an Annual Subscription (a 12-month commitment), you may be eligible to request early cancellation and switch to a free account:
2424

25-
1. Use the **left-hand navigation bar** to go to **Workspaces > Subscription**.
25+
1. Use the **left-hand navigation bar** to go to **Account > Subscription**.
2626
2. Click **Request early cancellation**.
2727

2828
**Note:** Early cancellations are subject to eligibility review.
@@ -33,7 +33,7 @@ If you're on an Annual Subscription (a 12-month commitment), you may be eligible
3333

3434
If you're on a Pay-per-use plan and want to switch to a free account:
3535

36-
1. Go to **Workspaces > Subscription**.
36+
1. Go to **Account > Subscription**.
3737
2. Click the three dots **(⋮)** next to your workspace name.
3838
3. Select **Delete workspace**.
3939

docs/articles/new-expensify/billing-and-subscriptions/Tax-Exemption.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ If your organization qualifies as tax-exempt, you can request to remove sales ta
2020

2121
**To request tax-exempt status for your Expensify subscription:**
2222

23-
1. Go to **Account > Workspaces > Subscription > Subscription settings**.
23+
1. Go to **Account > Subscription > Subscription settings**.
2424
2. Click **Tax exempt status**.
2525
3. Selecting **Tax exempt status** will begin a chat with Concierge.
2626
4. From there, communicate that your account is tax-exempt and attach a PDF of your tax-exempt document.

docs/articles/new-expensify/getting-started/Navigating-New-Expensify.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ The **Reports** tab consolidates all filtering and reporting tools:
4949

5050
# Workspaces Page
5151

52-
The **Workspaces** tab helps you manage everything related to your company or personal workspaces. Whether you're creating a new workspace, editing an existing one, or managing features like billing and card connections, this section is your main hub.
52+
The **Workspaces** tab shows you a clean list of all your workspaces and allows you to manage everything related to your company or personal workspaces. This section is your main hub for workspace management.
5353

5454
From the **Workspaces** tab, you can:
5555

@@ -59,10 +59,8 @@ From the **Workspaces** tab, you can:
5959
- **Enable features** – Turn on or off modules like Company Cards, Workflows, Tags, Distance tracking, and more via the **More features** menu.
6060
- **Set up accounting connections** – Integrate tools like QuickBooks or NetSuite under **Accounting**.
6161
- **Edit categories and tags** – Create or disable expense categories and tags to organize expenses.
62-
- **Track billing and subscription** – Navigate to the **Subscription** section to choose a plan, view billing history, or cancel your subscription.
63-
- **Access domains** (if applicable) – Allow centralized control over company-wide settings like SAML, domain groups, and company card feeds.
6462

65-
**Tip:** All workspace-specific settings live under this tab, while account specific settings live under the **Account** tab.
63+
**Tip:** All workspace-specific settings live under this tab, while account-level settings (including billing and subscription management) live under the **Account** tab.
6664

6765
---
6866

@@ -74,6 +72,7 @@ The **Account** tab includes all personal and account-level settings. Here’s w
7472
- **Wallet** – Add or manage personal payment methods (e.g., cards or bank accounts).
7573
- **Preferences** – Choose your default payment currency, language, and notification preferences.
7674
- **Security** – Set up two-factor authentication and manage device access.
75+
- **Subscription** – Manage your billing, view subscription details, and track usage.
7776
- **Help** – Access Expensify Help docs and reach out to Concierge.
7877
- **Switch to Expensify Classic** – Return to the classic layout if needed.
7978
- **About** – View app version details and system information.

patches/react-native/details.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# `react-native` patches
2+
3+
### [react-native+0.79.2+025+textinput-prevent-focus-on-first-responder.patch](react-native+0.79.2+025+textinput-prevent-focus-on-first-responder.patch)
4+
5+
- Reason: On iOS, a text input automatically becomes the "first responder" in UIKit's "UIResponder" chain. Once a text input becomes the first responder, it will be automatically focused. (This also causes the keyboard ot open)
6+
- This is not handled by React or React Native, but is rather an native iOS/UIKit behaviour. This patch adds additional an additional `TextInput` prop (`preventFocusOnFirstResponder`) and a ref method (`preventFocusOnFirstResponderOnce`) to bypass the focus on first responder.
7+
- In E/App this causes issues with e.g. the keyboard briefly opening after a modal has been dismissed before another modal is opened (`ReportActionContextMenu` -> `EmojiPicker`)
8+
- Upstream PR/issue: None, because this is not a real bug fix but a hotfix specific to Expensify
9+
- E/App issue: [#54813](https://github.com/Expensify/App/issues/54813)
10+
- PR Introducing Patch: [#61492](https://github.com/Expensify/App/pull/61492)
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js b/node_modules/react-native/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js
2+
index 8c40af6..2a32cd4 100644
3+
--- a/node_modules/react-native/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js
4+
+++ b/node_modules/react-native/Libraries/Components/TextInput/RCTMultilineTextInputNativeComponent.js
5+
@@ -21,7 +21,7 @@ type NativeType = HostComponent<{...}>;
6+
type NativeCommands = TextInputNativeCommands<NativeType>;
7+
8+
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
9+
- supportedCommands: ['focus', 'blur', 'setTextAndSelection'],
10+
+ supportedCommands: ['focus', 'blur', 'setTextAndSelection', 'preventFocusOnFirstResponderOnce'],
11+
});
12+
13+
export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
14+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js b/node_modules/react-native/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js
15+
index a52be63..820153a 100644
16+
--- a/node_modules/react-native/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js
17+
+++ b/node_modules/react-native/Libraries/Components/TextInput/RCTSingelineTextInputNativeComponent.js
18+
@@ -21,7 +21,7 @@ type NativeType = HostComponent<{...}>;
19+
type NativeCommands = TextInputNativeCommands<NativeType>;
20+
21+
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
22+
- supportedCommands: ['focus', 'blur', 'setTextAndSelection'],
23+
+ supportedCommands: ['focus', 'blur', 'setTextAndSelection', 'preventFocusOnFirstResponderOnce'],
24+
});
25+
26+
export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
27+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
28+
index 94b7d31..549ffab 100644
29+
--- a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
30+
+++ b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
31+
@@ -163,6 +163,7 @@ const RCTTextInputViewConfig = {
32+
lineBreakStrategyIOS: true,
33+
lineBreakModeIOS: true,
34+
smartInsertDelete: true,
35+
+ preventFocusOnFirstResponder: true,
36+
...ConditionallyIgnoredEventHandlers({
37+
onClear: true,
38+
onChange: true,
39+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts b/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
40+
index 2112772..4efd46e 100644
41+
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
42+
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
43+
@@ -770,6 +770,11 @@ export interface TextInputProps
44+
*/
45+
multiline?: boolean | undefined;
46+
47+
+ /**
48+
+ * If true, the text input will not restore focus when the input becomes first responder.
49+
+ */
50+
+ preventFocusOnFirstResponder?: boolean | undefined;
51+
+
52+
/**
53+
* Callback that is called when the text input is blurred
54+
*/
55+
@@ -1033,4 +1038,9 @@ export class TextInput extends TextInputBase {
56+
* Sets the start and end positions of text selection.
57+
*/
58+
setSelection: (start: number, end: number) => void;
59+
+
60+
+ /**
61+
+ * Prevents the text input once from restoring focus when the input becomes first responder.
62+
+ */
63+
+ preventFocusOnFirstResponderOnce: () => void;
64+
}
65+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js
66+
index 50f7794..d5585f0 100644
67+
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js
68+
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js
69+
@@ -1012,6 +1012,7 @@ export interface TextInputInstance extends HostInstance {
70+
+isFocused: () => boolean;
71+
+getNativeRef: () => ?HostInstance;
72+
+setSelection: (start: number, end: number) => void;
73+
+ +preventFocusOnFirstResponderOnce: () => void;
74+
}
75+
76+
/**
77+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
78+
index 1fb07fb..1fed107 100644
79+
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
80+
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
81+
@@ -1430,6 +1430,11 @@ function InternalTextInput(props: TextInputProps): React.Node {
82+
);
83+
}
84+
},
85+
+ preventFocusOnFirstResponderOnce: () => {
86+
+ if (inputRef.current != null && Platform.OS !== 'android') {
87+
+ viewCommands.preventFocusOnFirstResponderOnce(inputRef.current);
88+
+ }
89+
+ },
90+
});
91+
}
92+
},
93+
diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInputNativeCommands.js b/node_modules/react-native/Libraries/Components/TextInput/TextInputNativeCommands.js
94+
index 9da8899..bf87ffd 100644
95+
--- a/node_modules/react-native/Libraries/Components/TextInput/TextInputNativeCommands.js
96+
+++ b/node_modules/react-native/Libraries/Components/TextInput/TextInputNativeCommands.js
97+
@@ -22,8 +22,9 @@ export interface TextInputNativeCommands<T> {
98+
start: Int32,
99+
end: Int32,
100+
) => void;
101+
+ +preventFocusOnFirstResponderOnce: (viewRef: React.ElementRef<T>) => void;
102+
}
103+
104+
-const supportedCommands = ['focus', 'blur', 'setTextAndSelection'] as string[];
105+
+const supportedCommands = ['focus', 'blur', 'setTextAndSelection', 'preventFocusOnFirstResponderOnce'] as string[];
106+
107+
export default supportedCommands;
108+
diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
109+
index fef9c63..0a887d5 100644
110+
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
111+
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
112+
@@ -70,6 +70,16 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
113+
NSDictionary<NSAttributedStringKey, id> *_originalTypingAttributes;
114+
115+
BOOL _hasInputAccessoryView;
116+
+
117+
+ /**
118+
+ * Prevents automatic focus of the text input once it becomes the first responder (e.g. on modal dismissal).
119+
+ */
120+
+ BOOL _preventFocusOnFirstResponder;
121+
+
122+
+ /**
123+
+ * Same as above but is only used to prevent focus once
124+
+ */
125+
+ BOOL _preventFocusOnFirstResponderOnce;
126+
}
127+
128+
#pragma mark - UIView overrides
129+
@@ -295,6 +305,10 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
130+
_backedTextInputView.disableKeyboardShortcuts = newTextInputProps.disableKeyboardShortcuts;
131+
}
132+
133+
+ if (newTextInputProps.preventFocusOnFirstResponder != oldTextInputProps.preventFocusOnFirstResponder) {
134+
+ _preventFocusOnFirstResponder = newTextInputProps.preventFocusOnFirstResponder;
135+
+ }
136+
+
137+
[super updateProps:props oldProps:oldProps];
138+
139+
[self setDefaultInputAccessoryView];
140+
@@ -357,6 +371,15 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
141+
142+
- (BOOL)textInputShouldBeginEditing
143+
{
144+
+ if (_preventFocusOnFirstResponderOnce) {
145+
+ _preventFocusOnFirstResponderOnce = NO;
146+
+ return NO;
147+
+ }
148+
+
149+
+ if (_preventFocusOnFirstResponder) {
150+
+ return NO;
151+
+ }
152+
+
153+
return YES;
154+
}
155+
156+
@@ -576,6 +599,11 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
157+
_comingFromJS = NO;
158+
}
159+
160+
+- (void)preventFocusOnFirstResponderOnce
161+
+{
162+
+ _preventFocusOnFirstResponderOnce = YES;
163+
+}
164+
+
165+
#pragma mark - Default input accessory view
166+
167+
- (NSString *)returnKeyTypeToString:(UIReturnKeyType)returnKeyType
168+
diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h
169+
index f674d98..6ad10ec 100644
170+
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h
171+
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h
172+
@@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
173+
value:(NSString *__nullable)value
174+
start:(NSInteger)start
175+
end:(NSInteger)end;
176+
+- (void)preventFocusOnFirstResponderOnce;
177+
@end
178+
179+
RCT_EXTERN inline void
180+
@@ -109,6 +110,19 @@ RCTTextInputHandleCommand(id<RCTTextInputViewProtocol> componentView, const NSSt
181+
return;
182+
}
183+
184+
+ if ([commandName isEqualToString:@"preventFocusOnFirstResponderOnce"]) {
185+
+#if RCT_DEBUG
186+
+ if ([args count] != 0) {
187+
+ RCTLogError(
188+
+ @"%@ command %@ received %d arguments, expected %d.", @"TextInput", commandName, (int)[args count], 0);
189+
+ return;
190+
+ }
191+
+#endif
192+
+
193+
+ [componentView preventFocusOnFirstResponderOnce];
194+
+ return;
195+
+ }
196+
+
197+
#if RCT_DEBUG
198+
RCTLogError(@"%@ received command %@, which is not a supported command.", @"TextInput", commandName);
199+
#endif
200+
diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp
201+
index 47787a5..f671682 100644
202+
--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp
203+
+++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp
204+
@@ -127,6 +127,12 @@ BaseTextInputProps::BaseTextInputProps(
205+
"multiline",
206+
sourceProps.multiline,
207+
{false})),
208+
+ preventFocusOnFirstResponder(convertRawProp(
209+
+ context,
210+
+ rawProps,
211+
+ "preventFocusOnFirstResponder",
212+
+ sourceProps.preventFocusOnFirstResponder,
213+
+ {false})),
214+
disableKeyboardShortcuts(convertRawProp(
215+
context,
216+
rawProps,
217+
@@ -215,6 +221,7 @@ void BaseTextInputProps::setProp(
218+
RAW_SET_PROP_SWITCH_CASE_BASIC(submitBehavior);
219+
RAW_SET_PROP_SWITCH_CASE_BASIC(multiline);
220+
RAW_SET_PROP_SWITCH_CASE_BASIC(disableKeyboardShortcuts);
221+
+ RAW_SET_PROP_SWITCH_CASE_BASIC(preventFocusOnFirstResponder);
222+
}
223+
}
224+
225+
diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h
226+
index 3e93402..092dbf0 100644
227+
--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h
228+
+++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h
229+
@@ -79,6 +79,8 @@ class BaseTextInputProps : public ViewProps, public BaseTextProps {
230+
231+
bool multiline{false};
232+
233+
+ bool preventFocusOnFirstResponder{false};
234+
+
235+
bool disableKeyboardShortcuts{false};
236+
};
237+

0 commit comments

Comments
 (0)