import { openDB, type IDBPDatabase } from "idb" import { apiClient } from "./client" export interface PendingMutation { id: string timestamp: number type: | "send_message" | "update_flags" | "update_labels" | "delete_message" | "create_draft" | "update_draft" | "schedule_send" | "create_contact" | "update_contact" | "delete_draft" | "delete_contact" payload: unknown retries: number } const DB_NAME = "ultimail-offline-queue" const STORE_NAME = "mutations" let dbPromise: Promise | null = null function getDb() { if (!dbPromise) { dbPromise = openDB(DB_NAME, 1, { upgrade(db) { db.createObjectStore(STORE_NAME, { keyPath: "id" }) }, }) } return dbPromise } export async function enqueue(mutation: PendingMutation): Promise { const db = await getDb() await db.put(STORE_NAME, mutation) } export async function getAll(): Promise { const db = await getDb() return db.getAll(STORE_NAME) } export async function remove(id: string): Promise { const db = await getDb() await db.delete(STORE_NAME, id) } export async function getPendingCount(): Promise { const db = await getDb() return db.count(STORE_NAME) } const MUTATION_ENDPOINTS: Record string }> = { send_message: { method: "post", path: () => "/outbox" }, update_flags: { method: "put", path: (p) => `/messages/${p.message_id}/flags` }, update_labels: { method: "put", path: (p) => `/messages/${p.message_id}/labels` }, delete_message: { method: "delete", path: (p) => `/messages/${p.message_id}` }, create_draft: { method: "post", path: () => "/drafts" }, update_draft: { method: "put", path: (p) => `/drafts/${p.draft_id}` }, schedule_send: { method: "post", path: () => "/outbox/schedule" }, delete_draft: { method: "delete", path: (p) => `/drafts/${p.draft_id}` }, create_contact: { method: "post", path: () => "/contacts" }, update_contact: { method: "put", path: (p) => `/contacts/${p.uid}` }, delete_contact: { method: "delete", path: (p) => `/contacts/${p.uid}` }, } export async function flush(): Promise { const mutations = await getAll() const sorted = mutations.sort((a, b) => a.timestamp - b.timestamp) for (const mutation of sorted) { try { const endpoint = MUTATION_ENDPOINTS[mutation.type] const path = endpoint.path(mutation.payload) switch (endpoint.method) { case "post": await apiClient.post(path, mutation.payload) break case "put": await apiClient.put(path, mutation.payload) break case "delete": await apiClient.delete(path) break } await remove(mutation.id) } catch { const db = await getDb() await db.put(STORE_NAME, { ...mutation, retries: mutation.retries + 1 }) } } } if (typeof window !== "undefined") { window.addEventListener("online", () => { flush() }) }