Move more methods from ChatBoxView to shared base class

This commit is contained in:
JC Brand 2021-01-28 22:50:53 +01:00
parent bb317d1abb
commit b8d710800a
5 changed files with 170 additions and 207 deletions

View File

@ -914,6 +914,7 @@ const ChatBox = ModelWithContact.extend({
return;
} else {
u.safeSave(this, {'hidden': false});
this.trigger('show');
}
return this;
},

View File

@ -3,11 +3,9 @@ import UserDetailsModal from 'modals/user-details.js';
import log from '@converse/headless/log';
import tpl_chatbox from 'templates/chatbox.js';
import tpl_chatbox_head from 'templates/chatbox_head.js';
import tpl_spinner from 'templates/spinner.js';
import { __ } from 'i18n';
import { _converse, api, converse } from '@converse/headless/core';
import { debounce } from 'lodash-es';
import { html, render } from 'lit-html';
import { render } from 'lit-html';
const u = converse.env.utils;
const { dayjs } = converse.env;
@ -71,8 +69,7 @@ export default class ChatView extends BaseChatView {
this.listenTo(this.model, 'change:show_help_messages', this.renderHelpMessages);
await this.model.messages.fetched;
this.model.maybeShow();
this.scrollDown();
!this.model.get('hidden') && this.afterShown()
/**
* Triggered once the {@link _converse.ChatBoxView} has been initialized
* @event _converse#chatBoxViewInitialized
@ -82,21 +79,6 @@ export default class ChatView extends BaseChatView {
api.trigger('chatBoxViewInitialized', this);
}
initDebounced () {
this.markScrolled = debounce(this._markScrolled, 100);
this.debouncedScrollDown = debounce(this.scrollDown, 100);
// For tests that use Jasmine.Clock we want to turn of
// debouncing, since setTimeout breaks.
if (api.settings.get('debounced_content_rendering')) {
this.renderChatHistory = debounce(() => this.renderChatContent(false), 100);
this.renderNotifications = debounce(() => this.renderChatContent(true), 100);
} else {
this.renderChatHistory = () => this.renderChatContent(false);
this.renderNotifications = () => this.renderChatContent(true);
}
}
render () {
const result = tpl_chatbox(Object.assign(this.model.toJSON(), { 'markScrolled': ev => this.markScrolled(ev) }));
render(result, this);
@ -110,22 +92,6 @@ export default class ChatView extends BaseChatView {
return this;
}
onMessageAdded (message) {
this.renderChatHistory();
if (u.isNewMessage(message)) {
if (message.get('sender') === 'me') {
// We remove the "scrolled" flag so that the chat area
// gets scrolled down. We always want to scroll down
// when the user writes a message as opposed to when a
// message is received.
this.model.set('scrolled', false);
} else if (this.model.get('scrolled', true)) {
this.showNewMessagesIndicator();
}
}
}
getNotifications () {
if (this.model.notifications.get('chat_state') === _converse.COMPOSING) {
return __('%1$s is typing', this.model.getDisplayName());
@ -147,22 +113,6 @@ export default class ChatView extends BaseChatView {
];
}
renderHelpMessages () {
render(
html`
<converse-chat-help
.model=${this.model}
.messages=${this.getHelpMessages()}
?hidden=${!this.model.get('show_help_messages')}
type="info"
chat_type="${this.model.get('type')}"
></converse-chat-help>
`,
this.help_container
);
}
showControlBox () {
// Used in mobile view, to navigate back to the controlbox
_converse.chatboxviews.get('controlbox')?.show();
@ -285,51 +235,6 @@ export default class ChatView extends BaseChatView {
}
}
/**
* Scroll to the previously saved scrollTop position, or scroll
* down if it wasn't set.
*/
maintainScrollTop () {
const pos = this.model.get('scrollTop');
if (pos) {
this.msgs_container.scrollTop = pos;
} else {
this.scrollDown();
}
}
addSpinner (append = false) {
if (this.querySelector('.spinner') === null) {
const el = u.getElementFromTemplateResult(tpl_spinner());
if (append) {
this.content.insertAdjacentElement('beforeend', el);
this.scrollDown();
} else {
this.content.insertAdjacentElement('afterbegin', el);
}
}
}
clearSpinner () {
this.content.querySelectorAll('.spinner').forEach(u.removeElement);
}
onStatusMessageChanged (item) {
this.renderHeading();
/**
* When a contact's custom status message has changed.
* @event _converse#contactStatusMessageChanged
* @type {object}
* @property { object } contact - The chat buddy
* @property { string } message - The message text
* @example _converse.api.listen.on('contactStatusMessageChanged', obj => { ... });
*/
api.trigger('contactStatusMessageChanged', {
'contact': item.attributes,
'message': item.get('status')
});
}
/**
* Given a message element, determine wether it should be
* marked as a followup message to the previous element.
@ -472,10 +377,6 @@ export default class ChatView extends BaseChatView {
}
}
getOwnMessages () {
return this.model.messages.filter({ 'sender': 'me' });
}
onEscapePressed (ev) {
ev.preventDefault();
const idx = this.model.messages.findLastIndex('correcting');
@ -506,72 +407,6 @@ export default class ChatView extends BaseChatView {
}
}
onMessageEditButtonClicked (message) {
const currently_correcting = this.model.messages.findWhere('correcting');
const unsent_text = this.querySelector('.chat-textarea')?.value;
if (unsent_text && (!currently_correcting || currently_correcting.get('message') !== unsent_text)) {
if (!confirm(__('You have an unsent message which will be lost if you continue. Are you sure?'))) {
return;
}
}
if (currently_correcting !== message) {
currently_correcting?.save('correcting', false);
message.save('correcting', true);
this.insertIntoTextArea(u.prefixMentions(message), true, true);
} else {
message.save('correcting', false);
this.insertIntoTextArea('', true, false);
}
}
editLaterMessage () {
let message;
let idx = this.model.messages.findLastIndex('correcting');
if (idx >= 0) {
this.model.messages.at(idx).save('correcting', false);
while (idx < this.model.messages.length - 1) {
idx += 1;
const candidate = this.model.messages.at(idx);
if (candidate.get('editable')) {
message = candidate;
break;
}
}
}
if (message) {
this.insertIntoTextArea(u.prefixMentions(message), true, true);
message.save('correcting', true);
} else {
this.insertIntoTextArea('', true, false);
}
}
editEarlierMessage () {
let message;
let idx = this.model.messages.findLastIndex('correcting');
if (idx >= 0) {
this.model.messages.at(idx).save('correcting', false);
while (idx > 0) {
idx -= 1;
const candidate = this.model.messages.at(idx);
if (candidate.get('editable')) {
message = candidate;
break;
}
}
}
message =
message ||
this.getOwnMessages()
.reverse()
.find(m => m.get('editable'));
if (message) {
this.insertIntoTextArea(u.prefixMentions(message), true, true);
message.save('correcting', true);
}
}
inputChanged (ev) { // eslint-disable-line class-methods-use-this
const height = ev.target.scrollHeight + 'px';
if (ev.target.style.height != height) {
@ -580,17 +415,6 @@ export default class ChatView extends BaseChatView {
}
}
async clearMessages (ev) {
if (ev && ev.preventDefault) {
ev.preventDefault();
}
const result = confirm(__('Are you sure you want to clear the messages from this conversation?'));
if (result === true) {
await this.model.clearMessages();
}
return this;
}
onPresenceChanged (item) {
const show = item.get('show');
const fullname = this.model.getDisplayName();
@ -639,10 +463,6 @@ export default class ChatView extends BaseChatView {
this.maybeFocus();
}
showNewMessagesIndicator () {
u.showElement(this.querySelector('.new-msgs-indicator'));
}
viewUnreadMessages () {
this.model.save({ 'scrolled': false, 'scrollTop': null });
this.scrollDown();

View File

@ -67,10 +67,8 @@ class ControlBoxView extends ElementView {
}
}
async close (ev) {
if (ev && ev.preventDefault) {
ev.preventDefault();
}
close (ev) {
ev?.preventDefault?.();
if (
ev?.name === 'closeAllChatBoxes' &&
(_converse.disconnection_cause !== _converse.LOGOUT ||
@ -81,30 +79,11 @@ class ControlBoxView extends ElementView {
if (api.settings.get('sticky_controlbox')) {
return;
}
const connection = _converse?.connection || {};
if (connection.connected && !connection.disconnecting) {
await new Promise((resolve, reject) => {
return this.model.save(
{ 'closed': true },
{ 'success': resolve, 'error': reject, 'wait': true }
);
});
} else {
this.model.trigger('hide');
}
u.safeSave(this.model, { 'closed': true });
api.trigger('controlBoxClosed', this);
return this;
}
hide () {
if (api.settings.get('sticky_controlbox')) {
return;
}
u.addClass('hidden', this);
api.trigger('chatBoxClosed', this);
return this;
}
show () {
this.model.set('closed', false);
this.classList.remove('hidden');

View File

@ -1,10 +1,11 @@
import debounce from 'lodash/debounce';
import log from '@converse/headless/log';
import tpl_chatbox_message_form from 'templates/chatbox_message_form.js';
import tpl_spinner from 'templates/spinner.js';
import tpl_toolbar from 'templates/toolbar.js';
import { ElementView } from '@converse/skeletor/src/element.js';
import { __ } from 'i18n';
import { _converse, api, converse } from '@converse/headless/core';
import { debounce } from 'lodash-es';
import { html, render } from 'lit-html';
const u = converse.env.utils;
@ -45,6 +46,21 @@ export default class BaseChatView extends ElementView {
render(this.tpl_chat_content({ messages, 'notifications': this.getNotifications() }), this.msgs_container);
}
renderHelpMessages () {
render(
html`
<converse-chat-help
.model=${this.model}
.messages=${this.getHelpMessages()}
?hidden=${!this.model.get('show_help_messages')}
type="info"
chat_type="${this.model.get('type')}"
></converse-chat-help>
`,
this.help_container
);
}
renderMessageForm () {
const form_container = this.querySelector('.message-form-container');
render(
@ -160,6 +176,114 @@ export default class BaseChatView extends ElementView {
api.trigger('chatBoxFocused', this, ev);
}
/**
* Scroll to the previously saved scrollTop position, or scroll
* down if it wasn't set.
*/
maintainScrollTop () {
const pos = this.model.get('scrollTop');
if (pos) {
this.msgs_container.scrollTop = pos;
} else {
this.scrollDown();
}
}
addSpinner (append = false) {
if (this.querySelector('.spinner') === null) {
const el = u.getElementFromTemplateResult(tpl_spinner());
if (append) {
this.content.insertAdjacentElement('beforeend', el);
this.scrollDown();
} else {
this.content.insertAdjacentElement('afterbegin', el);
}
}
}
clearSpinner () {
this.content.querySelectorAll('.spinner').forEach(u.removeElement);
}
onStatusMessageChanged (item) {
this.renderHeading();
/**
* When a contact's custom status message has changed.
* @event _converse#contactStatusMessageChanged
* @type {object}
* @property { object } contact - The chat buddy
* @property { string } message - The message text
* @example _converse.api.listen.on('contactStatusMessageChanged', obj => { ... });
*/
api.trigger('contactStatusMessageChanged', {
'contact': item.attributes,
'message': item.get('status')
});
}
getOwnMessages () {
return this.model.messages.filter({ 'sender': 'me' });
}
async clearMessages (ev) {
if (ev && ev.preventDefault) {
ev.preventDefault();
}
const result = confirm(__('Are you sure you want to clear the messages from this conversation?'));
if (result === true) {
await this.model.clearMessages();
}
return this;
}
editEarlierMessage () {
let message;
let idx = this.model.messages.findLastIndex('correcting');
if (idx >= 0) {
this.model.messages.at(idx).save('correcting', false);
while (idx > 0) {
idx -= 1;
const candidate = this.model.messages.at(idx);
if (candidate.get('editable')) {
message = candidate;
break;
}
}
}
message =
message ||
this.getOwnMessages()
.reverse()
.find(m => m.get('editable'));
if (message) {
this.insertIntoTextArea(u.prefixMentions(message), true, true);
message.save('correcting', true);
}
}
editLaterMessage () {
let message;
let idx = this.model.messages.findLastIndex('correcting');
if (idx >= 0) {
this.model.messages.at(idx).save('correcting', false);
while (idx < this.model.messages.length - 1) {
idx += 1;
const candidate = this.model.messages.at(idx);
if (candidate.get('editable')) {
message = candidate;
break;
}
}
}
if (message) {
this.insertIntoTextArea(u.prefixMentions(message), true, true);
message.save('correcting', true);
} else {
this.insertIntoTextArea('', true, false);
}
}
async getHeadingDropdownItem (promise_or_data) { // eslint-disable-line class-methods-use-this
const data = await promise_or_data;
return html`
@ -183,6 +307,26 @@ export default class BaseChatView extends ElementView {
}
}
showNewMessagesIndicator () {
u.showElement(this.querySelector('.new-msgs-indicator'));
}
onMessageAdded (message) {
this.renderChatHistory();
if (u.isNewMessage(message)) {
if (message.get('sender') === 'me') {
// We remove the "scrolled" flag so that the chat area
// gets scrolled down. We always want to scroll down
// when the user writes a message as opposed to when a
// message is received.
this.model.set('scrolled', false);
} else if (this.model.get('scrolled', true)) {
this.showNewMessagesIndicator();
}
}
}
onEmojiReceivedFromPicker (emoji) {
const model = this.querySelector('converse-emoji-picker').model;
const autocompleting = model.get('autocompleting');
@ -190,6 +334,24 @@ export default class BaseChatView extends ElementView {
this.insertIntoTextArea(emoji, autocompleting, false, ac_position);
}
onMessageEditButtonClicked (message) {
const currently_correcting = this.model.messages.findWhere('correcting');
const unsent_text = this.querySelector('.chat-textarea')?.value;
if (unsent_text && (!currently_correcting || currently_correcting.get('message') !== unsent_text)) {
if (!confirm(__('You have an unsent message which will be lost if you continue. Are you sure?'))) {
return;
}
}
if (currently_correcting !== message) {
currently_correcting?.save('correcting', false);
message.save('correcting', true);
this.insertIntoTextArea(u.prefixMentions(message), true, true);
} else {
message.save('correcting', false);
this.insertIntoTextArea('', true, false);
}
}
/**
* Insert a particular string value into the textarea of this chat box.
* @private

View File

@ -13,6 +13,7 @@ export default (o) => html`
<div class="suggestion-box">
<ul class="suggestion-box__results suggestion-box__results--above" hidden=""></ul>
<textarea
autofocus
type="text"
class="chat-textarea suggestion-box__input
${ o.show_send_button ? 'chat-textarea-send-button' : '' }