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).
This commit is contained in:
@@ -30,6 +30,7 @@ import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
|
|||||||
import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting";
|
import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting";
|
||||||
import {useTranslations} from "next-intl";
|
import {useTranslations} from "next-intl";
|
||||||
import {LangContext, LangContextProps} from "@/context/LangContext";
|
import {LangContext, LangContextProps} from "@/context/LangContext";
|
||||||
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||||
|
|
||||||
interface ToolbarButton {
|
interface ToolbarButton {
|
||||||
action: () => void;
|
action: () => void;
|
||||||
@@ -136,6 +137,7 @@ export default function TextEditor() {
|
|||||||
const {chapter} = useContext(ChapterContext);
|
const {chapter} = useContext(ChapterContext);
|
||||||
const {errorMessage, successMessage} = useContext(AlertContext);
|
const {errorMessage, successMessage} = useContext(AlertContext);
|
||||||
const {session} = useContext(SessionContext);
|
const {session} = useContext(SessionContext);
|
||||||
|
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
|
||||||
|
|
||||||
const [mainTimer, setMainTimer] = useState<number>(0);
|
const [mainTimer, setMainTimer] = useState<number>(0);
|
||||||
const [showDraftCompanion, setShowDraftCompanion] = useState<boolean>(false);
|
const [showDraftCompanion, setShowDraftCompanion] = useState<boolean>(false);
|
||||||
@@ -282,13 +284,24 @@ export default function TextEditor() {
|
|||||||
const version: number = chapter.chapterContent.version || 0;
|
const version: number = chapter.chapterContent.version || 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response: boolean = await System.authPostToServer<boolean>(`chapter/content`, {
|
let response: boolean;
|
||||||
|
if (isCurrentlyOffline()){
|
||||||
|
response = await window.electron.invoke<boolean>('db:chapter:content:save',{
|
||||||
|
chapterId,
|
||||||
|
version,
|
||||||
|
content,
|
||||||
|
totalWordCount: editor.getText().length,
|
||||||
|
currentTime: mainTimer
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
response = await System.authPostToServer<boolean>(`chapter/content`, {
|
||||||
chapterId,
|
chapterId,
|
||||||
version,
|
version,
|
||||||
content,
|
content,
|
||||||
totalWordCount: editor.getText().length,
|
totalWordCount: editor.getText().length,
|
||||||
currentTime: mainTimer
|
currentTime: mainTimer
|
||||||
}, session?.accessToken ?? '');
|
}, session?.accessToken ?? '');
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
errorMessage(t('editor.error.savedFailed'));
|
errorMessage(t('editor.error.savedFailed'));
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
@@ -437,8 +450,7 @@ export default function TextEditor() {
|
|||||||
onClick={button.action}
|
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'}`}
|
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'}`}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={button.icon}
|
<FontAwesomeIcon icon={button.icon} className={'w-4 h-4 transition-transform duration-200 group-hover:scale-110'}/>
|
||||||
className={'w-4 h-4 transition-transform duration-200 group-hover:scale-110'}/>
|
|
||||||
{
|
{
|
||||||
button.label &&
|
button.label &&
|
||||||
<span className="ml-2 text-sm font-medium">
|
<span className="ml-2 text-sm font-medium">
|
||||||
@@ -455,7 +467,7 @@ export default function TextEditor() {
|
|||||||
onClick={handleShowUserSettings}
|
onClick={handleShowUserSettings}
|
||||||
icon={faCog}
|
icon={faCog}
|
||||||
/>
|
/>
|
||||||
{chapter?.chapterContent.version === 2 && (
|
{chapter?.chapterContent.version === 2 && !isCurrentlyOffline() && (
|
||||||
<CollapsableButton
|
<CollapsableButton
|
||||||
showCollapsable={showGhostWriter}
|
showCollapsable={showGhostWriter}
|
||||||
text={t("textEditor.ghostWriter")}
|
text={t("textEditor.ghostWriter")}
|
||||||
|
|||||||
@@ -123,11 +123,20 @@ export default function ScribeChapterComponent() {
|
|||||||
|
|
||||||
async function handleChapterUpdate(chapterId: string, title: string, chapterOrder: number): Promise<void> {
|
async function handleChapterUpdate(chapterId: string, title: string, chapterOrder: number): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const response: boolean = await System.authPostToServer<boolean>('chapter/update', {
|
let response: boolean;
|
||||||
|
if (isCurrentlyOffline()) {
|
||||||
|
response = await window.electron.invoke<boolean>('db:chapter:update',{
|
||||||
|
chapterId: chapterId,
|
||||||
|
chapterOrder: chapterOrder,
|
||||||
|
title: title,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
response = await System.authPostToServer<boolean>('chapter/update', {
|
||||||
chapterId: chapterId,
|
chapterId: chapterId,
|
||||||
chapterOrder: chapterOrder,
|
chapterOrder: chapterOrder,
|
||||||
title: title,
|
title: title,
|
||||||
}, userToken, lang);
|
}, userToken, lang);
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
errorMessage(t("scribeChapterComponent.errorChapterUpdate"));
|
errorMessage(t("scribeChapterComponent.errorChapterUpdate"));
|
||||||
return;
|
return;
|
||||||
@@ -159,10 +168,14 @@ export default function ScribeChapterComponent() {
|
|||||||
async function handleDeleteChapter(): Promise<void> {
|
async function handleDeleteChapter(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
setDeleteConfirmationMessage(false);
|
setDeleteConfirmationMessage(false);
|
||||||
const response: boolean = await System.authDeleteToServer<boolean>('chapter/remove', {
|
let response:boolean = false;
|
||||||
bookId: book?.bookId,
|
if (isCurrentlyOffline()) {
|
||||||
|
response = await window.electron.invoke<boolean>('db:chapter:remove', removeChapterId)
|
||||||
|
} else {
|
||||||
|
response = await System.authDeleteToServer<boolean>('chapter/remove', {
|
||||||
chapterId: removeChapterId,
|
chapterId: removeChapterId,
|
||||||
}, userToken, lang);
|
}, userToken, lang);
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
errorMessage(t("scribeChapterComponent.errorChapterDelete"));
|
errorMessage(t("scribeChapterComponent.errorChapterDelete"));
|
||||||
return;
|
return;
|
||||||
@@ -189,7 +202,7 @@ export default function ScribeChapterComponent() {
|
|||||||
try {
|
try {
|
||||||
let chapterId:string|null = null;
|
let chapterId:string|null = null;
|
||||||
if (isCurrentlyOffline()){
|
if (isCurrentlyOffline()){
|
||||||
chapterId = await window.electron.invoke<string>('db:chapter:create', {
|
chapterId = await window.electron.invoke<string>('db:chapter:add', {
|
||||||
bookId: book?.bookId,
|
bookId: book?.bookId,
|
||||||
chapterOrder: chapterOrder,
|
chapterOrder: chapterOrder,
|
||||||
title: chapterTitle
|
title: chapterTitle
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ ipcMain.handle('db:chapter:last', createHandler<string, ChapterProps | null>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// POST /chapter/add - Add new chapter
|
// POST /chapter/add - Add new chapter
|
||||||
ipcMain.handle('db:chapter:create', createHandler<AddChapterData, string>(
|
ipcMain.handle('db:chapter:add', createHandler<AddChapterData, string>(
|
||||||
function(userId: string, data: AddChapterData, lang: 'fr' | 'en'): string {
|
function(userId: string, data: AddChapterData, lang: 'fr' | 'en'): string {
|
||||||
return Chapter.addChapter(userId, data.bookId, data.title, 0, data.chapterOrder, lang);
|
return Chapter.addChapter(userId, data.bookId, data.title, 0, data.chapterOrder, lang);
|
||||||
}
|
}
|
||||||
@@ -115,8 +115,9 @@ ipcMain.handle('db:chapter:create', createHandler<AddChapterData, string>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// DELETE /chapter/remove - Remove chapter
|
// DELETE /chapter/remove - Remove chapter
|
||||||
ipcMain.handle('db:chapter:delete', createHandler<string, boolean>(
|
ipcMain.handle('db:chapter:remove', createHandler<string, boolean>(
|
||||||
function(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean {
|
function(userId: string, chapterId: string, lang: 'fr' | 'en'): boolean {
|
||||||
|
console.log(userId,chapterId,lang)
|
||||||
return Chapter.removeChapter(userId, chapterId, lang);
|
return Chapter.removeChapter(userId, chapterId, lang);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user