-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathnavigation-store.ts
More file actions
128 lines (112 loc) · 3.97 KB
/
navigation-store.ts
File metadata and controls
128 lines (112 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';
import { ALL_ROOMS_ID } from '@/app/constants/rooms';
import { isSection, pathToSection, type Section, sectionToPath } from '../navigation/sections';
let navigationStoreCleanup: (() => void) | null = null;
const MAX_RECENT_SECTIONS = 3;
function getRecentSectionHistory(nextSection: Section, currentRecentSections: Section[]) {
if (nextSection === 'home') {
return currentRecentSections;
}
return [nextSection, ...currentRecentSections.filter((section) => section !== nextSection)].slice(
0,
MAX_RECENT_SECTIONS
);
}
function sanitizeRecentSections(value: unknown): Section[] {
if (!Array.isArray(value)) {
return [];
}
const recentSections: Section[] = [];
for (const item of value) {
if (!isSection(item) || item === 'home' || recentSections.includes(item)) {
continue;
}
recentSections.push(item);
if (recentSections.length >= MAX_RECENT_SECTIONS) {
break;
}
}
return recentSections;
}
interface NavigationState {
currentRoom: string;
activeSection: Section;
recentSections: Section[];
lastNonHomeSection: Section | null;
applyNavigationState: (state: { currentRoom: string; activeSection: Section }) => void;
setCurrentRoom: (room: string) => void;
setActiveSection: (section: Section) => void;
syncActiveSectionFromLocation: (section: Section) => void;
}
const initialSection = (): Section =>
typeof window === 'undefined' ? 'home' : pathToSection(window.location.pathname);
export const useNavigationStore = create<NavigationState>()(
persist(
(set) => ({
currentRoom: ALL_ROOMS_ID,
activeSection: initialSection(),
recentSections: [],
lastNonHomeSection: null,
applyNavigationState: ({ currentRoom, activeSection }) => set({ currentRoom, activeSection }),
setCurrentRoom: (currentRoom) => set({ currentRoom }),
setActiveSection: (activeSection) => {
history.pushState({}, '', sectionToPath(activeSection));
window.scrollTo(0, 0);
set((state) => {
if (activeSection === 'home') {
return { activeSection };
}
return {
activeSection,
lastNonHomeSection: activeSection,
recentSections: getRecentSectionHistory(activeSection, state.recentSections),
};
});
},
syncActiveSectionFromLocation: (activeSection) => set({ activeSection }),
}),
{
name: 'ha-dashboard-navigation',
storage: createJSONStorage(() => localStorage),
// activeSection is derived from the URL; mobile recents stay persisted.
partialize: (state) => ({
currentRoom: state.currentRoom,
recentSections: state.recentSections,
lastNonHomeSection: state.lastNonHomeSection,
}),
merge: (persisted, current) => {
const p = (persisted as Partial<NavigationState> | null) ?? {};
return {
...current,
currentRoom:
typeof p.currentRoom === 'string' && p.currentRoom.length > 0
? p.currentRoom
: ALL_ROOMS_ID,
recentSections: sanitizeRecentSections(p.recentSections),
lastNonHomeSection:
p.lastNonHomeSection &&
isSection(p.lastNonHomeSection) &&
p.lastNonHomeSection !== 'home'
? p.lastNonHomeSection
: null,
};
},
}
)
);
export function startNavigationStoreSync() {
if (typeof window === 'undefined' || navigationStoreCleanup) {
return navigationStoreCleanup ?? (() => {});
}
const handlePopState = () => {
const section = pathToSection(window.location.pathname);
useNavigationStore.getState().syncActiveSectionFromLocation(section);
};
window.addEventListener('popstate', handlePopState);
navigationStoreCleanup = () => {
window.removeEventListener('popstate', handlePopState);
navigationStoreCleanup = null;
};
return navigationStoreCleanup;
}