Added toast function

This commit is contained in:
Olivier Gagnon 2021-10-13 17:25:58 -04:00
parent 921a1517df
commit 3f1d4875e7
10 changed files with 245 additions and 158 deletions

78
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
package-lock.json generated

@ -51,6 +51,7 @@
"monaco-editor": "^0.27.0", "monaco-editor": "^0.27.0",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"normalize.css": "^8.0.0", "normalize.css": "^8.0.0",
"notistack": "^2.0.2",
"numeral": "2.0.6", "numeral": "2.0.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
@ -18065,6 +18066,34 @@
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
}, },
"node_modules/notistack": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.2.tgz",
"integrity": "sha512-Z2kD6QK9n/9V9zXQfN3ZgH22TefBWE3OZ/q74gMVslGPA9j9WGjoi+YxsgkTAOY8NqTYdUe8aijd2kXsGNYp6g==",
"dependencies": {
"clsx": "^1.1.0",
"hoist-non-react-statics": "^3.3.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/notistack"
},
"peerDependencies": {
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@mui/material": "^5.0.0",
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/npm-conf": { "node_modules/npm-conf": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",
@ -42337,6 +42366,15 @@
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
}, },
"notistack": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/notistack/-/notistack-2.0.2.tgz",
"integrity": "sha512-Z2kD6QK9n/9V9zXQfN3ZgH22TefBWE3OZ/q74gMVslGPA9j9WGjoi+YxsgkTAOY8NqTYdUe8aijd2kXsGNYp6g==",
"requires": {
"clsx": "^1.1.0",
"hoist-non-react-statics": "^3.3.0"
}
},
"npm-conf": { "npm-conf": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",

@ -52,6 +52,7 @@
"monaco-editor": "^0.27.0", "monaco-editor": "^0.27.0",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"normalize.css": "^8.0.0", "normalize.css": "^8.0.0",
"notistack": "^2.0.2",
"numeral": "2.0.6", "numeral": "2.0.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",

@ -147,6 +147,7 @@ import { INetscriptSleeve, NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
import { INetscriptExtra, NetscriptExtra } from "./NetscriptFunctions/Extra"; import { INetscriptExtra, NetscriptExtra } from "./NetscriptFunctions/Extra";
import { INetscriptHacknet, NetscriptHacknet } from "./NetscriptFunctions/Hacknet"; import { INetscriptHacknet, NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
import { dialogBoxCreate } from "./ui/React/DialogBox"; import { dialogBoxCreate } from "./ui/React/DialogBox";
import { SnackbarEvents } from "./ui/React/Snackbar";
const defaultInterpreter = new Interpreter("", () => undefined); const defaultInterpreter = new Interpreter("", () => undefined);
@ -2683,6 +2684,11 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
alert: function (message: any): void { alert: function (message: any): void {
dialogBoxCreate(message); dialogBoxCreate(message);
}, },
toast: function (message: any, variant: any = "success"): void {
if (!["success", "info", "warning", "error"].includes(variant))
throw new Error(`variant must be one of "success", "info", "warning", or "error"`);
SnackbarEvents.emit(message, variant);
},
prompt: function (txt: any): any { prompt: function (txt: any): any {
if (!isString(txt)) { if (!isString(txt)) {
txt = JSON.stringify(txt); txt = JSON.stringify(txt);

@ -10,7 +10,7 @@ import { Settings } from "./Settings/Settings";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket"; import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
import { GameSavedEvents } from "./ui/React/Snackbar"; import { SnackbarEvents } from "./ui/React/Snackbar";
import * as ExportBonus from "./ExportBonus"; import * as ExportBonus from "./ExportBonus";
@ -61,7 +61,7 @@ class BitburnerSaveObject {
const saveString = this.getSaveString(); const saveString = this.getSaveString();
save(saveString) save(saveString)
.then(() => GameSavedEvents.emit()) .then(() => SnackbarEvents.emit("Game Saved!", "info"))
.catch((err) => console.error(err)); .catch((err) => console.error(err));
} }

@ -65,7 +65,7 @@ import { CharacterOverview } from "./React/CharacterOverview";
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic"; import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
import { workerScripts } from "../Netscript/WorkerScripts"; import { workerScripts } from "../Netscript/WorkerScripts";
import { Unclickable } from "../Exploits/Unclickable"; import { Unclickable } from "../Exploits/Unclickable";
import { Snackbar } from "./React/Snackbar"; import { Snackbar, SnackbarProvider } from "./React/Snackbar";
import { LogBoxManager } from "./React/LogBoxManager"; import { LogBoxManager } from "./React/LogBoxManager";
import { AlertManager } from "./React/AlertManager"; import { AlertManager } from "./React/AlertManager";
import { PromptManager } from "./React/PromptManager"; import { PromptManager } from "./React/PromptManager";
@ -294,100 +294,102 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
) : page === Page.Work ? ( ) : page === Page.Work ? (
<WorkInProgressRoot /> <WorkInProgressRoot />
) : ( ) : (
<Box display="flex" flexDirection="row" width="100%"> <SnackbarProvider>
<SidebarRoot player={player} router={Router} page={page} /> <Box display="flex" flexDirection="row" width="100%">
<Box className={classes.root} flexGrow={1} display="block" px={1} height="100vh"> <SidebarRoot player={player} router={Router} page={page} />
{page === Page.Terminal ? ( <Box className={classes.root} flexGrow={1} display="block" px={1} height="100vh">
<TerminalRoot terminal={terminal} router={Router} player={player} /> {page === Page.Terminal ? (
) : page === Page.Sleeves ? ( <TerminalRoot terminal={terminal} router={Router} player={player} />
<SleeveRoot /> ) : page === Page.Sleeves ? (
) : page === Page.Stats ? ( <SleeveRoot />
<CharacterStats /> ) : page === Page.Stats ? (
) : page === Page.ScriptEditor ? ( <CharacterStats />
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} /> ) : page === Page.ScriptEditor ? (
) : page === Page.ActiveScripts ? ( <ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
<ActiveScriptsRoot workerScripts={workerScripts} /> ) : page === Page.ActiveScripts ? (
) : page === Page.Hacknet ? ( <ActiveScriptsRoot workerScripts={workerScripts} />
<HacknetRoot player={player} /> ) : page === Page.Hacknet ? (
) : page === Page.CreateProgram ? ( <HacknetRoot player={player} />
<ProgramsRoot /> ) : page === Page.CreateProgram ? (
) : page === Page.Factions ? ( <ProgramsRoot />
<FactionsRoot player={player} router={Router} /> ) : page === Page.Factions ? (
) : page === Page.Faction ? ( <FactionsRoot player={player} router={Router} />
<FactionRoot faction={faction} /> ) : page === Page.Faction ? (
) : page === Page.Milestones ? ( <FactionRoot faction={faction} />
<MilestonesRoot player={player} /> ) : page === Page.Milestones ? (
) : page === Page.Tutorial ? ( <MilestonesRoot player={player} />
<TutorialRoot /> ) : page === Page.Tutorial ? (
) : page === Page.DevMenu ? ( <TutorialRoot />
<DevMenuRoot player={player} engine={engine} router={Router} /> ) : page === Page.DevMenu ? (
) : page === Page.Gang ? ( <DevMenuRoot player={player} engine={engine} router={Router} />
<GangRoot /> ) : page === Page.Gang ? (
) : page === Page.Corporation ? ( <GangRoot />
<CorporationRoot /> ) : page === Page.Corporation ? (
) : page === Page.Bladeburner ? ( <CorporationRoot />
<BladeburnerRoot /> ) : page === Page.Bladeburner ? (
) : page === Page.Resleeves ? ( <BladeburnerRoot />
<ResleeveRoot /> ) : page === Page.Resleeves ? (
) : page === Page.Travel ? ( <ResleeveRoot />
<TravelAgencyRoot p={player} router={Router} /> ) : page === Page.Travel ? (
) : page === Page.StockMarket ? ( <TravelAgencyRoot p={player} router={Router} />
<StockMarketRoot ) : page === Page.StockMarket ? (
buyStockLong={buyStock} <StockMarketRoot
buyStockShort={shortStock} buyStockLong={buyStock}
cancelOrder={cancelOrder} buyStockShort={shortStock}
eventEmitterForReset={eventEmitterForUiReset} cancelOrder={cancelOrder}
initStockMarket={initStockMarketFnForReact} eventEmitterForReset={eventEmitterForUiReset}
p={player} initStockMarket={initStockMarketFnForReact}
placeOrder={placeOrder} p={player}
sellStockLong={sellStock} placeOrder={placeOrder}
sellStockShort={sellShort} sellStockLong={sellStock}
stockMarket={StockMarket} sellStockShort={sellShort}
/> stockMarket={StockMarket}
) : page === Page.City ? ( />
<LocationCity /> ) : page === Page.City ? (
) : page === Page.Job ? ( <LocationCity />
<GenericLocation loc={location} /> ) : page === Page.Job ? (
) : page === Page.Location ? ( <GenericLocation loc={location} />
<GenericLocation loc={location} /> ) : page === Page.Location ? (
) : page === Page.Options ? ( <GenericLocation loc={location} />
<GameOptionsRoot ) : page === Page.Options ? (
player={player} <GameOptionsRoot
save={() => saveObject.saveGame()} player={player}
export={() => saveObject.exportGame()} save={() => saveObject.saveGame()}
forceKill={() => { export={() => saveObject.exportGame()}
for (const server of GetAllServers()) { forceKill={() => {
server.runningScripts = []; for (const server of GetAllServers()) {
} server.runningScripts = [];
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page."); }
}} dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
softReset={() => { }}
dialogBoxCreate("Soft Reset!"); softReset={() => {
prestigeAugmentation(); dialogBoxCreate("Soft Reset!");
Router.toTerminal(); prestigeAugmentation();
}} Router.toTerminal();
/> }}
) : page === Page.Augmentations ? ( />
<AugmentationsRoot ) : page === Page.Augmentations ? (
exportGameFn={() => { <AugmentationsRoot
saveObject.exportGame(); exportGameFn={() => {
onExport(player); saveObject.exportGame();
}} onExport(player);
installAugmentationsFn={() => { }}
installAugmentations(); installAugmentationsFn={() => {
Router.toTerminal(); installAugmentations();
}} Router.toTerminal();
/> }}
) : ( />
<> ) : (
<Typography>Cannot load</Typography> <>
</> <Typography>Cannot load</Typography>
)} </>
)}
</Box>
</Box> </Box>
</Box> <Snackbar />
</SnackbarProvider>
)} )}
<Unclickable /> <Unclickable />
<Snackbar />
<LogBoxManager /> <LogBoxManager />
<AlertManager /> <AlertManager />
<PromptManager /> <PromptManager />

@ -1,29 +1,48 @@
import React, { useState, useEffect } from "react"; import React, { useEffect } from "react";
import { useSnackbar, SnackbarProvider as SB } from "notistack";
import { Snackbar as S } from "@mui/material";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography"; import Alert from "@mui/material/Alert";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
export const GameSavedEvents = new EventEmitter<[]>(); interface IProps {
children: React.ReactNode | React.ReactNode[];
}
export function Snackbar(): React.ReactElement { export function SnackbarProvider(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false);
useEffect(() => GameSavedEvents.subscribe(() => setOpen(true)));
return ( return (
<S <SB dense maxSnack={9} anchorOrigin={{ horizontal: "right", vertical: "bottom" }} autoHideDuration={2000}>
open={open} {props.children}
anchorOrigin={{ </SB>
vertical: "top",
horizontal: "center",
}}
autoHideDuration={2000}
onClose={() => setOpen(false)}
>
<Paper sx={{ p: 2 }}>
<Typography>Game Saved!</Typography>
</Paper>
</S>
); );
} }
export const SnackbarEvents = new EventEmitter<[string, "success" | "warning" | "error" | "info"]>();
export function Snackbar(): React.ReactElement {
const { enqueueSnackbar } = useSnackbar();
useEffect(() =>
SnackbarEvents.subscribe((s, variant) =>
enqueueSnackbar(<Alert severity={variant}>{s}</Alert>, {
content: (k, m) => <Paper key={k}>{m}</Paper>,
variant: variant,
}),
),
);
return <></>;
// return (
// <S
// open={open}
// anchorOrigin={{
// vertical: "top",
// horizontal: "center",
// }}
// autoHideDuration={2000}
// onClose={() => setOpen(false)}
// >
// <Paper sx={{ p: 2 }}>
// <Typography>Game Saved!</Typography>
// </Paper>
// </S>
// );
}

@ -294,6 +294,27 @@ export function refreshTheme(): void {
}, },
}, },
}, },
MuiAlert: {
styleOverrides: {
root: {
backgroundColor: Settings.theme.black,
borderRadius: 0,
border: "1px solid " + Settings.theme.well,
},
standardSuccess: {
color: Settings.theme.successLight,
},
standardError: {
color: Settings.theme.errorlight,
},
standardWarning: {
color: Settings.theme.warninglight,
},
standardInfo: {
color: Settings.theme.infolight,
},
},
},
}, },
}); });
} }