79 lines
2.7 KiB
Svelte
79 lines
2.7 KiB
Svelte
<script>
|
|
import { afterUpdate } from "svelte";
|
|
import { messagesStoreProvider, smallViewport } from "../stores.js";
|
|
import Message from "./Message.svelte";
|
|
|
|
export let channelId;
|
|
let scrollTarget;
|
|
let scrollAnchor;
|
|
let shouldAutoscroll = true;
|
|
let lastScrollHeight = null;
|
|
let isScrolledToBottom = true;
|
|
|
|
$: messages = messagesStoreProvider.getStore(channelId);
|
|
|
|
afterUpdate(() => {
|
|
// hacky way to preserve scroll position when messages are pushed back
|
|
if (lastScrollHeight) {
|
|
scrollTarget.scrollTop = scrollTarget.scrollHeight - lastScrollHeight;
|
|
lastScrollHeight = null;
|
|
return;
|
|
}
|
|
if (shouldAutoscroll && scrollAnchor) {
|
|
scrollAnchor.scrollIntoView(false);
|
|
}
|
|
});
|
|
|
|
const onScroll = (e) => {
|
|
const { scrollTop, offsetHeight, scrollHeight } = e.target;
|
|
if ((scrollTop + offsetHeight) >= scrollHeight) { // user scrolled to bottom
|
|
messages.setIsCollectingOldMessages(true);
|
|
shouldAutoscroll = true;
|
|
isScrolledToBottom = true;
|
|
} else {
|
|
shouldAutoscroll = false;
|
|
isScrolledToBottom = false;
|
|
if (scrollTop === 0) {
|
|
// load older messages if the user scrolls to the top.
|
|
// save the current scroll height if the server returned any messages,
|
|
// before commiting them to the store. this is to provide the jank scroll height
|
|
// preservation
|
|
messages.loadOlderMessages(() => {
|
|
if (scrollTarget)
|
|
lastScrollHeight = scrollTarget.scrollHeight;
|
|
});
|
|
}
|
|
messages.setIsCollectingOldMessages(false);
|
|
}
|
|
};
|
|
|
|
const windowDidResize = () => {
|
|
// TODO: hack
|
|
// showSidebar is false on small viewports
|
|
// scrolling to bottom when the virtual keyboard pops up does not work on chromium purely based on isScrolledToBottom for some reason
|
|
if (isScrolledToBottom || $smallViewport) {
|
|
scrollAnchor.scrollIntoView(false);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<svelte:window on:resize={ windowDidResize } />
|
|
|
|
<style>
|
|
.messages-container {
|
|
height: 100%;
|
|
width: 100%;
|
|
flex-grow: 0;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
background-color: var(--background-color-1);
|
|
padding-top: var(--space-sm);
|
|
}
|
|
</style>
|
|
|
|
<div class="messages-container" on:scroll={ onScroll } bind:this={ scrollTarget }>
|
|
{#each $messages as message (message.id)}
|
|
<Message message={message} />
|
|
{/each}
|
|
<div bind:this={ scrollAnchor } />
|
|
</div>
|