From 6a76f695fe96c002738b71ff7208702011e480c1 Mon Sep 17 00:00:00 2001 From: Timothy Cardoza Date: Wed, 4 Mar 2026 18:47:18 -0600 Subject: [PATCH] Created share tag migration function --- src/store/index.ts | 124 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/store/index.ts b/src/store/index.ts index d16d137..f9d908d 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -873,6 +873,127 @@ export const subscribeToRealtime = async () => { }); }; +const runLegacyTagMigration = async () => { + const currentUserId = pb.authStore.model?.id; + if (!currentUserId) return; + + try { + const userRec = await pb.collection('users').getOne(currentUserId, { requestKey: null }); + const prefs = userRec.Taskgrid_pref || {}; + + if (prefs.migratedLegacyTagsV1) { + return; // Already migrated + } + + console.log("[MIGRATION] Starting legacy tag migration..."); + + // Fetch users and buckets + const [allUsers, allBuckets] = await Promise.all([ + pb.collection('users').getFullList({ + filter: 'verified = true', + fields: 'name,email', + requestKey: null + }), + pb.collection(BUCKETS_COLLECTION).getFullList({ + fields: 'name', + requestKey: null + }) + ]); + + const validNames = new Set([ + ...allBuckets.map(b => b.name.toLowerCase()), + ...allUsers.map(u => (u.name || u.email)?.toLowerCase()).filter(Boolean) + ]); + + // Fetch all Tasks for this user + const tasks = await pb.collection(TASGRID_COLLECTION).getFullList({ + filter: `user = "${currentUserId}"`, + requestKey: null + }); + + let tasksUpdated = 0; + for (const t of tasks) { + if (!t.tags || t.tags.length === 0) continue; + let changed = false; + const newTags = t.tags.map((tag: string) => { + if (!tag.startsWith("@") && validNames.has(tag.toLowerCase())) { + changed = true; + return `@${tag}`; + } + return tag; + }); + + if (changed) { + await pb.collection(TASGRID_COLLECTION).update(t.id, { tags: newTags }, { requestKey: null }); + tasksUpdated++; + } + } + + // Fetch all Notes for this user + const notes = await pb.collection(NOTES_COLLECTION).getFullList({ + filter: `user = "${currentUserId}"`, + requestKey: null + }); + + let notesUpdated = 0; + for (const n of notes) { + if (!n.tags || n.tags.length === 0) continue; + let changed = false; + const newTags = n.tags.map((tag: string) => { + if (!tag.startsWith("@") && validNames.has(tag.toLowerCase())) { + changed = true; + return `@${tag}`; + } + return tag; + }); + + if (changed) { + await pb.collection(NOTES_COLLECTION).update(n.id, { tags: newTags }, { requestKey: null }); + notesUpdated++; + } + } + + prefs.migratedLegacyTagsV1 = true; + await pb.collection('users').update(currentUserId, { Taskgrid_pref: prefs }, { requestKey: null }); + + if (pb.authStore.model) { + pb.authStore.model.Taskgrid_pref = prefs; + } + + console.log(`[MIGRATION] Completed. Updated ${tasksUpdated} tasks and ${notesUpdated} notes.`); + + // Update local state directly so it reflects immediately + setStore("tasks", (tasks) => tasks.map(t => { + let localChanged = false; + const nt = t.tags?.map(tag => { + if (!tag.startsWith("@") && validNames.has(tag.toLowerCase())) { + localChanged = true; + return `@${tag}`; + } + return tag; + }); + if (localChanged) return { ...t, tags: nt }; + return t; + })); + + setStore("notes", (items) => items.map(n => { + let localChanged = false; + const nt = n.tags?.map(tag => { + if (!tag.startsWith("@") && validNames.has(tag.toLowerCase())) { + localChanged = true; + return `@${tag}`; + } + return tag; + }); + if (localChanged) return { ...n, tags: nt }; + return n; + })); + + } catch (err) { + console.error("[MIGRATION] Failed:", err); + } +}; + export const initStore = async () => { if (!pb.authStore.isValid || store.isInitializing) return; setStore("isInitializing", true); @@ -1205,6 +1326,9 @@ export const initStore = async () => { // Fire off background sync await backgroundSync(); + // Run one-time migration for legacy tags + await runLegacyTagMigration(); + // Separate Templates load (less critical) try { const templates = await pb.collection(TASGRID_COLLECTION).getFullList({