mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
- Add IcsSource for calendar event integration into dataflow - Fix infinite loop with sourceSeq tracking mechanism - Resolve blank TaskView on quick startup with debounced events - Add change detection to prevent unnecessary batch updates - Implement render guards to prevent concurrent refreshes - Update reindex commands to support dataflow architecture - Persist ICS events separately in storage layer - Update architecture documentation with current implementation Fixes issues where: - TaskView showed blank despite having loaded tasks - Infinite "Batch update" loops occurred - ICS calendar events disappeared when background sync disabled - Rapid successive updates caused view resets before rendering
170 lines
No EOL
4.3 KiB
TypeScript
170 lines
No EOL
4.3 KiB
TypeScript
/**
|
|
* IcsSource - Event source for ICS calendar data
|
|
*
|
|
* This source integrates external calendar events into the dataflow architecture.
|
|
* It listens to IcsManager updates and emits standardized dataflow events.
|
|
*/
|
|
|
|
import { App, EventRef } from "obsidian";
|
|
import { Events, emit, Seq } from "../events/Events";
|
|
import type { IcsManager } from "../../managers/ics-manager";
|
|
import type { Task } from "../../types/task";
|
|
|
|
export class IcsSource {
|
|
private eventRefs: EventRef[] = [];
|
|
private isInitialized = false;
|
|
private lastIcsUpdateSeq = 0;
|
|
|
|
constructor(
|
|
private app: App,
|
|
private getIcsManager: () => IcsManager | undefined
|
|
) {}
|
|
|
|
/**
|
|
* Initialize the ICS source and start listening for calendar updates
|
|
*/
|
|
initialize(): void {
|
|
if (this.isInitialized) return;
|
|
|
|
console.log("[IcsSource] Initializing ICS event source...");
|
|
|
|
// Initial load of ICS events
|
|
this.loadAndEmitIcsEvents();
|
|
|
|
// Subscribe to ICS manager updates
|
|
this.subscribeToIcsUpdates();
|
|
|
|
this.isInitialized = true;
|
|
}
|
|
|
|
/**
|
|
* Subscribe to ICS manager update events
|
|
*/
|
|
private subscribeToIcsUpdates(): void {
|
|
// Listen for ICS cache updates
|
|
this.app.workspace.on("ics-cache-updated" as any, () => {
|
|
console.log("[IcsSource] ICS cache updated, reloading events...");
|
|
this.loadAndEmitIcsEvents();
|
|
});
|
|
|
|
// Listen for ICS configuration changes
|
|
this.app.workspace.on("ics-config-changed" as any, () => {
|
|
console.log("[IcsSource] ICS config changed, reloading events...");
|
|
this.loadAndEmitIcsEvents();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Load ICS events from manager and emit update event
|
|
*/
|
|
private async loadAndEmitIcsEvents(): Promise<void> {
|
|
const icsManager = this.getIcsManager();
|
|
if (!icsManager) {
|
|
console.log("[IcsSource] No ICS manager available");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Get all ICS events with sync
|
|
const icsEvents = await icsManager.getAllEventsWithSync();
|
|
|
|
// Convert ICS events to Task format with proper source marking
|
|
const icsTasks: Task[] = icsEvents.map((event: any) => ({
|
|
...event,
|
|
metadata: {
|
|
...event.metadata,
|
|
source: {
|
|
type: 'ics',
|
|
id: event.source?.id || 'unknown',
|
|
name: event.source?.name || 'ICS Calendar'
|
|
}
|
|
}
|
|
}));
|
|
|
|
console.log(`[IcsSource] Loaded ${icsTasks.length} ICS events`);
|
|
|
|
// Generate sequence for this update
|
|
this.lastIcsUpdateSeq = Seq.next();
|
|
|
|
// Emit ICS events update
|
|
emit(this.app, Events.ICS_EVENTS_UPDATED, {
|
|
events: icsTasks,
|
|
timestamp: Date.now(),
|
|
seq: this.lastIcsUpdateSeq,
|
|
stats: {
|
|
total: icsTasks.length,
|
|
sources: this.getSourceStats(icsTasks)
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("[IcsSource] Error loading ICS events:", error);
|
|
|
|
// Emit empty update on error to clear stale data
|
|
emit(this.app, Events.ICS_EVENTS_UPDATED, {
|
|
events: [],
|
|
timestamp: Date.now(),
|
|
seq: Seq.next(),
|
|
error: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get statistics about ICS sources
|
|
*/
|
|
private getSourceStats(events: Task[]): Record<string, number> {
|
|
const stats: Record<string, number> = {};
|
|
|
|
for (const event of events) {
|
|
const sourceId = event.metadata?.source?.id || 'unknown';
|
|
stats[sourceId] = (stats[sourceId] || 0) + 1;
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
/**
|
|
* Refresh ICS events manually
|
|
*/
|
|
async refresh(): Promise<void> {
|
|
console.log("[IcsSource] Manual refresh triggered");
|
|
await this.loadAndEmitIcsEvents();
|
|
}
|
|
|
|
/**
|
|
* Get current statistics
|
|
*/
|
|
getStats(): {
|
|
initialized: boolean;
|
|
lastUpdateSeq: number;
|
|
} {
|
|
return {
|
|
initialized: this.isInitialized,
|
|
lastUpdateSeq: this.lastIcsUpdateSeq
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Cleanup and destroy the source
|
|
*/
|
|
destroy(): void {
|
|
console.log("[IcsSource] Destroying ICS source...");
|
|
|
|
// Clear event listeners
|
|
for (const ref of this.eventRefs) {
|
|
this.app.vault.offref(ref);
|
|
}
|
|
this.eventRefs = [];
|
|
|
|
// Emit clear event
|
|
emit(this.app, Events.ICS_EVENTS_UPDATED, {
|
|
events: [],
|
|
timestamp: Date.now(),
|
|
seq: Seq.next(),
|
|
destroyed: true
|
|
});
|
|
|
|
this.isInitialized = false;
|
|
}
|
|
} |