Przeglądaj źródła

MUC occupants badges: displays short labels, with full label as title.

John Livingston 10 miesięcy temu
rodzic
commit
16d941aba5

+ 1 - 0
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)
 

+ 71 - 10
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 initBadgesDefinitions () {
+    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 currently_seen = {}
+        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();
+                currently_seen[definition.shortlabel]??= 0;
+                currently_seen[definition.shortlabel]++;
+            }
+        }
+        seen = currently_seen;
+    }
+}
+
+/**
+ * Badge template.
+ * @param {string} badge_code The badge to use ('owner', 'admin', ...)
+ */
+function tplBadge (badge_code) {
+    if (!badges_definitions) {
+        initBadgesDefinitions();
+    }
+    const definition = badges_definitions[badge_code];
+    if (!definition) { return ''; }
+
+    return html`<span title="${definition.label}" aria-label=${definition.label}
+        class="badge ${definition.classname ?? 'badge-info'}">${definition.shortlabel}</span>`;
+}
+
+
 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()}</span>
                     <span class="occupant-badges">
-                        ${ (affiliation === "owner") ? html`<span class="badge badge-groupchat">${i18n_owner}</span>` : '' }
-                        ${ (affiliation === "admin") ? html`<span class="badge badge-info">${i18n_admin}</span>` : '' }
-                        ${ (affiliation === "member") ? html`<span class="badge badge-info">${i18n_member}</span>` : '' }
-                        ${ (role === "moderator") ? html`<span class="badge badge-info">${i18n_moderator}</span>` : '' }
-                        ${ (role === "visitor") ? html`<span class="badge badge-secondary">${i18n_visitor}</span>`  : '' }
+                        ${ (affiliation === "owner") ? tplBadge('owner') : '' }
+                        ${ (affiliation === "admin") ? tplBadge('admin') : '' }
+                        ${ (affiliation === "member") ? tplBadge('member') : '' }
+                        ${ (role === "moderator") ? tplBadge('moderator') : '' }
+                        ${ (role === "visitor") ? tplBadge('visitor')  : '' }
                     </span>
                     ${
                         until(tplActionButtons(o))

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