'use client'; import {useContext, useEffect, useState} from 'react'; import {BookContext} from "@/context/BookContext"; import {ChapterProps} from "@/lib/models/Chapter"; import {ChapterContext} from '@/context/ChapterContext'; import {EditorContext} from '@/context/EditorContext' import {Editor, useEditor} from "@tiptap/react"; import StarterKit from "@tiptap/starter-kit"; import Underline from "@tiptap/extension-underline"; import TextAlign from "@tiptap/extension-text-align"; import {AlertContext, AlertProvider} from "@/context/AlertContext"; import System from "@/lib/models/System"; import {SessionContext} from '@/context/SessionContext'; import {SessionProps} from "@/lib/models/Session"; import User, {UserProps} from "@/lib/models/User"; import {BookProps} from "@/lib/models/Book"; import ScribeTopBar from "@/components/ScribeTopBar"; import ScribeControllerBar from "@/components/ScribeControllerBar"; import ScribeLeftBar from "@/components/leftbar/ScribeLeftBar"; import ScribeEditor from "@/components/editor/ScribeEditor"; import ComposerRightBar from "@/components/rightbar/ComposerRightBar"; import ScribeFooterBar from "@/components/ScribeFooterBar"; import GuideTour, {GuideStep} from "@/components/GuideTour"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {faBookMedical, faFeather} from "@fortawesome/free-solid-svg-icons"; import TermsOfUse from "@/components/TermsOfUse"; import frMessages from '@/lib/locales/fr.json'; import enMessages from '@/lib/locales/en.json'; import {NextIntlClientProvider, useTranslations} from "next-intl"; import {LangContext} from "@/context/LangContext"; import {AIUsageContext} from "@/context/AIUsageContext"; import OfflineProvider from "@/context/OfflineProvider"; import OfflineContext from "@/context/OfflineContext"; import OfflinePinSetup from "@/components/offline/OfflinePinSetup"; import OfflinePinVerify from "@/components/offline/OfflinePinVerify"; const messagesMap = { fr: frMessages, en: enMessages }; function ScribeContent() { const t = useTranslations(); const {lang: locale} = useContext(LangContext); const {errorMessage} = useContext(AlertContext); const {initializeDatabase, setOfflineMode, isCurrentlyOffline} = useContext(OfflineContext); const editor: Editor | null = useEditor({ extensions: [ StarterKit, Underline, TextAlign.configure({ types: ['heading', 'paragraph'], }), ], injectCSS: false, immediatelyRender: false, }); const [session, setSession] = useState({user: null, accessToken: '', isConnected: false}); const [currentChapter, setCurrentChapter] = useState(undefined); const [currentBook, setCurrentBook] = useState(null); const [currentCredits, setCurrentCredits] = useState(160); const [amountSpent, setAmountSpent] = useState(session.user?.aiUsage || 0); const [isLoading, setIsLoading] = useState(true); const [isTermsAccepted, setIsTermsAccepted] = useState(false); const [homeStepsGuide, setHomeStepsGuide] = useState(false); const [showPinSetup, setShowPinSetup] = useState(false); const [showPinVerify, setShowPinVerify] = useState(false); const homeSteps: GuideStep[] = [ { id: 0, x: 50, y: 50, title: t("homePage.guide.welcome", {name: session.user?.name || ''}), content: (

{t("homePage.guide.step0.description1")}


{t("homePage.guide.step0.description2")}

), }, { id: 1, position: 'right', targetSelector: `[data-guide="left-panel-container"]`, title: t("homePage.guide.step1.title"), content: (

: {t("homePage.guide.step1.addBook")}


: {t("homePage.guide.step1.generateStory")}

), }, { id: 2, title: t("homePage.guide.step2.title"), position: 'bottom', targetSelector: `[data-guide="search-bar"]`, content: (

{t("homePage.guide.step2.description")}

), }, { id: 3, title: t("homePage.guide.step3.title"), targetSelector: `[data-guide="user-dropdown"]`, position: 'auto', content: (

{t("homePage.guide.step3.description")}

), }, { id: 4, title: t("homePage.guide.step4.title"), content: (

{t("homePage.guide.step4.description1")}


{t("homePage.guide.step4.description2")}

), }, ]; useEffect((): void => { checkAuthentification().then() }, []); useEffect((): void => { if (session.isConnected) { setIsTermsAccepted(session.user?.termsAccepted ?? false); setHomeStepsGuide(User.guideTourDone(session.user?.guideTour ?? [], 'home-basic')); setIsLoading(false); } }, [session]); useEffect((): void => { if (currentBook) { getLastChapter().then(); } }, [currentBook]); // Check for PIN setup after successful connection useEffect(():void => { async function checkPinSetup() { if (session.isConnected && window.electron) { try { const offlineStatus = await window.electron.offlineModeGet(); if (!offlineStatus.hasPin) { setTimeout(():void => { console.log('[Page] Showing PIN setup dialog'); setShowPinSetup(true); }, 2000); } } catch (e:unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage('Unknown error occurred while checking offline mode') } } } } checkPinSetup().then(); }, [session.isConnected]); async function handlePinVerifySuccess(userId: string): Promise { console.log('[OfflinePin] PIN verified successfully for user:', userId); try { if (window.electron) { const encryptionKey:string|null = await window.electron.getUserEncryptionKey(userId); if (encryptionKey) { await window.electron.dbInitialize(userId, encryptionKey); const localUser:UserProps = await window.electron.invoke('db:user:info'); if (localUser && localUser.id) { setSession({ isConnected: true, user: localUser, accessToken: 'offline', // Special offline token }); setShowPinVerify(false); setCurrentCredits(localUser.creditsBalance || 0); setAmountSpent(localUser.aiUsage || 0); } else { errorMessage(t("homePage.errors.localDataError")); if (window.electron) { //window.electron.logout(); } } } else { errorMessage(t("homePage.errors.encryptionKeyError")); if (window.electron) { //window.electron.logout(); } } } } catch (error) { console.error('[OfflinePin] Error initializing offline mode:', error); errorMessage(t("homePage.errors.offlineModeError")); if (window.electron) { //window.electron.logout(); } } } async function handleHomeTour(): Promise { try { const response: boolean = await System.authPostToServer('logs/tour', { plateforme: 'web', tour: 'home-basic' }, session.accessToken, locale ); if (response) { setSession(User.setNewGuideTour(session, 'home-basic')); setHomeStepsGuide(false); } } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("homePage.errors.termsError")); } } } async function checkAuthentification(): Promise { let token: string | null = null; if (typeof window !== 'undefined' && window.electron) { try { token = await window.electron.getToken(); } catch (e) { console.error('Error getting token from electron:', e); } } if (token) { try { const user: UserProps = await System.authGetQueryToServer('user/infos', token, locale); if (!user) { errorMessage(t("homePage.errors.userNotFound")); // Token invalide, supprimer et logout if (window.electron) { await window.electron.removeToken(); window.electron.logout(); } return; } if (window.electron && user.id) { try { const initResult = await window.electron.initUser(user.id); if (!initResult.success) { console.error('[Page] Failed to initialize user:', initResult.error); } else { try { const offlineStatus = await window.electron.offlineModeGet(); if (!offlineStatus.hasPin) { setTimeout(():void => { setShowPinSetup(true); }, 2000); } } catch (error) { console.error('[Page] Error checking offline mode:', error); } } } catch (error) { console.error('[Page] Error initializing user:', error); } } setSession({ isConnected: true, user: user, accessToken: token, }); setCurrentCredits(user.creditsBalance) setAmountSpent(user.aiUsage) if (window.electron && user.id) { try { const dbInitialized:boolean = await initializeDatabase(user.id); if (dbInitialized) { try { await window.electron.invoke('db:user:sync', { userId: user.id, firstName: user.name, lastName: user.lastName, username: user.username, email: user.email }); console.log('User synced to local DB'); } catch (syncError) { console.error('Failed to sync user to local DB:', syncError); } } } catch (error) { console.error('Failed to initialize database:', error); } } } catch (e: unknown) { if (window.electron) { try { const offlineStatus = await window.electron.offlineModeGet(); if (offlineStatus.hasPin && offlineStatus.lastUserId) { setOfflineMode(prev => ({...prev, isOffline: true, isNetworkOnline: false})); setShowPinVerify(true); setIsLoading(false); return; } else { if (window.electron) { await window.electron.removeToken(); window.electron.logout(); } } } catch (offlineError) { console.error('[Auth] Error checking offline mode:', offlineError); } } if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("homePage.errors.authenticationError")); } } } else { if (window.electron) { try { const offlineStatus = await window.electron.offlineModeGet(); if (offlineStatus.hasPin && offlineStatus.lastUserId) { setOfflineMode(prev => ({...prev, isOffline: true, isNetworkOnline: false})); setShowPinVerify(true); setIsLoading(false); return; } } catch (error) { console.error('[Auth] Error checking offline mode:', error); } window.electron.logout(); } } } async function handleTermsAcceptance(): Promise { try { const response: boolean = await System.authPostToServer(`user/terms/accept`, { version: '2025-07-1' }, session.accessToken, locale); if (response) { setIsTermsAccepted(true); setHomeStepsGuide(true); const newSession: SessionProps = { ...session, user: { ...session?.user as UserProps, termsAccepted: true } } setSession(newSession); } else { errorMessage(t("homePage.errors.termsAcceptError")); } } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("homePage.errors.termsAcceptError")); } } } async function getLastChapter(): Promise { if (session?.accessToken) { try { let response: ChapterProps | null if (isCurrentlyOffline()){ response = await window.electron.invoke('db:chapter:last', currentBook?.bookId) } else { response = await System.authGetQueryToServer(`chapter/last-chapter`, session.accessToken, locale, {bookid: currentBook?.bookId}); } if (response) { setCurrentChapter(response) } else { setCurrentChapter(undefined); } } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t("homePage.errors.lastChapterError")); } } } } if (isLoading) { return (
ERitors Logo

{t("homePage.loading")}

) } return (
{ homeStepsGuide && !isCurrentlyOffline() && setHomeStepsGuide(false)}/> } { !isTermsAccepted && !isCurrentlyOffline() && } { showPinSetup && window.electron && ( setShowPinSetup(false)} onSuccess={() => { setShowPinSetup(false); console.log('[Page] PIN configured successfully'); }} /> ) } { showPinVerify && window.electron && ( { //window.electron.logout(); }} /> ) }
); } export default function Scribe() { const [locale, setLocale] = useState<'fr' | 'en'>('fr'); useEffect((): void => { const lang: "fr" | "en" | null = System.getCookie('lang') as "fr" | "en" | null; if (lang) { setLocale(lang); } }, []); const messages = messagesMap[locale]; return ( ); }