Remove CharacterComponent and CharacterDetail components
- Deleted `CharacterComponent` and `CharacterDetail` files from the project. - Refactored related logic to improve code maintainability and reduce redundancy.
This commit is contained in:
720
hooks/settings/useWorlds.ts
Normal file
720
hooks/settings/useWorlds.ts
Normal file
@@ -0,0 +1,720 @@
|
||||
'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 {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';
|
||||
|
||||
const initialWorldState: WorldProps = {
|
||||
id: '',
|
||||
name: '',
|
||||
history: '',
|
||||
politics: '',
|
||||
economy: '',
|
||||
religion: '',
|
||||
languages: '',
|
||||
laws: [],
|
||||
biomes: [],
|
||||
issues: [],
|
||||
customs: [],
|
||||
kingdoms: [],
|
||||
climate: [],
|
||||
resources: [],
|
||||
wildlife: [],
|
||||
arts: [],
|
||||
ethnicGroups: [],
|
||||
socialClasses: [],
|
||||
importantCharacters: [],
|
||||
};
|
||||
|
||||
export interface UseWorldsConfig {
|
||||
entityType: 'book' | 'series';
|
||||
entityId: string;
|
||||
}
|
||||
|
||||
export interface UseWorldsReturn {
|
||||
// State
|
||||
worlds: WorldProps[];
|
||||
seriesWorlds: SeriesWorldProps[];
|
||||
selectedWorldIndex: number;
|
||||
worldsSelector: SelectBoxProps[];
|
||||
toolEnabled: boolean;
|
||||
isLoading: boolean;
|
||||
isSeriesMode: boolean;
|
||||
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;
|
||||
toggleTool: (enabled: boolean) => Promise<void>;
|
||||
importFromSeries: (seriesWorldId: string) => Promise<void>;
|
||||
exportToSeries: () => Promise<void>;
|
||||
refreshWorlds: () => Promise<void>;
|
||||
refreshSeriesWorlds: () => Promise<void>;
|
||||
setShowAddNewWorld: (show: boolean) => void;
|
||||
setNewWorldName: (name: string) => void;
|
||||
setWorlds: React.Dispatch<React.SetStateAction<WorldProps[]>>;
|
||||
getSeriesWorldForCurrentWorld: () => SeriesWorldProps | null;
|
||||
|
||||
// Navigation actions
|
||||
enterDetailMode: (worldId: string) => void;
|
||||
enterEditMode: () => void;
|
||||
exitEditMode: (save: boolean) => Promise<void>;
|
||||
backToList: () => void;
|
||||
}
|
||||
|
||||
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 [worlds, setWorlds] = useState<WorldProps[]>([]);
|
||||
const [seriesWorlds, setSeriesWorlds] = useState<SeriesWorldProps[]>([]);
|
||||
const [selectedWorldIndex, setSelectedWorldIndex] = useState<number>(0);
|
||||
const [worldsSelector, setWorldsSelector] = useState<SelectBoxProps[]>([]);
|
||||
const [toolEnabled, setToolEnabled] = useState<boolean>(entityType === 'series' || (book?.tools?.worlds ?? false));
|
||||
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 window.electron.invoke<SeriesWorldProps[]>(
|
||||
'db:series:world:list',
|
||||
{seriesId: bookSeriesId}
|
||||
);
|
||||
} else {
|
||||
response = await System.authGetQueryToServer<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);
|
||||
}
|
||||
}
|
||||
}, [bookSeriesId, userToken, lang, isCurrentlyOffline, book?.localBook]);
|
||||
|
||||
const refreshWorlds = useCallback(async function (): Promise<void> {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (isSeriesMode) {
|
||||
// Series mode - dual logic
|
||||
let response: SeriesWorldProps[];
|
||||
if (isCurrentlyOffline() || localSeries) {
|
||||
response = await window.electron.invoke<SeriesWorldProps[]>(
|
||||
'db:series:world:list',
|
||||
{seriesId: entityId}
|
||||
);
|
||||
} else {
|
||||
response = await System.authGetQueryToServer<SeriesWorldProps[]>(
|
||||
'series/world/list',
|
||||
userToken,
|
||||
lang,
|
||||
{seriesid: entityId}
|
||||
);
|
||||
}
|
||||
if (response) {
|
||||
const mappedWorlds: WorldProps[] = response.map(function (world: SeriesWorldProps): WorldProps {
|
||||
return {
|
||||
id: world.id,
|
||||
name: world.name,
|
||||
history: world.history || '',
|
||||
politics: world.politics || '',
|
||||
economy: world.economy || '',
|
||||
religion: world.religion || '',
|
||||
languages: world.languages || '',
|
||||
laws: world.laws || [],
|
||||
biomes: world.biomes || [],
|
||||
issues: world.issues || [],
|
||||
customs: world.customs || [],
|
||||
kingdoms: world.kingdoms || [],
|
||||
climate: world.climate || [],
|
||||
resources: world.resources || [],
|
||||
wildlife: world.wildlife || [],
|
||||
arts: world.arts || [],
|
||||
ethnicGroups: world.ethnicGroups || [],
|
||||
socialClasses: world.socialClasses || [],
|
||||
importantCharacters: world.importantCharacters || [],
|
||||
};
|
||||
});
|
||||
setWorlds(mappedWorlds);
|
||||
const formattedWorlds: SelectBoxProps[] = response.map(function (world: SeriesWorldProps): SelectBoxProps {
|
||||
return {
|
||||
label: world.name,
|
||||
value: world.id,
|
||||
};
|
||||
});
|
||||
setWorldsSelector(formattedWorlds);
|
||||
}
|
||||
} else {
|
||||
let response: WorldListResponse;
|
||||
if (isCurrentlyOffline()) {
|
||||
response = await window.electron.invoke<WorldListResponse>('db:book:worlds:get', {bookid: entityId});
|
||||
} else if (book?.localBook) {
|
||||
response = await window.electron.invoke<WorldListResponse>('db:book:worlds:get', {bookid: entityId});
|
||||
} else {
|
||||
response = await System.authGetQueryToServer<WorldListResponse>(
|
||||
'book/worlds',
|
||||
userToken,
|
||||
lang,
|
||||
{bookid: entityId}
|
||||
);
|
||||
}
|
||||
if (response) {
|
||||
setWorlds(response.worlds);
|
||||
setToolEnabled(response.enabled);
|
||||
if (setBook && book) {
|
||||
setBook({
|
||||
...book,
|
||||
tools: {
|
||||
characters: book.tools?.characters ?? false,
|
||||
worlds: response.enabled,
|
||||
locations: book.tools?.locations ?? false,
|
||||
spells: book.tools?.spells ?? false
|
||||
}
|
||||
});
|
||||
}
|
||||
const formattedWorlds: SelectBoxProps[] = response.worlds.map(function (world: WorldProps): SelectBoxProps {
|
||||
return {
|
||||
label: world.name,
|
||||
value: world.id.toString(),
|
||||
};
|
||||
});
|
||||
setWorldsSelector(formattedWorlds);
|
||||
}
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("worldSetting.unknownError"));
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [entityId, isSeriesMode, userToken, lang, book, setBook, errorMessage, t, isCurrentlyOffline]);
|
||||
|
||||
const selectWorld = useCallback(function (worldId: string): void {
|
||||
const index: number = worlds.findIndex(function (world: WorldProps): boolean {
|
||||
return world.id === worldId;
|
||||
});
|
||||
if (index !== -1) {
|
||||
setSelectedWorldIndex(index);
|
||||
}
|
||||
}, [worlds]);
|
||||
|
||||
const updateWorldField = useCallback(function (field: keyof WorldProps, 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 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 window.electron.invoke<boolean>('db:book:tool:update', requestData);
|
||||
} else {
|
||||
response = await System.authPatchToServer<boolean>('book/tool-setting', requestData, userToken, lang);
|
||||
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === book?.bookId)) {
|
||||
addToQueue('db:book:tool:update', requestData);
|
||||
}
|
||||
}
|
||||
if (response && setBook && book) {
|
||||
setToolEnabled(enabled);
|
||||
setBook({
|
||||
...book,
|
||||
tools: {
|
||||
characters: book.tools?.characters ?? false,
|
||||
worlds: enabled,
|
||||
locations: book.tools?.locations ?? false,
|
||||
spells: book.tools?.spells ?? false
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
}
|
||||
}
|
||||
}, [isSeriesMode, book, setBook, userToken, lang, errorMessage, isCurrentlyOffline, localSyncedBooks, addToQueue]);
|
||||
|
||||
const addNewWorld = useCallback(async function (): Promise<void> {
|
||||
if (newWorldName.trim() === '') {
|
||||
errorMessage(t("worldSetting.newWorldNameError"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let newWorldId: string;
|
||||
if (isSeriesMode) {
|
||||
// Series mode - dual logic
|
||||
const addData = {
|
||||
seriesId: entityId,
|
||||
name: newWorldName,
|
||||
};
|
||||
if (isCurrentlyOffline() || localSeries) {
|
||||
newWorldId = await window.electron.invoke<string>('db:series:world:add', addData);
|
||||
} else {
|
||||
newWorldId = await System.authPostToServer<string>(
|
||||
'series/world/add',
|
||||
addData,
|
||||
userToken,
|
||||
lang
|
||||
);
|
||||
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
|
||||
addToQueue('db:series:world:add', {...addData, id: newWorldId});
|
||||
}
|
||||
}
|
||||
if (!newWorldId) {
|
||||
errorMessage(t("worldSetting.addWorldError"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const requestData = {
|
||||
worldName: newWorldName,
|
||||
bookId: entityId,
|
||||
};
|
||||
if (isCurrentlyOffline() || book?.localBook) {
|
||||
newWorldId = await window.electron.invoke<string>('db:book:world:add', requestData);
|
||||
} else {
|
||||
newWorldId = await System.authPostToServer<string>('book/world/add', requestData, userToken, lang);
|
||||
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
|
||||
addToQueue('db:book:world:add', {...requestData, id: newWorldId});
|
||||
}
|
||||
}
|
||||
if (!newWorldId) {
|
||||
errorMessage(t("worldSetting.addWorldError"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const newWorld: WorldProps = {
|
||||
...initialWorldState,
|
||||
id: newWorldId,
|
||||
name: newWorldName,
|
||||
};
|
||||
setWorlds(function (prev: WorldProps[]): WorldProps[] {
|
||||
return [...prev, newWorld];
|
||||
});
|
||||
setWorldsSelector(function (prev: SelectBoxProps[]): SelectBoxProps[] {
|
||||
return [...prev, {label: newWorldName, value: newWorldId}];
|
||||
});
|
||||
setNewWorldName('');
|
||||
setShowAddNewWorld(false);
|
||||
// Sélectionner le nouveau monde et passer en mode edit
|
||||
setSelectedWorldIndex(worlds.length); // Le nouveau monde est à la fin
|
||||
setViewMode('edit');
|
||||
setWorldBackup(null);
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
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;
|
||||
try {
|
||||
const currentWorld: WorldProps = worlds[selectedWorldIndex];
|
||||
if (isSeriesMode) {
|
||||
// Series mode - dual logic
|
||||
const updateData = {
|
||||
worldId: currentWorld.id,
|
||||
name: currentWorld.name,
|
||||
history: currentWorld.history,
|
||||
politics: currentWorld.politics,
|
||||
economy: currentWorld.economy,
|
||||
religion: currentWorld.religion,
|
||||
languages: currentWorld.languages,
|
||||
};
|
||||
let response: boolean;
|
||||
if (isCurrentlyOffline() || localSeries) {
|
||||
response = await window.electron.invoke<boolean>('db:series:world:update', updateData);
|
||||
} else {
|
||||
response = await System.authPatchToServer<boolean>('series/world/update', updateData, userToken, lang);
|
||||
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
|
||||
addToQueue('db:series:world:update', updateData);
|
||||
}
|
||||
}
|
||||
if (!response) {
|
||||
errorMessage(t("worldSetting.updateWorldError"));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const requestData = {
|
||||
world: currentWorld,
|
||||
bookId: entityId,
|
||||
};
|
||||
let response: boolean;
|
||||
if (isCurrentlyOffline() || book?.localBook) {
|
||||
response = await window.electron.invoke<boolean>('db:book:world:update', requestData);
|
||||
} else {
|
||||
response = await System.authPatchToServer<boolean>('book/world/update', requestData, userToken, lang);
|
||||
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
|
||||
addToQueue('db:book:world:update', requestData);
|
||||
}
|
||||
}
|
||||
if (!response) {
|
||||
errorMessage(t("worldSetting.updateWorldError"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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]);
|
||||
|
||||
const exportToSeries = useCallback(async function (): Promise<void> {
|
||||
const selectedWorld: WorldProps | undefined = worlds[selectedWorldIndex];
|
||||
if (!selectedWorld || !bookSeriesId) return;
|
||||
|
||||
try {
|
||||
const seriesWorldData = {
|
||||
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 → IPC
|
||||
seriesWorldId = await window.electron.invoke<string>('db:series:world:add', 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('db:series:world:add', {...seriesWorldData, id: seriesWorldId});
|
||||
}
|
||||
}
|
||||
|
||||
if (seriesWorldId) {
|
||||
const updateData = {
|
||||
world: {
|
||||
...selectedWorld,
|
||||
seriesWorldId: seriesWorldId
|
||||
},
|
||||
bookId: entityId,
|
||||
};
|
||||
|
||||
let updateResponse: boolean;
|
||||
if (isCurrentlyOffline() || book?.localBook) {
|
||||
// Mode offline ou livre local → IPC
|
||||
updateResponse = await window.electron.invoke<boolean>('db:book:world:update', 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('db:book:world:update', updateData);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateResponse) {
|
||||
setWorlds(function (prev: WorldProps[]): WorldProps[] {
|
||||
const updated: WorldProps[] = [...prev];
|
||||
updated[selectedWorldIndex] = {...selectedWorld, seriesWorldId: seriesWorldId};
|
||||
return updated;
|
||||
});
|
||||
const newSeriesWorld: SeriesWorldProps = {
|
||||
id: seriesWorldId,
|
||||
name: selectedWorld.name,
|
||||
history: selectedWorld.history || '',
|
||||
politics: selectedWorld.politics || '',
|
||||
economy: selectedWorld.economy || '',
|
||||
religion: selectedWorld.religion || '',
|
||||
languages: selectedWorld.languages || '',
|
||||
laws: [],
|
||||
biomes: [],
|
||||
issues: [],
|
||||
customs: [],
|
||||
kingdoms: [],
|
||||
climate: [],
|
||||
resources: [],
|
||||
wildlife: [],
|
||||
arts: [],
|
||||
ethnicGroups: [],
|
||||
socialClasses: [],
|
||||
importantCharacters: [],
|
||||
};
|
||||
setSeriesWorlds(function (prev: SeriesWorldProps[]): SeriesWorldProps[] {
|
||||
return [...prev, newSeriesWorld];
|
||||
});
|
||||
successMessage(t("worldSetting.exportSuccess"));
|
||||
}
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
}
|
||||
}
|
||||
}, [worlds, selectedWorldIndex, bookSeriesId, userToken, lang, successMessage, errorMessage, t, isCurrentlyOffline, book, addToQueue, localSyncedBooks, localSyncedSeries, entityId]);
|
||||
|
||||
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 → IPC
|
||||
worldId = await window.electron.invoke<string>('db:book:world:add', requestData);
|
||||
} 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('db:book:world:add', {...requestData, id: worldId});
|
||||
}
|
||||
}
|
||||
|
||||
if (!worldId) {
|
||||
errorMessage(t("worldSetting.importError"));
|
||||
return;
|
||||
}
|
||||
|
||||
const newWorld: WorldProps = {
|
||||
id: worldId,
|
||||
name: seriesWorld.name,
|
||||
history: seriesWorld.history || '',
|
||||
politics: seriesWorld.politics || '',
|
||||
economy: seriesWorld.economy || '',
|
||||
religion: seriesWorld.religion || '',
|
||||
languages: seriesWorld.languages || '',
|
||||
laws: [],
|
||||
biomes: [],
|
||||
issues: [],
|
||||
customs: [],
|
||||
kingdoms: [],
|
||||
climate: [],
|
||||
resources: [],
|
||||
wildlife: [],
|
||||
arts: [],
|
||||
ethnicGroups: [],
|
||||
socialClasses: [],
|
||||
importantCharacters: [],
|
||||
seriesWorldId: seriesWorldId,
|
||||
};
|
||||
setWorlds(function (prev: WorldProps[]): WorldProps[] {
|
||||
return [...prev, newWorld];
|
||||
});
|
||||
setWorldsSelector(function (prev: SelectBoxProps[]): SelectBoxProps[] {
|
||||
return [...prev, {label: seriesWorld.name, value: worldId}];
|
||||
});
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
}
|
||||
}
|
||||
}, [seriesWorlds, entityId, userToken, lang, errorMessage, t, isCurrentlyOffline, book, addToQueue, localSyncedBooks]);
|
||||
|
||||
const getSeriesWorldForCurrentWorld = useCallback(function (): SeriesWorldProps | null {
|
||||
const currentWorld: WorldProps | undefined = worlds[selectedWorldIndex];
|
||||
if (!currentWorld?.seriesWorldId) return null;
|
||||
return seriesWorlds.find(function (world: SeriesWorldProps): boolean {
|
||||
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 {
|
||||
return world.id === worldId;
|
||||
});
|
||||
if (index !== -1) {
|
||||
setSelectedWorldIndex(index);
|
||||
setViewMode('detail');
|
||||
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;
|
||||
}
|
||||
if (worldBackup) {
|
||||
setViewMode('detail');
|
||||
} else {
|
||||
setViewMode('list');
|
||||
}
|
||||
} else {
|
||||
if (worldBackup && selectedWorldIndex >= 0) {
|
||||
setWorlds(function (prev: WorldProps[]): WorldProps[] {
|
||||
const updated: WorldProps[] = [...prev];
|
||||
updated[selectedWorldIndex] = worldBackup;
|
||||
return updated;
|
||||
});
|
||||
setViewMode('detail');
|
||||
} else {
|
||||
setViewMode('list');
|
||||
}
|
||||
}
|
||||
setWorldBackup(null);
|
||||
}, [saveWorld, worldBackup, selectedWorldIndex]);
|
||||
|
||||
const backToList = useCallback(function (): void {
|
||||
setSelectedWorldIndex(0);
|
||||
setWorldBackup(null);
|
||||
setViewMode('list');
|
||||
}, []);
|
||||
|
||||
return {
|
||||
// State
|
||||
worlds,
|
||||
seriesWorlds,
|
||||
selectedWorldIndex,
|
||||
worldsSelector,
|
||||
toolEnabled,
|
||||
isLoading,
|
||||
isSeriesMode,
|
||||
bookSeriesId,
|
||||
showAddNewWorld,
|
||||
newWorldName,
|
||||
|
||||
// Navigation state
|
||||
viewMode,
|
||||
worldBackup,
|
||||
|
||||
// Actions
|
||||
selectWorld,
|
||||
addNewWorld,
|
||||
saveWorld,
|
||||
updateWorldField,
|
||||
updateWorldArrayField,
|
||||
toggleTool,
|
||||
importFromSeries,
|
||||
exportToSeries,
|
||||
refreshWorlds,
|
||||
refreshSeriesWorlds,
|
||||
setShowAddNewWorld,
|
||||
setNewWorldName,
|
||||
setWorlds,
|
||||
getSeriesWorldForCurrentWorld,
|
||||
|
||||
// Navigation actions
|
||||
enterDetailMode,
|
||||
enterEditMode,
|
||||
exitEditMode,
|
||||
backToList,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user