frontend: preserve scroll position when loading more messages

This commit is contained in:
hippoz 2022-04-22 16:43:26 +03:00
parent e19a3aa6cc
commit f7f536b318
Signed by: hippoz
GPG key ID: 7C52899193467641
3 changed files with 20 additions and 6 deletions

View file

@ -1,6 +1,5 @@
<script>
import Main from "./pages/main/Main.svelte";
import Main from "./pages/main/Main.svelte";
</script>
<Main />

View file

@ -1,11 +1,11 @@
<script>
import { afterUpdate, beforeUpdate } from "svelte";
import { element } from "svelte/internal";
import { messagesStoreProvider } from "../../../stores.js";
export let channelId;
let scrollTarget;
let shouldAutoscroll = false;
let lastScrollHeight = null;
$: messages = messagesStoreProvider.getStore(channelId);
@ -14,6 +14,12 @@ import { element } from "svelte/internal";
});
afterUpdate(() => {
// hacky way to preserve scroll position when messages are pushed back
if (lastScrollHeight) {
scrollTarget.scrollTop = scrollTarget.scrollHeight - lastScrollHeight;
lastScrollHeight = null;
return;
}
if (shouldAutoscroll && scrollTarget) {
scrollTarget.scrollTo(0, scrollTarget.scrollHeight);
}
@ -21,11 +27,18 @@ import { element } from "svelte/internal";
const onScroll = (e) => {
const { scrollTop, offsetHeight, scrollHeight } = e.target;
if ((scrollTop + offsetHeight) >= scrollHeight) {
if ((scrollTop + offsetHeight) >= scrollHeight) { // user scrolled to bottom
messages.setIsCollectingOldMessages(true);
} else {
if (scrollTop === 0) {
messages.loadOlderMessages();
// 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);
}

View file

@ -172,11 +172,13 @@ class MessageStore extends Store {
this.collectOldMessages();
}
async loadOlderMessages() {
async loadOlderMessages(beforeCommitToStore=null) {
const oldestMessage = this.value[0];
const endpoint = oldestMessage ? `channels/${this.channelId}/messages/?before=${oldestMessage.id}` : `channels/${this.channelId}/messages`;
const res = await request("GET", apiRoute(endpoint), true, null);
if (res.success && res.ok && res.json && res.json.length > 0) {
if (beforeCommitToStore)
beforeCommitToStore(res.json);
res.json.reverse();
this.value = res.json.concat(this.value);
this.updated();