learn and share notes on atproto (wip) 馃
malfestio.stormlightlabs.org/
readability
solid
axum
atproto
srs
1export type User = { did: string; handle: string };
2
3export type Visibility = { type: "Private" } | { type: "Unlisted" } | { type: "Public" } | {
4 type: "SharedWith";
5 content: string[];
6};
7
8export type CardType = "basic" | "cloze";
9
10export type Card = {
11 id?: string;
12 front: string;
13 back: string;
14 mediaUrl?: string;
15 cardType?: CardType;
16 hints?: string[];
17};
18
19export type Deck = {
20 id: string;
21 owner_did: string;
22 title: string;
23 description: string;
24 tags: string[];
25 visibility: Visibility;
26 published_at?: string;
27 fork_of?: string;
28};
29
30export type Note = {
31 id: string;
32 owner_did: string;
33 title: string;
34 body: string;
35 tags: string[];
36 visibility: Visibility;
37 published_at?: string;
38 created_at?: string;
39 updated_at?: string;
40 links?: string[];
41};
42
43export type CreateDeckPayload = {
44 title: string;
45 description: string;
46 tags: string[];
47 visibility: Visibility;
48 cards: Card[];
49};
50
51export type Grade = 0 | 1 | 2 | 3 | 4 | 5;
52
53export type ReviewCard = {
54 review_id: string;
55 card_id: string;
56 deck_id: string;
57 deck_title: string;
58 front: string;
59 back: string;
60 media_url?: string;
61 hints: string[];
62 due_at: string;
63};
64
65export type StudyStats = {
66 due_count: number;
67 current_streak: number;
68 longest_streak: number;
69 reviewed_today: number;
70 total_reviews: number;
71};
72
73export type ReviewResponse = { ease_factor: number; interval_days: number; repetitions: number; due_at: string };
74
75export type Comment = {
76 id: string;
77 deck_id: string;
78 author_did: string;
79 content: string;
80 parent_id: string | null;
81 created_at: string;
82};
83
84export type CommentNode = { comment: Comment; children: CommentNode[] };
85
86export type FeedFollows = { decks: Deck[] };
87
88export type SearchResult = {
89 item_type: "deck";
90 item_id: string;
91 creator_did: string;
92 data: Deck;
93 rank: number;
94 source?: "local" | "remote";
95} | {
96 item_type: "card";
97 item_id: string;
98 creator_did: string;
99 data: Card & { deck_id: string };
100 rank: number;
101 source?: "local" | "remote";
102} | {
103 item_type: "note";
104 item_id: string;
105 creator_did: string;
106 data: { id: string; title: string; owner_did: string };
107 rank: number;
108 source?: "local" | "remote";
109};
110
111export const asDeck = (r: SearchResult) => (r.item_type === "deck" ? r : undefined);
112export const asCard = (r: SearchResult) => (r.item_type === "card" ? r : undefined);
113export const asNote = (r: SearchResult) => (r.item_type === "note" ? r : undefined);
114
115export type Persona = "learner" | "creator" | "curator";
116
117export type UserPreferences = {
118 user_did: string;
119 persona: Persona | null;
120 onboarding_completed_at: string | null;
121 tutorial_deck_completed: boolean;
122 density_mode: "compact" | "comfortable" | "spacious" | null;
123};
124
125export type UserProfile = {
126 did: string;
127 follower_count: number;
128 following_count: number;
129 deck_count: number;
130 indexed_deck_count: number;
131};
132
133export type UpdatePreferencesPayload = {
134 persona?: Persona;
135 complete_onboarding?: boolean;
136 tutorial_deck_completed?: boolean;
137};
138
139export type CreateNotePayload = { title: string; body: string; tags: string[]; visibility: { type: string } };