Last active
          June 12, 2023 10:19 
        
      - 
      
- 
        Save tannerlinsley/0ffe9dbf87a6e1dcb88e529a1941c7e5 to your computer and use it in GitHub Desktop. 
Revisions
- 
        tannerlinsley revised this gist May 15, 2023 . 1 changed file with 7 additions and 0 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,7 @@ This middleware does a few interesting things: - Ensures a `url` shape in the zustand store, where we'll store URL information. - Assumes we will be storing our `url` state slice in the `?state` search parameter after it has been stringified and base 64 encoded. - On creation, decodes stores state from the `?state` search parameter into the `url` slice of our store. - After each state update, updates the `?state` search parameter with the new `url` state slice. - Sets up an event listener that listens for `popstate` and re-decodes the state from the URL into our store. 
- 
        tannerlinsley revised this gist May 15, 2023 . No changes.There are no files selected for viewing
- 
        tannerlinsley revised this gist May 15, 2023 . No changes.There are no files selected for viewing
- 
        tannerlinsley revised this gist May 15, 2023 . No changes.There are no files selected for viewing
- 
        tannerlinsley revised this gist May 15, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ import { create, type StateCreator } from 'zustand' import { immer } from 'zustand/middleware/immer' function functionalUpdate (updater, previous) { return typeof updater === 'function' ? updater(previous) : updater } 
- 
        tannerlinsley revised this gist May 15, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,7 +18,7 @@ export type PageContext = { projectId?: string } export type Page = 'projects' | 'projects.project' const urlMiddleware = <TState extends { url: any }>( 
- 
        tannerlinsley created this gist May 15, 2023 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,91 @@ import { create, type StateCreator } from 'zustand' import { immer } from 'zustand/middleware/immer' funciton 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' | 'plants' | 'products' 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 }) }, } }) ) )