chord-component
Version:
Lit-based web components for displaying musical chord diagrams and chord lists
357 lines (300 loc) • 9.93 kB
text/typescript
/**
* IndexedDB service for storing and retrieving chord data
*/
const DB_NAME = 'ChordComponentsDB';
const DB_VERSION = 2; // Incremented for new store
const STORE_NAME = 'chordData';
const USER_STORE_NAME = 'userChords';
export interface ChordData {
instrument: string;
chords: Record<string, any>;
timestamp: number;
}
export interface UserChordData {
key: string; // composite key: "instrument:chordName"
instrument: string;
chordName: string;
fingers: any[];
barres: any[];
position?: number; // Starting fret position (1 = first fret, etc.)
timestamp: number;
}
class IndexedDBService {
private dbPromise: Promise<IDBDatabase> | null = null;
/**
* Initialize and open the database
*/
private async openDB(): Promise<IDBDatabase> {
if (this.dbPromise) {
return this.dbPromise;
}
this.dbPromise = new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = () => {
reject(new Error(`Failed to open IndexedDB: ${request.error}`));
};
request.onsuccess = () => {
resolve(request.result);
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
// Create chord data store if it doesn't exist
if (!db.objectStoreNames.contains(STORE_NAME)) {
const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'instrument' });
objectStore.createIndex('timestamp', 'timestamp', { unique: false });
}
// Create user chords store if it doesn't exist
if (!db.objectStoreNames.contains(USER_STORE_NAME)) {
const userStore = db.createObjectStore(USER_STORE_NAME, { keyPath: 'key' });
userStore.createIndex('instrument', 'instrument', { unique: false });
userStore.createIndex('timestamp', 'timestamp', { unique: false });
}
};
});
return this.dbPromise;
}
/**
* Get chord data for a specific instrument from IndexedDB
*/
async getChordData(instrument: string): Promise<ChordData | null> {
try {
const db = await this.openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.get(instrument);
request.onsuccess = () => {
resolve(request.result || null);
};
request.onerror = () => {
reject(new Error(`Failed to get chord data: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
return null;
}
}
/**
* Store chord data for a specific instrument in IndexedDB
*/
async setChordData(instrument: string, chords: Record<string, any>): Promise<void> {
try {
const db = await this.openDB();
const data: ChordData = {
instrument,
chords,
timestamp: Date.now()
};
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readwrite');
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.put(data);
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
reject(new Error(`Failed to store chord data: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
throw error;
}
}
/**
* Get all stored instruments
*/
async getAllInstruments(): Promise<string[]> {
try {
const db = await this.openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.getAllKeys();
request.onsuccess = () => {
resolve(request.result as string[]);
};
request.onerror = () => {
reject(new Error(`Failed to get instruments: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
return [];
}
}
/**
* Clear all chord data from IndexedDB
*/
async clearAll(): Promise<void> {
try {
const db = await this.openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readwrite');
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.clear();
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
reject(new Error(`Failed to clear data: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
throw error;
}
}
/**
* Check if IndexedDB is available
*/
isAvailable(): boolean {
return typeof indexedDB !== 'undefined';
}
/**
* Save a user-defined chord
*/
async saveUserChord(instrument: string, chordName: string, chordData: { fingers: any[], barres: any[], position?: number }): Promise<void> {
try {
const db = await this.openDB();
const data: UserChordData = {
key: `${instrument}:${chordName}`,
instrument,
chordName,
fingers: chordData.fingers,
barres: chordData.barres,
position: chordData.position,
timestamp: Date.now()
};
return new Promise((resolve, reject) => {
const transaction = db.transaction([USER_STORE_NAME], 'readwrite');
const objectStore = transaction.objectStore(USER_STORE_NAME);
const request = objectStore.put(data);
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
reject(new Error(`Failed to save user chord: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
throw error;
}
}
/**
* Get a user-defined chord
*/
async getUserChord(instrument: string, chordName: string): Promise<UserChordData | null> {
try {
const db = await this.openDB();
const key = `${instrument}:${chordName}`;
return new Promise((resolve, reject) => {
const transaction = db.transaction([USER_STORE_NAME], 'readonly');
const objectStore = transaction.objectStore(USER_STORE_NAME);
const request = objectStore.get(key);
request.onsuccess = () => {
resolve(request.result || null);
};
request.onerror = () => {
reject(new Error(`Failed to get user chord: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
return null;
}
}
/**
* Delete a user-defined chord
*/
async deleteUserChord(instrument: string, chordName: string): Promise<void> {
try {
const db = await this.openDB();
const key = `${instrument}:${chordName}`;
return new Promise((resolve, reject) => {
const transaction = db.transaction([USER_STORE_NAME], 'readwrite');
const objectStore = transaction.objectStore(USER_STORE_NAME);
const request = objectStore.delete(key);
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
reject(new Error(`Failed to delete user chord: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
throw error;
}
}
/**
* Get all user-defined chords for an instrument
*/
async getUserChordsByInstrument(instrument: string): Promise<UserChordData[]> {
try {
const db = await this.openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([USER_STORE_NAME], 'readonly');
const objectStore = transaction.objectStore(USER_STORE_NAME);
const index = objectStore.index('instrument');
const request = index.getAll(instrument);
request.onsuccess = () => {
resolve(request.result || []);
};
request.onerror = () => {
reject(new Error(`Failed to get user chords: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
return [];
}
}
/**
* Get all user-defined chords
*/
async getAllUserChords(): Promise<UserChordData[]> {
try {
const db = await this.openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([USER_STORE_NAME], 'readonly');
const objectStore = transaction.objectStore(USER_STORE_NAME);
const request = objectStore.getAll();
request.onsuccess = () => {
resolve(request.result || []);
};
request.onerror = () => {
reject(new Error(`Failed to get all user chords: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
return [];
}
}
/**
* Clear all user-defined chords
*/
async clearUserChords(): Promise<void> {
try {
const db = await this.openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([USER_STORE_NAME], 'readwrite');
const objectStore = transaction.objectStore(USER_STORE_NAME);
const request = objectStore.clear();
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
reject(new Error(`Failed to clear user chords: ${request.error}`));
};
});
} catch (error) {
console.error('IndexedDB error:', error);
throw error;
}
}
}
export const indexedDBService = new IndexedDBService();