diff --git a/components/SyncBook.tsx b/components/SyncBook.tsx index b75f798..a770984 100644 --- a/components/SyncBook.tsx +++ b/components/SyncBook.tsx @@ -67,14 +67,14 @@ export default function SyncBook({bookId, status}: SyncBookProps) { return (
{currentStatus === 'synced' && ( - + )} {currentStatus === 'local-only' && ( void; } export default function TermsOfUse({onAccept}: TermsOfUseProps) { - const router: AppRouterInstance = useRouter(); - + const t = useTranslations(); + function handleAcceptTerm(): void { onAccept(); } - + + async function handleRefuse(): Promise { + if (isDesktop) { + await tauri.logout(); + } else { + window.location.href = 'https://eritors.com'; + } + } + return (
@@ -25,9 +35,8 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
-

Termes d'utilisation

-

Acceptation requise pour accéder à ERitors - Scribe

+

{t('terms.title')}

+

{t('terms.subtitle')}

@@ -35,33 +44,21 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {

- Acceptation obligatoire + {t('terms.mandatory')}

-

- Pour pouvoir utiliser nos services, tel qu'ERitors - Scribe, - vous devez accepter les termes d'utilisation en cliquant - sur J'accepte. -

-

- Veuillez lire attentivement la page détaillée des termes et conditions d'utilisation - avant de procéder à l'acceptation. -

-

- Si vous n'acceptez pas ces conditions, vous ne pourrez pas accéder à nos services - et serez redirigé vers la page d'accueil. -

+

+

{t('terms.mandatoryDesc2')}

+

{t('terms.mandatoryDesc3')}

- +

- Documentation complète + {t('terms.fullDoc')}

- Pour consulter l'intégralité de nos termes et conditions d'utilisation, - veuillez visiter notre page dédiée : + {t('terms.fullDocDesc')}

- Consulter les termes complets + {t('terms.fullDocLink')}
- +
@@ -81,12 +78,10 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {

- Importance capitale + {t('terms.importance')}

- Cette acceptation est obligatoire et constitue un prérequis légal - pour l'utilisation de nos services d'édition assistée par intelligence - artificielle. + {t('terms.importanceDesc')}

@@ -97,18 +92,18 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
- Décision requise pour continuer + {t('terms.required')}
- - Refuser et quitter - + {t('terms.refuse')} +
@@ -116,4 +111,4 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
); -} \ No newline at end of file +} diff --git a/components/book/BookCard.tsx b/components/book/BookCard.tsx index dbe2d19..38b9104 100644 --- a/components/book/BookCard.tsx +++ b/components/book/BookCard.tsx @@ -1,58 +1,65 @@ -import {Link} from '@/lib/navigation'; -import React from "react"; -import {BookProps} from "@/lib/types/book"; -import DeleteBook from "@/components/book/settings/DeleteBook"; -import {useTranslations} from '@/lib/i18n'; - -export default function BookCard( - { - book, - onClickCallback, - index - }: { - book: BookProps, - onClickCallback: Function; - index: number; - }) { - const t = useTranslations(); - - return ( - -
- {book.coverImage ? ( - {book.title - ) : ( -
- - {book.title.charAt(0).toUpperCase()} - -
- )} - -
-

- {book.title} -

- {book.subTitle && ( -

- {book.subTitle} -

- )} -
- -
e.preventDefault()} - {...(index === 0 && {'data-guide': 'bottom-book-card'})} - > - -
-
- - ) -} +import React from "react"; +import {isDesktop} from '@/lib/configs'; +import {BookProps} from "@/lib/types/book"; +import DeleteBook from "@/components/book/settings/DeleteBook"; +import SyncBook from "@/components/SyncBook"; +import {SyncType} from "@/context/BooksSyncContext"; +import {useTranslations} from '@/lib/i18n'; + +interface BookCardProps { + book: BookProps; + onClickCallback: (bookId: string) => void; + index: number; + syncStatus?: SyncType; +} + +export default function BookCard({book, onClickCallback, index, syncStatus}: BookCardProps) { + const t = useTranslations(); + + return ( +
+ + + {isDesktop && syncStatus && ( +
e.stopPropagation()}> + +
+ )} + +
+

+ {book.title} +

+ {book.subTitle && ( +

+ {book.subTitle} +

+ )} +
+ +
e.stopPropagation()} + {...(index === 0 && {'data-guide': 'bottom-book-card'})} + > + +
+
+ ) +} diff --git a/components/book/BookList.tsx b/components/book/BookList.tsx index 3818eba..c0630c6 100644 --- a/components/book/BookList.tsx +++ b/components/book/BookList.tsx @@ -17,7 +17,8 @@ import GuideTour, {GuideStep} from "@/components/GuideTour"; import {guideTourDone, setNewGuideTour} from "@/lib/utils/user"; import {useTranslations} from '@/lib/i18n'; import {LangContext, LangContextProps} from "@/context/LangContext"; -import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext"; +import {BooksSyncContext, BooksSyncContextProps, SyncType} from "@/context/BooksSyncContext"; +import {BookSyncCompare, SyncedBook} from "@/lib/types/synced-book"; import {SeriesListItemProps} from "@/lib/types/series"; import SeriesCard, {SeriesCardProps} from "@/components/series/SeriesCard"; import SeriesSetting from "@/components/series/SeriesSetting"; @@ -35,8 +36,11 @@ export default function BookList() { const router = useRouter(); const t = useTranslations(); const {lang}: LangContextProps = useContext(LangContext) - const {serverSyncedBooks}: BooksSyncContextProps = useContext(BooksSyncContext) - const {isCurrentlyOffline}: OfflineContextType = useContext(OfflineContext); + const { + serverSyncedBooks, booksToSyncFromServer, booksToSyncToServer, + serverOnlyBooks, localOnlyBooks + }: BooksSyncContextProps = useContext(BooksSyncContext); + const {isCurrentlyOffline, offlineMode}: OfflineContextType = useContext(OfflineContext); const [searchQuery, setSearchQuery] = useState(''); const [groupedItems, setGroupedItems] = useState>({}); @@ -95,27 +99,38 @@ export default function BookList() { ] useEffect((): void => { - if (groupedItems && Object.keys(groupedItems).length > 0 && guideTourDone(session.user?.guideTour || [], 'new-first-book')) { - setBookGuide(true); + if (groupedItems && Object.keys(groupedItems).length > 0) { + const notDone: boolean = isCurrentlyOffline() + ? localStorage.getItem('guide-tour-new-first-book') !== 'true' + : guideTourDone(session.user?.guideTour || [], 'new-first-book'); + if (notDone) setBookGuide(true); } }, [groupedItems]); useEffect((): void => { - loadBooksAndSeries().then() - }, [serverSyncedBooks]); + const canLoad: boolean = !isDesktop || + (!isCurrentlyOffline() || offlineMode.isDatabaseInitialized); + if (canLoad) loadBooksAndSeries().then(); + }, [serverSyncedBooks, offlineMode.isDatabaseInitialized, booksToSyncFromServer, booksToSyncToServer, serverOnlyBooks, localOnlyBooks]); useEffect((): void => { if (accessToken) loadBooksAndSeries().then(); }, [accessToken]); async function handleFirstBookGuide(): Promise { + if (isCurrentlyOffline()) { + localStorage.setItem('guide-tour-new-first-book', 'true'); + setBookGuide(false); + return; + } try { const response: boolean = await apiPost( 'logs/tour', - {plateforme: 'web', tour: 'new-first-book'}, + {plateforme: 'desktop', tour: 'new-first-book'}, session.accessToken, lang ); if (response) { + localStorage.setItem('guide-tour-new-first-book', 'true'); setSession(setNewGuideTour(session, 'new-first-book')); setBookGuide(false); } @@ -291,6 +306,23 @@ export default function BookList() { }, 0); } + function detectBookSyncStatus(bookId: string): SyncType { + if (!isDesktop || isCurrentlyOffline()) return 'synced'; + if (serverOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId)) { + return 'server-only'; + } + if (localOnlyBooks.find((book: SyncedBook): boolean => book.id === bookId)) { + return 'local-only'; + } + if (booksToSyncFromServer.find((book: BookSyncCompare): boolean => book.id === bookId)) { + return 'to-sync-from-server'; + } + if (booksToSyncToServer.find((book: BookSyncCompare): boolean => book.id === bookId)) { + return 'to-sync-to-server'; + } + return 'synced'; + } + function handleBookClick(bookId: string): void { router.push(`/book/${bookId}`); } @@ -388,11 +420,12 @@ export default function BookList() { return (
+ className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${bookGuide && 'mb-[200px]'}`}>
); diff --git a/components/editor/DraftCompanion.tsx b/components/editor/DraftCompanion.tsx index 2f34842..ca839cc 100644 --- a/components/editor/DraftCompanion.tsx +++ b/components/editor/DraftCompanion.tsx @@ -95,7 +95,7 @@ export default function DraftCompanion() { const isGPTEnabled: boolean = isOpenAIEnabled(session); const isSubTierTree: boolean = getSubLevel(session) === 3; - const hasAccess: boolean = isGPTEnabled || isSubTierTree; + const hasAccess: boolean = (isGPTEnabled || isSubTierTree) && !isCurrentlyOffline() && !book?.localBook; useEffect((): void => { getDraftContent().then(); diff --git a/components/leftbar/ScribeLeftBar.tsx b/components/leftbar/ScribeLeftBar.tsx index 9c1fe4d..a4db51b 100644 --- a/components/leftbar/ScribeLeftBar.tsx +++ b/components/leftbar/ScribeLeftBar.tsx @@ -12,10 +12,12 @@ import ImportBookForm from "@/components/book/ImportBookForm"; import {SessionContext, SessionContextProps} from "@/context/SessionContext"; import {useTranslations} from '@/lib/i18n'; import InsetPanel from "@/components/ui/InsetPanel"; +import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; export default function ScribeLeftBar() { const {book}: BookContextProps = useContext(BookContext); const {session}: SessionContextProps = useContext(SessionContext); + const {isCurrentlyOffline}: OfflineContextType = useContext(OfflineContext); const t = useTranslations(); const [panelHidden, setPanelHidden] = useState(false); @@ -100,7 +102,9 @@ export default function ScribeLeftBar() { }} /> )) : ( - homeComponents.map((component: PanelComponent) => ( + homeComponents.filter((component: PanelComponent): boolean => { + return !(isCurrentlyOffline() && component.id === 2); + }).map((component: PanelComponent) => ( void; @@ -69,77 +70,83 @@ export default function OfflinePinSetup({onClose, onSuccess, showOnFirstLogin}: }; return ( -
-
-
-
- -
-
-

- {showOnFirstLogin ? t('offline.pin.setup.titleFirstLogin') : t('offline.pin.setup.title')} -

+
+
+
+

+ + {showOnFirstLogin ? t('offline.pin.setup.titleFirstLogin') : t('offline.pin.setup.title')} +

+ {onClose && ( + + )} +
+ +
+

{t('offline.pin.setup.subtitle')}

-
-
-
-

- - {t('offline.pin.setup.description')} -

-
+
+

+ + {t('offline.pin.setup.description')} +

+
-
-
- -
+
+ +
+ setPin(e.target.value.replace(/\D/g, '').slice(0, 8))} + placeholder="••••" + maxLength={8} + className="input-base text-center text-lg tracking-widest" + disabled={isLoading} + /> + +
+
+ +
+ setPin(e.target.value.replace(/\D/g, '').slice(0, 8))} + value={confirmPin} + onChange={(e) => setConfirmPin(e.target.value.replace(/\D/g, '').slice(0, 8))} placeholder="••••" maxLength={8} className="input-base text-center text-lg tracking-widest" disabled={isLoading} /> -
-
-
- - setConfirmPin(e.target.value.replace(/\D/g, '').slice(0, 8))} - placeholder="••••" - maxLength={8} - className="input-base text-center text-lg tracking-widest" - disabled={isLoading} - /> + {error && ( +
+

{error}

+
+ )} + +

+ {t('offline.pin.setup.footer')} +

- {error && ( -
-

{error}

-
- )} - -
+
{onClose && (
- -

- {t('offline.pin.setup.footer')} -

); diff --git a/components/offline/OfflinePinVerify.tsx b/components/offline/OfflinePinVerify.tsx index 4333573..fc0cb4c 100644 --- a/components/offline/OfflinePinVerify.tsx +++ b/components/offline/OfflinePinVerify.tsx @@ -64,85 +64,81 @@ export default function OfflinePinVerify({onSuccess, onCancel}: OfflinePinVerify }; return ( -
-
-
-
-
- -
-
-
- -
-
-
-
- -
-

+
+
+
+

+ {t('offline.pin.verify.title')}

-

- {t('offline.pin.verify.subtitle')} -

-
- -
-
- setPin(e.target.value.replace(/\D/g, '').slice(0, 8))} - onKeyPress={handleKeyPress} - placeholder={t('offline.pin.verify.placeholder')} - maxLength={8} - autoFocus - className="input-base text-center text-lg tracking-widest" - disabled={isLoading || attempts > 2} - /> - +
+
- {error && ( -
-

{error}

-
- )} +
+
+

+ {t('offline.pin.verify.subtitle')} +

-
+
+ setPin(e.target.value.replace(/\D/g, '').slice(0, 8))} + onKeyPress={handleKeyPress} + placeholder={t('offline.pin.verify.placeholder')} + maxLength={8} + autoFocus + className="input-base text-center text-lg tracking-widest" + disabled={isLoading || attempts > 2} + /> + +
+ + {error && ( +
+

{error}

+
+ )} + + {attempts > 0 && attempts <= 2 && ( +

+ {t('offline.pin.verify.attemptsRemaining', {count: 3 - attempts})} +

+ )} +
+
+ +
- {onCancel && ( - + )} + - )} - +
- - {attempts > 0 && attempts <= 2 && ( -

- {t('offline.pin.verify.attemptsRemaining', {count: 3 - attempts})} -

- )}
); diff --git a/components/offline/OfflineToggle.tsx b/components/offline/OfflineToggle.tsx index 1220cd3..39aaa8b 100644 --- a/components/offline/OfflineToggle.tsx +++ b/components/offline/OfflineToggle.tsx @@ -1,23 +1,21 @@ 'use client'; -import { useContext } from 'react'; +import {useContext} from 'react'; import OfflineContext from '@/context/OfflineContext'; -import { Wifi, Circle } from 'lucide-react'; +import {Wifi, Circle} from 'lucide-react'; +import {useTranslations} from '@/lib/i18n'; export default function OfflineToggle() { - const { offlineMode, toggleOfflineMode } = useContext(OfflineContext); + const {offlineMode, toggleOfflineMode} = useContext(OfflineContext); + const t = useTranslations(); if (!offlineMode.isDatabaseInitialized) { return null; } - const handleToggle = () => { - toggleOfflineMode(); - }; - return ( ); diff --git a/components/rightbar/ComposerRightBar.tsx b/components/rightbar/ComposerRightBar.tsx index 15707df..18a5d7b 100644 --- a/components/rightbar/ComposerRightBar.tsx +++ b/components/rightbar/ComposerRightBar.tsx @@ -10,6 +10,7 @@ import {useTranslations} from '@/lib/i18n'; import PulseLoader from '@/components/ui/PulseLoader'; import InsetPanel from "@/components/ui/InsetPanel"; import IconButton from "@/components/ui/IconButton"; +import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; // Lazy loaded Editor components const WorldEditor = lazy(function () { @@ -45,6 +46,7 @@ function PanelContent({currentPanelId}: PanelContentProps): React.JSX.Element { export default function ComposerRightBar(): React.JSX.Element { const {book}: BookContextProps = useContext(BookContext); + const {isCurrentlyOffline}: OfflineContextType = useContext(OfflineContext); const t = useTranslations(); const [panelHidden, setPanelHidden] = useState(false); @@ -169,6 +171,9 @@ export default function ComposerRightBar(): React.JSX.Element { )}
{book ? editorComponents.filter(function (component: PanelComponent): boolean { + if ((isCurrentlyOffline() || book?.localBook) && component.id === 1) { + return false; + } if (component.id === 1 && book.quillsenseEnabled === false) { return false; } diff --git a/context/BooksSyncContext.ts b/context/BooksSyncContext.ts index 9096f26..67b152c 100644 --- a/context/BooksSyncContext.ts +++ b/context/BooksSyncContext.ts @@ -1,13 +1,34 @@ -import {SyncedBook} from "@/lib/types/synced-book"; +import {BookSyncCompare, SyncedBook} from "@/lib/types/synced-book"; import {Context, createContext, Dispatch, SetStateAction} from "react"; +export type SyncType = 'server-only' | 'local-only' | 'to-sync-from-server' | 'to-sync-to-server' | 'synced' + export interface BooksSyncContextProps { - setServerSyncedBooks: Dispatch>; serverSyncedBooks: SyncedBook[]; + setServerSyncedBooks: Dispatch>; + localSyncedBooks: SyncedBook[]; + setLocalSyncedBooks: Dispatch>; + booksToSyncFromServer: BookSyncCompare[]; + setBooksToSyncFromServer: Dispatch>; + booksToSyncToServer: BookSyncCompare[]; + setBooksToSyncToServer: Dispatch>; + setServerOnlyBooks: Dispatch>; + setLocalOnlyBooks: Dispatch>; + serverOnlyBooks: SyncedBook[]; + localOnlyBooks: SyncedBook[]; } export const BooksSyncContext: Context = createContext({ serverSyncedBooks: [], - setServerSyncedBooks: (): void => { - } -}) \ No newline at end of file + setServerSyncedBooks: (): void => {}, + localSyncedBooks: [], + setLocalSyncedBooks: (): void => {}, + booksToSyncFromServer: [], + setBooksToSyncFromServer: (): void => {}, + booksToSyncToServer: [], + setBooksToSyncToServer: (): void => {}, + setServerOnlyBooks: (): void => {}, + setLocalOnlyBooks: (): void => {}, + serverOnlyBooks: [], + localOnlyBooks: [] +}); diff --git a/context/OfflineContext.ts b/context/OfflineContext.ts index 405ac0b..17568df 100644 --- a/context/OfflineContext.ts +++ b/context/OfflineContext.ts @@ -1,11 +1,35 @@ -import {createContext} from 'react'; +import {createContext, Dispatch, SetStateAction} from 'react'; + +export interface OfflineMode { + isManuallyOffline: boolean; + isNetworkOnline: boolean; + isOffline: boolean; + isDatabaseInitialized: boolean; + error: string | null; +} export interface OfflineContextType { + offlineMode: OfflineMode; + setOfflineMode: Dispatch>; + toggleOfflineMode: () => void; + initializeDatabase: (userId: string, encryptionKey?: string) => Promise; isCurrentlyOffline: () => boolean; } +export const defaultOfflineMode: OfflineMode = { + isManuallyOffline: false, + isNetworkOnline: typeof navigator !== 'undefined' ? navigator.onLine : true, + isOffline: false, + isDatabaseInitialized: false, + error: null +}; + const OfflineContext = createContext({ - isCurrentlyOffline: () => false, + offlineMode: defaultOfflineMode, + setOfflineMode: () => {}, + toggleOfflineMode: () => {}, + initializeDatabase: async () => false, + isCurrentlyOffline: () => false }); export default OfflineContext; diff --git a/context/OfflineProvider.tsx b/context/OfflineProvider.tsx index e8f7832..4a73124 100644 --- a/context/OfflineProvider.tsx +++ b/context/OfflineProvider.tsx @@ -33,7 +33,7 @@ export default function OfflineProvider({ children }: OfflineProviderProps) { return true; } catch (error) { - console.error('Failed to initialize database:', error, 'userId:', userId, 'hasKey:', !!userKey); + console.error('Failed to initialize database:', error, 'userId:', userId, 'hasKey:', !!encryptionKey); setOfflineMode(prev => ({ ...prev, isDatabaseInitialized: false, diff --git a/lib/configs.ts b/lib/configs.ts index 5944c76..97e904f 100644 --- a/lib/configs.ts +++ b/lib/configs.ts @@ -17,4 +17,4 @@ export const configs: Configs = { appName: 'Eritors Scribe', appDescription: 'Eritors Scribe est une application de prise de notes et d\'écriture collaborative.', appVersion: packageJson.version, -}; \ No newline at end of file +}; diff --git a/lib/locales/en.json b/lib/locales/en.json index b196706..483dafe 100644 --- a/lib/locales/en.json +++ b/lib/locales/en.json @@ -1372,7 +1372,29 @@ "passwordUnknown": "An unknown error occurred." } }, + "terms": { + "title": "Terms of Use", + "subtitle": "Acceptance required to access ERitors Scribe", + "mandatory": "Mandatory acceptance", + "mandatoryDesc1": "To use our services, such as ERitors Scribe, you must accept the terms of use by clicking I accept.", + "mandatoryDesc2": "Please carefully read the detailed terms and conditions page before proceeding with acceptance.", + "mandatoryDesc3": "If you do not accept these conditions, you will not be able to access our services and will be redirected to the home page.", + "fullDoc": "Full documentation", + "fullDocDesc": "To view our complete terms and conditions, please visit our dedicated page:", + "fullDocLink": "View full terms", + "importance": "Critical importance", + "importanceDesc": "This acceptance is mandatory and constitutes a legal prerequisite for the use of our AI-assisted editing services.", + "required": "Decision required to continue", + "refuse": "Decline and exit", + "accept": "I accept the terms" + }, "offline": { + "toggle": { + "online": "Online", + "offline": "Offline", + "switchToOnline": "Switch to online mode", + "switchToOffline": "Switch to offline mode" + }, "mode": { "title": "Offline mode", "backToOnline": "Back online" diff --git a/lib/locales/fr.json b/lib/locales/fr.json index 1d97124..611188e 100644 --- a/lib/locales/fr.json +++ b/lib/locales/fr.json @@ -1371,7 +1371,29 @@ "passwordUnknown": "Une erreur inconnue est survenue." } }, + "terms": { + "title": "Termes d'utilisation", + "subtitle": "Acceptation requise pour accéder à ERitors Scribe", + "mandatory": "Acceptation obligatoire", + "mandatoryDesc1": "Pour pouvoir utiliser nos services, tel qu'ERitors Scribe, vous devez accepter les termes d'utilisation en cliquant sur J'accepte.", + "mandatoryDesc2": "Veuillez lire attentivement la page détaillée des termes et conditions d'utilisation avant de procéder à l'acceptation.", + "mandatoryDesc3": "Si vous n'acceptez pas ces conditions, vous ne pourrez pas accéder à nos services et serez redirigé vers la page d'accueil.", + "fullDoc": "Documentation complète", + "fullDocDesc": "Pour consulter l'intégralité de nos termes et conditions d'utilisation, veuillez visiter notre page dédiée :", + "fullDocLink": "Consulter les termes complets", + "importance": "Importance capitale", + "importanceDesc": "Cette acceptation est obligatoire et constitue un prérequis légal pour l'utilisation de nos services d'édition assistée par intelligence artificielle.", + "required": "Décision requise pour continuer", + "refuse": "Refuser et quitter", + "accept": "J'accepte les termes" + }, "offline": { + "toggle": { + "online": "En ligne", + "offline": "Hors ligne", + "switchToOnline": "Passer en mode en ligne", + "switchToOffline": "Passer en mode hors ligne" + }, "mode": { "title": "Mode hors-ligne", "backToOnline": "Retour en ligne" diff --git a/lib/tauri.ts b/lib/tauri.ts index 09079a5..30932a7 100644 --- a/lib/tauri.ts +++ b/lib/tauri.ts @@ -679,7 +679,12 @@ export async function applySeriesTombstones(tombstones: TombstoneRecord[]): Prom // ─── Window Management ────────────────────────────────────── +let loginWindowOpening = false; + export async function openLoginWindow(): Promise { + if (loginWindowOpening) return; + loginWindowOpening = true; + const {WebviewWindow} = await import('@tauri-apps/api/webviewWindow'); const {getCurrentWindow} = await import('@tauri-apps/api/window'); @@ -687,6 +692,7 @@ export async function openLoginWindow(): Promise { if (existing) { await existing.setFocus(); await getCurrentWindow().hide(); + loginWindowOpening = false; return; } @@ -701,6 +707,7 @@ export async function openLoginWindow(): Promise { }); loginWindow.once('tauri://created', async function () { + loginWindowOpening = false; await getCurrentWindow().hide(); }); } @@ -712,20 +719,20 @@ export async function loginSuccess(): Promise { const currentLabel = getCurrentWindow().label; if (currentLabel === 'login') { - const {emit} = await import('@tauri-apps/api/event'); - await emit('auth-success'); const mainWindow = await WebviewWindow.getByLabel('main'); if (mainWindow) { await mainWindow.show(); await mainWindow.setFocus(); + await mainWindow.emit('auth-success'); } - await getCurrentWindow().close(); + getCurrentWindow().close(); } else { window.location.reload(); } } export async function logout(): Promise { + await removeToken(); await openLoginWindow(); } diff --git a/lib/utils/cookies.ts b/lib/utils/cookies.ts index f02c2c4..3b3d2c3 100644 --- a/lib/utils/cookies.ts +++ b/lib/utils/cookies.ts @@ -1,3 +1,5 @@ +import {isDesktop} from '@/lib/configs'; + export function getCookie(name: string): string | null { const nameEQ: string = `${name}=`; const allCookies: string[] = document.cookie.split(';'); @@ -14,20 +16,20 @@ export function setCookie(name: string, value: string, days: number): void { date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); const expires: string = `expires=${date.toUTCString()}`; let domain: string = ''; - if (!/localhost|127\.0\.0\.1/.test(window.location.hostname)) { + if (!isDesktop && !/localhost|127\.0\.0\.1/.test(window.location.hostname)) { domain = `domain=${window.location.hostname};`; } - const secure: string = 'Secure;'; + const secure: string = isDesktop ? '' : 'Secure;'; const sameSite: string = 'SameSite=Strict;'; document.cookie = `${name}=${value}; ${expires}; ${domain} path=/; ${secure} ${sameSite}`; } export function removeCookie(name: string): void { let domain: string = ''; - if (!/localhost|127\.0\.0\.1/.test(window.location.hostname)) { + if (!isDesktop && !/localhost|127\.0\.0\.1/.test(window.location.hostname)) { domain = `domain=${window.location.hostname};`; } - const secure: string = 'Secure;'; + const secure: string = isDesktop ? '' : 'Secure;'; const sameSite: string = 'SameSite=Strict;'; document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`; } diff --git a/src-tauri/src/domains/book/repo.rs b/src-tauri/src/domains/book/repo.rs index 0967972..19bc1ab 100644 --- a/src-tauri/src/domains/book/repo.rs +++ b/src-tauri/src/domains/book/repo.rs @@ -19,9 +19,9 @@ pub struct BookQuery { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct EritBooksTable { pub book_id: String, + #[serde(rename = "type")] pub book_type: String, pub author_id: String, pub title: String, @@ -47,7 +47,6 @@ pub struct SyncedBookResult { #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookToolsTable { pub book_id: String, pub user_id: String, diff --git a/src-tauri/src/domains/book/service.rs b/src-tauri/src/domains/book/service.rs index 61a625f..4904dbf 100644 --- a/src-tauri/src/domains/book/service.rs +++ b/src-tauri/src/domains/book/service.rs @@ -180,18 +180,16 @@ pub struct CompleteBook { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookActSummariesTable { - pub summary_id: String, + pub act_sum_id: String, pub book_id: String, pub user_id: String, - pub act_number: i64, - pub summary: String, + pub act_index: i64, pub last_update: i64, + pub summary: Option, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookAIGuideLineTable { pub user_id: String, pub book_id: String, @@ -208,40 +206,44 @@ pub struct BookAIGuideLineTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookChaptersTable { pub chapter_id: String, pub book_id: String, - pub user_id: String, + pub author_id: String, pub title: String, pub hashed_title: String, + pub words_count: Option, pub chapter_order: i64, pub last_update: i64, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookChapterContentTable { pub content_id: String, pub chapter_id: String, - pub user_id: String, - pub content: Option, + pub author_id: String, pub version: i64, + pub content: Option, + pub words_count: Option, + pub time_on_it: Option, pub last_update: i64, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookChapterInfosTable { + pub chapter_info_id: String, pub chapter_id: String, - pub user_id: String, + pub act_id: Option, + pub incident_id: Option, + pub plot_point_id: Option, + pub book_id: String, + pub author_id: String, pub summary: Option, - pub notes: Option, + pub goal: Option, pub last_update: i64, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookCharactersTable { pub character_id: String, pub book_id: String, @@ -249,13 +251,13 @@ pub struct BookCharactersTable { pub first_name: String, pub last_name: Option, pub nickname: Option, - pub age: Option, + pub age: Option, pub gender: Option, pub species: Option, pub nationality: Option, pub status: Option, - pub title: Option, pub category: String, + pub title: Option, pub image: Option, pub role: Option, pub biography: Option, @@ -269,7 +271,6 @@ pub struct BookCharactersTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookCharactersAttributesTable { pub attr_id: String, pub character_id: String, @@ -280,7 +281,6 @@ pub struct BookCharactersAttributesTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookGuideLineTable { pub user_id: String, pub book_id: String, @@ -298,33 +298,27 @@ pub struct BookGuideLineTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookIncidentsTable { pub incident_id: String, - pub chapter_id: String, - pub user_id: String, - pub name: String, - pub hashed_name: String, - pub description: Option, - pub incident_order: i64, + pub author_id: String, + pub book_id: String, + pub title: String, + pub hashed_title: String, + pub summary: Option, pub last_update: i64, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookIssuesTable { pub issue_id: String, - pub chapter_id: String, - pub user_id: String, + pub author_id: String, + pub book_id: String, pub name: String, - pub hashed_name: String, - pub description: Option, - pub issue_order: i64, + pub hashed_issue_name: String, pub last_update: i64, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookLocationTable { pub loc_id: String, pub book_id: String, @@ -335,26 +329,24 @@ pub struct BookLocationTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookPlotPointsTable { pub plot_point_id: String, - pub chapter_id: String, - pub user_id: String, - pub name: String, - pub hashed_name: String, - pub description: Option, - pub plot_point_order: i64, + pub title: String, + pub hashed_title: String, + pub summary: Option, + pub linked_incident_id: Option, + pub author_id: String, + pub book_id: String, pub last_update: i64, } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookWorldTable { pub world_id: String, - pub book_id: String, - pub user_id: String, pub name: String, pub hashed_name: String, + pub author_id: String, + pub book_id: String, pub history: Option, pub politics: Option, pub economy: Option, @@ -364,7 +356,6 @@ pub struct BookWorldTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookWorldElementsTable { pub element_id: String, pub world_id: String, @@ -377,10 +368,9 @@ pub struct BookWorldElementsTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct LocationElementTable { pub element_id: String, - pub location_id: String, + pub location: String, pub user_id: String, pub element_name: String, pub original_name: String, @@ -389,7 +379,6 @@ pub struct LocationElementTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct LocationSubElementTable { pub sub_element_id: String, pub element_id: String, @@ -401,16 +390,15 @@ pub struct LocationSubElementTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookSpellsTable { pub spell_id: String, pub book_id: String, pub user_id: String, pub name: String, pub name_hash: String, - pub description: String, - pub appearance: String, - pub tags: String, + pub description: Option, + pub appearance: Option, + pub tags: Option, pub power_level: Option, pub components: Option, pub limitations: Option, @@ -419,13 +407,12 @@ pub struct BookSpellsTable { } #[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] pub struct BookSpellTagsTable { pub tag_id: String, pub book_id: String, pub user_id: String, pub name: String, - pub hashed_name: String, + pub name_hash: String, pub color: Option, pub last_update: i64, } diff --git a/src-tauri/src/domains/download/service.rs b/src-tauri/src/domains/download/service.rs index 438b466..1705134 100644 --- a/src-tauri/src/domains/download/service.rs +++ b/src-tauri/src/domains/download/service.rs @@ -49,29 +49,32 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook, } for incident in &data.incidents { - let encrypted_incident_title: String = encrypt_data_with_user_key(&incident.name, &user_encryption_key)?; - let encrypted_incident_summary: Option = if let Some(ref description) = incident.description { Some(encrypt_data_with_user_key(description, &user_encryption_key)?) } else { None }; - let incident_inserted: bool = incident_repo::insert_sync_incident(conn, &incident.incident_id, user_id, &incident.chapter_id, &encrypted_incident_title, &incident.hashed_name, encrypted_incident_summary.as_deref(), incident.last_update, lang)?; + let encrypted_incident_title: String = encrypt_data_with_user_key(&incident.title, &user_encryption_key)?; + let encrypted_incident_summary: Option = if let Some(ref summary) = incident.summary { Some(encrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; + let incident_inserted: bool = incident_repo::insert_sync_incident(conn, &incident.incident_id, user_id, &incident.book_id, &encrypted_incident_title, &incident.hashed_title, encrypted_incident_summary.as_deref(), incident.last_update, lang)?; if !incident_inserted { return Ok(false); } } for plot_point in &data.plot_points { - let encrypted_plot_point_title: String = encrypt_data_with_user_key(&plot_point.name, &user_encryption_key)?; - let encrypted_plot_point_summary: Option = if let Some(ref description) = plot_point.description { Some(encrypt_data_with_user_key(description, &user_encryption_key)?) } else { None }; - let plot_point_inserted: bool = plotpoint_repo::insert_sync_plot_point(conn, &plot_point.plot_point_id, &encrypted_plot_point_title, &plot_point.hashed_name, encrypted_plot_point_summary.as_deref(), None, user_id, &plot_point.chapter_id, plot_point.last_update, lang)?; + let encrypted_plot_point_title: String = encrypt_data_with_user_key(&plot_point.title, &user_encryption_key)?; + let encrypted_plot_point_summary: Option = if let Some(ref summary) = plot_point.summary { Some(encrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; + let plot_point_inserted: bool = plotpoint_repo::insert_sync_plot_point(conn, &plot_point.plot_point_id, &encrypted_plot_point_title, &plot_point.hashed_title, encrypted_plot_point_summary.as_deref(), plot_point.linked_incident_id.as_deref(), user_id, &plot_point.book_id, plot_point.last_update, lang)?; if !plot_point_inserted { return Ok(false); } } for chapter_content in &data.chapter_contents { - let encrypted_chapter_content: Option = if let Some(ref content) = chapter_content.content { Some(encrypt_data_with_user_key(content, &user_encryption_key)?) } else { None }; - let chapter_content_inserted: bool = chapter_content_repo::insert_sync_chapter_content(conn, &chapter_content.content_id, &chapter_content.chapter_id, user_id, chapter_content.version, encrypted_chapter_content.as_deref(), 0, 0, chapter_content.last_update, lang)?; + let content_str: Option = chapter_content.content.as_ref().map(|v| serde_json::to_string(v).unwrap_or_default()); + let encrypted_chapter_content: Option = if let Some(ref content) = content_str { Some(encrypt_data_with_user_key(content, &user_encryption_key)?) } else { None }; + let words_count: i64 = chapter_content.words_count.unwrap_or(0); + let time_on_it: i64 = chapter_content.time_on_it.unwrap_or(0); + let chapter_content_inserted: bool = chapter_content_repo::insert_sync_chapter_content(conn, &chapter_content.content_id, &chapter_content.chapter_id, user_id, chapter_content.version, encrypted_chapter_content.as_deref(), words_count, time_on_it, chapter_content.last_update, lang)?; if !chapter_content_inserted { return Ok(false); } } for chapter_info in &data.chapter_infos { let encrypted_chapter_summary: Option = if let Some(ref summary) = chapter_info.summary { Some(encrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; - let encrypted_chapter_goal: Option = if let Some(ref notes) = chapter_info.notes { Some(encrypt_data_with_user_key(notes, &user_encryption_key)?) } else { None }; - let chapter_info_inserted: bool = chapter_repo::insert_sync_chapter_info(conn, &chapter_info.chapter_id, &chapter_info.chapter_id, None, None, None, &chapter_info.chapter_id, user_id, encrypted_chapter_summary.as_deref(), encrypted_chapter_goal.as_deref(), chapter_info.last_update, lang)?; + let encrypted_chapter_goal: Option = if let Some(ref goal) = chapter_info.goal { Some(encrypt_data_with_user_key(goal, &user_encryption_key)?) } else { None }; + let chapter_info_inserted: bool = chapter_repo::insert_sync_chapter_info(conn, &chapter_info.chapter_info_id, &chapter_info.chapter_id, chapter_info.act_id, chapter_info.incident_id.as_deref(), chapter_info.plot_point_id.as_deref(), &chapter_info.book_id, user_id, encrypted_chapter_summary.as_deref(), encrypted_chapter_goal.as_deref(), chapter_info.last_update, lang)?; if !chapter_info_inserted { return Ok(false); } } @@ -80,7 +83,7 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook, first_name: encrypt_data_with_user_key(&character.first_name, &user_encryption_key)?, last_name: if let Some(ref last_name) = character.last_name { Some(encrypt_data_with_user_key(last_name, &user_encryption_key)?) } else { None }, nickname: if let Some(ref nickname) = character.nickname { Some(encrypt_data_with_user_key(nickname, &user_encryption_key)?) } else { None }, - age: if let Some(ref age) = character.age { Some(encrypt_data_with_user_key(&age.to_string(), &user_encryption_key)?) } else { None }, + age: if let Some(ref age) = character.age { Some(encrypt_data_with_user_key(age, &user_encryption_key)?) } else { None }, gender: if let Some(ref gender) = character.gender { Some(encrypt_data_with_user_key(gender, &user_encryption_key)?) } else { None }, species: if let Some(ref species) = character.species { Some(encrypt_data_with_user_key(species, &user_encryption_key)?) } else { None }, nationality: if let Some(ref nationality) = character.nationality { Some(encrypt_data_with_user_key(nationality, &user_encryption_key)?) } else { None }, @@ -117,7 +120,7 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook, for location_element in &data.location_elements { let encrypted_location_element_name: String = encrypt_data_with_user_key(&location_element.element_name, &user_encryption_key)?; let encrypted_location_element_description: Option = if let Some(ref element_description) = location_element.element_description { Some(encrypt_data_with_user_key(element_description, &user_encryption_key)?) } else { None }; - let location_element_inserted: bool = location_repo::insert_sync_location_element(conn, &location_element.element_id, &location_element.location_id, user_id, &encrypted_location_element_name, &location_element.original_name, encrypted_location_element_description.as_deref(), location_element.last_update, lang)?; + let location_element_inserted: bool = location_repo::insert_sync_location_element(conn, &location_element.element_id, &location_element.location, user_id, &encrypted_location_element_name, &location_element.original_name, encrypted_location_element_description.as_deref(), location_element.last_update, lang)?; if !location_element_inserted { return Ok(false); } } @@ -147,8 +150,8 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook, } for act_summary in &data.act_summaries { - let encrypted_act_summary: String = encrypt_data_with_user_key(&act_summary.summary, &user_encryption_key)?; - let act_summary_inserted: bool = act_repo::insert_sync_act_summary(conn, &act_summary.summary_id, &act_summary.book_id, user_id, act_summary.act_number, Some(&encrypted_act_summary), act_summary.last_update, lang)?; + let encrypted_act_summary: Option = if let Some(ref summary) = act_summary.summary { Some(encrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; + let act_summary_inserted: bool = act_repo::insert_sync_act_summary(conn, &act_summary.act_sum_id, &act_summary.book_id, user_id, act_summary.act_index, encrypted_act_summary.as_deref(), act_summary.last_update, lang)?; if !act_summary_inserted { return Ok(false); } } @@ -179,7 +182,7 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook, for issue in &data.issues { let encrypted_issue_name: String = encrypt_data_with_user_key(&issue.name, &user_encryption_key)?; - let issue_inserted: bool = issue_repo::insert_sync_issue(conn, &issue.issue_id, user_id, &issue.chapter_id, &encrypted_issue_name, &issue.hashed_name, issue.last_update, lang)?; + let issue_inserted: bool = issue_repo::insert_sync_issue(conn, &issue.issue_id, user_id, &issue.book_id, &encrypted_issue_name, &issue.hashed_issue_name, issue.last_update, lang)?; if !issue_inserted { return Ok(false); } } @@ -190,20 +193,20 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook, for spell_tag in &data.spell_tags { let encrypted_tag_name: String = encrypt_data_with_user_key(&spell_tag.name, &user_encryption_key)?; - let spell_tag_inserted: bool = spell_tag_repo::insert_sync_spell_tag(conn, &spell_tag.tag_id, &spell_tag.book_id, user_id, &encrypted_tag_name, &spell_tag.hashed_name, spell_tag.color.as_deref(), spell_tag.last_update, lang)?; + let spell_tag_inserted: bool = spell_tag_repo::insert_sync_spell_tag(conn, &spell_tag.tag_id, &spell_tag.book_id, user_id, &encrypted_tag_name, &spell_tag.name_hash, spell_tag.color.as_deref(), spell_tag.last_update, lang)?; if !spell_tag_inserted { return Ok(false); } } for spell in &data.spells { let encrypted_name: String = encrypt_data_with_user_key(&spell.name, &user_encryption_key)?; - let encrypted_description: String = encrypt_data_with_user_key(&spell.description, &user_encryption_key)?; - let encrypted_appearance: String = encrypt_data_with_user_key(&spell.appearance, &user_encryption_key)?; - let encrypted_tags: String = encrypt_data_with_user_key(&spell.tags, &user_encryption_key)?; + let encrypted_description: Option = if let Some(ref description) = spell.description { Some(encrypt_data_with_user_key(description, &user_encryption_key)?) } else { None }; + let encrypted_appearance: Option = if let Some(ref appearance) = spell.appearance { Some(encrypt_data_with_user_key(appearance, &user_encryption_key)?) } else { None }; + let encrypted_tags: Option = if let Some(ref tags) = spell.tags { Some(encrypt_data_with_user_key(tags, &user_encryption_key)?) } else { None }; let encrypted_power_level: Option = if let Some(ref power_level) = spell.power_level { Some(encrypt_data_with_user_key(power_level, &user_encryption_key)?) } else { None }; let encrypted_components: Option = if let Some(ref components) = spell.components { Some(encrypt_data_with_user_key(components, &user_encryption_key)?) } else { None }; let encrypted_limitations: Option = if let Some(ref limitations) = spell.limitations { Some(encrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None }; let encrypted_notes: Option = if let Some(ref notes) = spell.notes { Some(encrypt_data_with_user_key(notes, &user_encryption_key)?) } else { None }; - let spell_inserted: bool = spell_repo::insert_sync_spell(conn, &spell.spell_id, &spell.book_id, user_id, &encrypted_name, &spell.name_hash, Some(&encrypted_description), Some(&encrypted_appearance), Some(&encrypted_tags), encrypted_power_level.as_deref(), encrypted_components.as_deref(), encrypted_limitations.as_deref(), encrypted_notes.as_deref(), spell.last_update, lang)?; + let spell_inserted: bool = spell_repo::insert_sync_spell(conn, &spell.spell_id, &spell.book_id, user_id, &encrypted_name, &spell.name_hash, encrypted_description.as_deref(), encrypted_appearance.as_deref(), encrypted_tags.as_deref(), encrypted_power_level.as_deref(), encrypted_components.as_deref(), encrypted_limitations.as_deref(), encrypted_notes.as_deref(), spell.last_update, lang)?; if !spell_inserted { return Ok(false); } } diff --git a/src-tauri/src/domains/sync/service.rs b/src-tauri/src/domains/sync/service.rs index 0cd5eb0..c2ec061 100644 --- a/src-tauri/src/domains/sync/service.rs +++ b/src-tauri/src/domains/sync/service.rs @@ -195,11 +195,11 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat if !act_summary_results.is_empty() { let act_summary_record = &act_summary_results[0]; decrypted_act_summaries.push(BookActSummariesTable { - summary_id: act_summary_record.act_sum_id.clone(), + act_sum_id: act_summary_record.act_sum_id.clone(), book_id: act_summary_record.book_id.clone(), user_id: act_summary_record.user_id.clone(), - act_number: act_summary_record.act_index, - summary: if let Some(ref summary) = act_summary_record.summary { if summary.is_empty() { String::new() } else { decrypt_data_with_user_key(summary, &user_encryption_key)? } } else { String::new() }, + act_index: act_summary_record.act_index, + summary: if let Some(ref summary) = act_summary_record.summary { if summary.is_empty() { None } else { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } } else { None }, last_update: act_summary_record.last_update, }); } @@ -214,9 +214,10 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat decrypted_chapters.push(BookChaptersTable { chapter_id: chapter_record.chapter_id.clone(), book_id: chapter_record.book_id.clone(), - user_id: chapter_record.author_id.clone(), + author_id: chapter_record.author_id.clone(), title: decrypt_data_with_user_key(&chapter_record.title, &user_encryption_key)?, hashed_title: chapter_record.hashed_title.clone(), + words_count: chapter_record._words_count, chapter_order: chapter_record.chapter_order, last_update: chapter_record.last_update, }); @@ -231,12 +232,12 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat let plot_point_record = &plot_point_results[0]; decrypted_plot_points.push(BookPlotPointsTable { plot_point_id: plot_point_record.plot_point_id.clone(), - chapter_id: plot_point_record.linked_incident_id.clone().unwrap_or_default(), - user_id: plot_point_record.author_id.clone(), - name: decrypt_data_with_user_key(&plot_point_record.title, &user_encryption_key)?, - hashed_name: plot_point_record.hashed_title.clone(), - description: if let Some(ref summary) = plot_point_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }, - plot_point_order: 0, + title: decrypt_data_with_user_key(&plot_point_record.title, &user_encryption_key)?, + hashed_title: plot_point_record.hashed_title.clone(), + summary: if let Some(ref summary) = plot_point_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }, + linked_incident_id: plot_point_record.linked_incident_id.clone(), + author_id: plot_point_record.author_id.clone(), + book_id: plot_point_record._book_id.clone(), last_update: plot_point_record.last_update, }); } @@ -250,12 +251,11 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat let incident_record = &incident_results[0]; decrypted_incidents.push(BookIncidentsTable { incident_id: incident_record.incident_id.clone(), - chapter_id: incident_record.book_id.clone(), - user_id: incident_record.author_id.clone(), - name: decrypt_data_with_user_key(&incident_record.title, &user_encryption_key)?, - hashed_name: incident_record.hashed_title.clone(), - description: if let Some(ref summary) = incident_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }, - incident_order: 0, + author_id: incident_record.author_id.clone(), + book_id: incident_record.book_id.clone(), + title: decrypt_data_with_user_key(&incident_record.title, &user_encryption_key)?, + hashed_title: incident_record.hashed_title.clone(), + summary: if let Some(ref summary) = incident_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }, last_update: incident_record.last_update, }); } @@ -267,12 +267,18 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat let chapter_content_results: Vec = chapter_content_repo::fetch_complete_chapter_content_by_id(conn, chapter_content_id, lang)?; if !chapter_content_results.is_empty() { let chapter_content_record = &chapter_content_results[0]; + let decrypted_content: Option = if let Some(ref content) = chapter_content_record.content { + let decrypted: String = decrypt_data_with_user_key(content, &user_encryption_key)?; + serde_json::from_str(&decrypted).ok() + } else { None }; decrypted_chapter_contents.push(BookChapterContentTable { content_id: chapter_content_record.content_id.clone(), chapter_id: chapter_content_record.chapter_id.clone(), - user_id: chapter_content_record.author_id.clone(), - content: if let Some(ref content) = chapter_content_record.content { Some(decrypt_data_with_user_key(content, &user_encryption_key)?) } else { None }, + author_id: chapter_content_record.author_id.clone(), version: chapter_content_record.version, + content: decrypted_content, + words_count: Some(chapter_content_record._words_count), + time_on_it: Some(chapter_content_record._time_on_it), last_update: chapter_content_record.last_update, }); } @@ -285,10 +291,15 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat if !chapter_info_results.is_empty() { let chapter_info_record = &chapter_info_results[0]; decrypted_chapter_infos.push(BookChapterInfosTable { + chapter_info_id: chapter_info_record._chapter_info_id.clone(), chapter_id: chapter_info_record.chapter_id.clone(), - user_id: chapter_info_record.author_id.clone(), + act_id: chapter_info_record._act_id, + incident_id: chapter_info_record._incident_id.clone(), + plot_point_id: chapter_info_record._plot_point_id.clone(), + book_id: chapter_info_record._book_id.clone(), + author_id: chapter_info_record.author_id.clone(), summary: if let Some(ref summary) = chapter_info_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }, - notes: if let Some(ref goal) = chapter_info_record.goal { Some(decrypt_data_with_user_key(goal, &user_encryption_key)?) } else { None }, + goal: if let Some(ref goal) = chapter_info_record.goal { Some(decrypt_data_with_user_key(goal, &user_encryption_key)?) } else { None }, last_update: chapter_info_record.last_update, }); } @@ -307,7 +318,7 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat first_name: decrypt_data_with_user_key(&character_record.first_name, &user_encryption_key)?, last_name: if let Some(ref last_name) = character_record.last_name { Some(decrypt_data_with_user_key(last_name, &user_encryption_key)?) } else { None }, nickname: if let Some(ref nickname) = character_record.nickname { Some(decrypt_data_with_user_key(nickname, &user_encryption_key)?) } else { None }, - age: if let Some(ref age) = character_record.age { Some(decrypt_data_with_user_key(age, &user_encryption_key)?.parse().unwrap_or(0)) } else { None }, + age: if let Some(ref age) = character_record.age { Some(decrypt_data_with_user_key(age, &user_encryption_key)?) } else { None }, gender: if let Some(ref gender) = character_record.gender { Some(decrypt_data_with_user_key(gender, &user_encryption_key)?) } else { None }, species: if let Some(ref species) = character_record.species { Some(decrypt_data_with_user_key(species, &user_encryption_key)?) } else { None }, nationality: if let Some(ref nationality) = character_record.nationality { Some(decrypt_data_with_user_key(nationality, &user_encryption_key)?) } else { None }, @@ -370,7 +381,7 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat let location_element_record = &location_element_results[0]; decrypted_location_elements.push(LocationElementTable { element_id: location_element_record.element_id.clone(), - location_id: location_element_record.location.clone(), + location: location_element_record.location.clone(), user_id: location_element_record.user_id.clone(), element_name: decrypt_data_with_user_key(&location_element_record.element_name, &user_encryption_key)?, original_name: location_element_record.original_name.clone(), @@ -406,10 +417,10 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat let world_record = &world_results[0]; decrypted_worlds.push(BookWorldTable { world_id: world_record.world_id.clone(), - book_id: world_record.book_id.clone(), - user_id: world_record.author_id.clone(), name: decrypt_data_with_user_key(&world_record.name, &user_encryption_key)?, hashed_name: world_record.hashed_name.clone(), + author_id: world_record.author_id.clone(), + book_id: world_record.book_id.clone(), history: if let Some(ref history) = world_record.history { Some(decrypt_data_with_user_key(history, &user_encryption_key)?) } else { None }, politics: if let Some(ref politics) = world_record.politics { Some(decrypt_data_with_user_key(politics, &user_encryption_key)?) } else { None }, economy: if let Some(ref economy) = world_record.economy { Some(decrypt_data_with_user_key(economy, &user_encryption_key)?) } else { None }, @@ -447,12 +458,10 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat let issue_record = &issue_results[0]; decrypted_issues.push(BookIssuesTable { issue_id: issue_record.issue_id.clone(), - chapter_id: issue_record.book_id.clone(), - user_id: issue_record.author_id.clone(), + author_id: issue_record.author_id.clone(), + book_id: issue_record.book_id.clone(), name: decrypt_data_with_user_key(&issue_record.name, &user_encryption_key)?, - hashed_name: issue_record.hashed_issue_name.clone(), - description: None, - issue_order: 0, + hashed_issue_name: issue_record.hashed_issue_name.clone(), last_update: issue_record.last_update, }); } @@ -511,7 +520,7 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat book_id: spell_tag_record.book_id, user_id: spell_tag_record.user_id, name: decrypt_data_with_user_key(&spell_tag_record.name, &user_encryption_key)?, - hashed_name: spell_tag_record.name_hash, + name_hash: spell_tag_record.name_hash, color: spell_tag_record.color, last_update: spell_tag_record.last_update, }); @@ -529,9 +538,9 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat user_id: spell_record.user_id, name: decrypt_data_with_user_key(&spell_record.name, &user_encryption_key)?, name_hash: spell_record.name_hash, - description: if let Some(ref description) = spell_record.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() }, - appearance: if let Some(ref appearance) = spell_record.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() }, - tags: if let Some(ref tags) = spell_record.tags { if tags.is_empty() { String::new() } else { decrypt_data_with_user_key(tags, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() }, + description: if let Some(ref description) = spell_record.description { if description.is_empty() { None } else { Some(decrypt_data_with_user_key(description, &user_encryption_key)?) } } else { None }, + appearance: if let Some(ref appearance) = spell_record.appearance { if appearance.is_empty() { None } else { Some(decrypt_data_with_user_key(appearance, &user_encryption_key)?) } } else { None }, + tags: if let Some(ref tags) = spell_record.tags { if tags.is_empty() { None } else { Some(decrypt_data_with_user_key(tags, &user_encryption_key)?) } } else { None }, power_level: if let Some(ref power_level) = spell_record.power_level { if power_level.is_empty() { None } else { Some(decrypt_data_with_user_key(power_level, &user_encryption_key)?) } } else { None }, components: if let Some(ref components) = spell_record.components { if components.is_empty() { None } else { Some(decrypt_data_with_user_key(components, &user_encryption_key)?) } } else { None }, limitations: if let Some(ref limitations) = spell_record.limitations { if limitations.is_empty() { None } else { Some(decrypt_data_with_user_key(limitations, &user_encryption_key)?) } } else { None }, @@ -635,28 +644,27 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet if !server_act_summaries.is_empty() { for server_act_summary in server_act_summaries { - let act_summary_exists: bool = act_repo::act_summarize_exist(conn, user_id, &book_id, server_act_summary.act_number, lang)?; - let encrypted_summary: String = encrypt_data_with_user_key(if server_act_summary.summary.is_empty() { "" } else { &server_act_summary.summary }, &user_encryption_key)?; + let act_summary_exists: bool = act_repo::act_summarize_exist(conn, user_id, &book_id, server_act_summary.act_index, lang)?; + let encrypted_summary: String = encrypt_data_with_user_key(if let Some(ref s) = server_act_summary.summary { s } else { "" }, &user_encryption_key)?; if act_summary_exists { - let update_successful: bool = act_repo::update_act_summary(conn, user_id, &book_id, server_act_summary.act_number, &encrypted_summary, server_act_summary.last_update, lang)?; + let update_successful: bool = act_repo::update_act_summary(conn, user_id, &book_id, server_act_summary.act_index, &encrypted_summary, server_act_summary.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let _insert_result: String = act_repo::insert_act_summary(conn, &server_act_summary.summary_id, user_id, &book_id, server_act_summary.act_number, &encrypted_summary, server_act_summary.last_update, lang)?; + let _insert_result: String = act_repo::insert_act_summary(conn, &server_act_summary.act_sum_id, user_id, &book_id, server_act_summary.act_index, &encrypted_summary, server_act_summary.last_update, lang)?; } } } if !server_plot_points.is_empty() { for server_plot_point in server_plot_points { - let encrypted_title: String = encrypt_data_with_user_key(&server_plot_point.name, &user_encryption_key)?; - let encrypted_summary: String = encrypt_data_with_user_key(if let Some(ref description) = server_plot_point.description { description } else { "" }, &user_encryption_key)?; + let encrypted_title: String = encrypt_data_with_user_key(&server_plot_point.title, &user_encryption_key)?; + let encrypted_summary: String = encrypt_data_with_user_key(if let Some(ref summary) = server_plot_point.summary { summary } else { "" }, &user_encryption_key)?; let plot_point_exists: bool = plotpoint_repo::plot_point_exist(conn, user_id, &book_id, &server_plot_point.plot_point_id, lang)?; if plot_point_exists { - let update_successful: bool = plotpoint_repo::update_plot_point(conn, user_id, &book_id, &server_plot_point.plot_point_id, &encrypted_title, &server_plot_point.hashed_name, &encrypted_summary, server_plot_point.last_update, lang)?; + let update_successful: bool = plotpoint_repo::update_plot_point(conn, user_id, &book_id, &server_plot_point.plot_point_id, &encrypted_title, &server_plot_point.hashed_title, &encrypted_summary, server_plot_point.last_update, lang)?; if !update_successful { return Ok(false); } } else { - if server_plot_point.chapter_id.is_empty() { return Ok(false); } - let insert_successful: bool = plotpoint_repo::insert_sync_plot_point(conn, &server_plot_point.plot_point_id, &encrypted_title, &server_plot_point.hashed_name, Some(&encrypted_summary), Some(&server_plot_point.chapter_id), user_id, &book_id, server_plot_point.last_update, lang)?; + let insert_successful: bool = plotpoint_repo::insert_sync_plot_point(conn, &server_plot_point.plot_point_id, &encrypted_title, &server_plot_point.hashed_title, Some(&encrypted_summary), server_plot_point.linked_incident_id.as_deref(), user_id, &server_plot_point.book_id, server_plot_point.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -664,14 +672,14 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet if !server_incidents.is_empty() { for server_incident in server_incidents { - let encrypted_title: String = encrypt_data_with_user_key(&server_incident.name, &user_encryption_key)?; - let encrypted_summary: String = encrypt_data_with_user_key(if let Some(ref description) = server_incident.description { description } else { "" }, &user_encryption_key)?; + let encrypted_title: String = encrypt_data_with_user_key(&server_incident.title, &user_encryption_key)?; + let encrypted_summary: String = encrypt_data_with_user_key(if let Some(ref summary) = server_incident.summary { summary } else { "" }, &user_encryption_key)?; let incident_exists: bool = incident_repo::incident_exist(conn, user_id, &book_id, &server_incident.incident_id, lang)?; if incident_exists { - let update_successful: bool = incident_repo::update_incident(conn, user_id, &book_id, &server_incident.incident_id, &encrypted_title, &server_incident.hashed_name, &encrypted_summary, server_incident.last_update, lang)?; + let update_successful: bool = incident_repo::update_incident(conn, user_id, &book_id, &server_incident.incident_id, &encrypted_title, &server_incident.hashed_title, &encrypted_summary, server_incident.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let insert_successful: bool = incident_repo::insert_sync_incident(conn, &server_incident.incident_id, user_id, &book_id, &encrypted_title, &server_incident.hashed_name, Some(&encrypted_summary), server_incident.last_update, lang)?; + let insert_successful: bool = incident_repo::insert_sync_incident(conn, &server_incident.incident_id, user_id, &server_incident.book_id, &encrypted_title, &server_incident.hashed_title, Some(&encrypted_summary), server_incident.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -680,12 +688,15 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet if !server_chapter_contents.is_empty() { for server_chapter_content in server_chapter_contents { let chapter_content_exists: bool = chapter_content_repo::is_chapter_content_exist(conn, user_id, &server_chapter_content.content_id, lang)?; - let encrypted_content: String = encrypt_data_with_user_key(if let Some(ref content) = server_chapter_content.content { content } else { "" }, &user_encryption_key)?; + let content_str: String = server_chapter_content.content.as_ref().map(|v| serde_json::to_string(v).unwrap_or_default()).unwrap_or_default(); + let encrypted_content: String = encrypt_data_with_user_key(&content_str, &user_encryption_key)?; + let words_count: i64 = server_chapter_content.words_count.unwrap_or(0); + let time_on_it: i64 = server_chapter_content.time_on_it.unwrap_or(0); if chapter_content_exists { - let update_successful: bool = chapter_content_repo::update_chapter_content(conn, user_id, &server_chapter_content.chapter_id, server_chapter_content.version, &encrypted_content, 0, server_chapter_content.last_update, lang)?; + let update_successful: bool = chapter_content_repo::update_chapter_content(conn, user_id, &server_chapter_content.chapter_id, server_chapter_content.version, &encrypted_content, words_count, server_chapter_content.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let insert_successful: bool = chapter_content_repo::insert_sync_chapter_content(conn, &server_chapter_content.content_id, &server_chapter_content.chapter_id, user_id, server_chapter_content.version, Some(&encrypted_content), 0, 0, server_chapter_content.last_update, lang)?; + let insert_successful: bool = chapter_content_repo::insert_sync_chapter_content(conn, &server_chapter_content.content_id, &server_chapter_content.chapter_id, user_id, server_chapter_content.version, Some(&encrypted_content), words_count, time_on_it, server_chapter_content.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -695,12 +706,12 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet for server_chapter_info in server_chapter_infos { let chapter_info_exists: bool = chapter_repo::is_chapter_info_exist(conn, user_id, &server_chapter_info.chapter_id, lang); let encrypted_summary: String = encrypt_data_with_user_key(if let Some(ref summary) = server_chapter_info.summary { summary } else { "" }, &user_encryption_key)?; - let encrypted_goal: String = encrypt_data_with_user_key(if let Some(ref notes) = server_chapter_info.notes { notes } else { "" }, &user_encryption_key)?; + let encrypted_goal: String = encrypt_data_with_user_key(if let Some(ref goal) = server_chapter_info.goal { goal } else { "" }, &user_encryption_key)?; if chapter_info_exists { - let update_successful: bool = chapter_repo::update_chapter_infos(conn, user_id, &server_chapter_info.chapter_id, 0, &book_id, None, None, &encrypted_summary, Some(&encrypted_goal), server_chapter_info.last_update, lang)?; + let update_successful: bool = chapter_repo::update_chapter_infos(conn, user_id, &server_chapter_info.chapter_id, server_chapter_info.act_id.unwrap_or(0), &server_chapter_info.book_id, server_chapter_info.incident_id.as_deref(), server_chapter_info.plot_point_id.as_deref(), &encrypted_summary, Some(&encrypted_goal), server_chapter_info.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let insert_successful: bool = chapter_repo::insert_sync_chapter_info(conn, "", &server_chapter_info.chapter_id, Some(0), None, None, &book_id, user_id, Some(&encrypted_summary), Some(&encrypted_goal), server_chapter_info.last_update, lang)?; + let insert_successful: bool = chapter_repo::insert_sync_chapter_info(conn, &server_chapter_info.chapter_info_id, &server_chapter_info.chapter_id, server_chapter_info.act_id, server_chapter_info.incident_id.as_deref(), server_chapter_info.plot_point_id.as_deref(), &server_chapter_info.book_id, user_id, Some(&encrypted_summary), Some(&encrypted_goal), server_chapter_info.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -713,7 +724,7 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet first_name: encrypt_data_with_user_key(&server_character.first_name, &user_encryption_key)?, last_name: Some(encrypt_data_with_user_key(if let Some(ref last_name) = server_character.last_name { last_name } else { "" }, &user_encryption_key)?), nickname: Some(encrypt_data_with_user_key(if let Some(ref nickname) = server_character.nickname { nickname } else { "" }, &user_encryption_key)?), - age: Some(encrypt_data_with_user_key(&server_character.age.map_or(String::new(), |age| age.to_string()), &user_encryption_key)?), + age: Some(encrypt_data_with_user_key(if let Some(ref age) = server_character.age { age } else { "" }, &user_encryption_key)?), gender: Some(encrypt_data_with_user_key(if let Some(ref gender) = server_character.gender { gender } else { "" }, &user_encryption_key)?), species: Some(encrypt_data_with_user_key(if let Some(ref species) = server_character.species { species } else { "" }, &user_encryption_key)?), nationality: Some(encrypt_data_with_user_key(if let Some(ref nationality) = server_character.nationality { nationality } else { "" }, &user_encryption_key)?), @@ -778,7 +789,7 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet let update_successful: bool = location_repo::update_location_element(conn, user_id, &server_location_element.element_id, &encrypted_element_name, &server_location_element.original_name, &encrypted_element_description, server_location_element.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let insert_successful: bool = location_repo::insert_sync_location_element(conn, &server_location_element.element_id, &server_location_element.location_id, user_id, &encrypted_element_name, &server_location_element.original_name, Some(&encrypted_element_description), server_location_element.last_update, lang)?; + let insert_successful: bool = location_repo::insert_sync_location_element(conn, &server_location_element.element_id, &server_location_element.location, user_id, &encrypted_element_name, &server_location_element.original_name, Some(&encrypted_element_description), server_location_element.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -838,10 +849,10 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet let issue_exists: bool = issue_repo::issue_exist(conn, user_id, &book_id, &server_issue.issue_id, lang)?; let encrypted_name: String = encrypt_data_with_user_key(&server_issue.name, &user_encryption_key)?; if issue_exists { - let update_successful: bool = issue_repo::update_issue(conn, user_id, &book_id, &server_issue.issue_id, &encrypted_name, &server_issue.hashed_name, server_issue.last_update, lang)?; + let update_successful: bool = issue_repo::update_issue(conn, user_id, &book_id, &server_issue.issue_id, &encrypted_name, &server_issue.hashed_issue_name, server_issue.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let insert_successful: bool = issue_repo::insert_sync_issue(conn, &server_issue.issue_id, user_id, &book_id, &encrypted_name, &server_issue.hashed_name, server_issue.last_update, lang)?; + let insert_successful: bool = issue_repo::insert_sync_issue(conn, &server_issue.issue_id, user_id, &server_issue.book_id, &encrypted_name, &server_issue.hashed_issue_name, server_issue.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -900,10 +911,10 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet let spell_tag_exists: bool = spell_tag_repo::is_spell_tag_exist(conn, user_id, &server_spell_tag.tag_id, lang)?; let encrypted_name: String = encrypt_data_with_user_key(&server_spell_tag.name, &user_encryption_key)?; if spell_tag_exists { - let update_successful: bool = spell_tag_repo::update_sync_spell_tag(conn, user_id, &server_spell_tag.tag_id, &encrypted_name, &server_spell_tag.hashed_name, server_spell_tag.color.as_deref(), server_spell_tag.last_update, lang)?; + let update_successful: bool = spell_tag_repo::update_sync_spell_tag(conn, user_id, &server_spell_tag.tag_id, &encrypted_name, &server_spell_tag.name_hash, server_spell_tag.color.as_deref(), server_spell_tag.last_update, lang)?; if !update_successful { return Ok(false); } } else { - let insert_successful: bool = spell_tag_repo::insert_sync_spell_tag(conn, &server_spell_tag.tag_id, &server_spell_tag.book_id, user_id, &encrypted_name, &server_spell_tag.hashed_name, server_spell_tag.color.as_deref(), server_spell_tag.last_update, lang)?; + let insert_successful: bool = spell_tag_repo::insert_sync_spell_tag(conn, &server_spell_tag.tag_id, &server_spell_tag.book_id, user_id, &encrypted_name, &server_spell_tag.name_hash, server_spell_tag.color.as_deref(), server_spell_tag.last_update, lang)?; if !insert_successful { return Ok(false); } } } @@ -913,9 +924,9 @@ pub fn sync_book_from_server_to_client(conn: &Connection, user_id: &str, complet for server_spell in &complete_book.spells { let spell_exists: bool = spell_repo::is_spell_exist(conn, user_id, &server_spell.spell_id, lang)?; let encrypted_name: String = encrypt_data_with_user_key(&server_spell.name, &user_encryption_key)?; - let encrypted_description: Option = if server_spell.description.is_empty() { None } else { Some(encrypt_data_with_user_key(&server_spell.description, &user_encryption_key)?) }; - let encrypted_appearance: Option = if server_spell.appearance.is_empty() { None } else { Some(encrypt_data_with_user_key(&server_spell.appearance, &user_encryption_key)?) }; - let encrypted_tags: Option = if server_spell.tags.is_empty() { None } else { Some(encrypt_data_with_user_key(&server_spell.tags, &user_encryption_key)?) }; + let encrypted_description: Option = if let Some(ref desc) = server_spell.description { Some(encrypt_data_with_user_key(desc, &user_encryption_key)?) } else { None }; + let encrypted_appearance: Option = if let Some(ref app) = server_spell.appearance { Some(encrypt_data_with_user_key(app, &user_encryption_key)?) } else { None }; + let encrypted_tags: Option = if let Some(ref tags) = server_spell.tags { Some(encrypt_data_with_user_key(tags, &user_encryption_key)?) } else { None }; let encrypted_power_level: Option = if let Some(ref power_level) = server_spell.power_level { Some(encrypt_data_with_user_key(power_level, &user_encryption_key)?) } else { None }; let encrypted_components: Option = if let Some(ref components) = server_spell.components { Some(encrypt_data_with_user_key(components, &user_encryption_key)?) } else { None }; let encrypted_limitations: Option = if let Some(ref limitations) = server_spell.limitations { Some(encrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None }; diff --git a/src-tauri/src/domains/upload/service.rs b/src-tauri/src/domains/upload/service.rs index edffca0..b2ec7f7 100644 --- a/src-tauri/src/domains/upload/service.rs +++ b/src-tauri/src/domains/upload/service.rs @@ -102,9 +102,10 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan for act_summary in encrypted_act_summaries { let decrypted_summary: String = if let Some(ref summary) = act_summary.summary { if summary.is_empty() { String::new() } else { decrypt_data_with_user_key(summary, &user_encryption_key)? } } else { String::new() }; act_summaries.push(BookActSummariesTable { - summary_id: act_summary.act_sum_id, book_id: act_summary.book_id, - user_id: act_summary.user_id, act_number: act_summary.act_index, - summary: decrypted_summary, last_update: act_summary.last_update, + act_sum_id: act_summary.act_sum_id, book_id: act_summary.book_id, + user_id: act_summary.user_id, act_index: act_summary.act_index, + summary: if decrypted_summary.is_empty() { None } else { Some(decrypted_summary) }, + last_update: act_summary.last_update, }); } @@ -130,19 +131,23 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_title: String = decrypt_data_with_user_key(&chapter.title, &user_encryption_key)?; chapters.push(BookChaptersTable { chapter_id: chapter.chapter_id, book_id: chapter.book_id, - user_id: chapter.author_id, title: decrypted_title, - hashed_title: chapter.hashed_title, chapter_order: chapter.chapter_order, - last_update: chapter.last_update, + author_id: chapter.author_id, title: decrypted_title, + hashed_title: chapter.hashed_title, words_count: chapter._words_count, + chapter_order: chapter.chapter_order, last_update: chapter.last_update, }); } let mut chapter_contents: Vec = Vec::with_capacity(encrypted_chapter_contents.len()); for chapter_content in encrypted_chapter_contents { - let decrypted_content: Option = if let Some(ref content) = chapter_content.content { Some(decrypt_data_with_user_key(content, &user_encryption_key)?) } else { None }; + let decrypted_content: Option = if let Some(ref content) = chapter_content.content { + let decrypted: String = decrypt_data_with_user_key(content, &user_encryption_key)?; + serde_json::from_str(&decrypted).ok() + } else { None }; chapter_contents.push(BookChapterContentTable { content_id: chapter_content.content_id, chapter_id: chapter_content.chapter_id, - user_id: chapter_content.author_id, content: decrypted_content, - version: chapter_content.version, last_update: chapter_content.last_update, + author_id: chapter_content.author_id, version: chapter_content.version, + content: decrypted_content, words_count: Some(chapter_content._words_count), + time_on_it: Some(chapter_content._time_on_it), last_update: chapter_content.last_update, }); } @@ -151,8 +156,12 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_summary: Option = if let Some(ref summary) = chapter_info.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; let decrypted_goal: Option = if let Some(ref goal) = chapter_info.goal { Some(decrypt_data_with_user_key(goal, &user_encryption_key)?) } else { None }; chapter_infos.push(BookChapterInfosTable { - chapter_id: chapter_info.chapter_id, user_id: chapter_info.author_id, - summary: decrypted_summary, notes: decrypted_goal, + chapter_info_id: chapter_info._chapter_info_id.clone(), + chapter_id: chapter_info.chapter_id, act_id: chapter_info._act_id, + incident_id: chapter_info._incident_id.clone(), + plot_point_id: chapter_info._plot_point_id.clone(), + book_id: chapter_info._book_id.clone(), author_id: chapter_info.author_id, + summary: decrypted_summary, goal: decrypted_goal, last_update: chapter_info.last_update, }); } @@ -162,7 +171,7 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_first_name: String = decrypt_data_with_user_key(&character.first_name, &user_encryption_key)?; let decrypted_last_name: Option = if let Some(ref last_name) = character.last_name { Some(decrypt_data_with_user_key(last_name, &user_encryption_key)?) } else { None }; let decrypted_nickname: Option = if let Some(ref nickname) = character.nickname { Some(decrypt_data_with_user_key(nickname, &user_encryption_key)?) } else { None }; - let decrypted_age: Option = if let Some(ref age) = character.age { Some(decrypt_data_with_user_key(age, &user_encryption_key)?.parse().unwrap_or(0)) } else { None }; + let decrypted_age: Option = if let Some(ref age) = character.age { Some(decrypt_data_with_user_key(age, &user_encryption_key)?) } else { None }; let decrypted_gender: Option = if let Some(ref gender) = character.gender { Some(decrypt_data_with_user_key(gender, &user_encryption_key)?) } else { None }; let decrypted_species: Option = if let Some(ref species) = character.species { Some(decrypt_data_with_user_key(species, &user_encryption_key)?) } else { None }; let decrypted_nationality: Option = if let Some(ref nationality) = character.nationality { Some(decrypt_data_with_user_key(nationality, &user_encryption_key)?) } else { None }; @@ -231,10 +240,10 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_title: String = decrypt_data_with_user_key(&incident.title, &user_encryption_key)?; let decrypted_summary: Option = if let Some(ref summary) = incident.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; incidents.push(BookIncidentsTable { - incident_id: incident.incident_id, chapter_id: String::new(), - user_id: incident.author_id, name: decrypted_title, - hashed_name: incident.hashed_title, description: decrypted_summary, - incident_order: 0, last_update: incident.last_update, + incident_id: incident.incident_id, author_id: incident.author_id, + book_id: incident.book_id, title: decrypted_title, + hashed_title: incident.hashed_title, summary: decrypted_summary, + last_update: incident.last_update, }); } @@ -242,10 +251,10 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan for issue in encrypted_issues { let decrypted_name: String = decrypt_data_with_user_key(&issue.name, &user_encryption_key)?; issues.push(BookIssuesTable { - issue_id: issue.issue_id, chapter_id: String::new(), - user_id: issue.author_id, name: decrypted_name, - hashed_name: issue.hashed_issue_name, description: None, - issue_order: 0, last_update: issue.last_update, + issue_id: issue.issue_id, author_id: issue.author_id, + book_id: issue.book_id, name: decrypted_name, + hashed_issue_name: issue.hashed_issue_name, + last_update: issue.last_update, }); } @@ -264,10 +273,11 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_title: String = decrypt_data_with_user_key(&plot_point.title, &user_encryption_key)?; let decrypted_summary: Option = if let Some(ref summary) = plot_point.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None }; plot_points.push(BookPlotPointsTable { - plot_point_id: plot_point.plot_point_id, chapter_id: String::new(), - user_id: plot_point.author_id, name: decrypted_title, - hashed_name: plot_point.hashed_title, description: decrypted_summary, - plot_point_order: 0, last_update: plot_point.last_update, + plot_point_id: plot_point.plot_point_id, title: decrypted_title, + hashed_title: plot_point.hashed_title, summary: decrypted_summary, + linked_incident_id: plot_point.linked_incident_id, + author_id: plot_point.author_id, book_id: plot_point._book_id, + last_update: plot_point.last_update, }); } @@ -280,9 +290,10 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_religion: Option = if let Some(ref religion) = world.religion { Some(decrypt_data_with_user_key(religion, &user_encryption_key)?) } else { None }; let decrypted_languages: Option = if let Some(ref languages) = world.languages { Some(decrypt_data_with_user_key(languages, &user_encryption_key)?) } else { None }; worlds.push(BookWorldTable { - world_id: world.world_id, book_id: world.book_id, - user_id: world.author_id, name: decrypted_name, - hashed_name: world.hashed_name, history: decrypted_history, + world_id: world.world_id, name: decrypted_name, + hashed_name: world.hashed_name, author_id: world.author_id, + book_id: world.book_id, + history: decrypted_history, politics: decrypted_politics, economy: decrypted_economy, religion: decrypted_religion, languages: decrypted_languages, last_update: world.last_update, @@ -306,7 +317,7 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let decrypted_element_name: String = decrypt_data_with_user_key(&location_element.element_name, &user_encryption_key)?; let decrypted_element_description: Option = if let Some(ref element_description) = location_element.element_description { Some(decrypt_data_with_user_key(element_description, &user_encryption_key)?) } else { None }; location_elements.push(LocationElementTable { - element_id: location_element.element_id, location_id: location_element.location, + element_id: location_element.element_id, location: location_element.location, user_id: location_element.user_id, element_name: decrypted_element_name, original_name: location_element.original_name, element_description: decrypted_element_description, @@ -333,9 +344,9 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let mut spells: Vec = Vec::with_capacity(encrypted_spells.len()); for spell in encrypted_spells { let decrypted_name: String = decrypt_data_with_user_key(&spell.name, &user_encryption_key)?; - let decrypted_description: String = if let Some(ref description) = spell.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_encryption_key)? } } else { String::new() }; - let decrypted_appearance: String = if let Some(ref appearance) = spell.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_encryption_key)? } } else { String::new() }; - let decrypted_tags: String = if let Some(ref tags) = spell.tags { if tags.is_empty() { String::new() } else { decrypt_data_with_user_key(tags, &user_encryption_key)? } } else { String::new() }; + let decrypted_description: Option = if let Some(ref description) = spell.description { if description.is_empty() { None } else { Some(decrypt_data_with_user_key(description, &user_encryption_key)?) } } else { None }; + let decrypted_appearance: Option = if let Some(ref appearance) = spell.appearance { if appearance.is_empty() { None } else { Some(decrypt_data_with_user_key(appearance, &user_encryption_key)?) } } else { None }; + let decrypted_tags: Option = if let Some(ref tags) = spell.tags { if tags.is_empty() { None } else { Some(decrypt_data_with_user_key(tags, &user_encryption_key)?) } } else { None }; let decrypted_power_level: Option = if let Some(ref power_level) = spell.power_level { if power_level.is_empty() { None } else { Some(decrypt_data_with_user_key(power_level, &user_encryption_key)?) } } else { None }; let decrypted_components: Option = if let Some(ref components) = spell.components { if components.is_empty() { None } else { Some(decrypt_data_with_user_key(components, &user_encryption_key)?) } } else { None }; let decrypted_limitations: Option = if let Some(ref limitations) = spell.limitations { if limitations.is_empty() { None } else { Some(decrypt_data_with_user_key(limitations, &user_encryption_key)?) } } else { None }; @@ -357,7 +368,7 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan spell_tags.push(BookSpellTagsTable { tag_id: spell_tag.tag_id, book_id: spell_tag.book_id, user_id: spell_tag.user_id, name: decrypted_name, - hashed_name: spell_tag.name_hash, color: spell_tag.color, + name_hash: spell_tag.name_hash, color: spell_tag.color, last_update: spell_tag.last_update, }); }