2
0
Эх сурвалжийг харах

Work on showing a detailed occupant view in the MUC sidebar

JC Brand 9 сар өмнө
parent
commit
f9d687bba2

+ 66 - 0
src/plugins/muc-views/occupant.js

@@ -0,0 +1,66 @@
+import { Model } from '@converse/skeletor';
+import { _converse, api, converse } from "@converse/headless";
+import { CustomElement } from 'shared/components/element.js';
+import tplMUCOccupant from "./templates/muc-occupant.js";
+
+const { u } = converse.env;
+
+import './styles/muc-occupant.scss';
+
+
+export default class MUCOccupant extends CustomElement {
+
+    constructor () {
+        super();
+        this.muc_jid = null;
+        this.occupant_id = null;
+    }
+
+    static get properties () {
+        return {
+            muc_jid: { type: String },
+            occupant_id: { type: String }
+        }
+    }
+
+    initialize() {
+        super.initialize()
+        const { chatboxes } = _converse.state;
+        this.muc = chatboxes.get(this.muc_jid);
+        this.muc.initialized.then(() => this.requestUpdate());
+        this.model = this.muc.occupants.get(this.occupant_id);
+    }
+
+    render () {
+        return tplMUCOccupant(this);
+    }
+
+    getVcard () {
+        const model = this.model;
+        if (model.vcard) {
+            return model.vcard;
+        }
+        const jid = model?.get('jid') || model?.get('from');
+        return jid ? _converse.state.vcards.get(jid) : null;
+    }
+
+    addToContacts () {
+        const model = this.model;
+        const jid = model.get('jid');
+        if (jid) api.modal.show('converse-add-contact-modal', {'model': new Model({ jid })});
+    }
+
+    toggleForm (ev) {
+        const toggle = u.ancestor(ev.target, '.toggle-form');
+        const form = toggle.getAttribute('data-form');
+
+        if (form === 'row-form') {
+            this.show_role_form = !this.show_role_form;
+        } else {
+            this.show_affiliation_form = !this.show_affiliation_form;
+        }
+        this.render();
+    }
+}
+
+api.elements.define('converse-muc-occupant', MUCOccupant);

+ 2 - 1
src/plugins/muc-views/occupants.js

@@ -3,8 +3,9 @@ import { Model } from '@converse/skeletor';
 import { _converse, api, u, RosterFilter } from "@converse/headless";
 import { CustomElement } from 'shared/components/element.js';
 import tplMUCOccupants from "./templates/muc-occupants.js";
-import './modals/muc-invite.js';
 import 'shared/autocomplete/index.js';
+import './modals/muc-invite.js';
+import './occupant.js';
 
 import 'shared/styles/status.scss';
 import './styles/muc-occupants.scss';

+ 1 - 0
src/plugins/muc-views/sidebar.js

@@ -6,6 +6,7 @@ import './occupants.js';
 import 'shared/autocomplete/index.js';
 
 import 'shared/styles/status.scss';
