Skip to content

Commit

Permalink
Merge branch 'main' into fix-submitted-optimistic-message
Browse files Browse the repository at this point in the history
  • Loading branch information
FitseTLT committed Apr 8, 2024
2 parents b7b53dd + ec41a4b commit ad9b5d7
Show file tree
Hide file tree
Showing 81 changed files with 1,969 additions and 1,138 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001046009
versionName "1.4.60-9"
versionCode 1001046100
versionName "1.4.61-0"
}

flavorDimensions "default"
Expand Down
11 changes: 10 additions & 1 deletion assets/images/avatars/fallback-avatar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

63 changes: 63 additions & 0 deletions docs/articles/expensify-classic/workspaces/Currency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: Report Currency
description: Understanding expense and report currency
---

# Overview
As a workspace admin, you can choose a default currency for your employees' expense reports, and we’ll automatically convert any expenses into that currency.

Here are a few essential things to remember:

- Currency settings for a workspace apply to all expenses under that workspace. If you need different default currencies for certain employees, creating separate workspaces and configuring the currency settings is best.
- As an admin, the currency settings you establish in the workspace will take precedence over any currency settings individual users may have in their accounts.
- Currency is a workspace-level setting, meaning the currency you set will determine the currency for all expenses submitted on that workspace.

# How to select the currency on a workspace

## As an admin on a group workspace

1. Sign into your Expensify web account
2. Go to **Settings > Workspaces > Group > _[Workspace Name]_> Reports > Report Basics**
3. Adjust the **Report Output Currency**

## On an individual workspace

1. Sign into your Expensify web account
2. Go to **Settings > Workspaces > Individual >_[Workspace Name]_> Reports > Report Basics**
3. Adjust the **Report Output Currency**

Please note the currency setting on an individual workspace is overridden when you submit a report on a group workspace.

# Deep Dive

## Conversion Rates

Using data from Open Exchange Rates, Expensify takes the average rate on the day the expense occurred to convert an expense from one currency to another. The conversion rate can vary depending on when the expense happened since the rate is determined after the market closes on that specific date.

If the markets aren’t open on the day the expense takes place (i.e., on a Saturday), Expensify will use the daily average rate from the last available market day before the purchase took place.

When an expense is logged for a future date, possibly to anticipate a purchase that has yet to occur, we'll use the most recent available data. This means the report's value may change up to the day of that expense.

## Managing expenses for employees in several different countries

Suppose you have employees scattered across the globe who submit expense reports in various currencies. The best way to manage those expenses is to create separate group workspaces for each location or region where your employees are based.

Then, set the default currency for that workspace to match the currency in which the employees are reimbursed.

For example, if you have employees in the US, France, Japan, and India, you’d want to create four separate workspaces, add the employees to each, and then set the corresponding currency for each workspace.

{% include faq-begin.md %}

## I have expenses in several different currencies. How will this show up on a report?

If you're traveling to foreign countries during a reporting period and making purchases in various currencies, each expense is imported with the currency of the purchase.

On your expense report, Expensify will automatically convert each expense to the default currency set for the group workspace.

## How does the currency of an expense impact the conversion rate?

