|
12 | 12 | OpenIdConfig, |
13 | 13 | OpenIdCredential, |
14 | 14 | } from "$lib/generated/internet_identity_types"; |
| 15 | + import { waitFor } from "$lib/utils/utils"; |
| 16 | + import { t } from "$lib/stores/locale.store"; |
| 17 | + import { Trans } from "$lib/components/locale"; |
15 | 18 |
|
16 | 19 | interface Props { |
17 | | - linkOpenIdAccount: (config: OpenIdConfig) => Promise<void>; |
| 20 | + linkOpenIdAccount: (config: OpenIdConfig) => Promise<"cancelled" | void>; |
18 | 21 | continueWithPasskey: () => void; |
19 | 22 | openIdCredentials?: OpenIdCredential[]; |
20 | 23 | maxPasskeysReached?: boolean; |
|
27 | 30 | maxPasskeysReached, |
28 | 31 | }: Props = $props(); |
29 | 32 |
|
30 | | - let authenticatingGoogle = $state(false); |
31 | 33 | let authenticatingProviderId = $state<string | null>(); |
32 | | - let authenticating = $derived( |
33 | | - nonNullish(authenticatingProviderId) || authenticatingGoogle, |
34 | | - ); |
| 34 | + let cancelledProviderId = $state<string | null>(null); |
35 | 35 |
|
36 | 36 | const isPasskeySupported = nonNullish(window.PublicKeyCredential); |
37 | 37 |
|
38 | 38 | const handleContinueWithOpenId = async (config: OpenIdConfig) => { |
39 | 39 | authenticatingProviderId = config.client_id; |
40 | | - try { |
41 | | - await linkOpenIdAccount(config); |
42 | | - } finally { |
43 | | - authenticatingProviderId = null; |
| 40 | + const result = await linkOpenIdAccount(config); |
| 41 | + authenticatingProviderId = null; |
| 42 | +
|
| 43 | + if (result === "cancelled") { |
| 44 | + cancelledProviderId = config.client_id; |
| 45 | + await waitFor(4000); |
| 46 | + cancelledProviderId = null; |
44 | 47 | } |
45 | 48 | }; |
46 | 49 |
|
|
57 | 60 | <ShieldIllustration class="text-text-primary mb-8 h-24" /> |
58 | 61 | </div> |
59 | 62 | <h1 class="text-text-primary mb-3 text-2xl font-medium sm:text-center"> |
60 | | - Add access method |
| 63 | + {$t`Add access method`} |
61 | 64 | </h1> |
62 | 65 | <p |
63 | 66 | class="text-text-tertiary text-base font-medium text-balance sm:text-center" |
64 | 67 | > |
65 | | - Add another way to sign in with a passkey or third-party account for secure |
66 | | - access. |
| 68 | + <Trans> |
| 69 | + Add another way to sign in with a passkey or third-party account for |
| 70 | + secure access. |
| 71 | + </Trans> |
67 | 72 | </p> |
68 | 73 | </div> |
69 | 74 | <div class="flex flex-col items-stretch gap-6"> |
70 | 75 | {#if !isPasskeySupported} |
71 | 76 | <Alert |
72 | | - title="Passkeys not available here" |
73 | | - description="Passkeys are unavailable on this device or browser. Please choose |
74 | | - another access method to continue." |
| 77 | + title={$t`Passkeys not available here`} |
| 78 | + description={$t`Passkeys are unavailable on this device or browser. Please choose another access method to continue.`} |
75 | 79 | direction="horizontal" |
76 | 80 | /> |
77 | 81 | {/if} |
78 | 82 | <div class="flex flex-col items-stretch gap-3"> |
| 83 | + <div class="flex flex-row flex-nowrap justify-stretch gap-3"> |
| 84 | + {#each openIdProviders as provider} |
| 85 | + {@const name = provider.name} |
| 86 | + <Tooltip |
| 87 | + label={$t`Interaction canceled. Please try again.`} |
| 88 | + hidden={cancelledProviderId !== provider.client_id} |
| 89 | + manual |
| 90 | + > |
| 91 | + <Tooltip |
| 92 | + label={$t`You already have a ${name} account linked`} |
| 93 | + hidden={!hasCredential(provider.issuer)} |
| 94 | + > |
| 95 | + <Button |
| 96 | + onclick={() => handleContinueWithOpenId(provider)} |
| 97 | + variant="secondary" |
| 98 | + disabled={nonNullish(authenticatingProviderId) || |
| 99 | + hasCredential(provider.issuer)} |
| 100 | + size="xl" |
| 101 | + class="flex-1" |
| 102 | + aria-label={$t`Continue with ${name}`} |
| 103 | + > |
| 104 | + {#if authenticatingProviderId === provider.client_id} |
| 105 | + <ProgressRing /> |
| 106 | + {:else if provider.logo} |
| 107 | + <div class="size-6"> |
| 108 | + {@html provider.logo} |
| 109 | + </div> |
| 110 | + {/if} |
| 111 | + </Button> |
| 112 | + </Tooltip> |
| 113 | + </Tooltip> |
| 114 | + {/each} |
| 115 | + </div> |
79 | 116 | <Tooltip |
80 | | - label="You have reached the maximum number of passkeys." |
| 117 | + label={$t`You have reached the maximum number of passkeys`} |
81 | 118 | hidden={!maxPasskeysReached} |
82 | 119 | > |
83 | 120 | <Button |
84 | 121 | onclick={continueWithPasskey} |
85 | | - disabled={!isPasskeySupported || authenticating || maxPasskeysReached} |
| 122 | + variant="secondary" |
| 123 | + disabled={!isPasskeySupported || |
| 124 | + nonNullish(authenticatingProviderId) || |
| 125 | + maxPasskeysReached} |
86 | 126 | size="xl" |
87 | 127 | > |
88 | 128 | <PasskeyIcon /> |
89 | | - Continue with passkey |
| 129 | + <span>{$t`Continue with passkey`}</span> |
90 | 130 | </Button> |
91 | 131 | </Tooltip> |
92 | | - <div class="flex flex-row flex-nowrap justify-stretch gap-3"> |
93 | | - {#each openIdProviders as provider} |
94 | | - <Tooltip |
95 | | - label={`You already have a ${provider.name} account linked`} |
96 | | - hidden={!hasCredential(provider.issuer)} |
97 | | - > |
98 | | - <Button |
99 | | - onclick={() => handleContinueWithOpenId(provider)} |
100 | | - variant="secondary" |
101 | | - disabled={authenticating || hasCredential(provider.issuer)} |
102 | | - size="xl" |
103 | | - class="flex-1" |
104 | | - > |
105 | | - {#if authenticatingProviderId === provider.client_id} |
106 | | - <ProgressRing /> |
107 | | - {:else if provider.logo} |
108 | | - <div class="size-6"> |
109 | | - {@html provider.logo} |
110 | | - </div> |
111 | | - {/if} |
112 | | - </Button> |
113 | | - </Tooltip> |
114 | | - {/each} |
115 | | - </div> |
116 | 132 | </div> |
117 | 133 | </div> |
118 | 134 |
|
|
0 commit comments