From ddc4108837f54866907655c8f1725b79581db6ed Mon Sep 17 00:00:00 2001 From: Calvin Koepke <hello@calvinkoepke.com> Date: Mon, 2 Dec 2024 11:40:03 -0700 Subject: [PATCH] chore: add consents --- src/components/GlobalTPS/GlobalTPS.tsx | 4 +- src/components/InitialView/InitialView.tsx | 2 + src/components/LoginModal/LoginModal.tsx | 164 +++++++++++++++--- .../LoginModal/{indes.ts => index.ts} | 0 src/components/Speedometer/Speedometer.tsx | 5 +- src/constants.ts | 1 - src/types.ts | 1 + 7 files changed, 153 insertions(+), 24 deletions(-) rename src/components/LoginModal/{indes.ts => index.ts} (100%) diff --git a/src/components/GlobalTPS/GlobalTPS.tsx b/src/components/GlobalTPS/GlobalTPS.tsx index a963f0e..5b75d03 100644 --- a/src/components/GlobalTPS/GlobalTPS.tsx +++ b/src/components/GlobalTPS/GlobalTPS.tsx @@ -1,5 +1,4 @@ import { FC } from "react"; -import { GLOBAL_MAX_SPEED } from "../../constants"; import Card from "../Card"; import Speedometer from "../Speedometer"; import cx from "classnames"; @@ -16,6 +15,7 @@ const GlobalTPS: FC<GlobalTPSProps> = ({ }) => { const { globalStats } = useAppContext(); const transactions = globalStats?.txs_per_second ?? 0; + const maxTransactions = globalStats?.peak_txs_per_second ?? 0; return ( <div> @@ -40,7 +40,7 @@ const GlobalTPS: FC<GlobalTPSProps> = ({ })} > <Speedometer - maxSpeed={GLOBAL_MAX_SPEED} + maxSpeed={maxTransactions} transactions={Math.round(transactions)} /> </Card> diff --git a/src/components/InitialView/InitialView.tsx b/src/components/InitialView/InitialView.tsx index 0363f72..b40db5c 100644 --- a/src/components/InitialView/InitialView.tsx +++ b/src/components/InitialView/InitialView.tsx @@ -65,6 +65,8 @@ const InitialView: FC<InitialViewProps> = ({ startGame }) => { setSessionId(""); }; + console.log(accountData); + const renderButtons = () => { if (isLoadingUserData) { return <div className="h-72 flex items-center text-3xl">Loading...</div>; diff --git a/src/components/LoginModal/LoginModal.tsx b/src/components/LoginModal/LoginModal.tsx index 78b01a3..bb93012 100644 --- a/src/components/LoginModal/LoginModal.tsx +++ b/src/components/LoginModal/LoginModal.tsx @@ -1,4 +1,12 @@ -import React, { useEffect, useState } from "react"; +import React, { + Dispatch, + FC, + SetStateAction, + useEffect, + useState, +} from "react"; +import cx from "classnames"; + import Modal from "../Modal"; import { API_BASE_URL, API_KEY } from "../../constants"; import { useQuery } from "@tanstack/react-query"; @@ -21,6 +29,13 @@ interface LoginModalProps { showActionButtons: () => void; } +interface ITOU { + readRules: boolean; + oldEnough: boolean; + nonEmployee: boolean; + privacy: boolean; +} + const providerIcons: { [key: string]: JSX.Element } = { wallet: <FaWallet />, google: <FaGoogle />, @@ -29,6 +44,36 @@ const providerIcons: { [key: string]: JSX.Element } = { github: <FaGithub />, }; +const CheckBoxInput: FC<{ + consent: keyof ITOU; + label: string; + setTou: Dispatch<SetStateAction<ITOU>>; + tou: ITOU; +}> = ({ consent, label, tou, setTou }) => { + return ( + <div className="inline-flex items-start max-w-[600px] justify-start gap-4"> + <input + type="checkbox" + checked={tou[consent]} + onChange={(e) => { + setTou((prev) => ({ + ...prev, + [consent]: e.target.checked, + })); + }} + className={cx( + "appearance-none min-w-4 min-h-4 rounded-sm border-2 border-gray-500 transition-all duration-200 cursor-pointer", + "checked:bg-yellow-400 checked:shadow-[0_0_6px_2px_rgba(255,223,0,0.08),0_0_15px_4px_rgba(255,223,0,0.15)]", + )} + id={consent} + /> + <label className="text-sm" htmlFor={consent}> + {label} + </label> + </div> + ); +}; + const LoginModal: React.FC<LoginModalProps> = ({ close, isOpen, @@ -38,6 +83,13 @@ const LoginModal: React.FC<LoginModalProps> = ({ const { keys, setAccountData } = useAppContext(); const { publicKeyHashHex } = keys || {}; const [isWaitingSigning, setIsWaitingSigning] = useState(false); + const [tou, setTou] = useState<ITOU>({ + nonEmployee: false, + oldEnough: false, + privacy: false, + readRules: false, + }); + const [showSelection, setShowSelection] = useState<boolean>(false); const { data: providers, isLoading: isLoadingProviders } = useQuery<string[]>( { @@ -68,6 +120,14 @@ const LoginModal: React.FC<LoginModalProps> = ({ if (!isWaitingSigning) { close(); } + + setShowSelection(false); + setTou({ + nonEmployee: false, + oldEnough: false, + privacy: false, + readRules: false, + }); }; const handleLogin = (provider: string) => { @@ -77,35 +137,99 @@ const LoginModal: React.FC<LoginModalProps> = ({ setIsWaitingSigning(true); }; - const renderContent = () => { - if (isWaitingSigning) return <p>Waiting for you to sign-in...</p>; - if (isLoadingProviders || !publicKeyHashHex) return <p>Loading...</p>; - if (!providers?.length) return <p>No providers available.</p>; - + const renderConsentContent = () => { return ( - <div className="flex flex-col gap-6 items-center"> - {providers.map((provider) => ( - <Button - className="w-96 h-16 flex items-center gap-4 capitalize" - key={provider} - onClick={() => handleLogin(provider)} - > - {providerIcons[provider]} {provider} - </Button> - ))} + <div className="text-left flex flex-col gap-4"> + <h1 className="text-5xl uppercase">Tournament Consent</h1> + {/** + * Read the Rules + */} + <CheckBoxInput + consent="readRules" + label="I confirm that I read, understand and agree to the Hydra Doom Tournament Official Contest Rules." + tou={tou} + setTou={setTou} + /> + + {/** + * Are old enough to play + */} + <CheckBoxInput + consent="oldEnough" + label="I confirm that I am 18 years of age or older." + tou={tou} + setTou={setTou} + /> + + {/** + * Not an IOG Employee. + */} + <CheckBoxInput + consent="nonEmployee" + label="I confirm that I am not an employee of Input Output Global, Inc. or + its subsidiaries, affiliates or other disqualifying entities (as + more fully described in the Hydra Doom Tournament Official Contest + Rules), or an immediate family member or person living in the same + household of the foregoing." + tou={tou} + setTou={setTou} + /> + + {/** + * Privacy consent + */} + <CheckBoxInput + consent="privacy" + label="I understand my information will be securely stored and processed in + accordance with our Privacy Policy. By providing my information, I + consent to the collection, use and processing of my information as + described in this form and in the Hydra Doom Tournament Official + Contest Rules." + tou={tou} + setTou={setTou} + /> + <Button + className="place-self-center my-8 w-96 h-16 flex items-center gap-4 capitalize" + onClick={() => setShowSelection(true)} + disabled={Object.values(tou).includes(false)} + > + Continue + </Button> </div> ); }; - return ( - <Modal isOpen={isOpen} close={handleClose}> - <div className="text-center text-4xl flex flex-col gap-8"> + const renderLoginContent = () => { + if (isWaitingSigning) return <p>Waiting for you to sign-in...</p>; + if (isLoadingProviders || !publicKeyHashHex) return <p>Loading...</p>; + if (!providers?.length) return <p>No providers available.</p>; + + return ( + <> <h1 className="text-5xl uppercase">Tournament Login</h1> <p className="mb-4"> Please select a provider to login with. If you don't have an account you can create one with the provider of your choice. </p> - {renderContent()} + <div className="flex flex-col gap-6 items-center"> + {providers.map((provider) => ( + <Button + className="w-96 h-16 flex items-center gap-4 capitalize" + key={provider} + onClick={() => handleLogin(provider)} + > + {providerIcons[provider]} {provider} + </Button> + ))} + </div> + </> + ); + }; + + return ( + <Modal isOpen={isOpen} close={handleClose}> + <div className="text-center text-4xl flex flex-col gap-8 py-4"> + {showSelection ? renderLoginContent() : renderConsentContent()} </div> </Modal> ); diff --git a/src/components/LoginModal/indes.ts b/src/components/LoginModal/index.ts similarity index 100% rename from src/components/LoginModal/indes.ts rename to src/components/LoginModal/index.ts diff --git a/src/components/Speedometer/Speedometer.tsx b/src/components/Speedometer/Speedometer.tsx index 77e07a9..6a2bba6 100644 --- a/src/components/Speedometer/Speedometer.tsx +++ b/src/components/Speedometer/Speedometer.tsx @@ -25,7 +25,10 @@ const Speedometer: FC<SpeedometerProps> = ({ maxSpeed, transactions }) => { }} /> <div className="absolute -bottom-1 right-9"> - {Math.max(maxSpeed, transactions)} + {Intl.NumberFormat("en-US", { + notation: "compact", + maximumFractionDigits: 4, + }).format(Math.max(maxSpeed, transactions))} </div> </div> <div className="text-center">{transactions}</div> diff --git a/src/constants.ts b/src/constants.ts index 9f6c70d..60d7b7a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,6 @@ export const IS_LOCAL = !!import.meta.env.VITE_IS_LOCAL; export const CABINET_KEY = import.meta.env.VITE_CABINET_KEY; export const NETWORK_ID = Number(import.meta.env.VITE_NETWORK_ID); -export const GLOBAL_MAX_SPEED = 30 * 100; export const HANDLE_CACHE_KEY = "player-handle-cache"; export const MAX_SPEED = 40; export const REGIONS = [ diff --git a/src/types.ts b/src/types.ts index fa78ef3..069d0c9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,6 +13,7 @@ export interface GameStatistics { as_of: { secs_since_epoch: number; nanos_since_epoch: number }; bytes_per_second: number; kills_per_minute: number; + peak_txs_per_second: number; suicides_per_minute: number; total_bots: number; total_bytes: number;