mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-02 12:33:56 +01:00
Merge branch 'dev' into bugfix/2958
This commit is contained in:
commit
41974b042c
34
dist/vendor.bundle.js
vendored
34
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/vendor.bundle.js.map
vendored
2
dist/vendor.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
@ -542,28 +542,6 @@ Then in order to check its logs with 'tail' the same arguments must be used::
|
|||||||
|
|
||||||
$ tail foo.script 10 50000
|
$ tail foo.script 10 50000
|
||||||
|
|
||||||
theme
|
|
||||||
^^^^^
|
|
||||||
|
|
||||||
$ theme [preset] | [#background #text #highlight]
|
|
||||||
|
|
||||||
Change the color of the game's user interface
|
|
||||||
|
|
||||||
This command can be called with a preset theme. Currently, the supported presets are:
|
|
||||||
|
|
||||||
* default
|
|
||||||
* muted
|
|
||||||
* solarized
|
|
||||||
|
|
||||||
However, you can also specify your own color scheme using hex values.
|
|
||||||
To do so, you must specify three hex color values for the background
|
|
||||||
color, the text color, and the highlight color. These hex values must
|
|
||||||
be preceded by a pound sign (#) and must be either 3 or 6 digits. Example::
|
|
||||||
|
|
||||||
$ theme #ffffff #385 #235012
|
|
||||||
|
|
||||||
A color picker such as Google's can be used to get your desired hex color values
|
|
||||||
|
|
||||||
top
|
top
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
43
src/Arcade/ui/ArcadeRoot.tsx
Normal file
43
src/Arcade/ui/ArcadeRoot.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { BBCabinetRoot } from "./BBCabinet";
|
||||||
|
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import { AlertEvents } from "../../ui/React/AlertManager";
|
||||||
|
|
||||||
|
enum Page {
|
||||||
|
None,
|
||||||
|
Megabyteburner2000,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArcadeRoot(): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const [page, setPage] = useState(Page.None);
|
||||||
|
|
||||||
|
function mbBurner2000(): void {
|
||||||
|
if (player.sourceFileLvl(1) === 0) {
|
||||||
|
AlertEvents.emit("This machine is broken.");
|
||||||
|
} else {
|
||||||
|
setPage(Page.Megabyteburner2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page === Page.None) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button onClick={mbBurner2000}>Megabyte burner 2000</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let currentGame = <></>;
|
||||||
|
switch (page) {
|
||||||
|
case Page.Megabyteburner2000:
|
||||||
|
currentGame = <BBCabinetRoot />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button onClick={() => setPage(Page.None)}>Back</Button>
|
||||||
|
{currentGame}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
52
src/Arcade/ui/BBCabinet.tsx
Normal file
52
src/Arcade/ui/BBCabinet.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
|
const metaBB = "https://bitburner-official.github.io/bitburner-legacy/";
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
width: "1060px",
|
||||||
|
height: "800px",
|
||||||
|
border: "0px",
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
export function BBCabinetRoot(): React.ReactElement {
|
||||||
|
// prettier-ignore
|
||||||
|
const joystick =
|
||||||
|
<>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> </Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ,'" "', .-. </Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> / \ ( ) </Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | .-. '-' .-. </Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ / ( ) ( )</Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> '.___.' '-' .-. '-'</Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ||| ( ) </Typography>
|
||||||
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ||| '-' </Typography>
|
||||||
|
</>;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "1060px",
|
||||||
|
height: "800px",
|
||||||
|
padding: "0",
|
||||||
|
overflow: "hidden",
|
||||||
|
borderColor: "white",
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderWidth: "5px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<iframe src={metaBB} style={style} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "1060px",
|
||||||
|
borderColor: "white",
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderWidth: "5px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{joystick}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1703,7 +1703,7 @@ function initAugmentations(): void {
|
|||||||
"The left arm of a legendary BitRunner who ascended beyond this world. " +
|
"The left arm of a legendary BitRunner who ascended beyond this world. " +
|
||||||
"It projects a light blue energy shield that protects the exposed inner parts. " +
|
"It projects a light blue energy shield that protects the exposed inner parts. " +
|
||||||
"Even though it contains no weapons, the advanced tungsten titanium " +
|
"Even though it contains no weapons, the advanced tungsten titanium " +
|
||||||
"alloy increases the users strength to unbelievable levels. The augmentation " +
|
"alloy increases the user's strength to unbelievable levels. The augmentation " +
|
||||||
"gets more powerful over time for seemingly no reason.",
|
"gets more powerful over time for seemingly no reason.",
|
||||||
strength_mult: 2.7,
|
strength_mult: 2.7,
|
||||||
});
|
});
|
||||||
|
@ -273,7 +273,7 @@ export function EquipmentsSubpage(): React.ReactElement {
|
|||||||
sx={{ m: 1, width: '15%' }}
|
sx={{ m: 1, width: '15%' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: 'fit-content' }}>
|
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: '100%' }}>
|
||||||
{members.map((member: GangMember) => (
|
{members.map((member: GangMember) => (
|
||||||
<GangMemberUpgradePanel key={member.name} member={member} />
|
<GangMemberUpgradePanel key={member.name} member={member} />
|
||||||
))}
|
))}
|
||||||
|
@ -127,10 +127,10 @@ Cities[CityName.NewTokyo].asciiArt = `
|
|||||||
|
|
||||||
o
|
o
|
||||||
\\
|
\\
|
||||||
\\ [defcomm]
|
[arcade] E [defcomm]
|
||||||
\\
|
\\
|
||||||
o--x---A--x--o [travel agency]
|
o--x---A--x--o [travel agency]
|
||||||
7 8 10 G
|
7 8 10 H
|
||||||
[vitalife] o 12 [global pharmaceuticals]
|
[vitalife] o 12 [global pharmaceuticals]
|
||||||
|
|
|
|
||||||
o--D-x----x-------x-C-+--------x--x-B-x---x-o
|
o--D-x----x-------x-C-+--------x--x-B-x---x-o
|
||||||
@ -141,14 +141,14 @@ Cities[CityName.NewTokyo].asciiArt = `
|
|||||||
\\
|
\\
|
||||||
[hospital] o 15 [world stock exchange]
|
[hospital] o 15 [world stock exchange]
|
||||||
|
|
|
|
||||||
o--x--E--x-----x-----x---+---x----x--H--x-o
|
o--x--F--x-----x-----x---+---x----x--I--x-o
|
||||||
|
|
|
|
||||||
|
|
|
|
||||||
o 17
|
o 17
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
F [the slums]
|
G [the slums]
|
||||||
`;
|
`;
|
||||||
Cities[CityName.Sector12].asciiArt = `
|
Cities[CityName.Sector12].asciiArt = `
|
||||||
78 o 97
|
78 o 97
|
||||||
|
@ -54,6 +54,7 @@ export enum LocationName {
|
|||||||
NewTokyoGlobalPharmaceuticals = "Global Pharmaceuticals",
|
NewTokyoGlobalPharmaceuticals = "Global Pharmaceuticals",
|
||||||
NewTokyoNoodleBar = "Noodle Bar",
|
NewTokyoNoodleBar = "Noodle Bar",
|
||||||
NewTokyoVitaLife = "VitaLife",
|
NewTokyoVitaLife = "VitaLife",
|
||||||
|
NewTokyoArcade = "Arcade",
|
||||||
|
|
||||||
// Ishima
|
// Ishima
|
||||||
IshimaNovaMedical = "Nova Medical",
|
IshimaNovaMedical = "Nova Medical",
|
||||||
|
@ -215,6 +215,11 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
name: LocationName.NewTokyoVitaLife,
|
name: LocationName.NewTokyoVitaLife,
|
||||||
types: [LocationType.Company, LocationType.Special],
|
types: [LocationType.Company, LocationType.Special],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
city: CityName.NewTokyo,
|
||||||
|
name: LocationName.NewTokyoArcade,
|
||||||
|
types: [LocationType.Special],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
|
@ -34,10 +34,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
padding: "0px",
|
padding: "0px",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
function toLocation(router: IRouter, location: Location): void {
|
function toLocation(router: IRouter, location: Location): void {
|
||||||
if (location.name === LocationName.TravelAgency) {
|
if (location.name === LocationName.TravelAgency) {
|
||||||
router.toTravel();
|
router.toTravel();
|
||||||
@ -132,12 +131,14 @@ function ASCIICity(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
const elems: JSX.Element[] = [];
|
const elems: JSX.Element[] = [];
|
||||||
const lines = props.city.asciiArt.split("\n");
|
const lines = props.city.asciiArt.split("\n");
|
||||||
|
let i = 0;
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
elems.push(
|
elems.push(
|
||||||
<Typography key={line} sx={{ lineHeight: "1em", whiteSpace: "pre" }}>
|
<Typography key={i} sx={{ lineHeight: "1em", whiteSpace: "pre" }}>
|
||||||
{lineElems(line)}
|
{lineElems(line)}
|
||||||
</Typography>,
|
</Typography>,
|
||||||
);
|
);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{elems}</>;
|
return <>{elems}</>;
|
||||||
|
@ -32,6 +32,7 @@ import { CorruptableText } from "../../ui/React/CorruptableText";
|
|||||||
import { HacknetNode } from "../../Hacknet/HacknetNode";
|
import { HacknetNode } from "../../Hacknet/HacknetNode";
|
||||||
import { HacknetServer } from "../../Hacknet/HacknetServer";
|
import { HacknetServer } from "../../Hacknet/HacknetServer";
|
||||||
import { GetServer } from "../../Server/AllServers";
|
import { GetServer } from "../../Server/AllServers";
|
||||||
|
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
loc: Location;
|
loc: Location;
|
||||||
@ -81,7 +82,12 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
const text = inBladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
|
const text = inBladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
|
||||||
return <><br/><Button onClick={handleBladeburner}>{text}</Button></>;
|
return (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
<Button onClick={handleBladeburner}>{text}</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNoodleBar(): React.ReactElement {
|
function renderNoodleBar(): React.ReactElement {
|
||||||
@ -311,6 +317,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
case LocationName.IshimaGlitch: {
|
case LocationName.IshimaGlitch: {
|
||||||
return renderGlitch();
|
return renderGlitch();
|
||||||
}
|
}
|
||||||
|
case LocationName.NewTokyoArcade: {
|
||||||
|
return <ArcadeRoot />;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.error(`Location ${props.loc.name} doesn't have any special properties`);
|
console.error(`Location ${props.loc.name} doesn't have any special properties`);
|
||||||
return <></>;
|
return <></>;
|
||||||
|
@ -232,7 +232,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" });
|
MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" });
|
||||||
editor.focus();
|
editor.focus();
|
||||||
});
|
});
|
||||||
} catch {}
|
} catch { }
|
||||||
} else if (!options.vim) {
|
} else if (!options.vim) {
|
||||||
// Whem vim mode is disabled
|
// Whem vim mode is disabled
|
||||||
vimEditor?.dispose();
|
vimEditor?.dispose();
|
||||||
@ -478,7 +478,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
infLoop(newCode);
|
infLoop(newCode);
|
||||||
} catch (err) {}
|
} catch (err) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveScript(scriptToSave: OpenScript): void {
|
function saveScript(scriptToSave: OpenScript): void {
|
||||||
@ -877,6 +877,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
maxWidth: `${tabTextWidth}px`,
|
maxWidth: `${tabTextWidth}px`,
|
||||||
|
minHeight: '38.5px',
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
...colorProps,
|
...colorProps,
|
||||||
}}
|
}}
|
||||||
|
@ -110,107 +110,45 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const uninitialized = (): any => {
|
||||||
|
throw new Error("Router called before initialization");
|
||||||
|
};
|
||||||
|
|
||||||
export let Router: IRouter = {
|
export let Router: IRouter = {
|
||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
page: () => {
|
page: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
allowRouting: uninitialized,
|
||||||
},
|
toActiveScripts: uninitialized,
|
||||||
allowRouting: () => {
|
toAugmentations: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toBitVerse: uninitialized,
|
||||||
},
|
toBladeburner: uninitialized,
|
||||||
toActiveScripts: () => {
|
toStats: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toCity: uninitialized,
|
||||||
},
|
toCorporation: uninitialized,
|
||||||
toAugmentations: () => {
|
toCreateProgram: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toDevMenu: uninitialized,
|
||||||
},
|
toFaction: uninitialized,
|
||||||
toBitVerse: () => {
|
toFactions: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toGameOptions: uninitialized,
|
||||||
},
|
toGang: uninitialized,
|
||||||
toBladeburner: () => {
|
toHacknetNodes: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toInfiltration: uninitialized,
|
||||||
},
|
toJob: uninitialized,
|
||||||
toStats: () => {
|
toMilestones: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toResleeves: uninitialized,
|
||||||
},
|
toScriptEditor: uninitialized,
|
||||||
toCity: () => {
|
toSleeves: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toStockMarket: uninitialized,
|
||||||
},
|
toTerminal: uninitialized,
|
||||||
toCorporation: () => {
|
toTravel: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toTutorial: uninitialized,
|
||||||
},
|
toWork: uninitialized,
|
||||||
toCreateProgram: () => {
|
toBladeburnerCinematic: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toLocation: uninitialized,
|
||||||
},
|
toStaneksGift: uninitialized,
|
||||||
toDevMenu: () => {
|
toAchievements: uninitialized,
|
||||||
throw new Error("Router called before initialization");
|
toThemeBrowser: uninitialized,
|
||||||
},
|
toImportSave: uninitialized,
|
||||||
toFaction: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toFactions: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toGameOptions: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toGang: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toHacknetNodes: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toInfiltration: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toJob: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toMilestones: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toResleeves: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toScriptEditor: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toSleeves: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toStockMarket: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toTerminal: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toTravel: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toTutorial: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toWork: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toBladeburnerCinematic: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toLocation: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toStaneksGift: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toAchievements: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toThemeBrowser: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toImportSave: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function determineStartPage(player: IPlayer): Page {
|
function determineStartPage(player: IPlayer): Page {
|
||||||
@ -346,12 +284,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Wrap Router navigate functions to be able to disable the execution
|
// Wrap Router navigate functions to be able to disable the execution
|
||||||
_functions(Router).
|
_functions(Router)
|
||||||
filter((fnName) => fnName.startsWith('to')).
|
.filter((fnName) => fnName.startsWith("to"))
|
||||||
forEach((fnName) => {
|
.forEach((fnName) => {
|
||||||
// @ts-ignore - tslint does not like this, couldn't find a way to make it cooperate
|
// @ts-ignore - tslint does not like this, couldn't find a way to make it cooperate
|
||||||
Router[fnName] = _wrap(Router[fnName], (func, ...args) => {
|
Router[fnName] = _wrap(Router[fnName], (func, ...args) => {
|
||||||
if (!allowRoutingCalls) {
|
if (!allowRoutingCalls) {
|
||||||
@ -568,13 +505,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Page.ImportSave: {
|
case Page.ImportSave: {
|
||||||
mainPage = (
|
mainPage = <ImportSaveRoot importString={importString} automatic={importAutomatic} router={Router} />;
|
||||||
<ImportSaveRoot
|
|
||||||
importString={importString}
|
|
||||||
automatic={importAutomatic}
|
|
||||||
router={Router}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
withSidebar = false;
|
withSidebar = false;
|
||||||
withPopups = false;
|
withPopups = false;
|
||||||
bypassGame = true;
|
bypassGame = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user