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> <script>
import Main from "./pages/main/Main.svelte"; import Main from "./pages/main/Main.svelte";
</script> </script>
<Main /> <Main />

View file

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

View file

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