Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
30d08d2b
Commit
30d08d2b
authored
Jan 28, 2020
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
muc-views: Remove features section...
and replace with button to open invite modal
parent
9fb20567
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
94 additions
and
116 deletions
+94
-116
sass/_chatrooms.scss
sass/_chatrooms.scss
+4
-3
sass/_core.scss
sass/_core.scss
+16
-0
spec/muc.js
spec/muc.js
+61
-43
src/converse-muc-views.js
src/converse-muc-views.js
+1
-0
src/templates/muc_sidebar.js
src/templates/muc_sidebar.js
+12
-70
No files found.
sass/_chatrooms.scss
View file @
30d08d2b
...
...
@@ -181,9 +181,10 @@
margin-bottom
:
0
.5em
;
display
:
flex
;
flex-direction
:
row
;
.fa-user-plus
{
margin-top
:
0
.2em
;
}
}
.fa-user-plus
{
margin-right
:
0
.5em
;
}
.occupants-heading
{
...
...
sass/_core.scss
View file @
30d08d2b
...
...
@@ -541,6 +541,22 @@ body.converse-fullscreen {
}
}
.btn-circle
{
width
:
32px
;
height
:
32px
;
text-align
:
center
;
padding
:
0
.5em
0
;
font-size
:
var
(
--
font-size-small
);
line-height
:
1
.428571429
;
border-radius
:
50%
;
}
.btn
{
&
.fa
{
color
:
white
!
important
;
}
}
.badge-groupchat
{
background-color
:
var
(
--
chatroom-badge-color
);
border-color
:
transparent
;
...
...
spec/muc.js
View file @
30d08d2b
...
...
@@ -1944,16 +1944,16 @@
expect
(
view
.
model
.
getOwnAffiliation
()).
toBe
(
'
owner
'
);
expect
(
view
.
model
.
features
.
get
(
'
open
'
)).
toBe
(
false
);
expect
(
view
.
el
.
querySelector
(
'
.o
ccupants-header .fa-user-plus
'
)).
not
.
toBe
(
null
);
expect
(
view
.
el
.
querySelector
(
'
.o
pen-invite-modal
'
)).
not
.
toBe
(
null
);
// Members can't invite if the room isn't open
view
.
model
.
getOwnOccupant
().
set
(
'
affiliation
'
,
'
member
'
);
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.o
ccupants-header .fa-user-plus
'
)
===
null
);
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.o
pen-invite-modal
'
)
===
null
);
view
.
model
.
features
.
set
(
'
open
'
,
'
true
'
);
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.o
ccupants-header .fa-user-plus
'
));
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.o
pen-invite-modal
'
));
view
.
el
.
querySelector
(
'
.o
ccupants-header .fa-user-plus
'
).
click
();
view
.
el
.
querySelector
(
'
.o
pen-invite-modal
'
).
click
();
const
modal
=
view
.
sidebar_view
.
muc_invite_modal
;
await
u
.
waitUntil
(()
=>
u
.
isVisible
(
modal
.
el
),
1000
)
...
...
@@ -2506,26 +2506,38 @@
];
await
test_utils
.
openAndEnterChatRoom
(
_converse
,
'
room@conference.example.org
'
,
'
romeo
'
,
features
);
const
jid
=
'
room@conference.example.org
'
;
const
chatroomview
=
_converse
.
chatboxviews
.
get
(
jid
);
let
features_list
=
chatroomview
.
el
.
querySelector
(
'
.features-list
'
);
const
view
=
_converse
.
chatboxviews
.
get
(
jid
);
const
info_el
=
view
.
el
.
querySelector
(
"
.show-room-details-modal
"
);
info_el
.
click
();
const
modal
=
view
.
model
.
room_details_modal
;
await
u
.
waitUntil
(()
=>
u
.
isVisible
(
modal
.
el
),
1000
);
let
features_list
=
modal
.
el
.
querySelector
(
'
.features-list
'
);
let
features_shown
=
features_list
.
textContent
.
split
(
'
\n
'
).
map
(
s
=>
s
.
trim
()).
filter
(
s
=>
s
);
expect
(
_
.
difference
([
"
Password protected
"
,
"
Open
"
,
"
Temporary
"
,
"
Not anonymous
"
,
"
Not moderated
"
],
features_shown
).
length
).
toBe
(
0
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
hidden
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
mam_enabled
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
membersonly
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
moderated
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
nonanonymous
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
open
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
passwordprotected
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
persistent
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
publicroom
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
semianonymous
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
temporary
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
unmoderated
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
unsecured
'
)).
toBe
(
false
);
expect
(
chatroomview
.
el
.
querySelector
(
'
.chatbox-title__text
'
).
textContent
.
trim
()).
toBe
(
'
Room
'
);
chatroomview
.
el
.
querySelector
(
'
.configure-chatroom-button
'
).
click
();
expect
(
features_shown
.
join
(
'
'
)).
toBe
(
'
Password protected - This groupchat requires a password before entry
'
+
'
Open - Anyone can join this groupchat
'
+
'
Temporary - This groupchat will disappear once the last person leaves
'
+
'
Not anonymous - All other groupchat participants can see your XMPP address
'
+
'
Not moderated - Participants entering this groupchat can write right away
'
);
expect
(
view
.
model
.
features
.
get
(
'
hidden
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
mam_enabled
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
membersonly
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
moderated
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
nonanonymous
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
open
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
passwordprotected
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
persistent
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
publicroom
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
semianonymous
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
temporary
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
unmoderated
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
unsecured
'
)).
toBe
(
false
);
expect
(
view
.
el
.
querySelector
(
'
.chatbox-title__text
'
).
textContent
.
trim
()).
toBe
(
'
Room
'
);
view
.
el
.
querySelector
(
'
.configure-chatroom-button
'
).
click
();
const
IQs
=
_converse
.
connection
.
IQ_stanzas
;
let
iq
=
await
u
.
waitUntil
(()
=>
_
.
filter
(
...
...
@@ -2602,9 +2614,9 @@
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
response_el
));
const
el
=
await
u
.
waitUntil
(()
=>
document
.
querySelector
(
'
.chatroom-form legend
'
));
expect
(
el
.
textContent
.
trim
()).
toBe
(
"
Configuration for room@conference.example.org
"
);
sizzle
(
'
[name="muc#roomconfig_membersonly"]
'
,
chatroom
view
.
el
).
pop
().
click
();
sizzle
(
'
[name="muc#roomconfig_roomname"]
'
,
chatroom
view
.
el
).
pop
().
value
=
"
New room name
"
chatroom
view
.
el
.
querySelector
(
'
.chatroom-form input[type="submit"]
'
).
click
();
sizzle
(
'
[name="muc#roomconfig_membersonly"]
'
,
view
.
el
).
pop
().
click
();
sizzle
(
'
[name="muc#roomconfig_roomname"]
'
,
view
.
el
).
pop
().
value
=
"
New room name
"
view
.
el
.
querySelector
(
'
.chatroom-form input[type="submit"]
'
).
click
();
iq
=
await
u
.
waitUntil
(()
=>
_
.
filter
(
IQs
,
iq
=>
u
.
matchesSelector
(
iq
,
`iq[to="
${
jid
}
"][type="set"]`
)).
pop
());
const
result
=
$iq
({
...
...
@@ -2656,24 +2668,30 @@
_converse
.
connection
.
_dataRecv
(
test_utils
.
createRequest
(
features_stanza
));
await
u
.
waitUntil
(()
=>
new
Promise
(
success
=>
chatroom
view
.
model
.
features
.
on
(
'
change
'
,
success
)));
features_list
=
chatroomview
.
el
.
querySelector
(
'
.features-list
'
);
await
u
.
waitUntil
(()
=>
new
Promise
(
success
=>
view
.
model
.
features
.
on
(
'
change
'
,
success
)));
features_list
=
modal
.
el
.
querySelector
(
'
.features-list
'
);
features_shown
=
features_list
.
textContent
.
split
(
'
\n
'
).
map
(
s
=>
s
.
trim
()).
filter
(
s
=>
s
);
expect
(
_
.
difference
([
"
Password protected
"
,
"
Hidden
"
,
"
Members only
"
,
"
Temporary
"
,
"
Not anonymous
"
,
"
Not moderated
"
],
features_shown
).
length
).
toBe
(
0
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
hidden
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
mam_enabled
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
membersonly
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
moderated
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
nonanonymous
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
open
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
passwordprotected
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
persistent
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
publicroom
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
semianonymous
'
)).
toBe
(
false
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
temporary
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
unmoderated
'
)).
toBe
(
true
);
expect
(
chatroomview
.
model
.
features
.
get
(
'
unsecured
'
)).
toBe
(
false
);
expect
(
chatroomview
.
el
.
querySelector
(
'
.chatbox-title__text
'
).
textContent
.
trim
()).
toBe
(
'
New room name
'
);
expect
(
features_shown
.
join
(
'
'
)).
toBe
(
'
Password protected - This groupchat requires a password before entry
'
+
'
Hidden - This groupchat is not publicly searchable
'
+
'
Members only - This groupchat is restricted to members only
'
+
'
Temporary - This groupchat will disappear once the last person leaves
'
+
'
Not anonymous - All other groupchat participants can see your XMPP address
'
+
'
Not moderated - Participants entering this groupchat can write right away
'
);
expect
(
view
.
model
.
features
.
get
(
'
hidden
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
mam_enabled
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
membersonly
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
moderated
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
nonanonymous
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
open
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
passwordprotected
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
persistent
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
publicroom
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
semianonymous
'
)).
toBe
(
false
);
expect
(
view
.
model
.
features
.
get
(
'
temporary
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
unmoderated
'
)).
toBe
(
true
);
expect
(
view
.
model
.
features
.
get
(
'
unsecured
'
)).
toBe
(
false
);
expect
(
view
.
el
.
querySelector
(
'
.chatbox-title__text
'
).
textContent
.
trim
()).
toBe
(
'
New room name
'
);
done
();
}));
...
...
src/converse-muc-views.js
View file @
30d08d2b
...
...
@@ -615,6 +615,7 @@ converse.plugins.add('converse-muc-views', {
initialize
()
{
_converse
.
BootstrapModal
.
prototype
.
initialize
.
apply
(
this
,
arguments
);
this
.
listenTo
(
this
.
model
,
'
change
'
,
this
.
render
);
this
.
listenTo
(
this
.
model
.
features
,
'
change
'
,
this
.
render
);
this
.
listenTo
(
this
.
model
.
occupants
,
'
add
'
,
this
.
render
);
this
.
listenTo
(
this
.
model
.
occupants
,
'
change
'
,
this
.
render
);
},
...
...
src/templates/muc_sidebar.js
View file @
30d08d2b
import
{
html
}
from
"
lit-html
"
;
import
{
__
}
from
'
@converse/headless/i18n
'
;
import
{
pick
}
from
"
lodash
"
;
import
converse
from
"
@converse/headless/converse-core
"
;
import
tpl_occupant
from
"
./occupant.js
"
;
const
PRETTY_CHAT_STATUS
=
{
'
offline
'
:
'
Offline
'
,
'
unavailable
'
:
'
Unavailable
'
,
...
...
@@ -16,77 +15,21 @@ const PRETTY_CHAT_STATUS = {
const
occupant_hint
=
(
occupant
)
=>
__
(
'
Click to mention %1$s in your message.
'
,
occupant
.
get
(
'
nick
'
))
const
i18n_archived
=
__
(
'
Message archiving
'
);
const
i18n_archived_hint
=
__
(
'
Messages are archived on the server
'
);
const
i18n_features
=
__
(
'
Features
'
);
const
i18n_hidden
=
__
(
'
Hidden
'
);
const
i18n_invite_hint
=
__
(
'
Invite people to join this groupchat
'
);
const
i18n_members_only
=
__
(
'
Members only
'
);
const
i18n_members_only_hint
=
__
(
'
this groupchat is restricted to members only
'
);
const
i18n_moderated
=
__
(
'
Moderated
'
);
const
i18n_moderated_hint
=
__
(
'
Participants entering this groupchat need to request permission to write
'
);
const
i18n_no_password
=
__
(
'
No password
'
);
const
i18n_no_password_hint
=
__
(
'
This groupchat does not require a password upon entry
'
);
const
i18n_non_anon_hint
=
__
(
'
All other groupchat participants can see your XMPP address
'
);
const
i18n_not_anon
=
__
(
'
Not anonymous
'
);
const
i18n_not_moderated
=
__
(
'
Not moderated
'
);
const
i18n_not_searchable_hint
=
__
(
'
This groupchat is not publicly searchable
'
);
const
i18n_open
=
__
(
'
Open
'
);
const
i18n_open_hint
=
__
(
'
Anyone can join this groupchat
'
);
const
i18n_invite_hint
=
__
(
'
Invite someone
'
);
const
i18n_participants
=
__
(
'
Participants
'
);
const
i18n_password
=
__
(
'
Password protected
'
)
const
i18n_password_hint
=
__
(
'
This groupchat requires a password before entry
'
);
const
i18n_persistent
=
__
(
'
Persistent
'
);
const
i18n_persistent_hint
=
__
(
'
This groupchat persists even if it
\'
s unoccupied
'
);
const
i18n_public
=
__
(
'
Public
'
);
const
i18n_searchable_hint
=
__
(
'
This groupchat is publicly searchable
'
);
const
i18n_semi_anon
=
__
(
'
Semi-anonymous
'
);
const
i18n_semi_anon_hint
=
__
(
'
Only moderators can see your XMPP address
'
);
const
i18n_temporary
=
__
(
'
Temporary
'
);
const
i18n_temporary_hint
=
__
(
'
This groupchat will disappear once the last person leaves
'
);
const
i18n_unmoderated_hint
=
__
(
'
Participants entering this groupchat can write right away
'
);
function
renderFeatures
(
o
)
{
const
picks
=
pick
(
o
.
features
.
attributes
,
converse
.
ROOM_FEATURES
);
const
iteratee
=
(
a
,
v
)
=>
a
||
v
;
if
(
Object
.
values
(
picks
).
reduce
(
iteratee
))
{
return
tpl_features
(
o
.
features
.
toJSON
());
}
else
{
return
''
;
}
}
const
tpl_features
=
(
o
)
=>
html
`
<div class="chatroom-features">
<p class="occupants-heading">
${
i18n_features
}
</p>
<ul class="features-list">
${
(
o
.
passwordprotected
)
?
html
`<li class="feature" title="
${
i18n_password_hint
}
"><span class="fa fa-lock"></span>
${
i18n_password
}
</li>`
:
''
}
${
(
o
.
unsecured
)
?
html
`<li class="feature" title="
${
i18n_no_password_hint
}
"><span class="fa fa-unlock"></span>
${
i18n_no_password
}
</li>`
:
''
}
${
(
o
.
hidden
)
?
html
`<li class="feature" title="
${
i18n_not_searchable_hint
}
"><span class="fa fa-eye-slash"></span>
${
i18n_hidden
}
</li>`
:
''
}
${
(
o
.
public_room
)
?
html
`<li class="feature" title="
${
i18n_searchable_hint
}
"><span class="fa fa-eye"></span>
${
i18n_public
}
</li>`
:
''
}
${
(
o
.
membersonly
)
?
html
`<li class="feature" title="
${
i18n_members_only_hint
}
"><span class="fa fa-address-book"></span>
${
i18n_members_only
}
</li>`
:
''
}
${
(
o
.
open
)
?
html
`<li class="feature" title="
${
i18n_open_hint
}
"><span class="fa fa-globe"></span>
${
i18n_open
}
</li>`
:
''
}
${
(
o
.
persistent
)
?
html
`<li class="feature" title="
${
i18n_persistent_hint
}
"><span class="fa fa-save"></span>
${
i18n_persistent
}
</li>`
:
''
}
${
(
o
.
temporary
)
?
html
`<li class="feature" title="
${
i18n_temporary_hint
}
"><span class="fa fa-snowflake"></span>
${
i18n_temporary
}
</li>`
:
''
}
${
(
o
.
nonanonymous
)
?
html
`<li class="feature" title="
${
i18n_non_anon_hint
}
"><span class="fa fa-id-card"></span>
${
i18n_not_anon
}
</li>`
:
''
}
${
(
o
.
semianonymous
)
?
html
`<li class="feature" title="
${
i18n_semi_anon_hint
}
"><span class="fa fa-user-secret"></span>
${
i18n_semi_anon
}
</li>`
:
''
}
${
(
o
.
moderated
)
?
html
`<li class="feature" title="
${
i18n_moderated_hint
}
"><span class="fa fa-gavel"></span>
${
i18n_moderated
}
</li>`
:
''
}
${
(
o
.
unmoderated
)
?
html
`<li class="feature" title="
${
i18n_unmoderated_hint
}
"><span class="fa fa-info-circle"></span>
${
i18n_not_moderated
}
</li>`
:
''
}
${
(
o
.
mam_enabled
)
?
html
`<li class="feature" title="
${
i18n_archived_hint
}
"><span class="fa fa-database"></span>
${
i18n_archived
}
</li>`
:
''
}
</ul>
</div>
`
;
const
invite_button
=
(
o
)
=>
{
const
invite_widget
=
(
o
)
=>
{
if
(
o
.
invitesAllowed
())
{
return
html
`
<a class="fa fa-user-plus"
title="
${
i18n_invite_hint
}
"
@click=
${
o
.
showInviteModal
}
data-toggle="modal"
data-target="#muc-invite-modal"></a>`
;
<a class="open-invite-modal"
title="
${
i18n_invite_hint
}
"
data-toggle="modal"
data-target="#muc-invite-modal"
@click=
${
o
.
showInviteModal
}
>
<i class="btn btn-primary btn-circle fa fa-user-plus"></i>
${
i18n_invite_hint
}
</a>`
;
}
else
{
return
''
;
}
...
...
@@ -98,7 +41,6 @@ export default (o) => html`
<i class="hide-occupants fa fa-times"></i>
<div class="occupants-header--title">
<span class="occupants-heading">
${
i18n_participants
}
</span>
${
invite_button
(
o
)
}
</div>
</div>
<div class="dragresize dragresize-occupants-left"></div>
...
...
@@ -113,5 +55,5 @@ export default (o) => html`
);
})
}
<
/ul
>
$
{
renderFeatures
(
o
)
}
$
{
invite_widget
(
o
)
}
`
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment