UI: Correct behavior of "back" button on faction augs page

Plus router refactoring
This commit is contained in:
Aleksei Bezrodnov 2023-06-26 10:24:37 +02:00 committed by GitHub
parent 9a0a843ffc
commit 1af01401d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 173 additions and 201 deletions

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -10,7 +11,7 @@ export const BitFlumeEvent = new EventEmitter<[]>();
export function BitFlumeModal(): React.ReactElement { export function BitFlumeModal(): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
function flume(): void { function flume(): void {
Router.toBitVerse(true, false); Router.toPage(Page.BitVerse, { flume: true, quick: false });
setOpen(false); setOpen(false);
} }

@ -4,6 +4,7 @@ import { BlackOperationName, FactionName } from "@enums";
import { BlackOpList } from "./BlackOpList"; import { BlackOpList } from "./BlackOpList";
import { Bladeburner } from "../Bladeburner"; import { Bladeburner } from "../Bladeburner";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { CorruptableText } from "../../ui/React/CorruptableText"; import { CorruptableText } from "../../ui/React/CorruptableText";
interface IProps { interface IProps {
@ -28,7 +29,7 @@ export function BlackOpPage(props: IProps): React.ReactElement {
losses. losses.
</Typography> </Typography>
{props.bladeburner.blackops[BlackOperationName.OperationDaedalus] ? ( {props.bladeburner.blackops[BlackOperationName.OperationDaedalus] ? (
<Button sx={{ my: 1, p: 1 }} onClick={() => Router.toBitVerse(false, false)}> <Button sx={{ my: 1, p: 1 }} onClick={() => Router.toPage(Page.BitVerse, { flume: false, quick: false })}>
<CorruptableText content="Destroy w0rld_d34mon"></CorruptableText> <CorruptableText content="Destroy w0rld_d34mon"></CorruptableText>
</Button> </Button>
) : ( ) : (

@ -8,6 +8,7 @@ import { Money } from "../../ui/React/Money";
import { formatNumberNoSuffix, formatPopulation, formatBigNumber } from "../../ui/formatNumber"; import { formatNumberNoSuffix, formatPopulation, formatBigNumber } from "../../ui/formatNumber";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { joinFaction } from "../../Faction/FactionHelpers"; import { joinFaction } from "../../Faction/FactionHelpers";
import { Bladeburner } from "../Bladeburner"; import { Bladeburner } from "../Bladeburner";
@ -34,7 +35,7 @@ export function Stats(props: IProps): React.ReactElement {
joinFaction(faction); joinFaction(faction);
} }
Router.toFaction(faction); Router.toPage(Page.Faction, { faction });
} }
return ( return (

@ -16,6 +16,7 @@ import { Player } from "@player";
import { FactionName } from "@enums"; import { FactionName } from "@enums";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { GangConstants } from "../../Gang/data/Constants"; import { GangConstants } from "../../Gang/data/Constants";
import { checkForMessagesToSend } from "../../Message/MessageHelpers"; import { checkForMessagesToSend } from "../../Message/MessageHelpers";
@ -37,10 +38,10 @@ export function General(): React.ReactElement {
const upgradeRam = () => (Player.getHomeComputer().maxRam *= 2); const upgradeRam = () => (Player.getHomeComputer().maxRam *= 2);
// Node-clearing functions // Node-clearing functions
const quickB1tFlum3 = () => Router.toBitVerse(true, true); const quickB1tFlum3 = () => Router.toPage(Page.BitVerse, { flume: true, quick: true });
const b1tflum3 = () => Router.toBitVerse(true, false); const b1tflum3 = () => Router.toPage(Page.BitVerse, { flume: true, quick: false });
const quickHackW0r1dD43m0n = () => Router.toBitVerse(false, true); const quickHackW0r1dD43m0n = () => Router.toPage(Page.BitVerse, { flume: false, quick: true });
const hackW0r1dD43m0n = () => Router.toBitVerse(false, false); const hackW0r1dD43m0n = () => Router.toPage(Page.BitVerse, { flume: false, quick: false });
// Corp functions // Corp functions
const createCorporation = () => { const createCorporation = () => {

@ -1,5 +1,6 @@
import { Player } from "@player"; import { Player } from "@player";
import { Router } from "./ui/GameRoot"; import { Router } from "./ui/GameRoot";
import { Page } from "./ui/Router";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { SnackbarEvents } from "./ui/React/Snackbar"; import { SnackbarEvents } from "./ui/React/Snackbar";
import { ToastVariant } from "@enums"; import { ToastVariant } from "@enums";
@ -28,8 +29,8 @@ declare global {
triggerGameExport: () => void; triggerGameExport: () => void;
triggerScriptsExport: () => void; triggerScriptsExport: () => void;
getSaveData: () => { save: string; fileName: string }; getSaveData: () => { save: string; fileName: string };
getSaveInfo: (base64save: string) => Promise<ImportPlayerData | undefined>; getSaveInfo: (base64Save: string) => Promise<ImportPlayerData | undefined>;
pushSaveData: (base64save: string, automatic?: boolean) => void; pushSaveData: (base64Save: string, automatic?: boolean) => void;
}; };
electronBridge: { electronBridge: {
send: (channel: string, data?: unknown) => void; send: (channel: string, data?: unknown) => void;
@ -137,16 +138,17 @@ function initSaveFunctions(): void {
fileName: saveObject.getSaveFileName(), fileName: saveObject.getSaveFileName(),
}; };
}, },
getSaveInfo: async (base64save: string): Promise<ImportPlayerData | undefined> => { getSaveInfo: async (base64Save: string): Promise<ImportPlayerData | undefined> => {
try { try {
const data = await saveObject.getImportDataFromString(base64save); const data = await saveObject.getImportDataFromString(base64Save);
return data.playerData; return data.playerData;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return; return;
} }
}, },
pushSaveData: (base64save: string, automatic = false): void => Router.toImportSave(base64save, automatic), pushSaveData: (base64Save: string, automatic = false): void =>
Router.toPage(Page.ImportSave, { base64Save, automatic }),
}; };
// Will be consumed by the electron wrapper. // Will be consumed by the electron wrapper.

@ -1,5 +1,5 @@
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
import React from "react"; import React from "react";
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { getAugCost, getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers"; import { getAugCost, getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
@ -11,22 +11,18 @@ import { Player } from "@player";
import { formatBigNumber } from "../../ui/formatNumber"; import { formatBigNumber } from "../../ui/formatNumber";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { Router } from "../../ui/GameRoot";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers"; import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { useRerender } from "../../ui/React/hooks"; import { useRerender } from "../../ui/React/hooks";
interface IProps {
faction: Faction;
routeToMainPage: () => void;
}
/** Root React Component for displaying a faction's "Purchase Augmentations" page */ /** Root React Component for displaying a faction's "Purchase Augmentations" page */
export function AugmentationsPage(props: IProps): React.ReactElement { export function AugmentationsPage({ faction }: { faction: Faction }): React.ReactElement {
const rerender = useRerender(); const rerender = useRerender();
function getAugs(): AugmentationName[] { function getAugs(): AugmentationName[] {
return getFactionAugmentationsFiltered(props.faction); return getFactionAugmentationsFiltered(faction);
} }
function getAugsSorted(): AugmentationName[] { function getAugsSorted(): AugmentationName[] {
@ -66,7 +62,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const aug = Augmentations[augName]; const aug = Augmentations[augName];
const augCosts = getAugCost(aug); const augCosts = getAugCost(aug);
const repCost = augCosts.repCost; const repCost = augCosts.repCost;
const hasReq = props.faction.playerReputation >= repCost; const hasReq = faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug); const hasRep = hasAugmentationPrereqs(aug);
const hasCost = augCosts.moneyCost !== 0 && Player.money > augCosts.moneyCost; const hasCost = augCosts.moneyCost !== 0 && Player.money > augCosts.moneyCost;
return hasCost && hasReq && hasRep; return hasCost && hasReq && hasRep;
@ -126,7 +122,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const owned = augs.filter((aug) => !purchasable.includes(aug)); const owned = augs.filter((aug) => !purchasable.includes(aug));
const multiplierComponent = const multiplierComponent =
props.faction.name !== FactionName.ShadowsOfAnarchy ? ( faction.name !== FactionName.ShadowsOfAnarchy ? (
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
@ -171,27 +167,27 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
return ( return (
<> <>
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}> <Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
<Button onClick={props.routeToMainPage}>Back</Button> <Button onClick={() => Router.back()}>Back</Button>
<Typography variant="h4">Faction Augmentations - {props.faction.name}</Typography> <Typography variant="h4">Faction Augmentations - {faction.name}</Typography>
<Paper sx={{ p: 1, mb: 1 }}> <Paper sx={{ p: 1, mb: 1 }}>
<Typography> <Typography>
These are all of the Augmentations that are available to purchase from <b>{props.faction.name}</b>. These are all of the Augmentations that are available to purchase from <b>{faction.name}</b>. Augmentations
Augmentations are powerful upgrades that will enhance your abilities. are powerful upgrades that will enhance your abilities.
<br /> <br />
</Typography> </Typography>
<Box <Box
sx={{ sx={{
display: "grid", display: "grid",
gridTemplateColumns: `repeat(${props.faction.name === FactionName.ShadowsOfAnarchy ? "2" : "3"}, 1fr)`, gridTemplateColumns: `repeat(${faction.name === FactionName.ShadowsOfAnarchy ? "2" : "3"}, 1fr)`,
justifyItems: "center", justifyItems: "center",
my: 1, my: 1,
}} }}
> >
<>{multiplierComponent}</> <>{multiplierComponent}</>
<Typography> <Typography>
<b>Reputation:</b> <Reputation reputation={props.faction.playerReputation} /> <b>Reputation:</b> <Reputation reputation={faction.playerReputation} />
<br /> <br />
<b>Favor:</b> <Favor favor={Math.floor(props.faction.favor)} /> <b>Favor:</b> <Favor favor={Math.floor(faction.favor)} />
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)" }}> <Box sx={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)" }}>
@ -216,7 +212,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const costs = getAugCost(aug); const costs = getAugCost(aug);
return ( return (
hasAugmentationPrereqs(aug) && hasAugmentationPrereqs(aug) &&
props.faction.playerReputation >= costs.repCost && faction.playerReputation >= costs.repCost &&
(costs.moneyCost === 0 || Player.money > costs.moneyCost) (costs.moneyCost === 0 || Player.money > costs.moneyCost)
); );
}} }}
@ -224,12 +220,12 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
if (!Settings.SuppressBuyAugmentationConfirmation) { if (!Settings.SuppressBuyAugmentationConfirmation) {
showModal(true); showModal(true);
} else { } else {
purchaseAugmentation(aug, props.faction); purchaseAugmentation(aug, faction);
rerender(); rerender();
} }
}} }}
rep={props.faction.playerReputation} rep={faction.playerReputation}
faction={props.faction} faction={faction}
/> />
</> </>
); );

