summaryrefslogtreecommitdiff
path: root/trongleposting-client/src
diff options
context:
space:
mode:
Diffstat (limited to 'trongleposting-client/src')
-rw-r--r--trongleposting-client/src/App.css78
-rw-r--r--trongleposting-client/src/App.js126
-rw-r--r--trongleposting-client/src/index.js10
-rw-r--r--trongleposting-client/src/utils/generate_gruvbox.js14
4 files changed, 228 insertions, 0 deletions
diff --git a/trongleposting-client/src/App.css b/trongleposting-client/src/App.css
new file mode 100644
index 0000000..8e7ba34
--- /dev/null
+++ b/trongleposting-client/src/App.css
@@ -0,0 +1,78 @@
+body {
+ font-family: Consolas, monaco, monospace;
+ color: #fbf1c7;
+ margin: 0;
+ background-color: #3c3836;
+}
+
+.container {
+ max-width: 900px;
+ width: 80%;
+
+ border: 1px solid #b16286;
+ border-radius: 8px;
+ margin: 0;
+ padding: 24px;
+
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ -moz-transform: translateX(-50%) translateY(-50%);
+ -webkit-transform: translateX(-50%) translateY(-50%);
+ transform: translateX(-50%) translateY(-50%);
+ background-color: #282828;
+
+ box-shadow: rgb( 0, 0, 0, 0.6) 6px 45px 45px -12px;
+}
+
+.chat {
+ border-bottom: 1px solid #d65d0e;
+ height: 200px;
+ overflow-y: scroll;
+ padding-bottom: 12px;
+}
+
+* {
+ scrollbar-width: thin;
+ scrollbar-color: #d5c4a1 rgba(0,0,0,0);
+}
+
+*::-webkit-scrollbar {
+ width: 4px;
+}
+
+*::-webkit-scrollbar-track {
+ background: rgba(0,0,0,0);
+}
+
+*::-webkit-scrollbar-thumb {
+ background-color: #d5c4a1;
+ border-radius: 12px;
+}
+
+.input {
+ font-size: 16px;
+ font-size: max(16px, 1em);
+ font-family: inherit;
+ padding: 0.25em 0.5em;
+ background-color: rgba(0,0,0,0);
+ border: none;
+ border-bottom: 1px solid #83a598;
+ color: #d5c4a1;
+ margin-top: 12px;
+ margin-bottom: 12px;
+ display: block;
+}
+
+.input:focus {
+ outline: none;
+}
+
+.button {
+ padding: 12px;
+ cursor: pointer;
+ border: 1px solid #8ec07c;
+ color: #8ec07c;
+ border-radius: 8px;
+ display: inline-block;
+} \ No newline at end of file
diff --git a/trongleposting-client/src/App.js b/trongleposting-client/src/App.js
new file mode 100644
index 0000000..37643c5
--- /dev/null
+++ b/trongleposting-client/src/App.js
@@ -0,0 +1,126 @@
+import { useRef, useState, useEffect } from "react";
+import { generateGruvboxFromString } from "./utils/generate_gruvbox";
+import "./App.css";
+
+function App() {
+ const [socket, setSocket] = useState(null);
+ const [posts, setPosts] = useState([]);
+ const postsRef = useRef([]);
+ const [content, setContent] = useState("");
+ const [username, setUsername] = useState("");
+ const [error, setError] = useState("");
+ const [color, setColor] = useState("");
+
+ const scrollToBottomOfChat = () => {
+ const objDiv = document.getElementById("chat");
+ objDiv.scrollTop = objDiv.scrollHeight;
+ };
+
+ useEffect(() => {
+ const protocol = document.location.protocol === "https:" ? "wss:" : "ws:";
+ let socket;
+
+ fetch("/posts")
+ .then((r) => r.json())
+ .then((msgs) => {
+ postsRef.current = msgs;
+ setPosts(postsRef.current);
+ scrollToBottomOfChat();
+ })
+ .then(() => {
+ socket = new WebSocket(`${protocol}//${document.location.host}/`);
+ socket.addEventListener("open", () => {
+ socket.send("ping");
+ });
+ socket.addEventListener("message", (msg) => {
+ const { data } = msg;
+ if (data === "pong") return;
+
+ const chat = JSON.parse(msg.data);
+ if (chat.author) {
+ postsRef.current = [...postsRef.current, chat];
+ setPosts(postsRef.current);
+ scrollToBottomOfChat();
+ }
+ });
+
+ setSocket(socket);
+ });
+
+ return () => {
+ socket.close();
+ };
+ }, []);
+
+ useEffect(() => {
+ setColor(generateGruvboxFromString(username));
+ }, [username]);
+
+ const addPost = () => {
+ setError("");
+ if (socket) {
+ fetch("/posts", {
+ method: "POST",
+ headers: { Content: "application/json" },
+ body: JSON.stringify({ author: username, message: content }),
+ }).then(() => setContent(""));
+ }
+ };
+
+ return (
+ <div className="container" style={{ border: `1px solid ${color}` }}>
+ <div style={{ textAlign: "center" }}>
+ <h2>TronglePosting in ELisp</h2>
+ </div>
+ <div id="chat" className="chat">
+ <p>Welcome!</p>
+ {posts.map((post) => (
+ <div
+ key={post.id}
+ style={{
+ lineBreak: "normal",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ }}
+ >
+ <div>
+ <span style={{ color: generateGruvboxFromString(post.author) }}>
+ {post.author}:{" "}
+ </span>
+ <span>{post.message}</span>
+ </div>
+ <div style={{ fontSize: "x-small" }}>
+ [{new Date(post.date).toLocaleString()}]
+ </div>
+ </div>
+ ))}
+ </div>
+ <div>
+ <input
+ placeholder={"Username"}
+ className="input"
+ style={{ color }}
+ onChange={(e) => {
+ setUsername(e.target.value);
+ }}
+ value={username}
+ ></input>
+ <textarea
+ placeholder={"Message"}
+ className="input"
+ onChange={(e) => setContent(e.target.value)}
+ value={content}
+ rows={1}
+ cols={50}
+ ></textarea>
+ <div className="button" onClick={addPost}>
+ Post
+ </div>
+ {error ? <p style={{ color: "red" }}>{error}</p> : null}
+ </div>
+ </div>
+ );
+}
+
+export default App;
diff --git a/trongleposting-client/src/index.js b/trongleposting-client/src/index.js
new file mode 100644
index 0000000..7173ce5
--- /dev/null
+++ b/trongleposting-client/src/index.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+ReactDOM.render(
+ <React.StrictMode>
+ <App />
+ </React.StrictMode>,
+ document.getElementById('root')
+); \ No newline at end of file
diff --git a/trongleposting-client/src/utils/generate_gruvbox.js b/trongleposting-client/src/utils/generate_gruvbox.js
new file mode 100644
index 0000000..7ff109b
--- /dev/null
+++ b/trongleposting-client/src/utils/generate_gruvbox.js
@@ -0,0 +1,14 @@
+const gruvboxColors = [
+ "#b8bb26",
+ "#fabd2f",
+ "#83a598",
+ "#d3869b",
+ "#8ec07c",
+ "#458588",
+ "#cc241d",
+ "#d65d0e",
+ "#bdae93",
+];
+
+export const generateGruvboxFromString = (string) =>
+ gruvboxColors[Array.from(string).map((x) => x.charCodeAt(0)).reduce((a, x) => a+x, 0) % gruvboxColors.length]; \ No newline at end of file