This is the code that runs https://bandolier.learnjsthehardway.com/ for you to review. It uses the https://git.learnjsthehardway.com/learn-javascript-the-hard-way/bandolier-template to create the documentation for the project.
https://bandolier.learnjsthehardway.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
4.9 KiB
217 lines
4.9 KiB
<script>
|
|
import { user } from "$/client/stores.js";
|
|
import { reconnect_socket } from '$/client/websocket.js';
|
|
import { onMount, afterUpdate } from 'svelte';
|
|
import { fade } from 'svelte/transition';
|
|
import Login from './Login.svelte';
|
|
import Modal from './Modal.svelte';
|
|
|
|
let socket;
|
|
let admin_top = {initials: '@',
|
|
admin: true,
|
|
size_at: 0,
|
|
text: 'Welcome to the chat. Introduce yourself and abstain from drama.'
|
|
}
|
|
|
|
const messages_height = 11;
|
|
|
|
let admin_messages = [admin_top];
|
|
let messages = [admin_top];
|
|
|
|
let login_dialog = false;
|
|
|
|
let message_input = "";
|
|
|
|
$: if(admin_top && !admin_top.display_at && messages.length - admin_top.size_at > messages_height) {
|
|
// mark the new one for display
|
|
admin_top.display = true;
|
|
// kick off a timer to hide it after 10 seconds on top
|
|
admin_top.display_at = new Date();
|
|
}
|
|
|
|
const user_authenticated = () => {
|
|
login_dialog = false;
|
|
$user.authenticated = true; // this seems to not get updated sometimes
|
|
socket = reconnect_socket();
|
|
}
|
|
|
|
afterUpdate(() => {
|
|
let msg_scroll = document.getElementById('messages');
|
|
msg_scroll.scrollTop = msg_scroll.scrollHeight;
|
|
});
|
|
|
|
const config_socket = () => {
|
|
let sock = reconnect_socket();
|
|
|
|
sock.on("/chat/message", (data) => {
|
|
data.size_at = messages.length;
|
|
messages.push(data);
|
|
messages = messages;
|
|
|
|
if(data.admin) {
|
|
admin_messages.push(data);
|
|
admin_top = admin_messages[admin_messages.length - 1];
|
|
}
|
|
});
|
|
|
|
return sock;
|
|
}
|
|
|
|
|
|
onMount(() => {
|
|
if($user.authenticated) {
|
|
// the socket connection sometimes isn't actually
|
|
// authenticated to just reconnect if the users is auth
|
|
socket = config_socket();
|
|
}
|
|
|
|
window.setInterval(() => {
|
|
if(admin_top && admin_top.display) {
|
|
// see if this is old and kick it off
|
|
const live_time = Date.now() - admin_top.display_at;
|
|
if(live_time > 10000) {
|
|
admin_top.display = false;
|
|
}
|
|
}
|
|
}, 1000);
|
|
});
|
|
|
|
const focus_input = () => {
|
|
document.getElementById('message-input').focus();
|
|
}
|
|
|
|
const send_message = () => {
|
|
if(message_input.trim() !== "") {
|
|
let msg = {user_id: $user.id, text: message_input};
|
|
socket.emit("/chat/message", msg);
|
|
message_input = "";
|
|
}
|
|
|
|
focus_input();
|
|
}
|
|
|
|
</script>
|
|
|
|
<style>
|
|
chat {
|
|
display: flex;
|
|
flex-direction: column;
|
|
font-size: 0.8em;
|
|
position: relative;
|
|
height: 100%;
|
|
}
|
|
|
|
chat messages {
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: hidden;
|
|
overflow-x: hidden;
|
|
height: 100%;
|
|
margin-bottom: 0px;
|
|
}
|
|
|
|
chat messages:hover {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
chat messages message from {
|
|
margin-right: 0.9rem;
|
|
background-color: #555;
|
|
color: #fff;
|
|
padding: 0.2rem;
|
|
border-radius: 50%;
|
|
font-size: 0.5em;
|
|
border: 1px solid var(--color-accent);
|
|
}
|
|
|
|
chat messages message {
|
|
padding: 0.3rem;
|
|
}
|
|
|
|
chat message.persistent {
|
|
padding: 0.3rem;
|
|
box-shadow: var(--box-shadow) var(--color-shadow);
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
margin: 2px;
|
|
}
|
|
|
|
chat message.admin {
|
|
background-color: var(--color-bg-tertiary);
|
|
padding-top: 0.5rem;
|
|
padding-bottom: 0.5rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
chat form {
|
|
display: flex;
|
|
border: unset;
|
|
padding: unset;
|
|
justify-content: space-evenly;
|
|
max-width: unset;
|
|
min-width: unset;
|
|
flex-direction: column;
|
|
}
|
|
|
|
chat-input form {
|
|
box-shadow: unset;
|
|
}
|
|
|
|
chat-input form input {
|
|
border: 1px solid var(--color-accent);
|
|
margin-bottom: unset;
|
|
width: 100%;
|
|
max-width: unset;
|
|
padding: 4px !important;
|
|
background: var(--color-bg-secondary);
|
|
box-sizing: border-box;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
input#login-to-chat {
|
|
border: 1px solid #222;
|
|
margin-bottom: unset;
|
|
border-radius: 0px 0px 0px 0px;
|
|
}
|
|
|
|
input#login-to-chat:hover {
|
|
cursor: progress;
|
|
}
|
|
</style>
|
|
|
|
<chat data-testid="chat-panel">
|
|
{#if admin_top && admin_top.display }
|
|
<message in:fade out:fade class="admin persistent">
|
|
<from>{admin_top.initials}</from><span>{admin_top.text}</span>
|
|
</message>
|
|
{/if}
|
|
|
|
<messages id="messages">
|
|
{#each messages as message, i}
|
|
<message in:fade class:admin={ message.admin }>
|
|
<from>{message.initials}</from><span>{message.text}</span>
|
|
</message>
|
|
{/each}
|
|
</messages>
|
|
|
|
{#if $user.authenticated}
|
|
<chat-input in:fade>
|
|
<form autocomplete="off" on:submit|preventDefault={ send_message }>
|
|
<input id="message-input" type="text" name="message" bind:value={message_input}>
|
|
</form>
|
|
</chat-input>
|
|
{:else}
|
|
<input type="button" on:click={ () => login_dialog = true } id="login-to-chat" value="Login to Chat">
|
|
{/if}
|
|
</chat>
|
|
|
|
|
|
{#if login_dialog}
|
|
<Modal on:close={ () => login_dialog = false }>
|
|
<Login on:authenticated={ user_authenticated }
|
|
on:canceled={ () => login_dialog = false }
|
|
/>
|
|
</Modal>
|
|
{/if}
|
|
|