Skip to content

Commit 4ebc8e4

Browse files
authored
Update add/remove access method views. (#3465)
* Update access method views. * Fix e2e.
1 parent daf6b89 commit 4ebc8e4

File tree

9 files changed

+292
-176
lines changed

9 files changed

+292
-176
lines changed

src/frontend/src/lib/components/views/AccessMethods.svelte

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
import identityInfo from "$lib/stores/identity-info.state.svelte";
66
import AccessMethod from "$lib/components/ui/AccessMethod.svelte";
77
import PasskeyIcon from "$lib/components/icons/PasskeyIcon.svelte";
8-
import RemoveOpenIdCredential from "$lib/components/views/RemoveOpenIdCredential.svelte";
8+
import UnlinkOpenIdCredential from "$lib/components/views/UnlinkOpenIdCredential.svelte";
99
import { invalidateAll } from "$app/navigation";
1010
import type {
1111
AuthnMethodData,
1212
OpenIdCredential,
1313
} from "$lib/generated/internet_identity_types";
14-
import RemovePasskeyDialog from "$lib/components/views/RemovePasskeyDialog.svelte";
15-
import RenamePasskeyDialog from "$lib/components/views/RenamePasskeyDialog.svelte";
14+
import RemovePasskey from "$lib/components/views/RemovePasskey.svelte";
15+
import RenamePasskey from "$lib/components/views/RenamePasskey.svelte";
1616
import { nonNullish } from "@dfinity/utils";
1717
import { handleError } from "$lib/components/utils/error";
1818
import {
@@ -27,6 +27,7 @@
2727
import { openIdLogo, openIdName } from "$lib/utils/openID";
2828
import Tooltip from "../ui/Tooltip.svelte";
2929
import { accessMethods } from "$lib/derived/accessMethods.derived.svelte";
30+
import Dialog from "$lib/components/ui/Dialog.svelte";
3031
3132
let isAddAccessMethodWizardOpen = $state(false);
3233
let removableAuthnMethod = $state<AuthnMethodData | null>(null);
@@ -239,31 +240,37 @@
239240
</div>
240241

241242
{#if removableOpenIdCredential}
242-
<RemoveOpenIdCredential
243-
onRemove={handleRemoveOpenIdCredential}
244-
onClose={() => (removableOpenIdCredential = null)}
245-
openIDName={openIdName(
246-
removableOpenIdCredential.iss,
247-
removableOpenIdCredential.metadata,
248-
) ?? "Google"}
249-
isCurrentAccessMethod={isRemovableOpenIdCredentialCurrentAccessMethod}
250-
/>
243+
<Dialog onClose={() => (removableOpenIdCredential = null)}>
244+
<UnlinkOpenIdCredential
245+
onUnlink={handleRemoveOpenIdCredential}
246+
onCancel={() => (removableOpenIdCredential = null)}
247+
providerName={openIdName(
248+
removableOpenIdCredential.iss,
249+
removableOpenIdCredential.metadata,
250+
) ?? "Google"}
251+
isCurrentAccessMethod={isRemovableOpenIdCredentialCurrentAccessMethod}
252+
/>
253+
</Dialog>
251254
{/if}
252255

253256
{#if removableAuthnMethod}
254-
<RemovePasskeyDialog
255-
onRemove={handleRemovePasskey}
256-
onClose={() => (removableAuthnMethod = null)}
257-
isCurrentAccessMethod={isRemovableAuthnMethodCurrentAccessMethod}
258-
/>
257+
<Dialog onClose={() => (removableAuthnMethod = null)}>
258+
<RemovePasskey
259+
onRemove={handleRemovePasskey}
260+
onCancel={() => (removableAuthnMethod = null)}
261+
isCurrentAccessMethod={isRemovableAuthnMethodCurrentAccessMethod}
262+
/>
263+
</Dialog>
259264
{/if}
260265

261266
{#if renamableAuthnMethod}
262-
<RenamePasskeyDialog
263-
currentName={getAuthnMethodAlias(renamableAuthnMethod)}
264-
onRename={handleRenamePasskey}
265-
onClose={() => (renamableAuthnMethod = null)}
266-
/>
267+
<Dialog onClose={() => (renamableAuthnMethod = null)}>
268+
<RenamePasskey
269+
name={getAuthnMethodAlias(renamableAuthnMethod)}
270+
onRename={handleRenamePasskey}
271+
onCancel={() => (renamableAuthnMethod = null)}
272+
/>
273+
</Dialog>
267274
{/if}
268275

269276
{#if isAddAccessMethodWizardOpen}

src/frontend/src/lib/components/views/EditAccount.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
variant="primary"
104104
size="lg"
105105
type="submit"
106-
disabled={name.length === 0 ||
106+
disabled={name.trim().length === 0 ||
107107
name.length > 32 ||
108108
!hasChanges ||
109109
nameExists ||

src/frontend/src/lib/components/views/RemoveOpenIdCredential.svelte

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<script lang="ts">
2+
import FeaturedIcon from "$lib/components/ui/FeaturedIcon.svelte";
3+
import Button from "$lib/components/ui/Button.svelte";
4+
import { TriangleAlertIcon } from "@lucide/svelte";
5+
import ProgressRing from "$lib/components/ui/ProgressRing.svelte";
6+
import { t } from "$lib/stores/locale.store";
7+
import { Trans } from "$lib/components/locale";
8+
9+
interface Props {
10+
onRemove: () => Promise<void>;
11+
onCancel: () => void;
12+
isCurrentAccessMethod?: boolean;
13+
}
14+
15+
const { onRemove, onCancel, isCurrentAccessMethod = false }: Props = $props();
16+
17+
let isRemoving = $state(false);
18+
19+
const handleRemove = async () => {
20+
try {
21+
isRemoving = true;
22+
await onRemove();
23+
} finally {
24+
isRemoving = false;
25+
}
26+
};
27+
</script>
28+
29+
<div class="flex flex-1 flex-col">
30+
<div class="mb-8 flex flex-col">
31+
<FeaturedIcon variant="warning" size="lg" class="mb-3">
32+
<TriangleAlertIcon class="size-6" />
33+
</FeaturedIcon>
34+
<h1 class="text-text-primary mb-3 text-2xl font-medium">
35+
{$t`Are you sure?`}
36+
</h1>
37+
<div
38+
class={[
39+
"flex flex-col gap-4",
40+
"[&_p]:text-text-tertiary [&_p]:text-base [&_p]:font-medium",
41+
]}
42+
>
43+
<p>
44+
<Trans>
45+
Removing this passkey means you won't be able to use it to sign in
46+
anymore. You can always add a new one later.
47+
</Trans>
48+
</p>
49+
<p>
50+
<Trans>It won't be removed from your device or password manager.</Trans>
51+
</p>
52+
{#if isCurrentAccessMethod}
53+
<p>
54+
<Trans>
55+
As you are currently signed in with this passkey, you will be signed
56+
out.
57+
</Trans>
58+
</p>
59+
{/if}
60+
</div>
61+
</div>
62+
<div class="mt-auto flex flex-col items-stretch gap-3">
63+
<Button onclick={handleRemove} size="lg" danger disabled={isRemoving}>
64+
{#if isRemoving}
65+
<ProgressRing />
66+
<span>{$t`Removing passkey...`}</span>
67+
{:else}
68+
<span>{$t`Remove passkey`}</span>
69+
{/if}
70+
</Button>
71+
<Button
72+
onclick={onCancel}
73+
variant="tertiary"
74+
size="lg"
75+
disabled={isRemoving}
76+
>
77+
{$t`Cancel`}
78+
</Button>
79+
</div>
80+
</div>

src/frontend/src/lib/components/views/RemovePasskeyDialog.svelte

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<script lang="ts">
2+
import FeaturedIcon from "$lib/components/ui/FeaturedIcon.svelte";
3+
import Button from "$lib/components/ui/Button.svelte";
4+
import Input from "$lib/components/ui/Input.svelte";
5+
import { PencilIcon } from "@lucide/svelte";
6+
import { t } from "$lib/stores/locale.store";
7+
import ProgressRing from "$lib/components/ui/ProgressRing.svelte";
8+
9+
interface Props {
10+
name: string;
11+
onRename: (name: string) => Promise<void>;
12+
onCancel: () => void;
13+
}
14+
15+
const props: Props = $props();
16+
17+
let name = $state(props.name);
18+
let isSubmitting = $state(false);
19+
let inputRef = $state<HTMLInputElement>();
20+
21+
const hasChanges = $derived(props.name !== name.trim());
22+
23+
const handleSubmit = async () => {
24+
try {
25+
isSubmitting = true;
26+
await props.onRename(name.trim());
27+
} finally {
28+
isSubmitting = false;
29+
}
30+
};
31+
32+
$effect(() => {
33+
inputRef?.focus();
34+
});
35+
</script>
36+
37+
<form class="flex flex-1 flex-col">
38+
<div class="mb-8 flex flex-col">
39+
<FeaturedIcon size="lg" class="mb-4 self-start">
40+
<PencilIcon class="size-6" />
41+
</FeaturedIcon>
42+
<h1 class="text-text-primary mb-3 text-2xl font-medium">
43+
{$t`Rename passkey`}
44+
</h1>
45+
<p class="text-text-tertiary mb-6 text-base font-medium">
46+
{$t`Give your passkey a memorable name to help you identify it.`}
47+
</p>
48+
<Input
49+
bind:element={inputRef}
50+
bind:value={name}
51+
inputmode="text"
52+
placeholder={$t`Passkey name`}
53+
type="text"
54+
autocomplete="off"
55+
autocorrect="off"
56+
spellcheck="false"
57+
error={name.length > 32
58+
? $t`Maximum length is 32 characters.`
59+
: undefined}
60+
disabled={isSubmitting}
61+
aria-label={$t`Passkey name`}
62+
/>
63+
</div>
64+
<div class="mt-auto flex flex-col items-stretch gap-3">
65+
<Button
66+
type="submit"
67+
size="lg"
68+
onclick={handleSubmit}
69+
disabled={name.trim().length === 0 ||
70+
name.length > 32 ||
71+
!hasChanges ||
72+
isSubmitting}
73+
>
74+
{#if isSubmitting}
75+
<ProgressRing />
76+
<span>{$t`Saving changes...`}</span>
77+
{:else}
78+
<span>{$t`Save changes`}</span>
79+
{/if}
80+
</Button>
81+
<Button
82+
onclick={props.onCancel}
83+
variant="tertiary"
84+
size="lg"
85+
disabled={isSubmitting}
86+
>
87+
{$t`Cancel`}
88+
</Button>
89+
</div>
90+
</form>

0 commit comments

Comments
 (0)