|
import { create, type StateCreator } from 'zustand' |
|
import { immer } from 'zustand/middleware/immer' |
|
|
|
function functionalUpdate (updater, previous) { |
|
return typeof updater === 'function' ? updater(previous) : updater |
|
} |
|
|
|
export type Store = { |
|
url: { |
|
page: Page |
|
context: PageContext |
|
} |
|
setPage: (page: Page, context?: PageContext) => void |
|
createProject: () => void |
|
} |
|
|
|
export type PageContext = { |
|
projectId?: string |
|
} |
|
|
|
export type Page = 'projects' | 'projects.project' |
|
|
|
const urlMiddleware = |
|
<TState extends { url: any }>( |
|
prev: StateCreator<TState, any, any> |
|
): StateCreator<TState> => |
|
(set, get, api) => { |
|
const parseUrlState = () => { |
|
try { |
|
const search = |
|
new URLSearchParams(window.location.search.substring(1)).get( |
|
'state' |
|
) || '' |
|
const decoded = atob(search) |
|
return JSON.parse(decoded) |
|
} catch (e) { |
|
return |
|
} |
|
} |
|
|
|
const initialState = prev( |
|
(...args) => { |
|
set(...args) |
|
const stringified = JSON.stringify(get().url) |
|
const encoded = btoa(stringified) |
|
history.pushState(null, '', `/?state=${encoded}`) |
|
}, |
|
get, |
|
api |
|
) |
|
|
|
window.addEventListener('popstate', () => { |
|
set((state) => { |
|
return { |
|
...state, |
|
url: parseUrlState(), |
|
} |
|
}) |
|
}) |
|
|
|
return { |
|
...initialState, |
|
url: parseUrlState() || initialState.url, |
|
} |
|
} |
|
|
|
export const useStore = create( |
|
urlMiddleware( |
|
immer<Store>((set) => { |
|
return { |
|
url: { |
|
page: 'projects', |
|
context: {}, |
|
}, |
|
setPage: (page, context) => |
|
set((draft) => { |
|
draft.url.page = page |
|
if (context) { |
|
draft.url.context = functionalUpdate(context, draft.url.context) |
|
} |
|
}), |
|
createProject: () => { |
|
set((draft) => { |
|
draft.url.page = 'projects.project' |
|
draft.url.context.projectId = undefined |
|
}) |
|
}, |
|
} |
|
}) |
|
) |
|
) |
Neato.