Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql

docs(moderation): add client-side filtering pattern

+46
+46
docs/guides/moderation.md
··· 301 301 - `!warn` and `!hide` always apply their effects 302 302 303 303 Attempting to set a preference for a system label returns an error. 304 + 305 + ### Client-Side Filtering 306 + 307 + The server filters takedown labels automatically, but clients must apply user preferences for other labels. Here's the pattern: 308 + 309 + ```typescript 310 + // 1. Fetch preferences once and cache 311 + const prefs = await client.query(`{ 312 + viewerLabelPreferences { val visibility } 313 + }`) 314 + const prefMap = new Map(prefs.map(p => [p.val, p.visibility])) 315 + 316 + // 2. Check visibility for each record 317 + function getVisibility(record) { 318 + for (const label of record.labels ?? []) { 319 + const vis = prefMap.get(label.val) ?? 'WARN' 320 + if (vis === 'HIDE') return { show: false } 321 + if (vis === 'WARN') return { show: true, blur: true } 322 + } 323 + return { show: true, blur: false } 324 + } 325 + 326 + // 3. Apply in your UI 327 + function RecordCard({ record }) { 328 + const { show, blur } = getVisibility(record) 329 + 330 + if (!show) return null 331 + 332 + if (blur) { 333 + return ( 334 + <BlurOverlay onReveal={() => setRevealed(true)}> 335 + <Content record={record} /> 336 + </BlurOverlay> 337 + ) 338 + } 339 + 340 + return <Content record={record} /> 341 + } 342 + ``` 343 + 344 + Key points: 345 + 346 + - Cache preferences at session start or when user updates them 347 + - Default unknown labels to `WARN` for safety 348 + - Multiple labels on one record: apply the most restrictive 349 + - `IGNORE` and `SHOW` both display normally; `SHOW` is for explicit opt-in to adult content