Refactoring of the roster view.

* Removed the dependency on jQuery
* Contacts are now shown inside a group element, simplifying the code
This commit is contained in:
JC Brand 2017-12-17 15:03:28 +00:00
parent a0ba972468
commit 6c6ef1f1f9
13 changed files with 638 additions and 627 deletions

View File

@ -18,7 +18,7 @@
"lodash/prefer-lodash-method": [2, {
"ignoreMethods": [
"find", "endsWith", "startsWith", "filter", "reduce",
"map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase"
"map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase", "includes"
]
}],
"lodash/prefer-startswith": "off",

View File

@ -1203,8 +1203,9 @@
display: none; }
#converse-embedded-chat .collapsed,
#conversejs .collapsed {
height: 0;
overflow: hidden; }
height: 0 !important;
overflow: hidden !important;
padding: 0 !important; }
#converse-embedded-chat .locked,
#conversejs .locked {
padding-right: 22px; }
@ -2374,103 +2375,101 @@
margin: 0;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
display: none; }
#conversejs #converse-roster .roster-contacts dt.roster-group {
overflow-y: auto; }
#conversejs #converse-roster .roster-contacts .roster-group {
border: none;
color: #777;
display: none;
font-weight: normal;
margin: 1em 0 0.5em 0;
text-shadow: 0 1px 0 #FAFAFA; }
#conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle {
#conversejs #converse-roster .roster-contacts .roster-group .group-toggle {
color: #777;
display: block;
width: 100%; }
#conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle:hover {
width: 100%;
margin: 0.5em 0; }
#conversejs #converse-roster .roster-contacts .roster-group .group-toggle:hover {
color: #585B51; }
#conversejs #converse-roster .roster-contacts dd {
border: none;
clear: both;
color: #777;
display: block;
height: 24px;
overflow-y: hidden;
text-shadow: 0 1px 0 #FAFAFA;
line-height: 14px;
width: 100%;
height: 30px;
margin: 0;
padding: 0.5em 0 0 0; }
#conversejs #converse-roster .roster-contacts dd a:hover {
color: #206485; }
#conversejs #converse-roster .roster-contacts dd .open-chat {
margin: auto;
padding: 0;
width: 85%; }
#conversejs #converse-roster .roster-contacts dd .open-chat.unread-msgs {
font-weight: bold; }
#conversejs #converse-roster .roster-contacts dd .open-chat.unread-msgs .contact-name {
width: 70%; }
#conversejs #converse-roster .roster-contacts dd .open-chat .msgs-indicator {
background-color: #3AA569;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: 12px; }
#conversejs #converse-roster .roster-contacts dd .open-chat .contact-name {
#conversejs #converse-roster .roster-contacts .roster-group li {
border: none;
clear: both;
color: #777;
display: block;
height: 24px;
overflow-y: hidden;
text-shadow: 0 1px 0 #FAFAFA;
line-height: 14px;
width: 100%;
height: 30px;
margin: 0;
padding: 0.5em 0 0 0; }
#conversejs #converse-roster .roster-contacts .roster-group li a:hover {
color: #206485; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat {
margin: auto;
padding: 0;
width: 85%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs {
font-weight: bold; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs .contact-name {
width: 70%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .msgs-indicator {
background-color: #3AA569;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: 12px; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
float: none;
height: 60px; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name.unread-msgs {
max-width: 60%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .avatar {
float: left;
display: inline-block;
height: 60px; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .request-actions {
padding: 0 0 0 0.3em;
float: right; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .open-chat {
max-width: 70%; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .open-chat .req-contact-name {
width: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .req-contact-name {
line-height: 16px;
width: 69%;
padding: 0; }
#conversejs #converse-roster .roster-contacts .roster-group li.current-xmpp-contact span {
font-size: 14px;
float: left;
margin-right: 0.5em; }
#conversejs #converse-roster .roster-contacts .roster-group li.odd {
background-color: #DCEAC5;
/* Make this difference */ }
#conversejs #converse-roster .roster-contacts .roster-group li a, #conversejs #converse-roster .roster-contacts .roster-group li span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-overflow: ellipsis; }
#conversejs #converse-roster .roster-contacts .roster-group li span {
padding: 0;
height: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li a.decline-xmpp-request {
margin-left: 5px; }
#conversejs #converse-roster .roster-contacts .roster-group li a.remove-xmpp-contact {
font-size: 10px;
float: right;
margin: 0;
max-width: 80%;
float: none;
height: 60px; }
#conversejs #converse-roster .roster-contacts dd .open-chat .contact-name.unread-msgs {
max-width: 60%; }
#conversejs #converse-roster .roster-contacts dd .open-chat .avatar {
float: left;
display: inline-block;
height: 60px; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .request-actions {
padding: 0 0 0 0.3em;
float: right; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .open-chat {
max-width: 70%; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .open-chat .req-contact-name {
width: 100%; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .req-contact-name {
line-height: 16px;
width: 69%;
padding: 0; }
#conversejs #converse-roster .roster-contacts dd.current-xmpp-contact span {
font-size: 14px;
float: left;
margin-right: 0.5em; }
#conversejs #converse-roster .roster-contacts dd.odd {
background-color: #DCEAC5;
/* Make this difference */ }
#conversejs #converse-roster .roster-contacts dd a, #conversejs #converse-roster .roster-contacts dd span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }
#conversejs #converse-roster .roster-contacts dd span {
padding: 0;
height: 100%; }
#conversejs #converse-roster .roster-contacts dd a.decline-xmpp-request {
margin-left: 5px; }
#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact {
font-size: 10px;
float: right;
margin: 0;
padding: 0;
color: #A8ABA1; }
#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact:before {
font-size: 14px; }
#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact:hover {
color: #818479; }
padding: 0;
color: #A8ABA1; }
#conversejs #converse-roster .roster-contacts .roster-group li a.remove-xmpp-contact:before {
font-size: 14px; }
#conversejs #converse-roster .roster-contacts .roster-group li a.remove-xmpp-contact:hover {
color: #818479; }
#conversejs #converse-roster span.pending-contact-name {
line-height: 16px;
width: 100%; }

View File

@ -1203,8 +1203,9 @@
display: none; }
#converse-embedded-chat .collapsed,
#conversejs .collapsed {
height: 0;
overflow: hidden; }
height: 0 !important;
overflow: hidden !important;
padding: 0 !important; }
#converse-embedded-chat .locked,
#conversejs .locked {
padding-right: 22px; }
@ -2533,103 +2534,101 @@ body {
margin: 0;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
display: none; }
#conversejs #converse-roster .roster-contacts dt.roster-group {
overflow-y: auto; }
#conversejs #converse-roster .roster-contacts .roster-group {
border: none;
color: #777;
display: none;
font-weight: normal;
margin: 1em 0 0.5em 0;
text-shadow: 0 1px 0 #FAFAFA; }
#conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle {
#conversejs #converse-roster .roster-contacts .roster-group .group-toggle {
color: #777;
display: block;
width: 100%; }
#conversejs #converse-roster .roster-contacts dt.roster-group .group-toggle:hover {
width: 100%;
margin: 0.5em 0; }
#conversejs #converse-roster .roster-contacts .roster-group .group-toggle:hover {
color: #585B51; }
#conversejs #converse-roster .roster-contacts dd {
border: none;
clear: both;
color: #777;
display: block;
height: 24px;
overflow-y: hidden;
text-shadow: 0 1px 0 #FAFAFA;
line-height: 16px;
width: 100%;
height: 30px;
margin: 0;
padding: 0.5em 0 0 0; }
#conversejs #converse-roster .roster-contacts dd a:hover {
color: #206485; }
#conversejs #converse-roster .roster-contacts dd .open-chat {
margin: auto;
padding: 0;
width: 85%; }
#conversejs #converse-roster .roster-contacts dd .open-chat.unread-msgs {
font-weight: bold; }
#conversejs #converse-roster .roster-contacts dd .open-chat.unread-msgs .contact-name {
width: 70%; }
#conversejs #converse-roster .roster-contacts dd .open-chat .msgs-indicator {
background-color: #3AA569;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: 14px; }
#conversejs #converse-roster .roster-contacts dd .open-chat .contact-name {
#conversejs #converse-roster .roster-contacts .roster-group li {
border: none;
clear: both;
color: #777;
display: block;
height: 24px;
overflow-y: hidden;
text-shadow: 0 1px 0 #FAFAFA;
line-height: 16px;
width: 100%;
height: 30px;
margin: 0;
padding: 0.5em 0 0 0; }
#conversejs #converse-roster .roster-contacts .roster-group li a:hover {
color: #206485; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat {
margin: auto;
padding: 0;
width: 85%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs {
font-weight: bold; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs .contact-name {
width: 70%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .msgs-indicator {
background-color: #3AA569;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: 14px; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
float: none;
height: 30px; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name.unread-msgs {
max-width: 60%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .avatar {
float: left;
display: inline-block;
height: 30px; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .request-actions {
padding: 0 0 0 0.3em;
float: right; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .open-chat {
max-width: 70%; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .open-chat .req-contact-name {
width: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .req-contact-name {
line-height: 22px;
width: 69%;
padding: 0; }
#conversejs #converse-roster .roster-contacts .roster-group li.current-xmpp-contact span {
font-size: 16px;
float: left;
margin-right: 0.5em; }
#conversejs #converse-roster .roster-contacts .roster-group li.odd {
background-color: #DCEAC5;
/* Make this difference */ }
#conversejs #converse-roster .roster-contacts .roster-group li a, #conversejs #converse-roster .roster-contacts .roster-group li span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-overflow: ellipsis; }
#conversejs #converse-roster .roster-contacts .roster-group li span {
padding: 0;
height: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li a.decline-xmpp-request {
margin-left: 5px; }
#conversejs #converse-roster .roster-contacts .roster-group li a.remove-xmpp-contact {
font-size: 10px;
float: right;
margin: 0;
max-width: 80%;
float: none;
height: 30px; }
#conversejs #converse-roster .roster-contacts dd .open-chat .contact-name.unread-msgs {
max-width: 60%; }
#conversejs #converse-roster .roster-contacts dd .open-chat .avatar {
float: left;
display: inline-block;
height: 30px; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .request-actions {
padding: 0 0 0 0.3em;
float: right; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .open-chat {
max-width: 70%; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .open-chat .req-contact-name {
width: 100%; }
#conversejs #converse-roster .roster-contacts dd.requesting-xmpp-contact .req-contact-name {
line-height: 22px;
width: 69%;
padding: 0; }
#conversejs #converse-roster .roster-contacts dd.current-xmpp-contact span {
font-size: 16px;
float: left;
margin-right: 0.5em; }
#conversejs #converse-roster .roster-contacts dd.odd {
background-color: #DCEAC5;
/* Make this difference */ }
#conversejs #converse-roster .roster-contacts dd a, #conversejs #converse-roster .roster-contacts dd span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }
#conversejs #converse-roster .roster-contacts dd span {
padding: 0;
height: 100%; }
#conversejs #converse-roster .roster-contacts dd a.decline-xmpp-request {
margin-left: 5px; }
#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact {
font-size: 10px;
float: right;
margin: 0;
padding: 0;
color: #A8ABA1; }
#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact:before {
font-size: 16px; }
#conversejs #converse-roster .roster-contacts dd a.remove-xmpp-contact:hover {
color: #818479; }
padding: 0;
color: #A8ABA1; }
#conversejs #converse-roster .roster-contacts .roster-group li a.remove-xmpp-contact:before {
font-size: 16px; }
#conversejs #converse-roster .roster-contacts .roster-group li a.remove-xmpp-contact:hover {
color: #818479; }
#conversejs #converse-roster span.pending-contact-name {
line-height: 22px;
width: 100%; }

View File

@ -86,8 +86,9 @@
display: none;
}
.collapsed {
height: 0;
overflow: hidden;
height: 0 !important;
overflow: hidden !important;
padding: 0 !important;
}
.locked {

View File

@ -83,13 +83,10 @@
height: 100%;
overflow-x: hidden;
overflow-y: auto;
display: none;
dt.roster-group {
.roster-group {
border: none;
color: $text-color;
display: none;
font-weight: normal;
margin: 1em 0 0.5em 0;
text-shadow: 0 1px 0 $text-shadow-color;
.group-toggle {
&:hover {
@ -98,116 +95,117 @@
color: $text-color;
display: block;
width: 100%;
margin: 0.5em 0;;
}
}
dd {
border: none;
clear: both;
color: $text-color;
display: block;
height: 24px;
overflow-y: hidden;
text-shadow: 0 1px 0 $text-shadow-color;
line-height: $font-size;
width: 100%;
height: 30px;
margin: 0;
padding: 0.5em 0 0 0;
li {
border: none;
clear: both;
color: $text-color;
display: block;
height: 24px;
overflow-y: hidden;
text-shadow: 0 1px 0 $text-shadow-color;
line-height: $font-size;
width: 100%;
height: 30px;
margin: 0;
padding: 0.5em 0 0 0;
a:hover {
color: $dark-link-color;
}
.open-chat {
margin: auto;
padding: 0;
width: 85%;
&.unread-msgs {
font-weight: bold;
.contact-name {
width: 70%;
}
a:hover {
color: $dark-link-color;
}
.msgs-indicator {
background-color: $chat-head-color;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: $font-size-small;
}
.open-chat {
margin: auto;
padding: 0;
width: 85%;
&.unread-msgs {
font-weight: bold;
.contact-name {
width: 70%;
}
}
.contact-name {
.msgs-indicator {
background-color: $chat-head-color;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: $font-size-small;
}
.contact-name {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
float: none;
height: $roster-item-height;
&.unread-msgs {
max-width: 60%;
}
}
.avatar {
float: left;
display: inline-block;
height: $roster-item-height;
}
}
&.requesting-xmpp-contact {
.request-actions {
padding: 0 0 0 0.3em;
float: right;
}
.open-chat {
max-width: 70%;
.req-contact-name {
width: 100%;
}
}
.req-contact-name {
line-height: $line-height;
width: 69%;
padding: 0;
}
}
&.current-xmpp-contact span {
font-size: $font-size;
float: left;
margin-right: 0.5em;
}
&.odd {
background-color: #DCEAC5;
/* Make this difference */
}
a, span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
span {
padding: 0;
margin: 0;
max-width: 80%;
float: none;
height: $roster-item-height;
&.unread-msgs {
max-width: 60%;
height: 100%;
}
a {
&.decline-xmpp-request {
margin-left: 5px;
}
}
.avatar {
float: left;
display: inline-block;
height: $roster-item-height;
}
}
&.requesting-xmpp-contact {
.request-actions {
padding: 0 0 0 0.3em;
float: right;
}
.open-chat {
max-width: 70%;
.req-contact-name {
width: 100%;
}
}
.req-contact-name {
line-height: $line-height;
width: 69%;
padding: 0;
}
}
&.current-xmpp-contact span {
font-size: $font-size;
float: left;
margin-right: 0.5em;
}
&.odd {
background-color: #DCEAC5;
/* Make this difference */
}
a, span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
span {
padding: 0;
height: 100%;
}
a {
&.decline-xmpp-request {
margin-left: 5px;
}
&.remove-xmpp-contact {
font-size: $font-size-tiny;
float: right;
margin: 0;
padding: 0;
color: $subdued-color;
&:before {
font-size: $font-size;
}
&:hover {
color: $gray-color;
&.remove-xmpp-contact {
font-size: $font-size-tiny;
float: right;
margin: 0;
padding: 0;
color: $subdued-color;
&:before {
font-size: $font-size;
}
&:hover {
color: $gray-color;
}
}
}
}

View File

@ -110,7 +110,7 @@
_converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
// Test that they can be maximized again
@ -243,7 +243,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
var chatbox = test_utils.openChatBoxes(_converse, 1)[0],
@ -281,7 +281,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
var chatbox = test_utils.openChatBoxes(_converse, 1)[0],
@ -328,7 +328,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
spyOn(_converse, 'emit');
@ -443,7 +443,7 @@
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
// TODO: More tests can be added here...
@ -546,7 +546,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
spyOn(_converse, 'emit');
@ -885,7 +885,7 @@
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
// Send a message from a different resource
@ -1130,7 +1130,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
var contact_name = mock.cur_names[0];
@ -1191,7 +1191,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
spyOn(_converse, 'emit');
@ -1503,7 +1503,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300).then(function () {
spyOn(_converse.connection, 'send');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -1531,7 +1531,7 @@
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500).then(function () {
test_utils.openChatBoxFor(_converse, contact_jid);
var view = _converse.chatboxviews.get(contact_jid);
@ -1570,7 +1570,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -1694,7 +1694,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
_converse.TIMEOUTS.PAUSED = 200; // Make the timeout shorter so that we can test
@ -1757,7 +1757,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300)
.then(function () {
// TODO: only show paused state if the previous state was composing
@ -1842,7 +1842,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500).then(function () {
// Make the timeouts shorter so that we can test
_converse.TIMEOUTS.PAUSED = 200;
@ -1932,7 +1932,7 @@
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 300).then(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
@ -2327,7 +2327,7 @@
test_utils.createContacts(_converse, 'current');
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -2360,7 +2360,7 @@
test_utils.createContacts(_converse, 'current');
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -2394,7 +2394,7 @@
test_utils.createContacts(_converse, 'current');
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -2429,7 +2429,7 @@
test_utils.createContacts(_converse, 'current');
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -2462,7 +2462,7 @@
test_utils.createContacts(_converse, 'current');
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';

View File

@ -72,7 +72,7 @@
test_utils.createContacts(_converse, 'current');
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group .group-toggle').length;
}, 300)
.then(function () {
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
@ -129,9 +129,10 @@
return deferred.promise();
});
test_utils.openControlBox();
test_utils.createContacts(_converse, 'current');
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group .group-toggle').length;
}, 300).then(function () {
var jid = 'lounge@localhost';
var room = _converse.api.rooms.open(jid);
@ -256,7 +257,7 @@
' </query>'+
' </iq>')[0]));
test_utils.waitUntil(function () {
return test_utils.waitUntil(function () {
return sent_IQ.toLocaleString() !==
"<iq to='room@conference.example.org' type='get' xmlns='jabber:client' id='"+IQ_id+
"'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>";

View File

@ -5,21 +5,29 @@
var $pres = converse.env.$pres;
var $msg = converse.env.$msg;
var $iq = converse.env.$iq;
var u = converse.env.utils;
var checkHeaderToggling = function ($header) {
var $toggle = $header.find('a.group-toggle');
expect($header.css('display')).toEqual('block');
expect($header.nextUntil('dt', 'dd').length === $header.nextUntil('dt', 'dd:visible').length).toBeTruthy();
var checkHeaderToggling = function ($group) {
var $toggle = $group.find('a.group-toggle');
expect(u.isVisible($group[0])).toBeTruthy();
expect($group.find('ul.collapsed').length).toBe(0);
expect($toggle.hasClass('icon-closed')).toBeFalsy();
expect($toggle.hasClass('icon-opened')).toBeTruthy();
$toggle.click();
expect($toggle.hasClass('icon-closed')).toBeTruthy();
expect($toggle.hasClass('icon-opened')).toBeFalsy();
expect($header.nextUntil('dt', 'dd').length === $header.nextUntil('dt', 'dd:hidden').length).toBeTruthy();
$toggle.click();
expect($toggle.hasClass('icon-closed')).toBeFalsy();
expect($toggle.hasClass('icon-opened')).toBeTruthy();
expect($header.nextUntil('dt', 'dd').length === $header.nextUntil('dt', 'dd:visible').length).toBeTruthy();
return test_utils.waitUntil(function () {
return $group.find('ul.collapsed').length === 1;
}, 500).then(function () {
expect($toggle.hasClass('icon-closed')).toBeTruthy();
expect($toggle.hasClass('icon-opened')).toBeFalsy();
$toggle.click();
return test_utils.waitUntil(function () {
return $group.find('li').length === $group.find('li:visible').length
}, 500);
}).then(function () {
expect($toggle.hasClass('icon-closed')).toBeFalsy();
expect($toggle.hasClass('icon-opened')).toBeTruthy();
});
};
describe("The Control Box", function () {
@ -149,7 +157,7 @@
};
return test_utils.waitUntil(function () {
if (_converse.rosterview.$roster.hasScrollBar()) {
if ($(_converse.rosterview.roster_el).hasScrollBar()) {
return $filter.is(':visible');
} else {
return !$filter.is(':visible');
@ -165,54 +173,64 @@
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
var $filter;
var $roster;
_converse.roster_groups = true;
test_utils.openControlBox();
test_utils.createGroupedContacts(_converse);
$filter = _converse.rosterview.$('.roster-filter');
$roster = _converse.rosterview.$roster;
var $filter = _converse.rosterview.$('.roster-filter');
var $roster = $(_converse.rosterview.roster_el);
_converse.rosterview.filter_view.delegateEvents();
var promise = test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 15;
}, 500)
.then(function (contacts) {
expect($roster.find('dt:visible').length).toBe(5);
return $roster.find('li:visible').length === 15;
}, 500).then(function (contacts) {
expect($roster.find('ul.roster-group-contacts:visible').length).toBe(5);
$filter.val("candice");
$filter.trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 1;
return $roster.find('li:visible').length === 1;
}, 500);
}).then(function (contacts) {
expect($roster.find('dd:visible').eq(0).text().trim()).toBe('Candice van der Knijff');
expect($roster.find('dt:visible').length).toBe(1);
expect(_.trim($roster.find('dt:visible').eq(0).text())).toBe('colleagues');
// Only one roster contact is now visible
expect($roster.find('li:visible').length).toBe(1);
expect($roster.find('li:visible').eq(0).text().trim()).toBe('Candice van der Knijff');
// Only one foster group is still visible
expect($roster.find('.roster-group:visible').length).toBe(1);
expect(_.trim($roster.find('.roster-group:visible a.group-toggle').eq(0).text())).toBe('colleagues');
$filter = _converse.rosterview.$('.roster-filter');
$filter.val("an");
$filter.trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 5;
return $roster.find('li:visible').length === 5;
}, 500)
}).then(function (contacts) {
expect($roster.find('dt:visible').length).toBe(4);
// Five roster contact is now visible
expect($roster.find('li:visible').length).toBe(5);
// Four groups are still visible
var $groups = $roster.find('.roster-group:visible a.group-toggle');
expect($groups.length).toBe(4);
expect(_.trim($groups.eq(0).text())).toBe('colleagues');
expect(_.trim($groups.eq(1).text())).toBe('Family');
expect(_.trim($groups.eq(2).text())).toBe('friends & acquaintences');
expect(_.trim($groups.eq(3).text())).toBe('ænemies');
$filter = _converse.rosterview.$('.roster-filter');
$filter.val("xxx");
$filter.trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 0;
return $roster.find('li:visible').length === 0;
}, 500)
}).then(function () {
expect($roster.find('dt:visible').length).toBe(0);
expect($roster.find('ul.roster-group-contacts:visible a.group-toggle').length).toBe(0);
$filter = _converse.rosterview.$('.roster-filter');
$filter.val(""); // Check that contacts are shown again, when the filter string is cleared.
$filter.trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 15;
return $roster.find('li:visible').length === 15;
}, 500)
}).then(function () {
expect($roster.find('dt:visible').length).toBe(5);
expect($roster.find('ul.roster-group-contacts:visible').length).toBe(5);
_converse.roster_groups = false;
done();
});
@ -224,46 +242,49 @@
function (done, _converse) {
var $filter;
var $roster;
var $type;
_converse.roster_groups = true;
test_utils.openControlBox();
test_utils.createGroupedContacts(_converse);
_converse.rosterview.filter_view.delegateEvents();
$filter = _converse.rosterview.$('.roster-filter');
$roster = _converse.rosterview.$roster;
var $roster = $(_converse.rosterview.roster_el);
$type = _converse.rosterview.$('.filter-type');
$type.val('groups');
var promise = test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 15;
}, 500);
promise.then(function () {
expect($roster.find('dt:visible').length).toBe(5);
test_utils.waitUntil(function () {
return $roster.find('li:visible').length === 15;
}, 500).then(function () {
expect($roster.find('div.roster-group:visible a.group-toggle').length).toBe(5);
$filter.val("colleagues");
$filter.trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dt:visible').length === 1;
return $roster.find('div.roster-group:not(.collapsed) a.group-toggle').length === 1;
}, 500);
}).then(function () {
expect(_.trim($roster.find('dt:visible').eq(0).text())).toBe('colleagues');
expect($roster.find('dd:visible').length).toBe(3);
expect(_.trim($roster.find('div.roster-group:not(.collapsed) a').eq(0).text())).toBe('colleagues');
expect($roster.find('div.roster-group:not(.collapsed) li:visible').length).toBe(3);
// Check that all contacts under the group are shown
expect($roster.find('dt:visible').nextUntil('dt', 'dd:hidden').length).toBe(0);
expect($roster.find('div.roster-group:not(.collapsed) li:hidden').length).toBe(0);
$filter = _converse.rosterview.$('.roster-filter');
$filter.val("xxx").trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 0;
return $roster.find('div.roster-group.collapsed a.group-toggle').length === 5;
}, 700);
}).then(function () {
expect($roster.find('dt:visible').length).toBe(0);
expect($roster.find('div.roster-group:not(.collapsed) a').length).toBe(0);
$filter = _converse.rosterview.$('.roster-filter');
$filter.val(""); // Check that groups are shown again, when the filter string is cleared.
$filter.trigger('keydown');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 15;
return $roster.find('div.roster-group.collapsed a.group-toggle').length === 0;
}, 500);
}).then(function () {
expect($roster.find('dt:visible').length).toBe(5);
expect($roster.find('div.roster-group:not(collapsed)').length).toBe(5);
expect($roster.find('div.roster-group:not(collapsed) li').length).toBe(15);
done();
});
}));
@ -302,7 +323,6 @@
function (done, _converse) {
var $filter;
var $roster;
_converse.roster_groups = true;
test_utils.createGroupedContacts(_converse);
var jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
@ -313,21 +333,20 @@
var $type = _converse.rosterview.$('.filter-type');
$type.val('state').trigger('change');
$filter = _converse.rosterview.$('.state-type');
$roster = _converse.rosterview.$roster;
var $roster = $(_converse.rosterview.roster_el);
test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 15;
}, 500)
.then(function () {
expect($roster.find('dt:visible').length).toBe(5);
return $roster.find('li:visible').length === 15;
}, 500).then(function () {
expect($roster.find('ul.roster-group-contacts:visible').length).toBe(5);
$filter.val("online");
$filter.trigger('change');
return test_utils.waitUntil(function () {
return $roster.find('dd:visible').length === 1;
return $roster.find('li:visible').length === 1;
}, 500)
}).then(function () {
expect($roster.find('dd:visible').eq(0).text().trim()).toBe('Rinse Sommer');
expect($roster.find('dt:visible').length).toBe(1);
expect($roster.find('li:visible').eq(0).text().trim()).toBe('Rinse Sommer');
expect($roster.find('ul.roster-group-contacts:visible').length).toBe(1);
var $type = _converse.rosterview.$('.filter-type');
$type.val('contacts').trigger('change');
done();
@ -346,16 +365,19 @@
spyOn(_converse, 'emit');
spyOn(_converse.rosterview, 'update').and.callThrough();
_converse.rosterview.render();
test_utils.openControlBox();
test_utils.createContacts(_converse, 'pending');
test_utils.createContacts(_converse, 'requesting');
test_utils.createGroupedContacts(_converse);
// Check that the groups appear alphabetically and that
// requesting and pending contacts are last.
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
}, 500)
.then(function () {
var group_titles = $.map(_converse.rosterview.$el.find('dt'), function (o) { return $(o).text().trim(); });
return _converse.rosterview.$el.find('.roster-group:visible a.group-toggle').length;
}, 500).then(function () {
var group_titles = $.map(
_converse.rosterview.$el.find('.roster-group:visible a.group-toggle'),
function (o) { return $(o).text().trim(); }
);
expect(group_titles).toEqual([
"Contact requests",
"colleagues",
@ -367,7 +389,7 @@
]);
// Check that usernames appear alphabetically per group
_.each(_.keys(mock.groups), function (name) {
var $contacts = _converse.rosterview.$('dt.roster-group[data-group="'+name+'"]').nextUntil('dt', 'dd');
var $contacts = _converse.rosterview.$('.roster-group[data-group="'+name+'"] ul');
var names = $.map($contacts, function (o) { return $(o).text().trim(); });
expect(names).toEqual(_.clone(names).sort());
});
@ -384,6 +406,7 @@
var groups = ['colleagues', 'friends'];
spyOn(_converse, 'emit');
spyOn(_converse.rosterview, 'update').and.callThrough();
test_utils.openControlBox();
_converse.rosterview.render();
for (var i=0; i<mock.cur_names.length; i++) {
_converse.roster.create({
@ -395,12 +418,12 @@
});
}
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd').length;
return _converse.rosterview.$el.find('li:visible').length;
}, 500)
.then(function () {
// Check that usernames appear alphabetically per group
_.each(groups, function (name) {
var $contacts = _converse.rosterview.$('dt.roster-group[data-group="'+name+'"]').nextUntil('dt', 'dd');
var $contacts = _converse.rosterview.$('.roster-group[data-group="'+name+'"] li');
var names = $.map($contacts, function (o) { return $(o).text().trim(); });
expect(names).toEqual(_.clone(names).sort());
expect(names.length).toEqual(mock.cur_names.length);
@ -415,6 +438,8 @@
function (done, _converse) {
_converse.roster_groups = true;
test_utils.openControlBox();
var i=0, j=0;
var groups = {
'colleagues': 3,
@ -437,10 +462,16 @@
var $toggle = view.$el.find('a.group-toggle');
expect(view.model.get('state')).toBe('opened');
$toggle.click();
expect(view.model.get('state')).toBe('closed');
$toggle.click();
expect(view.model.get('state')).toBe('opened');
done();
return test_utils.waitUntil(function () {
return view.model.get('state') === 'closed';
}, 500).then(function () {
$toggle.click();
return test_utils.waitUntil(function () {
return view.model.get('state') === 'opened';
}, 500)
}).then(function () {
done();
});
}));
});
@ -448,7 +479,9 @@
function _addContacts (_converse) {
// Must be initialized, so that render is called and documentFragment set up.
test_utils.createContacts(_converse, 'pending').openControlBox().openContactsPanel(_converse);
test_utils.createContacts(_converse, 'pending');
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
}
it("can be collapsed under their own header",
@ -458,11 +491,12 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd').length;
}, 500)
.then(function () {
checkHeaderToggling.apply(_converse, [_converse.rosterview.get('Pending contacts').$el]);
done();
return _converse.rosterview.$el.find('li').length;
}, 500).then(function () {
checkHeaderToggling.apply(
_converse,
[_converse.rosterview.get('Pending contacts').$el]
).then(done);
});
}));
@ -490,16 +524,16 @@
function (done, _converse) {
_converse.show_only_online_users = true;
test_utils.openControlBox();
spyOn(_converse.rosterview, 'update').and.callThrough();
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd').length;
}, 500)
.then(function () {
return _converse.rosterview.$el.find('li').length;
}, 500).then(function () {
expect(_converse.rosterview.$el.is(':visible')).toEqual(true);
expect(_converse.rosterview.update).toHaveBeenCalled();
expect(_converse.rosterview.$el.find('dd:visible').length).toBe(3);
expect(_converse.rosterview.$el.find('dt:visible').length).toBe(1);
expect(_converse.rosterview.$el.find('li:visible').length).toBe(3);
expect(_converse.rosterview.$el.find('ul.roster-group-contacts:visible').length).toBe(1);
done();
});
}));
@ -513,13 +547,13 @@
spyOn(_converse.rosterview, 'update').and.callThrough();
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd:visible').length;
return _converse.rosterview.$el.find('li:visible').length;
}, 500)
.then(function () {
expect(_converse.rosterview.update).toHaveBeenCalled();
expect(_converse.rosterview.$el.is(':visible')).toBe(true);
expect(_converse.rosterview.$el.find('dd:visible').length).toBe(3);
expect(_converse.rosterview.$el.find('dt:visible').length).toBe(1);
expect(_converse.rosterview.$el.find('li:visible').length).toBe(3);
expect(_converse.rosterview.$el.find('ul.roster-group-contacts:visible').length).toBe(1);
done();
});
}));
@ -601,7 +635,7 @@
_converse.rosterview.$el.find(".pending-contact-name:contains('"+name+"')")
.parent().siblings('.remove-xmpp-contact').click();
}
expect(_converse.rosterview.$el.find('dt#pending-xmpp-contacts').is(':visible')).toBeFalsy();
expect(_converse.rosterview.$el.find('#pending-xmpp-contacts').is(':visible')).toBeFalsy();
done();
}));
@ -623,7 +657,7 @@
expect(_converse.rosterview.update).toHaveBeenCalled();
}
// Check that they are sorted alphabetically
t = _.reduce(_converse.rosterview.get('Pending contacts').$el.siblings('dd.pending-xmpp-contact').find('span'), function (result, value) {
t = _.reduce(_converse.rosterview.get('Pending contacts').$el.find('.pending-xmpp-contact span'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.pend_names.slice(0,i+1).sort().join(''));
@ -643,11 +677,12 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd:visible').length;
}, 500)
.then(function () {
checkHeaderToggling.apply(_converse, [_converse.rosterview.$el.find('dt.roster-group')]);
done();
return _converse.rosterview.$el.find('li:visible').length;
}, 500).then(function () {
checkHeaderToggling.apply(
_converse,
[_converse.rosterview.$el.find('.roster-group')]
).then(done);
});
}));
@ -659,10 +694,10 @@
_converse.roster_groups = false;
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd:visible').length;
return _converse.rosterview.$el.find('li:visible').length;
}, 500)
.then(function () {
_converse.rosterview.$el.find('dt.roster-group').find('a.group-toggle').click();
_converse.rosterview.$el.find('.roster-group a.group-toggle').click();
var name = "Max Mustermann";
var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
_converse.roster.create({
@ -694,10 +729,10 @@
expect(_converse.rosterview.update).toHaveBeenCalled();
}
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd').length;
return _converse.rosterview.$el.find('li').length;
}).then(function () {
// Check that they are sorted alphabetically
var t = _.reduce(_converse.rosterview.$('dt.roster-group').siblings('dd.current-xmpp-contact.offline').find('a.open-chat'), function (result, value) {
var t = _.reduce(_converse.rosterview.$('.roster-group').find('.current-xmpp-contact.offline a.open-chat'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
@ -712,9 +747,8 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dd').length;
}, 500)
.then(function () {
return _converse.rosterview.$el.find('li').length;
}, 500).then(function () {
var name = mock.cur_names[0];
var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
var contact = _converse.roster.get(jid);
@ -748,7 +782,7 @@
fullname: name
});
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
spyOn(window, 'confirm').and.returnValue(true);
@ -757,13 +791,13 @@
if (typeof callback === "function") { return callback(); }
});
expect(_converse.rosterview.$el.find('dt.roster-group').css('display')).toEqual('block');
expect(_converse.rosterview.$el.find('.roster-group').css('display')).toEqual('block');
_converse.rosterview.$el.find(".open-chat:contains('"+name+"')")
.parent().find('.remove-xmpp-contact').click();
expect(window.confirm).toHaveBeenCalled();
expect(_converse.connection.sendIQ).toHaveBeenCalled();
expect(contact.removeFromRoster).toHaveBeenCalled();
expect(_converse.rosterview.$el.find('dt.roster-group').css('display')).toEqual('none');
expect(_converse.rosterview.$el.find('.roster-group').css('display')).toEqual('none');
done();
});
}));
@ -775,7 +809,7 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var jid, t;
@ -787,7 +821,7 @@
_converse.roster.get(jid).set('chat_status', 'online');
expect(_converse.rosterview.update).toHaveBeenCalled();
// Check that they are sorted alphabetically
t = _.reduce($roster.find('dt.roster-group').siblings('dd.current-xmpp-contact.online').find('a.open-chat'), function (result, value) {
t = _.reduce($roster.find('.roster-group').find('.current-xmpp-contact.online a.open-chat'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
@ -803,7 +837,7 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var jid, t;
@ -815,9 +849,10 @@
_converse.roster.get(jid).set('chat_status', 'dnd');
expect(_converse.rosterview.update).toHaveBeenCalled();
// Check that they are sorted alphabetically
t = _.reduce($roster.find('dt.roster-group').siblings('dd.current-xmpp-contact.dnd').find('a.open-chat'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
t = _.reduce($roster.find('.roster-group .current-xmpp-contact.dnd a.open-chat'),
function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
}
done();
@ -831,7 +866,7 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var jid, t;
@ -843,9 +878,10 @@
_converse.roster.get(jid).set('chat_status', 'away');
expect(_converse.rosterview.update).toHaveBeenCalled();
// Check that they are sorted alphabetically
t = _.reduce($roster.find('dt.roster-group').siblings('dd.current-xmpp-contact.away').find('a.open-chat'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
t = _.reduce($roster.find('.roster-group .current-xmpp-contact.away a.open-chat'),
function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
}
done();
@ -859,7 +895,7 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var jid, t;
@ -871,9 +907,10 @@
_converse.roster.get(jid).set('chat_status', 'xa');
expect(_converse.rosterview.update).toHaveBeenCalled();
// Check that they are sorted alphabetically
t = _.reduce($roster.find('dt.roster-group').siblings('dd.current-xmpp-contact.xa').find('a.open-chat'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
t = _.reduce($roster.find('.roster-group .current-xmpp-contact.xa a.open-chat'),
function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
}
done();
@ -887,7 +924,7 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var jid, t;
@ -899,9 +936,10 @@
_converse.roster.get(jid).set('chat_status', 'unavailable');
expect(_converse.rosterview.update).toHaveBeenCalled();
// Check that they are sorted alphabetically
t = _.reduce($roster.find('dt.roster-group').siblings('dd.current-xmpp-contact.unavailable').find('a.open-chat'), function (result, value) {
return result + _.trim(value.textContent);
}, '');
t = _.reduce($roster.find('.roster-group .current-xmpp-contact.unavailable a.open-chat'),
function (result, value) {
return result + _.trim(value.textContent);
}, '');
expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
}
done();
@ -915,7 +953,7 @@
_addContacts(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var i, jid;
@ -940,7 +978,7 @@
_converse.roster.get(jid).set('chat_status', 'unavailable');
}
var contacts = _converse.rosterview.$el.find('dd.current-xmpp-contact');
var contacts = _converse.rosterview.$el.find('.current-xmpp-contact');
for (i=0; i<3; i++) {
expect($(contacts[i]).hasClass('online')).toBeTruthy();
expect($(contacts[i]).hasClass('both')).toBeTruthy();
@ -1029,7 +1067,7 @@
}
expect(_converse.rosterview.update).toHaveBeenCalled();
// Check that they are sorted alphabetically
children = _converse.rosterview.get('Contact requests').$el.siblings('dd.requesting-xmpp-contact').find('span');
children = _converse.rosterview.get('Contact requests').$el.find('.requesting-xmpp-contact span');
names = [];
children.each(addName);
expect(names.join('')).toEqual(mock.req_names.slice(0,mock.req_names.length+1).sort().join(''));
@ -1052,7 +1090,7 @@
fullname: name
});
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
expect(_converse.rosterview.get('Contact requests').$el.is(':visible')).toEqual(true);
@ -1072,11 +1110,12 @@
test_utils.createContacts(_converse, 'requesting').openControlBox();
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
}, 500)
.then(function () {
checkHeaderToggling.apply(_converse, [_converse.rosterview.get('Contact requests').$el]);
done();
return _converse.rosterview.$el.find('.roster-group').length;
}, 500).then(function () {
checkHeaderToggling.apply(
_converse,
[_converse.rosterview.get('Contact requests').$el]
).then(done);
});
}));
@ -1087,7 +1126,7 @@
test_utils.createContacts(_converse, 'requesting').openControlBox();
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
// TODO: Testing can be more thorough here, the user is
@ -1116,7 +1155,7 @@
test_utils.createContacts(_converse, 'requesting').openControlBox();
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
_converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
@ -1220,14 +1259,14 @@
test_utils.createContacts(_converse, 'all').openControlBox();
test_utils.openContactsPanel(_converse);
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
return _converse.rosterview.$el.find('.roster-group').length;
}, 500)
.then(function () {
var jid, name, i;
for (i=0; i<mock.cur_names.length; i++) {
name = mock.cur_names[i];
jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
var $dd = _converse.rosterview.$el.find("dd:contains('"+name+"')").children().first();
var $dd = _converse.rosterview.$el.find("li:contains('"+name+"')").children().first();
var dd_text = $dd.text();
var dd_title = $dd.attr('title');
expect(_.trim(dd_text)).toBe(name);
@ -1279,11 +1318,10 @@
fullname: mock.pend_names[0]
});
test_utils.waitUntil(function () {
return _converse.rosterview.$el.find('dt').length;
}, 500)
.then(function () {
return _converse.rosterview.$el.find('.roster-group').length;
}, 500).then(function () {
// Checking that only one entry is created because both JID is same (Case sensitive check)
expect(_converse.rosterview.$el.find('dd:visible').length).toBe(1);
expect(_converse.rosterview.$el.find('li:visible').length).toBe(1);
expect(_converse.rosterview.update).toHaveBeenCalled();
done();
});

View File

@ -229,15 +229,15 @@
// Check that the user is now properly shown as a pending
// contact in the roster.
var $header = $('a:contains("Pending contacts")');
return test_utils.waitUntil(function () {
return $('a:contains("Pending contacts")').length;
return $('a:contains("Pending contacts")').length && $header.is(":visible");
}, 300);
}).then(function () {
var $header = $('a:contains("Pending contacts")');
expect($header.length).toBe(1);
expect($header.is(":visible")).toBeTruthy();
var $contacts = $header.parent().nextUntil('dt', 'dd');
var $contacts = $header.parent().find('li');
expect($contacts.length).toBe(1);
expect($contacts.is(':visible')).toBeTruthy();
spyOn(contact, "ackSubscribe").and.callThrough();
/* Here we assume the "happy path" that the contact
@ -300,7 +300,7 @@
$header = $('a:contains("My contacts")');
expect($header.length).toBe(1);
expect($header.is(":visible")).toBeTruthy();
$contacts = $header.parent().nextUntil('dt', 'dd');
$contacts = $header.parent().find('li');
expect($contacts.length).toBe(1);
// Check that it has the right classes and text
expect($contacts.hasClass('to')).toBeTruthy();
@ -488,7 +488,7 @@
}).then(function () {
var $header = $('a:contains("My contacts")');
// remove the first user
$($header.parent().nextUntil('dt', 'dd').find('.remove-xmpp-contact').get(0)).click();
$($header.parent().find('li .remove-xmpp-contact').get(0)).click();
expect(window.confirm).toHaveBeenCalled();
/* Section 8.6 Removing a Roster Item and Cancelling All
@ -554,7 +554,7 @@
var $header = $('a:contains("Contact requests")');
expect($header.length).toBe(1);
expect($header.is(":visible")).toBeTruthy();
var $contacts = $header.parent().nextUntil('dt', 'dd');
var $contacts = $header.parent().find('li');
expect($contacts.length).toBe(1);
done();
});

View File

@ -1109,7 +1109,8 @@
*/
return new Promise((resolve, reject) => {
this.fetch({
add: true,
'add': true,
'silent': true,
success (collection) {
if (collection.length === 0) {
_converse.send_initial_presence = true;
@ -1429,6 +1430,7 @@
this.RosterGroup = Backbone.Model.extend({
initialize (attributes) {
this.set(_.assignIn({
description: __('Click to hide these contacts'),

View File

@ -7,8 +7,7 @@
/*global define */
(function (root, factory) {
define(["jquery.noconflict",
"converse-core",
define(["converse-core",
"tpl!group_header",
"tpl!pending_contact",
"tpl!requesting_contact",
@ -18,7 +17,6 @@
"converse-chatboxes"
], factory);
}(this, function (
$,
converse,
tpl_group_header,
tpl_pending_contact,
@ -27,7 +25,8 @@
tpl_roster_filter,
tpl_roster_item) {
"use strict";
const { Backbone, utils, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
const { Backbone, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
const u = converse.env.utils;
converse.plugins.add('converse-rosterview', {
@ -246,14 +245,14 @@
},
show () {
if (utils.isVisible(this.el)) { return this; }
if (u.isVisible(this.el)) { return this; }
this.el.classList.add('fade-in');
this.el.classList.remove('hidden');
return this;
},
hide () {
if (!utils.isVisible(this.el)) { return this; }
if (!u.isVisible(this.el)) { return this; }
this.model.save({
'filter_text': '',
'chat_state': ''
@ -278,22 +277,24 @@
id: 'converse-roster',
initialize () {
_converse.roster.on("add", this.onContactAdd, this);
_converse.roster.on("add", this.onContactAdded, this);
_converse.roster.on('change', this.onContactChange, this);
_converse.roster.on("destroy", this.update, this);
_converse.roster.on("remove", this.update, this);
this.model.on("add", this.onGroupAdd, this);
this.model.on("add", this.onGroupAdded, this);
this.model.on("reset", this.reset, this);
_converse.on('rosterGroupsFetched', this.positionFetchedGroups, this);
_converse.on('rosterContactsFetched', this.update, this);
_converse.on('rosterContactsFetched', () => {
_converse.roster.each(this.onContactAdded.bind(this));
this.update();
});
this.createRosterFilter();
},
render () {
this.renderRoster();
this.el.innerHTML = "";
this.el.appendChild(this.filter_view.render().el);
this.renderRoster();
if (!_converse.allow_contact_requests) {
// XXX: if we ever support live editing of config then
// we'll need to be able to remove this class on the fly.
@ -303,8 +304,10 @@
},
renderRoster () {
this.$roster = $(tpl_roster());
this.roster = this.$roster[0];
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', tpl_roster());
this.roster_el = div.firstChild;
this.el.insertAdjacentElement('beforeend', this.roster_el);
},
createRosterFilter () {
@ -334,14 +337,14 @@
}, 100),
update: _.debounce(function () {
if (_.isNull(this.roster.parentElement)) {
this.$el.append(this.$roster.show());
if (!u.isVisible(this.roster_el)) {
u.showElement(this.roster_el);
}
return this.showHideFilter();
}, _converse.animate ? 100 : 0),
showHideFilter () {
if (!utils.isVisible(this.el)) {
if (!u.isVisible(this.el)) {
return;
}
this.filter_view.showOrHide();
@ -361,9 +364,9 @@
if (type === 'groups') {
_.each(this.getAll(), function (view, idx) {
if (!_.includes(view.model.get('name').toLowerCase(), query.toLowerCase())) {
view.hide();
u.slideIn(view.el);
} else if (view.model.contacts.length > 0) {
view.show();
u.slideOut(view.el);
}
});
} else {
@ -376,18 +379,17 @@
reset () {
_converse.roster.reset();
this.removeAll();
this.renderRoster();
this.render().update();
return this;
},
onGroupAdd (group) {
onGroupAdded (group) {
const view = new _converse.RosterGroupView({model: group});
this.add(group.get('name'), view.render());
this.positionGroup(view);
this.add(group.get('name'), view);
this.positionGroup(group);
},
onContactAdd (contact) {
onContactAdded (contact) {
this.addRosterContact(contact).update();
this.updateFilter();
},
@ -435,47 +437,28 @@
* positioned aren't already in inserted into the
* roster DOM element.
*/
const that = this;
this.model.sort();
this.model.each(function (group, idx) {
let view = that.get(group.get('name'));
if (!view) {
view = new _converse.RosterGroupView({model: group});
that.add(group.get('name'), view.render());
}
if (idx === 0) {
that.$roster.append(view.$el);
} else {
that.appendGroup(view);
}
});
this.model.each(this.onGroupAdded.bind(this));
},
positionGroup (view) {
positionGroup (group) {
/* Place the group's DOM element in the correct alphabetical
* position amongst the other groups in the roster.
*
* NOTE: relies on the assumption that it will be called in
* the right order of appearance of groups.
*/
const $groups = this.$roster.find('.roster-group'),
index = $groups.length ? this.model.indexOf(view.model) : 0;
const view = this.get(group.get('name'));
view.render();
const list = this.roster_el,
index = this.model.indexOf(view.model);
if (index === 0) {
this.$roster.prepend(view.$el);
list.insertAdjacentElement('afterbegin', view.el);
} else if (index === (this.model.length-1)) {
this.appendGroup(view);
list.insertAdjacentElement('beforeend', view.el);
} else {
$($groups.eq(index)).before(view.$el);
}
return this;
},
appendGroup (view) {
/* Add the group at the bottom of the roster
*/
const $last = this.$roster.find('.roster-group').last();
const $siblings = $last.siblings('dd');
if ($siblings.length > 0) {
$siblings.last().after(view.$el);
} else {
$last.after(view.$el);
const neighbour_el = list.querySelector('div:nth-child('+index+')');
neighbour_el.insertAdjacentElement('afterend', view.el);
}
return this;
},
@ -524,7 +507,8 @@
_converse.RosterContactView = Backbone.View.extend({
tagName: 'dd',
tagName: 'li',
className: 'hidden',
events: {
"click .accept-xmpp-request": "acceptRequest",
@ -543,7 +527,7 @@
render () {
const that = this;
if (!this.mayBeShown()) {
this.$el.hide();
u.hideElement(this.el);
return this;
}
const item = this.model,
@ -564,7 +548,8 @@
that.el.classList.remove(cls);
}
});
this.$el.addClass(chat_status).data('status', chat_status);
this.el.classList.add(chat_status);
this.el.setAttribute('data-status', chat_status);
if ((ask === 'subscribe') || (subscription === 'from')) {
/* ask === 'subscribe'
@ -579,21 +564,21 @@
* So in both cases the user is a "pending" contact.
*/
this.el.classList.add('pending-xmpp-contact');
this.$el.html(tpl_pending_contact(
this.el.innerHTML = tpl_pending_contact(
_.extend(item.toJSON(), {
'desc_remove': __('Click to remove %1$s as a contact', item.get('fullname')),
'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts
})
));
);
} else if (requesting === true) {
this.el.classList.add('requesting-xmpp-contact');
this.$el.html(tpl_requesting_contact(
this.el.innerHTML = tpl_requesting_contact(
_.extend(item.toJSON(), {
'desc_accept': __("Click to accept the contact request from %1$s", item.get('fullname')),
'desc_decline': __("Click to decline the contact request from %1$s", item.get('fullname')),
'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts
})
));
);
} else if (subscription === 'both' || subscription === 'to') {
this.el.classList.add('current-xmpp-contact');
this.el.classList.remove(_.without(['both', 'to'], subscription)[0]);
@ -604,44 +589,25 @@
},
renderRosterItem (item) {
const chat_status = item.get('chat_status');
this.$el.html(tpl_roster_item(
this.el.innerHTML = tpl_roster_item(
_.extend(item.toJSON(), {
'desc_status': STATUSES[chat_status||'offline'],
'desc_status': STATUSES[item.get('chat_status')||'offline'],
'desc_chat': __('Click to chat with this contact'),
'desc_remove': __('Click to remove %1$s as a contact', item.get('fullname')),
'title_fullname': __('Name'),
'allow_contact_removal': _converse.allow_contact_removal,
'num_unread': item.get('num_unread') || 0
})
));
);
return this;
},
isGroupCollapsed () {
/* Check whether the group in which this contact appears is
* collapsed.
*/
// XXX: this sucks and is fragile.
// It's because I tried to do the "right thing"
// and use definition lists to represent roster groups.
// If roster group items were inside the group elements, we
// would simplify things by not having to check whether the
// group is collapsed or not.
const name = this.$el.prevAll('dt:first').data('group');
const group = _.head(_converse.rosterview.model.where({'name': name.toString()}));
if (group.get('state') === _converse.CLOSED) {
return true;
}
return false;
},
mayBeShown () {
/* Return a boolean indicating whether this contact should
* generally be visible in the roster.
*
* It doesn't check for the more specific case of whether
* the group it's in is collapsed (see isGroupCollapsed).
* the group it's in is collapsed.
*/
const chatStatus = this.model.get('chat_status');
if ((_converse.show_only_online_users && chatStatus !== 'online') ||
@ -705,19 +671,17 @@
_converse.RosterGroupView = Backbone.Overview.extend({
tagName: 'dt',
tagName: 'div',
className: 'roster-group',
events: {
"click a.group-toggle": "toggle"
},
initialize () {
this.model.contacts.on("add", this.addContact, this);
this.model.contacts.on("add", this.onContactAdded, this);
this.model.contacts.on("change:subscription", this.onContactSubscriptionChange, this);
this.model.contacts.on("change:requesting", this.onContactRequestChange, this);
this.model.contacts.on("change:chat_status", function (contact) {
// This might be optimized by instead of first sorting,
// finding the correct position in positionContact
this.model.contacts.sort();
this.positionContact(contact).render();
}, this);
@ -728,25 +692,27 @@
render () {
this.el.setAttribute('data-group', this.model.get('name'));
const html = tpl_group_header({
label_group: this.model.get('name'),
desc_group_toggle: this.model.get('description'),
toggle_state: this.model.get('state')
this.el.innerHTML = tpl_group_header({
'label_group': this.model.get('name'),
'desc_group_toggle': this.model.get('description'),
'toggle_state': this.model.get('state'),
'_converse': _converse
});
this.el.innerHTML = html;
this.contacts_el = this.el.querySelector('.roster-group-contacts');
return this;
},
addContact (contact) {
let view = new _converse.RosterContactView({model: contact});
this.add(contact.get('id'), view);
view = this.positionContact(contact).render();
if (view.mayBeShown()) {
onContactAdded (contact) {
let contact_view = new _converse.RosterContactView({model: contact});
this.add(contact.get('id'), contact_view);
contact_view = this.positionContact(contact).render();
if (contact_view.mayBeShown()) {
if (this.model.get('state') === _converse.CLOSED) {
if (view.$el[0].style.display !== "none") { view.$el.hide(); }
if (!this.$el.is(':visible')) { this.$el.show(); }
u.hideElement(contact_view.el);
u.showElement(this.el);
} else {
if (this.$el[0].style.display !== "block") { this.show(); }
u.showElement(contact_view.el);
u.showElement(this.el);
}
}
},
@ -756,30 +722,89 @@
* position amongst the other contacts in this group.
*/
const view = this.get(contact.get('id'));
view.render();
const list = this.contacts_el;
const index = this.model.contacts.indexOf(contact);
view.$el.detach();
if (index === 0) {
this.$el.after(view.$el);
list.insertAdjacentElement('afterbegin', view.el);
} else if (index === (this.model.contacts.length-1)) {
this.$el.nextUntil('dt').last().after(view.$el);
list.insertAdjacentElement('beforeend', view.el);
} else {
this.$el.nextUntil('dt').eq(index).before(view.$el);
const neighbour_el = list.querySelector('li:nth-child('+index+')');
neighbour_el.insertAdjacentElement('afterend', view.el);
}
return view;
},
show () {
this.$el.show();
_.each(this.getAll(), function (view) {
if (view.mayBeShown() && !view.isGroupCollapsed()) {
view.$el.show();
u.showElement(this.el);
_.each(this.getAll(), (contact_view) => {
if (contact_view.mayBeShown() && this.model.get('state') === _converse.OPENED) {
u.showElement(contact_view.el);
}
});
return this;
},
hide () {
this.$el.nextUntil('dt').addBack().hide();
collapse () {
return u.slideIn(this.contacts_el);
},
filterOutContacts (contacts=[]) {
/* Given a list of contacts, make sure they're filtered out
* (aka hidden) and that all other contacts are visible.
*
* If all contacts are hidden, then also hide the group
* title.
*/
let shown = 0;
const all_contact_views = this.getAll();
_.each(this.model.contacts.models, (contact) => {
const contact_view = this.get(contact.get('id'));
if (_.includes(contacts, contact)) {
u.hideElement(contact_view.el);
} else if (contact_view.mayBeShown()) {
u.showElement(contact_view.el);
shown += 1;
}
});
if (shown) {
u.showElement(this.el);
} else {
u.hideElement(this.el);
}
},
getFilterMatches (q, type) {
/* Given the filter query "q" and the filter type "type",
* return a list of contacts that need to be filtered out.
*/
if (q.length === 0) {
return [];
}
let matches;
q = q.toLowerCase();
if (type === 'state') {
if (this.model.get('name') === HEADER_REQUESTING_CONTACTS) {
// When filtering by chat state, we still want to
// show requesting contacts, even though they don't
// have the state in question.
matches = this.model.contacts.filter(
(contact) => u.contains.not('chat_status', q)(contact) && !contact.get('requesting')
);
} else if (q === 'unread_messages') {
matches = this.model.contacts.filter({'num_unread': 0});
} else {
matches = this.model.contacts.filter(
u.contains.not('chat_status', q)
);
}
} else {
matches = this.model.contacts.filter(
u.contains.not('fullname', q)
);
}
return matches;
},
filter (q, type) {
@ -788,80 +813,27 @@
* If all contacts are filtered out (i.e. hidden), then the
* group must be filtered out as well.
*/
let matches;
if (q.length === 0) {
if (this.model.get('state') === _converse.OPENED) {
this.model.contacts.each(
(item) => {
const view = this.get(item.get('id'));
if (view.mayBeShown() && !view.isGroupCollapsed()) {
view.$el.show();
}
}
);
}
this.showIfNecessary();
} else {
q = q.toLowerCase();
if (type === 'state') {
if (this.model.get('name') === HEADER_REQUESTING_CONTACTS) {
// When filtering by chat state, we still want to
// show requesting contacts, even though they don't
// have the state in question.
matches = this.model.contacts.filter(
(contact) => utils.contains.not('chat_status', q)(contact) && !contact.get('requesting')
);
} else if (q === 'unread_messages') {
matches = this.model.contacts.filter({'num_unread': 0});
} else {
matches = this.model.contacts.filter(
utils.contains.not('chat_status', q)
);
}
} else {
matches = this.model.contacts.filter(
utils.contains.not('fullname', q)
);
}
if (matches.length === this.model.contacts.length) {
// hide the whole group
this.hide();
} else {
_.each(matches, (item) => {
this.get(item.get('id')).$el.hide();
});
if (this.model.get('state') === _converse.OPENED) {
_.each(this.model.contacts.reject(
utils.contains.not('fullname', q)),
(item) => {
this.get(item.get('id')).$el.show();
});
}
this.showIfNecessary();
}
}
},
showIfNecessary () {
if (!this.$el.is(':visible') && this.model.contacts.length > 0) {
this.$el.show();
}
this.filterOutContacts(this.getFilterMatches(q, type));
},
toggle (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
const $el = $(ev.target);
if ($el.hasClass("icon-opened")) {
this.$el.nextUntil('dt').slideUp();
if (_.includes(ev.target.classList, "icon-opened")) {
this.model.save({state: _converse.CLOSED});
$el.removeClass("icon-opened").addClass("icon-closed");
this.collapse().then(() => {
ev.target.classList.remove("icon-opened");
ev.target.classList.add("icon-closed");
});
} else {
$el.removeClass("icon-closed").addClass("icon-opened");
ev.target.classList.remove("icon-closed");
ev.target.classList.add("icon-opened");
this.model.save({state: _converse.OPENED});
this.filter(
_converse.rosterview.$('.roster-filter').val() || '',
_converse.rosterview.$('.filter-type').val()
_converse.rosterview.el.querySelector('.roster-filter').value,
_converse.rosterview.el.querySelector('.filter-type').value
);
u.showElement(this.el);
u.slideOut(this.contacts_el);
}
},
@ -872,7 +844,7 @@
if (in_this_group && !in_this_overview) {
this.model.contacts.remove(cid);
} else if (!in_this_group && in_this_overview) {
this.addContact(contact);
this.onContactAdded(contact);
}
},
@ -899,7 +871,7 @@
onRemove (contact) {
this.remove(contact.get('id'));
if (this.model.contacts.length === 0) {
this.$el.hide();
u.hideElement(this.el);
}
}
});
@ -931,7 +903,7 @@
return; // The message has no text
}
if (chatbox.get('type') !== 'chatroom' &&
utils.isNewMessage(data.stanza) &&
u.isNewMessage(data.stanza) &&
chatbox.newMessageWillBeHidden()) {
const contact = _.head(_converse.roster.where({'jid': chatbox.get('jid')}));

View File

@ -1 +1,2 @@
<a href="#" class="group-toggle icon-{{{o.toggle_state}}}" title="{{{o.desc_group_toggle}}}">{{{o.label_group}}}</a>
<ul class="roster-group-contacts {[ if (o.toggle_state === o._converse.CLOSED) { ]} collapsed {[ } ]}"></ul>

View File

@ -1 +1 @@
<dl class="roster-contacts"></dl>
<div class="roster-contacts"></div>