Implement hybrid WebDAV + API favorite sync

- Keep WebDAV for reliable content sync
- Add REST API calls for favorite status only
- Match notes by modified timestamp + category (titles can differ)
- Sync favorites from API after WebDAV sync completes
- Update favorite via API when user toggles star
- Tested and working with mobile app sync
This commit is contained in:
drelich
2026-03-26 09:14:42 +01:00
parent 36733da434
commit 244ba69eed
4 changed files with 143 additions and 4 deletions

View File

@@ -117,6 +117,9 @@ export class SyncManager {
}
}
// Sync favorite status from API
await this.syncFavoriteStatus();
this.notifyStatus('idle', 0);
// Notify that sync is complete so UI can reload
@@ -131,6 +134,41 @@ export class SyncManager {
}
}
// Sync favorite status from API to local cache
private async syncFavoriteStatus(): Promise<void> {
if (!this.api) return;
try {
console.log('Syncing favorite status from API...');
const apiMetadata = await this.api.fetchNotesMetadata();
const cachedNotes = await localDB.getAllNotes();
// Map API notes by title and category for matching
const apiMap = new Map<string, {id: number, favorite: boolean}>();
for (const apiNote of apiMetadata) {
const key = `${apiNote.category}/${apiNote.title}`;
apiMap.set(key, { id: apiNote.id, favorite: apiNote.favorite });
}
// Update favorite status in cache for matching notes
for (const cachedNote of cachedNotes) {
const key = `${cachedNote.category}/${cachedNote.title}`;
const apiData = apiMap.get(key);
if (apiData && cachedNote.favorite !== apiData.favorite) {
console.log(`Updating favorite status for "${cachedNote.title}": ${cachedNote.favorite} -> ${apiData.favorite}`);
cachedNote.favorite = apiData.favorite;
await localDB.saveNote(cachedNote);
}
}
console.log('Favorite status sync complete');
} catch (error) {
console.error('Failed to sync favorite status:', error);
// Don't throw - favorite sync is non-critical
}
}
// Fetch all notes and cache them
private async fetchAndCacheNotes(): Promise<Note[]> {
if (!this.api) throw new Error('API not initialized');
@@ -197,6 +235,42 @@ export class SyncManager {
}
}
// Update favorite status via API
async updateFavoriteStatus(note: Note, favorite: boolean): Promise<void> {
if (!this.api) {
throw new Error('API not initialized');
}
if (!this.isOnline) {
// Update locally, will sync when back online
note.favorite = favorite;
await localDB.saveNote(note);
return;
}
try {
// Find API ID for this note
const apiId = await this.api.findApiIdForNote(note.title, note.category, note.modified);
if (apiId) {
// Update via API
await this.api.updateFavoriteStatus(apiId, favorite);
console.log(`Updated favorite status for "${note.title}" (API ID: ${apiId})`);
} else {
console.warn(`Could not find API ID for note: "${note.title}"`);
}
// Update local cache
note.favorite = favorite;
await localDB.saveNote(note);
} catch (error) {
console.error('Failed to update favorite status:', error);
// Still update locally
note.favorite = favorite;
await localDB.saveNote(note);
}
}
// Update note on server and cache
async updateNote(note: Note): Promise<Note> {
if (!this.api) {