From 1618b3bf92f5623be5865de485419b92ec3404f2 Mon Sep 17 00:00:00 2001 From: Timothy Cardoza Date: Thu, 19 Feb 2026 16:39:56 -0600 Subject: [PATCH] share rules update stable --- src/store/index.ts | 86 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/src/store/index.ts b/src/store/index.ts index 2f41360..fd4ab77 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -221,12 +221,27 @@ export const matchesFilter = (task: Task) => { const isExplicitlyShared = task.sharedWith?.some(s => s.userId === currentUserId); // Tag-based share rules targeting me - const isTagRuleShared = store.shareRules.some(r => - r.targetUserId === currentUserId && - r.ownerId === task.ownerId && - r.type === 'tag' && - task.tags?.includes(r.tagName || "") - ); + const isTagRuleShared = store.shareRules.some(r => { + if (r.targetUserId !== currentUserId) return false; + + // Global Rule / Self-Rule: Match purely on tag, ignore owner check + // logic: If I have a rule for tag "T" assigned to myself, show ANY task with tag "T" + if (r.ownerId === r.targetUserId && r.type === 'tag') { + const tagName = r.tagName || ""; + + // EXCLUDE Buckets from the main "Mine" view + // The user wants to see "Direct Shares" (User Tags), not "Bucket Subscriptions" here. + const isBucket = store.buckets.some(b => b.name.toLowerCase() === tagName.toLowerCase()); + if (isBucket) return false; + + return task.tags?.some(t => t.toLowerCase() === tagName.toLowerCase()); + } + + // Standard Rule: Match owner AND tag + return r.ownerId === task.ownerId && + r.type === 'tag' && + task.tags?.includes(r.tagName || ""); + }); // Bucket visibility logic for "My Bucket": // Users requested: "Any user who adds that bucket to their workspaces list can view the workspace and see tasks in that bucket from all users." @@ -778,6 +793,9 @@ export const initStore = async () => { .map(id => store.buckets.find(b => b.id === id)?.name) .filter(Boolean); + // Sync System Tags & Share Rules FIRST to ensure Self-Rules exist for query construction + await syncSystemTagsAndRules(); + // Add rules from shareRules store (which was just synced) const incomingRules = store.shareRules.filter(r => r.targetUserId === currentUserId); @@ -823,6 +841,7 @@ export const initStore = async () => { } }); + // 1. Fetch Global Rule Tasks (from ANY user) // 1. Fetch Global Rule Tasks (from ANY user) if (globalTagRules.length > 0) { // We want tasks that have ANY of these tags, AND are not completed @@ -943,8 +962,8 @@ export const initStore = async () => { } } - // Sync System Tags & Share Rules (Heavy maintenance task) - await syncSystemTagsAndRules(); + // Sync System Tags & Share Rules (Moved to top of backgroundSync to ensure rules exist before use) + // await syncSystemTagsAndRules(); }; // Fire off background sync after a short delay @@ -1619,6 +1638,45 @@ export const syncSystemTagsAndRules = async () => { // Legacy loop removed to prevent exponential growth } */ + + // --- Ensure Self-Rule for Current User (Global Subscription) --- + // This is the critical piece for "Global Sharing". + // We must ensure the user has a rule: Owner=Me, Target=Me, Tag=Me. + if (user.id === currentUserId) { + const selfRuleExists = normalizedRules.some(r => + r.ownerId === currentUserId && + r.targetUserId === currentUserId && + r.type === 'tag' && + r.tagName === userName + ); + + if (!selfRuleExists) { + try { + const record = await pb.collection(SHARE_RULES_COLLECTION).create({ + ownerId: currentUserId, + targetUserId: currentUserId, + type: 'tag', + tagName: userName, + share_mode: 'ADD_USER' // Mode doesn't strictly matter for self-rule visibility, but ADD_USER is safe default + }); + + // Update local store immediately to ensure loadAllHistory sees it + const newRule: ShareRule = { + id: record.id, + ownerId: currentUserId, + targetUserId: currentUserId, + type: 'tag', + tagName: userName, + share_mode: 'ADD_USER' + }; + setStore("shareRules", prev => [...prev, newRule]); + + console.log(`Created self-rule for global sharing: ${userName}`); + } catch (e) { + console.error(`Failed to create self-rule for ${userName}`, e); + } + } + } } } catch (err) { @@ -1694,12 +1752,15 @@ export const loadAllHistory = async () => { // Add Share Rules const incomingRules = store.shareRules.filter(r => r.targetUserId === userId); + console.log("[DEBUG] loadAllHistory - Incoming Rules for User:", userId, incomingRules); + if (incomingRules.length > 0) { const rulesToFetch: string[] = []; incomingRules.forEach((rule: any) => { // Global Rule (Self-Targeted) if (rule.ownerId === rule.targetUserId && rule.type === 'tag' && rule.tagName) { + console.log("[DEBUG] Found Self-Rule:", rule.tagName); rulesToFetch.push(`tags ~ "${rule.tagName}"`); } else { // Standard Rule @@ -1716,7 +1777,14 @@ export const loadAllHistory = async () => { // Let's just push them as individual queries for safety. rulesToFetch.forEach(filter => { - promises.push(pb.collection(TASGRID_COLLECTION).getFullList({ filter, sort: '-created', fields: LIST_FIELDS, requestKey: null }).catch(() => [])); + console.log("[DEBUG] Fetching with filter:", filter); + promises.push(pb.collection(TASGRID_COLLECTION).getFullList({ filter, sort: '-created', fields: LIST_FIELDS, requestKey: null }).then(res => { + console.log(`[DEBUG] Filter "${filter}" returned ${res.length} items`); + return res; + }).catch(err => { + console.error(`[DEBUG] Filter "${filter}" FAILED:`, err); + return []; + })); }); }