Expenses entered in a foreign currency are automatically converted to the default currency on your workspace. The conversion uses the day’s average trading rate pulled from [Open Exchange Rates](https://openexchangerates.org/).

If you want to bypass the exchange rate conversion, you can manually enter an expense in your default currency instead.

{% include faq-end.md %}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ If you need to enable direct debits from your verified bank account, your bank w

If using Expensify to process Bill payments, you'll also need to whitelist the ACH IDs from our partner [Stripe](https://support.stripe.com/questions/ach-direct-debit-company-ids-for-stripe?):
- The ACH CompanyIDs (1800948598 and 4270465600)
- The ACH Originator Name (Stripe Payments company)
- The ACH Originator Name (expensify.com)

If using Expensify to process international reimbursements from your USD bank account, you'll also need to whitelist the ACH IDs from our partner CorPay:
- The ACH CompanyIDs (1522304924 and 2522304924)
Expand Down
3 changes: 3 additions & 0 deletions docs/redirects.csv
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,6 @@ https://help.expensify.com/articles/expensify-classic/workspace-and-domain-setti
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Reimbursement,https://help.expensify.com/articles/expensify-classic/send-payments/Reimbursing-Reports
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Tags,https://help.expensify.com/articles/expensify-classic/workspaces/Tags
https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/User-Roles,https://help.expensify.com/articles/expensify-classic/workspaces/Change-member-workspace-roles
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/tax-tracking,https://help.expensify.com/articles/expensify-classic/expenses/expenses/Apply-Tax
https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/User-Roles.html,https://help.expensify.com/expensify-classic/hubs/copilots-and-delegates/
https://help.expensify.com/articles/expensify-classic/expensify-billing/Billing-Owner,https://help.expensify.com/articles/expensify-classic/workspaces/Assign-billing-owner-and-payment-account
7 changes: 6 additions & 1 deletion ios/NewExpensify/AppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ - (BOOL)application:(UIApplication *)application
// stopped by a native module in the JS so we can measure total time starting
// in the native layer and ending in the JS layer.
[RCTStartupTimer start];


if (![[NSUserDefaults standardUserDefaults] boolForKey:@"isFirstRunComplete"]) {
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isFirstRunComplete"];
}

return YES;
}

Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.4.60</string>
<string>1.4.61</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand All @@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.4.60.9</string>
<string>1.4.61.0</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.4.60</string>
<string>1.4.61</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.4.60.9</string>
<string>1.4.61.0</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions ios/NotificationServiceExtension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleShortVersionString</key>
<string>1.4.60</string>
<string>1.4.61</string>
<key>CFBundleVersion</key>
<string>1.4.60.9</string>
<string>1.4.61.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
Expand Down
11 changes: 6 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.4.60-9",
"version": "1.4.61-0",
"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.",
Expand Down Expand Up @@ -253,7 +253,7 @@
"css-loader": "^6.7.2",
"diff-so-fancy": "^1.3.0",
"dotenv": "^16.0.3",
"electron": "^29.0.0",
"electron": "^29.2.0",
"electron-builder": "24.13.2",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
Expand Down
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import HTMLEngineProvider from './components/HTMLEngineProvider';
import InitialURLContextProvider from './components/InitialURLContextProvider';
import {LocaleContextProvider} from './components/LocaleContextProvider';
import OnyxProvider from './components/OnyxProvider';
import OptionsListContextProvider from './components/OptionListContextProvider';
import PopoverContextProvider from './components/PopoverProvider';
import SafeArea from './components/SafeArea';
import ScrollOffsetContextProvider from './components/ScrollOffsetContextProvider';
Expand Down Expand Up @@ -83,7 +82,6 @@ function App({url}: AppProps) {
FullScreenContextProvider,
VolumeContextProvider,
VideoPopoverMenuContextProvider,
OptionsListContextProvider,
]}
>
<CustomStatusBarAndBackground />
Expand Down
5 changes: 5 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,11 @@ const CONST = {
RIGHT: 'right',
},
POPOVER_MENU_PADDING: 8,
RESTORE_FOCUS_TYPE: {
DEFAULT: 'default',
DELETE: 'delete',
PRESERVE: 'preserve',
},
},
TIMING: {
CALCULATE_MOST_RECENT_LAST_MODIFIED_ACTION: 'calc_most_recent_last_modified_action',
Expand Down
28 changes: 14 additions & 14 deletions src/components/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,26 @@ function Avatar({
setImageError(false);
}, [source]);

if (!source) {
return null;
}

const isWorkspace = type === CONST.ICON_TYPE_WORKSPACE;
const iconSize = StyleUtils.getAvatarSize(size);

const imageStyle: StyleProp<ImageStyle> = [StyleUtils.getAvatarStyle(size), imageStyles, styles.noBorderRadius];
const iconStyle = imageStyles ? [StyleUtils.getAvatarStyle(size), styles.bgTransparent, imageStyles] : undefined;

const iconFillColor = isWorkspace ? StyleUtils.getDefaultWorkspaceAvatarColor(name).fill : fill;
// We pass the color styles down to the SVG for the workspace and fallback avatar.
const useFallBackAvatar = imageError || source === Expensicons.FallbackAvatar || !source;
const fallbackAvatar = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatar(name) : fallbackIcon || Expensicons.FallbackAvatar;
const fallbackAvatarTestID = isWorkspace ? ReportUtils.getDefaultWorkspaceAvatarTestID(name) : fallbackIconTestID || 'SvgFallbackAvatar Icon';

const avatarSource = imageError ? fallbackAvatar : source;
const avatarSource = useFallBackAvatar ? fallbackAvatar : source;

let iconColors;
if (isWorkspace) {
iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(name);
} else if (useFallBackAvatar) {
iconColors = StyleUtils.getBackgroundColorAndFill(theme.border, theme.icon);
} else {
iconColors = null;
}

return (
<View style={[containerStyles, styles.pointerEventsNone]}>
Expand All @@ -107,13 +112,8 @@ function Avatar({
src={avatarSource}
height={iconSize}
width={iconSize}
fill={imageError ? theme.offline : iconFillColor}
additionalStyles={[
StyleUtils.getAvatarBorderStyle(size, type),
isWorkspace && StyleUtils.getDefaultWorkspaceAvatarColor(name),
imageError && StyleUtils.getBackgroundColorStyle(theme.fallbackIconColor),
iconAdditionalStyles,
]}
fill={imageError ? iconColors?.fill ?? theme.offline : iconColors?.fill ?? fill}
additionalStyles={[StyleUtils.getAvatarBorderStyle(size, type), iconColors, iconAdditionalStyles]}
/>
</View>
)}
Expand Down
21 changes: 14 additions & 7 deletions src/components/Composer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,20 @@ function Composer(
disabled={isDisabled}
onKeyPress={handleKeyPress}
onFocus={(e) => {
ReportActionComposeFocusManager.onComposerFocus(() => {
if (!textInput.current) {
return;
}

textInput.current.focus();
});
if (isReportActionCompose) {
ReportActionComposeFocusManager.onComposerFocus(null);
} else {
// While a user edits a comment, if they open the LHN menu, we want to ensure that
// the focus returns to the message edit composer after they click on a menu item (e.g. mark as read).
// To achieve this, we re-assign the focus callback here.
ReportActionComposeFocusManager.onComposerFocus(() => {
if (!textInput.current) {
return;
}

textInput.current.focus();
});
}

props.onFocus?.(e);
}}
Expand Down
2 changes: 2 additions & 0 deletions src/components/EmojiPicker/EmojiPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ const EmojiPicker = forwardRef((props, ref) => {
anchorDimensions={emojiAnchorDimension.current}
avoidKeyboard
shoudSwitchPositionIfOverflow
shouldEnableNewFocusManagement
restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE}
>
<EmojiPickerMenu
onEmojiSelected={selectEmoji}
Expand Down
Loading

0 comments on commit ad9b5d7

Please sign in to comment.