UI: Better support for custom player React content (#513)

This commit is contained in:
Snarling 2023-05-22 05:28:24 -04:00 committed by GitHub
parent 0d55b957f1
commit 87f2ae459d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 17 deletions

14
package-lock.json generated

@ -61,7 +61,7 @@
"@types/numeral": "^2.0.2",
"@types/react": "^17.0.52",
"@types/react-beautiful-dnd": "^13.1.3",
"@types/react-dom": "^17.0.18",
"@types/react-dom": "^17.0.20",
"@types/react-resizable": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
@ -4472,9 +4472,9 @@
}
},
"node_modules/@types/react-dom": {
"version": "17.0.18",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.18.tgz",
"integrity": "sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==",
"version": "17.0.20",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz",
"integrity": "sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==",
"dev": true,
"dependencies": {
"@types/react": "^17"
@ -18963,9 +18963,9 @@
}
},
"@types/react-dom": {
"version": "17.0.18",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.18.tgz",
"integrity": "sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==",
"version": "17.0.20",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz",
"integrity": "sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==",
"dev": true,
"requires": {
"@types/react": "^17"

@ -62,7 +62,7 @@
"@types/numeral": "^2.0.2",
"@types/react": "^17.0.52",
"@types/react-beautiful-dnd": "^13.1.3",
"@types/react-dom": "^17.0.18",
"@types/react-dom": "^17.0.20",
"@types/react-resizable": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",

@ -548,7 +548,7 @@ export const RamCosts: RamCostTree<NSFull> = {
alterReality: 0,
rainbow: 0,
heart: { break: 0 },
iKnowWhatImDoing: 0,
tprintRaw: 0,
printRaw: 0,
formulas: {

@ -1,4 +1,4 @@
import type React from "react";
import React from "react";
import { Player } from "../Player";
import { Exploit } from "../Exploits/Exploit";
import * as bcrypt from "bcryptjs";
@ -7,6 +7,10 @@ import { InternalAPI } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
import { Terminal } from "../Terminal";
import { RamCostConstants } from "../Netscript/RamCostGenerator";
import { CustomBoundary } from "../ui/Components/CustomBoundary";
// Incrementing value for custom element keys
let customElementKey = 0;
export interface INetscriptExtra {
heart: {
@ -17,7 +21,7 @@ export interface INetscriptExtra {
bypass(doc: Document): void;
alterReality(): void;
rainbow(guess: string): void;
iKnowWhatImDoing(): void;
tprintRaw(value: React.ReactNode): void;
printRaw(value: React.ReactNode): void;
}
@ -67,14 +71,15 @@ export function NetscriptExtra(): InternalAPI<INetscriptExtra> {
Player.giveExploit(Exploit.INeedARainbow);
return true;
},
iKnowWhatImDoing: (ctx) => () => {
helpers.log(ctx, () => "Unlocking unsupported feature: window.tprintRaw");
// @ts-expect-error window has no tprintRaw property defined
window.tprintRaw = Terminal.printRaw.bind(Terminal);
tprintRaw: () => (value) => {
Terminal.printRaw(
<CustomBoundary key={`PlayerContent${customElementKey++}`} children={value as React.ReactNode} />,
);
},
printRaw: (ctx) => (value) => {
// Using this voids the warranty on your tail log
ctx.workerScript.print(value as React.ReactNode);
ctx.workerScript.print(
<CustomBoundary key={`PlayerContent${customElementKey++}`} children={value as React.ReactNode} />,
);
},
};
}

@ -0,0 +1,31 @@
import React from "react";
import Typography from "@mui/material/Typography";
interface CustomBoundaryProps {
children: React.ReactNode;
}
interface CustomBoundaryState {
error?: Error;
}
/** Error boundary for custom content printed by the player using printRaw-like functions.
* Error boundaries are required to be class components due to no hook equivalent to componentDidCatch. */
export class CustomBoundary extends React.Component<CustomBoundaryProps, CustomBoundaryState> {
state: CustomBoundaryState;
constructor(props: CustomBoundaryProps) {
super(props);
this.state = { error: undefined };
}
componentDidCatch(error: Error): void {
this.setState({ error });
console.warn("Error in custom react content:");
console.error(error);
}
render(): React.ReactNode {
if (this.state.error) {
// Typography is used because there are no default page styles.
// Span is used because it does not conflict with the DOM validation nesting (default Typography element of p is invalid at this location in dom tree)
return <Typography component={"span"}>Error in custom react content. See console for details.</Typography>;
}
return <Typography component={"span"}>{this.props.children}</Typography>;
}
}