Refactor PublicRoute and ModelProvider for improved user flow and state management

- Simplified the `PublicRoute` component by removing the onboarding status check, allowing the `Home` component to handle redirection based on user profile loading.
- Enhanced the `ModelProvider` component by consolidating the initialization logic and improving error handling during model status checks and downloads.
- Introduced cancellation logic in the `ModelProvider` to prevent state updates after component unmounting, ensuring better resource management and preventing memory leaks.
This commit is contained in:
Steven Enamakel
2026-02-06 01:59:39 +05:30
parent cdb416192f
commit af7ca87831
2 changed files with 46 additions and 16 deletions
+2 -5
View File
@@ -1,6 +1,5 @@
import { Navigate } from 'react-router-dom';
import { selectIsOnboarded } from '../store/authSelectors';
import { useAppSelector } from '../store/hooks';
interface PublicRouteProps {
@@ -9,13 +8,11 @@ interface PublicRouteProps {
}
/**
* Public route component that redirects authenticated users
* If logged in and onboarded -> redirect to /home
* If logged in but not onboarded -> redirect to /onboarding
* Public route component that redirects authenticated users to /home.
* Home handles the onboarding redirect once the user profile is loaded.
*/
const PublicRoute = ({ children, redirectTo }: PublicRouteProps) => {
const token = useAppSelector(state => state.auth.token);
const isOnboarded = useAppSelector(selectIsOnboarded);
// If user is logged in, always go to home.
// Home itself will redirect to onboarding if needed.
+44 -11
View File
@@ -23,24 +23,33 @@ const ModelProvider = ({ children }: { children: React.ReactNode }) => {
state => state.model
);
const pollingRef = useRef(false);
const initDone = useRef(false);
// Initial status fetch + availability check
// Initial status fetch + availability check (runs once)
useEffect(() => {
if (initDone.current) return;
initDone.current = true;
const init = async () => {
console.log('[ModelProvider] Initializing...');
try {
const status = await invoke<ModelStatus>('model_get_status');
console.log('[ModelProvider] Initial status:', status);
dispatch(setModelStatus(status));
} catch {
// Non-Tauri environment
} catch (err) {
console.log('[ModelProvider] model_get_status failed (non-Tauri?):', err);
return; // Not a Tauri environment, nothing more to do
}
try {
const avail = await invoke<boolean>('model_is_available');
console.log('[ModelProvider] Model available:', avail);
if (avail) {
dispatch(setModelStatus({ ...(await invoke<ModelStatus>('model_get_status')) }));
const status = await invoke<ModelStatus>('model_get_status');
dispatch(setModelStatus(status));
}
} catch {
// Non-Tauri environment
} catch (err) {
console.log('[ModelProvider] model_is_available failed:', err);
}
};
init();
@@ -48,17 +57,37 @@ const ModelProvider = ({ children }: { children: React.ReactNode }) => {
// Auto-trigger download on desktop when model is available but not downloaded
useEffect(() => {
if (downloadTriggered || !available || downloaded || loading) return;
if (downloadTriggered) {
console.log('[ModelProvider] Auto-download: already triggered, skipping');
return;
}
if (!available) {
console.log('[ModelProvider] Auto-download: not available yet, waiting');
return;
}
if (downloaded) {
console.log('[ModelProvider] Auto-download: already downloaded, skipping');
return;
}
if (loading) {
console.log('[ModelProvider] Auto-download: already loading, skipping');
return;
}
const tryAutoDownload = async () => {
try {
const currentPlatform = await platform();
if (currentPlatform === 'android' || currentPlatform === 'ios') return;
} catch {
// Can't detect platform skip auto-download (likely web)
console.log('[ModelProvider] Platform:', currentPlatform);
if (currentPlatform === 'android' || currentPlatform === 'ios') {
console.log('[ModelProvider] Mobile platform, skipping auto-download');
return;
}
} catch (err) {
console.log('[ModelProvider] Platform detection failed (likely web), skipping:', err);
return;
}
console.log('[ModelProvider] Starting auto-download...');
dispatch(setDownloadTriggered(true));
dispatch(setModelLoading(true));
dispatch(setModelError(null));
@@ -66,8 +95,10 @@ const ModelProvider = ({ children }: { children: React.ReactNode }) => {
try {
await invoke('model_start_download');
const status = await invoke<ModelStatus>('model_get_status');
console.log('[ModelProvider] Download started, status:', status);
dispatch(setModelStatus(status));
} catch (err) {
console.error('[ModelProvider] Auto-download failed:', err);
dispatch(setModelError(err instanceof Error ? err.message : 'Failed to download model'));
}
};
@@ -83,6 +114,7 @@ const ModelProvider = ({ children }: { children: React.ReactNode }) => {
}
pollingRef.current = true;
console.log('[ModelProvider] Polling started (loading=true)');
const interval = setInterval(async () => {
if (!pollingRef.current) return;
@@ -90,10 +122,11 @@ const ModelProvider = ({ children }: { children: React.ReactNode }) => {
const status = await invoke<ModelStatus>('model_get_status');
dispatch(setModelStatus(status));
if (!status.loading) {
console.log('[ModelProvider] Polling stopped (loading done)', status);
pollingRef.current = false;
}
} catch {
// ignore
// ignore polling errors
}
}, POLL_INTERVAL);