+import './styles/muc-sidebar.scss';
 
 export default class MUCSidebar extends CustomElement {
 

+ 8 - 0
src/plugins/muc-views/styles/muc-occupant.scss

@@ -0,0 +1,8 @@
+.conversejs {
+    converse-muc-occupant {
+        .back-button {
+            font-size: 10px;
+            padding: 0.5em;
+        }
+    }
+}

+ 0 - 3
src/plugins/muc-views/styles/muc-occupants.scss

@@ -51,9 +51,6 @@
                     padding: 0.5em;
 
                     .occupants-heading {
-                        width: 100%;
-                        font-family: var(--heading-font);
-                        color: var(--groupchats-header-color-dark);
                         padding-left: 0;
                         margin-right: 0.5em;
                     }

+ 10 - 0
src/plugins/muc-views/styles/muc-sidebar.scss

@@ -0,0 +1,10 @@
+.conversejs {
+    converse-muc-sidebar {
+        .sidebar-heading {
+            width: 100%;
+            font-family: var(--heading-font);
+            color: var(--groupchats-header-color-dark);
+            padding-left: 0;
+        }
+    }
+}

+ 103 - 0
src/plugins/muc-views/templates/muc-occupant.js

@@ -0,0 +1,103 @@
+import { html } from 'lit';
+import { until } from 'lit/directives/until.js';
+import { _converse, api } from "@converse/headless";
+import { __ } from 'i18n';
+
+/**
+ * @param {import('../occupant').default} el
+ */
+export default (el) => {
+    const i18n_nickname = __('Nickname');
+    const i18n_affiliation = __('Affiliation');
+    const i18n_participants = __('Participants');
+    const i18n_add_to_contacts = __('Add to Contacts');
+    const i18n_no_occupant = __('No participant data found');
+
+    const jid = el.model?.get('jid');
+    const nick = el.model?.get('nick');
+    const occupant_id = el.model?.get('occupant_id');
+    const role = el.model?.get('role');
+    const affiliation = el.model?.get('affiliation');
+    const hats = el.model?.get('hats')?.length ? el.model.get('hats') : null;
+
+    const allowed_commands = el.muc.getAllowedCommands();
+    const may_moderate = allowed_commands.includes('modtools');
+    const can_see_real_jids = el.muc.features.get('nonanonymous') || el.muc.getOwnRole() === 'moderator';
+    const bare_jid = _converse.session.get('bare_jid');
+    const not_me =  jid != bare_jid;
+
+    const add_to_contacts = api.settings.get('singleton')
+        ? '' // in singleton mode, there is no roster, so adding to contact makes no sense.
+        : api.contacts.get(jid)
+            .then(contact => !contact && not_me && can_see_real_jids)
+            .then(add => add ? html`<li><button class="btn btn-primary" type="button" @click=${() => el.addToContacts()}>${i18n_add_to_contacts}</button></li>` : '');
+
+    return html`<span class="sidebar-heading">
+            <button
+                type="button"
+                class="btn btn--transparent back-button"
+                @click=${() => el.muc.save({ 'sidebar_view': 'occupants' })}
+            >
+                <converse-icon size="1em" class="fa fa-arrow-left"></converse-icon>
+            </button>
+            ${i18n_participants}</span
+        >
+
+        ${el.model ? html`
+            <div class="row">
+                <div class="col-auto">
+                    <converse-avatar
+                        .model=${el.model}
+                        class="avatar modal-avatar"
+                        name="${el.model.getDisplayName()}"
+                        nonce=${el.getVcard()?.get('vcard_updated')}
+                        height="120" width="120"></converse-avatar>
+                </div>
+                <div class="col">
+                    <ul class="occupant-details">
+                        <li>
+                            ${ nick ? html`<div class="row"><strong class="g-0">${i18n_nickname}:</strong></div><div class="row">${nick}</div>` : '' }
+                        </li>
+                        <li>
+                            ${ jid ? html`<div class="row"><strong class="g-0">${__('XMPP Address')}:</strong></div><div class="row">${jid}</div>` : '' }
+                        </li>
+                        <li>
+                            <div class="row"><strong class="g-0">${i18n_affiliation}:</strong></div>
+                            <div class="row">${affiliation}&nbsp;
+                                ${ may_moderate ? html`
+                                    <a href="#"
+                                    data-form="affiliation-form"
+                                    class="toggle-form right"
+                                    color="var(--secondary-color)"
+                                    @click=${(ev) => el.toggleForm(ev)}><converse-icon class="fa fa-wrench" size="1em"></converse-icon>
+                                    </a>
+                                    ${ el.show_affiliation_form ? html`<converse-muc-affiliation-form jid=${jid} .muc=${el.muc} affiliation=${affiliation}></converse-muc-affiliation-form>` : '' }` : ''
+                                }
+                            </div>
+                        </li>
+                        <li>
+                            <div class="row"><strong class="g-0">${__('Role')}:</strong></div>
+                            <div class="row">${role}&nbsp;
+                                ${ may_moderate && role ? html`
+                                    <a href="#"
+                                    data-form="row-form"
+                                    class="toggle-form right"
+                                    color="var(--secondary-color)"
+                                    @click=${(ev) => el.toggleForm(ev)}><converse-icon class="fa fa-wrench" size="1em"></converse-icon>
+                                    </a>
+                                    ${ el.show_role_form ? html`<converse-muc-role-form jid=${jid} .muc=${el.muc} role=${role}></converse-muc-role-form>` : '' }` : ''
+                                }
+                            </div>
+                        </li>
+                        <li>
+                            ${ hats ? html`<div class="row"><strong class="g-0">${__('Hats')}:</strong></div><div class="row">${hats}</div>` : '' }
+                        </li>
+                        <li>
+                            ${ occupant_id ? html`<div class="row"><strong class="g-0">${__('Occupant Id')}:</strong></div><div class="row">${occupant_id}</div>` : '' }
+                        </li>
+                        ${ until(add_to_contacts, '') }
+                    </ul>
+                </div>
+            </div>` : html`<p>${i18n_no_occupant}</p>`
+        }`;
+};

+ 1 - 1
src/plugins/muc-views/templates/muc-occupants.js

@@ -96,7 +96,7 @@ export default (el) => {
         <div class="occupants">
             <div class="occupants-header">
                 <div class="occupants-header--title">
-                    <span class="occupants-heading">${el.model.occupants.length} ${i18n_participants}</span>
+                    <span class="occupants-heading sidebar-heading">${el.model.occupants.length} ${i18n_participants}</span>
                     ${btns.length === 1
                         ? btns[0]
                             : html`<converse-dropdown

+ 7 - 4
src/plugins/muc-views/templates/muc-sidebar.js

@@ -1,13 +1,16 @@
-import 'shared/components/list-filter.js';
-import { __ } from 'i18n';
-import { html } from "lit";
+import { html } from 'lit';
 
 /**
  * @param {import('../sidebar').default} el
  */
 export default (el) => {
+    const model = el.model;
+    const sidebar_view = model.get('sidebar_view');
+    const occupant_id = sidebar_view.split('occupant:').pop();
     return html`
         <div class="dragresize-occupants-left">&nbsp;</div>
-        <converse-muc-occupants jid="${el.jid}"></converse-muc-occupants>
+        ${sidebar_view?.startsWith('occupant:')
+            ? html`<converse-muc-occupant muc_jid="${el.jid}" occupant_id="${occupant_id}"></converse-muc-occupant>`
+            : html`<converse-muc-occupants jid="${el.jid}"></converse-muc-occupants>`}
     `;
 };

+ 1 - 2
src/plugins/muc-views/templates/occupant.js

@@ -6,7 +6,6 @@ import { PRETTY_CHAT_STATUS } from '../constants.js';
 import { __ } from 'i18n';
 import { html } from "lit";
 import { until } from 'lit/directives/until.js';
-import { showOccupantModal } from '../utils.js';
 import { getAuthorStyle } from 'utils/color.js';
 import { getUnreadMsgsDisplay } from 'shared/chat/utils.js';
 
@@ -165,7 +164,7 @@ export default (el, o) => {
         <li class="occupant" id="${o.id}">
             <div class="row g-0">
                 <div class="col-auto">
-                    <a class="show-msg-author-modal" @click=${(ev) => showOccupantModal(ev, o)}>
+                    <a @click=${(ev) => el.model.save({'sidebar_view': `occupant:${o.id}`})}>
                         <converse-avatar
                             .model=${o}
                             class="avatar chat-msg__avatar"

+ 1 - 1
src/plugins/muc-views/tests/muc-private-messages.js

@@ -2,7 +2,7 @@
 const { stx, u } = converse.env;
 
 describe('When receiving a MUC private message', function () {
-    it(
+    fit(
         "doesn't appear in the main MUC chatarea",
         mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
             const muc_jid = 'coven@chat.shakespeare.lit';

+ 5 - 0
src/types/plugins/muc-views/occupant.d.ts

@@ -0,0 +1,5 @@
+export default class MUCOccupant extends CustomElement {
+    render(): import("lit").TemplateResult<1>;
+}
+import { CustomElement } from 'shared/components/element.js';
+//# sourceMappingURL=occupant.d.ts.map

+ 25 - 0
src/types/plugins/muc-views/occupants.d.ts

@@ -0,0 +1,25 @@
+export default class MUCOccupants extends CustomElement {
+    static get properties(): {
+        jid: {
+            type: StringConstructor;
+        };
+    };
+    jid: any;
+    initialize(): void;
+    filter: RosterFilter;
+    model: any;
+    render(): import("lit").TemplateResult<1>;
+    /**
+     * @param {MouseEvent} ev
+     */
+    showInviteModal(ev: MouseEvent): void;
+    /** @param {MouseEvent} ev */
+    toggleFilter(ev: MouseEvent): void;
+    /** @param {MouseEvent} ev */
+    closeSidebar(ev: MouseEvent): void;
+    /** @param {MouseEvent} ev */
+    onOccupantClicked(ev: MouseEvent): void;
+}
+import { CustomElement } from 'shared/components/element.js';
+import { RosterFilter } from "@converse/headless";
+//# sourceMappingURL=occupants.d.ts.map

+ 3 - 0
src/types/plugins/muc-views/templates/muc-occupant.d.ts

@@ -0,0 +1,3 @@
+declare function _default(): import("lit").TemplateResult<1>;
+export default _default;
+//# sourceMappingURL=muc-occupant.d.ts.map

+ 4 - 0
src/types/plugins/muc-views/templates/muc-occupants.d.ts

@@ -0,0 +1,4 @@
+declare function _default(el: import("../occupants").default): import("lit").TemplateResult<1>;
+export default _default;
+export type MUCOccupant = import("@converse/headless").MUCOccupant;
+//# sourceMappingURL=muc-occupants.d.ts.map