Skip to content

Commit

Permalink
Updated flash from nextjs by storing and restoring with cookies using…
Browse files Browse the repository at this point in the history
… nookies
  • Loading branch information
csprance committed Jul 26, 2022
1 parent d360b31 commit fe65aad
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 72 deletions.
24 changes: 16 additions & 8 deletions components/Notes.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { highlight, languages } from "prismjs";
import "prismjs/components/prism-markdown";
import * as React from "react";
import Editor from "react-simple-code-editor";
import styled from "styled-components";
import { highlight, languages } from 'prismjs';
import 'prismjs/components/prism-markdown';
import * as React from 'react';
import Editor from 'react-simple-code-editor';
import styled from 'styled-components';

import { useStore } from "../store";
import { bgColor, ctrlColor, inputBg } from "../styles";
import { useStore } from '../store';
import { bgColor, ctrlColor, inputBg } from '../styles';

const Wrapper = styled.div`
background: ${bgColor};
Expand All @@ -28,7 +28,15 @@ const Notes: React.FC<Props> = () => {
const { notes, setNotes } = useStore();
return (
<Wrapper>
<p style={{fontSize: '17.5px', padding: 0, margin: 0, width: '100%', textAlign: 'center' }}>
<p
style={{
fontSize: '17.5px',
padding: 0,
margin: 0,
width: '100%',
textAlign: 'center',
}}
>
Notes
</p>
<label className={'sr-only'} htmlFor={`notes-input`}>
Expand Down
2 changes: 1 addition & 1 deletion components/TimelineBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import RangeSlider from './RangeSlider';
interface Props {}
const TimelineBar: React.FC<Props> = () => {
const { grapher } = useStore();
const [t, setT] = React.useState(grapher.mTimeS);
const [t, setT] = React.useState( 0);
const [paused, setPaused] = React.useState(false);

React.useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"immer": "^9.0.15",
"mitt": "^3.0.0",
"next": "latest",
"nookies": "^2.5.2",
"prismjs": "^1.28.0",
"react": "latest",
"react-dom": "latest",
Expand Down
44 changes: 28 additions & 16 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import { AppProps } from 'next/app';
import Head from 'next/head';
import * as React from 'react';
import App, { AppContext, AppProps } from "next/app";
import Head from "next/head";
import { parseCookies, setCookie } from "nookies";
import * as React from "react";

import { extendMath } from '../lib/graphtoy/lib';
import { AsyncStorage, useStore } from '../store';
import { GlobalStyles, PrismA11lyTheme } from '../styles';
import { extendMath } from "../lib/graphtoy/lib";
import { Provider, State, useCreateStore } from "../store";
import { GlobalStyles, PrismA11lyTheme } from "../styles";

extendMath();

const MyApp = ({ Component, pageProps }: AppProps) => {
React.useEffect(() => {
if (useStore.persist) {
useStore.persist.setOptions({ getStorage: () => AsyncStorage });
useStore.persist.rehydrate();
}
}, []);
extendMath();

const MyApp = ({
Component,
pageProps,
}: AppProps & { state: State }) => {
const createStore = useCreateStore(pageProps);
createStore().subscribe((state)=> {
// On every state change store state in cookies
const stateNew = JSON.stringify({... state, grapher: null });
setCookie(null, 'graphtoy-plus', stateNew);
})
return (
<>
<Provider createStore={createStore}>
<GlobalStyles />
<PrismA11lyTheme />
<Head>
Expand All @@ -33,8 +37,16 @@ const MyApp = ({ Component, pageProps }: AppProps) => {
/>
</Head>
<Component {...pageProps} />
</>
</Provider>
);
};

MyApp.getInitialProps = async (appContext: AppContext) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
const cookies = parseCookies(appContext.ctx);
const state = JSON.parse(cookies['graphtoy-plus']);
return { ...appProps, pageProps: state };
};

export default MyApp;
114 changes: 68 additions & 46 deletions store/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import produce from 'immer';
import { useEffect, useState } from 'react';
import create from 'zustand';
import { StateStorage, persist } from 'zustand/middleware';
import { useLayoutEffect } from 'react';
import create, { StoreApi, UseBoundStore } from "zustand";
import createContext from 'zustand/context';
import { combine } from 'zustand/middleware';

import Grapher from '../lib/graphtoy';
import {
Expand All @@ -14,30 +15,43 @@ import {
import { Formula, Variable, VisualizerState } from '../lib/graphtoy/types';
import { makeMapPartialByID, sortById } from '../lib/utils';

export const useStore = create<MyStore>()(
persist(
(set, get) => ({
let store: StoreApi<MyStore>;

type InitialState = ReturnType<typeof getDefaultInitialState>;
type UseStoreState = typeof initializeStore extends (
...args: never
) => UseBoundStore<infer T>
? T
: never;

const getDefaultInitialState: () => State = () => ({
formulas: exampleFormulas1,
grapher: new Grapher(),
notes: '',
variables: defaultVariables,
formulaColors: [
'#ffc040',
'#ffffa0',
'#a0ffc0',
'#40c0ff',
'#d0a0ff',
'#ff80b0',
],
});

export const { Provider, useStoreApi, useStore, } = createContext<UseStoreState>();

export const initializeStore = (preloadedState = {}) =>
create<MyStore>()(
combine({ ...getDefaultInitialState(), ...preloadedState }, (set, get) => ({
// /////////////////////////////
// Notes
notes: '',
setNotes: (notes) => set({ notes }),
// ////////////////////////////
// Grapher
grapher: new Grapher(),
setGrapher: (grapher) => set({ grapher }),

// ////////////////////////////
// Formulas
// state
formulas: exampleFormulas1,
formulaColors: [
'#ffc040',
'#ffffa0',
'#a0ffc0',
'#40c0ff',
'#d0a0ff',
'#ff80b0',
],
// actions
toggleFormulaVisibility: (id) =>
set((state) => ({
Expand All @@ -55,7 +69,7 @@ export const useStore = create<MyStore>()(
.sort(sortById),
})),
setFormulaValue: (id, value) => {
get().setFormulaByID(id, { value });
(get() as any).setFormulaByID(id, { value });
},
clearFormulas: () => {
get().grapher.resetCoords();
Expand Down Expand Up @@ -135,10 +149,8 @@ export const useStore = create<MyStore>()(
});
}
},

///////////////////////
// Variables
variables: defaultVariables,
setVariable: (id, partial) =>
set((state) => ({
variables: state.variables
Expand All @@ -149,31 +161,41 @@ export const useStore = create<MyStore>()(
value: v.value === 0 ? v.value + Number.EPSILON : v.value,
})),
})),
}),
// Persistence settings
{
name: 'graphtoy-plus',
getStorage: () => sessionStorage,
partialize: (state) =>
// Filter grapher from being stored and persisted.
Object.fromEntries(
Object.entries(state).filter(([key]) => !['grapher'].includes(key)),
),
},
),
);
})),
);

export const useCreateStore = (serverInitialState: InitialState) => {
// For SSR & SSG, always use a new store.
if (typeof window === 'undefined') {
return () => initializeStore(serverInitialState);
}

const isReusingStore = Boolean(store);
// For CSR, always re-use same store.
store = store ?? initializeStore({ ...serverInitialState, grapher: new Grapher() });
// And if initialState changes, then merge states in the next render cycle.
//
// eslint complaining "React Hooks must be called in the exact same order in every component render"
// is ignorable as this code runs in same order in a given environment
// eslint-disable-next-line react-hooks/rules-of-hooks
useLayoutEffect(() => {
// serverInitialState is undefined for CSR pages. It is up to you if you want to reset
// states on CSR page navigation or not. I have chosen not to, but if you choose to,
// then add `serverInitialState = getDefaultInitialState()` here.
if (serverInitialState && isReusingStore) {
store.setState(
{
// re-use functions from existing store
...store.getState(),
// but reset all other properties.
...serverInitialState,
},
true, // replace states, rather than shallow merging
);
}
});

// Custom storage object
export const AsyncStorage: StateStorage = {
getItem: async (name: string): Promise<string | null> => {
return (await localStorage.getItem(name)) || null;
},
setItem: async (name: string, value: string): Promise<void> => {
await localStorage.setItem(name, value);
},
removeItem: async (name: string): Promise<void> => {
await localStorage.removeItem(name);
},
return () => store;
};

export interface State {
Expand Down
20 changes: 19 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,11 @@ convert-source-map@^1.7.0:
dependencies:
safe-buffer "~5.1.1"

cookie@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==

core-js-pure@^3.20.2:
version "3.23.5"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.5.tgz#23daaa9af9230e50f10b0fa4b8e6b87402be4c33"
Expand Down Expand Up @@ -1747,6 +1752,14 @@ node-releases@^2.0.6:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==

nookies@^2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/nookies/-/nookies-2.5.2.tgz#cc55547efa982d013a21475bd0db0c02c1b35b27"
integrity sha512-x0TRSaosAEonNKyCrShoUaJ5rrT5KHRNZ5DwPCuizjgrnkpE5DRf3VL7AyyQin4htict92X1EQ7ejDbaHDVdYA==
dependencies:
cookie "^0.4.1"
set-cookie-parser "^2.4.6"

object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
Expand Down Expand Up @@ -2059,6 +2072,11 @@ semver@^7.3.7:
dependencies:
lru-cache "^6.0.0"

set-cookie-parser@^2.4.6:
version "2.5.0"
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz#96b59525e1362c94335c3c761100bb6e8f2da4b0"
integrity sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==

shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
Expand Down Expand Up @@ -2318,7 +2336,7 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==

zustand@^4.0.0-rc.1:
zustand@^4.0.0-rc.4:
version "4.0.0-rc.4"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.0.0-rc.4.tgz#ed0e0f1fa3e1c7d0d9021739d862d88048d845da"
integrity sha512-BP35Rq40GBTKKtYyjLuZogzXGh289xqO8U8ivGIK43nRqURD2dEEImkUci1/jWRUz7J1OPJkUZuNySCho801gQ==
Expand Down

0 comments on commit fe65aad

Please sign in to comment.