Merge pull request #1226 from threehams/corp-overview-perf

Stop remounting components on every render on corp overview
This commit is contained in:
hydroflame 2021-09-13 00:22:01 -04:00 committed by GitHub
commit 38cf4bd3cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 327 additions and 309 deletions

@ -63,7 +63,6 @@ module.exports = {
"id-blacklist": ["error"], "id-blacklist": ["error"],
"id-length": ["off"], "id-length": ["off"],
"id-match": ["error"], "id-match": ["error"],
"implicit-arrow-linebreak": ["error", "beside"],
indent: ["off"], indent: ["off"],
"indent-legacy": ["off"], "indent-legacy": ["off"],
"init-declarations": ["off"], "init-declarations": ["off"],

@ -133,6 +133,7 @@
} }
.cmpy-mgmt-upgrade-div { .cmpy-mgmt-upgrade-div {
text-align: left;
display: inline-block; display: inline-block;
border: 1px solid #fff; border: 1px solid #fff;
margin: 2px; margin: 2px;

@ -40,9 +40,9 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
} }
return ( return (
<div className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}> <button className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}>
{text} {text}
<span className={"tooltiptext"}>{tooltip}</span> <span className={"tooltiptext"}>{tooltip}</span>
</div> </button>
); );
} }

@ -27,128 +27,100 @@ interface IProps {
player: IPlayer; player: IPlayer;
rerender: () => void; rerender: () => void;
} }
export function Overview({ corp, player, rerender }: IProps): React.ReactElement {
const profit: number = corp.revenue.minus(corp.expenses).toNumber();
export function Overview(props: IProps): React.ReactElement {
// Generic Function for Creating a button
interface ICreateButtonProps {
text: string;
class?: string;
className?: string;
display?: string;
tooltip?: string;
onClick?: (event: React.MouseEvent) => void;
}
function Button(props: ICreateButtonProps): React.ReactElement {
let className = props.className ? props.className : "std-button";
const hasTooltip = props.tooltip != null;
if (hasTooltip) className += " tooltip";
return (
<a className={className} onClick={props.onClick} style={{ display: props.display ? props.display : "block" }}>
{props.text}
{hasTooltip && <span className={"tooltiptext"}>{props.tooltip}</span>}
</a>
);
}
function openBribeFactionPopup(): void {
const popupId = "corp-bribe-popup";
createPopup(popupId, BribeFactionPopup, {
player: props.player,
popupId: popupId,
corp: props.corp,
});
}
const profit: number = props.corp.revenue.minus(props.corp.expenses).toNumber();
function DividendsStats(): React.ReactElement {
if (props.corp.dividendPercentage <= 0 || profit <= 0) return <></>;
const totalDividends = (props.corp.dividendPercentage / 100) * profit;
const retainedEarnings = profit - totalDividends;
const dividendsPerShare = totalDividends / props.corp.totalShares;
const playerEarnings = props.corp.numShares * dividendsPerShare;
return (
<>
Retained Profits (after dividends): <Money money={retainedEarnings} /> / s<br />
<br />
Dividend Percentage: {numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}
<br />
Dividends per share: <Money money={dividendsPerShare} /> / s<br />
Your earnings as a shareholder (Pre-Tax): <Money money={playerEarnings} /> / s<br />
Dividend Tax Rate: {props.corp.dividendTaxPercentage}%<br />
Your earnings as a shareholder (Post-Tax):{" "}
<Money money={playerEarnings * (1 - props.corp.dividendTaxPercentage / 100)} /> / s<br />
<br />
</>
);
}
function Mult(props: { name: string; mult: number }): React.ReactElement {
if (props.mult <= 1) return <></>;
return ( return (
<div>
<p> <p>
{props.name} Total Funds: <Money money={corp.funds.toNumber()} />
{numeralWrapper.format(props.mult, "0.000")} <br />
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br />
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s
<br />
Total Profits: <Money money={profit} /> / s<br />
<DividendsStats corp={corp} profit={profit} />
Publicly Traded: {corp.public ? "Yes" : "No"}
<br />
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")}
<br />
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"}
<br /> <br />
</p> </p>
); <p className="tooltip">
} Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
<span className="tooltiptext">
// Returns a string with general information about Corporation Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")}
function BonusTime(): React.ReactElement {
const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle;
if (storedTime <= 15000) return <></>;
return (
<p>
Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}
<br />
<br /> <br />
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")}
</span>
</p> </p>
); <br />
} <br />
<Mult name="Production Multiplier: " mult={corp.getProductionMultiplier()} />
function BribeButton(): React.ReactElement { <Mult name="Storage Multiplier: " mult={corp.getStorageMultiplier()} />
const canBribe = props.corp.determineValuation() >= CorporationConstants.BribeThreshold || true; <Mult name="Advertising Multiplier: " mult={corp.getAdvertisingMultiplier()} />
const bribeFactionsClass = canBribe ? "a-link-button" : "a-link-button-inactive"; <Mult name="Empl. Creativity Multiplier: " mult={corp.getEmployeeCreMultiplier()} />
return ( <Mult name="Empl. Charisma Multiplier: " mult={corp.getEmployeeChaMultiplier()} />
<Mult name="Empl. Intelligence Multiplier: " mult={corp.getEmployeeIntMultiplier()} />
<Mult name="Empl. Efficiency Multiplier: " mult={corp.getEmployeeEffMultiplier()} />
<Mult name="Sales Multiplier: " mult={corp.getSalesMultiplier()} />
<Mult name="Scientific Research Multiplier: " mult={corp.getScientificResearchMultiplier()} />
<br />
<BonusTime corp={corp} />
<div>
<Button <Button
className={bribeFactionsClass} className="a-link-button"
display="inline-block" display="inline-block"
onClick={openBribeFactionPopup} onClick={() => corp.getStarterGuide(player)}
text="Bribe Factions" text="Getting Started Guide"
tooltip={ tooltip={
canBribe "Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' " +
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation" "This is a .lit file that guides you through the beginning of setting up a Corporation and " +
: "Your Corporation is not powerful enough to bribe Faction leaders" "provides some tips/pointers for helping you get started with managing it."
} }
/> />
{corp.public ? (
<PublicButtons corp={corp} player={player} rerender={rerender} />
) : (
<PrivateButtons corp={corp} player={player} rerender={rerender} />
)}
<BribeButton corp={corp} player={player} />
</div>
<br />
<Upgrades corp={corp} player={player} rerender={rerender} />
</div>
); );
} }
interface IPrivateButtonsProps {
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
// Render the buttons for when your Corporation is still private
function PrivateButtons({ corp, player, rerender }: IPrivateButtonsProps): React.ReactElement {
function openFindInvestorsPopup(): void { function openFindInvestorsPopup(): void {
const popupId = "cmpy-mgmt-find-investors-popup"; const popupId = "cmpy-mgmt-find-investors-popup";
createPopup(popupId, FindInvestorsPopup, { createPopup(popupId, FindInvestorsPopup, {
rerender: props.rerender, rerender,
player: props.player, player,
popupId: popupId, popupId,
corp: props.corp, corp: corp,
}); });
} }
function openGoPublicPopup(): void { function openGoPublicPopup(): void {
const popupId = "cmpy-mgmt-go-public-popup"; const popupId = "cmpy-mgmt-go-public-popup";
createPopup(popupId, GoPublicPopup, { createPopup(popupId, GoPublicPopup, {
rerender: props.rerender, rerender,
player: props.player, player,
popupId: popupId, popupId,
corp: props.corp, corp: corp,
}); });
} }
// Render the buttons for when your Corporation is still private const fundingAvailable = corp.fundingRound < 4;
function PrivateButtons(): React.ReactElement {
const fundingAvailable = props.corp.fundingRound < 4;
const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive"; const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive";
const findInvestorsTooltip = fundingAvailable const findInvestorsTooltip = fundingAvailable
? "Search for private investors who will give you startup funding in exchangefor equity (stock shares) in your company" ? "Search for private investors who will give you startup funding in exchangefor equity (stock shares) in your company"
@ -177,48 +149,47 @@ export function Overview(props: IProps): React.ReactElement {
<br /> <br />
</> </>
); );
}
interface IUpgradeProps {
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
// Render the UI for Corporation upgrades
function Upgrades({ corp, player, rerender }: IUpgradeProps): React.ReactElement {
// Don't show upgrades
if (corp.divisions.length <= 0) {
return <h1>Unlock upgrades after creating your first division</h1>;
} }
function openSellSharesPopup(): void { return (
const popupId = "cmpy-mgmt-sell-shares-popup"; <div className={"cmpy-mgmt-upgrade-container"}>
createPopup(popupId, SellSharesPopup, { <h1 className={"cmpy-mgmt-upgrade-header"}> Unlocks </h1>
corp: props.corp, {Object.values(CorporationUnlockUpgrades)
player: props.player, .filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
popupId: popupId, .map((upgrade: CorporationUnlockUpgrade) => (
rerender: props.rerender, <UnlockUpgrade rerender={rerender} player={player} corp={corp} upgradeData={upgrade} key={upgrade[0]} />
}); ))}
}
function openBuybackSharesPopup(): void { <h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1>
const popupId = "corp-buyback-shares-popup"; {corp.upgrades
createPopup(popupId, BuybackSharesPopup, { .map((level: number, i: number) => CorporationUpgrades[i])
rerender: props.rerender, .map((upgrade: CorporationUpgrade) => (
player: props.player, <LevelableUpgrade rerender={rerender} player={player} corp={corp} upgrade={upgrade} key={upgrade[0]} />
popupId: popupId, ))}
corp: props.corp, </div>
}); );
} }
function openIssueNewSharesPopup(): void { interface IPublicButtonsProps {
const popupId = "cmpy-mgmt-issue-new-shares-popup"; corp: ICorporation;
createPopup(popupId, IssueNewSharesPopup, { player: IPlayer;
popupId: popupId, rerender: () => void;
corp: props.corp, }
});
}
function openIssueDividendsPopup(): void {
const popupId = "cmpy-mgmt-issue-dividends-popup";
createPopup(popupId, IssueDividendsPopup, {
popupId: popupId,
corp: props.corp,
});
}
// Render the buttons for when your Corporation has gone public
function PublicButtons(): React.ReactElement {
const corp = props.corp;
// Render the buttons for when your Corporation has gone public
function PublicButtons({ corp, player, rerender }: IPublicButtonsProps): React.ReactElement {
const sellSharesOnCd = corp.shareSaleCooldown > 0; const sellSharesOnCd = corp.shareSaleCooldown > 0;
const sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button"; const sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button";
const sellSharesTooltip = sellSharesOnCd const sellSharesTooltip = sellSharesOnCd
@ -233,6 +204,42 @@ export function Overview(props: IProps): React.ReactElement {
? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown) ? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown)
: "Issue new equity shares to raise capital."; : "Issue new equity shares to raise capital.";
function openSellSharesPopup(): void {
const popupId = "cmpy-mgmt-sell-shares-popup";
createPopup(popupId, SellSharesPopup, {
corp: corp,
player,
popupId,
rerender,
});
}
function openBuybackSharesPopup(): void {
const popupId = "corp-buyback-shares-popup";
createPopup(popupId, BuybackSharesPopup, {
rerender,
player,
popupId,
corp: corp,
});
}
function openIssueNewSharesPopup(): void {
const popupId = "cmpy-mgmt-issue-new-shares-popup";
createPopup(popupId, IssueNewSharesPopup, {
popupId,
corp: corp,
});
}
function openIssueDividendsPopup(): void {
const popupId = "cmpy-mgmt-issue-dividends-popup";
createPopup(popupId, IssueDividendsPopup, {
popupId,
corp: corp,
});
}
return ( return (
<> <>
<Button <Button
@ -267,102 +274,113 @@ export function Overview(props: IProps): React.ReactElement {
<br /> <br />
</> </>
); );
} }
// Render the UI for Corporation upgrades // Generic Function for Creating a button
function Upgrades(): React.ReactElement { interface ICreateButtonProps {
// Don't show upgrades text: string;
if (props.corp.divisions.length <= 0) { className?: string;
return <h1>Unlock upgrades after creating your first division</h1>; display?: string;
} tooltip?: string;
onClick?: (event: React.MouseEvent) => void;
}
function Button({ className = "std-button", text, display, tooltip, onClick }: ICreateButtonProps): React.ReactElement {
const hasTooltip = tooltip != null;
if (hasTooltip) className += " tooltip";
return ( return (
<div className={"cmpy-mgmt-upgrade-container"}> <button className={className} onClick={onClick} style={{ display: display ? display : "block" }}>
<h1 className={"cmpy-mgmt-upgrade-header"}> Unlocks </h1> {text}
{Object.values(CorporationUnlockUpgrades) {hasTooltip && <span className={"tooltiptext"}>{tooltip}</span>}
.filter((upgrade: CorporationUnlockUpgrade) => props.corp.unlockUpgrades[upgrade[0]] === 0) </button>
.map((upgrade: CorporationUnlockUpgrade) => ( );
<UnlockUpgrade }
rerender={props.rerender}
player={props.player} interface IBribeButtonProps {
corp={props.corp} player: IPlayer;
upgradeData={upgrade} corp: ICorporation;
key={upgrade[0]} }
/> function BribeButton({ player, corp }: IBribeButtonProps): React.ReactElement {
))} function openBribeFactionPopup(): void {
const popupId = "corp-bribe-popup";
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1> createPopup(popupId, BribeFactionPopup, {
{props.corp.upgrades player,
.map((level: number, i: number) => CorporationUpgrades[i]) popupId,
.map((upgrade: CorporationUpgrade) => ( corp: corp,
<LevelableUpgrade });
rerender={props.rerender} }
player={props.player}
corp={props.corp} const canBribe = corp.determineValuation() >= CorporationConstants.BribeThreshold || true;
upgrade={upgrade} const bribeFactionsClass = canBribe ? "a-link-button" : "a-link-button-inactive";
key={upgrade[0]} return (
/> <Button
))} className={bribeFactionsClass}
</div> display="inline-block"
); onClick={openBribeFactionPopup}
} text="Bribe Factions"
tooltip={
return ( canBribe
<div> ? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
<p> : "Your Corporation is not powerful enough to bribe Faction leaders"
Total Funds: <Money money={props.corp.funds.toNumber()} /> }
<br /> />
Total Revenue: <Money money={props.corp.revenue.toNumber()} /> / s<br /> );
Total Expenses: <Money money={props.corp.expenses.toNumber()} /> / s }
<br />
Total Profits: <Money money={profit} /> / s<br /> interface IDividendsStatsProps {
<DividendsStats /> corp: ICorporation;
Publicly Traded: {props.corp.public ? "Yes" : "No"} profit: number;
<br /> }
Owned Stock Shares: {numeralWrapper.format(props.corp.numShares, "0.000a")} function DividendsStats({ corp, profit }: IDividendsStatsProps): React.ReactElement {
<br /> if (corp.dividendPercentage <= 0 || profit <= 0) return <></>;
Stock Price: {props.corp.public ? <Money money={props.corp.sharePrice} /> : "N/A"} const totalDividends = (corp.dividendPercentage / 100) * profit;
<br /> const retainedEarnings = profit - totalDividends;
</p> const dividendsPerShare = totalDividends / corp.totalShares;
<p className="tooltip"> const playerEarnings = corp.numShares * dividendsPerShare;
Total Stock Shares: {numeralWrapper.format(props.corp.totalShares, "0.000a")} return (
<span className="tooltiptext"> <>
Outstanding Shares: {numeralWrapper.format(props.corp.issuedShares, "0.000a")} Retained Profits (after dividends): <Money money={retainedEarnings} /> / s
<br /> <br />
Private Shares:{" "} <br />
{numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")} Dividend Percentage: {numeralWrapper.format(corp.dividendPercentage / 100, "0%")}
</span> <br />
</p> Dividends per share: <Money money={dividendsPerShare} /> / s<br />
<br /> Your earnings as a shareholder (Pre-Tax): <Money money={playerEarnings} /> / s<br />
<br /> Dividend Tax Rate: {corp.dividendTaxPercentage}%<br />
<Mult name="Production Multiplier: " mult={props.corp.getProductionMultiplier()} /> Your earnings as a shareholder (Post-Tax):{" "}
<Mult name="Storage Multiplier: " mult={props.corp.getStorageMultiplier()} /> <Money money={playerEarnings * (1 - corp.dividendTaxPercentage / 100)} /> / s<br />
<Mult name="Advertising Multiplier: " mult={props.corp.getAdvertisingMultiplier()} /> <br />
<Mult name="Empl. Creativity Multiplier: " mult={props.corp.getEmployeeCreMultiplier()} /> </>
<Mult name="Empl. Charisma Multiplier: " mult={props.corp.getEmployeeChaMultiplier()} /> );
<Mult name="Empl. Intelligence Multiplier: " mult={props.corp.getEmployeeIntMultiplier()} /> }
<Mult name="Empl. Efficiency Multiplier: " mult={props.corp.getEmployeeEffMultiplier()} />
<Mult name="Sales Multiplier: " mult={props.corp.getSalesMultiplier()} /> interface IMultProps {
<Mult name="Scientific Research Multiplier: " mult={props.corp.getScientificResearchMultiplier()} /> name: string;
<br /> mult: number;
<BonusTime /> }
<div> function Mult({ name, mult }: IMultProps): React.ReactElement {
<Button if (mult <= 1) return <></>;
className="a-link-button" return (
display="inline-block" <p>
onClick={() => props.corp.getStarterGuide(props.player)} {name}
text="Getting Started Guide" {numeralWrapper.format(mult, "0.000")}
tooltip={ <br />
"Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' " + </p>
"This is a .lit file that guides you through the beginning of setting up a Corporation and " + );
"provides some tips/pointers for helping you get started with managing it." }
}
/> interface IBonusTimeProps {
{props.corp.public ? <PublicButtons /> : <PrivateButtons />} corp: ICorporation;
<BribeButton /> }
</div> // Returns a string with general information about Corporation
<br /> function BonusTime({ corp }: IBonusTimeProps): React.ReactElement {
<Upgrades /> const storedTime = corp.storedCycles * CONSTANTS.MilliPerCycle;
</div> if (storedTime <= 15000) return <></>;
return (
<p>
Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}
<br />
<br />
</p>
); );
} }

@ -34,9 +34,9 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
} }
return ( return (
<div className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}> <button className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}>
{text} {text}
<span className={"tooltiptext"}>{tooltip}</span> <span className={"tooltiptext"}>{tooltip}</span>
</div> </button>
); );
} }