From 59af83b635414c0407a3c3ba059ec5f546a19142 Mon Sep 17 00:00:00 2001 From: Abraham Makovetsky Date: Sun, 4 Aug 2024 15:44:50 +0300 Subject: [PATCH] chore(wallets discovery): added mobile os store links support --- packages/core/src/discovery.ts | 34 +++++++++++++++--- packages/core/src/main.ts | 8 +++-- packages/core/src/types.ts | 6 +++- packages/core/src/utils.ts | 3 ++ packages/ui/src/main.ts | 56 +++++++++++++++++++++++------- packages/ui/src/modal/Modal.svelte | 2 +- 6 files changed, 87 insertions(+), 22 deletions(-) diff --git a/packages/core/src/discovery.ts b/packages/core/src/discovery.ts index 6a79df6b..db7c301d 100644 --- a/packages/core/src/discovery.ts +++ b/packages/core/src/discovery.ts @@ -1,13 +1,37 @@ +import { ssrSafeWindow } from "./utils" import { metaMaskVirtualWallet } from "./wallet/virtualWallets/metaMaskVirtualWallet" +export type OperatingSystemStoreVersion = "ios" | "android" +export type BrowserStoreVersion = "chrome" | "firefox" | "edge" | "safari" + +type DownloadsRecord< + SV extends OperatingSystemStoreVersion | BrowserStoreVersion, + DL extends string, +> = Record + export type WalletProvider = { id: string name: string icon: string downloads: - | { chrome?: `https://chrome.google.com/webstore/detail/${string}` } - | { firefox?: `https://addons.mozilla.org/en-US/firefox/addon/${string}` } - | { edge?: `https://microsoftedge.microsoft.com/addons/detail/${string}` } + | DownloadsRecord< + "chrome", + `https://chrome.google.com/webstore/detail/${string}` + > + | DownloadsRecord< + "firefox", + `https://addons.mozilla.org/en-US/firefox/addon/${string}` + > + | DownloadsRecord< + "edge", + `https://microsoftedge.microsoft.com/addons/detail/${string}` + > + | DownloadsRecord<"safari", `https://apps.apple.com/us/app/${string}`> + | DownloadsRecord<"ios", `https://apps.apple.com/us/app/${string}`> + | DownloadsRecord< + "android", + `https://play.google.com/store/apps/details?id=${string}` + > } const wallets: WalletProvider[] = [ @@ -31,7 +55,9 @@ const wallets: WalletProvider[] = [ "https://chrome.google.com/webstore/detail/braavos-wallet/jnlgamecbpmbajjfhmmmlhejkemejdma", firefox: "https://addons.mozilla.org/en-US/firefox/addon/braavos-wallet", edge: "https://microsoftedge.microsoft.com/addons/detail/braavos-wallet/hkkpjehhcnhgefhbdcgfkeegglpjchdc", - }, + ios: `https://link.braavos.app/dapp/${ssrSafeWindow?.location?.host}`, + android: `https://link.braavos.app/dapp/${ssrSafeWindow?.location?.host}`, + } as Record, }, { id: metaMaskVirtualWallet.id, diff --git a/packages/core/src/main.ts b/packages/core/src/main.ts index fb1e8af5..b6c70695 100644 --- a/packages/core/src/main.ts +++ b/packages/core/src/main.ts @@ -1,7 +1,7 @@ import discovery, { type WalletProvider } from "./discovery" import { LocalStorageWrapper } from "./localStorageStore" import type { GetStarknetOptions, GetStarknetResult } from "./types" -import { pipe } from "./utils" +import { pipe, ssrSafeWindow } from "./utils" import { filterBy, filterByAuthorized } from "./wallet/filter" import { isFullWallet, @@ -43,17 +43,19 @@ export { scanObjectForWallets } from "./wallet/scan" export { isWalletObject } from "./wallet/isWalletObject" export type { + BrowserStoreVersion, DisconnectOptions, GetStarknetOptions, GetStarknetResult, GetWalletOptions, + OperatingSystemStoreVersion, WalletProvider, } from "./types" -const ssrSafeWindow = typeof window !== "undefined" ? window : {} +export { ssrSafeWindow } from "./utils" const defaultOptions: GetStarknetOptions = { - windowObject: ssrSafeWindow, + windowObject: ssrSafeWindow ?? {}, isWalletObject, storageFactoryImplementation: (name: string) => new LocalStorageWrapper(name), } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 787298ec..3fb0c9e9 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -8,7 +8,11 @@ import type { StarknetWindowObject, } from "@starknet-io/types-js" -export type { WalletProvider } from "./discovery" +export type { + BrowserStoreVersion, + OperatingSystemStoreVersion, + WalletProvider, +} from "./discovery" export interface GetStarknetOptions { windowObject: Record diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index a9b2f5e0..bca5d135 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -23,3 +23,6 @@ export function ensureKeysArray(keysGuard: { }) { return Object.keys(keysGuard) as (keyof T)[] } + +export const ssrSafeWindow: Window | null = + typeof window !== "undefined" ? window : null diff --git a/packages/ui/src/main.ts b/packages/ui/src/main.ts index 178a5827..f29bc60e 100644 --- a/packages/ui/src/main.ts +++ b/packages/ui/src/main.ts @@ -1,9 +1,12 @@ import show, { type WalletProviderWithStoreVersion } from "./modal" import sn, { + type BrowserStoreVersion, type DisconnectOptions, type GetWalletOptions, + type OperatingSystemStoreVersion, type RequestAccountsParameters, type StarknetWindowObject, + type WalletProvider, } from "@starknet-io/get-starknet-core" import Bowser from "bowser" @@ -12,11 +15,9 @@ export type { DisconnectOptions, } from "@starknet-io/get-starknet-core" -type StoreVersion = "chrome" | "firefox" | "edge" - const ssrSafeWindow = typeof window !== "undefined" ? window : null -function getStoreVersionFromBrowser(): StoreVersion | null { +function getBrowserStoreVersionFromBrowser(): BrowserStoreVersion | null { const browserName = Bowser.getParser(ssrSafeWindow?.navigator.userAgent) .getBrowserName() ?.toLowerCase() @@ -37,10 +38,25 @@ function getStoreVersionFromBrowser(): StoreVersion | null { } } +function getOperatingSystemStoreVersionFromBrowser(): OperatingSystemStoreVersion | null { + const os = + Bowser.getParser(ssrSafeWindow?.navigator.userAgent) + .getOS() + ?.name?.toLowerCase() ?? null + switch (os) { + case "ios": + case "android": + return os + default: + return null + } +} + export interface ConnectOptions extends GetWalletOptions { modalMode?: "alwaysAsk" | "canAsk" | "neverAsk" modalTheme?: "light" | "dark" | "system" - storeVersion?: StoreVersion + storeVersion?: BrowserStoreVersion + osVersion?: OperatingSystemStoreVersion } const enableWithVersion = async ( @@ -55,7 +71,8 @@ const enableWithVersion = async ( export const connect = async ({ modalMode = "canAsk", - storeVersion = getStoreVersionFromBrowser(), + storeVersion = getBrowserStoreVersionFromBrowser(), + osVersion = getOperatingSystemStoreVersionFromBrowser(), modalTheme, ...restOptions }: ConnectOptions = {}): Promise => { @@ -71,7 +88,7 @@ export const connect = async ({ // return `wallet` even if it's null/undefined since we aren't allowed // to show any "connect" related UI - return enableWithVersion(wallet, { silentMode: true }) + return enableWithVersion(wallet, { silent_mode: true }) } const installedWallets = await sn.getAvailableWallets(restOptions) @@ -90,13 +107,26 @@ export const connect = async ({ const discoveryWallets = await sn.getDiscoveryWallets(restOptions) - const discoveryWalletsByStoreVersion: WalletProviderWithStoreVersion[] = - discoveryWallets - .filter((w) => Boolean(w.downloads[storeVersion])) - .map(({ downloads, ...w }) => ({ - ...w, - download: downloads[storeVersion], - })) + const discoveryWalletsByStoreVersion = discoveryWallets.reduce< + WalletProviderWithStoreVersion[] + >((results, w) => { + const download = + // prioritize OS url + w.downloads[osVersion] || + // fallback to browser url + w.downloads[storeVersion] + if (download) { + const store = Object.keys(w.downloads).find( + (key) => w.downloads[key] === download, + ) as keyof WalletProvider["downloads"] + + const isMobileStore = store === "android" || store === "ios" + const name = isMobileStore ? `${w.name} Mobile` : `Install ${w.name}` + + results.push({ ...w, name, download }) + } + return results + }, []) return show({ lastWallet, diff --git a/packages/ui/src/modal/Modal.svelte b/packages/ui/src/modal/Modal.svelte index b4a640bb..db1f51ee 100644 --- a/packages/ui/src/modal/Modal.svelte +++ b/packages/ui/src/modal/Modal.svelte @@ -143,7 +143,7 @@ cb(null) } }}> - Install {discoveryWallet.name} + {discoveryWallet.name}