(feat): add draft caching
All checks were successful
/ build-web (push) Successful in 8m1s
/ build-desktop (linux) (push) Successful in 12m44s
/ build-mobile (push) Successful in 19m57s
/ release (push) Successful in 3m26s

This commit is contained in:
Alois 2026-08-02 01:25:39 +02:00
commit ab577283f9
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
5 changed files with 194 additions and 23 deletions

View file

@ -13,9 +13,11 @@ import {
} from "./helpers";
import {
accountIdSchema,
chatDraftSchema,
contactsSchema,
conversationWindowSchema,
userProfileSchema,
type ChatDraft,
type Contact,
type ConversationWindow,
type UserProfile,
@ -24,7 +26,7 @@ import {
export * from "./helpers";
export * from "./schemas";
type CacheStore = "contacts" | "profiles" | "conversations";
type CacheStore = "contacts" | "profiles" | "conversations" | "drafts";
export interface SecureValueCodec {
encode(value: unknown): unknown | Promise<unknown>;
@ -237,19 +239,25 @@ export function createCache(accountId: string, options: CacheOptions = {}) {
},
delete: (userId: number) => remove("conversations", String(userId)),
},
drafts: {
get: (userId: number) => read("drafts", String(userId), chatDraftSchema),
put: (userId: number, draft: ChatDraft) =>
write("drafts", String(userId), chatDraftSchema, draft),
delete: (userId: number) => remove("drafts", String(userId)),
},
clearAccount: async () => {
ensureOpen();
await Promise.all(
(["contacts", "profiles", "conversations"] as CacheStore[]).map(
async (store) => {
const storedEntries = await entries(store);
await Promise.all(
storedEntries.map(([key]) =>
deleteDatabaseEntry("cache", storedKey(store, key)),
),
);
},
),
(
["contacts", "profiles", "conversations", "drafts"] as CacheStore[]
).map(async (store) => {
const storedEntries = await entries(store);
await Promise.all(
storedEntries.map(([key]) =>
deleteDatabaseEntry("cache", storedKey(store, key)),
),
);
}),
);
},
close: async () => {

View file

@ -20,8 +20,14 @@ export const conversationWindowSchema = z.object({
LastMessageAt: z.number(),
Messages: z.array(cachedMessageSchema),
});
// Unlike cached messages, draft content is plaintext and must use a secure codec.
export const chatDraftSchema = z.object({
Content: z.string(),
ReplyId: z.number().optional(),
});
export type Contact = z.infer<typeof contactSchema>;
export type UserProfile = z.infer<typeof userProfileSchema>;
export type CachedMessage = z.infer<typeof cachedMessageSchema>;
export type ConversationWindow = z.infer<typeof conversationWindowSchema>;
export type ChatDraft = z.infer<typeof chatDraftSchema>;