diff --git a/src/GameOptions/ui/CreditsModal.tsx b/src/GameOptions/ui/CreditsModal.tsx new file mode 100644 index 000000000..b894865c9 --- /dev/null +++ b/src/GameOptions/ui/CreditsModal.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { Modal } from "../../ui/React/Modal"; +import { Typography, Link, Button } from "@mui/material"; + +import { CONSTANTS } from "../../Constants"; + +interface CreditsModalProps { + open: boolean; + onClose: () => void; +} + +const enclosed = /(\([^)]+\))/gm; //grab all filled () pairs +const recentPatchData = Array.from(new Set(CONSTANTS.LatestUpdate.match(enclosed))); + +const isDate = (data: string) => { + const regex = /^\(last update/gm; //(this) isn't @name, but may be useful + return regex.test(data); +}; +const updateMessage = []; //store last update message, eg (last updated 9/12/23) +if (isDate(recentPatchData[0])) updateMessage.push(recentPatchData[0]); + +const handle: string[] = []; +for (let i = 0; i < recentPatchData.length; i++) { + const atName = /(?:^[(]?(@[^\s),]+)[),]?)/gm; //make an array of only unique @handles + const whatWeWant = recentPatchData[i].replace(atName, "$1"); + if (isDate(recentPatchData[i]) || !recentPatchData[i].includes("@")) continue; + if (recentPatchData[i].includes(", ")) { + //if (@1, @2, ...@n) + recentPatchData.push(...recentPatchData[i].split(", ")); + continue; + } + if (!handle.includes(whatWeWant)) handle.push(whatWeWant); +} + +export function CreditsModal(props: CreditsModalProps): React.ReactElement { + const leadDevs = `danielyxie +Olivier Gagnon +@Snarling +`; + + const currentMaintainer = `@Snarling`; + + const handles = handle.sort((a, b) => a.localeCompare(b)).join(", "); + const contributorsURL = `https://github.com/bitburner-official/bitburner-src/graphs/contributors`; + const contributorsMessage = `Visit GitHub to see all contributors +or to participate yourself`; + const maxEM = Math.floor(contributorsMessage.length / 2); + + return ( + + + Bitburner + + Original Code and Concept + danielyxie +
+ Lead Developers: + {leadDevs} +
+ Current Maintainer + {currentMaintainer} +
+ Recent patch contributors: + + {/*rem unit = character px based, dynamic with font. balance contributor overflow vs longest other message*/} + {/*textoverflow "clip" forces very long @names to stretch a single line, it's silly*/} + {handles} + +
+ + + {contributorsMessage} + + +
+ + + +
+ ); +} diff --git a/src/GameOptions/ui/GameOptionsSidebar.tsx b/src/GameOptions/ui/GameOptionsSidebar.tsx index 4f2a7b4e6..2a5301d3b 100644 --- a/src/GameOptions/ui/GameOptionsSidebar.tsx +++ b/src/GameOptions/ui/GameOptionsSidebar.tsx @@ -1,4 +1,14 @@ -import { BugReport, Chat, Download, LibraryBooks, Palette, Reddit, Save, Upload } from "@mui/icons-material"; +import { + BugReport, + Chat, + Download, + LibraryBooks, + Palette, + Fingerprint, + Reddit, + Save, + Upload, +} from "@mui/icons-material"; import { Box, Button, List, ListItemButton, Paper, Tooltip, Typography } from "@mui/material"; import { default as React, useRef, useState } from "react"; import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal"; @@ -6,6 +16,7 @@ import { ImportData, saveObject } from "../../SaveObject"; import { StyleEditorButton } from "../../Themes/ui/StyleEditorButton"; import { ThemeEditorButton } from "../../Themes/ui/ThemeEditorButton"; import { ConfirmationModal } from "../../ui/React/ConfirmationModal"; +import { CreditsModal } from "./CreditsModal"; import { DeleteGameButton } from "../../ui/React/DeleteGameButton"; import { SnackbarEvents } from "../../ui/React/Snackbar"; import { ToastVariant } from "@enums"; @@ -49,6 +60,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => { const [importData, setImportData] = useState(null); const [confirmResetOpen, setConfirmResetOpen] = useState(false); + const [creditsOpen, setCreditsOpen] = useState(false); function startImport(): void { if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return; @@ -225,7 +237,8 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => { sx={{ gridArea: "links", display: "grid", - gridTemplateAreas: `"bug bug" + gridTemplateAreas: `"credits credits" + "bug bug" "discord reddit" "tut tut" "plaza plaza"`, @@ -233,14 +246,20 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => { my: 1, }} > - + + + setCreditsOpen(false)} />