feat: Message timestamps

This commit is contained in:
April Hall 2025-02-24 21:44:00 -05:00
parent 08493a24b7
commit e3c8f7fa3a
Signed by: arithefirst
GPG Key ID: 4508A15C4DB91C5B
9 changed files with 43 additions and 10 deletions

View File

@ -14,7 +14,7 @@ A more complex version of this list is available [here](https://trello.com/b/kJw
- [x] Notifications - [x] Notifications
- [x] Make the damn textbox stop unfocusing on every message submit - [x] Make the damn textbox stop unfocusing on every message submit
- [ ] Message context menus - [ ] Message context menus
- [ ] Message Timestamps - [x] Message Timestamps
- [x] Markdown Support - [x] Markdown Support
- [x] More Secure database passwords - [x] More Secure database passwords
- [x] Minio - [x] Minio

View File

@ -20,13 +20,15 @@ io.on('connection', async (socket) => {
if (msg.content !== '') { if (msg.content !== '') {
console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`); console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`);
// Store the message in the database // Store the message in the database
await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4()); const timestamp = new Date();
await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4(), timestamp);
const sender = authdb.getUser(msg.id); const sender = authdb.getUser(msg.id);
io!.emit('message', { io!.emit('message', {
user: sender.username, user: sender.username,
message: msg.content, message: msg.content,
imageSrc: sender.image, imageSrc: sender.image,
channel: msg.channel, channel: msg.channel,
timestamp: timestamp.getTime(),
}); });
} }
}); });

View File

@ -2,7 +2,7 @@
import { type TypeMessage } from '$lib/types'; import { type TypeMessage } from '$lib/types';
import Prose from '$lib/components/prose.svelte'; import Prose from '$lib/components/prose.svelte';
import renderMarkdown from '$lib/functions/renderMarkdown'; import renderMarkdown from '$lib/functions/renderMarkdown';
const { message, imageSrc, user }: TypeMessage = $props(); const { message, imageSrc, user, timestamp }: TypeMessage = $props();
</script> </script>
<div class="flex w-full p-2 hover:bg-zinc-200 dark:hover:bg-stone-900"> <div class="flex w-full p-2 hover:bg-zinc-200 dark:hover:bg-stone-900">
@ -12,7 +12,20 @@
</div> </div>
</div> </div>
<div class="w-full"> <div class="w-full">
<p class="inline-size-full break-words font-bold">{user}</p> <p class="inline-size-full flex items-center gap-1 break-words">
<span class="font-bold">{user}</span>
<span>·</span>
<span class="text-muted-foreground"
>{new Date(timestamp).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour12: true,
hour: 'numeric',
minute: 'numeric',
})}</span
>
</p>
<Prose class="inline-size-full text-wrap break-words font-sans">{@html renderMarkdown(message)}</Prose> <Prose class="inline-size-full text-wrap break-words font-sans">{@html renderMarkdown(message)}</Prose>
</div> </div>
</div> </div>

View File

@ -34,6 +34,7 @@ class Websocket {
message: newMsg.message, message: newMsg.message,
imageSrc: newMsg.imageSrc, imageSrc: newMsg.imageSrc,
user: newMsg.user, user: newMsg.user,
timestamp: newMsg.timestamp,
}, },
...this.messages, ...this.messages,
]; ];

View File

@ -28,13 +28,15 @@ export function startupSocketIOServer(httpServer: HttpServer | null) {
if (msg.content !== '') { if (msg.content !== '') {
console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`); console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`);
// Store the message in the database // Store the message in the database
await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4()); const timestamp = new Date();
await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4(), timestamp);
const sender = authdb.getUser(msg.id); const sender = authdb.getUser(msg.id);
io!.emit('message', { io!.emit('message', {
user: sender.username, user: sender.username,
message: msg.content, message: msg.content,
imageSrc: sender.image, imageSrc: sender.image,
channel: msg.channel, channel: msg.channel,
timestamp: timestamp.getTime(),
}); });
} }
}); });

View File

@ -6,6 +6,12 @@ interface Messages {
error: Error | null; error: Error | null;
} }
interface CassandraTimestamp {
low: number;
high: number;
unsigned: boolean;
}
function createDelay(ms: number) { function createDelay(ms: number) {
return new Promise((res) => setTimeout(res, ms)); return new Promise((res) => setTimeout(res, ms));
} }
@ -69,16 +75,15 @@ class Db {
} }
// Send message method // Send message method
async sendMessage(channelName: string, content: string, sender: string, id: string) { async sendMessage(channelName: string, content: string, sender: string, id: string, timestamp: Date) {
try { try {
const now = new Date();
channelName = sanitizeChannelName(channelName); channelName = sanitizeChannelName(channelName);
await this.client.execute(`INSERT INTO channels.${channelName} (id, message_content, channel_name, timestamp, sender) VALUES (?, ?, ?, ?, ?)`, { await this.client.execute(`INSERT INTO channels.${channelName} (id, message_content, channel_name, sender, timestamp) VALUES (?, ?, ?, ?, ?)`, {
id, id,
message_content: content, message_content: content,
channel_name: channelName, channel_name: channelName,
timestamp: now.getTime(),
sender, sender,
timestamp,
}); });
} catch (e) { } catch (e) {
console.error(`Error storing message: ${e as Error}`); console.error(`Error storing message: ${e as Error}`);
@ -130,6 +135,13 @@ class Db {
}; };
} }
} }
// Timestamp to Epoch method
tsEpoch(ts: CassandraTimestamp) {
const low = ts.low >>> 0;
const high = ts.high >>> 0;
return high * 2 ** 32 + low;
}
} }
const db = new Db(); const db = new Db();

View File

@ -2,6 +2,7 @@ export interface TypeMessage {
message: string; message: string;
imageSrc: string; imageSrc: string;
user: string; user: string;
timestamp: number;
} }
export interface TypeFullMessage { export interface TypeFullMessage {
@ -9,4 +10,5 @@ export interface TypeFullMessage {
message: string; message: string;
imageSrc: string; imageSrc: string;
user: string; user: string;
timestamp: number;
} }

View File

@ -31,6 +31,7 @@ export async function load({ params, request }): Promise<ChannelLoad> {
user: sender.username, user: sender.username,
imageSrc: sender.image, imageSrc: sender.image,
channel: value.channel, channel: value.channel,
timestamp: db.tsEpoch(value.timestamp),
}; };
}) })
: []; : [];

View File

@ -71,7 +71,7 @@
{#snippet message(messages: TypeMessage[])} {#snippet message(messages: TypeMessage[])}
{#each messages as message} {#each messages as message}
<Message imageSrc={message.imageSrc} user={message.user} message={message.message} /> <Message imageSrc={message.imageSrc} user={message.user} message={message.message} timestamp={message.timestamp} />
{/each} {/each}
{/snippet} {/snippet}