muc-views: replace VDOMView with HTMLView

This commit is contained in:
JC Brand 2020-01-26 18:36:25 +01:00
parent 9fb2d279e9
commit d32c4c1f61
13 changed files with 242 additions and 229 deletions

View File

@ -1528,9 +1528,8 @@
.c('value').t('cauldronburn');
_converse.connection._dataRecv(test_utils.createRequest(config_stanza));
await u.waitUntil(() => view.el.querySelectorAll('form.chatroom-form').length)
expect(view.el.querySelectorAll('form.chatroom-form').length).toBe(1);
expect(view.el.querySelectorAll('form.chatroom-form fieldset').length).toBe(2);
const form = await u.waitUntil(() => view.el.querySelector('.muc-config-form'));
expect(form.querySelectorAll('fieldset').length).toBe(2);
const membersonly = view.el.querySelectorAll('input[name="muc#roomconfig_membersonly"]');
expect(membersonly.length).toBe(1);
expect(membersonly[0].getAttribute('type')).toBe('checkbox');
@ -2336,7 +2335,7 @@
await u.waitUntil(() => view.el.querySelectorAll('li .occupant-nick').length, 500);
let occupants = view.el.querySelector('.occupant-list');
expect(occupants.childNodes.length).toBe(1);
expect(occupants.childElementCount).toBe(1);
expect(occupants.firstElementChild.querySelector('.occupant-nick').textContent.trim()).toBe("oldnick");
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(1);
@ -2368,7 +2367,7 @@
expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
occupants = view.el.querySelector('.occupant-list');
expect(occupants.childNodes.length).toBe(1);
expect(occupants.childElementCount).toBe(1);
presence = $pres().attrs({
from:'lounge@montague.lit/newnick',
@ -2394,7 +2393,7 @@
__(_converse.muc.new_nickname_messages["303"], "newnick")
);
occupants = view.el.querySelector('.occupant-list');
expect(occupants.childNodes.length).toBe(1);
expect(occupants.childElementCount).toBe(1);
expect(sizzle('.occupant-nick:first', occupants).pop().textContent.trim()).toBe("newnick");
done();
}));

View File

