diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cc8df9d --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +node-linker=hoisted \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 5dfb1c8..c5dfdea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ "editor.formatOnSave": true }, "[typescriptreact]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true }, "eslint.run": "onType", diff --git a/package.json b/package.json index 00fdaf5..c7fe1b2 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,9 @@ "@flamework/components": "^1.3.2", "@flamework/core": "^1.3.2", "@flamework/networking": "^1.3.2", - "@rbxts/fusion-0.3-temp": "^0.7.2", - "@rbxts/object-utils": "^1.0.4" + "@rbxts/object-utils": "^1.0.4", + "@rbxts/pretty-react-hooks": "^0.6.4", + "@rbxts/react": "^17.2.3", + "@rbxts/react-roblox": "^17.2.3" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 03ec599..f14ef63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,12 +17,18 @@ importers: '@flamework/networking': specifier: ^1.3.2 version: 1.3.2(@flamework/core@1.3.2) - '@rbxts/fusion-0.3-temp': - specifier: ^0.7.2 - version: 0.7.2 '@rbxts/object-utils': specifier: ^1.0.4 version: 1.0.4 + '@rbxts/pretty-react-hooks': + specifier: ^0.6.4 + version: 0.6.4(@rbxts/react-roblox@17.2.3)(@rbxts/react@17.2.3) + '@rbxts/react': + specifier: ^17.2.3 + version: 17.2.3 + '@rbxts/react-roblox': + specifier: ^17.2.3 + version: 17.2.3 devDependencies: '@eslint/eslintrc': specifier: ^3.3.1 @@ -173,18 +179,36 @@ packages: '@rbxts/compiler-types@3.0.0-types.0': resolution: {integrity: sha512-VGOHJPoL7+56NTatMGqQj3K7xWuzEV+aP4QD5vZiHu+bcff3kiTmtoadaF6NkJrmwfFAvbsd4Dg764ZjWNceag==} - '@rbxts/fusion-0.3-temp@0.7.2': - resolution: {integrity: sha512-204MueZ6/3sps9bf4H+usqAL3VO+M5cmHwQnRci0LEF/Q4yV1re/ID96BTYTaWlohI0Vn+yKE45iFYNoclhk5Q==} - '@rbxts/maid@1.1.0': resolution: {integrity: sha512-bVWXZ0p2M3OJzPzvN5fY0T4s37ezUMY7EX31Xspp7Ds4C/K9yE4MHMRXjtlNvsYVPmoc5tdhAbpZY02Veix5lg==} '@rbxts/object-utils@1.0.4': resolution: {integrity: sha512-dLLhf022ipV+9i910sOE7kl9losKHoon0WgeerHqVMQA5EYsLUsVT2AxhJuhk8MiDn5oJ2GiFofE/LadY9TpJQ==} + '@rbxts/pretty-react-hooks@0.6.4': + resolution: {integrity: sha512-yiuM3kOD0s2x0GFCUVfFdeSDJRX2ZycS88WOvqydaYwPIZ5rYDjzHjOzRgSpAzGcTVOFXsO64QOtD6svAdadzg==} + peerDependencies: + '@rbxts/react': '*' + '@rbxts/react-roblox': '*' + + '@rbxts/react-roblox@17.2.3': + resolution: {integrity: sha512-YgS+C3tf92iM1NTK1f6R2O2G8+nbcKFnblA0mTs2Nt878OsWLgxhnhkCBZ6wYv+yeDGD9/sssKHZ6s2LzUWTrQ==} + + '@rbxts/react-vendor@17.2.3': + resolution: {integrity: sha512-dWCiVelvTK6h920geaFH/OQ5zoWHyhyqyfH51hKx2/iVZ6OiiyNVJ5UqEIkJMG4HsJ8JnkTORizV3+MtQ41BtA==} + + '@rbxts/react@17.2.3': + resolution: {integrity: sha512-IoOB8e2W8MxT0MKWFf0Ls0vT61ImYL6ctHQgz5+oazma6z8bqFtJjgPxOuy6jmMSuyfBe7rYp/muou0kIjZT+Q==} + + '@rbxts/ripple@0.9.3': + resolution: {integrity: sha512-GmwjQl7a8pvGPGQYLmaAcf2RBPpneOL5P1+U8CzTHt9tBhCdk0TEJ/MQoTv9JnHD62RMBE1jogQBvuGgl9VXAg==} + '@rbxts/services@1.6.0': resolution: {integrity: sha512-YH02E1/tGHeMXB0Mam8qPdAZa2fjQWdylEscSL4Zn0RIaQeByYImA2qUsXM3nBdhcyKHDwltzSznDKyCGfFEWA==} + '@rbxts/set-timeout@1.1.2': + resolution: {integrity: sha512-P/A0IiH9wuZdSJYr4Us0MDFm61nvIFR0acfKFHLkcOsgvIgELC90Up9ugiSsaMEHRIcIcO5UjE39LuS3xTzQHw==} + '@rbxts/signal@1.1.1': resolution: {integrity: sha512-WX+ONE+ld4pG9PvRkR8OgDld9NpaV1RfXyUIw+Q2oXP/5rehkYzvt20NWtrLAP3NhMc5inYInLd+hnufey6nFw==} @@ -197,9 +221,6 @@ packages: '@rbxts/types@1.0.887': resolution: {integrity: sha512-vhp4vIKEfl1TaBcKxBxU5dTFsVNqLkvefoF9pw/Epb+Jk9T+0g4SFQQtBdCu3UdZcBGK/PWk/IJeEyoRNDMkSw==} - '@rbxts/ui-labs@2.4.2': - resolution: {integrity: sha512-9cyzDYN4mM7KSupZpbHRCUWCfSHzy8LCqa9Czys2jaKYUCn8oV4j5AE+5jTrHD/8RcltP5EvCSFIFwB0N/NfxA==} - '@roblox-ts/luau-ast@2.0.0': resolution: {integrity: sha512-cmMi093IdwBOLVxwuordhM8AmtbyTIyRpsTbB0D/JauidW4SXsQRQowSwWjHo4QP0DRJBXvOIlxtqEQi50uNzQ==} @@ -1078,16 +1099,37 @@ snapshots: '@rbxts/compiler-types@3.0.0-types.0': {} - '@rbxts/fusion-0.3-temp@0.7.2': - dependencies: - '@rbxts/ui-labs': 2.4.2 - '@rbxts/maid@1.1.0': {} '@rbxts/object-utils@1.0.4': {} + '@rbxts/pretty-react-hooks@0.6.4(@rbxts/react-roblox@17.2.3)(@rbxts/react@17.2.3)': + dependencies: + '@rbxts/react': 17.2.3 + '@rbxts/react-roblox': 17.2.3 + '@rbxts/ripple': 0.9.3 + '@rbxts/services': 1.6.0 + '@rbxts/set-timeout': 1.1.2 + + '@rbxts/react-roblox@17.2.3': + dependencies: + '@rbxts/react': 17.2.3 + '@rbxts/react-vendor': 17.2.3 + + '@rbxts/react-vendor@17.2.3': {} + + '@rbxts/react@17.2.3': + dependencies: + '@rbxts/react-vendor': 17.2.3 + + '@rbxts/ripple@0.9.3': {} + '@rbxts/services@1.6.0': {} + '@rbxts/set-timeout@1.1.2': + dependencies: + '@rbxts/services': 1.6.0 + '@rbxts/signal@1.1.1': {} '@rbxts/t@2.2.1': {} @@ -1096,8 +1138,6 @@ snapshots: '@rbxts/types@1.0.887': {} - '@rbxts/ui-labs@2.4.2': {} - '@roblox-ts/luau-ast@2.0.0': {} '@roblox-ts/path-translator@1.1.0': diff --git a/src/client/controllers/gui.ts b/src/client/controllers/gui.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/client/controllers/gui.tsx b/src/client/controllers/gui.tsx new file mode 100644 index 0000000..79acb6a --- /dev/null +++ b/src/client/controllers/gui.tsx @@ -0,0 +1,30 @@ +import { Controller, OnStart } from "@flamework/core"; +import React from "@rbxts/react"; +import { StrictMode } from "@rbxts/react"; +import { createPortal, createRoot } from "@rbxts/react-roblox"; +import { Players, StarterGui } from "@rbxts/services"; +import PlayersBoard from "shared/gui/players_board"; + +@Controller() +class GuiController implements OnStart { + onStart(): void { + StarterGui.SetCoreGuiEnabled(Enum.CoreGuiType.All, false); + StarterGui.SetCoreGuiEnabled(Enum.CoreGuiType.Chat, true); + try { + StarterGui.SetCore("ResetButtonCallback", true); + } catch (error) {} + + const root = createRoot(new Instance("Folder")); + + root.render( + + {createPortal( + + + , + (Players.LocalPlayer as unknown as { PlayerGui: PlayerGui }).PlayerGui, + )} + , + ); + } +} diff --git a/src/shared/gui/players_board/index.story.ts b/src/shared/gui/players_board/index.story.ts new file mode 100644 index 0000000..65d8e07 --- /dev/null +++ b/src/shared/gui/players_board/index.story.ts @@ -0,0 +1,4 @@ +import { hoarcekat } from "@rbxts/pretty-react-hooks"; +import PlayersBoard from "."; + +export = hoarcekat(PlayersBoard); diff --git a/src/shared/gui/players_board/index.tsx b/src/shared/gui/players_board/index.tsx new file mode 100644 index 0000000..f0e2bb9 --- /dev/null +++ b/src/shared/gui/players_board/index.tsx @@ -0,0 +1,108 @@ +import { lerp, useMotion } from "@rbxts/pretty-react-hooks"; +import React, { Component, ReactNode, StrictMode, useEffect, useState } from "@rbxts/react"; +import { Players } from "@rbxts/services"; +import Profile from "./profile"; + +function PlayerEntry({ + player, + selectedPlayer, + setSelectedPlayer, +}: { + player: Player; + setSelectedPlayer: React.Dispatch>; + selectedPlayer: Player | undefined; +}) { + const [enable, enableMotor] = useMotion(0); + useEffect(() => { + if (player === selectedPlayer) { + enableMotor.spring(90); + } else { + enableMotor.spring(0); + } + }, [selectedPlayer]); + return ( + + + { + setSelectedPlayer(player === selectedPlayer ? undefined : player); + }, + }} + > + "} + TextColor3={new Color3(1, 1, 1)} + TextScaled={true} + BackgroundTransparency={1} + Size={new UDim2(0.5, 0, 0.5, 0)} + Position={UDim2.fromScale(0, 0.25)} + SizeConstraint={Enum.SizeConstraint.RelativeYY} + Rotation={enable.map((t) => t)} + > + + ); +} + +export default function PlayersBoard() { + const [players, setPlayers] = useState(Players.GetPlayers()); + const [selectedPlayer, setSelectedPlayer] = useState(); + useEffect(() => { + const playerAdded = Players.PlayerAdded.Connect(() => { + setPlayers(Players.GetPlayers()); + }); + const playerRemoving = Players.PlayerRemoving.Connect(() => { + setPlayers(Players.GetPlayers()); + }); + return () => { + playerAdded.Disconnect(); + playerRemoving.Disconnect(); + }; + }, []); + + return ( + <> + + + + + {players.map((p) => ( + + ))} + + + + + {selectedPlayer ? : undefined} + + ); +} diff --git a/src/shared/gui/players_board/profile/index.story.tsx b/src/shared/gui/players_board/profile/index.story.tsx new file mode 100644 index 0000000..6123153 --- /dev/null +++ b/src/shared/gui/players_board/profile/index.story.tsx @@ -0,0 +1,17 @@ +import { StrictMode } from "@rbxts/react"; +import Profile from "."; +import React from "@rbxts/react"; +import { createRoot } from "@rbxts/react-roblox"; +import { Players } from "@rbxts/services"; + +export = function (target: Instance) { + const root = createRoot(target); + root.render( + + + , + ); + return function () { + root.unmount(); + }; +}; diff --git a/src/shared/gui/players_board/profile/index.tsx b/src/shared/gui/players_board/profile/index.tsx new file mode 100644 index 0000000..61865f8 --- /dev/null +++ b/src/shared/gui/players_board/profile/index.tsx @@ -0,0 +1,97 @@ +import React, { useEffect, useRef, useState } from "@rbxts/react"; +import { LocalizationService, Players } from "@rbxts/services"; + +function TimePlayed({ targetTime }: { targetTime: number }) { + const [time, setTime] = useState(); + const [id, setId] = useState(); + useEffect(() => { + let killed = false; + setId(DateTime.fromUnixTimestamp(targetTime)); + task.spawn(() => { + while (true) { + if (killed) return; + } + }); + return () => { + killed = true; + }; + }, [targetTime]); +} + +export default function Profile({ player }: { player: Player }) { + const [userIcon, setUserIcon] = useState(); + const ref = useRef(); + useEffect(() => { + const [image] = Players.GetUserThumbnailAsync( + player.UserId, + Enum.ThumbnailType.HeadShot, + Enum.ThumbnailSize.Size180x180, + ); + setUserIcon(image); + }, [player]); + const reeloffset = ref.current?.AbsoluteSize.X ?? 0; + return ( + + + + + + + + + + + + + + + + + ); +} diff --git a/tsconfig.json b/tsconfig.json index bc844de..69e7f35 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "allowSyntheticDefaultImports": true, "downlevelIteration": true, "jsx": "react", - "jsxFactory": "Fusion.jsx", + "jsxFactory": "React.createElement", "jsxFragmentFactory": "React.Fragment", "module": "commonjs", "moduleResolution": "Node",