Commit 8a7b2558 authored by JC Brand's avatar JC Brand

Show error messages via objects

Instead of injecting them directly into the DOM.
parent 41318504
......@@ -1776,7 +1776,6 @@
.t("We didn't like the name").nodeTree;
const view = _converse.chatboxviews.get('problematic@muc.montague.lit');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.el.querySelector('.chatroom-body .disconnect-msg').textContent.trim())
.toBe('This groupchat no longer exists');
......@@ -3062,7 +3061,7 @@
keyCode: 13
});
expect(_converse.connection.send).not.toHaveBeenCalled();
expect(view.el.querySelectorAll('.chat-error').length).toBe(1);
await u.waitUntil(() => view.el.querySelectorAll('.chat-error').length);
expect(view.el.querySelector('.chat-error').textContent.trim())
.toBe('Error: couldn\'t find a groupchat participant based on your arguments');
......@@ -3266,7 +3265,6 @@
await test_utils.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
const view = _converse.chatboxviews.get('lounge@montague.lit');
spyOn(view.model, 'setAffiliation').and.callThrough();
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view, 'validateRoleOrAffiliationChangeArgs').and.callThrough();
let presence = $pres({
......@@ -3290,17 +3288,20 @@
keyCode: 13
});
expect(view.validateRoleOrAffiliationChangeArgs).toHaveBeenCalled();
expect(view.showErrorMessage).toHaveBeenCalledWith(
const err_msg = await u.waitUntil(() => view.el.querySelector('.chat-error'));
expect(err_msg.textContent.trim()).toBe(
"Error: the \"owner\" command takes two arguments, the user's nickname and optionally a reason.");
expect(view.model.setAffiliation).not.toHaveBeenCalled();
// XXX: Calling onFormSubmitted directly, trying
// again via triggering Event doesn't work for some weird
// reason.
textarea.value = '/owner nobody You\'re responsible';
view.onFormSubmitted(new Event('submit'));
expect(view.showErrorMessage).toHaveBeenCalledWith(
await u.waitUntil(() => view.el.querySelectorAll('.chat-error').length === 2);
expect(Array.from(view.el.querySelectorAll('.chat-error')).pop().textContent.trim()).toBe(
"Error: couldn't find a groupchat participant based on your arguments");
expect(view.model.setAffiliation).not.toHaveBeenCalled();
// Call now with the correct of arguments.
......@@ -3312,7 +3313,6 @@
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(3);
expect(view.model.setAffiliation).toHaveBeenCalled();
expect(view.showErrorMessage.calls.count()).toBe(2);
// Check that the member list now gets updated
expect(sent_IQ.toLocaleString()).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
......@@ -3357,7 +3357,6 @@
await test_utils.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
const view = _converse.chatboxviews.get('lounge@montague.lit');
spyOn(view.model, 'setAffiliation').and.callThrough();
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view, 'validateRoleOrAffiliationChangeArgs').and.callThrough();
let presence = $pres({
......@@ -3381,8 +3380,7 @@
keyCode: 13
});
expect(view.validateRoleOrAffiliationChangeArgs).toHaveBeenCalled();
expect(view.showErrorMessage).toHaveBeenCalled();
expect(view.el.querySelector('.message:last-child').textContent.trim()).toBe(
await u.waitUntil(() => view.el.querySelector('.message:last-child')?.textContent?.trim() ===
"Error: the \"ban\" command takes two arguments, the user's nickname and optionally a reason.");
expect(view.model.setAffiliation).not.toHaveBeenCalled();
......@@ -3394,7 +3392,6 @@
view.onFormSubmitted(new Event('submit'));
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.showErrorMessage.calls.count()).toBe(1);
expect(view.model.setAffiliation).toHaveBeenCalled();
// Check that the member list now gets updated
expect(sent_IQ.toLocaleString()).toBe(
......@@ -3422,7 +3419,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
await u.waitUntil(() => view.el.querySelectorAll('.chat-info').length === 2);
expect(view.el.querySelectorAll('.chat-info__message')[0].textContent.trim()).toBe("annoyingGuy has been banned by romeo");
expect(view.el.querySelectorAll('.chat-info__message')[1].textContent.trim()).toBe("annoyingGuy has been banned by romeo");
expect(view.el.querySelector('.chat-info:last-child q').textContent.trim()).toBe("You're annoying");
presence = $pres({
'from': 'lounge@montague.lit/joe2',
......@@ -3439,7 +3436,7 @@
textarea.value = '/ban joe22';
view.onFormSubmitted(new Event('submit'));
expect(view.el.querySelector('.message:last-child').textContent.trim()).toBe(
await u.waitUntil(() => view.el.querySelector('.message:last-child')?.textContent?.trim() ===
"Error: couldn't find a groupchat participant based on your arguments");
done();
}));
......@@ -3461,7 +3458,6 @@
await test_utils.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
const view = _converse.api.chatviews.get(muc_jid);
spyOn(view.model, 'setRole').and.callThrough();
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view, 'validateRoleOrAffiliationChangeArgs').and.callThrough();
let presence = $pres({
......@@ -3485,7 +3481,7 @@
keyCode: 13
});
expect(view.validateRoleOrAffiliationChangeArgs).toHaveBeenCalled();
expect(view.showErrorMessage).toHaveBeenCalledWith(
await u.waitUntil(() => view.el.querySelector('.message:last-child')?.textContent?.trim() ===
"Error: the \"kick\" command takes two arguments, the user's nickname and optionally a reason.");
expect(view.model.setRole).not.toHaveBeenCalled();
// Call now with the correct amount of arguments.
......@@ -3496,7 +3492,6 @@
view.onFormSubmitted(new Event('submit'));
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.showErrorMessage.calls.count()).toBe(1);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
......@@ -3532,7 +3527,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
await u.waitUntil(() => view.el.querySelectorAll('.chat-info').length === 2);
expect(view.el.querySelectorAll('.chat-info__message')[0].textContent.trim()).toBe("annoying guy has been kicked out by romeo");
expect(view.el.querySelectorAll('.chat-info__message')[1].textContent.trim()).toBe("annoying guy has been kicked out by romeo");
expect(view.el.querySelector('.chat-info:last-child q').textContent.trim()).toBe("You're annoying");
done();
}));
......@@ -3553,7 +3548,6 @@
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
spyOn(view.model, 'setRole').and.callThrough();
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view, 'validateRoleOrAffiliationChangeArgs').and.callThrough();
// New user enters the groupchat
......@@ -3590,7 +3584,7 @@
});
expect(view.validateRoleOrAffiliationChangeArgs).toHaveBeenCalled();
expect(view.showErrorMessage).toHaveBeenCalledWith(
await u.waitUntil(() => view.el.querySelector('.message:last-child')?.textContent?.trim() ===
"Error: the \"op\" command takes two arguments, the user's nickname and optionally a reason.");
expect(view.model.setRole).not.toHaveBeenCalled();
......@@ -3602,7 +3596,6 @@
view.onFormSubmitted(new Event('submit'));
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.showErrorMessage.calls.count()).toBe(1);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
......@@ -3699,7 +3692,6 @@
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
spyOn(view.model, 'setRole').and.callThrough();
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view, 'validateRoleOrAffiliationChangeArgs').and.callThrough();
// New user enters the groupchat
......@@ -3712,7 +3704,7 @@
* </x>
* </presence>
*/
var presence = $pres({
let presence = $pres({
'from': 'lounge@montague.lit/annoyingGuy',
'id':'27C55F89-1C6A-459A-9EB5-77690145D624',
'to': 'romeo@montague.lit/desktop'
......@@ -3736,7 +3728,7 @@
});
expect(view.validateRoleOrAffiliationChangeArgs).toHaveBeenCalled();
expect(view.showErrorMessage).toHaveBeenCalledWith(
await u.waitUntil(() => view.el.querySelector('.message:last-child')?.textContent?.trim() ===
"Error: the \"mute\" command takes two arguments, the user's nickname and optionally a reason.");
expect(view.model.setRole).not.toHaveBeenCalled();
// Call now with the correct amount of arguments.
......@@ -3747,7 +3739,6 @@
view.onFormSubmitted(new Event('submit'));
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.showErrorMessage.calls.count()).toBe(1);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
......@@ -4060,7 +4051,6 @@
.c('error').attrs({by:'lounge@montague.lit', type:'auth'})
.c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent.trim())
.toBe('You have been banned from this groupchat.');
......@@ -4084,7 +4074,6 @@
.c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
const view = _converse.chatboxviews.get(muc_jid);
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(sizzle('.chatroom-body form.chatroom-form label:first', view.el).pop().textContent.trim())
.toBe('Please choose your nickname');
......@@ -4128,7 +4117,6 @@
.c('conflict').attrs({'xmlns':'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
const view = _converse.chatboxviews.get(muc_jid);
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view.model, 'join').and.callThrough();
// Simulate repeatedly that there's already someone in the groupchat
......@@ -4191,7 +4179,6 @@
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
.c('error').attrs({by:'lounge@montague.lit', type:'cancel'})
.c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent.trim())
.toBe('You are not allowed to create new groupchats.');
......@@ -4233,7 +4220,6 @@
.c('error').attrs({by:'lounge@montague.lit', type:'cancel'})
.c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent.trim())
.toBe("Your nickname doesn't conform to this groupchat's policies.");
......@@ -4275,7 +4261,6 @@
.c('error').attrs({by:'lounge@montague.lit', type:'cancel'})
.c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent.trim())
.toBe("This groupchat does not (yet) exist.");
......@@ -4317,7 +4302,6 @@
.c('error').attrs({by:'lounge@montague.lit', type:'cancel'})
.c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent.trim())
.toBe("This groupchat has reached its maximum number of participants.");
......
......@@ -706,14 +706,18 @@
_converse.connection._dataRecv(test_utils.createRequest(error));
await u.waitUntil(() => view.el.querySelectorAll('.chat-error').length === 1);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0);
expect(view.model.messages.length).toBe(2);
expect(view.model.messages.last().get('retracted')).toBeFalsy();
expect(view.model.messages.last().get('is_ephemeral')).toBeFalsy();
expect(view.model.messages.last().get('editable')).toBeTruthy();
expect(view.model.messages.length).toBe(3);
expect(view.model.messages.at(1).get('retracted')).toBeFalsy();
expect(view.model.messages.at(1).get('is_ephemeral')).toBeFalsy();
expect(view.model.messages.at(1).get('editable')).toBeTruthy();
const err_msg = "Sorry, something went wrong while trying to retract your message."
expect(view.model.messages.at(2).get('message')).toBe(err_msg);
expect(view.model.messages.at(2).get('type')).toBe('error');
expect(view.el.querySelectorAll('.chat-error').length).toBe(1);
const errmsg = view.el.querySelector('.chat-error');
expect(errmsg.textContent).toBe("Sorry, something went wrong while trying to retract your message.");
expect(errmsg.textContent.trim()).toBe("Sorry, something went wrong while trying to retract your message.");
done();
}));
......@@ -746,15 +750,15 @@
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg').length === 1);
await u.waitUntil(() => view.el.querySelectorAll('.chat-msg--retracted').length === 0);
expect(view.model.messages.length).toBe(2);
expect(view.model.messages.last().get('retracted')).toBeFalsy();
expect(view.model.messages.last().get('is_ephemeral')).toBeFalsy();
expect(view.model.messages.last().get('editable')).toBeTruthy();
expect(view.model.messages.length).toBe(4);
expect(view.model.messages.at(1).get('retracted')).toBeFalsy();
expect(view.model.messages.at(1).get('is_ephemeral')).toBeFalsy();
expect(view.model.messages.at(1).get('editable')).toBeTruthy();
const error_messages = view.el.querySelectorAll('.chat-error');
expect(error_messages.length).toBe(2);
expect(error_messages[0].textContent).toBe("Sorry, something went wrong while trying to retract your message.");
expect(error_messages[1].textContent).toBe("Timeout Error: No response from server");
expect(error_messages[0].textContent.trim()).toBe("Sorry, something went wrong while trying to retract your message.");
expect(error_messages[1].textContent.trim()).toBe("Timeout Error: No response from server");
done();
}));
......
......@@ -15,7 +15,6 @@ import log from "@converse/headless/log";
import tpl_chatbox from "templates/chatbox.js";
import tpl_chatbox_head from "templates/chatbox_head.js";
import tpl_chatbox_message_form from "templates/chatbox_message_form.html";
import tpl_error_message from "templates/error_message.html";
import tpl_help_message from "templates/help_message.html";
import tpl_info from "templates/info.html";
import tpl_new_day from "templates/new_day.html";
......@@ -518,14 +517,6 @@ converse.plugins.add('converse-chatview', {
return isodate;
},
showErrorMessage (message) {
this.msgs_container.insertAdjacentHTML(
'beforeend',
tpl_error_message({'message': message, 'isodate': (new Date()).toISOString() })
);
this.scrollDown();
},
addSpinner (append=false) {
if (this.el.querySelector('.spinner') === null) {
if (append) {
......
......@@ -1008,13 +1008,9 @@ converse.plugins.add('converse-muc-views', {
retractOwnMessage(message) {
this.model.retractOwnMessage(message)
.catch(e => {
const errmsg = __('Sorry, something went wrong while trying to retract your message.');
if (u.isErrorStanza(e)) {
this.showErrorMessage(errmsg);
} else {
this.showErrorMessage(errmsg);
this.showErrorMessage(e.message);
}
const message = __('Sorry, something went wrong while trying to retract your message.');
this.model.createMessage({message, 'type': 'error'});
!u.isErrorStanza(e) && this.model.createMessage({'message': e.message, 'type': 'error'});
log.error(e);
});
},
......@@ -1327,7 +1323,8 @@ converse.plugins.add('converse-muc-views', {
}
}
if (show_error) {
this.showErrorMessage(__('Forbidden: you do not have the necessary role in order to do that.'))
const message = __('Forbidden: you do not have the necessary role in order to do that.');
this.model.createMessage({message, 'type': 'error'});
}
return false;
},
......@@ -1347,16 +1344,19 @@ converse.plugins.add('converse-muc-views', {
}
}
if (show_error) {
this.showErrorMessage(__('Forbidden: you do not have the necessary affiliation in order to do that.'))
const message = __('Forbidden: you do not have the necessary affiliation in order to do that.');
this.model.createMessage({message, 'type': 'error'});
}
return false;
},
validateRoleOrAffiliationChangeArgs (command, args) {
if (!args) {
this.showErrorMessage(
__('Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.', command)
const message = __(
'Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.',
command
);
this.model.createMessage({message, 'type': 'error'});
return false;
}
return true;
......@@ -1371,17 +1371,20 @@ converse.plugins.add('converse-muc-views', {
}
const [text, references] = this.model.parseTextForReferences(args); // eslint-disable-line no-unused-vars
if (!references.length) {
this.showErrorMessage(__("Error: couldn't find a groupchat participant based on your arguments"));
const message = __("Error: couldn't find a groupchat participant based on your arguments");
this.model.createMessage({message, 'type': 'error'});
return;
}
if (references.length > 1) {
this.showErrorMessage(__("Error: found multiple groupchat participant based on your arguments"));
const message = __("Error: found multiple groupchat participant based on your arguments");
this.model.createMessage({message, 'type': 'error'});
return;
}
const nick_or_jid = references.pop().value;
const reason = args.split(nick_or_jid, 2)[1];
if (reason && !reason.startsWith(' ')) {
this.showErrorMessage(__("Error: couldn't find a groupchat participant based on your arguments"));
const message = __("Error: couldn't find a groupchat participant based on your arguments");
this.model.createMessage({message, 'type': 'error'});
return;
}
return nick_or_jid;
......@@ -1412,10 +1415,11 @@ converse.plugins.add('converse-muc-views', {
if (u.isValidJID(nick_or_jid)) {
jid = nick_or_jid;
} else {
this.showErrorMessage(__(
const message = __(
"Couldn't find a participant with that nickname. "+
"They might have left the groupchat."
));
);
this.model.createMessage({message, 'type': 'error'});
return;
}
}
......@@ -1459,10 +1463,10 @@ converse.plugins.add('converse-muc-views', {
onCommandError (err) {
log.fatal(err);
this.showErrorMessage(
const message =
__("Sorry, an error happened while running the command.") + " " +
__("Check your browser's developer console for details.")
);
__("Check your browser's developer console for details.");
this.model.createMessage({message, 'type': 'error'});
},
getAllowedCommands () {
......@@ -1608,7 +1612,9 @@ converse.plugins.add('converse-muc-views', {
break;
} else if (args.length === 0) {
// e.g. Your nickname is "coolguy69"
this.showErrorMessage(__('Your nickname is "%1$s"', this.model.get('nick')))
const message = __('Your nickname is "%1$s"', this.model.get('nick'));
this.model.createMessage({message, 'type': 'error'});
} else {
const jid = Strophe.getBareJidFromJid(this.model.get('jid'));
api.send($pres({
......@@ -1628,10 +1634,13 @@ converse.plugins.add('converse-muc-views', {
}
case 'register': {
if (args.length > 1) {
this.showErrorMessage(__('Error: invalid number of arguments'))
this.model.createMessage({
'message': __('Error: invalid number of arguments'),
'type': 'error'
});
} else {
this.model.registerNickname().then(err_msg => {
if (err_msg) this.showErrorMessage(err_msg)
err_msg && this.model.createMessage({'message': err_msg, 'type': 'error'});
});
}
break;
......@@ -2018,10 +2027,10 @@ converse.plugins.add('converse-muc-views', {
await this.model.sendConfiguration(configArray);
} catch (e) {
log.error(e);
this.showErrorMessage(
const message =
__("Sorry, an error occurred while trying to submit the config form.") + " " +
__("Check your browser's developer console for details.")
);
__("Check your browser's developer console for details.");
this.model.createMessage({message, 'type': 'error'});
}
await this.model.refreshDiscoInfo();
this.chatroomview.closeForm();
......
<div class="message chat-info chat-error" data-isodate="{{{o.isodate}}}">{{{o.message}}}</div>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment