mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-22 06:02:26 +01:00
UI: Break SidebarRoot into smaller components, and memoize (#246)
This commit is contained in:
parent
8d793ea271
commit
a46d34bd60
90
src/Sidebar/ui/SidebarAccordion.tsx
Normal file
90
src/Sidebar/ui/SidebarAccordion.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import React, { useMemo, useState } from "react";
|
||||||
|
import Collapse from "@mui/material/Collapse";
|
||||||
|
import ListItem from "@mui/material/ListItem";
|
||||||
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||||
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
|
||||||
|
import { SidebarItem, ICreateProps as IItemProps } from "./SidebarItem";
|
||||||
|
import type { Page } from "../../ui/Router";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
key_: string;
|
||||||
|
page: Page;
|
||||||
|
clickPage: (page: Page) => void;
|
||||||
|
flash: Page | null;
|
||||||
|
items: (IItemProps | boolean)[];
|
||||||
|
icon: React.ReactElement;
|
||||||
|
sidebarOpen: boolean;
|
||||||
|
classes: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't useCallback for this, because in the items map it would be
|
||||||
|
// called a changing number of times, and hooks can't be called in loops. So
|
||||||
|
// we set up this explicit cache of function objects instead.
|
||||||
|
// This is at module scope, because it's fine for all Accordions to share the
|
||||||
|
// same cache.
|
||||||
|
// WeakMap prevents memory leaks. We won't drop slices of the cache too soon,
|
||||||
|
// because the fn keys are themselves memoized elsewhere, which keeps them
|
||||||
|
// alive and thus keeps the WeakMap entries alive.
|
||||||
|
const clickFnCache = new WeakMap();
|
||||||
|
function getClickFn(toWrap: (page: Page) => void, page: Page) {
|
||||||
|
let first = clickFnCache.get(toWrap);
|
||||||
|
if (first === undefined) {
|
||||||
|
first = {};
|
||||||
|
clickFnCache.set(toWrap, first);
|
||||||
|
}
|
||||||
|
// Short-circuit: Avoid assign/eval of function on found
|
||||||
|
return (first[page] ??= () => toWrap(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can't be usefully memoized, because props.items is a new array every time.
|
||||||
|
export function SidebarAccordion(props: IProps): React.ReactElement {
|
||||||
|
const [open, setOpen] = useState(true);
|
||||||
|
// Obnoxious, because we can't modify props at all.
|
||||||
|
const li_classes = useMemo(() => ({ root: props.classes.listitem }), [props.classes.listitem]);
|
||||||
|
const icon = Object.assign({}, props.icon);
|
||||||
|
icon.props = Object.assign({ color: "primary" }, icon.props);
|
||||||
|
|
||||||
|
// Explicitily useMemo() to save rerendering deep chunks of this tree.
|
||||||
|
// memo() can't be (easily) used on components like <List>, because the
|
||||||
|
// props.children array will be a different object every time.
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{useMemo(
|
||||||
|
() => (
|
||||||
|
<ListItem classes={li_classes} button onClick={() => setOpen((open) => !open)}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Tooltip title={!props.sidebarOpen ? props.key_ : ""} children={icon} />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary={<Typography>{props.key_}</Typography>} />
|
||||||
|
{open ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />}
|
||||||
|
</ListItem>
|
||||||
|
),
|
||||||
|
[li_classes, props.sidebarOpen, props.key_, open, props.icon.type],
|
||||||
|
)}
|
||||||
|
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||||
|
{props.items.map((x) => {
|
||||||
|
if (typeof x !== "object") return null;
|
||||||
|
const { key_, icon, count, active } = x;
|
||||||
|
return (
|
||||||
|
<SidebarItem
|
||||||
|
key={key_}
|
||||||
|
key_={key_}
|
||||||
|
icon={icon}
|
||||||
|
count={count}
|
||||||
|
active={active ?? props.page === key_}
|
||||||
|
clickFn={getClickFn(props.clickPage, key_)}
|
||||||
|
flash={props.flash === key_}
|
||||||
|
classes={props.classes}
|
||||||
|
sidebarOpen={props.sidebarOpen}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Collapse>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
53
src/Sidebar/ui/SidebarItem.tsx
Normal file
53
src/Sidebar/ui/SidebarItem.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import React, { memo } from "react";
|
||||||
|
import Badge from "@mui/material/Badge";
|
||||||
|
import ListItem from "@mui/material/ListItem";
|
||||||
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
|
import type { Page } from "../../ui/Router";
|
||||||
|
|
||||||
|
export interface ICreateProps {
|
||||||
|
key_: Page;
|
||||||
|
icon: React.ReactElement;
|
||||||
|
count?: number;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IProps extends ICreateProps {
|
||||||
|
clickFn: () => void;
|
||||||
|
flash: boolean;
|
||||||
|
classes: any;
|
||||||
|
sidebarOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SidebarItem = memo(function (props: IProps): React.ReactElement {
|
||||||
|
// Use icon as a template. (We can't modify props)
|
||||||
|
const icon: React.ReactElement = {
|
||||||
|
type: props.icon.type,
|
||||||
|
key: props.icon.key,
|
||||||
|
props: {
|
||||||
|
color: props.flash ? "error" : !props.active ? "secondary" : "primary",
|
||||||
|
...props.icon.props,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
classes={{ root: props.classes.listitem }}
|
||||||
|
button
|
||||||
|
key={props.key_}
|
||||||
|
className={props.active ? props.classes.active : ""}
|
||||||
|
onClick={props.clickFn}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Badge badgeContent={(props.count ?? 0) > 0 ? props.count : undefined} color="error">
|
||||||
|
<Tooltip title={!props.sidebarOpen ? props.key_ : ""} children={icon} />
|
||||||
|
</Badge>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
<Typography color={props.flash ? "error" : !props.active ? "secondary" : "primary"} children={props.key_} />
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
});
|
@ -1,6 +1,5 @@
|
|||||||
import React, { useCallback, useState, useEffect } from "react";
|
import React, { useMemo, useCallback, useState, useEffect } from "react";
|
||||||
import { KEYCODE } from "../../utils/helpers/keyCodes";
|
import { KEYCODE } from "../../utils/helpers/keyCodes";
|
||||||
import clsx from "clsx";
|
|
||||||
import { styled, Theme, CSSObject } from "@mui/material/styles";
|
import { styled, Theme, CSSObject } 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";
|
||||||
@ -14,8 +13,6 @@ import ListItem from "@mui/material/ListItem";
|
|||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Collapse from "@mui/material/Collapse";
|
|
||||||
import Badge from "@mui/material/Badge";
|
|
||||||
|
|
||||||
import ComputerIcon from "@mui/icons-material/Computer";
|
import ComputerIcon from "@mui/icons-material/Computer";
|
||||||
import LastPageIcon from "@mui/icons-material/LastPage"; // Terminal
|
import LastPageIcon from "@mui/icons-material/LastPage"; // Terminal
|
||||||
@ -42,11 +39,10 @@ import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; // Achievements
|
|||||||
import AccountBoxIcon from "@mui/icons-material/AccountBox";
|
import AccountBoxIcon from "@mui/icons-material/AccountBox";
|
||||||
import PublicIcon from "@mui/icons-material/Public";
|
import PublicIcon from "@mui/icons-material/Public";
|
||||||
import LiveHelpIcon from "@mui/icons-material/LiveHelp";
|
import LiveHelpIcon from "@mui/icons-material/LiveHelp";
|
||||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
||||||
|
|
||||||
import { Router } from "../../ui/GameRoot";
|
import { Router } from "../../ui/GameRoot";
|
||||||
import { Page, SimplePage } from "../../ui/Router";
|
import { Page, SimplePage } from "../../ui/Router";
|
||||||
|
import { SidebarAccordion } from "./SidebarAccordion";
|
||||||
import { Player } from "@player";
|
import { Player } from "@player";
|
||||||
import { CONSTANTS } from "../../Constants";
|
import { CONSTANTS } from "../../Constants";
|
||||||
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
|
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
|
||||||
@ -59,6 +55,39 @@ import { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
|
|||||||
import { hash } from "../../hash/hash";
|
import { hash } from "../../hash/hash";
|
||||||
import { Locations } from "../../Locations/Locations";
|
import { Locations } from "../../Locations/Locations";
|
||||||
|
|
||||||
|
// All icon instances need to be constant, so they have stable object identity.
|
||||||
|
// Otherwise, the memoization of all the higher-level components doesn't work.
|
||||||
|
const computerIcon = <ComputerIcon />;
|
||||||
|
const lastPageIcon = <LastPageIcon />;
|
||||||
|
const createIcon = <CreateIcon />;
|
||||||
|
const storageIcon = <StorageIcon />;
|
||||||
|
const bugReportIcon = <BugReportIcon />;
|
||||||
|
const equalizerIcon = <EqualizerIcon />;
|
||||||
|
const contactsIcon = <ContactsIcon />;
|
||||||
|
const doubleArrowIcon = <DoubleArrowIcon style={{ transform: "rotate(-90deg)" }} />;
|
||||||
|
const accountTreeIcon = <AccountTreeIcon />;
|
||||||
|
const peopleAltIcon = <PeopleAltIcon />;
|
||||||
|
const locationCityIcon = <LocationCityIcon />;
|
||||||
|
const airplanemodeActiveIcon = <AirplanemodeActiveIcon />;
|
||||||
|
const workIcon = <WorkIcon />;
|
||||||
|
const trendingUpIcon = <TrendingUpIcon />;
|
||||||
|
const formatBoldIcon = <FormatBoldIcon />;
|
||||||
|
const businessIcon = <BusinessIcon />;
|
||||||
|
const sportsMmaIcon = <SportsMmaIcon />;
|
||||||
|
const checkIcon = <CheckIcon />;
|
||||||
|
const helpIcon = <HelpIcon />;
|
||||||
|
const settingsIcon = <SettingsIcon />;
|
||||||
|
const developerBoardIcon = <DeveloperBoardIcon />;
|
||||||
|
const emojiEventsIcon = <EmojiEventsIcon />;
|
||||||
|
const accountBoxIcon = <AccountBoxIcon />;
|
||||||
|
const publicIcon = <PublicIcon />;
|
||||||
|
const liveHelpIcon = <LiveHelpIcon />;
|
||||||
|
const chevronLeftIcon = <ChevronLeftIcon color="primary" />;
|
||||||
|
const chevronRightIcon = <ChevronRightIcon color="primary" />;
|
||||||
|
|
||||||
|
// Use constant Dividers just for performance
|
||||||
|
const divider = <Divider />;
|
||||||
|
|
||||||
const openedMixin = (theme: Theme): CSSObject => ({
|
const openedMixin = (theme: Theme): CSSObject => ({
|
||||||
width: theme.spacing(31),
|
width: theme.spacing(31),
|
||||||
transition: theme.transitions.create("width", {
|
transition: theme.transitions.create("width", {
|
||||||
@ -118,11 +147,6 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [hackingOpen, setHackingOpen] = useState(true);
|
|
||||||
const [characterOpen, setCharacterOpen] = useState(true);
|
|
||||||
const [worldOpen, setWorldOpen] = useState(true);
|
|
||||||
const [helpOpen, setHelpOpen] = useState(true);
|
|
||||||
|
|
||||||
let flash: Page | null = null;
|
let flash: Page | null = null;
|
||||||
switch (ITutorial.currStep) {
|
switch (ITutorial.currStep) {
|
||||||
case iTutorialSteps.CharacterGoToTerminalPage:
|
case iTutorialSteps.CharacterGoToTerminalPage:
|
||||||
@ -270,543 +294,112 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
Settings.IsSidebarOpened = !old;
|
Settings.IsSidebarOpened = !old;
|
||||||
return !old;
|
return !old;
|
||||||
});
|
});
|
||||||
|
const li_classes = useMemo(() => ({ root: classes.listitem }), [classes.listitem]);
|
||||||
|
|
||||||
|
// Explicitily useMemo() to save rerendering deep chunks of this tree.
|
||||||
|
// memo() can't be (easily) used on components like <List>, because the
|
||||||
|
// props.children array will be a different object every time.
|
||||||
return (
|
return (
|
||||||
<Drawer open={open} anchor="left" variant="permanent">
|
<Drawer open={open} anchor="left" variant="permanent">
|
||||||
<ListItem classes={{ root: classes.listitem }} button onClick={toggleDrawer}>
|
{useMemo(
|
||||||
<ListItemIcon>
|
() => (
|
||||||
{!open ? <ChevronRightIcon color="primary" /> : <ChevronLeftIcon color="primary" />}
|
<ListItem classes={li_classes} button onClick={toggleDrawer}>
|
||||||
</ListItemIcon>
|
<ListItemIcon>{!open ? chevronRightIcon : chevronLeftIcon}</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<Tooltip title={hash()}>
|
<Tooltip title={hash()}>
|
||||||
<Typography>Bitburner v{CONSTANTS.VersionString}</Typography>
|
<Typography>Bitburner v{CONSTANTS.VersionString}</Typography>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider />
|
),
|
||||||
|
[li_classes, open],
|
||||||
|
)}
|
||||||
|
{divider}
|
||||||
<List>
|
<List>
|
||||||
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHackingOpen((old) => !old)}>
|
<SidebarAccordion
|
||||||
<ListItemIcon>
|
key_="Hacking"
|
||||||
<Tooltip title={!open ? "Hacking" : ""}>
|
page={props.page}
|
||||||
<ComputerIcon color="primary" />
|
clickPage={clickPage}
|
||||||
</Tooltip>
|
flash={flash}
|
||||||
</ListItemIcon>
|
icon={computerIcon}
|
||||||
<ListItemText primary={<Typography>Hacking</Typography>} />
|
sidebarOpen={open}
|
||||||
{hackingOpen ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />}
|
classes={classes}
|
||||||
</ListItem>
|
items={[
|
||||||
<Collapse in={hackingOpen} timeout="auto" unmountOnExit>
|
{ key_: Page.Terminal, icon: lastPageIcon },
|
||||||
<List>
|
{ key_: Page.ScriptEditor, icon: createIcon },
|
||||||
<ListItem
|
{ key_: Page.ActiveScripts, icon: storageIcon },
|
||||||
classes={{ root: classes.listitem }}
|
{ key_: Page.CreateProgram, icon: bugReportIcon, count: programCount },
|
||||||
button
|
canStaneksGift && { key_: Page.StaneksGift, icon: developerBoardIcon },
|
||||||
key={"Terminal"}
|
]}
|
||||||
className={clsx({
|
/>
|
||||||
[classes.active]: props.page === Page.Terminal,
|
{divider}
|
||||||
})}
|
<SidebarAccordion
|
||||||
onClick={() => clickPage(Page.Terminal)}
|
key_="Character"
|
||||||
>
|
page={props.page}
|
||||||
<ListItemIcon>
|
clickPage={clickPage}
|
||||||
<Tooltip title={!open ? "Terminal" : ""}>
|
flash={flash}
|
||||||
<LastPageIcon
|
icon={accountBoxIcon}
|
||||||
color={flash === Page.Terminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}
|
sidebarOpen={open}
|
||||||
/>
|
classes={classes}
|
||||||
</Tooltip>
|
items={[
|
||||||
</ListItemIcon>
|
{ key_: Page.Stats, icon: equalizerIcon },
|
||||||
<ListItemText>
|
canOpenFactions && {
|
||||||
<Typography
|
key_: Page.Factions,
|
||||||
color={flash === Page.Terminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}
|
icon: contactsIcon,
|
||||||
>
|
active: [Page.Factions as Page, Page.Faction].includes(props.page),
|
||||||
Terminal
|
count: invitationsCount,
|
||||||
</Typography>
|
},
|
||||||
</ListItemText>
|
canOpenAugmentations && {
|
||||||
</ListItem>
|
key_: Page.Augmentations,
|
||||||
<ListItem
|
icon: doubleArrowIcon,
|
||||||
classes={{ root: classes.listitem }}
|
count: augmentationCount,
|
||||||
button
|
},
|
||||||
key={"Script Editor"}
|
{ key_: Page.Hacknet, icon: accountTreeIcon },
|
||||||
className={clsx({
|
canOpenSleeves && { key_: Page.Sleeves, icon: peopleAltIcon },
|
||||||
[classes.active]: props.page === Page.ScriptEditor,
|
]}
|
||||||
})}
|
/>
|
||||||
onClick={() => clickPage(Page.ScriptEditor)}
|
{divider}
|
||||||
>
|
<SidebarAccordion
|
||||||
<ListItemIcon>
|
key_="World"
|
||||||
<Tooltip title={!open ? "Script Editor" : ""}>
|
page={props.page}
|
||||||
<CreateIcon color={props.page !== Page.ScriptEditor ? "secondary" : "primary"} />
|
clickPage={clickPage}
|
||||||
</Tooltip>
|
flash={flash}
|
||||||
</ListItemIcon>
|
icon={publicIcon}
|
||||||
<ListItemText>
|
sidebarOpen={open}
|
||||||
<Typography color={props.page !== Page.ScriptEditor ? "secondary" : "primary"}>
|
classes={classes}
|
||||||
Script Editor
|
items={[
|
||||||
</Typography>
|
{
|
||||||
</ListItemText>
|
key_: Page.City,
|
||||||
</ListItem>
|
icon: locationCityIcon,
|
||||||
<ListItem
|
active: [Page.City as Page, Page.Grafting, Page.Location].includes(props.page),
|
||||||
classes={{ root: classes.listitem }}
|
},
|
||||||
button
|
{ key_: Page.Travel, icon: airplanemodeActiveIcon },
|
||||||
key={"Active Scripts"}
|
canJob && { key_: Page.Job, icon: workIcon },
|
||||||
className={clsx({
|
canStockMarket && { key_: Page.StockMarket, icon: trendingUpIcon },
|
||||||
[classes.active]: props.page === Page.ActiveScripts,
|
canBladeburner && { key_: Page.Bladeburner, icon: formatBoldIcon },
|
||||||
})}
|
canCorporation && { key_: Page.Corporation, icon: businessIcon },
|
||||||
onClick={() => clickPage(Page.ActiveScripts)}
|
canGang && { key_: Page.Gang, icon: sportsMmaIcon },
|
||||||
>
|
]}
|
||||||
<ListItemIcon>
|
/>
|
||||||
<Tooltip title={!open ? "Active Scripts" : ""}>
|
{divider}
|
||||||
<StorageIcon
|
<SidebarAccordion
|
||||||
color={
|
key_="Help"
|
||||||
flash === Page.ActiveScripts
|
page={props.page}
|
||||||
? "error"
|
clickPage={clickPage}
|
||||||
: props.page !== Page.ActiveScripts
|
flash={flash}
|
||||||
? "secondary"
|
icon={liveHelpIcon}
|
||||||
: "primary"
|
sidebarOpen={open}
|
||||||
}
|
classes={classes}
|
||||||
/>
|
items={[
|
||||||
</Tooltip>
|
{ key_: Page.Milestones, icon: checkIcon },
|
||||||
</ListItemIcon>
|
{ key_: Page.Tutorial, icon: helpIcon },
|
||||||
<ListItemText>
|
{ key_: Page.Achievements, icon: emojiEventsIcon },
|
||||||
<Typography
|
{ key_: Page.Options, icon: settingsIcon },
|
||||||
color={
|
process.env.NODE_ENV === "development" && { key_: Page.DevMenu, icon: developerBoardIcon },
|
||||||
flash === Page.ActiveScripts ? "error" : props.page !== Page.ActiveScripts ? "secondary" : "primary"
|
]}
|
||||||
}
|
/>
|
||||||
>
|
|
||||||
Active Scripts
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Create Program"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.CreateProgram,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.CreateProgram)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Badge badgeContent={programCount > 0 ? programCount : undefined} color="error">
|
|
||||||
<Tooltip title={!open ? "Create Program" : ""}>
|
|
||||||
<BugReportIcon color={props.page !== Page.CreateProgram ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</Badge>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.CreateProgram ? "secondary" : "primary"}>
|
|
||||||
Create Program
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
{canStaneksGift && (
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Staneks Gift"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.StaneksGift,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.StaneksGift)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Stanek's Gift" : ""}>
|
|
||||||
<DeveloperBoardIcon color={props.page !== Page.StaneksGift ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.StaneksGift ? "secondary" : "primary"}>
|
|
||||||
Stanek's Gift
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Collapse>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
<ListItem classes={{ root: classes.listitem }} button onClick={() => setCharacterOpen((old) => !old)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Character" : ""}>
|
|
||||||
<AccountBoxIcon color="primary" />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={<Typography>Character</Typography>} />
|
|
||||||
{characterOpen ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />}
|
|
||||||
</ListItem>
|
|
||||||
<Collapse in={characterOpen} timeout="auto" unmountOnExit>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Stats"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Stats,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Stats)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Stats" : ""}>
|
|
||||||
<EqualizerIcon
|
|
||||||
color={flash === Page.Stats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={flash === Page.Stats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"}>
|
|
||||||
Stats
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
{canOpenFactions && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Factions"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: [Page.Factions, Page.Faction].includes(props.page),
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Factions)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Badge badgeContent={invitationsCount !== 0 ? invitationsCount : undefined} color="error">
|
|
||||||
<Tooltip title={!open ? "Factions" : ""}>
|
|
||||||
<ContactsIcon
|
|
||||||
color={![Page.Factions, Page.Faction].includes(props.page) ? "secondary" : "primary"}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Badge>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={![Page.Factions, Page.Faction].includes(props.page) ? "secondary" : "primary"}>
|
|
||||||
Factions
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
{canOpenAugmentations && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Augmentations"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Augmentations,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Augmentations)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Badge badgeContent={augmentationCount !== 0 ? augmentationCount : undefined} color="error">
|
|
||||||
<Tooltip title={!open ? "Augmentations" : ""}>
|
|
||||||
<DoubleArrowIcon
|
|
||||||
style={{ transform: "rotate(-90deg)" }}
|
|
||||||
color={props.page !== Page.Augmentations ? "secondary" : "primary"}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Badge>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Augmentations ? "secondary" : "primary"}>
|
|
||||||
Augmentations
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Hacknet"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Hacknet,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Hacknet)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Hacknet" : ""}>
|
|
||||||
<AccountTreeIcon
|
|
||||||
color={flash === Page.Hacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography
|
|
||||||
color={flash === Page.Hacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"}
|
|
||||||
>
|
|
||||||
Hacknet
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
{canOpenSleeves && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Sleeves"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Sleeves,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Sleeves)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Sleeves" : ""}>
|
|
||||||
<PeopleAltIcon color={props.page !== Page.Sleeves ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Sleeves ? "secondary" : "primary"}>Sleeves</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</Collapse>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
<ListItem classes={{ root: classes.listitem }} button onClick={() => setWorldOpen((old) => !old)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "World" : ""}>
|
|
||||||
<PublicIcon color="primary" />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={<Typography>World</Typography>} />
|
|
||||||
{worldOpen ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />}
|
|
||||||
</ListItem>
|
|
||||||
<Collapse in={worldOpen} timeout="auto" unmountOnExit>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"City"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]:
|
|
||||||
props.page === Page.City || props.page === Page.Grafting || props.page === Page.Location,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.City)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "City" : ""}>
|
|
||||||
<LocationCityIcon
|
|
||||||
color={flash === Page.City ? "error" : props.page !== Page.City ? "secondary" : "primary"}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={flash === Page.City ? "error" : props.page !== Page.City ? "secondary" : "primary"}>
|
|
||||||
City
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Travel"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Travel,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Travel)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Travel" : ""}>
|
|
||||||
<AirplanemodeActiveIcon color={props.page !== Page.Travel ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Travel ? "secondary" : "primary"}>Travel</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
{canJob && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Job"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Job,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Job)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Job" : ""}>
|
|
||||||
<WorkIcon color={props.page !== Page.Job ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Job ? "secondary" : "primary"}>Job</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
{canStockMarket && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Stock Market"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.StockMarket,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.StockMarket)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Stock Market" : ""}>
|
|
||||||
<TrendingUpIcon color={props.page !== Page.StockMarket ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.StockMarket ? "secondary" : "primary"}>Stock Market</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
{canBladeburner && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Bladeburner"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Bladeburner,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Bladeburner)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Bladeburner" : ""}>
|
|
||||||
<FormatBoldIcon color={props.page !== Page.Bladeburner ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Bladeburner ? "secondary" : "primary"}>Bladeburner</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
{canCorporation && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Corp"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Corporation,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Corporation)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Corp" : ""}>
|
|
||||||
<BusinessIcon color={props.page !== Page.Corporation ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Corporation ? "secondary" : "primary"}>Corp</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
{canGang && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Gang"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Gang,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Gang)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Gang" : ""}>
|
|
||||||
<SportsMmaIcon color={props.page !== Page.Gang ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Gang ? "secondary" : "primary"}>Gang</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</Collapse>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHelpOpen((old) => !old)}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Help" : ""}>
|
|
||||||
<LiveHelpIcon color="primary" />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary={<Typography>Help</Typography>} />
|
|
||||||
{helpOpen ? <ExpandLessIcon color="primary" /> : <ExpandMoreIcon color="primary" />}
|
|
||||||
</ListItem>
|
|
||||||
<Collapse in={helpOpen} timeout="auto" unmountOnExit>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Milestones"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Milestones,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Milestones)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Milestones" : ""}>
|
|
||||||
<CheckIcon color={props.page !== Page.Milestones ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Milestones ? "secondary" : "primary"}>Milestones</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Tutorial"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Tutorial,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Tutorial)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Tutorial" : ""}>
|
|
||||||
<HelpIcon
|
|
||||||
color={flash === Page.Tutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography
|
|
||||||
color={flash === Page.Tutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"}
|
|
||||||
>
|
|
||||||
Tutorial
|
|
||||||
</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Achievements"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Achievements,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Achievements)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Achievements" : ""}>
|
|
||||||
<EmojiEventsIcon color={props.page !== Page.Achievements ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Achievements ? "secondary" : "primary"}>Achievements</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
<ListItem
|
|
||||||
button
|
|
||||||
key={"Options"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.Options,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.Options)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Options" : ""}>
|
|
||||||
<SettingsIcon color={props.page !== Page.Options ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.Options ? "secondary" : "primary"}>Options</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
{process.env.NODE_ENV === "development" && (
|
|
||||||
<ListItem
|
|
||||||
classes={{ root: classes.listitem }}
|
|
||||||
button
|
|
||||||
key={"Dev"}
|
|
||||||
className={clsx({
|
|
||||||
[classes.active]: props.page === Page.DevMenu,
|
|
||||||
})}
|
|
||||||
onClick={() => clickPage(Page.DevMenu)}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title={!open ? "Dev" : ""}>
|
|
||||||
<DeveloperBoardIcon color={props.page !== Page.DevMenu ? "secondary" : "primary"} />
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText>
|
|
||||||
<Typography color={props.page !== Page.DevMenu ? "secondary" : "primary"}>Dev</Typography>
|
|
||||||
</ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
)}
|
|
||||||
</Collapse>
|
|
||||||
</List>
|
</List>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user