Browse Source

Create `converse-omemo-profile` component

Removes the need to override the ProfileModal
JC Brand 3 years ago
parent
commit
284eccf047

+ 7 - 2
src/plugins/omemo/devicelist.js

@@ -126,11 +126,16 @@ const DeviceList = Model.extend({
         return api.pubsub.publish(null, Strophe.NS.OMEMO_DEVICELIST, item, options, false);
     },
 
-    removeOwnDevices (device_ids) {
+    async removeOwnDevices (device_ids) {
         if (this.get('jid') !== _converse.bare_jid) {
             throw new Error("Cannot remove devices from someone else's device list");
         }
-        device_ids.forEach(device_id => this.devices.get(device_id).destroy());
+        await Promise.all(device_ids.map(id => this.devices.get(id)).map(d =>
+            new Promise(resolve => d.destroy({
+                'success': resolve,
+                'error': (m, e) => { log.error(e); resolve(); }
+            }))
+        ));
         return this.publishDevices();
     }
 });

+ 3 - 3
src/plugins/omemo/index.js

@@ -3,6 +3,7 @@
  * @license Mozilla Public License (MPLv2)
  */
 import './fingerprints.js';
+import './profile.js';
 import 'modals/user-details.js';
 import 'plugins/profile/index.js';
 import ChatBox from './overrides/chatbox.js';
@@ -12,7 +13,6 @@ import DeviceList from './devicelist.js';
 import DeviceLists from './devicelists.js';
 import Devices from './devices.js';
 import OMEMOStore from './store.js';
-import ProfileModal from './overrides/profile-modal.js';
 import log from '@converse/headless/log';
 import omemo_api from './api.js';
 import { OMEMOEnabledChatBox } from './mixins/chatbox.js';
@@ -50,9 +50,9 @@ converse.plugins.add('converse-omemo', {
         );
     },
 
-    dependencies: ['converse-chatview', 'converse-pubsub', 'converse-profile'],
+    dependencies: ['converse-chatview', 'converse-pubsub'],
 
