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:
14
lib/errors.ts
Normal file
14
lib/errors.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Clean error messages from Electron IPC prefix
|
||||
* Transforms: "Error invoking remote method 'channel': Error: Message"
|
||||
* Into: "Message"
|
||||
*/
|
||||
export function cleanErrorMessage(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
return error.message.replace(/^Error invoking remote method '[^']+': Error: /, '');
|
||||
}
|
||||
if (typeof error === 'string') {
|
||||
return error.replace(/^Error invoking remote method '[^']+': Error: /, '');
|
||||
}
|
||||
return 'An unknown error occurred';
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
365
lib/utils/syncComparison.ts
Normal file
365
lib/utils/syncComparison.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
import {
|
||||
SyncedBook,
|
||||
SyncedChapter,
|
||||
SyncedChapterContent,
|
||||
SyncedChapterInfo,
|
||||
SyncedCharacter,
|
||||
SyncedCharacterAttribute,
|
||||
SyncedLocation,
|
||||
SyncedLocationElement,
|
||||
SyncedLocationSubElement,
|
||||
SyncedWorld,
|
||||
SyncedWorldElement,
|
||||
SyncedIncident,
|
||||
SyncedPlotPoint,
|
||||
SyncedIssue,
|
||||
SyncedActSummary,
|
||||
SyncedGuideLine,
|
||||
SyncedAIGuideLine
|
||||
} from "@/lib/models/SyncedBook";
|
||||
|
||||
/**
|
||||
* Résultat de comparaison pour un livre
|
||||
*/
|
||||
export interface BookSyncDiff {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
subTitle: string | null;
|
||||
bookNeedsUpdate: boolean; // Le livre lui-même a changé
|
||||
lastUpdate: number;
|
||||
chapters: SyncedChapter[];
|
||||
characters: SyncedCharacter[];
|
||||
locations: SyncedLocation[];
|
||||
worlds: SyncedWorld[];
|
||||
incidents: SyncedIncident[];
|
||||
plotPoints: SyncedPlotPoint[];
|
||||
issues: SyncedIssue[];
|
||||
actSummaries: SyncedActSummary[];
|
||||
guideLine: SyncedGuideLine | null;
|
||||
aiGuideLine: SyncedAIGuideLine | null;
|
||||
hasAnyChanges: boolean; // Indique si des changements existent
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare en profondeur deux livres synchronisés et retourne les différences
|
||||
*/
|
||||
export function compareBooks(serverBook: SyncedBook, localBook: SyncedBook): BookSyncDiff {
|
||||
const bookNeedsUpdate = serverBook.lastUpdate > localBook.lastUpdate;
|
||||
|
||||
// Comparer les chapitres
|
||||
const chaptersWithChanges: SyncedChapter[] = [];
|
||||
for (const serverChapter of serverBook.chapters) {
|
||||
const localChapter = localBook.chapters.find(c => c.id === serverChapter.id);
|
||||
|
||||
if (!localChapter) {
|
||||
// Chapitre n'existe pas localement
|
||||
chaptersWithChanges.push(serverChapter);
|
||||
} else if (serverChapter.lastUpdate > localChapter.lastUpdate) {
|
||||
// Chapitre a changé
|
||||
chaptersWithChanges.push(serverChapter);
|
||||
} else {
|
||||
// Vérifier les contenus du chapitre
|
||||
const contentsWithChanges: SyncedChapterContent[] = [];
|
||||
for (const serverContent of serverChapter.contents) {
|
||||
const localContent = localChapter.contents.find(c => c.id === serverContent.id);
|
||||
if (!localContent || serverContent.lastUpdate > localContent.lastUpdate) {
|
||||
contentsWithChanges.push(serverContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier l'info du chapitre
|
||||
let infoNeedsUpdate = false;
|
||||
if (serverChapter.info && localChapter.info) {
|
||||
infoNeedsUpdate = serverChapter.info.lastUpdate > localChapter.info.lastUpdate;
|
||||
} else if (serverChapter.info && !localChapter.info) {
|
||||
infoNeedsUpdate = true;
|
||||
}
|
||||
|
||||
// Si des contenus ou info ont changé, inclure le chapitre
|
||||
if (contentsWithChanges.length > 0 || infoNeedsUpdate) {
|
||||
chaptersWithChanges.push({
|
||||
...serverChapter,
|
||||
contents: contentsWithChanges.length > 0 ? contentsWithChanges : serverChapter.contents,
|
||||
info: infoNeedsUpdate ? serverChapter.info : null
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les personnages
|
||||
const charactersWithChanges: SyncedCharacter[] = [];
|
||||
for (const serverChar of serverBook.characters) {
|
||||
const localChar = localBook.characters.find(c => c.id === serverChar.id);
|
||||
|
||||
if (!localChar) {
|
||||
charactersWithChanges.push(serverChar);
|
||||
} else if (serverChar.lastUpdate > localChar.lastUpdate) {
|
||||
charactersWithChanges.push(serverChar);
|
||||
} else {
|
||||
// Vérifier les attributs
|
||||
const attributesWithChanges: SyncedCharacterAttribute[] = [];
|
||||
for (const serverAttr of serverChar.attributes) {
|
||||
const localAttr = localChar.attributes.find(a => a.id === serverAttr.id);
|
||||
if (!localAttr || serverAttr.lastUpdate > localAttr.lastUpdate) {
|
||||
attributesWithChanges.push(serverAttr);
|
||||
}
|
||||
}
|
||||
|
||||
if (attributesWithChanges.length > 0) {
|
||||
charactersWithChanges.push({
|
||||
...serverChar,
|
||||
attributes: attributesWithChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les locations
|
||||
const locationsWithChanges: SyncedLocation[] = [];
|
||||
for (const serverLoc of serverBook.locations) {
|
||||
const localLoc = localBook.locations.find(l => l.id === serverLoc.id);
|
||||
|
||||
if (!localLoc) {
|
||||
locationsWithChanges.push(serverLoc);
|
||||
} else if (serverLoc.lastUpdate > localLoc.lastUpdate) {
|
||||
locationsWithChanges.push(serverLoc);
|
||||
} else {
|
||||
// Vérifier les éléments
|
||||
const elementsWithChanges: SyncedLocationElement[] = [];
|
||||
for (const serverElem of serverLoc.elements) {
|
||||
const localElem = localLoc.elements.find(e => e.id === serverElem.id);
|
||||
|
||||
if (!localElem) {
|
||||
elementsWithChanges.push(serverElem);
|
||||
} else if (serverElem.lastUpdate > localElem.lastUpdate) {
|
||||
elementsWithChanges.push(serverElem);
|
||||
} else {
|
||||
// Vérifier les sous-éléments
|
||||
const subElementsWithChanges: SyncedLocationSubElement[] = [];
|
||||
for (const serverSubElem of serverElem.subElements) {
|
||||
const localSubElem = localElem.subElements.find(s => s.id === serverSubElem.id);
|
||||
if (!localSubElem || serverSubElem.lastUpdate > localSubElem.lastUpdate) {
|
||||
subElementsWithChanges.push(serverSubElem);
|
||||
}
|
||||
}
|
||||
|
||||
if (subElementsWithChanges.length > 0) {
|
||||
elementsWithChanges.push({
|
||||
...serverElem,
|
||||
subElements: subElementsWithChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsWithChanges.length > 0) {
|
||||
locationsWithChanges.push({
|
||||
...serverLoc,
|
||||
elements: elementsWithChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les mondes
|
||||
const worldsWithChanges: SyncedWorld[] = [];
|
||||
for (const serverWorld of serverBook.worlds) {
|
||||
const localWorld = localBook.worlds.find(w => w.id === serverWorld.id);
|
||||
|
||||
if (!localWorld) {
|
||||
worldsWithChanges.push(serverWorld);
|
||||
} else if (serverWorld.lastUpdate > localWorld.lastUpdate) {
|
||||
worldsWithChanges.push(serverWorld);
|
||||
} else {
|
||||
// Vérifier les éléments du monde
|
||||
const elementsWithChanges: SyncedWorldElement[] = [];
|
||||
for (const serverElem of serverWorld.elements) {
|
||||
const localElem = localWorld.elements.find(e => e.id === serverElem.id);
|
||||
if (!localElem || serverElem.lastUpdate > localElem.lastUpdate) {
|
||||
elementsWithChanges.push(serverElem);
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsWithChanges.length > 0) {
|
||||
worldsWithChanges.push({
|
||||
...serverWorld,
|
||||
elements: elementsWithChanges
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les incidents
|
||||
const incidentsWithChanges: SyncedIncident[] = [];
|
||||
for (const serverIncident of serverBook.incidents) {
|
||||
const localIncident = localBook.incidents.find(i => i.id === serverIncident.id);
|
||||
if (!localIncident || serverIncident.lastUpdate > localIncident.lastUpdate) {
|
||||
incidentsWithChanges.push(serverIncident);
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les plot points
|
||||
const plotPointsWithChanges: SyncedPlotPoint[] = [];
|
||||
for (const serverPlot of serverBook.plotPoints) {
|
||||
const localPlot = localBook.plotPoints.find(p => p.id === serverPlot.id);
|
||||
if (!localPlot || serverPlot.lastUpdate > localPlot.lastUpdate) {
|
||||
plotPointsWithChanges.push(serverPlot);
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les issues
|
||||
const issuesWithChanges: SyncedIssue[] = [];
|
||||
for (const serverIssue of serverBook.issues) {
|
||||
const localIssue = localBook.issues.find(i => i.id === serverIssue.id);
|
||||
if (!localIssue || serverIssue.lastUpdate > localIssue.lastUpdate) {
|
||||
issuesWithChanges.push(serverIssue);
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer les act summaries
|
||||
const actSummariesWithChanges: SyncedActSummary[] = [];
|
||||
for (const serverAct of serverBook.actSummaries) {
|
||||
const localAct = localBook.actSummaries.find(a => a.id === serverAct.id);
|
||||
if (!localAct || serverAct.lastUpdate > localAct.lastUpdate) {
|
||||
actSummariesWithChanges.push(serverAct);
|
||||
}
|
||||
}
|
||||
|
||||
// Comparer guideline
|
||||
let guideLineNeedsUpdate: SyncedGuideLine | null = null;
|
||||
if (serverBook.guideLine && localBook.guideLine) {
|
||||
if (serverBook.guideLine.lastUpdate > localBook.guideLine.lastUpdate) {
|
||||
guideLineNeedsUpdate = serverBook.guideLine;
|
||||
}
|
||||
} else if (serverBook.guideLine && !localBook.guideLine) {
|
||||
guideLineNeedsUpdate = serverBook.guideLine;
|
||||
}
|
||||
|
||||
// Comparer AI guideline
|
||||
let aiGuideLineNeedsUpdate: SyncedAIGuideLine | null = null;
|
||||
if (serverBook.aiGuideLine && localBook.aiGuideLine) {
|
||||
if (serverBook.aiGuideLine.lastUpdate > localBook.aiGuideLine.lastUpdate) {
|
||||
aiGuideLineNeedsUpdate = serverBook.aiGuideLine;
|
||||
}
|
||||
} else if (serverBook.aiGuideLine && !localBook.aiGuideLine) {
|
||||
aiGuideLineNeedsUpdate = serverBook.aiGuideLine;
|
||||
}
|
||||
|
||||
// Déterminer s'il y a des changements
|
||||
const hasAnyChanges = bookNeedsUpdate ||
|
||||
chaptersWithChanges.length > 0 ||
|
||||
charactersWithChanges.length > 0 ||
|
||||
locationsWithChanges.length > 0 ||
|
||||
worldsWithChanges.length > 0 ||
|
||||
incidentsWithChanges.length > 0 ||
|
||||
plotPointsWithChanges.length > 0 ||
|
||||
issuesWithChanges.length > 0 ||
|
||||
actSummariesWithChanges.length > 0 ||
|
||||
guideLineNeedsUpdate !== null ||
|
||||
aiGuideLineNeedsUpdate !== null;
|
||||
|
||||
return {
|
||||
id: serverBook.id,
|
||||
type: serverBook.type,
|
||||
title: serverBook.title,
|
||||
subTitle: serverBook.subTitle,
|
||||
bookNeedsUpdate,
|
||||
lastUpdate: serverBook.lastUpdate,
|
||||
chapters: chaptersWithChanges,
|
||||
characters: charactersWithChanges,
|
||||
locations: locationsWithChanges,
|
||||
worlds: worldsWithChanges,
|
||||
incidents: incidentsWithChanges,
|
||||
plotPoints: plotPointsWithChanges,
|
||||
issues: issuesWithChanges,
|
||||
actSummaries: actSummariesWithChanges,
|
||||
guideLine: guideLineNeedsUpdate,
|
||||
aiGuideLine: aiGuideLineNeedsUpdate,
|
||||
hasAnyChanges
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare tous les livres serveur vs locaux et retourne ceux qui ont des changements
|
||||
*/
|
||||
export function getBooksToSyncFromServer(serverBooks: SyncedBook[], localBooks: SyncedBook[]): BookSyncDiff[] {
|
||||
const booksWithChanges: BookSyncDiff[] = [];
|
||||
|
||||
for (const serverBook of serverBooks) {
|
||||
const localBook = localBooks.find(b => b.id === serverBook.id);
|
||||
|
||||
if (!localBook) {
|
||||
// Livre n'existe pas localement - tout le livre doit être synchronisé
|
||||
booksWithChanges.push({
|
||||
id: serverBook.id,
|
||||
type: serverBook.type,
|
||||
title: serverBook.title,
|
||||
subTitle: serverBook.subTitle,
|
||||
bookNeedsUpdate: true,
|
||||
lastUpdate: serverBook.lastUpdate,
|
||||
chapters: serverBook.chapters,
|
||||
characters: serverBook.characters,
|
||||
locations: serverBook.locations,
|
||||
worlds: serverBook.worlds,
|
||||
incidents: serverBook.incidents,
|
||||
plotPoints: serverBook.plotPoints,
|
||||
issues: serverBook.issues,
|
||||
actSummaries: serverBook.actSummaries,
|
||||
guideLine: serverBook.guideLine,
|
||||
aiGuideLine: serverBook.aiGuideLine,
|
||||
hasAnyChanges: true
|
||||
});
|
||||
} else {
|
||||
// Comparer en profondeur
|
||||
const diff = compareBooks(serverBook, localBook);
|
||||
if (diff.hasAnyChanges) {
|
||||
booksWithChanges.push(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return booksWithChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare tous les livres locaux vs serveur et retourne ceux qui ont des changements locaux
|
||||
*/
|
||||
export function getBooksToSyncToServer(localBooks: SyncedBook[], serverBooks: SyncedBook[]): BookSyncDiff[] {
|
||||
const booksWithChanges: BookSyncDiff[] = [];
|
||||
|
||||
for (const localBook of localBooks) {
|
||||
const serverBook = serverBooks.find(b => b.id === localBook.id);
|
||||
|
||||
if (!serverBook) {
|
||||
// Livre n'existe pas sur le serveur - tout le livre doit être envoyé
|
||||
booksWithChanges.push({
|
||||
id: localBook.id,
|
||||
type: localBook.type,
|
||||
title: localBook.title,
|
||||
subTitle: localBook.subTitle,
|
||||
bookNeedsUpdate: true,
|
||||
lastUpdate: localBook.lastUpdate,
|
||||
chapters: localBook.chapters,
|
||||
characters: localBook.characters,
|
||||
locations: localBook.locations,
|
||||
worlds: localBook.worlds,
|
||||
incidents: localBook.incidents,
|
||||
plotPoints: localBook.plotPoints,
|
||||
issues: localBook.issues,
|
||||
actSummaries: localBook.actSummaries,
|
||||
guideLine: localBook.guideLine,
|
||||
aiGuideLine: localBook.aiGuideLine,
|
||||
hasAnyChanges: true
|
||||
});
|
||||
} else {
|
||||
// Comparer en profondeur (local vs server)
|
||||
const diff = compareBooks(localBook, serverBook);
|
||||
if (diff.hasAnyChanges) {
|
||||
booksWithChanges.push(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return booksWithChanges;
|
||||
}
|
||||
Reference in New Issue
Block a user