Enforce functions (#625)

This commit is contained in:
Corwin 2024-04-09 20:22:28 +01:00 committed by GitHub
commit 646b7947e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 95 additions and 74 deletions

View file

@ -1,3 +1,6 @@
{ {
"extends": "next/core-web-vitals" "extends": "next/core-web-vitals",
"rules": {
"func-style": ["error", "declaration", {"allowArrowFunctions": false}]
}
} }

View file

@ -32,12 +32,18 @@ const InnerBlock = styled.div<{ $centered?: boolean }>`
margin-bottom: 40px; margin-bottom: 40px;
`; `;
export const ContentBlock: FC<{ export function ContentBlock({
color = "",
children,
uncentered = false,
}: {
color?: string; color?: string;
uncentered?: boolean; uncentered?: boolean;
children: ReactNode; children: ReactNode;
}> = ({ color = "", children, uncentered = false }) => ( }) {
return (
<Section $color={color}> <Section $color={color}>
<InnerBlock $centered={!uncentered}>{children}</InnerBlock> <InnerBlock $centered={!uncentered}>{children}</InnerBlock>
</Section> </Section>
); );
}

View file

@ -10,9 +10,11 @@ const BacktraceWrapper = styled.section`
justify-content: center; justify-content: center;
`; `;
const getBacktrace = () => window.location.hash.slice(1); function getBacktrace() {
return window.location.hash.slice(1);
}
export const BacktraceDisplay: FC = () => { export function BacktraceDisplay() {
const backtrace = useClientValue(getBacktrace) ?? ""; const backtrace = useClientValue(getBacktrace) ?? "";
return ( return (
@ -28,4 +30,4 @@ export const BacktraceDisplay: FC = () => {
</button> </button>
</BacktraceWrapper> </BacktraceWrapper>
); );
}; }

View file

@ -34,15 +34,15 @@ export function Slider({
}) { }) {
const slider = useRef<HTMLDivElement>(null); const slider = useRef<HTMLDivElement>(null);
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => { function handleClick(event: React.MouseEvent<HTMLDivElement>) {
onChange( onChange(
event.nativeEvent.offsetX / (event.target as HTMLDivElement).offsetWidth event.nativeEvent.offsetX / (event.target as HTMLDivElement).offsetWidth
); );
event.stopPropagation(); event.stopPropagation();
}; }
const handleDrag = (event: React.MouseEvent<HTMLDivElement>) => { function handleDrag(event: React.MouseEvent<HTMLDivElement>) {
const sliderRef = slider.current; const sliderRef = slider.current;
if (!sliderRef || event.buttons !== 1) { if (!sliderRef || event.buttons !== 1) {
@ -56,7 +56,7 @@ export function Slider({
const clamped = Math.min(1, Math.max(0, proportion)); const clamped = Math.min(1, Math.max(0, proportion));
onChange(clamped); onChange(clamped);
}; }
return ( return (
<SliderWrapper ref={slider} onClick={handleClick} onMouseMove={handleDrag}> <SliderWrapper ref={slider} onClick={handleClick} onMouseMove={handleDrag}>

View file

@ -1,7 +1,7 @@
import { FC, useState } from "react"; import { FC, useState } from "react";
import styled from "styled-components"; import styled from "styled-components";
const DefaultBindings = (): KeyBindings => { function DefaultBindings(): KeyBindings {
return { return {
A: "Z", A: "Z",
B: "X", B: "X",
@ -14,12 +14,14 @@ const DefaultBindings = (): KeyBindings => {
Left: "LEFT", Left: "LEFT",
Right: "RIGHT", Right: "RIGHT",
}; };
}; }
export const DefaultBindingsSet = (): Bindings => ({ export function DefaultBindingsSet(): Bindings {
return {
Actual: DefaultBindings(), Actual: DefaultBindings(),
Displayed: DefaultBindings(), Displayed: DefaultBindings(),
}); };
}
export enum GbaKey { export enum GbaKey {
A = "A", A = "A",
@ -69,18 +71,22 @@ export interface Bindings {
Actual: KeyBindings; Actual: KeyBindings;
} }
const toHumanName = (keyName: string) => { function toHumanName(keyName: string) {
return keyName.replace("Arrow", ""); return keyName.replace("Arrow", "");
}; }
export const BindingsControl: FC<{ export function BindingsControl({
bindings,
setBindings,
setPaused,
}: {
bindings: Bindings; bindings: Bindings;
setBindings: (a: Bindings) => void; setBindings: (a: Bindings) => void;
setPaused: (pause: boolean) => void; setPaused: (pause: boolean) => void;
}> = ({ bindings, setBindings, setPaused }) => { }) {
const [buttonToChange, setButtonToChange] = useState<GbaKey | null>(null); const [buttonToChange, setButtonToChange] = useState<GbaKey | null>(null);
const setKey = (key: string) => { function setKey(key: string) {
if (buttonToChange === null) return; if (buttonToChange === null) return;
const nextBindings = { const nextBindings = {
@ -94,12 +100,12 @@ export const BindingsControl: FC<{
setButtonToChange(null); setButtonToChange(null);
setBindings(nextBindings); setBindings(nextBindings);
setPaused(false); setPaused(false);
}; }
const onSelectButtonClick = (key: GbaKey) => { function onSelectButtonClick(key: GbaKey) {
setPaused(true); setPaused(true);
setButtonToChange(key); setButtonToChange(key);
}; }
return ( return (
<ButtonWrapper onKeyUp={(evt: React.KeyboardEvent) => setKey(evt.key)}> <ButtonWrapper onKeyUp={(evt: React.KeyboardEvent) => setKey(evt.key)}>
@ -116,4 +122,4 @@ export const BindingsControl: FC<{
))} ))}
</ButtonWrapper> </ButtonWrapper>
); );
}; }

View file

@ -41,7 +41,7 @@ export interface MgbaHandle {
buttonRelease: (key: GbaKey) => void; buttonRelease: (key: GbaKey) => void;
} }
const downloadGame = async (gameUrl: string): Promise<ArrayBuffer> => { async function downloadGame(gameUrl: string): Promise<ArrayBuffer> {
const game = await fetch(gameUrl); const game = await fetch(gameUrl);
if (gameUrl.endsWith(".gz")) { if (gameUrl.endsWith(".gz")) {
@ -52,7 +52,7 @@ const downloadGame = async (gameUrl: string): Promise<ArrayBuffer> => {
} else { } else {
return await game.arrayBuffer(); return await game.arrayBuffer();
} }
}; }
export const Mgba = forwardRef<MgbaHandle, MgbaProps>( export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
({ gameUrl, volume, controls, paused }, ref) => { ({ gameUrl, volume, controls, paused }, ref) => {

View file

@ -65,11 +65,13 @@ interface MgbaWrapperProps {
setIsPlaying?: (isPlaying: boolean) => void; setIsPlaying?: (isPlaying: boolean) => void;
} }
export const MgbaStandalone: FC<MgbaWrapperProps> = (props) => ( export function MgbaStandalone(props: MgbaWrapperProps) {
return (
<AppContainer> <AppContainer>
<MgbaWrapper {...props} /> <MgbaWrapper {...props} />
</AppContainer> </AppContainer>
); );
}
export const MgbaWrapper = forwardRef<MgbaHandle, MgbaWrapperProps>( export const MgbaWrapper = forwardRef<MgbaHandle, MgbaWrapperProps>(
({ gameUrl, isPlaying = true, setIsPlaying }, ref) => { ({ gameUrl, isPlaying = true, setIsPlaying }, ref) => {
@ -80,10 +82,12 @@ export const MgbaWrapper = forwardRef<MgbaHandle, MgbaWrapperProps>(
const [mgbaId, setMgbaId] = useState(0); const [mgbaId, setMgbaId] = useState(0);
const setVolume = (newVolume: number) => function setVolume(newVolume: number) {
setState({ volume: newVolume, bindings }); return setState({ volume: newVolume, bindings });
const setBindings = (newBindings: Bindings) => }
setState({ volume, bindings: newBindings }); function setBindings(newBindings: Bindings) {
return setState({ volume, bindings: newBindings });
}
const [paused, setPaused] = useState(false); const [paused, setPaused] = useState(false);

View file

@ -1,12 +1,12 @@
import { useEffect } from "react"; import { useEffect } from "react";
export const useAvoidItchIoScrolling = () => { export function useAvoidItchIoScrolling() {
useEffect(() => { useEffect(() => {
const eventHandler = (e: KeyboardEvent) => { function eventHandler(e: KeyboardEvent) {
if ([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) { if ([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
e.preventDefault(); e.preventDefault();
} }
}; }
window.addEventListener("keydown", eventHandler, false); window.addEventListener("keydown", eventHandler, false);
@ -14,4 +14,4 @@ export const useAvoidItchIoScrolling = () => {
window.removeEventListener("keydown", eventHandler, false); window.removeEventListener("keydown", eventHandler, false);
}; };
}, []); }, []);
}; }

View file

@ -2,7 +2,7 @@ import { MutableRefObject, useEffect } from "react";
import { mGBAEmulator } from "./vendor/mgba"; import { mGBAEmulator } from "./vendor/mgba";
export const useFrameSkip = (mgbaModule: MutableRefObject<mGBAEmulator>) => { export function useFrameSkip(mgbaModule: MutableRefObject<mGBAEmulator>) {
useEffect(() => { useEffect(() => {
let previous: number | undefined = undefined; let previous: number | undefined = undefined;
let stopped = false; let stopped = false;
@ -11,7 +11,7 @@ export const useFrameSkip = (mgbaModule: MutableRefObject<mGBAEmulator>) => {
let totalTime = 0; let totalTime = 0;
let paused = false; let paused = false;
const raf = (time: DOMHighResTimeStamp) => { function raf(time: DOMHighResTimeStamp) {
if (previous) { if (previous) {
const delta = time - previous; const delta = time - previous;
@ -45,7 +45,7 @@ export const useFrameSkip = (mgbaModule: MutableRefObject<mGBAEmulator>) => {
} }
window.requestAnimationFrame(raf); window.requestAnimationFrame(raf);
return () => { stopped = true; } return () => { stopped = true; };
}, [mgbaModule]); }, [mgbaModule]);

View file

@ -1,9 +1,7 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
export const useLocalStorage = <T>( export function useLocalStorage<T>(defaultValue: T,
defaultValue: T, appName: string): [T, (newValue: T) => void] {
appName: string
): [T, (newValue: T) => void] => {
const [value, setValue] = useState(() => { const [value, setValue] = useState(() => {
try { try {
const storageValue = localStorage.getItem(appName); const storageValue = localStorage.getItem(appName);
@ -26,4 +24,4 @@ export const useLocalStorage = <T>(
}, []); }, []);
return [value, setStoredValue]; return [value, setStoredValue];
}; }

View file

@ -1,12 +1,12 @@
import { useEffect } from "react"; import { useEffect } from "react";
export const useOnKeyUp = (targetKey: string, callback: () => void) => { export function useOnKeyUp(targetKey: string, callback: () => void) {
useEffect(() => { useEffect(() => {
const downHandler = (evnt: KeyboardEvent) => { function downHandler(evnt: KeyboardEvent) {
if (evnt.key === targetKey) { if (evnt.key === targetKey) {
callback(); callback();
} }
}; }
window.addEventListener("keyup", downHandler); window.addEventListener("keyup", downHandler);
@ -14,4 +14,4 @@ export const useOnKeyUp = (targetKey: string, callback: () => void) => {
window.removeEventListener("keyup", downHandler); window.removeEventListener("keyup", downHandler);
}; };
}, [callback, targetKey]); }, [callback, targetKey]);
}; }

View file

@ -52,7 +52,7 @@ const MobileControlsRow = styled.div<{
${(props) => props.$centered && `justify-content: center;`} ${(props) => props.$centered && `justify-content: center;`}
`; `;
const useSimpleButton = (mgba: MgbaHandle, button: GbaKey) => { function useSimpleButton(mgba: MgbaHandle, button: GbaKey) {
return useMemo(() => { return useMemo(() => {
return { return {
onTouchStart: () => { onTouchStart: () => {
@ -63,9 +63,9 @@ const useSimpleButton = (mgba: MgbaHandle, button: GbaKey) => {
}, },
}; };
}, [button, mgba]); }, [button, mgba]);
}; }
const relativeTouch = (touch: Touch) => { function relativeTouch(touch: Touch) {
const target = (touch.target as Element).getBoundingClientRect(); const target = (touch.target as Element).getBoundingClientRect();
const touchPoint = { x: touch.clientX, y: touch.clientY }; const touchPoint = { x: touch.clientX, y: touch.clientY };
@ -82,15 +82,15 @@ const relativeTouch = (touch: Touch) => {
}; };
return relativePosition; return relativePosition;
}; }
const useDpadTouch = (mgba: MgbaHandle) => { function useDpadTouch(mgba: MgbaHandle) {
const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>( const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>(
new Set() new Set()
); );
return useMemo(() => { return useMemo(() => {
const updateDpad = (touches: TouchList) => { function updateDpad(touches: TouchList) {
const currentlyPressed = new Set<GbaKey>(); const currentlyPressed = new Set<GbaKey>();
for (let touch of touches) { for (let touch of touches) {
@ -130,7 +130,7 @@ const useDpadTouch = (mgba: MgbaHandle) => {
} }
setTouchedButtons(currentlyPressed); setTouchedButtons(currentlyPressed);
}; }
return { return {
onTouchStart: (event: React.TouchEvent) => onTouchStart: (event: React.TouchEvent) =>
@ -141,15 +141,15 @@ const useDpadTouch = (mgba: MgbaHandle) => {
updateDpad(event.nativeEvent.targetTouches), updateDpad(event.nativeEvent.targetTouches),
}; };
}, [mgba, previouslyPressedButtons]); }, [mgba, previouslyPressedButtons]);
}; }
const useAbTouch = (mgba: MgbaHandle) => { function useAbTouch(mgba: MgbaHandle) {
const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>( const [previouslyPressedButtons, setTouchedButtons] = useState<Set<GbaKey>>(
new Set() new Set()
); );
return useMemo(() => { return useMemo(() => {
const updateAbButtons = (touches: TouchList) => { function updateAbButtons(touches: TouchList) {
const currentlyPressed = new Set<GbaKey>(); const currentlyPressed = new Set<GbaKey>();
for (let touch of touches) { for (let touch of touches) {
@ -173,7 +173,7 @@ const useAbTouch = (mgba: MgbaHandle) => {
} }
setTouchedButtons(currentlyPressed); setTouchedButtons(currentlyPressed);
}; }
return { return {
onTouchStart: (event: React.TouchEvent) => onTouchStart: (event: React.TouchEvent) =>
@ -184,9 +184,9 @@ const useAbTouch = (mgba: MgbaHandle) => {
updateAbButtons(event.nativeEvent.targetTouches), updateAbButtons(event.nativeEvent.targetTouches),
}; };
}, [mgba, previouslyPressedButtons]); }, [mgba, previouslyPressedButtons]);
}; }
export const MobileController: FC<{ mgba: MgbaHandle }> = ({ mgba }) => { export function MobileController({ mgba }: { mgba: MgbaHandle }) {
return ( return (
<MobileControls onContextMenu={(evt) => evt.preventDefault()}> <MobileControls onContextMenu={(evt) => evt.preventDefault()}>
<MobileControlsRow $size={MobileControlsSize.Small}> <MobileControlsRow $size={MobileControlsSize.Small}>
@ -226,4 +226,4 @@ export const MobileController: FC<{ mgba: MgbaHandle }> = ({ mgba }) => {
</MobileControlsRow> </MobileControlsRow>
</MobileControls> </MobileControls>
); );
}; }

View file

@ -69,14 +69,16 @@ const ShowOnWideScreen = styled.div`
} }
`; `;
const isTouchScreen = () => navigator.maxTouchPoints > 1; function isTouchScreen() {
return navigator.maxTouchPoints > 1;
}
function shouldStartPlaying(isTouchScreen: boolean | undefined) { function shouldStartPlaying(isTouchScreen: boolean | undefined) {
if (isTouchScreen === undefined) return false; if (isTouchScreen === undefined) return false;
return !isTouchScreen; return !isTouchScreen;
} }
const MgbaWithControllerSides = () => { function MgbaWithControllerSides() {
const mgba = useRef<MgbaHandle>(null); const mgba = useRef<MgbaHandle>(null);
const mgbaHandle = useMemo( const mgbaHandle = useMemo(
@ -128,7 +130,7 @@ const MgbaWithControllerSides = () => {
</ShowOnWideScreen> </ShowOnWideScreen>
</> </>
); );
}; }
export default function Home() { export default function Home() {
return ( return (
<> <>

View file

@ -1,6 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export const useClientValue = <T,>(fn: () => T) => { export function useClientValue<T>(fn: () => T) {
const [value, setValue] = useState<T>(); const [value, setValue] = useState<T>();
useEffect(() => { useEffect(() => {
setValue(fn()); setValue(fn());