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

@@ -163,12 +163,12 @@ export const bookTypes: SelectBoxProps[] = [
export default class Book {
constructor() {
}
static booksToSelectBox(books: BookProps[]): SelectBoxProps[] {
return books.map((book: BookProps) => {
static booksToSelectBox(books: SyncedBook[]): SelectBoxProps[] {
return books.map((book: SyncedBook):SelectBoxProps => {
return {
label: book.title,
value: book.bookId,
value: book.id,
}
});
}

View File

@@ -12,6 +12,7 @@ export interface EritBooksTable {
desired_word_count:number|null;
words_count:number|null;
cover_image:string|null;
last_update:number;
}
export interface BookActSummariesTable {
@@ -20,6 +21,7 @@ export interface BookActSummariesTable {
user_id: string;
act_index: number;
summary: string | null;
last_update: number;
}
export interface BookAIGuideLineTable {
@@ -41,9 +43,10 @@ export interface BookChaptersTable {
book_id: string;
author_id: string;
title: string;
hashed_title: string | null;
hashed_title: string;
words_count: number | null;
chapter_order: number | null;
chapter_order: number;
last_update: number;
}
export interface BookChapterContentTable {
@@ -54,18 +57,20 @@ export interface BookChapterContentTable {
content: string | null;
words_count: number;
time_on_it: number;
last_update: number;
}
export interface BookChapterInfosTable {
chapter_info_id: string;
chapter_id: string;
act_id: number | null;
act_id: number;
incident_id: string | null;
plot_point_id: string | null;
book_id: string;
author_id: string;
summary: string | null;
goal: string | null;
last_update: number;
}
export interface BookCharactersTable {
@@ -80,6 +85,7 @@ export interface BookCharactersTable {
role: string | null;
biography: string | null;
history: string | null;
last_update: number;
}
export interface BookCharactersAttributesTable {
@@ -88,6 +94,7 @@ export interface BookCharactersAttributesTable {
user_id: string;
attribute_name: string;
attribute_value: string;
last_update: number;
}
export interface BookGuideLineTable {
@@ -112,6 +119,7 @@ export interface BookIncidentsTable {
title: string;
hashed_title: string;
summary: string | null;
last_update: number;
}
export interface BookIssuesTable {
@@ -120,6 +128,7 @@ export interface BookIssuesTable {
book_id: string;
name: string;
hashed_issue_name: string;
last_update: number;
}
export interface BookLocationTable {
@@ -128,6 +137,7 @@ export interface BookLocationTable {
user_id: string;
loc_name: string;
loc_original_name: string;
last_update: number;
}
export interface BookPlotPointsTable {
@@ -138,6 +148,7 @@ export interface BookPlotPointsTable {
linked_incident_id: string | null;
author_id: string;
book_id: string;
last_update: number;
}
export interface BookWorldTable {
@@ -151,6 +162,7 @@ export interface BookWorldTable {
economy: string | null;
religion: string | null;
languages: string | null;
last_update: number;
}
export interface BookWorldElementsTable {
@@ -161,6 +173,7 @@ export interface BookWorldElementsTable {
name: string;
original_name: string;
description: string | null;
last_update: number;
}
export interface LocationElementTable {
@@ -170,6 +183,7 @@ export interface LocationElementTable {
element_name: string;
original_name: string;
element_description: string | null;
last_update: number;
}
export interface LocationSubElementTable {
@@ -179,4 +193,5 @@ export interface LocationSubElementTable {
sub_elem_name: string;
original_name: string;
sub_elem_description: string | null;
last_update: number;
}

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
};
}