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,21 +1,16 @@
'use client'
import {useCallback, useContext, useEffect, useState} from 'react';
import {SeriesLocationItem, SeriesLocationElement, SeriesLocationSubElement} from '@/lib/models/Series';
import {SessionContext} from '@/context/SessionContext';
import {BookContext} from '@/context/BookContext';
import {AlertContext} from '@/context/AlertContext';
import {SeriesLocationElement, SeriesLocationItem, SeriesLocationSubElement} 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 System from '@/lib/models/System';
import {useTranslations} from 'next-intl';
import {ViewMode} from '@/shared/interface';
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 {apiDelete, apiGet, apiPatch, apiPost} from '@/lib/api/client';
import {useTranslations} from '@/lib/i18n';
import {ViewMode} from '@/lib/types/settings';
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
export interface SubElement {
id: string;
@@ -58,12 +53,12 @@ export interface UseLocationsReturn {
newSectionName: string;
newElementNames: { [key: string]: string };
newSubElementNames: { [key: string]: string };
// Navigation state
viewMode: ViewMode;
selectedSectionIndex: number;
sectionsBackup: LocationProps[] | null;
// Actions
addSection: () => Promise<void>;
addElement: (sectionId: string) => Promise<void>;
@@ -73,7 +68,7 @@ export interface UseLocationsReturn {
removeSubElement: (sectionId: string, elementIndex: number, subElementIndex: number) => Promise<void>;
updateElement: (sectionId: string, elementIndex: number, field: keyof Element, value: string) => void;
updateSubElement: (sectionId: string, elementIndex: number, subElementIndex: number, field: keyof SubElement, value: string) => void;
saveLocations: () => Promise<boolean>;
saveLocations: () => Promise<void>;
toggleTool: (enabled: boolean) => Promise<void>;
importFromSeries: (seriesLocationId: string) => Promise<void>;
exportToSeries: (section: LocationProps) => Promise<void>;
@@ -82,7 +77,7 @@ export interface UseLocationsReturn {
setNewSectionName: (name: string) => void;
setNewElementNames: React.Dispatch<React.SetStateAction<{ [key: string]: string }>>;
setNewSubElementNames: React.Dispatch<React.SetStateAction<{ [key: string]: string }>>;
// Navigation actions
enterDetailMode: (sectionIndex: number) => void;
enterEditMode: () => void;
@@ -93,16 +88,12 @@ export interface UseLocationsReturn {
export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
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 [sections, setSections] = useState<LocationProps[]>([]);
const [seriesLocations, setSeriesLocations] = useState<SeriesLocationItem[]>([]);
const [toolEnabled, setToolEnabled] = useState<boolean>(entityType === 'series' || (book?.tools?.locations ?? false));
@@ -110,74 +101,59 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
const [newSectionName, setNewSectionName] = useState<string>('');
const [newElementNames, setNewElementNames] = useState<{ [key: string]: string }>({});
const [newSubElementNames, setNewSubElementNames] = useState<{ [key: string]: string }>({});
const [isAddingElement, setIsAddingElement] = useState<boolean>(false);
const [isAddingSubElement, setIsAddingSubElement] = useState<boolean>(false);
const [isAddingSection, setIsAddingSection] = useState<boolean>(false);
// Navigation state
const [viewMode, setViewMode] = useState<ViewMode>('list');
const [selectedSectionIndex, setSelectedSectionIndex] = useState<number>(-1);
const [sectionsBackup, setSectionsBackup] = useState<LocationProps[] | null>(null);
const isSeriesMode: boolean = entityType === 'series';
const bookSeriesId: string | null = book?.seriesId || null;
const userToken: string = session?.accessToken || '';
// Load locations on mount
useEffect(function (): void {
if (entityId) {
refreshLocations();
}
}, [entityId]);
// Load series locations for book mode
useEffect(function (): void {
if (bookSeriesId && !isSeriesMode) {
refreshSeriesLocations();
}
}, [bookSeriesId, isSeriesMode]);
const refreshSeriesLocations = useCallback(async function (): Promise<void> {
if (!bookSeriesId) return;
try {
let response: SeriesLocationItem[];
// Dual logic: offline ou livre local → IPC, sinon serveur
if (isCurrentlyOffline() || book?.localBook) {
response = await tauri.getSeriesLocationList(bookSeriesId);
} else {
response = await System.authGetQueryToServer<SeriesLocationItem[]>(
'series/location/list',
userToken,
lang,
{seriesid: bookSeriesId}
);
}
const response: SeriesLocationItem[] = await apiGet<SeriesLocationItem[]>(
'series/location/list',
userToken,
lang,
{seriesid: bookSeriesId}
);
if (response) {
setSeriesLocations(response);
}
} catch (e: unknown) {
if (e instanceof Error) {
console.error('Error loading series locations:', e.message);
errorMessage(e.message);
}
}
}, [bookSeriesId, userToken, lang, isCurrentlyOffline, book?.localBook]);
}, [bookSeriesId, userToken, lang]);
const refreshLocations = useCallback(async function (): Promise<void> {
setIsLoading(true);
try {
if (isSeriesMode) {
// Series mode - dual logic
let response: SeriesLocationItem[];
if (isCurrentlyOffline() || localSeries) {
response = await tauri.getSeriesLocationList(entityId);
} else {
response = await System.authGetQueryToServer<SeriesLocationItem[]>(
'series/location/list',
userToken,
lang,
{seriesid: entityId}
);
}
const response: SeriesLocationItem[] = await apiGet<SeriesLocationItem[]>(
'series/location/list',
userToken,
lang,
{seriesid: entityId}
);
if (response) {
const mappedLocations: LocationProps[] = response.map(function (loc: SeriesLocationItem): LocationProps {
return {
@@ -203,17 +179,12 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
}
} else {
let response: LocationListResponse;
if (isCurrentlyOffline()) {
response = await tauri.getAllLocations(entityId, true) as unknown as LocationListResponse;
} else if (book?.localBook) {
response = await tauri.getAllLocations(entityId, true) as unknown as LocationListResponse;
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.getAllLocations(entityId, book?.tools?.locations ?? false) as LocationListResponse;
} else {
response = await System.authGetQueryToServer<LocationListResponse>(
'location/all',
userToken,
lang,
{bookid: entityId}
);
response = await apiGet<LocationListResponse>('location/all', userToken, lang, {
bookid: entityId
});
}
if (response) {
setSections(response.locations);
@@ -240,25 +211,20 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
} finally {
setIsLoading(false);
}
}, [entityId, isSeriesMode, userToken, lang, book, setBook, errorMessage, t, isCurrentlyOffline]);
}, [entityId, isSeriesMode, userToken, lang, book, setBook, errorMessage, t]);
const toggleTool = useCallback(async function (enabled: boolean): Promise<void> {
if (isSeriesMode) return;
try {
const requestData = {
bookId: book?.bookId,
toolName: 'locations',
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 ?? '', 'locations', 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: 'locations',
enabled: enabled
}, userToken, lang);
}
if (response && setBook && book) {
setToolEnabled(enabled);
@@ -277,54 +243,40 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
errorMessage(e.message);
}
}
}, [isSeriesMode, book, setBook, userToken, lang, errorMessage, isCurrentlyOffline, addToQueue, localSyncedBooks]);
}, [isSeriesMode, book, setBook, userToken, lang, errorMessage]);
const addSection = useCallback(async function (): Promise<void> {
if (isAddingSection) return;
if (!newSectionName.trim()) {
errorMessage(t('locationComponent.errorSectionNameEmpty'));
return;
}
setIsAddingSection(true);
try {
let sectionId: string;
if (isSeriesMode) {
// Series mode - dual logic
const addData = {
seriesId: entityId,
name: newSectionName,
};
if (isCurrentlyOffline() || localSeries) {
sectionId = await tauri.addSeriesLocationSection(addData);
} else {
sectionId = await System.authPostToServer<string>(
'series/location/section/add',
addData,
userToken,
lang
);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('add_series_location_section', {data: {...addData, id: sectionId}});
}
sectionId = await apiPost<string>(
'series/location/section/add',
{
seriesId: entityId,
name: newSectionName,
},
userToken,
lang
);
if (!sectionId) {
errorMessage(t('locationComponent.errorUnknownAddSection'));
return;
}
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
sectionId = await tauri.addLocationSection(newSectionName, entityId);
if (!sectionId) {
errorMessage(t('locationComponent.errorUnknownAddSection'));
return;
}
} else {
const requestData = {
sectionId = await apiPost<string>('location/section/add', {
bookId: entityId,
locationName: newSectionName,
};
if (isCurrentlyOffline() || book?.localBook) {
sectionId = await tauri.addLocationSection(requestData.locationName, requestData.bookId, undefined, undefined);
} else {
sectionId = await System.authPostToServer<string>('location/section/add', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_location_section', {data: {...requestData, id: sectionId}});
}
}
}, userToken, lang);
if (!sectionId) {
errorMessage(t('locationComponent.errorUnknownAddSection'));
return;
@@ -345,58 +297,42 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
} else {
errorMessage(t('locationComponent.errorUnknownAddSection'));
}
} finally {
setIsAddingSection(false);
}
}, [newSectionName, isSeriesMode, entityId, userToken, lang, errorMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, book, isAddingSection]);
}, [newSectionName, isSeriesMode, entityId, userToken, lang, errorMessage, t]);
const addElement = useCallback(async function (sectionId: string): Promise<void> {
if (isAddingElement) return;
if (!newElementNames[sectionId]?.trim()) {
errorMessage(t('locationComponent.errorElementNameEmpty'));
return;
}
setIsAddingElement(true);
try {
let elementId: string;
if (isSeriesMode) {
// Series mode - dual logic
const addData = {
locationId: sectionId,
name: newElementNames[sectionId],
};
if (isCurrentlyOffline() || localSeries) {
elementId = await tauri.addSeriesLocationElement(addData);
} else {
elementId = await System.authPostToServer<string>(
'series/location/element/add',
addData,
userToken,
lang
);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('add_series_location_element', {data: {...addData, id: elementId}});
}
elementId = await apiPost<string>(
'series/location/element/add',
{
locationId: sectionId,
name: newElementNames[sectionId],
},
userToken,
lang
);
if (!elementId) {
errorMessage(t('locationComponent.errorUnknownAddElement'));
return;
}
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
elementId = await tauri.addLocationElement(sectionId, newElementNames[sectionId]);
if (!elementId) {
errorMessage(t('locationComponent.errorUnknownAddElement'));
return;
}
} else {
const requestData = {
elementId = await apiPost<string>('location/element/add', {
bookId: entityId,
locationId: sectionId,
elementName: newElementNames[sectionId],
};
if (isCurrentlyOffline() || book?.localBook) {
elementId = await tauri.addLocationElement(requestData.locationId, requestData.elementName, undefined);
} else {
elementId = await System.authPostToServer<string>('location/element/add', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_location_element', {data: {...requestData, id: elementId}});
}
}
}, userToken, lang);
if (!elementId) {
errorMessage(t('locationComponent.errorUnknownAddElement'));
return;
@@ -424,60 +360,44 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
} else {
errorMessage(t('locationComponent.errorUnknownAddElement'));
}
} finally {
setIsAddingElement(false);
}
}, [newElementNames, isSeriesMode, entityId, userToken, lang, errorMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, book, isAddingElement]);
}, [newElementNames, isSeriesMode, entityId, userToken, lang, errorMessage, t]);
const addSubElement = useCallback(async function (sectionId: string, elementIndex: number): Promise<void> {
if (isAddingSubElement) return;
if (!newSubElementNames[elementIndex]?.trim()) {
errorMessage(t('locationComponent.errorSubElementNameEmpty'));
return;
}
setIsAddingSubElement(true);
const sectionIndex: number = sections.findIndex(function (section: LocationProps): boolean {
return section.id === sectionId;
});
try {
let subElementId: string;
if (isSeriesMode) {
// Series mode - dual logic
const addData = {
elementId: sections[sectionIndex].elements[elementIndex].id,
name: newSubElementNames[elementIndex],
};
if (isCurrentlyOffline() || localSeries) {
subElementId = await tauri.addSeriesLocationSubElement(addData);
} else {
subElementId = await System.authPostToServer<string>(
'series/location/sub-element/add',
addData,
userToken,
lang
);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('add_series_location_sub_element', {data: {...addData, id: subElementId}});
}
subElementId = await apiPost<string>(
'series/location/sub-element/add',
{
elementId: sections[sectionIndex].elements[elementIndex].id,
name: newSubElementNames[elementIndex],
},
userToken,
lang
);
if (!subElementId) {
errorMessage(t('locationComponent.errorUnknownAddSubElement'));
return;
}
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
subElementId = await tauri.addLocationSubElement(sections[sectionIndex].elements[elementIndex].id, newSubElementNames[elementIndex]);
if (!subElementId) {
errorMessage(t('locationComponent.errorUnknownAddSubElement'));
return;
}
} else {
const requestData = {
subElementId = await apiPost<string>('location/sub-element/add', {
elementId: sections[sectionIndex].elements[elementIndex].id,
subElementName: newSubElementNames[elementIndex],
};
if (isCurrentlyOffline() || book?.localBook) {
subElementId = await tauri.addLocationSubElement(requestData.elementId, requestData.subElementName, undefined);
} else {
subElementId = await System.authPostToServer<string>('location/sub-element/add', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_location_sub_element', {data: {...requestData, id: subElementId}});
}
}
}, userToken, lang);
if (!subElementId) {
errorMessage(t('locationComponent.errorUnknownAddSubElement'));
return;
@@ -501,39 +421,22 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
} else {
errorMessage(t('locationComponent.errorUnknownAddSubElement'));
}
} finally {
setIsAddingSubElement(false);
}
}, [sections, newSubElementNames, isSeriesMode, userToken, lang, errorMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, entityId, book, isAddingSubElement]);
}, [sections, newSubElementNames, isSeriesMode, userToken, lang, errorMessage, t]);
const removeSection = useCallback(async function (sectionId: string): Promise<void> {
try {
let success: boolean;
const deletedAt: number = System.timeStampInSeconds();
if (isSeriesMode) {
// Series mode - dual logic
const deleteData = {locationId: sectionId, deletedAt};
if (isCurrentlyOffline() || localSeries) {
success = await tauri.deleteSeriesLocation(deleteData.locationId, deleteData.deletedAt);
} else {
success = await System.authDeleteToServer<boolean>('series/location/delete', deleteData, userToken, lang);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('delete_series_location', {data: deleteData});
}
}
success = await apiDelete<boolean>('series/location/delete', {
locationId: sectionId
}, userToken, lang);
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
success = await tauri.deleteLocationSection(sectionId, book?.bookId ?? '', Date.now());
} else {
const requestData = {
locationId: sectionId, bookId: entityId, deletedAt,
};
if (isCurrentlyOffline() || book?.localBook) {
success = await tauri.deleteLocationSection(requestData.locationId, requestData.bookId, requestData.deletedAt);
} else {
success = await System.authDeleteToServer<boolean>('location/delete', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('delete_location_section', {data: requestData});
}
}
success = await apiDelete<boolean>('location/delete', {
locationId: sectionId,
}, userToken, lang);
}
if (!success) {
errorMessage(t('locationComponent.errorUnknownDeleteSection'));
@@ -551,39 +454,24 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
errorMessage(t('locationComponent.errorUnknownDeleteSection'));
}
}
}, [isSeriesMode, userToken, lang, errorMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, entityId, book]);
}, [isSeriesMode, userToken, lang, errorMessage, t]);
const removeElement = useCallback(async function (sectionId: string, elementIndex: number): Promise<void> {
try {
const elementId: string | undefined = sections.find(function (section: LocationProps): boolean {
return section.id === sectionId;
})?.elements[elementIndex].id;
let success: boolean;
const deletedAt: number = System.timeStampInSeconds();
if (isSeriesMode) {
// Series mode - dual logic
const deleteData = {elementId, deletedAt};
if (isCurrentlyOffline() || localSeries) {
success = await tauri.deleteSeriesLocationElement(deleteData.elementId!, deleteData.deletedAt);
} else {
success = await System.authDeleteToServer<boolean>('series/location/element/delete', deleteData, userToken, lang);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('delete_series_location_element', {data: deleteData});
}
}
success = await apiDelete<boolean>('series/location/element/delete', {
elementId: elementId
}, userToken, lang);
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
success = await tauri.deleteLocationElement(elementId ?? '', book?.bookId ?? '', Date.now());
} else {
const requestData = {
elementId, bookId: entityId, deletedAt,
};
if (isCurrentlyOffline() || book?.localBook) {
success = await tauri.deleteLocationElement(requestData.elementId!, requestData.bookId, requestData.deletedAt);
} else {
success = await System.authDeleteToServer<boolean>('location/element/delete', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('delete_location_element', {data: requestData});
}
}
success = await apiDelete<boolean>('location/element/delete', {
elementId: elementId,
}, userToken, lang);
}
if (!success) {
errorMessage(t('locationComponent.errorUnknownDeleteElement'));
@@ -604,39 +492,24 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
errorMessage(t('locationComponent.errorUnknownDeleteElement'));
}
}
}, [sections, isSeriesMode, userToken, lang, errorMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, entityId, book]);
}, [sections, isSeriesMode, userToken, lang, errorMessage, t]);
const removeSubElement = useCallback(async function (sectionId: string, elementIndex: number, subElementIndex: number): Promise<void> {
try {
const subElementId: string | undefined = sections.find(function (section: LocationProps): boolean {
return section.id === sectionId;
})?.elements[elementIndex].subElements[subElementIndex].id;
let success: boolean;
const deletedAt: number = System.timeStampInSeconds();
if (isSeriesMode) {
// Series mode - dual logic
const deleteData = {subElementId, deletedAt};
if (isCurrentlyOffline() || localSeries) {
success = await tauri.deleteSeriesLocationSubElement(deleteData.subElementId!, deleteData.deletedAt);
} else {
success = await System.authDeleteToServer<boolean>('series/location/sub-element/delete', deleteData, userToken, lang);
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === entityId)) {
addToQueue('delete_series_location_sub_element', {data: deleteData});
}
}
success = await apiDelete<boolean>('series/location/sub-element/delete', {
subElementId: subElementId
}, userToken, lang);
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
success = await tauri.deleteLocationSubElement(subElementId ?? '', book?.bookId ?? '', Date.now());
} else {
const requestData = {
subElementId, bookId: entityId, deletedAt,
};
if (isCurrentlyOffline() || book?.localBook) {
success = await tauri.deleteLocationSubElement(requestData.subElementId!, requestData.bookId, requestData.deletedAt);
} else {
success = await System.authDeleteToServer<boolean>('location/sub-element/delete', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('delete_location_sub_element', {data: requestData});
}
}
success = await apiDelete<boolean>('location/sub-element/delete', {
subElementId: subElementId,
}, userToken, lang);
}
if (!success) {
errorMessage(t('locationComponent.errorUnknownDeleteSubElement'));
@@ -657,8 +530,8 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
errorMessage(t('locationComponent.errorUnknownDeleteSubElement'));
}
}
}, [sections, isSeriesMode, userToken, lang, errorMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, entityId, book]);
}, [sections, isSeriesMode, userToken, lang, errorMessage, t]);
const updateElement = useCallback(function (sectionId: string, elementIndex: number, field: keyof Element, value: string): void {
setSections(function (prev: LocationProps[]): LocationProps[] {
const updated: LocationProps[] = [...prev];
@@ -670,7 +543,7 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
return updated;
});
}, []);
const updateSubElement = useCallback(function (sectionId: string, elementIndex: number, subElementIndex: number, field: keyof SubElement, value: string): void {
setSections(function (prev: LocationProps[]): LocationProps[] {
const updated: LocationProps[] = [...prev];
@@ -681,80 +554,47 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
return updated;
});
}, []);
const saveLocations = useCallback(async function (): Promise<boolean> {
const saveLocations = useCallback(async function (): Promise<void> {
try {
const requestData = {
locations: sections,
};
let response: boolean;
if (isCurrentlyOffline() || book?.localBook) {
response = await tauri.updateLocations(requestData.locations);
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
response = await tauri.updateLocations(sections) as boolean;
} else {
response = await System.authPostToServer<boolean>('location/update', requestData, userToken, lang);
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('update_locations', {data: requestData});
}
response = await apiPost<boolean>('location/update', {
locations: sections,
}, userToken, lang);
}
if (!response) {
errorMessage(t('locationComponent.errorUnknownSave'));
return false;
return;
}
successMessage(t('locationComponent.successSave'));
return true;
} catch (e: unknown) {
if (e instanceof Error) {
errorMessage(e.message);
} else {
errorMessage(t('locationComponent.errorUnknownSave'));
}
return false;
}
}, [sections, userToken, lang, errorMessage, successMessage, t, isCurrentlyOffline, addToQueue, localSyncedBooks, entityId, book]);
}, [sections, userToken, lang, errorMessage, successMessage, t]);
const exportToSeries = useCallback(async function (section: LocationProps): Promise<void> {
if (!bookSeriesId) return;
try {
const seriesLocationData = {
const seriesLocationId: string = await apiPost<string>('series/location/section/add', {
seriesId: bookSeriesId,
name: section.name,
};
let seriesLocationId: string;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
seriesLocationId = await tauri.addSeriesLocationSection(seriesLocationData);
} else {
// Mode online → Serveur
seriesLocationId = await System.authPostToServer<string>('series/location/section/add', seriesLocationData, userToken, lang);
// Si la série a une copie locale → addToQueue avec l'ID du serveur
if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === bookSeriesId)) {
addToQueue('add_series_location_section', {data: {...seriesLocationData, id: seriesLocationId}});
}
}
}, userToken, lang);
if (seriesLocationId) {
const updateData = {
const updateResponse: boolean = await apiPost<boolean>('location/section/update', {
sectionId: section.id,
sectionName: section.name,
seriesLocationId: seriesLocationId,
};
let updateResponse: boolean;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
updateResponse = await tauri.updateLocationSectionWithSeriesLink(updateData.sectionId, updateData.sectionName, updateData.seriesLocationId);
} else {
// Mode online → Serveur
updateResponse = await System.authPostToServer<boolean>('location/section/update', updateData, userToken, lang);
// Si le livre a une copie locale → addToQueue
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('update_location_section_with_series_link', {data: updateData});
}
}
}, userToken, lang);
if (updateResponse) {
setSections(function (prev: LocationProps[]): LocationProps[] {
return prev.map(function (s: LocationProps): LocationProps {
@@ -777,32 +617,24 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
errorMessage(e.message);
}
}
}, [bookSeriesId, userToken, lang, successMessage, errorMessage, t, isCurrentlyOffline, book, addToQueue, localSyncedBooks, localSyncedSeries, entityId]);
}, [bookSeriesId, userToken, lang, successMessage, errorMessage, t]);
const importFromSeries = useCallback(async function (seriesLocationId: string): Promise<void> {
const seriesLocation: SeriesLocationItem | undefined = seriesLocations.find(function (location: SeriesLocationItem): boolean {
return location.id === seriesLocationId;
});
if (!seriesLocation) return;
try {
const sectionData = {
bookId: entityId,
locationName: seriesLocation.name,
seriesLocationId: seriesLocationId,
};
let sectionId: string;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
sectionId = await tauri.addLocationSection(sectionData.locationName, sectionData.bookId, undefined, sectionData.seriesLocationId);
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
sectionId = await tauri.addLocationSection(seriesLocation.name, entityId, undefined, seriesLocationId);
} else {
// Mode online → Serveur
sectionId = await System.authPostToServer<string>('location/section/add', sectionData, userToken, lang);
// Si le livre a une copie locale → addToQueue avec l'ID du serveur
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_location_section', {data: {...sectionData, id: sectionId}});
}
sectionId = await apiPost<string>('location/section/add', {
bookId: entityId,
locationName: seriesLocation.name,
seriesLocationId: seriesLocationId,
}, userToken, lang);
}
if (!sectionId) {
@@ -813,48 +645,32 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
const importedElements: Element[] = [];
for (const seriesElement of seriesLocation.elements) {
const elementData = {
bookId: entityId,
locationId: sectionId,
elementName: seriesElement.name,
};
let elementId: string;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
elementId = await tauri.addLocationElement(elementData.locationId, elementData.elementName, undefined);
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
elementId = await tauri.addLocationElement(sectionId, seriesElement.name);
} else {
// Mode online → Serveur
elementId = await System.authPostToServer<string>('location/element/add', elementData, userToken, lang);
// Si le livre a une copie locale → addToQueue avec l'ID du serveur
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_location_element', {data: {...elementData, id: elementId}});
}
elementId = await apiPost<string>('location/element/add', {
bookId: entityId,
locationId: sectionId,
elementName: seriesElement.name,
}, userToken, lang);
}
if (!elementId) continue;
const importedSubElements: SubElement[] = [];
for (const seriesSubElement of seriesElement.subElements) {
const subElementData = {
elementId: elementId,
subElementName: seriesSubElement.name,
};
let subElementId: string;
if (isCurrentlyOffline() || book?.localBook) {
// Mode offline ou livre local → Tauri
subElementId = await tauri.addLocationSubElement(subElementData.elementId, subElementData.subElementName, undefined);
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
subElementId = await tauri.addLocationSubElement(elementId, seriesSubElement.name);
} else {
// Mode online → Serveur
subElementId = await System.authPostToServer<string>('location/sub-element/add', subElementData, userToken, lang);
// Si le livre a une copie locale → addToQueue avec l'ID du serveur
if (localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === entityId)) {
addToQueue('add_location_sub_element', {data: {...subElementData, id: subElementId}});
}
subElementId = await apiPost<string>('location/sub-element/add', {
elementId: elementId,
subElementName: seriesSubElement.name,
}, userToken, lang);
}
if (subElementId) {
importedSubElements.push({
id: subElementId,
@@ -863,7 +679,7 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
});
}
}
importedElements.push({
id: elementId,
name: seriesElement.name,
@@ -871,7 +687,7 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
subElements: importedSubElements,
});
}
const newLocation: LocationProps = {
id: sectionId,
name: seriesLocation.name,
@@ -886,15 +702,15 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
errorMessage(e.message);
}
}
}, [seriesLocations, entityId, userToken, lang, errorMessage, successMessage, t, isCurrentlyOffline, book, addToQueue, localSyncedBooks]);
}, [seriesLocations, entityId, userToken, lang, errorMessage, successMessage, t]);
// Navigation functions
const enterDetailMode = useCallback(function (sectionIndex: number): void {
setSelectedSectionIndex(sectionIndex);
setViewMode('detail');
setSectionsBackup(null);
}, []);
const enterEditMode = useCallback(function (): void {
setSectionsBackup(sections.map(function (section: LocationProps): LocationProps {
return {
@@ -909,14 +725,10 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
}));
setViewMode('edit');
}, [sections]);
const exitEditMode = useCallback(async function (save: boolean): Promise<void> {
if (save) {
const success: boolean = await saveLocations();
if (!success) {
// Stay in edit mode on error
return;
}
await saveLocations();
setViewMode('detail');
} else {
if (sectionsBackup) {
@@ -928,13 +740,13 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
}
setSectionsBackup(null);
}, [saveLocations, sectionsBackup]);
const backToList = useCallback(function (): void {
setSelectedSectionIndex(-1);
setSectionsBackup(null);
setViewMode('list');
}, []);
return {
// State
sections,
@@ -946,12 +758,12 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
newSectionName,
newElementNames,
newSubElementNames,
// Navigation state
viewMode,
selectedSectionIndex,
sectionsBackup,
// Actions
addSection,
addElement,
@@ -970,7 +782,7 @@ export function useLocations(config: UseLocationsConfig): UseLocationsReturn {
setNewSectionName,
setNewElementNames,
setNewSubElementNames,
// Navigation actions
enterDetailMode,
enterEditMode,