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'); })); });