litechatplus/src/lib/ChatMessages.svelte
2023-02-11 18:56:59 +01:00

124 lines
3.0 KiB
Svelte

<script lang="ts">
import FAB from './ui/FAB.svelte';
import MessageView from './MessageView.svelte';
import Input from './Input.svelte';
import { Svrollbar } from 'svrollbar';
import { chat_lock, chat, localMessages as messages, shouldTweet } from './chat';
import { globalMode } from './settings';
import { onMount, onDestroy, tick } from 'svelte';
import type { Unsubscriber } from 'svelte/store';
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
import { faArrowDown } from '@fortawesome/free-solid-svg-icons';
let stickToBottom = true;
let viewport: Element, contents: Element;
let tweet: HTMLAudioElement;
shouldTweet.subscribe((should) => {
if (should) {
if (tweet) {
tweet.volume = 0.5;
tweet?.play().catch(() => {});
}
shouldTweet.set(false);
}
});
export async function scrollToBottom(smooth = true) {
viewport &&
viewport.scrollTo({
top: viewport.scrollHeight,
behavior: smooth ? 'smooth' : undefined
});
}
let releaseLock: Unsubscriber;
onMount(async () => {
releaseLock = chat_lock.subscribe(() => {});
await chat.hydrate();
await tick();
viewport && (viewport.scrollTop = viewport.scrollHeight);
});
messages.subscribe(async () => {
if (stickToBottom) {
// Wait for the message to be added to the DOM
await tick();
// Scroll the list to the bottom with the new DOM
await scrollToBottom(false);
// vlist?.scrollToIndex(ms.length);
// Fake debounce to make sure we're still stuck at the bottom
await tick();
stickToBottom = true;
}
});
onDestroy(() => {
releaseLock?.();
});
function onScroll(e: Event) {
if (!e.isTrusted) return;
const scrollOffset = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;
if (scrollOffset >= 80) {
stickToBottom = false;
} else if (scrollOffset < 80) {
stickToBottom = true;
}
}
</script>
<div class="scroller">
<audio src="/tweet.mp3" bind:this={tweet} />
<Svrollbar {viewport} {contents} margin={{ right: 6, bottom: 12, top: 12 }}
alwaysVisible
--svrollbar-track-width="8px" --svrollbar-thumb-width="4px" />
<MessageView messages="{$messages}" showChannel="{$globalMode}"
bind:viewport bind:contents on:scroll={onScroll} />
{#if !stickToBottom}
<FAB --background="{'var(--base-700)'}" on:click={() => scrollToBottom()}><FontAwesomeIcon icon="{faArrowDown}" /></FAB>
{/if}
</div>
<Input />
<style>
.scroller {
min-height: 0;
flex-grow: 1;
flex-basis: 0;
position: relative;
}
.scroller > :global(.viewport) {
overflow: scroll;
height: 100% !important;
}
.scroller :global(.contents) {
margin-bottom: 32px;
}
.scroller > :global(*) {
/* hide scrollbar */
-ms-overflow-style: none !important;
scrollbar-width: none !important;
}
.scroller > :global(*::-webkit-scrollbar) {
/* hide scrollbar */
display: none !important;
}
.scroller :global(.v-thumb) {
z-index: 101;
}
</style>