From ae420cbea337a092cd4b7615613cb900728876b0 Mon Sep 17 00:00:00 2001 From: lza_menace Date: Tue, 10 Jan 2023 12:43:26 -0800 Subject: [PATCH] add chat --- package-lock.json | 17 +++- package.json | 1 + src/index.js | 5 +- src/styles/buttons.css | 1 + src/styles/main.css | 2 +- src/styles/sections.css | 82 +++++++++++++++++- src/template/Sections.jsx | 170 ++++++++++++++++++++++++++++++++------ 7 files changed, 248 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8e5bc48..646c709 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "ethers": "^5.7.2", - "lodash": "^4.17.21", "rc-slider": "^10.1.0", "react": "^18.2.0", "react-awesome-button": "^7.0.4", @@ -23,6 +22,7 @@ "react-scroll": "^1.8.9", "react-scroll-parallax": "^3.3.2", "react-ts-typewriter": "^0.1.8-b", + "react-use-websocket": "^4.2.0", "typescript": "^4.9.4", "wagmi": "^0.9.2" } @@ -21998,6 +21998,15 @@ "react-dom": "^18.2.0" } }, + "node_modules/react-use-websocket": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-4.2.0.tgz", + "integrity": "sha512-ZovaTlc/tWX6a590fi3kMWImhyoWj46BWJWvO5oucZJzRnVVhYtes2D9g+5MKXjSdR7Es3456hB89v4/1pcBKg==", + "peerDependencies": { + "react": ">= 18.0.0", + "react-dom": ">= 18.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -41890,6 +41899,12 @@ "react-dom": "^18.2.0" } }, + "react-use-websocket": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-4.2.0.tgz", + "integrity": "sha512-ZovaTlc/tWX6a590fi3kMWImhyoWj46BWJWvO5oucZJzRnVVhYtes2D9g+5MKXjSdR7Es3456hB89v4/1pcBKg==", + "requires": {} + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 487d53b..429ac2b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react-scroll": "^1.8.9", "react-scroll-parallax": "^3.3.2", "react-ts-typewriter": "^0.1.8-b", + "react-use-websocket": "^4.2.0", "typescript": "^4.9.4", "wagmi": "^0.9.2" }, diff --git a/src/index.js b/src/index.js index d5b5aa1..26d0156 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,10 @@ ReactDOM.createRoot(document.getElementById("root")).render(
- +
diff --git a/src/styles/buttons.css b/src/styles/buttons.css index eb80084..8ecbe49 100644 --- a/src/styles/buttons.css +++ b/src/styles/buttons.css @@ -36,4 +36,5 @@ --button-danger-color-hover: #6d2d7e; --button-danger-color-active: #632973; --button-danger-border: none; + z-index: 0; } \ No newline at end of file diff --git a/src/styles/main.css b/src/styles/main.css index 5d32c0a..eaf3a81 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -70,7 +70,7 @@ nav { -moz-backdrop-filter: blur(3px); font-size: 14px; line-height: 14px; - z-index: 999; + z-index: 10; } nav::before { diff --git a/src/styles/sections.css b/src/styles/sections.css index 8444b17..40aa4b9 100644 --- a/src/styles/sections.css +++ b/src/styles/sections.css @@ -30,6 +30,10 @@ #heroText { font-size: 2.5em; } + + #gameStats { + display: none; + } } #heroImage { @@ -190,4 +194,80 @@ .nopad a { color: #ff00ea; -} \ No newline at end of file +} + +.m2 { + margin: 10px; +} + +#statsbox { + position: fixed; + bottom: 0; + left: 0; + background-color: black; + text-align: left; + width: 49%; + margin: 0; + max-height: 400px; + overflow-y: auto; + word-wrap: break-word; + display: flex; + flex-direction: column; + min-width: 300px; + box-shadow: -1px -1px 20px teal; +} + +#trollbox { + position: fixed; + bottom: 0; + right: 0; + background-color: black; + text-align: left; + width: 49%; + margin: 0; + max-height: 400px; + overflow-y: auto; + word-wrap: break-word; + display: flex; + flex-direction: column-reverse; + box-shadow: -1px -1px 20px #ff00ea; + min-width: 300px; +} + +@media (max-width: 600px) { + #statsbox { + min-width: 200px; + } + #trollbox { + min-width: 200px; + } +} + +#trollbox ul li, #trollbox span, #trollbox a { + list-style-type: none; + font-family: monospace; +} + +#trollbox form { + margin: .8em 0; +} + +#trollbox form input[type="text"] { + width: 60%; +} + +.fromAddress { + margin: 4px 4px 0 0; +} + +.messageLine { + width: 100%; + flex-direction: column-reverse; +} + +.messageDate { + font-size: 8px; + float: right; + line-height: 1em; +} + diff --git a/src/template/Sections.jsx b/src/template/Sections.jsx index fadb3d8..58f847c 100644 --- a/src/template/Sections.jsx +++ b/src/template/Sections.jsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { ethers, BigNumber } from 'ethers'; import { useAccount, useContractReads, useContractRead, usePrepareContractWrite, useContractWrite, useWaitForTransaction } from 'wagmi'; import { Parallax } from 'react-parallax'; import { NotificationContainer, NotificationManager } from 'react-notifications'; import { AwesomeButton } from 'react-awesome-button'; +import useWebSocket, { ReadyState } from 'react-use-websocket'; import Typewriter from 'react-ts-typewriter'; import Slider from 'rc-slider'; @@ -112,6 +113,7 @@ class GenerateBombOverlay extends React.Component { class GameMap extends React.Component { constructor(props) { super(props); + this.state = {showStats: false} } shorten(s) { @@ -123,21 +125,9 @@ class GameMap extends React.Component { <>

Metaverse Map

- {this.props.unaboomersRadicalized} / {this.props.unaboomerMaxSupply} Unaboomers radicalized and ready to terrorize the metaverse.
- {this.props.unaboomerMaxSupply - this.props.unaboomerMaxSurvivorCount - this.props.unaboomersRadicalized} potential Unaboomers remain.
+ {this.props.unaboomersRadicalized} / {this.props.unaboomerMaxSupply} Unaboomers radicalized and ready to terrorize the metaverse. {this.props.unaboomersKilled} killed.
This round will end and the project will fully start when {this.props.unaboomerMaxSurvivorCount} or less Unaboomers remain standing.

- {this.props.unaboomersRadicalized > 0 && ( -
- -
- )} {this.props.leaderKillCount > 0 && (
@@ -158,6 +148,35 @@ class GameMap extends React.Component {
+
+ {this.state.showStats && ( +
+ +

+ Kill Leader:
+ + {this.shorten(this.props.leaderAddress)} + +

+

+ {this.props.leaderKillCount} kills +

+
+

+ Active Unaboomers: {this.props.unaboomersRadicalized - this.props.unaboomersKilled}
+ Dead Unaboomers: {this.props.unaboomersKilled}
+ : Active Bombs: {this.props.bombsAssembled - this.props.bombsExploded}
+ : Exploded Bombs: {this.props.bombsExploded}
+ : Dud Bombs: {this.props.bombsExploded - this.props.unaboomersKilled}
+

+
+ )} + +
) } @@ -516,17 +535,20 @@ function Section3(props) { } {isConnected && options.unaboomerMaxSupply > 0 && ( - + <> + + + ) || isConnected && (

loading...

@@ -537,12 +559,108 @@ function Section3(props) { ) } +function Trollbox(props) { + const [showChat, setShowChat] = useState(false); + const [history, setHistory] = useState(() => { + const saved = localStorage.getItem('trollboxHistory'); + return saved || JSON.stringify([]) + }); + + const { sendJsonMessage, readyState } = useWebSocket(props.ws, { + onOpen: () => { + console.log('WebSocket connection established.'); + }, + onMessage: (msg) => { + let h = JSON.parse(history); + if (h.length > 500) { + h = h.splice(-500); + } + h.push(JSON.parse(msg.data)); + setHistory(JSON.stringify(h)); + }, + shouldReconnect: () => true + }); + + + useEffect(() => { + localStorage.setItem('trollboxHistory', history); + }, [history]) + + const connectionStatus = { + [ReadyState.CONNECTING]: 'Connecting', + [ReadyState.OPEN]: 'Open', + [ReadyState.CLOSING]: 'Closing', + [ReadyState.CLOSED]: 'Closed', + [ReadyState.UNINSTANTIATED]: 'Uninstantiated', + }[readyState]; + + function send(event) { + const message = (new FormData(event.target)).get("message"); + if (message) { + sendJsonMessage({ + from: props.address, + message + }) + } + event.target.reset(); + return false; + } + + function shorten(s) { + return s.slice(-6) + } + + return ( + <> +
+ {readyState == ReadyState.OPEN && showChat && ( + <> + +
{ + ev.preventDefault(); + send(ev); + }}> + + +
+
    + {JSON.parse(history).length > 0 && JSON.parse(history).map((message, idx) => ( +
  • + + + {shorten(message.from)} + + : + {message.message} +
  • + ))} +
+ + + )} + + {readyState == ReadyState.OPEN && !showChat && ( + + )} + + {readyState == ReadyState.CONNECTING && ( +

connecting to trollbox...

+ )} +
+ + ) +} + export function AllSections(props) { return ( <> - + ) } \ No newline at end of file