Add tests for contact removal via modal

This commit is contained in:
JC Brand 2018-05-10 22:14:37 +02:00
parent b1a54e626d
commit 09db3eddb0
10 changed files with 102 additions and 58 deletions

View File

@ -7055,9 +7055,6 @@ body.reset {
opacity: 0; } opacity: 0; }
100% { 100% {
opacity: 1; } } opacity: 1; } }
#conversejs .fade {
opacity: 0;
transition: opacity 0.20s linear; }
#conversejs .fade-in { #conversejs .fade-in {
opacity: 0; opacity: 0;
/* make things invisible upon start */ /* make things invisible upon start */
@ -7273,7 +7270,7 @@ body.reset {
border-radius: 25%; border-radius: 25%;
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 14px;
margin: 0 0.2em; margin: 0 0.2em;
padding: 0 0 0 0.5em; padding: 0 0 0 0.5em;
text-decoration: none; } text-decoration: none; }
@ -7299,8 +7296,8 @@ body.reset {
min-height: 1px; min-height: 1px;
padding-right: 15px; padding-right: 15px;
padding-left: 15px; padding-left: 15px;
flex: 0 0 25%; flex: 0 0 33.3333333333%;
max-width: 25%; max-width: 33.3333333333%;
padding: 0; } padding: 0; }
#conversejs .chat-head .user-custom-message { #conversejs .chat-head .user-custom-message {
color: white; color: white;
@ -7585,6 +7582,9 @@ body.reset {
top: 0; top: 0;
left: 0; } left: 0; }
#conversejs.converse-fullscreen .chatbox-btn {
font-size: 16px; }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
#conversejs:not(.converse-embedded) > .row { #conversejs:not(.converse-embedded) > .row {
flex-direction: row-reverse; } flex-direction: row-reverse; }

View File

@ -7055,9 +7055,6 @@ body.reset {
opacity: 0; } opacity: 0; }
100% { 100% {
opacity: 1; } } opacity: 1; } }
#conversejs .fade {
opacity: 0;
transition: opacity 0.20s linear; }
#conversejs .fade-in { #conversejs .fade-in {
opacity: 0; opacity: 0;
/* make things invisible upon start */ /* make things invisible upon start */
@ -7324,7 +7321,7 @@ body {
border-radius: 25%; border-radius: 25%;
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 14px;
margin: 0 0.2em; margin: 0 0.2em;
padding: 0 0 0 0.5em; padding: 0 0 0 0.5em;
text-decoration: none; } text-decoration: none; }
@ -7350,8 +7347,8 @@ body {
min-height: 1px; min-height: 1px;
padding-right: 15px; padding-right: 15px;
padding-left: 15px; padding-left: 15px;
flex: 0 0 25%; flex: 0 0 33.3333333333%;
max-width: 25%; max-width: 33.3333333333%;
padding: 0; } padding: 0; }
#conversejs .chat-head .user-custom-message { #conversejs .chat-head .user-custom-message {
color: white; color: white;
@ -7636,6 +7633,9 @@ body {
top: 0; top: 0;
left: 0; } left: 0; }
#conversejs.converse-fullscreen .chatbox-btn {
font-size: 16px; }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
#conversejs:not(.converse-embedded) > .row { #conversejs:not(.converse-embedded) > .row {
flex-direction: row-reverse; } flex-direction: row-reverse; }

View File

