Skip to content

Commit 16f0b1b

Browse files
committed
New access methods page design.
1 parent d226706 commit 16f0b1b

File tree

3 files changed

+549
-1
lines changed

3 files changed

+549
-1
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<script lang="ts">
2+
import { getMetadataString, openIdLogo, openIdName } from "$lib/utils/openID";
3+
import { EllipsisVerticalIcon, Link2OffIcon } from "@lucide/svelte";
4+
import { nonNullish } from "@dfinity/utils";
5+
import { nanosToMillis } from "$lib/utils/time";
6+
import Select from "$lib/components/ui/Select.svelte";
7+
import Button from "$lib/components/ui/Button.svelte";
8+
import Tooltip from "$lib/components/ui/Tooltip.svelte";
9+
import type { OpenIdCredential } from "$lib/generated/internet_identity_types";
10+
import { formatDate, formatRelative, t } from "$lib/stores/locale.store";
11+
12+
interface Props {
13+
openid: OpenIdCredential;
14+
onUnlink?: () => void;
15+
inUse?: boolean;
16+
}
17+
18+
const { openid, onUnlink, inUse }: Props = $props();
19+
20+
const name = $derived(openIdName(openid.iss, openid.metadata));
21+
const email = $derived(getMetadataString(openid.metadata, "email"));
22+
const logo = $derived(openIdLogo(openid.iss, openid.metadata));
23+
const options = $derived(
24+
nonNullish(onUnlink)
25+
? [
26+
{
27+
label: $t`Unlink`,
28+
icon: Link2OffIcon,
29+
onClick: onUnlink,
30+
},
31+
]
32+
: [],
33+
);
34+
</script>
35+
36+
<div class="mb-3 flex h-9 flex-row items-center">
37+
{#if nonNullish(logo)}
38+
<div class="text-fg-primary relative size-6">
39+
{@html logo}
40+
{#if inUse}
41+
<div
42+
class="bg-bg-success-secondary border-bg-primary absolute -top-0.25 -right-0.5 size-2.5 rounded-full border-2"
43+
></div>
44+
{/if}
45+
</div>
46+
{/if}
47+
{#if options.length > 0}
48+
<Select {options} align="end">
49+
<Button
50+
variant="tertiary"
51+
size="sm"
52+
iconOnly
53+
class="ml-auto"
54+
aria-label={$t`More options`}
55+
>
56+
<EllipsisVerticalIcon class="size-5" />
57+
</Button>
58+
</Select>
59+
{/if}
60+
</div>
61+
<div class="text-text-primary mb-1 text-base font-semibold">
62+
{$t`${name} account`}
63+
</div>
64+
<div class="text-text-tertiary text-sm">
65+
{email ?? $t`Hidden email`}
66+
</div>
67+
<div class="border-border-tertiary my-5 border-t"></div>
68+
<div class="mb-4 flex flex-row">
69+
<div class="flex flex-1 flex-col gap-1">
70+
<div class="text-text-primary text-xs font-semibold">
71+
{$t`Last used`}
72+
</div>
73+
<div class="text-text-primary cursor-default text-xs">
74+
{#if inUse}
75+
<Tooltip
76+
label={$t`Currently signed in with this account`}
77+
direction="up"
78+
align="start"
79+
>
80+
<span>{$t`Right now`}</span>
81+
</Tooltip>
82+
{:else if nonNullish(openid.last_usage_timestamp[0])}
83+
{@const date = new Date(nanosToMillis(openid.last_usage_timestamp[0]))}
84+
<Tooltip
85+
label={$formatDate(date, {
86+
timeStyle: "short",
87+
dateStyle: "medium",
88+
})}
89+
direction="up"
90+
align="start"
91+
>
92+
<span>{$formatRelative(date, { style: "long" })}</span>
93+
</Tooltip>
94+
{:else}
95+
<Tooltip label={$t`Has not been used yet`} direction="up" align="start">
96+
<span>{$t`n/a`}</span>
97+
</Tooltip>
98+
{/if}
99+
</div>
100+
</div>
101+
</div>
102+
<div class="text-text-primary text-xs">
103+
{$t`Sign in with your ${name} account from any device.`}
104+
</div>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<script lang="ts">
2+
import { EllipsisVerticalIcon, PencilIcon, Trash2Icon } from "@lucide/svelte";
3+
import { nonNullish } from "@dfinity/utils";
4+
import { nanosToMillis } from "$lib/utils/time";
5+
import Select from "$lib/components/ui/Select.svelte";
6+
import Button from "$lib/components/ui/Button.svelte";
7+
import Tooltip from "$lib/components/ui/Tooltip.svelte";
8+
import type { AuthnMethodData } from "$lib/generated/internet_identity_types";
9+
import { formatDate, formatRelative, t } from "$lib/stores/locale.store";
10+
import PasskeyIcon from "$lib/components/icons/PasskeyIcon.svelte";
11+
import { getAuthnMethodAlias } from "$lib/utils/webAuthn";
12+
import { isLegacyAuthnMethod } from "$lib/utils/accessMethods";
13+
14+
interface Props {
15+
passkey: AuthnMethodData;
16+
onRename?: () => void;
17+
onRemove?: () => void;
18+
inUse?: boolean;
19+
}
20+
21+
const { passkey, onRename, onRemove, inUse }: Props = $props();
22+
23+
const alias = $derived(getAuthnMethodAlias(passkey));
24+
const options = $derived([
25+
...(nonNullish(onRename)
26+
? [
27+
{
28+
label: $t`Rename`,
29+
icon: PencilIcon,
30+
onClick: onRename,
31+
},
32+
]
33+
: []),
34+
...(nonNullish(onRemove)
35+
? [
36+
{
37+
label: $t`Remove`,
38+
icon: Trash2Icon,
39+
onClick: onRemove,
40+
},
41+
]
42+
: []),
43+
]);
44+
const isLegacy = $derived(isLegacyAuthnMethod(passkey));
45+
</script>
46+
47+
<div class={[isLegacy && "opacity-50"]}>
48+
<div class="mb-3 flex h-9 flex-row items-center">
49+
<div class="relative">
50+
<PasskeyIcon class="text-fg-primary size-6" />
51+
{#if inUse}
52+
<div
53+
class="bg-bg-success-secondary border-bg-primary absolute top-0 -right-0.25 size-2.5 rounded-full border-2"
54+
></div>
55+
{/if}
56+
</div>
57+
{#if options.length > 0 && !isLegacy}
58+
<Select {options} align="end">
59+
<Button
60+
variant="tertiary"
61+
size="sm"
62+
iconOnly
63+
class="ml-auto"
64+
aria-label={$t`More options`}
65+
>
66+
<EllipsisVerticalIcon class="size-5" />
67+
</Button>
68+
</Select>
69+
{/if}
70+
</div>
71+
<div class="text-text-primary mb-1 text-base font-semibold">
72+
{alias}
73+
</div>
74+
<div class="text-text-tertiary text-sm">
75+
{$t`Passkey`}
76+
</div>
77+
<div class="border-border-tertiary my-5 border-t"></div>
78+
<div class="mb-4 flex flex-row">
79+
<div class="flex flex-1 flex-col gap-1">
80+
<div class="text-text-primary text-xs font-semibold">
81+
{$t`Last used`}
82+
</div>
83+
<div class="text-text-primary cursor-default text-xs">
84+
{#if inUse}
85+
<Tooltip
86+
label={$t`Currently signed in with this passkey`}
87+
direction="up"
88+
align="start"
89+
>
90+
<span>{$t`Right now`}</span>
91+
</Tooltip>
92+
{:else if nonNullish(passkey.last_authentication[0])}
93+
{@const date = new Date(
94+
nanosToMillis(passkey.last_authentication[0]),
95+
)}
96+
<Tooltip
97+
label={$formatDate(date, {
98+
timeStyle: "short",
99+
dateStyle: "medium",
100+
})}
101+
direction="up"
102+
align="start"
103+
>
104+
<span>{$formatRelative(date, { style: "long" })}</span>
105+
</Tooltip>
106+
{:else}
107+
<Tooltip
108+
label={$t`Has not been used yet`}
109+
direction="up"
110+
align="start"
111+
>
112+
<span>{$t`n/a`}</span>
113+
</Tooltip>
114+
{/if}
115+
</div>
116+
</div>
117+
</div>
118+
<div class="text-text-primary text-xs">
119+
{$t`Stored securely on your device, in your password manager, or on a security key.`}
120+
</div>
121+
</div>

0 commit comments

Comments
 (0)