mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-24 02:03:01 +02:00
Merge branch 'dev' into bugfix/3348
# Conflicts: # src/Faction/FactionInfo.tsx # src/NetscriptFunctions/Singularity.ts
This commit is contained in:
@@ -31,7 +31,7 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Tabs variant="fullWidth" value={tab} onChange={handleChange} sx={{ minWidth: 'fit-content', maxWidth: '25%' }}>
|
||||
<Tabs variant="fullWidth" value={tab} onChange={handleChange} sx={{ minWidth: "fit-content", maxWidth: "25%" }}>
|
||||
<Tab label={"Active"} value={"active"} />
|
||||
<Tab label={"Recently Killed"} value={"recent"} />
|
||||
</Tabs>
|
||||
|
||||
@@ -67,7 +67,13 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)} component={Paper}>
|
||||
<ListItemText primary={<Typography>└ {props.workerScript.name} {JSON.stringify(props.workerScript.args)}</Typography>} />
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography>
|
||||
└ {props.workerScript.name} {JSON.stringify(props.workerScript.args)}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
<Collapse in={open} timeout={0} unmountOnExit>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { EventEmitter } from "../utils/EventEmitter";
|
||||
import { Modal } from "./React/Modal";
|
||||
|
||||
const frames = [
|
||||
@@ -37,6 +38,8 @@ function isApr1(): boolean {
|
||||
return d.getMonth() === 3 && d.getDate() === 1;
|
||||
}
|
||||
|
||||
export const Apr1Events = new EventEmitter();
|
||||
|
||||
export function Apr1(): React.ReactElement {
|
||||
const [open, setOpen] = useState(isApr1());
|
||||
const [n, setN] = useState(0);
|
||||
@@ -45,6 +48,15 @@ export function Apr1(): React.ReactElement {
|
||||
const id = setInterval(() => setN((n) => (n + 1) % frames.length), 100);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
Apr1Events.subscribe(() => {
|
||||
setOpen(true);
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
if (!open) return <></>;
|
||||
|
||||
return (
|
||||
|
||||
@@ -17,7 +17,7 @@ interface IState {
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends React.Component<IProps, IState> {
|
||||
state: IState
|
||||
state: IState;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
@@ -25,7 +25,7 @@ export class ErrorBoundary extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.setState( { hasError: false } as IState);
|
||||
this.setState({ hasError: false } as IState);
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
||||
@@ -47,12 +47,18 @@ export class ErrorBoundary extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
return <RecoveryRoot router={this.props.router} softReset={this.props.softReset}
|
||||
errorData={errorData} resetError={() => this.reset()} />;
|
||||
return (
|
||||
<RecoveryRoot
|
||||
router={this.props.router}
|
||||
softReset={this.props.softReset}
|
||||
errorData={errorData}
|
||||
resetError={() => this.reset()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
static getDerivedStateFromError(error: Error): IState {
|
||||
return { hasError: true, error};
|
||||
return { hasError: true, error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export function AlertManager(): React.ReactElement {
|
||||
{alerts.length > 0 && (
|
||||
<Modal open={true} onClose={close}>
|
||||
<Box overflow="scroll" sx={{ overflowWrap: "break-word", whiteSpace: "pre-line" }}>
|
||||
<Typography component={'span'}>{alerts[0].text}</Typography>
|
||||
<Typography component={"span"}>{alerts[0].text}</Typography>
|
||||
</Box>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
@@ -33,9 +33,11 @@ export function CorruptableText(props: IProps): JSX.Element {
|
||||
const index = Math.random() * content.length;
|
||||
const letter = content.charAt(index);
|
||||
setContent((content) => replace(content, index, randomize(letter)));
|
||||
timers.push(window.setTimeout(() => {
|
||||
setContent((content) => replace(content, index, letter));
|
||||
}, 500));
|
||||
timers.push(
|
||||
window.setTimeout(() => {
|
||||
setContent((content) => replace(content, index, letter));
|
||||
}, 500),
|
||||
);
|
||||
}, 20);
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState } from "react";
|
||||
import { deleteGame } from "../../db";
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import { pushDisableRestore } from '../../Electron';
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import { pushDisableRestore } from "../../Electron";
|
||||
|
||||
interface IProps {
|
||||
color?: "primary" | "warning" | "error";
|
||||
@@ -14,23 +14,27 @@ interface IProps {
|
||||
export function DeleteGameButton({ color = "primary" }: IProps): React.ReactElement {
|
||||
const [modalOpened, setModalOpened] = useState(false);
|
||||
|
||||
return (<>
|
||||
<Tooltip title="This will permanently delete your local save game. Did you export it before?">
|
||||
<Button startIcon={<DeleteIcon />} color={color} onClick={() => setModalOpened(true)}>Delete Save</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
onConfirm={() => {
|
||||
setModalOpened(false);
|
||||
deleteGame()
|
||||
.then(() => {
|
||||
pushDisableRestore();
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
})
|
||||
.catch((r) => console.error(`Could not delete game: ${r}`));
|
||||
}}
|
||||
open={modalOpened}
|
||||
onClose={() => setModalOpened(false)}
|
||||
confirmationText={"Really delete your game? (It's permanent!)"}
|
||||
/>
|
||||
</>)
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="This will permanently delete your local save game. Did you export it before?">
|
||||
<Button startIcon={<DeleteIcon />} color={color} onClick={() => setModalOpened(true)}>
|
||||
Delete Save
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
onConfirm={() => {
|
||||
setModalOpened(false);
|
||||
deleteGame()
|
||||
.then(() => {
|
||||
pushDisableRestore();
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
})
|
||||
.catch((r) => console.error(`Could not delete game: ${r}`));
|
||||
}}
|
||||
open={modalOpened}
|
||||
onClose={() => setModalOpened(false)}
|
||||
confirmationText={"Really delete your game? (It's permanent!)"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import TextField from "@mui/material/TextField";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import UploadIcon from "@mui/icons-material/Upload";
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
import PaletteIcon from '@mui/icons-material/Palette';
|
||||
import PaletteIcon from "@mui/icons-material/Palette";
|
||||
|
||||
import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
@@ -157,7 +157,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
<Grid item xs={12} sm={6}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<Box display="grid" sx={{ width: 'fit-content', gridTemplateColumns: '1fr 3.5fr', gap: 1 }}>
|
||||
<Box display="grid" sx={{ width: "fit-content", gridTemplateColumns: "1fr 3.5fr", gap: 1 }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
|
||||
@@ -121,7 +121,7 @@ export function ImportSaveRoot(props: IProps): JSX.Element {
|
||||
Settings.AutosaveInterval = initialAutosave;
|
||||
pushImportResult(false);
|
||||
props.router.allowRouting(true);
|
||||
setHeadback(true)
|
||||
setHeadback(true);
|
||||
}
|
||||
|
||||
async function handleImport(): Promise<void> {
|
||||
@@ -211,12 +211,14 @@ export function ImportSaveRoot(props: IProps): JSX.Element {
|
||||
<TableRow>
|
||||
<TableCell>Saved On</TableCell>
|
||||
<TableCell>
|
||||
{(currentData.playerData?.lastSave ?? 0) > 0 ?
|
||||
new Date(currentData.playerData?.lastSave ?? 0).toLocaleString() : 'n/a'}
|
||||
{(currentData.playerData?.lastSave ?? 0) > 0
|
||||
? new Date(currentData.playerData?.lastSave ?? 0).toLocaleString()
|
||||
: "n/a"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{(importData.playerData?.lastSave ?? 0) > 0 ?
|
||||
new Date(importData.playerData?.lastSave ?? 0).toLocaleString() : 'n/a'}
|
||||
{(importData.playerData?.lastSave ?? 0) > 0
|
||||
? new Date(importData.playerData?.lastSave ?? 0).toLocaleString()
|
||||
: "n/a"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{importData.playerData?.lastSave !== currentData.playerData?.lastSave && (
|
||||
|
||||
@@ -18,7 +18,6 @@ import { Theme } from "@mui/material";
|
||||
import { findRunningScript } from "../../Script/ScriptHelpers";
|
||||
import { Player } from "../../Player";
|
||||
import { debounce } from "lodash";
|
||||
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
|
||||
|
||||
let layerCounter = 0;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IErrorData, newIssueUrl } from "../../utils/ErrorHelper";
|
||||
import { DeleteGameButton } from "./DeleteGameButton";
|
||||
import { SoftResetButton } from "./SoftResetButton";
|
||||
|
||||
import DirectionsRunIcon from '@mui/icons-material/DirectionsRun';
|
||||
import DirectionsRunIcon from "@mui/icons-material/DirectionsRun";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
|
||||
export let RecoveryMode = false;
|
||||
@@ -42,21 +42,27 @@ export function RecoveryRoot({ router, softReset, errorData, resetError }: IProp
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{ padding: "8px 16px", minHeight: "100vh", maxWidth: '1200px', boxSizing: "border-box",}}>
|
||||
<Box sx={{ padding: "8px 16px", minHeight: "100vh", maxWidth: "1200px", boxSizing: "border-box" }}>
|
||||
<Typography variant="h3">RECOVERY MODE ACTIVATED</Typography>
|
||||
<Typography>
|
||||
There was an error with your save file and the game went into recovery mode. In this mode saving is disabled
|
||||
and the game will automatically export your save file (to prevent corruption).
|
||||
There was an error with your save file and the game went into recovery mode. In this mode saving is disabled and
|
||||
the game will automatically export your save file (to prevent corruption).
|
||||
</Typography>
|
||||
<Typography>At this point it is recommended to alert a developer.</Typography>
|
||||
<Typography>
|
||||
<Link href={errorData?.issueUrl ?? newIssueUrl} target="_blank">File an issue on github</Link>
|
||||
<Link href={errorData?.issueUrl ?? newIssueUrl} target="_blank">
|
||||
File an issue on github
|
||||
</Link>
|
||||
</Typography>
|
||||
<Typography>
|
||||
<Link href="https://www.reddit.com/r/Bitburner/" target="_blank">Make a reddit post</Link>
|
||||
<Link href="https://www.reddit.com/r/Bitburner/" target="_blank">
|
||||
Make a reddit post
|
||||
</Link>
|
||||
</Typography>
|
||||
<Typography>
|
||||
<Link href="https://discord.gg/TFc3hKD" target="_blank">Post in the #bug-report channel on Discord.</Link>
|
||||
<Link href="https://discord.gg/TFc3hKD" target="_blank">
|
||||
Post in the #bug-report channel on Discord.
|
||||
</Link>
|
||||
</Typography>
|
||||
<Typography>Please include your save file.</Typography>
|
||||
<br />
|
||||
@@ -64,17 +70,17 @@ export function RecoveryRoot({ router, softReset, errorData, resetError }: IProp
|
||||
<Typography>You can disable recovery mode now. But chances are the game will not work correctly.</Typography>
|
||||
<ButtonGroup sx={{ my: 2 }}>
|
||||
<Tooltip title="Disables the recovery mode & attempt to head back to the terminal page. This may or may not work. Ensure you have saved the recovery file.">
|
||||
<Button onClick={recover} startIcon={<DirectionsRunIcon />}>Disable Recovery Mode</Button>
|
||||
<Button onClick={recover} startIcon={<DirectionsRunIcon />}>
|
||||
Disable Recovery Mode
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<SoftResetButton color="warning" onTriggered={softReset} />
|
||||
<DeleteGameButton color="error" />
|
||||
</ButtonGroup>
|
||||
|
||||
{errorData && (
|
||||
<Paper sx={{ px: 2, pt: 1, pb: 2, mt: 2}}>
|
||||
<Typography variant="h5">
|
||||
{errorData.title}
|
||||
</Typography>
|
||||
<Paper sx={{ px: 2, pt: 1, pb: 2, mt: 2 }}>
|
||||
<Typography variant="h5">{errorData.title}</Typography>
|
||||
<Box sx={{ my: 2 }}>
|
||||
<TextField
|
||||
label="Bug Report Text"
|
||||
@@ -84,11 +90,18 @@ export function RecoveryRoot({ router, softReset, errorData, resetError }: IProp
|
||||
multiline
|
||||
fullWidth
|
||||
rows={12}
|
||||
sx={{ "& .MuiOutlinedInput-root": { color: Settings.theme.secondary }}} />
|
||||
sx={{ "& .MuiOutlinedInput-root": { color: Settings.theme.secondary } }}
|
||||
/>
|
||||
</Box>
|
||||
<Tooltip title="Submitting an issue to GitHub really help us improve the game!">
|
||||
<Button component={Link} startIcon={<GitHubIcon />} color="info" sx={{ px: 2 }}
|
||||
href={errorData.issueUrl ?? newIssueUrl} target={"_blank"} >
|
||||
<Button
|
||||
component={Link}
|
||||
startIcon={<GitHubIcon />}
|
||||
color="info"
|
||||
sx={{ px: 2 }}
|
||||
href={errorData.issueUrl ?? newIssueUrl}
|
||||
target={"_blank"}
|
||||
>
|
||||
Submit Issue to GitHub
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -16,22 +16,29 @@ const useStyles = makeStyles(() => ({
|
||||
zIndex: `${logBoxBaseZIndex + 1000} !important` as any,
|
||||
|
||||
"& .MuiAlert-icon": {
|
||||
alignSelf: 'center',
|
||||
alignSelf: "center",
|
||||
},
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export function SnackbarProvider(props: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<SB dense maxSnack={9} anchorOrigin={{ horizontal: "right", vertical: "bottom" }} autoHideDuration={2000}
|
||||
classes={{ containerRoot: classes.snackbar }}>
|
||||
<SB
|
||||
dense
|
||||
maxSnack={9}
|
||||
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
|
||||
autoHideDuration={2000}
|
||||
classes={{ containerRoot: classes.snackbar }}
|
||||
>
|
||||
{props.children}
|
||||
</SB>
|
||||
);
|
||||
}
|
||||
|
||||
export const SnackbarEvents = new EventEmitter<[string | React.ReactNode, "success" | "warning" | "error" | "info", number]>();
|
||||
export const SnackbarEvents = new EventEmitter<
|
||||
[string | React.ReactNode, "success" | "warning" | "error" | "info", number]
|
||||
>();
|
||||
|
||||
export function Snackbar(): React.ReactElement {
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
||||
@@ -43,7 +50,7 @@ export function Snackbar(): React.ReactElement {
|
||||
variant: variant,
|
||||
autoHideDuration: duration,
|
||||
onClick: () => closeSnackbar(id),
|
||||
})
|
||||
});
|
||||
}),
|
||||
);
|
||||
return <></>;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Tooltip } from '@mui/material';
|
||||
import RestartAltIcon from '@mui/icons-material/RestartAlt';
|
||||
import { Tooltip } from "@mui/material";
|
||||
import RestartAltIcon from "@mui/icons-material/RestartAlt";
|
||||
|
||||
interface IProps {
|
||||
color?: "primary" | "warning" | "error";
|
||||
@@ -11,7 +11,11 @@ interface IProps {
|
||||
onTriggered: () => void;
|
||||
}
|
||||
|
||||
export function SoftResetButton({ color = "primary", noConfirmation = false, onTriggered }: IProps): React.ReactElement {
|
||||
export function SoftResetButton({
|
||||
color = "primary",
|
||||
noConfirmation = false,
|
||||
onTriggered,
|
||||
}: IProps): React.ReactElement {
|
||||
const [modalOpened, setModalOpened] = useState(false);
|
||||
|
||||
function handleButtonClick(): void {
|
||||
@@ -22,15 +26,19 @@ export function SoftResetButton({ color = "primary", noConfirmation = false, onT
|
||||
}
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Tooltip title="Perform a soft reset. Resets everything as if you had just purchased an Augmentation.">
|
||||
<Button startIcon={<RestartAltIcon />} color={color} onClick={handleButtonClick}>Soft Reset</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
onConfirm={onTriggered}
|
||||
open={modalOpened}
|
||||
onClose={() => setModalOpened(false)}
|
||||
confirmationText={"This will perform the same action as installing Augmentations, are you sure?"}
|
||||
/>
|
||||
</>)
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="Perform a soft reset. Resets everything as if you had just purchased an Augmentation.">
|
||||
<Button startIcon={<RestartAltIcon />} color={color} onClick={handleButtonClick}>
|
||||
Soft Reset
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
onConfirm={onTriggered}
|
||||
open={modalOpened}
|
||||
onClose={() => setModalOpened(false)}
|
||||
confirmationText={"This will perform the same action as installing Augmentations, are you sure?"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,16 @@ interface IStatsOverviewCellProps {
|
||||
color?: React.CSSProperties["color"];
|
||||
}
|
||||
|
||||
export function StatsProgressBar({ min, max, current, remaining, progress, color }: IProgressProps): React.ReactElement {
|
||||
export function StatsProgressBar({
|
||||
min,
|
||||
max,
|
||||
current,
|
||||
remaining,
|
||||
progress,
|
||||
color,
|
||||
}: IProgressProps): React.ReactElement {
|
||||
const tooltip = (
|
||||
<Typography sx={{ textAlign: 'right' }}>
|
||||
<Typography sx={{ textAlign: "right" }}>
|
||||
<strong>Progress:</strong>
|
||||
{numeralWrapper.formatExp(current)} / {numeralWrapper.formatExp(max - min)}
|
||||
<br />
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
Typography,
|
||||
TableCell,
|
||||
TableRow,
|
||||
} from "@mui/material";
|
||||
import { Typography, TableCell, TableRow } from "@mui/material";
|
||||
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
@@ -40,10 +36,8 @@ export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps):
|
||||
<Typography style={{ color: color }}>{name}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography style={{ color: color }}>
|
||||
{content}
|
||||
</Typography>
|
||||
<Typography style={{ color: color }}>{content}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,13 +14,13 @@ interface ICityProps {
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
travel: {
|
||||
color: theme.colors.white,
|
||||
lineHeight: "1em",
|
||||
whiteSpace: "pre",
|
||||
cursor: "pointer"
|
||||
},
|
||||
})
|
||||
travel: {
|
||||
color: theme.colors.white,
|
||||
lineHeight: "1em",
|
||||
whiteSpace: "pre",
|
||||
cursor: "pointer",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
function City(props: ICityProps): React.ReactElement {
|
||||
@@ -28,10 +28,7 @@ function City(props: ICityProps): React.ReactElement {
|
||||
if (props.city !== props.currentCity) {
|
||||
return (
|
||||
<Tooltip title={<Typography>{props.city}</Typography>}>
|
||||
<span
|
||||
onClick={() => props.onTravel(props.city)}
|
||||
className={classes.travel}
|
||||
>
|
||||
<span onClick={() => props.onTravel(props.city)} className={classes.travel}>
|
||||
{props.city[0]}
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
Reference in New Issue
Block a user