Update database schema and synchronization logic
- Add `useEffect` in `ScribeLeftBar` for handling book state changes. - Extend `BooksSyncContext` with new properties and stricter typings. - Refine `Repositories` to include `lastUpdate` handling for synchronization processes. - Add comprehensive `fetchComplete*` repository methods for retrieving entity-specific sync data. - Enhance offline logic for chapters, characters, locations, and world synchronization. - Improve error handling across IPC handlers and repositories.
This commit is contained in:
47
app/page.tsx
47
app/page.tsx
@@ -33,7 +33,7 @@ import OfflineProvider from "@/context/OfflineProvider";
|
|||||||
import OfflineContext from "@/context/OfflineContext";
|
import OfflineContext from "@/context/OfflineContext";
|
||||||
import OfflinePinSetup from "@/components/offline/OfflinePinSetup";
|
import OfflinePinSetup from "@/components/offline/OfflinePinSetup";
|
||||||
import OfflinePinVerify from "@/components/offline/OfflinePinVerify";
|
import OfflinePinVerify from "@/components/offline/OfflinePinVerify";
|
||||||
import {SyncedBook} from "@/lib/models/SyncedBook";
|
import {SyncedBook, BookSyncCompare, compareBookSyncs} from "@/lib/models/SyncedBook";
|
||||||
import {BooksSyncContext} from "@/context/BooksSyncContext";
|
import {BooksSyncContext} from "@/context/BooksSyncContext";
|
||||||
|
|
||||||
const messagesMap = {
|
const messagesMap = {
|
||||||
@@ -64,8 +64,8 @@ function ScribeContent() {
|
|||||||
|
|
||||||
const [serverSyncedBooks, setServerSyncedBooks] = useState<SyncedBook[]>([]);
|
const [serverSyncedBooks, setServerSyncedBooks] = useState<SyncedBook[]>([]);
|
||||||
const [localSyncedBooks, setLocalSyncedBooks] = useState<SyncedBook[]>([]);
|
const [localSyncedBooks, setLocalSyncedBooks] = useState<SyncedBook[]>([]);
|
||||||
const [booksToSyncFromServer, setBooksToSyncFromServer] = useState<SyncedBook[]>([]);
|
const [bookSyncDiffsFromServer, setBookSyncDiffsFromServer] = useState<BookSyncCompare[]>([]);
|
||||||
const [booksToSyncToServer, setBooksToSyncToServer] = useState<SyncedBook[]>([]);
|
const [bookSyncDiffsToServer, setBookSyncDiffsToServer] = useState<BookSyncCompare[]>([]);
|
||||||
const [serverOnlyBooks, setServerOnlyBooks] = useState<SyncedBook[]>([]);
|
const [serverOnlyBooks, setServerOnlyBooks] = useState<SyncedBook[]>([]);
|
||||||
const [localOnlyBooks, setLocalOnlyBooks] = useState<SyncedBook[]>([]);
|
const [localOnlyBooks, setLocalOnlyBooks] = useState<SyncedBook[]>([]);
|
||||||
|
|
||||||
@@ -166,16 +166,35 @@ function ScribeContent() {
|
|||||||
}, [currentBook]);
|
}, [currentBook]);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
setBooksToSyncFromServer(serverSyncedBooks.filter((serverBook: SyncedBook):boolean => {
|
const diffsFromServer: BookSyncCompare[] = [];
|
||||||
const localBook: SyncedBook | undefined = localSyncedBooks.find((localBook: SyncedBook):boolean => localBook.id === serverBook.id);
|
const diffsToServer: BookSyncCompare[] = [];
|
||||||
console.log('localBook from setBookToSyncFromServer',localBook);
|
|
||||||
console.log('serverBook from setBookToSyncFromServer',serverBook);
|
serverSyncedBooks.forEach((serverBook: SyncedBook): void => {
|
||||||
return !localBook || localBook.lastUpdate < serverBook.lastUpdate;
|
const localBook: SyncedBook | undefined = localSyncedBooks.find((book: SyncedBook): boolean => book.id === serverBook.id);
|
||||||
}))
|
if (!localBook) {
|
||||||
setBooksToSyncToServer(localSyncedBooks.filter((localBook: SyncedBook):boolean => {
|
return;
|
||||||
const serverBook: SyncedBook | undefined = serverSyncedBooks.find((serverBook: SyncedBook):boolean => serverBook.id === localBook.id);
|
}
|
||||||
return !serverBook || serverBook.lastUpdate < localBook.lastUpdate;
|
|
||||||
}))
|
const diff: BookSyncCompare | null = compareBookSyncs(serverBook, localBook);
|
||||||
|
if (diff) {
|
||||||
|
diffsFromServer.push(diff);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
localSyncedBooks.forEach((localBook: SyncedBook): void => {
|
||||||
|
const serverBook: SyncedBook | undefined = serverSyncedBooks.find((book: SyncedBook): boolean => book.id === localBook.id);
|
||||||
|
if (!serverBook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diff: BookSyncCompare | null = compareBookSyncs(localBook, serverBook);
|
||||||
|
if (diff) {
|
||||||
|
diffsToServer.push(diff);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setBookSyncDiffsFromServer(diffsFromServer);
|
||||||
|
setBookSyncDiffsToServer(diffsToServer);
|
||||||
setServerOnlyBooks(serverSyncedBooks.filter((serverBook: SyncedBook):boolean => !localSyncedBooks.find((localBook: SyncedBook):boolean => localBook.id === serverBook.id)))
|
setServerOnlyBooks(serverSyncedBooks.filter((serverBook: SyncedBook):boolean => !localSyncedBooks.find((localBook: SyncedBook):boolean => localBook.id === serverBook.id)))
|
||||||
setLocalOnlyBooks(localSyncedBooks.filter((localBook: SyncedBook):boolean => !serverSyncedBooks.find((serverBook: SyncedBook):boolean => serverBook.id === localBook.id)))
|
setLocalOnlyBooks(localSyncedBooks.filter((localBook: SyncedBook):boolean => !serverSyncedBooks.find((serverBook: SyncedBook):boolean => serverBook.id === localBook.id)))
|
||||||
}, [localSyncedBooks, serverSyncedBooks]);
|
}, [localSyncedBooks, serverSyncedBooks]);
|
||||||
@@ -488,7 +507,7 @@ function ScribeContent() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SessionContext.Provider value={{session: session, setSession: setSession}}>
|
<SessionContext.Provider value={{session: session, setSession: setSession}}>
|
||||||
<BooksSyncContext.Provider value={{serverSyncedBooks, localSyncedBooks, booksToSyncFromServer, booksToSyncToServer, serverOnlyBooks, localOnlyBooks}}>
|
<BooksSyncContext.Provider value={{serverSyncedBooks, localSyncedBooks, booksToSyncFromServer:bookSyncDiffsFromServer, booksToSyncToServer:bookSyncDiffsToServer, setServerOnlyBooks, setLocalOnlyBooks, serverOnlyBooks, localOnlyBooks}}>
|
||||||
<BookContext.Provider value={{book: currentBook, setBook: setCurrentBook}}>
|
<BookContext.Provider value={{book: currentBook, setBook: setCurrentBook}}>
|
||||||
<ChapterContext.Provider value={{chapter: currentChapter, setChapter: setCurrentChapter}}>
|
<ChapterContext.Provider value={{chapter: currentChapter, setChapter: setCurrentChapter}}>
|
||||||
<AIUsageContext.Provider value={{
|
<AIUsageContext.Provider value={{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {LangContext, LangContextProps} from "@/context/LangContext";
|
|||||||
import CreditCounter from "@/components/CreditMeters";
|
import CreditCounter from "@/components/CreditMeters";
|
||||||
import QuillSense from "@/lib/models/QuillSense";
|
import QuillSense from "@/lib/models/QuillSense";
|
||||||
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||||
|
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
|
||||||
|
|
||||||
export default function ScribeControllerBar() {
|
export default function ScribeControllerBar() {
|
||||||
const {chapter, setChapter} = useContext(ChapterContext);
|
const {chapter, setChapter} = useContext(ChapterContext);
|
||||||
@@ -27,6 +28,7 @@ export default function ScribeControllerBar() {
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const {lang, setLang} = useContext<LangContextProps>(LangContext);
|
const {lang, setLang} = useContext<LangContextProps>(LangContext);
|
||||||
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext)
|
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext)
|
||||||
|
const {serverOnlyBooks,localOnlyBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
|
||||||
|
|
||||||
const isGPTEnabled: boolean = !isCurrentlyOffline() && QuillSense.isOpenAIEnabled(session);
|
const isGPTEnabled: boolean = !isCurrentlyOffline() && QuillSense.isOpenAIEnabled(session);
|
||||||
const isGemini: boolean = !isCurrentlyOffline() && QuillSense.isOpenAIEnabled(session);
|
const isGemini: boolean = !isCurrentlyOffline() && QuillSense.isOpenAIEnabled(session);
|
||||||
@@ -120,7 +122,7 @@ export default function ScribeControllerBar() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="min-w-[200px]">
|
<div className="min-w-[200px]">
|
||||||
<SelectBox onChangeCallBack={(e) => getBook(e.target.value)}
|
<SelectBox onChangeCallBack={(e) => getBook(e.target.value)}
|
||||||
data={Book.booksToSelectBox(session.user?.books ?? [])} defaultValue={book?.bookId}
|
data={Book.booksToSelectBox([...serverOnlyBooks, ...localOnlyBooks])} defaultValue={book?.bookId}
|
||||||
placeholder={t("controllerBar.selectBook")}/>
|
placeholder={t("controllerBar.selectBook")}/>
|
||||||
</div>
|
</div>
|
||||||
{chapter && (
|
{chapter && (
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import {SessionContext} from "@/context/SessionContext";
|
|||||||
import {useTranslations} from "next-intl";
|
import {useTranslations} from "next-intl";
|
||||||
import {AlertContext} from "@/context/AlertContext";
|
import {AlertContext} from "@/context/AlertContext";
|
||||||
import {BookContext} from "@/context/BookContext";
|
import {BookContext} from "@/context/BookContext";
|
||||||
|
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
|
||||||
|
|
||||||
export default function ScribeFooterBar() {
|
export default function ScribeFooterBar() {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const {chapter} = useContext(ChapterContext);
|
const {chapter} = useContext(ChapterContext);
|
||||||
const {book} = useContext(BookContext);
|
const {book} = useContext(BookContext);
|
||||||
const editor: Editor | null = useContext(EditorContext).editor;
|
const editor: Editor | null = useContext(EditorContext).editor;
|
||||||
const {session} = useContext(SessionContext);
|
|
||||||
const {errorMessage} = useContext(AlertContext)
|
const {errorMessage} = useContext(AlertContext)
|
||||||
|
const {serverOnlyBooks,localOnlyBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
|
||||||
|
|
||||||
const [wordsCount, setWordsCount] = useState<number>(0);
|
const [wordsCount, setWordsCount] = useState<number>(0);
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ export default function ScribeFooterBar() {
|
|||||||
className="flex items-center gap-2 bg-secondary/50 px-4 py-2 rounded-xl border border-secondary shadow-sm">
|
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'}/>
|
<FontAwesomeIcon icon={faBook} className={'text-primary w-4 h-4'}/>
|
||||||
<span className="text-muted text-sm font-medium mr-1">{t('scribeFooterBar.books')}:</span>
|
<span className="text-muted text-sm font-medium mr-1">{t('scribeFooterBar.books')}:</span>
|
||||||
<span className="text-text-primary font-bold">{session.user?.books?.length}</span>
|
<span className="text-text-primary font-bold">{(serverOnlyBooks.length+localOnlyBooks.length)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import System from "@/lib/models/System";
|
|||||||
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
||||||
import {LangContext} from "@/context/LangContext";
|
import {LangContext} from "@/context/LangContext";
|
||||||
import {CompleteBook} from "@/lib/models/Book";
|
import {CompleteBook} from "@/lib/models/Book";
|
||||||
import {SyncType} from "@/context/BooksSyncContext";
|
import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext";
|
||||||
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
|
||||||
|
import {BookSyncCompare} from "@/lib/models/SyncedBook";
|
||||||
|
|
||||||
interface SyncBookProps {
|
interface SyncBookProps {
|
||||||
bookId: string;
|
bookId: string;
|
||||||
@@ -23,6 +24,7 @@ export default function SyncBook({bookId, status}: SyncBookProps) {
|
|||||||
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
|
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [currentStatus, setCurrentStatus] = useState<SyncType>(status);
|
const [currentStatus, setCurrentStatus] = useState<SyncType>(status);
|
||||||
|
const {booksToSyncToServer, booksToSyncFromServer} = useContext<BooksSyncContextProps>(BooksSyncContext)
|
||||||
|
|
||||||
const isOffline: boolean = isCurrentlyOffline();
|
const isOffline: boolean = isCurrentlyOffline();
|
||||||
|
|
||||||
@@ -53,11 +55,67 @@ export default function SyncBook({bookId, status}: SyncBookProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function syncFromServer(): Promise<void> {
|
async function syncFromServer(): Promise<void> {
|
||||||
// TODO: Implement sync from server (server has newer version)
|
if (isOffline) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const bookToFetch:BookSyncCompare|undefined = booksToSyncFromServer.find((book:BookSyncCompare):boolean => book.id === bookId);
|
||||||
|
if (!bookToFetch) {
|
||||||
|
errorMessage(t("bookCard.syncFromServerError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const response: CompleteBook = await System.authPostToServer('book/sync/server-to-client', {
|
||||||
|
bookToSync: bookToFetch
|
||||||
|
}, session.accessToken, lang);
|
||||||
|
if (!response) {
|
||||||
|
errorMessage(t("bookCard.syncFromServerError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const syncStatus:boolean = await window.electron.invoke<boolean>('db:book:sync:toClient', response);
|
||||||
|
if (!syncStatus) {
|
||||||
|
errorMessage(t("bookCard.syncFromServerError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCurrentStatus('synced');
|
||||||
|
} catch (e:unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
errorMessage(e.message);
|
||||||
|
} else {
|
||||||
|
errorMessage(t("bookCard.syncFromServerError"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncToServer(): Promise<void> {
|
async function syncToServer(): Promise<void> {
|
||||||
// TODO: Implement sync to server (local has newer version)
|
if (isOffline) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const bookToFetch:BookSyncCompare|undefined = booksToSyncToServer.find((book:BookSyncCompare):boolean => book.id === bookId);
|
||||||
|
if (!bookToFetch) {
|
||||||
|
errorMessage(t("bookCard.syncToServerError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bookToSync: CompleteBook = await window.electron.invoke<CompleteBook>('db:book:sync:toServer', bookToFetch);
|
||||||
|
if (!bookToSync) {
|
||||||
|
errorMessage(t("bookCard.syncToServerError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const response: boolean = await System.authPutToServer('book/sync/client-to-server', {
|
||||||
|
book: bookToSync
|
||||||
|
}, session.accessToken, lang);
|
||||||
|
if (!response) {
|
||||||
|
errorMessage(t("bookCard.syncToServerError"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCurrentStatus('synced');
|
||||||
|
} catch (e:unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
errorMessage(e.message);
|
||||||
|
} else {
|
||||||
|
errorMessage(t("bookCard.syncToServerError"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {useTranslations} from "next-intl";
|
|||||||
import {LangContext, LangContextProps} from "@/context/LangContext";
|
import {LangContext, LangContextProps} from "@/context/LangContext";
|
||||||
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||||
import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext";
|
import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext";
|
||||||
import {SyncedBook} from "@/lib/models/SyncedBook";
|
import {BookSyncCompare, SyncedBook} from "@/lib/models/SyncedBook";
|
||||||
|
|
||||||
export default function BookList() {
|
export default function BookList() {
|
||||||
const {session, setSession} = useContext(SessionContext);
|
const {session, setSession} = useContext(SessionContext);
|
||||||
@@ -190,10 +190,10 @@ export default function BookList() {
|
|||||||
if (localOnlyBooks.find((book: SyncedBook):boolean => book.id === bookId)) {
|
if (localOnlyBooks.find((book: SyncedBook):boolean => book.id === bookId)) {
|
||||||
return 'local-only';
|
return 'local-only';
|
||||||
}
|
}
|
||||||
if (booksToSyncFromServer.find((book: SyncedBook):boolean => book.id === bookId)) {
|
if (booksToSyncFromServer.find((book: BookSyncCompare):boolean => book.id === bookId)) {
|
||||||
return 'to-sync-from-server';
|
return 'to-sync-from-server';
|
||||||
}
|
}
|
||||||
if (booksToSyncToServer.find((book: SyncedBook):boolean => book.id === bookId)) {
|
if (booksToSyncToServer.find((book: BookSyncCompare):boolean => book.id === bookId)) {
|
||||||
return 'to-sync-to-server';
|
return 'to-sync-to-server';
|
||||||
}
|
}
|
||||||
return 'synced';
|
return 'synced';
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export default function ScribeChapterComponent() {
|
|||||||
const scrollContainerRef = useRef<HTMLUListElement>(null);
|
const scrollContainerRef = useRef<HTMLUListElement>(null);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
|
if (book)
|
||||||
getChapterList().then();
|
getChapterList().then();
|
||||||
}, [book]);
|
}, [book]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faBookMedical, faBookOpen, faFeather} from "@fortawesome/free-solid-svg-icons";
|
import {faBookMedical, faBookOpen, faFeather} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {useContext, useState} from "react";
|
import {useContext, useEffect, useState} from "react";
|
||||||
import {BookContext} from "@/context/BookContext";
|
import {BookContext} from "@/context/BookContext";
|
||||||
import ScribeChapterComponent from "@/components/leftbar/ScribeChapterComponent";
|
import ScribeChapterComponent from "@/components/leftbar/ScribeChapterComponent";
|
||||||
import PanelHeader from "@/components/PanelHeader";
|
import PanelHeader from "@/components/PanelHeader";
|
||||||
@@ -75,6 +75,14 @@ export default function ScribeLeftBar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(():void => {
|
||||||
|
if (!book){
|
||||||
|
setCurrentPanel(undefined);
|
||||||
|
setPanelHidden(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [book]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="left-panel-container" data-guide={"left-panel-container"} className="flex transition-all duration-300">
|
<div id="left-panel-container" data-guide={"left-panel-container"} className="flex transition-all duration-300">
|
||||||
<div className="bg-tertiary border-r border-secondary/50 p-3 flex flex-col space-y-3 shadow-xl">
|
<div className="bg-tertiary border-r border-secondary/50 p-3 flex flex-col space-y-3 shadow-xl">
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faFeather, faGlobe, faInfoCircle, faMapMarkerAlt, faUsers} from "@fortawesome/free-solid-svg-icons";
|
import {faFeather, faGlobe, faInfoCircle, faMapMarkerAlt, faUsers} from "@fortawesome/free-solid-svg-icons";
|
||||||
import {RefObject, useContext, useRef, useState} from "react";
|
import {RefObject, useContext, useEffect, useRef, useState} from "react";
|
||||||
import {BookContext} from "@/context/BookContext";
|
import {BookContext} from "@/context/BookContext";
|
||||||
import {ChapterContext} from "@/context/ChapterContext";
|
|
||||||
import {PanelComponent} from "@/lib/models/Editor";
|
import {PanelComponent} from "@/lib/models/Editor";
|
||||||
import PanelHeader from "@/components/PanelHeader";
|
import PanelHeader from "@/components/PanelHeader";
|
||||||
import AboutEditors from "@/components/rightbar/AboutERitors";
|
import AboutEditors from "@/components/rightbar/AboutERitors";
|
||||||
@@ -57,6 +56,14 @@ export default function ComposerRightBar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(():void => {
|
||||||
|
if (!book){
|
||||||
|
setCurrentPanel(undefined);
|
||||||
|
setPanelHidden(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [book]);
|
||||||
|
|
||||||
const editorComponents: PanelComponent[] = [
|
const editorComponents: PanelComponent[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import {SyncedBook} from "@/lib/models/SyncedBook";
|
import {BookSyncCompare, SyncedBook} from "@/lib/models/SyncedBook";
|
||||||
import {Context, createContext} from "react";
|
import {Context, createContext, Dispatch, SetStateAction} from "react";
|
||||||
|
|
||||||
export type SyncType = 'server-only' | 'local-only' | 'to-sync-from-server' | 'to-sync-to-server' | 'synced'
|
export type SyncType = 'server-only' | 'local-only' | 'to-sync-from-server' | 'to-sync-to-server' | 'synced'
|
||||||
|
|
||||||
export interface BooksSyncContextProps {
|
export interface BooksSyncContextProps {
|
||||||
serverSyncedBooks:SyncedBook[];
|
serverSyncedBooks:SyncedBook[];
|
||||||
localSyncedBooks:SyncedBook[];
|
localSyncedBooks:SyncedBook[];
|
||||||
booksToSyncFromServer:SyncedBook[];
|
booksToSyncFromServer:BookSyncCompare[];
|
||||||
booksToSyncToServer:SyncedBook[];
|
booksToSyncToServer:BookSyncCompare[];
|
||||||
|
setServerOnlyBooks:Dispatch<SetStateAction<SyncedBook[]>>;
|
||||||
|
setLocalOnlyBooks:Dispatch<SetStateAction<SyncedBook[]>>;
|
||||||
serverOnlyBooks:SyncedBook[];
|
serverOnlyBooks:SyncedBook[];
|
||||||
localOnlyBooks:SyncedBook[];
|
localOnlyBooks:SyncedBook[];
|
||||||
}
|
}
|
||||||
@@ -17,6 +19,8 @@ export const BooksSyncContext:Context<BooksSyncContextProps> = createContext<Boo
|
|||||||
localSyncedBooks:[],
|
localSyncedBooks:[],
|
||||||
booksToSyncFromServer:[],
|
booksToSyncFromServer:[],
|
||||||
booksToSyncToServer:[],
|
booksToSyncToServer:[],
|
||||||
|
setServerOnlyBooks:():void => {},
|
||||||
|
setLocalOnlyBooks:():void => {},
|
||||||
serverOnlyBooks:[],
|
serverOnlyBooks:[],
|
||||||
localOnlyBooks:[]
|
localOnlyBooks:[]
|
||||||
})
|
})
|
||||||
@@ -43,6 +43,8 @@ import fs from "fs";
|
|||||||
import Chapter, {ActChapter, ChapterContentData, ChapterProps} from "./Chapter.js";
|
import Chapter, {ActChapter, ChapterContentData, ChapterProps} from "./Chapter.js";
|
||||||
import UserRepo from "../repositories/user.repository.js";
|
import UserRepo from "../repositories/user.repository.js";
|
||||||
import ChapterRepo from "../repositories/chapter.repository.js";
|
import ChapterRepo from "../repositories/chapter.repository.js";
|
||||||
|
import CharacterRepo from "../repositories/character.repository.js";
|
||||||
|
import LocationRepo from "../repositories/location.repository.js";
|
||||||
|
|
||||||
export interface BookProps{
|
export interface BookProps{
|
||||||
id:string;
|
id:string;
|
||||||
@@ -97,6 +99,26 @@ export interface SyncedBook {
|
|||||||
aiGuideLine: SyncedAIGuideLine | null;
|
aiGuideLine: SyncedAIGuideLine | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BookSyncCompare {
|
||||||
|
id: string;
|
||||||
|
chapters: string[];
|
||||||
|
chapterContents: string[];
|
||||||
|
chapterInfos: string[];
|
||||||
|
characters: string[];
|
||||||
|
characterAttributes: string[];
|
||||||
|
locations: string[];
|
||||||
|
locationElements: string[];
|
||||||
|
locationSubElements: string[];
|
||||||
|
worlds: string[];
|
||||||
|
worldElements: string[];
|
||||||
|
incidents: string[];
|
||||||
|
plotPoints: string[];
|
||||||
|
issues: string[];
|
||||||
|
actSummaries: string[];
|
||||||
|
guideLine: boolean;
|
||||||
|
aiGuideLine: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SyncedChapter {
|
export interface SyncedChapter {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -617,7 +639,7 @@ export default class Book {
|
|||||||
if (actId === 1 || actId === 4 || actId === 5) {
|
if (actId === 1 || actId === 4 || actId === 5) {
|
||||||
const actSummary: string = act.summary ? System.encryptDataWithUserKey(act.summary, userKey) : '';
|
const actSummary: string = act.summary ? System.encryptDataWithUserKey(act.summary, userKey) : '';
|
||||||
try {
|
try {
|
||||||
BookRepo.updateActSummary(userId, bookId, actId, actSummary,lang);
|
BookRepo.updateActSummary(userId, bookId, actId, actSummary,System.timeStampInSeconds(),lang);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
const actSummaryId: string = System.createUniqueId();
|
const actSummaryId: string = System.createUniqueId();
|
||||||
BookRepo.insertActSummary(actSummaryId, userId, bookId, actId, actSummary,lang);
|
BookRepo.insertActSummary(actSummaryId, userId, bookId, actId, actSummary,lang);
|
||||||
@@ -632,7 +654,7 @@ export default class Book {
|
|||||||
const incidentName: string = incident.title;
|
const incidentName: string = incident.title;
|
||||||
const incidentHashedName: string = System.hashElement(incidentName);
|
const incidentHashedName: string = System.hashElement(incidentName);
|
||||||
const encryptedIncidentName: string = System.encryptDataWithUserKey(incidentName, userKey);
|
const encryptedIncidentName: string = System.encryptDataWithUserKey(incidentName, userKey);
|
||||||
BookRepo.updateIncident(userId, bookId, incidentId, encryptedIncidentName, incidentHashedName, incidentSummary, lang);
|
BookRepo.updateIncident(userId, bookId, incidentId, encryptedIncidentName, incidentHashedName, incidentSummary, System.timeStampInSeconds(), lang);
|
||||||
if (incident.chapters) {
|
if (incident.chapters) {
|
||||||
Chapter.updateChapterInfos(incident.chapters, userId, actId, bookId, incidentId, null, lang);
|
Chapter.updateChapterInfos(incident.chapters, userId, actId, bookId, incidentId, null, lang);
|
||||||
}
|
}
|
||||||
@@ -645,7 +667,7 @@ export default class Book {
|
|||||||
const plotPointName: string = plotPoint.title;
|
const plotPointName: string = plotPoint.title;
|
||||||
const plotPointHashedName: string = System.hashElement(plotPointName);
|
const plotPointHashedName: string = System.hashElement(plotPointName);
|
||||||
const encryptedPlotPointName: string = System.encryptDataWithUserKey(plotPointName, userKey);
|
const encryptedPlotPointName: string = System.encryptDataWithUserKey(plotPointName, userKey);
|
||||||
BookRepo.updatePlotPoint(userId, bookId, plotPointId, encryptedPlotPointName, plotPointHashedName, plotPointSummary, lang);
|
BookRepo.updatePlotPoint(userId, bookId, plotPointId, encryptedPlotPointName, plotPointHashedName, plotPointSummary, System.timeStampInSeconds(), lang);
|
||||||
if (plotPoint.chapters) {
|
if (plotPoint.chapters) {
|
||||||
Chapter.updateChapterInfos(plotPoint.chapters, userId, actId, bookId, null, plotPointId, lang);
|
Chapter.updateChapterInfos(plotPoint.chapters, userId, actId, bookId, null, plotPointId, lang);
|
||||||
}
|
}
|
||||||
@@ -664,7 +686,7 @@ export default class Book {
|
|||||||
const chapterHashedTitle: string = System.hashElement(chapterTitle);
|
const chapterHashedTitle: string = System.hashElement(chapterTitle);
|
||||||
const encryptedTitle: string = System.encryptDataWithUserKey(chapterTitle, userKey);
|
const encryptedTitle: string = System.encryptDataWithUserKey(chapterTitle, userKey);
|
||||||
const chapterOrder: number = chapter.chapterOrder;
|
const chapterOrder: number = chapter.chapterOrder;
|
||||||
ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, chapterHashedTitle, chapterOrder, lang);
|
ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, chapterHashedTitle, chapterOrder, System.timeStampInSeconds(), lang);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -849,7 +871,7 @@ export default class Book {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
BookRepo.updateWorld(userId, world.id, encryptName, System.hashElement(world.name), encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lang);
|
BookRepo.updateWorld(userId, world.id, encryptName, System.hashElement(world.name), encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, System.timeStampInSeconds(), lang);
|
||||||
return BookRepo.updateWorldElements(userId, elements, lang);
|
return BookRepo.updateWorldElements(userId, elements, lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1466,4 +1488,450 @@ export default class Book {
|
|||||||
return BookRepo.insertSyncIssue(issue.issue_id, userId, issue.book_id, encryptedIssueName, issue.hashed_issue_name, issue.last_update, lang);
|
return BookRepo.insertSyncIssue(issue.issue_id, userId, issue.book_id, encryptedIssueName, issue.hashed_issue_name, issue.last_update, lang);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getCompleteSyncBook(userId: string, data: BookSyncCompare, lang: "fr" | "en"):Promise<CompleteBook> {
|
||||||
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
|
const bookData: EritBooksTable[] = [];
|
||||||
|
const chaptersData: BookChaptersTable[] = [];
|
||||||
|
const plotPointsData: BookPlotPointsTable[] = [];
|
||||||
|
const incidentsData: BookIncidentsTable[] = [];
|
||||||
|
const chapterContentsData: BookChapterContentTable[] = [];
|
||||||
|
const chapterInfosData: BookChapterInfosTable[] = [];
|
||||||
|
const charactersData: BookCharactersTable[] = [];
|
||||||
|
const characterAttributesData: BookCharactersAttributesTable[] = [];
|
||||||
|
const locationsData: BookLocationTable[] = [];
|
||||||
|
const locationElementsData: LocationElementTable[] = [];
|
||||||
|
const locationSubElementsData: LocationSubElementTable[] = [];
|
||||||
|
const worldsData: BookWorldTable[] = [];
|
||||||
|
const worldElementsData: BookWorldElementsTable[] = [];
|
||||||
|
const actSummariesData: BookActSummariesTable[] = [];
|
||||||
|
const guideLineData: BookGuideLineTable[] = [];
|
||||||
|
const aiGuideLineData: BookAIGuideLineTable[] = [];
|
||||||
|
const issuesData: BookIssuesTable[] = [];
|
||||||
|
|
||||||
|
const actSummaries: string[] = data.actSummaries;
|
||||||
|
const chapters: string[] = data.chapters;
|
||||||
|
const plotPoints: string[] = data.plotPoints;
|
||||||
|
const incidents: string[] = data.incidents;
|
||||||
|
const chapterContents: string[] = data.chapterContents;
|
||||||
|
const chapterInfos: string[] = data.chapterInfos;
|
||||||
|
const characters: string[] = data.characters;
|
||||||
|
const characterAttributes: string[] = data.characterAttributes;
|
||||||
|
const locations: string[] = data.locations;
|
||||||
|
const locationElements: string[] = data.locationElements;
|
||||||
|
const locationSubElements: string[] = data.locationSubElements;
|
||||||
|
const worlds: string[] = data.worlds;
|
||||||
|
const worldElements: string[] = data.worldElements;
|
||||||
|
const issues: string[] = data.issues;
|
||||||
|
|
||||||
|
if (actSummaries && actSummaries.length > 0) {
|
||||||
|
for (const id of actSummaries) {
|
||||||
|
const actSummary: BookActSummariesTable[] = await BookRepo.fetchCompleteActSummaryById(id, lang);
|
||||||
|
if (actSummary.length>0) {
|
||||||
|
const actSummaryData: BookActSummariesTable = actSummary[0];
|
||||||
|
actSummariesData.push({
|
||||||
|
...actSummaryData,
|
||||||
|
summary: actSummaryData.summary ? System.decryptDataWithUserKey(actSummaryData.summary, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapters && chapters.length > 0) {
|
||||||
|
for (const id of chapters) {
|
||||||
|
const chapter: BookChaptersTable[] = await BookRepo.fetchCompleteChapterById(id, lang);
|
||||||
|
if (chapter.length>0) {
|
||||||
|
const chapterData: BookChaptersTable = chapter[0];
|
||||||
|
chaptersData.push({
|
||||||
|
...chapterData,
|
||||||
|
title: System.decryptDataWithUserKey(chapterData.title, userKey)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plotPoints && plotPoints.length > 0) {
|
||||||
|
for (const id of plotPoints) {
|
||||||
|
const plotPoint: BookPlotPointsTable[] = await BookRepo.fetchCompletePlotPointById(id, lang);
|
||||||
|
if (plotPoint.length>0) {
|
||||||
|
const plotPointData: BookPlotPointsTable = plotPoint[0];
|
||||||
|
plotPointsData.push({
|
||||||
|
...plotPointData,
|
||||||
|
title: System.decryptDataWithUserKey(plotPointData.title, userKey),
|
||||||
|
summary: plotPointData.summary ? System.decryptDataWithUserKey(plotPointData.summary, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incidents && incidents.length > 0) {
|
||||||
|
for (const id of incidents) {
|
||||||
|
const incident: BookIncidentsTable[] = await BookRepo.fetchCompleteIncidentById(id, lang);
|
||||||
|
if (incident.length>0) {
|
||||||
|
const incidentData: BookIncidentsTable = incident[0];
|
||||||
|
incidentsData.push({
|
||||||
|
...incidentData,
|
||||||
|
title: System.decryptDataWithUserKey(incidentData.title, userKey),
|
||||||
|
summary: incidentData.summary ? System.decryptDataWithUserKey(incidentData.summary, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapterContents && chapterContents.length > 0) {
|
||||||
|
for (const id of chapterContents) {
|
||||||
|
const chapterContent: BookChapterContentTable[] = await BookRepo.fetchCompleteChapterContentById(id, lang);
|
||||||
|
if (chapterContent.length>0) {
|
||||||
|
const chapterContentData: BookChapterContentTable = chapterContent[0];
|
||||||
|
chapterContentsData.push({
|
||||||
|
...chapterContentData,
|
||||||
|
content: chapterContentData.content ? JSON.parse(System.decryptDataWithUserKey(chapterContentData.content, userKey)) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapterInfos && chapterInfos.length > 0) {
|
||||||
|
for (const id of chapterInfos) {
|
||||||
|
const chapterInfo: BookChapterInfosTable[] = await BookRepo.fetchCompleteChapterInfoById(id, lang);
|
||||||
|
if (chapterInfo.length>0) {
|
||||||
|
const chapterInfoData: BookChapterInfosTable = chapterInfo[0];
|
||||||
|
chapterInfosData.push({
|
||||||
|
...chapterInfoData,
|
||||||
|
summary: chapterInfoData.summary ? System.decryptDataWithUserKey(chapterInfoData.summary, userKey) : null,
|
||||||
|
goal: chapterInfoData.goal ? System.decryptDataWithUserKey(chapterInfoData.goal, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characters && characters.length > 0) {
|
||||||
|
for (const id of characters) {
|
||||||
|
const character: BookCharactersTable[] = await BookRepo.fetchCompleteCharacterById(id, lang);
|
||||||
|
if (character.length>0) {
|
||||||
|
const characterData: BookCharactersTable = character[0];
|
||||||
|
charactersData.push({
|
||||||
|
...characterData,
|
||||||
|
first_name: System.decryptDataWithUserKey(characterData.first_name, userKey),
|
||||||
|
last_name: characterData.last_name ? System.decryptDataWithUserKey(characterData.last_name, userKey) : null,
|
||||||
|
category: System.decryptDataWithUserKey(characterData.category, userKey),
|
||||||
|
title: characterData.title ? System.decryptDataWithUserKey(characterData.title, userKey) : null,
|
||||||
|
role: characterData.role ? System.decryptDataWithUserKey(characterData.role, userKey) : null,
|
||||||
|
biography: characterData.biography ? System.decryptDataWithUserKey(characterData.biography, userKey) : null,
|
||||||
|
history: characterData.history ? System.decryptDataWithUserKey(characterData.history, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characterAttributes && characterAttributes.length > 0) {
|
||||||
|
for (const id of characterAttributes) {
|
||||||
|
const characterAttribute: BookCharactersAttributesTable[] = await BookRepo.fetchCompleteCharacterAttributeById(id, lang);
|
||||||
|
if (characterAttribute.length>0) {
|
||||||
|
const characterAttributeData: BookCharactersAttributesTable = characterAttribute[0];
|
||||||
|
characterAttributesData.push({
|
||||||
|
...characterAttributeData,
|
||||||
|
attribute_name: System.decryptDataWithUserKey(characterAttributeData.attribute_name, userKey),
|
||||||
|
attribute_value: System.decryptDataWithUserKey(characterAttributeData.attribute_value, userKey)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locations && locations.length > 0) {
|
||||||
|
for (const id of locations) {
|
||||||
|
const location: BookLocationTable[] = await BookRepo.fetchCompleteLocationById(id, lang);
|
||||||
|
if (location.length>0) {
|
||||||
|
const locationData: BookLocationTable = location[0];
|
||||||
|
locationsData.push({
|
||||||
|
...locationData,
|
||||||
|
loc_name: System.decryptDataWithUserKey(locationData.loc_name, userKey)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationElements && locationElements.length > 0) {
|
||||||
|
for (const id of locationElements) {
|
||||||
|
const locationElement: LocationElementTable[] = await BookRepo.fetchCompleteLocationElementById(id, lang);
|
||||||
|
if (locationElement.length>0) {
|
||||||
|
const locationElementData: LocationElementTable = locationElement[0];
|
||||||
|
locationElementsData.push({
|
||||||
|
...locationElementData,
|
||||||
|
element_name: System.decryptDataWithUserKey(locationElementData.element_name, userKey),
|
||||||
|
element_description: locationElementData.element_description ? System.decryptDataWithUserKey(locationElementData.element_description, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationSubElements && locationSubElements.length > 0) {
|
||||||
|
for (const id of locationSubElements) {
|
||||||
|
const locationSubElement: LocationSubElementTable[] = await BookRepo.fetchCompleteLocationSubElementById(id, lang);
|
||||||
|
if (locationSubElement.length>0) {
|
||||||
|
const locationSubElementData: LocationSubElementTable = locationSubElement[0];
|
||||||
|
locationSubElementsData.push({
|
||||||
|
...locationSubElementData,
|
||||||
|
sub_elem_name: System.decryptDataWithUserKey(locationSubElementData.sub_elem_name, userKey),
|
||||||
|
sub_elem_description: locationSubElementData.sub_elem_description ? System.decryptDataWithUserKey(locationSubElementData.sub_elem_description, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worlds && worlds.length > 0) {
|
||||||
|
for (const id of worlds) {
|
||||||
|
const world: BookWorldTable[] = await BookRepo.fetchCompleteWorldById(id, lang);
|
||||||
|
if (world.length>0) {
|
||||||
|
const worldData: BookWorldTable = world[0];
|
||||||
|
worldsData.push({
|
||||||
|
...worldData,
|
||||||
|
name: System.decryptDataWithUserKey(worldData.name, userKey),
|
||||||
|
history: worldData.history ? System.decryptDataWithUserKey(worldData.history, userKey) : null,
|
||||||
|
politics: worldData.politics ? System.decryptDataWithUserKey(worldData.politics, userKey) : null,
|
||||||
|
economy: worldData.economy ? System.decryptDataWithUserKey(worldData.economy, userKey) : null,
|
||||||
|
religion: worldData.religion ? System.decryptDataWithUserKey(worldData.religion, userKey) : null,
|
||||||
|
languages: worldData.languages ? System.decryptDataWithUserKey(worldData.languages, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worldElements && worldElements.length > 0) {
|
||||||
|
for (const id of worldElements) {
|
||||||
|
const worldElement: BookWorldElementsTable[] = await BookRepo.fetchCompleteWorldElementById(id, lang);
|
||||||
|
if (worldElement.length>0) {
|
||||||
|
const worldElementData: BookWorldElementsTable = worldElement[0];
|
||||||
|
worldElementsData.push({
|
||||||
|
...worldElementData,
|
||||||
|
name: System.decryptDataWithUserKey(worldElementData.name, userKey),
|
||||||
|
description: worldElementData.description ? System.decryptDataWithUserKey(worldElementData.description, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issues && issues.length > 0) {
|
||||||
|
for (const id of issues) {
|
||||||
|
const issue: BookIssuesTable[] = await BookRepo.fetchCompleteIssueById(id, lang);
|
||||||
|
if (issue.length>0) {
|
||||||
|
const issueData: BookIssuesTable = issue[0];
|
||||||
|
issuesData.push({
|
||||||
|
...issueData,
|
||||||
|
name: System.decryptDataWithUserKey(issueData.name, userKey)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(data.id)
|
||||||
|
const book: EritBooksTable[] = await BookRepo.fetchCompleteBookById(data.id, lang);
|
||||||
|
if (book.length>0) {
|
||||||
|
const bookDataItem: EritBooksTable = book[0];
|
||||||
|
bookData.push({
|
||||||
|
...bookDataItem,
|
||||||
|
title: System.decryptDataWithUserKey(bookDataItem.title, userKey),
|
||||||
|
sub_title: bookDataItem.sub_title ? System.decryptDataWithUserKey(bookDataItem.sub_title, userKey) : null,
|
||||||
|
summary: bookDataItem.summary ? System.decryptDataWithUserKey(bookDataItem.summary, userKey) : null,
|
||||||
|
cover_image: bookDataItem.cover_image ? System.decryptDataWithUserKey(bookDataItem.cover_image, userKey) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
eritBooks: bookData,
|
||||||
|
chapters: chaptersData,
|
||||||
|
plotPoints: plotPointsData,
|
||||||
|
incidents: incidentsData,
|
||||||
|
chapterContents: chapterContentsData,
|
||||||
|
chapterInfos: chapterInfosData,
|
||||||
|
characters: charactersData,
|
||||||
|
characterAttributes: characterAttributesData,
|
||||||
|
locations: locationsData,
|
||||||
|
locationElements: locationElementsData,
|
||||||
|
locationSubElements: locationSubElementsData,
|
||||||
|
worlds: worldsData,
|
||||||
|
worldElements: worldElementsData,
|
||||||
|
actSummaries: actSummariesData,
|
||||||
|
guideLine: guideLineData,
|
||||||
|
aiGuideLine: aiGuideLineData,
|
||||||
|
issues: issuesData
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static async syncBookFromServerToClient(userId:string,completeBook: CompleteBook,lang:"fr"|"en"):Promise<boolean> {
|
||||||
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
|
const actSummaries: BookActSummariesTable[] = completeBook.actSummaries;
|
||||||
|
const chapters: BookChaptersTable[] = completeBook.chapters;
|
||||||
|
const plotPoints: BookPlotPointsTable[] = completeBook.plotPoints;
|
||||||
|
const incidents: BookIncidentsTable[] = completeBook.incidents;
|
||||||
|
const chapterContents: BookChapterContentTable[] = completeBook.chapterContents;
|
||||||
|
const chapterInfos: BookChapterInfosTable[] = completeBook.chapterInfos;
|
||||||
|
const characters: BookCharactersTable[] = completeBook.characters;
|
||||||
|
const characterAttributes: BookCharactersAttributesTable[] = completeBook.characterAttributes;
|
||||||
|
const locations: BookLocationTable[] = completeBook.locations;
|
||||||
|
const locationElements: LocationElementTable[] = completeBook.locationElements;
|
||||||
|
const locationSubElements: LocationSubElementTable[] = completeBook.locationSubElements;
|
||||||
|
const worlds: BookWorldTable[] = completeBook.worlds;
|
||||||
|
const worldElements: BookWorldElementsTable[] = completeBook.worldElements;
|
||||||
|
const issues: BookIssuesTable[] = completeBook.issues;
|
||||||
|
|
||||||
|
const bookId: string = completeBook.eritBooks.length > 0 ? completeBook.eritBooks[0].book_id : '';
|
||||||
|
|
||||||
|
if (actSummaries && actSummaries.length > 0) {
|
||||||
|
for (const actSummary of actSummaries) {
|
||||||
|
const summary: string = System.encryptDataWithUserKey(actSummary.summary ? actSummary.summary : '', userKey)
|
||||||
|
const updated: boolean = BookRepo.updateActSummary(userId, bookId, actSummary.act_index, summary, actSummary.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapters && chapters.length > 0) {
|
||||||
|
for (const chapter of chapters) {
|
||||||
|
const title: string = System.encryptDataWithUserKey(chapter.title, userKey)
|
||||||
|
const updated: boolean = ChapterRepo.updateChapter(userId, chapter.chapter_id, title, chapter.hashed_title, chapter.chapter_order, chapter.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plotPoints && plotPoints.length > 0) {
|
||||||
|
for (const plotPoint of plotPoints) {
|
||||||
|
const title: string = System.encryptDataWithUserKey(plotPoint.title, userKey);
|
||||||
|
const summary: string = System.encryptDataWithUserKey(plotPoint.summary ? plotPoint.summary : '', userKey);
|
||||||
|
const updated: boolean = BookRepo.updatePlotPoint(userId, bookId, plotPoint.plot_point_id, title, plotPoint.hashed_title, summary, plotPoint.last_update,lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incidents && incidents.length > 0) {
|
||||||
|
for (const incident of incidents) {
|
||||||
|
const title: string = System.encryptDataWithUserKey(incident.title, userKey);
|
||||||
|
const summary: string = System.encryptDataWithUserKey(incident.summary ? incident.summary : '', userKey);
|
||||||
|
const updated: boolean = BookRepo.updateIncident(userId, bookId, incident.incident_id, title, incident.hashed_title, summary, incident.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapterContents && chapterContents.length > 0) {
|
||||||
|
for (const chapterContent of chapterContents) {
|
||||||
|
const content: string = System.encryptDataWithUserKey(chapterContent.content ? JSON.stringify(chapterContent.content) : '', userKey);
|
||||||
|
const updated: boolean = ChapterRepo.updateChapterContent(userId, chapterContent.chapter_id, chapterContent.version, content, chapterContent.words_count, chapterContent.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapterInfos && chapterInfos.length > 0) {
|
||||||
|
for (const chapterInfo of chapterInfos) {
|
||||||
|
const summary: string = System.encryptDataWithUserKey(chapterInfo.summary ? chapterInfo.summary : '', userKey);
|
||||||
|
const goal: string = System.encryptDataWithUserKey(chapterInfo.goal ? chapterInfo.goal : '', userKey);
|
||||||
|
const updated: boolean = ChapterRepo.updateChapterInfos(userId, chapterInfo.chapter_id, chapterInfo.act_id, bookId,chapterInfo.incident_id, chapterInfo.plot_point_id, summary, goal, chapterInfo.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characters && characters.length > 0) {
|
||||||
|
for (const character of characters) {
|
||||||
|
const firstName: string = System.encryptDataWithUserKey(character.first_name, userKey);
|
||||||
|
const lastName: string = System.encryptDataWithUserKey(character.last_name ? character.last_name : '', userKey);
|
||||||
|
const category: string = System.encryptDataWithUserKey(character.category, userKey);
|
||||||
|
const title: string = System.encryptDataWithUserKey(character.title ? character.title : '', userKey);
|
||||||
|
const role: string = System.encryptDataWithUserKey(character.role ? character.role : '', userKey);
|
||||||
|
const biography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
||||||
|
const history: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey);
|
||||||
|
const updated: boolean = CharacterRepo.updateCharacter(userId, character.character_id, firstName, lastName, title, category, character.image || '', role, biography, history, character.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (characterAttributes && characterAttributes.length > 0) {
|
||||||
|
for (const characterAttribute of characterAttributes) {
|
||||||
|
const attributeName: string = System.encryptDataWithUserKey(characterAttribute.attribute_name, userKey);
|
||||||
|
const attributeValue: string = System.encryptDataWithUserKey(characterAttribute.attribute_value, userKey);
|
||||||
|
const updated: boolean = CharacterRepo.updateCharacterAttribute(userId, characterAttribute.attr_id, attributeName, attributeValue, characterAttribute.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locations && locations.length > 0) {
|
||||||
|
for (const location of locations) {
|
||||||
|
const locName: string = System.encryptDataWithUserKey(location.loc_name, userKey);
|
||||||
|
const updated: boolean = LocationRepo.updateLocationSection(userId, location.loc_id, locName, location.loc_original_name, location.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationElements && locationElements.length > 0) {
|
||||||
|
for (const locationElement of locationElements) {
|
||||||
|
const elementName: string = System.encryptDataWithUserKey(locationElement.element_name, userKey);
|
||||||
|
const elementDescription: string = System.encryptDataWithUserKey(locationElement.element_description ? locationElement.element_description : '', userKey);
|
||||||
|
const updated: boolean = LocationRepo.updateLocationElement(userId, locationElement.element_id, elementName,locationElement.original_name, elementDescription, locationElement.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationSubElements && locationSubElements.length > 0) {
|
||||||
|
for (const locationSubElement of locationSubElements) {
|
||||||
|
const subElemName: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_name, userKey);
|
||||||
|
const subElemDescription: string = System.encryptDataWithUserKey(locationSubElement.sub_elem_description ? locationSubElement.sub_elem_description : '', userKey);
|
||||||
|
const updated: boolean = LocationRepo.updateLocationSubElement(userId, locationSubElement.sub_element_id, subElemName, locationSubElement.original_name, subElemDescription, locationSubElement.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worlds && worlds.length > 0) {
|
||||||
|
for (const world of worlds) {
|
||||||
|
const name: string = System.encryptDataWithUserKey(world.name, userKey);
|
||||||
|
const history: string = System.encryptDataWithUserKey(world.history ? world.history : '', userKey);
|
||||||
|
const politics: string = System.encryptDataWithUserKey(world.politics ? world.politics : '', userKey);
|
||||||
|
const economy: string = System.encryptDataWithUserKey(world.economy ? world.economy : '', userKey);
|
||||||
|
const religion: string = System.encryptDataWithUserKey(world.religion ? world.religion : '', userKey);
|
||||||
|
const languages: string = System.encryptDataWithUserKey(world.languages ? world.languages : '', userKey);
|
||||||
|
const updated: boolean = BookRepo.updateWorld(userId, world.world_id, name, world.hashed_name, history, politics, economy, religion, languages, world.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worldElements && worldElements.length > 0) {
|
||||||
|
for (const worldElement of worldElements) {
|
||||||
|
const name: string = System.encryptDataWithUserKey(worldElement.name, userKey);
|
||||||
|
const description: string = System.encryptDataWithUserKey(worldElement.description ? worldElement.description : '', userKey);
|
||||||
|
const updated: boolean = BookRepo.updateWorldElement(userId, worldElement.element_id, name, description, worldElement.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issues && issues.length > 0) {
|
||||||
|
for (const issue of issues) {
|
||||||
|
const name: string = System.encryptDataWithUserKey(issue.name, userKey);
|
||||||
|
const updated: boolean = BookRepo.updateIssue(userId, bookId, issue.issue_id, name, issue.hashed_issue_name, issue.last_update, lang);
|
||||||
|
if (!updated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ export default class Chapter {
|
|||||||
const response:string = await QS.request(prompt,'summary-chapter');
|
const response:string = await QS.request(prompt,'summary-chapter');
|
||||||
console.log(response);
|
console.log(response);
|
||||||
}*/
|
}*/
|
||||||
return ChapterRepo.updateChapterContent(userId, chapterId, version, encryptContent, wordsCount, lang);
|
return ChapterRepo.updateChapterContent(userId, chapterId, version, encryptContent, wordsCount, System.timeStampInSeconds(), lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getLastChapter(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterProps | null {
|
public static getLastChapter(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): ChapterProps | null {
|
||||||
@@ -200,7 +200,7 @@ export default class Chapter {
|
|||||||
const hashedTitle: string = System.hashElement(title);
|
const hashedTitle: string = System.hashElement(title);
|
||||||
const userKey: string = getUserEncryptionKey(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
const encryptedTitle: string = System.encryptDataWithUserKey(title, userKey);
|
const encryptedTitle: string = System.encryptDataWithUserKey(title, userKey);
|
||||||
return ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, hashedTitle, chapterOrder, lang);
|
return ChapterRepo.updateChapter(userId, chapterId, encryptedTitle, hashedTitle, chapterOrder, System.timeStampInSeconds(), lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateChapterInfos(chapters: ActChapter[], userId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, lang: 'fr' | 'en' = 'fr') {
|
static updateChapterInfos(chapters: ActChapter[], userId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, lang: 'fr' | 'en' = 'fr') {
|
||||||
@@ -209,7 +209,7 @@ export default class Chapter {
|
|||||||
const summary: string = chapter.summary ? System.encryptDataWithUserKey(chapter.summary, userKey) : '';
|
const summary: string = chapter.summary ? System.encryptDataWithUserKey(chapter.summary, userKey) : '';
|
||||||
const goal: string = chapter.goal ? System.encryptDataWithUserKey(chapter.goal, userKey) : '';
|
const goal: string = chapter.goal ? System.encryptDataWithUserKey(chapter.goal, userKey) : '';
|
||||||
const chapterId: string = chapter.chapterId;
|
const chapterId: string = chapter.chapterId;
|
||||||
ChapterRepo.updateChapterInfos(userId, chapterId, actId, bookId, incidentId, plotId, summary, goal, lang);
|
ChapterRepo.updateChapterInfos(userId, chapterId, actId, bookId, incidentId, plotId, summary, goal, System.timeStampInSeconds(), lang);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {getUserEncryptionKey} from "../keyManager.js";
|
|||||||
export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring';
|
export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring';
|
||||||
|
|
||||||
export interface CharacterPropsPost {
|
export interface CharacterPropsPost {
|
||||||
id: number | null;
|
id: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
category: CharacterCategory;
|
category: CharacterCategory;
|
||||||
@@ -118,6 +118,9 @@ export default class Character {
|
|||||||
|
|
||||||
static updateCharacter(userId: string, character: CharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean {
|
static updateCharacter(userId: string, character: CharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
const userKey: string = getUserEncryptionKey(userId);
|
const userKey: string = getUserEncryptionKey(userId);
|
||||||
|
if (!character.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
|
||||||
const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey);
|
const encryptedLastName: string = System.encryptDataWithUserKey(character.lastName, userKey);
|
||||||
const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey);
|
const encryptedTitle: string = System.encryptDataWithUserKey(character.title, userKey);
|
||||||
@@ -126,7 +129,7 @@ export default class Character {
|
|||||||
const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey);
|
const encryptedRole: string = System.encryptDataWithUserKey(character.role, userKey);
|
||||||
const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
const encryptedBiography: string = System.encryptDataWithUserKey(character.biography ? character.biography : '', userKey);
|
||||||
const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey);
|
const encryptedHistory: string = System.encryptDataWithUserKey(character.history ? character.history : '', userKey);
|
||||||
return CharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lang);
|
return CharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds(), lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
static addNewAttribute(characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
|
static addNewAttribute(characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
|
||||||
|
|||||||
@@ -118,17 +118,17 @@ export default class Location {
|
|||||||
for (const location of locations) {
|
for (const location of locations) {
|
||||||
const originalName: string = System.hashElement(location.name);
|
const originalName: string = System.hashElement(location.name);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(location.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(location.name, userKey);
|
||||||
LocationRepo.updateLocationSection(userId, location.id, encryptedName, originalName, lang)
|
LocationRepo.updateLocationSection(userId, location.id, encryptedName, originalName, System.timeStampInSeconds(),lang)
|
||||||
for (const element of location.elements) {
|
for (const element of location.elements) {
|
||||||
const originalName: string = System.hashElement(element.name);
|
const originalName: string = System.hashElement(element.name);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(element.name, userKey);
|
||||||
const encryptDescription: string = element.description ? System.encryptDataWithUserKey(element.description, userKey) : '';
|
const encryptDescription: string = element.description ? System.encryptDataWithUserKey(element.description, userKey) : '';
|
||||||
LocationRepo.updateLocationElement(userId, element.id, encryptedName, originalName, encryptDescription, lang)
|
LocationRepo.updateLocationElement(userId, element.id, encryptedName, originalName, encryptDescription, System.timeStampInSeconds(), lang)
|
||||||
for (const subElement of element.subElements) {
|
for (const subElement of element.subElements) {
|
||||||
const originalName: string = System.hashElement(subElement.name);
|
const originalName: string = System.hashElement(subElement.name);
|
||||||
const encryptedName: string = System.encryptDataWithUserKey(subElement.name, userKey);
|
const encryptedName: string = System.encryptDataWithUserKey(subElement.name, userKey);
|
||||||
const encryptDescription: string = subElement.description ? System.encryptDataWithUserKey(subElement.description, userKey) : '';
|
const encryptDescription: string = subElement.description ? System.encryptDataWithUserKey(subElement.description, userKey) : '';
|
||||||
LocationRepo.updateLocationSubElement(userId, subElement.id, encryptedName, originalName, encryptDescription, lang)
|
LocationRepo.updateLocationSubElement(userId, subElement.id, encryptedName, originalName, encryptDescription,System.timeStampInSeconds(),lang)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ export interface BookChaptersTable extends Record<string, SQLiteValue> {
|
|||||||
book_id: string;
|
book_id: string;
|
||||||
author_id: string;
|
author_id: string;
|
||||||
title: string;
|
title: string;
|
||||||
hashed_title: string | null;
|
hashed_title: string;
|
||||||
words_count: number | null;
|
words_count: number | null;
|
||||||
chapter_order: number | null;
|
chapter_order: number;
|
||||||
last_update: number;
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ export interface BookChapterContentTable extends Record<string, SQLiteValue> {
|
|||||||
export interface BookChapterInfosTable extends Record<string, SQLiteValue> {
|
export interface BookChapterInfosTable extends Record<string, SQLiteValue> {
|
||||||
chapter_info_id: string;
|
chapter_info_id: string;
|
||||||
chapter_id: string;
|
chapter_id: string;
|
||||||
act_id: number | null;
|
act_id: number;
|
||||||
incident_id: string | null;
|
incident_id: string | null;
|
||||||
plot_point_id: string | null;
|
plot_point_id: string | null;
|
||||||
book_id: string;
|
book_id: string;
|
||||||
@@ -626,10 +626,10 @@ export default class BookRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, lang: 'fr' | 'en'): boolean {
|
public static updateActSummary(userId: string, bookId: string, actId: number, summary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?', [summary, System.timeStampInSeconds(), userId, bookId, actId]);
|
const result: RunResult = db.run('UPDATE book_act_summaries SET summary=?, last_update=? WHERE user_id=? AND book_id=? AND act_sum_id=?', [summary, lastUpdate, userId, bookId, actId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -782,10 +782,10 @@ export default class BookRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, lang: 'fr' | 'en'): boolean {
|
public static updateIncident(userId: string, bookId: string, incidentId: string, encryptedIncidentName: string, incidentHashedName: string, incidentSummary: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?', [encryptedIncidentName, incidentHashedName, incidentSummary, System.timeStampInSeconds(), userId, bookId, incidentId]);
|
const result: RunResult = db.run('UPDATE book_incidents SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND incident_id=?', [encryptedIncidentName, incidentHashedName, incidentSummary, lastUpdate, userId, bookId, incidentId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -798,10 +798,10 @@ export default class BookRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lang: 'fr' | 'en'): boolean {
|
public static updatePlotPoint(userId: string, bookId: string, plotPointId: string, encryptedPlotPointName: string, plotPointHashedName: string, plotPointSummary: string, lastUpdate:number, lang: 'fr' | 'en'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?', [encryptedPlotPointName, plotPointHashedName, plotPointSummary, System.timeStampInSeconds(), userId, bookId, plotPointId]);
|
const result: RunResult = db.run('UPDATE book_plot_points SET title=?, hashed_title=?, summary=?, last_update=? WHERE author_id=? AND book_id=? AND plot_point_id=?', [encryptedPlotPointName, plotPointHashedName, plotPointSummary, lastUpdate, userId, bookId, plotPointId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -863,10 +863,10 @@ export default class BookRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updateWorld(userId: string, worldId: string, encryptName: string, hashedName: string, encryptHistory: string, encryptPolitics: string, encryptEconomy: string, encryptReligion: string, encryptLanguages: string, lang: 'fr' | 'en'): boolean {
|
public static updateWorld(userId: string, worldId: string, encryptName: string, hashedName: string, encryptHistory: string, encryptPolitics: string, encryptEconomy: string, encryptReligion: string, encryptLanguages: string, lastUpdate: number, lang: 'fr' | 'en'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?', [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, System.timeStampInSeconds(), userId, worldId]);
|
const result: RunResult = db.run('UPDATE book_world SET name=?, hashed_name=?, history=?, politics=?, economy=?, religion=?, languages=?, last_update=? WHERE author_id=? AND world_id=?', [encryptName, hashedName, encryptHistory, encryptPolitics, encryptEconomy, encryptReligion, encryptLanguages, lastUpdate, userId, worldId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -1809,4 +1809,309 @@ export default class BookRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteActSummaryById(id: string, lang: "fr" | "en"):Promise<BookActSummariesTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT act_sum_id, book_id, user_id, act_index, summary, last_update
|
||||||
|
FROM book_act_summaries
|
||||||
|
WHERE act_sum_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookActSummariesTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le résumé d'acte complet.` : `Unable to retrieve complete act summary.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteChapterById(id: string, lang: "fr" | "en"):Promise<BookChaptersTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT chapter_id, book_id, author_id, title, hashed_title, words_count, chapter_order, last_update
|
||||||
|
FROM book_chapters
|
||||||
|
WHERE chapter_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookChaptersTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le chapitre complet.` : `Unable to retrieve complete chapter.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompletePlotPointById(id: string, lang: "fr" | "en"):Promise<BookPlotPointsTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT plot_point_id, title, hashed_title, summary, linked_incident_id, author_id, book_id, last_update
|
||||||
|
FROM book_plot_points
|
||||||
|
WHERE plot_point_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookPlotPointsTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le point d'intrigue complet.` : `Unable to retrieve complete plot point.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteIncidentById(id: string, lang: "fr" | "en"):Promise<BookIncidentsTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT incident_id, author_id, book_id, title, hashed_title, summary, last_update
|
||||||
|
FROM book_incidents
|
||||||
|
WHERE incident_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookIncidentsTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer l'incident complet.` : `Unable to retrieve complete incident.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteChapterContentById(id: string, lang: "fr" | "en"):Promise<BookChapterContentTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update
|
||||||
|
FROM book_chapter_content
|
||||||
|
WHERE content_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookChapterContentTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le contenu de chapitre complet.` : `Unable to retrieve complete chapter content.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteChapterInfoById(id: string, lang: "fr" | "en"):Promise<BookChapterInfosTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT chapter_info_id, chapter_id, act_id, incident_id, plot_point_id, book_id, author_id, summary, goal, last_update
|
||||||
|
FROM book_chapter_infos
|
||||||
|
WHERE chapter_info_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookChapterInfosTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer les informations de chapitre complètes.` : `Unable to retrieve complete chapter info.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteCharacterById(id: string, lang: "fr" | "en"):Promise<BookCharactersTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT character_id, book_id, user_id, first_name, last_name, category, title, image, role, biography, history, last_update
|
||||||
|
FROM book_characters
|
||||||
|
WHERE character_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookCharactersTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le personnage complet.` : `Unable to retrieve complete character.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteCharacterAttributeById(id: string, lang: "fr" | "en"):Promise<BookCharactersAttributesTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT attr_id, character_id, user_id, attribute_name, attribute_value, last_update
|
||||||
|
FROM book_characters_attributes
|
||||||
|
WHERE attr_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookCharactersAttributesTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer l'attribut de personnage complet.` : `Unable to retrieve complete character attribute.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteLocationById(id: string, lang: "fr" | "en"):Promise<BookLocationTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update
|
||||||
|
FROM book_location
|
||||||
|
WHERE loc_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookLocationTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le lieu complet.` : `Unable to retrieve complete location.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteLocationElementById(id: string, lang: "fr" | "en"):Promise<LocationElementTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT element_id, location, user_id, element_name, original_name, element_description, last_update
|
||||||
|
FROM location_element
|
||||||
|
WHERE element_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as LocationElementTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de lieu complet.` : `Unable to retrieve complete location element.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteLocationSubElementById(id: string, lang: "fr" | "en"):Promise<LocationSubElementTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update
|
||||||
|
FROM location_sub_element
|
||||||
|
WHERE sub_element_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as LocationSubElementTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le sous-élément de lieu complet.` : `Unable to retrieve complete location sub-element.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteWorldById(id: string, lang: "fr" | "en"):Promise<BookWorldTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT world_id, name, hashed_name, author_id, book_id, history, politics, economy, religion, languages, last_update
|
||||||
|
FROM book_world
|
||||||
|
WHERE world_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookWorldTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le monde complet.` : `Unable to retrieve complete world.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteWorldElementById(id: string, lang: "fr" | "en"):Promise<BookWorldElementsTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT element_id, world_id, user_id, element_type, name, original_name, description, last_update
|
||||||
|
FROM book_world_elements
|
||||||
|
WHERE element_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookWorldElementsTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer l'élément de monde complet.` : `Unable to retrieve complete world element.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteIssueById(id: string, lang: "fr" | "en"):Promise<BookIssuesTable[]> {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT issue_id, author_id, book_id, name, hashed_issue_name, last_update
|
||||||
|
FROM book_issues
|
||||||
|
WHERE issue_id = ?`,
|
||||||
|
[id]
|
||||||
|
) as BookIssuesTable[];
|
||||||
|
} catch (e:unknown){
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le problème complet.` : `Unable to retrieve complete issue.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async fetchCompleteBookById(bookId: string, lang: "fr" | "en") {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
return db.all(
|
||||||
|
`SELECT * FROM erit_books
|
||||||
|
WHERE book_id = ?`,
|
||||||
|
[bookId]
|
||||||
|
) as EritBooksTable[];
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de récupérer le livre complet.` : `Unable to retrieve complete book.`);
|
||||||
|
} else {
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateWorldElement(userId: string, elementId: string, name: string, description: string, lastUpdate: number,lang: "fr" | "en"):boolean {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
const query:string = `UPDATE book_world_elements SET name = ?, description = ?, last_update = FROM_UNIXTIME(?) WHERE element_id = UUID_TO_BIN(?) AND user_id = UUID_TO_BIN(?)`;
|
||||||
|
const params:(string|number)[] = [name, description, lastUpdate, elementId, userId];
|
||||||
|
const result:RunResult = db.run(query, params);
|
||||||
|
return result.changes > 0;
|
||||||
|
} catch (e:unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(`DB Error: ${e.message}`);
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'élément du monde.` : `Unable to update world element.`);
|
||||||
|
} else {
|
||||||
|
console.error("An unknown error occurred.");
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static updateIssue(userId: string, bookId: string, issueId: string, name: string, hashedName: string, lastUpdate: number, lang: "fr" | "en"):boolean {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
const query:string = `UPDATE book_issues SET name = ?, hashed_issue_name = ?, last_update = FROM_UNIXTIME(?) WHERE issue_id = UUID_TO_BIN(?) AND author_id = UUID_TO_BIN(?) AND book_id = UUID_TO_BIN(?)`;
|
||||||
|
const params:(string|number)[] = [name, hashedName, lastUpdate, issueId, userId, bookId];
|
||||||
|
const result:RunResult = db.run(query, params);
|
||||||
|
return result.changes > 0;
|
||||||
|
} catch (e:unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(`DB Error: ${e.message}`);
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la problématique.` : `Unable to update issue.`);
|
||||||
|
} else {
|
||||||
|
console.error("An unknown error occurred.");
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,10 +212,10 @@ export default class ChapterRepo{
|
|||||||
return chapterInfoId;
|
return chapterInfoId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
public static updateChapter(userId: string, chapterId: string, encryptedTitle: string, hashTitle: string, chapterOrder: number, lastUpdate:number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, System.timeStampInSeconds(), userId, chapterId]);
|
const result: RunResult = db.run('UPDATE book_chapters SET title=?, hashed_title=?, chapter_order=?, last_update=? WHERE author_id=? AND chapter_id=?', [encryptedTitle, hashTitle, chapterOrder, lastUpdate, userId, chapterId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -228,16 +228,17 @@ export default class ChapterRepo{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updateChapterInfos(userId: string, chapterId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, summary: string, goal: string | null, lang: 'fr' | 'en' = 'fr'): boolean {
|
public static updateChapterInfos(userId: string, chapterId: string, actId: number, bookId: string, incidentId: string | null, plotId: string | null, summary: string, goal: string | null, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
let sql: string = `UPDATE book_chapter_infos
|
let sql: string = `UPDATE book_chapter_infos
|
||||||
SET summary=?,
|
SET summary=?,
|
||||||
goal=?
|
goal=?,
|
||||||
|
last_update=?
|
||||||
WHERE chapter_id = ?
|
WHERE chapter_id = ?
|
||||||
AND act_id = ?
|
AND act_id = ?
|
||||||
AND book_id = ?`;
|
AND book_id = ?`;
|
||||||
const params: any[] = [summary, goal, chapterId, actId, bookId];
|
const params: any[] = [summary, goal, lastUpdate, chapterId, actId, bookId];
|
||||||
if (incidentId) {
|
if (incidentId) {
|
||||||
sql += ` AND incident_id=?`;
|
sql += ` AND incident_id=?`;
|
||||||
params.push(incidentId);
|
params.push(incidentId);
|
||||||
@@ -265,15 +266,15 @@ export default class ChapterRepo{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
public static updateChapterContent(userId: string, chapterId: string, version: number, encryptContent: string, wordsCount: number, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, System.timeStampInSeconds(), chapterId, userId, version]);
|
const result: RunResult = db.run('UPDATE book_chapter_content SET content=?, words_count=?, last_update=? WHERE chapter_id=? AND author_id=? AND version=?', [encryptContent, wordsCount, lastUpdate, chapterId, userId, version]);
|
||||||
if (result.changes > 0) {
|
if (result.changes > 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const contentId:string = System.createUniqueId();
|
const contentId:string = System.createUniqueId();
|
||||||
const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount, System.timeStampInSeconds()]);
|
const insertResult: RunResult = db.run('INSERT INTO book_chapter_content (content_id,chapter_id, author_id, version, content, words_count, last_update) VALUES (?,?,?,?,?,?,?)', [contentId, chapterId, userId, version, encryptContent, wordsCount, lastUpdate]);
|
||||||
return insertResult.changes > 0;
|
return insertResult.changes > 0;
|
||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ export default class CharacterRepo {
|
|||||||
return attributeId;
|
return attributeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateCharacter(userId: string, id: number | null, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
static updateCharacter(userId: string, id: string, encryptedName: string, encryptedLastName: string, encryptedTitle: string, encryptedCategory: string, encryptedImage: string, encryptedRole: string, encryptedBiography: string, encryptedHistory: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, System.timeStampInSeconds(), id, userId]);
|
const result: RunResult = db.run('UPDATE `book_characters` SET `first_name`=?,`last_name`=?,`title`=?,`category`=?,`image`=?,`role`=?,`biography`=?,`history`=?,`last_update`=? WHERE `character_id`=? AND `user_id`=?', [encryptedName, encryptedLastName, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, lastUpdate, id, userId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -165,4 +165,19 @@ export default class CharacterRepo {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
static updateCharacterAttribute(userId: string, characterAttributeId: string, attributeName: string, attributeValue: string, lastUpdate: number,lang: "fr" | "en"):boolean {
|
||||||
|
try {
|
||||||
|
const db: Database = System.getDb();
|
||||||
|
const result:RunResult = db.run('UPDATE `book_characters_attributes` SET `attribute_name`=?,`attribute_value`=?, last_update=FROM_UNIXTIME(?) WHERE `attr_id`=UUID_TO_BIN(?) AND `user_id`=UUID_TO_BIN(?)', [attributeName, attributeValue, lastUpdate, characterAttributeId, userId]);
|
||||||
|
return result.changes > 0;
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(`DB Error: ${e.message}`);
|
||||||
|
throw new Error(lang === 'fr' ? `Impossible de mettre à jour l'attribut du personnage.` : `Unable to update character attribute.`);
|
||||||
|
} else {
|
||||||
|
console.error("An unknown error occurred.");
|
||||||
|
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,10 +107,10 @@ export default class LocationRepo {
|
|||||||
return subElementId;
|
return subElementId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
static updateLocationSubElement(userId: string, id: string, encryptedName: string, originalName: string, encryptDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=?, last_update=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, System.timeStampInSeconds(), id, userId]);
|
const result: RunResult = db.run('UPDATE location_sub_element SET sub_elem_name=?, original_name=?, sub_elem_description=?, last_update=? WHERE sub_element_id=? AND user_id=?', [encryptedName, originalName, encryptDescription, lastUpdate, id, userId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -123,10 +123,10 @@ export default class LocationRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
static updateLocationElement(userId: string, id: string, encryptedName: string, originalName: string, encryptedDescription: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, last_update=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, System.timeStampInSeconds(), id, userId]);
|
const result: RunResult = db.run('UPDATE location_element SET element_name=?, original_name=?, element_description=?, last_update=? WHERE element_id=? AND user_id=?', [encryptedName, originalName, encryptedDescription, lastUpdate, id, userId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -139,10 +139,10 @@ export default class LocationRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): boolean {
|
static updateLocationSection(userId: string, id: string, encryptedName: string, originalName: string, lastUpdate: number, lang: 'fr' | 'en' = 'fr'): boolean {
|
||||||
try {
|
try {
|
||||||
const db: Database = System.getDb();
|
const db: Database = System.getDb();
|
||||||
const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, last_update=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, System.timeStampInSeconds(), id, userId]);
|
const result: RunResult = db.run('UPDATE book_location SET loc_name=?, loc_original_name=?, last_update=? WHERE loc_id=? AND user_id=?', [encryptedName, originalName, lastUpdate, id, userId]);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { createHandler } from '../database/LocalSystem.js';
|
import { createHandler } from '../database/LocalSystem.js';
|
||||||
import Book, {CompleteBook, SyncedBook} from '../database/models/Book.js';
|
import Book, {BookSyncCompare, CompleteBook, SyncedBook} from '../database/models/Book.js';
|
||||||
import type { BookProps, GuideLine, GuideLineAI, Act, Issue, WorldProps } from '../database/models/Book.js';
|
import type { BookProps, GuideLine, GuideLineAI, Act, Issue, WorldProps } from '../database/models/Book.js';
|
||||||
import Chapter from '../database/models/Chapter.js';
|
import Chapter from '../database/models/Chapter.js';
|
||||||
import type { ChapterProps } from '../database/models/Chapter.js';
|
import type { ChapterProps } from '../database/models/Chapter.js';
|
||||||
@@ -130,6 +130,22 @@ ipcMain.handle('db:book:updateBasicInformation', createHandler<UpdateBookBasicDa
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// GET /book/sync/to-client - Get book data to sync to client
|
||||||
|
ipcMain.handle('db:book:sync:toServer', createHandler<BookSyncCompare, CompleteBook>(
|
||||||
|
async function(userId: string, data:BookSyncCompare, lang: 'fr' | 'en'):Promise<CompleteBook> {
|
||||||
|
return await Book.getCompleteSyncBook(userId, data, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// GET /book/sync/from-server - Get book data to sync from server
|
||||||
|
ipcMain.handle('db:book:sync:toClient', createHandler<CompleteBook, boolean>(
|
||||||
|
async function(userId: string, data:CompleteBook, lang: 'fr' | 'en'):Promise<boolean> {
|
||||||
|
return await Book.syncBookFromServerToClient(userId, data, lang);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// GET /book/guide-line - Get guideline
|
// GET /book/guide-line - Get guideline
|
||||||
ipcMain.handle('db:book:guideline:get',
|
ipcMain.handle('db:book:guideline:get',
|
||||||
createHandler<GetGuidelineData, GuideLine | null>(async function(userId: string, data: GetGuidelineData, lang: 'fr' | 'en') {
|
createHandler<GetGuidelineData, GuideLine | null>(async function(userId: string, data: GetGuidelineData, lang: 'fr' | 'en') {
|
||||||
|
|||||||
14
lib/errors.ts
Normal file
14
lib/errors.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Clean error messages from Electron IPC prefix
|
||||||
|
* Transforms: "Error invoking remote method 'channel': Error: Message"
|
||||||
|
* Into: "Message"
|
||||||
|
*/
|
||||||
|
export function cleanErrorMessage(error: unknown): string {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error.message.replace(/^Error invoking remote method '[^']+': Error: /, '');
|
||||||
|
}
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
return error.replace(/^Error invoking remote method '[^']+': Error: /, '');
|
||||||
|
}
|
||||||
|
return 'An unknown error occurred';
|
||||||
|
}
|
||||||
@@ -164,11 +164,11 @@ export default class Book {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static booksToSelectBox(books: BookProps[]): SelectBoxProps[] {
|
static booksToSelectBox(books: SyncedBook[]): SelectBoxProps[] {
|
||||||
return books.map((book: BookProps) => {
|
return books.map((book: SyncedBook):SelectBoxProps => {
|
||||||
return {
|
return {
|
||||||
label: book.title,
|
label: book.title,
|
||||||
value: book.bookId,
|
value: book.id,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export interface EritBooksTable {
|
|||||||
desired_word_count:number|null;
|
desired_word_count:number|null;
|
||||||
words_count:number|null;
|
words_count:number|null;
|
||||||
cover_image:string|null;
|
cover_image:string|null;
|
||||||
|
last_update:number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookActSummariesTable {
|
export interface BookActSummariesTable {
|
||||||
@@ -20,6 +21,7 @@ export interface BookActSummariesTable {
|
|||||||
user_id: string;
|
user_id: string;
|
||||||
act_index: number;
|
act_index: number;
|
||||||
summary: string | null;
|
summary: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookAIGuideLineTable {
|
export interface BookAIGuideLineTable {
|
||||||
@@ -41,9 +43,10 @@ export interface BookChaptersTable {
|
|||||||
book_id: string;
|
book_id: string;
|
||||||
author_id: string;
|
author_id: string;
|
||||||
title: string;
|
title: string;
|
||||||
hashed_title: string | null;
|
hashed_title: string;
|
||||||
words_count: number | null;
|
words_count: number | null;
|
||||||
chapter_order: number | null;
|
chapter_order: number;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookChapterContentTable {
|
export interface BookChapterContentTable {
|
||||||
@@ -54,18 +57,20 @@ export interface BookChapterContentTable {
|
|||||||
content: string | null;
|
content: string | null;
|
||||||
words_count: number;
|
words_count: number;
|
||||||
time_on_it: number;
|
time_on_it: number;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookChapterInfosTable {
|
export interface BookChapterInfosTable {
|
||||||
chapter_info_id: string;
|
chapter_info_id: string;
|
||||||
chapter_id: string;
|
chapter_id: string;
|
||||||
act_id: number | null;
|
act_id: number;
|
||||||
incident_id: string | null;
|
incident_id: string | null;
|
||||||
plot_point_id: string | null;
|
plot_point_id: string | null;
|
||||||
book_id: string;
|
book_id: string;
|
||||||
author_id: string;
|
author_id: string;
|
||||||
summary: string | null;
|
summary: string | null;
|
||||||
goal: string | null;
|
goal: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookCharactersTable {
|
export interface BookCharactersTable {
|
||||||
@@ -80,6 +85,7 @@ export interface BookCharactersTable {
|
|||||||
role: string | null;
|
role: string | null;
|
||||||
biography: string | null;
|
biography: string | null;
|
||||||
history: string | null;
|
history: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookCharactersAttributesTable {
|
export interface BookCharactersAttributesTable {
|
||||||
@@ -88,6 +94,7 @@ export interface BookCharactersAttributesTable {
|
|||||||
user_id: string;
|
user_id: string;
|
||||||
attribute_name: string;
|
attribute_name: string;
|
||||||
attribute_value: string;
|
attribute_value: string;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookGuideLineTable {
|
export interface BookGuideLineTable {
|
||||||
@@ -112,6 +119,7 @@ export interface BookIncidentsTable {
|
|||||||
title: string;
|
title: string;
|
||||||
hashed_title: string;
|
hashed_title: string;
|
||||||
summary: string | null;
|
summary: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookIssuesTable {
|
export interface BookIssuesTable {
|
||||||
@@ -120,6 +128,7 @@ export interface BookIssuesTable {
|
|||||||
book_id: string;
|
book_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
hashed_issue_name: string;
|
hashed_issue_name: string;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookLocationTable {
|
export interface BookLocationTable {
|
||||||
@@ -128,6 +137,7 @@ export interface BookLocationTable {
|
|||||||
user_id: string;
|
user_id: string;
|
||||||
loc_name: string;
|
loc_name: string;
|
||||||
loc_original_name: string;
|
loc_original_name: string;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookPlotPointsTable {
|
export interface BookPlotPointsTable {
|
||||||
@@ -138,6 +148,7 @@ export interface BookPlotPointsTable {
|
|||||||
linked_incident_id: string | null;
|
linked_incident_id: string | null;
|
||||||
author_id: string;
|
author_id: string;
|
||||||
book_id: string;
|
book_id: string;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookWorldTable {
|
export interface BookWorldTable {
|
||||||
@@ -151,6 +162,7 @@ export interface BookWorldTable {
|
|||||||
economy: string | null;
|
economy: string | null;
|
||||||
religion: string | null;
|
religion: string | null;
|
||||||
languages: string | null;
|
languages: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookWorldElementsTable {
|
export interface BookWorldElementsTable {
|
||||||
@@ -161,6 +173,7 @@ export interface BookWorldElementsTable {
|
|||||||
name: string;
|
name: string;
|
||||||
original_name: string;
|
original_name: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocationElementTable {
|
export interface LocationElementTable {
|
||||||
@@ -170,6 +183,7 @@ export interface LocationElementTable {
|
|||||||
element_name: string;
|
element_name: string;
|
||||||
original_name: string;
|
original_name: string;
|
||||||
element_description: string | null;
|
element_description: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocationSubElementTable {
|
export interface LocationSubElementTable {
|
||||||
@@ -179,4 +193,5 @@ export interface LocationSubElementTable {
|
|||||||
sub_elem_name: string;
|
sub_elem_name: string;
|
||||||
original_name: string;
|
original_name: string;
|
||||||
sub_elem_description: string | null;
|
sub_elem_description: string | null;
|
||||||
|
last_update: number;
|
||||||
}
|
}
|
||||||
@@ -110,3 +110,231 @@ export interface SyncedGuideLine {
|
|||||||
export interface SyncedAIGuideLine {
|
export interface SyncedAIGuideLine {
|
||||||
lastUpdate: number;
|
lastUpdate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BookSyncCompare {
|
||||||
|
id: string;
|
||||||
|
chapters: string[];
|
||||||
|
chapterContents: string[];
|
||||||
|
chapterInfos: string[];
|
||||||
|
characters: string[];
|
||||||
|
characterAttributes: string[];
|
||||||
|
locations: string[];
|
||||||
|
locationElements: string[];
|
||||||
|
locationSubElements: string[];
|
||||||
|
worlds: string[];
|
||||||
|
worldElements: string[];
|
||||||
|
incidents: string[];
|
||||||
|
plotPoints: string[];
|
||||||
|
issues: string[];
|
||||||
|
actSummaries: string[];
|
||||||
|
guideLine: boolean;
|
||||||
|
aiGuideLine: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareBookSyncs(newerBook: SyncedBook, olderBook: SyncedBook): BookSyncCompare | null {
|
||||||
|
const changedChapterIds: string[] = [];
|
||||||
|
const changedChapterContentIds: string[] = [];
|
||||||
|
const changedChapterInfoIds: string[] = [];
|
||||||
|
const changedCharacterIds: string[] = [];
|
||||||
|
const changedCharacterAttributeIds: string[] = [];
|
||||||
|
const changedLocationIds: string[] = [];
|
||||||
|
const changedLocationElementIds: string[] = [];
|
||||||
|
const changedLocationSubElementIds: string[] = [];
|
||||||
|
const changedWorldIds: string[] = [];
|
||||||
|
const changedWorldElementIds: string[] = [];
|
||||||
|
const changedIncidentIds: string[] = [];
|
||||||
|
const changedPlotPointIds: string[] = [];
|
||||||
|
const changedIssueIds: string[] = [];
|
||||||
|
const changedActSummaryIds: string[] = [];
|
||||||
|
let guideLineChanged: boolean = false;
|
||||||
|
let aiGuideLineChanged: boolean = false;
|
||||||
|
|
||||||
|
newerBook.chapters.forEach((newerChapter: SyncedChapter): void => {
|
||||||
|
const olderChapter: SyncedChapter | undefined = olderBook.chapters.find((chapter: SyncedChapter): boolean => chapter.id === newerChapter.id);
|
||||||
|
|
||||||
|
if (!olderChapter) {
|
||||||
|
changedChapterIds.push(newerChapter.id);
|
||||||
|
newerChapter.contents.forEach((content: SyncedChapterContent): void => {
|
||||||
|
changedChapterContentIds.push(content.id);
|
||||||
|
});
|
||||||
|
if (newerChapter.info) {
|
||||||
|
changedChapterInfoIds.push(newerChapter.info.id);
|
||||||
|
}
|
||||||
|
} else if (newerChapter.lastUpdate > olderChapter.lastUpdate) {
|
||||||
|
changedChapterIds.push(newerChapter.id);
|
||||||
|
} else {
|
||||||
|
newerChapter.contents.forEach((newerContent: SyncedChapterContent): void => {
|
||||||
|
const olderContent: SyncedChapterContent | undefined = olderChapter.contents.find((content: SyncedChapterContent): boolean => content.id === newerContent.id);
|
||||||
|
if (!olderContent || newerContent.lastUpdate > olderContent.lastUpdate) {
|
||||||
|
changedChapterContentIds.push(newerContent.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newerChapter.info && olderChapter.info) {
|
||||||
|
if (newerChapter.info.lastUpdate > olderChapter.info.lastUpdate) {
|
||||||
|
changedChapterInfoIds.push(newerChapter.info.id);
|
||||||
|
}
|
||||||
|
} else if (newerChapter.info && !olderChapter.info) {
|
||||||
|
changedChapterInfoIds.push(newerChapter.info.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.characters.forEach((newerCharacter: SyncedCharacter): void => {
|
||||||
|
const olderCharacter: SyncedCharacter | undefined = olderBook.characters.find((character: SyncedCharacter): boolean => character.id === newerCharacter.id);
|
||||||
|
|
||||||
|
if (!olderCharacter) {
|
||||||
|
changedCharacterIds.push(newerCharacter.id);
|
||||||
|
newerCharacter.attributes.forEach((attribute: SyncedCharacterAttribute): void => {
|
||||||
|
changedCharacterAttributeIds.push(attribute.id);
|
||||||
|
});
|
||||||
|
} else if (newerCharacter.lastUpdate > olderCharacter.lastUpdate) {
|
||||||
|
changedCharacterIds.push(newerCharacter.id);
|
||||||
|
} else {
|
||||||
|
newerCharacter.attributes.forEach((newerAttribute: SyncedCharacterAttribute): void => {
|
||||||
|
const olderAttribute: SyncedCharacterAttribute | undefined = olderCharacter.attributes.find((attribute: SyncedCharacterAttribute): boolean => attribute.id === newerAttribute.id);
|
||||||
|
if (!olderAttribute || newerAttribute.lastUpdate > olderAttribute.lastUpdate) {
|
||||||
|
changedCharacterAttributeIds.push(newerAttribute.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.locations.forEach((newerLocation: SyncedLocation): void => {
|
||||||
|
const olderLocation: SyncedLocation | undefined = olderBook.locations.find((location: SyncedLocation): boolean => location.id === newerLocation.id);
|
||||||
|
|
||||||
|
if (!olderLocation) {
|
||||||
|
changedLocationIds.push(newerLocation.id);
|
||||||
|
newerLocation.elements.forEach((element: SyncedLocationElement): void => {
|
||||||
|
changedLocationElementIds.push(element.id);
|
||||||
|
element.subElements.forEach((subElement: SyncedLocationSubElement): void => {
|
||||||
|
changedLocationSubElementIds.push(subElement.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (newerLocation.lastUpdate > olderLocation.lastUpdate) {
|
||||||
|
changedLocationIds.push(newerLocation.id);
|
||||||
|
} else {
|
||||||
|
newerLocation.elements.forEach((newerElement: SyncedLocationElement): void => {
|
||||||
|
const olderElement: SyncedLocationElement | undefined = olderLocation.elements.find((element: SyncedLocationElement): boolean => element.id === newerElement.id);
|
||||||
|
|
||||||
|
if (!olderElement) {
|
||||||
|
changedLocationElementIds.push(newerElement.id);
|
||||||
|
newerElement.subElements.forEach((subElement: SyncedLocationSubElement): void => {
|
||||||
|
changedLocationSubElementIds.push(subElement.id);
|
||||||
|
});
|
||||||
|
} else if (newerElement.lastUpdate > olderElement.lastUpdate) {
|
||||||
|
changedLocationElementIds.push(newerElement.id);
|
||||||
|
} else {
|
||||||
|
newerElement.subElements.forEach((newerSubElement: SyncedLocationSubElement): void => {
|
||||||
|
const olderSubElement: SyncedLocationSubElement | undefined = olderElement.subElements.find((subElement: SyncedLocationSubElement): boolean => subElement.id === newerSubElement.id);
|
||||||
|
if (!olderSubElement || newerSubElement.lastUpdate > olderSubElement.lastUpdate) {
|
||||||
|
changedLocationSubElementIds.push(newerSubElement.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.worlds.forEach((newerWorld: SyncedWorld): void => {
|
||||||
|
const olderWorld: SyncedWorld | undefined = olderBook.worlds.find((world: SyncedWorld): boolean => world.id === newerWorld.id);
|
||||||
|
|
||||||
|
if (!olderWorld) {
|
||||||
|
changedWorldIds.push(newerWorld.id);
|
||||||
|
newerWorld.elements.forEach((element: SyncedWorldElement): void => {
|
||||||
|
changedWorldElementIds.push(element.id);
|
||||||
|
});
|
||||||
|
} else if (newerWorld.lastUpdate > olderWorld.lastUpdate) {
|
||||||
|
changedWorldIds.push(newerWorld.id);
|
||||||
|
} else {
|
||||||
|
newerWorld.elements.forEach((newerElement: SyncedWorldElement): void => {
|
||||||
|
const olderElement: SyncedWorldElement | undefined = olderWorld.elements.find((element: SyncedWorldElement): boolean => element.id === newerElement.id);
|
||||||
|
if (!olderElement || newerElement.lastUpdate > olderElement.lastUpdate) {
|
||||||
|
changedWorldElementIds.push(newerElement.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.incidents.forEach((newerIncident: SyncedIncident): void => {
|
||||||
|
const olderIncident: SyncedIncident | undefined = olderBook.incidents.find((incident: SyncedIncident): boolean => incident.id === newerIncident.id);
|
||||||
|
if (!olderIncident || newerIncident.lastUpdate > olderIncident.lastUpdate) {
|
||||||
|
changedIncidentIds.push(newerIncident.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.plotPoints.forEach((newerPlotPoint: SyncedPlotPoint): void => {
|
||||||
|
const olderPlotPoint: SyncedPlotPoint | undefined = olderBook.plotPoints.find((plotPoint: SyncedPlotPoint): boolean => plotPoint.id === newerPlotPoint.id);
|
||||||
|
if (!olderPlotPoint || newerPlotPoint.lastUpdate > olderPlotPoint.lastUpdate) {
|
||||||
|
changedPlotPointIds.push(newerPlotPoint.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.issues.forEach((newerIssue: SyncedIssue): void => {
|
||||||
|
const olderIssue: SyncedIssue | undefined = olderBook.issues.find((issue: SyncedIssue): boolean => issue.id === newerIssue.id);
|
||||||
|
if (!olderIssue || newerIssue.lastUpdate > olderIssue.lastUpdate) {
|
||||||
|
changedIssueIds.push(newerIssue.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newerBook.actSummaries.forEach((newerActSummary: SyncedActSummary): void => {
|
||||||
|
const olderActSummary: SyncedActSummary | undefined = olderBook.actSummaries.find((actSummary: SyncedActSummary): boolean => actSummary.id === newerActSummary.id);
|
||||||
|
if (!olderActSummary || newerActSummary.lastUpdate > olderActSummary.lastUpdate) {
|
||||||
|
changedActSummaryIds.push(newerActSummary.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newerBook.guideLine && olderBook.guideLine) {
|
||||||
|
guideLineChanged = newerBook.guideLine.lastUpdate > olderBook.guideLine.lastUpdate;
|
||||||
|
} else if (newerBook.guideLine && !olderBook.guideLine) {
|
||||||
|
guideLineChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newerBook.aiGuideLine && olderBook.aiGuideLine) {
|
||||||
|
aiGuideLineChanged = newerBook.aiGuideLine.lastUpdate > olderBook.aiGuideLine.lastUpdate;
|
||||||
|
} else if (newerBook.aiGuideLine && !olderBook.aiGuideLine) {
|
||||||
|
aiGuideLineChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChanges: boolean =
|
||||||
|
changedChapterIds.length > 0 ||
|
||||||
|
changedChapterContentIds.length > 0 ||
|
||||||
|
changedChapterInfoIds.length > 0 ||
|
||||||
|
changedCharacterIds.length > 0 ||
|
||||||
|
changedCharacterAttributeIds.length > 0 ||
|
||||||
|
changedLocationIds.length > 0 ||
|
||||||
|
changedLocationElementIds.length > 0 ||
|
||||||
|
changedLocationSubElementIds.length > 0 ||
|
||||||
|
changedWorldIds.length > 0 ||
|
||||||
|
changedWorldElementIds.length > 0 ||
|
||||||
|
changedIncidentIds.length > 0 ||
|
||||||
|
changedPlotPointIds.length > 0 ||
|
||||||
|
changedIssueIds.length > 0 ||
|
||||||
|
changedActSummaryIds.length > 0 ||
|
||||||
|
guideLineChanged ||
|
||||||
|
aiGuideLineChanged;
|
||||||
|
|
||||||
|
if (!hasChanges) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: newerBook.id,
|
||||||
|
chapters: changedChapterIds,
|
||||||
|
chapterContents: changedChapterContentIds,
|
||||||
|
chapterInfos: changedChapterInfoIds,
|
||||||
|
characters: changedCharacterIds,
|
||||||
|
characterAttributes: changedCharacterAttributeIds,
|
||||||
|
locations: changedLocationIds,
|
||||||
|
locationElements: changedLocationElementIds,
|
||||||
|
locationSubElements: changedLocationSubElementIds,
|
||||||
|
worlds: changedWorldIds,
|
||||||
|
worldElements: changedWorldElementIds,
|
||||||
|
incidents: changedIncidentIds,
|
||||||
|
plotPoints: changedPlotPointIds,
|
||||||
|
issues: changedIssueIds,
|
||||||
|
actSummaries: changedActSummaryIds,
|
||||||
|
guideLine: guideLineChanged,
|
||||||
|
aiGuideLine: aiGuideLineChanged
|
||||||
|
};
|
||||||
|
}
|
||||||
365
lib/utils/syncComparison.ts
Normal file
365
lib/utils/syncComparison.ts
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
import {
|
||||||
|
SyncedBook,
|
||||||
|
SyncedChapter,
|
||||||
|
SyncedChapterContent,
|
||||||
|
SyncedChapterInfo,
|
||||||
|
SyncedCharacter,
|
||||||
|
SyncedCharacterAttribute,
|
||||||
|
SyncedLocation,
|
||||||
|
SyncedLocationElement,
|
||||||
|
SyncedLocationSubElement,
|
||||||
|
SyncedWorld,
|
||||||
|
SyncedWorldElement,
|
||||||
|
SyncedIncident,
|
||||||
|
SyncedPlotPoint,
|
||||||
|
SyncedIssue,
|
||||||
|
SyncedActSummary,
|
||||||
|
SyncedGuideLine,
|
||||||
|
SyncedAIGuideLine
|
||||||
|
} from "@/lib/models/SyncedBook";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résultat de comparaison pour un livre
|
||||||
|
*/
|
||||||
|
export interface BookSyncDiff {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
subTitle: string | null;
|
||||||
|
bookNeedsUpdate: boolean; // Le livre lui-même a changé
|
||||||
|
lastUpdate: number;
|
||||||
|
chapters: SyncedChapter[];
|
||||||
|
characters: SyncedCharacter[];
|
||||||
|
locations: SyncedLocation[];
|
||||||
|
worlds: SyncedWorld[];
|
||||||
|
incidents: SyncedIncident[];
|
||||||
|
plotPoints: SyncedPlotPoint[];
|
||||||
|
issues: SyncedIssue[];
|
||||||
|
actSummaries: SyncedActSummary[];
|
||||||
|
guideLine: SyncedGuideLine | null;
|
||||||
|
aiGuideLine: SyncedAIGuideLine | null;
|
||||||
|
hasAnyChanges: boolean; // Indique si des changements existent
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare en profondeur deux livres synchronisés et retourne les différences
|
||||||
|
*/
|
||||||
|
export function compareBooks(serverBook: SyncedBook, localBook: SyncedBook): BookSyncDiff {
|
||||||
|
const bookNeedsUpdate = serverBook.lastUpdate > localBook.lastUpdate;
|
||||||
|
|
||||||
|
// Comparer les chapitres
|
||||||
|
const chaptersWithChanges: SyncedChapter[] = [];
|
||||||
|
for (const serverChapter of serverBook.chapters) {
|
||||||
|
const localChapter = localBook.chapters.find(c => c.id === serverChapter.id);
|
||||||
|
|
||||||
|
if (!localChapter) {
|
||||||
|
// Chapitre n'existe pas localement
|
||||||
|
chaptersWithChanges.push(serverChapter);
|
||||||
|
} else if (serverChapter.lastUpdate > localChapter.lastUpdate) {
|
||||||
|
// Chapitre a changé
|
||||||
|
chaptersWithChanges.push(serverChapter);
|
||||||
|
} else {
|
||||||
|
// Vérifier les contenus du chapitre
|
||||||
|
const contentsWithChanges: SyncedChapterContent[] = [];
|
||||||
|
for (const serverContent of serverChapter.contents) {
|
||||||
|
const localContent = localChapter.contents.find(c => c.id === serverContent.id);
|
||||||
|
if (!localContent || serverContent.lastUpdate > localContent.lastUpdate) {
|
||||||
|
contentsWithChanges.push(serverContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'info du chapitre
|
||||||
|
let infoNeedsUpdate = false;
|
||||||
|
if (serverChapter.info && localChapter.info) {
|
||||||
|
infoNeedsUpdate = serverChapter.info.lastUpdate > localChapter.info.lastUpdate;
|
||||||
|
} else if (serverChapter.info && !localChapter.info) {
|
||||||
|
infoNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si des contenus ou info ont changé, inclure le chapitre
|
||||||
|
if (contentsWithChanges.length > 0 || infoNeedsUpdate) {
|
||||||
|
chaptersWithChanges.push({
|
||||||
|
...serverChapter,
|
||||||
|
contents: contentsWithChanges.length > 0 ? contentsWithChanges : serverChapter.contents,
|
||||||
|
info: infoNeedsUpdate ? serverChapter.info : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les personnages
|
||||||
|
const charactersWithChanges: SyncedCharacter[] = [];
|
||||||
|
for (const serverChar of serverBook.characters) {
|
||||||
|
const localChar = localBook.characters.find(c => c.id === serverChar.id);
|
||||||
|
|
||||||
|
if (!localChar) {
|
||||||
|
charactersWithChanges.push(serverChar);
|
||||||
|
} else if (serverChar.lastUpdate > localChar.lastUpdate) {
|
||||||
|
charactersWithChanges.push(serverChar);
|
||||||
|
} else {
|
||||||
|
// Vérifier les attributs
|
||||||
|
const attributesWithChanges: SyncedCharacterAttribute[] = [];
|
||||||
|
for (const serverAttr of serverChar.attributes) {
|
||||||
|
const localAttr = localChar.attributes.find(a => a.id === serverAttr.id);
|
||||||
|
if (!localAttr || serverAttr.lastUpdate > localAttr.lastUpdate) {
|
||||||
|
attributesWithChanges.push(serverAttr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributesWithChanges.length > 0) {
|
||||||
|
charactersWithChanges.push({
|
||||||
|
...serverChar,
|
||||||
|
attributes: attributesWithChanges
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les locations
|
||||||
|
const locationsWithChanges: SyncedLocation[] = [];
|
||||||
|
for (const serverLoc of serverBook.locations) {
|
||||||
|
const localLoc = localBook.locations.find(l => l.id === serverLoc.id);
|
||||||
|
|
||||||
|
if (!localLoc) {
|
||||||
|
locationsWithChanges.push(serverLoc);
|
||||||
|
} else if (serverLoc.lastUpdate > localLoc.lastUpdate) {
|
||||||
|
locationsWithChanges.push(serverLoc);
|
||||||
|
} else {
|
||||||
|
// Vérifier les éléments
|
||||||
|
const elementsWithChanges: SyncedLocationElement[] = [];
|
||||||
|
for (const serverElem of serverLoc.elements) {
|
||||||
|
const localElem = localLoc.elements.find(e => e.id === serverElem.id);
|
||||||
|
|
||||||
|
if (!localElem) {
|
||||||
|
elementsWithChanges.push(serverElem);
|
||||||
|
} else if (serverElem.lastUpdate > localElem.lastUpdate) {
|
||||||
|
elementsWithChanges.push(serverElem);
|
||||||
|
} else {
|
||||||
|
// Vérifier les sous-éléments
|
||||||
|
const subElementsWithChanges: SyncedLocationSubElement[] = [];
|
||||||
|
for (const serverSubElem of serverElem.subElements) {
|
||||||
|
const localSubElem = localElem.subElements.find(s => s.id === serverSubElem.id);
|
||||||
|
if (!localSubElem || serverSubElem.lastUpdate > localSubElem.lastUpdate) {
|
||||||
|
subElementsWithChanges.push(serverSubElem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subElementsWithChanges.length > 0) {
|
||||||
|
elementsWithChanges.push({
|
||||||
|
...serverElem,
|
||||||
|
subElements: subElementsWithChanges
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementsWithChanges.length > 0) {
|
||||||
|
locationsWithChanges.push({
|
||||||
|
...serverLoc,
|
||||||
|
elements: elementsWithChanges
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les mondes
|
||||||
|
const worldsWithChanges: SyncedWorld[] = [];
|
||||||
|
for (const serverWorld of serverBook.worlds) {
|
||||||
|
const localWorld = localBook.worlds.find(w => w.id === serverWorld.id);
|
||||||
|
|
||||||
|
if (!localWorld) {
|
||||||
|
worldsWithChanges.push(serverWorld);
|
||||||
|
} else if (serverWorld.lastUpdate > localWorld.lastUpdate) {
|
||||||
|
worldsWithChanges.push(serverWorld);
|
||||||
|
} else {
|
||||||
|
// Vérifier les éléments du monde
|
||||||
|
const elementsWithChanges: SyncedWorldElement[] = [];
|
||||||
|
for (const serverElem of serverWorld.elements) {
|
||||||
|
const localElem = localWorld.elements.find(e => e.id === serverElem.id);
|
||||||
|
if (!localElem || serverElem.lastUpdate > localElem.lastUpdate) {
|
||||||
|
elementsWithChanges.push(serverElem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementsWithChanges.length > 0) {
|
||||||
|
worldsWithChanges.push({
|
||||||
|
...serverWorld,
|
||||||
|
elements: elementsWithChanges
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les incidents
|
||||||
|
const incidentsWithChanges: SyncedIncident[] = [];
|
||||||
|
for (const serverIncident of serverBook.incidents) {
|
||||||
|
const localIncident = localBook.incidents.find(i => i.id === serverIncident.id);
|
||||||
|
if (!localIncident || serverIncident.lastUpdate > localIncident.lastUpdate) {
|
||||||
|
incidentsWithChanges.push(serverIncident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les plot points
|
||||||
|
const plotPointsWithChanges: SyncedPlotPoint[] = [];
|
||||||
|
for (const serverPlot of serverBook.plotPoints) {
|
||||||
|
const localPlot = localBook.plotPoints.find(p => p.id === serverPlot.id);
|
||||||
|
if (!localPlot || serverPlot.lastUpdate > localPlot.lastUpdate) {
|
||||||
|
plotPointsWithChanges.push(serverPlot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les issues
|
||||||
|
const issuesWithChanges: SyncedIssue[] = [];
|
||||||
|
for (const serverIssue of serverBook.issues) {
|
||||||
|
const localIssue = localBook.issues.find(i => i.id === serverIssue.id);
|
||||||
|
if (!localIssue || serverIssue.lastUpdate > localIssue.lastUpdate) {
|
||||||
|
issuesWithChanges.push(serverIssue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les act summaries
|
||||||
|
const actSummariesWithChanges: SyncedActSummary[] = [];
|
||||||
|
for (const serverAct of serverBook.actSummaries) {
|
||||||
|
const localAct = localBook.actSummaries.find(a => a.id === serverAct.id);
|
||||||
|
if (!localAct || serverAct.lastUpdate > localAct.lastUpdate) {
|
||||||
|
actSummariesWithChanges.push(serverAct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer guideline
|
||||||
|
let guideLineNeedsUpdate: SyncedGuideLine | null = null;
|
||||||
|
if (serverBook.guideLine && localBook.guideLine) {
|
||||||
|
if (serverBook.guideLine.lastUpdate > localBook.guideLine.lastUpdate) {
|
||||||
|
guideLineNeedsUpdate = serverBook.guideLine;
|
||||||
|
}
|
||||||
|
} else if (serverBook.guideLine && !localBook.guideLine) {
|
||||||
|
guideLineNeedsUpdate = serverBook.guideLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer AI guideline
|
||||||
|
let aiGuideLineNeedsUpdate: SyncedAIGuideLine | null = null;
|
||||||
|
if (serverBook.aiGuideLine && localBook.aiGuideLine) {
|
||||||
|
if (serverBook.aiGuideLine.lastUpdate > localBook.aiGuideLine.lastUpdate) {
|
||||||
|
aiGuideLineNeedsUpdate = serverBook.aiGuideLine;
|
||||||
|
}
|
||||||
|
} else if (serverBook.aiGuideLine && !localBook.aiGuideLine) {
|
||||||
|
aiGuideLineNeedsUpdate = serverBook.aiGuideLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Déterminer s'il y a des changements
|
||||||
|
const hasAnyChanges = bookNeedsUpdate ||
|
||||||
|
chaptersWithChanges.length > 0 ||
|
||||||
|
charactersWithChanges.length > 0 ||
|
||||||
|
locationsWithChanges.length > 0 ||
|
||||||
|
worldsWithChanges.length > 0 ||
|
||||||
|
incidentsWithChanges.length > 0 ||
|
||||||
|
plotPointsWithChanges.length > 0 ||
|
||||||
|
issuesWithChanges.length > 0 ||
|
||||||
|
actSummariesWithChanges.length > 0 ||
|
||||||
|
guideLineNeedsUpdate !== null ||
|
||||||
|
aiGuideLineNeedsUpdate !== null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: serverBook.id,
|
||||||
|
type: serverBook.type,
|
||||||
|
title: serverBook.title,
|
||||||
|
subTitle: serverBook.subTitle,
|
||||||
|
bookNeedsUpdate,
|
||||||
|
lastUpdate: serverBook.lastUpdate,
|
||||||
|
chapters: chaptersWithChanges,
|
||||||
|
characters: charactersWithChanges,
|
||||||
|
locations: locationsWithChanges,
|
||||||
|
worlds: worldsWithChanges,
|
||||||
|
incidents: incidentsWithChanges,
|
||||||
|
plotPoints: plotPointsWithChanges,
|
||||||
|
issues: issuesWithChanges,
|
||||||
|
actSummaries: actSummariesWithChanges,
|
||||||
|
guideLine: guideLineNeedsUpdate,
|
||||||
|
aiGuideLine: aiGuideLineNeedsUpdate,
|
||||||
|
hasAnyChanges
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare tous les livres serveur vs locaux et retourne ceux qui ont des changements
|
||||||
|
*/
|
||||||
|
export function getBooksToSyncFromServer(serverBooks: SyncedBook[], localBooks: SyncedBook[]): BookSyncDiff[] {
|
||||||
|
const booksWithChanges: BookSyncDiff[] = [];
|
||||||
|
|
||||||
|
for (const serverBook of serverBooks) {
|
||||||
|
const localBook = localBooks.find(b => b.id === serverBook.id);
|
||||||
|
|
||||||
|
if (!localBook) {
|
||||||
|
// Livre n'existe pas localement - tout le livre doit être synchronisé
|
||||||
|
booksWithChanges.push({
|
||||||
|
id: serverBook.id,
|
||||||
|
type: serverBook.type,
|
||||||
|
title: serverBook.title,
|
||||||
|
subTitle: serverBook.subTitle,
|
||||||
|
bookNeedsUpdate: true,
|
||||||
|
lastUpdate: serverBook.lastUpdate,
|
||||||
|
chapters: serverBook.chapters,
|
||||||
|
characters: serverBook.characters,
|
||||||
|
locations: serverBook.locations,
|
||||||
|
worlds: serverBook.worlds,
|
||||||
|
incidents: serverBook.incidents,
|
||||||
|
plotPoints: serverBook.plotPoints,
|
||||||
|
issues: serverBook.issues,
|
||||||
|
actSummaries: serverBook.actSummaries,
|
||||||
|
guideLine: serverBook.guideLine,
|
||||||
|
aiGuideLine: serverBook.aiGuideLine,
|
||||||
|
hasAnyChanges: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Comparer en profondeur
|
||||||
|
const diff = compareBooks(serverBook, localBook);
|
||||||
|
if (diff.hasAnyChanges) {
|
||||||
|
booksWithChanges.push(diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return booksWithChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare tous les livres locaux vs serveur et retourne ceux qui ont des changements locaux
|
||||||
|
*/
|
||||||
|
export function getBooksToSyncToServer(localBooks: SyncedBook[], serverBooks: SyncedBook[]): BookSyncDiff[] {
|
||||||
|
const booksWithChanges: BookSyncDiff[] = [];
|
||||||
|
|
||||||
|
for (const localBook of localBooks) {
|
||||||
|
const serverBook = serverBooks.find(b => b.id === localBook.id);
|
||||||
|
|
||||||
|
if (!serverBook) {
|
||||||
|
// Livre n'existe pas sur le serveur - tout le livre doit être envoyé
|
||||||
|
booksWithChanges.push({
|
||||||
|
id: localBook.id,
|
||||||
|
type: localBook.type,
|
||||||
|
title: localBook.title,
|
||||||
|
subTitle: localBook.subTitle,
|
||||||
|
bookNeedsUpdate: true,
|
||||||
|
lastUpdate: localBook.lastUpdate,
|
||||||
|
chapters: localBook.chapters,
|
||||||
|
characters: localBook.characters,
|
||||||
|
locations: localBook.locations,
|
||||||
|
worlds: localBook.worlds,
|
||||||
|
incidents: localBook.incidents,
|
||||||
|
plotPoints: localBook.plotPoints,
|
||||||
|
issues: localBook.issues,
|
||||||
|
actSummaries: localBook.actSummaries,
|
||||||
|
guideLine: localBook.guideLine,
|
||||||
|
aiGuideLine: localBook.aiGuideLine,
|
||||||
|
hasAnyChanges: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Comparer en profondeur (local vs server)
|
||||||
|
const diff = compareBooks(localBook, serverBook);
|
||||||
|
if (diff.hasAnyChanges) {
|
||||||
|
booksWithChanges.push(diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return booksWithChanges;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user