frontend: improve channel switch latency by reducing reflows due to scroll-related getters

This commit is contained in:
hippoz 2022-04-24 20:14:04 +03:00
parent 15e57c2372
commit 9122434a36
Signed by: hippoz
GPG key ID: 7C52899193467641
2 changed files with 39 additions and 31 deletions

View file

@ -0,0 +1,28 @@
<script>
export let message;
</script>
<style>
.message {
overflow-x: none;
word-break: break-all;
}
.message-content {
color: var(--foreground-color-2);
}
.pending {
color: var(--foreground-color-3);
}
.author {
font-weight: bold;
margin-right: var(--space-xxs);
}
</style>
<div class="message">
<span class="author">{ message.author_username }</span>
<span class="message-content" class:pending={ message._isPending }>{ message.content }</span>
</div>

View file

@ -1,27 +1,25 @@
<script> <script>
import { afterUpdate, beforeUpdate } from "svelte"; import { afterUpdate, beforeUpdate, onMount } from "svelte";
import { messagesStoreProvider } from "../../../stores.js"; import { messagesStoreProvider } from "../../../stores.js";
import Message from "./Message.svelte";
export let channelId; export let channelId;
let scrollTarget; let scrollTarget;
let shouldAutoscroll = false; let scrollAnchor;
let shouldAutoscroll = true;
let lastScrollHeight = null; let lastScrollHeight = null;
$: messages = messagesStoreProvider.getStore(channelId); $: messages = messagesStoreProvider.getStore(channelId);
beforeUpdate(() => {
shouldAutoscroll = scrollTarget && (scrollTarget.offsetHeight + scrollTarget.scrollTop) > (scrollTarget.scrollHeight - 20);
});
afterUpdate(() => { afterUpdate(() => {
// hacky way to preserve scroll position when messages are pushed back // hacky way to preserve scroll position when messages are pushed back
if (lastScrollHeight) { if (lastScrollHeight) {
scrollTarget.scrollTop = scrollTarget.scrollHeight - lastScrollHeight; scrollTarget.scrollTop = scrollTarget.scrollHeight - lastScrollHeight;
lastScrollHeight = null; lastScrollHeight = null;
return; return;
} }
if (shouldAutoscroll && scrollTarget) { if (shouldAutoscroll && scrollAnchor) {
scrollTarget.scrollTo(0, scrollTarget.scrollHeight); scrollAnchor.scrollIntoView(false);
} }
}); });
@ -29,7 +27,9 @@
const { scrollTop, offsetHeight, scrollHeight } = e.target; const { scrollTop, offsetHeight, scrollHeight } = e.target;
if ((scrollTop + offsetHeight) >= scrollHeight) { // user scrolled to bottom if ((scrollTop + offsetHeight) >= scrollHeight) { // user scrolled to bottom
messages.setIsCollectingOldMessages(true); messages.setIsCollectingOldMessages(true);
shouldAutoscroll = true;
} else { } else {
shouldAutoscroll = false;
if (scrollTop === 0) { if (scrollTop === 0) {
// load older messages if the user scrolls to the top. // load older messages if the user scrolls to the top.
// save the current scroll height if the server returned any messages, // save the current scroll height if the server returned any messages,
@ -57,31 +57,11 @@
overflow-x: hidden; overflow-x: hidden;
background-color: var(--background-color-1); background-color: var(--background-color-1);
} }
.message {
overflow-x: none;
word-break: break-all;
}
.message-content {
color: var(--foreground-color-2);
}
.pending {
color: var(--foreground-color-3);
}
.author {
font-weight: bold;
margin-right: var(--space-xxs);
}
</style> </style>
<div class="messages-container" on:scroll={ onScroll } bind:this={ scrollTarget }> <div class="messages-container" on:scroll={ onScroll } bind:this={ scrollTarget }>
{#each $messages as message (message.id)} {#each $messages as message (message.id)}
<div class="message"> <Message message={message} />
<span class="author">{ message.author_username }</span>
<span class="message-content" class:pending={ message._isPending }>{ message.content }</span>
</div>
{/each} {/each}
<div bind:this={ scrollAnchor } />
</div> </div>