import { RecordBacklinksPanel } from "$/components/diagnostics/RecordBacklinksPanel"; import { useModerationDecision } from "$/components/moderation/hooks/useModerationDecision"; import { ModeratedAvatar } from "$/components/moderation/ModeratedAvatar"; import { ModerationBadgeRow } from "$/components/moderation/ModerationBadgeRow"; import { Icon } from "$/components/shared/Icon"; import { useAppSession } from "$/contexts/app-session"; import type { DiagnosticBlockItem, DiagnosticDidProfile, DiagnosticLabel, DiagnosticList, DiagnosticStarterPack, } from "$/lib/api/diagnostics"; import { DiagnosticsController } from "$/lib/api/diagnostics"; import { collectModerationLabels } from "$/lib/moderation"; import { asRecord, getStringProperty } from "$/lib/type-guards"; import { shouldIgnoreKey } from "$/lib/utils/events"; import { formatHandle, initials, normalizeError } from "$/lib/utils/text"; import * as logger from "@tauri-apps/plugin-log"; import { createEffect, createMemo, createSignal, For, Match, onCleanup, onMount, Show, Switch } from "solid-js"; import { createStore } from "solid-js/store"; import { Motion, Presence } from "solid-motionone"; import { DiagnosticsBlockSkeleton, DiagnosticsLabelSkeleton, DiagnosticsListSkeleton, DiagnosticsStarterPackSkeleton, } from "./DiagnosticsSkeleton"; type DiagnosticsTab = "lists" | "labels" | "blocks" | "starterPacks" | "backlinks"; type DiagnosticsPanelProps = { did?: string | null; embedded?: boolean; onClose?: () => void; onOpenExplorerTarget?: (target: string) => void; recordUri?: string | null; }; type DiagnosticsState = { blockedBy: DiagnosticDidProfile[]; blockedByError: string | null; blockedByLoading: boolean; blocking: DiagnosticBlockItem[]; blockingError: string | null; blockingLoading: boolean; labels: DiagnosticLabel[]; labelsError: string | null; labelsLoading: boolean; labelsSourceProfiles: Record; lists: DiagnosticList[]; listsError: string | null; listsLoading: boolean; starterPacks: DiagnosticStarterPack[]; starterPacksError: string | null; starterPacksLoading: boolean; }; const DIAGNOSTICS_TABS: Array<{ label: string; value: DiagnosticsTab }> = [ { label: "Lists", value: "lists" }, { label: "Labels", value: "labels" }, { label: "Blocks", value: "blocks" }, { label: "Starter Packs", value: "starterPacks" }, { label: "Backlinks", value: "backlinks" }, ]; function createInitialState(): DiagnosticsState { return { blockedBy: [], blockedByError: null, blockedByLoading: true, blocking: [], blockingError: null, blockingLoading: true, labels: [], labelsError: null, labelsLoading: true, labelsSourceProfiles: {}, lists: [], listsError: null, listsLoading: true, starterPacks: [], starterPacksError: null, starterPacksLoading: true, }; } function createIdleState(): DiagnosticsState { return { blockedBy: [], blockedByError: null, blockedByLoading: false, blocking: [], blockingError: null, blockingLoading: false, labels: [], labelsError: null, labelsLoading: false, labelsSourceProfiles: {}, lists: [], listsError: null, listsLoading: false, starterPacks: [], starterPacksError: null, starterPacksLoading: false, }; } function purposeLabel(purpose: string | null | undefined) { const normalized = (purpose || "").toLowerCase(); switch (normalized) { case "app.bsky.graph.defs#curatelist": case "curate": case "curation": { return "Curation"; } case "app.bsky.graph.defs#modlist": case "modlist": case "moderation": { return "Moderation"; } case "app.bsky.graph.defs#referencelist": case "reference": { return "Reference"; } default: { if (normalized.endsWith("#curatelist")) { return "Curation"; } if (normalized.endsWith("#modlist")) { return "Moderation"; } if (normalized.endsWith("#referencelist")) { return "Reference"; } return "Other"; } } } function groupListsByPurpose(lists: DiagnosticList[]) { const grouped = [ { label: "Curation", items: lists.filter((list) => purposeLabel(list.purpose) === "Curation") }, { label: "Moderation", items: lists.filter((list) => purposeLabel(list.purpose) === "Moderation") }, { label: "Reference", items: lists.filter((list) => purposeLabel(list.purpose) === "Reference") }, { label: "Other", items: lists.filter((list) => purposeLabel(list.purpose) === "Other"), }, ].filter((group) => group.items.length > 0); return grouped.length > 0 ? grouped : [{ label: "Lists", items: lists }]; } function getDiagnosticEntryHandle(item: DiagnosticBlockItem | DiagnosticDidProfile) { if (item.profile?.handle) { return item.profile.handle; } if ("did" in item) { return item.did; } return item.subjectDid ?? item.profile?.did ?? "Unknown"; } function getLabelDefinition(value: string | null | undefined) { switch ((value || "").toLowerCase()) { case "!hide": { return "Hidden content label."; } case "!hide-media": { return "Media visibility label."; } case "!hide-replies": { return "Replies visibility label."; } case "!no-unauthenticated": { return "Requires a signed-in view."; } case "!warn": { return "Advisory moderation label."; } default: { return "Service-applied moderation metadata."; } } } function getLabelEffect(label: DiagnosticLabel) { if (label.neg) { return "This label negates a previous moderation decision."; } switch ((label.val || "").toLowerCase()) { case "!hide": case "!hide-media": case "!hide-replies": { return "It can change how the record or account is shown in supporting clients."; } case "!no-unauthenticated": { return "It can limit visibility for signed-out browsing."; } default: { return "Its exact effect depends on the labeling service and client policy."; } } } function getSourceProfileName(sourceProfiles: Record, src: string | null | undefined) { if (!src) { return "Unknown service"; } const profile = asRecord(sourceProfiles[src]); if (!profile) { return src; } return getStringProperty(profile, "displayName") ?? formatHandle(getStringProperty(profile, "handle"), null) ?? src; } export function DiagnosticsPanel(props: DiagnosticsPanelProps) { const session = useAppSession(); const [state, setState] = createStore(createInitialState()); const [activeTab, setActiveTab] = createSignal("lists"); const [blocksExpanded, setBlocksExpanded] = createSignal(false); const activeDid = createMemo(() => props.did?.trim() || session.activeDid || ""); const activeRecordUri = createMemo(() => props.recordUri?.trim() || ""); const isSelf = createMemo(() => activeDid() === session.activeDid); let requestId = 0; createEffect(() => { const did = activeDid(); if (!did) { setState(createIdleState()); return; } const currentRequest = ++requestId; setActiveTab("lists"); setBlocksExpanded(false); setState(createInitialState()); void loadLists(currentRequest, did); void loadLabels(currentRequest, did); void loadBlocks(currentRequest, did); void loadStarterPacks(currentRequest, did); }); function handleKeyDown(event: KeyboardEvent) { if (shouldIgnoreKey(event) || event.altKey || event.shiftKey) { return; } if (event.key >= "1" && event.key <= "5") { event.preventDefault(); const nextTab = DIAGNOSTICS_TABS[Number(event.key) - 1]?.value; if (nextTab) { setActiveTab(nextTab); } return; } if (event.key === "Escape") { props.onClose?.(); } } onMount(() => { document.addEventListener("keydown", handleKeyDown); onCleanup(() => document.removeEventListener("keydown", handleKeyDown)); }); async function loadLists(currentRequest: number, did: string) { try { const response = await DiagnosticsController.getAccountLists(did); if (currentRequest !== requestId) return; setState({ lists: response.lists, listsError: null, listsLoading: false }); } catch (error) { const message = normalizeError(error); if (currentRequest !== requestId) return; setState({ listsError: message, listsLoading: false }); logger.warn("failed to load diagnostics lists", { keyValues: { did, error: message } }); } } async function loadLabels(currentRequest: number, did: string) { try { const response = await DiagnosticsController.getAccountLabels(did); if (currentRequest !== requestId) return; setState({ labels: response.labels, labelsError: null, labelsLoading: false, labelsSourceProfiles: response.sourceProfiles, }); } catch (error) { const message = normalizeError(error); if (currentRequest !== requestId) return; setState({ labelsError: message, labelsLoading: false, labelsSourceProfiles: {} }); logger.warn("failed to load diagnostics labels", { keyValues: { did, error: message } }); } } async function loadBlocks(currentRequest: number, did: string) { const [blockedBy, blocking] = await Promise.allSettled([ DiagnosticsController.getAccountBlockedBy(did, 25), DiagnosticsController.getAccountBlocking(did), ]); if (currentRequest !== requestId) { return; } if (blockedBy.status === "fulfilled") { setState({ blockedBy: blockedBy.value.items, blockedByError: null, blockedByLoading: false }); } else { const message = normalizeError(blockedBy.reason); setState({ blockedByError: message, blockedByLoading: false }); logger.warn("failed to load diagnostics blocked-by data", { keyValues: { did, error: message } }); } if (blocking.status === "fulfilled") { setState({ blocking: blocking.value.items, blockingError: null, blockingLoading: false }); } else { const message = normalizeError(blocking.reason); setState({ blockingError: message, blockingLoading: false }); logger.warn("failed to load diagnostics blocking data", { keyValues: { did, error: message } }); } } async function loadStarterPacks(currentRequest: number, did: string) { try { const response = await DiagnosticsController.getAccountStarterPacks(did); if (currentRequest !== requestId) return; setState({ starterPacks: response.starterPacks, starterPacksError: null, starterPacksLoading: false }); } catch (error) { const message = normalizeError(error); if (currentRequest !== requestId) return; setState({ starterPacksError: message, starterPacksLoading: false }); logger.warn("failed to load diagnostics starter packs", { keyValues: { did, error: message } }); } } return (
void loadBlocks(requestId, activeDid())} onRetryBlocking={() => void loadBlocks(requestId, activeDid())} onRetryLabels={() => void loadLabels(requestId, activeDid())} onRetryLists={() => void loadLists(requestId, activeDid())} onRetryStarterPacks={() => void loadStarterPacks(requestId, activeDid())} onToggleBlocks={() => setBlocksExpanded((value) => !value)} recordUri={activeRecordUri()} state={state} />
); } function DiagnosticsHeader(props: { did: string; embedded: boolean; isSelf: boolean; onClose?: () => void }) { return (

Context

Social Diagnostics

{props.isSelf ? "Your boundaries and public footprint" : "Public social context for this account"}

{props.did || "No account selected"}

); } function DiagnosticsTabs(props: { activeTab: DiagnosticsTab; onSelectTab: (tab: DiagnosticsTab) => void }) { const activeIndex = createMemo(() => DIAGNOSTICS_TABS.findIndex((tab) => tab.value === props.activeTab)); return ( ); } function DiagnosticsViewport( props: { activeTab: DiagnosticsTab; blocksExpanded: boolean; isSelf: boolean; onOpenExplorerTarget?: (target: string) => void; onRetryBlockedBy: () => void; onRetryBlocking: () => void; onRetryLabels: () => void; onRetryLists: () => void; onRetryStarterPacks: () => void; onToggleBlocks: () => void; recordUri: string; state: DiagnosticsState; }, ) { return (
); } function DiagnosticsListsTab( props: { error: string | null; lists: DiagnosticList[]; loading: boolean; onOpenExplorerTarget?: (target: string) => void; onRetry: () => void; }, ) { return (
}> {(group) => (

{group.label}

{(list) => }
)}
}> {(error) => }
); } function DiagnosticsLabelsTab( props: { error: string | null; labels: DiagnosticLabel[]; loading: boolean; onRetry: () => void; sourceProfiles: Record; }, ) { return (
} each={props.labels}> {(label, index) => ( )} }> {(error) => }
); } function DiagnosticsBlocksTab( props: { blockedBy: DiagnosticDidProfile[]; blockedByError: string | null; blockedByLoading: boolean; blocking: DiagnosticBlockItem[]; blockingError: string | null; blockingLoading: boolean; expanded: boolean; isSelf: boolean; onRetryBlockedBy: () => void; onRetryBlocking: () => void; onToggleExpanded: () => void; }, ) { const blockedByCount = createMemo(() => props.blockedBy.length); const blockingCount = createMemo(() => props.blocking.length); return (
Blocks are a normal part of social media. This data is public on the AT Protocol.
); } function DiagnosticsBlock( props: { error: string | null; items: DiagnosticBlockItem[] | DiagnosticDidProfile[]; loading: boolean; onRetry: () => void; title: string; }, ) { const items = createMemo(() => props.items.map((item) => ({ available: item.availability === "available", avatar: item.availability === "available" ? item.profile?.avatar ?? null : null, description: item.availability === "available" ? item.profile?.description ?? null : null, displayName: item.profile?.displayName ?? null, handle: getDiagnosticEntryHandle(item), labels: item.availability === "available" ? item.profile?.labels ?? null : null, unavailableMessage: item.unavailableMessage ?? "Profile unavailable", })) ); return ( }> {(error) => } ); } function DiagnosticsStarterPacksTab( props: { error: string | null; loading: boolean; onOpenExplorerTarget?: (target: string) => void; onRetry: () => void; starterPacks: DiagnosticStarterPack[]; }, ) { return (
}> {(pack) => } }> {(error) => }
); } function DiagnosticsTabIntro(props: { description: string; title: string }) { return (

{props.title}

{props.description}

); } function DiagnosticsError(props: { message: string | null; onRetry?: () => void }) { return (

{props.message}

); } function DiagnosticsEmptyState(props: { copy: string }) { return (
{props.copy}
); } function StatCard(props: { label: string; value: number }) { return (

{props.label}

{props.value}

); } function LabelChip(props: { index: number; label: DiagnosticLabel; sourceName: string }) { const title = createMemo(() => [ `Label: ${props.label.val ?? "Unknown"}`, `Definition: ${getLabelDefinition(props.label.val)}`, `Source: ${props.sourceName}`, `Effect: ${getLabelEffect(props.label)}`, ].join("\n") ); return ( {props.label.val ?? "label"} {props.sourceName} ); } function ListCard(props: { list: DiagnosticList; onOpenExplorerTarget?: (target: string) => void }) { const count = () => props.list.memberCount ?? props.list.listItemCount ?? 0; const title = () => props.list.title ?? props.list.name ?? "Untitled list"; return (

{title()}

{purposeLabel(props.list.purpose)}

Owner: {formatHandle(props.list.creator?.handle, null)}

{props.list.description ?? "No description provided."}

{count()} members {uri => ( )}
); } function StarterPackCard(props: { onOpenExplorerTarget?: (target: string) => void; pack: DiagnosticStarterPack }) { const count = () => props.pack.listItemCount ?? props.pack.record?.listItemsSample?.length ?? 0; const title = () => props.pack.title ?? props.pack.name ?? props.pack.record?.name ?? "Starter pack"; return (

{title()}

Creator: {formatHandle(props.pack.creator?.handle ?? null, null)}

{props.pack.description ?? props.pack.record?.description ?? "No description provided."}

{count()} members {uri => ( )}
); } function BlockProfileList( props: { items: Array< { available: boolean; avatar?: string | null; description?: string | null; displayName?: string | null; handle: string; labels?: DiagnosticLabel[] | null; unavailableMessage: string; } >; title: string; }, ) { return (

{props.title}

{(item, index) => }
); } function BlockProfileRow( props: { index: number; item: { available: boolean; avatar?: string | null; description?: string | null; displayName?: string | null; handle: string; labels?: DiagnosticLabel[] | null; unavailableMessage: string; }; }, ) { const name = createMemo(() => props.item.displayName ?? props.item.handle); const profileLabels = () => collectModerationLabels({ labels: props.item.labels ?? null }); const avatarDecision = useModerationDecision(profileLabels, "avatar"); const profileDecision = useModerationDecision(profileLabels, "profileList"); return ( }>

{name()}

{formatHandle(props.item.handle, null)}

{(description) =>

{description()}

}

{props.item.unavailableMessage}

); }