-    overrides: { ProfileModal, ChatBox },
+    overrides: { ChatBox },
 
     initialize () {
         api.settings.extend({ 'omemo_default': false });

+ 0 - 73
src/plugins/omemo/overrides/profile-modal.js

@@ -1,73 +0,0 @@
-import debounce from 'lodash-es/debounce';
-import log from '@converse/headless/log';
-import { __ } from 'i18n';
-import { _converse, api, converse } from '@converse/headless/core';
-
-const { Strophe, sizzle, u } = converse.env;
-
-
-const ProfileModal = {
-    events: {
-        'change input.select-all': 'selectAll',
-        'click .generate-bundle': 'generateOMEMODeviceBundle',
-        'submit .fingerprint-removal': 'removeSelectedFingerprints'
-    },
-
-    initialize () {
-        this.debouncedRender = debounce(this.render, 50);
-        this.devicelist = _converse.devicelists.get(_converse.bare_jid);
-        this.listenTo(this.devicelist.devices, 'change:bundle', this.debouncedRender);
-        this.listenTo(this.devicelist.devices, 'reset', this.debouncedRender);
-        this.listenTo(this.devicelist.devices, 'reset', this.debouncedRender);
-        this.listenTo(this.devicelist.devices, 'remove', this.debouncedRender);
-        this.listenTo(this.devicelist.devices, 'add', this.debouncedRender);
-        return this.__super__.initialize.apply(this, arguments);
-    },
-
-    beforeRender () {
-        const device_id = _converse.omemo_store?.get('device_id');
-        if (device_id) {
-            this.current_device = this.devicelist.devices.get(device_id);
-            this.other_devices = this.devicelist.devices.filter(d => d.get('id') !== device_id);
-        }
-        return this.__super__.beforeRender?.apply(this, arguments);
-    },
-
-    selectAll (ev) {
-        let sibling = u.ancestor(ev.target, 'li');
-        while (sibling) {
-            sibling.querySelector('input[type="checkbox"]').checked = ev.target.checked;
-            sibling = sibling.nextElementSibling;
-        }
-    },
-
-    removeSelectedFingerprints (ev) {
-        ev.preventDefault();
-        ev.stopPropagation();
-        ev.target.querySelector('.select-all').checked = false;
-        const device_ids = sizzle('.fingerprint-removal-item input[type="checkbox"]:checked', ev.target).map(
-            c => c.value
-        );
-        this.devicelist
-            .removeOwnDevices(device_ids)
-            .then(this.modal.hide)
-            .catch(err => {
-                log.error(err);
-                _converse.api.alert(Strophe.LogLevel.ERROR, __('Error'), [
-                    __('Sorry, an error occurred while trying to remove the devices.')
-                ]);
-            });
-    },
-
-    generateOMEMODeviceBundle (ev) {
-        ev.preventDefault();
-        if (confirm(__(
-            'Are you sure you want to generate new OMEMO keys? ' +
-            'This will remove your old keys and all previously encrypted messages will no longer be decryptable on this device.'
-        ))) {
-            api.omemo.bundle.generate();
-        }
-    }
-}
-
-export default ProfileModal;

+ 74 - 0
src/plugins/omemo/profile.js

@@ -0,0 +1,74 @@
+import log from '@converse/headless/log';
+import tpl_profile from './templates/profile.js';
+import { CustomElement } from 'shared/components/element.js';
+import { __ } from 'i18n';
+import { _converse, api, converse } from "@converse/headless/core";
+
+const { Strophe, sizzle, u } = converse.env;
+
+
+export class Profile extends CustomElement {
+
+    async initialize () {
+        this.devicelist = await _converse.devicelists.getDeviceList(_converse.bare_jid);
+        await this.setAttributes();
+        this.listenTo(this.devicelist.devices, 'change:bundle', () => this.requestUpdate());
+        this.listenTo(this.devicelist.devices, 'reset', () => this.requestUpdate());
+        this.listenTo(this.devicelist.devices, 'reset', () => this.requestUpdate());
+        this.listenTo(this.devicelist.devices, 'remove', () => this.requestUpdate());
+        this.listenTo(this.devicelist.devices, 'add', () => this.requestUpdate());
+        this.requestUpdate();
+    }
+
+    async setAttributes () {
+        this.device_id = await api.omemo.getDeviceID();
+        this.current_device = this.devicelist.devices.get(this.device_id);
+        this.other_devices = this.devicelist.devices.filter(d => d.get('id') !== this.device_id);
+    }
+
+    render () {
+        return this.devicelist ? tpl_profile(this) : '';
+    }
+
+    selectAll (ev) {  // eslint-disable-line class-methods-use-this
+        let sibling = u.ancestor(ev.target, 'li');
+        while (sibling) {
+            sibling.querySelector('input[type="checkbox"]').checked = ev.target.checked;
+            sibling = sibling.nextElementSibling;
+        }
+    }
+
+    async removeSelectedFingerprints (ev) {
+        ev.preventDefault();
+        ev.stopPropagation();
+        ev.target.querySelector('.select-all').checked = false;
+        const device_ids = sizzle('.fingerprint-removal-item input[type="checkbox"]:checked', ev.target).map(
+            c => c.value
+        );
+
+        try {
+            await this.devicelist.removeOwnDevices(device_ids);
+        } catch (err) {
+            log.error(err);
+            _converse.api.alert(Strophe.LogLevel.ERROR, __('Error'), [
+                __('Sorry, an error occurred while trying to remove the devices.')
+            ]);
+        }
+        await this.setAttributes();
+        this.requestUpdate();
+    }
+
+    async generateOMEMODeviceBundle (ev) {
+        ev.preventDefault();
+        if (confirm(__(
+            'Are you sure you want to generate new OMEMO keys? ' +
+            'This will remove your old keys and all previously encrypted messages will no longer be decryptable on this device.'
+        ))) {
+            await api.omemo.bundle.generate();
+            await this.setAttributes();
+            this.requestUpdate();
+        }
+    }
+}
+
+api.elements.define('converse-omemo-profile', Profile);

+ 81 - 0
src/plugins/omemo/templates/profile.js

@@ -0,0 +1,81 @@
+import spinner from "templates/spinner.js";
+import { formatFingerprint } from 'plugins/omemo/utils.js';
+import { html } from "lit";
+import { __ } from 'i18n';
+
+
+const fingerprint = (el) => html`
+    <span class="fingerprint">${formatFingerprint(el.current_device.get('bundle').fingerprint)}</span>`;
+
+
+const device_with_fingerprint = (el) => {
+    const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following fingerprint');
+    return html`
+        <li class="fingerprint-removal-item list-group-item nopadding">
+            <label>
+            <input type="checkbox" value="${el.device.get('id')}"
+                aria-label="${i18n_fingerprint_checkbox_label}"/>
+            <span class="fingerprint">${formatFingerprint(el.device.get('bundle').fingerprint)}</span>
+            </label>
+        </li>
+    `;
+}
+
+
+const device_without_fingerprint = (el) => {
+    const i18n_device_without_fingerprint = __('Device without a fingerprint');
+    const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following device');
+    return html`
+        <li class="fingerprint-removal-item list-group-item nopadding">
+            <label>
+            <input type="checkbox" value="${el.device.get('id')}"
+                aria-label="${i18n_fingerprint_checkbox_label}"/>
+            <span>${i18n_device_without_fingerprint}</span>
+            </label>
+        </li>
+    `;
+}
+
+
+const device_item = (el) => html`
+    ${(el.device.get('bundle') && el.device.get('bundle').fingerprint) ? device_with_fingerprint(el) : device_without_fingerprint(el) }
+`;
+
+
+const device_list = (el) => {
+    const i18n_other_devices = __('Other OMEMO-enabled devices');
+    const i18n_other_devices_label = __('Checkbox to select fingerprints of all other OMEMO devices');
+    const i18n_remove_devices = __('Remove checked devices and close');
+    const i18n_select_all = __('Select all');
+    return html`
+        <ul class="list-group fingerprints">
+            <li class="list-group-item nopadding active">
+                <label>
+                    <input type="checkbox" class="select-all" @change=${el.selectAll} title="${i18n_select_all}" aria-label="${i18n_other_devices_label}"/>
+                    ${i18n_other_devices}
+                </label>
+            </li>
+            ${ el.other_devices?.map(device => device_item(Object.assign({device}, el))) }
+        </ul>
+        <div class="form-group"><button type="submit" class="save-form btn btn-primary">${i18n_remove_devices}</button></div>
+    `;
+}
+
+
+export default (el) => {
+    const i18n_fingerprint = __("This device's OMEMO fingerprint");
+    const i18n_generate = __('Generate new keys and fingerprint');
+    return html`
+        <form class="converse-form fingerprint-removal" @submit=${el.removeSelectedFingerprints}>
+            <ul class="list-group fingerprints">
+                <li class="list-group-item active">${i18n_fingerprint}</li>
+                <li class="list-group-item">
+                    ${ (el.current_device && el.current_device.get('bundle') && el.current_device.get('bundle').fingerprint) ? fingerprint(el) : spinner() }
+                </li>
+            </ul>
+            <div class="form-group">
+                <button type="button" class="generate-bundle btn btn-danger" @click=${el.generateOMEMODeviceBundle}>${i18n_generate}</button>
+            </div>
+            ${ el.other_devices?.length ? device_list(el) : '' }
+        </form>`;
+}

+ 8 - 85
src/plugins/profile/templates/profile_modal.js

@@ -1,90 +1,13 @@
 import "shared/components/image-picker.js";
-import spinner from "templates/spinner.js";
 import { __ } from 'i18n';
 import { _converse } from  "@converse/headless/core";
 import { html } from "lit";
 import { modal_header_close_button } from "plugins/modal/templates/buttons.js";
-import { formatFingerprint } from 'plugins/omemo/utils.js';
 
-
-const fingerprint = (o) => html`
-    <span class="fingerprint">${formatFingerprint(o.view.current_device.get('bundle').fingerprint)}</span>`;
-
-
-const device_with_fingerprint = (o) => {
-    const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following fingerprint');
-    return html`
-        <li class="fingerprint-removal-item list-group-item nopadding">
-            <label>
-            <input type="checkbox" value="${o.device.get('id')}"
-                aria-label="${i18n_fingerprint_checkbox_label}"/>
-            <span class="fingerprint">${formatFingerprint(o.device.get('bundle').fingerprint)}</span>
-            </label>
-        </li>
-    `;
-}
-
-
-const device_without_fingerprint = (o) => {
-    const i18n_device_without_fingerprint = __('Device without a fingerprint');
-    const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following device');
-    return html`
-        <li class="fingerprint-removal-item list-group-item nopadding">
-            <label>
-            <input type="checkbox" value="${o.device.get('id')}"
-                aria-label="${i18n_fingerprint_checkbox_label}"/>
-            <span>${i18n_device_without_fingerprint}</span>
-            </label>
-        </li>
-    `;
-}
-
-
-const device_item = (o) => html`
-    ${(o.device.get('bundle') && o.device.get('bundle').fingerprint) ? device_with_fingerprint(o) : device_without_fingerprint(o) }
-`;
-
-
-const device_list = (o) => {
-    const i18n_other_devices = __('Other OMEMO-enabled devices');
-    const i18n_other_devices_label = __('Checkbox to select fingerprints of all other OMEMO devices');
-    const i18n_remove_devices = __('Remove checked devices and close');
-    const i18n_select_all = __('Select all');
-    return html`
-        <ul class="list-group fingerprints">
-            <li class="list-group-item nopadding active">
-                <label>
-                    <input type="checkbox" class="select-all" title="${i18n_select_all}" aria-label="${i18n_other_devices_label}"/>
-                    ${i18n_other_devices}
-                </label>
-            </li>
-            ${ o.view.other_devices?.map(device => device_item(Object.assign({device}, o))) }
-        </ul>
-        <div class="form-group"><button type="submit" class="save-form btn btn-primary">${i18n_remove_devices}</button></div>
-    `;
-}
-
-
-// TODO: this needs to go as a component into the OMEMO plugin folder
-const omemo_page = (o) => {
-    const i18n_fingerprint = __("This device's OMEMO fingerprint");
-    const i18n_generate = __('Generate new keys and fingerprint');
-    return html`
-        <div class="tab-pane" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
-            <form class="converse-form fingerprint-removal">
-                <ul class="list-group fingerprints">
-                    <li class="list-group-item active">${i18n_fingerprint}</li>
-                    <li class="list-group-item">
-                        ${ (o.view.current_device && o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) ? fingerprint(o) : spinner() }
-                    </li>
-                </ul>
-                <div class="form-group">
-                    <button type="button" class="generate-bundle btn btn-danger">${i18n_generate}</button>
-                </div>
-                ${ o.view.other_devices?.length ? device_list(o) : '' }
-            </form>
-        </div>`;
-}
+const omemo_page = () => html`
+    <div class="tab-pane" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
+        <converse-omemo-profile></converse-omemo-profile>
+    </div>`;
 
 
 export default (o) => {
@@ -100,7 +23,7 @@ export default (o) => {
     const i18n_omemo = __('OMEMO');
     const i18n_profile = __('Profile');
 
-    const navigation = o.view.current_device ?
+    const navigation =
         html`<ul class="nav nav-pills justify-content-center">
             <li role="presentation" class="nav-item">
                 <a class="nav-link active" id="profile-tab" href="#profile-tabpanel" aria-controls="profile-tabpanel" role="tab" data-toggle="tab">${i18n_profile}</a>
@@ -108,7 +31,7 @@ export default (o) => {
             <li role="presentation" class="nav-item">
                 <a class="nav-link" id="omemo-tab" href="#omemo-tabpanel" aria-controls="omemo-tabpanel" role="tab" data-toggle="tab">${i18n_omemo}</a>
             </li>
-        </ul>` : '';
+        </ul>`;
 
     return html`
         <div class="modal-dialog" role="document">
@@ -119,7 +42,7 @@ export default (o) => {
                 </div>
                 <div class="modal-body">
                     <span class="modal-alert"></span>
-                    ${_converse.pluggable.plugins['converse-omemo'].enabled(_converse) && navigation || ''}
+                    ${_converse.pluggable.plugins['converse-omemo']?.enabled(_converse) ? navigation : ''}
                     <div class="tab-content">
                         <div class="tab-pane active" id="profile-tabpanel" role="tabpanel" aria-labelledby="profile-tab">
                             <form class="converse-form converse-form--modal profile-form" action="#">
@@ -161,7 +84,7 @@ export default (o) => {
                                 </div>
                             </form>
                         </div>
-                        ${ _converse.pluggable.plugins['converse-omemo'].enabled(_converse) && omemo_page(o) || '' }
+                        ${ _converse.pluggable.plugins['converse-omemo']?.enabled(_converse) ? omemo_page(o) : '' }
                     </div>
                 </div>
             </div>