Compare commits
8 Commits
992736167c
...
34f3552a40
| Author | SHA1 | Date | |
|---|---|---|---|
| 34f3552a40 | |||
| 2226d8bcfc | |||
| ba2178613b | |||
| c7d79fd5b8 | |||
| 7e23c3173a | |||
| 8fa8698c1e | |||
| df0b1f3573 | |||
| 3c3799dfd5 |
@@ -1,7 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
|
||||
<title>dicor</title>
|
||||
<link rel="stylesheet" type="text/css" href="dicor_files/index.css">
|
||||
</head>
|
||||
<body><script src="dicor_files/index.js"></script><div><div class="server-bar"><div class="guild"><div class="pillWrapper"><div class="pill unread"></div></div><div class="guildname">meow</div></div><div class="guild"><div class="pillWrapper"><div class="pill unread"></div></div><div class="guildname">mrrow</div></div><div class="guild"><div class="pillWrapper"><div class="pill unread"></div></div><div class="guildname">mrrp</div></div></div><div class="channel-bar"><h2>undefined</h2><div class="channel"><div class="pillWrapper"><div class="pill unread"></div></div><span class="channelname">meow</span></div></div><div class="messages"><div class="message bottom "><img class="messagePfp" width="48" height="48" src="dicor_files/pfp.png"><div class="messageAuthor"><span>Niko (defautluser0, 4160)</span> <div class="badge" style="--badge-color: #a200ff;">DEV</div> <div class="messageTimestamp">Today at 22:09</div></div><div class="messageContent"><span>buh</span></div></div><div class="message bottom "><img class="messagePfp" width="48" height="48" src="dicor_files/pfp2.png"><div class="messageAuthor"><span>Snow32 (snow32a, 240389667688512)</span> <div class="messageTimestamp">Today at 22:09</div></div><div class="messageContent"><span>g</span></div></div><div class="message "><img class="messagePfp" width="48" height="48" src="dicor_files/pfp.png"><div class="messageAuthor"><span>Niko (defautluser0, 4160)</span> <div class="badge" style="--badge-color: #a200ff;">DEV</div> <div class="messageTimestamp">Today at 22:10</div></div><div class="messageContent"><span>afsg</span></div></div><div class="message inline "><div class="messageContent"><span>sagdf</span></div></div><div class="message inline "><div class="messageContent"><span>a</span></div></div><div class="message inline "><div class="messageContent"><span>sg</span></div></div><div class="message inline "><div class="messageContent"><span>adsg</span></div></div><div class="message inline "><div class="messageContent"><span>asd</span></div></div><div class="message inline bottom "><div class="messageContent"><span>ga</span></div></div><div></div></div><div class="textbox"><div class="editor" contenteditable="true" data-text="Type here..."></div></div></div></body></html>
|
||||
@@ -1,209 +0,0 @@
|
||||
/* src/style.css */
|
||||
:root {
|
||||
--brand-color: #a200ff;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
color: #fff;
|
||||
background-color: #333;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.mentions {
|
||||
color: #fff;
|
||||
display: flex;
|
||||
background-color: #ff1717;
|
||||
border-radius: 50%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
padding: 2px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.channel-bar {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
padding: 5px;
|
||||
right: calc(100% - 250px);
|
||||
}
|
||||
|
||||
.pillWrapper {
|
||||
contain: layout size;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pill {
|
||||
opacity: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 0 4px 4px 0;
|
||||
width: 8px;
|
||||
height: 0;
|
||||
transition: height .2s, translate .3s, opacity .1s;
|
||||
}
|
||||
|
||||
.pill.unread {
|
||||
opacity: 1;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.pill.hover {
|
||||
opacity: 1;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.pill.open {
|
||||
opacity: 1;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.channel .mentions {
|
||||
position: relative;
|
||||
bottom: 27px;
|
||||
left: 85%;
|
||||
}
|
||||
|
||||
.channel .pillWrapper {
|
||||
position: relative;
|
||||
height: 17px;
|
||||
top: 9px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.channel .channelname {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.server-bar {
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.guild {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.guild .guildname {
|
||||
position: relative;
|
||||
top: -20px;
|
||||
right: -10px;
|
||||
}
|
||||
|
||||
.guild .pillWrapper {
|
||||
position: relative;
|
||||
top: 9px;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.messages {
|
||||
overflow: hidden scroll;
|
||||
position: absolute;
|
||||
width: calc(100% - 250px);
|
||||
height: calc(100% - 44px);
|
||||
padding: 5px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.message {
|
||||
position: relative;
|
||||
margin-bottom: 2px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.message.bottom {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.message.sending {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.message.mentionsyou {
|
||||
background-color: #b87d00;
|
||||
}
|
||||
|
||||
.message .messagePfp {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
border-radius: 100%;
|
||||
left: -50px;
|
||||
}
|
||||
|
||||
.message .messagePfp:active {
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.message .messageAuthor span {
|
||||
cursor: pointer;
|
||||
max-width: -moz-fit-content;
|
||||
max-width: fit-content;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.message .messageAuthor span:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.message .messageAuthor .messageTimestamp {
|
||||
font-weight: initial;
|
||||
color: #777;
|
||||
display: inline;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.message .messageContent {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.message .mention {
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
background-color: #0099ff54;
|
||||
border-radius: 3px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.message .mention:hover {
|
||||
background-color: #0099ff79;
|
||||
}
|
||||
|
||||
.message .badge {
|
||||
background-color: var(--badge-color);
|
||||
display: inline;
|
||||
border-radius: 10%;
|
||||
max-width: -moz-fit-content;
|
||||
max-width: fit-content;
|
||||
padding: 2px;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
.textbox {
|
||||
position: absolute;
|
||||
background-color: #222;
|
||||
width: calc(100% - 250px);
|
||||
padding: 3px;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.editor:empty:before {
|
||||
content: attr(data-text);
|
||||
color: gray;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 191 KiB |
36
serve.ts
36
serve.ts
@@ -2,7 +2,7 @@ import { SHA512_256, type ServerWebSocket } from "bun";
|
||||
import { SnowflakeGenerator } from "./snowflake.ts";
|
||||
|
||||
function getContentType(path: string): string {
|
||||
if (path.endsWith(".html")) return "text/html";
|
||||
if (path.endsWith(".html") || path.endsWith(".htm")) return "text/html";
|
||||
if (path.endsWith(".css")) return "text/css";
|
||||
if (path.endsWith(".js")) return "application/javascript";
|
||||
if (path.endsWith(".json")) return "application/json";
|
||||
@@ -32,18 +32,19 @@ enum OpcodesServerbound {
|
||||
}
|
||||
|
||||
interface user {
|
||||
name: string;
|
||||
name?: string;
|
||||
username: string;
|
||||
pfp: string;
|
||||
id: number;
|
||||
token: string;
|
||||
password: string;
|
||||
dev: boolean;
|
||||
pfp?: string;
|
||||
id: string;
|
||||
token?: string;
|
||||
password?: string;
|
||||
dev?: boolean;
|
||||
bot?: boolean;
|
||||
}
|
||||
|
||||
interface message {
|
||||
id: string;
|
||||
author: user;
|
||||
author: string | user; // server sends the user object but stores only the id for easier user.json modification
|
||||
content: string;
|
||||
timestamp: number;
|
||||
editedTimestamp?: number;
|
||||
@@ -120,10 +121,15 @@ const server = Bun.serve({
|
||||
data: user[0],
|
||||
})
|
||||
);
|
||||
let msgs = JSON.parse(JSON.stringify(messages));
|
||||
for (const ms of msgs) {
|
||||
const user = users.find(user => user.id === ms.author);
|
||||
ms.author = user || { username: "unknown-user", id: "0" };
|
||||
}
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
op: OpcodesClientbound.initMessages,
|
||||
data: messages,
|
||||
data: msgs,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
@@ -134,7 +140,7 @@ const server = Bun.serve({
|
||||
user = users.filter((user) => user.token === obj.data.token) as user[];
|
||||
if (user && user.length !== 0) {
|
||||
obj.op = OpcodesClientbound.updateMessages;
|
||||
obj.data.id = generator.generateSnowflake();
|
||||
obj.data.id = String(generator.generateSnowflake());
|
||||
obj.data.author = JSON.parse(JSON.stringify(user[0]));
|
||||
obj.data.author.password = null;
|
||||
obj.data.author.token = null;
|
||||
@@ -143,6 +149,7 @@ const server = Bun.serve({
|
||||
obj.data.author.dev = user[0].dev;
|
||||
ws.publish("messages", JSON.stringify(obj));
|
||||
ws.send(JSON.stringify(obj));
|
||||
obj.data.author = obj.data.author.id;
|
||||
messages.push(obj.data);
|
||||
Bun.write("./db/messages.json", JSON.stringify(messages));
|
||||
} else {
|
||||
@@ -186,7 +193,14 @@ const server = Bun.serve({
|
||||
path = "/index.html";
|
||||
}
|
||||
|
||||
if (url.pathname === "/ws") {
|
||||
if (path === "/pfps/" || path === "/pfps") {
|
||||
return new Response(
|
||||
"<center><h1>403 Forbidden</h1><hr><p>Bun</p></center>",
|
||||
{ status: 403, headers: { "Content-Type": "text/html" } }
|
||||
);
|
||||
}
|
||||
|
||||
if (path === "/ws") {
|
||||
const upgraded = server.upgrade(req, {
|
||||
// @ts-expect-error guhh whqt
|
||||
data: {
|
||||
|
||||
@@ -7,12 +7,19 @@ import { ServerBar } from "@components/ServerBar.tsx";
|
||||
export function App(): ReactElement {
|
||||
const [serverName, setServerName] = useState("undefined");
|
||||
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
||||
const chatbar = document.getElementById("editor");
|
||||
chatbar?.focus();
|
||||
chatbar?.onkeydown?.(e);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ServerBar setServerName={setServerName} currentServer={serverName} />
|
||||
<ChannelBar servername={serverName} />
|
||||
<RenderMessages />
|
||||
<Chatbar />
|
||||
<Chatbar channelName="undefined" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { user } from "@localtypes";
|
||||
import { sendChatMessage, token, self, getUsers, subscribeUsers } from "@ws";
|
||||
import { useRef, useState, useSyncExternalStore, type ReactElement } from "react";
|
||||
|
||||
export function Chatbar(): ReactElement {
|
||||
export function Chatbar({channelName}: {channelName: string}): ReactElement {
|
||||
const [message, setMessage] = useState("");
|
||||
const users: user[] = useSyncExternalStore(
|
||||
subscribeUsers,
|
||||
@@ -12,11 +12,11 @@ export function Chatbar(): ReactElement {
|
||||
const editorRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
if (event.key === "Enter" && !event.shiftKey && message.trim().length !== 0) {
|
||||
event.preventDefault();
|
||||
if (message.trim() !== "") {
|
||||
const msg = replaceMentions(message.trim());
|
||||
sendChatMessage({id: Date.now(), author: self, content: msg, timestamp: Date.now(), token: token!, hasSent: false});
|
||||
sendChatMessage({id: Date.now(), author: self, content: msg, timestamp: Date.now(), token: token!, hasSent: false, replyTo: undefined});
|
||||
console.log("Sent message:", msg);
|
||||
setMessage("");
|
||||
if (editorRef.current) {
|
||||
@@ -57,10 +57,12 @@ export function Chatbar(): ReactElement {
|
||||
<div
|
||||
ref={editorRef}
|
||||
className="editor"
|
||||
id="editor"
|
||||
contentEditable
|
||||
tabIndex={0}
|
||||
onKeyDown={handleKeyDown}
|
||||
onInput={handleChange}
|
||||
data-text="Type here..."
|
||||
data-text={`Message ${channelName}`}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
9
src/components/Icons.tsx
Normal file
9
src/components/Icons.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ReactElement } from "react";
|
||||
|
||||
export function ReplyIcon(): ReactElement {
|
||||
return (
|
||||
<svg className="icon" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" view-box="0 0 24 24">
|
||||
<path fill="currentColor" d="M2.3 7.3a1 1 0 0 0 0 1.4l5 5a1 1 0 0 0 1.4-1.4L5.42 9H11a7 7 0 0 1 7 7v4a1 1 0 1 0 2 0v-4a9 9 0 0 0-9-9H5.41l3.3-3.3a1 1 0 0 0-1.42-1.4l-5 5Z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { RefObject, useEffect, useRef, useSyncExternalStore, type ReactElement } from "react";
|
||||
import { user, message } from "@localtypes";
|
||||
import { subscribeUsers, getUsers, subscribeMessages, getMessages, self } from "@ws";
|
||||
import { RefObject, useEffect, useRef, useState, useSyncExternalStore, type ReactElement } from "react";
|
||||
import { user, message, id } from "@localtypes";
|
||||
import { subscribeUsers, getUsers, subscribeMessages, getMessages, self, replyToMessage } from "@ws";
|
||||
import { ReplyIcon } from "@components/Icons.tsx";
|
||||
|
||||
function formatUnixTimestamp(ts: number) {
|
||||
const date = new Date(ts);
|
||||
@@ -23,13 +24,11 @@ function formatUnixTimestamp(ts: number) {
|
||||
|
||||
export function RenderMessages(): ReactElement {
|
||||
let currentAuthor: user | null = null;
|
||||
const messages: (message | null)[] = useSyncExternalStore(
|
||||
const messages: (null | message)[] = useSyncExternalStore(
|
||||
subscribeMessages,
|
||||
getMessages
|
||||
);
|
||||
|
||||
console.log(messages);
|
||||
|
||||
const bottomRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -53,11 +52,13 @@ export function RenderMessages(): ReactElement {
|
||||
key={message.id}
|
||||
author={message.author}
|
||||
content={message.content}
|
||||
inline={inline}
|
||||
id={message.id}
|
||||
inline={inline && !message.replyTo}
|
||||
bottom={isLastMessageFromAuthor}
|
||||
timestamp={message.timestamp}
|
||||
hasSent={message.hasSent}
|
||||
mention={message.content.includes(`<@${self.id}>`) || message.content.includes("@everyone") || message.content.includes("@here")}
|
||||
repliesTo={message.replyTo}
|
||||
/>
|
||||
);
|
||||
} else return ""
|
||||
@@ -67,11 +68,39 @@ export function RenderMessages(): ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
function Message({author, content, inline = false, bottom = false, timestamp = 0, hasSent, mention}: {author: user, content: string, inline?: boolean, bottom?: boolean, timestamp?: number, hasSent: boolean, mention: boolean}): ReactElement {
|
||||
function Message({
|
||||
author,
|
||||
content,
|
||||
id,
|
||||
inline = false,
|
||||
bottom = false,
|
||||
timestamp = 0,
|
||||
hasSent = false,
|
||||
mention,
|
||||
repliesTo,
|
||||
roleColor,
|
||||
}: {
|
||||
author: user,
|
||||
content: string,
|
||||
id: id,
|
||||
inline?: boolean,
|
||||
bottom?: boolean,
|
||||
timestamp?: number,
|
||||
hasSent: boolean,
|
||||
mention: boolean,
|
||||
repliesTo?: id,
|
||||
roleColor?: string,
|
||||
}): ReactElement {
|
||||
const users: user[] = useSyncExternalStore(
|
||||
subscribeUsers,
|
||||
getUsers
|
||||
);
|
||||
const messages: (message | null)[] = useSyncExternalStore(
|
||||
subscribeMessages,
|
||||
getMessages
|
||||
)
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
|
||||
function formatContent(content: string): ReactElement[] {
|
||||
const parts = content.split('\n'); // Split the content into parts by newlines
|
||||
@@ -122,10 +151,28 @@ function Message({author, content, inline = false, bottom = false, timestamp = 0
|
||||
}
|
||||
const formattedContent = formatContent(content);
|
||||
|
||||
let replyContent: ReactElement[] = [];
|
||||
const replyTo = messages.find(msg => msg?.id === repliesTo);
|
||||
if (replyTo) {
|
||||
replyContent = formatContent(replyTo.content);
|
||||
}
|
||||
|
||||
function reply() {
|
||||
console.log(id);
|
||||
replyToMessage(id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`message ${inline ? "inline" : ""} ${bottom ? "bottom" : ""} ${hasSent ? "" : "sending"} ${mention ? "mentionsyou" : ""}`}>
|
||||
<div
|
||||
className={`message ${inline ? "inline" : ""} ${bottom ? "bottom" : ""} ${hasSent ? "" : "sending"} ${mention ? "mentionsyou" : ""}`}
|
||||
style={{["--role-color" as any]: roleColor ?? "#fff"}}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
>
|
||||
{hover && <MessageHoverActions reply={reply} />}
|
||||
{replyTo && replyContent ? <Reply author={replyTo.author} content={replyContent} /> : ""}
|
||||
{!inline ? <img className="messagePfp" src={`/pfps/${author.pfp}.png`} width={48} height={48}></img> : ""}
|
||||
{!inline ? <div className="messageAuthor"><span>{self.dev ? `${author.name} (${author.username}, ${author.id})` : author.name ?? author.username}</span> {author.dev ? <Badge text="DEV" /> : ""} <Timestamp ts={timestamp} /></div> : ""}
|
||||
{!inline ? <div className="messageAuthor"><span>{self.dev ? `${author.name ?? "no name"} (${author.username}, ${author.id})` : author.name ?? author.username}</span> {author.dev ? <Badge text="DEV" /> : ""} {author.bot && <Badge text="BOT" />} <Timestamp ts={timestamp} /></div> : ""}
|
||||
<div className="messageContent">{formattedContent}</div>
|
||||
</div>
|
||||
)
|
||||
@@ -148,3 +195,21 @@ function Mention({name, username}: { name?: string, username: string }): ReactEl
|
||||
<div className="mention">@{name ?? username}</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Reply({author, content}: {author: user, content: ReactElement[] | string}): ReactElement {
|
||||
return (
|
||||
<div className="reply">
|
||||
<div className="replyBorderThing"></div>
|
||||
<span>{self.dev ? `${author.name ?? "no name"} (${author.username}, ${author.id})` : author.name ?? author.username}</span> {author.dev ? <Badge text="DEV" /> : ""} {author.bot && <Badge text="BOT" />}
|
||||
<div className="replyContent">{content}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MessageHoverActions({reply}: {reply: () => void}): ReactElement {
|
||||
return (
|
||||
<div className="messageHoverActions">
|
||||
<div className="reply" onClick={reply}><ReplyIcon /></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -123,6 +123,9 @@ html {
|
||||
&.bottom {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
position: relative;
|
||||
&.sending {
|
||||
color: #777;
|
||||
@@ -136,7 +139,7 @@ html {
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
&:active {
|
||||
top: 1px;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
}
|
||||
.messageAuthor {
|
||||
@@ -147,6 +150,7 @@ html {
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
color: var(--role-color);
|
||||
}
|
||||
.messageTimestamp {
|
||||
font-weight: initial;
|
||||
@@ -177,6 +181,52 @@ html {
|
||||
font-size: 8pt;
|
||||
display: inline;
|
||||
}
|
||||
.messageHoverActions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -15px;
|
||||
}
|
||||
}
|
||||
|
||||
.reply {
|
||||
margin-left: 31px;
|
||||
position: relative;
|
||||
&>span {
|
||||
cursor: pointer;
|
||||
color: var(--role-color);
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.replyBorderThing {
|
||||
width: 24px;
|
||||
height: 9px;
|
||||
display: inline;
|
||||
position: absolute;
|
||||
transform: translateY(7px);
|
||||
left: -28px;
|
||||
border-left: solid #595a63 2px;
|
||||
border-top: solid #595a63 2px;
|
||||
border-top-left-radius: 6px;
|
||||
}
|
||||
.replyContent {
|
||||
top: -1px;
|
||||
position: relative;
|
||||
display: inline;
|
||||
color: #999;
|
||||
&:hover {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.badge {
|
||||
background-color: var(--badge-color);
|
||||
max-width: fit-content;
|
||||
padding: 2px;
|
||||
border-radius: 10%;
|
||||
font-size: 8pt;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.textbox {
|
||||
@@ -185,6 +235,8 @@ html {
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
width: calc(100% - 250px);
|
||||
max-height: 208px;
|
||||
overflow-y: scroll;
|
||||
background-color: #222;
|
||||
}
|
||||
.editor:empty:before {
|
||||
|
||||
@@ -8,4 +8,5 @@ export interface user {
|
||||
token: null,
|
||||
password: null,
|
||||
dev: boolean;
|
||||
bot: boolean;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { message, user } from "@localtypes";
|
||||
import { id, message, user } from "@localtypes";
|
||||
|
||||
let passwd = "";
|
||||
let token = localStorage.getItem("token") ?? "";
|
||||
@@ -16,6 +16,7 @@ let queue: (message | null)[] = new Array(6).fill(null);
|
||||
const listenersMessages = new Set<() => void>();
|
||||
const listenersUsers = new Set<() => void>();
|
||||
let visibleMessages: (message | null)[] = [];
|
||||
let replyingTo: id | undefined = undefined;
|
||||
let dirty = true;
|
||||
|
||||
function rebuildVisibleMessages() {
|
||||
@@ -54,6 +55,11 @@ const onclose = () => {
|
||||
console.log("ws disconnected. reconnecting");
|
||||
ws = new WebSocket("ws://localhost/ws");
|
||||
ws.onopen = () => {
|
||||
messages = [];
|
||||
users = [];
|
||||
queue = new Array(6).fill(null);
|
||||
dirty = true;
|
||||
|
||||
console.log("opened websocket!");
|
||||
if (!token) {
|
||||
ws.send(JSON.stringify({
|
||||
@@ -165,6 +171,11 @@ export function getUsers(): user[] {
|
||||
return users;
|
||||
}
|
||||
|
||||
export function replyToMessage(id: id) {
|
||||
replyingTo = id;
|
||||
console.log(replyingTo);
|
||||
}
|
||||
|
||||
export function sendChatMessage(message: message & { token?: string }) {
|
||||
if (queue.filter(m => m !== null).length < 6) {
|
||||
console.log(message);
|
||||
@@ -174,6 +185,9 @@ export function sendChatMessage(message: message & { token?: string }) {
|
||||
i = 6;
|
||||
}
|
||||
}
|
||||
message.replyTo = replyingTo;
|
||||
replyingTo = undefined;
|
||||
console.log(replyingTo);
|
||||
dirty = true;
|
||||
notifyMessages();
|
||||
ws.send(JSON.stringify({op: OpcodesServerbound.sendMessage, data: message}));
|
||||
|
||||
Reference in New Issue
Block a user