diff options
author | Zhongheng Liu <z.liu@outlook.com.gr> | 2024-01-20 12:06:34 +0200 |
---|---|---|
committer | Zhongheng Liu <z.liu@outlook.com.gr> | 2024-01-20 12:06:34 +0200 |
commit | 56feab2ea11d3da6032e02b7845235ef1e01c9f0 (patch) | |
tree | 44f33a9bae7863e0ca10e8121d83fb27451ddb64 | |
parent | 7d4953fea6d50eb15120f2be34ffa22302fb4713 (diff) | |
download | epq-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.tsx | 9 | ||||
-rw-r--r-- | src/Chat/Chat.tsx | 241 | ||||
-rw-r--r-- | src/Login/Login.css | 4 | ||||
-rw-r--r-- | src/Login/Login.tsx | 278 | ||||
-rw-r--r-- | src/MessageDisplay/MessageDisplay.tsx | 111 | ||||
-rw-r--r-- | src/type/messageTypes.ts | 57 | ||||
-rw-r--r-- | src/type/userTypes.ts | 10 |
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; }; |