frontend: message loading

This command finalizes support for dynamic message loading. The behavior is as follows:
When a user selects a channel for the first time, an "initial load" of messages will happen.
When the user is scrolled all the way to the bottom of the message view,
the store will continuously remove old messages to save memory.
Scrolling all the way to the top loads more messages.
This commit is contained in:
hippoz 2022-04-20 03:14:28 +03:00
parent da7e57fe82
commit 7260525eec
Signed by: hippoz
GPG key ID: 7C52899193467641
6 changed files with 38 additions and 21 deletions

View file

@ -49,7 +49,7 @@ import Messages from "./Messages.svelte";
<HashIcon /> <HashIcon />
<span class="h5 channel-heading">{ channel.name }</span> <span class="h5 channel-heading">{ channel.name }</span>
</div> </div>
<Messages channelId={ channel.id } /> <Messages channelId="{ channel.id }" />
<div class="message-input-container"> <div class="message-input-container">
<input type="text" class="message-input"> <input type="text" class="message-input">
</div> </div>

View file

@ -3,16 +3,22 @@
export let channelId; export let channelId;
const messages = messagesStoreProvider.getStore(channelId); $: messages = messagesStoreProvider.getStore(channelId);
if (!messages.didInitialLoad) $: {
messages.doInitialLoad(); if (!messages.didDoInitialLoad) {
messages.doInitialLoad();
}
}
const onScroll = (e) => { const onScroll = (e) => {
const { scrollTop, offsetHeight, scrollHeight } = e.target; const { scrollTop, offsetHeight, scrollHeight } = e.target;
if (scrollTop === 0) {
messages.loadOlderMessages();
}
if ((scrollTop + offsetHeight) >= scrollHeight) { if ((scrollTop + offsetHeight) >= scrollHeight) {
setIsCollectingOldMessages(true); messages.setIsCollectingOldMessages(true);
} else { } else {
setIsCollectingOldMessages(false); messages.setIsCollectingOldMessages(false);
} }
}; };

View file

@ -1,6 +1,7 @@
import App from './components/App.svelte'; import App from './components/App.svelte';
import gateway from './gateway'; import gateway from './gateway';
import { initStorageDefaults } from './storage'; import request from './request';
import { apiRoute, initStorageDefaults } from './storage';
initStorageDefaults(); initStorageDefaults();
gateway.init(); gateway.init();
@ -11,6 +12,14 @@ if (loadingElement) {
loadingElement.parentElement.removeChild(loadingElement); loadingElement.parentElement.removeChild(loadingElement);
} }
window.__testing = async () => {
for (let i = 0; i < 100; i++) {
await request("POST", apiRoute("channels/6/messages"), true, {
content: `test ${i}`
});
}
};
const app = new App({ const app = new App({
target: document.body target: document.body
}); });

View file

@ -6,6 +6,7 @@ export default async function(method, endpoint, auth=true, body=null) {
}; };
if (body) { if (body) {
options.body = JSON.stringify(body);
options.headers = { options.headers = {
...options.headers || {}, ...options.headers || {},
"Content-Type": "application/json" "Content-Type": "application/json"

View file

@ -141,6 +141,7 @@ class MessageStore extends Store {
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) {
res.json.reverse();
this.value = res.json.concat(this.value); this.value = res.json.concat(this.value);
this.updated(); this.updated();
} }
@ -168,10 +169,10 @@ class MessagesStoreProvider {
} }
getStore(channelId) { getStore(channelId) {
if (!this.storeByChannel[channelId]) { if (!this.storeByChannel.get(channelId)) {
this.storeByChannel[channelId] = new MessageStore(channelId); this.storeByChannel.set(channelId, new MessageStore(channelId));
} }
return this.storeByChannel[channelId]; return this.storeByChannel.get(channelId);
} }
} }

View file

@ -19,13 +19,13 @@ content-type: application/json
### ###
GET http://localhost:3000/api/v1/users/self HTTP/1.1 GET http://localhost:3000/api/v1/users/self HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
### ###
POST http://localhost:3000/api/v1/channels HTTP/1.1 POST http://localhost:3000/api/v1/channels HTTP/1.1
content-type: application/json content-type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
{ {
"name": "channel 4" "name": "channel 4"
@ -35,7 +35,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6M
PUT http://localhost:3000/api/v1/channels/7 HTTP/1.1 PUT http://localhost:3000/api/v1/channels/7 HTTP/1.1
content-type: application/json content-type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
#Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwidHlwZSI6MSwiaWF0IjoxNjQ5MjU5NDUwLCJleHAiOjE2NDk0MzIyNTB9.JmF9NujFZnln7A-ynNpeyayGBqmR5poAyACYV6RnSQY #Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwidHlwZSI6MSwiaWF0IjoxNjQ5MjU5NDUwLCJleHAiOjE2NDk0MzIyNTB9.JmF9NujFZnln7A-ynNpeyayGBqmR5poAyACYV6RnSQY
{ {
@ -45,24 +45,24 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6M
### ###
DELETE http://localhost:3000/api/v1/channels/9 HTTP/1.1 DELETE http://localhost:3000/api/v1/channels/9 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
#Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwidHlwZSI6MSwiaWF0IjoxNjQ5MjU5NDUwLCJleHAiOjE2NDk0MzIyNTB9.JmF9NujFZnln7A-ynNpeyayGBqmR5poAyACYV6RnSQY #Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwidHlwZSI6MSwiaWF0IjoxNjQ5MjU5NDUwLCJleHAiOjE2NDk0MzIyNTB9.JmF9NujFZnln7A-ynNpeyayGBqmR5poAyACYV6RnSQY
### ###
GET http://localhost:3000/api/v1/channels/1 HTTP/1.1 GET http://localhost:3000/api/v1/channels/1 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
### ###
GET http://localhost:3000/api/v1/channels HTTP/1.1 GET http://localhost:3000/api/v1/channels HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
### ###
POST http://localhost:3000/api/v1/channels/1/messages HTTP/1.1 POST http://localhost:3000/api/v1/channels/1/messages HTTP/1.1
content-type: application/json content-type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
{ {
"content": "i love cheese" "content": "i love cheese"
@ -71,13 +71,13 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6M
### ###
GET http://localhost:3000/api/v1/channels/5/messages HTTP/1.1 GET http://localhost:3000/api/v1/channels/5/messages HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
### ###
PUT http://localhost:3000/api/v1/messages/3 HTTP/1.1 PUT http://localhost:3000/api/v1/messages/3 HTTP/1.1
content-type: application/json content-type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
{ {
"content": "hello again!" "content": "hello again!"
@ -86,9 +86,9 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6M
### ###
GET http://localhost:3000/api/v1/messages/3 GET http://localhost:3000/api/v1/messages/3
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss
### ###
DELETE http://localhost:3000/api/v1/messages/2 HTTP/1.1 DELETE http://localhost:3000/api/v1/messages/2 HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwMzg2NjcyLCJleHAiOjE2NTA1NTk0NzJ9.qoBPvIwm9uFf5ZhEjlo87FUgm3qsCHq-AbcZ_WDrtWY Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidHlwZSI6MSwiaWF0IjoxNjUwNDA4NTQ1LCJleHAiOjE2NTA1ODEzNDV9.DCt_IpukPaihSGl1N8a5ve5pd75N4aePxR4YKzw98Ss