@enbox/browser 0.3.70 → 0.3.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -7
- package/dist/browser.mjs +14 -14
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/blob-url-pool.js +36 -0
- package/dist/esm/blob-url-pool.js.map +1 -0
- package/dist/esm/browser-connect-handler.js +3 -1
- package/dist/esm/browser-connect-handler.js.map +1 -1
- package/dist/esm/file-envelope-url-store.js +203 -0
- package/dist/esm/file-envelope-url-store.js.map +1 -0
- package/dist/esm/index.js +12 -12
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/object-url-reference.js +68 -0
- package/dist/esm/object-url-reference.js.map +1 -0
- package/dist/esm/relay-connect-runner.js +9 -1
- package/dist/esm/relay-connect-runner.js.map +1 -1
- package/dist/esm/ui/connect-modal.js +134 -16
- package/dist/esm/ui/connect-modal.js.map +1 -1
- package/dist/types/blob-url-pool.d.ts +16 -0
- package/dist/types/blob-url-pool.d.ts.map +1 -0
- package/dist/types/browser-connect-handler.d.ts +8 -2
- package/dist/types/browser-connect-handler.d.ts.map +1 -1
- package/dist/types/file-envelope-url-store.d.ts +30 -0
- package/dist/types/file-envelope-url-store.d.ts.map +1 -0
- package/dist/types/index.d.ts +16 -14
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/object-url-reference.d.ts +20 -0
- package/dist/types/object-url-reference.d.ts.map +1 -0
- package/dist/types/relay-connect-runner.d.ts +4 -0
- package/dist/types/relay-connect-runner.d.ts.map +1 -1
- package/dist/types/ui/connect-modal.d.ts +24 -11
- package/dist/types/ui/connect-modal.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/blob-url-pool.ts +55 -0
- package/src/browser-connect-handler.ts +13 -2
- package/src/file-envelope-url-store.ts +260 -0
- package/src/index.ts +23 -159
- package/src/object-url-reference.ts +82 -0
- package/src/relay-connect-runner.ts +17 -2
- package/src/ui/connect-modal.ts +187 -28
package/src/ui/connect-modal.ts
CHANGED
|
@@ -59,6 +59,12 @@ export type ConnectMethod = 'phone' | 'browser';
|
|
|
59
59
|
/** localStorage key remembering the last successful method + wallet. */
|
|
60
60
|
const LAST_CHOICE_STORAGE_KEY = 'enbox:connect:lastChoice';
|
|
61
61
|
|
|
62
|
+
/** localStorage key remembering the exact wallet route for each delegate/profile pair. */
|
|
63
|
+
const SESSION_CHOICES_STORAGE_KEY = 'enbox:connect:sessionChoices';
|
|
64
|
+
|
|
65
|
+
/** Bounds local reconnect provenance retained by one dapp origin. */
|
|
66
|
+
const MAX_SESSION_CHOICES = 20;
|
|
67
|
+
|
|
62
68
|
/**
|
|
63
69
|
* Safety margin subtracted from the relay pointer TTL before re-minting.
|
|
64
70
|
*
|
|
@@ -168,8 +174,9 @@ export interface ConnectModalOptions {
|
|
|
168
174
|
preferredMethod?: ConnectMethod;
|
|
169
175
|
|
|
170
176
|
/**
|
|
171
|
-
* Remember the successful method + wallet in localStorage
|
|
172
|
-
* the
|
|
177
|
+
* Remember the successful method + wallet in localStorage. Refreshes reuse
|
|
178
|
+
* the route saved for their delegate DID; new connections use the latest
|
|
179
|
+
* successful route as a convenience.
|
|
173
180
|
* @default true
|
|
174
181
|
*/
|
|
175
182
|
rememberChoice?: boolean;
|
|
@@ -180,6 +187,15 @@ export interface ConnectModalOptions {
|
|
|
180
187
|
/** Icon URL of the requesting application. */
|
|
181
188
|
appIcon?: string;
|
|
182
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Stable application identifier hint available to the wallet during approval.
|
|
192
|
+
* Defaults to the browser's canonical origin.
|
|
193
|
+
*/
|
|
194
|
+
applicationId?: string;
|
|
195
|
+
|
|
196
|
+
/** Wallet profile DID that a refresh must renew. */
|
|
197
|
+
expectedProviderDid?: string;
|
|
198
|
+
|
|
183
199
|
/**
|
|
184
200
|
* Relay base URL override for the phone path. When omitted, each wallet's
|
|
185
201
|
* `connectServerUrl` is discovered from its `/.well-known/enbox-connect`
|
|
@@ -216,15 +232,7 @@ export interface ConnectModalDeps {
|
|
|
216
232
|
runRelay: typeof runRelayConnect;
|
|
217
233
|
|
|
218
234
|
/** Runs one popup handshake (browser path); must open the popup synchronously. */
|
|
219
|
-
runPopup:
|
|
220
|
-
walletUrl: string;
|
|
221
|
-
permissionRequests: ConnectPermissionRequest[];
|
|
222
|
-
appName: string;
|
|
223
|
-
appIcon?: string;
|
|
224
|
-
timeoutMs?: number;
|
|
225
|
-
delegatePortableDid?: PortableDid;
|
|
226
|
-
requestType?: ConnectRequestType;
|
|
227
|
-
}) => Promise<ConnectResult | undefined>;
|
|
235
|
+
runPopup: typeof runPopupConnect;
|
|
228
236
|
|
|
229
237
|
/** Resolves a wallet origin to its relay `connectServerUrl` (or undefined). */
|
|
230
238
|
discoverConnectServerUrl: (walletOrigin: string) => Promise<string | undefined>;
|
|
@@ -254,9 +262,11 @@ async function runPopupConnect(options: {
|
|
|
254
262
|
permissionRequests: ConnectPermissionRequest[];
|
|
255
263
|
appName: string;
|
|
256
264
|
appIcon?: string;
|
|
265
|
+
applicationId?: string;
|
|
257
266
|
timeoutMs?: number;
|
|
258
267
|
delegatePortableDid?: PortableDid;
|
|
259
268
|
requestType?: ConnectRequestType;
|
|
269
|
+
expectedProviderDid?: string;
|
|
260
270
|
}): Promise<ConnectResult | undefined> {
|
|
261
271
|
// The transport constructor calls `window.open` synchronously — callers
|
|
262
272
|
// must invoke this inside the user-gesture call stack.
|
|
@@ -269,10 +279,12 @@ async function runPopupConnect(options: {
|
|
|
269
279
|
return await client.connect({
|
|
270
280
|
appName : options.appName,
|
|
271
281
|
appIcon : options.appIcon,
|
|
282
|
+
applicationId : options.applicationId,
|
|
272
283
|
clientMetadata : collectBrowserClientMetadata(),
|
|
273
284
|
permissionRequests : options.permissionRequests,
|
|
274
285
|
delegatePortableDid : options.delegatePortableDid,
|
|
275
286
|
requestType : options.requestType,
|
|
287
|
+
expectedProviderDid : options.expectedProviderDid,
|
|
276
288
|
});
|
|
277
289
|
}
|
|
278
290
|
|
|
@@ -301,6 +313,12 @@ interface LastChoice {
|
|
|
301
313
|
walletUrl: string;
|
|
302
314
|
}
|
|
303
315
|
|
|
316
|
+
/** Stable reconnect provenance for one delegated app session. */
|
|
317
|
+
interface SessionChoice extends LastChoice {
|
|
318
|
+
delegateDid: string;
|
|
319
|
+
providerDid: string;
|
|
320
|
+
}
|
|
321
|
+
|
|
304
322
|
function readLastChoice(storage: ConnectModalDeps['storage']): LastChoice | undefined {
|
|
305
323
|
try {
|
|
306
324
|
const raw = storage?.getItem(LAST_CHOICE_STORAGE_KEY);
|
|
@@ -321,6 +339,78 @@ function writeLastChoice(storage: ConnectModalDeps['storage'], choice: LastChoic
|
|
|
321
339
|
} catch { /* best effort */ }
|
|
322
340
|
}
|
|
323
341
|
|
|
342
|
+
function isSessionChoice(value: unknown): value is SessionChoice {
|
|
343
|
+
if (typeof value !== 'object' || value === null) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
const choice = value as Partial<SessionChoice>;
|
|
347
|
+
return (choice.method === 'phone' || choice.method === 'browser')
|
|
348
|
+
&& isValidRememberedWalletUrl(choice.walletUrl)
|
|
349
|
+
&& typeof choice.delegateDid === 'string'
|
|
350
|
+
&& choice.delegateDid.length > 0
|
|
351
|
+
&& typeof choice.providerDid === 'string'
|
|
352
|
+
&& choice.providerDid.length > 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function readSessionChoices(storage: ConnectModalDeps['storage']): SessionChoice[] {
|
|
356
|
+
try {
|
|
357
|
+
const raw = storage?.getItem(SESSION_CHOICES_STORAGE_KEY);
|
|
358
|
+
if (raw === null || raw === undefined) {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
362
|
+
return Array.isArray(parsed) ? parsed.filter(isSessionChoice).slice(-MAX_SESSION_CHOICES) : [];
|
|
363
|
+
} catch {
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function readSessionChoice(
|
|
369
|
+
storage: ConnectModalDeps['storage'],
|
|
370
|
+
delegateDid: string | undefined,
|
|
371
|
+
expectedProviderDid: string | undefined,
|
|
372
|
+
): SessionChoice | undefined {
|
|
373
|
+
if (delegateDid === undefined) {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
const choices = readSessionChoices(storage);
|
|
377
|
+
for (let index = choices.length - 1; index >= 0; index--) {
|
|
378
|
+
const choice = choices[index];
|
|
379
|
+
if (choice.delegateDid === delegateDid
|
|
380
|
+
&& (expectedProviderDid === undefined || choice.providerDid === expectedProviderDid)) {
|
|
381
|
+
return choice;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function writeSessionChoice(storage: ConnectModalDeps['storage'], choice: SessionChoice): void {
|
|
388
|
+
try {
|
|
389
|
+
const choices = readSessionChoices(storage).filter(entry =>
|
|
390
|
+
entry.delegateDid !== choice.delegateDid || entry.providerDid !== choice.providerDid
|
|
391
|
+
);
|
|
392
|
+
choices.push(choice);
|
|
393
|
+
storage?.setItem(SESSION_CHOICES_STORAGE_KEY, JSON.stringify(choices.slice(-MAX_SESSION_CHOICES)));
|
|
394
|
+
} catch { /* best effort */ }
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function removeSessionChoice(
|
|
398
|
+
storage: ConnectModalDeps['storage'],
|
|
399
|
+
delegateDid: string,
|
|
400
|
+
providerDid: string,
|
|
401
|
+
): void {
|
|
402
|
+
try {
|
|
403
|
+
const choices = readSessionChoices(storage).filter(choice =>
|
|
404
|
+
choice.delegateDid !== delegateDid || choice.providerDid !== providerDid
|
|
405
|
+
);
|
|
406
|
+
if (choices.length === 0) {
|
|
407
|
+
storage?.removeItem(SESSION_CHOICES_STORAGE_KEY);
|
|
408
|
+
} else {
|
|
409
|
+
storage?.setItem(SESSION_CHOICES_STORAGE_KEY, JSON.stringify(choices));
|
|
410
|
+
}
|
|
411
|
+
} catch { /* best effort */ }
|
|
412
|
+
}
|
|
413
|
+
|
|
324
414
|
/** One cancellable relay session. */
|
|
325
415
|
interface RelaySession {
|
|
326
416
|
cancelled: Promise<never>;
|
|
@@ -379,24 +469,37 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
379
469
|
const wallets = dedupeWalletsByUrl(options.wallets ?? []);
|
|
380
470
|
const rememberChoice = options.rememberChoice ?? true;
|
|
381
471
|
const appName = options.appName ?? window.location.host;
|
|
382
|
-
const
|
|
472
|
+
const applicationId = options.applicationId ?? window.location.origin;
|
|
383
473
|
const isMobile = deps.isMobile();
|
|
384
474
|
const refreshing = options.mode === 'refresh';
|
|
385
475
|
|
|
386
476
|
const lastChoice = rememberChoice ? readLastChoice(deps.storage) : undefined;
|
|
477
|
+
let savedRoute = rememberChoice && refreshing
|
|
478
|
+
? readSessionChoice(
|
|
479
|
+
deps.storage,
|
|
480
|
+
options.delegatePortableDid?.uri,
|
|
481
|
+
options.expectedProviderDid,
|
|
482
|
+
)
|
|
483
|
+
: undefined;
|
|
484
|
+
const expectedProviderDid = options.expectedProviderDid ?? savedRoute?.providerDid;
|
|
485
|
+
const relayWalletPath = options.relayWalletPath ?? '/connect/app';
|
|
486
|
+
const savedRouteEscapeLabel = options.walletUrl === undefined
|
|
487
|
+
? 'Choose another wallet or method'
|
|
488
|
+
: 'Choose another method';
|
|
387
489
|
|
|
388
|
-
//
|
|
490
|
+
// Refresh routes resolve by delegate first; general preferences remain a
|
|
491
|
+
// best-effort fallback for sessions created before route-aware storage.
|
|
389
492
|
const rememberedWalletUrl = lastChoice !== undefined
|
|
390
493
|
&& (refreshing || walletInCatalog(wallets, lastChoice.walletUrl))
|
|
391
494
|
? lastChoice.walletUrl
|
|
392
495
|
: undefined;
|
|
393
|
-
|
|
394
|
-
|
|
496
|
+
let walletUrl = savedRoute?.walletUrl
|
|
497
|
+
?? options.walletUrl
|
|
395
498
|
?? rememberedWalletUrl
|
|
396
499
|
?? wallets[0]?.url;
|
|
397
500
|
|
|
398
|
-
//
|
|
399
|
-
let method: ConnectMethod = options.preferredMethod ?? lastChoice?.method ?? 'phone';
|
|
501
|
+
// An exact refresh route wins over generic handler and global preferences.
|
|
502
|
+
let method: ConnectMethod = savedRoute?.method ?? options.preferredMethod ?? lastChoice?.method ?? 'phone';
|
|
400
503
|
|
|
401
504
|
return new Promise<ConnectResult | undefined>((resolve, reject) => {
|
|
402
505
|
// ── Host + shadow root ─────────────────────────────────────
|
|
@@ -517,8 +620,14 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
517
620
|
};
|
|
518
621
|
|
|
519
622
|
const succeed = (result: ConnectResult): void => {
|
|
520
|
-
if (rememberChoice) {
|
|
521
|
-
writeLastChoice(deps.storage, { method, walletUrl
|
|
623
|
+
if (rememberChoice && walletUrl !== undefined) {
|
|
624
|
+
writeLastChoice(deps.storage, { method, walletUrl });
|
|
625
|
+
writeSessionChoice(deps.storage, {
|
|
626
|
+
delegateDid : result.delegatePortableDid.uri,
|
|
627
|
+
providerDid : result.connectedDid,
|
|
628
|
+
method,
|
|
629
|
+
walletUrl,
|
|
630
|
+
});
|
|
522
631
|
}
|
|
523
632
|
renderConnected();
|
|
524
633
|
setTimeout(settleWith(settle, () => resolve(result)), 1_200);
|
|
@@ -703,15 +812,47 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
703
812
|
);
|
|
704
813
|
};
|
|
705
814
|
|
|
815
|
+
const escapeSavedRoute = (): void => {
|
|
816
|
+
if (savedRoute === undefined) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
relaySession?.cancel();
|
|
821
|
+
relaySession = undefined;
|
|
822
|
+
clearRemint();
|
|
823
|
+
removeSessionChoice(deps.storage, savedRoute.delegateDid, savedRoute.providerDid);
|
|
824
|
+
savedRoute = undefined;
|
|
825
|
+
if (options.walletUrl !== undefined) {
|
|
826
|
+
walletUrl = options.walletUrl;
|
|
827
|
+
}
|
|
828
|
+
renderFooter();
|
|
829
|
+
setStage(
|
|
830
|
+
el('p', 'stage-caption', 'Choose another way to reconnect.'),
|
|
831
|
+
el('p', 'stage-subline', 'We’ll still require the same wallet profile used for this session.'),
|
|
832
|
+
);
|
|
833
|
+
};
|
|
834
|
+
|
|
706
835
|
const renderError = (message: string, error?: Error): void => {
|
|
707
836
|
setStage(
|
|
708
837
|
el('p', 'stage-caption', message),
|
|
709
838
|
stageButton('Start over', () => { void startMethod(); }),
|
|
839
|
+
...(savedRoute !== undefined
|
|
840
|
+
? [stageLinkButton(savedRouteEscapeLabel, escapeSavedRoute)]
|
|
841
|
+
: []),
|
|
710
842
|
stageLinkButton('Close', settleWith(settle, () => reject(error ?? new Error(`[@enbox/browser] ${message}`)))),
|
|
711
843
|
);
|
|
712
844
|
};
|
|
713
845
|
|
|
714
846
|
const renderPhoneUnavailable = (): void => {
|
|
847
|
+
if (savedRoute !== undefined) {
|
|
848
|
+
setStage(
|
|
849
|
+
el('p', 'stage-caption', 'Your saved wallet connection is unavailable right now.'),
|
|
850
|
+
el('p', 'stage-subline', 'Try again in a moment to reconnect the same session.'),
|
|
851
|
+
stageButton('Try again', () => { void startPhone(); }),
|
|
852
|
+
stageLinkButton(savedRouteEscapeLabel, escapeSavedRoute),
|
|
853
|
+
);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
715
856
|
setStage(
|
|
716
857
|
el('p', 'stage-caption', 'This wallet can’t connect by phone here.'),
|
|
717
858
|
el('p', 'stage-subline', 'You can continue in this browser instead.'),
|
|
@@ -724,12 +865,24 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
724
865
|
const popupSurface = isMobile ? 'tab' : 'window';
|
|
725
866
|
|
|
726
867
|
const renderPopupPrompt = (): void => {
|
|
868
|
+
const walletName = walletUrl !== undefined ? walletByUrl(walletUrl).name : 'your wallet';
|
|
869
|
+
const exactReconnect = savedRoute !== undefined;
|
|
870
|
+
const connectDescription = isMobile
|
|
871
|
+
? 'Your wallet opens in a new tab to approve this connection — no code needed.'
|
|
872
|
+
: 'Your wallet opens in a small window to approve this connection.';
|
|
873
|
+
const description = exactReconnect
|
|
874
|
+
? `We’ll open the same ${walletName} profile used for this session.`
|
|
875
|
+
: connectDescription;
|
|
876
|
+
const connectButtonLabel = isMobile ? 'Open wallet' : 'Open wallet window';
|
|
877
|
+
const buttonLabel = exactReconnect
|
|
878
|
+
? `Open ${walletName} ${popupSurface}`
|
|
879
|
+
: connectButtonLabel;
|
|
727
880
|
setStage(
|
|
728
|
-
el('p', 'stage-caption',
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
stageButton(
|
|
881
|
+
el('p', 'stage-caption', exactReconnect
|
|
882
|
+
? `Reconnect with ${walletName}`
|
|
883
|
+
: 'Connect with a wallet in this browser'),
|
|
884
|
+
el('p', 'stage-subline', description),
|
|
885
|
+
stageButton(buttonLabel, () => { startPopup(); }),
|
|
733
886
|
);
|
|
734
887
|
};
|
|
735
888
|
|
|
@@ -747,7 +900,9 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
747
900
|
? `Your browser blocked the wallet ${popupSurface}.`
|
|
748
901
|
: `The wallet ${popupSurface} was closed.`),
|
|
749
902
|
stageButton(blocked ? 'Open it now' : `Reopen ${popupSurface}`, () => { startPopup(); }),
|
|
750
|
-
|
|
903
|
+
...(savedRoute !== undefined
|
|
904
|
+
? [stageLinkButton(savedRouteEscapeLabel, escapeSavedRoute)]
|
|
905
|
+
: [stageLinkButton(isMobile ? 'Use a code instead' : 'Use your phone instead', () => { void switchMethod('phone'); })]),
|
|
751
906
|
);
|
|
752
907
|
};
|
|
753
908
|
|
|
@@ -833,10 +988,12 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
833
988
|
walletUri : new URL(relayWalletPath, origin).toString(),
|
|
834
989
|
appName,
|
|
835
990
|
appIcon : options.appIcon,
|
|
991
|
+
applicationId,
|
|
836
992
|
clientMetadata : collectBrowserClientMetadata(),
|
|
837
993
|
permissionRequests : options.permissionRequests,
|
|
838
994
|
delegatePortableDid : options.delegatePortableDid,
|
|
839
995
|
requestType : options.mode,
|
|
996
|
+
expectedProviderDid,
|
|
840
997
|
timeoutMs : options.timeout,
|
|
841
998
|
cancelled : session.cancelled,
|
|
842
999
|
onWalletUriReady : (handoff): void => {
|
|
@@ -907,9 +1064,11 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
907
1064
|
permissionRequests : options.permissionRequests,
|
|
908
1065
|
appName,
|
|
909
1066
|
appIcon : options.appIcon,
|
|
1067
|
+
applicationId,
|
|
910
1068
|
timeoutMs : options.timeout,
|
|
911
1069
|
delegatePortableDid : options.delegatePortableDid,
|
|
912
1070
|
requestType : options.mode,
|
|
1071
|
+
expectedProviderDid,
|
|
913
1072
|
});
|
|
914
1073
|
if (settled) { return; }
|
|
915
1074
|
if (result === undefined) {
|
|
@@ -945,7 +1104,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
945
1104
|
const renderFooter = (): void => {
|
|
946
1105
|
footer.replaceChildren();
|
|
947
1106
|
|
|
948
|
-
if (
|
|
1107
|
+
if (savedRoute === undefined && options.walletUrl === undefined) {
|
|
949
1108
|
composeWalletRow();
|
|
950
1109
|
const switcher = buildWalletSwitcher();
|
|
951
1110
|
if (switcher.label !== undefined) {
|
|
@@ -958,7 +1117,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
958
1117
|
const row = document.createElement('div');
|
|
959
1118
|
row.className = 'footer-row';
|
|
960
1119
|
|
|
961
|
-
if (method === 'phone') {
|
|
1120
|
+
if (method === 'phone' && savedRoute === undefined) {
|
|
962
1121
|
const alt = document.createElement('button');
|
|
963
1122
|
alt.className = 'footer-link method-link';
|
|
964
1123
|
// On a phone both methods open the wallet on this device — what the
|
|
@@ -967,7 +1126,7 @@ export function runConnectModal(options: ConnectModalOptions): Promise<ConnectRe
|
|
|
967
1126
|
// startPopup runs synchronously in this click handler.
|
|
968
1127
|
alt.addEventListener('click', () => { startPopup(); });
|
|
969
1128
|
row.appendChild(alt);
|
|
970
|
-
} else {
|
|
1129
|
+
} else if (method === 'browser' && savedRoute === undefined) {
|
|
971
1130
|
const alt = document.createElement('button');
|
|
972
1131
|
alt.className = 'footer-link method-link';
|
|
973
1132
|
alt.textContent = isMobile ? 'Use a code instead →' : 'Use your phone instead →';
|