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.

refactor: api controller pattern

+27 -13
+3 -3
src/components/feeds/DraftsList.tsx
··· 1 1 import { Icon } from "$/components/shared/Icon"; 2 - import { deleteDraft, listDrafts } from "$/lib/api/drafts"; 2 + import { DraftController } from "$/lib/api/drafts"; 3 3 import { formatRelativeTime } from "$/lib/feeds"; 4 4 import type { Draft } from "$/lib/types"; 5 5 import { normalizeError } from "$/lib/utils/text"; ··· 38 38 setLoading(true); 39 39 setError(null); 40 40 try { 41 - const result = await listDrafts(props.accountDid); 41 + const result = await DraftController.listDrafts(props.accountDid); 42 42 setDrafts(result); 43 43 } catch (err) { 44 44 logger.error(`Failed to load drafts: ${normalizeError(err)}`); ··· 71 71 } 72 72 73 73 try { 74 - await deleteDraft(id); 74 + await DraftController.deleteDraft(id); 75 75 setDrafts((prev) => prev.filter((d) => d.id !== id)); 76 76 } catch (err) { 77 77 logger.error(`Failed to delete draft ${id}: ${normalizeError(err)}`);
+13 -8
src/components/feeds/useFeedWorkspaceController.ts
··· 1 1 import { usePostInteractions } from "$/components/posts/usePostInteractions"; 2 - import { deleteDraft, getDraft, listDrafts, saveDraft } from "$/lib/api/drafts"; 2 + import { DraftController } from "$/lib/api/drafts"; 3 3 import { 4 4 createPost, 5 5 getFeedGenerators, ··· 173 173 const stored = workspace.preferences?.savedFeeds ?? []; 174 174 return stored.length > 0 ? stored : [DEFAULT_TIMELINE]; 175 175 }); 176 + 176 177 const pinnedFeeds = createMemo(() => { 177 178 const pinned = savedFeeds().filter((feed) => feed.pinned); 178 179 return pinned.length > 0 ? pinned : [DEFAULT_TIMELINE]; 179 180 }); 181 + 180 182 const drawerFeeds = createMemo(() => savedFeeds().filter((feed) => !feed.pinned)); 181 183 const activeFeed = createMemo(() => { 182 184 const feedId = workspace.activeFeedId; 183 185 return savedFeeds().find((feed) => feed.id === feedId) ?? pinnedFeeds()[0] ?? DEFAULT_TIMELINE; 184 186 }); 187 + 185 188 const activePref = createMemo(() => { 186 189 const feed = activeFeed(); 187 190 return workspace.localPrefs[feed.value] ?? createDefaultFeedPref(feed); 188 191 }); 192 + 189 193 const activeFeedState = createMemo(() => workspace.feedStates[activeFeed().id]); 190 194 const visibleItems = createMemo(() => applyFeedPreferences(activeFeedState()?.items ?? [], activePref())); 191 195 const composerHasContent = createMemo(() => { ··· 198 202 const match = /(^|\s)([@#][^\s@#]*)$/u.exec(workspace.composer.text); 199 203 return match?.[2] ?? null; 200 204 }); 205 + 201 206 const composerSuggestions = createMemo(() => { 202 207 const token = composerToken(); 203 208 if (!token) { ··· 419 424 }; 420 425 421 426 try { 422 - const result = await saveDraft(input); 427 + const result = await DraftController.saveDraft(input); 423 428 setWorkspace("composer", "draftId", result.id); 424 429 setWorkspace("composer", "quoteRef", quoteRef); 425 430 setWorkspace("composer", "replyParentRef", replyParentRef); ··· 458 463 459 464 async function refreshDraftCount() { 460 465 try { 461 - const drafts = await listDrafts(props.activeSession.did); 466 + const drafts = await DraftController.listDrafts(props.activeSession.did); 462 467 setWorkspace("draftCount", drafts.length); 463 468 } catch (error) { 464 469 logger.error(`Failed to refresh draft count: ${normalizeError(error)}`); ··· 503 508 } 504 509 505 510 try { 506 - await getDraft(savedId); 511 + await DraftController.getDraft(savedId); 507 512 setWorkspace("restoreDraftId", savedId); 508 513 } catch (error) { 509 514 logger.error(`Autosave draft ${savedId} not found, clearing: ${normalizeError(error)}`); ··· 750 755 751 756 if (draftId) { 752 757 try { 753 - await deleteDraft(draftId); 758 + await DraftController.deleteDraft(draftId); 754 759 await refreshDraftCount(); 755 760 bumpDraftsListRefresh(); 756 761 } catch (error) { ··· 855 860 856 861 if (draftId) { 857 862 try { 858 - await deleteDraft(draftId); 863 + await DraftController.deleteDraft(draftId); 859 864 await refreshDraftCount(); 860 865 bumpDraftsListRefresh(); 861 866 } catch (error) { ··· 1026 1031 } 1027 1032 1028 1033 try { 1029 - const draft = await getDraft(id); 1034 + const draft = await DraftController.getDraft(id); 1030 1035 loadDraft(draft); 1031 1036 } catch (error) { 1032 1037 logger.error(`Failed to restore draft ${id}: ${normalizeError(error)}`); ··· 1043 1048 1044 1049 if (id) { 1045 1050 try { 1046 - await deleteDraft(id); 1051 + await DraftController.deleteDraft(id); 1047 1052 await refreshDraftCount(); 1048 1053 bumpDraftsListRefresh(); 1049 1054 } catch (error) {
+1 -1
src/lib/api/conversations.test.ts src/lib/api/tests/conversations.test.ts
··· 4 4 parseGetMessagesResponse, 5 5 parseListConvosResponse, 6 6 parseSendMessageResponse, 7 - } from "./conversations"; 7 + } from "../conversations"; 8 8 9 9 function createMember(overrides: Record<string, unknown> = {}) { 10 10 return { did: "did:plc:bob", displayName: "Bob", handle: "bob.test", ...overrides };
+9
src/lib/api/drafts.ts
··· 16 16 export function deleteDraft(id: string): Promise<void> { 17 17 return invoke("delete_draft", { id }); 18 18 } 19 + 20 + /** 21 + * Controller for managing drafts, providing methods to list, get, save, and delete drafts. 22 + * 23 + * Note: this is a new-ish pattern I'm trying out. 24 + * @author Owais 25 + * @date 2026/04/07 26 + */ 27 + export const DraftController = { listDrafts, getDraft, saveDraft, deleteDraft };
+1 -1
src/lib/api/notifications.test.ts src/lib/api/tests/notifications.test.ts
··· 1 1 import { describe, expect, it } from "vitest"; 2 - import { parseListNotificationsResponse } from "./notifications"; 2 + import { parseListNotificationsResponse } from "../notifications"; 3 3 4 4 function createNotification() { 5 5 return {