- 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.
789 lines
33 KiB
TypeScript
789 lines
33 KiB
TypeScript
'use client'
|
|
import {useCallback, useContext, useEffect, useState} from 'react';
|
|
import {SpellEditState, SpellListItem, SpellListResponse, SpellProps, SpellTagProps} from '@/lib/types/spell';
|
|
import {initialSpellState} from '@/lib/constants/spell';
|
|
import {SeriesSpellDetailResponse, SeriesSpellListItem, SeriesSpellListResponse} 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 {apiDelete, apiGet, apiPatch, apiPost, apiPut} 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 UseSpellsConfig {
|
|
entityType: 'book' | 'series';
|
|
entityId: string;
|
|
}
|
|
|
|
export interface UseSpellsReturn {
|
|
spells: SpellListItem[];
|
|
seriesSpells: SeriesSpellListItem[];
|
|
tags: SpellTagProps[];
|
|
selectedSpell: SpellEditState | null;
|
|
selectedSeriesSpell: SeriesSpellDetailResponse | null;
|
|
toolEnabled: boolean;
|
|
isLoading: boolean;
|
|
isSeriesMode: boolean;
|
|
bookSeriesId: string | null;
|
|
showTagManager: boolean;
|
|
viewMode: ViewMode;
|
|
spellBackup: SpellEditState | null;
|
|
selectSpell: (spell: SpellListItem) => Promise<void>;
|
|
addNewSpell: () => void;
|
|
clearSelection: () => void;
|
|
saveSpell: () => Promise<boolean>;
|
|
deleteSpell: (spellId: string) => Promise<void>;
|
|
updateSpellField: (key: keyof SpellEditState, value: string | string[] | null) => void;
|
|
toggleTool: (enabled: boolean) => Promise<void>;
|
|
importFromSeries: (seriesSpellId: string) => Promise<void>;
|
|
exportToSeries: () => Promise<void>;
|
|
refreshSeriesSpells: () => Promise<void>;
|
|
setSelectedSpell: React.Dispatch<React.SetStateAction<SpellEditState | null>>;
|
|
setShowTagManager: (show: boolean) => void;
|
|
|
|
enterDetailMode: (spell: SpellListItem) => Promise<void>;
|
|
enterEditMode: () => void;
|
|
exitEditMode: (save: boolean) => Promise<void>;
|
|
backToList: () => void;
|
|
|
|
createTag: (name: string, color: string) => Promise<SpellTagProps | null>;
|
|
updateTag: (tagId: string, name: string, color: string) => Promise<boolean>;
|
|
deleteTag: (tagId: string) => Promise<boolean>;
|
|
handleSyncComplete: () => Promise<void>;
|
|
}
|
|
|
|
export function useSpells(config: UseSpellsConfig): UseSpellsReturn {
|
|
const {entityType, entityId} = config;
|
|
const t = useTranslations();
|
|
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 [spells, setSpells] = useState<SpellListItem[]>([]);
|
|
const [seriesSpells, setSeriesSpells] = useState<SeriesSpellListItem[]>([]);
|
|
const [tags, setTags] = useState<SpellTagProps[]>([]);
|
|
const [selectedSpell, setSelectedSpell] = useState<SpellEditState | null>(null);
|
|
const [selectedSeriesSpell, setSelectedSeriesSpell] = useState<SeriesSpellDetailResponse | null>(null);
|
|
const [toolEnabled, setToolEnabled] = useState<boolean>(entityType === 'series' || (book?.tools?.spells ?? false));
|
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
const [showTagManager, setShowTagManager] = useState<boolean>(false);
|
|
|
|
const [viewMode, setViewMode] = useState<ViewMode>('list');
|
|
const [spellBackup, setSpellBackup] = useState<SpellEditState | null>(null);
|
|
|
|
const isSeriesMode: boolean = entityType === 'series';
|
|
const bookSeriesId: string | null = book?.seriesId || null;
|
|
const userToken: string = session?.accessToken || '';
|
|
|
|
useEffect(function (): void {
|
|
if (entityId) {
|
|
refreshSpells().then();
|
|
}
|
|
}, [entityId]);
|
|
|
|
useEffect(function (): void {
|
|
if (bookSeriesId && !isSeriesMode) {
|
|
refreshSeriesSpells().then();
|
|
}
|
|
}, [bookSeriesId, isSeriesMode]);
|
|
|
|
const refreshSeriesSpells = useCallback(async function (): Promise<void> {
|
|
if (!bookSeriesId) return;
|
|
try {
|
|
const response: SeriesSpellListResponse = await apiGet<SeriesSpellListResponse>(
|
|
'series/spell/list',
|
|
userToken,
|
|
lang,
|
|
{seriesid: bookSeriesId}
|
|
);
|
|
if (response) {
|
|
setSeriesSpells(response.spells);
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
}
|
|
}, [bookSeriesId, userToken, lang]);
|
|
|
|
const refreshSpells = useCallback(async function (): Promise<void> {
|
|
setIsLoading(true);
|
|
try {
|
|
if (isSeriesMode) {
|
|
const response: SeriesSpellListResponse = await apiGet<SeriesSpellListResponse>(
|
|
'series/spell/list',
|
|
userToken,
|
|
lang,
|
|
{seriesid: entityId}
|
|
);
|
|
if (response) {
|
|
const mappedSpells: SpellListItem[] = response.spells.map(function (spell: SeriesSpellListItem): SpellListItem {
|
|
return {
|
|
id: spell.id,
|
|
name: spell.name,
|
|
description: spell.description,
|
|
tags: spell.tags ? spell.tags.map(function (tagId: string): SpellTagProps {
|
|
const foundTag: SpellTagProps | undefined = response.tags.find(function (t: SpellTagProps): boolean {
|
|
return t.id === tagId;
|
|
});
|
|
return foundTag || {id: tagId, name: tagId, color: null};
|
|
}) : [],
|
|
};
|
|
});
|
|
setSpells(mappedSpells);
|
|
setTags(response.tags || []);
|
|
}
|
|
} else {
|
|
let response: SpellListResponse;
|
|
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
response = await tauri.getSpellList(entityId, book?.tools?.spells ?? false) as SpellListResponse;
|
|
} else {
|
|
response = await apiGet<SpellListResponse>('spell/list', userToken, lang, {
|
|
bookid: entityId
|
|
});
|
|
}
|
|
if (response) {
|
|
setSpells(response.spells.map(function (spell: SpellListItem): SpellListItem {
|
|
return {
|
|
...spell,
|
|
tags: spell.tags || []
|
|
};
|
|
}));
|
|
setTags(response.tags || []);
|
|
setToolEnabled(response.enabled);
|
|
if (setBook && book) {
|
|
setBook({
|
|
...book,
|
|
tools: {
|
|
characters: book.tools?.characters ?? false,
|
|
worlds: book.tools?.worlds ?? false,
|
|
locations: book.tools?.locations ?? false,
|
|
spells: response.enabled
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t("common.unknownError"));
|
|
}
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [entityId, isSeriesMode, userToken, lang, book, setBook, errorMessage, t]);
|
|
|
|
const selectSpell = useCallback(async function (spell: SpellListItem): Promise<void> {
|
|
const tagIds: string[] = spell.tags ? spell.tags.map(function (tag: SpellTagProps): string {
|
|
return tag.id;
|
|
}) : [];
|
|
|
|
setSelectedSpell({
|
|
id: spell.id,
|
|
name: spell.name,
|
|
description: spell.description,
|
|
appearance: '',
|
|
tags: tagIds,
|
|
powerLevel: null,
|
|
components: null,
|
|
limitations: null,
|
|
notes: null,
|
|
seriesSpellId: spell.seriesSpellId || null,
|
|
});
|
|
setSelectedSeriesSpell(null);
|
|
|
|
try {
|
|
if (isSeriesMode) {
|
|
const response: SeriesSpellDetailResponse = await apiGet<SeriesSpellDetailResponse>(
|
|
'series/spell/detail',
|
|
userToken,
|
|
lang,
|
|
{spellid: spell.id}
|
|
);
|
|
if (response) {
|
|
setSelectedSpell(function (prev: SpellEditState | null): SpellEditState | null {
|
|
if (!prev) return null;
|
|
return {
|
|
...prev,
|
|
appearance: response.appearance,
|
|
powerLevel: response.powerLevel,
|
|
components: response.components,
|
|
limitations: response.limitations,
|
|
notes: response.notes,
|
|
};
|
|
});
|
|
}
|
|
} else {
|
|
let response: SpellProps;
|
|
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
response = await tauri.getSpellDetail(spell.id) as SpellProps;
|
|
} else {
|
|
response = await apiGet<SpellProps>('spell/detail', userToken, lang, {
|
|
spellid: spell.id
|
|
});
|
|
}
|
|
if (response) {
|
|
setSelectedSpell(function (prev: SpellEditState | null): SpellEditState | null {
|
|
if (!prev) return null;
|
|
return {
|
|
...prev,
|
|
appearance: response.appearance,
|
|
powerLevel: response.powerLevel,
|
|
components: response.components,
|
|
limitations: response.limitations,
|
|
notes: response.notes,
|
|
seriesSpellId: response.seriesSpellId || null,
|
|
};
|
|
});
|
|
|
|
if (response.seriesSpellId) {
|
|
const seriesSpellResponse: SeriesSpellDetailResponse = await apiGet<SeriesSpellDetailResponse>(
|
|
'series/spell/detail',
|
|
userToken,
|
|
lang,
|
|
{spellid: response.seriesSpellId}
|
|
);
|
|
if (seriesSpellResponse) {
|
|
setSelectedSeriesSpell(seriesSpellResponse);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
}
|
|
}, [isSeriesMode, userToken, lang, errorMessage]);
|
|
|
|
const addNewSpell = useCallback(function (): void {
|
|
setSelectedSpell({...initialSpellState});
|
|
setSelectedSeriesSpell(null);
|
|
setViewMode('edit');
|
|
setSpellBackup(null);
|
|
}, []);
|
|
|
|
const clearSelection = useCallback(function (): void {
|
|
setSelectedSpell(null);
|
|
setSelectedSeriesSpell(null);
|
|
setViewMode('list');
|
|
setSpellBackup(null);
|
|
}, []);
|
|
|
|
const updateSpellField = useCallback(function (key: keyof SpellEditState, value: string | string[] | null): void {
|
|
if (selectedSpell) {
|
|
setSelectedSpell({...selectedSpell, [key]: value});
|
|
}
|
|
}, [selectedSpell]);
|
|
|
|
const toggleTool = useCallback(async function (enabled: boolean): Promise<void> {
|
|
if (isSeriesMode) return;
|
|
try {
|
|
let response: boolean;
|
|
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
response = await tauri.updateBookToolSetting(book?.bookId ?? '', 'spells', enabled);
|
|
} else {
|
|
response = await apiPatch<boolean>('book/tool-setting', {
|
|
bookId: book?.bookId,
|
|
toolName: 'spells',
|
|
enabled: enabled
|
|
}, userToken, lang);
|
|
}
|
|
if (response && setBook && book) {
|
|
setToolEnabled(enabled);
|
|
setBook({
|
|
...book,
|
|
tools: {
|
|
characters: book.tools?.characters ?? false,
|
|
worlds: book.tools?.worlds ?? false,
|
|
locations: book.tools?.locations ?? false,
|
|
spells: enabled
|
|
}
|
|
});
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
}
|
|
}, [isSeriesMode, book, setBook, userToken, lang, errorMessage]);
|
|
|
|
const saveSpell = useCallback(async function (): Promise<boolean> {
|
|
if (!selectedSpell) return false;
|
|
|
|
if (selectedSpell.id === null) {
|
|
return await addSpellInternal(selectedSpell);
|
|
} else {
|
|
return await updateSpellInternal(selectedSpell);
|
|
}
|
|
}, [selectedSpell]);
|
|
|
|
async function addSpellInternal(spell: SpellEditState): Promise<boolean> {
|
|
if (!spell.name) {
|
|
errorMessage(t("spellComponent.errorNameRequired"));
|
|
return false;
|
|
}
|
|
try {
|
|
let newSpellId: string;
|
|
const spellData: Record<string, string | string[] | null> = {
|
|
...(isSeriesMode ? {seriesId: entityId} : {bookId: entityId}),
|
|
name: spell.name,
|
|
description: spell.description,
|
|
appearance: spell.appearance,
|
|
tags: spell.tags,
|
|
powerLevel: spell.powerLevel,
|
|
components: spell.components,
|
|
limitations: spell.limitations,
|
|
notes: spell.notes,
|
|
};
|
|
if (isSeriesMode) {
|
|
newSpellId = await apiPost<string>('series/spell/add', spellData, userToken, lang);
|
|
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
newSpellId = await tauri.createSpell(entityId, spellData) as string;
|
|
} else {
|
|
newSpellId = await apiPost<string>('spell/add', spellData, userToken, lang);
|
|
}
|
|
if (!newSpellId) {
|
|
errorMessage(t("spellComponent.errorAddSpell"));
|
|
return false;
|
|
}
|
|
const resolvedTags: SpellTagProps[] = tags.filter(function (tag: SpellTagProps): boolean {
|
|
return spell.tags.includes(tag.id);
|
|
});
|
|
const newSpellListItem: SpellListItem = {
|
|
id: newSpellId,
|
|
name: spell.name,
|
|
description: spell.description.length > 150
|
|
? spell.description.substring(0, 150) + '...'
|
|
: spell.description,
|
|
tags: resolvedTags,
|
|
};
|
|
setSpells(function (prev: SpellListItem[]): SpellListItem[] {
|
|
return [...prev, newSpellListItem];
|
|
});
|
|
setSelectedSpell(null);
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t("common.unknownError"));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function updateSpellInternal(spellToUpdate: SpellEditState): Promise<boolean> {
|
|
if (!spellToUpdate.id) return false;
|
|
if (!spellToUpdate.name) {
|
|
errorMessage(t("spellComponent.errorNameRequired"));
|
|
return false;
|
|
}
|
|
try {
|
|
const spellData: Record<string, string | string[] | null> = {
|
|
id: spellToUpdate.id,
|
|
name: spellToUpdate.name,
|
|
description: spellToUpdate.description,
|
|
appearance: spellToUpdate.appearance,
|
|
tags: spellToUpdate.tags,
|
|
powerLevel: spellToUpdate.powerLevel,
|
|
components: spellToUpdate.components,
|
|
limitations: spellToUpdate.limitations,
|
|
notes: spellToUpdate.notes,
|
|
};
|
|
let success: boolean;
|
|
if (isSeriesMode) {
|
|
success = await apiPut<boolean>('series/spell/update', spellData, userToken, lang);
|
|
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
success = await tauri.updateSpell(spellToUpdate.id!, spellData);
|
|
} else {
|
|
success = await apiPut<boolean>('spell/update', spellData, userToken, lang);
|
|
}
|
|
if (!success) {
|
|
errorMessage(t("spellComponent.errorUpdateSpell"));
|
|
return false;
|
|
}
|
|
const resolvedTags: SpellTagProps[] = tags.filter(function (tag: SpellTagProps): boolean {
|
|
return spellToUpdate.tags.includes(tag.id);
|
|
});
|
|
setSpells(function (prev: SpellListItem[]): SpellListItem[] {
|
|
return prev.map(function (spell: SpellListItem): SpellListItem {
|
|
return spell.id === spellToUpdate.id ? {
|
|
id: spellToUpdate.id,
|
|
name: spellToUpdate.name,
|
|
description: spellToUpdate.description.length > 150
|
|
? spellToUpdate.description.substring(0, 150) + '...'
|
|
: spellToUpdate.description,
|
|
tags: resolvedTags,
|
|
} : spell;
|
|
});
|
|
});
|
|
setSelectedSpell(null);
|
|
successMessage(t("spellComponent.successUpdate"));
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t("common.unknownError"));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const deleteSpell = useCallback(async function (spellId: string): Promise<void> {
|
|
try {
|
|
let success: boolean;
|
|
if (isSeriesMode) {
|
|
success = await apiDelete<boolean>('series/spell/delete', {spellId}, userToken, lang);
|
|
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
success = await tauri.deleteSpell(spellId, book?.bookId ?? '', Date.now());
|
|
} else {
|
|
success = await apiDelete<boolean>('spell/delete', {spellId}, userToken, lang);
|
|
}
|
|
if (!success) {
|
|
errorMessage(t("spellComponent.errorDeleteSpell"));
|
|
return;
|
|
}
|
|
setSpells(function (prev: SpellListItem[]): SpellListItem[] {
|
|
return prev.filter(function (s: SpellListItem): boolean {
|
|
return s.id !== spellId;
|
|
});
|
|
});
|
|
setSelectedSpell(null);
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t("common.unknownError"));
|
|
}
|
|
}
|
|
}, [isSeriesMode, userToken, lang, errorMessage, t]);
|
|
|
|
const exportToSeries = useCallback(async function (): Promise<void> {
|
|
if (!selectedSpell || !selectedSpell.id || !bookSeriesId) return;
|
|
|
|
try {
|
|
const seriesSpellId: string = await apiPost<string>(
|
|
'series/spell/add',
|
|
{
|
|
seriesId: bookSeriesId,
|
|
name: selectedSpell.name,
|
|
description: selectedSpell.description,
|
|
appearance: selectedSpell.appearance || '',
|
|
tags: [],
|
|
powerLevel: selectedSpell.powerLevel || null,
|
|
components: selectedSpell.components || null,
|
|
limitations: selectedSpell.limitations || null,
|
|
notes: selectedSpell.notes || null,
|
|
},
|
|
userToken,
|
|
lang
|
|
);
|
|
|
|
if (seriesSpellId) {
|
|
const updateSuccess: boolean = await apiPut<boolean>('spell/update', {
|
|
id: selectedSpell.id,
|
|
name: selectedSpell.name,
|
|
description: selectedSpell.description,
|
|
appearance: selectedSpell.appearance,
|
|
tags: selectedSpell.tags,
|
|
powerLevel: selectedSpell.powerLevel,
|
|
components: selectedSpell.components,
|
|
limitations: selectedSpell.limitations,
|
|
notes: selectedSpell.notes,
|
|
seriesSpellId: seriesSpellId
|
|
}, userToken, lang);
|
|
|
|
if (updateSuccess) {
|
|
setSelectedSpell({...selectedSpell, seriesSpellId: seriesSpellId});
|
|
setSpells(function (prev: SpellListItem[]): SpellListItem[] {
|
|
return prev.map(function (s: SpellListItem): SpellListItem {
|
|
return s.id === selectedSpell.id ? {...s, seriesSpellId: seriesSpellId} : s;
|
|
});
|
|
});
|
|
const newSeriesSpell: SeriesSpellListItem = {
|
|
id: seriesSpellId,
|
|
name: selectedSpell.name,
|
|
description: selectedSpell.description.length > 150
|
|
? selectedSpell.description.substring(0, 150) + '...'
|
|
: selectedSpell.description,
|
|
tags: null,
|
|
};
|
|
setSeriesSpells(function (prev: SeriesSpellListItem[]): SeriesSpellListItem[] {
|
|
return [...prev, newSeriesSpell];
|
|
});
|
|
successMessage(t("spellComponent.exportSuccess"));
|
|
}
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
}
|
|
}, [selectedSpell, bookSeriesId, userToken, lang, successMessage, errorMessage, t]);
|
|
|
|
const importFromSeries = useCallback(async function (seriesSpellId: string): Promise<void> {
|
|
try {
|
|
const seriesSpellDetail: SeriesSpellDetailResponse = await apiGet<SeriesSpellDetailResponse>(
|
|
'series/spell/detail',
|
|
userToken,
|
|
lang,
|
|
{spellid: seriesSpellId}
|
|
);
|
|
|
|
if (!seriesSpellDetail) return;
|
|
|
|
let createdSpellId: string;
|
|
const spellImportData: Record<string, unknown> = {
|
|
bookId: entityId,
|
|
name: seriesSpellDetail.name,
|
|
description: seriesSpellDetail.description,
|
|
appearance: seriesSpellDetail.appearance || '',
|
|
tags: [],
|
|
powerLevel: seriesSpellDetail.powerLevel,
|
|
components: seriesSpellDetail.components,
|
|
limitations: seriesSpellDetail.limitations,
|
|
notes: seriesSpellDetail.notes,
|
|
seriesSpellId: seriesSpellId
|
|
};
|
|
if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
createdSpellId = await tauri.createSpell(entityId, spellImportData) as string;
|
|
} else {
|
|
createdSpellId = await apiPost<string>('spell/add', spellImportData, userToken, lang);
|
|
}
|
|
|
|
if (createdSpellId) {
|
|
const newSpellListItem: SpellListItem = {
|
|
id: createdSpellId,
|
|
name: seriesSpellDetail.name,
|
|
description: seriesSpellDetail.description.length > 150
|
|
? seriesSpellDetail.description.substring(0, 150) + '...'
|
|
: seriesSpellDetail.description,
|
|
tags: [],
|
|
seriesSpellId: seriesSpellId,
|
|
};
|
|
setSpells(function (prev: SpellListItem[]): SpellListItem[] {
|
|
return [...prev, newSpellListItem];
|
|
});
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
}
|
|
}, [entityId, userToken, lang, errorMessage, successMessage, t]);
|
|
|
|
const createTag = useCallback(async function (name: string, color: string): Promise<SpellTagProps | null> {
|
|
try {
|
|
if (isSeriesMode) {
|
|
const tagId: string = await apiPost<string>(
|
|
'series/spell/tag/add',
|
|
{
|
|
seriesId: entityId,
|
|
name: name,
|
|
color: color,
|
|
},
|
|
userToken,
|
|
lang
|
|
);
|
|
if (tagId) {
|
|
const newTag: SpellTagProps = {id: tagId, name: name, color: color};
|
|
setTags(function (prev: SpellTagProps[]): SpellTagProps[] {
|
|
return [...prev, newTag];
|
|
});
|
|
return newTag;
|
|
}
|
|
return null;
|
|
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
const result = await tauri.createSpellTag(entityId, name, color);
|
|
const newTag: SpellTagProps = result as SpellTagProps;
|
|
if (newTag && newTag.id) {
|
|
setTags(function (prev: SpellTagProps[]): SpellTagProps[] {
|
|
return [...prev, newTag];
|
|
});
|
|
return newTag;
|
|
}
|
|
return null;
|
|
} else {
|
|
const newTag: SpellTagProps = await apiPost<SpellTagProps>('spell/tag/add', {
|
|
bookId: entityId,
|
|
name: name,
|
|
color: color,
|
|
}, userToken, lang);
|
|
if (newTag && newTag.id) {
|
|
setTags(function (prev: SpellTagProps[]): SpellTagProps[] {
|
|
return [...prev, newTag];
|
|
});
|
|
return newTag;
|
|
}
|
|
return null;
|
|
}
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
return null;
|
|
}
|
|
}, [isSeriesMode, entityId, userToken, lang, errorMessage]);
|
|
|
|
const updateTag = useCallback(async function (tagId: string, name: string, color: string): Promise<boolean> {
|
|
try {
|
|
let success: boolean;
|
|
if (isSeriesMode) {
|
|
success = await apiPut<boolean>('series/spell/tag/update', {
|
|
tagId, name, color
|
|
}, userToken, lang);
|
|
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
success = await tauri.updateSpellTag(tagId, name, color);
|
|
} else {
|
|
success = await apiPut<boolean>('spell/tag/update', {
|
|
tagId, name, color
|
|
}, userToken, lang);
|
|
}
|
|
if (!success) {
|
|
errorMessage(t("spellComponent.updateSuccess"));
|
|
return false;
|
|
}
|
|
setTags(function (prev: SpellTagProps[]): SpellTagProps[] {
|
|
return prev.map(function (tag: SpellTagProps): SpellTagProps {
|
|
return tag.id === tagId ? {id: tagId, name: name, color: color} : tag;
|
|
});
|
|
});
|
|
return true;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
return false;
|
|
}
|
|
}, [isSeriesMode, userToken, lang, errorMessage, t]);
|
|
|
|
const deleteTag = useCallback(async function (tagId: string): Promise<boolean> {
|
|
try {
|
|
let success: boolean;
|
|
if (isSeriesMode) {
|
|
success = await apiDelete<boolean>('series/spell/tag/delete', {tagId}, userToken, lang);
|
|
} else if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
|
|
success = await tauri.deleteSpellTag(tagId, book?.bookId ?? '', Date.now());
|
|
} else {
|
|
success = await apiDelete<boolean>('spell/tag/delete', {
|
|
tagId, bookId: entityId
|
|
}, userToken, lang);
|
|
}
|
|
if (success) {
|
|
setTags(function (prev: SpellTagProps[]): SpellTagProps[] {
|
|
return prev.filter(function (tag: SpellTagProps): boolean {
|
|
return tag.id !== tagId;
|
|
});
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (e: unknown) {
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
}
|
|
return false;
|
|
}
|
|
}, [isSeriesMode, entityId, userToken, lang, errorMessage]);
|
|
|
|
const handleSyncComplete = useCallback(async function (): Promise<void> {
|
|
if (selectedSpell?.seriesSpellId) {
|
|
const seriesSpellResponse: SeriesSpellDetailResponse = await apiGet<SeriesSpellDetailResponse>(
|
|
'series/spell/detail',
|
|
userToken,
|
|
lang,
|
|
{spellid: selectedSpell.seriesSpellId}
|
|
);
|
|
if (seriesSpellResponse) {
|
|
setSelectedSeriesSpell(seriesSpellResponse);
|
|
}
|
|
}
|
|
}, [selectedSpell?.seriesSpellId, userToken, lang]);
|
|
|
|
const enterDetailMode = useCallback(async function (spell: SpellListItem): Promise<void> {
|
|
await selectSpell(spell);
|
|
setViewMode('detail');
|
|
setSpellBackup(null);
|
|
}, [selectSpell]);
|
|
|
|
const enterEditMode = useCallback(function (): void {
|
|
if (selectedSpell) {
|
|
setSpellBackup({...selectedSpell});
|
|
}
|
|
setViewMode('edit');
|
|
}, [selectedSpell]);
|
|
|
|
const exitEditMode = useCallback(async function (save: boolean): Promise<void> {
|
|
if (save) {
|
|
const success: boolean = await saveSpell();
|
|
if (!success) return;
|
|
if (spellBackup) {
|
|
setViewMode('detail');
|
|
} else {
|
|
setViewMode('list');
|
|
}
|
|
} else {
|
|
if (spellBackup) {
|
|
setSelectedSpell(spellBackup);
|
|
setViewMode('detail');
|
|
} else {
|
|
setSelectedSpell(null);
|
|
setViewMode('list');
|
|
}
|
|
}
|
|
setSpellBackup(null);
|
|
}, [saveSpell, spellBackup]);
|
|
|
|
const backToList = useCallback(function (): void {
|
|
setSelectedSpell(null);
|
|
setSelectedSeriesSpell(null);
|
|
setSpellBackup(null);
|
|
setViewMode('list');
|
|
}, []);
|
|
|
|
return {
|
|
spells,
|
|
seriesSpells,
|
|
tags,
|
|
selectedSpell,
|
|
selectedSeriesSpell,
|
|
toolEnabled,
|
|
isLoading,
|
|
isSeriesMode,
|
|
bookSeriesId,
|
|
showTagManager,
|
|
viewMode,
|
|
spellBackup,
|
|
selectSpell,
|
|
addNewSpell,
|
|
clearSelection,
|
|
saveSpell,
|
|
deleteSpell,
|
|
updateSpellField,
|
|
toggleTool,
|
|
importFromSeries,
|
|
exportToSeries,
|
|
refreshSeriesSpells,
|
|
setSelectedSpell,
|
|
setShowTagManager,
|
|
enterDetailMode,
|
|
enterEditMode,
|
|
exitEditMode,
|
|
backToList,
|
|
createTag,
|
|
updateTag,
|
|
deleteTag,
|
|
handleSyncComplete,
|
|
};
|
|
}
|