mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
Make useLocalStorage actually work
This commit is contained in:
parent
472d641cfc
commit
683afbf133
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import { Mgba } from "./mgba";
|
||||
import { useRef, useState } from "react";
|
||||
import { Mgba, MgbaHandle } from "./mgba";
|
||||
import { BindingsControl, DefaultBindingsSet, Bindings } from "./bindings";
|
||||
import { styled } from "styled-components";
|
||||
import { useOnKeyUp } from "./useOnKeyUp.hook";
|
||||
|
@ -16,24 +16,28 @@ const VolumeLabel = styled.label`
|
|||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const CloseButton = styled.button`
|
||||
const ActionButton = styled.button`
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
`;
|
||||
|
||||
function App() {
|
||||
const [volumeState, setVolume] = useState(1.0);
|
||||
const [bindingsState, setBindings] = useState(DefaultBindingsSet());
|
||||
|
||||
const { volume, bindings } = useLocalStorage(
|
||||
{ volume: volumeState, bindings: bindingsState },
|
||||
const [{ volume, bindings }, setState] = useLocalStorage(
|
||||
{ volume: 1.0, bindings: DefaultBindingsSet() },
|
||||
"agbrswebplayer"
|
||||
);
|
||||
|
||||
const setVolume = (newVolume: number) =>
|
||||
setState({ volume: newVolume, bindings });
|
||||
const setBindings = (newBindings: Bindings) =>
|
||||
setState({ volume, bindings: newBindings });
|
||||
|
||||
const [paused, setPaused] = useState(false);
|
||||
|
||||
const [showBindings, setShowBindings] = useState(false);
|
||||
|
||||
const mgbaRef = useRef<MgbaHandle>(null);
|
||||
|
||||
useOnKeyUp("Escape", () => {
|
||||
setShowBindings(!showBindings);
|
||||
});
|
||||
|
@ -48,9 +52,11 @@ function App() {
|
|||
volume={volume}
|
||||
setVolume={setVolume}
|
||||
hide={() => setShowBindings(false)}
|
||||
restart={() => mgbaRef.current?.restart()}
|
||||
/>
|
||||
)}
|
||||
<Mgba
|
||||
ref={mgbaRef}
|
||||
gameUrl="/game.gba"
|
||||
volume={volume}
|
||||
controls={bindings.Actual}
|
||||
|
@ -67,6 +73,7 @@ function BindingsWindow({
|
|||
volume,
|
||||
setVolume,
|
||||
hide,
|
||||
restart,
|
||||
}: {
|
||||
bindings: Bindings;
|
||||
setBindings: (b: Bindings) => void;
|
||||
|
@ -74,6 +81,7 @@ function BindingsWindow({
|
|||
volume: number;
|
||||
setVolume: (v: number) => void;
|
||||
hide: () => void;
|
||||
restart: () => void;
|
||||
}) {
|
||||
return (
|
||||
<BindingsDialog open onClose={hide}>
|
||||
|
@ -97,7 +105,8 @@ function BindingsWindow({
|
|||
setBindings={setBindings}
|
||||
setPaused={setPaused}
|
||||
/>
|
||||
<CloseButton onClick={hide}>Close</CloseButton>
|
||||
<ActionButton onClick={restart}>Restart</ActionButton>
|
||||
<ActionButton onClick={hide}>Close</ActionButton>
|
||||
</BindingsDialog>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
FC,
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import mGBA from "./vendor/mgba";
|
||||
import { KeyBindings } from "./bindings";
|
||||
import { styled } from "styled-components";
|
||||
|
@ -28,7 +35,12 @@ const MgbaCanvas = styled.canvas`
|
|||
max-height: 100%;
|
||||
`;
|
||||
|
||||
export const Mgba: FC<MgbaProps> = ({ gameUrl, volume, controls, paused }) => {
|
||||
export interface MgbaHandle {
|
||||
restart: () => void;
|
||||
}
|
||||
|
||||
export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
|
||||
({ gameUrl, volume, controls, paused }, ref) => {
|
||||
const canvas = useRef(null);
|
||||
const mgbaModule = useRef<Module>({});
|
||||
|
||||
|
@ -112,5 +124,12 @@ export const Mgba: FC<MgbaProps> = ({ gameUrl, volume, controls, paused }) => {
|
|||
}
|
||||
}, [gameLoaded, paused]);
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
restart: () => mgbaModule.current.quickReload(),
|
||||
};
|
||||
});
|
||||
|
||||
return <MgbaCanvas ref={canvas} />;
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,36 +1,28 @@
|
|||
import { useRef, useLayoutEffect, useEffect } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export const useLocalStorage = <T>(currentValue: T, appName: string): T => {
|
||||
const initialValue = useRef<T>();
|
||||
|
||||
const isFirstRun = !initialValue.current;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!initialValue.current) {
|
||||
export const useLocalStorage = <T>(
|
||||
defaultValue: T,
|
||||
appName: string
|
||||
): [T, (newValue: T) => void] => {
|
||||
const [value, setValue] = useState(() => {
|
||||
try {
|
||||
const storageValue = localStorage.getItem(appName);
|
||||
if (storageValue) {
|
||||
initialValue.current = JSON.parse(storageValue);
|
||||
return JSON.parse(storageValue);
|
||||
} else {
|
||||
initialValue.current = currentValue;
|
||||
return defaultValue;
|
||||
}
|
||||
} catch {
|
||||
initialValue.current = currentValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
});
|
||||
|
||||
const setStoredValue = useCallback((newValue: T) => {
|
||||
setValue(newValue);
|
||||
try {
|
||||
localStorage.setItem(appName, JSON.stringify(newValue));
|
||||
} catch {}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (initialValue.current && currentValue) {
|
||||
localStorage.setItem(appName, JSON.stringify(currentValue));
|
||||
}
|
||||
} catch {}
|
||||
}, [currentValue]);
|
||||
|
||||
if (isFirstRun) {
|
||||
return initialValue.current ?? currentValue;
|
||||
} else {
|
||||
return currentValue;
|
||||
}
|
||||
return [value, setStoredValue];
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue