fix: refactor logout to use synchronous flow with full page redirect
- Rewrite logout() from async to synchronous to prevent React re-renders with stale state
- Replace router.push('/login') with redirectToLogin() (window.location.replace) in all page auth guards for reliable navigation in Edge/Safari
- Add performFullLogout() helper that clears auth state, feature stores, and localStorage
- Fix persist middleware partialize to return {} when not authenticated, preventing state resurrection
- Use keepalive fetch for background cookie/token cleanup so redirect fires immediately
- Remove unused useRouter imports from page.tsx and contacts/page.tsx
- Simplify all page logout handlers to directly call logout()
Fixes #63
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
} from "date-fns";
|
||||
import { useCalendarStore } from "@/stores/calendar-store";
|
||||
import { isCalendarViewMode } from "@/stores/calendar-store";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useAuthStore, redirectToLogin } from "@/stores/auth-store";
|
||||
import { useEmailStore } from "@/stores/email-store";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
import { useIdentityStore } from "@/stores/identity-store";
|
||||
@@ -105,7 +105,7 @@ export default function CalendarPage() {
|
||||
useEffect(() => {
|
||||
if (initialCheckDone && !isAuthenticated && !authLoading) {
|
||||
try { sessionStorage.setItem('redirect_after_login', window.location.pathname); } catch { /* ignore */ }
|
||||
router.push("/login");
|
||||
redirectToLogin();
|
||||
} else if (client && !supportsCalendar) {
|
||||
router.push("/");
|
||||
}
|
||||
@@ -721,7 +721,7 @@ export default function CalendarPage() {
|
||||
collapsed
|
||||
quota={quota}
|
||||
isPushConnected={isPushConnected}
|
||||
onLogout={() => { logout(); if (!useAuthStore.getState().isAuthenticated) router.push('/login'); }}
|
||||
onLogout={logout}
|
||||
onManageApps={handleManageApps}
|
||||
onInlineApp={handleInlineApp}
|
||||
onCloseInlineApp={closeInlineApp}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
||||
import { useRouter } from "@/i18n/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { ArrowLeft, Users } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -16,7 +15,7 @@ import { ContactsSidebar, type ContactCategory } from "@/components/contacts/con
|
||||
import { ContactImportDialog } from "@/components/contacts/contact-import-dialog";
|
||||
import { exportContacts } from "@/components/contacts/contact-export";
|
||||
import { useContactStore, getContactDisplayName } from "@/stores/contact-store";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useAuthStore, redirectToLogin } from "@/stores/auth-store";
|
||||
import { useEmailStore } from "@/stores/email-store";
|
||||
import { toast } from "@/stores/toast-store";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -39,7 +38,6 @@ type View =
|
||||
| "bulk-add-to-group";
|
||||
|
||||
export default function ContactsPage() {
|
||||
const router = useRouter();
|
||||
const t = useTranslations("contacts");
|
||||
const { client, isAuthenticated, logout, checkAuth, isLoading: authLoading } = useAuthStore();
|
||||
const { showAppsModal, inlineApp, loadedApps, handleManageApps, handleInlineApp, closeInlineApp, closeAppsModal } = useSidebarApps();
|
||||
@@ -109,9 +107,9 @@ export default function ContactsPage() {
|
||||
useEffect(() => {
|
||||
if (initialCheckDone && !isAuthenticated && !authLoading) {
|
||||
try { sessionStorage.setItem('redirect_after_login', window.location.pathname); } catch { /* ignore */ }
|
||||
router.push("/login");
|
||||
redirectToLogin();
|
||||
}
|
||||
}, [initialCheckDone, isAuthenticated, authLoading, router]);
|
||||
}, [initialCheckDone, isAuthenticated, authLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (client && supportsSync && !hasFetched.current) {
|
||||
@@ -594,7 +592,7 @@ export default function ContactsPage() {
|
||||
collapsed
|
||||
quota={quota}
|
||||
isPushConnected={isPushConnected}
|
||||
onLogout={() => { logout(); if (!useAuthStore.getState().isAuthenticated) router.push('/login'); }}
|
||||
onLogout={logout}
|
||||
onManageApps={handleManageApps}
|
||||
onInlineApp={handleInlineApp}
|
||||
onCloseInlineApp={closeInlineApp}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ArrowLeft } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||
import { useConfirmDialog } from "@/hooks/use-confirm-dialog";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useAuthStore, redirectToLogin } from "@/stores/auth-store";
|
||||
import { useEmailStore } from "@/stores/email-store";
|
||||
import { useFileStore } from "@/stores/file-store";
|
||||
import { toast } from "@/stores/toast-store";
|
||||
@@ -112,9 +112,9 @@ export default function FilesPage() {
|
||||
useEffect(() => {
|
||||
if (initialCheckDone && !isAuthenticated && !authLoading) {
|
||||
try { sessionStorage.setItem('redirect_after_login', window.location.pathname); } catch { /* ignore */ }
|
||||
router.push("/login");
|
||||
redirectToLogin();
|
||||
}
|
||||
}, [initialCheckDone, isAuthenticated, authLoading, router]);
|
||||
}, [initialCheckDone, isAuthenticated, authLoading]);
|
||||
|
||||
// Initialize JMAP files client
|
||||
useEffect(() => {
|
||||
@@ -357,7 +357,7 @@ export default function FilesPage() {
|
||||
collapsed
|
||||
quota={quota}
|
||||
isPushConnected={isPushConnected}
|
||||
onLogout={() => { logout(); if (!useAuthStore.getState().isAuthenticated) router.push('/login'); }}
|
||||
onLogout={logout}
|
||||
onManageApps={handleManageApps}
|
||||
onInlineApp={handleInlineApp}
|
||||
onCloseInlineApp={closeInlineApp}
|
||||
|
||||
+4
-11
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useRef, useMemo, useCallback } from "react";
|
||||
import { useRouter } from "@/i18n/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Sidebar } from "@/components/layout/sidebar";
|
||||
import { EmailList } from "@/components/email/email-list";
|
||||
@@ -13,7 +12,7 @@ import { MobileHeader, MobileViewerHeader } from "@/components/layout/mobile-hea
|
||||
import { ThreadGroup, Email } from "@/lib/jmap/types";
|
||||
import { KeyboardShortcutsModal } from "@/components/keyboard-shortcuts-modal";
|
||||
import { useEmailStore } from "@/stores/email-store";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useAuthStore, redirectToLogin } from "@/stores/auth-store";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
import { useIdentityStore } from "@/stores/identity-store";
|
||||
import { useUIStore } from "@/stores/ui-store";
|
||||
@@ -48,7 +47,6 @@ import { Button } from "@/components/ui/button";
|
||||
import { useConfig } from "@/hooks/use-config";
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
const t = useTranslations();
|
||||
const tCommon = useTranslations('common');
|
||||
const { appName } = useConfig();
|
||||
@@ -285,9 +283,9 @@ export default function Home() {
|
||||
useEffect(() => {
|
||||
if (initialCheckDone && !isAuthenticated && !authLoading) {
|
||||
try { sessionStorage.setItem('redirect_after_login', window.location.pathname); } catch { /* ignore */ }
|
||||
router.push('/login');
|
||||
redirectToLogin();
|
||||
}
|
||||
}, [initialCheckDone, isAuthenticated, authLoading, router]);
|
||||
}, [initialCheckDone, isAuthenticated, authLoading]);
|
||||
|
||||
// Load mailboxes and emails when authenticated (only if not already loaded)
|
||||
useEffect(() => {
|
||||
@@ -768,12 +766,7 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
if (!useAuthStore.getState().isAuthenticated) {
|
||||
router.push('/login');
|
||||
}
|
||||
};
|
||||
const handleLogout = logout;
|
||||
|
||||
const handleSearch = async (query: string) => {
|
||||
if (!client) return;
|
||||
|
||||
@@ -44,7 +44,7 @@ import { FilesSettingsComponent } from '@/components/settings/files-settings';
|
||||
import { ContactsSettings } from '@/components/settings/contacts-settings';
|
||||
import { SmimeSettings } from '@/components/settings/smime-settings';
|
||||
import { SidebarAppsSettings } from '@/components/settings/sidebar-apps-settings';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useAuthStore, redirectToLogin } from '@/stores/auth-store';
|
||||
import { useEmailStore } from '@/stores/email-store';
|
||||
import { useIsDesktop } from '@/hooks/use-media-query';
|
||||
import { NavigationRail } from '@/components/layout/navigation-rail';
|
||||
@@ -122,9 +122,9 @@ export default function SettingsPage() {
|
||||
useEffect(() => {
|
||||
if (initialCheckDone && !isAuthenticated && !authLoading) {
|
||||
try { sessionStorage.setItem('redirect_after_login', window.location.pathname); } catch { /* ignore */ }
|
||||
router.push('/login');
|
||||
redirectToLogin();
|
||||
}
|
||||
}, [initialCheckDone, isAuthenticated, authLoading, router]);
|
||||
}, [initialCheckDone, isAuthenticated, authLoading]);
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return null;
|
||||
@@ -286,7 +286,7 @@ export default function SettingsPage() {
|
||||
{/* Logout */}
|
||||
<div className="border-t border-border px-5 py-3">
|
||||
<button
|
||||
onClick={() => { logout(); if (!useAuthStore.getState().isAuthenticated) router.push('/login'); }}
|
||||
onClick={logout}
|
||||
className="w-full flex items-center gap-3 py-2.5 text-sm text-destructive hover:bg-muted rounded-md px-2 transition-colors duration-150"
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
@@ -317,7 +317,7 @@ export default function SettingsPage() {
|
||||
collapsed
|
||||
quota={quota}
|
||||
isPushConnected={isPushConnected}
|
||||
onLogout={() => { logout(); if (!useAuthStore.getState().isAuthenticated) router.push('/login'); }}
|
||||
onLogout={logout}
|
||||
onManageApps={handleManageApps}
|
||||
onInlineApp={handleInlineApp}
|
||||
onCloseInlineApp={closeInlineApp}
|
||||
|
||||
Reference in New Issue
Block a user