From e1abcd9490f8b3a86f9f18b39fc69442acd98149 Mon Sep 17 00:00:00 2001 From: natreex Date: Wed, 26 Nov 2025 22:03:22 -0500 Subject: [PATCH] Add offline mode logic for chapter operations and refactor IPC handlers - Integrate `OfflineContext` into `TextEditor` and `ScribeChapterComponent` to handle offline scenarios for chapter CRUD operations. - Add conditional logic to switch between server API requests and offline IPC handlers (`db:chapter:add`, `db:chapter:remove`, `db:chapter:content:save`, `db:chapter:update`). - Refactor and rename IPC handlers (`db:chapter:create` to `db:chapter:add`, `db:chapter:delete` to `db:chapter:remove`) for consistency. - Update UI to disable certain actions when offline (e.g., GhostWriter button). --- components/editor/TextEditor.tsx | 32 ++++++++++++------ components/leftbar/ScribeChapterComponent.tsx | 33 +++++++++++++------ electron/ipc/chapter.ipc.ts | 5 +-- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/components/editor/TextEditor.tsx b/components/editor/TextEditor.tsx index d8d6e06..1cb8662 100644 --- a/components/editor/TextEditor.tsx +++ b/components/editor/TextEditor.tsx @@ -30,6 +30,7 @@ import {IconDefinition} from "@fortawesome/fontawesome-svg-core"; import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting"; import {useTranslations} from "next-intl"; import {LangContext, LangContextProps} from "@/context/LangContext"; +import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; interface ToolbarButton { action: () => void; @@ -136,6 +137,7 @@ export default function TextEditor() { const {chapter} = useContext(ChapterContext); const {errorMessage, successMessage} = useContext(AlertContext); const {session} = useContext(SessionContext); + const {isCurrentlyOffline} = useContext(OfflineContext); const [mainTimer, setMainTimer] = useState(0); const [showDraftCompanion, setShowDraftCompanion] = useState(false); @@ -282,13 +284,24 @@ export default function TextEditor() { const version: number = chapter.chapterContent.version || 0; try { - const response: boolean = await System.authPostToServer(`chapter/content`, { - chapterId, - version, - content, - totalWordCount: editor.getText().length, - currentTime: mainTimer - }, session?.accessToken ?? ''); + let response: boolean; + if (isCurrentlyOffline()){ + response = await window.electron.invoke('db:chapter:content:save',{ + chapterId, + version, + content, + totalWordCount: editor.getText().length, + currentTime: mainTimer + }) + } else { + response = await System.authPostToServer(`chapter/content`, { + chapterId, + version, + content, + totalWordCount: editor.getText().length, + currentTime: mainTimer + }, session?.accessToken ?? ''); + } if (!response) { errorMessage(t('editor.error.savedFailed')); setIsSaving(false); @@ -437,8 +450,7 @@ export default function TextEditor() { onClick={button.action} className={`group flex items-center px-3 py-2 rounded-lg transition-all duration-200 ${button.isActive ? 'bg-primary text-text-primary shadow-md shadow-primary/30 scale-105' : 'text-muted hover:text-text-primary hover:bg-secondary/50 hover:shadow-sm hover:scale-105'}`} > - + { button.label && @@ -455,7 +467,7 @@ export default function TextEditor() { onClick={handleShowUserSettings} icon={faCog} /> - {chapter?.chapterContent.version === 2 && ( + {chapter?.chapterContent.version === 2 && !isCurrentlyOffline() && ( { try { - const response: boolean = await System.authPostToServer('chapter/update', { - chapterId: chapterId, - chapterOrder: chapterOrder, - title: title, - }, userToken, lang); + 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; @@ -159,10 +168,14 @@ export default function ScribeChapterComponent() { async function handleDeleteChapter(): Promise { try { setDeleteConfirmationMessage(false); - const response: boolean = await System.authDeleteToServer('chapter/remove', { - bookId: book?.bookId, - chapterId: removeChapterId, - }, userToken, lang); + 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; @@ -189,7 +202,7 @@ export default function ScribeChapterComponent() { try { let chapterId:string|null = null; if (isCurrentlyOffline()){ - chapterId = await window.electron.invoke('db:chapter:create', { + chapterId = await window.electron.invoke('db:chapter:add', { bookId: book?.bookId, chapterOrder: chapterOrder, title: chapterTitle diff --git a/electron/ipc/chapter.ipc.ts b/electron/ipc/chapter.ipc.ts index d76310b..f7e8aaa 100644 --- a/electron/ipc/chapter.ipc.ts +++ b/electron/ipc/chapter.ipc.ts @@ -107,7 +107,7 @@ ipcMain.handle('db:chapter:last', createHandler( ); // POST /chapter/add - Add new chapter -ipcMain.handle('db:chapter:create', createHandler( +ipcMain.handle('db:chapter:add', createHandler( function(userId: string, data: AddChapterData, lang: 'fr' | 'en'): string { return Chapter.addChapter(userId, data.bookId, data.title, 0, data.chapterOrder, lang); } @@ -115,8 +115,9 @@ ipcMain.handle('db:chapter:create', createHandler( ); // DELETE /chapter/remove - Remove chapter -ipcMain.handle('db:chapter:delete', createHandler( +ipcMain.handle('db:chapter:remove', createHandler( function(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean { + console.log(userId,chapterId,lang) return Chapter.removeChapter(userId, chapterId, lang); } )