Add offline mode support with PIN configuration and management
- Introduce `OfflinePinSetup` component for users to configure secure offline access. - Add new `AIUsageContext` and extend `OfflineProvider` for offline-related state management. - Implement offline login functionality in `electron/main.ts` with PIN verification and fallback support. - Enhance IPC handlers to manage offline mode data, PIN setup, and synchronization. - Update localization files (`en.json`, `fr.json`) with offline mode and PIN-related strings. - Add `bcrypt` and `@types/bcrypt` dependencies for secure PIN hashing and validation. - Refactor login and session management to handle offline mode scenarios with improved error handling and flow.
This commit is contained in:
94
app/page.tsx
94
app/page.tsx
@@ -33,6 +33,7 @@ import {LangContext} from "@/context/LangContext";
|
||||
import {AIUsageContext} from "@/context/AIUsageContext";
|
||||
import OfflineProvider from "@/context/OfflineProvider";
|
||||
import OfflineContext from "@/context/OfflineContext";
|
||||
import OfflinePinSetup from "@/components/offline/OfflinePinSetup";
|
||||
|
||||
const messagesMap = {
|
||||
fr: frMessages,
|
||||
@@ -70,6 +71,7 @@ function ScribeContent() {
|
||||
|
||||
const [isTermsAccepted, setIsTermsAccepted] = useState<boolean>(false);
|
||||
const [homeStepsGuide, setHomeStepsGuide] = useState<boolean>(false);
|
||||
const [showPinSetup, setShowPinSetup] = useState<boolean>(false);
|
||||
|
||||
const homeSteps: GuideStep[] = [
|
||||
{
|
||||
@@ -160,6 +162,31 @@ function ScribeContent() {
|
||||
getLastChapter().then();
|
||||
}
|
||||
}, [currentBook]);
|
||||
|
||||
// Check for PIN setup after successful connection
|
||||
useEffect(() => {
|
||||
async function checkPinSetup() {
|
||||
if (session.isConnected && window.electron) {
|
||||
try {
|
||||
const offlineStatus = await window.electron.offlineModeGet();
|
||||
console.log('[Page] Session connected, offline status:', offlineStatus);
|
||||
|
||||
if (!offlineStatus.hasPin) {
|
||||
console.log('[Page] No PIN configured, will show setup dialog');
|
||||
// Show PIN setup dialog after a short delay
|
||||
setTimeout(() => {
|
||||
console.log('[Page] Showing PIN setup dialog');
|
||||
setShowPinSetup(true);
|
||||
}, 2000); // 2 seconds delay after page load
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Page] Error checking offline mode:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkPinSetup();
|
||||
}, [session.isConnected]); // Run when session connection status changes
|
||||
|
||||
async function handleHomeTour(): Promise<void> {
|
||||
try {
|
||||
@@ -221,6 +248,23 @@ function ScribeContent() {
|
||||
console.error('[Page] Failed to initialize user:', initResult.error);
|
||||
} else {
|
||||
console.log('[Page] User initialized successfully, key created:', initResult.keyCreated);
|
||||
|
||||
// Check if PIN is configured for offline mode
|
||||
try {
|
||||
const offlineStatus = await window.electron.offlineModeGet();
|
||||
console.log('[Page] Offline status:', offlineStatus);
|
||||
if (!offlineStatus.hasPin) {
|
||||
// First login or no PIN configured yet
|
||||
// Show PIN setup dialog after a short delay
|
||||
console.log('[Page] No PIN configured, will show setup dialog');
|
||||
setTimeout(() => {
|
||||
console.log('[Page] Showing PIN setup dialog');
|
||||
setShowPinSetup(true);
|
||||
}, 2000); // 2 seconds delay after successful login
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Page] Error checking offline mode:', error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Page] Error initializing user:', error);
|
||||
@@ -261,6 +305,44 @@ 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);
|
||||
}
|
||||
}
|
||||
} catch (offlineError) {
|
||||
console.error('[Auth] Error checking offline mode:', offlineError);
|
||||
}
|
||||
}
|
||||
|
||||
// If not in offline mode or failed to load local data, show error and logout
|
||||
if (e instanceof Error) {
|
||||
errorMessage(e.message);
|
||||
} else {
|
||||
@@ -381,6 +463,18 @@ function ScribeContent() {
|
||||
{
|
||||
!isTermsAccepted && <TermsOfUse onAccept={handleTermsAcceptance}/>
|
||||
}
|
||||
{
|
||||
showPinSetup && window.electron && (
|
||||
<OfflinePinSetup
|
||||
showOnFirstLogin={true}
|
||||
onClose={() => setShowPinSetup(false)}
|
||||
onSuccess={() => {
|
||||
setShowPinSetup(false);
|
||||
console.log('[Page] PIN configured successfully');
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</AIUsageContext.Provider>
|
||||
</ChapterContext.Provider>
|
||||
</BookContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user