CSS styling overhaul

FIXED Issue with rendering time - minutes now in fixed 2-digit format
ADD Sidebar Topbar components to help with UI/UX
ADD Avatar and Menu PNG files for style
TODO Improve styling
ET AL removed redundant code
This commit is contained in:
Zhongheng Liu 2024-01-31 21:07:45 +02:00
commit 4117c11c35
No known key found for this signature in database
16 changed files with 261 additions and 66 deletions

View file

@ -1,6 +1,6 @@
body {
background-color: black;
color: #00FF33;
/* background-color: black;
color: #00FF33; */
margin: 1%;
min-height: 100vh;
}

View file

@ -7,6 +7,8 @@ import strings from "./Intl/strings.json";
import { LangContext, LoginContext, LoginType } from "./context";
import { contentTypes, domain, endpoints, port } from "./consts";
import { Login } from "./Login/Login";
import { Sidebar } from "./Sidebar/Sidebar";
import { Topbar } from "./Topbar/Topbar";
// what we "in the business" call type gymnastics
const Wrapper = (): React.ReactElement => {
const [lang, setLang] = useState<LangType>("en_US");
@ -16,11 +18,19 @@ const Wrapper = (): React.ReactElement => {
? `IRC logged in as ${login.username}`
: "IRC Chat";
}, [login]);
const [sidebarEnabled, setSidebarEnabled] = useState(false);
return (
<LangContext.Provider value={lang}>
<h1>{strings[lang].homepage.title}</h1>
<p>{strings[lang].homepage.description}</p>
<LoginContext.Provider value={login}>
<Topbar
setSidebarEnable={(enabled: boolean) =>
setSidebarEnabled(enabled)
}
></Topbar>
<Sidebar
isEnabled={sidebarEnabled}
setEnable={(enabled: boolean) => setSidebarEnabled(enabled)}
></Sidebar>
{/* callbacks for altering the Lang/Login contexts */}
<Login
setLogin={(value) => {
@ -97,8 +107,8 @@ const App = ({
})
.then((responseBody: { success: boolean }) => {
if (responseBody.success) {
// TODO Put new username response true handler method stub
} else {
// TODO Put new username response true handler method stub
} else {
console.error(
"Server POST message failed."
);

View file

@ -12,6 +12,14 @@
}
.chat {
/* float: left; */
/* min-height: 80vh; */
position: relative;
box-shadow:
0 2.8px 2.2px rgba(0, 0, 0, 0.034),
0 6.7px 5.3px rgba(0, 0, 0, 0.048),
0 12.5px 10px rgba(0, 0, 0, 0.06),
0 22.3px 17.9px rgba(0, 0, 0, 0.072),
0 41.8px 33.4px rgba(0, 0, 0, 0.086),
0 100px 80px rgba(0, 0, 0, 0.12)
}

View file

@ -1,6 +1,6 @@
{
"variableNames": ["userName", "content"],
"acceptedLangs": ["en_US", "zh_TW", "el_GR"],
"acceptedLangs": ["en_US", "zh_TW", "el_GR", "ar_SA"],
"en_US": {
"homepage": {
"userNamePrompt": "Your username: ",

View file

@ -1,3 +1,4 @@
.uname-error-text {
color: red;
}
.login {}

View file

@ -5,64 +5,61 @@ import strings from "../Intl/strings.json";
import "./MessageDisplay.css";
import { queryByRole } from "@testing-library/react";
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() >= 10
? dateTime.getMinutes()
: `0${dateTime.getMinutes().toString()}`
} ${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>
);
}
};

View file

@ -0,0 +1,5 @@
.avatar {
width: 100%;
display: flex;
justify-content:center;
}

View file

@ -0,0 +1,9 @@
import "./Avatar.css";
import placeholderImage from "./placeholder.jpg";
export const Avatar = () => {
return (
<div className="avatar">
<img src={placeholderImage} width="75px"></img>
</div>
);
};

View file

@ -0,0 +1,50 @@
import "../Sidebar.css";
import { Avatar } from "./Avatar";
export const SidebarMenuItem = ({
text,
href,
handler,
}: {
text: string;
href?: string;
handler?: () => void;
}) => {
return (
<div
className="sidebar-menu-item"
onClick={
handler
? () => {
handler();
}
: () => {}
}
>
<li>
<span>
<i>{href ? <a href={href}>{text}</a> : text}</i>
</span>
</li>
<hr></hr>
</div>
);
};
export const SidebarMenu = ({
exitHandler,
}: {
exitHandler: (enabled: boolean) => void;
}) => {
return (
<div className="sidebar-menu">
<Avatar></Avatar>
<SidebarMenuItem text="My Account"></SidebarMenuItem>
<SidebarMenuItem text="Personalisation"></SidebarMenuItem>
<SidebarMenuItem text="Language"></SidebarMenuItem>
<SidebarMenuItem text="Server Configuration"></SidebarMenuItem>
<SidebarMenuItem
text="Return to homepage"
handler={() => exitHandler(false)}
></SidebarMenuItem>
</div>
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

47
src/Sidebar/Sidebar.css Normal file
View file

@ -0,0 +1,47 @@
.sidebar {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
animation: fadeIn, 5s;
z-index: 10;
}
.sidebar-menu-item {
margin: 5px;
transition: all 0.5s;
}
.sidebar-menu-item:hover {
background-color: rgba(0, 0, 0, 0.2);
box-shadow: 5px 5px rgba(0, 0, 0, 0.5);
}
.sidebar-content {
background-color: white;
width: max(15%, 200px);
height: 100%;
box-shadow: 10px 10px rgba(0, 0, 0, 0.5);
}
@keyframes fadeIn {
0% {background-color: transparent;}
100% {background-color: rgba(0, 0, 0, 0.5);;}
}
@-moz-keyframes fadeIn {
0% {background-color: transparent;}
100% {background-color: rgba(0, 0, 0, 0.5);;}
}
@-webkit-keyframes fadeIn {
0% {background-color: transparent;}
100% {background-color: rgba(0, 0, 0, 0.5);;}
}
@-o-keyframes fadeIn {
0% {background-color: transparent;}
100% {background-color: rgba(0, 0, 0, 0.5);;}
}
@-ms-keyframes fadeIn {
0% {background-color: transparent;}
100% {background-color: rgba(0, 0, 0, 0.5);;}
}

24
src/Sidebar/Sidebar.tsx Normal file
View file

@ -0,0 +1,24 @@
import { useState } from "react";
import "./Sidebar.css";
import { SidebarMenu } from "./Components/SidebarMenu";
export const Sidebar = ({
isEnabled,
setEnable,
}: {
isEnabled: boolean;
setEnable: (enabled: boolean) => void;
}) => {
return isEnabled ? (
<div className="sidebar">
<div className="sidebar-content">
<SidebarMenu
exitHandler={(value) => {
setEnable(value);
}}
></SidebarMenu>
</div>
</div>
) : (
<></>
);
};

10
src/Topbar/Topbar.css Normal file
View file

@ -0,0 +1,10 @@
.topbar {
width: 100vw;
height: 10%;
z-index: 5;
display: flex;
justify-content: baseline;
}
.topbar-span .children {
margin-left: 10px;
}

33
src/Topbar/Topbar.tsx Normal file
View file

@ -0,0 +1,33 @@
import "./Topbar.css";
import strings from "../Intl/strings.json";
import { useContext } from "react";
import { LangContext } from "../context";
import menu from "./menu.png";
export const Topbar = ({
setSidebarEnable,
}: {
setSidebarEnable: (enabled: boolean) => void;
}) => {
const lang = useContext(LangContext);
return (
<div className="topbar">
<img
onClick={() => {
setSidebarEnable(true);
}}
src={menu}
width="100px"
height="100px"
alt="Open Selection Menu"
></img>
<span className="topbar-span">
<h1 className="topbar-span children">
{strings[lang].homepage.title}
</h1>
{/* <p className="topbar-span children">
{strings[lang].homepage.description}
</p> */}
</span>
</div>
);
};

BIN
src/Topbar/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -42,5 +42,6 @@ export type Message = {
content: string;
timeMillis: number;
};
// Type gymnastics to provide dynamic ESLint support
export const acceptedLangs = ["en_US", "zh_TW", "el_GR", "ar_SA"] as const;
export type LangType = (typeof acceptedLangs)[number];