'use client' import React, {createContext, forwardRef, useContext, useEffect, useImperativeHandle, useState} from 'react'; import {BookContext, BookContextProps} from '@/context/BookContext'; import {SessionContext, SessionContextProps} from '@/context/SessionContext'; import {AlertContext, AlertContextProps} from '@/context/AlertContext'; import {apiGet, apiPost} from '@/lib/api/client'; import {isDesktop} from '@/lib/configs'; import * as tauri from '@/lib/tauri'; import OfflineContext, {OfflineContextType} from '@/context/OfflineContext'; import {BooksSyncContext, BooksSyncContextProps} from '@/context/BooksSyncContext'; import {SyncedBook} from '@/lib/types/synced-book'; import {LocalSyncQueueContext, LocalSyncQueueContextProps} from '@/context/SyncQueueContext'; import {Act as ActType, Incident, Issue, PlotPoint} from '@/lib/types/book'; import {ActChapter, ChapterListProps} from '@/lib/types/chapter'; import MainChapter from "@/components/book/settings/story/MainChapter"; import Issues from "@/components/book/settings/story/Issue"; import Act from "@/components/book/settings/story/Act"; import {useTranslations} from '@/lib/i18n'; import {LangContext, LangContextProps} from "@/context/LangContext"; import {SettingRef} from "@/lib/types/settings"; export const StoryContext = createContext<{ acts: ActType[]; setActs: React.Dispatch>; mainChapters: ChapterListProps[]; setMainChapters: React.Dispatch>; issues: Issue[]; setIssues: React.Dispatch>; }>({ acts: [], setActs: (): void => { }, mainChapters: [], setMainChapters: (): void => { }, issues: [], setIssues: (): void => { }, }); interface StoryFetchData { mainChapter: ChapterListProps[]; acts: ActType[]; issues: Issue[]; } export function Story(_props: object, ref: React.ForwardedRef): React.JSX.Element { const t = useTranslations(); const {lang}: LangContextProps = useContext(LangContext); const {book}: BookContextProps = useContext(BookContext); const bookId: string = book?.bookId ? book.bookId.toString() : ''; const {session}: SessionContextProps = useContext(SessionContext); const userToken: string = session.accessToken; const {errorMessage, successMessage}: AlertContextProps = useContext(AlertContext); const {isCurrentlyOffline}: OfflineContextType = useContext(OfflineContext); const {addToQueue}: LocalSyncQueueContextProps = useContext(LocalSyncQueueContext); const {localSyncedBooks}: BooksSyncContextProps = useContext(BooksSyncContext); const [acts, setActs] = useState([]); const [issues, setIssues] = useState([]); const [mainChapters, setMainChapters] = useState([]); const [isLoading, setIsLoading] = useState(true); useImperativeHandle(ref, function (): SettingRef { return { handleSave: handleSave }; }); useEffect((): void => { getStoryData().then(); }, [userToken]); useEffect((): void => { cleanupDeletedChapters(); }, [mainChapters]); async function getStoryData(): Promise { try { let response: StoryFetchData; if (isDesktop && (isCurrentlyOffline() || book?.localBook)) { response = await tauri.getBookStory(bookId) as StoryFetchData; } else { response = await apiGet(`book/story`, userToken, lang, { bookid: bookId, }); } if (response) { setActs(response.acts); setMainChapters(response.mainChapter); setIssues(response.issues); setIsLoading(false); } } catch (e: unknown) { setIsLoading(false); if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("story.errorUnknownFetch")); } } } function cleanupDeletedChapters(): void { const existingChapterIds: string[] = mainChapters.map((ch: ChapterListProps): string => ch.chapterId); const updatedActs: ActType[] = acts.map((act: ActType): ActType => { const filteredChapters: ActChapter[] = act.chapters?.filter((chapter: ActChapter): boolean => existingChapterIds.includes(chapter.chapterId)) || []; const updatedIncidents: Incident[] = act.incidents?.map((incident: Incident): Incident => { return { ...incident, chapters: incident.chapters?.filter((chapter: ActChapter): boolean => existingChapterIds.includes(chapter.chapterId)) || [] }; }) || []; const updatedPlotPoints: PlotPoint[] = act.plotPoints?.map((plotPoint: PlotPoint): PlotPoint => { return { ...plotPoint, chapters: plotPoint.chapters?.filter((chapter: ActChapter): boolean => existingChapterIds.includes(chapter.chapterId)) || [] }; }) || []; return { ...act, chapters: filteredChapters, incidents: updatedIncidents, plotPoints: updatedPlotPoints, }; }); setActs(updatedActs); } async function handleSave(): Promise { try { let response: boolean; if (isDesktop && (isCurrentlyOffline() || book?.localBook)) { response = await tauri.updateBookStory({ bookId, acts, mainChapters, issues, }); } else { const storyData = {bookId, acts, mainChapters, issues}; response = await apiPost('book/story', storyData, userToken, lang); if (isDesktop && localSyncedBooks.find((sb: SyncedBook): boolean => sb.id === bookId)) { addToQueue('update_book_story', storyData); } } if (!response) { errorMessage(t("story.errorSave")) } successMessage(t("story.successSave")); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("story.errorUnknownSave")); } } } return (
); } export default forwardRef(Story);