Move MUC invite widget into a modal
This commit is contained in:
parent
c6ac03e94e
commit
9fb2056753
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -16094,8 +16094,8 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"skeletor.js": {
|
"skeletor.js": {
|
||||||
"version": "github:skeletorjs/skeletor#29a6d8f707076e865133b8f36f07c76ba4b4b582",
|
"version": "github:skeletorjs/skeletor#9a4487496bd2810b2f0847acbca136333cf9cfb0",
|
||||||
"from": "github:skeletorjs/skeletor#29a6d8f707076e865133b8f36f07c76ba4b4b582",
|
"from": "github:skeletorjs/skeletor#9a4487496bd2810b2f0847acbca136333cf9cfb0",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,12 +172,25 @@
|
||||||
.hide-occupants {
|
.hide-occupants {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.occupants-header--title {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.fa-user-plus {
|
||||||
|
margin-top: 0.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.occupants-heading {
|
.occupants-heading {
|
||||||
font-family: var(--heading-font);
|
font-family: var(--heading-font);
|
||||||
margin-bottom: 0.5em;
|
color: var(--chatroom-head-color);
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
.chatroom-features {
|
.chatroom-features {
|
||||||
display: var(--occupants-features-display);
|
display: var(--occupants-features-display);
|
||||||
|
|
38
spec/muc.js
38
spec/muc.js
|
@ -1940,36 +1940,54 @@
|
||||||
'muc_anonymous'
|
'muc_anonymous'
|
||||||
]
|
]
|
||||||
await test_utils.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
await test_utils.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
||||||
spyOn(_converse.api, "trigger").and.callThrough();
|
|
||||||
spyOn(window, 'prompt').and.callFake(() => "Please join!");
|
|
||||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||||
|
|
||||||
expect(view.model.getOwnAffiliation()).toBe('owner');
|
expect(view.model.getOwnAffiliation()).toBe('owner');
|
||||||
expect(view.model.features.get('open')).toBe(false);
|
expect(view.model.features.get('open')).toBe(false);
|
||||||
expect(view.el.querySelectorAll('input.invited-contact').length).toBe(1);
|
|
||||||
|
|
||||||
|
expect(view.el.querySelector('.occupants-header .fa-user-plus')).not.toBe(null);
|
||||||
|
|
||||||
|
// Members can't invite if the room isn't open
|
||||||
view.model.getOwnOccupant().set('affiliation', 'member');
|
view.model.getOwnOccupant().set('affiliation', 'member');
|
||||||
await u.waitUntil(() => view.el.querySelectorAll('input.invited-contact').length === 0);
|
await u.waitUntil(() => view.el.querySelector('.occupants-header .fa-user-plus') === null);
|
||||||
|
|
||||||
view.model.features.set('open', 'true');
|
view.model.features.set('open', 'true');
|
||||||
|
await u.waitUntil(() => view.el.querySelector('.occupants-header .fa-user-plus'));
|
||||||
|
|
||||||
|
view.el.querySelector('.occupants-header .fa-user-plus').click();
|
||||||
|
const modal = view.sidebar_view.muc_invite_modal;
|
||||||
|
await u.waitUntil(() => u.isVisible(modal.el), 1000)
|
||||||
|
|
||||||
|
expect(modal.el.querySelectorAll('#invitee_jids').length).toBe(1);
|
||||||
|
expect(modal.el.querySelectorAll('textarea').length).toBe(1);
|
||||||
|
|
||||||
spyOn(view.model, 'directInvite').and.callThrough();
|
spyOn(view.model, 'directInvite').and.callThrough();
|
||||||
await u.waitUntil(() => view.el.querySelectorAll('input.invited-contact').length);
|
|
||||||
const input = view.el.querySelector('input.invited-contact');
|
const input = modal.el.querySelector('#invitee_jids');
|
||||||
expect(input.getAttribute('placeholder')).toBe('Invite');
|
|
||||||
input.value = "Balt";
|
input.value = "Balt";
|
||||||
|
modal.el.querySelector('button[type="submit"]').click();
|
||||||
|
|
||||||
|
await u.waitUntil(() => modal.el.querySelector('.error'));
|
||||||
|
|
||||||
|
const error = modal.el.querySelector('.error');
|
||||||
|
expect(error.textContent).toBe('Please enter a valid XMPP address');
|
||||||
|
|
||||||
let evt = new Event('input');
|
let evt = new Event('input');
|
||||||
input.dispatchEvent(evt);
|
input.dispatchEvent(evt);
|
||||||
|
|
||||||
let sent_stanza;
|
let sent_stanza;
|
||||||
spyOn(_converse.connection, 'send').and.callFake(stanza => (sent_stanza = stanza));
|
spyOn(_converse.connection, 'send').and.callFake(stanza => (sent_stanza = stanza));
|
||||||
const hint = await u.waitUntil(() => view.el.querySelector('.suggestion-box__results li'));
|
const hint = await u.waitUntil(() => modal.el.querySelector('.suggestion-box__results li'));
|
||||||
expect(input.value).toBe('Balt');
|
expect(input.value).toBe('Balt');
|
||||||
expect(hint.textContent.trim()).toBe('Balthasar');
|
expect(hint.textContent.trim()).toBe('Balthasar');
|
||||||
|
|
||||||
evt = new Event('mousedown', {'bubbles': true});
|
evt = new Event('mousedown', {'bubbles': true});
|
||||||
evt.button = 0;
|
evt.button = 0;
|
||||||
hint.dispatchEvent(evt);
|
hint.dispatchEvent(evt);
|
||||||
expect(window.prompt).toHaveBeenCalled();
|
|
||||||
|
const textarea = modal.el.querySelector('textarea');
|
||||||
|
textarea.value = "Please join!";
|
||||||
|
modal.el.querySelector('button[type="submit"]').click();
|
||||||
|
|
||||||
expect(view.model.directInvite).toHaveBeenCalled();
|
expect(view.model.directInvite).toHaveBeenCalled();
|
||||||
expect(sent_stanza.toLocaleString()).toBe(
|
expect(sent_stanza.toLocaleString()).toBe(
|
||||||
`<message from="romeo@montague.lit/orchard" `+
|
`<message from="romeo@montague.lit/orchard" `+
|
||||||
|
|
|
@ -23,7 +23,7 @@ import tpl_chatroom_details_modal from "templates/chatroom_details_modal.js";
|
||||||
import tpl_chatroom_disconnect from "templates/chatroom_disconnect.html";
|
import tpl_chatroom_disconnect from "templates/chatroom_disconnect.html";
|
||||||
import tpl_muc_config_form from "templates/muc_config_form.js";
|
import tpl_muc_config_form from "templates/muc_config_form.js";
|
||||||
import tpl_chatroom_head from "templates/chatroom_head.html";
|
import tpl_chatroom_head from "templates/chatroom_head.html";
|
||||||
import tpl_chatroom_invite from "templates/chatroom_invite.html";
|
import tpl_muc_invite_modal from "templates/muc_invite_modal.js";
|
||||||
import tpl_chatroom_nickname_form from "templates/chatroom_nickname_form.html";
|
import tpl_chatroom_nickname_form from "templates/chatroom_nickname_form.html";
|
||||||
import tpl_muc_password_form from "templates/muc_password_form.js";
|
import tpl_muc_password_form from "templates/muc_password_form.js";
|
||||||
import tpl_muc_sidebar from "templates/muc_sidebar.js";
|
import tpl_muc_sidebar from "templates/muc_sidebar.js";
|
||||||
|
@ -701,7 +701,7 @@ converse.plugins.add('converse-muc-views', {
|
||||||
this.onMouseUp = this.onMouseUp.bind(this);
|
this.onMouseUp = this.onMouseUp.bind(this);
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
this.createOccupantsView();
|
this.createSidebarView();
|
||||||
await this.updateAfterMessagesFetched();
|
await this.updateAfterMessagesFetched();
|
||||||
this.onConnectionStatusChanged();
|
this.onConnectionStatusChanged();
|
||||||
/**
|
/**
|
||||||
|
@ -770,15 +770,15 @@ converse.plugins.add('converse-muc-views', {
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
createOccupantsView () {
|
createSidebarView () {
|
||||||
this.model.occupants.chatroomview = this;
|
this.model.occupants.chatroomview = this;
|
||||||
this.occupants_view = new _converse.ChatRoomOccupantsView({'model': this.model.occupants});
|
this.sidebar_view = new _converse.MUCSidebar({'model': this.model.occupants});
|
||||||
const container_el = this.el.querySelector('.chatroom-body');
|
const container_el = this.el.querySelector('.chatroom-body');
|
||||||
const occupants_width = this.model.get('occupants_width');
|
const occupants_width = this.model.get('occupants_width');
|
||||||
if (this.occupants_view && occupants_width !== undefined) {
|
if (this.sidebar_view && occupants_width !== undefined) {
|
||||||
this.occupants_view.el.style.flex = "0 0 " + occupants_width + "px";
|
this.sidebar_view.el.style.flex = "0 0 " + occupants_width + "px";
|
||||||
}
|
}
|
||||||
container_el.insertAdjacentElement('beforeend', this.occupants_view.el);
|
container_el.insertAdjacentElement('beforeend', this.sidebar_view.el);
|
||||||
},
|
},
|
||||||
|
|
||||||
onStartResizeOccupants (ev) {
|
onStartResizeOccupants (ev) {
|
||||||
|
@ -786,7 +786,7 @@ converse.plugins.add('converse-muc-views', {
|
||||||
this.el.addEventListener('mousemove', this.onMouseMove);
|
this.el.addEventListener('mousemove', this.onMouseMove);
|
||||||
this.el.addEventListener('mouseup', this.onMouseUp);
|
this.el.addEventListener('mouseup', this.onMouseUp);
|
||||||
|
|
||||||
const style = window.getComputedStyle(this.occupants_view.el);
|
const style = window.getComputedStyle(this.sidebar_view.el);
|
||||||
this.width = parseInt(style.width.replace(/px$/, ''), 10);
|
this.width = parseInt(style.width.replace(/px$/, ''), 10);
|
||||||
this.prev_pageX = ev.pageX;
|
this.prev_pageX = ev.pageX;
|
||||||
},
|
},
|
||||||
|
@ -795,7 +795,7 @@ converse.plugins.add('converse-muc-views', {
|
||||||
if (this.resizing) {
|
if (this.resizing) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const delta = this.prev_pageX - ev.pageX;
|
const delta = this.prev_pageX - ev.pageX;
|
||||||
this.resizeOccupantsView(delta, ev.pageX);
|
this.resizeSidebarView(delta, ev.pageX);
|
||||||
this.prev_pageX = ev.pageX;
|
this.prev_pageX = ev.pageX;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -806,26 +806,26 @@ converse.plugins.add('converse-muc-views', {
|
||||||
this.resizing = false;
|
this.resizing = false;
|
||||||
this.el.removeEventListener('mousemove', this.onMouseMove);
|
this.el.removeEventListener('mousemove', this.onMouseMove);
|
||||||
this.el.removeEventListener('mouseup', this.onMouseUp);
|
this.el.removeEventListener('mouseup', this.onMouseUp);
|
||||||
const element_position = this.occupants_view.el.getBoundingClientRect();
|
const element_position = this.sidebar_view.el.getBoundingClientRect();
|
||||||
const occupants_width = this.calculateOccupantsWidth(element_position, 0);
|
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);
|
_converse.connection.connected ? this.model.save(attrs) : this.model.set(attrs);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
resizeOccupantsView (delta, current_mouse_position) {
|
resizeSidebarView (delta, current_mouse_position) {
|
||||||
const element_position = this.occupants_view.el.getBoundingClientRect();
|
const element_position = this.sidebar_view.el.getBoundingClientRect();
|
||||||
if (this.is_minimum) {
|
if (this.is_minimum) {
|
||||||
this.is_minimum = element_position.left < current_mouse_position;
|
this.is_minimum = element_position.left < current_mouse_position;
|
||||||
} else if (this.is_maximum) {
|
} else if (this.is_maximum) {
|
||||||
this.is_maximum = element_position.left > current_mouse_position;
|
this.is_maximum = element_position.left > current_mouse_position;
|
||||||
} else {
|
} else {
|
||||||
const occupants_width = this.calculateOccupantsWidth(element_position, delta);
|
const occupants_width = this.calculateSidebarWidth(element_position, delta);
|
||||||
this.occupants_view.el.style.flex = "0 0 " + occupants_width + "px";
|
this.sidebar_view.el.style.flex = "0 0 " + occupants_width + "px";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
calculateOccupantsWidth(element_position, delta) {
|
calculateSidebarWidth(element_position, delta) {
|
||||||
let occupants_width = element_position.width + delta;
|
let occupants_width = element_position.width + delta;
|
||||||
const room_width = this.el.clientWidth;
|
const room_width = this.el.clientWidth;
|
||||||
// keeping display in boundaries
|
// keeping display in boundaries
|
||||||
|
@ -2120,19 +2120,62 @@ converse.plugins.add('converse-muc-views', {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_converse.ChatRoomOccupantsView = HTMLView.extend({
|
_converse.MUCInviteModal = _converse.BootstrapModal.extend({
|
||||||
|
id: "muc-invite-modal",
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||||
|
this.listenTo(this.model, 'change', this.render);
|
||||||
|
this.initInviteWidget();
|
||||||
|
},
|
||||||
|
|
||||||
|
toHTML () {
|
||||||
|
return tpl_muc_invite_modal(Object.assign(
|
||||||
|
this.model.toJSON(), {
|
||||||
|
'submitInviteForm': ev => this.submitInviteForm(ev)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
initInviteWidget () {
|
||||||
|
if (this.invite_auto_complete) {
|
||||||
|
this.invite_auto_complete.destroy();
|
||||||
|
}
|
||||||
|
const list = _converse.roster.map(i => ({'label': i.getDisplayName(), 'value': i.get('jid')}));
|
||||||
|
const el = this.el.querySelector('.suggestion-box').parentElement;
|
||||||
|
this.invite_auto_complete = new _converse.AutoComplete(el, {
|
||||||
|
'min_chars': 1,
|
||||||
|
'list': list
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
submitInviteForm (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
// TODO: Add support for sending an invite to multiple JIDs
|
||||||
|
const data = new FormData(ev.target);
|
||||||
|
const jid = data.get('invitee_jids');
|
||||||
|
const reason = data.get('reason');
|
||||||
|
if (u.isValidJID(jid)) {
|
||||||
|
// TODO: Create and use API here
|
||||||
|
this.chatroomview.model.directInvite(jid, reason);
|
||||||
|
this.modal.hide();
|
||||||
|
} else {
|
||||||
|
this.model.set({'invalid_invite_jid': true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
_converse.MUCSidebar = HTMLView.extend({
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
className: 'occupants col-md-3 col-4',
|
className: 'occupants col-md-3 col-4',
|
||||||
|
|
||||||
async initialize () {
|
async initialize () {
|
||||||
this.chatroomview = this.model.chatroomview;
|
this.chatroomview = this.model.chatroomview;
|
||||||
this.listenTo(this.model, 'add', this.maybeRenderInviteWidget);
|
|
||||||
this.listenTo(this.model, 'add', this.render);
|
this.listenTo(this.model, 'add', this.render);
|
||||||
this.listenTo(this.model, 'remove', this.render);
|
this.listenTo(this.model, 'remove', this.render);
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(this.model, 'change', this.render);
|
||||||
this.listenTo(this.model, 'change:affiliation', this.maybeRenderInviteWidget);
|
|
||||||
this.listenTo(this.chatroomview.model.features, 'change', this.render);
|
this.listenTo(this.chatroomview.model.features, 'change', this.render);
|
||||||
this.listenTo(this.chatroomview.model.features, 'change:open', this.renderInviteWidget);
|
|
||||||
this.listenTo(this.chatroomview.model, 'change:hidden_occupants', this.setVisibility);
|
this.listenTo(this.chatroomview.model, 'change:hidden_occupants', this.setVisibility);
|
||||||
this.render();
|
this.render();
|
||||||
await this.model.fetched;
|
await this.model.fetched;
|
||||||
|
@ -2141,21 +2184,16 @@ converse.plugins.add('converse-muc-views', {
|
||||||
toHTML () {
|
toHTML () {
|
||||||
return tpl_muc_sidebar(
|
return tpl_muc_sidebar(
|
||||||
Object.assign(this.chatroomview.model.toJSON(), {
|
Object.assign(this.chatroomview.model.toJSON(), {
|
||||||
'allow_muc_invitations': _converse.allow_muc_invitations,
|
|
||||||
'features': this.chatroomview.model.features,
|
'features': this.chatroomview.model.features,
|
||||||
'label_occupants': __('Participants'),
|
'occupants': this.model.models,
|
||||||
'occupants': this.model.models
|
'invitesAllowed': () => this.invitesAllowed(),
|
||||||
|
'showInviteModal': ev => this.showInviteModal(ev)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
afterRender () {
|
afterRender () {
|
||||||
if (_converse.allow_muc_invitations) {
|
|
||||||
// TODO: the invite widget needs to be rendered via a directive
|
|
||||||
_converse.api.waitUntil('rosterContactsFetched').then(() => this.renderInviteWidget());
|
|
||||||
}
|
|
||||||
this.setVisibility();
|
this.setVisibility();
|
||||||
this.setOccupantsHeight();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setVisibility () {
|
setVisibility () {
|
||||||
|
@ -2167,34 +2205,18 @@ converse.plugins.add('converse-muc-views', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
maybeRenderInviteWidget (occupant) {
|
showInviteModal (ev) {
|
||||||
if (occupant.get('jid') === _converse.bare_jid) {
|
ev.preventDefault();
|
||||||
this.renderInviteWidget();
|
if (this.muc_invite_modal === undefined) {
|
||||||
|
this.muc_invite_modal = new _converse.MUCInviteModal({'model': new Model()});
|
||||||
|
// TODO: remove once we have API for sending direct invite
|
||||||
|
this.muc_invite_modal.chatroomview = this.chatroomview;
|
||||||
}
|
}
|
||||||
},
|
this.muc_invite_modal.show(ev);
|
||||||
|
|
||||||
renderInviteWidget () {
|
|
||||||
// TODO: this needs to be rendered inside muc_sidebar.js
|
|
||||||
const widget = this.el.querySelector('.room-invite');
|
|
||||||
if (this.shouldInviteWidgetBeShown()) {
|
|
||||||
if (widget === null) {
|
|
||||||
const heading = this.el.querySelector('.occupants-heading');
|
|
||||||
heading.insertAdjacentHTML(
|
|
||||||
'afterend',
|
|
||||||
tpl_chatroom_invite({
|
|
||||||
'error_message': null,
|
|
||||||
'label_invitation': __('Invite'),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
this.initInviteWidget();
|
|
||||||
}
|
|
||||||
} else if (widget !== null) {
|
|
||||||
widget.remove();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setOccupantsHeight () {
|
setOccupantsHeight () {
|
||||||
|
// TODO: remove the features section in sidebar and then this as well
|
||||||
const el = this.el.querySelector('.chatroom-features');
|
const el = this.el.querySelector('.chatroom-features');
|
||||||
if (el) {
|
if (el) {
|
||||||
this.el.querySelector('.occupant-list').style.cssText =
|
this.el.querySelector('.occupant-list').style.cssText =
|
||||||
|
@ -2202,74 +2224,12 @@ converse.plugins.add('converse-muc-views', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
promptForInvite (suggestion) {
|
invitesAllowed () {
|
||||||
let reason = '';
|
|
||||||
if (!_converse.auto_join_on_invite) {
|
|
||||||
reason = prompt(
|
|
||||||
__('You are about to invite %1$s to the groupchat "%2$s". '+
|
|
||||||
'You may optionally include a message, explaining the reason for the invitation.',
|
|
||||||
suggestion.text.label, this.chatroomview.model.getDisplayName())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (reason !== null) {
|
|
||||||
this.chatroomview.model.directInvite(suggestion.text.value, reason);
|
|
||||||
}
|
|
||||||
const form = this.el.querySelector('.room-invite form'),
|
|
||||||
input = form.querySelector('.invited-contact'),
|
|
||||||
error = form.querySelector('.error');
|
|
||||||
if (error !== null) {
|
|
||||||
error.parentNode.removeChild(error);
|
|
||||||
}
|
|
||||||
input.value = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
inviteFormSubmitted (evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
const el = evt.target.querySelector('input.invited-contact');
|
|
||||||
const jid = el.value;
|
|
||||||
if (u.isValid(jid)) {
|
|
||||||
this.promptForInvite({
|
|
||||||
'target': el,
|
|
||||||
'text': {'label': jid, 'value': jid}}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
evt.target.outerHTML = tpl_chatroom_invite({
|
|
||||||
'error_message': __('Please enter a valid XMPP address'),
|
|
||||||
'label_invitation': __('Invite'),
|
|
||||||
});
|
|
||||||
this.initInviteWidget();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldInviteWidgetBeShown () {
|
|
||||||
return _converse.allow_muc_invitations &&
|
return _converse.allow_muc_invitations &&
|
||||||
(this.chatroomview.model.features.get('open') ||
|
(this.chatroomview.model.features.get('open') ||
|
||||||
this.chatroomview.model.getOwnAffiliation() === "owner"
|
this.chatroomview.model.getOwnAffiliation() === "owner"
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
initInviteWidget () {
|
|
||||||
const form = this.el.querySelector('.room-invite form');
|
|
||||||
if (form === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
form.addEventListener('submit', this.inviteFormSubmitted.bind(this), false);
|
|
||||||
const list = _converse.roster.map(i => ({'label': i.getDisplayName(), 'value': i.get('jid')}));
|
|
||||||
const el = this.el.querySelector('.suggestion-box').parentElement;
|
|
||||||
|
|
||||||
if (this.invite_auto_complete) {
|
|
||||||
this.invite_auto_complete.destroy();
|
|
||||||
}
|
|
||||||
this.invite_auto_complete = new _converse.AutoComplete(el, {
|
|
||||||
'min_chars': 1,
|
|
||||||
'list': list
|
|
||||||
});
|
|
||||||
this.invite_auto_complete.on('suggestion-box-selectcomplete', ev => this.promptForInvite(ev));
|
|
||||||
this.invite_auto_complete.on('suggestion-box-open', () => {
|
|
||||||
this.invite_auto_complete.ul.setAttribute('style', `max-height: calc(${this.el.offsetHeight}px - 80px);`);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
},
|
},
|
||||||
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"skeletor.js": "skeletorjs/skeletor#29a6d8f707076e865133b8f36f07c76ba4b4b582",
|
"skeletor.js": "skeletorjs/skeletor#9a4487496bd2810b2f0847acbca136333cf9cfb0",
|
||||||
"backbone": "1.4",
|
"backbone": "1.4",
|
||||||
"backbone.browserStorage": "conversejs/backbone.browserStorage#674ba3aa0e4d0f0b0dcac48fcc7dea531012828f",
|
"backbone.browserStorage": "conversejs/backbone.browserStorage#674ba3aa0e4d0f0b0dcac48fcc7dea531012828f",
|
||||||
"filesize": "^4.1.2",
|
"filesize": "^4.1.2",
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<div class="suggestion-box room-invite">
|
|
||||||
<form>
|
|
||||||
{[ if (o.error_message) { ]} <div class="error error-feedback">{{{o.error_message}}}</div> {[ } ]}
|
|
||||||
<div class="form-group">
|
|
||||||
<input class="form-control invited-contact suggestion-box__input"
|
|
||||||
placeholder="{{{o.label_invitation}}}"
|
|
||||||
type="text"/>
|
|
||||||
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<ul class="suggestion-box__results suggestion-box__results--below" hidden=""></ul>
|
|
||||||
</div>
|
|
46
src/templates/muc_invite_modal.js
Normal file
46
src/templates/muc_invite_modal.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { html } from "lit-html";
|
||||||
|
import { __ } from '@converse/headless/i18n';
|
||||||
|
import { modal_header_close_button } from "./buttons"
|
||||||
|
|
||||||
|
const i18n_invite = __('Invite');
|
||||||
|
const i18n_invite_heading = __('Invite someone to this groupchat');
|
||||||
|
const error_message = __('Please enter a valid XMPP address');
|
||||||
|
const i18n_invite_label = __('XMPP Address');
|
||||||
|
const i18n_reason = __('Optional reason for the invitation');
|
||||||
|
|
||||||
|
|
||||||
|
export default (o) => html`
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="add-chatroom-modal-label">${i18n_invite_heading}</h5>
|
||||||
|
${modal_header_close_button}
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<span class="modal-alert"></span>
|
||||||
|
<div class="suggestion-box room-invite">
|
||||||
|
<form @submit=${o.submitInviteForm}>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="clearfix" for="invitee_jids">${i18n_invite_label}:</label>
|
||||||
|
${ o.invalid_invite_jid ? html`<div class="error error-feedback">${error_message}</div>` : '' }
|
||||||
|
<input class="form-control suggestion-box__input"
|
||||||
|
required="required"
|
||||||
|
name="invitee_jids"
|
||||||
|
id="invitee_jids"
|
||||||
|
type="text"/>
|
||||||
|
<span class="suggestion-box__additions visually-hidden" role="status" aria-live="assertive" aria-relevant="additions"></span>
|
||||||
|
<ul class="suggestion-box__results suggestion-box__results--below" hidden=""></ul>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>${i18n_reason}:</label>
|
||||||
|
<textarea class="form-control" name="reason"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary">${i18n_invite}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -20,6 +20,7 @@ const i18n_archived = __('Message archiving');
|
||||||
const i18n_archived_hint = __('Messages are archived on the server');
|
const i18n_archived_hint = __('Messages are archived on the server');
|
||||||
const i18n_features = __('Features');
|
const i18n_features = __('Features');
|
||||||
const i18n_hidden = __('Hidden');
|
const i18n_hidden = __('Hidden');
|
||||||
|
const i18n_invite_hint = __('Invite people to join this groupchat');
|
||||||
const i18n_members_only = __('Members only');
|
const i18n_members_only = __('Members only');
|
||||||
const i18n_members_only_hint = __('this groupchat is restricted to members only');
|
const i18n_members_only_hint = __('this groupchat is restricted to members only');
|
||||||
const i18n_moderated = __('Moderated');
|
const i18n_moderated = __('Moderated');
|
||||||
|
@ -32,6 +33,7 @@ const i18n_not_moderated = __('Not moderated');
|
||||||
const i18n_not_searchable_hint = __('This groupchat is not publicly searchable');
|
const i18n_not_searchable_hint = __('This groupchat is not publicly searchable');
|
||||||
const i18n_open = __('Open');
|
const i18n_open = __('Open');
|
||||||
const i18n_open_hint = __('Anyone can join this groupchat');
|
const i18n_open_hint = __('Anyone can join this groupchat');
|
||||||
|
const i18n_participants = __('Participants');
|
||||||
const i18n_password = __('Password protected')
|
const i18n_password = __('Password protected')
|
||||||
const i18n_password_hint = __('This groupchat requires a password before entry');
|
const i18n_password_hint = __('This groupchat requires a password before entry');
|
||||||
const i18n_persistent = __('Persistent');
|
const i18n_persistent = __('Persistent');
|
||||||
|
@ -77,11 +79,27 @@ const tpl_features = (o) => html`
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const invite_button = (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>`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default (o) => html`
|
export default (o) => html`
|
||||||
<div class="occupants-header">
|
<div class="occupants-header">
|
||||||
<i class="hide-occupants fa fa-times"></i>
|
<i class="hide-occupants fa fa-times"></i>
|
||||||
<p class="occupants-heading">${o.label_occupants}</p>
|
<div class="occupants-header--title">
|
||||||
|
<span class="occupants-heading">${i18n_participants}</span>
|
||||||
|
${ invite_button(o) }
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dragresize dragresize-occupants-left"></div>
|
<div class="dragresize dragresize-occupants-left"></div>
|
||||||
<ul class="occupant-list">
|
<ul class="occupant-list">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user