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;