fix: extract tags client-side for offline notes and server-side on sync

- Import extractTags in notes store to populate tags on optimistic notes
- Re-extract tags on offline update when title or content changes
- Run extractTags + syncNoteTags in processSyncPush after transaction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-03-06 23:12:36 +01:00
co-authored by Claude Opus 4.6
parent 99602d6a24
commit 6d3ab0ba04
2 changed files with 26 additions and 3 deletions
+10 -3
View File
@@ -2,6 +2,7 @@ import { writable, derived } from 'svelte/store';
import type { Note, NoteFilter, NoteCreate, NoteUpdate } from '$lib/types/index.js';
import { addToSyncQueue, putNote, deleteNoteFromIdb, getAllNotes } from '$lib/sync/idb.js';
import { showToast } from '$lib/stores/toast.js';
import { extractTags } from '$lib/utils/tags.js';
export const notes = writable<Note[]>([]);
export const currentFilter = writable<NoteFilter>('all');
@@ -78,10 +79,12 @@ export async function createNote(note: NoteCreate): Promise<Note | null> {
} catch (err) {
if (isNetworkError(err)) {
const now = new Date();
const title = note.title ?? '';
const content = note.content ?? '';
const optimistic: Note = {
id: crypto.randomUUID(),
title: note.title ?? '',
content: note.content ?? '',
title,
content,
color: note.color ?? 'default',
pinned: note.pinned ?? false,
archived: false,
@@ -91,7 +94,8 @@ export async function createNote(note: NoteCreate): Promise<Note | null> {
sortOrder: 0,
createdAt: now,
updatedAt: now,
version: 1
version: 1,
tags: extractTags(`${title} ${content}`)
};
notes.update((list) => [optimistic, ...list]);
await putNote(optimistic);
@@ -124,6 +128,9 @@ export async function updateNote(id: string, updates: NoteUpdate): Promise<Note
list.map((n) => {
if (n.id === id) {
optimistic = { ...n, ...updates, updatedAt: new Date() };
if (updates.title !== undefined || updates.content !== undefined) {
optimistic.tags = extractTags(`${optimistic.title} ${optimistic.content}`);
}
return optimistic;
}
return n;
+16
View File
@@ -1,12 +1,16 @@
import { db } from '$lib/server/db/index.js';
import { notes, syncLog } from '$lib/server/db/schema.js';
import { eq, gt } from 'drizzle-orm';
import { extractTags } from '$lib/utils/tags.js';
import { syncNoteTags } from '$lib/server/tags.js';
import type { SyncQueueItem } from './idb.js';
/**
* Process incoming sync changes from client.
*/
export async function processSyncPush(changes: SyncQueueItem[]): Promise<void> {
const noteIdsToSyncTags: string[] = [];
db.transaction((tx) => {
for (const change of changes) {
switch (change.operation) {
@@ -48,6 +52,10 @@ export async function processSyncPush(changes: SyncQueueItem[]): Promise<void> {
})
.run();
}
if (change.data.title !== undefined || change.data.content !== undefined) {
noteIdsToSyncTags.push(change.noteId);
}
break;
}
case 'delete': {
@@ -66,6 +74,14 @@ export async function processSyncPush(changes: SyncQueueItem[]): Promise<void> {
.run();
}
});
for (const noteId of noteIdsToSyncTags) {
const note = db.select().from(notes).where(eq(notes.id, noteId)).get();
if (note) {
const content = `${note.title} ${note.content}`;
syncNoteTags(noteId, extractTags(content));
}
}
}
/**