diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0624054c9..b0ab08d52 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to ArConnect +# Contributing to Wander -Thank you for your interest in contributing to the ArConnect extension! Below are the guidelines to help you get started. +Thank you for your interest in contributing to the Wander extension! Below are the guidelines to help you get started. > **Important:** Always work from the "development" branch when making contributions. This ensures your changes are based on the latest development version. diff --git a/.github/README.md b/.github/README.md index 3bc2368c1..a9d345cb8 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,10 +1,10 @@ -# ArConnect +# Wander -ArConnect is a browser extension allowing Arweave wallet holders to interact with dApps securely and easily. +Wander is a browser extension allowing Arweave wallet holders to interact with dApps securely and easily. ## API -You can interact with basic ArConnect functionalities using [`arweave-js`](https://npmjs.com/arweave). To create a transaction, you just don't pass in the user's wallet instance: +You can interact with basic Wander functionalities using [`arweave-js`](https://npmjs.com/arweave). To create a transaction, you just don't pass in the user's wallet instance: ```ts const tx = await arweave.createTransaction({ @@ -12,7 +12,7 @@ const tx = await arweave.createTransaction({ }); ``` -Than, you can use ArConnect to add the users wallet to the transaction and sign it (as before, you don't pass in the user's wallet instance, that is done by ArConnect): +Than, you can use Wander to add the users wallet to the transaction and sign it (as before, you don't pass in the user's wallet instance, that is done by Wander): ```ts await arweave.transactions.sign(tx); @@ -22,21 +22,21 @@ Done! Now you can post the transaction. ## Events -ArConnect has some useful custom events. +Wander has some useful custom events. ### `arweaveWalletLoaded` -Triggers when the ArConnect global object (`window.arweaveWallet`) is injected into the page. It can be useful when executing functions on page load. +Triggers when the Wander global object (`window.arweaveWallet`) is injected into the page. It can be useful when executing functions on page load. ```ts window.addEventListener("arweaveWalletLoaded", () => { - /** Handle ArConnect load event **/ + /** Handle Wander load event **/ }); ``` ### `walletSwitch` -Triggers, when the user switches their wallet in the ArConnect extension popup. +Triggers, when the user switches their wallet in the Wander extension popup. ```ts window.addEventListener("walletSwitch", (e) => { @@ -49,13 +49,13 @@ Requires the `ACCESS_ADDRESS` and the `ACCESS_ALL_ADDRESSES` [permissions](#perm ## Other supported functions -ArConnect supports much more with it's powerful API. These features are not integrated into arweave-js right now, but please let us know if you would like to see them added or not. You can access all of these using the global `window.arweaveWallet` object (`window.arweaveWallet.getActiveAddress()`, etc.). +Wander supports much more with it's powerful API. These features are not integrated into arweave-js right now, but please let us know if you would like to see them added or not. You can access all of these using the global `window.arweaveWallet` object (`window.arweaveWallet.getActiveAddress()`, etc.). All of these functions are asynchronous, so you will need to `await` them. If you are using Typescript, read [this](#typescript-types) for type declarations. ### `connect(permissions, appInfo?, gateway?)` -Connect to ArConnect and request permissions. This function can always be called again if you want to request more permissions for your site. See the available permissions [here](#permissions). +Connect to Wander and request permissions. This function can always be called again if you want to request more permissions for your site. See the available permissions [here](#permissions). - `permissions`: An array of [permissions](#permissions) - `appInfo`: Optional information about your application (see the [format](#app-info)) @@ -82,7 +82,7 @@ Connect to ArConnect and request permissions. This function can always be called ### `disconnect()` -Disconnect from ArConnect. Removes all permissions from your site. +Disconnect from Wander. Removes all permissions from your site. ### `getActiveAddress(): Promise` @@ -102,7 +102,7 @@ Requires the `ACCESS_PUBLIC_KEY` [permission](#permissions). ### `getAllAddresses(): Promise` -Get all addresses added to the ArConnect extension +Get all addresses added to the Wander extension - `returns`: A list of the added wallets' addresses. @@ -163,7 +163,7 @@ Requires the `DECRYPT` [permission](#permissions). ### ~~`signature(data, options): Promise`~~ -> ⚠️ **Deprecation warning:** The `signature()` function is deprecated in ArConnect 1.0.0. Read about the alternatives below. +> ⚠️ **Deprecation warning:** The `signature()` function is deprecated in Wander 1.0.0. Read about the alternatives below. #### Alternatives @@ -215,7 +215,7 @@ Requires the `SIGNATURE` [permission](#permissions). #### Options -ArConnect allows you to customize the hash algorithm (`SHA-256` by default): +Wander allows you to customize the hash algorithm (`SHA-256` by default): ```ts export interface SignMessageOptions { @@ -229,7 +229,7 @@ Verify validity of a cryptographic signature for a given piece of data - `data`: `ArrayBuffer` data to verify against the signature - `signature`: `ArrayBuffer | string` Signature to validate -- `publicKey?`: `string` Arweave wallet `JWK.n` field, tx owner field or [public key from ArConnect](#getactivepublickey-promisestring) +- `publicKey?`: `string` Arweave wallet `JWK.n` field, tx owner field or [public key from Wander](#getactivepublickey-promisestring) - `options`: [`SignMessageOptions`](#options) Configuration for the signature
- `returns`: `Boolean` Validity of the signature @@ -252,7 +252,7 @@ Requires the `ACCESS_ARWEAVE_CONFIG` [permission](#permissions). ### `addToken(id, type?, gateway?)` -Add a token to the user's wallet (ArConnect). The token will show up in ArConnect assets / collectibles. +Add a token to the user's wallet (Wander). The token will show up in Wander assets / collectibles. > **Note:** You do not need to be connected in order to add a token @@ -260,11 +260,11 @@ Add a token to the user's wallet (ArConnect). The token will show up in ArConnec - `type`: Optional token type (`asset` or `collectible`) - `gateway`: Optional gateway to fetch the token from (see the [format](#arweave-config)) -> **Warning:** If the gateway is defined, ArConnect will not use the default Warp Mainnet Gateway, but the custom one. This might slow down evaluation! +> **Warning:** If the gateway is defined, Wander will not use the default Warp Mainnet Gateway, but the custom one. This might slow down evaluation! ### `isTokenAdded(id)` -Check if a token has been added to the user's wallet (ArConnect). +Check if a token has been added to the user's wallet (Wander). - `id`: ID of the token to add
@@ -277,13 +277,13 @@ There are 8 permissions currently available. When calling `connect`, you need to The permissions: - `ACCESS_ADDRESS`: - Access the current address selected in ArConnect + Access the current address selected in Wander - `ACCESS_PUBLIC_KEY` - Access the public key of the current address selected in ArConnect + Access the public key of the current address selected in Wander - `ACCESS_ALL_ADDRESSES`: - Access all addresses added to ArConnect + Access all addresses added to Wander - `SIGN_TRANSACTION`: Sign a transaction @@ -317,7 +317,7 @@ The user can set a custom Arweave config in the extension. It implements the fol ## Typescript types -To support ArConnect types, you can install the npm package `arconnect`, like this: +To support Wander types, you can install the npm package `arconnect`, like this: ```sh npm i -D arconnect diff --git a/assets/setup/explore_tour.png b/assets/setup/explore_tour.png index 8357f6997..fa72bb67f 100644 Binary files a/assets/setup/explore_tour.png and b/assets/setup/explore_tour.png differ diff --git a/assets/setup/send_tour.png b/assets/setup/send_tour.png index 6d7f5c6f4..00d5fe0eb 100644 Binary files a/assets/setup/send_tour.png and b/assets/setup/send_tour.png differ diff --git a/src/components/auth/Message.tsx b/src/components/auth/Message.tsx index c68e168e7..033bcecd7 100644 --- a/src/components/auth/Message.tsx +++ b/src/components/auth/Message.tsx @@ -77,8 +77,10 @@ const EncodingSelect = styled.select` background-color: transparent; `; -const MessageText = styled(Text).attrs({ - noMargin: true -})` +const MessageText = styled.div` font-size: 0.9rem; + word-wrap: break-word; + white-space: pre-wrap; + height: 200px; + overflow-y: auto; `; diff --git a/src/components/hardware/HardwareWalletTheme.tsx b/src/components/hardware/HardwareWalletTheme.tsx index 5f546263b..9e56bc33f 100644 --- a/src/components/hardware/HardwareWalletTheme.tsx +++ b/src/components/hardware/HardwareWalletTheme.tsx @@ -22,7 +22,7 @@ import { function hardwareThemeModifier(theme: ArconnectTheme): ArconnectTheme { return { ...theme, - theme: "154, 184, 255", + theme: "#9AB8FF", primary: "#9AB8FF", primaryBtnHover: "#6F93E1" }; diff --git a/src/components/popup/list/TokenListItem.tsx b/src/components/popup/list/TokenListItem.tsx index c59b3bf0d..9bbe9ab81 100644 --- a/src/components/popup/list/TokenListItem.tsx +++ b/src/components/popup/list/TokenListItem.tsx @@ -94,6 +94,7 @@ const DivDescriptionWrapper = styled.div` const DivTitleWrapper = styled.div` font-size: 1rem; font-weight: 600; + color: ${(props) => props.theme.primaryText}; `; const DivListItem = styled.div` @@ -122,7 +123,7 @@ const ImgTokenLogo = styled.img.attrs({ const SpanTokenType = styled.span` padding: 0.08rem 0.2rem; background-color: rgb(${(props) => props.theme.theme}); - color: #fff; + color: ${(props) => props.theme.primaryText}; font-weight: 500; font-size: 0.5rem; text-transform: uppercase; diff --git a/src/lib/coingecko.ts b/src/lib/coingecko.ts index 610a77827..ff154745a 100644 --- a/src/lib/coingecko.ts +++ b/src/lib/coingecko.ts @@ -3,6 +3,7 @@ import BigNumber from "bignumber.js"; import { useState, useCallback, useEffect } from "react"; import redstone from "redstone-api"; import { retryWithDelay } from "~utils/promises/retry"; +import { ExtensionStorage } from "~utils/storage"; /** * Compare two currencies @@ -30,15 +31,7 @@ export async function getArPrice(currency: string) { try { return await getPrice("arweave", currency.toLowerCase()); } catch (error) { - console.error(error, "redirecting to redstone"); - - const res = await redstone.getPrice("AR"); - - if (!res.value) { - return 0; - } - - return res.source.coingecko; + throw new Error("Failed to fetch AR price"); } } @@ -52,8 +45,20 @@ export function useArPrice(currency: string) { queryKey: ["arPrice", currency], queryFn: async () => { if (!currency) return "0"; - const result = await getArPrice(currency); - return String(result || "0"); + + try { + const result = await getArPrice(currency); + if (result) { + await ExtensionStorage.set("last_saved_price", String(result)); + return String(result); + } + } catch (error) { + const lastPrice = await ExtensionStorage.get( + "last_saved_price" + ); + if (lastPrice) return lastPrice; + } + return "0"; }, select: (data) => data || "0", retry: 3, diff --git a/src/routes/popup/purchase.tsx b/src/routes/popup/purchase.tsx index 1b4431a9f..4c6c0bafb 100644 --- a/src/routes/popup/purchase.tsx +++ b/src/routes/popup/purchase.tsx @@ -3,10 +3,13 @@ import { ListItem, ButtonV2, Loading, - useToasts, ListItemIcon } from "@arconnect/components"; -import { Input as InputV2, useInput } from "@arconnect/components-rebrand"; +import { + Input as InputV2, + useInput, + useToasts +} from "@arconnect/components-rebrand"; import browser from "webextension-polyfill"; import { Bank, BankNote01, ChevronDown } from "@untitled-ui/icons-react"; import switchIcon from "url:/assets/ecosystem/switch-vertical.svg"; @@ -26,6 +29,7 @@ import arLogo from "url:/assets/ecosystem/ar-logo.svg"; import CommonImage from "~components/common/Image"; import getSymbolFromCurrency from "currency-symbol-map"; import { useStorage } from "@plasmohq/storage/hook"; +import { WarningIcon } from "~components/popup/Token"; export function PurchaseView() { const { navigate } = useLocation(); @@ -42,6 +46,8 @@ export function PurchaseView() { const [quote, setQuote] = useState(); const [exchangeRate, setExchangeRate] = useState(0); const [payInputValue, setpayInputValue] = useState(""); + const [unavailableQuote, setUnavailableQuote] = useState(false); + const [error, setError] = useState(null); const { setToast } = useToasts(); const theme = useTheme(); @@ -70,6 +76,10 @@ export function PurchaseView() { if (quote) { const rate = quote.fiatAmount / quote.cryptoAmount; setExchangeRate(rate); + setUnavailableQuote(false); + } else { + setExchangeRate(0); + setUnavailableQuote(true); } setQuote(quote); setLoading(false); @@ -144,11 +154,14 @@ export function PurchaseView() { try { const resJson = await response.json(); if (resJson?.error?.message) { - setToast({ - type: "error", - content: resJson?.error?.message, - duration: 2400 - }); + // TODO: decide whether to notify errors by toast or warning styled component + // to make it consistent with mobile app + // setToast({ + // type: "error", + // content: resJson?.error?.message, + // duration: 2400 + // }); + setError(resJson?.error?.message); } else { throw new Error("Network response was not ok"); } @@ -172,12 +185,15 @@ export function PurchaseView() { fetchQuote(); } else { setQuote(null); + setExchangeRate(0); + setUnavailableQuote(false); } }, [debouncedYouPayInput, selectedCurrency, paymentMethod, arConversion]); useEffect(() => { youPayInput.setState(""); setExchangeRate(0); + setUnavailableQuote(false); }, [selectedCurrency]); const buyAR = async () => { @@ -212,6 +228,13 @@ export function PurchaseView() { + {unavailableQuote && ( + + + {error} + + + )} setShowPaymentSelector(true)} disabled={!paymentMethod} @@ -688,3 +711,23 @@ export const TokenLogo = styled(CommonImage).attrs({ display: block; vertical-align: middle; `; + +const WarningWrapper = styled.div` + display: flex; + justify-content: center; + text-align: center; + align-items: center; + flex-direction: row; + margin-bottom: 10px; +`; + +const WarningContent = styled.span` + font-size: 16px; + border: 1px solid ${(props) => props.theme.surfaceTertiary}; + border-radius: 30px; + padding: 12px; + display: flex; + align-items: center; + gap: 10px; + color: ${(props) => props.theme.primaryTextv2}; +`; diff --git a/src/routes/popup/settings/tokens/[id]/index.tsx b/src/routes/popup/settings/tokens/[id]/index.tsx index c3c03346f..415511e81 100644 --- a/src/routes/popup/settings/tokens/[id]/index.tsx +++ b/src/routes/popup/settings/tokens/[id]/index.tsx @@ -1,11 +1,5 @@ -import { - ButtonV2, - SelectV2, - Spacer, - Text, - TooltipV2, - useToasts -} from "@arconnect/components"; +import { ButtonV2, Spacer, Text, TooltipV2 } from "@arconnect/components"; +import { Select as SelectV2, useToasts } from "@arconnect/components-rebrand"; import type { TokenType } from "~tokens/token"; import { useStorage } from "~utils/storage"; import { ExtensionStorage } from "~utils/storage"; @@ -13,7 +7,7 @@ import { TrashIcon } from "@iconicicons/react"; import { removeToken } from "~tokens"; import { useMemo } from "react"; import browser from "webextension-polyfill"; -import styled from "styled-components"; +import styled, { useTheme } from "styled-components"; import copy from "copy-to-clipboard"; import { formatAddress } from "~utils/format"; import { CopyButton } from "~components/dashboard/subsettings/WalletSettings"; @@ -21,7 +15,6 @@ import HeadV2 from "~components/popup/HeadV2"; import type { CommonRouteProps } from "~wallets/router/router.types"; import { useLocation } from "~wallets/router/router.utils"; import { LoadingView } from "~components/page/common/loading/loading.view"; - export interface TokenSettingsParams { id: string; } @@ -30,6 +23,7 @@ export type TokenSettingsProps = CommonRouteProps; export function TokenSettingsView({ params: { id } }: TokenSettingsProps) { const { navigate } = useLocation(); + const theme = useTheme(); // ao tokens const [aoTokens, setAoTokens] = useStorage( @@ -95,6 +89,7 @@ export function TokenSettingsView({ params: { id } }: TokenSettingsProps) { setToast({ type: "info", content: browser.i18n.getMessage("copied_address", [ + token.ticker, formatAddress(token.id, 8) ]), duration: 2200 @@ -105,6 +100,7 @@ export function TokenSettingsView({ params: { id } }: TokenSettingsProps) { { // @ts-expect-error @@ -166,8 +162,10 @@ const BasePropertyText = styled(Text).attrs({ font-weight: 500; `; -const PropertyName = styled(BasePropertyText)``; +const PropertyName = styled(BasePropertyText)` + color: ${(props) => props.theme.primaryText}; +`; const PropertyValue = styled(BasePropertyText)` - color: ${(props) => props.theme.primaryText}; + color: ${(props) => props.theme.secondaryText}; `; diff --git a/src/routes/welcome/generate/done.tsx b/src/routes/welcome/generate/done.tsx index 80271b1c3..ffe8e33e5 100644 --- a/src/routes/welcome/generate/done.tsx +++ b/src/routes/welcome/generate/done.tsx @@ -11,6 +11,7 @@ import { formatAddress } from "~utils/format"; import Squircle from "~components/Squircle"; import { useLocation } from "~wallets/router/router.utils"; import type { CommonRouteProps } from "~wallets/router/router.types"; +import { useActiveWallet } from "~wallets/hooks"; export type GenerateDoneWelcomeViewProps = CommonRouteProps; @@ -20,6 +21,7 @@ export function GenerateDoneWelcomeView({ }: GenerateDoneWelcomeViewProps) { // wallet context const { wallet } = useContext(WalletContext); + const activeWallet = useActiveWallet(); const { navigate } = useLocation(); const { setupMode } = params; @@ -73,7 +75,7 @@ export function GenerateDoneWelcomeView({ {browser.i18n.getMessage("your_wallet_address_is")} diff --git a/src/routes/welcome/generate/permissions.tsx b/src/routes/welcome/generate/permissions.tsx index c660a07ca..08533744d 100644 --- a/src/routes/welcome/generate/permissions.tsx +++ b/src/routes/welcome/generate/permissions.tsx @@ -22,6 +22,7 @@ import { useStorage } from "@plasmohq/storage/hook"; import { ExtensionStorage } from "~utils/storage"; import { loadTokens } from "~tokens/token"; import { addWallet } from "~wallets"; +import { useHardwareApi } from "~wallets/hooks"; export type PermissionsWelcomeViewProps = CommonRouteProps; @@ -31,6 +32,8 @@ export function PermissionsWelcomeView({ }: PermissionsWelcomeViewProps) { const { navigate } = useLocation(); + const hardwareApi = useHardwareApi(); + const { wallet } = useContext(WalletContext); const { password } = useContext(PasswordContext); const walletRef = useRef(wallet); @@ -74,6 +77,20 @@ export function PermissionsWelcomeView({ const startTime = Date.now(); setLoading(true); + if (hardwareApi) { + await loadTokens(); + + // log user onboarded + await trackEvent(EventType.ONBOARDED, {}); + + if (!analyticSetting && !answered) { + await setAnswered(true); + await setAnalyticSetting(false); + } + + // redirect to getting started pages + navigate(`/${params.setupMode}/${Number(params.page) + 1}`); + } // add wallet if (!walletRef.current.address || !walletRef.current.jwk) { diff --git a/src/routes/welcome/load/wallets.tsx b/src/routes/welcome/load/wallets.tsx index 555115bf5..f01f84e18 100644 --- a/src/routes/welcome/load/wallets.tsx +++ b/src/routes/welcome/load/wallets.tsx @@ -44,6 +44,11 @@ import QRLoopScanner, { } from "~components/welcome/load/QRLoopScanner"; import { useScanner, AnimatedQRScanner } from "@arconnect/keystone-sdk"; import { addHardwareWallet } from "~wallets/hardware"; +import { + Alert, + Icon as WarningIcon +} from "~components/auth/CustomGatewayWarning"; +import { useActiveWallet } from "~wallets/hooks"; export type WalletsWelcomeViewProps = CommonRouteProps; @@ -59,6 +64,7 @@ export function WalletsWelcomeView({ params }: WalletsWelcomeViewProps) { // wallet context const { wallet, setWallet } = useContext(WalletContext); + const activeWallet = useActiveWallet(); // wallet generation taking longer const [showLongWaitMessage, setShowLongWaitMessage] = useState(false); @@ -262,7 +268,7 @@ export function WalletsWelcomeView({ params }: WalletsWelcomeViewProps) { {!wallet?.address ? ( - qweq{browser.i18n.getMessage("provide_seedphrase_paragraph")} + {browser.i18n.getMessage("provide_seedphrase_paragraph")} navigate(`/`)} /> ); - } else + } else if (params.setupMode === "keystoneLoad") { return ( - {!wallet?.address ? ( + {!activeWallet?.address ? ( - - {browser.i18n.getMessage("scan_qr_code_description")} -
- - {browser.i18n.getMessage("scan_qr_code_instruction")} - {scanMode ? ( - params.setupMode === "qrLoad" ? ( - { - setLoadedWallet(result); - setScanMode(false); - done(result); - }} - /> - ) : ( - - ) + ) : (
- + +
+ + + {browser.i18n.getMessage("keystone_features_warning")} +
)} @@ -504,35 +506,43 @@ export function WalletsWelcomeView({ params }: WalletsWelcomeViewProps) { ) : ( - - {browser.i18n.getMessage("found_account_with_phrase")} - - - - {wallet.address} +
+
+ + {browser.i18n.getMessage("found_account_with_phrase")} - + + + {activeWallet?.address} + + +
+ + + + )} - {!wallet?.address ? ( - - {loading && showLongWaitMessage && ( - - {browser.i18n.getMessage("longer_than_usual")} - - )} - - ) : ( + {wallet?.address && ( )} - navigate(`/`)} /> ); + } + return ( + + {!wallet?.address ? ( + + + {browser.i18n.getMessage("scan_qr_code_description")} + +
+ + {browser.i18n.getMessage("scan_qr_code_instruction")} + + {scanMode ? ( + params.setupMode === "qrLoad" ? ( + { + setLoadedWallet(result); + setScanMode(false); + done(result); + }} + /> + ) : ( + + ) + ) : ( +
+ +
+ )} +
+
+ ) : ( + + + {browser.i18n.getMessage("found_account_with_phrase")} + + + + {wallet.address} + + + + )} + {!wallet?.address ? ( + + {loading && showLongWaitMessage && ( + + {browser.i18n.getMessage("longer_than_usual")} + + )} + + ) : ( + + + + + )} + navigate(`/`)} /> +
+ ); } const KeystoneScanner = ({ @@ -626,8 +734,7 @@ const KeystoneScanner = ({ /> - {browser.i18n.getMessage("progress")}:{" "} - {Math.round(scanner.progress * 100)}% + {browser.i18n.getMessage("progress")}: {Math.round(scanner.progress)}% ); @@ -645,7 +752,6 @@ const Container = styled.div` justify-content: space-between; overflow: scroll; height: 100%; - gap: 24px; `; const Content = styled.div` diff --git a/src/routes/welcome/setup.tsx b/src/routes/welcome/setup.tsx index 8e2b5bcd2..e1bad5768 100644 --- a/src/routes/welcome/setup.tsx +++ b/src/routes/welcome/setup.tsx @@ -43,6 +43,13 @@ const LoadViews = [ PermissionsWelcomeView, GenerateDoneWelcomeView ]; +const KeystoneViews = [ + WalletsWelcomeView, + PasswordWelcomeView, + ThemeWelcomeView, + PermissionsWelcomeView, + GenerateDoneWelcomeView +]; // TODO: Use a nested router instead: const ViewsBySetupMode = { @@ -59,7 +66,7 @@ const ViewsBySetupMode = { recoveryPhraseLoad: LoadViews, keyfileLoad: LoadViews, qrLoad: LoadViews, - keystoneLoad: LoadViews + keystoneLoad: KeystoneViews } as const; const VIEW_TITLES_BY_SETUP_MODE = { @@ -79,6 +86,13 @@ const remainingLoadSubtitles = [ "congratulations" ]; +const remainingKeystoneSubtitles = [ + "create_a_password", + "choose_ui_theme", + "enable_permissions", + "congratulations" +]; + const VIEW_SUBTITLES_BY_SETUP_MODE = { generate: [ "name_your_account", @@ -93,7 +107,7 @@ const VIEW_SUBTITLES_BY_SETUP_MODE = { recoveryPhraseLoad: ["enter_recovery_phrase", ...remainingLoadSubtitles], keyfileLoad: ["upload_key_file", ...remainingLoadSubtitles], qrLoad: ["scan_qr_code", ...remainingLoadSubtitles], - keystoneLoad: ["scan_qr_code", ...remainingLoadSubtitles] + keystoneLoad: ["keystone_connect_title", ...remainingKeystoneSubtitles] }; export type WelcomeSetupMode =