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 144 lines 4.1 kB view raw
1import { 2 type ColumnWidth, 3 type DiagnosticsColumnConfig, 4 type ExplorerColumnConfig, 5 type FeedColumnConfig, 6 isFeedType, 7 type ProfileColumnConfig, 8 type SearchColumnConfig, 9} from "$/lib/api/types/columns"; 10import { getFeedName } from "$/lib/feeds"; 11import type { FeedGeneratorView, SavedFeedItem } from "$/lib/types"; 12import type { ResolvedFeedColumn } from "./types"; 13 14function feedConfigToSavedFeedItem(config: FeedColumnConfig): SavedFeedItem { 15 return { 16 id: config.feedUri || "following", 17 pinned: false, 18 type: config.feedType, 19 value: config.feedUri || "following", 20 }; 21} 22 23function parseExplorerConfig(config: string): ExplorerColumnConfig | null { 24 try { 25 const parsed = JSON.parse(config) as unknown; 26 if (parsed && typeof parsed === "object" && "targetUri" in parsed) { 27 return parsed as ExplorerColumnConfig; 28 } 29 return null; 30 } catch { 31 return null; 32 } 33} 34 35export function cycleWidth(current: ColumnWidth): ColumnWidth { 36 switch (current) { 37 case "narrow": { 38 return "standard"; 39 } 40 case "standard": { 41 return "wide"; 42 } 43 case "wide": { 44 return "narrow"; 45 } 46 } 47} 48 49export function parseFeedConfig(config: string): FeedColumnConfig | null { 50 try { 51 const parsed = JSON.parse(config) as Record<string, unknown>; 52 if (!parsed || typeof parsed !== "object") { 53 return null; 54 } 55 56 if (!isFeedType(parsed.feedType) || typeof parsed.feedUri !== "string") { 57 return null; 58 } 59 60 if (parsed.title !== undefined && parsed.title !== null && typeof parsed.title !== "string") { 61 return null; 62 } 63 64 return { feedType: parsed.feedType, feedUri: parsed.feedUri, title: parsed.title as string | null | undefined }; 65 } catch { 66 return null; 67 } 68} 69 70export function parseDiagnosticsConfig(config: string): DiagnosticsColumnConfig | null { 71 try { 72 const parsed = JSON.parse(config) as unknown; 73 if (parsed && typeof parsed === "object" && "did" in parsed) { 74 return parsed as DiagnosticsColumnConfig; 75 } 76 return null; 77 } catch { 78 return null; 79 } 80} 81 82export function parseSearchConfig(config: string): SearchColumnConfig | null { 83 try { 84 const parsed = JSON.parse(config) as unknown; 85 if (parsed && typeof parsed === "object" && "mode" in parsed && "query" in parsed) { 86 return parsed as SearchColumnConfig; 87 } 88 return null; 89 } catch { 90 return null; 91 } 92} 93 94export function parseProfileConfig(config: string): ProfileColumnConfig | null { 95 try { 96 const parsed = JSON.parse(config) as unknown; 97 if (parsed && typeof parsed === "object" && "actor" in parsed) { 98 return parsed as ProfileColumnConfig; 99 } 100 return null; 101 } catch { 102 return null; 103 } 104} 105 106export function parseColumnTitle(kind: string, config: string): string { 107 switch (kind) { 108 case "feed": { 109 return "Feed"; 110 } 111 case "explorer": { 112 const parsed = parseExplorerConfig(config); 113 if (!parsed?.targetUri) return "Explorer"; 114 return parsed.targetUri.length > 30 ? `${parsed.targetUri.slice(0, 30)}` : parsed.targetUri; 115 } 116 case "diagnostics": { 117 const parsed = parseDiagnosticsConfig(config); 118 return parsed?.did ?? "Diagnostics"; 119 } 120 case "messages": { 121 return "Messages"; 122 } 123 case "search": { 124 const parsed = parseSearchConfig(config); 125 const query = parsed?.query.trim(); 126 return query ? `Search: ${query}` : "Search"; 127 } 128 case "profile": { 129 const parsed = parseProfileConfig(config); 130 return parsed?.displayName?.trim() || parsed?.handle?.trim() || parsed?.actor || "Profile"; 131 } 132 default: { 133 return "Column"; 134 } 135 } 136} 137 138type Opts = { generator?: FeedGeneratorView; savedFeedTitle?: string | null }; 139 140export function resolveFeedColumn(config: FeedColumnConfig, options: Opts = {}): ResolvedFeedColumn { 141 const feed = feedConfigToSavedFeedItem(config); 142 const hydratedTitle = options.generator?.displayName || config.title?.trim() || options.savedFeedTitle?.trim(); 143 return { config, feed, generator: options.generator, title: getFeedName(feed, hydratedTitle) }; 144}