diff options
Diffstat (limited to 'src/client/db/repositories.ts')
| -rw-r--r-- | src/client/db/repositories.ts | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/src/client/db/repositories.ts b/src/client/db/repositories.ts index a2f0b41..104f026 100644 --- a/src/client/db/repositories.ts +++ b/src/client/db/repositories.ts @@ -2,8 +2,13 @@ import { v4 as uuidv4 } from "uuid"; import { CardState, db, + FieldType, type LocalCard, type LocalDeck, + type LocalNote, + type LocalNoteFieldType, + type LocalNoteFieldValue, + type LocalNoteType, type LocalReviewLog, } from "./index"; @@ -380,3 +385,437 @@ export const localReviewLogRepository = { .toArray(); }, }; + +/** + * Local note type repository for IndexedDB operations + */ +export const localNoteTypeRepository = { + /** + * Get all note types for a user (excluding soft-deleted) + */ + async findByUserId(userId: string): Promise<LocalNoteType[]> { + return db.noteTypes + .where("userId") + .equals(userId) + .filter((noteType) => noteType.deletedAt === null) + .toArray(); + }, + + /** + * Get a note type by ID + */ + async findById(id: string): Promise<LocalNoteType | undefined> { + return db.noteTypes.get(id); + }, + + /** + * Create a new note type + */ + async create( + data: Omit< + LocalNoteType, + "id" | "createdAt" | "updatedAt" | "deletedAt" | "syncVersion" | "_synced" + >, + ): Promise<LocalNoteType> { + const now = new Date(); + const noteType: LocalNoteType = { + id: uuidv4(), + ...data, + createdAt: now, + updatedAt: now, + deletedAt: null, + syncVersion: 0, + _synced: false, + }; + await db.noteTypes.add(noteType); + return noteType; + }, + + /** + * Update a note type + */ + async update( + id: string, + data: Partial< + Pick< + LocalNoteType, + "name" | "frontTemplate" | "backTemplate" | "isReversible" + > + >, + ): Promise<LocalNoteType | undefined> { + const noteType = await db.noteTypes.get(id); + if (!noteType) return undefined; + + const updatedNoteType: LocalNoteType = { + ...noteType, + ...data, + updatedAt: new Date(), + _synced: false, + }; + await db.noteTypes.put(updatedNoteType); + return updatedNoteType; + }, + + /** + * Soft delete a note type + */ + async delete(id: string): Promise<boolean> { + const noteType = await db.noteTypes.get(id); + if (!noteType) return false; + + await db.noteTypes.update(id, { + deletedAt: new Date(), + updatedAt: new Date(), + _synced: false, + }); + return true; + }, + + /** + * Get all unsynced note types + */ + async findUnsynced(): Promise<LocalNoteType[]> { + return db.noteTypes.filter((noteType) => !noteType._synced).toArray(); + }, + + /** + * Mark a note type as synced + */ + async markSynced(id: string, syncVersion: number): Promise<void> { + await db.noteTypes.update(id, { _synced: true, syncVersion }); + }, + + /** + * Upsert a note type from server (for sync pull) + */ + async upsertFromServer(noteType: LocalNoteType): Promise<void> { + await db.noteTypes.put({ ...noteType, _synced: true }); + }, +}; + +/** + * Local note field type repository for IndexedDB operations + */ +export const localNoteFieldTypeRepository = { + /** + * Get all field types for a note type (excluding soft-deleted) + */ + async findByNoteTypeId(noteTypeId: string): Promise<LocalNoteFieldType[]> { + const fields = await db.noteFieldTypes + .where("noteTypeId") + .equals(noteTypeId) + .filter((field) => field.deletedAt === null) + .toArray(); + // Sort by order + return fields.sort((a, b) => a.order - b.order); + }, + + /** + * Get a field type by ID + */ + async findById(id: string): Promise<LocalNoteFieldType | undefined> { + return db.noteFieldTypes.get(id); + }, + + /** + * Create a new field type + */ + async create( + data: Omit< + LocalNoteFieldType, + | "id" + | "fieldType" + | "createdAt" + | "updatedAt" + | "deletedAt" + | "syncVersion" + | "_synced" + >, + ): Promise<LocalNoteFieldType> { + const now = new Date(); + const fieldType: LocalNoteFieldType = { + id: uuidv4(), + ...data, + fieldType: FieldType.Text, + createdAt: now, + updatedAt: now, + deletedAt: null, + syncVersion: 0, + _synced: false, + }; + await db.noteFieldTypes.add(fieldType); + return fieldType; + }, + + /** + * Update a field type + */ + async update( + id: string, + data: Partial<Pick<LocalNoteFieldType, "name" | "order">>, + ): Promise<LocalNoteFieldType | undefined> { + const fieldType = await db.noteFieldTypes.get(id); + if (!fieldType) return undefined; + + const updatedFieldType: LocalNoteFieldType = { + ...fieldType, + ...data, + updatedAt: new Date(), + _synced: false, + }; + await db.noteFieldTypes.put(updatedFieldType); + return updatedFieldType; + }, + + /** + * Soft delete a field type + */ + async delete(id: string): Promise<boolean> { + const fieldType = await db.noteFieldTypes.get(id); + if (!fieldType) return false; + + await db.noteFieldTypes.update(id, { + deletedAt: new Date(), + updatedAt: new Date(), + _synced: false, + }); + return true; + }, + + /** + * Get all unsynced field types + */ + async findUnsynced(): Promise<LocalNoteFieldType[]> { + return db.noteFieldTypes.filter((field) => !field._synced).toArray(); + }, + + /** + * Mark a field type as synced + */ + async markSynced(id: string, syncVersion: number): Promise<void> { + await db.noteFieldTypes.update(id, { _synced: true, syncVersion }); + }, + + /** + * Upsert a field type from server (for sync pull) + */ + async upsertFromServer(fieldType: LocalNoteFieldType): Promise<void> { + await db.noteFieldTypes.put({ ...fieldType, _synced: true }); + }, +}; + +/** + * Local note repository for IndexedDB operations + */ +export const localNoteRepository = { + /** + * Get all notes for a deck (excluding soft-deleted) + */ + async findByDeckId(deckId: string): Promise<LocalNote[]> { + return db.notes + .where("deckId") + .equals(deckId) + .filter((note) => note.deletedAt === null) + .toArray(); + }, + + /** + * Get all notes for a note type (excluding soft-deleted) + */ + async findByNoteTypeId(noteTypeId: string): Promise<LocalNote[]> { + return db.notes + .where("noteTypeId") + .equals(noteTypeId) + .filter((note) => note.deletedAt === null) + .toArray(); + }, + + /** + * Get a note by ID + */ + async findById(id: string): Promise<LocalNote | undefined> { + return db.notes.get(id); + }, + + /** + * Create a new note + */ + async create( + data: Omit< + LocalNote, + "id" | "createdAt" | "updatedAt" | "deletedAt" | "syncVersion" | "_synced" + >, + ): Promise<LocalNote> { + const now = new Date(); + const note: LocalNote = { + id: uuidv4(), + ...data, + createdAt: now, + updatedAt: now, + deletedAt: null, + syncVersion: 0, + _synced: false, + }; + await db.notes.add(note); + return note; + }, + + /** + * Update a note's metadata (triggers updatedAt change) + */ + async update(id: string): Promise<LocalNote | undefined> { + const note = await db.notes.get(id); + if (!note) return undefined; + + const updatedNote: LocalNote = { + ...note, + updatedAt: new Date(), + _synced: false, + }; + await db.notes.put(updatedNote); + return updatedNote; + }, + + /** + * Soft delete a note and its related cards + */ + async delete(id: string): Promise<boolean> { + const note = await db.notes.get(id); + if (!note) return false; + + const now = new Date(); + + // Cascade soft-delete to all cards associated with this note + await db.cards.where("noteId").equals(id).modify({ + deletedAt: now, + updatedAt: now, + _synced: false, + }); + + // Soft delete the note + await db.notes.update(id, { + deletedAt: now, + updatedAt: now, + _synced: false, + }); + + return true; + }, + + /** + * Get all unsynced notes + */ + async findUnsynced(): Promise<LocalNote[]> { + return db.notes.filter((note) => !note._synced).toArray(); + }, + + /** + * Mark a note as synced + */ + async markSynced(id: string, syncVersion: number): Promise<void> { + await db.notes.update(id, { _synced: true, syncVersion }); + }, + + /** + * Upsert a note from server (for sync pull) + */ + async upsertFromServer(note: LocalNote): Promise<void> { + await db.notes.put({ ...note, _synced: true }); + }, +}; + +/** + * Local note field value repository for IndexedDB operations + */ +export const localNoteFieldValueRepository = { + /** + * Get all field values for a note + */ + async findByNoteId(noteId: string): Promise<LocalNoteFieldValue[]> { + return db.noteFieldValues.where("noteId").equals(noteId).toArray(); + }, + + /** + * Get a field value by ID + */ + async findById(id: string): Promise<LocalNoteFieldValue | undefined> { + return db.noteFieldValues.get(id); + }, + + /** + * Get a field value by note ID and field type ID + */ + async findByNoteIdAndFieldTypeId( + noteId: string, + noteFieldTypeId: string, + ): Promise<LocalNoteFieldValue | undefined> { + return db.noteFieldValues + .where("noteId") + .equals(noteId) + .filter((value) => value.noteFieldTypeId === noteFieldTypeId) + .first(); + }, + + /** + * Create a new field value + */ + async create( + data: Omit< + LocalNoteFieldValue, + "id" | "createdAt" | "updatedAt" | "syncVersion" | "_synced" + >, + ): Promise<LocalNoteFieldValue> { + const now = new Date(); + const fieldValue: LocalNoteFieldValue = { + id: uuidv4(), + ...data, + createdAt: now, + updatedAt: now, + syncVersion: 0, + _synced: false, + }; + await db.noteFieldValues.add(fieldValue); + return fieldValue; + }, + + /** + * Update a field value + */ + async update( + id: string, + data: Partial<Pick<LocalNoteFieldValue, "value">>, + ): Promise<LocalNoteFieldValue | undefined> { + const fieldValue = await db.noteFieldValues.get(id); + if (!fieldValue) return undefined; + + const updatedFieldValue: LocalNoteFieldValue = { + ...fieldValue, + ...data, + updatedAt: new Date(), + _synced: false, + }; + await db.noteFieldValues.put(updatedFieldValue); + return updatedFieldValue; + }, + + /** + * Get all unsynced field values + */ + async findUnsynced(): Promise<LocalNoteFieldValue[]> { + return db.noteFieldValues.filter((value) => !value._synced).toArray(); + }, + + /** + * Mark a field value as synced + */ + async markSynced(id: string, syncVersion: number): Promise<void> { + await db.noteFieldValues.update(id, { _synced: true, syncVersion }); + }, + + /** + * Upsert a field value from server (for sync pull) + */ + async upsertFromServer(fieldValue: LocalNoteFieldValue): Promise<void> { + await db.noteFieldValues.put({ ...fieldValue, _synced: true }); + }, +}; |
