Remove unused components and models for improved maintainability

- Deleted redundant components (`AddActionButton`, `AlertBox`, `AlertStack`, `BackButton`, `CancelButton`, and `CollapsableArea`) and related files.
- Removed unused models (`Book`, `BookSerie`, `BookTables`, `Character`, and `Chapter`) to reduce codebase clutter.
- Updated project structure and references to reflect these removals.
This commit is contained in:
natreex
2026-03-22 22:37:31 -04:00
parent e8aaef108b
commit 64ed90d993
229 changed files with 15091 additions and 21289 deletions

View File

@@ -1,23 +1,18 @@
'use client'
import {useCallback, useContext, useEffect, useState} from 'react';
import {WorldListResponse, WorldProps} from '@/lib/models/World';
import {SeriesWorldProps} from '@/lib/models/Series';
import {SessionContext} from '@/context/SessionContext';
import {BookContext} from '@/context/BookContext';
import {AlertContext} from '@/context/AlertContext';
import {WorldElement, WorldElementSection, WorldListResponse, WorldProps, WorldTextField} from '@/lib/types/world';
import {SeriesWorldProps} from '@/lib/types/series';
import {SessionContext, SessionContextProps} from '@/context/SessionContext';
import {BookContext, BookContextProps} from '@/context/BookContext';
import {AlertContext, AlertContextProps} from '@/context/AlertContext';
import {LangContext, LangContextProps} from '@/context/LangContext';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from '@/context/SyncQueueContext';
import {BooksSyncContext, BooksSyncContextProps} from '@/context/BooksSyncContext';
import {SyncedBook} from '@/lib/models/SyncedBook';
import {SeriesContext, SeriesContextProps} from '@/context/SeriesContext';
import {SeriesSyncContext, SeriesSyncContextProps} from '@/context/SeriesSyncContext';
import {SyncedSeries} from '@/lib/models/SyncedSeries';
import System from '@/lib/models/System';
import {useTranslations} from 'next-intl';
import {SelectBoxProps} from '@/shared/interface';
import {ViewMode} from '@/shared/interface';
import {apiGet, apiPatch, apiPost} from '@/lib/api/client';
import {useTranslations} from '@/lib/i18n';
import {SelectBoxProps} from '@/components/form/SelectBox';
import {ViewMode} from '@/lib/types/settings';
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
const initialWorldState: WorldProps = {
id: '',
@@ -58,17 +53,17 @@ export interface UseWorldsReturn {
bookSeriesId: string | null;
showAddNewWorld: boolean;
newWorldName: string;
// Navigation state
viewMode: ViewMode;
worldBackup: WorldProps | null;
// Actions
selectWorld: (worldId: string) => void;
addNewWorld: () => Promise<void>;
saveWorld: () => Promise<boolean>;
updateWorldField: (field: keyof WorldProps, value: string) => void;
updateWorldArrayField: (field: keyof WorldProps, value: unknown) => void;
saveWorld: () => Promise<void>;
updateWorldField: (field: WorldTextField, value: string) => void;
updateWorldArrayField: (field: WorldElementSection, value: WorldElement[]) => void;
toggleTool: (enabled: boolean) => Promise<void>;
importFromSeries: (seriesWorldId: string) => Promise<void>;
exportToSeries: () => Promise<void>;
@@ -78,7 +73,7 @@ export interface UseWorldsReturn {
setNewWorldName: (name: string) => void;
setWorlds: React.Dispatch<React.SetStateAction<WorldProps[]>>;
getSeriesWorldForCurrentWorld: () => SeriesWorldProps | null;
// Navigation actions
enterDetailMode: (worldId: string) => void;
enterEditMode: () => void;
@@ -89,15 +84,11 @@ export interface UseWorldsReturn {
export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
const {entityType, entityId} = config;
const t = useTranslations();
const {lang} = useContext<LangContextProps>(LangContext);
const {session} = useContext(SessionContext);
const {book, setBook} = useContext(BookContext);
const {errorMessage, successMessage} = useContext(AlertContext);
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
const {addToQueue} = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
const {localSyncedBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
const {localSeries} = useContext<SeriesContextProps>(SeriesContext);
const {localSyncedSeries} = useContext<SeriesSyncContextProps>(SeriesSyncContext);
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
const {book, setBook}: BookContextProps = useContext<BookContextProps>(BookContext);
const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
const [worlds, setWorlds] = useState<WorldProps[]>([]);
const [seriesWorlds, setSeriesWorlds] = useState<SeriesWorldProps[]>([]);
@@ -107,70 +98,58 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
const [isLoading, setIsLoading] = useState<boolean>(true);
const [showAddNewWorld, setShowAddNewWorld] = useState<boolean>(false);
const [newWorldName, setNewWorldName] = useState<string>('');
// Navigation state
const [viewMode, setViewMode] = useState<ViewMode>('list');
const [worldBackup, setWorldBackup] = useState<WorldProps | null>(null);
const isSeriesMode: boolean = entityType === 'series';
const bookSeriesId: string | null = book?.seriesId || null;
const userToken: string = session?.accessToken || '';
// Load worlds on mount
useEffect(function (): void {
if (entityId) {
refreshWorlds();
}
}, [entityId]);
// Load series worlds for book mode
useEffect(function (): void {
if (bookSeriesId && !isSeriesMode) {
refreshSeriesWorlds();
}
}, [bookSeriesId, isSeriesMode]);
const refreshSeriesWorlds = useCallback(async function (): Promise<void> {
if (!bookSeriesId) return;
try {
let response: SeriesWorldProps[];
// Dual logic: offline ou livre local → IPC, sinon serveur
if (isCurrentlyOffline() || book?.localBook) {
response = await tauri.getSeriesWorldList(bookSeriesId);
} else {
response = await System.authGetQueryToServer<SeriesWorldProps[]>(
'series/world/list',
userToken,
lang,
{seriesid: bookSeriesId}
);
}
const response: SeriesWorldProps[] = await apiGet<SeriesWorldProps[]>(
'series/world/list',
userToken,
lang,
{seriesid: bookSeriesId}
);
if (response) {
setSeriesWorlds(response);
}
} catch (e: unknown) {
if (e instanceof Error) {
console.error('Error loading series worlds:', e.message);
errorMessage(e.message);
}
}
}, [bookSeriesId, userToken, lang, isCurrentlyOffline, book?.localBook]);
}, [bookSeriesId, userToken, lang]);
const refreshWorlds = useCallback(async function (): Promise<void> {
setIsLoading(true);
try {
if (isSeriesMode) {
// Series mode - dual logic
let response: SeriesWorldProps[];
if (isCurrentlyOffline() || localSeries) {
response = await tauri.getSeriesWorldList(entityId);
} else {
response = await System.authGetQueryToServer<SeriesWorldProps[]>(
'series/world/list',
userToken,
lang,
{seriesid: entityId}
);
}
const response: SeriesWorldProps[] = await apiGet<SeriesWorldProps[]>(
'series/world/list',
userToken,
lang,
{seriesid: entityId}
);
if (response) {
const mappedWorlds: WorldProps[] = response.map(function (world: SeriesWorldProps): WorldProps {
return {
@@ -206,17 +185,12 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
}
} else {
let response: WorldListResponse;
if (isCurrentlyOffline()) {
response = await tauri.getWorlds(entityId, true) as unknown as WorldListResponse;
} else if (book?.localBook) {
response = await tauri.getWorlds(entityId, true) as unknown as WorldListResponse;
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.getWorlds(entityId, book?.tools?.worlds ?? false) as WorldListResponse;
} else {
response = await System.authGetQueryToServer<WorldListResponse>(
'book/worlds',
userToken,
lang,
{bookid: entityId}
);
response = await apiGet<WorldListResponse>('book/worlds', userToken, lang, {
bookid: entityId
});
}
if (response) {
setWorlds(response.worlds);
@@ -250,8 +224,8 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
} finally {
setIsLoading(false);
}
}, [entityId, isSeriesMode, userToken, lang, book, setBook, errorMessage, t, isCurrentlyOffline]);
}, [entityId, isSeriesMode, userToken, lang, book, setBook, errorMessage, t]);
const selectWorld = useCallback(function (worldId: string): void {
const index: number = worlds.findIndex(function (world: WorldProps): boolean {
return world.id === worldId;
@@ -260,40 +234,35 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
setSelectedWorldIndex(index);
}
}, [worlds]);
const updateWorldField = useCallback(function (field: keyof WorldProps, value: string): void {
const updateWorldField = useCallback(function (field: WorldTextField, value: string): void {
setWorlds(function (prev: WorldProps[]): WorldProps[] {
const updated: WorldProps[] = [...prev];
(updated[selectedWorldIndex][field] as string) = value;
return updated;
});
}, [selectedWorldIndex]);
const updateWorldArrayField = useCallback(function (field: keyof WorldProps, value: unknown): void {
setWorlds(function (prev: WorldProps[]): WorldProps[] {
const updated: WorldProps[] = [...prev];
// @ts-ignore - Le type dépend du champ
updated[selectedWorldIndex][field] = value;
return updated;
});
}, [selectedWorldIndex]);
const updateWorldArrayField = useCallback(function (field: WorldElementSection, value: WorldElement[]): void {
setWorlds(function (prev: WorldProps[]): WorldProps[] {
const updated: WorldProps[] = [...prev];
updated[selectedWorldIndex][field] = value;
return updated;
});
}, [selectedWorldIndex]);
const toggleTool = useCallback(async function (enabled: boolean): Promise<void> {
if (isSeriesMode) return;
try {
const requestData = {
bookId: book?.bookId,
toolName: 'worlds',
enabled: enabled
};
let response: boolean;
if (isCurrentlyOffline() || book?.localBook) {
response = await tauri.updateBookToolSetting(requestData.bookId, requestData.toolName, requestData.enabled);
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.updateBookToolSetting(book?.bookId ?? '', 'worlds', enabled);
} else {
response = await System.authPatchToServer<boolean>('book/tool-setting', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === book?.bookId)) {
addToQueue('update_book_tool_setting', {data: requestData});
}
response = await apiPatch<boolean>('book/tool-setting', {
bookId: book?.bookId,
toolName: 'worlds',
enabled: enabled
}, userToken, lang);
}
if (response && setBook && book) {
setToolEnabled(enabled);
@@ -312,8 +281,8 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
errorMessage(e.message);
}
}
}, [isSeriesMode, book, setBook, userToken, lang, errorMessage, isCurrentlyOffline, localSyncedBooks, addToQueue]);
}, [isSeriesMode, book, setBook, userToken, lang, errorMessage]);
const addNewWorld = useCallback(async function (): Promise<void> {
if (newWorldName.trim() === '') {
errorMessage(t("worldSetting.newWorldNameError"));
@@ -322,41 +291,30 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
try {
let newWorldId: string;
if (isSeriesMode) {
// Series mode - dual logic
const addData = {
seriesId: entityId,
name: newWorldName,
};
if (isCurrentlyOffline() || localSeries) {
newWorldId = await tauri.addSeriesWorld(addData);
} else {
newWorldId = await System.authPostToServer<string>(
'series/world/add',
addData,
userToken,
lang
);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('add_series_world', {data: {...addData, id: newWorldId}});
}
newWorldId = await apiPost<string>(
'series/world/add',
{
seriesId: entityId,
name: newWorldName,
},
userToken,
lang
);
if (!newWorldId) {
errorMessage(t("worldSetting.addWorldError"));
return;
}
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
newWorldId = await tauri.addWorld(entityId, newWorldName);
if (!newWorldId) {
errorMessage(t("worldSetting.addWorldError"));
return;
}
} else {
const requestData = {
newWorldId = await apiPost<string>('book/world/add', {
worldName: newWorldName,
bookId: entityId,
};
if (isCurrentlyOffline() || book?.localBook) {
newWorldId = await tauri.addWorld(requestData.bookId || entityId, requestData.worldName, requestData.id, requestData.seriesWorldId);
} else {
newWorldId = await System.authPostToServer<string>('book/world/add', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_world', {data: {...requestData, id: newWorldId}});
}
}
}, userToken, lang);
if (!newWorldId) {
errorMessage(t("worldSetting.addWorldError"));
return;
@@ -386,15 +344,14 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
errorMessage(t("worldSetting.unknownError"));
}
}
}, [newWorldName, isSeriesMode, entityId, userToken, lang, errorMessage, t, isCurrentlyOffline, book?.localBook, localSyncedBooks, addToQueue]);
const saveWorld = useCallback(async function (): Promise<boolean> {
if (worlds.length === 0) return false;
}, [newWorldName, isSeriesMode, entityId, userToken, lang, errorMessage, t]);
const saveWorld = useCallback(async function (): Promise<void> {
if (worlds.length === 0) return;
try {
const currentWorld: WorldProps = worlds[selectedWorldIndex];
if (isSeriesMode) {
// Series mode - dual logic
const updateData = {
const response: boolean = await apiPatch<boolean>('series/world/update', {
worldId: currentWorld.id,
name: currentWorld.name,
history: currentWorld.history,
@@ -402,111 +359,62 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
economy: currentWorld.economy,
religion: currentWorld.religion,
languages: currentWorld.languages,
};
let response: boolean;
if (isCurrentlyOffline() || localSeries) {
response = await tauri.updateSeriesWorld(updateData);
} else {
response = await System.authPatchToServer<boolean>('series/world/update', updateData, userToken, lang);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('update_series_world', {data: updateData});
}
}
}, userToken, lang);
if (!response) {
errorMessage(t("worldSetting.updateWorldError"));
return false;
return;
}
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
const response: boolean = await tauri.updateWorld(currentWorld);
if (!response) {
errorMessage(t("worldSetting.updateWorldError"));
return;
}
} else {
const requestData = {
const response: boolean = await apiPatch<boolean>('book/world/update', {
world: currentWorld,
bookId: entityId,
};
let response: boolean;
if (isCurrentlyOffline() || book?.localBook) {
response = await tauri.updateWorld(requestData.world || requestData);
} else {
response = await System.authPatchToServer<boolean>('book/world/update', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('update_world', {data: requestData});
}
}
}, userToken, lang);
if (!response) {
errorMessage(t("worldSetting.updateWorldError"));
return false;
return;
}
}
successMessage(t("worldSetting.updateWorldSuccess"));
return true;
} catch (e: unknown) {
if (e instanceof Error) {
errorMessage(e.message);
} else {
errorMessage(t("worldSetting.unknownError"));
}
return false;
}
}, [worlds, selectedWorldIndex, isSeriesMode, entityId, userToken, lang, errorMessage, successMessage, t, isCurrentlyOffline, book?.localBook, localSyncedBooks, addToQueue]);
}, [worlds, selectedWorldIndex, isSeriesMode, entityId, userToken, lang, errorMessage, successMessage, t]);
const exportToSeries = useCallback(async function (): Promise<void> {
const selectedWorld: WorldProps | undefined = worlds[selectedWorldIndex];
if (!selectedWorld || !bookSeriesId) return;
try {
const seriesWorldData = {
const seriesWorldId: string = await apiPost<string>('series/world/add', {
seriesId: bookSeriesId,
name: selectedWorld.name,
history: selectedWorld.history || null,
politics: selectedWorld.politics || null,
economy: selectedWorld.economy || null,
religion: selectedWorld.religion || null,
languages: selectedWorld.languages || null,
};
let seriesWorldId: string;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
seriesWorldId = await tauri.addSeriesWorld(seriesWorldData);
} else {
// Mode online → Serveur
seriesWorldId = await System.authPostToServer<string>('series/world/add', {
seriesId: bookSeriesId,
world: {
name: selectedWorld.name,
history: selectedWorld.history || null,
politics: selectedWorld.politics || null,
economy: selectedWorld.economy || null,
religion: selectedWorld.religion || null,
languages: selectedWorld.languages || null,
}
}, userToken, lang);
// Si la série a une copie locale → addToQueue
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === bookSeriesId)) {
addToQueue('add_series_world', {data: {...seriesWorldData, id: seriesWorldId}});
world: {
name: selectedWorld.name,
history: selectedWorld.history || null,
politics: selectedWorld.politics || null,
economy: selectedWorld.economy || null,
religion: selectedWorld.religion || null,
languages: selectedWorld.languages || null,
}
}
}, userToken, lang);
if (seriesWorldId) {
const updateData = {
const updateResponse: boolean = await apiPost<boolean>('book/world/update', {
world: {
...selectedWorld,
seriesWorldId: seriesWorldId
},
bookId: entityId,
};
let updateResponse: boolean;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
updateResponse = await tauri.updateWorld(updateData.world || updateData);
} else {
// Mode online → Serveur
updateResponse = await System.authPostToServer<boolean>('book/world/update', updateData, userToken, lang);
// Si le livre a une copie locale → addToQueue
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('update_world', {data: updateData});
}
}
}, userToken, lang);
if (updateResponse) {
setWorlds(function (prev: WorldProps[]): WorldProps[] {
const updated: WorldProps[] = [...prev];
@@ -545,39 +453,31 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
errorMessage(e.message);
}
}
}, [worlds, selectedWorldIndex, bookSeriesId, userToken, lang, successMessage, errorMessage, t, isCurrentlyOffline, book, addToQueue, localSyncedBooks, localSyncedSeries, entityId]);
}, [worlds, selectedWorldIndex, bookSeriesId, userToken, lang, successMessage, errorMessage, t]);
const importFromSeries = useCallback(async function (seriesWorldId: string): Promise<void> {
const seriesWorld: SeriesWorldProps | undefined = seriesWorlds.find(function (w: SeriesWorldProps): boolean {
return w.id === seriesWorldId;
});
if (!seriesWorld) return;
try {
const requestData = {
worldName: seriesWorld.name,
bookId: entityId,
seriesWorldId: seriesWorldId,
};
let worldId: string;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
worldId = await tauri.addWorld(requestData.bookId || entityId, requestData.worldName, requestData.id, requestData.seriesWorldId);
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
worldId = await tauri.addWorld(entityId, seriesWorld.name, undefined, seriesWorldId);
} else {
// Mode online → Serveur
worldId = await System.authPostToServer<string>('book/world/add', requestData, userToken, lang);
// Si le livre a une copie locale → addToQueue
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_world', {data: {...requestData, id: worldId}});
}
worldId = await apiPost<string>('book/world/add', {
worldName: seriesWorld.name,
bookId: entityId,
seriesWorldId: seriesWorldId,
}, userToken, lang);
}
if (!worldId) {
errorMessage(t("worldSetting.importError"));
return;
}
const newWorld: WorldProps = {
id: worldId,
name: seriesWorld.name,
@@ -611,8 +511,8 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
errorMessage(e.message);
}
}
}, [seriesWorlds, entityId, userToken, lang, errorMessage, t, isCurrentlyOffline, book, addToQueue, localSyncedBooks]);
}, [seriesWorlds, entityId, userToken, lang, errorMessage, t]);
const getSeriesWorldForCurrentWorld = useCallback(function (): SeriesWorldProps | null {
const currentWorld: WorldProps | undefined = worlds[selectedWorldIndex];
if (!currentWorld?.seriesWorldId) return null;
@@ -620,7 +520,7 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
return world.id === currentWorld.seriesWorldId;
}) || null;
}, [worlds, selectedWorldIndex, seriesWorlds]);
// Navigation functions
const enterDetailMode = useCallback(function (worldId: string): void {
const index: number = worlds.findIndex(function (world: WorldProps): boolean {
@@ -632,21 +532,17 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
setWorldBackup(null);
}
}, [worlds]);
const enterEditMode = useCallback(function (): void {
if (worlds.length > 0 && selectedWorldIndex >= 0) {
setWorldBackup({...worlds[selectedWorldIndex]});
}
setViewMode('edit');
}, [worlds, selectedWorldIndex]);
const exitEditMode = useCallback(async function (save: boolean): Promise<void> {
if (save) {
const success: boolean = await saveWorld();
if (!success) {
// Stay in edit mode on error
return;
}
await saveWorld();
if (worldBackup) {
setViewMode('detail');
} else {
@@ -666,13 +562,13 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
}
setWorldBackup(null);
}, [saveWorld, worldBackup, selectedWorldIndex]);
const backToList = useCallback(function (): void {
setSelectedWorldIndex(0);
setWorldBackup(null);
setViewMode('list');
}, []);
return {
// State
worlds,
@@ -685,11 +581,11 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
bookSeriesId,
showAddNewWorld,
newWorldName,
// Navigation state
viewMode,
worldBackup,
// Actions
selectWorld,
addNewWorld,
@@ -705,7 +601,7 @@ export function useWorlds(config: UseWorldsConfig): UseWorldsReturn {
setNewWorldName,
setWorlds,
getSeriesWorldForCurrentWorld,
// Navigation actions
enterDetailMode,
enterEditMode,