Implements offline-first calendar sync infrastructure

Adds IndexedDB and operation queue for robust offline synchronization
Introduces SyncManager to handle background data synchronization
Supports local event operations with automatic remote sync queuing

Enhances application reliability and user experience in low/no connectivity scenarios
This commit is contained in:
Janus C. H. Knudsen 2025-11-05 00:37:57 +01:00
parent 9c765b35ab
commit e7011526e3
20 changed files with 3822 additions and 57 deletions

View file

@ -21,9 +21,16 @@ import { EdgeScrollManager } from './managers/EdgeScrollManager';
import { DragHoverManager } from './managers/DragHoverManager';
import { HeaderManager } from './managers/HeaderManager';
// Import repositories
// Import repositories and storage
import { IEventRepository } from './repositories/IEventRepository';
import { MockEventRepository } from './repositories/MockEventRepository';
import { IndexedDBEventRepository } from './repositories/IndexedDBEventRepository';
import { ApiEventRepository } from './repositories/ApiEventRepository';
import { IndexedDBService } from './storage/IndexedDBService';
import { OperationQueue } from './storage/OperationQueue';
// Import workers
import { SyncManager } from './workers/SyncManager';
// Import renderers
import { DateHeaderRenderer, type IHeaderRenderer } from './renderers/DateHeaderRenderer';
@ -53,8 +60,8 @@ async function handleDeepLinking(eventManager: EventManager, urlManager: URLMana
console.log(`Deep linking to event ID: ${eventId}`);
// Wait a bit for managers to be fully ready
setTimeout(() => {
const success = eventManager.navigateToEvent(eventId);
setTimeout(async () => {
const success = await eventManager.navigateToEvent(eventId);
if (!success) {
console.warn(`Deep linking failed: Event with ID ${eventId} not found`);
}
@ -73,6 +80,22 @@ async function initializeCalendar(): Promise<void> {
// Load configuration from JSON
const config = await ConfigManager.load();
// ========================================
// Initialize IndexedDB and seed if needed
// ========================================
const indexedDB = new IndexedDBService();
await indexedDB.initialize();
await indexedDB.seedIfEmpty();
// Create operation queue
const queue = new OperationQueue(indexedDB);
// Create API repository (placeholder for now)
const apiRepository = new ApiEventRepository(config.apiEndpoint || '/api');
// Create IndexedDB repository
const repository = new IndexedDBEventRepository(indexedDB, queue);
// Create NovaDI container
const container = new Container();
const builder = container.builder();
@ -86,8 +109,13 @@ async function initializeCalendar(): Promise<void> {
// Register configuration instance
builder.registerInstance(config).as<Configuration>();
// Register repositories
builder.registerType(MockEventRepository).as<IEventRepository>();
// Register IndexedDB and storage instances
builder.registerInstance(indexedDB).as<IndexedDBService>();
builder.registerInstance(queue).as<OperationQueue>();
builder.registerInstance(apiRepository).as<ApiEventRepository>();
// Register repository
builder.registerInstance(repository).as<IEventRepository>();
// Register renderers
builder.registerType(DateHeaderRenderer).as<IHeaderRenderer>();
@ -143,6 +171,13 @@ async function initializeCalendar(): Promise<void> {
await calendarManager.initialize?.();
await resizeHandleManager.initialize?.();
// ========================================
// Initialize and start SyncManager
// ========================================
const syncManager = new SyncManager(eventBus, queue, indexedDB, apiRepository);
syncManager.startSync();
console.log('SyncManager initialized and started');
// Handle deep linking after managers are initialized
await handleDeepLinking(eventManager, urlManager);
@ -153,12 +188,18 @@ async function initializeCalendar(): Promise<void> {
app: typeof app;
calendarManager: typeof calendarManager;
eventManager: typeof eventManager;
syncManager: typeof syncManager;
indexedDB: typeof indexedDB;
queue: typeof queue;
};
}).calendarDebug = {
eventBus,
app,
calendarManager,
eventManager,
syncManager,
indexedDB,
queue,
};
} catch (error) {