aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZhongheng Liu <z.liu@outlook.com.gr>2024-01-20 12:06:34 +0200
committerZhongheng Liu <z.liu@outlook.com.gr>2024-01-20 12:06:34 +0200
commit56feab2ea11d3da6032e02b7845235ef1e01c9f0 (patch)
tree44f33a9bae7863e0ca10e8121d83fb27451ddb64
parent7d4953fea6d50eb15120f2be34ffa22302fb4713 (diff)
downloadepq-web-56feab2ea11d3da6032e02b7845235ef1e01c9f0.tar.gz
epq-web-56feab2ea11d3da6032e02b7845235ef1e01c9f0.tar.bz2
epq-web-56feab2ea11d3da6032e02b7845235ef1e01c9f0.zip
format and use more aggressive tabstop widths
-rw-r--r--src/App.tsx9
-rw-r--r--src/Chat/Chat.tsx241
-rw-r--r--src/Login/Login.css4
-rw-r--r--src/Login/Login.tsx278
-rw-r--r--src/MessageDisplay/MessageDisplay.tsx111
-rw-r--r--src/type/messageTypes.ts57
-rw-r--r--src/type/userTypes.ts10
7 files changed, 366 insertions, 344 deletions
diff --git a/src/App.tsx b/src/App.tsx
index 47d2f05..e5d3328 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -90,15 +90,6 @@ const App = ({
}
});
};
- // if (!username) {
- // var newName = prompt(home.userNamePrompt) as string;
- // while (!validateName(newName)) {
- // console.log(newName);
-
- // prompt("Username invalid! Please enter again.") as string;
- // }
- // setUsername(newName);
- // }
if (!login) {
return <></>;
} else
diff --git a/src/Chat/Chat.tsx b/src/Chat/Chat.tsx
index 00e0d0f..7aff06f 100644
--- a/src/Chat/Chat.tsx
+++ b/src/Chat/Chat.tsx
@@ -1,9 +1,9 @@
import React, {
- ReactElement,
- useContext,
- useEffect,
- useRef,
- useState,
+ ReactElement,
+ useContext,
+ useEffect,
+ useRef,
+ useState,
} from "react";
import { MessageDisplay } from "../MessageDisplay/MessageDisplay";
import { Client } from "@stomp/stompjs";
@@ -13,117 +13,128 @@ import strings from "../Intl/strings.json";
import { LangContext } from "../context";
import { connectionAddress, endpoints } from "../consts";
const Chat = ({ user }: { user: string }): React.ReactElement => {
- const lang = useContext(LangContext);
- const chatPage = strings[lang].chat;
- const [messages, setMessages] = useState<ReactElement[]>([]);
- let stompClientRef = useRef(
- new Client({
- brokerURL: connectionAddress,
- })
- );
- // TODO solve issue with non-static markup
- stompClientRef.current.onConnect = (frame) => {
- stompClientRef.current.subscribe(endpoints.subscription, (message) => {
- console.log(`Collected new message: ${message.body}`);
- const messageBody = JSON.parse(message.body) as Message;
- console.log(messageBody);
- setMessages((message) => {
- return message.concat([
- <MessageDisplay
- key={`${messageBody.type}@${messageBody.timeMillis}`}
- {...messageBody}
- />,
- ]);
- });
- });
- stompClientRef.current.publish({
- body: JSON.stringify({
- type: MessageType.HELLO,
- fromUserId: user,
- toUserId: "everyone",
- content: `${user} has joined the server!`,
- timeMillis: Date.now(),
- }),
- destination: endpoints.destination,
- });
- };
+ const lang = useContext(LangContext);
+ const chatPage = strings[lang].chat;
+ const [messages, setMessages] = useState<ReactElement[]>([]);
+ let stompClientRef = useRef(
+ new Client({
+ brokerURL: connectionAddress,
+ })
+ );
+ // TODO solve issue with non-static markup
+ stompClientRef.current.onConnect = (frame) => {
+ stompClientRef.current.subscribe(
+ endpoints.subscription,
+ (message) => {
+ console.log(
+ `Collected new message: ${message.body}`
+ );
+ const messageBody = JSON.parse(
+ message.body
+ ) as Message;
+ console.log(messageBody);
+ setMessages((message) => {
+ return message.concat([
+ <MessageDisplay
+ key={`${messageBody.type}@${messageBody.timeMillis}`}
+ {...messageBody}
+ />,
+ ]);
+ });
+ }
+ );
+ stompClientRef.current.publish({
+ body: JSON.stringify({
+ type: MessageType.HELLO,
+ fromUserId: user,
+ toUserId: "everyone",
+ content: `${user} has joined the server!`,
+ timeMillis: Date.now(),
+ }),
+ destination: endpoints.destination,
+ });
+ };
+ // Generic error handlers
+ stompClientRef.current.onWebSocketError = (error) => {
+ console.error("Error with websocket", error);
+ };
- // Generic error handlers
- stompClientRef.current.onWebSocketError = (error) => {
- console.error("Error with websocket", error);
- };
+ stompClientRef.current.onStompError = (frame) => {
+ console.error(
+ "Broker reported error: " + frame.headers["message"]
+ );
+ console.error("Additional details: " + frame.body);
+ };
- stompClientRef.current.onStompError = (frame) => {
- console.error("Broker reported error: " + frame.headers["message"]);
- console.error("Additional details: " + frame.body);
- };
-
- // Button press event handler.
- const sendData = () => {
- const entryElement: HTMLInputElement = document.getElementById(
- "data-entry"
- ) as HTMLInputElement;
- if (entryElement.value === "") {
- return;
- }
- const messageData: Message = {
- type: MessageType.MESSAGE,
- fromUserId: user,
- toUserId: "everyone",
- content: entryElement.value,
- timeMillis: Date.now(),
- };
- console.log(
- `STOMP connection status: ${stompClientRef.current.connected}`
- );
- stompClientRef.current.publish({
- body: JSON.stringify(messageData),
- destination: endpoints.destination,
- headers: {
- "Content-Type": "application/json; charset=utf-8",
- },
- });
- entryElement.value = "";
- };
- useEffect(() => {
- // Stomp client is disconnected after each re-render
- // This should be actively avoided
- stompClientRef.current.activate();
- return () => {
- stompClientRef.current.deactivate();
- };
- }, []);
- // https://www.w3schools.com/jsref/obj_keyboardevent.asp
- document.addEventListener("keydown", (ev: KeyboardEvent) => {
- if (ev.key === "Enter") {
- sendData();
- }
- });
- useEffect(() => {
- try {
- const elem = document.querySelector(".chat-inner-wrapper");
- if (elem) {
- elem.scrollTop = elem.scrollHeight;
- } else {
- }
- } catch (err) {
- console.log("error encountered");
- }
- return () => {};
- }, [messages]);
- return (
- <fieldset className="chat">
- <legend>
- Logged in as <b>{user}</b>
- </legend>
- <div className="chat-inner-wrapper">{messages}</div>
- <span className="entry-box">
- <input id="data-entry"></input>
- <button onClick={() => sendData()}>
- {chatPage.sendButtonPrompt}
- </button>
- </span>
- </fieldset>
- );
+ // Button press event handler.
+ const sendData = () => {
+ const entryElement: HTMLInputElement = document.getElementById(
+ "data-entry"
+ ) as HTMLInputElement;
+ if (entryElement.value === "") {
+ return;
+ }
+ const messageData: Message = {
+ type: MessageType.MESSAGE,
+ fromUserId: user,
+ toUserId: "everyone",
+ content: entryElement.value,
+ timeMillis: Date.now(),
+ };
+ console.log(
+ `STOMP connection status: ${stompClientRef.current.connected}`
+ );
+ stompClientRef.current.publish({
+ body: JSON.stringify(messageData),
+ destination: endpoints.destination,
+ headers: {
+ "Content-Type":
+ "application/json; charset=utf-8",
+ },
+ });
+ entryElement.value = "";
+ };
+ useEffect(() => {
+ // Stomp client is disconnected after each re-render
+ // This should be actively avoided
+ stompClientRef.current.activate();
+ return () => {
+ stompClientRef.current.deactivate();
+ };
+ }, []);
+ // https://www.w3schools.com/jsref/obj_keyboardevent.asp
+ document.addEventListener("keydown", (ev: KeyboardEvent) => {
+ if (ev.key === "Enter") {
+ sendData();
+ }
+ });
+ useEffect(() => {
+ try {
+ const elem = document.querySelector(
+ ".chat-inner-wrapper"
+ );
+ if (elem) {
+ elem.scrollTop = elem.scrollHeight;
+ } else {
+ }
+ } catch (err) {
+ console.log("error encountered");
+ }
+ return () => {};
+ }, [messages]);
+ return (
+ <fieldset className="chat">
+ <legend>
+ Logged in as <b>{user}</b>
+ </legend>
+ <div className="chat-inner-wrapper">{messages}</div>
+ <span className="entry-box">
+ <input id="data-entry"></input>
+ <button onClick={() => sendData()}>
+ {chatPage.sendButtonPrompt}
+ </button>
+ </span>
+ </fieldset>
+ );
};
export default Chat;
diff --git a/src/Login/Login.css b/src/Login/Login.css
index 957484e..21bceff 100644
--- a/src/Login/Login.css
+++ b/src/Login/Login.css
@@ -1,3 +1,3 @@
.uname-error-text {
- color: red;
-} \ No newline at end of file
+ color: red;
+}
diff --git a/src/Login/Login.tsx b/src/Login/Login.tsx
index 6de932c..7f5ff79 100644
--- a/src/Login/Login.tsx
+++ b/src/Login/Login.tsx
@@ -4,138 +4,156 @@ import { LoginType } from "../context";
import { User } from "../type/userTypes";
import "./Login.css";
const encrypt = (rawPasswordString: string) => {
- // TODO Encryption method stub
- return rawPasswordString;
+ // TODO Encryption method stub
+ return rawPasswordString;
};
export const Login = ({
- setLogin,
+ setLogin,
}: {
- setLogin: (newLogin: LoginType | undefined) => void;
+ setLogin: (newLogin: LoginType | undefined) => void;
}): React.ReactElement => {
- const [valid, setValid] = useState<boolean | undefined>(true);
- const [validText, setValidText] = useState<string | undefined>();
- const registrationHandler = () => {
- const uname = (document.getElementById("username") as HTMLInputElement)
- .value;
- const passwd = encrypt(
- (document.getElementById("passwd") as HTMLInputElement).value
- );
- fetch(`http://${domain}:${port}${endpoints.user}`, {
- method: "POST",
- mode: "cors",
- headers: contentTypes.json,
- body: JSON.stringify({
- userName: uname,
- dateJoined: Date.now(),
- passwordHash: passwd,
- }),
- }).then((response) => {
- if (response.status === 400) {
- // 400 Bad request
- console.log("Username is taken or invalid!");
- setValid(false);
- setValidText("Username is taken or invalid!");
- } else if (response.status === 200) {
- // 200 OK
- const futureDate = new Date();
- futureDate.setHours(futureDate.getHours() + 2);
- setLogin({
- username: uname,
- lastSeen: Date.now(),
- validUntil: futureDate.getUTCMilliseconds(),
- });
- document.title = `IRC User ${uname}`;
- }
- });
- };
- // login button press handler
- const loginHandler = () => {
- const uname = (document.getElementById("username") as HTMLInputElement)
- .value;
- const passwd = encrypt(
- (document.getElementById("passwd") as HTMLInputElement).value
- );
- // async invocation of Fetch API
- fetch(`http://${domain}:${port}${endpoints.user}?name=${uname}`, {
- method: "GET",
- mode: "cors",
- })
- .then((res) => {
- if (res.status === 404) {
- console.log("404 not found encountered");
- throw new Error("Username does not exist");
- } else if (res.status === 200) {
- console.log("200 OK");
- }
- return res.json();
- })
- .then((userObject) => {
- if (!userObject) {
- return;
- }
- const user = userObject as User;
- const validLogin = passwd === user.passwordHash;
- if (!validLogin) {
- // login invalid
- throw new Error("Password incorrect!");
- } else {
- // login valid
- const validUntilDate: Date = new Date();
- validUntilDate.setHours(validUntilDate.getHours() + 2);
- setLogin({
- username: user.userName,
- lastSeen: user.lastSeen,
- validUntil: validUntilDate.getUTCMilliseconds(),
- });
- document.title = `IRC User ${uname}`;
- }
- })
- .catch((reason: Error) => {
- setValid(false);
- setValidText(reason.message);
- });
- };
- return (
- <div className="login">
- <fieldset>
- <legend>Login window</legend>
- <p className="uname-error-text">
- {valid && valid !== undefined ? "" : validText}
- </p>
- <label htmlFor="username">Username: </label>
- <br />
- <input id="username" type="text"></input>
- <br />
- <label htmlFor="passwd">Password: </label>
- <br />
- <input id="passwd" type="password"></input>
- <br />
- <button
- type="submit"
- onClick={() => {
- loginHandler();
- }}
- >
- Login
- </button>
- <button
- type="submit"
- onClick={() => {
- registrationHandler();
- }}
- >
- Register
- </button>
- <button
- type="submit"
- onClick={() => {
- setLogin(undefined);
- setValid(false);
- }}
- >
- Logout
- </button>
- </fieldset>
- </div>
- );
+ const [valid, setValid] = useState<boolean | undefined>(true);
+ const [validText, setValidText] = useState<string | undefined>();
+ const registrationHandler = () => {
+ const uname = (
+ document.getElementById("username") as HTMLInputElement
+ ).value;
+ const passwd = encrypt(
+ (document.getElementById("passwd") as HTMLInputElement)
+ .value
+ );
+ fetch(`http://${domain}:${port}${endpoints.user}`, {
+ method: "POST",
+ mode: "cors",
+ headers: contentTypes.json,
+ body: JSON.stringify({
+ userName: uname,
+ dateJoined: Date.now(),
+ passwordHash: passwd,
+ }),
+ }).then((response) => {
+ if (response.status === 400) {
+ // 400 Bad request
+ console.log("Username is taken or invalid!");
+ setValid(false);
+ setValidText("Username is taken or invalid!");
+ } else if (response.status === 200) {
+ // 200 OK
+ const futureDate = new Date();
+ futureDate.setHours(futureDate.getHours() + 2);
+ setLogin({
+ username: uname,
+ lastSeen: Date.now(),
+ validUntil: futureDate.getUTCMilliseconds(),
+ });
+ document.title = `IRC User ${uname}`;
+ }
+ });
+ };
+ // login button press handler
+ const loginHandler = () => {
+ const uname = (
+ document.getElementById("username") as HTMLInputElement
+ ).value;
+ const passwd = encrypt(
+ (document.getElementById("passwd") as HTMLInputElement)
+ .value
+ );
+ // async invocation of Fetch API
+ fetch(
+ `http://${domain}:${port}${endpoints.user}?name=${uname}`,
+ {
+ method: "GET",
+ mode: "cors",
+ }
+ )
+ .then((res) => {
+ if (res.status === 404) {
+ console.log(
+ "404 not found encountered"
+ );
+ throw new Error(
+ "Username does not exist"
+ );
+ } else if (res.status === 200) {
+ console.log("200 OK");
+ }
+ return res.json();
+ })
+ .then((userObject) => {
+ if (!userObject) {
+ return;
+ }
+ const user = userObject as User;
+ const validLogin = passwd === user.passwordHash;
+ if (!validLogin) {
+ // login invalid
+ throw new Error("Password incorrect!");
+ } else {
+ // login valid
+ const validUntilDate: Date = new Date();
+ validUntilDate.setHours(
+ validUntilDate.getHours() + 2
+ );
+ setLogin({
+ username: user.userName,
+ lastSeen: user.lastSeen,
+ validUntil: validUntilDate.getUTCMilliseconds(),
+ });
+ document.title = `IRC User ${uname}`;
+ }
+ })
+ .catch((reason: Error) => {
+ setValid(false);
+ setValidText(reason.message);
+ });
+ };
+ return (
+ <div className="login">
+ <fieldset>
+ <legend>Login window</legend>
+ <p className="uname-error-text">
+ {valid && valid !== undefined
+ ? ""
+ : validText}
+ </p>
+ <label htmlFor="username">Username: </label>
+ <br />
+ <input id="username" type="text"></input>
+ <br />
+ <label htmlFor="passwd">Password: </label>
+ <br />
+ <input id="passwd" type="password"></input>
+ <br />
+ <button
+ disabled={!valid}
+ type="submit"
+ onClick={() => {
+ loginHandler();
+ }}
+ >
+ Login
+ </button>
+ <button
+ disabled={!valid}
+ type="submit"
+ onClick={() => {
+ registrationHandler();
+ }}
+ >
+ Register
+ </button>
+ <button
+ disabled={valid}
+ type="submit"
+ onClick={() => {
+ setLogin(undefined);
+ setValid(false);
+ }}
+ >
+ Logout
+ </button>
+ </fieldset>
+ </div>
+ );
};
diff --git a/src/MessageDisplay/MessageDisplay.tsx b/src/MessageDisplay/MessageDisplay.tsx
index 19853ca..92e9e79 100644
--- a/src/MessageDisplay/MessageDisplay.tsx
+++ b/src/MessageDisplay/MessageDisplay.tsx
@@ -4,57 +4,64 @@ import { LangContext } from "../context";
import strings from "../Intl/strings.json";
import "./MessageDisplay.css";
export const MessageDisplay = ({
- type,
- fromUserId,
- toUserId,
- content,
- timeMillis,
+ type,
+ fromUserId,
+ toUserId,
+ content,
+ timeMillis,
}: Message): React.ReactElement<Message> => {
- const dateTime: Date = new Date(timeMillis);
- const lang = useContext(LangContext);
- const msgPage = strings[lang].chat;
- /* FIXED funny error
- * DESCRIPTION
- * The line below was
- * return (<p>[{dateTime.toLocaleString(Intl.DateTimeFormat().resolvedOptions().timeZone)}]...</p>)
- * The line incorrectly generated a value of "UTC" as the parameter to toLocaleString()
- * While "UTC" is an accepted string value, in EEST, aka. "Europe/Athens" timezone string is not an acceptable parameter.
- * This caused the return statement to fail, and the message fails to render, despite it being correctly committed to the db.
- * Funny clown moment 🤡
- */
- const timeString = `${
- dateTime.getHours() > 12
- ? dateTime.getHours() - 12
- : dateTime.getHours()
- }:${dateTime.getMinutes()} ${dateTime.getHours() > 12 ? "PM" : "AM"}`;
- switch (type) {
- case MessageType.HELLO as MessageType:
- return (
- <p className="msg">
- [{timeString}]{" "}
- {msgPage.joinMessage.replace("$userName", fromUserId)}
- </p>
- );
- case MessageType.MESSAGE as MessageType:
- return (
- <p className="msg">
- [{timeString}]{" "}
- {msgPage.serverMessage
- .replace("$userName", fromUserId)
- .replace("$content", content)}
- </p>
- );
- case MessageType.DATA as MessageType:
- return <></>;
- case MessageType.CHNAME as MessageType:
- return <></>;
- default:
- console.error("Illegal MessageType reported!");
- return (
- <p className="msg-err">
- [{timeString}] **THIS MESSAGE CANNOT BE CORRECTLY SHOWN
- BECAUSE THE CLIENT ENCOUNTERED AN ERROR**
- </p>
- );
- }
+ const dateTime: Date = new Date(timeMillis);
+ const lang = useContext(LangContext);
+ const msgPage = strings[lang].chat;
+ /* FIXED funny error
+ * DESCRIPTION
+ * The line below was
+ * return (<p>[{dateTime.toLocaleString(Intl.DateTimeFormat().resolvedOptions().timeZone)}]...</p>)
+ * The line incorrectly generated a value of "UTC" as the parameter to toLocaleString()
+ * While "UTC" is an accepted string value, in EEST, aka. "Europe/Athens" timezone string is not an acceptable parameter.
+ * This caused the return statement to fail, and the message fails to render, despite it being correctly committed to the db.
+ * Funny clown moment 🤡
+ */
+ const timeString = `${
+ dateTime.getHours() > 12
+ ? dateTime.getHours() - 12
+ : dateTime.getHours()
+ }:${dateTime.getMinutes()} ${dateTime.getHours() > 12 ? "PM" : "AM"}`;
+ switch (type) {
+ case MessageType.HELLO as MessageType:
+ return (
+ <p className="msg">
+ [{timeString}]{" "}
+ {msgPage.joinMessage.replace(
+ "$userName",
+ fromUserId
+ )}
+ </p>
+ );
+ case MessageType.MESSAGE as MessageType:
+ return (
+ <p className="msg">
+ [{timeString}]{" "}
+ {msgPage.serverMessage
+ .replace(
+ "$userName",
+ fromUserId
+ )
+ .replace("$content", content)}
+ </p>
+ );
+ case MessageType.DATA as MessageType:
+ return <></>;
+ case MessageType.CHNAME as MessageType:
+ return <></>;
+ default:
+ console.error("Illegal MessageType reported!");
+ return (
+ <p className="msg-err">
+ [{timeString}] **THIS MESSAGE CANNOT BE
+ CORRECTLY SHOWN BECAUSE THE CLIENT
+ ENCOUNTERED AN ERROR**
+ </p>
+ );
+ }
};
diff --git a/src/type/messageTypes.ts b/src/type/messageTypes.ts
index 3243d25..f83594a 100644
--- a/src/type/messageTypes.ts
+++ b/src/type/messageTypes.ts
@@ -1,51 +1,46 @@
export const enum MessageType {
- MESSAGE = "MESSAGE",
- CHNAME = "CHNAME",
- HELLO = "HELLO",
- DATA = "DATA",
+ MESSAGE = "MESSAGE",
+ CHNAME = "CHNAME",
+ HELLO = "HELLO",
+ DATA = "DATA",
}
export enum SystemMessageCode {
- REQ,
- RES,
- ERR,
+ REQ,
+ RES,
+ ERR,
}
export type HistoryFetchResult = {
- count: number;
- items: Array<ChatMessage>;
+ count: number;
+ items: Array<ChatMessage>;
};
export type ErrorResult = {
- text: string;
+ text: string;
};
export type TimestampSendRequest = {
- ts: number;
+ ts: number;
};
export type SystemMessage = {
- code: SystemMessageCode;
- data: HistoryFetchResult | ErrorResult | TimestampSendRequest;
+ code: SystemMessageCode;
+ data: HistoryFetchResult | ErrorResult | TimestampSendRequest;
};
export type ChatMessage = {
- fromUserId: string;
- toUserId: string;
- content: string;
- timeMillis: number;
+ fromUserId: string;
+ toUserId: string;
+ content: string;
+ timeMillis: number;
};
export type HelloMessage = {
- fromUserId: string;
- timeMillis: number;
+ fromUserId: string;
+ timeMillis: number;
};
export type DataMessage = {};
export type Message = {
- type: MessageType;
- // data: SystemMessage | ChatMessage | HelloMessage
- fromUserId: string;
- toUserId: string;
- content: string;
- timeMillis: number;
+ type: MessageType;
+ // data: SystemMessage | ChatMessage | HelloMessage
+ fromUserId: string;
+ toUserId: string;
+ content: string;
+ timeMillis: number;
};
-export const acceptedLangs = [
- "en_US",
- "zh_TW",
- "el_GR",
- "ar_SA"
-] as const;
+export const acceptedLangs = ["en_US", "zh_TW", "el_GR", "ar_SA"] as const;
export type LangType = (typeof acceptedLangs)[number];
diff --git a/src/type/userTypes.ts b/src/type/userTypes.ts
index 7d03592..b888745 100644
--- a/src/type/userTypes.ts
+++ b/src/type/userTypes.ts
@@ -1,7 +1,7 @@
export type User = {
- id: number;
- userName: string;
- dateJoined: number;
- lastSeen: number;
- passwordHash: string;
+ id: number;
+ userName: string;
+ dateJoined: number;
+ lastSeen: number;
+ passwordHash: string;
};