]> git.localhorst.tv Git - alttp.git/blob - resources/js/hooks/snes.js
05e84b2e496d488452a6e53fcfbc32e553cae1f2
[alttp.git] / resources / js / hooks / snes.js
1 import PropTypes from 'prop-types';
2 import React from 'react';
3
4 import SNESSocket from '../helpers/SNESSocket';
5
6 const context = React.createContext({});
7
8 export const useSNES = () => React.useContext(context);
9
10 export const SNESProvider = ({ children }) => {
11         const [enabled, setEnabled] = React.useState(false);
12
13         const sock = React.useRef(null);
14
15         const [settings, setSettings] = React.useState({
16                 proto: 'ws',
17                 host: 'localhost',
18                 port: 8080,
19                 device: '',
20         });
21
22         const [status, setStatus] = React.useState({
23                 connected: false,
24                 device: '',
25                 deviceList: [],
26                 error: false,
27         });
28
29         React.useEffect(() => {
30                 if (sock.current) {
31                         sock.current.close();
32                         sock.current = null;
33                 }
34                 if (enabled) {
35                         const tryAttach = () => {
36                                 const { deviceList } = sock.current;
37                                 let device = '';
38                                 if (deviceList.includes(settings.device)) {
39                                         device = settings.device;
40                                 } else if (deviceList.length > 0) {
41                                         device = deviceList[0];
42                                 }
43                                 setStatus(s => ({ ...s, device, deviceList }));
44                                 if (device) {
45                                         sock.current.attachDevice(device);
46                                 }
47                         };
48                         sock.current = new SNESSocket(`${settings.proto}://${settings.host}:${settings.port}`);
49                         sock.current.onclose = () => {
50                                 setStatus({
51                                         connected: false,
52                                         device: '',
53                                         deviceList: [],
54                                         error: false,
55                                 });
56                         };
57                         sock.current.onerror = (e) => {
58                                 setStatus({
59                                         connected: false,
60                                         device: '',
61                                         deviceList: [],
62                                         error: e,
63                                 });
64                         };
65                         sock.current.onopen = () => {
66                                 setStatus({
67                                         connected: true,
68                                         device: '',
69                                         deviceList: [],
70                                         error: false,
71                                 });
72                                 sock.current.requestDeviceList(() => {
73                                         tryAttach();
74                                 });
75                         };
76                         const watchdog = setInterval(() => {
77                                 if (!sock.current.isOpen()) {
78                                         sock.current.open();
79                                         return;
80                                 }
81                                 if (!sock.current.device) {
82                                         sock.current.requestDeviceList(() => {
83                                                 tryAttach();
84                                         });
85                                 }
86                         }, 5000);
87                         return () => {
88                                 clearInterval(watchdog);
89                         };
90                 }
91         }, [enabled, settings]);
92
93         const enable = React.useCallback(() => {
94                 setEnabled(prevEnabled => {
95                         if (prevEnabled) return true;
96                         return true;
97                 });
98         }, []);
99
100         const disable = React.useCallback(() => {
101                 setEnabled(prevEnabled => {
102                         if (!prevEnabled) return false;
103                         return false;
104                 });
105         }, []);
106
107         React.useEffect(() => {
108                 const savedSettings = localStorage.getItem('snes.settings');
109                 if (savedSettings) {
110                         setSettings(JSON.parse(savedSettings));
111                 }
112         }, []);
113
114         const value = React.useMemo(() => {
115                 return { disable, enable, enabled, settings, sock, status };
116         }, [disable, enable, enabled, settings, sock, status]);
117
118         return <context.Provider value={value}>
119                 {children}
120         </context.Provider>;
121 };
122
123 SNESProvider.propTypes = {
124         children: PropTypes.node,
125 };