sync overview updates

Using EventEmitter. Also get rid of ShownContext as it is no longer needed with this event based rerender.
This commit is contained in:
omuretsu 2023-01-02 08:54:18 -05:00
parent 90fc82b86f
commit d00ed1f4ee

@ -1,5 +1,5 @@
// Root React Component for the Corporation UI // Root React Component for the Corporation UI
import React, { useMemo, useState, useEffect, createContext, useContext } from "react"; import React, { useMemo, useState, useEffect } from "react";
import { Theme, useTheme } from "@mui/material/styles"; import { Theme, useTheme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
@ -40,10 +40,11 @@ import { ActionIdentifier } from "../../Bladeburner/ActionIdentifier";
import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { Skills } from "../../PersonObjects/Skills"; import { Skills } from "../../PersonObjects/Skills";
import { calculateSkillProgress } from "../../PersonObjects/formulas/skill"; import { calculateSkillProgress } from "../../PersonObjects/formulas/skill";
import { EventEmitter } from "../../utils/EventEmitter";
type SkillRowName = "Hack" | "Str" | "Def" | "Dex" | "Agi" | "Cha" | "Int"; type SkillRowName = "Hack" | "Str" | "Def" | "Dex" | "Agi" | "Cha" | "Int";
type RowName = SkillRowName | "HP" | "Money"; type RowName = SkillRowName | "HP" | "Money";
const ShownContext = createContext(true); //Context for whether to update components const OverviewEventEmitter = new EventEmitter();
// These values aren't displayed, they're just used for comparison to check if state has changed // These values aren't displayed, they're just used for comparison to check if state has changed
const valUpdaters: Record<RowName, () => any> = { const valUpdaters: Record<RowName, () => any> = {
@ -97,17 +98,15 @@ interface SkillBarProps {
color?: string; color?: string;
} }
function SkillBar({ name, color }: SkillBarProps): React.ReactElement { function SkillBar({ name, color }: SkillBarProps): React.ReactElement {
const shown = useContext(ShownContext);
const [mult, setMult] = useState(skillMultUpdaters[name]?.()); const [mult, setMult] = useState(skillMultUpdaters[name]?.());
const [progress, setProgress] = useState(calculateSkillProgress(Player.exp[skillNameMap[name]], mult)); const [progress, setProgress] = useState(calculateSkillProgress(Player.exp[skillNameMap[name]], mult));
useEffect(() => { useEffect(() => {
if (!shown) return; const clearSubscription = OverviewEventEmitter.subscribe(() => {
const interval = setInterval(() => {
setMult(skillMultUpdaters[name]()); setMult(skillMultUpdaters[name]());
setProgress(calculateSkillProgress(Player.exp[skillNameMap[name] as keyof Skills], mult)); setProgress(calculateSkillProgress(Player.exp[skillNameMap[name] as keyof Skills], mult));
}, 600); });
return () => clearInterval(interval); return clearSubscription;
}, [shown]); }, []);
return ( return (
<TableRow> <TableRow>
<StatsProgressOverviewCell progress={progress} color={color} /> <StatsProgressOverviewCell progress={progress} color={color} />
@ -122,12 +121,10 @@ interface ValProps {
export function Val({ name, color }: ValProps): React.ReactElement { export function Val({ name, color }: ValProps): React.ReactElement {
//val isn't actually used here, the update of val just forces a refresh of the formattedVal that gets shown //val isn't actually used here, the update of val just forces a refresh of the formattedVal that gets shown
const setVal = useState(valUpdaters[name]())[1]; const setVal = useState(valUpdaters[name]())[1];
const shown = useContext(ShownContext);
useEffect(() => { useEffect(() => {
if (!shown) return; const clearSubscription = OverviewEventEmitter.subscribe(() => setVal(valUpdaters[name]()));
const interval = setInterval(() => setVal(valUpdaters[name]()), 600); return clearSubscription;
return () => clearInterval(interval); }, []);
}, [shown]);
return <Typography color={color}>{formattedVals[name]()}</Typography>; return <Typography color={color}>{formattedVals[name]()}</Typography>;
} }
@ -172,17 +169,18 @@ export function CharacterOverview({ parentOpen, save, killScripts }: OverviewPro
const [hasIntelligence, setHasIntelligence] = useState(Player.skills.intelligence > 0); const [hasIntelligence, setHasIntelligence] = useState(Player.skills.intelligence > 0);
const [showBars, setShowBars] = useState(!Settings.DisableOverviewProgressBars); const [showBars, setShowBars] = useState(!Settings.DisableOverviewProgressBars);
useEffect(() => { useEffect(() => {
if (!parentOpen) return; // No rerendering if overview is hidden, for performance
const interval = setInterval(() => { const interval = setInterval(() => {
//Todo: Consider making these event-based instead of requiring interval polling?
setHasIntelligence(Player.skills.intelligence > 0); setHasIntelligence(Player.skills.intelligence > 0);
setShowBars(!Settings.DisableOverviewProgressBars); setShowBars(!Settings.DisableOverviewProgressBars);
}, 1000); OverviewEventEmitter.emit(); // Tell every other updating component to update as well
}, 600);
return () => clearInterval(interval); return () => clearInterval(interval);
}, []); }, [parentOpen]);
const classes = useStyles(); const classes = useStyles();
const theme = useTheme(); const theme = useTheme();
return ( return (
<ShownContext.Provider value={parentOpen}> <>
<Table sx={{ display: "block", m: 1 }}> <Table sx={{ display: "block", m: 1 }}>
<TableBody> <TableBody>
<DataRow name="HP" showBar={false} color={theme.colors.hp} cellType={"cellNone"} /> <DataRow name="HP" showBar={false} color={theme.colors.hp} cellType={"cellNone"} />
@ -236,7 +234,7 @@ export function CharacterOverview({ parentOpen, save, killScripts }: OverviewPro
</Box> </Box>
</Box> </Box>
<KillScriptsModal open={killOpen} onClose={() => setKillOpen(false)} killScripts={killScripts} /> <KillScriptsModal open={killOpen} onClose={() => setKillOpen(false)} killScripts={killScripts} />
</ShownContext.Provider> </>
); );
} }
@ -251,14 +249,12 @@ function ActionText(props: { action: ActionIdentifier }): React.ReactElement {
} }
function BladeburnerText(): React.ReactElement { function BladeburnerText(): React.ReactElement {
const shown = useContext(ShownContext);
const classes = useStyles(); const classes = useStyles();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
useEffect(() => { useEffect(() => {
if (!shown) return; const clearSubscription = OverviewEventEmitter.subscribe(() => setRerender((old) => !old));
const interval = setInterval(() => setRerender((old) => !old), 600); return clearSubscription;
return () => clearInterval(interval); }, []);
}, [shown]);
const action = Player.bladeburner?.action; const action = Player.bladeburner?.action;
return useMemo( return useMemo(
@ -330,13 +326,11 @@ function WorkInProgressOverview({ tooltip, children, header }: WorkInProgressOve
} }
function Work(): React.ReactElement { function Work(): React.ReactElement {
const shown = useContext(ShownContext);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
useEffect(() => { useEffect(() => {
if (!shown) return; const clearSubscription = OverviewEventEmitter.subscribe(() => setRerender((old) => !old));
const interval = setInterval(() => setRerender((old) => !old), 600); return clearSubscription;
return () => clearInterval(interval); }, []);
}, [shown]);
if (Player.currentWork === null || Player.focus) return <></>; if (Player.currentWork === null || Player.focus) return <></>;