Use API to get chat/room models in components

This commit is contained in:
JC Brand 2021-06-22 14:07:45 +02:00
parent afd737f965
commit 6dea5959cc
6 changed files with 56 additions and 31 deletions

View File

@ -15,17 +15,26 @@ export default class ChatBottomPanel extends ElementView {
'click .toggle-clear': 'clearMessages' 'click .toggle-clear': 'clearMessages'
}; };
constructor () {
super();
this.debouncedRender = debounce(this.render, 100);
}
async connectedCallback () { async connectedCallback () {
super.connectedCallback(); super.connectedCallback();
this.debouncedRender = debounce(this.render, 100); await this.initialize();
this.model = _converse.chatboxes.get(this.getAttribute('jid')); this.render(); // don't call in initialize, since the MUCBottomPanel subclasses it
// and we want to render after it has finished as wel.
}
async initialize () {
this.model = await api.chatboxes.get(this.getAttribute('jid'));
await this.model.initialized; await this.model.initialized;
this.listenTo(this.model, 'change:num_unread', this.debouncedRender) this.listenTo(this.model, 'change:num_unread', this.debouncedRender)
this.listenTo(this.model, 'emoji-picker-autocomplete', this.autocompleteInPicker); this.listenTo(this.model, 'emoji-picker-autocomplete', this.autocompleteInPicker);
this.addEventListener('focusin', ev => this.emitFocused(ev)); this.addEventListener('focusin', ev => this.emitFocused(ev));
this.addEventListener('focusout', ev => this.emitBlurred(ev)); this.addEventListener('focusout', ev => this.emitBlurred(ev));
this.render();
} }
render () { render () {

View File

@ -14,8 +14,7 @@ describe("A Chat Message", function () {
await mock.openChatBoxFor(_converse, contact_jid); await mock.openChatBoxFor(_converse, contact_jid);
const view = _converse.chatboxviews.get(contact_jid); const view = _converse.chatboxviews.get(contact_jid);
await _converse.handleMessageStanza(mock.createChatMessage(_converse, contact_jid, 'This message will be read')); await _converse.handleMessageStanza(mock.createChatMessage(_converse, contact_jid, 'This message will be read'));
const msg_el = await u.waitUntil(() => view.querySelector('converse-chat-message')); await u.waitUntil(() => view.querySelector('converse-chat-message .chat-msg__text')?.textContent === 'This message will be read');
expect(msg_el.querySelector('.chat-msg__text').textContent).toBe('This message will be read');
expect(view.model.get('num_unread')).toBe(0); expect(view.model.get('num_unread')).toBe(0);
_converse.windowState = 'hidden'; _converse.windowState = 'hidden';
@ -26,6 +25,7 @@ describe("A Chat Message", function () {
expect(view.model.get('first_unread_id')).toBe(view.model.messages.last().get('id')); expect(view.model.get('first_unread_id')).toBe(view.model.messages.last().get('id'));
await u.waitUntil(() => view.querySelectorAll('converse-chat-message').length === 2); await u.waitUntil(() => view.querySelectorAll('converse-chat-message').length === 2);
await u.waitUntil(() => view.querySelector('converse-chat-message:last-child .chat-msg__text')?.textContent === 'This message will be new');
const last_msg_el = view.querySelector('converse-chat-message:last-child'); const last_msg_el = view.querySelector('converse-chat-message:last-child');
expect(last_msg_el.firstElementChild?.textContent).toBe('New messages'); expect(last_msg_el.firstElementChild?.textContent).toBe('New messages');
done(); done();

View File

@ -1,6 +1,5 @@
import 'shared/autocomplete/index.js'; import 'shared/autocomplete/index.js';
import BottomPanel from 'plugins/chatview/bottom-panel.js'; import BottomPanel from 'plugins/chatview/bottom-panel.js';
import debounce from 'lodash-es/debounce';
import tpl_muc_bottom_panel from './templates/muc-bottom-panel.js'; import tpl_muc_bottom_panel from './templates/muc-bottom-panel.js';
import { _converse, api, converse } from "@converse/headless/core"; import { _converse, api, converse } from "@converse/headless/core";
import { render } from 'lit'; import { render } from 'lit';
@ -15,17 +14,14 @@ export default class MUCBottomPanel extends BottomPanel {
'click .send-button': 'sendButtonClicked', 'click .send-button': 'sendButtonClicked',
} }
async connectedCallback () { async initialize () {
// this.model gets set in the super method and we also wait there for this.model.initialized await super.initialize();
await super.connectedCallback();
this.debouncedRender = debounce(this.render, 100);
this.listenTo(this.model, 'change:hidden_occupants', this.debouncedRender); this.listenTo(this.model, 'change:hidden_occupants', this.debouncedRender);
this.listenTo(this.model, 'change:num_unread_general', this.debouncedRender) this.listenTo(this.model, 'change:num_unread_general', this.debouncedRender)
this.listenTo(this.model.features, 'change:moderated', this.debouncedRender); this.listenTo(this.model.features, 'change:moderated', this.debouncedRender);
this.listenTo(this.model.occupants, 'add', this.renderIfOwnOccupant) this.listenTo(this.model.occupants, 'add', this.renderIfOwnOccupant)
this.listenTo(this.model.occupants, 'change:role', this.renderIfOwnOccupant); this.listenTo(this.model.occupants, 'change:role', this.renderIfOwnOccupant);
this.listenTo(this.model.session, 'change:connection_status', this.debouncedRender); this.listenTo(this.model.session, 'change:connection_status', this.debouncedRender);
this.render();
} }
render () { render () {

View File

@ -12,7 +12,7 @@ describe("Emojis", function () {
const muc_jid = 'lounge@montague.lit'; const muc_jid = 'lounge@montague.lit';
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
const view = _converse.chatboxviews.get(muc_jid); const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => view.querySelector('converse-emoji-dropdown')); await u.waitUntil(() => view.querySelector('converse-emoji-picker'));
const textarea = view.querySelector('textarea.chat-textarea'); const textarea = view.querySelector('textarea.chat-textarea');
textarea.value = ':gri'; textarea.value = ':gri';
@ -80,7 +80,7 @@ describe("Emojis", function () {
await mock.waitForRoster(_converse, 'current', 0); await mock.waitForRoster(_converse, 'current', 0);
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
const view = _converse.chatboxviews.get(muc_jid); const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => view.querySelector('converse-emoji-dropdown')); await u.waitUntil(() => view.querySelector('converse-emoji-picker'));
const textarea = view.querySelector('textarea.chat-textarea'); const textarea = view.querySelector('textarea.chat-textarea');
textarea.value = ':'; textarea.value = ':';
// Press tab // Press tab
@ -130,7 +130,7 @@ describe("Emojis", function () {
await mock.waitForRoster(_converse, 'current', 0); await mock.waitForRoster(_converse, 'current', 0);
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
const view = _converse.chatboxviews.get(muc_jid); const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => view.querySelector('converse-emoji-dropdown')); await u.waitUntil(() => view.querySelector('converse-emoji-picker'));
const textarea = view.querySelector('textarea.chat-textarea'); const textarea = view.querySelector('textarea.chat-textarea');
textarea.value = ':gri'; textarea.value = ':gri';

View File

@ -1,6 +1,6 @@
import './message-history'; import './message-history';
import { CustomElement } from 'shared/components/element.js'; import { CustomElement } from 'shared/components/element.js';
import { _converse, api } from '@converse/headless/core'; import { api } from '@converse/headless/core';
import { html } from 'lit'; import { html } from 'lit';
import { markScrolled } from './utils.js'; import { markScrolled } from './utils.js';
@ -25,8 +25,8 @@ export default class ChatContent extends CustomElement {
this.removeEventListener('scroll', markScrolled); this.removeEventListener('scroll', markScrolled);
} }
initialize () { async initialize () {
this.model = _converse.chatboxes.get(this.jid); await this.setModels();
this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate); this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate);
this.listenTo(this.model.messages, 'add', this.requestUpdate); this.listenTo(this.model.messages, 'add', this.requestUpdate);
this.listenTo(this.model.messages, 'change', this.requestUpdate); this.listenTo(this.model.messages, 'change', this.requestUpdate);
@ -43,7 +43,16 @@ export default class ChatContent extends CustomElement {
this.addEventListener('scroll', markScrolled); this.addEventListener('scroll', markScrolled);
} }
async setModels () {
this.model = await api.chatboxes.get(this.jid);
await this.model.initialized;
this.requestUpdate();
}
render () { render () {
if (!this.model) {
return '';
}
// This element has "flex-direction: reverse", so elements here are // This element has "flex-direction: reverse", so elements here are
// shown in reverse order. // shown in reverse order.
return html` return html`

View File

@ -28,23 +28,13 @@ export default class Message extends CustomElement {
} }
} }
render () {
if (this.show_spinner) {
return tpl_spinner();
} else if (this.model.get('file') && !this.model.get('oob_url')) {
return this.renderFileProgress();
} else if (['error', 'info'].includes(this.model.get('type'))) {
return this.renderInfoMessage();
} else {
return this.renderChatMessage();
}
}
connectedCallback () { connectedCallback () {
super.connectedCallback(); super.connectedCallback();
this.chatbox = _converse.chatboxes.get(this.jid); this.initialize();
this.model = this.chatbox.messages.get(this.mid); }
async initialize () {
await this.setModels();
this.listenTo(this.chatbox, 'change:first_unread_id', this.requestUpdate); this.listenTo(this.chatbox, 'change:first_unread_id', this.requestUpdate);
this.listenTo(this.model, 'change', this.requestUpdate); this.listenTo(this.model, 'change', this.requestUpdate);
this.model.vcard && this.listenTo(this.model.vcard, 'change', this.requestUpdate); this.model.vcard && this.listenTo(this.model.vcard, 'change', this.requestUpdate);
@ -60,6 +50,27 @@ export default class Message extends CustomElement {
} }
} }
async setModels () {
this.chatbox = await api.chatboxes.get(this.jid);
await this.chatbox.initialized;
this.model = this.chatbox.messages.get(this.mid);
this.requestUpdate();
}
render () {
if (!this.model) {
return '';
} else if (this.show_spinner) {
return tpl_spinner();
} else if (this.model.get('file') && !this.model.get('oob_url')) {
return this.renderFileProgress();
} else if (['error', 'info'].includes(this.model.get('type'))) {
return this.renderInfoMessage();
} else {
return this.renderChatMessage();
}
}
getProps () { getProps () {
return Object.assign( return Object.assign(
this.model.toJSON(), this.model.toJSON(),