Retro Bulletin Board Systems on atproto. Web app and TUI. lazy mirror of alyraffauf/atbbs atbbs.xyz
forums python tui atproto bbs
3
fork

Configure Feed

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

web: add useModerationMutations hook

+75 -89
+53
web/src/hooks/useModerationMutations.ts
··· 1 + import { useMutation } from "@tanstack/react-query"; 2 + import { useAuth } from "../lib/auth"; 3 + import { resolveIdentity } from "../lib/atproto"; 4 + import { 5 + createBan, 6 + createHide, 7 + deleteBan, 8 + deleteHide, 9 + } from "../lib/writes"; 10 + import { alertOnError } from "../lib/alerts"; 11 + 12 + // Shared ban/unban/hide/unhide mutations 13 + // `ban` accepts either a DID or a handle 14 + export function useModerationMutations() { 15 + const { agent } = useAuth(); 16 + 17 + const ban = useMutation({ 18 + mutationFn: async (identifier: string) => { 19 + if (!agent) throw new Error("Not signed in"); 20 + const did = identifier.startsWith("did:") 21 + ? identifier 22 + : (await resolveIdentity(identifier)).did; 23 + await createBan(agent, did); 24 + }, 25 + onError: alertOnError("ban"), 26 + }); 27 + 28 + const unban = useMutation({ 29 + mutationFn: async (rkey: string) => { 30 + if (!agent) throw new Error("Not signed in"); 31 + await deleteBan(agent, rkey); 32 + }, 33 + onError: alertOnError("unban"), 34 + }); 35 + 36 + const hide = useMutation({ 37 + mutationFn: async (uri: string) => { 38 + if (!agent) throw new Error("Not signed in"); 39 + await createHide(agent, uri); 40 + }, 41 + onError: alertOnError("hide"), 42 + }); 43 + 44 + const unhide = useMutation({ 45 + mutationFn: async (rkey: string) => { 46 + if (!agent) throw new Error("Not signed in"); 47 + await deleteHide(agent, rkey); 48 + }, 49 + onError: alertOnError("unhide"), 50 + }); 51 + 52 + return { ban, unban, hide, unhide }; 53 + }
+16 -54
web/src/pages/SysopModerate.tsx
··· 1 1 import { useState } from "react"; 2 - import { useSuspenseQuery, useMutation } from "@tanstack/react-query"; 2 + import { useSuspenseQuery } from "@tanstack/react-query"; 3 3 import { useAuth } from "../lib/auth"; 4 - import { resolveIdentity } from "../lib/atproto"; 5 4 import { bbsQuery, sysopModerationQuery } from "../lib/queries"; 6 5 import HandleInput from "../components/form/HandleInput"; 7 6 import { Button } from "../components/form/Form"; 8 7 import { usePageTitle } from "../hooks/usePageTitle"; 9 - import { 10 - createBan, 11 - createHide, 12 - deleteBan, 13 - deleteHide, 14 - } from "../lib/writes"; 15 - import { alertOnError } from "../lib/alerts"; 8 + import { useModerationMutations } from "../hooks/useModerationMutations"; 16 9 17 10 export default function SysopModerate() { 18 - const { user, agent } = useAuth(); 11 + const { user } = useAuth(); 19 12 const [identifier, setIdentifier] = useState(""); 20 13 const [hideUri, setHideUri] = useState(""); 21 14 usePageTitle("Moderate community — atbbs"); ··· 27 20 ); 28 21 const { banRkeys, bannedHandles, hideRkeys, hidden } = moderation; 29 22 30 - const banMutation = useMutation({ 31 - mutationFn: async (identifier: string) => { 32 - if (!agent) throw new Error("Not signed in"); 33 - let did = identifier; 34 - if (!did.startsWith("did:")) did = (await resolveIdentity(did)).did; 35 - await createBan(agent, did); 36 - }, 37 - onSuccess: () => setIdentifier(""), 38 - onError: alertOnError("ban"), 39 - }); 40 - 41 - const unbanMutation = useMutation({ 42 - mutationFn: async (rkey: string) => { 43 - if (!agent) throw new Error("Not signed in"); 44 - await deleteBan(agent, rkey); 45 - }, 46 - }); 47 - 48 - const hideMutation = useMutation({ 49 - mutationFn: async (uri: string) => { 50 - if (!agent) throw new Error("Not signed in"); 51 - await createHide(agent, uri); 52 - }, 53 - onSuccess: () => setHideUri(""), 54 - }); 55 - 56 - const unhideMutation = useMutation({ 57 - mutationFn: async (rkey: string) => { 58 - if (!agent) throw new Error("Not signed in"); 59 - await deleteHide(agent, rkey); 60 - }, 61 - }); 23 + const { ban, unban, hide, unhide } = useModerationMutations(); 62 24 63 - function ban() { 25 + function onBan() { 64 26 const id = identifier.trim(); 65 27 if (!id) return; 66 - banMutation.mutate(id); 28 + ban.mutate(id, { onSuccess: () => setIdentifier("") }); 67 29 } 68 30 69 - function unban(rkey: string) { 31 + function onUnban(rkey: string) { 70 32 if (!confirm("Unban this user?")) return; 71 - unbanMutation.mutate(rkey); 33 + unban.mutate(rkey); 72 34 } 73 35 74 - function hide() { 36 + function onHide() { 75 37 const uri = hideUri.trim(); 76 38 if (!uri.startsWith("at://")) { 77 39 alert("Enter a valid AT-URI."); 78 40 return; 79 41 } 80 - hideMutation.mutate(uri); 42 + hide.mutate(uri, { onSuccess: () => setHideUri("") }); 81 43 } 82 44 83 - function unhide(rkey: string) { 45 + function onUnhide(rkey: string) { 84 46 if (!confirm("Unhide this post?")) return; 85 - unhideMutation.mutate(rkey); 47 + unhide.mutate(rkey); 86 48 } 87 49 88 50 return ( ··· 113 75 </a> 114 76 {banRkeys[did] && ( 115 77 <button 116 - onClick={() => unban(banRkeys[did])} 78 + onClick={() => onUnban(banRkeys[did])} 117 79 className="text-xs text-neutral-400 hover:text-red-400 shrink-0" 118 80 > 119 81 unban ··· 129 91 onChange={setIdentifier} 130 92 className="flex-1" 131 93 /> 132 - <Button onClick={ban}>ban</Button> 94 + <Button onClick={onBan}>ban</Button> 133 95 </div> 134 96 </div> 135 97 ··· 153 115 </a> 154 116 {hideRkeys[post.uri] && ( 155 117 <button 156 - onClick={() => unhide(hideRkeys[post.uri])} 118 + onClick={() => onUnhide(hideRkeys[post.uri])} 157 119 className="text-xs text-neutral-400 hover:text-red-400 shrink-0" 158 120 > 159 121 unhide ··· 172 134 aria-label="Post URI to hide" 173 135 className="flex-1 bg-neutral-900 border border-neutral-800 rounded px-3 py-2 text-neutral-200 placeholder-neutral-500 focus:outline-none focus:border-neutral-600" 174 136 /> 175 - <Button onClick={hide}>hide</Button> 137 + <Button onClick={onHide}>hide</Button> 176 138 </div> 177 139 </div> 178 140 </div>
+6 -35
web/src/pages/Thread.tsx
··· 9 9 import { makeAtUri, nowIso, parseAtUri } from "../lib/util"; 10 10 import * as limits from "../lib/limits"; 11 11 import { 12 - createBan, 13 - createHide, 14 12 createPost, 15 - deleteBan, 16 - deleteHide, 17 13 deleteRecord, 18 14 uploadAttachments, 19 15 } from "../lib/writes"; 16 + import { useModerationMutations } from "../hooks/useModerationMutations"; 20 17 import { 21 18 bbsModerationQuery, 22 19 bbsQuery, ··· 174 171 onError: alertOnError("delete"), 175 172 }); 176 173 177 - const banMutation = useMutation({ 178 - mutationFn: async (banDid: string) => { 179 - if (!agent) throw new Error("Not signed in"); 180 - await createBan(agent, banDid); 181 - }, 182 - }); 183 - 184 - const unbanMutation = useMutation({ 185 - mutationFn: async (rkey: string) => { 186 - if (!agent) throw new Error("Not signed in"); 187 - await deleteBan(agent, rkey); 188 - }, 189 - }); 190 - 191 - const hideMutation = useMutation({ 192 - mutationFn: async (uri: string) => { 193 - if (!agent) throw new Error("Not signed in"); 194 - await createHide(agent, uri); 195 - }, 196 - }); 197 - 198 - const unhideMutation = useMutation({ 199 - mutationFn: async (rkey: string) => { 200 - if (!agent) throw new Error("Not signed in"); 201 - await deleteHide(agent, rkey); 202 - }, 203 - }); 174 + const { ban, unban, hide, unhide } = useModerationMutations(); 204 175 205 176 // --- Handlers --- 206 177 ··· 226 197 227 198 function onBan(banDid: string) { 228 199 if (!confirm("Ban this user from your community?")) return; 229 - banMutation.mutate(banDid); 200 + ban.mutate(banDid); 230 201 } 231 202 232 203 function onUnban(rkey: string) { 233 204 if (!confirm("Unban this user?")) return; 234 - unbanMutation.mutate(rkey); 205 + unban.mutate(rkey); 235 206 } 236 207 237 208 function onHide(uri: string) { 238 209 if (!confirm("Hide this post?")) return; 239 - hideMutation.mutate(uri); 210 + hide.mutate(uri); 240 211 } 241 212 242 213 function onUnhide(rkey: string) { 243 214 if (!confirm("Unhide this post?")) return; 244 - unhideMutation.mutate(rkey); 215 + unhide.mutate(rkey); 245 216 } 246 217 247 218 if (threadHidden) {