Remove SyncService and introduce context-based offline mode and state management
- Delete `SyncService` and its associated bidirectional synchronization logic. - Add multiple context providers (`OfflineProvider`, `AlertProvider`, `LangContext`, `UserContext`, `SessionContext`, `WorldContext`, `SettingBookContext`) for contextual state management. - Implement `SecureStorage` for OS-level secure data encryption and replace dependency on `SyncService` synchronization. - Update localization files (`en.json`, `fr.json`) with offline mode and error-related strings.
This commit is contained in:
127
app/page.tsx
127
app/page.tsx
@@ -34,6 +34,7 @@ import {AIUsageContext} from "@/context/AIUsageContext";
|
||||
import OfflineProvider from "@/context/OfflineProvider";
|
||||
import OfflineContext from "@/context/OfflineContext";
|
||||
import OfflinePinSetup from "@/components/offline/OfflinePinSetup";
|
||||
import OfflinePinVerify from "@/components/offline/OfflinePinVerify";
|
||||
|
||||
const messagesMap = {
|
||||
fr: frMessages,
|
||||
@@ -72,6 +73,7 @@ function ScribeContent() {
|
||||
const [isTermsAccepted, setIsTermsAccepted] = useState<boolean>(false);
|
||||
const [homeStepsGuide, setHomeStepsGuide] = useState<boolean>(false);
|
||||
const [showPinSetup, setShowPinSetup] = useState<boolean>(false);
|
||||
const [showPinVerify, setShowPinVerify] = useState<boolean>(false);
|
||||
|
||||
const homeSteps: GuideStep[] = [
|
||||
{
|
||||
@@ -188,6 +190,52 @@ function ScribeContent() {
|
||||
checkPinSetup();
|
||||
}, [session.isConnected]); // Run when session connection status changes
|
||||
|
||||
async function handlePinVerifySuccess(userId: string): Promise<void> {
|
||||
console.log('[OfflinePin] PIN verified successfully for user:', userId);
|
||||
|
||||
try {
|
||||
// Initialize database with user's encryption key
|
||||
if (window.electron) {
|
||||
const encryptionKey:string|null = await window.electron.getUserEncryptionKey(userId);
|
||||
if (encryptionKey) {
|
||||
await window.electron.dbInitialize(userId, encryptionKey);
|
||||
|
||||
// Load user from local DB
|
||||
const localUser = await window.electron.invoke('db:user:info');
|
||||
if (localUser && localUser.success) {
|
||||
// Use local data and continue in offline mode
|
||||
setSession({
|
||||
isConnected: true,
|
||||
user: localUser.data,
|
||||
accessToken: 'offline', // Special offline token
|
||||
});
|
||||
setShowPinVerify(false);
|
||||
setCurrentCredits(localUser.data.creditsBalance || 0);
|
||||
setAmountSpent(localUser.data.aiUsage || 0);
|
||||
|
||||
console.log('[OfflinePin] Running in offline mode');
|
||||
} else {
|
||||
errorMessage(t("homePage.errors.localDataError"));
|
||||
if (window.electron) {
|
||||
//window.electron.logout();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorMessage(t("homePage.errors.encryptionKeyError"));
|
||||
if (window.electron) {
|
||||
//window.electron.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[OfflinePin] Error initializing offline mode:', error);
|
||||
errorMessage(t("homePage.errors.offlineModeError"));
|
||||
if (window.electron) {
|
||||
//window.electron.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleHomeTour(): Promise<void> {
|
||||
try {
|
||||
const response: boolean = await System.authPostToServer<boolean>('logs/tour', {
|
||||
@@ -211,7 +259,6 @@ function ScribeContent() {
|
||||
}
|
||||
|
||||
async function checkAuthentification(): Promise<void> {
|
||||
// Essayer de récupérer le token depuis electron-store en priorité
|
||||
let token: string | null = null;
|
||||
|
||||
if (typeof window !== 'undefined' && window.electron) {
|
||||
@@ -222,11 +269,6 @@ function ScribeContent() {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback sur les cookies si pas d'Electron
|
||||
if (!token) {
|
||||
token = System.getCookie('token');
|
||||
}
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const user: UserProps = await System.authGetQueryToServer<UserProps>('user/infos', token, locale);
|
||||
@@ -239,6 +281,8 @@ function ScribeContent() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('user: ' , user);
|
||||
|
||||
// Initialize user in Electron (sets userId and creates/gets encryption key)
|
||||
if (window.electron && user.id) {
|
||||
@@ -306,35 +350,19 @@ function ScribeContent() {
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
console.log('[Auth] Server error, checking offline mode...');
|
||||
|
||||
// Check if we can use offline mode
|
||||
if (window.electron) {
|
||||
try {
|
||||
// Check offline mode status
|
||||
const offlineStatus = await window.electron.invoke('offline:mode:get');
|
||||
|
||||
// If offline mode is enabled and we have local data
|
||||
if (offlineStatus.enabled && offlineStatus.hasPin) {
|
||||
console.log('[Auth] Offline mode enabled, loading local user data');
|
||||
|
||||
// Try to load user from local DB
|
||||
try {
|
||||
const localUser = await window.electron.invoke('db:user:info');
|
||||
if (localUser && localUser.success) {
|
||||
// Use local data
|
||||
setSession({
|
||||
isConnected: true,
|
||||
user: localUser.data,
|
||||
accessToken: 'offline', // Special offline token
|
||||
});
|
||||
setIsLoading(false);
|
||||
|
||||
// Show offline mode notification
|
||||
console.log('[Auth] Running in offline mode');
|
||||
return;
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error('[Auth] Failed to load local user:', dbError);
|
||||
const offlineStatus = await window.electron.offlineModeGet();
|
||||
|
||||
if (offlineStatus.hasPin && offlineStatus.lastUserId) {
|
||||
console.log('[Auth] Server unreachable but PIN configured, showing PIN verification');
|
||||
setShowPinVerify(true);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
} else {
|
||||
if (window.electron) {
|
||||
await window.electron.removeToken();
|
||||
window.electron.logout();
|
||||
}
|
||||
}
|
||||
} catch (offlineError) {
|
||||
@@ -342,23 +370,28 @@ function ScribeContent() {
|
||||
}
|
||||
}
|
||||
|
||||
// If not in offline mode or failed to load local data, show error and logout
|
||||
// If not in offline mode or no PIN configured, show error and logout
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
errorMessage(t("homePage.errors.authenticationError"));
|
||||
}
|
||||
// Token invalide/erreur auth, supprimer et logout
|
||||
if (window.electron) {
|
||||
await window.electron.removeToken();
|
||||
window.electron.logout();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Pas de token - en Electron cela ne devrait jamais arriver
|
||||
// car main.ts vérifie le token avant d'ouvrir mainWindow
|
||||
// Si on arrive ici, c'est une erreur - fermer et ouvrir login
|
||||
if (window.electron) {
|
||||
try {
|
||||
const offlineStatus = await window.electron.offlineModeGet();
|
||||
|
||||
if (offlineStatus.hasPin && offlineStatus.lastUserId) {
|
||||
console.log('[Auth] No token but PIN configured, showing PIN verification for offline mode');
|
||||
setShowPinVerify(true);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Auth] Error checking offline mode:', error);
|
||||
}
|
||||
|
||||
window.electron.logout();
|
||||
}
|
||||
}
|
||||
@@ -475,6 +508,16 @@ function ScribeContent() {
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
showPinVerify && window.electron && (
|
||||
<OfflinePinVerify
|
||||
onSuccess={handlePinVerifySuccess}
|
||||
onCancel={():void => {
|
||||
//window.electron.logout();
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</AIUsageContext.Provider>
|
||||
</ChapterContext.Provider>
|
||||
</BookContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user