import {ChapterListProps, ChapterProps} from "@/lib/models/Chapter"; import {useContext, useEffect, useRef, useState} from "react"; import System from "@/lib/models/System"; import {BookContext} from "@/context/BookContext"; import {AlertContext} from "@/context/AlertContext"; import {ChapterContext} from "@/context/ChapterContext"; import {SessionContext} from "@/context/SessionContext"; import {faSheetPlastic} from "@fortawesome/free-solid-svg-icons"; import ListItem from "@/components/ListItem"; import AlertBox from "@/components/AlertBox"; import {useTranslations} from "next-intl"; import InlineAddInput from "@/components/form/InlineAddInput"; import {LangContext} from "@/context/LangContext"; import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; export default function ScribeChapterComponent() { const t = useTranslations(); const {lang} = useContext(LangContext); const {isCurrentlyOffline} = useContext(OfflineContext); const {book} = useContext(BookContext); const {chapter, setChapter} = useContext(ChapterContext); const {errorMessage, successMessage} = useContext(AlertContext); const {session} = useContext(SessionContext); const userToken: string = session?.accessToken ? session?.accessToken : ''; const [chapters, setChapters] = useState([]) const [newChapterName, setNewChapterName] = useState(''); const [newChapterOrder, setNewChapterOrder] = useState(1); const [deleteConfirmationMessage, setDeleteConfirmationMessage] = useState(false); const [removeChapterId, setRemoveChapterId] = useState(''); const chapterRefs = useRef>(new Map()); const scrollContainerRef = useRef(null); useEffect((): void => { getChapterList().then(); }, [book]); useEffect((): void => { setNewChapterOrder(getNextChapterOrder()); }, [chapters]); useEffect((): void => { if (chapter?.chapterId && scrollContainerRef.current) { setTimeout(() => { const element = chapterRefs.current.get(chapter.chapterId); const container = scrollContainerRef.current; if (element && container) { const containerRect:DOMRect = container.getBoundingClientRect(); const elementRect:DOMRect = element.getBoundingClientRect(); const relativeTop:number = elementRect.top - containerRect.top + container.scrollTop; const scrollPosition:number = relativeTop - (containerRect.height / 2) + (elementRect.height / 2); container.scrollTo({ top: Math.max(0, scrollPosition), behavior: 'smooth' }); } }, 100); } }, [chapter?.chapterId]); function getNextChapterOrder(): number { const maxOrder: number = Math.max(0, ...chapters.map((chap: ChapterListProps) => chap.chapterOrder ?? 0)); return maxOrder + 1; } async function getChapterList(): Promise { try { let response: ChapterListProps[]|null; if (isCurrentlyOffline()){ response = await window.electron.invoke('db:book:chapters', book?.bookId) } else { response = await System.authGetQueryToServer(`book/chapters?id=${book?.bookId}`, userToken, lang); } if (response) { setChapters(response); } } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("scribeChapterComponent.errorFetchChapters")); } } } async function getChapter(chapterId: string): Promise { const version: number = chapter?.chapterContent.version ? chapter?.chapterContent.version : 2; try { let response: ChapterProps | null = null if (isCurrentlyOffline()) { response = await window.electron.invoke('db:chapter:whole', { bookid: book?.bookId, id: chapterId, version: version, }) } else { response = await System.authGetQueryToServer(`chapter/whole`, userToken, lang, { bookid: book?.bookId, id: chapterId, version: version, }); } if (!response) { errorMessage(t("scribeChapterComponent.errorFetchChapter")); return; } setChapter(response); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("scribeChapterComponent.errorFetchChapter")); } } } async function handleChapterUpdate(chapterId: string, title: string, chapterOrder: number): Promise { try { let response: boolean; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:chapter:update',{ chapterId: chapterId, chapterOrder: chapterOrder, title: title, }) } else { response = await System.authPostToServer('chapter/update', { chapterId: chapterId, chapterOrder: chapterOrder, title: title, }, userToken, lang); } if (!response) { errorMessage(t("scribeChapterComponent.errorChapterUpdate")); return; } successMessage(t("scribeChapterComponent.successUpdate")); setChapters((prevState: ChapterListProps[]): ChapterListProps[] => { return prevState.map((chapter: ChapterListProps): ChapterListProps => { if (chapter.chapterId === chapterId) { chapter.chapterOrder = chapterOrder; chapter.title = title; } return chapter; }); }); } catch (e: unknown) { if (e instanceof Error) { errorMessage(t("scribeChapterComponent.errorChapterUpdateFr")); } else { errorMessage(t("scribeChapterComponent.errorChapterUpdateEn")); } } } async function handleDeleteConfirmation(chapterId: string): Promise { setDeleteConfirmationMessage(true); setRemoveChapterId(chapterId); } async function handleDeleteChapter(): Promise { try { setDeleteConfirmationMessage(false); let response:boolean = false; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:chapter:remove', removeChapterId) } else { response = await System.authDeleteToServer('chapter/remove', { chapterId: removeChapterId, }, userToken, lang); } if (!response) { errorMessage(t("scribeChapterComponent.errorChapterDelete")); return; } const updatedChapters: ChapterListProps[] = chapters.filter( (chapter: ChapterListProps): boolean => chapter.chapterId !== removeChapterId, ); setChapters(updatedChapters); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("scribeChapterComponent.unknownErrorChapterDelete")); } } } async function handleAddChapter(chapterOrder: number): Promise { if (!newChapterName && chapterOrder >= 0) { errorMessage(t("scribeChapterComponent.errorChapterNameRequired")); return; } const chapterTitle: string = chapterOrder >= 0 ? newChapterName : book?.title as string; try { let chapterId:string|null = null; if (isCurrentlyOffline()){ chapterId = await window.electron.invoke('db:chapter:add', { bookId: book?.bookId, chapterOrder: chapterOrder, title: chapterTitle }) } else { chapterId = await System.authPostToServer('chapter/add', { bookId: book?.bookId, chapterOrder: chapterOrder, title: chapterTitle }, userToken, lang); } if (!chapterId) { errorMessage(t("scribeChapterComponent.errorChapterSubmit", {chapterName: newChapterName})); return; } const newChapter: ChapterListProps = { chapterId: chapterId, title: chapterTitle, chapterOrder: chapterOrder } setChapters((prevState: ChapterListProps[]): ChapterListProps[] => { return [newChapter, ...prevState] }) await getChapter(chapterId); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("scribeChapterComponent.errorChapterSubmit", {chapterName: newChapterName})); } } } return (

{t("scribeChapterComponent.sheetHeading")}

    { chapters.filter((chap: ChapterListProps): boolean => { return chap.chapterOrder !== undefined && chap.chapterOrder < 0; }) .sort((a: ChapterListProps, b: ChapterListProps): number => { const aOrder: number = a.chapterOrder ?? 0; const bOrder: number = b.chapterOrder ?? 0; return aOrder - bOrder; }).map((chap: ChapterListProps) => (
    { if (el) { chapterRefs.current.set(chap.chapterId, el); } else { chapterRefs.current.delete(chap.chapterId); } }}> => getChapter(chap.chapterId)} selectedId={chapter?.chapterId ?? ''} id={chap.chapterId} text={chap.title}/>
    )) } { chapters.filter((chap: ChapterListProps): boolean => { return chap.chapterOrder !== undefined && chap.chapterOrder < 0; }).length === 0 &&
  • => handleAddChapter(-1)} className="group p-3 bg-secondary/30 rounded-xl hover:bg-secondary cursor-pointer transition-all hover:shadow-md border border-secondary/30 hover:border-primary/30"> {t("scribeChapterComponent.createSheet")}
  • }

{t("scribeChapterComponent.chaptersHeading")}

    { chapters.filter((chap: ChapterListProps): boolean => { return !(chap.chapterOrder && chap.chapterOrder < 0); }) .sort((a: ChapterListProps, b: ChapterListProps): number => { const aOrder: number = a.chapterOrder ?? 0; const bOrder: number = b.chapterOrder ?? 0; return aOrder - bOrder; }).map((chap: ChapterListProps) => (
    { if (el) { chapterRefs.current.set(chap.chapterId, el); } else { chapterRefs.current.delete(chap.chapterId); } }}> => getChapter(chap.chapterId)} isEditable={true} handleUpdate={handleChapterUpdate} handleDelete={handleDeleteConfirmation} selectedId={chapter?.chapterId ?? ''} id={chap.chapterId} text={chap.title} numericalIdentifier={chap.chapterOrder}/>
    )) }
=> { await handleAddChapter(newChapterOrder); setNewChapterName(""); }} showNumericalInput={true} />
{ deleteConfirmationMessage && => handleDeleteChapter()} onCancel={(): void => setDeleteConfirmationMessage(false)}/> }
) }