Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 102 additions & 15 deletions src/lib/stores/boardstore/nostr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,35 +500,107 @@ export class NostrIntegration {
}
}

console.log('[BoardStore] 🛰️ Subscribing to board, card AND deletion events (Event-Driven v2.0)');
console.log('[BoardStore] 🛰️ Subscribing to board, card AND deletion events (Collaboration v3.0)');

// ⚡ OPTIMIZATION: Filtere alte Deletion Events aus (nur letzte 7 Tage)
// Verhindert, dass hunderte alte Deletion Events bei jedem Start verarbeitet werden
const sevenDaysAgo = Math.floor((Date.now() - 7 * 24 * 60 * 60 * 1000) / 1000);

// ⚠️ Filtere nach Boards/Cards die der User erstellt hat
// Für Collaboration: Später könnten wir auch nach #p-tags (maintainers) filtern
const sub = this.ndk.subscribe(
// 🔧 COLLABORATION FIX: Separate Subscriptions für bessere Filter-Kontrolle

// 1️⃣ Subscription für eigene Board-Events (Kind 30301)
const ownBoardsSub = this.ndk.subscribe(
{
kinds: [30301, 30302, 5] as number[], // Board, Card, Deletion
authors: [pubkey], // Boards und Cards die dieser User erstellt hat
since: sevenDaysAgo // ⚡ OPTIMIZATION: Nur Events der letzten 7 Tage
kinds: [30301] as number[], // Board
authors: [pubkey], // Nur eigene Boards
since: sevenDaysAgo
} as any,
{ closeOnEose: false }
);

sub.on('event', async (event: any) => {
ownBoardsSub.on('event', async (event: any) => {
if (event.kind === 30301) {
// ===== BOARD-EVENT HANDLER =====
await this.handleBoardEvent(event, currentBoard, boardStore);
} else if (event.kind === 30302) {
// ===== CARD-EVENT HANDLER =====
}
});

// 2️⃣ Subscription für Card-Events des AKTUELLEN Boards (Kind 30302)
// 🎯 CRITICAL: Empfange Cards von ALLEN Nutzern für dieses Board!
const boardRef = `30301:${currentBoard.author || pubkey}:${currentBoard.id}`;
const cardsSub = this.ndk.subscribe(
{
kinds: [30302] as number[], // Card
'#a': [boardRef], // Alle Cards die zu diesem Board gehören
since: sevenDaysAgo
} as any,
{ closeOnEose: false }
);

cardsSub.on('event', async (event: any) => {
if (event.kind === 30302) {
await this.handleCardEvent(event, currentBoard, boardStore);
} else if (event.kind === 5) {
// ===== DELETION-EVENT HANDLER =====
}
});

// 3️⃣ Subscription für Deletion-Events (Kind 5)
// 🎯 COLLABORATION: Empfange Deletions von ALLEN Nutzern für dieses Board
// Filter: Kind 5 Events die auf dieses Board referenzieren (via 'a' tags)
const boardATag = `30301:${currentBoard.author || pubkey}:${currentBoard.id}`;
const deletionsSub = this.ndk.subscribe(
{
kinds: [5] as number[], // Deletion
// ⚠️ Wichtig: Nicht auf '#a' einschränken!
// Viele Card-Deletions referenzieren NUR die Card ('30302:...')
// Wir filtern präzise in handleDeletionEvent() anhand der 'a' tags
since: sevenDaysAgo
} as any,
{ closeOnEose: false }
);

deletionsSub.on('event', async (event: any) => {
if (event.kind === 5) {
// Filter: Nur Deletion-Events die zu diesem Board gehören
// (entweder Card-Deletions mit Referenz auf Cards dieses Boards,
// oder Board-Deletion für dieses Board)
await this.handleDeletionEvent(event, boardStore);
}
});

// Legacy subscription variable - speichere alle Subscriptions für Cleanup
const subscriptions = [ownBoardsSub, cardsSub, deletionsSub];

// 4️⃣ COLLABORATION: Wenn aktuelles Board geteilt ist, empfange auch Updates vom Owner
// Dies ermöglicht dass Viewer/Editors sehen wenn der Owner das Board ändert
if (currentBoard.author && currentBoard.author !== pubkey) {
console.log('[BoardStore] 🤝 Subscribing to shared board updates from owner:', currentBoard.author);

const sharedBoardSub = this.ndk.subscribe(
{
kinds: [30301] as number[], // Board-Updates vom Owner
authors: [currentBoard.author],
'#d': [currentBoard.id], // Nur dieses Board
since: sevenDaysAgo
} as any,
{ closeOnEose: false }
);

sharedBoardSub.on('event', async (event: any) => {
if (event.kind === 30301) {
await this.handleBoardEvent(event, currentBoard, boardStore);
}
});

subscriptions.push(sharedBoardSub);
}

// Cleanup-Wrapper für alle Subscriptions
const sub = {
stop: () => {
subscriptions.forEach(s => {
try { s.stop(); } catch {}
});
}
};

this.boardSubscription = sub;

Expand Down Expand Up @@ -1094,12 +1166,27 @@ export class NostrIntegration {
continue;
}

// 🔍 FILTER: Only process deletions for cards from current board
const currentBoard = boardStore.data;
if (!currentBoard) {
// No board loaded, skip
continue;
}

// Check if card belongs to current board
const result = currentBoard.findCardAndColumn(cardId);
if (!result) {
// Card not in current board, skip
continue;
}

// Track deletion timestamp (für Ordering)
this.cardDeletionTimestamps.set(cardId, deleteTime);

console.log(`🗑️ Deleting card ${cardId.substring(0, 16)}... from board (deletion event)`);

// ⚡ v2.0: Direkte Store-API (SECONDARY action)
boardStore.deleteCardFromNostr(cardId);
// Silent - kein Log bei Card-Deletion
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/lib/stores/kanbanStore.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,10 @@ export class BoardStore {
// Non-blocking error - board can still be used without followers loaded
});

// 🎯 COLLABORATION FIX: Subscription für neues Board neu starten
// Dies ist wichtig, damit Card-Events für das NEUE Board empfangen werden!
this.subscribeToNostrUpdates();

return true;
}

Expand Down
12 changes: 8 additions & 4 deletions src/routes/cardsboard/Board.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@
isDragging = true;
isLocalDnD = true; // ← NEU: Markiere lokale DnD-Operation
columns = e.detail.items;
}
function handleDndFinalizeColumns(e: any) {
}

function handleDndFinalizeColumns(e: any) {
isDragging = false;
const finalItems = e.detail.items;

Expand Down Expand Up @@ -123,7 +124,8 @@
console.log('🔓 Board.svelte: isLocalDnD = false (allow parent sync again)');
}, 2000); // ← Erhöht auf 2 Sekunden für sicheren Roundtrip
}
function handleItemFinalize(columnIdx: number, newItems: CardItem[]) {

function handleItemFinalize(columnIdx: number, newItems: CardItem[]) {
// Immutable update: Erstelle neues columns Array
const updatedColumns = columns.map((col, idx) =>
idx === columnIdx
Expand All @@ -132,7 +134,9 @@
);
columns = updatedColumns;
onFinalUpdate(updatedColumns);
} function handleCardAction(cardId: string, action: string) {
}

function handleCardAction(cardId: string, action: string) {
console.log('Card action:', cardId, action);
// Handle card actions like complete, edit, etc.
}
Expand Down
159 changes: 0 additions & 159 deletions src/routes/cardsboard/Column.paste-example.txt

This file was deleted.

Loading
Loading