Some ignored filles was missing
This commit is contained in:
parent
7db22245e2
commit
fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions
340
wwwroot/js/storage/IndexedDBService.js
Normal file
340
wwwroot/js/storage/IndexedDBService.js
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
/**
|
||||
* IndexedDB Service for Calendar App
|
||||
* Handles local storage of events and sync queue
|
||||
*/
|
||||
export class IndexedDBService {
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
/**
|
||||
* Initialize and open the database
|
||||
*/
|
||||
async initialize() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(IndexedDBService.DB_NAME, IndexedDBService.DB_VERSION);
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to open IndexedDB: ${request.error}`));
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
this.initialized = true;
|
||||
resolve();
|
||||
};
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
// Create events store
|
||||
if (!db.objectStoreNames.contains(IndexedDBService.EVENTS_STORE)) {
|
||||
const eventsStore = db.createObjectStore(IndexedDBService.EVENTS_STORE, { keyPath: 'id' });
|
||||
eventsStore.createIndex('start', 'start', { unique: false });
|
||||
eventsStore.createIndex('end', 'end', { unique: false });
|
||||
eventsStore.createIndex('syncStatus', 'syncStatus', { unique: false });
|
||||
}
|
||||
// Create operation queue store
|
||||
if (!db.objectStoreNames.contains(IndexedDBService.QUEUE_STORE)) {
|
||||
const queueStore = db.createObjectStore(IndexedDBService.QUEUE_STORE, { keyPath: 'id' });
|
||||
queueStore.createIndex('timestamp', 'timestamp', { unique: false });
|
||||
}
|
||||
// Create sync state store
|
||||
if (!db.objectStoreNames.contains(IndexedDBService.SYNC_STATE_STORE)) {
|
||||
db.createObjectStore(IndexedDBService.SYNC_STATE_STORE, { keyPath: 'key' });
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Check if database is initialized
|
||||
*/
|
||||
isInitialized() {
|
||||
return this.initialized;
|
||||
}
|
||||
/**
|
||||
* Ensure database is initialized
|
||||
*/
|
||||
ensureDB() {
|
||||
if (!this.db) {
|
||||
throw new Error('IndexedDB not initialized. Call initialize() first.');
|
||||
}
|
||||
return this.db;
|
||||
}
|
||||
// ========================================
|
||||
// Event CRUD Operations
|
||||
// ========================================
|
||||
/**
|
||||
* Get a single event by ID
|
||||
*/
|
||||
async getEvent(id) {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readonly');
|
||||
const store = transaction.objectStore(IndexedDBService.EVENTS_STORE);
|
||||
const request = store.get(id);
|
||||
request.onsuccess = () => {
|
||||
const event = request.result;
|
||||
resolve(event ? this.deserializeEvent(event) : null);
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get event ${id}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get all events
|
||||
*/
|
||||
async getAllEvents() {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readonly');
|
||||
const store = transaction.objectStore(IndexedDBService.EVENTS_STORE);
|
||||
const request = store.getAll();
|
||||
request.onsuccess = () => {
|
||||
const events = request.result;
|
||||
resolve(events.map(e => this.deserializeEvent(e)));
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get all events: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Save an event (create or update)
|
||||
*/
|
||||
async saveEvent(event) {
|
||||
const db = this.ensureDB();
|
||||
const serialized = this.serializeEvent(event);
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(IndexedDBService.EVENTS_STORE);
|
||||
const request = store.put(serialized);
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to save event ${event.id}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Delete an event
|
||||
*/
|
||||
async deleteEvent(id) {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.EVENTS_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(IndexedDBService.EVENTS_STORE);
|
||||
const request = store.delete(id);
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to delete event ${id}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
// ========================================
|
||||
// Queue Operations
|
||||
// ========================================
|
||||
/**
|
||||
* Add operation to queue
|
||||
*/
|
||||
async addToQueue(operation) {
|
||||
const db = this.ensureDB();
|
||||
const queueItem = {
|
||||
...operation,
|
||||
id: `${operation.type}-${operation.eventId}-${Date.now()}`
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(IndexedDBService.QUEUE_STORE);
|
||||
const request = store.put(queueItem);
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to add to queue: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get all queue operations (sorted by timestamp)
|
||||
*/
|
||||
async getQueue() {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readonly');
|
||||
const store = transaction.objectStore(IndexedDBService.QUEUE_STORE);
|
||||
const index = store.index('timestamp');
|
||||
const request = index.getAll();
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result);
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get queue: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Remove operation from queue
|
||||
*/
|
||||
async removeFromQueue(id) {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(IndexedDBService.QUEUE_STORE);
|
||||
const request = store.delete(id);
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to remove from queue: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Clear entire queue
|
||||
*/
|
||||
async clearQueue() {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.QUEUE_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(IndexedDBService.QUEUE_STORE);
|
||||
const request = store.clear();
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to clear queue: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
// ========================================
|
||||
// Sync State Operations
|
||||
// ========================================
|
||||
/**
|
||||
* Save sync state value
|
||||
*/
|
||||
async setSyncState(key, value) {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.SYNC_STATE_STORE], 'readwrite');
|
||||
const store = transaction.objectStore(IndexedDBService.SYNC_STATE_STORE);
|
||||
const request = store.put({ key, value });
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to set sync state ${key}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get sync state value
|
||||
*/
|
||||
async getSyncState(key) {
|
||||
const db = this.ensureDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction([IndexedDBService.SYNC_STATE_STORE], 'readonly');
|
||||
const store = transaction.objectStore(IndexedDBService.SYNC_STATE_STORE);
|
||||
const request = store.get(key);
|
||||
request.onsuccess = () => {
|
||||
const result = request.result;
|
||||
resolve(result ? result.value : null);
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get sync state ${key}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
// ========================================
|
||||
// Serialization Helpers
|
||||
// ========================================
|
||||
/**
|
||||
* Serialize event for IndexedDB storage (convert Dates to ISO strings)
|
||||
*/
|
||||
serializeEvent(event) {
|
||||
return {
|
||||
...event,
|
||||
start: event.start instanceof Date ? event.start.toISOString() : event.start,
|
||||
end: event.end instanceof Date ? event.end.toISOString() : event.end
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Deserialize event from IndexedDB (convert ISO strings to Dates)
|
||||
*/
|
||||
deserializeEvent(event) {
|
||||
return {
|
||||
...event,
|
||||
start: typeof event.start === 'string' ? new Date(event.start) : event.start,
|
||||
end: typeof event.end === 'string' ? new Date(event.end) : event.end
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Close database connection
|
||||
*/
|
||||
close() {
|
||||
if (this.db) {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete entire database (for testing/reset)
|
||||
*/
|
||||
static async deleteDatabase() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.deleteDatabase(IndexedDBService.DB_NAME);
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to delete database: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Seed IndexedDB with mock data if empty
|
||||
*/
|
||||
async seedIfEmpty(mockDataUrl = 'data/mock-events.json') {
|
||||
try {
|
||||
const existingEvents = await this.getAllEvents();
|
||||
if (existingEvents.length > 0) {
|
||||
console.log(`IndexedDB already has ${existingEvents.length} events - skipping seed`);
|
||||
return;
|
||||
}
|
||||
console.log('IndexedDB is empty - seeding with mock data');
|
||||
// Check if online to fetch mock data
|
||||
if (!navigator.onLine) {
|
||||
console.warn('Offline and IndexedDB empty - starting with no events');
|
||||
return;
|
||||
}
|
||||
// Fetch mock events
|
||||
const response = await fetch(mockDataUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch mock events: ${response.statusText}`);
|
||||
}
|
||||
const mockEvents = await response.json();
|
||||
// Convert and save to IndexedDB
|
||||
for (const event of mockEvents) {
|
||||
const calendarEvent = {
|
||||
...event,
|
||||
start: new Date(event.start),
|
||||
end: new Date(event.end),
|
||||
allDay: event.allDay || false,
|
||||
syncStatus: 'synced'
|
||||
};
|
||||
await this.saveEvent(calendarEvent);
|
||||
}
|
||||
console.log(`Seeded IndexedDB with ${mockEvents.length} mock events`);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to seed IndexedDB:', error);
|
||||
// Don't throw - allow app to start with empty calendar
|
||||
}
|
||||
}
|
||||
}
|
||||
IndexedDBService.DB_NAME = 'CalendarDB';
|
||||
IndexedDBService.DB_VERSION = 1;
|
||||
IndexedDBService.EVENTS_STORE = 'events';
|
||||
IndexedDBService.QUEUE_STORE = 'operationQueue';
|
||||
IndexedDBService.SYNC_STATE_STORE = 'syncState';
|
||||
//# sourceMappingURL=IndexedDBService.js.map
|
||||
Loading…
Add table
Add a link
Reference in a new issue