@ -5,7 +5,6 @@
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption"; import { DonateOption } from "./DonateOption";
import { Info } from "./Info"; import { Info } from "./Info";
import { Option } from "./Option"; import { Option } from "./Option";
@ -24,10 +23,9 @@ import { FactionWork } from "../../Work/FactionWork";
import { useRerender } from "../../ui/React/hooks"; import { useRerender } from "../../ui/React/hooks";
import { repNeededToDonate } from "../formulas/donation"; import { repNeededToDonate } from "../formulas/donation";
interface IProps { type FactionRootProps = {
faction: Faction; faction: Faction;
augPage: boolean; };
}
// Info text for all options on the UI // Info text for all options on the UI
const hackingContractsInfo = const hackingContractsInfo =
@ -148,11 +146,8 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
); );
} }
export function FactionRoot(props: IProps): React.ReactElement { export function FactionRoot({ faction }: FactionRootProps): React.ReactElement {
const rerender = useRerender(200); const rerender = useRerender(200);
const [purchasingAugs, setPurchasingAugs] = useState(props.augPage);
const faction = props.faction;
if (!Player.factions.includes(faction.name)) { if (!Player.factions.includes(faction.name)) {
return ( return (
@ -165,9 +160,11 @@ export function FactionRoot(props: IProps): React.ReactElement {
); );
} }
return purchasingAugs ? ( return (
<AugmentationsPage faction={faction} routeToMainPage={() => setPurchasingAugs(false)} /> <MainPage
) : ( rerender={rerender}
<MainPage rerender={rerender} faction={faction} onAugmentations={() => setPurchasingAugs(true)} /> faction={faction}
onAugmentations={() => Router.toPage(Page.FactionAugmentations, { faction })}
/>
); );
} }

@ -1,16 +1,18 @@
import type { Faction } from "../Faction";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material"; import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material";
import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material"; import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material";
import { Player } from "@player"; import { Player } from "@player";
import { FactionName } from "@enums";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { formatFavor, formatReputation } from "../../ui/formatNumber"; import { formatFavor, formatReputation } from "../../ui/formatNumber";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { FactionName } from "@enums"; import { Page } from "../../ui/Router";
import { useRerender } from "../../ui/React/hooks";
import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers"; import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers";
import { Factions } from "../Factions"; import { Factions } from "../Factions";
import { useRerender } from "../../ui/React/hooks";
export const InvitationsSeen: string[] = []; export const InvitationsSeen: string[] = [];
@ -53,11 +55,11 @@ const FactionElement = (props: FactionElementProps): React.ReactElement => {
const augsLeft = getFactionAugmentationsFiltered(props.faction).filter((aug) => !Player.hasAugmentation(aug)).length; const augsLeft = getFactionAugmentationsFiltered(props.faction).filter((aug) => !Player.hasAugmentation(aug)).length;
function openFaction(faction: Faction): void { function openFaction(faction: Faction): void {
Router.toFaction(faction); Router.toPage(Page.Faction, { faction });
} }
function openFactionAugPage(faction: Faction): void { function openFactionAugPage(faction: Faction): void {
Router.toFaction(faction, true); Router.toPage(Page.FactionAugmentations, { faction });
} }
function acceptInvitation(event: React.MouseEvent<HTMLButtonElement>, faction: FactionName): void { function acceptInvitation(event: React.MouseEvent<HTMLButtonElement>, faction: FactionName): void {

@ -95,7 +95,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
function compareSaveGame(): void { function compareSaveGame(): void {
if (!importData) return; if (!importData) return;
Router.toImportSave(importData.base64); Router.toPage(Page.ImportSave, { base64Save: importData.base64 });
setImportSaveOpen(false); setImportSaveOpen(false);
setImportData(null); setImportData(null);
} }

@ -43,7 +43,7 @@ function toLocation(location: Location): void {
} else if (location.name === LocationName.WorldStockExchange) { } else if (location.name === LocationName.WorldStockExchange) {
Router.toPage(Page.StockMarket); Router.toPage(Page.StockMarket);
} else { } else {
Router.toLocation(location); Router.toPage(Page.Location, { location });
} }
} }

@ -151,11 +151,10 @@ export function CompanyLocation(props: IProps): React.ReactElement {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
const loc = location; if (!location.infiltrationData)
if (!loc.infiltrationData)
throw new Error(`trying to start infiltration at ${props.locName} but the infiltrationData is null`); throw new Error(`trying to start infiltration at ${props.locName} but the infiltrationData is null`);
Router.toInfiltration(loc); Router.toPage(Page.Infiltration, { location });
} }
function work(e: React.MouseEvent<HTMLElement>): void { function work(e: React.MouseEvent<HTMLElement>): void {

@ -23,8 +23,8 @@ import { findCrime } from "../Crime/CrimeHelpers";
import { CompanyPositions } from "../Company/CompanyPositions"; import { CompanyPositions } from "../Company/CompanyPositions";
import { DarkWebItems } from "../DarkWeb/DarkWebItems"; import { DarkWebItems } from "../DarkWeb/DarkWebItems";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { SpecialServers } from "../Server/data/SpecialServers";
import { Page } from "../ui/Router"; import { Page } from "../ui/Router";
import { SpecialServers } from "../Server/data/SpecialServers";
import { Locations } from "../Locations/Locations"; import { Locations } from "../Locations/Locations";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
@ -231,7 +231,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
} else if (location.name === LocationName.WorldStockExchange) { } else if (location.name === LocationName.WorldStockExchange) {
Router.toPage(Page.StockMarket); Router.toPage(Page.StockMarket);
} else { } else {
Router.toLocation(location); Router.toPage(Page.Location, { location });
} }
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000); Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000);
return true; return true;
@ -559,7 +559,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
server.backdoorInstalled = true; server.backdoorInstalled = true;
if (SpecialServers.WorldDaemon === server.hostname) { if (SpecialServers.WorldDaemon === server.hostname) {
return Router.toBitVerse(false, false); return Router.toPage(Page.BitVerse, { flume: false, quick: false });
} }
// Manunally check for faction invites // Manunally check for faction invites
Engine.Counters.checkFactionInvitations = 0; Engine.Counters.checkFactionInvitations = 0;

