diff --git a/CHANGES.md b/CHANGES.md
index 11275eef0c..7d3fc976ea 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -37,6 +37,7 @@
- Add a new theme 'Cyberpunk' and remove the old 'Concord' theme.
- Improved accessibility.
- New "getOccupantActionButtons" hook, so that plugins can add actions on MUC occupants.
+- MUC occupants badges: displays short labels, with full label as title.
- New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#show-background)
diff --git a/src/plugins/muc-views/templates/occupant.js b/src/plugins/muc-views/templates/occupant.js
index d8ff85c3fd..32019f6624 100644
--- a/src/plugins/muc-views/templates/occupant.js
+++ b/src/plugins/muc-views/templates/occupant.js
@@ -13,6 +13,72 @@ const i18n_occupant_hint = /** @param {MUCOccupant} o */(o) => {
return __('Click to mention %1$s in your message.', o.get('nick'));
}
+let badges_definitions; // will be initialized at first call (to be sure that the __ function is correctly loaded).
+
+/**
+ * Inits badges definitions.
+ * For short labels, it will use the label first letter. If there is ambigous short labels, it will try to add up to 4 letters.
+ * Letters will be uppercase.
+ */
+function init_badges_definitions () {
+ badges_definitions = {}
+ badges_definitions['owner'] = {
+ label: __('Owner'),
+ classname: 'badge-groupchat'
+ };
+ badges_definitions['admin'] = {
+ label: __('Admin'),
+ classname: 'badge-info'
+ };
+ badges_definitions['member'] = {
+ label: __('Member'),
+ classname: 'badge-info'
+ };
+ badges_definitions['moderator'] = {
+ label: __('Moderator'),
+ classname: 'badge-info'
+ };
+ badges_definitions['visitor'] = {
+ label: __('Visitor'),
+ classname: 'badge-secondary'
+ };
+
+ // And now we must compute unique short labels.
+ let seen;
+ for (
+ let current_length = 1;
+ current_length < 5 && (!seen || Object.values(seen).find(count => count > 1));
+ current_length++
+ ) {
+ const currentlySeen = {}
+ for (const definition of Object.values(badges_definitions)) {
+ if (!seen || (seen[definition.shortlabel] ?? 0) >= 2) {
+ // (first loop, or count >= 2 in the previous loop)
+ definition.shortlabel = definition.label.substr(0, current_length).toLocaleUpperCase();
+ currentlySeen[definition.shortlabel]??= 0;
+ currentlySeen[definition.shortlabel]++;
+ }
+ }
+ seen = currentlySeen;
+ }
+}
+
+/**
+ * Badge template.
+ * @param {string} badge_code The badge to use ('owner', 'admin', ...)
+ */
+function tplBadge (badge_code) {
+ if (!badges_definitions) {
+ init_badges_definitions();
+ }
+ const definition = badges_definitions[badge_code];
+ if (!definition) { return ''; }
+
+ return html`${definition.shortlabel}`;
+}
+
+
const occupant_title = /** @param {MUCOccupant} o */(o) => {
const role = o.get('role');
const hint_occupant = i18n_occupant_hint(o);
@@ -78,11 +144,6 @@ async function tplActionButtons (o) {
export default (o, chat) => {
const affiliation = o.get('affiliation');
const hint_show = PRETTY_CHAT_STATUS[o.get('show')];
- const i18n_admin = __('Admin');
- const i18n_member = __('Member');
- const i18n_moderator = __('Moderator');
- const i18n_owner = __('Owner');
- const i18n_visitor = __('Visitor');
const role = o.get('role');
const show = o.get('show');
@@ -122,11 +183,11 @@ export default (o, chat) => {
@click=${chat.onOccupantClicked}
style="${getAuthorStyle(o)}">${o.getDisplayName()}
- ${ (affiliation === "owner") ? html`${i18n_owner}` : '' }
- ${ (affiliation === "admin") ? html`${i18n_admin}` : '' }
- ${ (affiliation === "member") ? html`${i18n_member}` : '' }
- ${ (role === "moderator") ? html`${i18n_moderator}` : '' }
- ${ (role === "visitor") ? html`${i18n_visitor}` : '' }
+ ${ (affiliation === "owner") ? tplBadge('owner') : '' }
+ ${ (affiliation === "admin") ? tplBadge('admin') : '' }
+ ${ (affiliation === "member") ? tplBadge('member') : '' }
+ ${ (role === "moderator") ? tplBadge('moderator') : '' }
+ ${ (role === "visitor") ? tplBadge('visitor') : '' }
${
until(tplActionButtons(o))
diff --git a/src/plugins/muc-views/tests/occupants.js b/src/plugins/muc-views/tests/occupants.js
index d7d9ec9f95..fd6a535c78 100644
--- a/src/plugins/muc-views/tests/occupants.js
+++ b/src/plugins/muc-views/tests/occupants.js
@@ -175,8 +175,11 @@ describe("The occupants sidebar", function () {
expect(occupants.length).toBe(1);
expect(occupants[0].querySelector('.occupant-nick').textContent.trim()).toBe("romeo");
expect(occupants[0].querySelectorAll('.badge').length).toBe(2);
- expect(occupants[0].querySelectorAll('.badge')[0].textContent.trim()).toBe('Owner');
- expect(sizzle('.badge:last', occupants[0]).pop().textContent.trim()).toBe('Moderator');
+ expect(occupants[0].querySelectorAll('.badge')[0].textContent.trim()).toBe('O');
+ expect(occupants[0].querySelectorAll('.badge')[0].getAttribute('title').trim()).toBe('Owner');
+ expect(occupants[0].querySelectorAll('.badge')[0].getAttribute('aria-label').trim()).toBe('Owner');
+ expect(sizzle('.badge:last', occupants[0]).pop().textContent.trim()).toBe('MO');
+ expect(sizzle('.badge:last', occupants[0]).pop().getAttribute('title').trim()).toBe('Moderator');
var presence = $pres({
to:'romeo@montague.lit/pda',
@@ -199,8 +202,12 @@ describe("The occupants sidebar", function () {
);
expect(occupants[1].querySelector('.occupant-nick').textContent.trim()).toBe("romeo");
expect(occupants[0].querySelectorAll('.badge').length).toBe(2);
- expect(occupants[0].querySelectorAll('.badge')[0].textContent.trim()).toBe('Admin');
- expect(occupants[0].querySelectorAll('.badge')[1].textContent.trim()).toBe('Moderator');
+ expect(occupants[0].querySelectorAll('.badge')[0].textContent.trim()).toBe('A');
+ expect(occupants[0].querySelectorAll('.badge')[0].getAttribute('title').trim()).toBe('Admin');
+ expect(occupants[0].querySelectorAll('.badge')[0].getAttribute('aria-label').trim()).toBe('Admin');
+ expect(occupants[0].querySelectorAll('.badge')[1].textContent.trim()).toBe('MO');
+ expect(occupants[0].querySelectorAll('.badge')[1].getAttribute('title').trim()).toBe('Moderator');
+ expect(occupants[0].querySelectorAll('.badge')[1].getAttribute('aria-label').trim()).toBe('Moderator');
contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@montague.lit';
presence = $pres({
@@ -222,6 +229,7 @@ describe("The occupants sidebar", function () {
contact_jid + ' This user can NOT send messages in this groupchat. Click to mention visitorwoman in your message.'
);
expect(occupants[2].querySelectorAll('.badge').length).toBe(1);
- expect(sizzle('.badge', occupants[2]).pop().textContent.trim()).toBe('Visitor');
+ expect(sizzle('.badge', occupants[2]).pop().textContent.trim()).toBe('V');
+ expect(sizzle('.badge', occupants[2]).pop().getAttribute('title').trim()).toBe('Visitor');
}));
});