an independent Bluesky client using Constellation, PDS Queries, and other services
reddwarf.app
frontend
spa
bluesky
reddwarf
microcosm
client
app
1import { useQueries } from "@tanstack/react-query";
2import { useAtom, useSetAtom } from "jotai";
3import { useEffect, useRef } from "react";
4
5import { FORCED_LABELER_DIDS, UNAUTHED_FORCE_WARN_LABELS } from "~/../policy";
6import { useAuth } from "~/providers/UnifiedAuthProvider";
7import { labelerConfigAtom } from "~/state/moderationAtoms";
8import type { LabelerDefinition, LabelPreference, LabelValueDefinition } from "~/types/moderation";
9import { slingshotURLAtom } from "~/utils/atoms";
10import { useQueryIdentity } from "~/utils/useQuery";
11import { useQueryPreferences } from "~/utils/useQuery";
12
13// Manual DID document resolution
14const fetchDidDocument = async (did: string): Promise<any> => {
15 if (did.startsWith("did:plc:")) {
16 const response = await fetch(
17 `https://plc.directory/${encodeURIComponent(did)}`,
18 );
19 if (!response.ok)
20 throw new Error(`Failed to fetch PLC DID document for ${did}`);
21 return response.json();
22 } else if (did.startsWith("did:web:")) {
23 const handle = did.replace("did:web:", "");
24 const url = `https://${handle}/.well-known/did.json`;
25 const response = await fetch(url);
26 if (!response.ok)
27 throw new Error(
28 `Failed to fetch web DID document for ${did} (CORS or not found)`,
29 );
30 return response.json();
31 } else {
32 throw new Error(`Unsupported DID type: ${did}`);
33 }
34};
35
36export const ModerationInitializer = () => {
37 const { agent, status } = useAuth();
38 const setLabelerConfig = useSetAtom(labelerConfigAtom);
39 const [slingshoturl] = useAtom(slingshotURLAtom);
40
41 // Define clear boolean for mode
42 const isUnauthed = status === "signedOut" || !agent;
43
44 // Track previous status to detect transitions
45 const prevStatusRef = useRef(status);
46
47 // --- 1. THE HARD FLUSH ---
48 // When Auth Status changes (Logged In <-> Logged Out), immediately wipe the config.
49 // This prevents "Authed" prefs from bleeding into "Unauthed" state and vice versa
50 // while the async queries are spinning up.
51 useEffect(() => {
52 if (prevStatusRef.current !== status) {
53 console.log(`[Moderation] Auth status changed (${prevStatusRef.current} -> ${status}). Flushing config.`);
54 setLabelerConfig([]); // <--- WIPE CLEAN
55 prevStatusRef.current = status;
56 }
57 }, [status, setLabelerConfig]);
58
59 // 2. Get User Identity (Only if authed)
60 const { data: identity } = useQueryIdentity(agent?.did);
61
62 // 3. Get User Preferences (Only if authed)
63 const { data: prefs } = useQueryPreferences({
64 agent: agent ?? undefined,
65 pdsUrl: identity?.pds,
66 });
67
68 // 4. Identify Labeler DIDs
69 // Important: If unauthed, userPrefDids MUST be empty, even if cache exists.
70 const userPrefDids = !isUnauthed
71 ? prefs?.preferences
72 ?.find((pref: any) => pref.$type === "app.bsky.actor.defs#labelersPref")
73 ?.labelers?.map((l: any) => l.did) ?? []
74 : [];
75
76 // 5. Force Bsky DID + User DIDs
77 const activeLabelerDids = Array.from(
78 new Set([...FORCED_LABELER_DIDS, ...userPrefDids])
79 );
80
81 // 6. Parallel fetch DID Docs
82 const labelerDidDocQueries = useQueries({
83 queries: activeLabelerDids.map((did: string) => ({
84 queryKey: ["labelerDidDoc", did],
85 queryFn: () => fetchDidDocument(did),
86 staleTime: 1000 * 60 * 60 * 24,
87 })),
88 });
89
90 // 7. Parallel fetch Service Records
91 const labelerServiceQueries = useQueries({
92 queries: activeLabelerDids.map((did: string) => ({
93 queryKey: ["labelerService", did],
94 queryFn: async () => {
95 const host = slingshoturl || "public.api.bsky.app";
96 const response = await fetch(
97 `https://${host}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent("app.bsky.labeler.service")}&rkey=self`,
98 );
99 if (!response.ok) throw new Error("Failed to fetch labeler service");
100 return response.json();
101 },
102 staleTime: 1000 * 60 * 60,
103 })),
104 });
105
106 useEffect(() => {
107 // Guard: Wait for queries
108 if (
109 labelerDidDocQueries.some((q) => q.isLoading) ||
110 labelerServiceQueries.some((q) => q.isLoading)
111 ) {
112 return;
113 }
114
115 // Guard: If we are supposed to be Authed, but prefs haven't loaded yet,
116 // DO NOT run the logic. Wait. This prevents falling back to defaults temporarily.
117 if (!isUnauthed && !prefs) {
118 return;
119 }
120
121 // A. Extract User Global Overrides
122 // STRICT SEPARATION: If unauthed, force this to be empty to ensure no leakage.
123 const globalPrefs: Record<string, LabelPreference> = {};
124
125 if (!isUnauthed && prefs?.preferences) {
126 const contentLabelPrefs = prefs.preferences.filter(
127 (pref: any) => pref.$type === "app.bsky.actor.defs#contentLabelPref",
128 );
129 contentLabelPrefs.forEach((pref: any) => {
130 globalPrefs[pref.label] = pref.visibility as LabelPreference;
131 });
132 }
133
134 const definitions: LabelerDefinition[] = activeLabelerDids
135 .map((did: string, index: number) => {
136 const didDocQuery = labelerDidDocQueries[index];
137 const serviceQuery = labelerServiceQueries[index];
138
139 if (!didDocQuery.data || !serviceQuery.data) return null;
140
141 const didDoc = didDocQuery.data as any;
142 const atprotoLabelerService = didDoc?.service?.find(
143 (s: any) => s.id === "#atproto_labeler",
144 );
145
146 const record = (serviceQuery.data as any).value;
147
148 // B. Gather ALL identifiers
149 const allIdentifiers = new Set<string>();
150 record.policies?.labelValues?.forEach((val: string) => allIdentifiers.add(val));
151 record.policies?.labelValueDefinitions?.forEach((def: any) => allIdentifiers.add(def.identifier));
152
153 // C. Create Metadata Map
154 const labelDefs: Record<string, LabelValueDefinition> = {};
155 if (record.policies.labelValueDefinitions) {
156 record.policies.labelValueDefinitions.forEach((def: any) => {
157 labelDefs[def.identifier] = {
158 identifier: def.identifier,
159 severity: def.severity,
160 blurs: def.blurs,
161 adultOnly: def.adultOnly,
162 defaultSetting: def.defaultSetting,
163 locales: def.locales || []
164 };
165 });
166 }
167
168 // D. Resolve Preferences
169 const supportedLabels: Record<string, LabelPreference> = {};
170
171 allIdentifiers.forEach((val) => {
172 // todo this works but with how useModeration hooks works right now old verdicts wont get stale-d
173 // it only works right now because these are warns and warns are negligable i guess
174 // --- BRANCH 1: UNAUTHED MODE ---
175 if (isUnauthed) {
176 // 1. Strict Force Overrides
177 if (UNAUTHED_FORCE_WARN_LABELS.has(val)) {
178 supportedLabels[val] = "warn"; // or 'hide' if that's what your policy constant implies
179 return;
180 }
181
182 // 2. Default Labeler Settings
183 const def = labelDefs[val];
184 const rawDefault = def?.defaultSetting || "ignore";
185
186 // 3. Apply Unauthed-Specific Aliasing (Optional)
187 // e.g., if you want to hide 'inform' labels for unauthed users
188 supportedLabels[val] = rawDefault as LabelPreference;
189 return;
190 }
191
192 // --- BRANCH 2: AUTHED MODE ---
193 // 1. User Global Override (Highest Priority)
194 const globalPref = globalPrefs[val];
195 if (globalPref) {
196 supportedLabels[val] = globalPref;
197 return;
198 }
199
200 // 2. Labeler Default
201 const def = labelDefs[val];
202 const rawDefault = def?.defaultSetting || "ignore";
203
204 supportedLabels[val] = rawDefault as LabelPreference;
205 });
206
207 return {
208 did: did,
209 url: atprotoLabelerService?.serviceEndpoint || record.serviceEndpoint,
210 isDefault: FORCED_LABELER_DIDS.includes(did),
211 supportedLabels,
212 labelDefs,
213 };
214 })
215 .filter(Boolean) as LabelerDefinition[];
216
217 setLabelerConfig(definitions);
218 }, [
219 prefs,
220 labelerDidDocQueries,
221 labelerServiceQueries,
222 setLabelerConfig,
223 activeLabelerDids,
224 isUnauthed // <--- Critical dependency triggers re-eval on login/out
225 ]);
226
227 return null;
228};