Skip to content

Commit

Permalink
Merge branch 'main' into feat/49472-bank-account-verification
Browse files Browse the repository at this point in the history
  • Loading branch information
koko57 committed Sep 23, 2024
2 parents c0e2248 + 5e106f0 commit c216e85
Show file tree
Hide file tree
Showing 69 changed files with 787 additions and 333 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ module.exports = {
'plugin:you-dont-need-lodash-underscore/all',
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler'],
plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler', 'lodash'],
ignorePatterns: ['lib/**'],
parser: '@typescript-eslint/parser',
parserOptions: {
Expand Down Expand Up @@ -231,6 +231,7 @@ module.exports = {
'you-dont-need-lodash-underscore/throttle': 'off',
// The suggested alternative (structuredClone) is not supported in Hermes:https://github.com/facebook/hermes/issues/684
'you-dont-need-lodash-underscore/clone-deep': 'off',
'lodash/import-scope': ['error', 'method'],
'prefer-regex-literals': 'off',
'valid-jsdoc': 'off',
'jsdoc/no-types': 'error',
Expand Down
1 change: 1 addition & 0 deletions .github/libs/promiseWhile.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// eslint-disable-next-line lodash/import-scope
import type {DebouncedFunc} from 'lodash';

/**
Expand Down
Binary file added assets/animations/BankVault.lottie
Binary file not shown.
Binary file added assets/animations/GenericEmptyState.lottie
Binary file not shown.
Binary file added assets/animations/TripsEmptyState.lottie
Binary file not shown.
45 changes: 45 additions & 0 deletions contributingGuides/STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
- [Stateless components vs Pure Components vs Class based components vs Render Props](#stateless-components-vs-pure-components-vs-class-based-components-vs-render-props---when-to-use-what)
- [Use Refs Appropriately](#use-refs-appropriately)
- [Are we allowed to use [insert brand new React feature]?](#are-we-allowed-to-use-insert-brand-new-react-feature-why-or-why-not)
- [Handling Scroll Issues with Nested Lists in React Native](#handling-scroll-issues-with-nested-lists-in-react-native)
- [Wrong Approach (Using SelectionList)](#wrong-approach-using-selectionlist)
- [Correct Approach (Using SelectionList)](#correct-approach-using-selectionlist)
- [React Hooks: Frequently Asked Questions](#react-hooks-frequently-asked-questions)
- [Onyx Best Practices](#onyx-best-practices)
- [Collection Keys](#collection-keys)
Expand Down Expand Up @@ -1105,6 +1108,48 @@ There are several ways to use and declare refs and we prefer the [callback metho

We love React and learning about all the new features that are regularly being added to the API. However, we try to keep our organization's usage of React limited to the most stable set of features that React offers. We do this mainly for **consistency** and so our engineers don't have to spend extra time trying to figure out how everything is working. That said, if you aren't sure if we have adopted something, please ask us first.


## Handling Scroll Issues with Nested Lists in React Native

### Problem

When using `SelectionList` alongside other components (e.g., `Text`, `Button`), wrapping them inside a `ScrollView` can lead to alignment and performance issues. Additionally, using `ScrollView` with nested `FlatList` or `SectionList` causes the error:

> "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation."

### Solution

The correct approach is avoid using `ScrollView`. You can add props like `listHeaderComponent` and `listFooterComponent` to add other components before or after the list while keeping the layout scrollable.

### Wrong Approach (Using `SelectionList`)

```jsx
<ScrollView>
<Text>Header Content</Text>
<SelectionList
sections={[{data}]}
ListItem={RadioListItem}
onSelectRow={handleSelect}
/>
<Button title="Submit" onPress={handleSubmit} />
</ScrollView>
```

### Correct Approach (Using `SelectionList`)

```jsx
<SelectionList
sections={[{item}]}
ListItem={RadioListItem}
onSelectRow={handleSelect}
listHeaderComponent={<Text>Header Content</Text>}
listFooterComponent={<Button title="Submit" onPress={handleSubmit} />}
/>
```

This ensures optimal performance and avoids layout issues.


## React Hooks: Frequently Asked Questions

### Are Hooks a Replacement for HOCs or Render Props?
Expand Down
6 changes: 6 additions & 0 deletions docs/_data/_routes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,9 @@ platforms:
title: Settings
icon: /assets/images/gears.svg
description: Manage profile settings and notifications.

- href: billing-and-subscriptions
title: Expensify Billing & Subscriptions
icon: /assets/images/subscription-annual.svg
description: Review Expensify's subscription options, plan types, and payment methods.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Billing and Subscriptions
description: Coming soon
---

# Coming Soon
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ To pay an invoice,
You can also view all unpaid invoices by searching for the sender’s email or phone number on the left-hand side of the app. The invoices waiting for your payment will have a green dot.

{% include faq-begin.md %}
# FAQ

**Can someone else pay an invoice besides the person who received it?**

Expand All @@ -54,6 +53,7 @@ You will need to work with the vendor to discuss alternative payment options. Yo
You can add additional payment methods to your [Wallet](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Set-up-your-wallet). Click **Account Settings** > **Wallet** > click **Add Bank Account**.

You will be prompted to choose a payment method when paying future invoices.

{% include faq-end.md %}

</div>
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Only workspace admins can send invoices. Invoices can be sent directly from Expe
If you have not [connected a business bank account](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Business-Bank-Account) to receive invoice payments, you will see an **Invoice balance** in your [Wallet](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Set-up-your-wallet). Expensify will automatically transfer these invoice payments once a business bank account is connected.


# FAQs
{% include faq-begin.md %}

**Why do I need to create a workspace to send an invoice?**

Expand Down Expand Up @@ -90,5 +90,6 @@ When invoicing is disabled, all previously created invoice rooms and historical

We can only accept a private domain website to ensure the security of your business. If you receive an error when entering your website, it is likely because the domain is not recognized as private. Make sure you are using a business email with a private domain. If you continue to experience issues, contact our support team at concierge@expensify.com for further assistance.

{% include faq-end.md %}

</div>
48 changes: 48 additions & 0 deletions docs/articles/new-expensify/workspaces/Set-up-workflows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: Workspace Workflows
description: Configure your workspace settings
---

Workflows allow you to enable or disable expense approval settings. If you are tracking your personal expenses, you probably don’t need someone to approve those expenses. But if you are tracking employee expenses, you might want extra approval settings for those expenses.

{% include info.html %}
Workflows are available for Collect and Control workspaces. Additionally, you must be a Workspace Admin to complete this process.
{% include end-info.html %}

# Enable workflows

1. Click your profile image or icon in the bottom left menu.
2. Scroll down and click **Workspaces** in the left menu.
3. Select the workspace you want to enable approval settings for.
4. Click **More features** in the left menu.
5. Under the Spend section, enable the Workflows toggle.

# Select workflows

You can choose to require additional approvals and/or allow delayed submissions.

1. Click **Workflows** in the left menu.
2. Enable the toggle for the desired workflow setting:

- **Add Approvals**: Requires additional approval for an expense before a payment can be authorized. Once enabled, click the Approver field to select an approver. This person must be a workspace admin. The default approver is the workspace owner.
- **Delay Submissions**: Determines when issue-free expenses are automatically submitted. Once enabled, choose a delay frequency:
-- With delayed submission **enabled**, all reimbursable and non-reimbursable expenses will be submitted at a designated frequency.
-- If delay submission is **disabled**, all reimbursable and non-reimbursable expenses are submitted instantly.

# Set up payment account

The payments section is where you’ll set up your business bank account for payments of expenses and invoices.

1. Click **Workflows**.
2. Enable the Payments toggle.
3. Click **Connect Bank Account**.
4. Follow the prompts to [connect your company bank account](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Business-Bank-Account).
5. Once enabled, choose an authorized expense payer. The authorized payer is a workspace admin who has access to the business bank account. They will be the default reimburser/payor for expenses submitted to the workspace.

{% include faq-begin.md %}

**If I have delayed submission and an approver, what should I expect with a report?**

When Add Approver is enabled with Delay Submission, expense reports go from Open > Processing > Approved. If delayed submission is disabled, expense reports go from Processing > Approved.

{% include faq-end.md %}
Binary file added docs/assets/images/addbankaccount_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/images/addbankaccount_02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/images/addbankaccount_03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/images/invoice_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/images/invoice_02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions docs/new-expensify/hubs/billing-and-subscriptions/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
layout: default
title: Billing & Subscriptions
---

{% include hub.html %}
24 changes: 21 additions & 3 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@
"eslint-plugin-storybook": "^0.8.0",
"eslint-plugin-testing-library": "^6.2.2",
"eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0",
"eslint-plugin-lodash": "^7.4.0",
"html-webpack-plugin": "^5.5.0",
"http-server": "^14.1.1",
"jest": "29.4.1",
Expand Down
6 changes: 4 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,9 @@ const CONST = {
},
// Multiplier for gyroscope animation in order to make it a bit more subtle
ANIMATION_GYROSCOPE_VALUE: 0.4,
ANIMATION_PAY_BUTTON_DURATION: 200,
ANIMATION_PAY_BUTTON_HIDE_DELAY: 1000,
ANIMATION_PAID_DURATION: 200,
ANIMATION_PAID_CHECKMARK_DELAY: 300,
ANIMATION_PAID_BUTTON_HIDE_DELAY: 1000,
BACKGROUND_IMAGE_TRANSITION_DURATION: 1000,
SCREEN_TRANSITION_END_TIMEOUT: 1000,
ARROW_HIDE_DELAY: 3000,
Expand Down Expand Up @@ -5764,6 +5765,7 @@ const CONST = {
ICON_HEIGHT: 160,

CATEGORIES_ARTICLE_LINK: 'https://help.expensify.com/articles/expensify-classic/workspaces/Create-categories#import-custom-categories',
MEMBERS_ARTICLE_LINK: 'https://help.expensify.com/articles/expensify-classic/workspaces/Invite-members-and-assign-roles#import-a-group-of-members',
TAGS_ARTICLE_LINK: 'https://help.expensify.com/articles/expensify-classic/workspaces/Create-tags#import-a-spreadsheet-1',
},

Expand Down
8 changes: 8 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,14 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/members',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/members` as const,
},
WORKSPACE_MEMBERS_IMPORT: {
route: 'settings/workspaces/:policyID/members/import',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/members/import` as const,
},
WORKSPACE_MEMBERS_IMPORTED: {
route: 'settings/workspaces/:policyID/members/imported',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/members/imported` as const,
},
POLICY_ACCOUNTING: {
route: 'settings/workspaces/:policyID/accounting',
getRoute: (policyID: string, newConnectionName?: ConnectionName, integrationToDisconnect?: ConnectionName, shouldDisconnectIntegrationBeforeConnecting?: boolean) => {
Expand Down
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ const SCREENS = {
INVOICES_COMPANY_WEBSITE: 'Workspace_Invoices_Company_Website',
TRAVEL: 'Workspace_Travel',
MEMBERS: 'Workspace_Members',
MEMBERS_IMPORT: 'Members_Import',
MEMBERS_IMPORTED: 'Members_Imported',
INVITE: 'Workspace_Invite',
INVITE_MESSAGE: 'Workspace_Invite_Message',
CATEGORIES: 'Workspace_Categories',
Expand Down
7 changes: 1 addition & 6 deletions src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import lodashDebounce from 'lodash/debounce';
import React, {useCallback} from 'react';
import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import {runOnUI, scrollTo} from 'react-native-reanimated';
import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
Expand Down Expand Up @@ -67,11 +66,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r

const scrollToHeader = (headerIndex: number) => {
const calculatedOffset = Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW) * CONST.EMOJI_PICKER_HEADER_HEIGHT;
runOnUI(() => {
'worklet';

scrollTo(emojiListRef, 0, calculatedOffset, true);
})();
emojiListRef.current?.scrollToOffset({offset: calculatedOffset, animated: true});
};

/**
Expand Down
7 changes: 2 additions & 5 deletions src/components/EmojiPicker/EmojiPickerMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import throttle from 'lodash/throttle';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import {scrollTo} from 'react-native-reanimated';
import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
Expand Down Expand Up @@ -114,9 +113,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
const filterEmojis = throttle((searchTerm: string) => {
const [normalizedSearchTerm, newFilteredEmojiList] = suggestEmojis(searchTerm);

if (emojiListRef.current) {
scrollTo(emojiListRef, 0, 0, false);
}
emojiListRef.current?.scrollToOffset({offset: 0, animated: false});
if (normalizedSearchTerm === '') {
// There are no headers when searching, so we need to re-make them sticky when there is no search term
setFilteredEmojis(allEmojis);
Expand Down Expand Up @@ -241,7 +238,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
}

const calculatedOffset = Math.floor(headerIndex / CONST.EMOJI_NUM_PER_ROW) * CONST.EMOJI_PICKER_HEADER_HEIGHT;
scrollTo(emojiListRef, 0, calculatedOffset, true);
emojiListRef.current?.scrollToOffset({offset: calculatedOffset, animated: true});
},
[emojiListRef],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type {FlashList} from '@shopify/flash-list';
import {useCallback, useEffect, useMemo, useState} from 'react';
import {useAnimatedRef} from 'react-native-reanimated';
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import emojis from '@assets/emojis';
import {useFrequentlyUsedEmojis} from '@components/OnyxProvider';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -10,7 +9,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
import * as EmojiUtils from '@libs/EmojiUtils';

const useEmojiPickerMenu = () => {
const emojiListRef = useAnimatedRef<FlashList<EmojiUtils.EmojiPickerListItem>>();
const emojiListRef = useRef<FlashList<EmojiUtils.EmojiPickerListItem>>(null);
const frequentlyUsedEmojis = useFrequentlyUsedEmojis();
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
const allEmojis = useMemo(() => EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis), [frequentlyUsedEmojis]);
Expand Down
Loading

0 comments on commit c216e85

Please sign in to comment.