import * as ATPAPI from "@atproto/api";
import {
isAdultContentPref,
isBskyAppStatePref,
isContentLabelPref,
isFeedViewPref,
isLabelersPref,
isMutedWordsPref,
isSavedFeedsPref,
} from "@atproto/api/dist/client/types/app/bsky/actor/defs";
import { createFileRoute } from "@tanstack/react-router";
import { useAtom } from "jotai";
import { Switch } from "radix-ui";
import { Header } from "~/components/Header";
import { useAuth } from "~/providers/UnifiedAuthProvider";
import { quickAuthAtom } from "~/utils/atoms";
import { useQueryIdentity, useQueryPreferences } from "~/utils/useQuery";
import { renderSnack } from "./__root";
import { NotificationItem } from "./notifications";
import { SettingHeading } from "./settings";
export const Route = createFileRoute("/moderation")({
component: RouteComponent,
});
function RouteComponent() {
const { agent } = useAuth();
const [quickAuth, setQuickAuth] = useAtom(quickAuthAtom);
const isAuthRestoring = quickAuth ? status === "loading" : false;
const identityresultmaybe = useQueryIdentity(
!isAuthRestoring ? agent?.did : undefined
);
const identity = identityresultmaybe?.data;
const prefsresultmaybe = useQueryPreferences({
agent: !isAuthRestoring ? (agent ?? undefined) : undefined,
pdsUrl: !isAuthRestoring ? identity?.pds : undefined,
});
const rawprefs = prefsresultmaybe?.data?.preferences as
| ATPAPI.AppBskyActorGetPreferences.OutputSchema["preferences"]
| undefined;
//console.log(JSON.stringify(prefs, null, 2))
const parsedPref = parsePreferences(rawprefs);
return (
);
}
export function TripleToggle({
value,
onChange,
}: {
value: "ignore" | "warn" | "hide";
onChange?: (newValue: "ignore" | "warn" | "hide") => void;
}) {
const options: Array<"ignore" | "warn" | "hide"> = ["ignore", "warn", "hide"];
return (
{options.map((opt) => {
const isActive = opt === value;
return (
);
})}
);
}
type PrefItem =
ATPAPI.AppBskyActorGetPreferences.OutputSchema["preferences"][number];
export interface NormalizedPreferences {
contentLabelPrefs: Record;
mutedWords: string[];
feedViewPrefs: Record;
labelers: string[];
adultContentEnabled: boolean;
savedFeeds: {
pinned: string[];
saved: string[];
};
nuxs: string[];
}
export function parsePreferences(
prefs?: PrefItem[]
): NormalizedPreferences | undefined {
if (!prefs) return undefined;
const normalized: NormalizedPreferences = {
contentLabelPrefs: {},
mutedWords: [],
feedViewPrefs: {},
labelers: [],
adultContentEnabled: false,
savedFeeds: { pinned: [], saved: [] },
nuxs: [],
};
for (const pref of prefs) {
switch (pref.$type) {
case "app.bsky.actor.defs#contentLabelPref":
if (!isContentLabelPref(pref)) break;
normalized.contentLabelPrefs[pref.label] = pref.visibility;
break;
case "app.bsky.actor.defs#mutedWordsPref":
if (!isMutedWordsPref(pref)) break;
for (const item of pref.items ?? []) {
normalized.mutedWords.push(item.value);
}
break;
case "app.bsky.actor.defs#feedViewPref":
if (!isFeedViewPref(pref)) break;
normalized.feedViewPrefs[pref.feed] = pref;
break;
case "app.bsky.actor.defs#labelersPref":
if (!isLabelersPref(pref)) break;
normalized.labelers.push(...(pref.labelers?.map((l) => l.did) ?? []));
break;
case "app.bsky.actor.defs#adultContentPref":
if (!isAdultContentPref(pref)) break;
normalized.adultContentEnabled = !!pref.enabled;
break;
case "app.bsky.actor.defs#savedFeedsPref":
if (!isSavedFeedsPref(pref)) break;
normalized.savedFeeds.pinned.push(...(pref.pinned ?? []));
normalized.savedFeeds.saved.push(...(pref.saved ?? []));
break;
case "app.bsky.actor.defs#bskyAppStatePref":
if (!isBskyAppStatePref(pref)) break;
normalized.nuxs.push(...(pref.nuxs?.map((n) => n.id) ?? []));
break;
default:
// unknown pref type — just ignore for now
break;
}
}
return normalized;
}