From ff530f34423b2fcdfbd851b4ae4ca94c8b26a783 Mon Sep 17 00:00:00 2001 From: natreex Date: Fri, 19 Dec 2025 15:42:35 -0500 Subject: [PATCH] Refactor character, chapter, and story components to support offline mode - Add `OfflineContext` and `BookContext` to components for offline state management. - Introduce conditional logic to toggle between server API requests and offline IPC handlers for CRUD operations. - Refine `TextEditor`, `DraftCompanion`, and other components to disable actions or features unavailable in offline mode. - Improve error handling and user feedback in both online and offline scenarios. --- app/page.tsx | 10 +- .../book/settings/BasicInformationSetting.tsx | 27 +++-- components/book/settings/DeleteBook.tsx | 22 ++-- .../characters/CharacterComponent.tsx | 67 ++++++++---- .../settings/characters/CharacterDetail.tsx | 14 ++- .../settings/guide-line/GuideLineSetting.tsx | 79 ++++++++++---- .../settings/locations/LocationComponent.tsx | 102 +++++++++++++----- components/book/settings/story/Act.tsx | 78 ++++++++++---- components/book/settings/story/Issue.tsx | 36 +++++-- .../book/settings/story/MainChapter.tsx | 12 ++- .../book/settings/story/StorySetting.tsx | 16 ++- .../book/settings/world/WorldElement.tsx | 32 ++++-- .../book/settings/world/WorldSetting.tsx | 40 +++++-- components/editor/DraftCompanion.tsx | 46 ++++++-- components/editor/TextEditor.tsx | 28 +++-- components/rightbar/ComposerRightBar.tsx | 2 +- 16 files changed, 454 insertions(+), 157 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 88dc5f7..f8bb33e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -475,7 +475,15 @@ function ScribeContent() { } 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 (currentBook?.localBook) { + if (!offlineMode.isDatabaseInitialized) { + setCurrentChapter(undefined); + return; + } + 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) diff --git a/components/book/settings/BasicInformationSetting.tsx b/components/book/settings/BasicInformationSetting.tsx index 08c04eb..410f181 100644 --- a/components/book/settings/BasicInformationSetting.tsx +++ b/components/book/settings/BasicInformationSetting.tsx @@ -127,14 +127,25 @@ function BasicInformationSetting(props: any, ref: any) { bookId: bookId }); } else { - response = await System.authPostToServer('book/basic-information', { - title: title, - subTitle: subTitle, - summary: summary, - publicationDate: publicationDate, - wordCount: wordCount, - bookId: bookId - }, userToken, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:book:updateBasicInformation', { + title: title, + subTitle: subTitle, + summary: summary, + publicationDate: publicationDate, + wordCount: wordCount, + bookId: bookId + }); + } else { + response = await System.authPostToServer('book/basic-information', { + title: title, + subTitle: subTitle, + summary: summary, + publicationDate: publicationDate, + wordCount: wordCount, + bookId: bookId + }, userToken, lang); + } } if (!response) { errorMessage(t('basicInformationSetting.error.update')); diff --git a/components/book/settings/DeleteBook.tsx b/components/book/settings/DeleteBook.tsx index bf9a934..578f822 100644 --- a/components/book/settings/DeleteBook.tsx +++ b/components/book/settings/DeleteBook.tsx @@ -8,6 +8,7 @@ import {LangContext, LangContextProps} from "@/context/LangContext"; import {AlertContext, AlertContextProps} from "@/context/AlertContext"; import AlertBox from "@/components/AlertBox"; import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; +import {BookContext} from "@/context/BookContext"; interface DeleteBookProps { bookId: string; @@ -17,6 +18,7 @@ export default function DeleteBook({bookId}: DeleteBookProps) { const {session, setSession} = useContext(SessionContext); const {lang} = useContext(LangContext) const {isCurrentlyOffline} = useContext(OfflineContext); + const {book} = useContext(BookContext); const [showConfirmBox, setShowConfirmBox] = useState(false); const {errorMessage} = useContext(AlertContext) @@ -32,14 +34,20 @@ export default function DeleteBook({bookId}: DeleteBookProps) { id: bookId, }); } else { - response = await System.authDeleteToServer( - `book/delete`, - { + if (book?.localBook) { + response = await window.electron.invoke('db:book:delete', { id: bookId, - }, - session.accessToken, - lang - ); + }); + } else { + response = await System.authDeleteToServer( + `book/delete`, + { + id: bookId, + }, + session.accessToken, + lang + ); + } } if (response) { setShowConfirmBox(false); diff --git a/components/book/settings/characters/CharacterComponent.tsx b/components/book/settings/characters/CharacterComponent.tsx index 346faed..59a36fe 100644 --- a/components/book/settings/characters/CharacterComponent.tsx +++ b/components/book/settings/characters/CharacterComponent.tsx @@ -70,9 +70,13 @@ export function CharacterComponent(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:character:list', {bookid: book?.bookId}); } else { - response = await System.authGetQueryToServer(`character/list`, session.accessToken, lang, { - bookid: book?.bookId, - }); + if (book?.localBook) { + response = await window.electron.invoke('db:character:list', {bookid: book?.bookId}); + } else { + response = await System.authGetQueryToServer(`character/list`, session.accessToken, lang, { + bookid: book?.bookId, + }); + } } if (response) { setCharacters(response); @@ -122,10 +126,17 @@ export function CharacterComponent(props: any, ref: any) { character: updatedCharacter, }); } else { - characterId = await System.authPostToServer(`character/add`, { - bookId: book?.bookId, - character: updatedCharacter, - }, session.accessToken, lang); + if (book?.localBook) { + characterId = await window.electron.invoke('db:character:create', { + bookId: book?.bookId, + character: updatedCharacter, + }); + } else { + characterId = await System.authPostToServer(`character/add`, { + bookId: book?.bookId, + character: updatedCharacter, + }, session.accessToken, lang); + } } if (!characterId) { errorMessage(t("characterComponent.errorAddCharacter")); @@ -151,9 +162,15 @@ export function CharacterComponent(props: any, ref: any) { character: updatedCharacter, }); } else { - response = await System.authPostToServer(`character/update`, { - character: updatedCharacter, - }, session.accessToken, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:character:update', { + character: updatedCharacter, + }); + } else { + response = await System.authPostToServer(`character/update`, { + character: updatedCharacter, + }, session.accessToken, lang); + } } if (!response) { errorMessage(t("characterComponent.errorUpdateCharacter")); @@ -205,11 +222,19 @@ export function CharacterComponent(props: any, ref: any) { name: value.name, }); } else { - attributeId = await System.authPostToServer(`character/attribute/add`, { - characterId: selectedCharacter.id, - type: section, - name: value.name, - }, session.accessToken, lang); + if (book?.localBook) { + attributeId = await window.electron.invoke('db:character:attribute:add', { + characterId: selectedCharacter.id, + type: section, + name: value.name, + }); + } else { + attributeId = await System.authPostToServer(`character/attribute/add`, { + characterId: selectedCharacter.id, + type: section, + name: value.name, + }, session.accessToken, lang); + } } if (!attributeId) { errorMessage(t("characterComponent.errorAddAttribute")); @@ -251,9 +276,15 @@ export function CharacterComponent(props: any, ref: any) { attributeId: attrId, }); } else { - response = await System.authDeleteToServer(`character/attribute/delete`, { - attributeId: attrId, - }, session.accessToken, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:character:attribute:delete', { + attributeId: attrId, + }); + } else { + response = await System.authDeleteToServer(`character/attribute/delete`, { + attributeId: attrId, + }, session.accessToken, lang); + } } if (!response) { errorMessage(t("characterComponent.errorRemoveAttribute")); diff --git a/components/book/settings/characters/CharacterDetail.tsx b/components/book/settings/characters/CharacterDetail.tsx index 1a30989..004e1b3 100644 --- a/components/book/settings/characters/CharacterDetail.tsx +++ b/components/book/settings/characters/CharacterDetail.tsx @@ -30,6 +30,7 @@ import CharacterSectionElement from "@/components/book/settings/characters/Chara import {useTranslations} from "next-intl"; import {LangContext} from "@/context/LangContext"; import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; +import {BookContext} from "@/context/BookContext"; interface CharacterDetailProps { selectedCharacter: CharacterProps | null; @@ -57,6 +58,7 @@ export default function CharacterDetail( const t = useTranslations(); const {lang} = useContext(LangContext); const {isCurrentlyOffline} = useContext(OfflineContext); + const {book} = useContext(BookContext); const {session} = useContext(SessionContext); const {errorMessage} = useContext(AlertContext); @@ -74,9 +76,15 @@ export default function CharacterDetail( characterId: selectedCharacter?.id, }); } else { - response = await System.authGetQueryToServer(`character/attribute`, session.accessToken, lang, { - characterId: selectedCharacter?.id, - }); + if (book?.localBook) { + response = await window.electron.invoke('db:character:attributes', { + characterId: selectedCharacter?.id, + }); + } else { + response = await System.authGetQueryToServer(`character/attribute`, session.accessToken, lang, { + characterId: selectedCharacter?.id, + }); + } } if (response) { setSelectedCharacter({ diff --git a/components/book/settings/guide-line/GuideLineSetting.tsx b/components/book/settings/guide-line/GuideLineSetting.tsx index 3e3f5a6..9807e22 100644 --- a/components/book/settings/guide-line/GuideLineSetting.tsx +++ b/components/book/settings/guide-line/GuideLineSetting.tsx @@ -81,7 +81,11 @@ function GuideLineSetting(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:book:guideline:ai:get', {id: bookId}); } else { - response = await System.authGetQueryToServer(`book/ai/guideline`, userToken, lang, {id: bookId}); + if (book?.localBook) { + response = await window.electron.invoke('db:book:guideline:ai:get', {id: bookId}); + } else { + response = await System.authGetQueryToServer(`book/ai/guideline`, userToken, lang, {id: bookId}); + } } if (response) { setPlotSummary(response.globalResume); @@ -107,12 +111,16 @@ function GuideLineSetting(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:book:guideline:get', {id: bookId}); } else { - response = await System.authGetQueryToServer( - `book/guide-line`, - userToken, - lang, - {id: bookId}, - ); + if (book?.localBook) { + response = await window.electron.invoke('db:book:guideline:get', {id: bookId}); + } else { + response = await System.authGetQueryToServer( + `book/guide-line`, + userToken, + lang, + {id: bookId}, + ); + } } if (response) { setTone(response.tone); @@ -154,12 +162,16 @@ function GuideLineSetting(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:book:guideline:update', guidelineData); } else { - response = await System.authPostToServer( - 'book/guide-line', - guidelineData, - userToken, - lang, - ); + if (book?.localBook) { + response = await window.electron.invoke('db:book:guideline:update', guidelineData); + } else { + response = await System.authPostToServer( + 'book/guide-line', + guidelineData, + userToken, + lang, + ); + } } if (!response) { errorMessage(t("guideLineSetting.saveError")); @@ -177,9 +189,9 @@ function GuideLineSetting(props: any, ref: any) { async function saveQuillSense(): Promise { try { - const response: boolean = await System.authPostToServer( - 'quillsense/book/guide-line', - { + let response: boolean; + if (isCurrentlyOffline()) { + response = await window.electron.invoke('db:book:guideline:ai:update', { bookId: bookId, plotSummary: plotSummary, verbTense: verbTense, @@ -188,10 +200,37 @@ function GuideLineSetting(props: any, ref: any) { toneAtmosphere: toneAtmosphere, language: language, themes: themes, - }, - userToken, - lang, - ); + }); + } else { + if (book?.localBook) { + response = await window.electron.invoke('db:book:guideline:ai:update', { + bookId: bookId, + plotSummary: plotSummary, + verbTense: verbTense, + narrativeType: narrativeType, + dialogueType: dialogueType, + toneAtmosphere: toneAtmosphere, + language: language, + themes: themes, + }); + } else { + response = await System.authPostToServer( + 'quillsense/book/guide-line', + { + bookId: bookId, + plotSummary: plotSummary, + verbTense: verbTense, + narrativeType: narrativeType, + dialogueType: dialogueType, + toneAtmosphere: toneAtmosphere, + language: language, + themes: themes, + }, + userToken, + lang, + ); + } + } if (response) { successMessage(t("guideLineSetting.saveSuccess")); } else { diff --git a/components/book/settings/locations/LocationComponent.tsx b/components/book/settings/locations/LocationComponent.tsx index bdefec1..b51923d 100644 --- a/components/book/settings/locations/LocationComponent.tsx +++ b/components/book/settings/locations/LocationComponent.tsx @@ -64,9 +64,13 @@ export function LocationComponent(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:location:all', {bookid: bookId}); } else { - response = await System.authGetQueryToServer(`location/all`, token, lang, { - bookid: bookId, - }); + if (book?.localBook) { + response = await window.electron.invoke('db:location:all', {bookid: bookId}); + } else { + response = await System.authGetQueryToServer(`location/all`, token, lang, { + bookid: bookId, + }); + } } if (response && response.length > 0) { setSections(response); @@ -93,10 +97,17 @@ export function LocationComponent(props: any, ref: any) { locationName: newSectionName, }); } else { - sectionId = await System.authPostToServer(`location/section/add`, { - bookId: bookId, - locationName: newSectionName, - }, token, lang); + if (book?.localBook) { + sectionId = await window.electron.invoke('db:location:section:add', { + bookId: bookId, + locationName: newSectionName, + }); + } else { + sectionId = await System.authPostToServer(`location/section/add`, { + bookId: bookId, + locationName: newSectionName, + }, token, lang); + } } if (!sectionId) { errorMessage(t('locationComponent.errorUnknownAddSection')); @@ -132,12 +143,20 @@ export function LocationComponent(props: any, ref: any) { elementName: newElementNames[sectionId], }); } else { - elementId = await System.authPostToServer(`location/element/add`, { + if (book?.localBook) { + elementId = await window.electron.invoke('db:location:element:add', { bookId: bookId, locationId: sectionId, elementName: newElementNames[sectionId], - }, - token, lang); + }); + } else { + elementId = await System.authPostToServer(`location/element/add`, { + bookId: bookId, + locationId: sectionId, + elementName: newElementNames[sectionId], + }, + token, lang); + } } if (!elementId) { errorMessage(t('locationComponent.errorUnknownAddElement')); @@ -198,10 +217,17 @@ export function LocationComponent(props: any, ref: any) { subElementName: newSubElementNames[elementIndex], }); } else { - subElementId = await System.authPostToServer(`location/sub-element/add`, { - elementId: sections[sectionIndex].elements[elementIndex].id, - subElementName: newSubElementNames[elementIndex], - }, token, lang); + if (book?.localBook) { + subElementId = await window.electron.invoke('db:location:subelement:add', { + elementId: sections[sectionIndex].elements[elementIndex].id, + subElementName: newSubElementNames[elementIndex], + }); + } else { + subElementId = await System.authPostToServer(`location/sub-element/add`, { + elementId: sections[sectionIndex].elements[elementIndex].id, + subElementName: newSubElementNames[elementIndex], + }, token, lang); + } } if (!subElementId) { errorMessage(t('locationComponent.errorUnknownAddSubElement')); @@ -254,9 +280,15 @@ export function LocationComponent(props: any, ref: any) { elementId: elementId, }); } else { - response = await System.authDeleteToServer(`location/element/delete`, { - elementId: elementId, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:location:element:delete', { + elementId: elementId, + }); + } else { + response = await System.authDeleteToServer(`location/element/delete`, { + elementId: elementId, + }, token, lang); + } } if (!response) { errorMessage(t('locationComponent.errorUnknownDeleteElement')); @@ -288,9 +320,15 @@ export function LocationComponent(props: any, ref: any) { subElementId: subElementId, }); } else { - response = await System.authDeleteToServer(`location/sub-element/delete`, { - subElementId: subElementId, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:location:subelement:delete', { + subElementId: subElementId, + }); + } else { + response = await System.authDeleteToServer(`location/sub-element/delete`, { + subElementId: subElementId, + }, token, lang); + } } if (!response) { errorMessage(t('locationComponent.errorUnknownDeleteSubElement')); @@ -317,9 +355,15 @@ export function LocationComponent(props: any, ref: any) { locationId: sectionId, }); } else { - response = await System.authDeleteToServer(`location/delete`, { - locationId: sectionId, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:location:delete', { + locationId: sectionId, + }); + } else { + response = await System.authDeleteToServer(`location/delete`, { + locationId: sectionId, + }, token, lang); + } } if (!response) { errorMessage(t('locationComponent.errorUnknownDeleteSection')); @@ -344,9 +388,15 @@ export function LocationComponent(props: any, ref: any) { locations: sections, }); } else { - response = await System.authPostToServer(`location/update`, { - locations: sections, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:location:update', { + locations: sections, + }); + } else { + response = await System.authPostToServer(`location/update`, { + locations: sections, + }, token, lang); + } } if (!response) { errorMessage(t('locationComponent.errorUnknownSave')); diff --git a/components/book/settings/story/Act.tsx b/components/book/settings/story/Act.tsx index 70624d3..ee29115 100644 --- a/components/book/settings/story/Act.tsx +++ b/components/book/settings/story/Act.tsx @@ -80,10 +80,17 @@ export default function Act({acts, setActs, mainChapters}: ActProps) { name: newIncidentTitle, }); } else { - incidentId = await System.authPostToServer('book/incident/new', { - bookId, - name: newIncidentTitle, - }, token, lang); + if (book?.localBook) { + incidentId = await window.electron.invoke('db:book:incident:add', { + bookId, + name: newIncidentTitle, + }); + } else { + incidentId = await System.authPostToServer('book/incident/new', { + bookId, + name: newIncidentTitle, + }, token, lang); + } } if (!incidentId) { errorMessage(t('errorAddIncident')); @@ -125,10 +132,17 @@ export default function Act({acts, setActs, mainChapters}: ActProps) { incidentId, }); } else { - response = await System.authDeleteToServer('book/incident/remove', { - bookId, - incidentId, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:book:incident:remove', { + bookId, + incidentId, + }); + } else { + response = await System.authDeleteToServer('book/incident/remove', { + bookId, + incidentId, + }, token, lang); + } } if (!response) { errorMessage(t('errorDeleteIncident')); @@ -166,11 +180,19 @@ export default function Act({acts, setActs, mainChapters}: ActProps) { incidentId: selectedIncidentId, }); } else { - plotId = await System.authPostToServer('book/plot/new', { - bookId, - name: newPlotPointTitle, - incidentId: selectedIncidentId, - }, token, lang); + if (book?.localBook) { + plotId = await window.electron.invoke('db:book:plot:add', { + bookId, + name: newPlotPointTitle, + incidentId: selectedIncidentId, + }); + } else { + plotId = await System.authPostToServer('book/plot/new', { + bookId, + name: newPlotPointTitle, + incidentId: selectedIncidentId, + }, token, lang); + } } if (!plotId) { errorMessage(t('errorAddPlotPoint')); @@ -212,9 +234,15 @@ export default function Act({acts, setActs, mainChapters}: ActProps) { plotId: plotPointId, }); } else { - response = await System.authDeleteToServer('book/plot/remove', { - plotId: plotPointId, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:book:plot:remove', { + plotId: plotPointId, + }); + } else { + response = await System.authDeleteToServer('book/plot/remove', { + plotId: plotPointId, + }, token, lang); + } } if (!response) { errorMessage(t('errorDeletePlotPoint')); @@ -264,7 +292,11 @@ export default function Act({acts, setActs, mainChapters}: ActProps) { if (isCurrentlyOffline()) { linkId = await window.electron.invoke('db:chapter:information:add', linkData); } else { - linkId = await System.authPostToServer('chapter/resume/add', linkData, token, lang); + if (book?.localBook) { + linkId = await window.electron.invoke('db:chapter:information:add', linkData); + } else { + linkId = await System.authPostToServer('chapter/resume/add', linkData, token, lang); + } } if (!linkId) { errorMessage(t('errorLinkChapter')); @@ -346,9 +378,15 @@ export default function Act({acts, setActs, mainChapters}: ActProps) { chapterInfoId, }); } else { - response = await System.authDeleteToServer('chapter/resume/remove', { - chapterInfoId, - }, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:chapter:information:remove', { + chapterInfoId, + }); + } else { + response = await System.authDeleteToServer('chapter/resume/remove', { + chapterInfoId, + }, token, lang); + } } if (!response) { errorMessage(t('errorUnlinkChapter')); diff --git a/components/book/settings/story/Issue.tsx b/components/book/settings/story/Issue.tsx index 393a7a1..cdfbbe8 100644 --- a/components/book/settings/story/Issue.tsx +++ b/components/book/settings/story/Issue.tsx @@ -42,10 +42,17 @@ export default function Issues({issues, setIssues}: IssuesProps) { name: newIssueName, }); } else { - issueId = await System.authPostToServer('book/issue/add', { - bookId, - name: newIssueName, - }, token, lang); + if (book?.localBook) { + issueId = await window.electron.invoke('db:book:issue:add', { + bookId, + name: newIssueName, + }); + } else { + issueId = await System.authPostToServer('book/issue/add', { + bookId, + name: newIssueName, + }, token, lang); + } } if (!issueId) { errorMessage(t("issues.errorAdd")); @@ -81,15 +88,22 @@ export default function Issues({issues, setIssues}: IssuesProps) { issueId, }); } else { - response = await System.authDeleteToServer( - 'book/issue/remove', - { + if (book?.localBook) { + response = await window.electron.invoke('db:book:issue:remove', { bookId, issueId, - }, - token, - lang - ); + }); + } else { + response = await System.authDeleteToServer( + 'book/issue/remove', + { + bookId, + issueId, + }, + token, + lang + ); + } } if (response) { const updatedIssues: Issue[] = issues.filter((issue: Issue): boolean => issue.id !== issueId,); diff --git a/components/book/settings/story/MainChapter.tsx b/components/book/settings/story/MainChapter.tsx index 4a18f4f..c9b061e 100644 --- a/components/book/settings/story/MainChapter.tsx +++ b/components/book/settings/story/MainChapter.tsx @@ -88,7 +88,11 @@ export default function MainChapter({chapters, setChapters}: MainChapterProps) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:chapter:remove', deleteData); } else { - response = await System.authDeleteToServer('chapter/remove', deleteData, token, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:chapter:remove', deleteData); + } else { + response = await System.authDeleteToServer('chapter/remove', deleteData, token, lang); + } } if (!response) { errorMessage(t("mainChapter.errorDelete")); @@ -120,7 +124,11 @@ export default function MainChapter({chapters, setChapters}: MainChapterProps) { if (isCurrentlyOffline()) { responseId = await window.electron.invoke('db:chapter:add', chapterData); } else { - responseId = await System.authPostToServer('chapter/add', chapterData, token); + if (book?.localBook) { + responseId = await window.electron.invoke('db:chapter:add', chapterData); + } else { + responseId = await System.authPostToServer('chapter/add', chapterData, token); + } } if (!responseId) { errorMessage(t("mainChapter.errorAdd")); diff --git a/components/book/settings/story/StorySetting.tsx b/components/book/settings/story/StorySetting.tsx index 144780a..ce4dec5 100644 --- a/components/book/settings/story/StorySetting.tsx +++ b/components/book/settings/story/StorySetting.tsx @@ -74,9 +74,13 @@ export function Story(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:book:story:get', {bookid: bookId}); } else { - response = await System.authGetQueryToServer(`book/story`, userToken, lang, { - bookid: bookId, - }); + if (book?.localBook) { + response = await window.electron.invoke('db:book:story:get', {bookid: bookId}); + } else { + response = await System.authGetQueryToServer(`book/story`, userToken, lang, { + bookid: bookId, + }); + } } if (response) { setActs(response.acts); @@ -136,7 +140,11 @@ export function Story(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:book:story:update', storyData); } else { - response = await System.authPostToServer('book/story', storyData, userToken, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:book:story:update', storyData); + } else { + response = await System.authPostToServer('book/story', storyData, userToken, lang); + } } if (!response) { errorMessage(t("story.errorSave")) diff --git a/components/book/settings/world/WorldElement.tsx b/components/book/settings/world/WorldElement.tsx index 10e3c2f..7f45d17 100644 --- a/components/book/settings/world/WorldElement.tsx +++ b/components/book/settings/world/WorldElement.tsx @@ -12,6 +12,7 @@ import InputField from "@/components/form/InputField"; import {useTranslations} from "next-intl"; import {LangContext, LangContextProps} from "@/context/LangContext"; import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; +import {BookContext} from "@/context/BookContext"; interface WorldElementInputProps { sectionLabel: string; @@ -22,6 +23,7 @@ export default function WorldElementComponent({sectionLabel, sectionType}: World const t = useTranslations(); const {lang} = useContext(LangContext); const {isCurrentlyOffline} = useContext(OfflineContext); + const {book} = useContext(BookContext); const {worlds, setWorlds, selectedWorldIndex} = useContext(WorldContext); const {errorMessage, successMessage} = useContext(AlertContext); const {session} = useContext(SessionContext); @@ -40,9 +42,15 @@ export default function WorldElementComponent({sectionLabel, sectionType}: World elementId: elementId, }); } else { - response = await System.authDeleteToServer('book/world/element/delete', { - elementId: elementId, - }, session.accessToken, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:book:world:element:remove', { + elementId: elementId, + }); + } else { + response = await System.authDeleteToServer('book/world/element/delete', { + elementId: elementId, + }, session.accessToken, lang); + } } if (!response) { errorMessage(t("worldSetting.unknownError")) @@ -76,11 +84,19 @@ export default function WorldElementComponent({sectionLabel, sectionType}: World elementName: newElementName, }); } else { - elementId = await System.authPostToServer('book/world/element/add', { - elementType: section, - worldId: worlds[selectedWorldIndex].id, - elementName: newElementName, - }, session.accessToken, lang); + if (book?.localBook) { + elementId = await window.electron.invoke('db:book:world:element:add', { + elementType: section, + worldId: worlds[selectedWorldIndex].id, + elementName: newElementName, + }); + } else { + elementId = await System.authPostToServer('book/world/element/add', { + elementType: section, + worldId: worlds[selectedWorldIndex].id, + elementName: newElementName, + }, session.accessToken, lang); + } } if (!elementId) { errorMessage(t("worldSetting.unknownError")) diff --git a/components/book/settings/world/WorldSetting.tsx b/components/book/settings/world/WorldSetting.tsx index 06d3839..9f5eb52 100644 --- a/components/book/settings/world/WorldSetting.tsx +++ b/components/book/settings/world/WorldSetting.tsx @@ -55,9 +55,13 @@ export function WorldSetting(props: any, ref: any) { if (isCurrentlyOffline()) { response = await window.electron.invoke('db:book:worlds:get', {bookid: bookId}); } else { - response = await System.authGetQueryToServer(`book/worlds`, session.accessToken, lang, { - bookid: bookId, - }); + if (book?.localBook) { + response = await window.electron.invoke('db:book:worlds:get', {bookid: bookId}); + } else { + response = await System.authGetQueryToServer(`book/worlds`, session.accessToken, lang, { + bookid: bookId, + }); + } } if (response) { setWorlds(response); @@ -91,10 +95,17 @@ export function WorldSetting(props: any, ref: any) { bookId: bookId, }); } else { - worldId = await System.authPostToServer('book/world/add', { - worldName: newWorldName, - bookId: bookId, - }, session.accessToken, lang); + if (book?.localBook) { + worldId = await window.electron.invoke('db:book:world:add', { + worldName: newWorldName, + bookId: bookId, + }); + } else { + worldId = await System.authPostToServer('book/world/add', { + worldName: newWorldName, + bookId: bookId, + }, session.accessToken, lang); + } } if (!worldId) { errorMessage(t("worldSetting.addWorldError")); @@ -147,10 +158,17 @@ export function WorldSetting(props: any, ref: any) { bookId: bookId, }); } else { - response = await System.authPutToServer('book/world/update', { - world: worlds[selectedWorldIndex], - bookId: bookId, - }, session.accessToken, lang); + if (book?.localBook) { + response = await window.electron.invoke('db:book:world:update', { + world: worlds[selectedWorldIndex], + bookId: bookId, + }); + } else { + response = await System.authPutToServer('book/world/update', { + world: worlds[selectedWorldIndex], + bookId: bookId, + }, session.accessToken, lang); + } } if (!response) { errorMessage(t("worldSetting.updateWorldError")); diff --git a/components/editor/DraftCompanion.tsx b/components/editor/DraftCompanion.tsx index edd1ee3..2e40645 100644 --- a/components/editor/DraftCompanion.tsx +++ b/components/editor/DraftCompanion.tsx @@ -32,6 +32,7 @@ import {LangContext, LangContextProps} from "@/context/LangContext"; import {BookTags} from "@/lib/models/Book"; import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext"; import {configs} from "@/lib/configs"; +import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; interface CompanionContent { version: number; @@ -43,6 +44,7 @@ export default function DraftCompanion() { const t = useTranslations(); const {setTotalPrice, setTotalCredits} = useContext(AIUsageContext) const {lang} = useContext(LangContext) + const {isCurrentlyOffline} = useContext(OfflineContext); const mainEditor: Editor | null = useEditor({ extensions: [ @@ -95,7 +97,7 @@ export default function DraftCompanion() { const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session); const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3; - const hasAccess: boolean = isGPTEnabled || isSubTierTree; + const hasAccess: boolean = (isGPTEnabled || isSubTierTree) && !isCurrentlyOffline() && !book?.localBook; useEffect((): void => { getDraftContent().then(); @@ -106,11 +108,28 @@ export default function DraftCompanion() { async function getDraftContent(): Promise { try { - const response: CompanionContent = await System.authGetQueryToServer(`chapter/content/companion`, session.accessToken, lang, { - bookid: book?.bookId, - chapterid: chapter?.chapterId, - version: chapter?.chapterContent.version, - }); + let response: CompanionContent | null; + if (isCurrentlyOffline()) { + response = await window.electron.invoke('db:chapter:content:companion', { + bookid: book?.bookId, + chapterid: chapter?.chapterId, + version: chapter?.chapterContent.version, + }); + } else { + if (book?.localBook) { + response = await window.electron.invoke('db:chapter:content:companion', { + bookid: book?.bookId, + chapterid: chapter?.chapterId, + version: chapter?.chapterContent.version, + }); + } else { + response = await System.authGetQueryToServer(`chapter/content/companion`, session.accessToken, lang, { + bookid: book?.bookId, + chapterid: chapter?.chapterId, + version: chapter?.chapterContent.version, + }); + } + } if (response && mainEditor) { mainEditor.commands.setContent(JSON.parse(response.content)); setDraftVersion(response.version); @@ -145,9 +164,18 @@ export default function DraftCompanion() { async function fetchTags(): Promise { try { - const responseTags: BookTags = await System.authGetQueryToServer(`book/tags`, session.accessToken, lang, { - bookId: book?.bookId - }); + let responseTags: BookTags | null; + if (isCurrentlyOffline()) { + responseTags = await window.electron.invoke('db:book:tags', book?.bookId); + } else { + if (book?.localBook) { + responseTags = await window.electron.invoke('db:book:tags', book?.bookId); + } else { + responseTags = await System.authGetQueryToServer(`book/tags`, session.accessToken, lang, { + bookId: book?.bookId + }); + } + } if (responseTags) { setCharacters(responseTags.characters); setLocations(responseTags.locations); diff --git a/components/editor/TextEditor.tsx b/components/editor/TextEditor.tsx index c0094f1..0154a41 100644 --- a/components/editor/TextEditor.tsx +++ b/components/editor/TextEditor.tsx @@ -22,6 +22,7 @@ import {ChapterContext} from '@/context/ChapterContext'; import System from '@/lib/models/System'; import {AlertContext} from '@/context/AlertContext'; import {SessionContext} from "@/context/SessionContext"; +import {BookContext} from '@/context/BookContext'; import DraftCompanion from "@/components/editor/DraftCompanion"; import GhostWriter from "@/components/ghostwriter/GhostWriter"; import SubmitButtonWLoading from "@/components/form/SubmitButtonWLoading"; @@ -135,6 +136,7 @@ export default function TextEditor() { const {lang} = useContext(LangContext) const {editor} = useContext(EditorContext); const {chapter} = useContext(ChapterContext); + const {book} = useContext(BookContext); const {errorMessage, successMessage} = useContext(AlertContext); const {session} = useContext(SessionContext); const {isCurrentlyOffline} = useContext(OfflineContext); @@ -294,13 +296,23 @@ export default function TextEditor() { currentTime: mainTimer }) } else { - response = await System.authPostToServer(`chapter/content`, { - chapterId, - version, - content, - totalWordCount: editor.getText().length, - currentTime: mainTimer - }, session?.accessToken, lang); + if (book?.localBook) { + 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, lang); + } } if (!response) { errorMessage(t('editor.error.savedFailed')); @@ -467,7 +479,7 @@ export default function TextEditor() { onClick={handleShowUserSettings} icon={faCog} /> - {chapter?.chapterContent.version === 2 && !isCurrentlyOffline() && ( + {chapter?.chapterContent.version === 2 && !isCurrentlyOffline() && !book?.localBook && ( {book ? editorComponents .filter((component: PanelComponent):boolean => { - return !(isCurrentlyOffline() && component.id === 1); + return !((isCurrentlyOffline() || book?.localBook) && component.id === 1); }) .map((component: PanelComponent) => (