Files
ERitors-Scribe-Desktop/components/ScribeFooterBar.tsx
natreex 8eab6fd771 Enhance synchronization logic and offline handling
- Refactor components to support conditional offline and online CRUD operations.
- Introduce `addToQueue` mechanism for syncing offline changes to the server.
- Add `isChapterContentExist` method and related existence checks in repositories.
- Consolidate data structures and streamline book, chapter, character, and guideline synchronization workflows.
- Encrypt additional character fields and adjust repository inserts for offline data.
2026-01-07 20:43:34 -05:00

111 lines
5.7 KiB
TypeScript

import {ChapterContext} from "@/context/ChapterContext";
import {EditorContext} from "@/context/EditorContext";
import {useContext, useEffect, useState} from "react";
import {Editor} from "@tiptap/react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faBook, faChartSimple, faHeart, faSheetPlastic, faHardDrive} from "@fortawesome/free-solid-svg-icons";
import {useTranslations} from "next-intl";
import {AlertContext} from "@/context/AlertContext";
import {BookContext} from "@/context/BookContext";
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
export default function ScribeFooterBar() {
const t = useTranslations();
const {chapter} = useContext(ChapterContext);
const {book} = useContext(BookContext);
const editor: Editor | null = useContext(EditorContext).editor;
const {errorMessage} = useContext(AlertContext)
const {offlineMode} = useContext<OfflineContextType>(OfflineContext)
const {localOnlyBooks,serverSyncedBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
const [wordsCount, setWordsCount] = useState<number>(0);
useEffect((): void => {
getWordCount();
}, [editor?.state.doc.textContent]);
function getWordCount(): void {
if (editor) {
try {
const content: string = editor?.state.doc.textContent;
const texteNormalise: string = content
.replace(/'/g, ' ')
.replace(/-/g, ' ')
.replace(/\s+/g, ' ')
.trim();
const mots: string[] = texteNormalise.split(' ');
const wordCount: number = mots.filter(
(mot: string): boolean => mot.length > 0,
).length;
setWordsCount(wordCount);
} catch (e: unknown) {
if (e instanceof Error) {
errorMessage(t('errors.wordCountError') + ` (${e.message})`);
} else {
errorMessage(t('errors.wordCountError'));
}
}
}
}
return (
<div
className="px-6 py-3 bg-tertiary/90 backdrop-blur-sm border-t border-secondary/50 text-text-primary flex justify-between items-center shadow-lg">
<div>
<span className="flex items-center gap-2">
{chapter && (
<span className="inline-flex items-center px-3 py-1 rounded-lg bg-primary/10 border border-primary/30">
<span className="text-primary font-bold text-sm">
{chapter.chapterOrder < 0 ? t('scribeFooterBar.sheet') : `${chapter.chapterOrder}.`}
</span>
</span>
)}
<span className={'flex items-center gap-2 font-medium'}>
{
chapter?.title || book?.title || (
<>
<span>{t('scribeFooterBar.madeWith')}</span>
<FontAwesomeIcon color={'red'} icon={faHeart} className={'w-4 h-4 animate-pulse'}/>
</>
)}
</span>
</span>
</div>
{
chapter || book ? (
<div className="flex items-center space-x-3">
<div
className="flex items-center gap-2 bg-secondary/50 px-4 py-2 rounded-xl border border-secondary shadow-sm">
<FontAwesomeIcon icon={faChartSimple} className="text-primary text-sm w-4 h-4"/>
<span className="text-muted text-sm font-medium">{t('scribeFooterBar.words')}:</span>
<span className="text-text-primary font-bold">{wordsCount}</span>
</div>
<div
className="flex items-center gap-2 bg-secondary/50 px-4 py-2 rounded-xl border border-secondary shadow-sm">
<FontAwesomeIcon icon={faSheetPlastic} className={'text-primary w-4 h-4'}/>
<span className="text-text-primary font-bold">{Math.ceil(wordsCount / 300)}</span>
</div>
</div>
) : (
<div className="flex items-center space-x-3">
{
!offlineMode.isOffline && <div
className="flex items-center gap-2 bg-secondary/50 px-4 py-2 rounded-xl border border-secondary shadow-sm">
<FontAwesomeIcon icon={faBook} className={'text-primary w-4 h-4'}/>
<span className="text-text-primary font-bold">{serverSyncedBooks.length}</span>
</div>
}
{(localOnlyBooks.length > 0 || offlineMode.isOffline) && (
<div
className="flex items-center gap-2 bg-secondary/50 px-4 py-2 rounded-xl border border-secondary shadow-sm">
<FontAwesomeIcon icon={faHardDrive} className={'text-primary w-4 h-4'}/>
<span className="text-text-primary font-bold">{localOnlyBooks.length}</span>
</div>
)}
</div>
)
}
</div>
)
}