diff --git a/web/src/lib/api/moderationLabels.ts b/web/src/lib/api/moderationLabels.ts index fb6cd0925..f51aa733d 100644 --- a/web/src/lib/api/moderationLabels.ts +++ b/web/src/lib/api/moderationLabels.ts @@ -5,16 +5,22 @@ import { type Did, type Nsid } from "@atcute/lexicons/syntax"; +import type { BobbinContext } from "$lib/api/client"; import { + enrich, labelsOf, target, TYPE_LABELS, + type Enriched, type Label, type LabelsDescriptor, type RecordPath, type Sidecar } from "$lib/api/enrich"; import { PROFILE_COLLECTION } from "$lib/api/profile"; +import type { ProfileRecord, RecordView } from "$lib/api/records"; + +export type LabeledProfile = Enriched>; const SEVERITY = ["hide", "warn"] as const; @@ -86,3 +92,19 @@ export const subjectModeration = ( accountModerationOf(data, subject.author), recordModerationOf(data, collection, subject.uri) ); + +export const getLabeledProfile = (ctx: BobbinContext, did: string): Promise => + enrich>(ctx, { + xrpc: "sh.tangled.actor.getProfile", + params: { actor: `at://${did}/${PROFILE_COLLECTION}/self` }, + enrich: [target(PROFILE_MODERATION, ["uri"])] + }); + +export const profileModeration = ( + account?: Moderation, + profile?: LabeledProfile +): Moderation | undefined => + strongestModeration( + account, + profile && recordModerationOf(profile.data, PROFILE_COLLECTION, profile.output.uri) + ); diff --git a/web/src/lib/components/profile/ProfileChrome.stories.svelte b/web/src/lib/components/profile/ProfileChrome.stories.svelte index 6ace610cf..b6833e694 100644 --- a/web/src/lib/components/profile/ProfileChrome.stories.svelte +++ b/web/src/lib/components/profile/ProfileChrome.stories.svelte @@ -1,6 +1,7 @@ diff --git a/web/src/lib/components/profile/UserProfileChrome.svelte b/web/src/lib/components/profile/UserProfileChrome.svelte index 8342d14c9..5f5598582 100644 --- a/web/src/lib/components/profile/UserProfileChrome.svelte +++ b/web/src/lib/components/profile/UserProfileChrome.svelte @@ -1,8 +1,11 @@ +{#snippet gatedContent()} + {#if gate.masked} + + {:else} + {@render content()} + {/if} +{/snippet} +
{identity.handle} - {#if shown?.pronouns} + {#if shown?.pronouns && !gate.masked} (editing = false)} />
+ {:else if gate.masked} + {:else} {#if shown?.description} - {#if networkVouches.length > 0} + {#if networkVouches.length > 0 && !gate.masked}
{/if} -
{@render content()}
+ {#if !gate.masked} +
{@render content()}
+ {/if} {:else} -
{@render content()}
+
{@render gatedContent()}
{/if} diff --git a/web/src/lib/components/profile/organization/OrganizationChrome.svelte b/web/src/lib/components/profile/organization/OrganizationChrome.svelte index e0f166111..7d5f8535f 100644 --- a/web/src/lib/components/profile/organization/OrganizationChrome.svelte +++ b/web/src/lib/components/profile/organization/OrganizationChrome.svelte @@ -5,8 +5,11 @@ import Building2 from "$icon/building-2"; import { createLoad } from "$lib/action.svelte"; import { viewerControlsOrganization } from "$lib/api/organizations"; + import type { Moderation } from "$lib/api/moderationLabels"; import type { ProfileRecord } from "$lib/api/records"; import { getAuth } from "$lib/auth.svelte"; + import ModerationNotice from "$lib/components/moderation/ModerationNotice.svelte"; + import { createModerationGate } from "$lib/components/moderation/gate.svelte"; import FollowButton from "$lib/components/profile/FollowButton.svelte"; import ProfileBio from "$lib/components/profile/ProfileBio.svelte"; import ProfileMoreMenu from "$lib/components/profile/ProfileMoreMenu.svelte"; @@ -26,6 +29,7 @@ viewerFollowRkey?: string | null; delegationDid?: string; activeTab: string; + moderation?: Moderation; content: Snippet; } @@ -36,6 +40,7 @@ viewerFollowRkey, delegationDid = "", activeTab, + moderation, content }: Props = $props(); @@ -46,6 +51,11 @@ return viewerControlsOrganization(agent, delegationDid, identity.did); }); + const gate = createModerationGate( + () => moderation, + () => auth.currentDid === identity.did || controls.data === true + ); + const blurLayers = [1.5, 2, 4, 6]; const followersHref = $derived( @@ -58,6 +68,14 @@ ); +{#snippet gatedContent()} + {#if gate.masked} + + {:else} + {@render content()} + {/if} +{/snippet} +