Merge pull request #1294 from danielyxie/big-container

Active Scripts in Mui
This commit is contained in:
hydroflame 2021-09-18 00:16:24 -04:00 committed by GitHub
commit d1cb5b313a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 411 additions and 402 deletions

@ -1,128 +0,0 @@
@import "theme";
.active-scripts-list {
list-style-type: none;
}
.active-scripts-container {
> p {
margin: 6px;
padding: 4px;
}
.accordion-header {
> pre {
color: white;
}
}
}
.active-scripts-server-header {
background-color: #444;
font-size: $defaultFontSize * 1.25;
color: #fff;
margin: 6px 6px 0 6px;
padding: 6px;
cursor: pointer;
width: 60%;
text-align: left;
border: none;
outline: none;
&:after {
content: "\02795"; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&.active,
&:hover {
background-color: #555;
}
}
.active-scripts-server-header.active {
&:after {
content: "\2796"; /* "minus" sign (-) */
font-size: $defaultFontSize * 0.8125;
color: #fff;
float: right;
margin-left: 5px;
}
&:hover {
background-color: #666;
}
}
.active-scripts-server-panel {
margin: 0 6px 6px 6px;
padding: 0 6px 6px 6px;
width: 55%;
margin-left: 5%;
display: none;
div,
ul,
ul > li {
background-color: #555;
}
}
.active-scripts-script-header {
background-color: #555;
border: none;
color: var(--my-font-color);
cursor: pointer;
display: block;
outline: none;
padding: 4px 25px 4px 10px;
position: relative;
text-align: left;
width: auto;
&:after {
content: "\02795"; /* "plus" sign (+) */
font-size: $defaultFontSize * 0.8125;
float: right;
margin-left: 5px;
color: transparent;
text-shadow: 0 0 0 var(--my-font-color);
position: absolute;
bottom: 4px;
}
&.active:after {
content: "\2796"; /* "minus" sign (-) */
}
&:hover,
&.active:hover {
background-color: #666;
}
&.active {
background-color: #555;
}
}
.active-scripts-script-panel {
background-color: #555;
display: none;
font-size: 14px;
margin-bottom: 6px;
padding: 0 18px;
width: auto;
pre,
h2,
ul,
li {
background-color: #555;
width: auto;
color: #fff;
margin-left: 5%;
}
}

@ -32,7 +32,7 @@
.loaderoverlay {
$spinnerBoxSize: 200px;
$themeColor: #6f3;
$themeColor: #0c0;
position: absolute;
width: 100%;

@ -4,7 +4,7 @@
@import "reset";
:root {
--my-font-color: #6f3;
--my-font-color: #0c0;
--my-background-color: #000;
--my-highlight-color: #fff;
--my-prompt-color: #f92672;

@ -340,15 +340,13 @@ export class Blackjack extends Game<Props, State> {
{/* Buttons */}
{!gameInProgress ? (
<div>
<MuiButton color="primary" onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
<MuiButton onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
Start
</MuiButton>
</div>
) : (
<div>
<MuiButton color="primary" onClick={this.playerHit}>
Hit
</MuiButton>
<MuiButton onClick={this.playerHit}>Hit</MuiButton>
<MuiButton color="secondary" onClick={this.playerStay}>
Stay
</MuiButton>

@ -4,7 +4,6 @@ import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import React from "react";
import { TTheme as Theme } from "./ui/React/Theme";
import { General } from "./DevMenu/ui/General";
import { Stats } from "./DevMenu/ui/Stats";
@ -30,32 +29,30 @@ interface IProps {
export function DevMenuRoot(props: IProps): React.ReactElement {
return (
<Theme>
<>
<h1>Development Menu - Only meant to be used for testing/debugging</h1>
<General player={props.player} router={props.router} />
<Stats player={props.player} />
<Factions player={props.player} />
<Augmentations player={props.player} />
<SourceFiles player={props.player} />
<Programs player={props.player} />
<Servers />
<Companies />
<>
<h1>Development Menu - Only meant to be used for testing/debugging</h1>
<General player={props.player} router={props.router} />
<Stats player={props.player} />
<Factions player={props.player} />
<Augmentations player={props.player} />
<SourceFiles player={props.player} />
<Programs player={props.player} />
<Servers />
<Companies />
{props.player.bladeburner instanceof Bladeburner && <BladeburnerElem player={props.player} />}
{props.player.bladeburner instanceof Bladeburner && <BladeburnerElem player={props.player} />}
{props.player.inGang() && <Gang player={props.player} />}
{props.player.inGang() && <Gang player={props.player} />}
{props.player.hasCorporation() && <Corporation player={props.player} />}
{props.player.hasCorporation() && <Corporation player={props.player} />}
<CodingContracts />
<CodingContracts />
{props.player.hasWseAccount && <StockMarket />}
{props.player.hasWseAccount && <StockMarket />}
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />}
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />}
<TimeSkip player={props.player} engine={props.engine} />
</>
</Theme>
<TimeSkip player={props.player} engine={props.engine} />
</>
);
}

@ -37,12 +37,12 @@ export function Adjuster(props: IProps): React.ReactElement {
startAdornment: (
<>
<Tooltip title="Add a lot">
<IconButton color="primary" onClick={tons} size="large">
<IconButton onClick={tons} size="large">
<DoubleArrowIcon style={{ transform: "rotate(-90deg)" }} />
</IconButton>
</Tooltip>
<Tooltip title="Add">
<IconButton color="primary" onClick={() => add(typeof value !== "string" ? value : 0)} size="large">
<IconButton onClick={() => add(typeof value !== "string" ? value : 0)} size="large">
<AddIcon />
</IconButton>
</Tooltip>
@ -51,16 +51,12 @@ export function Adjuster(props: IProps): React.ReactElement {
endAdornment: (
<>
<Tooltip title="Remove">
<IconButton
color="primary"
onClick={() => subtract(typeof value !== "string" ? value : 0)}
size="large"
>
<IconButton onClick={() => subtract(typeof value !== "string" ? value : 0)} size="large">
<RemoveIcon />
</IconButton>
</Tooltip>
<Tooltip title="Reset">
<IconButton color="primary" onClick={reset} size="large">
<IconButton onClick={reset} size="large">
<ClearIcon />
</IconButton>
</Tooltip>

@ -40,7 +40,7 @@ export function Augmentations(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Augmentations</h2>
</AccordionSummary>
@ -53,23 +53,21 @@ export function Augmentations(props: IProps): React.ReactElement {
</td>
<td>
<Select
id="dev-augs-dropdown"
className="dropdown"
onChange={setAugmentationDropdown}
value={augmentation}
startAdornment={
<>
<IconButton color="primary" onClick={queueAllAugs} size="large">
<IconButton onClick={queueAllAugs} size="large">
<ReplyAllIcon />
</IconButton>
<IconButton color="primary" onClick={queueAug} size="large">
<IconButton onClick={queueAug} size="large">
<ReplyIcon />
</IconButton>
</>
}
endAdornment={
<>
<IconButton color="primary" onClick={clearAugs} size="large">
<IconButton onClick={clearAugs} size="large">
<ClearIcon />
</IconButton>
</>

@ -55,7 +55,7 @@ export function Bladeburner(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Bladeburner</h2>
</AccordionSummary>

@ -25,7 +25,7 @@ export function CodingContracts(): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Coding Contracts</h2>
</AccordionSummary>

@ -69,7 +69,7 @@ export function Companies(): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Companies</h2>
</AccordionSummary>

@ -67,7 +67,7 @@ export function Corporation(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Corporation</h2>
</AccordionSummary>

@ -97,7 +97,7 @@ export function Factions(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Factions</h2>
</AccordionSummary>
@ -119,10 +119,10 @@ export function Factions(props: IProps): React.ReactElement {
value={faction}
startAdornment={
<>
<IconButton color="primary" onClick={receiveAllInvites} size="large">
<IconButton onClick={receiveAllInvites} size="large">
<ReplyAllIcon />
</IconButton>
<IconButton color="primary" onClick={receiveInvite} size="large">
<IconButton onClick={receiveInvite} size="large">
<ReplyIcon />
</IconButton>
</>

@ -36,7 +36,7 @@ export function Gang(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Gang</h2>
</AccordionSummary>

@ -43,7 +43,7 @@ export function General(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>General</h2>
</AccordionSummary>

@ -35,7 +35,7 @@ export function Programs(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Programs</h2>
</AccordionSummary>

@ -75,7 +75,7 @@ export function Servers(): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Servers</h2>
</AccordionSummary>

@ -38,7 +38,7 @@ export function Sleeves(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Sleeves</h2>
</AccordionSummary>

@ -51,7 +51,7 @@ export function SourceFiles(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Source-Files</h2>
</AccordionSummary>

@ -136,7 +136,7 @@ export function Stats(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Experience / Stats</h2>
</AccordionSummary>

@ -80,7 +80,7 @@ export function StockMarket(): React.ReactElement {
);
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Stock Market</h2>
</AccordionSummary>

@ -28,7 +28,7 @@ export function TimeSkip(props: IProps): React.ReactElement {
}
return (
<Accordion>
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Time skip</h2>
</AccordionSummary>

@ -207,10 +207,10 @@ function setTheme() {
/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_BACKGROUND_COLOR) &&
/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_PROMPT_COLOR)
) {
document.body.style.setProperty("--my-highlight-color", FconfSettings.THEME_HIGHLIGHT_COLOR);
document.body.style.setProperty("--my-font-color", FconfSettings.THEME_FONT_COLOR);
document.body.style.setProperty("--my-background-color", FconfSettings.THEME_BACKGROUND_COLOR);
document.body.style.setProperty("--my-prompt-color", FconfSettings.THEME_PROMPT_COLOR);
// document.body.style.setProperty("--my-highlight-color", FconfSettings.THEME_HIGHLIGHT_COLOR);
// document.body.style.setProperty("--my-font-color", FconfSettings.THEME_FONT_COLOR);
// document.body.style.setProperty("--my-background-color", FconfSettings.THEME_BACKGROUND_COLOR);
// document.body.style.setProperty("--my-prompt-color", FconfSettings.THEME_PROMPT_COLOR);
}
}

@ -55,7 +55,9 @@ export function GangStats(props: IProps): React.ReactElement {
</p>
<br />
<div>
<p style={{ display: "inline-block" }}>Money gain rate: {MoneyRate(5 * props.gang.moneyGainRate)}</p>
<p style={{ display: "inline-block" }}>
Money gain rate: <MoneyRate money={5 * props.gang.moneyGainRate} />
</p>
</div>
<br />
<p className="tooltip" style={{ display: "inline-block" }}>

@ -28,7 +28,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
const tasks = props.gang.getAllTaskNames();
const data = [
[`Money:`, MoneyRate(5 * props.member.calculateMoneyGain(props.gang))],
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(props.gang)} />],
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(props.gang))} / sec`],
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(props.gang))} / sec`],
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],

@ -144,7 +144,8 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
<div className={"row"}>
<p>Production:</p>
<span className={"text money-gold"}>
<Money money={node.totalMoneyGenerated} player={props.player} /> ({MoneyRate(node.moneyGainRatePerSecond)})
<Money money={node.totalMoneyGenerated} player={props.player} /> (
<MoneyRate money={node.moneyGainRatePerSecond} />)
</span>
</div>
<div className={"row"}>

@ -25,7 +25,7 @@ export function PlayerInfo(props: IProps): React.ReactElement {
if (hasServers) {
prod = HashRate(props.totalProduction);
} else {
prod = MoneyRate(props.totalProduction);
prod = <MoneyRate money={props.totalProduction} />;
}
return (

@ -586,7 +586,7 @@ export function createAndAddWorkerScript(runningScriptObj, server, parent) {
* Updates the online running time stat of all running scripts
*/
export function updateOnlineScriptTimes(numCycles = 1) {
var time = (numCycles * Engine._idleSpeed) / 1000; //seconds
var time = (numCycles * CONSTANTS._idleSpeed) / 1000; //seconds
for (const ws of workerScripts.values()) {
ws.scriptRef.onlineRunningTime += time;
}

@ -175,7 +175,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
// });
} else {
data = [
[`Money:`, MoneyRate(5 * props.sleeve.gainRatesForTask.money)],
[`Money:`, <MoneyRate money={5 * props.sleeve.gainRatesForTask.money} />],
[`Hacking Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.hack)} / s`],
[`Strength Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.str)} / s`],
[`Defense Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.def)} / s`],

@ -333,18 +333,18 @@ export function SidebarRoot(props: IProps): React.ReactElement {
<Drawer open={open} anchor="left" variant="permanent">
<ListItem classes={{ root: classes.listitem }} button onClick={toggleDrawer}>
<ListItemIcon>
{!open ? <ChevronRightIcon color={"primary"} /> : <ChevronLeftIcon color={"primary"} />}
{!open ? <ChevronRightIcon color="primary" /> : <ChevronLeftIcon color="primary" />}
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Bitburner v{CONSTANTS.Version}</Typography>} />
<ListItemText primary={<Typography>Bitburner v{CONSTANTS.Version}</Typography>} />
</ListItem>
<Divider />
<List>
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHackingOpen((old) => !old)}>
<ListItemIcon>
<ComputerIcon color={"primary"} />
<ComputerIcon color="primary" />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Hacking</Typography>} />
{hackingOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
<ListItemText primary={<Typography>Hacking</Typography>} />
{hackingOpen ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</ListItem>
<Collapse in={hackingOpen} timeout="auto" unmountOnExit>
<List>
@ -435,10 +435,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
<Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setCharacterOpen((old) => !old)}>
<ListItemIcon>
<AccountBoxIcon color={"primary"} />
<AccountBoxIcon color="primary" />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Character</Typography>} />
{characterOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
<ListItemText primary={<Typography>Character</Typography>} />
{characterOpen ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</ListItem>
<Collapse in={characterOpen} timeout="auto" unmountOnExit>
<ListItem
@ -545,10 +545,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
<Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setWorldOpen((old) => !old)}>
<ListItemIcon>
<PublicIcon color={"primary"} />
<PublicIcon color="primary" />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">World</Typography>} />
{worldOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
<ListItemText primary={<Typography>World</Typography>} />
{worldOpen ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</ListItem>
<Collapse in={worldOpen} timeout="auto" unmountOnExit>
<ListItem
@ -678,10 +678,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
<Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHelpOpen((old) => !old)}>
<ListItemIcon>
<LiveHelpIcon color={"primary"} />
<LiveHelpIcon color="primary" />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Help</Typography>} />
{helpOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
<ListItemText primary={<Typography>Help</Typography>} />
{helpOpen ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</ListItem>
<Collapse in={helpOpen} timeout="auto" unmountOnExit>
<ListItem

@ -8,7 +8,6 @@ import "../css/mainmenu.scss";
import "../css/characteroverview.scss";
import "../css/terminal.scss";
import "../css/scripteditor.scss";
import "../css/activescripts.scss";
import "../css/hacknetnodes.scss";
import "../css/menupages.scss";
import "../css/augmentations.scss";

@ -8,10 +8,11 @@ import { ScriptProduction } from "./ScriptProduction";
import { ServerAccordions } from "./ServerAccordions";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
type IProps = {
p: IPlayer;
workerScripts: Map<number, WorkerScript>;
};
@ -27,15 +28,15 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
}, []);
return (
<div className="active-scripts-container">
<p>
<>
<Typography>
This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the servers
on which they are running.
</p>
</Typography>
<ScriptProduction {...props} />
<ServerAccordions {...props} />
</div>
</>
);
}

@ -5,16 +5,44 @@
import * as React from "react";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../React/Money";
import { MoneyRate } from "../React/MoneyRate";
import { use } from "../Context";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
type IProps = {
p: IPlayer;
workerScripts: Map<number, WorkerScript>;
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
cell: {
borderBottom: "none",
padding: theme.spacing(1),
margin: theme.spacing(1),
whiteSpace: "nowrap",
},
size: {
width: "1px",
},
}),
);
export function ScriptProduction(props: IProps): React.ReactElement {
const prodRateSinceLastAug = props.p.scriptProdSinceLastAug / (props.p.playtimeSinceLastAug / 1000);
const player = use.Player();
const classes = useStyles();
const prodRateSinceLastAug = player.scriptProdSinceLastAug / (player.playtimeSinceLastAug / 1000);
let onlineProduction = 0;
for (const ws of props.workerScripts.values()) {
@ -22,27 +50,29 @@ export function ScriptProduction(props: IProps): React.ReactElement {
}
return (
<p id="active-scripts-total-prod">
Total online production of Active scripts:&nbsp;
<span className="money-gold">
<span id="active-scripts-total-production-active">
<Money money={onlineProduction} />
</span>{" "}
/ sec
</span>
<br />
Total online production since last Aug installation:&nbsp;
<span id="active-scripts-total-prod-aug-total" className="money-gold">
<Money money={props.p.scriptProdSinceLastAug} />
</span>
&nbsp;(
<span className="money-gold">
<span id="active-scripts-total-prod-aug-avg" className="money-gold">
<Money money={prodRateSinceLastAug} />
</span>{" "}
/ sec
</span>
)
</p>
<Table size="small" classes={{ root: classes.size }}>
<TableBody>
<TableRow>
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
<Typography variant="body2">Total online production of Active scripts:</Typography>
</TableCell>
<TableCell align="left" classes={{ root: classes.cell }}>
<Typography variant="body2">
<Money money={player.scriptProdSinceLastAug} />
</Typography>
</TableCell>
</TableRow>
<TableRow style={{ width: "1px" }}>
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
<Typography variant="body2">Total online production since last Aug installation:</Typography>
</TableCell>
<TableCell align="left" classes={{ root: classes.cell }}>
<Typography variant="body2">
(<MoneyRate money={prodRateSinceLastAug} />)
</Typography>
</TableCell>
</TableRow>
</TableBody>
</Table>
);
}

@ -4,7 +4,15 @@
*/
import * as React from "react";
import { BBAccordion } from "../React/BBAccordion";
import Typography from "@mui/material/Typography";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
import { ServerAccordionContent } from "./ServerAccordionContent";
import { BaseServer } from "../../Server/BaseServer";
@ -18,6 +26,7 @@ type IProps = {
};
export function ServerAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = React.useState(false);
const server = props.server;
// Accordion's header text
@ -34,9 +43,14 @@ export function ServerAccordion(props: IProps): React.ReactElement {
const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`;
return (
<BBAccordion
headerContent={<pre>{headerTxt}</pre>}
panelContent={<ServerAccordionContent workerScripts={props.workerScripts} />}
/>
<>
<ListItemButton onClick={() => setOpen((old) => !old)} component={Paper}>
<ListItemText primary={<Typography style={{ whiteSpace: "pre-wrap" }}>{headerTxt}</Typography>} />
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} timeout={0} unmountOnExit>
<ServerAccordionContent workerScripts={props.workerScripts} />
</Collapse>
</>
);
}

@ -2,6 +2,10 @@ import React, { useState } from "react";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
import { AccordionButton } from "../React/AccordionButton";
import Paper from "@mui/material/Paper";
import List from "@mui/material/List";
import TablePagination from "@mui/material/TablePagination";
import { TablePaginationActionsAll } from "../React/TablePaginationActionsAll";
const pageSize = 20;
@ -10,61 +14,34 @@ interface IProps {
}
export function ServerAccordionContent(props: IProps): React.ReactElement {
if (props.workerScripts.length > pageSize) {
return <ServerAccordionContentPaginated workerScripts={props.workerScripts} />;
}
const scripts = props.workerScripts.map((ws) => {
return <WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />;
});
return <ul>{scripts}</ul>;
}
export function ServerAccordionContentPaginated(props: IProps): React.ReactElement {
const [page, setPage] = useState(0);
const scripts: React.ReactElement[] = [];
const maxPage = Math.ceil(props.workerScripts.length / pageSize);
const maxScript = Math.min((page + 1) * pageSize, props.workerScripts.length);
for (let i = page * pageSize; i < maxScript; i++) {
const ws = props.workerScripts[i];
scripts.push(<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />);
}
const [rowsPerPage, setRowsPerPage] = useState(10);
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
function capPage(page: number): number {
if (page < 0) {
page = 0;
}
if (maxPage - 1 < page) {
page = maxPage - 1;
}
return page;
}
// in case we're on an invalid page number because scripts were killed.
const capped = capPage(page);
if (capped !== page) setPage(capped);
function changePage(n: number): void {
setPage((newPage) => {
newPage += n;
newPage = Math.round(newPage);
return capPage(newPage);
});
}
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
return (
<>
<ul>{scripts}</ul>
<AccordionButton onClick={() => changePage(-1e99)} text="<<" />
<AccordionButton onClick={() => changePage(-1)} text="<" />
<span className="text">
{page + 1} / {maxPage}
</span>
<AccordionButton onClick={() => changePage(1)} text=">" />
<AccordionButton onClick={() => changePage(1e99)} text=">>" />
<List>
{props.workerScripts.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((ws) => (
<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />
))}
</List>
<TablePagination
rowsPerPageOptions={[10, 15, 20]}
component="div"
count={props.workerScripts.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActionsAll}
/>
</>
);
}

@ -2,14 +2,20 @@
* React Component for rendering the Accordion elements for all servers
* on which scripts are running
*/
import * as React from "react";
import React, { useState, useEffect } from "react";
import { ServerAccordion } from "./ServerAccordion";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import List from "@mui/material/List";
import TablePagination from "@mui/material/TablePagination";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
import { getServer } from "../../Server/ServerHelpers";
import { BaseServer } from "../../Server/BaseServer";
import { TablePaginationActionsAll } from "../React/TablePaginationActionsAll";
// Map of server hostname -> all workerscripts on that server for all active scripts
interface IServerData {
@ -18,7 +24,7 @@ interface IServerData {
}
interface IServerToScriptsMap {
[key: string]: IServerData;
[key: string]: IServerData | undefined;
}
type IProps = {
@ -31,72 +37,91 @@ type IState = {
const subscriberId = "ActiveScriptsUI";
export class ServerAccordions extends React.Component<IProps, IState> {
serverToScriptMap: IServerToScriptsMap = {};
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
};
this.updateServerToScriptsMap();
this.rerender = this.rerender.bind(this);
export function ServerAccordions(props: IProps): React.ReactElement {
const [filter, setFilter] = useState("");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
componentDidMount(): void {
useEffect(() => {
WorkerScriptStartStopEventEmitter.addSubscriber({
cb: this.rerender,
cb: rerender,
id: subscriberId,
});
return () => WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
}, []);
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
function handleFilterChange(event: React.ChangeEvent<HTMLInputElement>): void {
setFilter(event.target.value);
setPage(0);
}
componentWillUnmount(): void {
WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
}
updateServerToScriptsMap(): void {
const map: IServerToScriptsMap = {};
for (const ws of this.props.workerScripts.values()) {
const server = getServer(ws.serverIp);
if (server == null) {
console.warn(`WorkerScript has invalid IP address: ${ws.serverIp}`);
continue;
}
if (map[server.hostname] == null) {
map[server.hostname] = {
server: server,
workerScripts: [],
};
}
map[server.hostname].workerScripts.push(ws);
const serverToScriptMap: IServerToScriptsMap = {};
for (const ws of props.workerScripts.values()) {
const server = getServer(ws.serverIp);
if (server == null) {
console.warn(`WorkerScript has invalid IP address: ${ws.serverIp}`);
continue;
}
this.serverToScriptMap = map;
let data = serverToScriptMap[server.hostname];
if (data === undefined) {
serverToScriptMap[server.hostname] = {
server: server,
workerScripts: [],
};
data = serverToScriptMap[server.hostname];
}
if (data !== undefined) data.workerScripts.push(ws);
}
rerender(): void {
this.updateServerToScriptsMap();
this.setState((prevState) => {
return { rerenderFlag: !prevState.rerenderFlag };
});
}
const filtered = Object.values(serverToScriptMap).filter((data) => data && data.server.hostname.includes(filter));
render(): React.ReactNode {
const elems = Object.keys(this.serverToScriptMap).map((serverName) => {
const data = this.serverToScriptMap[serverName];
return <ServerAccordion key={serverName} server={data.server} workerScripts={data.workerScripts} />;
});
return (
<ul className="active-scripts-list" id="active-scripts-list">
{elems}
</ul>
);
}
return (
<>
<TextField
value={filter}
onChange={handleFilterChange}
color="primary"
autoFocus
variant="standard"
InputProps={{
startAdornment: <Typography m={1}>Filter:</Typography>,
spellCheck: false,
}}
/>
<List>
{filtered.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((data) => {
return (
data && (
<ServerAccordion key={data.server.hostname} server={data.server} workerScripts={data.workerScripts} />
)
);
})}
</List>
<TablePagination
rowsPerPageOptions={[10, 15, 20]}
component="div"
count={filtered.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActionsAll}
/>
</>
);
}

@ -6,8 +6,23 @@ import * as React from "react";
import { numeralWrapper } from "../numeralFormat";
import { BBAccordion } from "../React/BBAccordion";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { AccordionButton } from "../React/AccordionButton";
import IconButton from "@mui/material/IconButton";
import DeleteIcon from "@mui/icons-material/Delete";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { WorkerScript } from "../../Netscript/WorkerScript";
@ -23,6 +38,7 @@ type IProps = {
};
export function WorkerScriptAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = React.useState(false);
const workerScript = props.workerScript;
const scriptRef = workerScript.scriptRef;
@ -41,12 +57,13 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
const offlineEps = scriptRef.offlineExpGained / scriptRef.offlineRunningTime;
return (
<BBAccordion
headerClass="active-scripts-script-header"
headerContent={<>{props.workerScript.name}</>}
panelClass="active-scripts-script-panel"
panelContent={
<>
<>
<ListItemButton onClick={() => setOpen((old) => !old)} component={Paper}>
<ListItemText primary={<Typography style={{ whiteSpace: "pre-wrap" }}>{props.workerScript.name}</Typography>} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout={0} unmountOnExit>
<Box m={3}>
<pre>Threads: {numeralWrapper.formatThreads(props.workerScript.scriptRef.threads)}</pre>
<pre>Args: {arrayToString(props.workerScript.args)}</pre>
<pre>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</pre>
@ -68,10 +85,14 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
</pre>
<pre>{Array(26).join(" ") + numeralWrapper.formatExp(offlineEps) + " hacking exp / second"}</pre>
<AccordionButton onClick={logClickHandler} text="Log" />
<AccordionButton onClick={killScriptClickHandler} text="Kill Script" />
</>
}
/>
<Button onClick={logClickHandler}>
<Typography>Log</Typography>
</Button>
<IconButton onClick={killScriptClickHandler}>
<DeleteIcon color="error" />
</IconButton>
</Box>
</Collapse>
</>
);
}

@ -278,7 +278,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
) : page === Page.CreateScript ? (
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
) : page === Page.ActiveScripts ? (
<ActiveScriptsRoot p={player} workerScripts={workerScripts} />
<ActiveScriptsRoot workerScripts={workerScripts} />
) : page === Page.Hacknet ? (
<HacknetRoot player={player} />
) : page === Page.CreateProgram ? (

@ -257,7 +257,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
</ListItem>
<ListItem>
<FormControlLabel
control={<Switch color="primary" checked={suppressMessages} onChange={handleSuppressMessagesChange} />}
control={<Switch checked={suppressMessages} onChange={handleSuppressMessagesChange} />}
label={
<Tooltip
title={
@ -275,13 +275,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
</ListItem>
<ListItem>
<FormControlLabel
control={
<Switch
color="primary"
checked={suppressFactionInvites}
onChange={handleSuppressFactionInvitesChange}
/>
}
control={<Switch checked={suppressFactionInvites} onChange={handleSuppressFactionInvitesChange} />}
label={
<Tooltip
title={
@ -299,11 +293,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
<ListItem>
<FormControlLabel
control={
<Switch
color="primary"
checked={suppressTravelConfirmations}
onChange={handleSuppressTravelConfirmationsChange}
/>
<Switch checked={suppressTravelConfirmations} onChange={handleSuppressTravelConfirmationsChange} />
}
label={
<Tooltip
@ -323,7 +313,6 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
<FormControlLabel
control={
<Switch
color="primary"
checked={suppressBuyAugmentationConfirmation}
onChange={handleSuppressBuyAugmentationConfirmationChange}
/>
@ -344,11 +333,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
<ListItem>
<FormControlLabel
control={
<Switch
color="primary"
checked={suppressHospitalizationPopup}
onChange={handleSuppressHospitalizationPopupChange}
/>
<Switch checked={suppressHospitalizationPopup} onChange={handleSuppressHospitalizationPopupChange} />
}
label={
<Tooltip
@ -368,11 +353,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
<ListItem>
<FormControlLabel
control={
<Switch
color="primary"
checked={suppressBladeburnerPopup}
onChange={handleSuppressBladeburnerPopupChange}
/>
<Switch checked={suppressBladeburnerPopup} onChange={handleSuppressBladeburnerPopupChange} />
}
label={
<Tooltip
@ -391,7 +372,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
)}
<ListItem>
<FormControlLabel
control={<Switch color="primary" checked={disableHotkeys} onChange={handleDisableHotkeysChange} />}
control={<Switch checked={disableHotkeys} onChange={handleDisableHotkeysChange} />}
label={
<Tooltip
title={
@ -409,7 +390,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
</ListItem>
<ListItem>
<FormControlLabel
control={<Switch color="primary" checked={disableASCIIArt} onChange={handleDisableASCIIArtChange} />}
control={<Switch checked={disableASCIIArt} onChange={handleDisableASCIIArtChange} />}
label={
<Tooltip title={<Typography>If this is set all ASCII art will be disabled.</Typography>}>
<Typography>Disable ascii art</Typography>
@ -419,9 +400,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
</ListItem>
<ListItem>
<FormControlLabel
control={
<Switch color="primary" checked={disableTextEffects} onChange={handleDisableTextEffectsChange} />
}
control={<Switch checked={disableTextEffects} onChange={handleDisableTextEffectsChange} />}
label={
<Tooltip
title={

@ -2,6 +2,6 @@ import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
export function MoneyRate(money: number): JSX.Element {
export function MoneyRate({ money }: { money: number }): JSX.Element {
return <Money money={`${numeralWrapper.formatMoney(money)} / sec`} />;
}

@ -0,0 +1,69 @@
import * as React from "react";
import { useTheme } from "@mui/material/styles";
import Box from "@mui/material/Box";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableFooter from "@mui/material/TableFooter";
import TablePagination from "@mui/material/TablePagination";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import IconButton from "@mui/material/IconButton";
import FirstPageIcon from "@mui/icons-material/FirstPage";
import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft";
import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight";
import LastPageIcon from "@mui/icons-material/LastPage";
interface TablePaginationActionsProps {
count: number;
page: number;
rowsPerPage: number;
onPageChange: (event: React.MouseEvent<HTMLButtonElement>, newPage: number) => void;
}
export function TablePaginationActionsAll(props: TablePaginationActionsProps) {
const theme = useTheme();
const { count, page, rowsPerPage, onPageChange } = props;
const handleFirstPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, 0);
};
const handleBackButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, page - 1);
};
const handleNextButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, page + 1);
};
const handleLastPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
};
return (
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
<IconButton onClick={handleFirstPageButtonClick} disabled={page === 0} aria-label="first page">
{theme.direction === "rtl" ? <LastPageIcon /> : <FirstPageIcon />}
</IconButton>
<IconButton onClick={handleBackButtonClick} disabled={page === 0} aria-label="previous page">
{theme.direction === "rtl" ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
</IconButton>
<IconButton
onClick={handleNextButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="next page"
>
{theme.direction === "rtl" ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
</IconButton>
<IconButton
onClick={handleLastPageButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="last page"
>
{theme.direction === "rtl" ? <FirstPageIcon /> : <LastPageIcon />}
</IconButton>
</Box>
);
}

@ -19,6 +19,14 @@ export const colors = {
secondary: "#888",
secondarydark: "#666",
warninglight: "#ff0",
warning: "#cc0",
warningdark: "#990",
infolight: "#69f",
info: "#36c",
infodark: "#039",
welllight: "#444",
well: "#222",
white: "#fff",
@ -49,6 +57,16 @@ export const theme = createTheme({
main: colors.error,
dark: colors.errordark,
},
info: {
light: colors.infolight,
main: colors.info,
dark: colors.infodark,
},
warning: {
light: colors.warninglight,
main: colors.warning,
dark: colors.warningdark,
},
background: {
paper: colors.well,
},
@ -64,9 +82,9 @@ export const theme = createTheme({
styleOverrides: {
root: {
backgroundColor: colors.well,
color: colors.primary,
},
input: {
color: colors.primary,
"&::placeholder": {
userSelect: "none",
color: colors.primarydark,
@ -225,11 +243,19 @@ export const theme = createTheme({
MuiPaper: {
styleOverrides: {
root: {
borderRadius: 0,
backgroundColor: colors.black,
border: "1px solid " + colors.welllight,
},
},
},
MuiTablePagination: {
styleOverrides: {
select: {
color: colors.primary,
},
},
},
},
});

@ -47,7 +47,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> ({MoneyRate(player.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
{Reputation(player.workRepGained)} ({ReputationRate(player.workRepGainRate * CYCLES_PER_SEC)}) reputation for
this faction <br />
@ -112,7 +113,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
<br />
This has cost you: <br />
<Money money={-player.workMoneyGained} /> ({MoneyRate(player.workMoneyLossRate * CYCLES_PER_SEC)}) <br />
<Money money={-player.workMoneyGained} /> (<MoneyRate money={player.workMoneyLossRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
You have gained: <br />
{numeralWrapper.formatExp(player.workHackExpGained)} (
@ -173,7 +175,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> ({MoneyRate(player.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
{Reputation(player.workRepGained)} ({ReputationRate(player.workRepGainRate * CYCLES_PER_SEC)}) reputation for
this company <br />
@ -241,7 +244,8 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
You have earned: <br />
<br />
<Money money={player.workMoneyGained} /> ({MoneyRate(player.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<Money money={player.workMoneyGained} /> (<MoneyRate money={player.workMoneyGainRate * CYCLES_PER_SEC} />){" "}
<br />
<br />
{Reputation(player.workRepGained)} (
{Reputation(`${numeralWrapper.formatExp(player.workRepGainRate * CYCLES_PER_SEC)} / sec`)}