add: added some ui improvements

This commit is contained in:
Lukian 2025-05-12 20:48:55 +02:00
parent 8d6e1f8618
commit b707e2cead
21 changed files with 236 additions and 121 deletions

View file

@ -84,6 +84,10 @@ router.get('/:name', async (req, res) => {
return res.status(404).send({ error: 'Emoji not found' });
}
if (!fs.existsSync(`data/emojis/${emoji[0].file}`)) {
return res.sendFile(path.join(__dirname, `../images/default.png`), { headers: { 'Content-Type': 'image' } });
}
res.sendFile(path.join(__dirname, `../data/emojis/${emoji[0].file}`), { headers: { 'Content-Type': 'image' } });
});

View file

@ -81,7 +81,7 @@ router.get('/:username/pfp', async (req, res) => {
const pfp = user[0].pfp;
if (!pfp || !fs.existsSync(path.join(__dirname, `../data/pfps/${pfp}`))) {
return res.sendFile(path.join(__dirname, `../images/default-pfp.png`), { headers: { 'Content-Type': 'image' } });
return res.sendFile(path.join(__dirname, `../images/default.png`), { headers: { 'Content-Type': 'image' } });
}
res.sendFile(path.join(__dirname, `../data/pfps/${pfp}`), { headers: { 'Content-Type': 'image' } });

View file

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Before After
Before After

Binary file not shown.

View file

@ -148,31 +148,30 @@ export default function MessageComponent({ message, user, channel }: {
<img src="/le_poisson_steve.png" alt="Le poisson steve" className="fish" />
)}
</div>
{channel?.owner_id == user?.id || user?.admin == 1 || user?.username == message.username ? (
user?.id === message.user_id || user?.id === channel?.owner_id || user?.admin === 1) && (
<button onClick={handleDelete}>Delete</button>
) : (
<p>In <Link to={`/c/${message.channel_name}`}>{message.channel_name}</Link></p>
{(channel?.owner_id == user?.id || user?.admin == 1 || user?.id == message.user_id) && (
<button onClick={handleDelete} className="forum-button">Delete</button>
)}
{channel && user && !reply && (
<button onClick={() => setReply(true)}>Reply</button>
<button onClick={() => setReply(true)} className="forum-button">Reply</button>
)}
{reply && (
<form onSubmit={(e) => handleReply(e, message.id)} className="message-form">
<form onSubmit={(e) => handleReply(e, message.id)} className="form-horizontal">
<input
className="forum-input"
type="text"
placeholder="Message"
value={replyContent}
onChange={(e) => setReplyContent(e.target.value)}
ref={ref}
/>
<button type="submit">Send</button>
<button type="submit" className="forum-button">Send</button>
{searchedUsers.length > 0 && (
<div className="mentions">
<div className="input-menu">
{searchedUsers.map((user) => (
<div key={user.id} className="mention">
<div key={user.id} className="input-menu-item">
<Link to={`/u/${user.username}`}>{user.username}</Link>
<button
className="forum-button"
type="button"
onClick={() => {
setReplyContent(
@ -189,12 +188,13 @@ export default function MessageComponent({ message, user, channel }: {
</div>
)}
{searchedEmojis.length > 0 && (
<div className="emojis">
<div className="input-menu">
{searchedEmojis.map((emoji) => (
<div key={emoji.id} className="search-emoji">
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emojis-emoji" />
<div key={emoji.id} className="input-menu-item">
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emoji" />
<span>:{emoji.name}:</span>
<button
className="forum-button"
type="button"
onClick={() => {
setReplyContent(
@ -210,7 +210,7 @@ export default function MessageComponent({ message, user, channel }: {
))}
</div>
)}
<button onClick={() => setReply(false)}>Cancel</button>
<button onClick={() => setReply(false)} className="forum-button">Cancel</button>
</form>
)}
{message.has_replies == true && (

View file

@ -25,7 +25,7 @@ export default function TopBar({ user }: { user: User | undefined }) {
{user ? (
<div className="topbar-right">
<Link to={`/u/${user.username}`}>{user.username}</Link>
<button onClick={() => {
<button className="forum-button" onClick={() => {
localStorage.removeItem("token")
window.location.reload()
}}>
@ -49,7 +49,7 @@ export default function TopBar({ user }: { user: User | undefined }) {
{user ? (
<div className="burger-menu-user">
<Link to={`/u/${user.username}`}>{user.username}</Link>
<button onClick={() => {
<button className="forum-button" onClick={() => {
localStorage.removeItem("token")
window.location.reload()
}}>
@ -64,7 +64,7 @@ export default function TopBar({ user }: { user: User | undefined }) {
)}
</div>
)}
<button onClick={() => setBurgerMenuOpen(!burgerMenuOpen)}>
<button onClick={() => setBurgerMenuOpen(!burgerMenuOpen)} className="forum-button">
{burgerMenuOpen ? "Close" : "Menu"}
</button>
</div>

View file

@ -1,8 +1,27 @@
@font-face {
font-family: DepartureMono;
src: url("/DepartureMonoNerdFontMono-Regular.otf") format("opentype");
}
* {
font-family: DepartureMono;
}
html {
background: linear-gradient(#FFE0E3, #f59ebb);
min-height: 100vh;
}
input[type=file] {
display: none;
}
label {
display: flex;
justify-content: center;
align-items: center;
}
.forum-page {
display: flex;
flex-direction: column;
@ -23,6 +42,82 @@ html {
background-color: #fff6fd;
}
.center {
justify-content: center;
align-items: center;
}
.forum-button {
padding: 5px;
border-radius: 0px;
background-color: #ebb8e6;
border: 1px solid #270722;
box-shadow: none;
font-size: 13px;
}
.forum-button span {
font-size: 13px;
}
.forum-button:hover {
background-color: #d4b4f0;
}
.forum-input {
padding: 5px;
border-radius: 0px;
background-color: #fff6fd;
border: 1px solid #270722;
box-shadow: none;
}
.form-horizontal {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 5px;
position: relative;
}
.form-vertical {
display: flex;
flex-direction: column;
justify-content: start;
align-items: left;
gap: 5px;
position: relative;
}
.input-menu {
z-index: 1;
position: absolute;
top: 100%;
border: 1px solid #270722;
background-color: #fff6fd;
padding: 5px;
display: flex;
flex-direction: column;
justify-content: start;
align-items: left;
gap: 5px;
min-width: 20%;
}
.input-menu-item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: top;
gap: 5px;
}
.emoji {
max-width: 1em;
max-height: 1em;
}
.cat {
width: 100px;
}
@ -50,6 +145,32 @@ html {
color: #e4d9ec;
}
.forum-button {
border: 1px solid #a678af;
background-color: #847996;
color: black;
}
.forum-button:hover {
background-color: #584f68;
}
.forum-input {
border: 1px solid #a678af;
background-color: #1b1b23;
color: #e4d9ec;
}
.forum-input::placeholder {
color: #e4d9ec;
}
.input-menu {
border: 1px solid #a678af;
background-color: #1b1b23;
color: #e4d9ec;
}
a {
color: #b88edc;
text-decoration: underline;

View file

@ -198,32 +198,37 @@ export default function ChannelPage({socket}: {socket: WebSocket}) {
<img src="/cat.jpg" alt="cat" className="cat" />
)}
{user?.admin == 1 && (
<button onClick={purgeChannel}>
<button onClick={purgeChannel} className="forum-button">
Purge Channel
</button>
)}
{user?.admin == 1 && (
<button onClick={deleteChannel}>
<button onClick={deleteChannel} className="forum-button">
Delete Channel
</button>
)}
{token ? (
<form onSubmit={handleSubmit} className="message-form">
<form onSubmit={handleSubmit} className="form-horizontal">
<input
className="forum-input"
type="text"
placeholder="Message"
value={message}
onChange={(e) => setMessage(e.target.value)}
ref={ref}
/>
<input type="file" name="attachment" id="attachment" />
<button type="submit">Send</button>
<label htmlFor="attachment">
<span className="forum-button">Upload Attachment</span>
<input type="file" name="attachment" id="attachment" className="forum-input" />
</label>
<button type="submit" className="forum-button">Send</button>
{searchedUsers.length > 0 && (
<div className="mentions">
<div className="input-menu">
{searchedUsers.map((user) => (
<div key={user.id} className="mention">
<div key={user.id} className="input-menu-item">
<Link to={`/u/${user.username}`}>{user.username}</Link>
<button
className="forum-button"
type="button"
onClick={() => {
setMessage(
@ -240,12 +245,13 @@ export default function ChannelPage({socket}: {socket: WebSocket}) {
</div>
)}
{searchedEmojis.length > 0 && (
<div className="emojis">
<div className="input-menu">
{searchedEmojis.map((emoji) => (
<div key={emoji.id} className="search-emoji">
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emojis-emoji" />
<div key={emoji.id} className="input-menu-item">
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emoji" />
<span>:{emoji.name}:</span>
<button
className="forum-button"
type="button"
onClick={() => {
setMessage(
@ -280,7 +286,7 @@ export default function ChannelPage({socket}: {socket: WebSocket}) {
/>
))}
{messages.length == maxMessageToShown && (
<button onClick={() => setMaxMessageToShown(maxMessageToShown + 10)}>Show more</button>
<button onClick={() => setMaxMessageToShown(maxMessageToShown + 10)} className="forum-button">Show more</button>
)}
</div>
) : (

View file

@ -79,6 +79,7 @@ export default function ChannelsPage({socket}: {socket: WebSocket}) {
<h2>Channels</h2>
<Link to="/create-channel">Create channel</Link>
<input
className="forum-input"
type="text"
placeholder="Search channels"
value={search}
@ -89,7 +90,7 @@ export default function ChannelsPage({socket}: {socket: WebSocket}) {
<li key={channel.id}>
<Link to={`/c/${channel.name}`}>{channel.name}</Link>
{user?.admin == 1 && (
<button onClick={() => deleteChannel(channel.name)}>
<button onClick={() => deleteChannel(channel.name)} className="forum-button">
Delete
</button>
)}

View file

@ -41,25 +41,27 @@ export default function CreateChannel() {
<TopBar user={user}/>
<div className="forum-section">
<h1>Create Channel</h1>
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit} className="form-horizontal">
<p>
{!/^[a-zA-Z0-9-_]+$/.test(name) && name.length != 0 && (
<span>Channel name can only contain letters, numbers, - and _</span>
)}
</p>
<input
className="forum-input"
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
className="forum-input"
type="text"
placeholder="Description"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
<button type="submit" disabled={!/^[a-zA-Z0-9-_]+$/.test(name)}>
<button type="submit" disabled={!/^[a-zA-Z0-9-_]+$/.test(name)} className="forum-button">
Create
</button>
</form>

View file

@ -52,20 +52,24 @@ export default function CreateEmoji() {
<TopBar user={user}/>
<div className="forum-section">
<h1>Create Emoji</h1>
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit} className="form-horizontal">
<p>
{!/^[a-zA-Z0-9-_]+$/.test(name) && name.length != 0 && (
<span>Emoji name can only contain letters, numbers, - and _</span>
)}
</p>
<input
className="forum-input"
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<label htmlFor="emoji" className="forum-button">
<span>Upload Emoji</span>
<input type="file" name="emoji" id="emoji" accept="image/*"/>
<button type="submit" disabled={!/^[a-zA-Z0-9-_]+$/.test(name)}>
</label>
<button type="submit" disabled={!/^[a-zA-Z0-9-_]+$/.test(name)} className="forum-button">
Create
</button>
</form>

View file

@ -105,26 +105,29 @@ export default function EditProfile() {
<TopBar user={user}/>
<div className="forum-section">
<h2>Edit Profile Picture</h2>
<form>
<form className="form-horizontal">
<label htmlFor="pfp">
<span className="forum-button">Upload new profile picture</span>
<input type="file" name="pfp" id="pfp" accept="image/*" />
<button onClick={uploadPfp}>Save</button>
<button onClick={deletePfp}>Delete</button>
</label>
<button onClick={uploadPfp} className="forum-button">Save</button>
<button onClick={deletePfp} className="forum-button">Delete</button>
</form>
</div>
<div className="forum-section">
<h2>Edit Username</h2>
<form>
<input type="text" name="username" id="username" placeholder={user?.username} />
<button onClick={editUsername}>Save</button>
<form className="form-horizontal">
<input type="text" name="username" id="username" placeholder={user?.username} className="forum-input" />
<button onClick={editUsername} className="forum-button">Save</button>
</form>
</div>
<div className="forum-section">
<h2>Edit Password</h2>
<form>
<input type="password" name="old-password" id="old-password" placeholder="Old password" />
<input type="password" name="password" id="password" placeholder="New password" />
<input type="password" name="confirm-password" id="confirm-password" placeholder="Confirm new password" />
<button onClick={editPassword}>Save</button>
<form className="form-horizontal">
<input type="password" name="old-password" id="old-password" placeholder="Old password" className="forum-input" />
<input type="password" name="password" id="password" placeholder="New password" className="forum-input" />
<input type="password" name="confirm-password" id="confirm-password" placeholder="Confirm new password" className="forum-input" />
<button onClick={editPassword} className="forum-button">Save</button>
</form>
</div>
</div>

View file

@ -84,6 +84,7 @@ export default function EmojisPage({socket}: {socket: WebSocket}) {
<h2>Emojis</h2>
<Link to="/create-emoji">Create emoji</Link>
<input
className="forum-input"
type="text"
placeholder="Search emojis"
value={search}
@ -92,10 +93,10 @@ export default function EmojisPage({socket}: {socket: WebSocket}) {
<ul>
{emojis?.sort().filter((emoji) => emoji.name.toLowerCase().includes(search.toLowerCase())).map((emoji) => (
<li key={emoji.id}>
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emoji" />
<img src={`/api/emojis/${emoji.name}`} alt={emoji.name} className="emoji-fat" />
<span>:{emoji.name}:</span>
{user?.admin == 1 && (
<button onClick={() => deleteEmoji(emoji.name)}>
<button onClick={() => deleteEmoji(emoji.name)} className="forum-button">
Delete
</button>
)}

View file

@ -199,6 +199,7 @@ export default function Home({socket}: {socket: WebSocket}) {
<div className="channels-search">
<h3>Search channels</h3>
<input
className="forum-input"
type="text"
placeholder="Search channels"
value={search}

View file

@ -24,22 +24,24 @@ export default function Login() {
return (
<div className="forum-page">
<TopBar user={undefined}/>
<div className="forum-section">
<div className="forum-section center">
<h2>Login</h2>
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit} className="form-vertical">
<input
className="forum-input"
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
className="forum-input"
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Login</button>
<button type="submit" className="forum-button">Login</button>
</form>
<Link to="/register">Register</Link>
</div>

View file

@ -31,27 +31,27 @@ export default function Register () {
return (
<div className="forum-page">
<TopBar user={undefined}/>
<div className="forum-section">
<div className="forum-section center">
<h2>Register</h2>
<form onSubmit={handleSubmit}>
<p>
<form onSubmit={handleSubmit} className="form-vertical">
{!/^[a-zA-Z0-9-_]+$/.test(username) && username.length != 0 && (
<span>Username can only contain letters, numbers, - and _</span>
<p>Username can only contain letters, numbers, - and _</p>
)}
</p>
<input
className="forum-input"
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
className="forum-input"
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit" disabled={!/^[a-zA-Z0-9-_]+$/.test(username)}>
<button type="submit" disabled={!/^[a-zA-Z0-9-_]+$/.test(username)} className="forum-button">
Register
</button>
</form>

View file

@ -132,14 +132,14 @@ export default function UserPage({socket}: {socket: WebSocket}) {
<Link to="/edit-profile">Edit profile</Link>
)}
{user?.admin == 1 && (
<div>
<button onClick={deleteUser}>
<button onClick={deleteUser} className="forum-button">
Delete user
</button>
<button onClick={deleteUserPfp}>
)}
{user?.admin == 1 && (
<button onClick={deleteUserPfp} className="forum-button">
Delete profile picture
</button>
</div>
)}
</div>
<div className="forum-section">

View file

@ -78,6 +78,7 @@ export default function UsersPage({socket}: {socket: WebSocket}) {
<div className="forum-section">
<h2>Users</h2>
<input
className="forum-input"
type="text"
placeholder="Search users"
value={search}
@ -88,7 +89,7 @@ export default function UsersPage({socket}: {socket: WebSocket}) {
<li key={user.id}>
<Link to={`/u/${user.username}`}>{user.username}</Link>
{thisUser?.admin == 1 && (
<button onClick={() => deleteUser(user.username)}>
<button onClick={() => deleteUser(user.username)} className="forum-button">
Delete
</button>
)}

View file

@ -3,55 +3,6 @@
position: relative;
}
.mentions {
position: absolute;
top: 100%;
border: 1px solid #270722;
padding: 5px;
display: flex;
flex-direction: column;
justify-content: start;
align-items: left;
gap: 5px;
min-width: 20%;
background-color: white;
}
.mention {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: top;
gap: 5px;
}
.emojis {
position: absolute;
top: 100%;
border: 1px solid #270722;
padding: 5px;
display: flex;
flex-direction: column;
justify-content: start;
align-items: left;
gap: 5px;
min-width: 20%;
background-color: white;
}
.search-emoji {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: top;
gap: 5px;
}
.emojis-emoji {
max-width: 1em;
max-height: 1em;
}
.login-prompt {
margin: 16px 0;
display: flex;

View file

@ -1,4 +1,4 @@
.emoji {
max-width: 5em;
max-height: 5em;
.emoji-fat {
max-width: 2em;
max-height: 2em;
}

View file

@ -2,6 +2,7 @@
justify-content: space-between;
align-items: center;
flex-direction: row;
height: 45px;
}
.topbar-left {
@ -9,6 +10,17 @@
justify-content: space-between;
align-items: center;
gap: 10px;
z-index: 2;
}
.topbar-center {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
left: 0;
z-index: 1;
}
.topbar-right {
@ -16,6 +28,7 @@
justify-content: space-between;
align-items: center;
gap: 10px;
z-index: 2;
}
.topbar-user-pfp {
@ -46,12 +59,17 @@
.topbar {
flex-direction: column;
align-items: center;
height: auto;
}
.topbar-left {
display: none;
}
.topbar-center {
position: relative;
}
.topbar-right {
display: none;
}