@ -5,12 +5,11 @@
* @license Mozilla Public License (MPLv2)
*/
import "converse-modal";
import "backbone.vdomview";
import "formdata-polyfill";
import "@converse/headless/utils/muc";
import { get, head, isString, isUndefined, pick } from "lodash";
import { get, head, isString, isUndefined } from "lodash";
import { HTMLView } from 'skeletor.js/src/htmlview.js';
import { Model } from 'skeletor.js/src/model.js';
import { OrderedListView } from "skeletor.js/src/overview";
import { View } from "skeletor.js/src/view";
import { __ } from '@converse/headless/i18n';
import converse from "@converse/headless/converse-core";
@ -22,17 +21,15 @@ import tpl_chatroom_bottom_panel from "templates/chatroom_bottom_panel.html";
import tpl_chatroom_destroyed from "templates/chatroom_destroyed.html";
import tpl_chatroom_details_modal from "templates/chatroom_details_modal.js";
import tpl_chatroom_disconnect from "templates/chatroom_disconnect.html";
import tpl_chatroom_features from "templates/chatroom_features.html";
import tpl_chatroom_form from "templates/chatroom_form.html";
import tpl_muc_config_form from "templates/muc_config_form.js";
import tpl_chatroom_head from "templates/chatroom_head.html";
import tpl_chatroom_invite from "templates/chatroom_invite.html";
import tpl_chatroom_nickname_form from "templates/chatroom_nickname_form.html";
import tpl_chatroom_password_form from "templates/chatroom_password_form.html";
import tpl_chatroom_sidebar from "templates/chatroom_sidebar.html";
import tpl_muc_password_form from "templates/muc_password_form.js";
import tpl_muc_sidebar from "templates/muc_sidebar.js";
import tpl_info from "templates/info.html";
import tpl_list_chatrooms_modal from "templates/list_chatrooms_modal.js";
import tpl_moderator_tools_modal from "templates/moderator_tools_modal.js";
import tpl_occupant from "templates/occupant.html";
import tpl_room_description from "templates/room_description.html";
import tpl_room_item from "templates/room_item.html";
import tpl_room_panel from "templates/room_panel.html";
@ -40,7 +37,7 @@ import tpl_rooms_results from "templates/rooms_results.html";
import tpl_spinner from "templates/spinner.html";
import xss from "xss/dist/xss";
const { Backbone, Strophe, sizzle, $iq, $pres } = converse.env;
const { Strophe, sizzle, $iq, $pres } = converse.env;
const u = converse.env.utils;
const ROLES = ['moderator', 'participant', 'visitor'];
@ -1593,10 +1590,12 @@ converse.plugins.add('converse-muc-views', {
u.safeSave(this.model.session, {'connection_status': converse.ROOMSTATUS.NICKNAME_REQUIRED});
},
closeForm () {
/* Remove the configuration form without submitting and
* return to the chat view.
/**
* Remove the configuration form without submitting and return to the chat view.
* @private
* @method _converse.ChatRoomView#closeForm
*/
closeForm () {
sizzle('.chatroom-form-container', this.el).forEach(e => u.addClass('hidden', e));
this.renderAfterTransition();
},
@ -2040,12 +2039,8 @@ converse.plugins.add('converse-muc-views', {
});
_converse.MUCConfigForm = Backbone.VDOMView.extend({
className: 'muc-config-form',
events: {
'submit .chatroom-form': 'submitConfigForm',
'click .button-cancel': 'closeConfigForm'
},
_converse.MUCConfigForm = HTMLView.extend({
className: 'chatroom-form-container muc-config-form',
initialize (attrs) {
this.chatroomview = attrs.chatroomview;
@ -2066,11 +2061,12 @@ converse.plugins.add('converse-muc-views', {
'new_password': !password_protected,
'fixed_username': this.model.get('jid')
};
return tpl_chatroom_form({
'__': __,
'title': get(stanza.querySelector('title'), 'textContent'),
return tpl_muc_config_form({
'closeConfigForm': ev => this.closeConfigForm(ev),
'fields': fields.map(f => u.xForm2webForm(f, stanza, options)),
'instructions': get(stanza.querySelector('instructions'), 'textContent'),
'fields': fields.map(f => u.xForm2webForm(f, stanza, options))
'submitConfigForm': ev => this.submitConfigForm(ev),
'title': get(stanza.querySelector('title'), 'textContent')
});
},
@ -2098,11 +2094,8 @@ converse.plugins.add('converse-muc-views', {
});
_converse.MUCPasswordForm = Backbone.VDOMView.extend({
className: 'muc-password-form',
events: {
'submit form': 'submitPassword',
},
_converse.MUCPasswordForm = HTMLView.extend({
className: 'chatroom-form-container muc-password-form',
initialize (attrs) {
this.chatroomview = attrs.chatroomview;
@ -2111,14 +2104,10 @@ converse.plugins.add('converse-muc-views', {
},
toHTML () {
const err_msg = this.model.get('validation_message');
return tpl_chatroom_password_form({
return tpl_muc_password_form({
'jid': this.model.get('jid'),
'heading': __('This groupchat requires a password'),
'label_password': __('Password: '),
'label_submit': __('Submit'),
'error_class': err_msg ? 'error' : '',
'validation_message': err_msg
'submitPassword': ev => this.submitPassword(ev),
'validation_message': this.model.get('validation_message')
});
},
@ -2131,67 +2120,42 @@ converse.plugins.add('converse-muc-views', {
});
_converse.ChatRoomOccupantView = Backbone.VDOMView.extend({
tagName: 'li',
initialize () {
this.listenTo(this.model, 'change', this.render);
},
toHTML () {
const show = this.model.get('show');
return tpl_occupant(
Object.assign({
__,
show,
'jid': '',
'hint_show': _converse.PRETTY_CHAT_STATUS[show],
'hint_occupant': __('Click to mention %1$s in your message.', this.model.get('nick'))
}, this.model.toJSON())
);
},
destroy () {
this.el.parentElement.removeChild(this.el);
}
});
_converse.ChatRoomOccupantsView = OrderedListView.extend({
_converse.ChatRoomOccupantsView = HTMLView.extend({
tagName: 'div',
className: 'occupants col-md-3 col-4',
listItems: 'model',
sortEvent: 'change:role',
listSelector: '.occupant-list',
ItemView: _converse.ChatRoomOccupantView,
async initialize () {
OrderedListView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'add', this.maybeRenderInviteWidget);
this.listenTo(this.model, 'change:affiliation', this.maybeRenderInviteWidget);
this.chatroomview = this.model.chatroomview;
this.listenTo(this.chatroomview.model.features, 'change', this.renderRoomFeatures);
this.listenTo(this.model, 'add', this.maybeRenderInviteWidget);
this.listenTo(this.model, 'add', this.render);
this.listenTo(this.model, 'remove', 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:open', this.renderInviteWidget);
this.listenTo(this.chatroomview.model, 'change:hidden_occupants', this.setVisibility);
this.render();
await this.model.fetched;
this.sortAndPositionAllItems();
},
render () {
this.el.innerHTML = tpl_chatroom_sidebar(
toHTML () {
return tpl_muc_sidebar(
Object.assign(this.chatroomview.model.toJSON(), {
'allow_muc_invitations': _converse.allow_muc_invitations,
'label_occupants': __('Participants')
'features': this.chatroomview.model.features,
'label_occupants': __('Participants'),
'occupants': this.model.models
})
);
},
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();
return this.renderRoomFeatures();
this.setOccupantsHeight();
},
setVisibility () {
@ -2210,6 +2174,7 @@ converse.plugins.add('converse-muc-views', {
},
renderInviteWidget () {
// TODO: this needs to be rendered inside muc_sidebar.js
const widget = this.el.querySelector('.room-invite');
if (this.shouldInviteWidgetBeShown()) {
if (widget === null) {
@ -2229,26 +2194,14 @@ converse.plugins.add('converse-muc-views', {
return this;
},
renderRoomFeatures () {
const features = this.chatroomview.model.features,
picks = pick(features.attributes, converse.ROOM_FEATURES),
iteratee = (a, v) => a || v;
if (Object.values(picks).reduce(iteratee)) {
const el = this.el.querySelector('.chatroom-features');
el.innerHTML = tpl_chatroom_features(Object.assign(features.toJSON(), {__}));
this.setOccupantsHeight();
}
return this;
},
setOccupantsHeight () {
const el = this.el.querySelector('.chatroom-features');
if (el) {
this.el.querySelector('.occupant-list').style.cssText =
`height: calc(100% - ${el.offsetHeight}px - 5em);`;
}
},
promptForInvite (suggestion) {
let reason = '';
if (!_converse.auto_join_on_invite) {
@ -2417,7 +2370,7 @@ converse.plugins.add('converse-muc-views', {
* @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 {Backbone.View} Backbone.View representing the groupchat
* @returns {View} View representing the groupchat
*
* @example
* // To return a single view, provide the JID of the groupchat

View File

@ -3,6 +3,7 @@
* @copyright 2020, the Converse.js contributors
* @license Mozilla Public License (MPLv2)
*/
import "backbone.vdomview";
import "@converse/headless/converse-chatboxes";
import "@converse/headless/converse-roster";
import "converse-modal";

View File

@ -145,15 +145,6 @@ _converse.STATUS_WEIGHTS = {
'chat': 1, // We currently don't differentiate between "chat" and "online"
'online': 1
};
_converse.PRETTY_CHAT_STATUS = {
'offline': 'Offline',
'unavailable': 'Unavailable',
'xa': 'Extended Away',
'away': 'Away',
'dnd': 'Do not disturb',
'chat': 'Chattty',
'online': 'Online'
};
_converse.ANONYMOUS = 'anonymous';
_converse.CLOSED = 'closed';
_converse.EXTERNAL = 'external';

View File

@ -1,42 +0,0 @@
<p class="occupants-heading">{{{o.__('Features')}}}</p>
<ul class="features-list">
{[ if (o.passwordprotected) { ]}
<li class="feature" title="{{{ o.__('This groupchat requires a password before entry') }}}"><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}}</li>
{[ } ]}
{[ if (o.unsecured) { ]}
<li class="feature" title="{{{ o.__('This groupchat does not require a password upon entry') }}}"><span class="fa fa-unlock"></span>{{{ o.__('No password') }}}</li>
{[ } ]}
{[ if (o.hidden) { ]}
<li class="feature" title="{{{ o.__('This groupchat is not publicly searchable') }}}"><span class="fa fa-eye-slash"></span>{{{ o.__('Hidden') }}}</li>
{[ } ]}
{[ if (o.public_room) { ]}
<li class="feature" title="{{{ o.__('This groupchat is publicly searchable') }}}"><span class="fa fa-eye"></span>{{{ o.__('Public') }}}</li>
{[ } ]}
{[ if (o.membersonly) { ]}
<li class="feature" title="{{{ o.__('this groupchat is restricted to members only') }}}"><span class="fa fa-address-book"></span>{{{ o.__('Members only') }}}</li>
{[ } ]}
{[ if (o.open) { ]}
<li class="feature" title="{{{ o.__('Anyone can join this groupchat') }}}"><span class="fa fa-globe"></span>{{{ o.__('Open') }}}</li>
{[ } ]}
{[ if (o.persistent) { ]}
<li class="feature" title="{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}"><span class="fa fa-save"></span>{{{ o.__('Persistent') }}}</li>
{[ } ]}
{[ if (o.temporary) { ]}
<li class="feature" title="{{{ o.__('This groupchat will disappear once the last person leaves') }}}"><span class="fa fa-snowflake"></span>{{{ o.__('Temporary') }}}</li>
{[ } ]}
{[ if (o.nonanonymous) { ]}
<li class="feature" title="{{{ o.__('All other groupchat participants can see your XMPP address') }}}"><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}}</li>
{[ } ]}
{[ if (o.semianonymous) { ]}
<li class="feature" title="{{{ o.__('Only moderators can see your XMPP address') }}}"><span class="fa fa-user-secret"></span>{{{ o.__('Semi-anonymous') }}}</li>
{[ } ]}
{[ if (o.moderated) { ]}
<li class="feature" title="{{{ o.__('Participants entering this groupchat need to request permission to write') }}}"><span class="fa fa-gavel"></span>{{{ o.__('Moderated') }}}</li>
{[ } ]}
{[ if (o.unmoderated) { ]}
<li class="feature" title="{{{ o.__('Participants entering this groupchat can write right away') }}}"><span class="fa fa-info-circle"></span>{{{ o.__('Not moderated') }}}</li>
{[ } ]}
{[ if (o.mam_enabled) { ]}
<li class="feature" title="{{{ o.__('Messages are archived on the server') }}}"><span class="fa fa-database"></span>{{{ o.__('Message archiving') }}}</li>
{[ } ]}
</ul>

View File

@ -1,16 +0,0 @@
<div class="chatroom-form-container muc-config-form">
<form class="converse-form chatroom-form" autocomplete="off">
<fieldset class="form-group">
<legend>{{{o.title}}}</legend>
{[ if (o.title !== o.instructions) { ]}
<p class="form-help">{{{o.instructions}}}</p>
{[ } ]}
<!-- Fields are generated internally, with xForm2webForm -->
{[ o.fields.forEach(function (field) { ]} {{ field }} {[ }) ]}
</fieldset>
<fieldset>
<input type="submit" class="btn btn-primary" value="{{{o.__('Save')}}}"/>
<input type="button" class="btn btn-secondary .button-cancel" value="{{{o.__('Cancel')}}}"/>
</fieldset>
</form>
</div>

View File

@ -1,14 +0,0 @@
<div class="chatroom-form-container muc-password-form">
<form class="converse-form chatroom-form converse-centered-form">
<fieldset class="form-group">
<label>{{{o.heading}}}</label>
<p class="validation-message">{{{o.validation_message}}}</p>
<input class="hidden-username" type="text" autocomplete="username" value="{{{o.jid}}}"></input>
<input type="password" name="password" required="required"
class="form-control {{o.error_class}}" placeholder="{{{o.label_password}}}"/>
</fieldset>
<fieldset class="form-group">
<input class="btn btn-primary" type="submit" value="{{{o.label_submit}}}"/>
</fieldset>
</form>
</div>

View File

@ -1,9 +0,0 @@
<!-- <div class="occupants"> -->
<div class="occupants-header">
<i class="hide-occupants fa fa-times"></i>
<p class="occupants-heading">{{{o.label_occupants}}}</p>
</div>
<div class="dragresize dragresize-occupants-left"></div>
<ul class="occupant-list"></ul>
<div class="chatroom-features"></div>
<!-- </div> -->

View File

@ -0,0 +1,21 @@
import { html } from "lit-html";
import { __ } from '@converse/headless/i18n';
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
const i18n_save = __('Save');
const i18n_cancel = __('Cancel');
export default (o) => html`
<form class="converse-form chatroom-form" autocomplete="off" @submit=${o.submitConfigForm}>
<fieldset class="form-group">
<legend>${o.title}</legend>
${ (o.title !== o.instructions) ? html`<p class="form-help">${o.instructions}</p>` : '' }
<!-- Fields are generated internally, with xForm2webForm -->
${ o.fields.map(field => unsafeHTML(field)) }
</fieldset>
<fieldset>
<input type="submit" class="btn btn-primary" value="${i18n_save}">
<input type="button" class="btn btn-secondary button-cancel" value="${i18n_cancel}" @click=${o.closeConfigForm}>
</fieldset>
</form>
`;

View File

@ -0,0 +1,25 @@
import { html } from "lit-html";
import { __ } from '@converse/headless/i18n';
const i18n_heading = __('This groupchat requires a password');
const i18n_password = __('Password: ');
const i18n_submit = __('Submit');
export default (o) => html`
<form class="converse-form chatroom-form converse-centered-form" @submit=${o.submitPassword}>
<fieldset class="form-group">
<label>${i18n_heading}</label>
<p class="validation-message">${o.validation_message}</p>
<input class="hidden-username" type="text" autocomplete="username" value="${o.jid}"></input>
<input type="password"
name="password"
required="required"
class="form-control ${o.validation_message ? 'error': ''}"
placeholder="${i18n_password}"/>
</fieldset>
<fieldset class="form-group">
<input class="btn btn-primary" type="submit" value="${i18n_submit}"/>
</fieldset>
</form>
`;

View File

@ -0,0 +1,99 @@
import { html } from "lit-html";
import { __ } from '@converse/headless/i18n';
import { pick } from "lodash";
import converse from "@converse/headless/converse-core";
import tpl_occupant from "./occupant.js";
const PRETTY_CHAT_STATUS = {
'offline': 'Offline',
'unavailable': 'Unavailable',
'xa': 'Extended Away',
'away': 'Away',
'dnd': 'Do not disturb',
'chat': 'Chattty',
'online': 'Online'
};
const occupant_hint = (occupant) => __('Click to mention %1$s in your message.', occupant.get('nick'))
const i18n_archived = __('Message archiving');
const i18n_archived_hint = __('Messages are archived on the server');
const i18n_features = __('Features');
const i18n_hidden = __('Hidden');
const i18n_members_only = __('Members only');
const i18n_members_only_hint = __('this groupchat is restricted to members only');
const i18n_moderated = __('Moderated');
const i18n_moderated_hint = __('Participants entering this groupchat need to request permission to write');
const i18n_no_password = __('No password');
const i18n_no_password_hint = __('This groupchat does not require a password upon entry');
const i18n_non_anon_hint = __('All other groupchat participants can see your XMPP address');
const i18n_not_anon = __('Not anonymous');
const i18n_not_moderated = __('Not moderated');
const i18n_not_searchable_hint = __('This groupchat is not publicly searchable');
const i18n_open = __('Open');
const i18n_open_hint = __('Anyone can join this groupchat');
const i18n_password = __('Password protected')
const i18n_password_hint = __('This groupchat requires a password before entry');
const i18n_persistent = __('Persistent');
const i18n_persistent_hint = __('This groupchat persists even if it\'s unoccupied');
const i18n_public = __('Public');
const i18n_searchable_hint = __('This groupchat is publicly searchable');
const i18n_semi_anon = __('Semi-anonymous');
const i18n_semi_anon_hint = __('Only moderators can see your XMPP address');
const i18n_temporary = __('Temporary');
const i18n_temporary_hint = __('This groupchat will disappear once the last person leaves');
const i18n_unmoderated_hint = __('Participants entering this groupchat can write right away');
function renderFeatures (o) {
const picks = pick(o.features.attributes, converse.ROOM_FEATURES);
const iteratee = (a, v) => a || v;
if (Object.values(picks).reduce(iteratee)) {
return tpl_features(o.features.toJSON());
} else {
return '';
}
}
const tpl_features = (o) => html`
<div class="chatroom-features">
<p class="occupants-heading">${i18n_features}</p>
<ul class="features-list">
${ (o.passwordprotected) ? html`<li class="feature" title="${ i18n_password_hint }"><span class="fa fa-lock"></span>${ i18n_password }</li>` : '' }
${ (o.unsecured) ? html`<li class="feature" title="${ i18n_no_password_hint }"><span class="fa fa-unlock"></span>${ i18n_no_password }</li>` : '' }
${ (o.hidden) ? html`<li class="feature" title="${ i18n_not_searchable_hint }"><span class="fa fa-eye-slash"></span>${ i18n_hidden }</li>` : '' }
${ (o.public_room) ? html`<li class="feature" title="${ i18n_searchable_hint }"><span class="fa fa-eye"></span>${ i18n_public }</li>` : '' }
${ (o.membersonly) ? html`<li class="feature" title="${ i18n_members_only_hint }"><span class="fa fa-address-book"></span>${ i18n_members_only }</li>` : '' }
${ (o.open) ? html`<li class="feature" title="${ i18n_open_hint }"><span class="fa fa-globe"></span>${ i18n_open }</li>` : '' }
${ (o.persistent) ? html`<li class="feature" title="${ i18n_persistent_hint }"><span class="fa fa-save"></span>${ i18n_persistent }</li>` : '' }
${ (o.temporary) ? html`<li class="feature" title="${ i18n_temporary_hint }"><span class="fa fa-snowflake"></span>${ i18n_temporary }</li>` : '' }
${ (o.nonanonymous) ? html`<li class="feature" title="${ i18n_non_anon_hint }"><span class="fa fa-id-card"></span>${ i18n_not_anon }</li>` : '' }
${ (o.semianonymous) ? html`<li class="feature" title="${ i18n_semi_anon_hint }"><span class="fa fa-user-secret"></span>${ i18n_semi_anon }</li>` : '' }
${ (o.moderated) ? html`<li class="feature" title="${ i18n_moderated_hint }"><span class="fa fa-gavel"></span>${ i18n_moderated }</li>` : '' }
${ (o.unmoderated) ? html`<li class="feature" title="${ i18n_unmoderated_hint }"><span class="fa fa-info-circle"></span>${ i18n_not_moderated }</li>` : '' }
${ (o.mam_enabled) ? html`<li class="feature" title="${ i18n_archived_hint }"><span class="fa fa-database"></span>${ i18n_archived }</li>` : '' }
</ul>
</div>
`;
export default (o) => html`
<div class="occupants-header">
<i class="hide-occupants fa fa-times"></i>
<p class="occupants-heading">${o.label_occupants}</p>
</div>
<div class="dragresize dragresize-occupants-left"></div>
<ul class="occupant-list">
${ o.occupants.map(occupant => {
return tpl_occupant(
Object.assign({
'jid': '',
'hint_show': PRETTY_CHAT_STATUS[occupant.get('show')],
'hint_occupant': occupant_hint(occupant)
}, occupant.toJSON())
);
}) }
</ul>
${ renderFeatures(o) }
`;

View File

@ -1,40 +0,0 @@
<li class="occupant" id="{{{ o.id }}}"
{[ if (o.role === "moderator") { ]}
title="{{{ o.jid }}} {{{ o.__('This user is a moderator.') }}} {{{ o.hint_occupant }}}"
{[ } ]}
{[ if (o.role === "participant") { ]}
title="{{{ o.jid }}} {{{ o.__('This user can send messages in this groupchat.') }}} {{{ o.hint_occupant }}}"
{[ } ]}
{[ if (o.role === "visitor") { ]}
title="{{{ o.jid }}} {{{ o.__('This user can NOT send messages in this groupchat.') }}} {{{ o.hint_occupant }}}"
{[ } ]}
{[ if (!["visitor", "participant", "moderator"].includes(o.role)) { ]}
title="{{{ o.jid }}} {{{ o.hint_occupant }}}"
{[ } ]}>
<div class="row no-gutters">
<div class="col-auto">
<div class="occupant-status occupant-{{{o.show}}} circle" title="{{{o.hint_show}}}"></div>
</div>
<div class="col occupant-nick-badge">
<span class="occupant-nick">{{{o.nick || o.jid}}}</span>
<span class="occupant-badges">
{[ if (o.affiliation === "owner") { ]}
<span class="badge badge-groupchat">{{{o.__('Owner')}}}</span>
{[ } ]}
{[ if (o.affiliation === "admin") { ]}
<span class="badge badge-info">{{{o.__('Admin')}}}</span>
{[ } ]}
{[ if (o.affiliation === "member") { ]}
<span class="badge badge-info">{{{o.__('Member')}}}</span>
{[ } ]}
{[ if (o.role === "moderator") { ]}
<span class="badge badge-info">{{{o.__('Moderator')}}}</span>
{[ } ]}
{[ if (o.role === "visitor") { ]}
<span class="badge badge-secondary">{{{o.__('Visitor')}}}</span>
{[ } ]}
</span>
</div>
</div>
</li>

45
src/templates/occupant.js Normal file
View File

@ -0,0 +1,45 @@
import { html } from "lit-html";
import { __ } from '@converse/headless/i18n';
const i18n_moderator_hint = ('This user is a moderator.');
const i18n_participant_hint = __('This user can send messages in this groupchat.');
const i18n_visitor_hint = __('This user can NOT send messages in this groupchat.')
const i18n_owner = __('Owner');
const i18n_admin = __('Admin');
const i18n_member = __('Member');
const i18n_moderator = __('Moderator');
const i18n_visitor = __('Visitor');
const occupant_title = (o) => {
if (o.role === "moderator") {
return `${o.jid} ${i18n_moderator_hint} ${o.hint_occupant}`;
} else if (o.role === "participant") {
return `${o.jid} ${i18n_participant_hint} ${o.hint_occupant}`;
} else if (o.role === "visitor") {
return `${o.jid} ${i18n_visitor_hint} ${o.hint_occupant}`;
} else if (!["visitor", "participant", "moderator"].includes(o.role)) {
return `${o.jid} ${o.hint_occupant}`;
}
}
export default (o) => html`
<li class="occupant" id="${o.id}" title="${occupant_title(o)}">
<div class="row no-gutters">
<div class="col-auto">
<div class="occupant-status occupant-${o.show} circle" title="${o.hint_show}"></div>
</div>
<div class="col occupant-nick-badge">
<span class="occupant-nick">${o.nick || o.jid}</span>
<span class="occupant-badges">
${ (o.affiliation === "owner") ? html`<span class="badge badge-groupchat">${i18n_owner}</span>` : '' }
${ (o.affiliation === "admin") ? html`<span class="badge badge-info">${i18n_admin}</span>` : '' }
${ (o.affiliation === "member") ? html`<span class="badge badge-info">${i18n_member}</span>` : '' }
${ (o.role === "moderator") ? html`<span class="badge badge-info">${i18n_moderator}</span>` : '' }
${ (o.role === "visitor") ? html`<span class="badge badge-secondary">${i18n_visitor}</span>` : '' }
</span>
</div>
</div>
</li>
`;