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, { "lodash/prefer-lodash-method": [2, {
"ignoreMethods": [ "ignoreMethods": [
"find", "endsWith", "startsWith", "filter", "reduce", "find", "endsWith", "startsWith", "filter", "reduce",
"map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase" "map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase", "includes"
] ]
}], }],
"lodash/prefer-startswith": "off", "lodash/prefer-startswith": "off",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,8 +7,7 @@
/*global define */ /*global define */
(function (root, factory) { (function (root, factory) {
define(["jquery.noconflict", define(["converse-core",
"converse-core",
"tpl!group_header", "tpl!group_header",
"tpl!pending_contact", "tpl!pending_contact",
"tpl!requesting_contact", "tpl!requesting_contact",
@ -18,7 +17,6 @@
"converse-chatboxes" "converse-chatboxes"
], factory); ], factory);
}(this, function ( }(this, function (
$,
converse, converse,
tpl_group_header, tpl_group_header,
tpl_pending_contact, tpl_pending_contact,
@ -27,7 +25,8 @@
tpl_roster_filter, tpl_roster_filter,
tpl_roster_item) { tpl_roster_item) {
"use strict"; "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', { converse.plugins.add('converse-rosterview', {
@ -246,14 +245,14 @@
}, },
show () { show () {
if (utils.isVisible(this.el)) { return this; } if (u.isVisible(this.el)) { return this; }
this.el.classList.add('fade-in'); this.el.classList.add('fade-in');
this.el.classList.remove('hidden'); this.el.classList.remove('hidden');
return this; return this;
}, },
hide () { hide () {
if (!utils.isVisible(this.el)) { return this; } if (!u.isVisible(this.el)) { return this; }
this.model.save({ this.model.save({
'filter_text': '', 'filter_text': '',
'chat_state': '' 'chat_state': ''
@ -278,22 +277,24 @@
id: 'converse-roster', id: 'converse-roster',
initialize () { 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('change', this.onContactChange, this);
_converse.roster.on("destroy", this.update, this); _converse.roster.on("destroy", this.update, this);
_converse.roster.on("remove", 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); this.model.on("reset", this.reset, this);
_converse.on('rosterGroupsFetched', this.positionFetchedGroups, 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(); this.createRosterFilter();
}, },
render () { render () {
this.renderRoster();
this.el.innerHTML = ""; this.el.innerHTML = "";
this.el.appendChild(this.filter_view.render().el); this.el.appendChild(this.filter_view.render().el);
this.renderRoster();
if (!_converse.allow_contact_requests) { if (!_converse.allow_contact_requests) {
// XXX: if we ever support live editing of config then // XXX: if we ever support live editing of config then
// we'll need to be able to remove this class on the fly. // we'll need to be able to remove this class on the fly.
@ -303,8 +304,10 @@
}, },
renderRoster () { renderRoster () {
this.$roster = $(tpl_roster()); const div = document.createElement('div');
this.roster = this.$roster[0]; div.insertAdjacentHTML('beforeend', tpl_roster());
this.roster_el = div.firstChild;
this.el.insertAdjacentElement('beforeend', this.roster_el);
}, },
createRosterFilter () { createRosterFilter () {
@ -334,14 +337,14 @@
}, 100), }, 100),
update: _.debounce(function () { update: _.debounce(function () {
if (_.isNull(this.roster.parentElement)) { if (!u.isVisible(this.roster_el)) {
this.$el.append(this.$roster.show()); u.showElement(this.roster_el);
} }
return this.showHideFilter(); return this.showHideFilter();
}, _converse.animate ? 100 : 0), }, _converse.animate ? 100 : 0),
showHideFilter () { showHideFilter () {
if (!utils.isVisible(this.el)) { if (!u.isVisible(this.el)) {
return; return;
} }
this.filter_view.showOrHide(); this.filter_view.showOrHide();
@ -361,9 +364,9 @@
if (type === 'groups') { if (type === 'groups') {
_.each(this.getAll(), function (view, idx) { _.each(this.getAll(), function (view, idx) {
if (!_.includes(view.model.get('name').toLowerCase(), query.toLowerCase())) { if (!_.includes(view.model.get('name').toLowerCase(), query.toLowerCase())) {
view.hide(); u.slideIn(view.el);
} else if (view.model.contacts.length > 0) { } else if (view.model.contacts.length > 0) {
view.show(); u.slideOut(view.el);
} }
}); });
} else { } else {
@ -376,18 +379,17 @@
reset () { reset () {
_converse.roster.reset(); _converse.roster.reset();
this.removeAll(); this.removeAll();
this.renderRoster();
this.render().update(); this.render().update();
return this; return this;
}, },
onGroupAdd (group) { onGroupAdded (group) {
const view = new _converse.RosterGroupView({model: group}); const view = new _converse.RosterGroupView({model: group});
this.add(group.get('name'), view.render()); this.add(group.get('name'), view);
this.positionGroup(view); this.positionGroup(group);
}, },
onContactAdd (contact) { onContactAdded (contact) {
this.addRosterContact(contact).update(); this.addRosterContact(contact).update();
this.updateFilter(); this.updateFilter();
}, },
@ -435,47 +437,28 @@
* positioned aren't already in inserted into the * positioned aren't already in inserted into the
* roster DOM element. * roster DOM element.
*/ */
const that = this;
this.model.sort(); this.model.sort();
this.model.each(function (group, idx) { this.model.each(this.onGroupAdded.bind(this));
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);
}
});
}, },
positionGroup (view) { positionGroup (group) {
/* Place the group's DOM element in the correct alphabetical /* Place the group's DOM element in the correct alphabetical
* position amongst the other groups in the roster. * 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'), const view = this.get(group.get('name'));
index = $groups.length ? this.model.indexOf(view.model) : 0; view.render();
const list = this.roster_el,
index = this.model.indexOf(view.model);
if (index === 0) { if (index === 0) {
this.$roster.prepend(view.$el); list.insertAdjacentElement('afterbegin', view.el);
} else if (index === (this.model.length-1)) { } else if (index === (this.model.length-1)) {
this.appendGroup(view); list.insertAdjacentElement('beforeend', view.el);
} else { } else {
$($groups.eq(index)).before(view.$el); const neighbour_el = list.querySelector('div:nth-child('+index+')');
} neighbour_el.insertAdjacentElement('afterend', 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);
} }
return this; return this;
}, },
@ -524,7 +507,8 @@
_converse.RosterContactView = Backbone.View.extend({ _converse.RosterContactView = Backbone.View.extend({
tagName: 'dd', tagName: 'li',
className: 'hidden',
events: { events: {
"click .accept-xmpp-request": "acceptRequest", "click .accept-xmpp-request": "acceptRequest",
@ -543,7 +527,7 @@
render () { render () {
const that = this; const that = this;
if (!this.mayBeShown()) { if (!this.mayBeShown()) {
this.$el.hide(); u.hideElement(this.el);
return this; return this;
} }
const item = this.model, const item = this.model,
@ -564,7 +548,8 @@
that.el.classList.remove(cls); 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')) { if ((ask === 'subscribe') || (subscription === 'from')) {
/* ask === 'subscribe' /* ask === 'subscribe'
@ -579,21 +564,21 @@
* So in both cases the user is a "pending" contact. * So in both cases the user is a "pending" contact.
*/ */
this.el.classList.add('pending-xmpp-contact'); this.el.classList.add('pending-xmpp-contact');
this.$el.html(tpl_pending_contact( this.el.innerHTML = tpl_pending_contact(
_.extend(item.toJSON(), { _.extend(item.toJSON(), {
'desc_remove': __('Click to remove %1$s as a contact', item.get('fullname')), 'desc_remove': __('Click to remove %1$s as a contact', item.get('fullname')),
'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts 'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts
}) })
)); );
} else if (requesting === true) { } else if (requesting === true) {
this.el.classList.add('requesting-xmpp-contact'); this.el.classList.add('requesting-xmpp-contact');
this.$el.html(tpl_requesting_contact( this.el.innerHTML = tpl_requesting_contact(
_.extend(item.toJSON(), { _.extend(item.toJSON(), {
'desc_accept': __("Click to accept the contact request from %1$s", item.get('fullname')), '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')), 'desc_decline': __("Click to decline the contact request from %1$s", item.get('fullname')),
'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts 'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts
}) })
)); );
} else if (subscription === 'both' || subscription === 'to') { } else if (subscription === 'both' || subscription === 'to') {
this.el.classList.add('current-xmpp-contact'); this.el.classList.add('current-xmpp-contact');
this.el.classList.remove(_.without(['both', 'to'], subscription)[0]); this.el.classList.remove(_.without(['both', 'to'], subscription)[0]);
@ -604,44 +589,25 @@
}, },
renderRosterItem (item) { renderRosterItem (item) {
const chat_status = item.get('chat_status'); this.el.innerHTML = tpl_roster_item(
this.$el.html(tpl_roster_item(
_.extend(item.toJSON(), { _.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_chat': __('Click to chat with this contact'),
'desc_remove': __('Click to remove %1$s as a contact', item.get('fullname')), 'desc_remove': __('Click to remove %1$s as a contact', item.get('fullname')),
'title_fullname': __('Name'), 'title_fullname': __('Name'),
'allow_contact_removal': _converse.allow_contact_removal, 'allow_contact_removal': _converse.allow_contact_removal,
'num_unread': item.get('num_unread') || 0 'num_unread': item.get('num_unread') || 0
}) })
)); );
return this; 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 () { mayBeShown () {
/* Return a boolean indicating whether this contact should /* Return a boolean indicating whether this contact should
* generally be visible in the roster. * generally be visible in the roster.
* *
* It doesn't check for the more specific case of whether * 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'); const chatStatus = this.model.get('chat_status');
if ((_converse.show_only_online_users && chatStatus !== 'online') || if ((_converse.show_only_online_users && chatStatus !== 'online') ||
@ -705,19 +671,17 @@
_converse.RosterGroupView = Backbone.Overview.extend({ _converse.RosterGroupView = Backbone.Overview.extend({
tagName: 'dt', tagName: 'div',
className: 'roster-group', className: 'roster-group',
events: { events: {
"click a.group-toggle": "toggle" "click a.group-toggle": "toggle"
}, },
initialize () { 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:subscription", this.onContactSubscriptionChange, this);
this.model.contacts.on("change:requesting", this.onContactRequestChange, this); this.model.contacts.on("change:requesting", this.onContactRequestChange, this);
this.model.contacts.on("change:chat_status", function (contact) { 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.model.contacts.sort();
this.positionContact(contact).render(); this.positionContact(contact).render();
}, this); }, this);
@ -728,25 +692,27 @@
render () { render () {
this.el.setAttribute('data-group', this.model.get('name')); this.el.setAttribute('data-group', this.model.get('name'));
const html = tpl_group_header({ this.el.innerHTML = tpl_group_header({
label_group: this.model.get('name'), 'label_group': this.model.get('name'),
desc_group_toggle: this.model.get('description'), 'desc_group_toggle': this.model.get('description'),
toggle_state: this.model.get('state') 'toggle_state': this.model.get('state'),
'_converse': _converse
}); });
this.el.innerHTML = html; this.contacts_el = this.el.querySelector('.roster-group-contacts');
return this; return this;
}, },
addContact (contact) { onContactAdded (contact) {
let view = new _converse.RosterContactView({model: contact}); let contact_view = new _converse.RosterContactView({model: contact});
this.add(contact.get('id'), view); this.add(contact.get('id'), contact_view);
view = this.positionContact(contact).render(); contact_view = this.positionContact(contact).render();
if (view.mayBeShown()) { if (contact_view.mayBeShown()) {
if (this.model.get('state') === _converse.CLOSED) { if (this.model.get('state') === _converse.CLOSED) {
if (view.$el[0].style.display !== "none") { view.$el.hide(); } u.hideElement(contact_view.el);
if (!this.$el.is(':visible')) { this.$el.show(); } u.showElement(this.el);
} else { } 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. * position amongst the other contacts in this group.
*/ */
const view = this.get(contact.get('id')); const view = this.get(contact.get('id'));
view.render();
const list = this.contacts_el;
const index = this.model.contacts.indexOf(contact); const index = this.model.contacts.indexOf(contact);
view.$el.detach();
if (index === 0) { if (index === 0) {
this.$el.after(view.$el); list.insertAdjacentElement('afterbegin', view.el);
} else if (index === (this.model.contacts.length-1)) { } else if (index === (this.model.contacts.length-1)) {
this.$el.nextUntil('dt').last().after(view.$el); list.insertAdjacentElement('beforeend', view.el);
} else { } 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; return view;
}, },
show () { show () {
this.$el.show(); u.showElement(this.el);
_.each(this.getAll(), function (view) { _.each(this.getAll(), (contact_view) => {
if (view.mayBeShown() && !view.isGroupCollapsed()) { if (contact_view.mayBeShown() && this.model.get('state') === _converse.OPENED) {
view.$el.show(); u.showElement(contact_view.el);
} }
}); });
return this; return this;
}, },
hide () { collapse () {
this.$el.nextUntil('dt').addBack().hide(); 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) { filter (q, type) {
@ -788,80 +813,27 @@
* If all contacts are filtered out (i.e. hidden), then the * If all contacts are filtered out (i.e. hidden), then the
* group must be filtered out as well. * group must be filtered out as well.
*/ */
let matches; this.filterOutContacts(this.getFilterMatches(q, type));
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();
}
}, },
toggle (ev) { toggle (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); } if (ev && ev.preventDefault) { ev.preventDefault(); }
const $el = $(ev.target); if (_.includes(ev.target.classList, "icon-opened")) {
if ($el.hasClass("icon-opened")) {
this.$el.nextUntil('dt').slideUp();
this.model.save({state: _converse.CLOSED}); 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 { } 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.model.save({state: _converse.OPENED});
this.filter( this.filter(
_converse.rosterview.$('.roster-filter').val() || '', _converse.rosterview.el.querySelector('.roster-filter').value,
_converse.rosterview.$('.filter-type').val() _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) { if (in_this_group && !in_this_overview) {
this.model.contacts.remove(cid); this.model.contacts.remove(cid);
} else if (!in_this_group && in_this_overview) { } else if (!in_this_group && in_this_overview) {
this.addContact(contact); this.onContactAdded(contact);
} }
}, },
@ -899,7 +871,7 @@
onRemove (contact) { onRemove (contact) {
this.remove(contact.get('id')); this.remove(contact.get('id'));
if (this.model.contacts.length === 0) { if (this.model.contacts.length === 0) {
this.$el.hide(); u.hideElement(this.el);
} }
} }
}); });
@ -931,7 +903,7 @@
return; // The message has no text return; // The message has no text
} }
if (chatbox.get('type') !== 'chatroom' && if (chatbox.get('type') !== 'chatroom' &&
utils.isNewMessage(data.stanza) && u.isNewMessage(data.stanza) &&
chatbox.newMessageWillBeHidden()) { chatbox.newMessageWillBeHidden()) {
const contact = _.head(_converse.roster.where({'jid': chatbox.get('jid')})); 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> <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>