@ -23,7 +23,7 @@
border-radius: 25%; border-radius: 25%;
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: $box-close-font-size; font-size: $chatbox-button-size;
margin: 0 0.2em; margin: 0 0.2em;
padding: 0 0 0 0.5em; padding: 0 0 0 0.5em;
text-decoration: none; text-decoration: none;
@ -54,7 +54,7 @@
.chatbox-buttons { .chatbox-buttons {
flex-direction: row-reverse; flex-direction: row-reverse;
@include make-col-ready(); @include make-col-ready();
@include make-col(3); @include make-col(4);
padding: 0; padding: 0;
} }
@ -431,6 +431,12 @@
} }
} }
#conversejs.converse-fullscreen {
.chatbox-btn {
font-size: $fullpage-chatbox-button-size;
}
}
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
#conversejs:not(.converse-embedded) { #conversejs:not(.converse-embedded) {
> .row { > .row {

View File

@ -287,11 +287,6 @@ body.reset {
100% { opacity: 1 } 100% { opacity: 1 }
} }
.fade {
opacity: 0;
transition: opacity 0.20s linear;
}
.fade-in { .fade-in {
@include fade-in; @include fade-in;
} }

View File

@ -116,4 +116,6 @@ $box-close-button-padding-top: 4px !default;
$box-close-button-padding-bottom: 4px !default; $box-close-button-padding-bottom: 4px !default;
$box-close-button-padding-left: 4px !default; $box-close-button-padding-left: 4px !default;
$box-close-button-padding-right: 4px !default; $box-close-button-padding-right: 4px !default;
$box-close-font-size: 16px !default;
$chatbox-button-size: 14px !default;
$fullpage-chatbox-button-size: 16px !default;

View File

@ -239,9 +239,8 @@
this.vcard = _converse.vcards.create({'jid': this.get('jid')}); this.vcard = _converse.vcards.create({'jid': this.get('jid')});
} }
_converse.api.waitUntil('rosterContactsFetched').then(() => { _converse.api.waitUntil('rosterContactsFetched').then(() => {
this.contact = _converse.roster.findWhere({'jid': this.get('jid')}); this.addRelatedContact(_converse.roster.findWhere({'jid': this.get('jid')}));
}); });
this.messages = new _converse.Messages(); this.messages = new _converse.Messages();
this.messages.browserStorage = new Backbone.BrowserStorage[_converse.message_storage]( this.messages.browserStorage = new Backbone.BrowserStorage[_converse.message_storage](
b64_sha1(`converse.messages${this.get('jid')}${_converse.bare_jid}`)); b64_sha1(`converse.messages${this.get('jid')}${_converse.bare_jid}`));
@ -264,6 +263,13 @@
}); });
}, },
addRelatedContact (contact) {
if (!_.isUndefined(contact)) {
this.contact = contact;
this.trigger('contactAdded', contact);
}
},
getDisplayName () { getDisplayName () {
return this.vcard.get('fullname') || this.get('jid'); return this.vcard.get('fullname') || this.get('jid');
}, },
@ -774,9 +780,22 @@
_converse.emit('privateChatsAutoJoined'); _converse.emit('privateChatsAutoJoined');
} }
/************************ BEGIN Event Handlers ************************/ /************************ BEGIN Event Handlers ************************/
_converse.on('chatBoxesFetched', autoJoinChats); _converse.on('chatBoxesFetched', autoJoinChats);
_converse.api.waitUntil('rosterContactsFetched').then(() => {
_converse.roster.on('add', (contact) => {
/* When a new contact is added, check if we already have a
* chatbox open for it, and if so attach it to the chatbox.
*/
const chatbox = _converse.chatboxes.findWhere({'jid': contact.get('jid')});
chatbox.addRelatedContact(contact);
});
});
_converse.on('addClientFeatures', () => { _converse.on('addClientFeatures', () => {
_converse.api.disco.addFeature(Strophe.NS.HTTPUPLOAD); _converse.api.disco.addFeature(Strophe.NS.HTTPUPLOAD);
_converse.api.disco.addFeature(Strophe.NS.OUTOFBAND); _converse.api.disco.addFeature(Strophe.NS.OUTOFBAND);

View File

@ -240,40 +240,60 @@
'click button.remove-contact': 'removeContact' 'click button.remove-contact': 'removeContact'
}, },
initialize () {
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
this.model.on('contactAdded', this.registerContactEventHandlers, this);
this.registerContactEventHandlers();
},
toHTML () { toHTML () {
return tpl_user_details_modal(_.extend( return tpl_user_details_modal(_.extend(
this.model.toJSON(), this.model.toJSON(),
this.model.vcard.toJSON(), { this.model.vcard.toJSON(), {
'allow_contact_removal': _converse.allow_contact_removal,
'alt_profile_image': __("The User's Profile Image"), 'alt_profile_image': __("The User's Profile Image"),
'display_name': this.model.getDisplayName(), 'display_name': this.model.getDisplayName(),
'is_roster_contact': !_.isUndefined(this.model.contact),
'label_close': __('Close'), 'label_close': __('Close'),
'label_email': __('Email'), 'label_email': __('Email'),
'label_fullname': __('Full Name'), 'label_fullname': __('Full Name'),
'label_jid': __('Jabber ID'),
'label_nickname': __('Nickname'), 'label_nickname': __('Nickname'),
'label_role': __('Role'),
'label_remove': __('Remove as contact'), 'label_remove': __('Remove as contact'),
'label_url': __('URL'), 'label_role': __('Role'),
'allow_contact_removal': _converse.allow_contact_removal, 'label_url': __('URL')
'is_roster_contact': !_.isUndefined(this.model.contact)
})); }));
}, },
registerContactEventHandlers () {
if (!_.isUndefined(this.model.contact)) {
this.model.contact.on('change', this.render, this);
this.model.contact.vcard.on('change', this.render, this);
this.model.contact.on('destroy', () => {
delete this.model.contact;
this.render();
});
}
},
removeContact (ev) { removeContact (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
if (!_converse.allow_contact_removal) { return; } if (!_converse.allow_contact_removal) { return; }
const result = confirm(__("Are you sure you want to remove this contact?")); const result = confirm(__("Are you sure you want to remove this contact?"));
if (result === true) { if (result === true) {
this.modal.hide();
this.model.contact.removeFromRoster( this.model.contact.removeFromRoster(
(iq) => { (iq) => {
this.model.contact.destroy(); this.model.contact.destroy();
delete this.model.contact;
}, },
(err) => { (err) => {
_converse.log(err, Strophe.LogLevel.ERROR); _converse.log(err, Strophe.LogLevel.ERROR);
_converse.api.alert.show( _converse.api.alert.show(
Strophe.LogLevel.ERROR, Strophe.LogLevel.ERROR,
__('Error'), __('Error'),
[__('Sorry, there was an error while trying to remove %1$s as a contact.', name)] [__('Sorry, there was an error while trying to remove %1$s as a contact.',
this.model.contact.getDisplayName())
]
) )
} }
); );

View File

@ -401,15 +401,15 @@
addAndSubscribe (jid, name, groups, message, attributes) { addAndSubscribe (jid, name, groups, message, attributes) {
/* Add a roster contact and then once we have confirmation from /* Add a roster contact and then once we have confirmation from
* the XMPP server we subscribe to that contact's presence updates. * the XMPP server we subscribe to that contact's presence updates.
* Parameters: * Parameters:
* (String) jid - The Jabber ID of the user being added and subscribed to. * (String) jid - The Jabber ID of the user being added and subscribed to.
* (String) name - The name of that user * (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to * (Array of Strings) groups - Any roster groups the user might belong to
* (String) message - An optional message to explain the * (String) message - An optional message to explain the
* reason for the subscription request. * reason for the subscription request.
* (Object) attributes - Any additional attributes to be stored on the user's model. * (Object) attributes - Any additional attributes to be stored on the user's model.
*/ */
const handler = (contact) => { const handler = (contact) => {
if (contact instanceof _converse.RosterContact) { if (contact instanceof _converse.RosterContact) {
contact.subscribe(message); contact.subscribe(message);
@ -420,14 +420,14 @@
sendContactAddIQ (jid, name, groups, callback, errback) { sendContactAddIQ (jid, name, groups, callback, errback) {
/* Send an IQ stanza to the XMPP server to add a new roster contact. /* Send an IQ stanza to the XMPP server to add a new roster contact.
* *
* Parameters: * Parameters:
* (String) jid - The Jabber ID of the user being added * (String) jid - The Jabber ID of the user being added
* (String) name - The name of that user * (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to * (Array of Strings) groups - Any roster groups the user might belong to
* (Function) callback - A function to call once the IQ is returned * (Function) callback - A function to call once the IQ is returned
* (Function) errback - A function to call if an error occured * (Function) errback - A function to call if an error occured
*/ */
name = _.isEmpty(name)? jid: name; name = _.isEmpty(name)? jid: name;
const iq = $iq({type: 'set'}) const iq = $iq({type: 'set'})
.c('query', {xmlns: Strophe.NS.ROSTER}) .c('query', {xmlns: Strophe.NS.ROSTER})
@ -438,16 +438,16 @@
addContactToRoster (jid, name, groups, attributes) { addContactToRoster (jid, name, groups, attributes) {
/* Adds a RosterContact instance to _converse.roster and /* Adds a RosterContact instance to _converse.roster and
* registers the contact on the XMPP server. * registers the contact on the XMPP server.
* Returns a promise which is resolved once the XMPP server has * Returns a promise which is resolved once the XMPP server has
* responded. * responded.
* *
* Parameters: * Parameters:
* (String) jid - The Jabber ID of the user being added and subscribed to. * (String) jid - The Jabber ID of the user being added and subscribed to.
* (String) name - The name of that user * (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to * (Array of Strings) groups - Any roster groups the user might belong to
* (Object) attributes - Any additional attributes to be stored on the user's model. * (Object) attributes - Any additional attributes to be stored on the user's model.
*/ */
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
groups = groups || []; groups = groups || [];
this.sendContactAddIQ(jid, name, groups, this.sendContactAddIQ(jid, name, groups,

View File

@ -7,13 +7,14 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
{[ if (o.image) { ]} {[ if (o.image) { ]}
<img alt="{{{o.alt_profile_image}}}" <img alt="{{{o.alt_profile_image}}}"
class="img-thumbnail avatar align-self-center" class="img-thumbnail avatar align-self-center mb-3"
height="100" width="100" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/> height="100" width="100" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
{[ } ]} {[ } ]}
{[ if (o.fullname) { ]} {[ if (o.fullname) { ]}
<p><label>{{{o.label_fullname}}}:</label>&nbsp;{{{o.fullname}}}</p> <p><label>{{{o.label_fullname}}}:</label>&nbsp;{{{o.fullname}}}</p>
{[ } ]} {[ } ]}
<p><label>{{{o.label_jid}}}:</label>&nbsp;{{{o.jid}}}</p>
{[ if (o.nickname) { ]} {[ if (o.nickname) { ]}
<p><label>{{{o.label_nickname}}}:</label>&nbsp;{{{o.nickname}}}</p> <p><label>{{{o.label_nickname}}}:</label>&nbsp;{{{o.nickname}}}</p>
{[ } ]} {[ } ]}

View File

@ -51,6 +51,7 @@ var specs = [
"spec/controlbox", "spec/controlbox",
"spec/roster", "spec/roster",
"spec/chatbox", "spec/chatbox",
"spec/user-details-modal",
"spec/messages", "spec/messages",
"spec/chatroom", "spec/chatroom",
"spec/minchats", "spec/minchats",