@ -1,7 +1,7 @@
import type { Augmentation } from "../../../Augmentation/Augmentation"; import type { Augmentation } from "../../../Augmentation/Augmentation";
import { Player } from "@player"; import { Player } from "@player";
import { AugmentationName, LocationName } from "@enums"; import { AugmentationName } from "@enums";
import React, { useState } from "react"; import React, { useState } from "react";
import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material"; import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material";
@ -11,7 +11,6 @@ import { GraftingWork } from "../../../Work/GraftingWork";
import { Augmentations } from "../../../Augmentation/Augmentations"; import { Augmentations } from "../../../Augmentation/Augmentations";
import { CONSTANTS } from "../../../Constants"; import { CONSTANTS } from "../../../Constants";
import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers"; import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers";
import { Locations } from "../../../Locations/Locations";
import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums";
import { Settings } from "../../../Settings/Settings"; import { Settings } from "../../../Settings/Settings";
import { Router } from "../../../ui/GameRoot"; import { Router } from "../../../ui/GameRoot";
@ -87,7 +86,7 @@ export const GraftingRoot = (): React.ReactElement => {
return ( return (
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}> <Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
<Button onClick={() => Router.toLocation(Locations[LocationName.NewTokyoVitaLife])}>Back</Button> <Button onClick={() => Router.back()}>Back</Button>
<Typography variant="h4">Grafting Laboratory</Typography> <Typography variant="h4">Grafting Laboratory</Typography>
<Typography> <Typography>
You find yourself in a secret laboratory, owned by a mysterious researcher. You find yourself in a secret laboratory, owned by a mysterious researcher.

@ -41,7 +41,7 @@ import PublicIcon from "@mui/icons-material/Public";
import LiveHelpIcon from "@mui/icons-material/LiveHelp"; import LiveHelpIcon from "@mui/icons-material/LiveHelp";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page, SimplePage } from "../../ui/Router"; import { Page, isSimplePage } from "../../ui/Router";
import { SidebarAccordion } from "./SidebarAccordion"; import { SidebarAccordion } from "./SidebarAccordion";
import { Player } from "@player"; import { Player } from "@player";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
@ -104,11 +104,7 @@ const useStyles = makeStyles((theme: Theme) =>
}), }),
); );
interface IProps { export function SidebarRoot(props: { page: Page }): React.ReactElement {
page: Page;
}
export function SidebarRoot(props: IProps): React.ReactElement {
useRerender(200); useRerender(200);
let flash: Page | null = null; let flash: Page | null = null;
@ -239,11 +235,11 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const clickPage = useCallback( const clickPage = useCallback(
(page: Page) => { (page: Page) => {
if (page === Page.Job) { if (page === Page.Job) {
Router.toJob(Locations[Object.keys(Player.jobs)[0]]); Router.toPage(page, { location: Locations[Object.keys(Player.jobs)[0]] });
} else if (page == Page.ScriptEditor) { } else if (page == Page.ScriptEditor) {
Router.toScriptEditor(); Router.toPage(page, {});
} else if ((Object.values(SimplePage) as Page[]).includes(page)) { } else if (isSimplePage(page)) {
Router.toPage(page as SimplePage); Router.toPage(page);
} else { } else {
throw new Error("Can't handle click on Page " + page); throw new Error("Can't handle click on Page " + page);
} }

@ -1,5 +1,6 @@
import { Output, Link, RawOutput, TTimer } from "./OutputTypes"; import { Output, Link, RawOutput, TTimer } from "./OutputTypes";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { HacknetServer } from "../Hacknet/HacknetServer"; import { HacknetServer } from "../Hacknet/HacknetServer";
import { BaseServer } from "../Server/BaseServer"; import { BaseServer } from "../Server/BaseServer";
@ -206,7 +207,7 @@ export class Terminal {
// Success! // Success!
server.backdoorInstalled = true; server.backdoorInstalled = true;
if (SpecialServers.WorldDaemon === server.hostname) { if (SpecialServers.WorldDaemon === server.hostname) {
Router.toBitVerse(false, false); Router.toPage(Page.BitVerse, { flume: false, quick: false });
return; return;
} }
// Manunally check for faction invites // Manunally check for faction invites
@ -301,7 +302,7 @@ export class Terminal {
if (Player.bitNodeN == null) { if (Player.bitNodeN == null) {
Player.bitNodeN = 1; Player.bitNodeN = 1;
} }
Router.toBitVerse(false, false); Router.toPage(Page.BitVerse, { flume: false, quick: false });
return; return;
} }
// Manunally check for faction invites // Manunally check for faction invites

@ -1,5 +1,5 @@
import { Terminal } from "../../../Terminal"; import { Terminal } from "../../../Terminal";
import { ScriptEditorRouteOptions } from "../../../ui/Router"; import { ScriptEditorRouteOptions, Page } from "../../../ui/Router";
import { Router } from "../../../ui/GameRoot"; import { Router } from "../../../ui/GameRoot";
import { BaseServer } from "../../../Server/BaseServer"; import { BaseServer } from "../../../Server/BaseServer";
import { CursorPositions } from "../../../ScriptEditor/CursorPositions"; import { CursorPositions } from "../../../ScriptEditor/CursorPositions";
@ -26,17 +26,17 @@ export async function main(ns) {
export function commonEditor( export function commonEditor(
command: string, command: string,
{ args, server }: EditorParameters, { args, server }: EditorParameters,
scriptEditorRouteOptions?: ScriptEditorRouteOptions, options?: ScriptEditorRouteOptions,
): void { ): void {
if (args.length < 1) return Terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`); if (args.length < 1) return Terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`);
const filesToOpen = new Map<ScriptFilePath | TextFilePath, string>(); const files = new Map<ScriptFilePath | TextFilePath, string>();
for (const arg of args) { for (const arg of args) {
const pattern = String(arg); const pattern = String(arg);
// Glob of existing files // Glob of existing files
if (pattern.includes("*") || pattern.includes("?")) { if (pattern.includes("*") || pattern.includes("?")) {
for (const [path, file] of getGlobbedFileMap(pattern, server, Terminal.currDir)) { for (const [path, file] of getGlobbedFileMap(pattern, server, Terminal.currDir)) {
filesToOpen.set(path, file.content); files.set(path, file.content);
} }
continue; continue;
} }
@ -49,8 +49,8 @@ export function commonEditor(
} }
const file = server.getContentFile(path); const file = server.getContentFile(path);
const content = file ? file.content : isNs2(path) ? newNs2Template : ""; const content = file ? file.content : isNs2(path) ? newNs2Template : "";
filesToOpen.set(path, content); files.set(path, content);
if (content === newNs2Template) CursorPositions.saveCursor(path, { row: 3, column: 5 }); if (content === newNs2Template) CursorPositions.saveCursor(path, { row: 3, column: 5 });
} }
Router.toScriptEditor(filesToOpen, scriptEditorRouteOptions); Router.toPage(Page.ScriptEditor, { files, options });
} }

@ -1,15 +1,17 @@
import React from "react";
import { Theme } from "@mui/material/styles";
import type { TextFilePath } from "../../Paths/TextFilePath"; import type { TextFilePath } from "../../Paths/TextFilePath";
import type { ContractFilePath } from "../../Paths/ContractFilePath"; import type { ContractFilePath } from "../../Paths/ContractFilePath";
import type { ProgramFilePath } from "../../Paths/ProgramFilePath"; import type { ProgramFilePath } from "../../Paths/ProgramFilePath";
import type { ContentFilePath } from "../../Paths/ContentFile"; import type { ContentFilePath } from "../../Paths/ContentFile";
import type { ScriptFilePath } from "../../Paths/ScriptFilePath"; import type { ScriptFilePath } from "../../Paths/ScriptFilePath";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import React from "react";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Terminal } from "../../Terminal"; import { Terminal } from "../../Terminal";
import libarg from "arg"; import libarg from "arg";
import { showLiterature } from "../../Literature/LiteratureHelpers"; import { showLiterature } from "../../Literature/LiteratureHelpers";
@ -133,9 +135,9 @@ export function ls(args: (string | number | boolean)[], server: BaseServer): voi
const fullPath = combinePath(baseDirectory, props.path); const fullPath = combinePath(baseDirectory, props.path);
function onClick() { function onClick() {
const code = server.scripts.get(fullPath)?.content ?? ""; const code = server.scripts.get(fullPath)?.content ?? "";
const map = new Map<ContentFilePath, string>(); const files = new Map<ContentFilePath, string>();
map.set(fullPath, code); files.set(fullPath, code);
Router.toScriptEditor(map); Router.toPage(Page.ScriptEditor, { files });
} }
return ( return (
<span> <span>

@ -1,6 +1,3 @@
import type { ScriptFilePath } from "../Paths/ScriptFilePath";
import type { TextFilePath } from "../Paths/TextFilePath";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { createStyles, makeStyles } from "@mui/styles"; import { createStyles, makeStyles } from "@mui/styles";
import { Box, Typography } from "@mui/material"; import { Box, Typography } from "@mui/material";
@ -11,19 +8,16 @@ import { installAugmentations } from "../Augmentation/AugmentationHelpers";
import { saveObject } from "../SaveObject"; import { saveObject } from "../SaveObject";
import { onExport } from "../ExportBonus"; import { onExport } from "../ExportBonus";
import { LocationName } from "@enums"; import { LocationName } from "@enums";
import { Location } from "../Locations/Location";
import { ITutorial, iTutorialStart } from "../InteractiveTutorial"; import { ITutorial, iTutorialStart } from "../InteractiveTutorial";
import { InteractiveTutorialRoot } from "./InteractiveTutorial/InteractiveTutorialRoot"; import { InteractiveTutorialRoot } from "./InteractiveTutorial/InteractiveTutorialRoot";
import { ITutorialEvents } from "./InteractiveTutorial/ITutorialEvents"; import { ITutorialEvents } from "./InteractiveTutorial/ITutorialEvents";
import { Faction } from "../Faction/Faction";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
import { dialogBoxCreate } from "./React/DialogBox"; import { dialogBoxCreate } from "./React/DialogBox";
import { GetAllServers } from "../Server/AllServers"; import { GetAllServers } from "../Server/AllServers";
import { Factions } from "../Faction/Factions";
import { StockMarket } from "../StockMarket/StockMarket"; import { StockMarket } from "../StockMarket/StockMarket";
import { Page, SimplePage, IRouter } from "./Router"; import { Page, PageWithContext, IRouter, ComplexPage, PageContext } from "./Router";
import { Overview } from "./React/Overview"; import { Overview } from "./React/Overview";
import { SidebarRoot } from "../Sidebar/ui/SidebarRoot"; import { SidebarRoot } from "../Sidebar/ui/SidebarRoot";
import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot"; import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot";
@ -47,6 +41,7 @@ import { TutorialRoot } from "../Tutorial/ui/TutorialRoot";
import { ActiveScriptsRoot } from "./ActiveScripts/ActiveScriptsRoot"; import { ActiveScriptsRoot } from "./ActiveScripts/ActiveScriptsRoot";
import { FactionsRoot } from "../Faction/ui/FactionsRoot"; import { FactionsRoot } from "../Faction/ui/FactionsRoot";
import { FactionRoot } from "../Faction/ui/FactionRoot"; import { FactionRoot } from "../Faction/ui/FactionRoot";
import { AugmentationsPage as FactionAugmentations } from "../Faction/ui/AugmentationsPage";
import { CharacterStats } from "./CharacterStats"; import { CharacterStats } from "./CharacterStats";
import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot"; import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot";
import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot"; import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot";
@ -72,7 +67,6 @@ import { ImportSave } from "./React/ImportSave";
import { BypassWrapper } from "./React/BypassWrapper"; import { BypassWrapper } from "./React/BypassWrapper";
import { Apr1 } from "./Apr1"; import { Apr1 } from "./Apr1";
import { isFactionWork } from "../Work/FactionWork";
import { V2Modal } from "../utils/V2Modal"; import { V2Modal } from "../utils/V2Modal";
import { MathJaxContext } from "better-react-mathjax"; import { MathJaxContext } from "better-react-mathjax";
import { useRerender } from "./React/hooks"; import { useRerender } from "./React/hooks";
@ -100,6 +94,8 @@ const uninitialized = (): void => {
throw new Error("Router called before initialization"); throw new Error("Router called before initialization");
}; };
const MAX_PAGES_IN_HISTORY = 10;
export let Router: IRouter = { export let Router: IRouter = {
isInitialized: false, isInitialized: false,
page: () => { page: () => {
@ -109,16 +105,12 @@ export let Router: IRouter = {
toPage: () => { toPage: () => {
throw new Error("Router called before initialization"); throw new Error("Router called before initialization");
}, },
toBitVerse: uninitialized, back: () => {
toFaction: uninitialized, throw new Error("Router called before initialization");
toInfiltration: uninitialized, },
toJob: uninitialized,
toScriptEditor: uninitialized,
toLocation: uninitialized,
toImportSave: uninitialized,
}; };
function determineStartPage(): Page { function determineStartPage() {
if (RecoveryMode) return Page.Recovery; if (RecoveryMode) return Page.Recovery;
if (Player.currentWork !== null) return Page.Work; if (Player.currentWork !== null) return Page.Work;
return Page.Terminal; return Page.Terminal;
@ -126,36 +118,21 @@ function determineStartPage(): Page {
export function GameRoot(): React.ReactElement { export function GameRoot(): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const [{ files, vim }, setEditorOptions] = useState<{
files: Map<ScriptFilePath | TextFilePath, string>; const [pages, setPages] = useState<PageWithContext[]>(() => [{ page: determineStartPage() }]);
vim: boolean; const pageWithContext = pages[0];
}>({
files: new Map(), const setNextPage = (pageWithContext: PageWithContext) =>
vim: false, setPages((prev) => {
}); const next = [pageWithContext, ...prev];
const [page, setPage] = useState(determineStartPage()); next.length = Math.min(next.length, MAX_PAGES_IN_HISTORY);
return next;
});
const rerender = useRerender(); const rerender = useRerender();
const [augPage, setAugPage] = useState<boolean>(false);
const [faction, setFaction] = useState<Faction>(
isFactionWork(Player.currentWork) ? Factions[Player.currentWork.factionName] : (undefined as unknown as Faction),
);
if (faction === undefined && page === Page.Faction)
throw new Error("Trying to go to a page without the proper setup");
const [flume, setFlume] = useState<boolean>(false);
const [quick, setQuick] = useState<boolean>(false);
const [location, setLocation] = useState<Location>(undefined as unknown as Location);
if (location === undefined && (page === Page.Infiltration || page === Page.Location || page === Page.Job))
throw new Error("Trying to go to a page without the proper setup");
const [cinematicText, setCinematicText] = useState("");
const [errorBoundaryKey, setErrorBoundaryKey] = useState<number>(0); const [errorBoundaryKey, setErrorBoundaryKey] = useState<number>(0);
const [importString, setImportString] = useState<string>(undefined as unknown as string);
const [importAutomatic, setImportAutomatic] = useState<boolean>(false);
if (importString === undefined && page === Page.ImportSave)
throw new Error("Trying to go to a page without the proper setup");
const [allowRoutingCalls, setAllowRoutingCalls] = useState(true); const [allowRoutingCalls, setAllowRoutingCalls] = useState(true);
function resetErrorBoundary(): void { function resetErrorBoundary(): void {
@ -180,67 +157,28 @@ export function GameRoot(): React.ReactElement {
Router = { Router = {
isInitialized: true, isInitialized: true,
page: () => page, page: () => pageWithContext.page,
allowRouting: (value: boolean) => setAllowRoutingCalls(value), allowRouting: (value: boolean) => setAllowRoutingCalls(value),
toPage: (page: SimplePage) => { toPage: (page: Page, context?: PageContext<ComplexPage>) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toPage"); if (!allowRoutingCalls) return attemptedForbiddenRouting("toPage");
switch (page) { switch (page) {
case Page.Travel: case Page.Travel:
Player.gotoLocation(LocationName.TravelAgency); Player.gotoLocation(LocationName.TravelAgency);
break; break;
case Page.BladeburnerCinematic: case Page.BitVerse:
setPage(page); calculateAchievements();
setCinematicText(cinematicText); break;
return;
} }
setPage(page); setNextPage({ page, ...context } as PageWithContext);
}, },
toFaction: (faction: Faction, augPage = false) => { back: () => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toFaction"); if (!allowRoutingCalls) return attemptedForbiddenRouting("back");
setAugPage(augPage); setPages((pages) => pages.slice(1));
setPage(Page.Faction);
if (faction) setFaction(faction);
},
toScriptEditor: (files = new Map(), options) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toScriptEditor");
setEditorOptions({
files,
vim: !!options?.vim,
});
setPage(Page.ScriptEditor);
},
toJob: (location: Location) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toJob");
setLocation(location);
setPage(Page.Job);
},
toBitVerse: (flume: boolean, quick: boolean) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toBitVerse");
setFlume(flume);
setQuick(quick);
calculateAchievements();
setPage(Page.BitVerse);
},
toInfiltration: (location: Location) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toInfiltration");
setLocation(location);
setPage(Page.Infiltration);
},
toLocation: (location: Location) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toLocation");
setLocation(location);
setPage(Page.Location);
},
toImportSave: (base64save: string, automatic = false) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toImportSave");
setImportString(base64save);
setImportAutomatic(automatic);
setPage(Page.ImportSave);
}, },
}; };
useEffect(() => { useEffect(() => {
if (page !== Page.Terminal) window.scrollTo(0, 0); if (pageWithContext.page !== Page.Terminal) window.scrollTo(0, 0);
}); });
function softReset(): void { function softReset(): void {
@ -254,7 +192,7 @@ export function GameRoot(): React.ReactElement {
let withSidebar = true; let withSidebar = true;
let withPopups = true; let withPopups = true;
let bypassGame = false; let bypassGame = false;
switch (page) { switch (pageWithContext.page) {
case Page.Recovery: { case Page.Recovery: {
mainPage = <RecoveryRoot softReset={softReset} />; mainPage = <RecoveryRoot softReset={softReset} />;
withSidebar = false; withSidebar = false;
@ -263,13 +201,13 @@ export function GameRoot(): React.ReactElement {
break; break;
} }
case Page.BitVerse: { case Page.BitVerse: {
mainPage = <BitverseRoot flume={flume} quick={quick} />; mainPage = <BitverseRoot flume={pageWithContext.flume} quick={pageWithContext.quick} />;
withSidebar = false; withSidebar = false;
withPopups = false; withPopups = false;
break; break;
} }
case Page.Infiltration: { case Page.Infiltration: {
mainPage = <InfiltrationRoot location={location} />; mainPage = <InfiltrationRoot location={pageWithContext.location} />;
withSidebar = false; withSidebar = false;
withPopups = false; withPopups = false;
break; break;
@ -302,7 +240,13 @@ export function GameRoot(): React.ReactElement {
break; break;
} }
case Page.ScriptEditor: { case Page.ScriptEditor: {
mainPage = <ScriptEditorRoot files={files} hostname={Player.getCurrentServer().hostname} vim={vim} />; mainPage = (
<ScriptEditorRoot
files={pageWithContext.files ?? new Map()}
hostname={Player.getCurrentServer().hostname}
vim={!!pageWithContext.options?.vim}
/>
);
break; break;
} }
case Page.ActiveScripts: { case Page.ActiveScripts: {
@ -322,7 +266,11 @@ export function GameRoot(): React.ReactElement {
break; break;
} }
case Page.Faction: { case Page.Faction: {
mainPage = <FactionRoot faction={faction} augPage={augPage} />; mainPage = <FactionRoot faction={pageWithContext.faction} />;
break;
}
case Page.FactionAugmentations: {
mainPage = <FactionAugmentations faction={pageWithContext.faction} />;
break; break;
} }
case Page.Milestones: { case Page.Milestones: {
@ -375,7 +323,7 @@ export function GameRoot(): React.ReactElement {
} }
case Page.Job: case Page.Job:
case Page.Location: { case Page.Location: {
mainPage = <GenericLocation loc={location} />; mainPage = <GenericLocation loc={pageWithContext.location} />;
break; break;
} }
case Page.Options: { case Page.Options: {
@ -417,7 +365,7 @@ export function GameRoot(): React.ReactElement {
break; break;
} }
case Page.ImportSave: { case Page.ImportSave: {
mainPage = <ImportSave importString={importString} automatic={importAutomatic} />; mainPage = <ImportSave importString={pageWithContext.base64Save} automatic={!!pageWithContext.automatic} />;
withSidebar = false; withSidebar = false;
withPopups = false; withPopups = false;
bypassGame = true; bypassGame = true;
@ -444,7 +392,7 @@ export function GameRoot(): React.ReactElement {
</Overview> </Overview>
{withSidebar ? ( {withSidebar ? (
<Box display="flex" flexDirection="row" width="100%"> <Box display="flex" flexDirection="row" width="100%">
<SidebarRoot page={page} /> <SidebarRoot page={pageWithContext.page} />
<Box className={classes.root}>{mainPage}</Box> <Box className={classes.root}>{mainPage}</Box>
</Box> </Box>
) : ( ) : (

@ -3,7 +3,7 @@ import type { TextFilePath } from "../Paths/TextFilePath";
import type { Faction } from "../Faction/Faction"; import type { Faction } from "../Faction/Faction";
import type { Location } from "../Locations/Location"; import type { Location } from "../Locations/Location";
// These enums don't need enum helper support for now // This enum doesn't need enum helper support for now
/** /**
* The full-screen page the player is currently be on. * The full-screen page the player is currently be on.
* These are "simple" pages that don't require any extra parameters to * These are "simple" pages that don't require any extra parameters to
@ -38,14 +38,12 @@ export enum SimplePage {
ThemeBrowser = "Theme Browser", ThemeBrowser = "Theme Browser",
} }
/**
* "Complex" pages that need a custom transition function.
*/
export enum ComplexPage { export enum ComplexPage {
BitVerse = "BitVerse", BitVerse = "BitVerse",
Faction = "Faction",
Infiltration = "Infiltration", Infiltration = "Infiltration",
Job = "Job", Job = "Job",
Faction = "Faction",
FactionAugmentations = "Faction Augmentations",
ScriptEditor = "Script Editor", ScriptEditor = "Script Editor",
Location = "Location", Location = "Location",
ImportSave = "Import Save", ImportSave = "Import Save",
@ -56,6 +54,35 @@ export enum ComplexPage {
export type Page = SimplePage | ComplexPage; export type Page = SimplePage | ComplexPage;
export const Page = { ...SimplePage, ...ComplexPage }; export const Page = { ...SimplePage, ...ComplexPage };
export type PageContext<T extends Page> = T extends ComplexPage.BitVerse
? { flume: boolean; quick: boolean }
: T extends ComplexPage.Infiltration
? { location: Location }
: T extends ComplexPage.Job
? { location: Location }
: T extends ComplexPage.Faction
? { faction: Faction }
: T extends ComplexPage.FactionAugmentations
? { faction: Faction }
: T extends ComplexPage.ScriptEditor
? { files?: Map<ScriptFilePath | TextFilePath, string>; options?: ScriptEditorRouteOptions }
: T extends ComplexPage.Location
? { location: Location }
: T extends ComplexPage.ImportSave
? { base64Save: string; automatic?: boolean }
: never;
export type PageWithContext =
| ({ page: ComplexPage.BitVerse } & PageContext<ComplexPage.BitVerse>)
| ({ page: ComplexPage.Infiltration } & PageContext<ComplexPage.Infiltration>)
| ({ page: ComplexPage.Job } & PageContext<ComplexPage.Job>)
| ({ page: ComplexPage.Faction } & PageContext<ComplexPage.Faction>)
| ({ page: ComplexPage.FactionAugmentations } & PageContext<ComplexPage.FactionAugmentations>)
| ({ page: ComplexPage.ScriptEditor } & PageContext<ComplexPage.ScriptEditor>)
| ({ page: ComplexPage.Location } & PageContext<ComplexPage.Location>)
| ({ page: ComplexPage.ImportSave } & PageContext<ComplexPage.ImportSave>)
| { page: SimplePage };
export interface ScriptEditorRouteOptions { export interface ScriptEditorRouteOptions {
vim: boolean; vim: boolean;
} }
@ -66,11 +93,10 @@ export interface IRouter {
page(): Page; page(): Page;
allowRouting(value: boolean): void; allowRouting(value: boolean): void;
toPage(page: SimplePage): void; toPage(page: SimplePage): void;
toBitVerse(flume: boolean, quick: boolean): void; toPage<T extends ComplexPage>(page: T, context: PageContext<T>): void;
toFaction(faction: Faction, augPage?: boolean): void; // faction name /** go to a preveious page (if any) */
toInfiltration(location: Location): void; back(): void;
toJob(location: Location): void;
toScriptEditor(files?: Map<ScriptFilePath | TextFilePath, string>, options?: ScriptEditorRouteOptions): void;
toLocation(location: Location): void;
toImportSave(base64Save: string, automatic?: boolean): void;
} }
const simplePages = Object.values(SimplePage);
export const isSimplePage = (page: Page): page is SimplePage => simplePages.includes(page as SimplePage);

@ -206,7 +206,7 @@ export function WorkInProgressRoot(): React.ReactElement {
workInfo = { workInfo = {
buttons: { buttons: {
cancel: () => { cancel: () => {
Router.toLocation(Locations[LocationName.Slums]); Router.toPage(Page.Location, { location: Locations[LocationName.Slums] });
Player.finishWork(true); Player.finishWork(true);
}, },
unfocus: () => { unfocus: () => {
@ -374,11 +374,11 @@ export function WorkInProgressRoot(): React.ReactElement {
workInfo = { workInfo = {
buttons: { buttons: {
cancel: () => { cancel: () => {
Router.toFaction(faction); Router.toPage(Page.Faction, { faction });
Player.finishWork(true); Player.finishWork(true);
}, },
unfocus: () => { unfocus: () => {
Router.toFaction(faction); Router.toPage(Page.Faction, { faction });
Player.stopFocusing(); Player.stopFocusing();
}, },
}, },
@ -426,11 +426,11 @@ export function WorkInProgressRoot(): React.ReactElement {
buttons: { buttons: {
cancel: () => { cancel: () => {
Player.finishWork(true); Player.finishWork(true);
Router.toJob(Locations[comp.name]); Router.toPage(Page.Job, { location: Locations[comp.name] });
}, },
unfocus: () => { unfocus: () => {
Player.stopFocusing(); Player.stopFocusing();
Router.toJob(Locations[comp.name]); Router.toPage(Page.Job, { location: Locations[comp.name] });
}, },
}, },
title: ( title: (