From f283110707d5edc166bbe05e5482d38fa29de29e Mon Sep 17 00:00:00 2001 From: Jordan Ribbink <17958158+jribbink@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:25:36 -0700 Subject: [PATCH] Fix deep linking for in-app browsers (#1966) --- .changeset/unlucky-melons-arrive.md | 6 ++ package-lock.json | 32 +++++------ packages/fcl-core/src/current-user/index.js | 14 +++-- packages/fcl-wc/src/service.ts | 61 +++------------------ packages/fcl-wc/src/utils.ts | 21 +++++++ 5 files changed, 60 insertions(+), 74 deletions(-) create mode 100644 .changeset/unlucky-melons-arrive.md diff --git a/.changeset/unlucky-melons-arrive.md b/.changeset/unlucky-melons-arrive.md new file mode 100644 index 000000000..2b97fca2e --- /dev/null +++ b/.changeset/unlucky-melons-arrive.md @@ -0,0 +1,6 @@ +--- +"@onflow/fcl-core": minor +"@onflow/fcl-wc": minor +--- + +Fix deep linking issues with mobile wallets diff --git a/package-lock.json b/package-lock.json index 0b4ff173f..4270496f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28759,16 +28759,16 @@ }, "packages/fcl": { "name": "@onflow/fcl", - "version": "1.12.0", + "version": "1.12.2", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "@onflow/config": "1.5.0", - "@onflow/fcl-core": "1.11.0", - "@onflow/fcl-wc": "5.2.0", + "@onflow/fcl-core": "1.11.1", + "@onflow/fcl-wc": "5.3.1", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.2", - "@onflow/sdk": "1.5.2", + "@onflow/sdk": "1.5.3", "@onflow/types": "1.4.0", "@onflow/util-actor": "1.3.3", "@onflow/util-address": "1.2.2", @@ -28829,7 +28829,7 @@ }, "packages/fcl-core": { "name": "@onflow/fcl-core", - "version": "1.11.0", + "version": "1.11.1", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", @@ -28837,7 +28837,7 @@ "@onflow/config": "1.5.0", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.2", - "@onflow/sdk": "1.5.2", + "@onflow/sdk": "1.5.3", "@onflow/types": "1.4.0", "@onflow/util-actor": "1.3.3", "@onflow/util-address": "1.2.2", @@ -28894,15 +28894,15 @@ }, "packages/fcl-react-native": { "name": "@onflow/fcl-react-native", - "version": "1.9.4", + "version": "1.9.5", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "@onflow/config": "1.5.0", - "@onflow/fcl-core": "1.11.0", + "@onflow/fcl-core": "1.11.1", "@onflow/interaction": "0.0.11", "@onflow/rlp": "1.2.2", - "@onflow/sdk": "1.5.2", + "@onflow/sdk": "1.5.3", "@onflow/types": "1.4.0", "@onflow/util-actor": "1.3.3", "@onflow/util-address": "1.2.2", @@ -28947,7 +28947,7 @@ }, "packages/fcl-wc": { "name": "@onflow/fcl-wc", - "version": "5.2.0", + "version": "5.3.1", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.9", @@ -28967,7 +28967,7 @@ "jest": "^29.5.0" }, "peerDependencies": { - "@onflow/fcl-core": "1.11.0" + "@onflow/fcl-core": "1.11.1" } }, "packages/fcl/node_modules/@onflow/fcl-wc/node_modules/@onflow/config": { @@ -29476,13 +29476,13 @@ }, "packages/sdk": { "name": "@onflow/sdk", - "version": "1.5.2", + "version": "1.5.3", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "@onflow/config": "1.5.0", "@onflow/rlp": "1.2.2", - "@onflow/transport-http": "1.10.1", + "@onflow/transport-http": "1.10.2", "@onflow/typedefs": "1.3.1", "@onflow/util-actor": "1.3.3", "@onflow/util-address": "1.2.2", @@ -29531,7 +29531,7 @@ }, "devDependencies": { "@onflow/fcl-bundle": "1.5.0", - "@onflow/sdk": "1.5.2", + "@onflow/sdk": "1.5.3", "jest": "^29.5.0" } }, @@ -29561,7 +29561,7 @@ }, "packages/transport-http": { "name": "@onflow/transport-http", - "version": "1.10.1", + "version": "1.10.2", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", @@ -29578,7 +29578,7 @@ "devDependencies": { "@onflow/fcl-bundle": "1.5.0", "@onflow/rlp": "1.2.2", - "@onflow/sdk": "1.5.2", + "@onflow/sdk": "1.5.3", "@onflow/types": "1.4.0", "jest": "^29.5.0" } diff --git a/packages/fcl-core/src/current-user/index.js b/packages/fcl-core/src/current-user/index.js index d80ed1455..4f8ee796b 100644 --- a/packages/fcl-core/src/current-user/index.js +++ b/packages/fcl-core/src/current-user/index.js @@ -271,7 +271,14 @@ const getResolvePreAuthz = addr: az.identity.address, keyId: az.identity.keyId, signingFunction(signable) { - return execService({service: az, msg: signable, platform}) + return execService({ + service: az, + msg: signable, + platform, + opts: { + initiatedByPreAuthz: true, + }, + }) }, role: { proposer: role === "PROPOSER", @@ -315,10 +322,6 @@ const getAuthorization = }) ) if (authz) { - let windowRef - if (isMobile() && authz.method === "WC/RPC") { - windowRef = window.open("", "_blank") - } return { ...account, tempId: "CURRENT_USER", @@ -334,7 +337,6 @@ const getAuthorization = msg: signable, opts: { includeOlderJsonRpcCall: true, - windowRef, }, platform, }) diff --git a/packages/fcl-wc/src/service.ts b/packages/fcl-wc/src/service.ts index f66746989..9f154ece5 100644 --- a/packages/fcl-wc/src/service.ts +++ b/packages/fcl-wc/src/service.ts @@ -1,6 +1,6 @@ import {invariant} from "@onflow/util-invariant" import {log, LEVELS} from "@onflow/util-logger" -import {isMobile, isIOS} from "./utils" +import {isMobile, openDeeplink} from "./utils" import {FLOW_METHODS, REQUEST_TYPES} from "./constants" import {SignClient} from "@walletconnect/sign-client/dist/types/client" import {createSessionProposal, makeSessionData, request} from "./session" @@ -60,7 +60,7 @@ const makeExec = ( const client = await clientPromise invariant(!!client, "WalletConnect is not initialized") - let session: any, pairing: any, windowRef: any + let session: any, pairing: any const method = service.endpoint const appLink = validateAppLink(service) const pairings = client.pairing.getAll({active: true}) @@ -74,14 +74,6 @@ const makeExec = ( session = client.session.get(client.session.keys.at(lastKeyIndex)!) } - if (isMobile()) { - if (opts.windowRef) { - windowRef = opts.windowRef - } else { - windowRef = window.open("", "_blank") - } - } - if (session == null) { session = await new Promise((resolve, reject) => { function onClose() { @@ -92,7 +84,6 @@ const makeExec = ( service, onClose, appLink, - windowRef, client, method, pairing, @@ -114,8 +105,12 @@ const makeExec = ( }) } - if (isMobile() && method !== FLOW_METHODS.FLOW_AUTHN) { - openDeepLink() + if ( + isMobile() && + method !== FLOW_METHODS.FLOW_AUTHN && + !(method === FLOW_METHODS.FLOW_AUTHZ && opts.initiatedByPreAuthz) + ) { + openDeeplink(appLink) } // Make request to the WalletConnect client and return the result @@ -125,10 +120,6 @@ const makeExec = ( session, client, abortSignal, - }).finally(() => { - if (windowRef && !windowRef.closed) { - windowRef.close() - } }) function validateAppLink({uid}: {uid: string}) { @@ -141,35 +132,6 @@ const makeExec = ( } return uid } - - function openDeepLink() { - if (windowRef) { - if (appLink.startsWith("http") && !isIOS()) { - // Workaround for https://github.com/rainbow-me/rainbowkit/issues/524. - // Using 'window.open' causes issues on iOS in non-Safari browsers and - // WebViews where a blank tab is left behind after connecting. - // This is especially bad in some WebView scenarios (e.g. following a - // link from Twitter) where the user doesn't have any mechanism for - // closing the blank tab. - // For whatever reason, links with a target of "_blank" don't suffer - // from this problem, and programmatically clicking a detached link - // element with the same attributes also avoids the issue. - const link = document.createElement("a") - link.href = appLink - link.target = "_blank" - link.rel = "noreferrer noopener" - link.click() - } else { - windowRef.location.href = appLink - } - } else { - log({ - title: "Problem opening deep link in new window", - message: `Window failed to open (was it blocked by the browser?)`, - level: LEVELS.warn, - }) - } - } } } @@ -179,7 +141,6 @@ function connectWc(WalletConnectModal: Promise) { service, onClose, appLink, - windowRef, client, method, pairing, @@ -190,7 +151,6 @@ function connectWc(WalletConnectModal: Promise) { service: any onClose: any appLink: string - windowRef: any client: SignClient method: string pairing: any @@ -228,7 +188,7 @@ function connectWc(WalletConnectModal: Promise) { if (isMobile()) { const queryString = new URLSearchParams({uri: uri}).toString() let url = pairing == null ? appLink + "?" + queryString : appLink - windowRef.location.href = url + openDeeplink(url) } else if (!pairing) { if (!pairingModalOverride) { walletConnectModal = new (await WalletConnectModal)({ @@ -267,9 +227,6 @@ function connectWc(WalletConnectModal: Promise) { onClose() throw error } finally { - if (windowRef && !windowRef.closed) { - windowRef.close() - } walletConnectModal?.closeModal() } } diff --git a/packages/fcl-wc/src/utils.ts b/packages/fcl-wc/src/utils.ts index 7cb9c47bd..de0247166 100644 --- a/packages/fcl-wc/src/utils.ts +++ b/packages/fcl-wc/src/utils.ts @@ -73,3 +73,24 @@ export function isIOS() { export function isMobile() { return isAndroid() || isIOS() } + +export function openDeeplink(url: string) { + if (url.startsWith("http")) { + // Workaround for https://github.com/rainbow-me/rainbowkit/issues/524. + // Using 'window.open' causes issues on iOS in non-Safari browsers and + // WebViews where a blank tab is left behind after connecting. + // This is especially bad in some WebView scenarios (e.g. following a + // link from Twitter) where the user doesn't have any mechanism for + // closing the blank tab. + // For whatever reason, links with a target of "_blank" don't suffer + // from this problem, and programmatically clicking a detached link + // element with the same attributes also avoids the issue. + const link = document.createElement("a") + link.href = url + link.target = "_blank" + link.rel = "noreferrer noopener" + link.click() + } else { + window.open(url, "_blank") + } +}