From 149d687fd6ffb0e4a1eaebe2a99a4da249509d8a Mon Sep 17 00:00:00 2001 From: David Walker Date: Wed, 21 Dec 2022 15:27:51 -0800 Subject: [PATCH] UI: Memoize character overview (#247) --- src/ui/GameRoot.tsx | 16 +- src/ui/React/CharacterOverview.tsx | 514 ++++++++++++++--------------- src/ui/React/Overview.tsx | 4 +- 3 files changed, 258 insertions(+), 276 deletions(-) diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 9cbee6eee..9d49ff0e5 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -432,11 +432,17 @@ export function GameRoot(): React.ReactElement { - {!ITutorial.isRunning ? ( - saveObject.saveGame()} killScripts={killAllScripts} /> - ) : ( - - )} + {(parentOpen) => + !ITutorial.isRunning ? ( + saveObject.saveGame()} + killScripts={killAllScripts} + /> + ) : ( + + ) + } {withSidebar ? ( diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx index 227d2c1c1..55da3f029 100644 --- a/src/ui/React/CharacterOverview.tsx +++ b/src/ui/React/CharacterOverview.tsx @@ -1,5 +1,5 @@ // Root React Component for the Corporation UI -import React, { useState, useEffect } from "react"; +import React, { useMemo, useState, useEffect } from "react"; import { Theme, useTheme } from "@mui/material/styles"; import makeStyles from "@mui/styles/makeStyles"; @@ -38,41 +38,11 @@ import { isCompanyWork } from "../../Work/CompanyWork"; import { isCrimeWork } from "../../Work/CrimeWork"; interface IProps { + parentOpen: boolean; save: () => void; killScripts: () => void; } -function Intelligence(): React.ReactElement { - const theme = useTheme(); - const classes = useStyles(); - if (Player.skills.intelligence === 0) return <>; - const progress = Player.calculateSkillProgress(Player.exp.intelligence); - - return ( - <> - - - Int  - - - {formatNumber(Player.skills.intelligence, 0)} - - - - {/*Hook for player scripts*/} - - - - - - {!Settings.DisableOverviewProgressBars && ( - - )} - - - ); -} - function Bladeburner(): React.ReactElement { const classes = useStyles(); const bladeburner = Player.bladeburner; @@ -81,18 +51,28 @@ function Bladeburner(): React.ReactElement { if (action.type === "Idle") return <>; return ( <> - - - Bladeburner: - - - - - - {action.type}: {action.name} - - - + {useMemo( + () => ( + + + Bladeburner: + + + ), + [classes.cellNone], + )} + {useMemo( + () => ( + + + + {action.type}: {action.name} + + + + ), + [classes.cellNone, action.type, action.name], + )} ); } @@ -101,15 +81,13 @@ interface WorkInProgressOverviewProps { tooltip: React.ReactNode; header: React.ReactNode; children: React.ReactNode; - onClickFocus: () => void; } -function WorkInProgressOverview({ - tooltip, - children, - onClickFocus, - header, -}: WorkInProgressOverviewProps): React.ReactElement { +const onClickFocusWork = (): void => { + Player.startFocusing(); + Router.toPage(Page.Work); +}; +function WorkInProgressOverview({ tooltip, children, header }: WorkInProgressOverviewProps): React.ReactElement { const classes = useStyles(); return ( <> @@ -127,22 +105,23 @@ function WorkInProgressOverview({ {children} - - - - - + {useMemo( + () => ( + + + + + + ), + [classes.cellNone], + )} ); } function Work(): React.ReactElement { - const onClickFocus = (): void => { - Player.startFocusing(); - Router.toPage(Page.Work); - }; if (Player.currentWork === null || Player.focus) return <>; let details = <>; @@ -219,7 +198,7 @@ function Work(): React.ReactElement { } return ( - + {innerText} ); @@ -275,13 +254,63 @@ const useStyles = makeStyles((theme: Theme) => export { useStyles as characterOverviewStyles }; -export function CharacterOverview({ save, killScripts }: IProps): React.ReactElement { +function rowWithHook(name: string, value: string, className: string, cellNone: string): React.ReactElement { + return useMemo( + () => ( + + + {name}  + + + {value} + + + + {/*Hook for player scripts*/} + + + + ), + [name, value, className, cellNone], + ); +} + +function statItem( + name: string, + value: number, + className: string, + cellNone: string, + themeColor: React.CSSProperties["color"], + exp: number, + mult: number, + bitNodeMult: number, +): React.ReactElement[] { + return [ + rowWithHook(name, formatNumber(value, 0), className, cellNone), + useMemo(() => { + const progress = Player.calculateSkillProgress(exp, mult * bitNodeMult); + return ( + + {!Settings.DisableOverviewProgressBars && ( + + )} + + ); + }, [Settings.DisableOverviewProgressBars, themeColor, exp, mult, bitNodeMult]), + ]; +} + +export function CharacterOverview({ parentOpen, save, killScripts }: IProps): React.ReactElement { const [killOpen, setKillOpen] = useState(false); const setRerender = useState(false)[1]; + // Don't rerender while the overview is closed. useEffect(() => { - const id = setInterval(() => setRerender((old) => !old), 600); - return () => clearInterval(id); - }, []); + if (parentOpen) { + const id = setInterval(() => setRerender((old) => !old), 600); + return () => clearInterval(id); + } + return () => null; + }, [parentOpen]); const classes = useStyles(); const theme = useTheme(); @@ -289,219 +318,166 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle Player.exp.hacking, Player.mults.hacking * BitNodeMultipliers.HackingLevelMultiplier, ); - const strengthProgress = Player.calculateSkillProgress( - Player.exp.strength, - Player.mults.strength * BitNodeMultipliers.StrengthLevelMultiplier, - ); - const defenseProgress = Player.calculateSkillProgress( - Player.exp.defense, - Player.mults.defense * BitNodeMultipliers.DefenseLevelMultiplier, - ); - const dexterityProgress = Player.calculateSkillProgress( - Player.exp.dexterity, - Player.mults.dexterity * BitNodeMultipliers.DexterityLevelMultiplier, - ); - const agilityProgress = Player.calculateSkillProgress( - Player.exp.agility, - Player.mults.agility * BitNodeMultipliers.AgilityLevelMultiplier, - ); - const charismaProgress = Player.calculateSkillProgress( - Player.exp.charisma, - Player.mults.charisma * BitNodeMultipliers.CharismaLevelMultiplier, - ); - return ( <> - - - HP  - - - - {numeralWrapper.formatHp(Player.hp.current)} / {numeralWrapper.formatHp(Player.hp.max)} - - - - - {/*Hook for player scripts*/} - - - + {rowWithHook( + "HP", + numeralWrapper.formatHp(Player.hp.current) + "\u00a0/\u00a0" + numeralWrapper.formatHp(Player.hp.max), + classes.hp, + classes.cellNone, + )} + {rowWithHook("Money", numeralWrapper.formatMoney(Player.money), classes.money, classes.cellNone)} - - - Money  - - - {numeralWrapper.formatMoney(Player.money)} - - - - {/*Hook for player scripts*/} - - - + {useMemo( + // Hack is a special-case, because of its overview-hack-hook placement + () => ( + + + Hack  + + + {formatNumber(Player.skills.hacking, 0)} + + + ), + [Player.skills.hacking, classes.hack, classes.cellNone], + )} + {useMemo( + () => ( + + {!Settings.DisableOverviewProgressBars && ( + + )} + + ), + [Settings.DisableOverviewProgressBars, Player.exp.hacking, Player.mults.hacking, theme.colors.hack], + )} + {useMemo( + () => ( + + + + + + + {/*Hook for player scripts*/} + + + + ), + [classes.cell, classes.hack], + )} - - - Hack  - - - {formatNumber(Player.skills.hacking, 0)} - - - - {!Settings.DisableOverviewProgressBars && ( - + {statItem( + "Str", + Player.skills.strength, + classes.combat, + classes.cellNone, + theme.colors.combat, + Player.exp.strength, + Player.mults.strength, + BitNodeMultipliers.StrengthLevelMultiplier, + )} + {statItem( + "Def", + Player.skills.defense, + classes.combat, + classes.cellNone, + theme.colors.combat, + Player.exp.defense, + Player.mults.defense, + BitNodeMultipliers.DefenseLevelMultiplier, + )} + {statItem( + "Dex", + Player.skills.dexterity, + classes.combat, + classes.cellNone, + theme.colors.combat, + Player.exp.dexterity, + Player.mults.dexterity, + BitNodeMultipliers.DexterityLevelMultiplier, + )} + {statItem( + "Agi", + Player.skills.agility, + classes.combat, + classes.cellNone, + theme.colors.combat, + Player.exp.agility, + Player.mults.agility, + BitNodeMultipliers.AgilityLevelMultiplier, + )} + {statItem( + "Cha", + Player.skills.charisma, + classes.cha, + classes.cellNone, + theme.colors.cha, + Player.exp.charisma, + Player.mults.charisma, + BitNodeMultipliers.CharismaLevelMultiplier, + )} + {Player.skills.intelligence !== 0 && + statItem( + "Int", + Player.skills.intelligence, + classes.int, + classes.cellNone, + theme.colors.int, + Player.exp.intelligence, + 1, + 1, )} - - - - - - - - {/*Hook for player scripts*/} - - - - - - - Str  - - - {formatNumber(Player.skills.strength, 0)} - - - - {/*Hook for player scripts*/} - - - - - {!Settings.DisableOverviewProgressBars && ( - - )} - - - - - Def  - - - {formatNumber(Player.skills.defense, 0)} - - - - {/*Hook for player scripts*/} - - - - - {!Settings.DisableOverviewProgressBars && ( - - )} - - - - - Dex  - - - {formatNumber(Player.skills.dexterity, 0)} - - - - {/*Hook for player scripts*/} - - - - - {!Settings.DisableOverviewProgressBars && ( - - )} - - - - - Agi  - - - {formatNumber(Player.skills.agility, 0)} - - - - {/*Hook for player scripts*/} - - - - - {!Settings.DisableOverviewProgressBars && ( - - )} - - - - - Cha  - - - {formatNumber(Player.skills.charisma, 0)} - - - - {/*Hook for player scripts*/} - - - - - {!Settings.DisableOverviewProgressBars && ( - - )} - - - - - - - - {/*Hook for player scripts*/} - - - - - {/*Hook for player scripts*/} - - - - - {/*Hook for player scripts*/} - - - + {useMemo( + () => ( + + + + {/*Hook for player scripts*/} + + + + + {/*Hook for player scripts*/} + + + + + {/*Hook for player scripts*/} + + + + ), + [classes.cell, classes.hack], + )}
- - - - - - - - - - setKillOpen(true)}> - - - - - - + {useMemo( + () => ( + + + + + + + + + + setKillOpen(true)}> + + + + + + + ), + [Settings.theme.welllight, save, Settings.AutosaveInterval], + )} setKillOpen(false)} killScripts={killScripts} /> ); diff --git a/src/ui/React/Overview.tsx b/src/ui/React/Overview.tsx index cccf54681..af5938be2 100644 --- a/src/ui/React/Overview.tsx +++ b/src/ui/React/Overview.tsx @@ -53,7 +53,7 @@ const useStyles = makeStyles({ }); interface IProps { - children: JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement; + children: (parentOpen: boolean) => JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement; mode: "tutorial" | "overview"; } @@ -141,7 +141,7 @@ export function Overview({ children, mode }: IProps): React.ReactElement {
- {children} + {children(open)}