an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
at main 201 lines 4.8 kB view raw
1import * as ATPAPI from "@atproto/api"; 2 3import { 4 type HydratedLabelValueDefinition, 5 type labelpref, 6 useAutoLabelConfig, 7} from "~/providers/AutoLabelProvider"; 8import { useQueryLabels } from "~/utils/useQuery"; 9 10export function normalizeLabels( 11 labels: ATPAPI.ComAtprotoLabelDefs.Label[], 12): ATPAPI.ComAtprotoLabelDefs.Label[] { 13 const map = new Map<string, ATPAPI.ComAtprotoLabelDefs.Label>(); 14 15 for (const label of labels) { 16 const key = `${label.src}::${label.uri}::${label.val}`; 17 18 if (label.neg) { 19 map.delete(key); 20 } else { 21 map.set(key, label); 22 } 23 } 24 25 return [...map.values()]; 26} 27 28function computeVerdict( 29 prefs: labelpref[], 30 labels: ATPAPI.ComAtprotoLabelDefs.Label[], 31): ATPAPI.AppBskyActorDefs.ContentLabelPref["visibility"] { 32 for (const pref of prefs) { 33 // check if any label matches this pref 34 const hit = labels.some((l) => l.src === pref.did && l.val === pref.label); 35 36 if (!hit) continue; 37 38 if (pref.visibility === "hide") { 39 return "hide"; 40 } 41 42 if (pref.visibility === "warn") { 43 return "warn"; 44 } 45 } 46 47 return "ignore"; 48} 49 50export function useAutoLabels({ 51 subjects, 52 type, 53}: { 54 subjects: string[]; 55 type: "post" | "profile" | "account"; 56}): { 57 results: Map< 58 string, 59 { 60 labelVerdict: ATPAPI.AppBskyActorDefs.ContentLabelPref["visibility"]; 61 labels: ATPAPI.ComAtprotoLabelDefs.Label[]; 62 } 63 >; 64 hydratedLabelDefs: Map< 65 string, 66 HydratedLabelValueDefinition 67 >; 68} { 69 const { 70 activeLabelerDids, 71 mergedPrefContentLabels, 72 hydratedLabelDefs, 73 isLoading: configLoading, 74 isError: configError, 75 sendError, 76 } = useAutoLabelConfig(); 77 78 const results = new Map< 79 string, 80 { 81 labelVerdict: ATPAPI.AppBskyActorDefs.ContentLabelPref["visibility"]; 82 labels: ATPAPI.ComAtprotoLabelDefs.Label[]; 83 } 84 >(); 85 86 // const res = useQueryLabelMerge({ 87 // s: subjects, 88 // l: activeLabelerDids, 89 // strict: false, 90 // }); 91 const res = useQueryLabels(subjects,activeLabelerDids); 92 if (configLoading || res.isLoading) { 93 for (const subject of subjects) { 94 const subjectLabels = [] as ATPAPI.ComAtprotoLabelDefs.Label[]; 95 results.set(subject, { 96 labelVerdict: "loading", 97 labels: subjectLabels, 98 }); 99 } 100 return { 101 results, 102 hydratedLabelDefs, 103 }; 104 } 105 if (configError || res.isError || res.data?.error) { 106 if (res.isError) { 107 sendError({ 108 did: "!all", 109 message: "queryFailure" 110 }) 111 } else 112 if (res.data?.error) { 113 console.log("fuck shit !internal-IDK-unknown, ", res.data) 114 res.data?.error?.forEach((err)=>{ 115 sendError({ 116 did: err.s, 117 message: err.e || "!internal-uAL-unknown, len: " + res.data.error?.length + ". label-length" + res.data.labels.length 118 }) 119 }) 120 } 121 console.error("Error fetching auto-label configuration or subject labels",{ 122 configError: configError, 123 isLoading: res.isLoading, 124 isError: res.isError, 125 data: res.data, 126 //error: res.error, 127 }); 128 129 for (const subject of subjects) { 130 const subjectLabels = [] as ATPAPI.ComAtprotoLabelDefs.Label[]; 131 results.set(subject, { 132 labelVerdict: "error", 133 labels: subjectLabels, 134 }); 135 } 136 return { 137 results, 138 hydratedLabelDefs, 139 }; 140 } 141 142 if (res.data && (res.data.labels.length === 0) ) { 143 // early return because wow you did it theres no labels to be computed! wonderful! wow! 144 // group labels by subject (uri) 145 146 for (const subject of subjects) { 147 const subjectLabels = [] as ATPAPI.ComAtprotoLabelDefs.Label[]; 148 results.set(subject, { 149 labelVerdict: "ignore", 150 labels: subjectLabels, 151 }); 152 } 153 return { 154 results, 155 hydratedLabelDefs, 156 }; 157 } 158 159 const effectiveLabels = normalizeLabels(res.data?.labels ?? []); 160 161 // group labels by subject (uri) 162 const labelsBySubject = new Map< 163 string, 164 ATPAPI.ComAtprotoLabelDefs.Label[] 165 >(); 166 167 for (const label of effectiveLabels) { 168 const arr = labelsBySubject.get(label.uri) ?? []; 169 arr.push(label); 170 labelsBySubject.set(label.uri, arr); 171 } 172 173 for (const subject of subjects) { 174 const subjectLabels = labelsBySubject.get(subject) ?? []; 175 const verdict = computeVerdict( 176 mergedPrefContentLabels, 177 subjectLabels, 178 ); 179 results.set(subject, { 180 labelVerdict: verdict, 181 labels: subjectLabels, 182 }); 183 } 184 185 return { 186 results, 187 hydratedLabelDefs, 188 }; 189} 190 191export function getGetHydratedLabelDefs( 192 hydratedLabelDefs: Map< 193 string, 194 HydratedLabelValueDefinition 195 >, 196) { 197 function getHydratedLabelDefs(did: string, label: string) { 198 return hydratedLabelDefs.get(did + "::" + label); 199 } 200 return getHydratedLabelDefs; 201}