useSetupOptions.ts
ts
import { deflateRaw, inflateRaw } from 'pako';
import { useEffect, useState } from 'react';
import type SetupOptions from '../types/SetupOptions';
import { assert } from '../types/SetupOptions';
const initialState = {
  width: 768,
  height: 432,
  zoom: 224,
  center: '0',
  julia: '-0.8+0.156*i',
  iterate: 'z**2+c',
  iterations: 512,
  escape: 2,
  seed: '2*pi*(n/N+1/2)',
  red: '255/2*(sin(seed)+1)',
  green: '255/2*(sin(seed+2*pi/3)+1)',
  blue: '255/2*(sin(seed+4*pi/3)+1)',
};
const toUint8Array = (string: string) => Uint8Array.from(
  string, c => c.charCodeAt(0)
);
const fromUint8Array = (uint8Array: Uint8Array) => Array.from(
  uint8Array, code => String.fromCharCode(code)
).join('');
const toHash = (state: SetupOptions) => `#${
  btoa(
    fromUint8Array(
      deflateRaw(
        JSON.stringify(state)
      )
    )
  )
}`;
const fromHash = (hash: string) => assert(
  JSON.parse(
    inflateRaw(
      toUint8Array(
        atob(hash.slice(1))
      ),
      { to: 'string' }
    )
  )
);
const useSetupOptions = () => {
  const [state, setState] = useState<SetupOptions>(() => {
    try {
      return fromHash(window.location.hash);
    } catch (_) {
      return initialState;
    }
  });
  useEffect(() => {
    const hash = toHash(state);
    switch (window.location.hash) {
      case hash:
        break;
      case '':
        window.history.replaceState(state, document.title, `${window.location.pathname}${hash}`);
        break;
      default:
        window.history.pushState(state, document.title, `${window.location.pathname}${hash}`);
    }
  }, [state]);
  useEffect(() => {
    const listener = () => {
      setState(fromHash(window.location.hash));
    };
    window.addEventListener('popstate', listener);
    return () => {
      window.removeEventListener('popstate', listener);
    };
  }, []);
  return [state, setState] as const;
};
export default useSetupOptions;
No comments yet.