Render chat messages loading spinner declaratively

instead of imperatively.

Add new non-persisted UI model for rendering UI changes. Currently only
being used for rendering the spinner
This commit is contained in:
JC Brand 2021-06-02 23:52:16 +02:00
parent 59d55b3526
commit ec93e2fff3
7 changed files with 12 additions and 23 deletions

View File

@ -56,6 +56,7 @@ const ChatBox = ModelWithContact.extend({
this.set({'box_id': `box-${jid}`}); this.set({'box_id': `box-${jid}`});
this.initNotifications(); this.initNotifications();
this.initMessages(); this.initMessages();
this.initUI();
if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) { if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) {
this.presence = _converse.presences.findWhere({'jid': jid}) || _converse.presences.create({'jid': jid}); this.presence = _converse.presences.findWhere({'jid': jid}) || _converse.presences.create({'jid': jid});
@ -106,6 +107,10 @@ const ChatBox = ModelWithContact.extend({
}); });
}, },
initUI () {
this.ui = new Model();
},
initNotifications () { initNotifications () {
this.notifications = new Model(); this.notifications = new Model();
}, },

View File

@ -90,6 +90,7 @@ const ChatRoomMixin = {
this.set('box_id', `box-${this.get('jid')}`); this.set('box_id', `box-${this.get('jid')}`);
this.initNotifications(); this.initNotifications();
this.initMessages(); this.initMessages();
this.initUI();
this.initOccupants(); this.initOccupants();
this.initDiscoModels(); // sendChatState depends on this.features this.initDiscoModels(); // sendChatState depends on this.features
this.registerHandlers(); this.registerHandlers();

View File

@ -188,7 +188,6 @@ describe("A Chat Message", function () {
await _converse.handleMessageStanza(msg); await _converse.handleMessageStanza(msg);
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 7); await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 7);
view.clearSpinner(); //cleanup
expect(view.querySelectorAll('.date-separator').length).toEqual(4); expect(view.querySelectorAll('.date-separator').length).toEqual(4);
let day = sizzle('.date-separator:first', view).pop(); let day = sizzle('.date-separator:first', view).pop();
@ -704,7 +703,7 @@ describe("A Chat Message", function () {
jasmine.clock().tick(1*ONE_MINUTE_LATER); jasmine.clock().tick(1*ONE_MINUTE_LATER);
await mock.sendMessage(view, "Another message within 10 minutes, but from a different person"); await mock.sendMessage(view, "Another message within 10 minutes, but from a different person");
expect(view.querySelectorAll('.message').length).toBe(6); await u.waitUntil(() => view.querySelectorAll('.message').length === 6);
expect(view.querySelectorAll('.chat-msg').length).toBe(5); expect(view.querySelectorAll('.chat-msg').length).toBe(5);
const nth_child = (n) => `converse-chat-message:nth-child(${n}) .chat-msg`; const nth_child = (n) => `converse-chat-message:nth-child(${n}) .chat-msg`;

View File

@ -8,13 +8,13 @@ export async function fetchMessagesOnScrollUp (view) {
if (oldest_message) { if (oldest_message) {
const by_jid = is_groupchat ? view.model.get('jid') : _converse.bare_jid; const by_jid = is_groupchat ? view.model.get('jid') : _converse.bare_jid;
const stanza_id = oldest_message && oldest_message.get(`stanza_id ${by_jid}`); const stanza_id = oldest_message && oldest_message.get(`stanza_id ${by_jid}`);
view.addSpinner(); view.model.ui.set('chat-content-spinner-top', true);
if (stanza_id) { if (stanza_id) {
await fetchArchivedMessages(view.model, { 'before': stanza_id }); await fetchArchivedMessages(view.model, { 'before': stanza_id });
} else { } else {
await fetchArchivedMessages(view.model, { 'end': oldest_message.get('time') }); await fetchArchivedMessages(view.model, { 'end': oldest_message.get('time') });
} }
view.clearSpinner(); view.model.ui.set('chat-content-spinner-top', false);
if (api.settings.get('allow_url_history_change')) { if (api.settings.get('allow_url_history_change')) {
_converse.router.history.navigate(`#${oldest_message.get('msgid')}`); _converse.router.history.navigate(`#${oldest_message.get('msgid')}`);
} }

View File

@ -1,6 +1,5 @@
import debounce from 'lodash-es/debounce'; import debounce from 'lodash-es/debounce';
import log from '@converse/headless/log'; import log from '@converse/headless/log';
import tpl_spinner from 'templates/spinner.js';
import { ElementView } from '@converse/skeletor/src/element.js'; import { ElementView } from '@converse/skeletor/src/element.js';
import { _converse, api, converse } from '@converse/headless/core'; import { _converse, api, converse } from '@converse/headless/core';
@ -92,23 +91,6 @@ export default class BaseChatView extends ElementView {
} }
} }
addSpinner (append = false) {
const content = this.querySelector('.chat-content');
if (this.querySelector('.spinner') === null) {
const el = u.getElementFromTemplateResult(tpl_spinner());
if (append) {
content.insertAdjacentElement('beforeend', el);
this.scrollDown();
} else {
content.insertAdjacentElement('afterbegin', el);
}
}
}
clearSpinner () {
this.querySelectorAll('.chat-content .spinner').forEach(u.removeElement);
}
onStatusMessageChanged (item) { onStatusMessageChanged (item) {
this.renderHeading(); this.renderHeading();
/** /**

View File

@ -21,10 +21,10 @@ export default class ChatContent extends CustomElement {
this.listenTo(this.model.messages, 'remove', this.requestUpdate); this.listenTo(this.model.messages, 'remove', this.requestUpdate);
this.listenTo(this.model.messages, 'reset', this.requestUpdate); this.listenTo(this.model.messages, 'reset', this.requestUpdate);
this.listenTo(this.model.notifications, 'change', this.requestUpdate); this.listenTo(this.model.notifications, 'change', this.requestUpdate);
this.listenTo(this.model.ui, 'change', this.requestUpdate);
if (this.model.occupants) { if (this.model.occupants) {
this.listenTo(this.model.occupants, 'change', this.requestUpdate); this.listenTo(this.model.occupants, 'change', this.requestUpdate);
} }
// We jot down whether we were scrolled down before rendering, because when an // We jot down whether we were scrolled down before rendering, because when an
// image loads, it triggers 'scroll' and the chat will be marked as scrolled, // image loads, it triggers 'scroll' and the chat will be marked as scrolled,
// which is technically true, but not what we want because the user // which is technically true, but not what we want because the user
@ -37,6 +37,7 @@ export default class ChatContent extends CustomElement {
render () { render () {
return html` return html`
${ this.model.ui.get('chat-content-spinner-top') ? html`<span class="spinner fa fa-spinner centered"></span>` : '' }
<converse-message-history <converse-message-history
.model=${this.model} .model=${this.model}
.messages=${[...this.model.messages.models]}> .messages=${[...this.model.messages.models]}>

View File

@ -40,6 +40,7 @@
// bosh_service_url: 'http://chat.example.org:5280/http-bind', // bosh_service_url: 'http://chat.example.org:5280/http-bind',
muc_show_logs_before_join: true, muc_show_logs_before_join: true,
whitelisted_plugins: ['converse-debug', 'converse-batched-probe'], whitelisted_plugins: ['converse-debug', 'converse-batched-probe'],
blacklisted_plugins: [],
}); });
}); });
</script> </script>