diff --git a/web/src/lib/components/profile/NetworkVouches.svelte b/web/src/lib/components/profile/NetworkVouches.svelte index 6b92db0c..39cbbaf1 100644 --- a/web/src/lib/components/profile/NetworkVouches.svelte +++ b/web/src/lib/components/profile/NetworkVouches.svelte @@ -7,9 +7,11 @@ // handle of the profile these vouches are about subjectHandle: string; vouches: NetworkVouch[]; + // forwarded to the avatar lists; off inside a hovercard to avoid nesting + popover?: boolean; } - let { subjectHandle, vouches }: Props = $props(); + let { subjectHandle, vouches, popover = true }: Props = $props(); const vouchers = $derived(vouches.filter((v) => v.kind === "vouch")); const denouncers = $derived(vouches.filter((v) => v.kind === "denounce")); @@ -40,7 +42,7 @@ Vouched for by {vouchers.length} {plural(vouchers.length)} - + {/if} @@ -52,7 +54,7 @@ Denounced by {denouncers.length} {plural(denouncers.length)} - + {/if} diff --git a/web/src/lib/components/profile/ProfilePopover.stories.svelte b/web/src/lib/components/profile/ProfilePopover.stories.svelte new file mode 100644 index 00000000..669d8d7f --- /dev/null +++ b/web/src/lib/components/profile/ProfilePopover.stories.svelte @@ -0,0 +1,122 @@ + + + + + {#snippet template(args)} + +
+ + + +
+
+ {/snippet} +
+ + + + {#snippet template(args)} + +
+ + {HANDLE} + +
+
+ {/snippet} +
+ + + + {#snippet template()} + +
+ + + +
+
+ {/snippet} +
+ + + + {#snippet template(args)} + +
+ + + +
+
+ {/snippet} +
+ + + + {#snippet template(args)} + +
+ + + +
+
+ {/snippet} +
diff --git a/web/src/lib/components/profile/ProfilePopover.svelte b/web/src/lib/components/profile/ProfilePopover.svelte new file mode 100644 index 00000000..79a6c75c --- /dev/null +++ b/web/src/lib/components/profile/ProfilePopover.svelte @@ -0,0 +1,166 @@ + + + + {#snippet content()} + {#if status === "loaded" && card} + {@const cardHandle = card.handle} +
+
+ + + +
+ + {cardHandle} + + {#if card.pronouns} +

+ {card.pronouns} +

+ {/if} +
+
+ + {#if card.description} +

+ {card.description} +

+ {/if} + + + + {#if !card.isSelf} +
+
+ +
+ {#if signedIn} +
+ +
+ {/if} +
+ {/if} + + {#if networkVouches.length > 0} + + {/if} +
+ {:else if status === "error"} +

Couldn't load profile.

+ {:else} +
+ +
+ {/if} + {/snippet} + {@render children()} +
diff --git a/web/src/lib/components/profile/VouchButton.svelte b/web/src/lib/components/profile/VouchButton.svelte index 3813f838..0e1d9254 100644 --- a/web/src/lib/components/profile/VouchButton.svelte +++ b/web/src/lib/components/profile/VouchButton.svelte @@ -46,7 +46,10 @@ let reason = $state(""); let popoverEl = $state(); - const popoverId = $derived(`vouch-popover-${profileDid.replace(/[^a-zA-Z0-9]/g, "-")}`); + // unique per instance: the same subject can render more than one vouch button on a + // page (e.g. several profile hovercards), and a did-derived id would collide + const uid = $props.id(); + const popoverId = `vouch-popover-${uid}`; const label = $derived(profileLabel ?? "this user"); @@ -71,10 +74,20 @@ ? look.vouch : current?.kind === "denounce" ? look.denounce - : ({ icon: ShieldQuestionMark, text: "Vouch", variant: "default", color: "" } as const); + : ({ + icon: ShieldQuestionMark, + text: "Vouch", + variant: "default", + color: "" + } as const); return bordered ? { icon: base.icon, text: base.text, variant: base.variant, colorClass: "" } - : { icon: base.icon, text: base.text, variant: "ghost" as const, colorClass: base.color }; + : { + icon: base.icon, + text: base.text, + variant: "ghost" as const, + colorClass: base.color + }; }); const onToggle = (event: ToggleEvent) => { @@ -116,100 +129,100 @@ {trigger.text} -
-
-

Vouch for {label}

-

- {#if current} - You {current.kind === "vouch" ? "vouched for" : "denounced"} - {label}. You can change your decision below. - {:else} - Vouching builds a web-of-trust across Tangled. Vouch for users you have had positive - interactions with. - - Read more - {/if} -

-
- - {#if networkVouches.length > 0} -
-

From your network

- -
- {/if} - -
- {#each choices as option (option.value)} - {@const Icon = option.icon} - {@const active = choice === option.value} - - {/each} -
- - {#if choice !== "none"} - - {/if} - - + Read more + {/if} +

+
-
- - -
+