diff --git a/android/app/build.gradle b/android/app/build.gradle index a28dc8661799..dd19b75b1e49 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,8 +107,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009000107 - versionName "9.0.1-7" + versionCode 1009000114 + versionName "9.0.1-14" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg b/assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg new file mode 100644 index 000000000000..91af173f0357 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/articles/expensify-classic/connect-credit-cards/company-cards/Company-Card-Settings.md b/docs/articles/expensify-classic/connect-credit-cards/company-cards/Company-Card-Settings.md index bc9801060223..0fde76c8fa92 100644 --- a/docs/articles/expensify-classic/connect-credit-cards/company-cards/Company-Card-Settings.md +++ b/docs/articles/expensify-classic/connect-credit-cards/company-cards/Company-Card-Settings.md @@ -80,7 +80,7 @@ When you link your credit cards to Expensify, the transactions will appear in ea After a card is connected via direct connection or via Approved! banks, Expensify will import 30-90 days' worth of historical transactions to your account (the timeframe is based on your bank's discretion). Any historical expenses beyond that date range can be imported using the CSV spreadsheet import method. ## Using eReceipts -Expensify eReceipts serve as digital substitutes for paper receipts in your purchase transactions, eliminating the necessity to retain physical receipts or utilize SmartScanning receipts. In the case of Expensify Card transactions, eReceipts are automatically generated for all amounts. For other card programs, eReceipts are specifically generated for purchases amounting to $75 or less, provided the transactions are in USD. +Expensify eReceipts serve as digital substitutes for paper receipts in your purchase transactions, eliminating the necessity to retain physical receipts or utilize SmartScanning receipts. In the case of Expensify Card transactions, eReceipts are automatically generated for all amounts in the following categories: Airlines, Commuter expenses, Gas, Groceries, Mail, Meals, Car rental, Taxis, and Utilities. For other card programs, eReceipts are specifically generated for purchases amounting to $75 or less, provided the transactions are in USD. To ensure seamless automatic importation, it's essential to maintain your transactions in US Dollars. Additionally, eReceipts can be directly imported from your bank account. Please be aware that CSV/OFX imported files of bank transactions do not support eReceipts. It's important to note that eReceipts are not generated for lodging expenses. Moreover, due to incomplete or inaccurate category information from certain banks, there may be instances of invalid eReceipts being generated for hotel purchases. If you choose to re-categorize expenses, a similar situation may arise. It's crucial to remember that our Expensify eReceipt Guarantee excludes coverage for hotel and motel expenses. diff --git a/docs/articles/new-expensify/expenses/Set-up-your-wallet.md b/docs/articles/new-expensify/expenses/Set-up-your-wallet.md index de1ee61066b0..7f7b6196707d 100644 --- a/docs/articles/new-expensify/expenses/Set-up-your-wallet.md +++ b/docs/articles/new-expensify/expenses/Set-up-your-wallet.md @@ -5,6 +5,8 @@ description: Send and receive payments by adding your payment account
To send and receive money using Expensify, you’ll first need to set up your Expensify Wallet by adding your payment account. +![The Wallet Tab where you can add a personal bank account]({{site.url}}/assets/images/ExpensifyHelp_R5_Wallet_1.png){:width="100%"} + {% include selector.html values="desktop, mobile" %} {% include option.html value="desktop" %} diff --git a/docs/articles/new-expensify/getting-started/Create-a-company-workspace.md b/docs/articles/new-expensify/getting-started/Create-a-company-workspace.md index cbe21c9db20a..e25ae580e87a 100644 --- a/docs/articles/new-expensify/getting-started/Create-a-company-workspace.md +++ b/docs/articles/new-expensify/getting-started/Create-a-company-workspace.md @@ -30,6 +30,12 @@ You can get support any time by locating your chat with Concierge in your chat i
  • Click Default Currency to set the currency for all expenses submitted under the workspace. Expensify automatically converts all other currencies to your default currency.
  • +![Click your profile image or icon]({{site.url}}/assets/images/ExpensifyHelp_R1_CreateWorkspace_1.png){:width="100%"} + +![Click Workspaces in the left menu and New Worksapce]({{site.url}}/assets/images/ExpensifyHelp_R1_CreateWorkspace_2.png){:width="100%"} + +![Options to make changes like a custom workspace name]({{site.url}}/assets/images/ExpensifyHelp_R1_CreateWorkspace_3.png){:width="100%"} + # 3. Invite members
      @@ -46,6 +52,12 @@ Once the invite is accepted, the new members will appear in your members list. You can also invite members on the workspace’s Profile page by clicking **Share** to share the workspace’s URL or QR code. {% include end-info.html %} +![Click Members on the left and click Invite member]({{site.url}}/assets/images/ExpensifyHelp_R1_InviteMembers_1.png){:width="100%"} + +![Use the search field to find the individual by name, email, or phone number]({{site.url}}/assets/images/ExpensifyHelp_R1_InviteMembers_2.png){:width="100%"} + +![Enter a custom message into the Message field]({{site.url}}/assets/images/ExpensifyHelp_R1_InviteMembers_3.png){:width="100%"} + # 4. Set admins Admins are members of your workspace that have permissions to manage the workspace. The table below shows the difference between member and admin permissions: diff --git a/docs/articles/new-expensify/getting-started/Join-your-company's-workspace.md b/docs/articles/new-expensify/getting-started/Join-your-company's-workspace.md index 9c5aea0c61ae..a4747c2d95e5 100644 --- a/docs/articles/new-expensify/getting-started/Join-your-company's-workspace.md +++ b/docs/articles/new-expensify/getting-started/Join-your-company's-workspace.md @@ -51,6 +51,8 @@ Upload your expenses and check your reports right from your phone by downloading {% include end-option.html %} {% include end-selector.html %} + +![The profile page to change options like display name]({{site.url}}/assets/images/ExpensifyHelp_R2_Profile_1.png){:width="100%"} # 3. Meet Concierge diff --git a/docs/articles/new-expensify/workspaces/Create-expense-categories.md b/docs/articles/new-expensify/workspaces/Create-expense-categories.md index 7b8d29d09d1c..6f810d8f96d0 100644 --- a/docs/articles/new-expensify/workspaces/Create-expense-categories.md +++ b/docs/articles/new-expensify/workspaces/Create-expense-categories.md @@ -8,6 +8,8 @@ In Expensify, categories refer to the **chart of accounts, GL accounts, expense An admin can manually create categories for a workspace, or they will be automatically imported if your workspace is connected to another platform such as QuickBooks Online, QuickBooks Desktop, Intacct, Xero, or NetSuite. These imported categories can be enabled or disabled to use as categories for expenses added to Expensify. Additionally, Expensify will learn how you apply categories to specific merchants over time and apply them automatically. +![The Categories tab]({{site.url}}/assets/images/ExpensifyHelp_R3_Categories_1.png){:width="100%"} + # Manually add or delete categories {% include selector.html values="desktop, mobile" %} diff --git a/docs/articles/new-expensify/workspaces/Create-expense-tags.md b/docs/articles/new-expensify/workspaces/Create-expense-tags.md index cf7bdd6fc6a7..e048ad9ca768 100644 --- a/docs/articles/new-expensify/workspaces/Create-expense-tags.md +++ b/docs/articles/new-expensify/workspaces/Create-expense-tags.md @@ -8,6 +8,8 @@ In Expensify, tags refer to **classes, projects, cost centers, locations, custom An admin can manually create tags for a workspace, or they will be automatically imported if your workspace is connected to an accounting system, like QuickBooks Online or Xero. These imported tags can be enabled or disabled to use as tags for expenses added to Expensify. Additionally, Expensify will learn how you apply tags to specific merchants over time and apply them automatically. +![The Tags tab]({{site.url}}/assets/images/ExpensifyHelp_R4_Tags_2.png){:width="100%"} + # Manually add or delete tags {% include selector.html values="desktop, mobile" %} @@ -24,6 +26,8 @@ To manually add a tag, 7. Click **Add Tag** in the top right. 8. Enter a name for the tag and click **Save**. +![The toggle to enable Tags]({{site.url}}/assets/images/ExpensifyHelp_R4_Tags_1.png){:width="100%"} + To delete a tag, 1. Click the tag on the Tags page. diff --git a/docs/assets/images/ExpensifyHelp-QBO-1.png b/docs/assets/images/ExpensifyHelp-QBO-1.png new file mode 100644 index 000000000000..c612cb760d58 Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-QBO-1.png differ diff --git a/docs/assets/images/ExpensifyHelp-QBO-2.png b/docs/assets/images/ExpensifyHelp-QBO-2.png new file mode 100644 index 000000000000..7fbc99503f2e Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-QBO-2.png differ diff --git a/docs/assets/images/ExpensifyHelp-QBO-3.png b/docs/assets/images/ExpensifyHelp-QBO-3.png new file mode 100644 index 000000000000..600a5903c05f Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-QBO-3.png differ diff --git a/docs/assets/images/ExpensifyHelp-Xero-1.png b/docs/assets/images/ExpensifyHelp-Xero-1.png new file mode 100644 index 000000000000..c612cb760d58 Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-Xero-1.png differ diff --git a/docs/assets/images/ExpensifyHelp-Xero-2.png b/docs/assets/images/ExpensifyHelp-Xero-2.png new file mode 100644 index 000000000000..7fbc99503f2e Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-Xero-2.png differ diff --git a/docs/assets/images/ExpensifyHelp-Xero-3.png b/docs/assets/images/ExpensifyHelp-Xero-3.png new file mode 100644 index 000000000000..e340a302bd89 Binary files /dev/null and b/docs/assets/images/ExpensifyHelp-Xero-3.png differ diff --git a/docs/assets/images/ExpensifyHelp_ApproveExpense_1.png b/docs/assets/images/ExpensifyHelp_ApproveExpense_1.png new file mode 100644 index 000000000000..8a721744860f Binary files /dev/null and b/docs/assets/images/ExpensifyHelp_ApproveExpense_1.png differ diff --git a/docs/assets/images/ExpensifyHelp_ApproveExpense_2.png b/docs/assets/images/ExpensifyHelp_ApproveExpense_2.png new file mode 100644 index 000000000000..25e11f0e7624 Binary files /dev/null and b/docs/assets/images/ExpensifyHelp_ApproveExpense_2.png differ diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 687d2bdbb662..eb251dc53b1a 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 9.0.1.7 + 9.0.1.14 FullStory OrgId diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index 514500458c71..efc72698918f 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 9.0.1.7 + 9.0.1.14 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 548446ce7280..6a414d579761 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.0.1 CFBundleVersion - 9.0.1.7 + 9.0.1.14 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index 7b4c0faa515a..db6fa70d9598 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.0.1-7", + "version": "9.0.1-14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.0.1-7", + "version": "9.0.1-14", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -106,7 +106,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.52", + "react-native-onyx": "2.0.53", "react-native-pager-view": "6.2.3", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", @@ -136,8 +136,7 @@ "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", - "semver": "^7.5.2", - "shim-keyboard-event-key": "^1.0.3" + "semver": "^7.5.2" }, "devDependencies": { "@actions/core": "1.10.0", @@ -31608,9 +31607,9 @@ } }, "node_modules/react-native-onyx": { - "version": "2.0.52", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.52.tgz", - "integrity": "sha512-uXlNhQg1UStx1W/U+9GYtIhLvx3vTIpN1WwE1gsiVxvimnUzKpQX/JBkgpR9b48ZoxsdiZXOT5kKLlqGCa6O1g==", + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.53.tgz", + "integrity": "sha512-ObNk5MhLOAVkLgE0NCI04CEO3qaP5ZG+NY1Kn3UnxcHlhyLlDQb10EOiDWSLwNR2s4K3kK+ge7Xmo6N0VdMyyA==", "dependencies": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", @@ -33920,10 +33919,6 @@ "shellcheck": "shellcheck-stable/shellcheck" } }, - "node_modules/shim-keyboard-event-key": { - "version": "1.0.3", - "license": "MIT" - }, "node_modules/side-channel": { "version": "1.0.4", "license": "MIT", diff --git a/package.json b/package.json index 115df5b986ca..f5261521cb7f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.0.1-7", + "version": "9.0.1-14", "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.", @@ -159,7 +159,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.52", + "react-native-onyx": "2.0.53", "react-native-pager-view": "6.2.3", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", @@ -189,8 +189,7 @@ "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", - "semver": "^7.5.2", - "shim-keyboard-event-key": "^1.0.3" + "semver": "^7.5.2" }, "devDependencies": { "@actions/core": "1.10.0", diff --git a/src/CONST.ts b/src/CONST.ts index dc8fb1040a76..f8ce1a574d49 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -363,6 +363,7 @@ const CONST = { SPOTNANA_TRAVEL: 'spotnanaTravel', NETSUITE_ON_NEW_EXPENSIFY: 'netsuiteOnNewExpensify', REPORT_FIELDS_FEATURE: 'reportFieldsFeature', + WORKSPACE_FEEDS: 'workspaceFeeds', }, BUTTON_STATES: { DEFAULT: 'default', @@ -941,20 +942,6 @@ const CONST = { RESIZE_DEBOUNCE_TIME: 100, UNREAD_UPDATE_DEBOUNCE_TIME: 300, }, - SEARCH_TABLE_COLUMNS: { - RECEIPT: 'receipt', - DATE: 'date', - MERCHANT: 'merchant', - DESCRIPTION: 'description', - FROM: 'from', - TO: 'to', - CATEGORY: 'category', - TAG: 'tag', - TOTAL_AMOUNT: 'amount', - TYPE: 'type', - ACTION: 'action', - TAX_AMOUNT: 'taxAmount', - }, PRIORITY_MODE: { GSD: 'gsd', DEFAULT: 'default', @@ -1264,6 +1251,7 @@ const CONST = { }, CENTRAL_PANE_ANIMATION_HEIGHT: 200, LHN_SKELETON_VIEW_ITEM_HEIGHT: 64, + SEARCH_SKELETON_VIEW_ITEM_HEIGHT: 108, EXPENSIFY_PARTNER_NAME: 'expensify.com', EMAIL: { ACCOUNTING: 'accounting@expensify.com', @@ -3540,12 +3528,7 @@ const CONST = { SCAN: 'scan', DISTANCE: 'distance', }, - TAB_SEARCH: { - ALL: 'all', - SHARED: 'shared', - DRAFTS: 'drafts', - FINISHED: 'finished', - }, + STATUS_TEXT_MAX_LENGTH: 100, DROPDOWN_BUTTON_SIZE: { @@ -4846,28 +4829,52 @@ const CONST = { ADHOC: ' AdHoc', }, - SEARCH_TRANSACTION_TYPE: { - CASH: 'cash', - CARD: 'card', - DISTANCE: 'distance', - }, - - SEARCH_RESULTS_PAGE_SIZE: 50, - - SEARCH_DATA_TYPES: { - TRANSACTION: 'transaction', - REPORT: 'report', + SEARCH: { + RESULTS_PAGE_SIZE: 50, + DATA_TYPES: { + TRANSACTION: 'transaction', + REPORT: 'report', + }, + ACTION_TYPES: { + DONE: 'done', + PAID: 'paid', + VIEW: 'view', + }, + TRANSACTION_TYPE: { + CASH: 'cash', + CARD: 'card', + DISTANCE: 'distance', + }, + SORT_ORDER: { + ASC: 'asc', + DESC: 'desc', + }, + TAB: { + ALL: 'all', + SHARED: 'shared', + DRAFTS: 'drafts', + FINISHED: 'finished', + }, + TABLE_COLUMNS: { + RECEIPT: 'receipt', + DATE: 'date', + MERCHANT: 'merchant', + DESCRIPTION: 'description', + FROM: 'from', + TO: 'to', + CATEGORY: 'category', + TAG: 'tag', + TOTAL_AMOUNT: 'amount', + TYPE: 'type', + ACTION: 'action', + TAX_AMOUNT: 'taxAmount', + }, }, REFERRER: { NOTIFICATION: 'notification', }, - SORT_ORDER: { - ASC: 'asc', - DESC: 'desc', - }, - SUBSCRIPTION_SIZE_LIMIT: 20000, PAYMENT_CARD_CURRENCY: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c2c5a4e70dfe..d354ee1c47fd 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -917,6 +917,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/taxes', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const, }, + RESTRICTED_ACTION: { + route: 'restricted-action/workspace/:policyID', + getRoute: (policyID: string) => `restricted-action/workspace/${policyID}` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5a6ec3675e24..d8a2d166099e 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -142,6 +142,7 @@ const SCREENS = { TRAVEL: 'Travel', SEARCH_REPORT: 'SearchReport', SETTINGS_CATEGORIES: 'SettingsCategories', + RESTRICTED_ACTION: 'RestrictedAction', }, ONBOARDING_MODAL: { ONBOARDING: 'Onboarding', @@ -384,6 +385,7 @@ const SCREENS = { KEYBOARD_SHORTCUTS: 'KeyboardShortcuts', TRANSACTION_RECEIPT: 'TransactionReceipt', FEATURE_TRAINING_ROOT: 'FeatureTraining_Root', + RESTRICTED_ACTION_ROOT: 'RestrictedAction_Root', } as const; type Screen = DeepValueOf; diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 17a2f6212447..9bd6142b5604 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -14,6 +14,7 @@ import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as UserLocation from '@libs/actions/UserLocation'; import * as ApiUtils from '@libs/ApiUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; import type {GeolocationErrorCodeType} from '@libs/getCurrentPosition/getCurrentPosition.types'; @@ -249,12 +250,17 @@ function AddressSearch( setIsFetchingCurrentLocation(false); setLocationErrorCode(null); + const {latitude, longitude} = successData.coords; + const location = { - lat: successData.coords.latitude, - lng: successData.coords.longitude, + lat: latitude, + lng: longitude, address: CONST.YOUR_LOCATION_TEXT, name: CONST.YOUR_LOCATION_TEXT, }; + + // Update the current user location + UserLocation.setUserLocation({longitude, latitude}); onPress?.(location); }, (errorData) => { diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index 9f42e9597c79..f54eddcbeb79 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -44,6 +44,8 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false const mutationObserverRef = useRef(null); const lastScrollOffsetRef = useRef(0); const isListRenderedRef = useRef(false); + const mvcpAutoscrollToTopThresholdRef = useRef(mvcpAutoscrollToTopThreshold); + mvcpAutoscrollToTopThresholdRef.current = mvcpAutoscrollToTopThreshold; const getScrollOffset = useCallback((): number => { if (!scrollRef.current) { @@ -105,11 +107,11 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false const scrollOffset = getScrollOffset(); prevFirstVisibleOffsetRef.current = firstVisibleViewOffset; scrollToOffset(scrollOffset + delta, false); - if (mvcpAutoscrollToTopThreshold != null && scrollOffset <= mvcpAutoscrollToTopThreshold) { + if (mvcpAutoscrollToTopThresholdRef.current != null && scrollOffset <= mvcpAutoscrollToTopThresholdRef.current) { scrollToOffset(0, true); } } - }, [getScrollOffset, scrollToOffset, mvcpMinIndexForVisible, mvcpAutoscrollToTopThreshold, horizontal]); + }, [getScrollOffset, scrollToOffset, mvcpMinIndexForVisible, horizontal]); const setupMutationObserver = useCallback(() => { const contentView = getContentView(); diff --git a/src/components/FocusTrap/FocusTrapForModal/index.web.tsx b/src/components/FocusTrap/FocusTrapForModal/index.web.tsx index 1ea425f0f9fd..be5da8c49a78 100644 --- a/src/components/FocusTrap/FocusTrapForModal/index.web.tsx +++ b/src/components/FocusTrap/FocusTrapForModal/index.web.tsx @@ -9,7 +9,7 @@ function FocusTrapForModal({children, active}: FocusTrapForModalProps) { active={active} focusTrapOptions={{ trapStack: sharedTrapStack, - allowOutsideClick: true, + clickOutsideDeactivates: true, initialFocus: false, fallbackFocus: document.body, }} diff --git a/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts b/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts index dd1a65cdb75a..be772c6ae10c 100644 --- a/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts +++ b/src/components/FocusTrap/WIDE_LAYOUT_INACTIVE_SCREENS.ts @@ -33,6 +33,8 @@ const WIDE_LAYOUT_INACTIVE_SCREENS: string[] = [ SCREENS.WORKSPACE.REPORT_FIELDS, SCREENS.WORKSPACE.DISTANCE_RATES, SCREENS.SEARCH.CENTRAL_PANE, + SCREENS.SETTINGS.TROUBLESHOOT, + SCREENS.SETTINGS.SAVE_THE_WORLD, ]; export default WIDE_LAYOUT_INACTIVE_SCREENS; diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 3fe36239d631..e699badc43ec 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -61,6 +61,7 @@ import House from '@assets/images/simple-illustrations/simple-illustration__hous import InvoiceBlue from '@assets/images/simple-illustrations/simple-illustration__invoice.svg'; import Lightbulb from '@assets/images/simple-illustrations/simple-illustration__lightbulb.svg'; import LockClosed from '@assets/images/simple-illustrations/simple-illustration__lockclosed.svg'; +import LockClosedOrange from '@assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg'; import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg'; import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg'; import Mailbox from '@assets/images/simple-illustrations/simple-illustration__mailbox.svg'; @@ -192,4 +193,5 @@ export { SendMoney, CheckmarkCircle, CreditCardEyes, + LockClosedOrange, }; diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index c6be99297182..283f7c396edb 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -25,7 +25,7 @@ import type {ComponentProps, MapViewOnyxProps} from './types'; import utils from './utils'; const MapView = forwardRef( - ({accessToken, style, mapPadding, userLocation: cachedUserLocation, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true}, ref) => { + ({accessToken, style, mapPadding, userLocation, styleURL, pitchEnabled, initialState, waypoints, directionCoordinates, onMapReady, interactive = true}, ref) => { const navigation = useNavigation(); const {isOffline} = useNetwork(); const {translate} = useLocalize(); @@ -34,7 +34,7 @@ const MapView = forwardRef( const cameraRef = useRef(null); const [isIdle, setIsIdle] = useState(false); const initialLocation = useMemo(() => initialState && {longitude: initialState.location[0], latitude: initialState.location[1]}, [initialState]); - const [currentPosition, setCurrentPosition] = useState(cachedUserLocation ?? initialLocation); + const currentPosition = userLocation ?? initialLocation; const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); const shouldInitializeCurrentPosition = useRef(true); @@ -50,7 +50,6 @@ const MapView = forwardRef( return; } UserLocation.clearUserLocation(); - setCurrentPosition(initialLocation); }, [initialLocation], ); @@ -74,7 +73,6 @@ const MapView = forwardRef( getCurrentPosition((params) => { const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - setCurrentPosition(currentCoords); UserLocation.setUserLocation(currentCoords); }, setCurrentPositionToInitialState); }, [isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index c573550e49fb..01d7134a96da 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -12,6 +12,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; +import usePrevious from '@hooks/usePrevious'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -39,7 +40,7 @@ const MapView = forwardRef( waypoints, mapPadding, accessToken, - userLocation: cachedUserLocation, + userLocation, directionCoordinates, initialState = {location: CONST.MAPBOX.DEFAULT_COORDINATE, zoom: CONST.MAPBOX.DEFAULT_ZOOM}, interactive = true, @@ -55,7 +56,8 @@ const MapView = forwardRef( const [mapRef, setMapRef] = useState(null); const initialLocation = useMemo(() => ({longitude: initialState.location[0], latitude: initialState.location[1]}), [initialState]); - const [currentPosition, setCurrentPosition] = useState(cachedUserLocation ?? initialLocation); + const currentPosition = userLocation ?? initialLocation; + const prevUserPosition = usePrevious(currentPosition); const [userInteractedWithMap, setUserInteractedWithMap] = useState(false); const [shouldResetBoundaries, setShouldResetBoundaries] = useState(false); const setRef = useCallback((newRef: MapRef | null) => setMapRef(newRef), []); @@ -73,7 +75,6 @@ const MapView = forwardRef( return; } UserLocation.clearUserLocation(); - setCurrentPosition(initialLocation); }, [initialLocation], ); @@ -97,7 +98,6 @@ const MapView = forwardRef( getCurrentPosition((params) => { const currentCoords = {longitude: params.coords.longitude, latitude: params.coords.latitude}; - setCurrentPosition(currentCoords); UserLocation.setUserLocation(currentCoords); }, setCurrentPositionToInitialState); }, [isOffline, shouldPanMapToCurrentPosition, setCurrentPositionToInitialState]), @@ -112,11 +112,15 @@ const MapView = forwardRef( return; } + // Avoid animating the naviagtion to the same location + const shouldAnimate = prevUserPosition.longitude !== currentPosition.longitude || prevUserPosition.latitude !== currentPosition.latitude; + mapRef.flyTo({ center: [currentPosition.longitude, currentPosition.latitude], zoom: CONST.MAPBOX.DEFAULT_ZOOM, + animate: shouldAnimate, }); - }, [currentPosition, userInteractedWithMap, mapRef, shouldPanMapToCurrentPosition]); + }, [currentPosition, mapRef, prevUserPosition, shouldPanMapToCurrentPosition]); const resetBoundaries = useCallback(() => { if (!waypoints || waypoints.length === 0) { diff --git a/src/components/Search.tsx b/src/components/Search.tsx index 93792120fe99..cfb0192669d1 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -35,7 +35,7 @@ type SearchProps = { sortOrder?: SortOrder; }; -const sortableSearchTabs: SearchQuery[] = [CONST.TAB_SEARCH.ALL]; +const sortableSearchTabs: SearchQuery[] = [CONST.SEARCH.TAB.ALL]; const transactionItemMobileHeight = 100; const reportItemTransactionHeight = 52; const listItemPadding = 12; // this is equivalent to 'mb3' on every transaction/report list item @@ -125,7 +125,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) { return; } const currentOffset = searchResults?.search?.offset ?? 0; - SearchActions.search({hash, query, offset: currentOffset + CONST.SEARCH_RESULTS_PAGE_SIZE, sortBy, sortOrder}); + SearchActions.search({hash, query, offset: currentOffset + CONST.SEARCH.RESULTS_PAGE_SIZE, sortBy, sortOrder}); }; const type = SearchUtils.getSearchType(searchResults?.search); diff --git a/src/components/SelectionList/Search/ActionCell.tsx b/src/components/SelectionList/Search/ActionCell.tsx new file mode 100644 index 000000000000..6aabfebf0da9 --- /dev/null +++ b/src/components/SelectionList/Search/ActionCell.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import {View} from 'react-native'; +import Badge from '@components/Badge'; +import Button from '@components/Button'; +import * as Expensicons from '@components/Icon/Expensicons'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; + +type ActionCellProps = { + onButtonPress: () => void; + action?: string; + isLargeScreenWidth?: boolean; +}; + +function ActionCell({onButtonPress, action = CONST.SEARCH.ACTION_TYPES.VIEW, isLargeScreenWidth = true}: ActionCellProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + + if (action === CONST.SEARCH.ACTION_TYPES.PAID || action === CONST.SEARCH.ACTION_TYPES.DONE) { + const buttonTextKey = action === CONST.SEARCH.ACTION_TYPES.PAID ? 'iou.settledExpensify' : 'common.done'; + return ( + + + + ); + } + + return ( +