import React from 'react' import listenActWebSocket from './ACTWebsocket' import './css/App.css' import Action from './Action' import RotationContainer from './Rotation' import ReactDOM from 'react-dom' export default function App() { // NOTE: unlike class state, useState doesn't do object merging; instead, it directly holds values const [actionList, setActionList] = React.useState([]) const [encounterList, setEncounterList] = React.useState([]) React.useEffect(() => { // These values are only used internally by the handler, // we don't need to notify React that they were updated, // or keep their updates synchronized with actionList updates. // // This means we don't have to keep them in State (or Reducer)! let selfId let lastTimestamp = '' let lastAction = -1 let currentZone = 'Unknown' // we need keys to persist for each push, even if we shorten the array later, // so we store the key with the action; can't just use array index due to CSS let lastKey = 1 // listenActWebSocket should be changed to return the websocket, // and this effect should return a function that disconnects the websocket // // like "return () => { ws.close() }" let ws = listenActWebSocket((data, code) => { const openNewEncounter = (timestamp) => { setEncounterList(encounterList => { if(encounterList[0] && encounterList[0].rotation && encounterList[0].rotation.length <= 0) { encounterList.shift() } encounterList.unshift({ name: currentZone, rotation: [] }) return encounterList.slice(0,3) }) } if (data.charID) { selfId = data.charID openNewEncounter() return } switch(code) { case '00': const [, , refCode, , message] = data.split('|') if(refCode === '0038' && message === 'end') openNewEncounter() return case '01': const [, , , zoneName] = data.split('|') currentZone = zoneName return case '02': const [, , logCharIdHex] = data.split('|') selfId = parseInt(logCharIdHex, 16) openNewEncounter() return case '33': const [, , , controlCode] = data.split('|') if(controlCode === '40000012' || controlCode === '40000010') openNewEncounter() return default: break } //if it's not any of these it must be Network(AOE)Ability, the bulk of what we want to handle if (selfId === undefined) return const [, logTimestamp, logCharIdHex, , logActionIdHex] = data.split('|') // microoptimization: since selfId updates way less often, // save selfId as data.charID.toString(16), that way you don't need to // parse logCharIdHex every time if (parseInt(logCharIdHex, 16) !== selfId) return // we do a mathematical comparison with action though so can't optimize this away const action = parseInt(logActionIdHex, 16) if ( action <= 8 || (logTimestamp === lastTimestamp && action === lastAction) ) return if((Date.now() - Date.parse(lastTimestamp)) > 120000) openNewEncounter()//last action > 120s ago lastTimestamp = logTimestamp lastAction = action const key = (lastKey % 256) + 1 lastKey = key ReactDOM.unstable_batchedUpdates(() => { setActionList(actionList => actionList.concat({ action, key })) setEncounterList(encounterList => { if(!encounterList[0]) { encounterList[0] = { name: currentZone, rotation: [] } } encounterList[0].rotation.push( action ) return encounterList }) }) // This _probably_ should be done as a separate React.useEffect instead, // which runs as an effect whenever the value of actionList changes. // The problem there is, it would have to detect whether the list grew // since the last time it was called, otherwise it'd react (heh) to its own // updates. // // Easier to pair it with the previous set. setTimeout(() => { setActionList(actionList => actionList.slice(1)) }, 10000) }) return () => { ws.close() } }, []) return (