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
e8536ebc
Commit
e8536ebc
authored
Dec 06, 2020
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move converse-muc-views plugin into own folder
parent
2b6c56f1
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
613 additions
and
570 deletions
+613
-570
src/converse.js
src/converse.js
+1
-1
src/plugins/muc-views/api.js
src/plugins/muc-views/api.js
+69
-0
src/plugins/muc-views/config-form.js
src/plugins/muc-views/config-form.js
+65
-0
src/plugins/muc-views/index.js
src/plugins/muc-views/index.js
+163
-0
src/plugins/muc-views/muc.js
src/plugins/muc-views/muc.js
+206
-569
src/plugins/muc-views/password-form.js
src/plugins/muc-views/password-form.js
+30
-0
src/plugins/muc-views/rooms-panel.js
src/plugins/muc-views/rooms-panel.js
+79
-0
No files found.
src/converse.js
View file @
e8536ebc
...
...
@@ -22,7 +22,7 @@ import "./plugins/dragresize.js"; // Allows chat boxes to be resized b
import
"
./plugins/fullscreen.js
"
;
import
"
./plugins/mam-views.js
"
;
import
"
./plugins/minimize.js
"
;
// Allows chat boxes to be minimized
import
"
./plugins/muc-views.js
"
;
// Views related to MUC
import
"
./plugins/muc-views
/index
.js
"
;
// Views related to MUC
import
"
./plugins/headlines-view.js
"
;
import
"
./plugins/notifications.js
"
;
import
"
./plugins/omemo.js
"
;
...
...
src/plugins/muc-views/api.js
0 → 100644
View file @
e8536ebc
import
{
_converse
,
api
}
from
"
@converse/headless/core
"
;
export
default
{
/**
* The "roomviews" namespace groups methods relevant to chatroom
* (aka groupchats) views.
*
* @namespace _converse.api.roomviews
* @memberOf _converse.api
*/
roomviews
:
{
/**
* Retrieves a groupchat (aka chatroom) view. The chat should already be open.
*
* @method _converse.api.roomviews.get
* @param {String|string[]} name - e.g. 'coven@conference.shakespeare.lit' or
* ['coven@conference.shakespeare.lit', 'cave@conference.shakespeare.lit']
* @returns {View} View representing the groupchat
*
* @example
* // To return a single view, provide the JID of the groupchat
* const view = _converse.api.roomviews.get('coven@conference.shakespeare.lit');
*
* @example
* // To return an array of views, provide an array of JIDs:
* const views = _converse.api.roomviews.get(['coven@conference.shakespeare.lit', 'cave@conference.shakespeare.lit']);
*
* @example
* // To return views of all open groupchats, call the method without any parameters::
* const views = _converse.api.roomviews.get();
*
*/
get
(
jids
)
{
if
(
Array
.
isArray
(
jids
))
{
const
views
=
api
.
chatviews
.
get
(
jids
);
return
views
.
filter
(
v
=>
v
.
model
.
get
(
'
type
'
)
===
_converse
.
CHATROOMS_TYPE
)
}
else
{
const
view
=
api
.
chatviews
.
get
(
jids
);
if
(
view
.
model
.
get
(
'
type
'
)
===
_converse
.
CHATROOMS_TYPE
)
{
return
view
;
}
else
{
return
null
;
}
}
},
/**
* Lets you close open chatrooms.
*
* You can call this method without any arguments to close
* all open chatrooms, or you can specify a single JID or
* an array of JIDs.
*
* @method _converse.api.roomviews.close
* @param {(String[]|String)} jids The JID or array of JIDs of the chatroom(s)
* @returns { Promise } - Promise which resolves once the views have been closed.
*/
close
(
jids
)
{
let
views
;
if
(
jids
===
undefined
)
{
views
=
_converse
.
chatboxviews
;
}
else
if
(
typeof
jids
===
'
string
'
)
{
views
=
[
_converse
.
chatboxviews
.
get
(
jids
)].
filter
(
v
=>
v
);
}
else
if
(
Array
.
isArray
(
jids
))
{
views
=
jids
.
map
(
jid
=>
_converse
.
chatboxviews
.
get
(
jid
));
}
return
Promise
.
all
(
views
.
map
(
v
=>
(
v
.
is_chatroom
&&
v
.
model
&&
v
.
close
())))
}
}
}
src/plugins/muc-views/config-form.js
0 → 100644
View file @
e8536ebc
import
log
from
"
@converse/headless/log
"
;
import
tpl_muc_config_form
from
"
templates/muc_config_form.js
"
;
import
{
View
}
from
'
@converse/skeletor/src/view.js
'
;
import
{
__
}
from
'
i18n
'
;
import
{
api
,
converse
}
from
"
@converse/headless/core
"
;
const
{
sizzle
}
=
converse
.
env
;
const
u
=
converse
.
env
.
utils
;
const
MUCConfigForm
=
View
.
extend
({
className
:
'
chatroom-form-container muc-config-form
'
,
initialize
(
attrs
)
{
this
.
chatroomview
=
attrs
.
chatroomview
;
this
.
listenTo
(
this
.
chatroomview
.
model
.
features
,
'
change:passwordprotected
'
,
this
.
render
);
this
.
listenTo
(
this
.
chatroomview
.
model
.
features
,
'
change:config_stanza
'
,
this
.
render
);
this
.
render
();
},
toHTML
()
{
const
stanza
=
u
.
toStanza
(
this
.
model
.
get
(
'
config_stanza
'
));
const
whitelist
=
api
.
settings
.
get
(
'
roomconfig_whitelist
'
);
let
fields
=
sizzle
(
'
field
'
,
stanza
);
if
(
whitelist
.
length
)
{
fields
=
fields
.
filter
(
f
=>
whitelist
.
includes
(
f
.
getAttribute
(
'
var
'
)));
}
const
password_protected
=
this
.
model
.
features
.
get
(
'
passwordprotected
'
);
const
options
=
{
'
new_password
'
:
!
password_protected
,
'
fixed_username
'
:
this
.
model
.
get
(
'
jid
'
)
};
return
tpl_muc_config_form
({
'
closeConfigForm
'
:
ev
=>
this
.
closeConfigForm
(
ev
),
'
fields
'
:
fields
.
map
(
f
=>
u
.
xForm2webForm
(
f
,
stanza
,
options
)),
'
instructions
'
:
stanza
.
querySelector
(
'
instructions
'
)?.
textContent
,
'
submitConfigForm
'
:
ev
=>
this
.
submitConfigForm
(
ev
),
'
title
'
:
stanza
.
querySelector
(
'
title
'
)?.
textContent
});
},
async
submitConfigForm
(
ev
)
{
ev
.
preventDefault
();
const
inputs
=
sizzle
(
'
:input:not([type=button]):not([type=submit])
'
,
ev
.
target
);
const
config_array
=
inputs
.
map
(
u
.
webForm2xForm
).
filter
(
f
=>
f
);
try
{
await
this
.
model
.
sendConfiguration
(
config_array
);
}
catch
(
e
)
{
log
.
error
(
e
);
const
message
=
__
(
"
Sorry, an error occurred while trying to submit the config form.
"
)
+
"
"
+
__
(
"
Check your browser's developer console for details.
"
);
api
.
alert
(
'
error
'
,
__
(
'
Error
'
),
message
);
}
await
this
.
model
.
refreshDiscoInfo
();
this
.
chatroomview
.
closeForm
();
},
closeConfigForm
(
ev
)
{
ev
.
preventDefault
();
this
.
chatroomview
.
closeForm
();
}
});
export
default
MUCConfigForm
src/plugins/muc-views/index.js
0 → 100644
View file @
e8536ebc
/**
* @module converse-muc-views
* @copyright 2020, the Converse.js contributors
* @description XEP-0045 Multi-User Chat Views
* @license Mozilla Public License (MPLv2)
*/
import
'
../../components/muc-sidebar
'
;
import
'
../chatview/index.js
'
;
import
'
../modal.js
'
;
import
'
@converse/headless/utils/muc
'
;
import
ChatRoomViewMixin
from
'
./muc.js
'
;
import
MUCConfigForm
from
'
./config-form.js
'
;
import
MUCPasswordForm
from
'
./password-form.js
'
;
import
log
from
'
@converse/headless/log
'
;
import
muc_api
from
'
./api.js
'
;
import
{
RoomsPanel
,
RoomsPanelViewMixin
}
from
'
./rooms-panel.js
'
;
import
{
api
,
converse
,
_converse
}
from
'
@converse/headless/core
'
;
const
{
Strophe
}
=
converse
.
env
;
function
setMUCDomain
(
domain
,
controlboxview
)
{
controlboxview
.
getRoomsPanel
().
model
.
save
(
'
muc_domain
'
,
Strophe
.
getDomainFromJid
(
domain
));
}
function
setMUCDomainFromDisco
(
controlboxview
)
{
/* Check whether service discovery for the user's domain
* returned MUC information and use that to automatically
* set the MUC domain in the "Add groupchat" modal.
*/
function
featureAdded
(
feature
)
{
if
(
!
feature
)
{
return
;
}
if
(
feature
.
get
(
'
var
'
)
===
Strophe
.
NS
.
MUC
)
{
feature
.
entity
.
getIdentity
(
'
conference
'
,
'
text
'
).
then
(
identity
=>
{
if
(
identity
)
{
setMUCDomain
(
feature
.
get
(
'
from
'
),
controlboxview
);
}
});
}
}
api
.
waitUntil
(
'
discoInitialized
'
)
.
then
(()
=>
{
api
.
listen
.
on
(
'
serviceDiscovered
'
,
featureAdded
);
// Features could have been added before the controlbox was
// initialized. We're only interested in MUC
_converse
.
disco_entities
.
each
(
entity
=>
featureAdded
(
entity
.
features
.
findWhere
({
'
var
'
:
Strophe
.
NS
.
MUC
})));
})
.
catch
(
e
=>
log
.
error
(
e
));
}
function
fetchAndSetMUCDomain
(
controlboxview
)
{
if
(
controlboxview
.
model
.
get
(
'
connected
'
))
{
if
(
!
controlboxview
.
getRoomsPanel
().
model
.
get
(
'
muc_domain
'
))
{
if
(
api
.
settings
.
get
(
'
muc_domain
'
)
===
undefined
)
{
setMUCDomainFromDisco
(
controlboxview
);
}
else
{
setMUCDomain
(
api
.
settings
.
get
(
'
muc_domain
'
),
controlboxview
);
}
}
}
}
function
openChatRoomFromURIClicked
(
ev
)
{
ev
.
preventDefault
();
api
.
rooms
.
open
(
ev
.
target
.
href
);
}
async
function
addView
(
model
)
{
const
views
=
_converse
.
chatboxviews
;
if
(
!
views
.
get
(
model
.
get
(
'
id
'
))
&&
model
.
get
(
'
type
'
)
===
_converse
.
CHATROOMS_TYPE
&&
model
.
isValid
())
{
await
model
.
initialized
;
return
views
.
add
(
model
.
get
(
'
id
'
),
new
_converse
.
ChatRoomView
({
model
}));
}
}
converse
.
plugins
.
add
(
'
converse-muc-views
'
,
{
/* Dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin. They are "optional" because they might not be
* available, in which case any overrides applicable to them will be
* ignored.
*
* NB: These plugins need to have already been loaded via require.js.
*
* It's possible to make these dependencies "non-optional".
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*/
dependencies
:
[
'
converse-autocomplete
'
,
'
converse-modal
'
,
'
converse-controlbox
'
,
'
converse-chatview
'
],
overrides
:
{
ControlBoxView
:
{
renderControlBoxPane
()
{
this
.
__super__
.
renderControlBoxPane
.
apply
(
this
,
arguments
);
if
(
api
.
settings
.
get
(
'
allow_muc
'
))
{
this
.
renderRoomsPanel
();
}
}
}
},
initialize
()
{
const
{
_converse
}
=
this
;
api
.
promises
.
add
([
'
roomsPanelRendered
'
]);
// Configuration values for this plugin
// ====================================
// Refer to docs/source/configuration.rst for explanations of these
// configuration settings.
api
.
settings
.
extend
({
'
auto_list_rooms
'
:
false
,
'
cache_muc_messages
'
:
true
,
'
locked_muc_nickname
'
:
false
,
'
modtools_disable_query
'
:
[],
'
modtools_disable_assign
'
:
false
,
'
muc_disable_slash_commands
'
:
false
,
'
muc_mention_autocomplete_filter
'
:
'
contains
'
,
'
muc_mention_autocomplete_min_chars
'
:
0
,
'
muc_mention_autocomplete_show_avatar
'
:
true
,
'
muc_roomid_policy
'
:
null
,
'
muc_roomid_policy_hint
'
:
null
,
'
roomconfig_whitelist
'
:
[],
'
show_retraction_warning
'
:
true
,
'
visible_toolbar_buttons
'
:
{
'
toggle_occupants
'
:
true
}
});
_converse
.
MUCConfigForm
=
MUCConfigForm
;
_converse
.
MUCPasswordForm
=
MUCPasswordForm
;
_converse
.
ChatRoomView
=
_converse
.
ChatBoxView
.
extend
(
ChatRoomViewMixin
);
_converse
.
RoomsPanel
=
RoomsPanel
;
_converse
.
ControlBoxView
&&
Object
.
assign
(
_converse
.
ControlBoxView
.
prototype
,
RoomsPanelViewMixin
);
Object
.
assign
(
_converse
.
api
,
muc_api
);
/************************ BEGIN Event Handlers ************************/
api
.
listen
.
on
(
'
chatBoxViewsInitialized
'
,
()
=>
{
_converse
.
chatboxviews
.
delegate
(
'
click
'
,
'
a.open-chatroom
'
,
openChatRoomFromURIClicked
);
_converse
.
chatboxes
.
on
(
'
add
'
,
addView
);
});
api
.
listen
.
on
(
'
clearSession
'
,
()
=>
{
const
view
=
_converse
.
chatboxviews
.
get
(
'
controlbox
'
);
if
(
view
&&
view
.
roomspanel
)
{
view
.
roomspanel
.
model
.
destroy
();
view
.
roomspanel
.
remove
();
delete
view
.
roomspanel
;
}
});
api
.
listen
.
on
(
'
controlBoxInitialized
'
,
view
=>
{
if
(
!
api
.
settings
.
get
(
'
allow_muc
'
))
{
return
;
}
fetchAndSetMUCDomain
(
view
);
view
.
model
.
on
(
'
change:connected
'
,
()
=>
fetchAndSetMUCDomain
(
view
));
});
/************************ END Event Handlers ************************/
}
});
src/plugins/muc-views.js
→
src/plugins/muc-views
/muc
.js
View file @
e8536ebc
/**
* @module converse-muc-views
* @copyright 2020, the Converse.js contributors
* @description XEP-0045 Multi-User Chat Views
* @license Mozilla Public License (MPLv2)
*/
import
"
../components/muc-sidebar
"
;
import
"
./chatview/index.js
"
;
import
"
./modal.js
"
;
import
"
@converse/headless/utils/muc
"
;
import
AddMUCModal
from
'
../modals/add-muc.js
'
;
import
MUCInviteModal
from
'
../modals/muc-invite.js
'
;
import
MUCListModal
from
'
../modals/muc-list.js
'
;
import
ModeratorToolsModal
from
"
../modals/moderator-tools.js
"
;
import
OccupantModal
from
'
../modals/occupant.js
'
;
import
RoomDetailsModal
from
'
../modals/muc-details.js
'
;
import
log
from
"
@converse/headless/log
"
;
import
tpl_chatroom
from
"
../templates/chatroom.js
"
;
import
tpl_chatroom_head
from
"
../templates/chatroom_head.js
"
;
import
tpl_muc_bottom_panel
from
"
../templates/muc_bottom_panel.js
"
;
import
tpl_muc_config_form
from
"
../templates/muc_config_form.js
"
;
import
tpl_muc_destroyed
from
"
../templates/muc_destroyed.js
"
;
import
tpl_muc_disconnect
from
"
../templates/muc_disconnect.js
"
;
import
tpl_muc_nickname_form
from
"
../templates/muc_nickname_form.js
"
;
import
tpl_muc_password_form
from
"
../templates/muc_password_form.js
"
;
import
tpl_room_panel
from
"
../templates/room_panel.js
"
;
import
tpl_spinner
from
"
../templates/spinner.js
"
;
import
'
./config-form.js
'
;
import
'
./password-form.js
'
;
import
MUCInviteModal
from
'
modals/muc-invite.js
'
;
import
ModeratorToolsModal
from
'
modals/moderator-tools.js
'
;
import
OccupantModal
from
'
modals/occupant.js
'
;
import
RoomDetailsModal
from
'
modals/muc-details.js
'
;
import
log
from
'
@converse/headless/log
'
;
import
tpl_chatroom
from
'
templates/chatroom.js
'
;
import
tpl_chatroom_head
from
'
templates/chatroom_head.js
'
;
import
tpl_muc_bottom_panel
from
'
templates/muc_bottom_panel.js
'
;
import
tpl_muc_destroyed
from
'
templates/muc_destroyed.js
'
;
import
tpl_muc_disconnect
from
'
templates/muc_disconnect.js
'
;
import
tpl_muc_nickname_form
from
'
templates/muc_nickname_form.js
'
;
import
tpl_spinner
from
'
templates/spinner.js
'
;
import
{
Model
}
from
'
@converse/skeletor/src/model.js
'
;
import
{
View
}
from
'
@converse/skeletor/src/view.js
'
;
import
{
__
}
from
'
../i18n
'
;
import
{
_converse
,
api
,
converse
}
from
"
@converse/headless/core
"
;
import
{
debounce
}
from
"
lodash-es
"
;
import
{
render
}
from
"
lit-html
"
;
import
{
__
}
from
'
i18n
'
;
import
{
_converse
,
api
,
converse
}
from
'
@converse/headless/core
'
;
import
{
debounce
}
from
'
lodash-es
'
;
import
{
render
}
from
'
lit-html
'
;
const
{
Strophe
,
sizzle
,
$pres
}
=
converse
.
env
;
const
u
=
converse
.
env
.
utils
;
...
...
@@ -46,24 +32,22 @@ const COMMAND_TO_ROLE = {
'
mute
'
:
'
visitor
'
,
'
op
'
:
'
moderator
'
,
'
voice
'
:
'
participant
'
}
}
;
const
COMMAND_TO_AFFILIATION
=
{
'
admin
'
:
'
admin
'
,
'
ban
'
:
'
outcast
'
,
'
member
'
:
'
member
'
,
'
owner
'
:
'
owner
'
,
'
revoke
'
:
'
none
'
}
};
/**
* NativeView which renders a groupchat, based upon
* { @link _converse.ChatBoxView } for normal one-on-one chat boxes.
* @class
* Mixin which turns a ChatBoxView into a ChatRoomView
* @mixin
* @namespace _converse.ChatRoomView
* @memberOf _converse
*/
export
const
ChatRoomView
=
_converse
.
ChatBoxView
.
extend
(
{
const
ChatRoomViewMixin
=
{
length
:
300
,
tagName
:
'
div
'
,
className
:
'
chatbox chatroom hidden
'
,
...
...
@@ -73,7 +57,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
'
click .hide-occupants
'
:
'
hideOccupants
'
,
'
click .new-msgs-indicator
'
:
'
viewUnreadMessages
'
,
// Arrow functions don't work here because you can't bind a different `this` param to them.
'
click .occupant-nick
'
:
function
(
ev
)
{
this
.
insertIntoTextArea
(
ev
.
target
.
textContent
)
},
'
click .occupant-nick
'
:
function
(
ev
)
{
this
.
insertIntoTextArea
(
ev
.
target
.
textContent
);
},
'
click .send-button
'
:
'
onFormSubmitted
'
,
'
dragover .chat-textarea
'
:
'
onDragOver
'
,
'
drop .chat-textarea
'
:
'
onDrop
'
,
...
...
@@ -82,15 +68,19 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
'
keyup .chat-textarea
'
:
'
onKeyUp
'
,
'
mousedown .dragresize-occupants-left
'
:
'
onStartResizeOccupants
'
,
'
paste .chat-textarea
'
:
'
onPaste
'
,
'
submit .muc-nickname-form
'
:
'
submitNickname
'
,
'
submit .muc-nickname-form
'
:
'
submitNickname
'
},
async
initialize
()
{
this
.
initDebounced
();
this
.
listenTo
(
this
.
model
,
'
change
'
,
debounce
(()
=>
this
.
renderHeading
(),
250
));
this
.
listenTo
(
this
.
model
,
'
change
'
,
debounce
(()
=>
this
.
renderHeading
(),
250
)
);
this
.
listenTo
(
this
.
model
,
'
change:composing_spoiler
'
,
this
.
renderMessageForm
);
this
.
listenTo
(
this
.
model
,
'
change:hidden
'
,
m
=>
m
.
get
(
'
hidden
'
)
?
this
.
hide
()
:
this
.
show
(
));
this
.
listenTo
(
this
.
model
,
'
change:hidden
'
,
m
=>
(
m
.
get
(
'
hidden
'
)
?
this
.
hide
()
:
this
.
show
()
));
this
.
listenTo
(
this
.
model
,
'
change:hidden_occupants
'
,
this
.
onSidebarToggle
);
this
.
listenTo
(
this
.
model
,
'
configurationNeeded
'
,
this
.
getAndRenderConfigurationForm
);
this
.
listenTo
(
this
.
model
,
'
destroy
'
,
this
.
hide
);
...
...
@@ -101,8 +91,8 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
this
.
listenTo
(
this
.
model
.
session
,
'
change:connection_status
'
,
this
.
onConnectionStatusChanged
);
// Bind so that we can pass it to addEventListener and removeEventListener
this
.
onMouseMove
=
this
.
onMouseMove
.
bind
(
this
);
this
.
onMouseUp
=
this
.
onMouseUp
.
bind
(
this
);
this
.
onMouseMove
=
this
.
onMouseMove
.
bind
(
this
);
this
.
onMouseUp
=
this
.
onMouseUp
.
bind
(
this
);
await
this
.
render
();
...
...
@@ -142,16 +132,20 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
async
render
()
{
const
sidebar_hidden
=
!
this
.
shouldShowSidebar
();
this
.
el
.
setAttribute
(
'
id
'
,
this
.
model
.
get
(
'
box_id
'
));
render
(
tpl_chatroom
({
sidebar_hidden
,
'
model
'
:
this
.
model
,
'
occupants
'
:
this
.
model
.
occupants
,
'
show_sidebar
'
:
!
this
.
model
.
get
(
'
hidden_occupants
'
)
&&
this
.
model
.
session
.
get
(
'
connection_status
'
)
===
converse
.
ROOMSTATUS
.
ENTERED
,
'
markScrolled
'
:
ev
=>
this
.
markScrolled
(
ev
),
'
muc_show_logs_before_join
'
:
api
.
settings
.
get
(
'
muc_show_logs_before_join
'
),
'
show_send_button
'
:
_converse
.
show_send_button
,
}),
this
.
el
);
render
(
tpl_chatroom
({
sidebar_hidden
,
'
model
'
:
this
.
model
,
'
occupants
'
:
this
.
model
.
occupants
,
'
show_sidebar
'
:
!
this
.
model
.
get
(
'
hidden_occupants
'
)
&&
this
.
model
.
session
.
get
(
'
connection_status
'
)
===
converse
.
ROOMSTATUS
.
ENTERED
,
'
markScrolled
'
:
ev
=>
this
.
markScrolled
(
ev
),
'
muc_show_logs_before_join
'
:
api
.
settings
.
get
(
'
muc_show_logs_before_join
'
),
'
show_send_button
'
:
_converse
.
show_send_button
}),
this
.
el
);
this
.
notifications
=
this
.
el
.
querySelector
(
'
.chat-content__notifications
'
);
this
.
content
=
this
.
el
.
querySelector
(
'
.chat-content
'
);
...
...
@@ -159,8 +153,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
this
.
help_container
=
this
.
el
.
querySelector
(
'
.chat-content__help
'
);
this
.
renderBottomPanel
();
if
(
!
api
.
settings
.
get
(
'
muc_show_logs_before_join
'
)
&&
this
.
model
.
session
.
get
(
'
connection_status
'
)
!==
converse
.
ROOMSTATUS
.
ENTERED
)
{
if
(
!
api
.
settings
.
get
(
'
muc_show_logs_before_join
'
)
&&
this
.
model
.
session
.
get
(
'
connection_status
'
)
!==
converse
.
ROOMSTATUS
.
ENTERED
)
{
this
.
showSpinner
();
}
// Render header as late as possible since it's async and we
...
...
@@ -173,17 +169,19 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
getNotifications
()
{
const
actors_per_state
=
this
.
model
.
notifications
.
toJSON
();
const
role_changes
=
api
.
settings
.
get
(
'
muc_show_info_messages
'
)
const
role_changes
=
api
.
settings
.
get
(
'
muc_show_info_messages
'
)
.
filter
(
role_change
=>
converse
.
MUC_ROLE_CHANGES_LIST
.
includes
(
role_change
));
const
join_leave_events
=
api
.
settings
.
get
(
'
muc_show_info_messages
'
)
const
join_leave_events
=
api
.
settings
.
get
(
'
muc_show_info_messages
'
)
.
filter
(
join_leave_event
=>
converse
.
MUC_TRAFFIC_STATES_LIST
.
includes
(
join_leave_event
));
const
states
=
[...
converse
.
CHAT_STATES
,
...
join_leave_events
,
...
role_changes
];
return
states
.
reduce
((
result
,
state
)
=>
{
const
existing_actors
=
actors_per_state
[
state
];
if
(
!
(
existing_actors
?.
length
)
)
{
if
(
!
existing_actors
?.
length
)
{
return
result
;
}
const
actors
=
existing_actors
.
map
(
a
=>
this
.
model
.
getOccupant
(
a
)?.
getDisplayName
()
||
a
);
...
...
@@ -199,18 +197,20 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
else
if
(
state
===
'
exited
'
)
{
return
`
${
result
}${
__
(
'
%1$s has left the groupchat
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
op
'
)
{
return
`
${
result
}${
__
(
"
%1$s is now a moderator
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s is now a moderator
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
deop
'
)
{
return
`
${
result
}${
__
(
"
%1$s is no longer a moderator
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s is no longer a moderator
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
voice
'
)
{
return
`
${
result
}${
__
(
"
%1$s has been given a voice
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s has been given a voice
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
mute
'
)
{
return
`
${
result
}${
__
(
"
%1$s has been muted
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s has been muted
'
,
actors
[
0
])}
\n`
;
}
}
else
if
(
actors
.
length
>
1
)
{
let
actors_str
;
if
(
actors
.
length
>
3
)
{
actors_str
=
`
${
Array
.
from
(
actors
).
slice
(
0
,
2
).
join
(
'
,
'
)}
and others`
;
actors_str
=
`
${
Array
.
from
(
actors
)
.
slice
(
0
,
2
)
.
join
(
'
,
'
)}
and others`
;
}
else
{
const
last_actor
=
actors
.
pop
();
actors_str
=
__
(
'
%1$s and %2$s
'
,
actors
.
join
(
'
,
'
),
last_actor
);
...
...
@@ -227,13 +227,13 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
else
if
(
state
===
'
exited
'
)
{
return
`
${
result
}${
__
(
'
%1$s have left the groupchat
'
,
actors_str
)}
\n`
;
}
else
if
(
state
===
'
op
'
)
{
return
`
${
result
}${
__
(
"
%1$s are now moderators
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s are now moderators
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
deop
'
)
{
return
`
${
result
}${
__
(
"
%1$s are no longer moderators
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s are no longer moderators
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
voice
'
)
{
return
`
${
result
}${
__
(
"
%1$s have been given voices
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s have been given voices
'
,
actors
[
0
])}
\n`
;
}
else
if
(
state
===
'
mute
'
)
{
return
`
${
result
}${
__
(
"
%1$s have been muted
"
,
actors
[
0
])}
\n`
;
return
`
${
result
}${
__
(
'
%1$s have been muted
'
,
actors
[
0
])}
\n`
;
}
}
return
result
;
...
...
@@ -241,7 +241,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
},
getHelpMessages
()
{
const
setting
=
api
.
settings
.
get
(
"
muc_disable_slash_commands
"
);
const
setting
=
api
.
settings
.
get
(
'
muc_disable_slash_commands
'
);
const
disabled_commands
=
Array
.
isArray
(
setting
)
?
setting
:
[];
return
[
`<strong>/admin</strong>:
${
__
(
"
Change user's affiliation to admin
"
)}
`
,
...
...
@@ -259,13 +259,14 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
`<strong>/nick</strong>:
${
__
(
'
Change your nickname
'
)}
`
,
`<strong>/op</strong>:
${
__
(
'
Grant moderator role to user
'
)}
`
,
`<strong>/owner</strong>:
${
__
(
'
Grant ownership of this groupchat
'
)}
`
,
`<strong>/register</strong>:
${
__
(
"
Register your nickname
"
)}
`
,
`<strong>/register</strong>:
${
__
(
'
Register your nickname
'
)}
`
,
`<strong>/revoke</strong>:
${
__
(
"
Revoke the user's current affiliation
"
)}
`
,
`<strong>/subject</strong>:
${
__
(
'
Set groupchat subject
'
)}
`
,
`<strong>/topic</strong>:
${
__
(
'
Set groupchat subject (alias for /subject)
'
)}
`
,
`<strong>/voice</strong>:
${
__
(
'
Allow muted user to post messages
'
)}
`
].
filter
(
line
=>
disabled_commands
.
every
(
c
=>
(
!
line
.
startsWith
(
c
+
'
<
'
,
9
))))
.
filter
(
line
=>
this
.
getAllowedCommands
().
some
(
c
=>
line
.
startsWith
(
c
+
'
<
'
,
9
)));
]
.
filter
(
line
=>
disabled_commands
.
every
(
c
=>
!
line
.
startsWith
(
c
+
'
<
'
,
9
)))
.
filter
(
line
=>
this
.
getAllowedCommands
().
some
(
c
=>
line
.
startsWith
(
c
+
'
<
'
,
9
)));
},
/**
...
...
@@ -319,7 +320,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
const
sidebar_el
=
this
.
el
.
querySelector
(
'
converse-muc-sidebar
'
);
const
element_position
=
sidebar_el
.
getBoundingClientRect
();
const
occupants_width
=
this
.
calculateSidebarWidth
(
element_position
,
0
);
const
attrs
=
{
occupants_width
};
const
attrs
=
{
occupants_width
};
_converse
.
connection
.
connected
?
this
.
model
.
save
(
attrs
)
:
this
.
model
.
set
(
attrs
);
}
},
...
...
@@ -333,23 +334,23 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
this
.
is_maximum
=
element_position
.
left
>
current_mouse_position
;
}
else
{
const
occupants_width
=
this
.
calculateSidebarWidth
(
element_position
,
delta
);
sidebar_el
.
style
.
flex
=
"
0 0
"
+
occupants_width
+
"
px
"
;
sidebar_el
.
style
.
flex
=
'
0 0
'
+
occupants_width
+
'
px
'
;
}
},
calculateSidebarWidth
(
element_position
,
delta
)
{
calculateSidebarWidth
(
element_position
,
delta
)
{
let
occupants_width
=
element_position
.
width
+
delta
;
const
room_width
=
this
.
el
.
clientWidth
;
// keeping display in boundaries
if
(
occupants_width
<
(
room_width
*
0.20
)
)
{
if
(
occupants_width
<
room_width
*
0.2
)
{
// set pixel to 20% width
occupants_width
=
(
room_width
*
0.20
)
;
occupants_width
=
room_width
*
0.2
;
this
.
is_minimum
=
true
;
}
else
if
(
occupants_width
>
(
room_width
*
0.75
)
)
{
}
else
if
(
occupants_width
>
room_width
*
0.75
)
{
// set pixel to 75% width
occupants_width
=
(
room_width
*
0.75
)
;
occupants_width
=
room_width
*
0.75
;
this
.
is_maximum
=
true
;
}
else
if
(
(
room_width
-
occupants_width
)
<
250
)
{
}
else
if
(
room_width
-
occupants_width
<
250
)
{
// resize occupants if chat-area becomes smaller than 250px (min-width property set in css)
occupants_width
=
room_width
-
250
;
this
.
is_maximum
=
true
;
...
...
@@ -361,39 +362,39 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
},
getAutoCompleteList
()
{
return
this
.
model
.
getAllKnownNicknames
().
map
(
nick
=>
({
'
label
'
:
nick
,
'
value
'
:
`@
${
nick
}
`
}));
return
this
.
model
.
getAllKnownNicknames
().
map
(
nick
=>
({
'
label
'
:
nick
,
'
value
'
:
`@
${
nick
}
`
}));
},
getAutoCompleteListItem
(
text
,
input
)
{
getAutoCompleteListItem
(
text
,
input
)
{
input
=
input
.
trim
();
const
element
=
document
.
createElement
(
"
li
"
);
element
.
setAttribute
(
"
aria-selected
"
,
"
false
"
);
const
element
=
document
.
createElement
(
'
li
'
);
element
.
setAttribute
(
'
aria-selected
'
,
'
false
'
);
if
(
api
.
settings
.
get
(
'
muc_mention_autocomplete_show_avatar
'
))
{
const
img
=
document
.
createElement
(
"
img
"
);
let
dataUri
=
"
data:
"
+
_converse
.
DEFAULT_IMAGE_TYPE
+
"
;base64,
"
+
_converse
.
DEFAULT_IMAGE
;
const
img
=
document
.
createElement
(
'
img
'
);
let
dataUri
=
'
data:
'
+
_converse
.
DEFAULT_IMAGE_TYPE
+
'
;base64,
'
+
_converse
.
DEFAULT_IMAGE
;
if
(
_converse
.
vcards
)
{
const
vcard
=
_converse
.
vcards
.
findWhere
({
'
nickname
'
:
text
});
if
(
vcard
)
dataUri
=
"
data:
"
+
vcard
.
get
(
'
image_type
'
)
+
"
;base64,
"
+
vcard
.
get
(
'
image
'
);
const
vcard
=
_converse
.
vcards
.
findWhere
({
'
nickname
'
:
text
});
if
(
vcard
)
dataUri
=
'
data:
'
+
vcard
.
get
(
'
image_type
'
)
+
'
;base64,
'
+
vcard
.
get
(
'
image
'
);
}
img
.
setAttribute
(
"
src
"
,
dataUri
);
img
.
setAttribute
(
"
width
"
,
"
22
"
);
img
.
setAttribute
(
"
class
"
,
"
avatar avatar-autocomplete
"
);
img
.
setAttribute
(
'
src
'
,
dataUri
);
img
.
setAttribute
(
'
width
'
,
'
22
'
);
img
.
setAttribute
(
'
class
'
,
'
avatar avatar-autocomplete
'
);
element
.
appendChild
(
img
);
}
const
regex
=
new
RegExp
(
"
(
"
+
input
+
"
)
"
,
"
ig
"
);
const
regex
=
new
RegExp
(
'
(
'
+
input
+
'
)
'
,
'
ig
'
);
const
parts
=
input
?
text
.
split
(
regex
)
:
[
text
];
parts
.
forEach
(
txt
=>
{
if
(
input
&&
txt
.
match
(
regex
))
{
const
match
=
document
.
createElement
(
"
mark
"
);
match
.
textContent
=
txt
;
element
.
appendChild
(
match
);
const
match
=
document
.
createElement
(
'
mark
'
);
match
.
textContent
=
txt
;
element
.
appendChild
(
match
);
}
else
{
element
.
appendChild
(
document
.
createTextNode
(
txt
));
element
.
appendChild
(
document
.
createTextNode
(
txt
));
}
});
...
...
@@ -407,10 +408,11 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
'
min_chars
'
:
api
.
settings
.
get
(
'
muc_mention_autocomplete_min_chars
'
),
'
match_current_word
'
:
true
,
'
list
'
:
()
=>
this
.
getAutoCompleteList
(),
'
filter
'
:
api
.
settings
.
get
(
'
muc_mention_autocomplete_filter
'
)
==
'
contains
'
?
_converse
.
FILTER_CONTAINS
:
_converse
.
FILTER_STARTSWITH
,
'
ac_triggers
'
:
[
"
Tab
"
,
"
@
"
],
'
filter
'
:
api
.
settings
.
get
(
'
muc_mention_autocomplete_filter
'
)
==
'
contains
'
?
_converse
.
FILTER_CONTAINS
:
_converse
.
FILTER_STARTSWITH
,
'
ac_triggers
'
:
[
'
Tab
'
,
'
@
'
],
'
include_triggers
'
:
[],
'
item
'
:
this
.
getAutoCompleteListItem
});
...
...
@@ -442,10 +444,11 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
},
async
onMessageRetractButtonClicked
(
message
)
{
const
retraction_warning
=
__
(
"
Be aware that other XMPP/Jabber clients (and servers) may
"
+
"
not yet support retractions and that this message may not
"
+
"
be removed everywhere.
"
);
const
retraction_warning
=
__
(
'
Be aware that other XMPP/Jabber clients (and servers) may
'
+
'
not yet support retractions and that this message may not
'
+
'
be removed everywhere.
'
);
if
(
message
.
mayBeRetracted
())
{
const
messages
=
[
__
(
'
Are you sure you want to retract this message?
'
)];
...
...
@@ -457,7 +460,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
if
(
message
.
get
(
'
sender
'
)
===
'
me
'
)
{
let
messages
=
[
__
(
'
Are you sure you want to retract this message?
'
)];
if
(
api
.
settings
.
get
(
'
show_retraction_warning
'
))
{
messages
=
[
messages
[
0
],
retraction_warning
,
messages
[
1
]]
messages
=
[
messages
[
0
],
retraction_warning
,
messages
[
1
]]
;
}
!!
(
await
api
.
confirm
(
__
(
'
Confirm
'
),
messages
))
&&
this
.
retractOtherMessage
(
message
);
}
else
{
...
...
@@ -466,14 +469,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
__
(
'
You may optionally include a message, explaining the reason for the retraction.
'
)
];
if
(
api
.
settings
.
get
(
'
show_retraction_warning
'
))
{
messages
=
[
messages
[
0
],
retraction_warning
,
messages
[
1
]]
messages
=
[
messages
[
0
],
retraction_warning
,
messages
[
1
]]
;
}
const
reason
=
await
api
.
prompt
(
__
(
'
Message Retraction
'
),
messages
,
__
(
'
Optional reason
'
)
);
(
reason
!==
false
)
&&
this
.
retractOtherMessage
(
message
,
reason
);
const
reason
=
await
api
.
prompt
(
__
(
'
Message Retraction
'
),
messages
,
__
(
'
Optional reason
'
));
reason
!==
false
&&
this
.
retractOtherMessage
(
message
,
reason
);
}
}
else
{
const
err_msg
=
__
(
`Sorry, you're not allowed to retract this message`
);
...
...
@@ -510,20 +509,20 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
if
(
modal
)
{
modal
.
model
.
set
(
'
affiliation
'
,
affiliation
);
}
else
{
const
model
=
new
Model
({
'
affiliation
'
:
affiliation
});
modal
=
api
.
modal
.
create
(
ModeratorToolsModal
,
{
model
,
_converse
,
'
chatroomview
'
:
this
});
const
model
=
new
Model
({
'
affiliation
'
:
affiliation
});
modal
=
api
.
modal
.
create
(
ModeratorToolsModal
,
{
model
,
_converse
,
'
chatroomview
'
:
this
});
}
modal
.
show
();
},
showRoomDetailsModal
(
ev
)
{
ev
.
preventDefault
();
api
.
modal
.
show
(
RoomDetailsModal
,
{
'
model
'
:
this
.
model
},
ev
);
api
.
modal
.
show
(
RoomDetailsModal
,
{
'
model
'
:
this
.
model
},
ev
);
},
showOccupantDetailsModal
(
ev
,
message
)
{
ev
.
preventDefault
();
api
.
modal
.
show
(
OccupantModal
,
{
'
model
'
:
message
.
occupant
},
ev
);
api
.
modal
.
show
(
OccupantModal
,
{
'
model
'
:
message
.
occupant
},
ev
);
},
showChatStateNotification
(
message
)
{
...
...
@@ -534,8 +533,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
},
shouldShowSidebar
()
{
return
!
this
.
model
.
get
(
'
hidden_occupants
'
)
&&
this
.
model
.
session
.
get
(
'
connection_status
'
)
===
converse
.
ROOMSTATUS
.
ENTERED
;
return
(
!
this
.
model
.
get
(
'
hidden_occupants
'
)
&&
this
.
model
.
session
.
get
(
'
connection_status
'
)
===
converse
.
ROOMSTATUS
.
ENTERED
);
},
onSidebarToggle
()
{
...
...
@@ -598,9 +599,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
if
(
subject
&&
subject
.
text
)
{
buttons
.
push
({
'
i18n_text
'
:
subject_hidden
?
__
(
'
Show topic
'
)
:
__
(
'
Hide topic
'
),
'
i18n_title
'
:
subject_hidden
?
__
(
'
Show the topic message in the heading
'
)
:
__
(
'
Hide the topic in the heading
'
),
'
i18n_title
'
:
subject_hidden
?
__
(
'
Show the topic message in the heading
'
)
:
__
(
'
Hide the topic in the heading
'
),
'
handler
'
:
ev
=>
this
.
toggleTopic
(
ev
),
'
a_class
'
:
'
hide-topic
'
,
'
icon_class
'
:
'
fa-minus-square
'
,
...
...
@@ -608,7 +609,6 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
});
}
const
conn_status
=
this
.
model
.
session
.
get
(
'
connection_status
'
);
if
(
conn_status
===
converse
.
ROOMSTATUS
.
ENTERED
)
{
const
allowed_commands
=
this
.
getAllowedCommands
();
...
...
@@ -634,7 +634,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
}
if
(
!
api
.
settings
.
get
(
"
singleton
"
))
{
if
(
!
api
.
settings
.
get
(
'
singleton
'
))
{
buttons
.
push
({
'
i18n_text
'
:
__
(
'
Leave
'
),
'
i18n_title
'
:
__
(
'
Leave and close this groupchat
'
),
...
...
@@ -645,7 +645,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
result
&&
this
.
close
(
ev
);
},
'
a_class
'
:
'
close-chatbox-button
'
,
'
standalone
'
:
api
.
settings
.
get
(
"
view_mode
"
)
===
'
overlayed
'
,
'
standalone
'
:
api
.
settings
.
get
(
'
view_mode
'
)
===
'
overlayed
'
,
'
icon_class
'
:
'
fa-sign-out-alt
'
,
'
name
'
:
'
signout
'
});
...
...
@@ -669,8 +669,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
subject_hidden
,
'
dropdown_btns
'
:
dropdown_btns
.
map
(
b
=>
this
.
getHeadingDropdownItem
(
b
)),
'
standalone_btns
'
:
standalone_btns
.
map
(
b
=>
this
.
getHeadingStandaloneButton
(
b
)),
'
title
'
:
this
.
model
.
getDisplayName
(),
}));
'
title
'
:
this
.
model
.
getDisplayName
()
})
);
},
toggleTopic
()
{
...
...
@@ -679,10 +680,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
showInviteModal
(
ev
)
{
ev
.
preventDefault
();
api
.
modal
.
show
(
MUCInviteModal
,
{
'
model
'
:
new
Model
(),
'
chatroomview
'
:
this
},
ev
);
api
.
modal
.
show
(
MUCInviteModal
,
{
'
model
'
:
new
Model
(),
'
chatroomview
'
:
this
},
ev
);
},
/**
* Callback method that gets called after the chat has become visible.
* @private
...
...
@@ -718,13 +718,11 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
},
getToolbarOptions
()
{
return
Object
.
assign
(
_converse
.
ChatBoxView
.
prototype
.
getToolbarOptions
.
apply
(
this
,
arguments
),
{
'
is_groupchat
'
:
true
,
'
label_hide_occupants
'
:
__
(
'
Hide the list of participants
'
),
'
show_occupants_toggle
'
:
_converse
.
visible_toolbar_buttons
.
toggle_occupants
}
);
return
Object
.
assign
(
_converse
.
ChatBoxView
.
prototype
.
getToolbarOptions
.
apply
(
this
,
arguments
),
{
'
is_groupchat
'
:
true
,
'
label_hide_occupants
'
:
__
(
'
Hide the list of participants
'
),
'
show_occupants_toggle
'
:
_converse
.
visible_toolbar_buttons
.
toggle_occupants
});
},
/**
...
...
@@ -734,7 +732,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
*/
async
close
()
{
this
.
hide
();
if
(
_converse
.
router
.
history
.
getFragment
()
===
"
converse/room?jid=
"
+
this
.
model
.
get
(
'
jid
'
))
{
if
(
_converse
.
router
.
history
.
getFragment
()
===
'
converse/room?jid=
'
+
this
.
model
.
get
(
'
jid
'
))
{
_converse
.
router
.
navigate
(
''
);
}
await
this
.
model
.
leave
();
...
...
@@ -751,18 +749,18 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
ev
.
preventDefault
();
ev
.
stopPropagation
();
}
this
.
model
.
save
({
'
hidden_occupants
'
:
true
});
this
.
model
.
save
({
'
hidden_occupants
'
:
true
});
this
.
scrollDown
();
},
verifyRoles
(
roles
,
occupant
,
show_error
=
true
)
{
verifyRoles
(
roles
,
occupant
,
show_error
=
true
)
{
if
(
!
Array
.
isArray
(
roles
))
{
throw
new
TypeError
(
'
roles must be an Array
'
);
}
if
(
!
roles
.
length
)
{
return
true
;
}
occupant
=
occupant
||
this
.
model
.
occupants
.
findWhere
({
'
jid
'
:
_converse
.
bare_jid
});
occupant
=
occupant
||
this
.
model
.
occupants
.
findWhere
({
'
jid
'
:
_converse
.
bare_jid
});
if
(
occupant
)
{
const
role
=
occupant
.
get
(
'
role
'
);
if
(
roles
.
includes
(
role
))
{
...
...
@@ -771,19 +769,19 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
if
(
show_error
)
{
const
message
=
__
(
'
Forbidden: you do not have the necessary role in order to do that.
'
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
}
return
false
;
},
verifyAffiliations
(
affiliations
,
occupant
,
show_error
=
true
)
{
verifyAffiliations
(
affiliations
,
occupant
,
show_error
=
true
)
{
if
(
!
Array
.
isArray
(
affiliations
))
{
throw
new
TypeError
(
'
affiliations must be an Array
'
);
}
if
(
!
affiliations
.
length
)
{
return
true
;
}
occupant
=
occupant
||
this
.
model
.
occupants
.
findWhere
({
'
jid
'
:
_converse
.
bare_jid
});
occupant
=
occupant
||
this
.
model
.
occupants
.
findWhere
({
'
jid
'
:
_converse
.
bare_jid
});
if
(
occupant
)
{
const
a
=
occupant
.
get
(
'
affiliation
'
);
if
(
affiliations
.
includes
(
a
))
{
...
...
@@ -792,7 +790,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
if
(
show_error
)
{
const
message
=
__
(
'
Forbidden: you do not have the necessary affiliation in order to do that.
'
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
}
return
false
;
},
...
...
@@ -803,7 +801,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
'
Error: the "%1$s" command takes two arguments, the user
\'
s nickname and optionally a reason.
'
,
command
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
return
false
;
}
return
true
;
...
...
@@ -814,24 +812,24 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
return
args
.
trim
();
}
if
(
!
args
.
startsWith
(
'
@
'
))
{
args
=
'
@
'
+
args
;
args
=
'
@
'
+
args
;
}
const
[
text
,
references
]
=
this
.
model
.
parseTextForReferences
(
args
);
// eslint-disable-line no-unused-vars
if
(
!
references
.
length
)
{
const
message
=
__
(
"
Error: couldn't find a groupchat participant based on your arguments
"
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
return
;
}
if
(
references
.
length
>
1
)
{
const
message
=
__
(
"
Error: found multiple groupchat participant based on your arguments
"
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
const
message
=
__
(
'
Error: found multiple groupchat participant based on your arguments
'
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
return
;
}
const
nick_or_jid
=
references
.
pop
().
value
;
const
reason
=
args
.
split
(
nick_or_jid
,
2
)[
1
];
if
(
reason
&&
!
reason
.
startsWith
(
'
'
))
{
const
message
=
__
(
"
Error: couldn't find a groupchat participant based on your arguments
"
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
return
;
}
return
nick_or_jid
;
...
...
@@ -863,10 +861,9 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
jid
=
nick_or_jid
;
}
else
{
const
message
=
__
(
"
Couldn't find a participant with that nickname.
"
+
"
They might have left the groupchat.
"
"
Couldn't find a participant with that nickname.
"
+
'
They might have left the groupchat.
'
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
return
;
}
}
...
...
@@ -874,16 +871,17 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
if
(
occupant
&&
api
.
settings
.
get
(
'
auto_register_muc_nickname
'
))
{
attrs
[
'
nick
'
]
=
occupant
.
get
(
'
nick
'
);
}
this
.
model
.
setAffiliation
(
affiliation
,
[
attrs
])
this
.
model
.
setAffiliation
(
affiliation
,
[
attrs
])
.
then
(()
=>
this
.
model
.
occupants
.
fetchMembers
())
.
catch
(
err
=>
this
.
onCommandError
(
err
));
},
getReason
(
args
)
{
return
args
.
includes
(
'
,
'
)
?
args
.
slice
(
args
.
indexOf
(
'
,
'
)
+
1
).
trim
()
:
null
;
return
args
.
includes
(
'
,
'
)
?
args
.
slice
(
args
.
indexOf
(
'
,
'
)
+
1
).
trim
()
:
null
;
},
setRole
(
command
,
args
,
required_affiliations
=
[],
required_roles
=
[])
{
setRole
(
command
,
args
,
required_affiliations
=
[],
required_roles
=
[])
{
/* Check that a command to change a groupchat user's role or
* affiliation has anough arguments.
*/
...
...
@@ -911,9 +909,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
onCommandError
(
err
)
{
log
.
fatal
(
err
);
const
message
=
__
(
"
Sorry, an error happened while running the command.
"
)
+
"
"
+
__
(
'
Sorry, an error happened while running the command.
'
)
+
'
'
+
__
(
"
Check your browser's developer console for details.
"
);
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
},
getAllowedCommands
()
{
...
...
@@ -921,7 +920,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
if
(
this
.
model
.
config
.
get
(
'
changesubject
'
)
||
[
'
owner
'
,
'
admin
'
].
includes
(
this
.
model
.
getOwnAffiliation
()))
{
allowed_commands
=
[...
allowed_commands
,
...[
'
subject
'
,
'
topic
'
]];
}
const
occupant
=
this
.
model
.
occupants
.
findWhere
({
'
jid
'
:
_converse
.
bare_jid
});
const
occupant
=
this
.
model
.
occupants
.
findWhere
({
'
jid
'
:
_converse
.
bare_jid
});
if
(
this
.
verifyAffiliations
([
'
owner
'
],
occupant
,
false
))
{
allowed_commands
=
allowed_commands
.
concat
(
OWNER_COMMANDS
).
concat
(
ADMIN_COMMANDS
);
}
else
if
(
this
.
verifyAffiliations
([
'
admin
'
],
occupant
,
false
))
{
...
...
@@ -943,42 +942,48 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
async
destroy
()
{
const
messages
=
[
__
(
'
Are you sure you want to destroy this groupchat?
'
)];
let
fields
=
[{
'
name
'
:
'
challenge
'
,
'
label
'
:
__
(
'
Please enter the XMPP address of this groupchat to confirm
'
),
'
challenge
'
:
this
.
model
.
get
(
'
jid
'
),
'
placeholder
'
:
__
(
'
name@example.org
'
),
'
required
'
:
true
},
{
'
name
'
:
'
reason
'
,
'
label
'
:
__
(
'
Optional reason for destroying this groupchat
'
),
'
placeholder
'
:
__
(
'
Reason
'
)
},
{
'
name
'
:
'
newjid
'
,
'
label
'
:
__
(
'
Optional XMPP address for a new groupchat that replaces this one
'
),
'
placeholder
'
:
__
(
'
replacement@example.org
'
)
}];
let
fields
=
[
{
'
name
'
:
'
challenge
'
,
'
label
'
:
__
(
'
Please enter the XMPP address of this groupchat to confirm
'
),
'
challenge
'
:
this
.
model
.
get
(
'
jid
'
),
'
placeholder
'
:
__
(
'
name@example.org
'
),
'
required
'
:
true
},
{
'
name
'
:
'
reason
'
,
'
label
'
:
__
(
'
Optional reason for destroying this groupchat
'
),
'
placeholder
'
:
__
(
'
Reason
'
)
},
{
'
name
'
:
'
newjid
'
,
'
label
'
:
__
(
'
Optional XMPP address for a new groupchat that replaces this one
'
),
'
placeholder
'
:
__
(
'
replacement@example.org
'
)
}
];
try
{
fields
=
await
api
.
confirm
(
__
(
'
Confirm
'
),
messages
,
fields
);
const
reason
=
fields
.
filter
(
f
=>
f
.
name
===
'
reason
'
).
pop
()?.
value
;
const
newjid
=
fields
.
filter
(
f
=>
f
.
name
===
'
newjid
'
).
pop
()?.
value
;
return
this
.
model
.
sendDestroyIQ
(
reason
,
newjid
).
then
(()
=>
this
.
close
())
return
this
.
model
.
sendDestroyIQ
(
reason
,
newjid
).
then
(()
=>
this
.
close
())
;
}
catch
(
e
)
{
log
.
error
(
e
);
}
},
parseMessageForCommands
(
text
)
{
if
(
api
.
settings
.
get
(
'
muc_disable_slash_commands
'
)
&&
!
Array
.
isArray
(
api
.
settings
.
get
(
'
muc_disable_slash_commands
'
)))
{
if
(
api
.
settings
.
get
(
'
muc_disable_slash_commands
'
)
&&
!
Array
.
isArray
(
api
.
settings
.
get
(
'
muc_disable_slash_commands
'
))
)
{
return
_converse
.
ChatBoxView
.
prototype
.
parseMessageForCommands
.
apply
(
this
,
arguments
);
}
text
=
text
.
replace
(
/^
\s
*/
,
""
);
text
=
text
.
replace
(
/^
\s
*/
,
''
);
const
command
=
(
text
.
match
(
/^
\/([
a-zA-Z
]
*
)
?
/
)
||
[
''
]).
pop
().
toLowerCase
();
if
(
!
command
)
{
return
false
;
}
const
args
=
text
.
slice
((
'
/
'
+
command
).
length
+
1
).
trim
();
const
args
=
text
.
slice
((
'
/
'
+
command
).
length
+
1
).
trim
();
if
(
!
this
.
getAllowedCommands
().
includes
(
command
))
{
return
false
;
}
...
...
@@ -1014,9 +1019,10 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
break
;
}
case
'
help
'
:
{
this
.
model
.
set
({
'
show_help_messages
'
:
true
});
this
.
model
.
set
({
'
show_help_messages
'
:
true
});
break
;
}
case
'
kick
'
:
{
}
case
'
kick
'
:
{
this
.
setRole
(
command
,
args
,
[],
[
'
moderator
'
]);
break
;
}
...
...
@@ -1034,15 +1040,16 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
else
if
(
args
.
length
===
0
)
{
// e.g. Your nickname is "coolguy69"
const
message
=
__
(
'
Your nickname is "%1$s"
'
,
this
.
model
.
get
(
'
nick
'
));
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
this
.
model
.
createMessage
({
message
,
'
type
'
:
'
error
'
});
}
else
{
const
jid
=
Strophe
.
getBareJidFromJid
(
this
.
model
.
get
(
'
jid
'
));
api
.
send
(
$pres
({
from
:
_converse
.
connection
.
jid
,
to
:
`
${
jid
}
/
${
args
}
`
,
id
:
u
.
getUniqueId
()
}).
tree
());
api
.
send
(
$pres
({
from
:
_converse
.
connection
.
jid
,
to
:
`
${
jid
}
/
${
args
}
`
,
id
:
u
.
getUniqueId
()
}).
tree
()
);
}
break
;
}
...
...
@@ -1061,7 +1068,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
});
}
else
{
this
.
model
.
registerNickname
().
then
(
err_msg
=>
{
err_msg
&&
this
.
model
.
createMessage
({
'
message
'
:
err_msg
,
'
type
'
:
'
error
'
});
err_msg
&&
this
.
model
.
createMessage
({
'
message
'
:
err_msg
,
'
type
'
:
'
error
'
});
});
}
break
;
...
...
@@ -1130,7 +1137,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
container
.
insertAdjacentElement
(
'
beforeend
'
,
form_el
);
}
}
u
.
safeSave
(
this
.
model
.
session
,
{
'
connection_status
'
:
converse
.
ROOMSTATUS
.
NICKNAME_REQUIRED
});
u
.
safeSave
(
this
.
model
.
session
,
{
'
connection_status
'
:
converse
.
ROOMSTATUS
.
NICKNAME_REQUIRED
});
},
/**
...
...
@@ -1160,7 +1167,8 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
getAndRenderConfigurationForm
()
{
if
(
!
this
.
config_form
||
!
u
.
isVisible
(
this
.
config_form
.
el
))
{
this
.
showSpinner
();
this
.
model
.
fetchRoomConfiguration
()
this
.
model
.
fetchRoomConfiguration
()
.
then
(
iq
=>
this
.
renderConfigurationForm
(
iq
))
.
catch
(
e
=>
log
.
error
(
e
));
}
else
{
...
...
@@ -1185,7 +1193,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
'
model
'
:
new
Model
({
'
validation_message
'
:
message
}),
'
chatroomview
'
:
this
,
'
chatroomview
'
:
this
});
const
container_el
=
this
.
el
.
querySelector
(
'
.chatroom-body
'
);
container_el
.
insertAdjacentElement
(
'
beforeend
'
,
this
.
password_form
.
el
);
...
...
@@ -1265,7 +1273,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
* @param {string} nick
*/
getPreviousJoinOrLeaveNotification
(
el
,
nick
)
{
const
today
=
(
new
Date
()
).
toISOString
().
split
(
'
T
'
)[
0
];
const
today
=
new
Date
(
).
toISOString
().
split
(
'
T
'
)[
0
];
while
(
el
!==
null
)
{
if
(
!
el
.
classList
.
contains
(
'
chat-info
'
))
{
return
;
...
...
@@ -1277,10 +1285,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
return
;
}
const
data
=
el
?.
dataset
||
{};
if
(
data
.
join
===
nick
||
data
.
leave
===
nick
||
data
.
leavejoin
===
nick
||
data
.
joinleave
===
nick
)
{
if
(
data
.
join
===
nick
||
data
.
leave
===
nick
||
data
.
leavejoin
===
nick
||
data
.
joinleave
===
nick
)
{
return
el
;
}
el
=
el
.
previousElementSibling
;
...
...
@@ -1295,7 +1300,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
* @method _converse.ChatRoomView#renderAfterTransition
*/
renderAfterTransition
()
{
const
conn_status
=
this
.
model
.
session
.
get
(
'
connection_status
'
)
const
conn_status
=
this
.
model
.
session
.
get
(
'
connection_status
'
)
;
if
(
conn_status
==
converse
.
ROOMSTATUS
.
NICKNAME_REQUIRED
)
{
this
.
renderNicknameForm
();
}
else
if
(
conn_status
==
converse
.
ROOMSTATUS
.
PASSWORD_REQUIRED
)
{
...
...
@@ -1312,10 +1317,7 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
sizzle
(
'
.spinner
'
,
this
.
el
).
forEach
(
u
.
removeElement
);
this
.
hideChatRoomContents
();
const
container_el
=
this
.
el
.
querySelector
(
'
.chatroom-body
'
);
container_el
.
insertAdjacentElement
(
'
afterbegin
'
,
u
.
getElementFromTemplateResult
(
tpl_spinner
())
);
container_el
.
insertAdjacentElement
(
'
afterbegin
'
,
u
.
getElementFromTemplateResult
(
tpl_spinner
()));
},
/**
...
...
@@ -1333,371 +1335,6 @@ export const ChatRoomView = _converse.ChatBoxView.extend({
}
return
this
;
}
});
/**
* View which renders MUC section of the control box.
* @class
* @namespace _converse.RoomsPanel
* @memberOf _converse
*/
export
const
RoomsPanel
=
View
.
extend
({
tagName
:
'
div
'
,
className
:
'
controlbox-section
'
,
id
:
'
chatrooms
'
,
events
:
{
'
click a.controlbox-heading__btn.show-add-muc-modal
'
:
'
showAddRoomModal
'
,
'
click a.controlbox-heading__btn.show-list-muc-modal
'
:
'
showMUCListModal
'
},
toHTML
()
{
return
tpl_room_panel
({
'
heading_chatrooms
'
:
__
(
'
Groupchats
'
),
'
title_new_room
'
:
__
(
'
Add a new groupchat
'
),
'
title_list_rooms
'
:
__
(
'
Query for groupchats
'
)
});
},
showAddRoomModal
(
ev
)
{
api
.
modal
.
show
(
AddMUCModal
,
{
'
model
'
:
this
.
model
},
ev
);
},
showMUCListModal
(
ev
)
{
api
.
modal
.
show
(
MUCListModal
,
{
'
model
'
:
this
.
model
},
ev
);
}
});
converse
.
plugins
.
add
(
'
converse-muc-views
'
,
{
/* Dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin. They are "optional" because they might not be
* available, in which case any overrides applicable to them will be
* ignored.
*
* NB: These plugins need to have already been loaded via require.js.
*
* It's possible to make these dependencies "non-optional".
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*/
dependencies
:
[
"
converse-autocomplete
"
,
"
converse-modal
"
,
"
converse-controlbox
"
,
"
converse-chatview
"
],
overrides
:
{
ControlBoxView
:
{
renderControlBoxPane
()
{
this
.
__super__
.
renderControlBoxPane
.
apply
(
this
,
arguments
);
if
(
api
.
settings
.
get
(
'
allow_muc
'
))
{
this
.
renderRoomsPanel
();
}
}
}
},
initialize
()
{
const
{
_converse
}
=
this
;
api
.
promises
.
add
([
'
roomsPanelRendered
'
]);
// Configuration values for this plugin
// ====================================
// Refer to docs/source/configuration.rst for explanations of these
// configuration settings.
api
.
settings
.
extend
({
'
auto_list_rooms
'
:
false
,
'
cache_muc_messages
'
:
true
,
'
locked_muc_nickname
'
:
false
,
'
modtools_disable_query
'
:
[],
'
modtools_disable_assign
'
:
false
,
'
muc_disable_slash_commands
'
:
false
,
'
muc_mention_autocomplete_filter
'
:
'
contains
'
,
'
muc_mention_autocomplete_min_chars
'
:
0
,
'
muc_mention_autocomplete_show_avatar
'
:
true
,
'
muc_roomid_policy
'
:
null
,
'
muc_roomid_policy_hint
'
:
null
,
'
roomconfig_whitelist
'
:
[],
'
show_retraction_warning
'
:
true
,
'
visible_toolbar_buttons
'
:
{
'
toggle_occupants
'
:
true
}
});
_converse
.
ChatRoomView
=
ChatRoomView
;
_converse
.
RoomsPanel
=
RoomsPanel
;
const
viewWithRoomsPanel
=
{
renderRoomsPanel
()
{
if
(
this
.
roomspanel
&&
u
.
isInDOM
(
this
.
roomspanel
.
el
))
{
return
this
.
roomspanel
;
}
const
id
=
`converse.roomspanel
${
_converse
.
bare_jid
}
`
;
this
.
roomspanel
=
new
_converse
.
RoomsPanel
({
'
model
'
:
new
(
_converse
.
RoomsPanelModel
.
extend
({
id
,
'
browserStorage
'
:
_converse
.
createStore
(
id
)
}))()
});
this
.
roomspanel
.
model
.
fetch
();
this
.
el
.
querySelector
(
'
.controlbox-pane
'
).
insertAdjacentElement
(
'
beforeEnd
'
,
this
.
roomspanel
.
render
().
el
);
/**
* Triggered once the section of the { @link _converse.ControlBoxView }
* which shows gropuchats has been rendered.
* @event _converse#roomsPanelRendered
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
*/
api
.
trigger
(
'
roomsPanelRendered
'
);
return
this
.
roomspanel
;
},
getRoomsPanel
()
{
if
(
this
.
roomspanel
&&
u
.
isInDOM
(
this
.
roomspanel
.
el
))
{
return
this
.
roomspanel
;
}
else
{
return
this
.
renderRoomsPanel
();
}
}
}
};
if
(
_converse
.
ControlBoxView
)
{
Object
.
assign
(
_converse
.
ControlBoxView
.
prototype
,
viewWithRoomsPanel
);
}
_converse
.
MUCConfigForm
=
View
.
extend
({
className
:
'
chatroom-form-container muc-config-form
'
,
initialize
(
attrs
)
{
this
.
chatroomview
=
attrs
.
chatroomview
;
this
.
listenTo
(
this
.
chatroomview
.
model
.
features
,
'
change:passwordprotected
'
,
this
.
render
);
this
.
listenTo
(
this
.
chatroomview
.
model
.
features
,
'
change:config_stanza
'
,
this
.
render
);
this
.
render
();
},
toHTML
()
{
const
stanza
=
u
.
toStanza
(
this
.
model
.
get
(
'
config_stanza
'
));
const
whitelist
=
api
.
settings
.
get
(
'
roomconfig_whitelist
'
);
let
fields
=
sizzle
(
'
field
'
,
stanza
);
if
(
whitelist
.
length
)
{
fields
=
fields
.
filter
(
f
=>
whitelist
.
includes
(
f
.
getAttribute
(
'
var
'
)));
}
const
password_protected
=
this
.
model
.
features
.
get
(
'
passwordprotected
'
);
const
options
=
{
'
new_password
'
:
!
password_protected
,
'
fixed_username
'
:
this
.
model
.
get
(
'
jid
'
)
};
return
tpl_muc_config_form
({
'
closeConfigForm
'
:
ev
=>
this
.
closeConfigForm
(
ev
),
'
fields
'
:
fields
.
map
(
f
=>
u
.
xForm2webForm
(
f
,
stanza
,
options
)),
'
instructions
'
:
stanza
.
querySelector
(
'
instructions
'
)?.
textContent
,
'
submitConfigForm
'
:
ev
=>
this
.
submitConfigForm
(
ev
),
'
title
'
:
stanza
.
querySelector
(
'
title
'
)?.
textContent
});
},
async
submitConfigForm
(
ev
)
{
ev
.
preventDefault
();
const
inputs
=
sizzle
(
'
:input:not([type=button]):not([type=submit])
'
,
ev
.
target
);
const
config_array
=
inputs
.
map
(
u
.
webForm2xForm
).
filter
(
f
=>
f
);
try
{
await
this
.
model
.
sendConfiguration
(
config_array
);
}
catch
(
e
)
{
log
.
error
(
e
);
const
message
=
__
(
"
Sorry, an error occurred while trying to submit the config form.
"
)
+
"
"
+
__
(
"
Check your browser's developer console for details.
"
);
api
.
alert
(
'
error
'
,
__
(
'
Error
'
),
message
);
}
await
this
.
model
.
refreshDiscoInfo
();
this
.
chatroomview
.
closeForm
();
},
closeConfigForm
(
ev
)
{
ev
.
preventDefault
();
this
.
chatroomview
.
closeForm
();
}
});
_converse
.
MUCPasswordForm
=
View
.
extend
({
className
:
'
chatroom-form-container muc-password-form
'
,
initialize
(
attrs
)
{
this
.
chatroomview
=
attrs
.
chatroomview
;
this
.
listenTo
(
this
.
model
,
'
change:validation_message
'
,
this
.
render
);
this
.
render
();
},
toHTML
()
{
return
tpl_muc_password_form
({
'
jid
'
:
this
.
model
.
get
(
'
jid
'
),
'
submitPassword
'
:
ev
=>
this
.
submitPassword
(
ev
),
'
validation_message
'
:
this
.
model
.
get
(
'
validation_message
'
)
});
},
submitPassword
(
ev
)
{
ev
.
preventDefault
();
const
password
=
this
.
el
.
querySelector
(
'
input[type=password]
'
).
value
;
this
.
chatroomview
.
model
.
join
(
this
.
chatroomview
.
model
.
get
(
'
nick
'
),
password
);
this
.
model
.
set
(
'
validation_message
'
,
null
);
}
});
function
setMUCDomain
(
domain
,
controlboxview
)
{
controlboxview
.
getRoomsPanel
().
model
.
save
(
'
muc_domain
'
,
Strophe
.
getDomainFromJid
(
domain
));
}
function
setMUCDomainFromDisco
(
controlboxview
)
{
/* Check whether service discovery for the user's domain
* returned MUC information and use that to automatically
* set the MUC domain in the "Add groupchat" modal.
*/
function
featureAdded
(
feature
)
{
if
(
!
feature
)
{
return
;
}
if
(
feature
.
get
(
'
var
'
)
===
Strophe
.
NS
.
MUC
)
{
feature
.
entity
.
getIdentity
(
'
conference
'
,
'
text
'
).
then
(
identity
=>
{
if
(
identity
)
{
setMUCDomain
(
feature
.
get
(
'
from
'
),
controlboxview
);
}
});
}
}
api
.
waitUntil
(
'
discoInitialized
'
).
then
(()
=>
{
api
.
listen
.
on
(
'
serviceDiscovered
'
,
featureAdded
);
// Features could have been added before the controlbox was
// initialized. We're only interested in MUC
_converse
.
disco_entities
.
each
(
entity
=>
featureAdded
(
entity
.
features
.
findWhere
({
'
var
'
:
Strophe
.
NS
.
MUC
})));
}).
catch
(
e
=>
log
.
error
(
e
));
}
function
fetchAndSetMUCDomain
(
controlboxview
)
{
if
(
controlboxview
.
model
.
get
(
'
connected
'
))
{
if
(
!
controlboxview
.
getRoomsPanel
().
model
.
get
(
'
muc_domain
'
))
{
if
(
api
.
settings
.
get
(
'
muc_domain
'
)
===
undefined
)
{
setMUCDomainFromDisco
(
controlboxview
);
}
else
{
setMUCDomain
(
api
.
settings
.
get
(
'
muc_domain
'
),
controlboxview
);
}
}
}
}
/************************ BEGIN Event Handlers ************************/
api
.
listen
.
on
(
'
chatBoxViewsInitialized
'
,
()
=>
{
function
openChatRoomFromURIClicked
(
ev
)
{
ev
.
preventDefault
();
api
.
rooms
.
open
(
ev
.
target
.
href
);
}
_converse
.
chatboxviews
.
delegate
(
'
click
'
,
'
a.open-chatroom
'
,
openChatRoomFromURIClicked
);
async
function
addView
(
model
)
{
const
views
=
_converse
.
chatboxviews
;
if
(
!
views
.
get
(
model
.
get
(
'
id
'
))
&&
model
.
get
(
'
type
'
)
===
_converse
.
CHATROOMS_TYPE
&&
model
.
isValid
()
)
{
await
model
.
initialized
;
return
views
.
add
(
model
.
get
(
'
id
'
),
new
_converse
.
ChatRoomView
({
model
}));
}
}
_converse
.
chatboxes
.
on
(
'
add
'
,
addView
);
});
api
.
listen
.
on
(
'
clearSession
'
,
()
=>
{
const
view
=
_converse
.
chatboxviews
.
get
(
'
controlbox
'
);
if
(
view
&&
view
.
roomspanel
)
{
view
.
roomspanel
.
model
.
destroy
();
view
.
roomspanel
.
remove
();
delete
view
.
roomspanel
;
}
});
api
.
listen
.
on
(
'
controlBoxInitialized
'
,
(
view
)
=>
{
if
(
!
api
.
settings
.
get
(
'
allow_muc
'
))
{
return
;
}
fetchAndSetMUCDomain
(
view
);
view
.
model
.
on
(
'
change:connected
'
,
()
=>
fetchAndSetMUCDomain
(
view
));
});
/************************ END Event Handlers ************************/
/************************ BEGIN API ************************/
Object
.
assign
(
_converse
.
api
,
{
/**
* The "roomviews" namespace groups methods relevant to chatroom
* (aka groupchats) views.
*
* @namespace _converse.api.roomviews
* @memberOf _converse.api
*/
roomviews
:
{
/**
* Retrieves a groupchat (aka chatroom) view. The chat should already be open.
*
* @method _converse.api.roomviews.get
* @param {String|string[]} name - e.g. 'coven@conference.shakespeare.lit' or
* ['coven@conference.shakespeare.lit', 'cave@conference.shakespeare.lit']
* @returns {View} View representing the groupchat
*
* @example
* // To return a single view, provide the JID of the groupchat
* const view = _converse.api.roomviews.get('coven@conference.shakespeare.lit');
*
* @example
* // To return an array of views, provide an array of JIDs:
* const views = _converse.api.roomviews.get(['coven@conference.shakespeare.lit', 'cave@conference.shakespeare.lit']);
*
* @example
* // To return views of all open groupchats, call the method without any parameters::
* const views = _converse.api.roomviews.get();
*
*/
get
(
jids
)
{
if
(
Array
.
isArray
(
jids
))
{
const
views
=
api
.
chatviews
.
get
(
jids
);
return
views
.
filter
(
v
=>
v
.
model
.
get
(
'
type
'
)
===
_converse
.
CHATROOMS_TYPE
)
}
else
{
const
view
=
api
.
chatviews
.
get
(
jids
);
if
(
view
.
model
.
get
(
'
type
'
)
===
_converse
.
CHATROOMS_TYPE
)
{
return
view
;
}
else
{
return
null
;
}
}
},
/**
* Lets you close open chatrooms.
*
* You can call this method without any arguments to close
* all open chatrooms, or you can specify a single JID or
* an array of JIDs.
*
* @method _converse.api.roomviews.close
* @param {(String[]|String)} jids The JID or array of JIDs of the chatroom(s)
* @returns { Promise } - Promise which resolves once the views have been closed.
*/
close
(
jids
)
{
let
views
;
if
(
jids
===
undefined
)
{
views
=
_converse
.
chatboxviews
;
}
else
if
(
typeof
jids
===
'
string
'
)
{
views
=
[
_converse
.
chatboxviews
.
get
(
jids
)].
filter
(
v
=>
v
);
}
else
if
(
Array
.
isArray
(
jids
))
{
views
=
jids
.
map
(
jid
=>
_converse
.
chatboxviews
.
get
(
jid
));
}
return
Promise
.
all
(
views
.
map
(
v
=>
(
v
.
is_chatroom
&&
v
.
model
&&
v
.
close
())))
}
}
});
}
});
export
default
ChatRoomViewMixin
;
src/plugins/muc-views/password-form.js
0 → 100644
View file @
e8536ebc
import
tpl_muc_password_form
from
"
templates/muc_password_form.js
"
;
import
{
View
}
from
'
@converse/skeletor/src/view.js
'
;
const
MUCPasswordForm
=
View
.
extend
({
className
:
'
chatroom-form-container muc-password-form
'
,
initialize
(
attrs
)
{
this
.
chatroomview
=
attrs
.
chatroomview
;
this
.
listenTo
(
this
.
model
,
'
change:validation_message
'
,
this
.
render
);
this
.
render
();
},
toHTML
()
{
return
tpl_muc_password_form
({
'
jid
'
:
this
.
model
.
get
(
'
jid
'
),
'
submitPassword
'
:
ev
=>
this
.
submitPassword
(
ev
),
'
validation_message
'
:
this
.
model
.
get
(
'
validation_message
'
)
});
},
submitPassword
(
ev
)
{
ev
.
preventDefault
();
const
password
=
this
.
el
.
querySelector
(
'
input[type=password]
'
).
value
;
this
.
chatroomview
.
model
.
join
(
this
.
chatroomview
.
model
.
get
(
'
nick
'
),
password
);
this
.
model
.
set
(
'
validation_message
'
,
null
);
}
});
export
default
MUCPasswordForm
;
src/plugins/muc-views/rooms-panel.js
0 → 100644
View file @
e8536ebc
import
AddMUCModal
from
'
modals/add-muc.js
'
;
import
tpl_room_panel
from
'
templates/room_panel.js
'
;
import
{
View
}
from
'
@converse/skeletor/src/view.js
'
;
import
MUCListModal
from
'
modals/muc-list.js
'
;
import
{
_converse
,
api
,
converse
}
from
'
@converse/headless/core
'
;
import
{
__
}
from
'
i18n
'
;
const
u
=
converse
.
env
.
utils
;
/**
* View which renders MUC section of the control box.
* @class
* @namespace _converse.RoomsPanel
* @memberOf _converse
*/
export
const
RoomsPanel
=
View
.
extend
({
tagName
:
'
div
'
,
className
:
'
controlbox-section
'
,
id
:
'
chatrooms
'
,
events
:
{
'
click a.controlbox-heading__btn.show-add-muc-modal
'
:
'
showAddRoomModal
'
,
'
click a.controlbox-heading__btn.show-list-muc-modal
'
:
'
showMUCListModal
'
},
toHTML
()
{
return
tpl_room_panel
({
'
heading_chatrooms
'
:
__
(
'
Groupchats
'
),
'
title_new_room
'
:
__
(
'
Add a new groupchat
'
),
'
title_list_rooms
'
:
__
(
'
Query for groupchats
'
)
});
},
showAddRoomModal
(
ev
)
{
api
.
modal
.
show
(
AddMUCModal
,
{
'
model
'
:
this
.
model
},
ev
);
},
showMUCListModal
(
ev
)
{
api
.
modal
.
show
(
MUCListModal
,
{
'
model
'
:
this
.
model
},
ev
);
}
});
/**
* Mixin which adds the ability to a ControlBox to render a list of open groupchats
* @mixin
*/
export
const
RoomsPanelViewMixin
=
{
renderRoomsPanel
()
{
if
(
this
.
roomspanel
&&
u
.
isInDOM
(
this
.
roomspanel
.
el
))
{
return
this
.
roomspanel
;
}
const
id
=
`converse.roomspanel
${
_converse
.
bare_jid
}
`
;
this
.
roomspanel
=
new
_converse
.
RoomsPanel
({
'
model
'
:
new
(
_converse
.
RoomsPanelModel
.
extend
({
id
,
'
browserStorage
'
:
_converse
.
createStore
(
id
)
}))()
});
this
.
roomspanel
.
model
.
fetch
();
this
.
el
.
querySelector
(
'
.controlbox-pane
'
).
insertAdjacentElement
(
'
beforeEnd
'
,
this
.
roomspanel
.
render
().
el
);
/**
* Triggered once the section of the { @link _converse.ControlBoxView }
* which shows gropuchats has been rendered.
* @event _converse#roomsPanelRendered
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
*/
api
.
trigger
(
'
roomsPanelRendered
'
);
return
this
.
roomspanel
;
},
getRoomsPanel
()
{
if
(
this
.
roomspanel
&&
u
.
isInDOM
(
this
.
roomspanel
.
el
))
{
return
this
.
roomspanel
;
}
else
{
return
this
.
renderRoomsPanel
();
}
}
};
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