From 91b68db1fe6b4d24b7b402b65cc2e68cddd79ec2 Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Sat, 1 Mar 2025 03:52:08 +0700 Subject: [PATCH] BUGFIX: Multiple problems with Job tab (#1976) --- src/Locations/ui/GenericLocation.tsx | 123 ++++++++++++++++----------- src/Locations/ui/JobRoot.tsx | 16 +++- src/Sidebar/ui/SidebarRoot.tsx | 7 +- src/ui/Enums.ts | 2 +- src/ui/GameRoot.tsx | 2 +- src/ui/Router.ts | 3 - src/ui/WorkInProgressRoot.tsx | 4 +- src/utils/KeyBindingUtils.ts | 4 +- 8 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/Locations/ui/GenericLocation.tsx b/src/Locations/ui/GenericLocation.tsx index 2740188ea..46b8d44d3 100644 --- a/src/Locations/ui/GenericLocation.tsx +++ b/src/Locations/ui/GenericLocation.tsx @@ -30,64 +30,83 @@ import { Page } from "../../ui/Router"; import { serverMetadata } from "../../Server/data/servers"; import { Tooltip } from "@mui/material"; import { getEnumHelper } from "../../utils/EnumHelper"; +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; interface IProps { - loc: Location; + location: Location; showBackButton: boolean; } -export function GenericLocation({ loc, showBackButton }: IProps): React.ReactElement { - /** - * Determine what needs to be rendered for this location based on the locations - * type. Returns an array of React components that should be rendered - */ - function getLocationSpecificContent(): React.ReactNode[] { - const content: React.ReactNode[] = []; +/** + * Determine what needs to be rendered for this location based on the locations + * type. Returns an array of React components that should be rendered + */ +function getLocationSpecificContent(location: Location): React.ReactNode[] { + const content: React.ReactNode[] = []; - if (loc.types.includes(LocationType.Company)) { - if (!getEnumHelper("CompanyName").isMember(loc.name)) { - throw new Error(`Location name ${loc.name} is for a company but is not a company name.`); - } - content.push(); + if (location.types.includes(LocationType.Company)) { + if (!getEnumHelper("CompanyName").isMember(location.name)) { + throw new Error(`Location name ${location.name} is for a company but is not a company name.`); } - - if (loc.types.includes(LocationType.Gym)) { - content.push(); - } - - if (loc.types.includes(LocationType.Hospital)) { - content.push(); - } - - if (loc.types.includes(LocationType.Slums)) { - content.push(); - } - - if (loc.types.includes(LocationType.Special)) { - content.push(); - } - - if (loc.types.includes(LocationType.TechVendor)) { - content.push(); - } - - if (loc.types.includes(LocationType.TravelAgency)) { - content.push(); - } - - if (loc.types.includes(LocationType.University)) { - content.push(); - } - - if (loc.types.includes(LocationType.Casino)) { - content.push(); - } - - return content; + content.push(); } - const locContent: React.ReactNode[] = getLocationSpecificContent(); - const serverMeta = serverMetadata.find((s) => s.specialName === loc.name); + if (location.types.includes(LocationType.Gym)) { + content.push(); + } + + if (location.types.includes(LocationType.Hospital)) { + content.push(); + } + + if (location.types.includes(LocationType.Slums)) { + content.push(); + } + + if (location.types.includes(LocationType.Special)) { + content.push(); + } + + if (location.types.includes(LocationType.TechVendor)) { + content.push(); + } + + if (location.types.includes(LocationType.TravelAgency)) { + content.push(); + } + + if (location.types.includes(LocationType.University)) { + content.push(); + } + + if (location.types.includes(LocationType.Casino)) { + content.push(); + } + + return content; +} + +export function GenericLocation({ location, showBackButton }: IProps): React.ReactElement { + /** + * location can be undefined if GenericLocation is used like this: + * + * + * + * We need to check it before using. + */ + if (location == null) { + exceptionAlert(new Error(`GenericLocation is used with invalid location.`), true); + /** + * Return to the Terminal tab. We put the call of Router.toPage() inside setTimeout to avoid updating GameRoot while + * rendering this component. + */ + setTimeout(() => { + Router.toPage(Page.Terminal); + }, 100); + return <>; + } + const locationContent: React.ReactNode[] = getLocationSpecificContent(location); + const serverMeta = serverMetadata.find((s) => s.specialName === location.name); const server = GetServer(serverMeta ? serverMeta.hostname : ""); const backdoorInstalled = server !== null && isBackdoorInstalled(server); @@ -99,14 +118,14 @@ export function GenericLocation({ loc, showBackButton }: IProps): React.ReactEle {backdoorInstalled && serverMeta ? ( - + ) : ( - loc.name + location.name )} - {locContent} + {locationContent} ); } diff --git a/src/Locations/ui/JobRoot.tsx b/src/Locations/ui/JobRoot.tsx index fecfd2e27..9161a0770 100644 --- a/src/Locations/ui/JobRoot.tsx +++ b/src/Locations/ui/JobRoot.tsx @@ -1,11 +1,12 @@ import React from "react"; -import { Box } from "@mui/material"; +import { Box, Typography } from "@mui/material"; import { Player } from "@player"; import { getRecordKeys } from "../../Types/Record"; import { useCycleRerender } from "../../ui/React/hooks"; import { Locations } from "../Locations"; import { GenericLocation } from "./GenericLocation"; +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; export function JobRoot(): React.ReactElement { useCycleRerender(); @@ -13,14 +14,23 @@ export function JobRoot(): React.ReactElement { const jobs = getRecordKeys(Player.jobs).map((companyName) => { const location = Locations[companyName]; if (location == null) { - throw new Error(`Invalid company name: ${companyName}`); + exceptionAlert(new Error(`Player.jobs contains invalid data. companyName: ${companyName}.`), true); + return <>; } return ( - ; + ; ); }); + if (jobs.length === 0) { + return ( + + No jobs + + ); + } + return {jobs}; } diff --git a/src/Sidebar/ui/SidebarRoot.tsx b/src/Sidebar/ui/SidebarRoot.tsx index 50a12a49e..0fa7c766a 100644 --- a/src/Sidebar/ui/SidebarRoot.tsx +++ b/src/Sidebar/ui/SidebarRoot.tsx @@ -54,7 +54,6 @@ import { AugmentationName, CityName } from "@enums"; import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot"; import { InvitationsSeen } from "../../Faction/ui/FactionsRoot"; import { commitHash } from "../../utils/helpers/commitHash"; -import { Locations } from "../../Locations/Locations"; import { useCycleRerender } from "../../ui/React/hooks"; import { playerHasDiscoveredGo } from "../../Go/effects/effect"; import { knowAboutBitverse } from "../../BitNode/BitNodeUtils"; @@ -176,9 +175,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement { const clickPage = useCallback( (page: Page) => { - if (page === Page.Job) { - Router.toPage(page, { location: Locations[Object.keys(Player.jobs)[0]] }); - } else if (page == Page.ScriptEditor || page == Page.Documentation) { + if (page == Page.ScriptEditor || page == Page.Documentation) { Router.toPage(page, {}); } else if (isSimplePage(page)) { Router.toPage(page); @@ -221,7 +218,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement { return canOpenSleeves; case SimplePage.Grafting: return canOpenGrafting; - case ComplexPage.Job: + case SimplePage.Job: return canJob; case SimplePage.StockMarket: return canStockMarket; diff --git a/src/ui/Enums.ts b/src/ui/Enums.ts index 25fb9630f..c3f510b75 100644 --- a/src/ui/Enums.ts +++ b/src/ui/Enums.ts @@ -31,6 +31,7 @@ export enum SimplePage { StockMarket = "Stock Market", Terminal = "Terminal", Travel = "Travel", + Job = "Job", Work = "Work", BladeburnerCinematic = "Bladeburner Cinematic", Loading = "Loading", @@ -43,7 +44,6 @@ export enum SimplePage { export enum ComplexPage { BitVerse = "BitVerse", Infiltration = "Infiltration", - Job = "Job", Faction = "Faction", FactionAugmentations = "Faction Augmentations", ScriptEditor = "Script Editor", diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 4cb920d32..74ac4927c 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -337,7 +337,7 @@ export function GameRoot(): React.ReactElement { mainPage = ; break; case Page.Location: { - mainPage = ; + mainPage = ; break; } case Page.Options: { diff --git a/src/ui/Router.ts b/src/ui/Router.ts index 89ed12f68..2cf895f97 100644 --- a/src/ui/Router.ts +++ b/src/ui/Router.ts @@ -14,8 +14,6 @@ export type PageContext = 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 @@ -33,7 +31,6 @@ export type PageContext = T extends ComplexPage.BitVerse export type PageWithContext = | ({ page: ComplexPage.BitVerse } & PageContext) | ({ page: ComplexPage.Infiltration } & PageContext) - | ({ page: ComplexPage.Job } & PageContext) | ({ page: ComplexPage.Faction } & PageContext) | ({ page: ComplexPage.FactionAugmentations } & PageContext) | ({ page: ComplexPage.ScriptEditor } & PageContext) diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index cfa7db910..e16f6a250 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -428,11 +428,11 @@ export function WorkInProgressRoot(): React.ReactElement { buttons: { cancel: () => { Player.finishWork(true); - Router.toPage(Page.Job, { location: Locations[comp.name] }); + Router.toPage(Page.Job); }, unfocus: () => { Player.stopFocusing(); - Router.toPage(Page.Job, { location: Locations[comp.name] }); + Router.toPage(Page.Job); }, }, title: ( diff --git a/src/utils/KeyBindingUtils.ts b/src/utils/KeyBindingUtils.ts index 750f24d56..4044be4bb 100644 --- a/src/utils/KeyBindingUtils.ts +++ b/src/utils/KeyBindingUtils.ts @@ -28,7 +28,7 @@ export const GoToPageKeyBindingTypes = [ SimplePage.Hacknet, SimplePage.City, SimplePage.Travel, - ComplexPage.Job, + SimplePage.Job, SimplePage.StockMarket, SimplePage.Go, SimplePage.Milestones, @@ -165,7 +165,7 @@ export const DefaultKeyBindings: Record