Add BooksSyncContext, refine database schema, and enhance synchronization support

- Introduce `BooksSyncContext` for managing book synchronization states (server-only, local-only, to-sync, etc.).
- Remove `UserContext` and related dependencies.
- Refine localization strings (`en.json`) with sync-related updates (e.g., "toSyncFromServer", "toSyncToServer").
- Extend database schema with additional tables and fields for syncing books, chapters, and related entities.
- Add `last_update` fields and update corresponding repository methods to support synchronization logic.
- Enhance IPC handlers with stricter typing, data validation, and sync-aware operations.
This commit is contained in:
natreex
2025-12-07 14:36:03 -05:00
parent db2c88a42d
commit bb331b5c22
22 changed files with 2594 additions and 370 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -78,7 +78,7 @@ export default class ChapterRepo{
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order) VALUES (?,?,?,?,?,?,?)', [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder]);
result = db.run('INSERT INTO book_chapters (chapter_id, author_id, book_id, title, hashed_title, words_count, chapter_order, last_update) VALUES (?,?,?,?,?,?,?,?)', [chapterId, userId, bookId, title, hashedTitle, wordsCount, chapterOrder, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -88,11 +88,10 @@ export default class ChapterRepo{
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return chapterId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est passé lors de l'ajout du chapitre.` : `Error adding chapter.`);
}
return chapterId;
}
public static fetchWholeChapter(userId: string, chapterId: string, version: number, lang: 'fr' | 'en' = 'fr'): ChapterContentQueryResult {
@@ -197,7 +196,7 @@ export default class ChapterRepo{
}
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, book_id, author_id, incident_id, plot_point_id, summary, goal) VALUES (?,?,?,?,?,?,?,?,?)', [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '']);
result = db.run('INSERT INTO book_chapter_infos (chapter_info_id, chapter_id, act_id, book_id, author_id, incident_id, plot_point_id, summary, goal, last_update) VALUES (?,?,?,?,?,?,?,?,?,?)', [chapterInfoId, chapterId, actId, bookId, userId, incidentId, plotId, '', '', System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -207,17 +206,16 @@ export default class ChapterRepo{
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return chapterInfoId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite pendant la liaison du chapitre.` : `Error linking chapter.`);
}
return chapterInfoId;
}
public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, userId, chapterId]);
const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, System.timeStampInSeconds(), userId, chapterId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
@@ -270,12 +268,12 @@ export default class ChapterRepo{
public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, chapterId, userId, version]);
const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, System.timeStampInSeconds(), chapterId, userId, version]);
if (result.changes > 0) {
return true;
} else {
const contentId:string = System.createUniqueId();
const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count) VALUES (?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount]);
const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount, System.timeStampInSeconds()]);
return insertResult.changes > 0;
}
} catch (e: unknown) {

View File

@@ -54,7 +54,7 @@ export default class CharacterRepo {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history) VALUES (?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory]);
result = db.run('INSERT INTO `book_characters` (character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', [characterId, bookId, userId, encryptedName, encryptedLastName, encryptedCategory, encryptedTitle, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -64,18 +64,17 @@ export default class CharacterRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return characterId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du personnage.` : `Error adding character.`);
}
return characterId;
}
static insertAttribute(attributeId: string, characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value) VALUES (?,?,?,?,?)', [attributeId, characterId, userId, type, name]);
result = db.run('INSERT INTO `book_characters_attributes` (attr_id, character_id, user_id, attribute_name, attribute_value, last_update) VALUES (?,?,?,?,?,?)', [attributeId, characterId, userId, type, name, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -85,17 +84,16 @@ export default class CharacterRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return attributeId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'attribut.` : `Error adding attribute.`);
}
return attributeId;
}
static updateCharacter(userId: string, id: number | null, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, id, userId]);
const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds(), id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {

View File

@@ -51,7 +51,7 @@ export default class LocationRepo {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name) VALUES (?, ?, ?, ?, ?)', [locationId, bookId, userId, encryptedName, originalName]);
result = db.run('INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?, ?, ?, ?, ?, ?)', [locationId, bookId, userId, encryptedName, originalName, System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -61,18 +61,17 @@ export default class LocationRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return locationId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de la section d'emplacement.` : `Error adding location section.`);
}
return locationId;
}
static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description) VALUES (?,?,?,?,?,?)', [elementId, locationId, userId, encryptedName, originalName, '']);
result = db.run('INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?,?,?,?,?,?,?)', [elementId, locationId, userId, encryptedName, originalName, '', System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -82,18 +81,17 @@ export default class LocationRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return elementId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'élément d'emplacement.` : `Error adding location element.`);
}
return elementId;
}
static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let result: RunResult;
try {
const db: Database = System.getDb();
result = db.run('INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description) VALUES (?,?,?,?,?,?)', [subElementId, elementId, userId, encryptedName, originalName, '']);
result = db.run('INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?,?,?,?,?,?,?)', [subElementId, elementId, userId, encryptedName, originalName, '', System.timeStampInSeconds()]);
} catch (e: unknown) {
if (e instanceof Error) {
console.error(`DB Error: ${e.message}`);
@@ -103,17 +101,16 @@ export default class LocationRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (result.changes > 0) {
return subElementId;
} else {
if (!result || result.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du sous-élément d'emplacement.` : `Error adding location sub-element.`);
}
return subElementId;
}
static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, id, userId]);
const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=?, last_update=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, System.timeStampInSeconds(), id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
@@ -129,7 +126,7 @@ export default class LocationRepo {
static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, id, userId]);
const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, last_update=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, System.timeStampInSeconds(), id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {
@@ -145,7 +142,7 @@ export default class LocationRepo {
static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, id, userId]);
const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, last_update=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, System.timeStampInSeconds(), id, userId]);
return result.changes > 0;
} catch (e: unknown) {
if (e instanceof Error) {