From 4bec8ad020e658fdbed7ea4dc34867b3c73fdb93 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Fri, 24 Apr 2026 18:57:38 -0700 Subject: [PATCH 1/8] feat: add home screen long-press shortcuts via expo-quick-actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Help users about to uninstall Edge recover their accounts: add a warning shortcut linking to the "save 2FA & credentials before uninstalling" support article, and a direct "Contact Support" link. Use expo-quick-actions so titles/subtitles flow through lstrings and Crowdin instead of duplicating translations across 12 locales of InfoPlist.strings and values-*/strings.xml. Shortcuts are registered dynamically in JS, which means they only appear after the first app launch post-install — acceptable since the audience is established users considering uninstall, not fresh installs. The `import 'expo-modules-core'` side effect in index.ts is non- obvious but required: expo-quick-actions reads globalThis.expo.modules.ExpoQuickActions at module-evaluation time, and that global is installed lazily by expo-modules-core's NativeModule/SharedObject/EventEmitter module imports. Without one of those evaluating first the global stays empty and every expo-quick-actions call silently no-ops, including setItems, so the shortcuts never appear. --- CHANGELOG.md | 1 + index.ts | 1 + ios/Podfile.lock | 6 ++ package.json | 1 + src/components/App.tsx | 2 + .../services/QuickActionsManager.tsx | 51 +++++++++++ src/locales/en_US.ts | 6 ++ src/locales/strings/enUS.json | 4 + yarn.lock | 84 +++++++++---------- 9 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 src/components/services/QuickActionsManager.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index e680084d384..1215cb1d54b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased (develop) +- added: Home screen long-press shortcuts for "⚠️ Save 2FA First!" warning and "Contact Support". - added: Show swap KYC/terms modal for NExchange - added: Nym mixnet warning in Stake, Unstake, and Claim Rewards scenes - changed: Migrate Thorchain Savers and Thorchain Yield endpoints off NineRealms to gateway.liquify.com. diff --git a/index.ts b/index.ts index a621e39496c..d3a7346aec2 100644 --- a/index.ts +++ b/index.ts @@ -1,3 +1,4 @@ +import 'expo-modules-core' import 'react-native-gesture-handler' import './src/app' import './src/perf' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5fc5a714e69..584bb278d19 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -87,6 +87,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - ExpoQuickActions (5.0.0): + - ExpoModulesCore - fast_float (6.1.4) - FBLazyVector (0.79.2) - Firebase/CoreOnly (10.29.0): @@ -2889,6 +2891,7 @@ DEPENDENCIES: - ExpoFont (from `../node_modules/expo-font/ios`) - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) + - ExpoQuickActions (from `../node_modules/expo-quick-actions/ios`) - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) @@ -3089,6 +3092,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-keep-awake/ios" ExpoModulesCore: :path: "../node_modules/expo-modules-core" + ExpoQuickActions: + :path: "../node_modules/expo-quick-actions/ios" fast_float: :podspec: "../node_modules/react-native/third-party-podspecs/fast_float.podspec" FBLazyVector: @@ -3347,6 +3352,7 @@ SPEC CHECKSUMS: ExpoFont: cf508bc2e6b70871e05386d71cab927c8524cc8e ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7 ExpoModulesCore: 471ae18809dc8a5c9a623193a317eef6048a4f8a + ExpoQuickActions: fdbda7f5874aed3dd2b1d891ec00ab3300dc7541 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 84b955f7b4da8b895faf5946f73748267347c975 Firebase: cec914dab6fd7b1bd8ab56ea07ce4e03dd251c2d diff --git a/package.json b/package.json index 345e1ec54dc..53db9e78926 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "edge-login-ui-rn": "^3.35.5", "ethers": "^5.7.2", "expo": "^53.0.0", + "expo-quick-actions": "^5.0.0", "jsrsasign": "^11.1.0", "marked": "^15.0.9", "p-debounce": "^4.0.0", diff --git a/src/components/App.tsx b/src/components/App.tsx index 0e2a98349e2..5f73973ea2d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -10,6 +10,7 @@ import { SafeAreaProvider } from 'react-native-safe-area-context' import { useHandler } from '../hooks/useHandler' import { CrashScene } from './scenes/CrashScene' import { EdgeCoreManager } from './services/EdgeCoreManager' +import { QuickActionsManager } from './services/QuickActionsManager' import { StatusBarManager } from './services/StatusBarManager' import { ThemeProvider } from './services/ThemeContext' @@ -38,6 +39,7 @@ const MainApp: React.FC = () => { fallback={} > + diff --git a/src/components/services/QuickActionsManager.tsx b/src/components/services/QuickActionsManager.tsx new file mode 100644 index 00000000000..4f398c8fd46 --- /dev/null +++ b/src/components/services/QuickActionsManager.tsx @@ -0,0 +1,51 @@ +import * as QuickActions from 'expo-quick-actions' +import { useQuickActionCallback } from 'expo-quick-actions/hooks' +import * as React from 'react' +import { Linking, Platform } from 'react-native' + +import { useHandler } from '../../hooks/useHandler' +import { lstrings } from '../../locales/strings' +import { showError } from './AirshipInstance' + +const DO_NOT_UNINSTALL_URL = + 'https://support.edge.app/en/articles/14439418-warning-don-t-uninstall-edge-without-your-login-credentials' +const CONTACT_SUPPORT_URL = + 'https://support.edge.app/en/articles/14054649-need-help-reach-out-via-our-chat-bubble?chat=open' + +export const QuickActionsManager: React.FC = () => { + React.useEffect(() => { + QuickActions.setItems([ + { + id: 'do_not_uninstall', + title: lstrings.shortcut_do_not_uninstall_title, + subtitle: lstrings.shortcut_do_not_uninstall_subtitle, + icon: Platform.select({ + ios: 'symbol:nosign', + default: 'prohibit' + }), + params: { url: DO_NOT_UNINSTALL_URL } + }, + { + id: 'contact_support', + title: lstrings.shortcut_contact_support_title, + subtitle: lstrings.shortcut_contact_support_subtitle, + icon: Platform.select({ + ios: 'symbol:message.fill', + default: 'message' + }), + params: { url: CONTACT_SUPPORT_URL } + } + ]).catch(showError) + }, []) + + const handleQuickAction = useHandler((action: QuickActions.Action) => { + const url = action.params?.url + if (typeof url === 'string') { + Linking.openURL(url).catch(showError) + } + }) + + useQuickActionCallback(handleQuickAction) + + return null +} diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index d451fd23a61..0d81d758985 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -2551,6 +2551,12 @@ const strings = { 'Please ensure all details are correct before making the transfer.', // #endregion + // Home screen long-press shortcuts (expo-quick-actions) + shortcut_do_not_uninstall_title: '⚠️ Save 2FA First!', + shortcut_do_not_uninstall_subtitle: 'Login requires 2FA & credentials!', + shortcut_contact_support_title: 'Contact Support', + shortcut_contact_support_subtitle: 'Get help from our live support agents', + unknown_error_message: 'An unknown error occurred.' } as const diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 584b22395b4..a55e99ee014 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1990,5 +1990,9 @@ "ramp_account_number_label": "Account Number", "ramp_routing_number_label": "Routing Number", "ramp_bank_routing_warning": "Please ensure all details are correct before making the transfer.", + "shortcut_do_not_uninstall_title": "⚠️ Save 2FA First!", + "shortcut_do_not_uninstall_subtitle": "Login requires 2FA & credentials!", + "shortcut_contact_support_title": "Contact Support", + "shortcut_contact_support_subtitle": "Get help from our live support agents", "unknown_error_message": "An unknown error occurred." } diff --git a/yarn.lock b/yarn.lock index e125ef7cc9b..126a5206a82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1063,20 +1063,7 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" - integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.0" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.0" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.0" - debug "^4.3.1" - -"@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.25.3", "@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.28.0", "@babel/traverse@^7.7.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.0.tgz#518aa113359b062042379e333db18380b537e34b" integrity sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg== @@ -1973,7 +1960,7 @@ resolve-from "^5.0.0" semver "^7.6.0" -"@expo/image-utils@^0.7.6": +"@expo/image-utils@^0.7.6", "@expo/image-utils@~0.7.4": version "0.7.6" resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.7.6.tgz#b8442bef770e1c7b39997d57f666bffeeced0a7a" integrity sha512-GKnMqC79+mo/1AFrmAcUcGfbsXXTRqOMNS1umebuevl3aaw+ztsYEFEiuNhHZW7PQ3Xs3URNT513ZxKhznDscw== @@ -6771,7 +6758,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.0.0, ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== @@ -6798,6 +6785,16 @@ ajv@^8.0.0, ajv@^8.8.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" +ajv@^8.9.0: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9" + integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + algo-msgpack-with-bigint@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz#38bb717220525b3ff42232eefdcd9efb9ad405d6" @@ -10621,6 +10618,15 @@ expo-modules-core@2.5.0: dependencies: invariant "^2.2.4" +expo-quick-actions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/expo-quick-actions/-/expo-quick-actions-5.0.0.tgz#6b3c5c21190607f779be1588ebaf119577a3c120" + integrity sha512-NSsDhfbal11gXsHkJbvYVw7x0QUCKrEth2kBBKZUv03dX4J7ZPADSV89LyEpOVYXCkrw6LuanlEtKavg/BFaRA== + dependencies: + "@expo/image-utils" "~0.7.4" + schema-utils "^4.2.0" + sf-symbols-typescript "^2.1.0" + expo@^53.0.0: version "53.0.20" resolved "https://registry.yarnpkg.com/expo/-/expo-53.0.20.tgz#e11b553322de313d47c407367d8d48d3a073e3cc" @@ -16414,6 +16420,16 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +schema-utils@^4.2.0: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" @@ -16658,6 +16674,11 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sf-symbols-typescript@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz#926d6e0715e3d8784cadf7658431e36581254208" + integrity sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw== + sha.js@^2.3.6, sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.12, sha.js@^2.4.8: version "2.4.12" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.12.tgz#eb8b568bf383dfd1867a32c3f2b74eb52bdbf23f" @@ -17138,16 +17159,7 @@ string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -17256,7 +17268,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -17270,13 +17282,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -18664,7 +18669,7 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -18682,15 +18687,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 8a75023d59391b7e64561bed74f141ab4de1960a Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 10:45:08 -0700 Subject: [PATCH 2/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- src/components/services/QuickActionsManager.tsx | 12 +++++------- src/theme/edgeConfig.ts | 8 +++++++- src/types/types.ts | 8 ++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/services/QuickActionsManager.tsx b/src/components/services/QuickActionsManager.tsx index 4f398c8fd46..e30144892d6 100644 --- a/src/components/services/QuickActionsManager.tsx +++ b/src/components/services/QuickActionsManager.tsx @@ -5,15 +5,13 @@ import { Linking, Platform } from 'react-native' import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' +import { config } from '../../theme/appConfig' import { showError } from './AirshipInstance' -const DO_NOT_UNINSTALL_URL = - 'https://support.edge.app/en/articles/14439418-warning-don-t-uninstall-edge-without-your-login-credentials' -const CONTACT_SUPPORT_URL = - 'https://support.edge.app/en/articles/14054649-need-help-reach-out-via-our-chat-bubble?chat=open' - export const QuickActionsManager: React.FC = () => { React.useEffect(() => { + const { quickActions } = config + if (quickActions == null) return QuickActions.setItems([ { id: 'do_not_uninstall', @@ -23,7 +21,7 @@ export const QuickActionsManager: React.FC = () => { ios: 'symbol:nosign', default: 'prohibit' }), - params: { url: DO_NOT_UNINSTALL_URL } + params: { url: quickActions.uninstallWarningUrl } }, { id: 'contact_support', @@ -33,7 +31,7 @@ export const QuickActionsManager: React.FC = () => { ios: 'symbol:message.fill', default: 'message' }), - params: { url: CONTACT_SUPPORT_URL } + params: { url: quickActions.contactSupportUrl } } ]).catch(showError) }, []) diff --git a/src/theme/edgeConfig.ts b/src/theme/edgeConfig.ts index ec16637a1d4..6eb1376174f 100644 --- a/src/theme/edgeConfig.ts +++ b/src/theme/edgeConfig.ts @@ -37,5 +37,11 @@ export const edgeConfig: AppConfig = { supportSite: 'https://help.edge.app/support/tickets/new', termsOfServiceSite: 'https://edge.app/tos/', website: 'https://edge.app', - supportChatSite: 'https://support.edge.app/hc/en-us?chat=open' + supportChatSite: 'https://support.edge.app/hc/en-us?chat=open', + quickActions: { + uninstallWarningUrl: + 'https://support.edge.app/en/articles/14439418-warning-don-t-uninstall-edge-without-your-login-credentials', + contactSupportUrl: + 'https://support.edge.app/en/articles/14054649-need-help-reach-out-via-our-chat-bubble?chat=open' + } } diff --git a/src/types/types.ts b/src/types/types.ts index 0706fc503fd..e0d1717f143 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -382,6 +382,14 @@ export interface AppConfig { * Support article for pending transactions "Learn more" link. */ pendingTxLearnMoreUrl?: string + /** + * Home screen long-press quick action shortcuts. + * Omit to disable the shortcuts for a build. + */ + quickActions?: { + uninstallWarningUrl: string + contactSupportUrl: string + } extraTab?: { webviewUrl: string tabType: 'edgeProvider' | 'webview' From e47165977c3229f6e4ce190ac65bea4700602c62 Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 10:46:41 -0700 Subject: [PATCH 3/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- .../services/QuickActionsManager.tsx | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/components/services/QuickActionsManager.tsx b/src/components/services/QuickActionsManager.tsx index e30144892d6..caf28c5816a 100644 --- a/src/components/services/QuickActionsManager.tsx +++ b/src/components/services/QuickActionsManager.tsx @@ -1,45 +1,57 @@ import * as QuickActions from 'expo-quick-actions' import { useQuickActionCallback } from 'expo-quick-actions/hooks' -import * as React from 'react' +import type * as React from 'react' import { Linking, Platform } from 'react-native' +import { useAsyncEffect } from '../../hooks/useAsyncEffect' import { useHandler } from '../../hooks/useHandler' import { lstrings } from '../../locales/strings' import { config } from '../../theme/appConfig' import { showError } from './AirshipInstance' export const QuickActionsManager: React.FC = () => { - React.useEffect(() => { - const { quickActions } = config - if (quickActions == null) return - QuickActions.setItems([ - { - id: 'do_not_uninstall', - title: lstrings.shortcut_do_not_uninstall_title, - subtitle: lstrings.shortcut_do_not_uninstall_subtitle, - icon: Platform.select({ - ios: 'symbol:nosign', - default: 'prohibit' - }), - params: { url: quickActions.uninstallWarningUrl } - }, - { - id: 'contact_support', - title: lstrings.shortcut_contact_support_title, - subtitle: lstrings.shortcut_contact_support_subtitle, - icon: Platform.select({ - ios: 'symbol:message.fill', - default: 'message' - }), - params: { url: quickActions.contactSupportUrl } + useAsyncEffect( + async () => { + const { quickActions } = config + if (quickActions == null) return + try { + await QuickActions.setItems([ + { + id: 'do_not_uninstall', + title: lstrings.shortcut_do_not_uninstall_title, + subtitle: lstrings.shortcut_do_not_uninstall_subtitle, + icon: Platform.select({ + ios: 'symbol:nosign', + default: 'prohibit' + }), + params: { url: quickActions.uninstallWarningUrl } + }, + { + id: 'contact_support', + title: lstrings.shortcut_contact_support_title, + subtitle: lstrings.shortcut_contact_support_subtitle, + icon: Platform.select({ + ios: 'symbol:message.fill', + default: 'message' + }), + params: { url: quickActions.contactSupportUrl } + } + ]) + } catch (error: unknown) { + showError(error) } - ]).catch(showError) - }, []) + }, + [], + 'QuickActionsManager' + ) - const handleQuickAction = useHandler((action: QuickActions.Action) => { + const handleQuickAction = useHandler(async (action: QuickActions.Action) => { const url = action.params?.url - if (typeof url === 'string') { - Linking.openURL(url).catch(showError) + if (typeof url !== 'string') return + try { + await Linking.openURL(url) + } catch (error: unknown) { + showError(error) } }) From 34af354e53316b42d5bf9a287a3a45da01d40364 Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 10:47:18 -0700 Subject: [PATCH 4/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- android/app/src/main/res/drawable/message.xml | 11 +++++++++++ android/app/src/main/res/drawable/prohibit.xml | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 android/app/src/main/res/drawable/message.xml create mode 100644 android/app/src/main/res/drawable/prohibit.xml diff --git a/android/app/src/main/res/drawable/message.xml b/android/app/src/main/res/drawable/message.xml new file mode 100644 index 00000000000..1404fef6584 --- /dev/null +++ b/android/app/src/main/res/drawable/message.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/android/app/src/main/res/drawable/prohibit.xml b/android/app/src/main/res/drawable/prohibit.xml new file mode 100644 index 00000000000..2735fe7ae15 --- /dev/null +++ b/android/app/src/main/res/drawable/prohibit.xml @@ -0,0 +1,11 @@ + + + + + From 108710ccd10d6ab0179ef1e164e726f9a3d9ea73 Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 10:47:43 -0700 Subject: [PATCH 5/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/index.ts b/index.ts index d3a7346aec2..3a27fe7aef1 100644 --- a/index.ts +++ b/index.ts @@ -1,3 +1,8 @@ +// This side-effect import must come before expo-quick-actions is evaluated: +// expo-quick-actions reads globalThis.expo.modules.ExpoQuickActions at module +// evaluation time, and that global is installed lazily by expo-modules-core. +// Without it, every expo-quick-actions call (including setItems) silently +// no-ops and the home screen shortcuts never appear. import 'expo-modules-core' import 'react-native-gesture-handler' import './src/app' From c96ec2dbe6e606990f493bf86aceb6fd5212906d Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 10:48:26 -0700 Subject: [PATCH 6/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- src/locales/en_US.ts | 2 +- src/locales/strings/enUS.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index 0d81d758985..3b16349b969 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -2555,7 +2555,7 @@ const strings = { shortcut_do_not_uninstall_title: '⚠️ Save 2FA First!', shortcut_do_not_uninstall_subtitle: 'Login requires 2FA & credentials!', shortcut_contact_support_title: 'Contact Support', - shortcut_contact_support_subtitle: 'Get help from our live support agents', + shortcut_contact_support_subtitle: 'Get help from our support team', unknown_error_message: 'An unknown error occurred.' } as const diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index a55e99ee014..e94aac8aac8 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1993,6 +1993,6 @@ "shortcut_do_not_uninstall_title": "⚠️ Save 2FA First!", "shortcut_do_not_uninstall_subtitle": "Login requires 2FA & credentials!", "shortcut_contact_support_title": "Contact Support", - "shortcut_contact_support_subtitle": "Get help from our live support agents", + "shortcut_contact_support_subtitle": "Get help from our support team", "unknown_error_message": "An unknown error occurred." } From fdff64ef9e564b59589a123d21965302f340b5c1 Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 11:46:32 -0700 Subject: [PATCH 7/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- .../src/main/java/co/edgesecure/app/MainActivity.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/co/edgesecure/app/MainActivity.kt b/android/app/src/main/java/co/edgesecure/app/MainActivity.kt index 178abd63ef8..f7ea6a3ac21 100644 --- a/android/app/src/main/java/co/edgesecure/app/MainActivity.kt +++ b/android/app/src/main/java/co/edgesecure/app/MainActivity.kt @@ -8,6 +8,7 @@ import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate import com.zoontek.rnbootsplash.RNBootSplash +import expo.modules.ReactActivityDelegateWrapper class MainActivity : ReactActivity() { /** @@ -18,10 +19,16 @@ class MainActivity : ReactActivity() { /** * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]. + * The Expo wrapper forwards activity lifecycle events (onCreate intent capture, onNewIntent) + * to expo modules; expo-quick-actions needs it to deliver shortcut taps on cold start. */ override fun createReactActivityDelegate(): ReactActivityDelegate = - DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + ReactActivityDelegateWrapper( + this, + BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + ) // Edge addition override fun onCreate(savedInstanceState: Bundle?) { From 2951d4d36c3955038d738b95125b9d1d740b3b72 Mon Sep 17 00:00:00 2001 From: peachbits Date: Thu, 11 Jun 2026 12:46:36 -0700 Subject: [PATCH 8/8] fixup! feat: add home screen long-press shortcuts via expo-quick-actions --- ios/edge/AppDelegate.swift | 24 ++++++++++++++++++++++++ patches/expo-quick-actions+5.0.0.patch | 13 +++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 patches/expo-quick-actions+5.0.0.patch diff --git a/ios/edge/AppDelegate.swift b/ios/edge/AppDelegate.swift index 9483d70d2ba..5e72ff52b2e 100644 --- a/ios/edge/AppDelegate.swift +++ b/ios/edge/AppDelegate.swift @@ -1,3 +1,4 @@ +import ExpoQuickActions import Firebase import FirebaseMessaging import RNBootSplash @@ -56,6 +57,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Client-side background fetch interval: application.setMinimumBackgroundFetchInterval(60 * 60 * 12) + // Capture a home-screen quick action on a cold launch so + // expo-quick-actions can report it as the initial action once JS loads. + // This delegate is not an ExpoAppDelegate, so the library's own + // subscriber never runs. Edge addition. + if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem { + ExpoQuickActions.initialAction = shortcutItem + } + // React Native template code: let delegate = ReactNativeDelegate() let factory = RCTReactNativeFactory(delegate: delegate) @@ -75,6 +84,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + /** + * Handles home-screen quick action taps while the app is running. + * Mirrors ExpoQuickActionsAppDelegate, which never runs because this + * delegate is not an ExpoAppDelegate. Edge addition. + */ + func application( + _ application: UIApplication, + performActionFor shortcutItem: UIApplicationShortcutItem, + completionHandler: @escaping (Bool) -> Void + ) { + NotificationCenter.default.post( + name: Notification.Name("onQuickAction"), object: shortcutItem) + completionHandler(true) + } + /** * Periodic background fetch logic. * Edge addition. diff --git a/patches/expo-quick-actions+5.0.0.patch b/patches/expo-quick-actions+5.0.0.patch new file mode 100644 index 00000000000..e4a4636711e --- /dev/null +++ b/patches/expo-quick-actions+5.0.0.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/expo-quick-actions/ios/ExpoQuickActionsModule.swift b/node_modules/expo-quick-actions/ios/ExpoQuickActionsModule.swift +index 252da49..12a29e4 100644 +--- a/node_modules/expo-quick-actions/ios/ExpoQuickActionsModule.swift ++++ b/node_modules/expo-quick-actions/ios/ExpoQuickActionsModule.swift +@@ -71,7 +71,7 @@ func toActionObject(item: UIApplicationShortcutItem?) -> ActionObject? { + ) + } + +-var initialAction: UIApplicationShortcutItem? ++public var initialAction: UIApplicationShortcutItem? + + public class ExpoQuickActionsModule: Module { +