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:
natreex
2026-03-23 11:56:35 -04:00
parent 64ed90d993
commit a114592ac9
23 changed files with 588 additions and 438 deletions

View File

@@ -17,4 +17,4 @@ export const configs: Configs = {
appName: 'Eritors Scribe',
appDescription: 'Eritors Scribe est une application de prise de notes et d\'écriture collaborative.',
appVersion: packageJson.version,
};
};

View File

@@ -1372,7 +1372,29 @@
"passwordUnknown": "An unknown error occurred."
}
},
"terms": {
"title": "Terms of Use",
"subtitle": "Acceptance required to access ERitors Scribe",
"mandatory": "Mandatory acceptance",
"mandatoryDesc1": "To use our services, such as <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": {
"toggle": {
"online": "Online",
"offline": "Offline",
"switchToOnline": "Switch to online mode",
"switchToOffline": "Switch to offline mode"
},
"mode": {
"title": "Offline mode",
"backToOnline": "Back online"

View File

@@ -1371,7 +1371,29 @@
"passwordUnknown": "Une erreur inconnue est survenue."
}
},
"terms": {
"title": "Termes d'utilisation",
"subtitle": "Acceptation requise pour accéder à ERitors Scribe",
"mandatory": "Acceptation obligatoire",
"mandatoryDesc1": "Pour pouvoir utiliser nos services, tel qu'<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": {
"toggle": {
"online": "En ligne",
"offline": "Hors ligne",
"switchToOnline": "Passer en mode en ligne",
"switchToOffline": "Passer en mode hors ligne"
},
"mode": {
"title": "Mode hors-ligne",
"backToOnline": "Retour en ligne"

View File

@@ -679,7 +679,12 @@ export async function applySeriesTombstones(tombstones: TombstoneRecord[]): Prom
// ─── Window Management ──────────────────────────────────────
let loginWindowOpening = false;
export async function openLoginWindow(): Promise<void> {
if (loginWindowOpening) return;
loginWindowOpening = true;
const {WebviewWindow} = await import('@tauri-apps/api/webviewWindow');
const {getCurrentWindow} = await import('@tauri-apps/api/window');
@@ -687,6 +692,7 @@ export async function openLoginWindow(): Promise<void> {
if (existing) {
await existing.setFocus();
await getCurrentWindow().hide();
loginWindowOpening = false;
return;
}
@@ -701,6 +707,7 @@ export async function openLoginWindow(): Promise<void> {
});
loginWindow.once('tauri://created', async function () {
loginWindowOpening = false;
await getCurrentWindow().hide();
});
}
@@ -712,20 +719,20 @@ export async function loginSuccess(): Promise<void> {
const currentLabel = getCurrentWindow().label;
if (currentLabel === 'login') {
const {emit} = await import('@tauri-apps/api/event');
await emit('auth-success');
const mainWindow = await WebviewWindow.getByLabel('main');
if (mainWindow) {
await mainWindow.show();
await mainWindow.setFocus();
await mainWindow.emit('auth-success');
}
await getCurrentWindow().close();
getCurrentWindow().close();
} else {
window.location.reload();
}
}
export async function logout(): Promise<void> {
await removeToken();
await openLoginWindow();
}

View File

@@ -1,3 +1,5 @@
import {isDesktop} from '@/lib/configs';
export function getCookie(name: string): string | null {
const nameEQ: string = `${name}=`;
const allCookies: string[] = document.cookie.split(';');
@@ -14,20 +16,20 @@ export function setCookie(name: string, value: string, days: number): void {
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires: string = `expires=${date.toUTCString()}`;
let domain: string = '';
if (!/localhost|127\.0\.0\.1/.test(window.location.hostname)) {
if (!isDesktop && !/localhost|127\.0\.0\.1/.test(window.location.hostname)) {
domain = `domain=${window.location.hostname};`;
}
const secure: string = 'Secure;';
const secure: string = isDesktop ? '' : 'Secure;';
const sameSite: string = 'SameSite=Strict;';
document.cookie = `${name}=${value}; ${expires}; ${domain} path=/; ${secure} ${sameSite}`;
}
export function removeCookie(name: string): void {
let domain: string = '';
if (!/localhost|127\.0\.0\.1/.test(window.location.hostname)) {
if (!isDesktop && !/localhost|127\.0\.0\.1/.test(window.location.hostname)) {
domain = `domain=${window.location.hostname};`;
}
const secure: string = 'Secure;';
const secure: string = isDesktop ? '' : 'Secure;';
const sameSite: string = 'SameSite=Strict;';
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`;
}