diff --git a/apps/browser-extension/src/entrypoints/popup/App.tsx b/apps/browser-extension/src/entrypoints/popup/App.tsx
index a7807a30d..b7b737b2d 100644
--- a/apps/browser-extension/src/entrypoints/popup/App.tsx
+++ b/apps/browser-extension/src/entrypoints/popup/App.tsx
@@ -19,7 +19,9 @@ import Logout from '@/entrypoints/popup/pages/Logout';
import Settings from '@/entrypoints/popup/pages/Settings';
import { useMinDurationLoading } from '@/hooks/useMinDurationLoading';
+
import '@/entrypoints/popup/style.css';
+import { NavigationProvider } from './context/NavigationContext';
/**
* Route configuration.
@@ -74,44 +76,45 @@ const App: React.FC = () => {
return (
-
- {isLoading && (
-
-
-
- )}
+
+
+ {isLoading && (
+
+
+
+ )}
-
-
+
+
-
-
- {message && (
-
{message}
- )}
-
- {routes.map((route) => (
-
- ))}
-
-
-
-
-
-
+
+
+ {message && (
+
{message}
+ )}
+
+ {routes.map((route) => (
+
+ ))}
+
+
+
+
+
+
);
};
diff --git a/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx b/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx
index 4c1d8b22e..1f79b64cc 100644
--- a/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx
+++ b/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx
@@ -18,9 +18,13 @@ const BottomNav: React.FC = () => {
// Add effect to update currentTab based on route
useEffect(() => {
- const path = location.pathname.substring(1) as TabName;
- if (['credentials', 'emails', 'settings'].includes(path)) {
- setCurrentTab(path);
+ const path = location.pathname.substring(1); // Remove leading slash
+ const tabNames: TabName[] = ['credentials', 'emails', 'settings'];
+
+ // Find the first tab name that matches the start of the path
+ const matchingTab = tabNames.find(tab => path === tab || path.startsWith(`${tab}/`));
+ if (matchingTab) {
+ setCurrentTab(matchingTab);
}
}, [location]);
diff --git a/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx
new file mode 100644
index 000000000..910c4a674
--- /dev/null
+++ b/apps/browser-extension/src/entrypoints/popup/context/NavigationContext.tsx
@@ -0,0 +1,85 @@
+import React, { createContext, useContext, useEffect, useState, useMemo, useCallback } from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import { storage } from '#imports';
+
+const LAST_VISITED_PAGE_KEY = 'local:lastVisitedPage';
+const LAST_VISITED_TIME_KEY = 'local:lastVisitedTime';
+const PAGE_MEMORY_DURATION = 120 * 1000; // 2 minutes in milliseconds
+
+type NavigationContextType = {
+ storeCurrentPage: () => Promise;
+ restoreLastPage: () => Promise;
+};
+
+const NavigationContext = createContext(undefined);
+
+/**
+ * Navigation provider component that handles storing and restoring the last visited page.
+ */
+export const NavigationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [isInitialized, setIsInitialized] = useState(false);
+
+ /**
+ * Store the current page path and timestamp in storage.
+ */
+ const storeCurrentPage = useCallback(async (): Promise => {
+ await storage.setItem(LAST_VISITED_PAGE_KEY, location.pathname);
+ await storage.setItem(LAST_VISITED_TIME_KEY, Date.now());
+ }, [location.pathname]);
+
+ /**
+ * Restore the last visited page if it was visited within the memory duration.
+ */
+ const restoreLastPage = useCallback(async (): Promise => {
+ const lastPage = await storage.getItem(LAST_VISITED_PAGE_KEY) as string;
+ const lastVisitTime = await storage.getItem(LAST_VISITED_TIME_KEY) as number;
+
+ if (lastPage && lastVisitTime) {
+ const timeSinceLastVisit = Date.now() - lastVisitTime;
+ if (timeSinceLastVisit <= PAGE_MEMORY_DURATION) {
+ navigate(lastPage);
+ }
+ }
+ }, [navigate]);
+
+ // Store the current page whenever it changes
+ useEffect(() => {
+ if (isInitialized) {
+ storeCurrentPage();
+ }
+ }, [location.pathname, isInitialized, storeCurrentPage]);
+
+ // Restore the last page on initial load
+ useEffect(() => {
+ if (!isInitialized) {
+ restoreLastPage();
+ setIsInitialized(true);
+ }
+ }, [isInitialized, restoreLastPage]);
+
+ const contextValue = useMemo(() => ({
+ storeCurrentPage,
+ restoreLastPage
+ }), [storeCurrentPage, restoreLastPage]);
+
+ return (
+
+ {children}
+
+ );
+};
+
+/**
+ * Hook to access the navigation context.
+ * @returns The navigation context
+ */
+export const useNavigation = (): NavigationContextType => {
+ const context = useContext(NavigationContext);
+ if (context === undefined) {
+ throw new Error('useNavigation must be used within a NavigationProvider');
+ }
+ return context;
+};
diff --git a/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx b/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx
index 44340f044..78dcb15c1 100644
--- a/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx
+++ b/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx
@@ -1,6 +1,8 @@
import React, { useState, useEffect } from 'react';
import * as Yup from 'yup';
+import { useLoading } from '@/entrypoints/popup/context/LoadingContext';
+
import { AppInfo } from '@/utils/AppInfo';
import { GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, DISABLED_SITES_KEY, VAULT_LOCKED_DISMISS_UNTIL_KEY } from '@/utils/Constants';
@@ -55,6 +57,7 @@ const AuthSettings: React.FC = () => {
const [customClientUrl, setCustomClientUrl] = useState('');
const [isGloballyEnabled, setIsGloballyEnabled] = useState(true);
const [errors, setErrors] = useState<{ apiUrl?: string; clientUrl?: string }>({});
+ const { setIsInitialLoading } = useLoading();
useEffect(() => {
/**
@@ -83,10 +86,11 @@ const AuthSettings: React.FC = () => {
} else {
setSelectedOption(DEFAULT_OPTIONS[0].value);
}
+ setIsInitialLoading(false);
};
loadStoredSettings();
- }, []);
+ }, [setIsInitialLoading]);
/**
* Handle option change
diff --git a/apps/browser-extension/src/entrypoints/popup/pages/CredentialAddEdit.tsx b/apps/browser-extension/src/entrypoints/popup/pages/CredentialAddEdit.tsx
index df39c7c8c..1af637655 100644
--- a/apps/browser-extension/src/entrypoints/popup/pages/CredentialAddEdit.tsx
+++ b/apps/browser-extension/src/entrypoints/popup/pages/CredentialAddEdit.tsx
@@ -106,7 +106,7 @@ const CredentialAddEdit: React.FC = () => {
setTimeout(() => {
serviceNameRef.current?.focus();
}, 100);
-
+ setIsInitialLoading(false);
return;
}
@@ -122,6 +122,7 @@ const CredentialAddEdit: React.FC = () => {
});
setMode('manual');
+ setIsInitialLoading(false);
// On create mode, focus the service name field after a short delay to ensure the component is mounted
} else {
@@ -130,6 +131,7 @@ const CredentialAddEdit: React.FC = () => {
}
} catch (err) {
console.error('Error loading credential:', err);
+ setIsInitialLoading(false);
}
}, [dbContext.sqliteClient, id, navigate, setIsInitialLoading, setValue]);
diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx
index c46a23558..8ab8f3a55 100644
--- a/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx
+++ b/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx
@@ -29,7 +29,7 @@ const Login: React.FC = () => {
username: '',
password: '',
});
- const { showLoading, hideLoading } = useLoading();
+ const { showLoading, hideLoading, setIsInitialLoading } = useLoading();
const [rememberMe, setRememberMe] = useState(true);
const [loginResponse, setLoginResponse] = useState(null);
const [passwordHashString, setPasswordHashString] = useState(null);
@@ -53,9 +53,10 @@ const Login: React.FC = () => {
}
setClientUrl(clientUrl);
+ setIsInitialLoading(false);
};
loadClientUrl();
- }, []);
+ }, [setIsInitialLoading]);
/**
* Handle submit
diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx
index 4e8dc8ac8..3109e319d 100644
--- a/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx
+++ b/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx
@@ -29,7 +29,7 @@ const Unlock: React.FC = () => {
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
- const { showLoading, hideLoading } = useLoading();
+ const { showLoading, hideLoading, setIsInitialLoading } = useLoading();
useEffect(() => {
/**
@@ -41,10 +41,11 @@ const Unlock: React.FC = () => {
if (statusError !== null) {
await webApi.logout(statusError);
}
+ setIsInitialLoading(false);
};
checkStatus();
- }, [webApi, authContext]);
+ }, [webApi, authContext, setIsInitialLoading]);
/**
* Handle submit