Add terms of use translations, sync detection, and refactor book components
- Introduced new translations for terms of use in French and English locales. - Added sync status detection logic for books in `BookList` and `BookCard` components. - Refactored `BookCard` to handle additional props and improve layout flexibility. - Enhanced `TermsOfUse` component with complete localization support and refuse functionality. - Updated data decryption logic in Rust services to handle optional fields and additional metadata consistently. - Improved offline/online synchronization workflows with extended context properties.
This commit is contained in:
@@ -67,14 +67,14 @@ export default function SyncBook({bookId, status}: SyncBookProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{currentStatus === 'synced' && (
|
{currentStatus === 'synced' && (
|
||||||
<Cloud className="w-4 h-4 text-gray-light" title={t("bookCard.synced")}/>
|
<IconButton icon={Cloud} variant="ghost" tooltip={t("bookCard.synced")} disabled/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentStatus === 'local-only' && (
|
{currentStatus === 'local-only' && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={CloudUpload}
|
icon={CloudUpload}
|
||||||
variant={isOffline ? 'muted' : 'primary'}
|
variant={isOffline ? 'ghost' : 'primary'}
|
||||||
size="sm"
|
size="md"
|
||||||
onClick={upload}
|
onClick={upload}
|
||||||
disabled={isOffline}
|
disabled={isOffline}
|
||||||
tooltip={t("bookCard.localOnly")}
|
tooltip={t("bookCard.localOnly")}
|
||||||
@@ -83,8 +83,8 @@ export default function SyncBook({bookId, status}: SyncBookProps) {
|
|||||||
{currentStatus === 'server-only' && (
|
{currentStatus === 'server-only' && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={CloudDownload}
|
icon={CloudDownload}
|
||||||
variant={isOffline ? 'muted' : 'primary'}
|
variant={isOffline ? 'ghost' : 'primary'}
|
||||||
size="sm"
|
size="md"
|
||||||
onClick={download}
|
onClick={download}
|
||||||
disabled={isOffline}
|
disabled={isOffline}
|
||||||
tooltip={t("bookCard.serverOnly")}
|
tooltip={t("bookCard.serverOnly")}
|
||||||
@@ -93,8 +93,8 @@ export default function SyncBook({bookId, status}: SyncBookProps) {
|
|||||||
{currentStatus === 'to-sync-from-server' && (
|
{currentStatus === 'to-sync-from-server' && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={CloudDownload}
|
icon={CloudDownload}
|
||||||
variant={isOffline ? 'muted' : 'primary'}
|
variant={isOffline ? 'ghost' : 'primary'}
|
||||||
size="sm"
|
size="md"
|
||||||
onClick={syncFromServer}
|
onClick={syncFromServer}
|
||||||
disabled={isOffline}
|
disabled={isOffline}
|
||||||
tooltip={t("bookCard.toSyncFromServer")}
|
tooltip={t("bookCard.toSyncFromServer")}
|
||||||
@@ -103,8 +103,8 @@ export default function SyncBook({bookId, status}: SyncBookProps) {
|
|||||||
{currentStatus === 'to-sync-to-server' && (
|
{currentStatus === 'to-sync-to-server' && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={CloudUpload}
|
icon={CloudUpload}
|
||||||
variant={isOffline ? 'muted' : 'primary'}
|
variant={isOffline ? 'ghost' : 'primary'}
|
||||||
size="sm"
|
size="md"
|
||||||
onClick={syncToServer}
|
onClick={syncToServer}
|
||||||
disabled={isOffline}
|
disabled={isOffline}
|
||||||
tooltip={t("bookCard.toSyncToServer")}
|
tooltip={t("bookCard.toSyncToServer")}
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {ExternalLink, FileText} from 'lucide-react';
|
import {ExternalLink, FileText} from 'lucide-react';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
import {AppRouterInstance, Link, useRouter} from '@/lib/navigation';
|
import {isDesktop} from '@/lib/configs';
|
||||||
|
import * as tauri from '@/lib/tauri';
|
||||||
|
import {useTranslations} from '@/lib/i18n';
|
||||||
|
|
||||||
interface TermsOfUseProps {
|
interface TermsOfUseProps {
|
||||||
onAccept: () => void;
|
onAccept: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
||||||
const router: AppRouterInstance = useRouter();
|
const t = useTranslations();
|
||||||
|
|
||||||
function handleAcceptTerm(): void {
|
function handleAcceptTerm(): void {
|
||||||
onAccept();
|
onAccept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRefuse(): Promise<void> {
|
||||||
|
if (isDesktop) {
|
||||||
|
await tauri.logout();
|
||||||
|
} else {
|
||||||
|
window.location.href = 'https://eritors.com';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-50 bg-darkest-background/90 backdrop-blur-sm flex items-center justify-center p-6 font-['Lora']">
|
className="fixed inset-0 z-50 bg-darkest-background/90 backdrop-blur-sm flex items-center justify-center p-6 font-['Lora']">
|
||||||
@@ -25,9 +35,8 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
|||||||
<FileText className="text-primary w-6 h-6" strokeWidth={1.75}/>
|
<FileText className="text-primary w-6 h-6" strokeWidth={1.75}/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-text-primary font-bold text-2xl">Termes d'utilisation</h2>
|
<h2 className="text-text-primary font-bold text-2xl">{t('terms.title')}</h2>
|
||||||
<p className="text-text-secondary text-sm mt-1">Acceptation requise pour accéder à ERitors
|
<p className="text-text-secondary text-sm mt-1">{t('terms.subtitle')}</p>
|
||||||
Scribe</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,33 +44,21 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="bg-primary/5 border border-primary/20 rounded-xl p-6">
|
<div className="bg-primary/5 border border-primary/20 rounded-xl p-6">
|
||||||
<h3 className="text-text-primary font-semibold text-lg mb-4">
|
<h3 className="text-text-primary font-semibold text-lg mb-4">
|
||||||
Acceptation obligatoire
|
{t('terms.mandatory')}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="text-text-secondary text-base leading-relaxed space-y-4">
|
<div className="text-text-secondary text-base leading-relaxed space-y-4">
|
||||||
<p>
|
<p dangerouslySetInnerHTML={{__html: t('terms.mandatoryDesc1')}}/>
|
||||||
Pour pouvoir utiliser nos services, tel qu'<strong className="text-primary">ERitors
|
<p>{t('terms.mandatoryDesc2')}</p>
|
||||||
Scribe</strong>,
|
<p>{t('terms.mandatoryDesc3')}</p>
|
||||||
vous devez accepter les termes d'utilisation en cliquant
|
|
||||||
sur <strong>J'accepte</strong>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Veuillez lire attentivement la page détaillée des termes et conditions d'utilisation
|
|
||||||
avant de procéder à l'acceptation.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Si vous n'acceptez pas ces conditions, vous ne pourrez pas accéder à nos services
|
|
||||||
et serez redirigé vers la page d'accueil.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-tertiary border border-secondary rounded-xl p-6">
|
<div className="bg-tertiary border border-secondary rounded-xl p-6">
|
||||||
<h3 className="text-text-primary font-semibold text-lg mb-4">
|
<h3 className="text-text-primary font-semibold text-lg mb-4">
|
||||||
Documentation complète
|
{t('terms.fullDoc')}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-text-secondary text-base leading-relaxed mb-4">
|
<p className="text-text-secondary text-base leading-relaxed mb-4">
|
||||||
Pour consulter l'intégralité de nos termes et conditions d'utilisation,
|
{t('terms.fullDocDesc')}
|
||||||
veuillez visiter notre page dédiée :
|
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="/terms"
|
href="/terms"
|
||||||
@@ -69,7 +66,7 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="inline-flex items-center space-x-2 text-primary hover:text-primary-light transition-colors duration-200 font-medium"
|
className="inline-flex items-center space-x-2 text-primary hover:text-primary-light transition-colors duration-200 font-medium"
|
||||||
>
|
>
|
||||||
<span>Consulter les termes complets</span>
|
<span>{t('terms.fullDocLink')}</span>
|
||||||
<ExternalLink className="w-4 h-4" strokeWidth={1.75}/>
|
<ExternalLink className="w-4 h-4" strokeWidth={1.75}/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,12 +78,10 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-text-primary font-semibold text-base mb-2">
|
<h4 className="text-text-primary font-semibold text-base mb-2">
|
||||||
Importance capitale
|
{t('terms.importance')}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-text-secondary text-sm leading-relaxed">
|
<p className="text-text-secondary text-sm leading-relaxed">
|
||||||
Cette acceptation est obligatoire et constitue un prérequis légal
|
{t('terms.importanceDesc')}
|
||||||
pour l'utilisation de nos services d'édition assistée par intelligence
|
|
||||||
artificielle.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,18 +92,18 @@ export default function TermsOfUse({onAccept}: TermsOfUseProps) {
|
|||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center space-x-2 text-text-secondary text-sm">
|
<div className="flex items-center space-x-2 text-text-secondary text-sm">
|
||||||
<FileText className="text-primary w-4 h-4" strokeWidth={1.75}/>
|
<FileText className="text-primary w-4 h-4" strokeWidth={1.75}/>
|
||||||
<span>Décision requise pour continuer</span>
|
<span>{t('terms.required')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<Link
|
<button
|
||||||
href="https://eritors.com"
|
onClick={handleRefuse}
|
||||||
className="text-muted hover:text-text-primary px-6 py-3 rounded-xl hover:bg-secondary transition-colors duration-150 text-sm font-medium"
|
className="text-muted hover:text-text-primary px-6 py-3 rounded-xl hover:bg-secondary transition-colors duration-150 text-sm font-medium"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
Refuser et quitter
|
{t('terms.refuse')}
|
||||||
</Link>
|
</button>
|
||||||
<Button variant="primary" size="lg" onClick={handleAcceptTerm}>
|
<Button variant="primary" size="lg" onClick={handleAcceptTerm}>
|
||||||
J'accepte les termes
|
{t('terms.accept')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
import {Link} from '@/lib/navigation';
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import {isDesktop} from '@/lib/configs';
|
||||||
import {BookProps} from "@/lib/types/book";
|
import {BookProps} from "@/lib/types/book";
|
||||||
import DeleteBook from "@/components/book/settings/DeleteBook";
|
import DeleteBook from "@/components/book/settings/DeleteBook";
|
||||||
|
import SyncBook from "@/components/SyncBook";
|
||||||
|
import {SyncType} from "@/context/BooksSyncContext";
|
||||||
import {useTranslations} from '@/lib/i18n';
|
import {useTranslations} from '@/lib/i18n';
|
||||||
|
|
||||||
export default function BookCard(
|
interface BookCardProps {
|
||||||
{
|
book: BookProps;
|
||||||
book,
|
onClickCallback: (bookId: string) => void;
|
||||||
onClickCallback,
|
|
||||||
index
|
|
||||||
}: {
|
|
||||||
book: BookProps,
|
|
||||||
onClickCallback: Function;
|
|
||||||
index: number;
|
index: number;
|
||||||
}) {
|
syncStatus?: SyncType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BookCard({book, onClickCallback, index, syncStatus}: BookCardProps) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/book/${book.bookId}`}>
|
|
||||||
<div
|
<div
|
||||||
className="group relative aspect-[2/3] rounded-xl overflow-hidden cursor-pointer transition-all duration-300 hover:ring-1 hover:ring-text-primary/20">
|
className="group relative aspect-[2/3] rounded-xl overflow-hidden cursor-pointer transition-all duration-300 hover:ring-1 hover:ring-text-primary/20">
|
||||||
|
<button onClick={(): void => onClickCallback(book.bookId)} className="w-full h-full text-left block"
|
||||||
|
type="button">
|
||||||
{book.coverImage ? (
|
{book.coverImage ? (
|
||||||
<img
|
<img
|
||||||
src={book.coverImage}
|
src={book.coverImage}
|
||||||
@@ -33,6 +34,13 @@ export default function BookCard(
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{isDesktop && syncStatus && (
|
||||||
|
<div className="absolute top-2 left-2 cursor-default" onClick={(e: React.MouseEvent): void => e.stopPropagation()}>
|
||||||
|
<SyncBook status={syncStatus} bookId={book.bookId}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="absolute inset-x-0 bottom-0 bg-darkest-background/70 p-3">
|
<div className="absolute inset-x-0 bottom-0 bg-darkest-background/70 p-3">
|
||||||
<h3 className="text-text-primary font-bold text-sm truncate">
|
<h3 className="text-text-primary font-bold text-sm truncate">
|
||||||
@@ -47,12 +55,11 @@ export default function BookCard(
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
||||||
onClick={(e: React.MouseEvent): void => e.preventDefault()}
|
onClick={(e: React.MouseEvent): void => e.stopPropagation()}
|
||||||
{...(index === 0 && {'data-guide': 'bottom-book-card'})}
|
{...(index === 0 && {'data-guide': 'bottom-book-card'})}
|
||||||
>
|
>
|
||||||
<DeleteBook bookId={book.bookId}/>
|
<DeleteBook bookId={book.bookId}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import GuideTour, {GuideStep} from "@/components/GuideTour";
|
|||||||
import {guideTourDone, setNewGuideTour} from "@/lib/utils/user";
|
import {guideTourDone, setNewGuideTour} from "@/lib/utils/user";
|
||||||
import {useTranslations} from '@/lib/i18n';
|
import {useTranslations} from '@/lib/i18n';
|
||||||
import {LangContext, LangContextProps} from "@/context/LangContext";
|
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 {SeriesListItemProps} from "@/lib/types/series";
|
||||||
import SeriesCard, {SeriesCardProps} from "@/components/series/SeriesCard";
|
import SeriesCard, {SeriesCardProps} from "@/components/series/SeriesCard";
|
||||||
import SeriesSetting from "@/components/series/SeriesSetting";
|
import SeriesSetting from "@/components/series/SeriesSetting";
|
||||||
@@ -35,8 +36,11 @@ export default function BookList() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext)
|
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext)
|
||||||
const {serverSyncedBooks}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext)
|
const {
|
||||||
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
serverSyncedBooks, booksToSyncFromServer, booksToSyncToServer,
|
||||||
|
serverOnlyBooks, localOnlyBooks
|
||||||
|
}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext);
|
||||||
|
const {isCurrentlyOffline, offlineMode}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||||
const [groupedItems, setGroupedItems] = useState<Record<string, CategoryItem[]>>({});
|
const [groupedItems, setGroupedItems] = useState<Record<string, CategoryItem[]>>({});
|
||||||
@@ -95,27 +99,38 @@ export default function BookList() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
if (groupedItems && Object.keys(groupedItems).length > 0 && guideTourDone(session.user?.guideTour || [], 'new-first-book')) {
|
if (groupedItems && Object.keys(groupedItems).length > 0) {
|
||||||
setBookGuide(true);
|
const notDone: boolean = isCurrentlyOffline()
|
||||||
|
? localStorage.getItem('guide-tour-new-first-book') !== 'true'
|
||||||
|
: guideTourDone(session.user?.guideTour || [], 'new-first-book');
|
||||||
|
if (notDone) setBookGuide(true);
|
||||||
}
|
}
|
||||||
}, [groupedItems]);
|
}, [groupedItems]);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
loadBooksAndSeries().then()
|
const canLoad: boolean = !isDesktop ||
|
||||||
}, [serverSyncedBooks]);
|
(!isCurrentlyOffline() || offlineMode.isDatabaseInitialized);
|
||||||
|
if (canLoad) loadBooksAndSeries().then();
|
||||||
|
}, [serverSyncedBooks, offlineMode.isDatabaseInitialized, booksToSyncFromServer, booksToSyncToServer, serverOnlyBooks, localOnlyBooks]);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
if (accessToken) loadBooksAndSeries().then();
|
if (accessToken) loadBooksAndSeries().then();
|
||||||
}, [accessToken]);
|
}, [accessToken]);
|
||||||
|
|
||||||
async function handleFirstBookGuide(): Promise<void> {
|
async function handleFirstBookGuide(): Promise<void> {
|
||||||
|
if (isCurrentlyOffline()) {
|
||||||
|
localStorage.setItem('guide-tour-new-first-book', 'true');
|
||||||
|
setBookGuide(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const response: boolean = await apiPost<boolean>(
|
const response: boolean = await apiPost<boolean>(
|
||||||
'logs/tour',
|
'logs/tour',
|
||||||
{plateforme: 'web', tour: 'new-first-book'},
|
{plateforme: 'desktop', tour: 'new-first-book'},
|
||||||
session.accessToken, lang
|
session.accessToken, lang
|
||||||
);
|
);
|
||||||
if (response) {
|
if (response) {
|
||||||
|
localStorage.setItem('guide-tour-new-first-book', 'true');
|
||||||
setSession(setNewGuideTour(session, 'new-first-book'));
|
setSession(setNewGuideTour(session, 'new-first-book'));
|
||||||
setBookGuide(false);
|
setBookGuide(false);
|
||||||
}
|
}
|
||||||
@@ -291,6 +306,23 @@ export default function BookList() {
|
|||||||
}, 0);
|
}, 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 {
|
function handleBookClick(bookId: string): void {
|
||||||
router.push(`/book/${bookId}`);
|
router.push(`/book/${bookId}`);
|
||||||
}
|
}
|
||||||
@@ -388,11 +420,12 @@ export default function BookList() {
|
|||||||
return (
|
return (
|
||||||
<div key={item.book.bookId}
|
<div key={item.book.bookId}
|
||||||
{...(idx === 0 && {'data-guide': 'book-card'})}
|
{...(idx === 0 && {'data-guide': 'book-card'})}
|
||||||
className={`flex-shrink-0 w-64 sm:w-52 md:w-48 lg:w-56 xl:w-64 p-2 box-border ${guideTourDone(session.user?.guideTour || [], 'new-first-book') && 'mb-[200px]'}`}>
|
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]'}`}>
|
||||||
<BookCard
|
<BookCard
|
||||||
book={item.book}
|
book={item.book}
|
||||||
onClickCallback={handleBookClick}
|
onClickCallback={handleBookClick}
|
||||||
index={idx}
|
index={idx}
|
||||||
|
syncStatus={detectBookSyncStatus(item.book.bookId)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export default function DraftCompanion() {
|
|||||||
|
|
||||||
const isGPTEnabled: boolean = isOpenAIEnabled(session);
|
const isGPTEnabled: boolean = isOpenAIEnabled(session);
|
||||||
const isSubTierTree: boolean = getSubLevel(session) === 3;
|
const isSubTierTree: boolean = getSubLevel(session) === 3;
|
||||||
const hasAccess: boolean = isGPTEnabled || isSubTierTree;
|
const hasAccess: boolean = (isGPTEnabled || isSubTierTree) && !isCurrentlyOffline() && !book?.localBook;
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
getDraftContent().then();
|
getDraftContent().then();
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import ImportBookForm from "@/components/book/ImportBookForm";
|
|||||||
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
|
||||||
import {useTranslations} from '@/lib/i18n';
|
import {useTranslations} from '@/lib/i18n';
|
||||||
import InsetPanel from "@/components/ui/InsetPanel";
|
import InsetPanel from "@/components/ui/InsetPanel";
|
||||||
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||||
|
|
||||||
export default function ScribeLeftBar() {
|
export default function ScribeLeftBar() {
|
||||||
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
|
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
|
||||||
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
|
const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
|
||||||
|
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
const [panelHidden, setPanelHidden] = useState<boolean>(false);
|
const [panelHidden, setPanelHidden] = useState<boolean>(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) => (
|
||||||
<IconButton
|
<IconButton
|
||||||
key={component.id}
|
key={component.id}
|
||||||
icon={component.icon}
|
icon={component.icon}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {useTranslations} from '@/lib/i18n';
|
|||||||
import {Lock, ShieldCheck, Eye, EyeOff} from 'lucide-react';
|
import {Lock, ShieldCheck, Eye, EyeOff} from 'lucide-react';
|
||||||
import * as tauri from '@/lib/tauri';
|
import * as tauri from '@/lib/tauri';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
|
import IconButton from '@/components/ui/IconButton';
|
||||||
|
|
||||||
interface OfflinePinSetupProps {
|
interface OfflinePinSetupProps {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@@ -69,30 +70,31 @@ export default function OfflinePinSetup({onClose, onSuccess, showOnFirstLogin}:
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-darkest-background/60 backdrop-blur-md">
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-darkest-background/60 backdrop-blur-md animate-fadeIn">
|
||||||
<div className="bg-tertiary rounded-2xl p-6 max-w-md w-full mx-4 shadow-2xl">
|
<div className="relative bg-tertiary text-text-primary rounded-xl max-w-md w-full flex flex-col">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex justify-between items-center px-6 py-4">
|
||||||
<div className="p-3 bg-primary/10 rounded-lg">
|
<h2 className="flex items-center gap-3 font-['ADLaM_Display'] text-xl tracking-wide">
|
||||||
<ShieldCheck className="w-6 h-6 text-primary"/>
|
<ShieldCheck className="w-6 h-6" strokeWidth={1.75}/>
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<h2 className="text-xl font-bold text-text-primary">
|
|
||||||
{showOnFirstLogin ? t('offline.pin.setup.titleFirstLogin') : t('offline.pin.setup.title')}
|
{showOnFirstLogin ? t('offline.pin.setup.titleFirstLogin') : t('offline.pin.setup.title')}
|
||||||
</h2>
|
</h2>
|
||||||
|
{onClose && (
|
||||||
|
<IconButton icon={Lock} variant="light" onClick={onClose}/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-darkest-background rounded-xl mx-2 mb-2">
|
||||||
|
<div className="p-5 space-y-4">
|
||||||
<p className="text-sm text-muted">
|
<p className="text-sm text-muted">
|
||||||
{t('offline.pin.setup.subtitle')}
|
{t('offline.pin.setup.subtitle')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-info/10 border border-info/20 rounded-lg p-3 mb-4">
|
<div className="p-3 bg-info/10 border border-info/20 rounded-lg">
|
||||||
<p className="text-sm text-info flex items-center gap-2">
|
<p className="text-sm text-info flex items-center gap-2">
|
||||||
<Lock className="w-3 h-3"/>
|
<Lock className="w-3 h-3 shrink-0"/>
|
||||||
{t('offline.pin.setup.description')}
|
{t('offline.pin.setup.description')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-primary mb-2">
|
<label className="block text-sm font-medium text-text-primary mb-2">
|
||||||
{t('offline.pin.setup.pinLabel')}
|
{t('offline.pin.setup.pinLabel')}
|
||||||
@@ -131,15 +133,20 @@ export default function OfflinePinSetup({onClose, onSuccess, showOnFirstLogin}:
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mt-3 p-2 bg-error/10 border border-error/20 rounded-lg">
|
<div className="p-2 bg-error/10 border border-error/20 rounded-lg">
|
||||||
<p className="text-sm text-error">{error}</p>
|
<p className="text-sm text-error">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-6 flex gap-3 justify-end">
|
<p className="text-xs text-muted text-center">
|
||||||
|
{t('offline.pin.setup.footer')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3 px-6 py-4">
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<Button variant="ghost" onClick={onClose} disabled={isLoading}>
|
<Button variant="ghost" onClick={onClose} disabled={isLoading}>
|
||||||
{t('offline.pin.setup.laterButton')}
|
{t('offline.pin.setup.laterButton')}
|
||||||
@@ -156,10 +163,6 @@ export default function OfflinePinSetup({onClose, onSuccess, showOnFirstLogin}:
|
|||||||
{t('offline.pin.setup.configureButton')}
|
{t('offline.pin.setup.configureButton')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="mt-4 text-xs text-muted text-center">
|
|
||||||
{t('offline.pin.setup.footer')}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -64,31 +64,24 @@ export default function OfflinePinVerify({onSuccess, onCancel}: OfflinePinVerify
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background">
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-darkest-background/60 backdrop-blur-md animate-fadeIn">
|
||||||
<div className="bg-tertiary rounded-2xl p-6 max-w-md w-full mx-4 shadow-2xl">
|
<div className="relative bg-tertiary text-text-primary rounded-xl max-w-md w-full flex flex-col">
|
||||||
<div className="flex items-center justify-center mb-6">
|
<div className="flex justify-between items-center px-6 py-4">
|
||||||
<div className="relative">
|
<h2 className="flex items-center gap-3 font-['ADLaM_Display'] text-xl tracking-wide">
|
||||||
<div className="p-4 bg-primary/10 rounded-full">
|
<Lock className="w-6 h-6" strokeWidth={1.75}/>
|
||||||
<Lock className="w-8 h-8 text-primary"/>
|
|
||||||
</div>
|
|
||||||
<div className="absolute -bottom-1 -right-1 p-1 bg-tertiary rounded-full">
|
|
||||||
<div className="p-1 bg-warning/20 rounded-full">
|
|
||||||
<Wifi className="w-3 h-3 text-warning"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-center mb-6">
|
|
||||||
<h2 className="text-xl font-bold text-text-primary mb-2">
|
|
||||||
{t('offline.pin.verify.title')}
|
{t('offline.pin.verify.title')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-muted">
|
<div className="p-1.5 bg-warning/20 rounded-full">
|
||||||
{t('offline.pin.verify.subtitle')}
|
<Wifi className="w-4 h-4 text-warning"/>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="bg-darkest-background rounded-xl mx-2 mb-2">
|
||||||
|
<div className="p-5 space-y-4">
|
||||||
|
<p className="text-sm text-muted text-center">
|
||||||
|
{t('offline.pin.verify.subtitle')}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type={showPin ? 'text' : 'password'}
|
type={showPin ? 'text' : 'password'}
|
||||||
@@ -109,18 +102,26 @@ export default function OfflinePinVerify({onSuccess, onCancel}: OfflinePinVerify
|
|||||||
{showPin ? <EyeOff className="w-4 h-4"/> : <Eye className="w-4 h-4"/>}
|
{showPin ? <EyeOff className="w-4 h-4"/> : <Eye className="w-4 h-4"/>}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="mb-4 p-3 bg-error/10 border border-error/20 rounded-lg">
|
<div className="p-3 bg-error/10 border border-error/20 rounded-lg">
|
||||||
<p className="text-sm text-error text-center">{error}</p>
|
<p className="text-sm text-error text-center">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex gap-3">
|
{attempts > 0 && attempts <= 2 && (
|
||||||
|
<p className="text-xs text-muted text-center">
|
||||||
|
{t('offline.pin.verify.attemptsRemaining', {count: 3 - attempts})}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between px-6 py-4">
|
||||||
<Button variant="danger" icon={LogOut} onClick={handleLogout} disabled={isLoading}>
|
<Button variant="danger" icon={LogOut} onClick={handleLogout} disabled={isLoading}>
|
||||||
{t('userMenu.logout')}
|
{t('userMenu.logout')}
|
||||||
</Button>
|
</Button>
|
||||||
|
<div className="flex gap-3">
|
||||||
{onCancel && (
|
{onCancel && (
|
||||||
<Button variant="secondary" onClick={onCancel} disabled={isLoading}>
|
<Button variant="secondary" onClick={onCancel} disabled={isLoading}>
|
||||||
{t('offline.pin.verify.cancelButton')}
|
{t('offline.pin.verify.cancelButton')}
|
||||||
@@ -137,12 +138,7 @@ export default function OfflinePinVerify({onSuccess, onCancel}: OfflinePinVerify
|
|||||||
{t('offline.pin.verify.unlockButton')}
|
{t('offline.pin.verify.unlockButton')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{attempts > 0 && attempts <= 2 && (
|
|
||||||
<p className="mt-4 text-xs text-muted text-center">
|
|
||||||
{t('offline.pin.verify.attemptsRemaining', {count: 3 - attempts})}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,21 +3,19 @@
|
|||||||
import {useContext} from 'react';
|
import {useContext} from 'react';
|
||||||
import OfflineContext from '@/context/OfflineContext';
|
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() {
|
export default function OfflineToggle() {
|
||||||
const {offlineMode, toggleOfflineMode} = useContext(OfflineContext);
|
const {offlineMode, toggleOfflineMode} = useContext(OfflineContext);
|
||||||
|
const t = useTranslations();
|
||||||
|
|
||||||
if (!offlineMode.isDatabaseInitialized) {
|
if (!offlineMode.isDatabaseInitialized) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleToggle = () => {
|
|
||||||
toggleOfflineMode();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={handleToggle}
|
onClick={toggleOfflineMode}
|
||||||
className={`
|
className={`
|
||||||
flex items-center gap-2 px-3 py-1.5 rounded-lg border transition-all
|
flex items-center gap-2 px-3 py-1.5 rounded-lg border transition-all
|
||||||
${offlineMode.isOffline
|
${offlineMode.isOffline
|
||||||
@@ -25,14 +23,14 @@ export default function OfflineToggle() {
|
|||||||
: 'bg-green-500/20 border-green-500/40 hover:bg-green-500/30'
|
: 'bg-green-500/20 border-green-500/40 hover:bg-green-500/30'
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
title={offlineMode.isOffline ? 'Passer en mode en ligne' : 'Passer en mode hors ligne'}
|
title={offlineMode.isOffline ? t('offline.toggle.switchToOnline') : t('offline.toggle.switchToOffline')}
|
||||||
>
|
>
|
||||||
{offlineMode.isOffline
|
{offlineMode.isOffline
|
||||||
? <Circle className="w-3 h-3 text-orange-300"/>
|
? <Circle className="w-3 h-3 text-orange-300"/>
|
||||||
: <Wifi className="w-3 h-3 text-green-300"/>
|
: <Wifi className="w-3 h-3 text-green-300"/>
|
||||||
}
|
}
|
||||||
<span className={`text-xs font-medium ${offlineMode.isOffline ? 'text-orange-200' : 'text-green-200'}`}>
|
<span className={`text-xs font-medium ${offlineMode.isOffline ? 'text-orange-200' : 'text-green-200'}`}>
|
||||||
{offlineMode.isOffline ? 'Hors ligne' : 'En ligne'}
|
{offlineMode.isOffline ? t('offline.toggle.offline') : t('offline.toggle.online')}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {useTranslations} from '@/lib/i18n';
|
|||||||
import PulseLoader from '@/components/ui/PulseLoader';
|
import PulseLoader from '@/components/ui/PulseLoader';
|
||||||
import InsetPanel from "@/components/ui/InsetPanel";
|
import InsetPanel from "@/components/ui/InsetPanel";
|
||||||
import IconButton from "@/components/ui/IconButton";
|
import IconButton from "@/components/ui/IconButton";
|
||||||
|
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
|
||||||
|
|
||||||
// Lazy loaded Editor components
|
// Lazy loaded Editor components
|
||||||
const WorldEditor = lazy(function () {
|
const WorldEditor = lazy(function () {
|
||||||
@@ -45,6 +46,7 @@ function PanelContent({currentPanelId}: PanelContentProps): React.JSX.Element {
|
|||||||
|
|
||||||
export default function ComposerRightBar(): React.JSX.Element {
|
export default function ComposerRightBar(): React.JSX.Element {
|
||||||
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
|
const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
|
||||||
|
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
const [panelHidden, setPanelHidden] = useState<boolean>(false);
|
const [panelHidden, setPanelHidden] = useState<boolean>(false);
|
||||||
@@ -169,6 +171,9 @@ export default function ComposerRightBar(): React.JSX.Element {
|
|||||||
)}
|
)}
|
||||||
<div className="bg-tertiary p-1 flex flex-col space-y-2">
|
<div className="bg-tertiary p-1 flex flex-col space-y-2">
|
||||||
{book ? editorComponents.filter(function (component: PanelComponent): boolean {
|
{book ? editorComponents.filter(function (component: PanelComponent): boolean {
|
||||||
|
if ((isCurrentlyOffline() || book?.localBook) && component.id === 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (component.id === 1 && book.quillsenseEnabled === false) {
|
if (component.id === 1 && book.quillsenseEnabled === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
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 {
|
export interface BooksSyncContextProps {
|
||||||
setServerSyncedBooks: Dispatch<SetStateAction<SyncedBook[]>>;
|
|
||||||
serverSyncedBooks: SyncedBook[];
|
serverSyncedBooks: SyncedBook[];
|
||||||
|
setServerSyncedBooks: Dispatch<SetStateAction<SyncedBook[]>>;
|
||||||
|
localSyncedBooks: SyncedBook[];
|
||||||
|
setLocalSyncedBooks: Dispatch<SetStateAction<SyncedBook[]>>;
|
||||||
|
booksToSyncFromServer: BookSyncCompare[];
|
||||||
|
setBooksToSyncFromServer: Dispatch<SetStateAction<BookSyncCompare[]>>;
|
||||||
|
booksToSyncToServer: BookSyncCompare[];
|
||||||
|
setBooksToSyncToServer: Dispatch<SetStateAction<BookSyncCompare[]>>;
|
||||||
|
setServerOnlyBooks: Dispatch<SetStateAction<SyncedBook[]>>;
|
||||||
|
setLocalOnlyBooks: Dispatch<SetStateAction<SyncedBook[]>>;
|
||||||
|
serverOnlyBooks: SyncedBook[];
|
||||||
|
localOnlyBooks: SyncedBook[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BooksSyncContext: Context<BooksSyncContextProps> = createContext<BooksSyncContextProps>({
|
export const BooksSyncContext: Context<BooksSyncContextProps> = createContext<BooksSyncContextProps>({
|
||||||
serverSyncedBooks: [],
|
serverSyncedBooks: [],
|
||||||
setServerSyncedBooks: (): void => {
|
setServerSyncedBooks: (): void => {},
|
||||||
}
|
localSyncedBooks: [],
|
||||||
})
|
setLocalSyncedBooks: (): void => {},
|
||||||
|
booksToSyncFromServer: [],
|
||||||
|
setBooksToSyncFromServer: (): void => {},
|
||||||
|
booksToSyncToServer: [],
|
||||||
|
setBooksToSyncToServer: (): void => {},
|
||||||
|
setServerOnlyBooks: (): void => {},
|
||||||
|
setLocalOnlyBooks: (): void => {},
|
||||||
|
serverOnlyBooks: [],
|
||||||
|
localOnlyBooks: []
|
||||||
|
});
|
||||||
|
|||||||
@@ -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 {
|
export interface OfflineContextType {
|
||||||
|
offlineMode: OfflineMode;
|
||||||
|
setOfflineMode: Dispatch<SetStateAction<OfflineMode>>;
|
||||||
|
toggleOfflineMode: () => void;
|
||||||
|
initializeDatabase: (userId: string, encryptionKey?: string) => Promise<boolean>;
|
||||||
isCurrentlyOffline: () => boolean;
|
isCurrentlyOffline: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultOfflineMode: OfflineMode = {
|
||||||
|
isManuallyOffline: false,
|
||||||
|
isNetworkOnline: typeof navigator !== 'undefined' ? navigator.onLine : true,
|
||||||
|
isOffline: false,
|
||||||
|
isDatabaseInitialized: false,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
|
||||||
const OfflineContext = createContext<OfflineContextType>({
|
const OfflineContext = createContext<OfflineContextType>({
|
||||||
isCurrentlyOffline: () => false,
|
offlineMode: defaultOfflineMode,
|
||||||
|
setOfflineMode: () => {},
|
||||||
|
toggleOfflineMode: () => {},
|
||||||
|
initializeDatabase: async () => false,
|
||||||
|
isCurrentlyOffline: () => false
|
||||||
});
|
});
|
||||||
|
|
||||||
export default OfflineContext;
|
export default OfflineContext;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function OfflineProvider({ children }: OfflineProviderProps) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} 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 => ({
|
setOfflineMode(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
isDatabaseInitialized: false,
|
isDatabaseInitialized: false,
|
||||||
|
|||||||
@@ -1372,7 +1372,29 @@
|
|||||||
"passwordUnknown": "An unknown error occurred."
|
"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 <strong>ERitors Scribe</strong>, you must accept the terms of use by clicking <strong>I accept</strong>.",
|
||||||
|
"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": {
|
"offline": {
|
||||||
|
"toggle": {
|
||||||
|
"online": "Online",
|
||||||
|
"offline": "Offline",
|
||||||
|
"switchToOnline": "Switch to online mode",
|
||||||
|
"switchToOffline": "Switch to offline mode"
|
||||||
|
},
|
||||||
"mode": {
|
"mode": {
|
||||||
"title": "Offline mode",
|
"title": "Offline mode",
|
||||||
"backToOnline": "Back online"
|
"backToOnline": "Back online"
|
||||||
|
|||||||
@@ -1371,7 +1371,29 @@
|
|||||||
"passwordUnknown": "Une erreur inconnue est survenue."
|
"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'<strong>ERitors Scribe</strong>, vous devez accepter les termes d'utilisation en cliquant sur <strong>J'accepte</strong>.",
|
||||||
|
"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": {
|
"offline": {
|
||||||
|
"toggle": {
|
||||||
|
"online": "En ligne",
|
||||||
|
"offline": "Hors ligne",
|
||||||
|
"switchToOnline": "Passer en mode en ligne",
|
||||||
|
"switchToOffline": "Passer en mode hors ligne"
|
||||||
|
},
|
||||||
"mode": {
|
"mode": {
|
||||||
"title": "Mode hors-ligne",
|
"title": "Mode hors-ligne",
|
||||||
"backToOnline": "Retour en ligne"
|
"backToOnline": "Retour en ligne"
|
||||||
|
|||||||
13
lib/tauri.ts
13
lib/tauri.ts
@@ -679,7 +679,12 @@ export async function applySeriesTombstones(tombstones: TombstoneRecord[]): Prom
|
|||||||
|
|
||||||
// ─── Window Management ──────────────────────────────────────
|
// ─── Window Management ──────────────────────────────────────
|
||||||
|
|
||||||
|
let loginWindowOpening = false;
|
||||||
|
|
||||||
export async function openLoginWindow(): Promise<void> {
|
export async function openLoginWindow(): Promise<void> {
|
||||||
|
if (loginWindowOpening) return;
|
||||||
|
loginWindowOpening = true;
|
||||||
|
|
||||||
const {WebviewWindow} = await import('@tauri-apps/api/webviewWindow');
|
const {WebviewWindow} = await import('@tauri-apps/api/webviewWindow');
|
||||||
const {getCurrentWindow} = await import('@tauri-apps/api/window');
|
const {getCurrentWindow} = await import('@tauri-apps/api/window');
|
||||||
|
|
||||||
@@ -687,6 +692,7 @@ export async function openLoginWindow(): Promise<void> {
|
|||||||
if (existing) {
|
if (existing) {
|
||||||
await existing.setFocus();
|
await existing.setFocus();
|
||||||
await getCurrentWindow().hide();
|
await getCurrentWindow().hide();
|
||||||
|
loginWindowOpening = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -701,6 +707,7 @@ export async function openLoginWindow(): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loginWindow.once('tauri://created', async function () {
|
loginWindow.once('tauri://created', async function () {
|
||||||
|
loginWindowOpening = false;
|
||||||
await getCurrentWindow().hide();
|
await getCurrentWindow().hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -712,20 +719,20 @@ export async function loginSuccess(): Promise<void> {
|
|||||||
const currentLabel = getCurrentWindow().label;
|
const currentLabel = getCurrentWindow().label;
|
||||||
|
|
||||||
if (currentLabel === 'login') {
|
if (currentLabel === 'login') {
|
||||||
const {emit} = await import('@tauri-apps/api/event');
|
|
||||||
await emit('auth-success');
|
|
||||||
const mainWindow = await WebviewWindow.getByLabel('main');
|
const mainWindow = await WebviewWindow.getByLabel('main');
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
await mainWindow.show();
|
await mainWindow.show();
|
||||||
await mainWindow.setFocus();
|
await mainWindow.setFocus();
|
||||||
|
await mainWindow.emit('auth-success');
|
||||||
}
|
}
|
||||||
await getCurrentWindow().close();
|
getCurrentWindow().close();
|
||||||
} else {
|
} else {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logout(): Promise<void> {
|
export async function logout(): Promise<void> {
|
||||||
|
await removeToken();
|
||||||
await openLoginWindow();
|
await openLoginWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {isDesktop} from '@/lib/configs';
|
||||||
|
|
||||||
export function getCookie(name: string): string | null {
|
export function getCookie(name: string): string | null {
|
||||||
const nameEQ: string = `${name}=`;
|
const nameEQ: string = `${name}=`;
|
||||||
const allCookies: string[] = document.cookie.split(';');
|
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));
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
const expires: string = `expires=${date.toUTCString()}`;
|
const expires: string = `expires=${date.toUTCString()}`;
|
||||||
let domain: string = '';
|
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};`;
|
domain = `domain=${window.location.hostname};`;
|
||||||
}
|
}
|
||||||
const secure: string = 'Secure;';
|
const secure: string = isDesktop ? '' : 'Secure;';
|
||||||
const sameSite: string = 'SameSite=Strict;';
|
const sameSite: string = 'SameSite=Strict;';
|
||||||
document.cookie = `${name}=${value}; ${expires}; ${domain} path=/; ${secure} ${sameSite}`;
|
document.cookie = `${name}=${value}; ${expires}; ${domain} path=/; ${secure} ${sameSite}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeCookie(name: string): void {
|
export function removeCookie(name: string): void {
|
||||||
let domain: string = '';
|
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};`;
|
domain = `domain=${window.location.hostname};`;
|
||||||
}
|
}
|
||||||
const secure: string = 'Secure;';
|
const secure: string = isDesktop ? '' : 'Secure;';
|
||||||
const sameSite: string = 'SameSite=Strict;';
|
const sameSite: string = 'SameSite=Strict;';
|
||||||
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`;
|
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ pub struct BookQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct EritBooksTable {
|
pub struct EritBooksTable {
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
pub book_type: String,
|
pub book_type: String,
|
||||||
pub author_id: String,
|
pub author_id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@@ -47,7 +47,6 @@ pub struct SyncedBookResult {
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookToolsTable {
|
pub struct BookToolsTable {
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
|
|||||||
@@ -180,18 +180,16 @@ pub struct CompleteBook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookActSummariesTable {
|
pub struct BookActSummariesTable {
|
||||||
pub summary_id: String,
|
pub act_sum_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub act_number: i64,
|
pub act_index: i64,
|
||||||
pub summary: String,
|
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
|
pub summary: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookAIGuideLineTable {
|
pub struct BookAIGuideLineTable {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
@@ -208,40 +206,44 @@ pub struct BookAIGuideLineTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookChaptersTable {
|
pub struct BookChaptersTable {
|
||||||
pub chapter_id: String,
|
pub chapter_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub user_id: String,
|
pub author_id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub hashed_title: String,
|
pub hashed_title: String,
|
||||||
|
pub words_count: Option<i64>,
|
||||||
pub chapter_order: i64,
|
pub chapter_order: i64,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookChapterContentTable {
|
pub struct BookChapterContentTable {
|
||||||
pub content_id: String,
|
pub content_id: String,
|
||||||
pub chapter_id: String,
|
pub chapter_id: String,
|
||||||
pub user_id: String,
|
pub author_id: String,
|
||||||
pub content: Option<String>,
|
|
||||||
pub version: i64,
|
pub version: i64,
|
||||||
|
pub content: Option<serde_json::Value>,
|
||||||
|
pub words_count: Option<i64>,
|
||||||
|
pub time_on_it: Option<i64>,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookChapterInfosTable {
|
pub struct BookChapterInfosTable {
|
||||||
|
pub chapter_info_id: String,
|
||||||
pub chapter_id: String,
|
pub chapter_id: String,
|
||||||
pub user_id: String,
|
pub act_id: Option<i64>,
|
||||||
|
pub incident_id: Option<String>,
|
||||||
|
pub plot_point_id: Option<String>,
|
||||||
|
pub book_id: String,
|
||||||
|
pub author_id: String,
|
||||||
pub summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
pub notes: Option<String>,
|
pub goal: Option<String>,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookCharactersTable {
|
pub struct BookCharactersTable {
|
||||||
pub character_id: String,
|
pub character_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
@@ -249,13 +251,13 @@ pub struct BookCharactersTable {
|
|||||||
pub first_name: String,
|
pub first_name: String,
|
||||||
pub last_name: Option<String>,
|
pub last_name: Option<String>,
|
||||||
pub nickname: Option<String>,
|
pub nickname: Option<String>,
|
||||||
pub age: Option<i64>,
|
pub age: Option<String>,
|
||||||
pub gender: Option<String>,
|
pub gender: Option<String>,
|
||||||
pub species: Option<String>,
|
pub species: Option<String>,
|
||||||
pub nationality: Option<String>,
|
pub nationality: Option<String>,
|
||||||
pub status: Option<String>,
|
pub status: Option<String>,
|
||||||
pub title: Option<String>,
|
|
||||||
pub category: String,
|
pub category: String,
|
||||||
|
pub title: Option<String>,
|
||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
pub role: Option<String>,
|
pub role: Option<String>,
|
||||||
pub biography: Option<String>,
|
pub biography: Option<String>,
|
||||||
@@ -269,7 +271,6 @@ pub struct BookCharactersTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookCharactersAttributesTable {
|
pub struct BookCharactersAttributesTable {
|
||||||
pub attr_id: String,
|
pub attr_id: String,
|
||||||
pub character_id: String,
|
pub character_id: String,
|
||||||
@@ -280,7 +281,6 @@ pub struct BookCharactersAttributesTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookGuideLineTable {
|
pub struct BookGuideLineTable {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
@@ -298,33 +298,27 @@ pub struct BookGuideLineTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookIncidentsTable {
|
pub struct BookIncidentsTable {
|
||||||
pub incident_id: String,
|
pub incident_id: String,
|
||||||
pub chapter_id: String,
|
pub author_id: String,
|
||||||
pub user_id: String,
|
pub book_id: String,
|
||||||
pub name: String,
|
pub title: String,
|
||||||
pub hashed_name: String,
|
pub hashed_title: String,
|
||||||
pub description: Option<String>,
|
pub summary: Option<String>,
|
||||||
pub incident_order: i64,
|
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookIssuesTable {
|
pub struct BookIssuesTable {
|
||||||
pub issue_id: String,
|
pub issue_id: String,
|
||||||
pub chapter_id: String,
|
pub author_id: String,
|
||||||
pub user_id: String,
|
pub book_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub hashed_name: String,
|
pub hashed_issue_name: String,
|
||||||
pub description: Option<String>,
|
|
||||||
pub issue_order: i64,
|
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookLocationTable {
|
pub struct BookLocationTable {
|
||||||
pub loc_id: String,
|
pub loc_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
@@ -335,26 +329,24 @@ pub struct BookLocationTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookPlotPointsTable {
|
pub struct BookPlotPointsTable {
|
||||||
pub plot_point_id: String,
|
pub plot_point_id: String,
|
||||||
pub chapter_id: String,
|
pub title: String,
|
||||||
pub user_id: String,
|
pub hashed_title: String,
|
||||||
pub name: String,
|
pub summary: Option<String>,
|
||||||
pub hashed_name: String,
|
pub linked_incident_id: Option<String>,
|
||||||
pub description: Option<String>,
|
pub author_id: String,
|
||||||
pub plot_point_order: i64,
|
pub book_id: String,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookWorldTable {
|
pub struct BookWorldTable {
|
||||||
pub world_id: String,
|
pub world_id: String,
|
||||||
pub book_id: String,
|
|
||||||
pub user_id: String,
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub hashed_name: String,
|
pub hashed_name: String,
|
||||||
|
pub author_id: String,
|
||||||
|
pub book_id: String,
|
||||||
pub history: Option<String>,
|
pub history: Option<String>,
|
||||||
pub politics: Option<String>,
|
pub politics: Option<String>,
|
||||||
pub economy: Option<String>,
|
pub economy: Option<String>,
|
||||||
@@ -364,7 +356,6 @@ pub struct BookWorldTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookWorldElementsTable {
|
pub struct BookWorldElementsTable {
|
||||||
pub element_id: String,
|
pub element_id: String,
|
||||||
pub world_id: String,
|
pub world_id: String,
|
||||||
@@ -377,10 +368,9 @@ pub struct BookWorldElementsTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct LocationElementTable {
|
pub struct LocationElementTable {
|
||||||
pub element_id: String,
|
pub element_id: String,
|
||||||
pub location_id: String,
|
pub location: String,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub element_name: String,
|
pub element_name: String,
|
||||||
pub original_name: String,
|
pub original_name: String,
|
||||||
@@ -389,7 +379,6 @@ pub struct LocationElementTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct LocationSubElementTable {
|
pub struct LocationSubElementTable {
|
||||||
pub sub_element_id: String,
|
pub sub_element_id: String,
|
||||||
pub element_id: String,
|
pub element_id: String,
|
||||||
@@ -401,16 +390,15 @@ pub struct LocationSubElementTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookSpellsTable {
|
pub struct BookSpellsTable {
|
||||||
pub spell_id: String,
|
pub spell_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub name_hash: String,
|
pub name_hash: String,
|
||||||
pub description: String,
|
pub description: Option<String>,
|
||||||
pub appearance: String,
|
pub appearance: Option<String>,
|
||||||
pub tags: String,
|
pub tags: Option<String>,
|
||||||
pub power_level: Option<String>,
|
pub power_level: Option<String>,
|
||||||
pub components: Option<String>,
|
pub components: Option<String>,
|
||||||
pub limitations: Option<String>,
|
pub limitations: Option<String>,
|
||||||
@@ -419,13 +407,12 @@ pub struct BookSpellsTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BookSpellTagsTable {
|
pub struct BookSpellTagsTable {
|
||||||
pub tag_id: String,
|
pub tag_id: String,
|
||||||
pub book_id: String,
|
pub book_id: String,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub hashed_name: String,
|
pub name_hash: String,
|
||||||
pub color: Option<String>,
|
pub color: Option<String>,
|
||||||
pub last_update: i64,
|
pub last_update: i64,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,29 +49,32 @@ pub fn save_complete_book(conn: &Connection, user_id: &str, data: &CompleteBook,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for incident in &data.incidents {
|
for incident in &data.incidents {
|
||||||
let encrypted_incident_title: String = encrypt_data_with_user_key(&incident.name, &user_encryption_key)?;
|
let encrypted_incident_title: String = encrypt_data_with_user_key(&incident.title, &user_encryption_key)?;
|
||||||
let encrypted_incident_summary: Option<String> = if let Some(ref description) = incident.description { Some(encrypt_data_with_user_key(description, &user_encryption_key)?) } else { None };
|
let encrypted_incident_summary: Option<String> = 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.chapter_id, &encrypted_incident_title, &incident.hashed_name, encrypted_incident_summary.as_deref(), incident.last_update, lang)?;
|
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); }
|
if !incident_inserted { return Ok(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
for plot_point in &data.plot_points {
|
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_title: String = encrypt_data_with_user_key(&plot_point.title, &user_encryption_key)?;
|
||||||
let encrypted_plot_point_summary: Option<String> = if let Some(ref description) = plot_point.description { Some(encrypt_data_with_user_key(description, &user_encryption_key)?) } else { None };
|
let encrypted_plot_point_summary: Option<String> = 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_name, encrypted_plot_point_summary.as_deref(), None, user_id, &plot_point.chapter_id, plot_point.last_update, lang)?;
|
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); }
|
if !plot_point_inserted { return Ok(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
for chapter_content in &data.chapter_contents {
|
for chapter_content in &data.chapter_contents {
|
||||||
let encrypted_chapter_content: Option<String> = if let Some(ref content) = chapter_content.content { Some(encrypt_data_with_user_key(content, &user_encryption_key)?) } else { None };
|
let content_str: Option<String> = chapter_content.content.as_ref().map(|v| serde_json::to_string(v).unwrap_or_default());
|
||||||
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 encrypted_chapter_content: Option<String> = 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); }
|
if !chapter_content_inserted { return Ok(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
for chapter_info in &data.chapter_infos {
|
for chapter_info in &data.chapter_infos {
|
||||||
let encrypted_chapter_summary: Option<String> = if let Some(ref summary) = chapter_info.summary { Some(encrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None };
|
let encrypted_chapter_summary: Option<String> = 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<String> = if let Some(ref notes) = chapter_info.notes { Some(encrypt_data_with_user_key(notes, &user_encryption_key)?) } else { None };
|
let encrypted_chapter_goal: Option<String> = 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_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 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); }
|
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)?,
|
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 },
|
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 },
|
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 },
|
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 },
|
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 },
|
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 {
|
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_name: String = encrypt_data_with_user_key(&location_element.element_name, &user_encryption_key)?;
|
||||||
let encrypted_location_element_description: Option<String> = if let Some(ref element_description) = location_element.element_description { Some(encrypt_data_with_user_key(element_description, &user_encryption_key)?) } else { None };
|
let encrypted_location_element_description: Option<String> = 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); }
|
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 {
|
for act_summary in &data.act_summaries {
|
||||||
let encrypted_act_summary: String = encrypt_data_with_user_key(&act_summary.summary, &user_encryption_key)?;
|
let encrypted_act_summary: Option<String> = 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.summary_id, &act_summary.book_id, user_id, act_summary.act_number, Some(&encrypted_act_summary), act_summary.last_update, lang)?;
|
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); }
|
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 {
|
for issue in &data.issues {
|
||||||
let encrypted_issue_name: String = encrypt_data_with_user_key(&issue.name, &user_encryption_key)?;
|
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); }
|
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 {
|
for spell_tag in &data.spell_tags {
|
||||||
let encrypted_tag_name: String = encrypt_data_with_user_key(&spell_tag.name, &user_encryption_key)?;
|
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); }
|
if !spell_tag_inserted { return Ok(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
for spell in &data.spells {
|
for spell in &data.spells {
|
||||||
let encrypted_name: String = encrypt_data_with_user_key(&spell.name, &user_encryption_key)?;
|
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_description: Option<String> = if let Some(ref description) = spell.description { Some(encrypt_data_with_user_key(description, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_appearance: String = encrypt_data_with_user_key(&spell.appearance, &user_encryption_key)?;
|
let encrypted_appearance: Option<String> = if let Some(ref appearance) = spell.appearance { Some(encrypt_data_with_user_key(appearance, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_tags: String = encrypt_data_with_user_key(&spell.tags, &user_encryption_key)?;
|
let encrypted_tags: Option<String> = if let Some(ref tags) = spell.tags { Some(encrypt_data_with_user_key(tags, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_power_level: Option<String> = if let Some(ref power_level) = spell.power_level { Some(encrypt_data_with_user_key(power_level, &user_encryption_key)?) } else { None };
|
let encrypted_power_level: Option<String> = 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<String> = if let Some(ref components) = spell.components { Some(encrypt_data_with_user_key(components, &user_encryption_key)?) } else { None };
|
let encrypted_components: Option<String> = if let Some(ref components) = spell.components { Some(encrypt_data_with_user_key(components, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_limitations: Option<String> = if let Some(ref limitations) = spell.limitations { Some(encrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None };
|
let encrypted_limitations: Option<String> = if let Some(ref limitations) = spell.limitations { Some(encrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_notes: Option<String> = if let Some(ref notes) = spell.notes { Some(encrypt_data_with_user_key(notes, &user_encryption_key)?) } else { None };
|
let encrypted_notes: Option<String> = 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); }
|
if !spell_inserted { return Ok(false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,11 +195,11 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat
|
|||||||
if !act_summary_results.is_empty() {
|
if !act_summary_results.is_empty() {
|
||||||
let act_summary_record = &act_summary_results[0];
|
let act_summary_record = &act_summary_results[0];
|
||||||
decrypted_act_summaries.push(BookActSummariesTable {
|
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(),
|
book_id: act_summary_record.book_id.clone(),
|
||||||
user_id: act_summary_record.user_id.clone(),
|
user_id: act_summary_record.user_id.clone(),
|
||||||
act_number: act_summary_record.act_index,
|
act_index: 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() },
|
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,
|
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 {
|
decrypted_chapters.push(BookChaptersTable {
|
||||||
chapter_id: chapter_record.chapter_id.clone(),
|
chapter_id: chapter_record.chapter_id.clone(),
|
||||||
book_id: chapter_record.book_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)?,
|
title: decrypt_data_with_user_key(&chapter_record.title, &user_encryption_key)?,
|
||||||
hashed_title: chapter_record.hashed_title.clone(),
|
hashed_title: chapter_record.hashed_title.clone(),
|
||||||
|
words_count: chapter_record._words_count,
|
||||||
chapter_order: chapter_record.chapter_order,
|
chapter_order: chapter_record.chapter_order,
|
||||||
last_update: chapter_record.last_update,
|
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];
|
let plot_point_record = &plot_point_results[0];
|
||||||
decrypted_plot_points.push(BookPlotPointsTable {
|
decrypted_plot_points.push(BookPlotPointsTable {
|
||||||
plot_point_id: plot_point_record.plot_point_id.clone(),
|
plot_point_id: plot_point_record.plot_point_id.clone(),
|
||||||
chapter_id: plot_point_record.linked_incident_id.clone().unwrap_or_default(),
|
title: decrypt_data_with_user_key(&plot_point_record.title, &user_encryption_key)?,
|
||||||
user_id: plot_point_record.author_id.clone(),
|
hashed_title: plot_point_record.hashed_title.clone(),
|
||||||
name: decrypt_data_with_user_key(&plot_point_record.title, &user_encryption_key)?,
|
summary: if let Some(ref summary) = plot_point_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None },
|
||||||
hashed_name: plot_point_record.hashed_title.clone(),
|
linked_incident_id: plot_point_record.linked_incident_id.clone(),
|
||||||
description: if let Some(ref summary) = plot_point_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None },
|
author_id: plot_point_record.author_id.clone(),
|
||||||
plot_point_order: 0,
|
book_id: plot_point_record._book_id.clone(),
|
||||||
last_update: plot_point_record.last_update,
|
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];
|
let incident_record = &incident_results[0];
|
||||||
decrypted_incidents.push(BookIncidentsTable {
|
decrypted_incidents.push(BookIncidentsTable {
|
||||||
incident_id: incident_record.incident_id.clone(),
|
incident_id: incident_record.incident_id.clone(),
|
||||||
chapter_id: incident_record.book_id.clone(),
|
author_id: incident_record.author_id.clone(),
|
||||||
user_id: incident_record.author_id.clone(),
|
book_id: incident_record.book_id.clone(),
|
||||||
name: decrypt_data_with_user_key(&incident_record.title, &user_encryption_key)?,
|
title: decrypt_data_with_user_key(&incident_record.title, &user_encryption_key)?,
|
||||||
hashed_name: incident_record.hashed_title.clone(),
|
hashed_title: 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 },
|
summary: if let Some(ref summary) = incident_record.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None },
|
||||||
incident_order: 0,
|
|
||||||
last_update: incident_record.last_update,
|
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::BookChapterContentTable> = chapter_content_repo::fetch_complete_chapter_content_by_id(conn, chapter_content_id, lang)?;
|
let chapter_content_results: Vec<chapter_content_repo::BookChapterContentTable> = chapter_content_repo::fetch_complete_chapter_content_by_id(conn, chapter_content_id, lang)?;
|
||||||
if !chapter_content_results.is_empty() {
|
if !chapter_content_results.is_empty() {
|
||||||
let chapter_content_record = &chapter_content_results[0];
|
let chapter_content_record = &chapter_content_results[0];
|
||||||
|
let decrypted_content: Option<serde_json::Value> = 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 {
|
decrypted_chapter_contents.push(BookChapterContentTable {
|
||||||
content_id: chapter_content_record.content_id.clone(),
|
content_id: chapter_content_record.content_id.clone(),
|
||||||
chapter_id: chapter_content_record.chapter_id.clone(),
|
chapter_id: chapter_content_record.chapter_id.clone(),
|
||||||
user_id: chapter_content_record.author_id.clone(),
|
author_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 },
|
|
||||||
version: chapter_content_record.version,
|
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,
|
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() {
|
if !chapter_info_results.is_empty() {
|
||||||
let chapter_info_record = &chapter_info_results[0];
|
let chapter_info_record = &chapter_info_results[0];
|
||||||
decrypted_chapter_infos.push(BookChapterInfosTable {
|
decrypted_chapter_infos.push(BookChapterInfosTable {
|
||||||
|
chapter_info_id: chapter_info_record._chapter_info_id.clone(),
|
||||||
chapter_id: chapter_info_record.chapter_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 },
|
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,
|
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)?,
|
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 },
|
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 },
|
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 },
|
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 },
|
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 },
|
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];
|
let location_element_record = &location_element_results[0];
|
||||||
decrypted_location_elements.push(LocationElementTable {
|
decrypted_location_elements.push(LocationElementTable {
|
||||||
element_id: location_element_record.element_id.clone(),
|
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(),
|
user_id: location_element_record.user_id.clone(),
|
||||||
element_name: decrypt_data_with_user_key(&location_element_record.element_name, &user_encryption_key)?,
|
element_name: decrypt_data_with_user_key(&location_element_record.element_name, &user_encryption_key)?,
|
||||||
original_name: location_element_record.original_name.clone(),
|
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];
|
let world_record = &world_results[0];
|
||||||
decrypted_worlds.push(BookWorldTable {
|
decrypted_worlds.push(BookWorldTable {
|
||||||
world_id: world_record.world_id.clone(),
|
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)?,
|
name: decrypt_data_with_user_key(&world_record.name, &user_encryption_key)?,
|
||||||
hashed_name: world_record.hashed_name.clone(),
|
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 },
|
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 },
|
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 },
|
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];
|
let issue_record = &issue_results[0];
|
||||||
decrypted_issues.push(BookIssuesTable {
|
decrypted_issues.push(BookIssuesTable {
|
||||||
issue_id: issue_record.issue_id.clone(),
|
issue_id: issue_record.issue_id.clone(),
|
||||||
chapter_id: issue_record.book_id.clone(),
|
author_id: issue_record.author_id.clone(),
|
||||||
user_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)?,
|
name: decrypt_data_with_user_key(&issue_record.name, &user_encryption_key)?,
|
||||||
hashed_name: issue_record.hashed_issue_name.clone(),
|
hashed_issue_name: issue_record.hashed_issue_name.clone(),
|
||||||
description: None,
|
|
||||||
issue_order: 0,
|
|
||||||
last_update: issue_record.last_update,
|
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,
|
book_id: spell_tag_record.book_id,
|
||||||
user_id: spell_tag_record.user_id,
|
user_id: spell_tag_record.user_id,
|
||||||
name: decrypt_data_with_user_key(&spell_tag_record.name, &user_encryption_key)?,
|
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,
|
color: spell_tag_record.color,
|
||||||
last_update: spell_tag_record.last_update,
|
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,
|
user_id: spell_record.user_id,
|
||||||
name: decrypt_data_with_user_key(&spell_record.name, &user_encryption_key)?,
|
name: decrypt_data_with_user_key(&spell_record.name, &user_encryption_key)?,
|
||||||
name_hash: spell_record.name_hash,
|
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() },
|
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() { String::new() } else { decrypt_data_with_user_key(appearance, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() },
|
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() { String::new() } else { decrypt_data_with_user_key(tags, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() },
|
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 },
|
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 },
|
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 },
|
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() {
|
if !server_act_summaries.is_empty() {
|
||||||
for server_act_summary in server_act_summaries {
|
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 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 server_act_summary.summary.is_empty() { "" } else { &server_act_summary.summary }, &user_encryption_key)?;
|
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 {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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() {
|
if !server_plot_points.is_empty() {
|
||||||
for server_plot_point in server_plot_points {
|
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_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 description) = server_plot_point.description { description } else { "" }, &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)?;
|
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 {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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_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)?;
|
||||||
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)?;
|
|
||||||
if !insert_successful { return Ok(false); }
|
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() {
|
if !server_incidents.is_empty() {
|
||||||
for server_incident in server_incidents {
|
for server_incident in server_incidents {
|
||||||
let encrypted_title: String = encrypt_data_with_user_key(&server_incident.name, &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 description) = server_incident.description { description } else { "" }, &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)?;
|
let incident_exists: bool = incident_repo::incident_exist(conn, user_id, &book_id, &server_incident.incident_id, lang)?;
|
||||||
if incident_exists {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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); }
|
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() {
|
if !server_chapter_contents.is_empty() {
|
||||||
for server_chapter_content in server_chapter_contents {
|
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 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 {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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); }
|
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 {
|
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 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_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 {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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); }
|
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)?,
|
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)?),
|
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)?),
|
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)?),
|
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)?),
|
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)?),
|
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)?;
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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); }
|
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 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)?;
|
let encrypted_name: String = encrypt_data_with_user_key(&server_issue.name, &user_encryption_key)?;
|
||||||
if issue_exists {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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); }
|
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 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)?;
|
let encrypted_name: String = encrypt_data_with_user_key(&server_spell_tag.name, &user_encryption_key)?;
|
||||||
if spell_tag_exists {
|
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); }
|
if !update_successful { return Ok(false); }
|
||||||
} else {
|
} 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); }
|
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 {
|
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 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_name: String = encrypt_data_with_user_key(&server_spell.name, &user_encryption_key)?;
|
||||||
let encrypted_description: Option<String> = if server_spell.description.is_empty() { None } else { Some(encrypt_data_with_user_key(&server_spell.description, &user_encryption_key)?) };
|
let encrypted_description: Option<String> = if let Some(ref desc) = server_spell.description { Some(encrypt_data_with_user_key(desc, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_appearance: Option<String> = if server_spell.appearance.is_empty() { None } else { Some(encrypt_data_with_user_key(&server_spell.appearance, &user_encryption_key)?) };
|
let encrypted_appearance: Option<String> = if let Some(ref app) = server_spell.appearance { Some(encrypt_data_with_user_key(app, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_tags: Option<String> = if server_spell.tags.is_empty() { None } else { Some(encrypt_data_with_user_key(&server_spell.tags, &user_encryption_key)?) };
|
let encrypted_tags: Option<String> = 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<String> = 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_power_level: Option<String> = 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<String> = if let Some(ref components) = server_spell.components { Some(encrypt_data_with_user_key(components, &user_encryption_key)?) } else { None };
|
let encrypted_components: Option<String> = if let Some(ref components) = server_spell.components { Some(encrypt_data_with_user_key(components, &user_encryption_key)?) } else { None };
|
||||||
let encrypted_limitations: Option<String> = if let Some(ref limitations) = server_spell.limitations { Some(encrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None };
|
let encrypted_limitations: Option<String> = if let Some(ref limitations) = server_spell.limitations { Some(encrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None };
|
||||||
|
|||||||
@@ -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 {
|
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() };
|
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 {
|
act_summaries.push(BookActSummariesTable {
|
||||||
summary_id: act_summary.act_sum_id, book_id: act_summary.book_id,
|
act_sum_id: act_summary.act_sum_id, book_id: act_summary.book_id,
|
||||||
user_id: act_summary.user_id, act_number: act_summary.act_index,
|
user_id: act_summary.user_id, act_index: act_summary.act_index,
|
||||||
summary: decrypted_summary, last_update: act_summary.last_update,
|
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)?;
|
let decrypted_title: String = decrypt_data_with_user_key(&chapter.title, &user_encryption_key)?;
|
||||||
chapters.push(BookChaptersTable {
|
chapters.push(BookChaptersTable {
|
||||||
chapter_id: chapter.chapter_id, book_id: chapter.book_id,
|
chapter_id: chapter.chapter_id, book_id: chapter.book_id,
|
||||||
user_id: chapter.author_id, title: decrypted_title,
|
author_id: chapter.author_id, title: decrypted_title,
|
||||||
hashed_title: chapter.hashed_title, chapter_order: chapter.chapter_order,
|
hashed_title: chapter.hashed_title, words_count: chapter._words_count,
|
||||||
last_update: chapter.last_update,
|
chapter_order: chapter.chapter_order, last_update: chapter.last_update,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut chapter_contents: Vec<BookChapterContentTable> = Vec::with_capacity(encrypted_chapter_contents.len());
|
let mut chapter_contents: Vec<BookChapterContentTable> = Vec::with_capacity(encrypted_chapter_contents.len());
|
||||||
for chapter_content in encrypted_chapter_contents {
|
for chapter_content in encrypted_chapter_contents {
|
||||||
let decrypted_content: Option<String> = if let Some(ref content) = chapter_content.content { Some(decrypt_data_with_user_key(content, &user_encryption_key)?) } else { None };
|
let decrypted_content: Option<serde_json::Value> = 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 {
|
chapter_contents.push(BookChapterContentTable {
|
||||||
content_id: chapter_content.content_id, chapter_id: chapter_content.chapter_id,
|
content_id: chapter_content.content_id, chapter_id: chapter_content.chapter_id,
|
||||||
user_id: chapter_content.author_id, content: decrypted_content,
|
author_id: chapter_content.author_id, version: chapter_content.version,
|
||||||
version: chapter_content.version, last_update: chapter_content.last_update,
|
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<String> = if let Some(ref summary) = chapter_info.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None };
|
let decrypted_summary: Option<String> = if let Some(ref summary) = chapter_info.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None };
|
||||||
let decrypted_goal: Option<String> = if let Some(ref goal) = chapter_info.goal { Some(decrypt_data_with_user_key(goal, &user_encryption_key)?) } else { None };
|
let decrypted_goal: Option<String> = 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_infos.push(BookChapterInfosTable {
|
||||||
chapter_id: chapter_info.chapter_id, user_id: chapter_info.author_id,
|
chapter_info_id: chapter_info._chapter_info_id.clone(),
|
||||||
summary: decrypted_summary, notes: decrypted_goal,
|
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,
|
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_first_name: String = decrypt_data_with_user_key(&character.first_name, &user_encryption_key)?;
|
||||||
let decrypted_last_name: Option<String> = if let Some(ref last_name) = character.last_name { Some(decrypt_data_with_user_key(last_name, &user_encryption_key)?) } else { None };
|
let decrypted_last_name: Option<String> = 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<String> = if let Some(ref nickname) = character.nickname { Some(decrypt_data_with_user_key(nickname, &user_encryption_key)?) } else { None };
|
let decrypted_nickname: Option<String> = if let Some(ref nickname) = character.nickname { Some(decrypt_data_with_user_key(nickname, &user_encryption_key)?) } else { None };
|
||||||
let decrypted_age: Option<i64> = 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<String> = if let Some(ref age) = character.age { Some(decrypt_data_with_user_key(age, &user_encryption_key)?) } else { None };
|
||||||
let decrypted_gender: Option<String> = if let Some(ref gender) = character.gender { Some(decrypt_data_with_user_key(gender, &user_encryption_key)?) } else { None };
|
let decrypted_gender: Option<String> = if let Some(ref gender) = character.gender { Some(decrypt_data_with_user_key(gender, &user_encryption_key)?) } else { None };
|
||||||
let decrypted_species: Option<String> = if let Some(ref species) = character.species { Some(decrypt_data_with_user_key(species, &user_encryption_key)?) } else { None };
|
let decrypted_species: Option<String> = if let Some(ref species) = character.species { Some(decrypt_data_with_user_key(species, &user_encryption_key)?) } else { None };
|
||||||
let decrypted_nationality: Option<String> = if let Some(ref nationality) = character.nationality { Some(decrypt_data_with_user_key(nationality, &user_encryption_key)?) } else { None };
|
let decrypted_nationality: Option<String> = 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_title: String = decrypt_data_with_user_key(&incident.title, &user_encryption_key)?;
|
||||||
let decrypted_summary: Option<String> = if let Some(ref summary) = incident.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None };
|
let decrypted_summary: Option<String> = if let Some(ref summary) = incident.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None };
|
||||||
incidents.push(BookIncidentsTable {
|
incidents.push(BookIncidentsTable {
|
||||||
incident_id: incident.incident_id, chapter_id: String::new(),
|
incident_id: incident.incident_id, author_id: incident.author_id,
|
||||||
user_id: incident.author_id, name: decrypted_title,
|
book_id: incident.book_id, title: decrypted_title,
|
||||||
hashed_name: incident.hashed_title, description: decrypted_summary,
|
hashed_title: incident.hashed_title, summary: decrypted_summary,
|
||||||
incident_order: 0, last_update: incident.last_update,
|
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 {
|
for issue in encrypted_issues {
|
||||||
let decrypted_name: String = decrypt_data_with_user_key(&issue.name, &user_encryption_key)?;
|
let decrypted_name: String = decrypt_data_with_user_key(&issue.name, &user_encryption_key)?;
|
||||||
issues.push(BookIssuesTable {
|
issues.push(BookIssuesTable {
|
||||||
issue_id: issue.issue_id, chapter_id: String::new(),
|
issue_id: issue.issue_id, author_id: issue.author_id,
|
||||||
user_id: issue.author_id, name: decrypted_name,
|
book_id: issue.book_id, name: decrypted_name,
|
||||||
hashed_name: issue.hashed_issue_name, description: None,
|
hashed_issue_name: issue.hashed_issue_name,
|
||||||
issue_order: 0, last_update: issue.last_update,
|
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_title: String = decrypt_data_with_user_key(&plot_point.title, &user_encryption_key)?;
|
||||||
let decrypted_summary: Option<String> = if let Some(ref summary) = plot_point.summary { Some(decrypt_data_with_user_key(summary, &user_encryption_key)?) } else { None };
|
let decrypted_summary: Option<String> = 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_points.push(BookPlotPointsTable {
|
||||||
plot_point_id: plot_point.plot_point_id, chapter_id: String::new(),
|
plot_point_id: plot_point.plot_point_id, title: decrypted_title,
|
||||||
user_id: plot_point.author_id, name: decrypted_title,
|
hashed_title: plot_point.hashed_title, summary: decrypted_summary,
|
||||||
hashed_name: plot_point.hashed_title, description: decrypted_summary,
|
linked_incident_id: plot_point.linked_incident_id,
|
||||||
plot_point_order: 0, last_update: plot_point.last_update,
|
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<String> = if let Some(ref religion) = world.religion { Some(decrypt_data_with_user_key(religion, &user_encryption_key)?) } else { None };
|
let decrypted_religion: Option<String> = if let Some(ref religion) = world.religion { Some(decrypt_data_with_user_key(religion, &user_encryption_key)?) } else { None };
|
||||||
let decrypted_languages: Option<String> = if let Some(ref languages) = world.languages { Some(decrypt_data_with_user_key(languages, &user_encryption_key)?) } else { None };
|
let decrypted_languages: Option<String> = if let Some(ref languages) = world.languages { Some(decrypt_data_with_user_key(languages, &user_encryption_key)?) } else { None };
|
||||||
worlds.push(BookWorldTable {
|
worlds.push(BookWorldTable {
|
||||||
world_id: world.world_id, book_id: world.book_id,
|
world_id: world.world_id, name: decrypted_name,
|
||||||
user_id: world.author_id, name: decrypted_name,
|
hashed_name: world.hashed_name, author_id: world.author_id,
|
||||||
hashed_name: world.hashed_name, history: decrypted_history,
|
book_id: world.book_id,
|
||||||
|
history: decrypted_history,
|
||||||
politics: decrypted_politics, economy: decrypted_economy,
|
politics: decrypted_politics, economy: decrypted_economy,
|
||||||
religion: decrypted_religion, languages: decrypted_languages,
|
religion: decrypted_religion, languages: decrypted_languages,
|
||||||
last_update: world.last_update,
|
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_name: String = decrypt_data_with_user_key(&location_element.element_name, &user_encryption_key)?;
|
||||||
let decrypted_element_description: Option<String> = if let Some(ref element_description) = location_element.element_description { Some(decrypt_data_with_user_key(element_description, &user_encryption_key)?) } else { None };
|
let decrypted_element_description: Option<String> = 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 {
|
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,
|
user_id: location_element.user_id, element_name: decrypted_element_name,
|
||||||
original_name: location_element.original_name,
|
original_name: location_element.original_name,
|
||||||
element_description: decrypted_element_description,
|
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<BookSpellsTable> = Vec::with_capacity(encrypted_spells.len());
|
let mut spells: Vec<BookSpellsTable> = Vec::with_capacity(encrypted_spells.len());
|
||||||
for spell in encrypted_spells {
|
for spell in encrypted_spells {
|
||||||
let decrypted_name: String = decrypt_data_with_user_key(&spell.name, &user_encryption_key)?;
|
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_description: Option<String> = 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: 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_appearance: Option<String> = 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: 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_tags: Option<String> = 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<String> = 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_power_level: Option<String> = 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<String> = 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_components: Option<String> = 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<String> = 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 };
|
let decrypted_limitations: Option<String> = 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 {
|
spell_tags.push(BookSpellTagsTable {
|
||||||
tag_id: spell_tag.tag_id, book_id: spell_tag.book_id,
|
tag_id: spell_tag.tag_id, book_id: spell_tag.book_id,
|
||||||
user_id: spell_tag.user_id, name: decrypted_name,
|
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,
|
last_update: spell_tag.last_update,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user