BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 147 lines 3.5 kB view raw
1import type { ModerationLabel, ModerationUiDecision } from "$/lib/types"; 2 3/** 4 * Official Bluesky labeler DID (@moderation.bsky.app) 5 */ 6export const BUILTIN_LABELER_DID = "did:plc:ar7c4by46qjdydhdevvrndac"; 7 8export const DEFAULT_MODERATION_DECISION: ModerationUiDecision = { 9 alert: false, 10 blur: "none", 11 filter: false, 12 inform: false, 13 noOverride: false, 14}; 15 16export type ModerationLabelSummary = { key: string; source: string; value: string }; 17 18export function asModerationLabels(value: unknown): ModerationLabel[] { 19 const record = asRecord(value); 20 const labels = record?.["labels"]; 21 if (!Array.isArray(labels)) { 22 return []; 23 } 24 25 return labels.filter((label): label is ModerationLabel => isRecordLike(label)); 26} 27 28export function collectModerationLabels(...values: unknown[]): ModerationLabel[] { 29 const labels = values.flatMap((value) => asModerationLabels(value)); 30 if (labels.length <= 1) { 31 return labels; 32 } 33 34 const deduped: ModerationLabel[] = []; 35 const seen = new Set<string>(); 36 37 for (const label of labels) { 38 const source = typeof label.src === "string" ? label.src : ""; 39 const value = typeof label.val === "string" ? label.val : ""; 40 const uri = typeof label.uri === "string" ? label.uri : ""; 41 const key = `${source}|${value}|${uri}`; 42 if (seen.has(key)) { 43 continue; 44 } 45 46 seen.add(key); 47 deduped.push(label); 48 } 49 50 return deduped; 51} 52 53export function moderationLabelsKey(labels: ModerationLabel[]): string { 54 if (labels.length === 0) { 55 return ""; 56 } 57 58 const tokens = labels.map((label) => { 59 const source = typeof label.src === "string" ? label.src.trim() : ""; 60 const value = typeof label.val === "string" ? label.val.trim() : ""; 61 const uri = typeof label.uri === "string" ? label.uri.trim() : ""; 62 return `${source}|${value}|${uri}`; 63 }); 64 65 return tokens.toSorted().join(";"); 66} 67 68export function summarizeModerationLabels(labels: ModerationLabel[], limit = 3): ModerationLabelSummary[] { 69 if (labels.length === 0) { 70 return []; 71 } 72 73 const summaries: ModerationLabelSummary[] = []; 74 const seen = new Set<string>(); 75 76 for (const label of labels) { 77 const value = toLabelDisplayValue(label.val); 78 if (!value) { 79 continue; 80 } 81 82 const source = toSourceDisplayValue(label.src); 83 const key = `${source}|${value}`; 84 if (seen.has(key)) { 85 continue; 86 } 87 88 seen.add(key); 89 summaries.push({ key, source, value }); 90 91 if (summaries.length >= limit) { 92 break; 93 } 94 } 95 96 return summaries; 97} 98 99function toLabelDisplayValue(value: unknown): string | null { 100 if (typeof value !== "string") { 101 return null; 102 } 103 104 const normalized = value.trim(); 105 if (!normalized) { 106 return null; 107 } 108 109 if (normalized.startsWith("!")) { 110 return `Not ${normalized.slice(1)}`; 111 } 112 113 return normalized; 114} 115 116function toSourceDisplayValue(source: unknown): string { 117 if (typeof source !== "string") { 118 return "Unknown"; 119 } 120 121 const normalized = source.trim(); 122 if (!normalized) { 123 return "Unknown"; 124 } 125 126 if (!normalized.startsWith("did:")) { 127 return normalized; 128 } 129 130 if (normalized.length <= 22) { 131 return normalized; 132 } 133 134 return `${normalized.slice(0, 16)}...${normalized.slice(-6)}`; 135} 136 137function asRecord(value: unknown): Record<string, unknown> | null { 138 if (!isRecordLike(value)) { 139 return null; 140 } 141 142 return value; 143} 144 145function isRecordLike(value: unknown): value is Record<string, unknown> { 146 return !!value && typeof value === "object" && !Array.isArray(value); 147}