Update database schema and synchronization logic

- Add `useEffect` in `ScribeLeftBar` for handling book state changes.
- Extend `BooksSyncContext` with new properties and stricter typings.
- Refine `Repositories` to include `lastUpdate` handling for synchronization processes.
- Add comprehensive `fetchComplete*` repository methods for retrieving entity-specific sync data.
- Enhance offline logic for chapters, characters, locations, and world synchronization.
- Improve error handling across IPC handlers and repositories.
This commit is contained in:
natreex
2025-12-15 20:55:24 -05:00
parent bb331b5c22
commit 64c7cb6243
23 changed files with 1609 additions and 79 deletions

View File

@@ -109,4 +109,232 @@ export interface SyncedGuideLine {
export interface SyncedAIGuideLine {
lastUpdate: number;
}
export interface BookSyncCompare {
id: string;
chapters: string[];
chapterContents: string[];
chapterInfos: string[];
characters: string[];
characterAttributes: string[];
locations: string[];
locationElements: string[];
locationSubElements: string[];
worlds: string[];
worldElements: string[];
incidents: string[];
plotPoints: string[];
issues: string[];
actSummaries: string[];
guideLine: boolean;
aiGuideLine: boolean;
}
export function compareBookSyncs(newerBook: SyncedBook, olderBook: SyncedBook): BookSyncCompare | null {
const changedChapterIds: string[] = [];
const changedChapterContentIds: string[] = [];
const changedChapterInfoIds: string[] = [];
const changedCharacterIds: string[] = [];
const changedCharacterAttributeIds: string[] = [];
const changedLocationIds: string[] = [];
const changedLocationElementIds: string[] = [];
const changedLocationSubElementIds: string[] = [];
const changedWorldIds: string[] = [];
const changedWorldElementIds: string[] = [];
const changedIncidentIds: string[] = [];
const changedPlotPointIds: string[] = [];
const changedIssueIds: string[] = [];
const changedActSummaryIds: string[] = [];
let guideLineChanged: boolean = false;
let aiGuideLineChanged: boolean = false;
newerBook.chapters.forEach((newerChapter: SyncedChapter): void => {
const olderChapter: SyncedChapter | undefined = olderBook.chapters.find((chapter: SyncedChapter): boolean => chapter.id === newerChapter.id);
if (!olderChapter) {
changedChapterIds.push(newerChapter.id);
newerChapter.contents.forEach((content: SyncedChapterContent): void => {
changedChapterContentIds.push(content.id);
});
if (newerChapter.info) {
changedChapterInfoIds.push(newerChapter.info.id);
}
} else if (newerChapter.lastUpdate > olderChapter.lastUpdate) {
changedChapterIds.push(newerChapter.id);
} else {
newerChapter.contents.forEach((newerContent: SyncedChapterContent): void => {
const olderContent: SyncedChapterContent | undefined = olderChapter.contents.find((content: SyncedChapterContent): boolean => content.id === newerContent.id);
if (!olderContent || newerContent.lastUpdate > olderContent.lastUpdate) {
changedChapterContentIds.push(newerContent.id);
}
});
if (newerChapter.info && olderChapter.info) {
if (newerChapter.info.lastUpdate > olderChapter.info.lastUpdate) {
changedChapterInfoIds.push(newerChapter.info.id);
}
} else if (newerChapter.info && !olderChapter.info) {
changedChapterInfoIds.push(newerChapter.info.id);
}
}
});
newerBook.characters.forEach((newerCharacter: SyncedCharacter): void => {
const olderCharacter: SyncedCharacter | undefined = olderBook.characters.find((character: SyncedCharacter): boolean => character.id === newerCharacter.id);
if (!olderCharacter) {
changedCharacterIds.push(newerCharacter.id);
newerCharacter.attributes.forEach((attribute: SyncedCharacterAttribute): void => {
changedCharacterAttributeIds.push(attribute.id);
});
} else if (newerCharacter.lastUpdate > olderCharacter.lastUpdate) {
changedCharacterIds.push(newerCharacter.id);
} else {
newerCharacter.attributes.forEach((newerAttribute: SyncedCharacterAttribute): void => {
const olderAttribute: SyncedCharacterAttribute | undefined = olderCharacter.attributes.find((attribute: SyncedCharacterAttribute): boolean => attribute.id === newerAttribute.id);
if (!olderAttribute || newerAttribute.lastUpdate > olderAttribute.lastUpdate) {
changedCharacterAttributeIds.push(newerAttribute.id);
}
});
}
});
newerBook.locations.forEach((newerLocation: SyncedLocation): void => {
const olderLocation: SyncedLocation | undefined = olderBook.locations.find((location: SyncedLocation): boolean => location.id === newerLocation.id);
if (!olderLocation) {
changedLocationIds.push(newerLocation.id);
newerLocation.elements.forEach((element: SyncedLocationElement): void => {
changedLocationElementIds.push(element.id);
element.subElements.forEach((subElement: SyncedLocationSubElement): void => {
changedLocationSubElementIds.push(subElement.id);
});
});
} else if (newerLocation.lastUpdate > olderLocation.lastUpdate) {
changedLocationIds.push(newerLocation.id);
} else {
newerLocation.elements.forEach((newerElement: SyncedLocationElement): void => {
const olderElement: SyncedLocationElement | undefined = olderLocation.elements.find((element: SyncedLocationElement): boolean => element.id === newerElement.id);
if (!olderElement) {
changedLocationElementIds.push(newerElement.id);
newerElement.subElements.forEach((subElement: SyncedLocationSubElement): void => {
changedLocationSubElementIds.push(subElement.id);
});
} else if (newerElement.lastUpdate > olderElement.lastUpdate) {
changedLocationElementIds.push(newerElement.id);
} else {
newerElement.subElements.forEach((newerSubElement: SyncedLocationSubElement): void => {
const olderSubElement: SyncedLocationSubElement | undefined = olderElement.subElements.find((subElement: SyncedLocationSubElement): boolean => subElement.id === newerSubElement.id);
if (!olderSubElement || newerSubElement.lastUpdate > olderSubElement.lastUpdate) {
changedLocationSubElementIds.push(newerSubElement.id);
}
});
}
});
}
});
newerBook.worlds.forEach((newerWorld: SyncedWorld): void => {
const olderWorld: SyncedWorld | undefined = olderBook.worlds.find((world: SyncedWorld): boolean => world.id === newerWorld.id);
if (!olderWorld) {
changedWorldIds.push(newerWorld.id);
newerWorld.elements.forEach((element: SyncedWorldElement): void => {
changedWorldElementIds.push(element.id);
});
} else if (newerWorld.lastUpdate > olderWorld.lastUpdate) {
changedWorldIds.push(newerWorld.id);
} else {
newerWorld.elements.forEach((newerElement: SyncedWorldElement): void => {
const olderElement: SyncedWorldElement | undefined = olderWorld.elements.find((element: SyncedWorldElement): boolean => element.id === newerElement.id);
if (!olderElement || newerElement.lastUpdate > olderElement.lastUpdate) {
changedWorldElementIds.push(newerElement.id);
}
});
}
});
newerBook.incidents.forEach((newerIncident: SyncedIncident): void => {
const olderIncident: SyncedIncident | undefined = olderBook.incidents.find((incident: SyncedIncident): boolean => incident.id === newerIncident.id);
if (!olderIncident || newerIncident.lastUpdate > olderIncident.lastUpdate) {
changedIncidentIds.push(newerIncident.id);
}
});
newerBook.plotPoints.forEach((newerPlotPoint: SyncedPlotPoint): void => {
const olderPlotPoint: SyncedPlotPoint | undefined = olderBook.plotPoints.find((plotPoint: SyncedPlotPoint): boolean => plotPoint.id === newerPlotPoint.id);
if (!olderPlotPoint || newerPlotPoint.lastUpdate > olderPlotPoint.lastUpdate) {
changedPlotPointIds.push(newerPlotPoint.id);
}
});
newerBook.issues.forEach((newerIssue: SyncedIssue): void => {
const olderIssue: SyncedIssue | undefined = olderBook.issues.find((issue: SyncedIssue): boolean => issue.id === newerIssue.id);
if (!olderIssue || newerIssue.lastUpdate > olderIssue.lastUpdate) {
changedIssueIds.push(newerIssue.id);
}
});
newerBook.actSummaries.forEach((newerActSummary: SyncedActSummary): void => {
const olderActSummary: SyncedActSummary | undefined = olderBook.actSummaries.find((actSummary: SyncedActSummary): boolean => actSummary.id === newerActSummary.id);
if (!olderActSummary || newerActSummary.lastUpdate > olderActSummary.lastUpdate) {
changedActSummaryIds.push(newerActSummary.id);
}
});
if (newerBook.guideLine && olderBook.guideLine) {
guideLineChanged = newerBook.guideLine.lastUpdate > olderBook.guideLine.lastUpdate;
} else if (newerBook.guideLine && !olderBook.guideLine) {
guideLineChanged = true;
}
if (newerBook.aiGuideLine && olderBook.aiGuideLine) {
aiGuideLineChanged = newerBook.aiGuideLine.lastUpdate > olderBook.aiGuideLine.lastUpdate;
} else if (newerBook.aiGuideLine && !olderBook.aiGuideLine) {
aiGuideLineChanged = true;
}
const hasChanges: boolean =
changedChapterIds.length > 0 ||
changedChapterContentIds.length > 0 ||
changedChapterInfoIds.length > 0 ||
changedCharacterIds.length > 0 ||
changedCharacterAttributeIds.length > 0 ||
changedLocationIds.length > 0 ||
changedLocationElementIds.length > 0 ||
changedLocationSubElementIds.length > 0 ||
changedWorldIds.length > 0 ||
changedWorldElementIds.length > 0 ||
changedIncidentIds.length > 0 ||
changedPlotPointIds.length > 0 ||
changedIssueIds.length > 0 ||
changedActSummaryIds.length > 0 ||
guideLineChanged ||
aiGuideLineChanged;
if (!hasChanges) {
return null;
}
return {
id: newerBook.id,
chapters: changedChapterIds,
chapterContents: changedChapterContentIds,
chapterInfos: changedChapterInfoIds,
characters: changedCharacterIds,
characterAttributes: changedCharacterAttributeIds,
locations: changedLocationIds,
locationElements: changedLocationElementIds,
locationSubElements: changedLocationSubElementIds,
worlds: changedWorldIds,
worldElements: changedWorldElementIds,
incidents: changedIncidentIds,
plotPoints: changedPlotPointIds,
issues: changedIssueIds,
actSummaries: changedActSummaryIds,
guideLine: guideLineChanged,
aiGuideLine: aiGuideLineChanged
};
}