Add character deletion functionality with confirmation workflow
- Added `handleDeleteCharacter` method to handle character deletion with confirmation prompts. - Updated `CharacterComponent` and `CharacterDetail` to include delete button and related logic. - Localized new strings for character deletion (e.g., confirmation prompts, success/error messages). - Enhanced database repository methods (`deleteCharacter`) to handle character deletion securely. - Improved synchronization workflows to accommodate character deletion.
This commit is contained in:
@@ -22,6 +22,9 @@ import {
|
||||
} from "../repositories/location.repository.js";
|
||||
import { BookPlotPointsTable } from "../repositories/plotpoint.repository.js";
|
||||
import { BookWorldElementsTable, BookWorldTable } from "../repositories/world.repository.js";
|
||||
import { BookSpellsTable } from "../repositories/spell.repo.js";
|
||||
import { BookSpellTagsTable } from "../repositories/spelltag.repo.js";
|
||||
import { SyncedSpell, SyncedSpellTag } from "./Spell.js";
|
||||
import { CompleteChapterContent, SyncedChapter } from "./Chapter.js";
|
||||
import { SyncedCharacter } from "./Character.js";
|
||||
import { SyncedLocation } from "./Location.js";
|
||||
@@ -42,6 +45,7 @@ export interface BookToolsSettings {
|
||||
characters: boolean;
|
||||
worlds: boolean;
|
||||
locations: boolean;
|
||||
spells: boolean;
|
||||
}
|
||||
|
||||
export interface BookProps {
|
||||
@@ -79,6 +83,8 @@ export interface CompleteBook {
|
||||
locationElements: LocationElementTable[];
|
||||
locationSubElements: LocationSubElementTable[];
|
||||
bookTools: BookToolsTable[];
|
||||
spells: BookSpellsTable[];
|
||||
spellTags: BookSpellTagsTable[];
|
||||
}
|
||||
|
||||
export interface SyncedBook {
|
||||
@@ -98,6 +104,8 @@ export interface SyncedBook {
|
||||
guideLine: SyncedGuideLine | null;
|
||||
aiGuideLine: SyncedAIGuideLine | null;
|
||||
bookTools: SyncedBookTools | null;
|
||||
spells: SyncedSpell[];
|
||||
spellTags: SyncedSpellTag[];
|
||||
}
|
||||
|
||||
export interface BookSyncCompare {
|
||||
@@ -119,6 +127,8 @@ export interface BookSyncCompare {
|
||||
guideLine: boolean;
|
||||
aiGuideLine: boolean;
|
||||
bookTools: boolean;
|
||||
spells: string[];
|
||||
spellTags: string[];
|
||||
}
|
||||
|
||||
export interface CompleteBookData {
|
||||
@@ -272,7 +282,8 @@ export default class Book {
|
||||
tools: {
|
||||
characters: bookTools ? bookTools.characters_enabled === 1 : false,
|
||||
worlds: bookTools ? bookTools.worlds_enabled === 1 : false,
|
||||
locations: bookTools ? bookTools.locations_enabled === 1 : false
|
||||
locations: bookTools ? bookTools.locations_enabled === 1 : false,
|
||||
spells: bookTools ? bookTools.spells_enabled === 1 : false
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -310,8 +321,8 @@ export default class Book {
|
||||
return BookRepo.deleteBook(userId, bookId, lang);
|
||||
}
|
||||
|
||||
public static updateBookToolSetting(userId: string, bookId: string, toolName: 'characters' | 'worlds' | 'locations', enabled: boolean, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||
const columnName: 'characters_enabled' | 'worlds_enabled' | 'locations_enabled' = `${toolName}_enabled` as 'characters_enabled' | 'worlds_enabled' | 'locations_enabled';
|
||||
public static updateBookToolSetting(userId: string, bookId: string, toolName: 'characters' | 'worlds' | 'locations' | 'spells', enabled: boolean, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||
const columnName: 'characters_enabled' | 'worlds_enabled' | 'locations_enabled' | 'spells_enabled' = `${toolName}_enabled` as 'characters_enabled' | 'worlds_enabled' | 'locations_enabled' | 'spells_enabled';
|
||||
return BookRepo.updateBookToolSetting(userId, bookId, columnName, enabled, System.timeStampInSeconds(), lang);
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,17 @@ export default class Character {
|
||||
return CharacterRepo.deleteAttribute(userId, attributeId, lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a character and all its related data.
|
||||
* @param userId - The unique identifier of the user
|
||||
* @param characterId - The unique identifier of the character to delete
|
||||
* @param lang - The language code for localization (defaults to 'fr')
|
||||
* @returns True if the deletion was successful
|
||||
*/
|
||||
static deleteCharacter(userId: string, characterId: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||
return CharacterRepo.deleteCharacter(userId, characterId, lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all attributes for a specific character, grouped by type.
|
||||
* Decrypts attribute data using the user's encryption key.
|
||||
|
||||
@@ -28,6 +28,8 @@ import GuidelineRepo, {
|
||||
BookGuideLineTable
|
||||
} from "../repositories/guideline.repository.js";
|
||||
import IssueRepository, {BookIssuesTable} from "../repositories/issue.repository.js";
|
||||
import SpellRepo, {BookSpellsTable} from "../repositories/spell.repo.js";
|
||||
import SpellTagRepo, {BookSpellTagsTable} from "../repositories/spelltag.repo.js";
|
||||
|
||||
export default class Download {
|
||||
/**
|
||||
@@ -198,8 +200,54 @@ export default class Download {
|
||||
});
|
||||
if (!issuesInserted) return false;
|
||||
|
||||
return data.bookTools.every((bookTool: BookToolsTable): boolean => {
|
||||
return BookRepo.insertSyncBookTools(bookTool.book_id, userId, bookTool.characters_enabled, bookTool.worlds_enabled, bookTool.locations_enabled, bookTool.last_update, lang);
|
||||
const bookToolsInserted: boolean = data.bookTools.every((bookTool: BookToolsTable): boolean => {
|
||||
return BookRepo.insertSyncBookTools(bookTool.book_id, userId, bookTool.characters_enabled, bookTool.worlds_enabled, bookTool.locations_enabled, bookTool.spells_enabled, bookTool.last_update, lang);
|
||||
});
|
||||
if (!bookToolsInserted) return false;
|
||||
|
||||
const spellTagsInserted: boolean = data.spellTags.every((spellTag: BookSpellTagsTable): boolean => {
|
||||
const encryptedTagName: string = System.encryptDataWithUserKey(spellTag.name, userEncryptionKey);
|
||||
return SpellTagRepo.insertSyncSpellTag(
|
||||
spellTag.tag_id,
|
||||
spellTag.book_id,
|
||||
userId,
|
||||
encryptedTagName,
|
||||
spellTag.name_hash,
|
||||
spellTag.color,
|
||||
spellTag.last_update,
|
||||
lang
|
||||
);
|
||||
});
|
||||
if (!spellTagsInserted) return false;
|
||||
|
||||
const spellsInserted: boolean = data.spells.every((spell: BookSpellsTable): boolean => {
|
||||
const encryptedName: string = System.encryptDataWithUserKey(spell.name, userEncryptionKey);
|
||||
const encryptedDescription: string = System.encryptDataWithUserKey(spell.description, userEncryptionKey);
|
||||
const encryptedAppearance: string = System.encryptDataWithUserKey(spell.appearance, userEncryptionKey);
|
||||
const encryptedTags: string = System.encryptDataWithUserKey(spell.tags, userEncryptionKey);
|
||||
const encryptedPowerLevel: string | null = spell.power_level ? System.encryptDataWithUserKey(spell.power_level, userEncryptionKey) : null;
|
||||
const encryptedComponents: string | null = spell.components ? System.encryptDataWithUserKey(spell.components, userEncryptionKey) : null;
|
||||
const encryptedLimitations: string | null = spell.limitations ? System.encryptDataWithUserKey(spell.limitations, userEncryptionKey) : null;
|
||||
const encryptedNotes: string | null = spell.notes ? System.encryptDataWithUserKey(spell.notes, userEncryptionKey) : null;
|
||||
return SpellRepo.insertSyncSpell(
|
||||
spell.spell_id,
|
||||
spell.book_id,
|
||||
userId,
|
||||
encryptedName,
|
||||
spell.name_hash,
|
||||
encryptedDescription,
|
||||
encryptedAppearance,
|
||||
encryptedTags,
|
||||
encryptedPowerLevel,
|
||||
encryptedComponents,
|
||||
encryptedLimitations,
|
||||
encryptedNotes,
|
||||
spell.last_update,
|
||||
lang
|
||||
);
|
||||
});
|
||||
if (!spellsInserted) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { getUserEncryptionKey } from "../keyManager.js";
|
||||
import System from "../System.js";
|
||||
import { BookSyncCompare, CompleteBook, SyncedBook, SyncedBookTools } from "./Book.js";
|
||||
import { SyncedSpell, SyncedSpellTag } from "./Spell.js";
|
||||
import SpellRepo, { BookSpellsTable, SyncedSpellResult } from "../repositories/spell.repo.js";
|
||||
import SpellTagRepo, { BookSpellTagsTable, SyncedSpellTagResult } from "../repositories/spelltag.repo.js";
|
||||
import BookRepo, { EritBooksTable, SyncedBookResult, BookToolsTable, SyncedBookToolsResult } from "../repositories/book.repository.js";
|
||||
import ChapterRepo, {
|
||||
BookChapterInfosTable,
|
||||
@@ -87,6 +90,8 @@ export default class Sync {
|
||||
const decryptedGuideLines: BookGuideLineTable[] = [];
|
||||
const decryptedAIGuideLines: BookAIGuideLineTable[] = [];
|
||||
const decryptedIssues: BookIssuesTable[] = [];
|
||||
const decryptedSpells: BookSpellsTable[] = [];
|
||||
const decryptedSpellTags: BookSpellTagsTable[] = [];
|
||||
|
||||
const actSummaryIds: string[] = syncCompareData.actSummaries;
|
||||
const chapterIds: string[] = syncCompareData.chapters;
|
||||
@@ -102,6 +107,8 @@ export default class Sync {
|
||||
const worldIds: string[] = syncCompareData.worlds;
|
||||
const worldElementIds: string[] = syncCompareData.worldElements;
|
||||
const issueIds: string[] = syncCompareData.issues;
|
||||
const spellIds: string[] = syncCompareData.spells;
|
||||
const spellTagIds: string[] = syncCompareData.spellTags;
|
||||
|
||||
if (actSummaryIds && actSummaryIds.length > 0) {
|
||||
for (const actSummaryId of actSummaryIds) {
|
||||
@@ -338,6 +345,37 @@ export default class Sync {
|
||||
}
|
||||
}
|
||||
|
||||
if (spellTagIds && spellTagIds.length > 0) {
|
||||
for (const spellTagId of spellTagIds) {
|
||||
const spellTagRecord: BookSpellTagsTable | null = SpellTagRepo.fetchSpellTagTableById(userId, spellTagId, lang);
|
||||
if (spellTagRecord) {
|
||||
decryptedSpellTags.push({
|
||||
...spellTagRecord,
|
||||
name: System.decryptDataWithUserKey(spellTagRecord.name, userEncryptionKey)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spellIds && spellIds.length > 0) {
|
||||
for (const spellId of spellIds) {
|
||||
const spellRecord: BookSpellsTable | null = SpellRepo.fetchSpellTableById(userId, spellId, lang);
|
||||
if (spellRecord) {
|
||||
decryptedSpells.push({
|
||||
...spellRecord,
|
||||
name: System.decryptDataWithUserKey(spellRecord.name, userEncryptionKey),
|
||||
description: System.decryptDataWithUserKey(spellRecord.description, userEncryptionKey),
|
||||
appearance: System.decryptDataWithUserKey(spellRecord.appearance, userEncryptionKey),
|
||||
tags: System.decryptDataWithUserKey(spellRecord.tags, userEncryptionKey),
|
||||
power_level: spellRecord.power_level ? System.decryptDataWithUserKey(spellRecord.power_level, userEncryptionKey) : null,
|
||||
components: spellRecord.components ? System.decryptDataWithUserKey(spellRecord.components, userEncryptionKey) : null,
|
||||
limitations: spellRecord.limitations ? System.decryptDataWithUserKey(spellRecord.limitations, userEncryptionKey) : null,
|
||||
notes: spellRecord.notes ? System.decryptDataWithUserKey(spellRecord.notes, userEncryptionKey) : null
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bookResults: EritBooksTable[] = await BookRepo.fetchCompleteBookById(syncCompareData.id, lang);
|
||||
if (bookResults.length > 0) {
|
||||
const bookRecord: EritBooksTable = bookResults[0];
|
||||
@@ -371,7 +409,9 @@ export default class Sync {
|
||||
guideLine: decryptedGuideLines,
|
||||
aiGuideLine: decryptedAIGuideLines,
|
||||
issues: decryptedIssues,
|
||||
bookTools: bookTools
|
||||
bookTools: bookTools,
|
||||
spells: decryptedSpells,
|
||||
spellTags: decryptedSpellTags
|
||||
};
|
||||
}
|
||||
|
||||
@@ -730,13 +770,56 @@ export default class Sync {
|
||||
|
||||
if (completeBook.bookTools && completeBook.bookTools.length > 0) {
|
||||
for (const serverBookTool of completeBook.bookTools) {
|
||||
const success: boolean = BookRepo.insertSyncBookTools(serverBookTool.book_id, userId, serverBookTool.characters_enabled, serverBookTool.worlds_enabled, serverBookTool.locations_enabled, serverBookTool.last_update, lang);
|
||||
const success: boolean = BookRepo.insertSyncBookTools(bookId, userId, serverBookTool.characters_enabled, serverBookTool.worlds_enabled, serverBookTool.locations_enabled, serverBookTool.spells_enabled, serverBookTool.last_update, lang);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completeBook.spellTags && completeBook.spellTags.length > 0) {
|
||||
for (const serverSpellTag of completeBook.spellTags) {
|
||||
const spellTagExists: boolean = SpellTagRepo.isSpellTagExist(userId, serverSpellTag.tag_id, lang);
|
||||
const encryptedName: string = System.encryptDataWithUserKey(serverSpellTag.name, userEncryptionKey);
|
||||
if (spellTagExists) {
|
||||
const updateSuccessful: boolean = SpellTagRepo.updateSyncSpellTag(userId, serverSpellTag.tag_id, encryptedName, serverSpellTag.name_hash, serverSpellTag.color, serverSpellTag.last_update, lang);
|
||||
if (!updateSuccessful) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const insertSuccessful: boolean = SpellTagRepo.insertSyncSpellTag(serverSpellTag.tag_id, serverSpellTag.book_id, userId, encryptedName, serverSpellTag.name_hash, serverSpellTag.color, serverSpellTag.last_update, lang);
|
||||
if (!insertSuccessful) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completeBook.spells && completeBook.spells.length > 0) {
|
||||
for (const serverSpell of completeBook.spells) {
|
||||
const spellExists: boolean = SpellRepo.isSpellExist(userId, serverSpell.spell_id, lang);
|
||||
const encryptedName: string = System.encryptDataWithUserKey(serverSpell.name, userEncryptionKey);
|
||||
const encryptedDescription: string = System.encryptDataWithUserKey(serverSpell.description, userEncryptionKey);
|
||||
const encryptedAppearance: string = System.encryptDataWithUserKey(serverSpell.appearance, userEncryptionKey);
|
||||
const encryptedTags: string = System.encryptDataWithUserKey(serverSpell.tags, userEncryptionKey);
|
||||
const encryptedPowerLevel: string | null = serverSpell.power_level ? System.encryptDataWithUserKey(serverSpell.power_level, userEncryptionKey) : null;
|
||||
const encryptedComponents: string | null = serverSpell.components ? System.encryptDataWithUserKey(serverSpell.components, userEncryptionKey) : null;
|
||||
const encryptedLimitations: string | null = serverSpell.limitations ? System.encryptDataWithUserKey(serverSpell.limitations, userEncryptionKey) : null;
|
||||
const encryptedNotes: string | null = serverSpell.notes ? System.encryptDataWithUserKey(serverSpell.notes, userEncryptionKey) : null;
|
||||
if (spellExists) {
|
||||
const updateSuccessful: boolean = SpellRepo.updateSyncSpell(userId, serverSpell.spell_id, encryptedName, serverSpell.name_hash, encryptedDescription, encryptedAppearance, encryptedTags, encryptedPowerLevel, encryptedComponents, encryptedLimitations, encryptedNotes, serverSpell.last_update, lang);
|
||||
if (!updateSuccessful) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const insertSuccessful: boolean = SpellRepo.insertSyncSpell(serverSpell.spell_id, serverSpell.book_id, userId, encryptedName, serverSpell.name_hash, encryptedDescription, encryptedAppearance, encryptedTags, encryptedPowerLevel, encryptedComponents, encryptedLimitations, encryptedNotes, serverSpell.last_update, lang);
|
||||
if (!insertSuccessful) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -767,7 +850,9 @@ export default class Sync {
|
||||
allIssues,
|
||||
allActSummaries,
|
||||
allGuidelines,
|
||||
allAIGuidelines
|
||||
allAIGuidelines,
|
||||
allSpells,
|
||||
allSpellTags
|
||||
]: [
|
||||
SyncedBookResult[],
|
||||
SyncedChapterResult[],
|
||||
@@ -785,7 +870,9 @@ export default class Sync {
|
||||
SyncedIssueResult[],
|
||||
SyncedActSummaryResult[],
|
||||
SyncedGuideLineResult[],
|
||||
SyncedAIGuideLineResult[]
|
||||
SyncedAIGuideLineResult[],
|
||||
SyncedSpellResult[],
|
||||
SyncedSpellTagResult[]
|
||||
] = await Promise.all([
|
||||
BookRepo.fetchSyncedBooks(userId, lang),
|
||||
ChapterRepo.fetchSyncedChapters(userId, lang),
|
||||
@@ -803,7 +890,9 @@ export default class Sync {
|
||||
IssueRepository.fetchSyncedIssues(userId, lang),
|
||||
ActRepository.fetchSyncedActSummaries(userId, lang),
|
||||
GuidelineRepo.fetchSyncedGuideLine(userId, lang),
|
||||
GuidelineRepo.fetchSyncedAIGuideLine(userId, lang)
|
||||
GuidelineRepo.fetchSyncedAIGuideLine(userId, lang),
|
||||
SpellRepo.fetchSyncedSpells(userId, lang),
|
||||
SpellTagRepo.fetchSyncedSpellTags(userId, lang)
|
||||
]);
|
||||
|
||||
return allBooks.map((bookRecord: SyncedBookResult): SyncedBook => {
|
||||
@@ -958,6 +1047,22 @@ export default class Sync {
|
||||
lastUpdate: bookToolsQuery.last_update
|
||||
} : null;
|
||||
|
||||
const bookSpells: SyncedSpell[] = allSpells
|
||||
.filter((spellRecord: SyncedSpellResult): boolean => spellRecord.book_id === currentBookId)
|
||||
.map((spellRecord: SyncedSpellResult): SyncedSpell => ({
|
||||
id: spellRecord.spell_id,
|
||||
name: System.decryptDataWithUserKey(spellRecord.name, userEncryptionKey),
|
||||
lastUpdate: spellRecord.last_update
|
||||
}));
|
||||
|
||||
const bookSpellTags: SyncedSpellTag[] = allSpellTags
|
||||
.filter((spellTagRecord: SyncedSpellTagResult): boolean => spellTagRecord.book_id === currentBookId)
|
||||
.map((spellTagRecord: SyncedSpellTagResult): SyncedSpellTag => ({
|
||||
id: spellTagRecord.tag_id,
|
||||
name: System.decryptDataWithUserKey(spellTagRecord.name, userEncryptionKey),
|
||||
lastUpdate: spellTagRecord.last_update
|
||||
}));
|
||||
|
||||
return {
|
||||
id: currentBookId,
|
||||
type: bookRecord.type,
|
||||
@@ -974,7 +1079,9 @@ export default class Sync {
|
||||
actSummaries: bookActSummaries,
|
||||
guideLine: bookGuideLine,
|
||||
aiGuideLine: bookAIGuideLine,
|
||||
bookTools: bookTools
|
||||
bookTools: bookTools,
|
||||
spells: bookSpells,
|
||||
spellTags: bookSpellTags
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import WorldRepository, {
|
||||
BookWorldTable
|
||||
} from "../repositories/world.repository.js";
|
||||
import ChapterContentRepository, { BookChapterContentTable } from "../repositories/chaptercontent.repository.js";
|
||||
import SpellRepo, { BookSpellsTable } from "../repositories/spell.repo.js";
|
||||
import SpellTagRepo, { BookSpellTagsTable } from "../repositories/spelltag.repo.js";
|
||||
|
||||
export default class Upload {
|
||||
/**
|
||||
@@ -52,7 +54,9 @@ export default class Upload {
|
||||
encryptedLocations,
|
||||
encryptedPlotPoints,
|
||||
encryptedWorlds,
|
||||
bookToolsData
|
||||
bookToolsData,
|
||||
encryptedSpells,
|
||||
encryptedSpellTags
|
||||
]: [
|
||||
EritBooksTable[],
|
||||
BookActSummariesTable[],
|
||||
@@ -65,7 +69,9 @@ export default class Upload {
|
||||
BookLocationTable[],
|
||||
BookPlotPointsTable[],
|
||||
BookWorldTable[],
|
||||
BookToolsTable | null
|
||||
BookToolsTable | null,
|
||||
BookSpellsTable[],
|
||||
BookSpellTagsTable[]
|
||||
] = await Promise.all([
|
||||
BookRepo.fetchEritBooksTable(userId, bookId, lang),
|
||||
ActRepository.fetchBookActSummaries(userId, bookId, lang),
|
||||
@@ -78,7 +84,9 @@ export default class Upload {
|
||||
LocationRepo.fetchBookLocations(userId, bookId, lang),
|
||||
PlotPointRepository.fetchBookPlotPoints(userId, bookId, lang),
|
||||
WorldRepository.fetchBookWorlds(userId, bookId, lang),
|
||||
BookRepo.fetchBookTools(userId, bookId, lang)
|
||||
BookRepo.fetchBookTools(userId, bookId, lang),
|
||||
SpellRepo.fetchBookSpellsTable(userId, bookId, lang),
|
||||
SpellTagRepo.fetchBookSpellTagsTable(userId, bookId, lang)
|
||||
]);
|
||||
|
||||
const [
|
||||
@@ -239,6 +247,23 @@ export default class Upload {
|
||||
|
||||
const bookTools: BookToolsTable[] = bookToolsData ? [bookToolsData] : [];
|
||||
|
||||
const spells: BookSpellsTable[] = encryptedSpells.map((spell: BookSpellsTable): BookSpellsTable => ({
|
||||
...spell,
|
||||
name: System.decryptDataWithUserKey(spell.name, userEncryptionKey),
|
||||
description: System.decryptDataWithUserKey(spell.description, userEncryptionKey),
|
||||
appearance: System.decryptDataWithUserKey(spell.appearance, userEncryptionKey),
|
||||
tags: System.decryptDataWithUserKey(spell.tags, userEncryptionKey),
|
||||
power_level: spell.power_level ? System.decryptDataWithUserKey(spell.power_level, userEncryptionKey) : null,
|
||||
components: spell.components ? System.decryptDataWithUserKey(spell.components, userEncryptionKey) : null,
|
||||
limitations: spell.limitations ? System.decryptDataWithUserKey(spell.limitations, userEncryptionKey) : null,
|
||||
notes: spell.notes ? System.decryptDataWithUserKey(spell.notes, userEncryptionKey) : null
|
||||
}));
|
||||
|
||||
const spellTags: BookSpellTagsTable[] = encryptedSpellTags.map((spellTag: BookSpellTagsTable): BookSpellTagsTable => ({
|
||||
...spellTag,
|
||||
name: System.decryptDataWithUserKey(spellTag.name, userEncryptionKey)
|
||||
}));
|
||||
|
||||
return {
|
||||
eritBooks,
|
||||
actSummaries,
|
||||
@@ -257,7 +282,9 @@ export default class Upload {
|
||||
worldElements,
|
||||
locationElements,
|
||||
locationSubElements,
|
||||
bookTools
|
||||
bookTools,
|
||||
spells,
|
||||
spellTags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface BookToolsTable extends Record<string, SQLiteValue> {
|
||||
characters_enabled: number;
|
||||
worlds_enabled: number;
|
||||
locations_enabled: number;
|
||||
spells_enabled: number;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
@@ -379,7 +380,7 @@ export default class BookRepo {
|
||||
static fetchBookTools(userId: string, bookId: string, lang: 'fr' | 'en'): BookToolsTable | null {
|
||||
try {
|
||||
const db: Database = System.getDb();
|
||||
const query: string = 'SELECT book_id, user_id, characters_enabled, worlds_enabled, locations_enabled, last_update FROM book_tools WHERE user_id=? AND book_id=?';
|
||||
const query: string = 'SELECT book_id, user_id, characters_enabled, worlds_enabled, locations_enabled, spells_enabled, last_update FROM book_tools WHERE user_id=? AND book_id=?';
|
||||
const params: SQLiteValue[] = [userId, bookId];
|
||||
const result = db.get(query, params) as BookToolsTable | undefined;
|
||||
return result ?? null;
|
||||
@@ -392,7 +393,7 @@ export default class BookRepo {
|
||||
}
|
||||
}
|
||||
|
||||
static updateBookToolSetting(userId: string, bookId: string, toolName: 'characters_enabled' | 'worlds_enabled' | 'locations_enabled', enabled: boolean, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||
static updateBookToolSetting(userId: string, bookId: string, toolName: 'characters_enabled' | 'worlds_enabled' | 'locations_enabled' | 'spells_enabled', enabled: boolean, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||
const enabledValue: number = enabled ? 1 : 0;
|
||||
try {
|
||||
const db: Database = System.getDb();
|
||||
@@ -404,8 +405,9 @@ export default class BookRepo {
|
||||
const charactersValue: number = toolName === 'characters_enabled' ? enabledValue : 0;
|
||||
const worldsValue: number = toolName === 'worlds_enabled' ? enabledValue : 0;
|
||||
const locationsValue: number = toolName === 'locations_enabled' ? enabledValue : 0;
|
||||
const insertQuery: string = 'INSERT INTO book_tools (book_id, user_id, characters_enabled, worlds_enabled, locations_enabled, last_update) VALUES (?, ?, ?, ?, ?, ?)';
|
||||
const insertResult: RunResult = db.run(insertQuery, [bookId, userId, charactersValue, worldsValue, locationsValue, lastUpdate]);
|
||||
const spellsValue: number = toolName === 'spells_enabled' ? enabledValue : 0;
|
||||
const insertQuery: string = 'INSERT INTO book_tools (book_id, user_id, characters_enabled, worlds_enabled, locations_enabled, spells_enabled, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)';
|
||||
const insertResult: RunResult = db.run(insertQuery, [bookId, userId, charactersValue, worldsValue, locationsValue, spellsValue, lastUpdate]);
|
||||
return insertResult.changes > 0;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
@@ -420,11 +422,11 @@ export default class BookRepo {
|
||||
* Upserts book tools settings during sync.
|
||||
* Inserts if not exists, updates if exists.
|
||||
*/
|
||||
static insertSyncBookTools(bookId: string, userId: string, charactersEnabled: number, worldsEnabled: number, locationsEnabled: number, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||
static insertSyncBookTools(bookId: string, userId: string, charactersEnabled: number, worldsEnabled: number, locationsEnabled: number, spellsEnabled: number, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||
try {
|
||||
const db: Database = System.getDb();
|
||||
const query: string = 'INSERT INTO book_tools (book_id, user_id, characters_enabled, worlds_enabled, locations_enabled, last_update) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (book_id, user_id) DO UPDATE SET characters_enabled = excluded.characters_enabled, worlds_enabled = excluded.worlds_enabled, locations_enabled = excluded.locations_enabled, last_update = excluded.last_update';
|
||||
const params: SQLiteValue[] = [bookId, userId, charactersEnabled, worldsEnabled, locationsEnabled, lastUpdate];
|
||||
const query: string = 'INSERT INTO book_tools (book_id, user_id, characters_enabled, worlds_enabled, locations_enabled, spells_enabled, last_update) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (book_id, user_id) DO UPDATE SET characters_enabled = excluded.characters_enabled, worlds_enabled = excluded.worlds_enabled, locations_enabled = excluded.locations_enabled, spells_enabled = excluded.spells_enabled, last_update = excluded.last_update';
|
||||
const params: SQLiteValue[] = [bookId, userId, charactersEnabled, worldsEnabled, locationsEnabled, spellsEnabled, lastUpdate];
|
||||
db.run(query, params);
|
||||
return true;
|
||||
} catch (error: unknown) {
|
||||
|
||||
@@ -198,6 +198,32 @@ export default class CharacterRepo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a character and all its related data (attributes) from the database.
|
||||
* @param userId - The unique identifier of the user
|
||||
* @param characterId - The unique identifier of the character to delete
|
||||
* @param lang - The language for error messages ('fr' or 'en')
|
||||
* @returns True if the deletion was successful, false otherwise
|
||||
*/
|
||||
static deleteCharacter(userId: string, characterId: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||
try {
|
||||
const db: Database = System.getDb();
|
||||
const deleteAttributesQuery: string = 'DELETE FROM `book_characters_attributes` WHERE `character_id`=? AND `user_id`=?';
|
||||
db.run(deleteAttributesQuery, [characterId, userId]);
|
||||
const deleteCharacterQuery: string = 'DELETE FROM `book_characters` WHERE `character_id`=? AND `user_id`=?';
|
||||
const result: RunResult = db.run(deleteCharacterQuery, [characterId, userId]);
|
||||
return result.changes > 0;
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(`DB Error: ${error.message}`);
|
||||
throw new Error(lang === 'fr' ? `Impossible de supprimer le personnage.` : `Unable to delete character.`);
|
||||
} else {
|
||||
console.error("An unknown error occurred.");
|
||||
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a character attribute from the database.
|
||||
* @param userId - The unique identifier of the user
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sqlite3 from 'node-sqlite3-wasm';
|
||||
import { app } from 'electron';
|
||||
|
||||
type Database = sqlite3.Database;
|
||||
|
||||
@@ -18,11 +19,9 @@ const schemaVersion = 1;
|
||||
* DEV ONLY - S'exécute à chaque refresh, pas besoin de version
|
||||
* Mets ta query, test, efface après
|
||||
*/
|
||||
const devQueries: string[] = [
|
||||
const devQueries: string[] = [];
|
||||
|
||||
];
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
const isDev:boolean = !app.isPackaged;
|
||||
|
||||
function columnExists(db: Database, table: string, column: string): boolean {
|
||||
const result = db.all(`PRAGMA table_info(${table})`) as { name: string }[];
|
||||
|
||||
@@ -74,4 +74,15 @@ ipcMain.handle('db:character:update', createHandler<UpdateCharacterData, boolean
|
||||
return Character.updateCharacter(userId, data.character, lang);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// DELETE /character/delete - Delete character
|
||||
interface DeleteCharacterData {
|
||||
characterId: string;
|
||||
}
|
||||
ipcMain.handle('db:character:delete', createHandler<DeleteCharacterData, boolean>(
|
||||
function(userId: string, data: DeleteCharacterData, lang: 'fr' | 'en'): boolean {
|
||||
return Character.deleteCharacter(userId, data.characterId, lang);
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -15,6 +15,7 @@ import './ipc/chapter.ipc.js';
|
||||
import './ipc/character.ipc.js';
|
||||
import './ipc/location.ipc.js';
|
||||
import './ipc/offline.ipc.js';
|
||||
import './ipc/spell.ipc.js';
|
||||
|
||||
// Fix pour __dirname en ES modules
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
|
||||
Reference in New Issue
Block a user