--- /dev/null
+import PropTypes from 'prop-types';
+import React from 'react';
+
+import SNESSocket from '../helpers/SNESSocket';
+
+const context = React.createContext({});
+
+export const useSNES = () => React.useContext(context);
+
+export const SNESProvider = ({ children }) => {
+ const [enabled, setEnabled] = React.useState(false);
+
+ const sock = React.useRef(null);
+
+ const [settings, setSettings] = React.useState({
+ proto: 'ws',
+ host: 'localhost',
+ port: 8080,
+ device: '',
+ });
+
+ const [status, setStatus] = React.useState({
+ connected: false,
+ device: '',
+ deviceList: [],
+ error: false,
+ });
+
+ React.useEffect(() => {
+ if (sock.current) {
+ sock.current.close();
+ sock.current = null;
+ }
+ if (enabled) {
+ const tryAttach = () => {
+ const { deviceList } = sock.current;
+ let device = '';
+ if (deviceList.includes(settings.device)) {
+ device = settings.device;
+ } else if (deviceList.length > 0) {
+ device = deviceList[0];
+ }
+ setStatus(s => ({ ...s, device, deviceList }));
+ if (device) {
+ sock.current.attachDevice(device);
+ }
+ };
+ sock.current = new SNESSocket(`${settings.proto}://${settings.host}:${settings.port}`);
+ sock.current.onclose = () => {
+ setStatus({
+ connected: false,
+ device: '',
+ deviceList: [],
+ error: false,
+ });
+ };
+ sock.current.onerror = (e) => {
+ setStatus({
+ connected: false,
+ device: '',
+ deviceList: [],
+ error: e,
+ });
+ };
+ sock.current.onopen = () => {
+ setStatus({
+ connected: true,
+ device: '',
+ deviceList: [],
+ error: false,
+ });
+ sock.current.requestDeviceList(() => {
+ tryAttach();
+ });
+ };
+ const watchdog = setInterval(() => {
+ if (!sock.current.isOpen()) {
+ sock.current.open();
+ return;
+ }
+ if (!sock.current.device) {
+ sock.current.requestDeviceList(() => {
+ tryAttach();
+ });
+ }
+ }, 5000);
+ return () => {
+ clearInterval(watchdog);
+ };
+ }
+ }, [enabled, settings]);
+
+ const enable = React.useCallback(() => {
+ setEnabled(prevEnabled => {
+ if (prevEnabled) return true;
+ return true;
+ });
+ }, []);
+
+ const disable = React.useCallback(() => {
+ setEnabled(prevEnabled => {
+ if (!prevEnabled) return false;
+ return false;
+ });
+ }, []);
+
+ React.useEffect(() => {
+ const savedSettings = localStorage.getItem('snes.settings');
+ if (savedSettings) {
+ setSettings(JSON.parse(savedSettings));
+ }
+ }, []);
+
+ const value = React.useMemo(() => {
+ return { disable, enable, enabled, settings, sock, status };
+ }, [disable, enable, enabled, settings, sock, status]);
+
+ return <context.Provider value={value}>
+ {children}
+ </context.Provider>;
+};
+
+SNESProvider.propTypes = {
+ children: PropTypes.node,
+};