(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1import { atom } from "nanostores";
2import { getPreferences, updatePreferences } from "../api/client";
3import type {
4 LabelerSubscription,
5 LabelPreference,
6 LabelVisibility,
7} from "../types";
8
9export interface Preferences {
10 externalLinkSkippedHostnames: string[];
11 subscribedLabelers: LabelerSubscription[];
12 labelPreferences: LabelPreference[];
13 disableExternalLinkWarning: boolean;
14 enableCommunityBookmarks: boolean;
15}
16
17export const $preferences = atom<Preferences>({
18 externalLinkSkippedHostnames: [],
19 subscribedLabelers: [],
20 labelPreferences: [],
21 disableExternalLinkWarning: false,
22 enableCommunityBookmarks: true,
23});
24
25export async function loadPreferences() {
26 const prefs = await getPreferences();
27 $preferences.set({
28 externalLinkSkippedHostnames: prefs.externalLinkSkippedHostnames || [],
29 subscribedLabelers: prefs.subscribedLabelers || [],
30 labelPreferences: prefs.labelPreferences || [],
31 disableExternalLinkWarning: !!prefs.disableExternalLinkWarning,
32 enableCommunityBookmarks: !!prefs.enableCommunityBookmarks,
33 });
34}
35
36export async function addSkippedHostname(hostname: string) {
37 const current = $preferences.get();
38 if (current.externalLinkSkippedHostnames.includes(hostname)) return;
39
40 const updated = {
41 ...current,
42 externalLinkSkippedHostnames: [
43 ...current.externalLinkSkippedHostnames,
44 hostname,
45 ],
46 };
47 $preferences.set(updated);
48 await updatePreferences(updated);
49}
50
51export async function addLabeler(did: string) {
52 const current = $preferences.get();
53 if (current.subscribedLabelers.some((l) => l.did === did)) return;
54
55 const updated = {
56 ...current,
57 subscribedLabelers: [...current.subscribedLabelers, { did }],
58 };
59 $preferences.set(updated);
60 await updatePreferences(updated);
61}
62
63export async function removeLabeler(did: string) {
64 const current = $preferences.get();
65 const updated = {
66 ...current,
67 subscribedLabelers: current.subscribedLabelers.filter((l) => l.did !== did),
68 };
69 $preferences.set(updated);
70 await updatePreferences(updated);
71}
72
73export async function setLabelVisibility(
74 labelerDid: string,
75 label: string,
76 visibility: LabelVisibility,
77) {
78 const current = $preferences.get();
79 const filtered = current.labelPreferences.filter(
80 (p) => !(p.labelerDid === labelerDid && p.label === label),
81 );
82 const newPrefs =
83 visibility === "warn"
84 ? filtered
85 : [...filtered, { labelerDid, label, visibility }];
86 const updated = { ...current, labelPreferences: newPrefs };
87 $preferences.set(updated);
88 await updatePreferences(updated);
89}
90
91export function getLabelVisibility(
92 labelerDid: string,
93 label: string,
94): LabelVisibility {
95 const prefs = $preferences.get();
96 const pref = prefs.labelPreferences.find(
97 (p) => p.labelerDid === labelerDid && p.label === label,
98 );
99 return pref?.visibility || "warn";
100}
101
102export async function setDisableExternalLinkWarning(disabled: boolean) {
103 const current = $preferences.get();
104 if (current.disableExternalLinkWarning === disabled) return;
105
106 const updated = {
107 ...current,
108 disableExternalLinkWarning: disabled,
109 };
110 $preferences.set(updated);
111 await updatePreferences(updated);
112}
113
114export async function setEnableCommunityBookmarks(enabled: boolean) {
115 const current = $preferences.get();
116 if (current.enableCommunityBookmarks === enabled) return;
117
118 const updated = {
119 ...current,
120 enableCommunityBookmarks: enabled,
121 };
122 $preferences.set(updated);
123 await updatePreferences(updated);
124}