diff --git a/core/constellation.py b/core/constellation.py index 351668d..6e3efe6 100644 --- a/core/constellation.py +++ b/core/constellation.py @@ -30,7 +30,9 @@ async def get_backlinks( return BacklinksResponse( total=data["total"], records=[ - BacklinkRef(did=entry["did"], collection=entry["collection"], rkey=entry["rkey"]) + BacklinkRef( + did=entry["did"], collection=entry["collection"], rkey=entry["rkey"] + ) for entry in data["records"] ], cursor=data.get("cursor"), diff --git a/core/records.py b/core/records.py index 52572e1..b6d2cad 100644 --- a/core/records.py +++ b/core/records.py @@ -11,7 +11,11 @@ from core import lexicon from core.constellation import get_board_activity, get_replies, get_root_posts from core.filters import filter_moderated from core.models import AtUri, AuthError, BBS, Board, MiniDoc, Post, Record -from core.slingshot import get_records_batch, get_records_by_uri, resolve_identities_batch +from core.slingshot import ( + get_records_batch, + get_records_by_uri, + resolve_identities_batch, +) from core.util import now_iso @@ -65,7 +69,9 @@ async def hydrate_threads( records = await get_records_batch(client, backlinks.records) if banned_dids or hidden_posts: - records = filter_moderated(records, banned_dids or set(), hidden_posts or set()) + records = filter_moderated( + records, banned_dids or set(), hidden_posts or set() + ) for record in records: thread_uri = record.value.get("root") or record.uri @@ -81,7 +87,9 @@ async def hydrate_threads( root_records = await get_records_by_uri(client, thread_uris) root_records = [record for record in root_records if not record.value.get("root")] if banned_dids or hidden_posts: - root_records = filter_moderated(root_records, banned_dids or set(), hidden_posts or set()) + root_records = filter_moderated( + root_records, banned_dids or set(), hidden_posts or set() + ) # Phase 3: Resolve authors and build Post objects uri_to_did = {record.uri: AtUri.parse(record.uri).did for record in root_records} @@ -96,7 +104,9 @@ async def hydrate_threads( # Set last_activity_at and sort by it (bump order) for thread in threads: thread.last_activity_at = last_activity.get(thread.uri, thread.created_at) - threads.sort(key=lambda thread: thread.last_activity_at or thread.created_at, reverse=True) + threads.sort( + key=lambda thread: thread.last_activity_at or thread.created_at, reverse=True + ) return threads, scan_cursor @@ -553,7 +563,9 @@ async def fetch_inbox( backlinks = await get_replies(client, post_uri, limit=BACKLINK_LIMIT) records = await get_records_batch(client, backlinks.records) parsed = {record.uri: AtUri.parse(record.uri) for record in records} - records = [record for record in records if parsed[record.uri].did != did] + records = [ + record for record in records if parsed[record.uri].did != did + ] if not records: return [] @@ -598,7 +610,9 @@ async def fetch_inbox( records = await get_records_batch(client, backlinks.records) parsed = {record.uri: AtUri.parse(record.uri) for record in records} - records = [record for record in records if parsed[record.uri].did != did] + records = [ + record for record in records if parsed[record.uri].did != did + ] if not records: return [] diff --git a/core/resolver.py b/core/resolver.py index 0943324..43c0fc9 100644 --- a/core/resolver.py +++ b/core/resolver.py @@ -58,12 +58,12 @@ async def _resolve_bbs(client: httpx.AsyncClient, handle: str) -> BBS: board_tasks = [] for uri in board_uris: parsed = AtUri.parse(uri) - board_tasks.append(get_record(client, parsed.did, parsed.collection, parsed.rkey)) + board_tasks.append( + get_record(client, parsed.did, parsed.collection, parsed.rkey) + ) news_task = get_root_posts(client, site_uri) - results = await asyncio.gather( - *board_tasks, news_task, return_exceptions=True - ) + results = await asyncio.gather(*board_tasks, news_task, return_exceptions=True) board_records = results[: len(board_uris)] news_result = results[len(board_uris)] @@ -88,10 +88,7 @@ async def _resolve_bbs(client: httpx.AsyncClient, handle: str) -> BBS: else: sysop_news = [ref for ref in news_result.records if ref.did == identity.did] news_records = await get_records_batch(client, sysop_news) - news = [ - post_from_record(record, identity) - for record in news_records - ] + news = [post_from_record(record, identity) for record in news_records] news.sort(key=lambda post: post.created_at, reverse=True) site = Site( diff --git a/core/slingshot.py b/core/slingshot.py index 73f370a..589aef9 100644 --- a/core/slingshot.py +++ b/core/slingshot.py @@ -71,7 +71,10 @@ async def get_records_by_uri( ) -> list[Record]: """Fetch multiple records by AT-URI, skipping failures.""" parsed = [AtUri.parse(uri) for uri in uris] - tasks = [get_record(client, at_uri.did, at_uri.collection, at_uri.rkey) for at_uri in parsed] + tasks = [ + get_record(client, at_uri.did, at_uri.collection, at_uri.rkey) + for at_uri in parsed + ] results = await asyncio.gather(*tasks, return_exceptions=True) return [result for result in results if isinstance(result, Record)] diff --git a/telnet/server.py b/telnet/server.py index a627c5e..86cf2f3 100644 --- a/telnet/server.py +++ b/telnet/server.py @@ -126,7 +126,8 @@ async def show_board(writer, board, threads, has_next): for index, thread in enumerate(threads, 1): date = format_datetime_utc(thread.last_activity_at or thread.created_at) await write( - writer, f" {index}. {thread.title} · {thread.author.handle} · {date}\r\n" + writer, + f" {index}. {thread.title} · {thread.author.handle} · {date}\r\n", ) cmds = ["[#] open thread"] @@ -151,7 +152,8 @@ async def show_thread_header(writer, thread): async def show_replies(writer, replies): for reply in replies: await write( - writer, f" {reply.author.handle} · {format_datetime_utc(reply.created_at)}\r\n" + writer, + f" {reply.author.handle} · {format_datetime_utc(reply.created_at)}\r\n", ) for line in reply.body.splitlines(): await write(writer, f" {line}\r\n") diff --git a/tui/screens/compose/upload.py b/tui/screens/compose/upload.py index 24a120e..1121512 100644 --- a/tui/screens/compose/upload.py +++ b/tui/screens/compose/upload.py @@ -36,7 +36,11 @@ async def upload_file(screen, file_path: str, session: dict) -> list[dict] | Non try: blob_ref = await upload_blob( - screen.app.http_client, session, cleaned_bytes, mime_type, session_updater=nonce_updater + screen.app.http_client, + session, + cleaned_bytes, + mime_type, + session_updater=nonce_updater, ) return [{"file": blob_ref, "name": path.name}] except Exception as error: diff --git a/tui/screens/home.py b/tui/screens/home.py index 023c2fe..4e8d9c6 100644 --- a/tui/screens/home.py +++ b/tui/screens/home.py @@ -80,9 +80,7 @@ class HomeScreen(Screen): """Check the user doesn't already have a BBS, then open the create screen.""" session = self.app.user_session try: - await get_record( - self.app.http_client, session["did"], lexicon.SITE, "self" - ) + await get_record(self.app.http_client, session["did"], lexicon.SITE, "self") # If we got here the record exists — they already have a BBS. self.notify( "You already have a BBS. Dial your handle to manage it.", diff --git a/tui/screens/sysop/bbs_form.py b/tui/screens/sysop/bbs_form.py index 8cf5476..ce50a42 100644 --- a/tui/screens/sysop/bbs_form.py +++ b/tui/screens/sysop/bbs_form.py @@ -53,9 +53,7 @@ class BBSFormMixin: ) for board in self._boards: slug = board["slug"] - yield Static( - f" {slug}", classes="subtitle", id=f"board-label-{slug}" - ) + yield Static(f" {slug}", classes="subtitle", id=f"board-label-{slug}") yield Input( value=board["name"], id=f"board-name-{slug}", diff --git a/tui/screens/thread.py b/tui/screens/thread.py index 1d99dd7..bc77824 100644 --- a/tui/screens/thread.py +++ b/tui/screens/thread.py @@ -11,7 +11,11 @@ from textual.widgets import Footer, Static from core import lexicon from core.models import BBS, AtUri, AuthError, Post as PostModel -from core.records import delete_record, hydrate_replies as fetch_replies, post_from_record +from core.records import ( + delete_record, + hydrate_replies as fetch_replies, + post_from_record, +) from core.slingshot import get_record, resolve_identity from tui.screens.compose import ComposeReplyScreen from tui.util import ban_user, hide_post, require_session, require_sysop @@ -171,9 +175,7 @@ class ThreadScreen(Screen): ) # Focus first reply - replies = [ - post for post in self.query(Post) if self._is_reply_widget(post) - ] + replies = [post for post in self.query(Post) if self._is_reply_widget(post)] if replies: replies[0].focus() diff --git a/web/src/components/ErrorPage.tsx b/web/src/components/ErrorPage.tsx index bf98561..16d4763 100644 --- a/web/src/components/ErrorPage.tsx +++ b/web/src/components/ErrorPage.tsx @@ -9,7 +9,10 @@ export default function ErrorPage() { let title = "Something went wrong."; let detail: string | null = null; - let action: { to: string; label: string } = { to: "/", label: "← back to home" }; + let action: { to: string; label: string } = { + to: "/", + label: "← back to home", + }; if (error instanceof BBSNotFoundError) { title = "BBS not found."; @@ -19,7 +22,8 @@ export default function ErrorPage() { if (user) { detail = "This account isn't running a BBS yet."; } else { - detail = "This account isn't running a BBS yet. Is this you? Log in to start one."; + detail = + "This account isn't running a BBS yet. Is this you? Log in to start one."; action = { to: "/login", label: "log in" }; } } else if (error instanceof NetworkError) { diff --git a/web/src/hooks/useThreadReplies.ts b/web/src/hooks/useThreadReplies.ts index 3205c6b..7fb1534 100644 --- a/web/src/hooks/useThreadReplies.ts +++ b/web/src/hooks/useThreadReplies.ts @@ -178,7 +178,9 @@ export function useThreadReplies(loaded: ThreadLoaderData) { parseAtUri(uri), ); const parentRecords = await getRecordsBatch(parentRefs); - const parentDids = parentRecords.map((record) => parseAtUri(record.uri).did); + const parentDids = parentRecords.map( + (record) => parseAtUri(record.uri).did, + ); const parentAuthors = await resolveIdentitiesBatch(parentDids); for (const record of parentRecords) { const reply = recordToReply(record, parentAuthors); diff --git a/web/src/lib/atproto.ts b/web/src/lib/atproto.ts index e718a6c..444d90d 100644 --- a/web/src/lib/atproto.ts +++ b/web/src/lib/atproto.ts @@ -82,9 +82,7 @@ export async function getRecordByUri(uri: string): Promise { return getRecord(did, collection, rkey); } -export async function getRecordsByUri( - uris: string[], -): Promise { +export async function getRecordsByUri(uris: string[]): Promise { const results = await Promise.allSettled( uris.map((uri) => getRecordByUri(uri)), ); diff --git a/web/src/lib/bbs.ts b/web/src/lib/bbs.ts index fd440aa..8d64227 100644 --- a/web/src/lib/bbs.ts +++ b/web/src/lib/bbs.ts @@ -15,11 +15,7 @@ import { is } from "@atcute/lexicons/validations"; import { mainSchema as siteSchema } from "../lexicons/types/xyz/atbbs/site"; import { mainSchema as boardSchema } from "../lexicons/types/xyz/atbbs/board"; import { mainSchema as postSchema } from "../lexicons/types/xyz/atbbs/post"; -import type { - XyzAtbbsSite, - XyzAtbbsBoard, - XyzAtbbsPost, -} from "../lexicons"; +import type { XyzAtbbsSite, XyzAtbbsBoard, XyzAtbbsPost } from "../lexicons"; export class BBSNotFoundError extends Error {} export class NoBBSError extends Error {} diff --git a/web/src/lib/profile.ts b/web/src/lib/profile.ts index e9c6e1e..056422b 100644 --- a/web/src/lib/profile.ts +++ b/web/src/lib/profile.ts @@ -42,8 +42,7 @@ export async function fetchProfile(handle: string): Promise { profileResult.status === "fulfilled" && is(profileSchema, profileResult.value.value) ) { - const value = profileResult.value - .value as unknown as XyzAtbbsProfile.Main; + const value = profileResult.value.value as unknown as XyzAtbbsProfile.Main; profile.name = value.name; profile.pronouns = value.pronouns; profile.bio = value.bio; diff --git a/web/src/pages/BBS.tsx b/web/src/pages/BBS.tsx index 75ee741..dfb088b 100644 --- a/web/src/pages/BBS.tsx +++ b/web/src/pages/BBS.tsx @@ -67,7 +67,9 @@ export default function BBSPage() { setNewsBody(""); setNewsFiles([]); } catch (error: unknown) { - alert(`Could not post: ${error instanceof Error ? error.message : error}`); + alert( + `Could not post: ${error instanceof Error ? error.message : error}`, + ); } finally { setPostingNews(false); } diff --git a/web/src/pages/SysopCreate.tsx b/web/src/pages/SysopCreate.tsx index f56e262..7fcf737 100644 --- a/web/src/pages/SysopCreate.tsx +++ b/web/src/pages/SysopCreate.tsx @@ -60,7 +60,9 @@ export default function SysopCreate() { name: name.trim(), description: description.trim(), intro, - boards: cleanBoards.map((board) => makeAtUri(user.did, BOARD, board.slug)), + boards: cleanBoards.map((board) => + makeAtUri(user.did, BOARD, board.slug), + ), createdAt: now, }); navigate(`/bbs/${user.handle}`); diff --git a/web/src/pages/SysopEdit.tsx b/web/src/pages/SysopEdit.tsx index a2f41ce..d8c69ee 100644 --- a/web/src/pages/SysopEdit.tsx +++ b/web/src/pages/SysopEdit.tsx @@ -62,7 +62,9 @@ export default function SysopEdit() { name: name.trim(), description: description.trim(), intro, - boards: cleanBoards.map((board) => makeAtUri(user.did, BOARD, board.slug)), + boards: cleanBoards.map((board) => + makeAtUri(user.did, BOARD, board.slug), + ), createdAt: bbs.site.createdAt || now, updatedAt: now, }); diff --git a/web/src/pages/Thread.tsx b/web/src/pages/Thread.tsx index ab41287..aec4f83 100644 --- a/web/src/pages/Thread.tsx +++ b/web/src/pages/Thread.tsx @@ -172,16 +172,12 @@ function ThreadPage({ loaded }: { loaded: LoaderData }) { reply={reply} userDid={user?.did ?? ""} sysopDid={bbs.identity.did} - parentPost={ - reply.parent ? replyCache[reply.parent] : undefined - } + parentPost={reply.parent ? replyCache[reply.parent] : undefined} onReplyTo={() => setReplyingTo({ uri: reply.uri, handle: reply.handle }) } onParentClick={ - reply.parent - ? () => scrollToReply(reply.parent!) - : undefined + reply.parent ? () => scrollToReply(reply.parent!) : undefined } onDelete={() => onDeleteReply(reply)} onBan={() => onBan(reply.did)} diff --git a/web/src/router/loaders/board.ts b/web/src/router/loaders/board.ts index 1ce143e..a92b532 100644 --- a/web/src/router/loaders/board.ts +++ b/web/src/router/loaders/board.ts @@ -49,7 +49,12 @@ export async function hydrateThreadPage( for (let scanCount = 0; scanCount < MAX_SCANS; scanCount++) { if (lastActivity.size >= PAGE_SIZE) break; - const backlinks = await getBacklinks(boardUri, `${POST}:scope`, 100, scanCursor); + const backlinks = await getBacklinks( + boardUri, + `${POST}:scope`, + 100, + scanCursor, + ); if (!backlinks.records.length) break; const records = await getRecordsBatch(backlinks.records);