Remember last visited page per section in localStorage
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
|||||||
isLinkEnterprise,
|
isLinkEnterprise,
|
||||||
isLinkVisible,
|
isLinkVisible,
|
||||||
} from '@/lib/layout';
|
} from '@/lib/layout';
|
||||||
|
import { findLastVisitedLinkInLayout, setLastVisitedSection } from '@/lib/lastVisited';
|
||||||
import type { Layout, LayoutItem, LayoutSubItem } from '@/types/schema';
|
import type { Layout, LayoutItem, LayoutSubItem } from '@/types/schema';
|
||||||
|
|
||||||
function LucideIcon({ name, className }: { name: string; className?: string }) {
|
function LucideIcon({ name, className }: { name: string; className?: string }) {
|
||||||
@@ -185,6 +186,7 @@ function SidebarSubItem({ item, depth, sectionName, currentPath, navigate, editi
|
|||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
onUpsell();
|
onUpsell();
|
||||||
} else {
|
} else {
|
||||||
|
setLastVisitedSection(sectionName, item.viewName);
|
||||||
navigate(path);
|
navigate(path);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -270,6 +272,7 @@ function SidebarTopItem({ item, sectionName, currentPath, navigate, edition, onU
|
|||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
onUpsell();
|
onUpsell();
|
||||||
} else {
|
} else {
|
||||||
|
setLastVisitedSection(sectionName, viewName);
|
||||||
navigate(path);
|
navigate(path);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -365,7 +368,9 @@ export function Sidebar() {
|
|||||||
const handleSectionClick = (target: Layout) => {
|
const handleSectionClick = (target: Layout) => {
|
||||||
setActiveSection(target.name);
|
setActiveSection(target.name);
|
||||||
const canGet = (prefix: string) => permissions.includes(`${prefix}Get`);
|
const canGet = (prefix: string) => permissions.includes(`${prefix}Get`);
|
||||||
|
const last = findLastVisitedLinkInLayout(schema, target, edition, canGet, hasPermission);
|
||||||
const first =
|
const first =
|
||||||
|
last ??
|
||||||
findFirstAccessibleLinkInLayout(schema, target, edition, canGet, hasPermission) ??
|
findFirstAccessibleLinkInLayout(schema, target, edition, canGet, hasPermission) ??
|
||||||
findFirstVisibleLinkInLayout(schema, target, edition, canGet, hasPermission);
|
findFirstVisibleLinkInLayout(schema, target, edition, canGet, hasPermission);
|
||||||
if (first) navigate(`/${target.name}/${first}`);
|
if (first) navigate(`/${target.name}/${first}`);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { ModeToggle } from '@/components/common/ModeToggle';
|
|||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
|
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
|
||||||
import { findFirstAccessibleLinkInLayout, findFirstVisibleLinkInLayout, visibleLayouts } from '@/lib/layout';
|
import { findFirstAccessibleLinkInLayout, findFirstVisibleLinkInLayout, visibleLayouts } from '@/lib/layout';
|
||||||
|
import { findLastVisitedLinkInLayout } from '@/lib/lastVisited';
|
||||||
import { useUIStore } from '@/stores/uiStore';
|
import { useUIStore } from '@/stores/uiStore';
|
||||||
import { useAuthStore } from '@/stores/authStore';
|
import { useAuthStore } from '@/stores/authStore';
|
||||||
import { buildEndSessionUrl, getPostLogoutRedirectUri } from '@/services/auth/oauth';
|
import { buildEndSessionUrl, getPostLogoutRedirectUri } from '@/services/auth/oauth';
|
||||||
@@ -152,7 +153,9 @@ export function TopBar() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveSection(layout.name);
|
setActiveSection(layout.name);
|
||||||
const canGet = (prefix: string) => hasObjectPermission(prefix, 'Get');
|
const canGet = (prefix: string) => hasObjectPermission(prefix, 'Get');
|
||||||
|
const lastLink = findLastVisitedLinkInLayout(schema, layout, edition, canGet, hasPermission);
|
||||||
const firstLink =
|
const firstLink =
|
||||||
|
lastLink ??
|
||||||
findFirstAccessibleLinkInLayout(schema, layout, edition, canGet, hasPermission) ??
|
findFirstAccessibleLinkInLayout(schema, layout, edition, canGet, hasPermission) ??
|
||||||
findFirstVisibleLinkInLayout(schema, layout, edition, canGet, hasPermission);
|
findFirstVisibleLinkInLayout(schema, layout, edition, canGet, hasPermission);
|
||||||
if (firstLink) {
|
if (firstLink) {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { isLinkAccessible } from './layout';
|
||||||
|
import type { Layout, Schema } from '@/types/schema';
|
||||||
|
|
||||||
|
type CanGet = (prefix: string) => boolean;
|
||||||
|
type HasPermission = (permission: string) => boolean;
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'stalwart-last-visited';
|
||||||
|
|
||||||
|
// Stores the last visited view name for a given top-level section in localStorage.
|
||||||
|
export function setLastVisitedSection(section: string, viewName: string): void {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(STORAGE_KEY);
|
||||||
|
const data = raw ? (JSON.parse(raw) as Record<string, string>) : {};
|
||||||
|
data[section] = viewName;
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
||||||
|
} catch {
|
||||||
|
// Ignore storage errors (e.g. private mode).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the last visited view name for a section, or null if none is stored.
|
||||||
|
export function getLastVisitedSection(section: string): string | null {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(STORAGE_KEY);
|
||||||
|
if (!raw) return null;
|
||||||
|
const data = JSON.parse(raw) as Record<string, string>;
|
||||||
|
return data[section] ?? null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the stored view name only if it is still accessible in the target layout.
|
||||||
|
export function findLastVisitedLinkInLayout(
|
||||||
|
schema: Schema,
|
||||||
|
layout: Layout,
|
||||||
|
edition: string,
|
||||||
|
canGet: CanGet,
|
||||||
|
hasPermission: HasPermission,
|
||||||
|
): string | null {
|
||||||
|
const last = getLastVisitedSection(layout.name);
|
||||||
|
if (!last) return null;
|
||||||
|
if (isLinkAccessible(schema, last, edition, canGet, hasPermission)) return last;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user