Files
frontend/src/routes/chat/ChatRoom.svelte

222 lines
5.8 KiB
Svelte

<script lang="ts">
import ChatRoomHeader from './ChatRoomHeader.svelte';
import Loading from '$lib/components/loading.svelte';
import {
currentSelectedRoom,
messageStore,
rateLimitStore,
selectedServer,
configStore,
currentRoomsStore
} from '$lib/stores';
import { Experiences } from '$lib/types';
import { getEpochFromTimestamp, getTimestampFromEpoch, updateMessages } from '$lib/utils';
import { toastStore } from '@skeletonlabs/skeleton';
import type { MessageI } from 'discreetly-interfaces';
import type { Socket } from 'socket.io-client';
import { io } from 'socket.io-client';
import { onDestroy, onMount } from 'svelte';
import Conversation from './Conversation.svelte';
import Draw from './Draw.svelte';
import InputPrompt from './InputPrompt.svelte';
let scrollChatToBottom: () => {};
let socket: Socket;
let connected: boolean = false;
let lastRoom = '';
let onlineMembers = '?';
$: currentEpoch = 0;
$: timeLeftInEpoch = '0';
$: roomId = $currentSelectedRoom.roomId!.toString();
$: userMessageLimit = $currentSelectedRoom.userMessageLimit ?? 1;
$: roomRateLimit = $currentSelectedRoom.rateLimit ?? 0;
$: if (!$rateLimitStore[roomId]) {
console.debug('Resetting rate limit store for room', roomId);
$rateLimitStore[roomId] = {
lastEpoch: currentEpoch,
messagesSent: 0
};
}
let unsubscribeStore = currentSelectedRoom.subscribe((currentValue) => {
updateMessages($selectedServer, roomId);
});
$: messagesLeft = () => {
if (currentEpoch > 0) {
if ($rateLimitStore[roomId].lastEpoch !== currentEpoch) {
$rateLimitStore[roomId] = {
lastEpoch: currentEpoch,
messagesSent: 0
};
return userMessageLimit;
} else {
return userMessageLimit - $rateLimitStore[roomId].messagesSent;
}
}
return 0;
};
$: messageId = userMessageLimit - messagesLeft();
$: try {
if (lastRoom) {
socket.emit('leavingRoom', lastRoom);
}
socket.emit('joiningRoom', $currentSelectedRoom?.roomId.toString());
console.debug('Joining room', $currentSelectedRoom?.roomId.toString());
} catch {
} finally {
}
function updateEpoch() {
if ($currentSelectedRoom === undefined) {
$currentSelectedRoom = $currentRoomsStore[0];
}
currentEpoch = Math.floor(Date.now() / $currentSelectedRoom.rateLimit!);
timeLeftInEpoch = (
($currentSelectedRoom.rateLimit! -
(Date.now() -
getTimestampFromEpoch($currentSelectedRoom.rateLimit!, currentEpoch).timestamp)) /
1000
).toFixed(1);
}
onMount(() => {
console.log('MESSAGES LEFT: ' + messagesLeft());
socket = io($selectedServer);
socket.on('connect', () => {
connected = true;
const engine = socket.io.engine;
updateMessages($selectedServer, $currentSelectedRoom?.roomId.toString());
if ($configStore.experience == Experiences.Chat) {
scrollChatToBottom();
}
engine.once('upgrade', () => {
console.debug('Upgraded connection to', engine.transport.name);
});
engine.on('close', (reason) => {
console.debug('socket-io-transport-closed', reason);
});
socket.emit('joiningRoom', $currentSelectedRoom?.roomId.toString());
});
socket.on('disconnected', () => {
connected = false;
console.debug('disconnected');
});
socket.on('connect_error', (err) => {
console.debug('chat connection error', err.message);
});
socket.on('connect_timeout', (err) => {
console.debug('chat connection timeout', err.message);
});
socket.on('error', (err) => {
console.debug('chat websocket error', err.message);
});
socket.on('messageBroadcast', (data: MessageI) => {
console.debug('Received Message: ', data);
const roomId = data.roomId?.toString();
if (roomId) {
if (!$messageStore[roomId]) {
console.debug('Creating room in message store', roomId);
$messageStore[roomId] = [] as MessageI[];
}
if (typeof data.proof === 'string') {
data.proof = JSON.parse(data.proof as string);
}
if (!data.epoch) {
data.epoch = getEpochFromTimestamp(
$currentSelectedRoom.rateLimit!,
+data.timeStamp!
).epoch;
}
$messageStore[roomId] = [...$messageStore[roomId], data];
if ($messageStore[roomId].length > 500) {
$messageStore[roomId] = $messageStore[roomId].slice(-500); // Keep only the latest 500 messages
}
scrollChatToBottom();
}
socket.on('Members', (data: string) => {
console.debug(`Members Online: ${data}`);
onlineMembers = data;
});
socket.on('systemBroadcast', (data: string) => {
toastStore.trigger({ message: data, timeout: 3000 });
console.debug('Received System Message: ', data);
});
scrollChatToBottom();
});
setInterval(() => {
updateEpoch();
}, 100);
});
onDestroy(() => {
unsubscribeStore();
socket.emit('leavingRoom', $currentSelectedRoom?.roomId);
socket.disconnect();
});
</script>
{#if $currentSelectedRoom}
<div id="chat" class="grid grid-rows-[auto,1fr,auto]">
<ChatRoomHeader
{connected}
{currentEpoch}
{timeLeftInEpoch}
{userMessageLimit}
{messageId}
{messagesLeft}
{roomRateLimit}
/>
{#if $configStore.experience == Experiences.Chat}
{#key $currentSelectedRoom.roomId}
<Conversation bind:scrollChatBottom={scrollChatToBottom} {roomRateLimit} />
{/key}
<InputPrompt
{socket}
{connected}
{currentEpoch}
{userMessageLimit}
{messageId}
{messagesLeft}
/>
{:else if $configStore.experience == Experiences.Draw}
<Draw />
{:else}
{#key $currentSelectedRoom.roomId}
<Conversation bind:scrollChatBottom={scrollChatToBottom} {roomRateLimit} />
{/key}
<InputPrompt
{socket}
{connected}
{currentEpoch}
{userMessageLimit}
{messageId}
{messagesLeft}
/>
{/if}
<!-- Conversation -->
<!-- Prompt -->
</div>
{:else}
<Loading />
{/if}
<style>
#chat {
max-height: calc(100vh - 56px);
grid-area: chat;
}
</style>