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

Add modal for changing your nickname...

once you're already in a MUC.
JC Brand 3 жил өмнө
parent
commit
f4fdc36d31

+ 1 - 1
CHANGES.md

@@ -5,8 +5,8 @@
 - Updated translations: lt
 - Increased stanza timeout from 10 to 20 seconds
 - Replace various font icons with SVG icons
-- Fix OMEMO race condition related to automatic reconnection
 - #1761: Add a new dark theme based on the [Dracula](https://draculatheme.com/) theme
+- #2733: Fix OMEMO race condition related to automatic reconnection
 - #2751: Media not rendered when Converse runs in a browser extension
 - #2786: Fix webpack configuration not working on Windows OS
 - #2788: `TypeError` when trying to use `@converse/headless`

+ 15 - 8
src/headless/plugins/muc/muc.js

@@ -124,6 +124,10 @@ const ChatRoomMixin = {
         this.initialized.resolve();
     },
 
+    isEntered () {
+        return this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED;
+    },
+
     /**
      * Checks whether we're still joined and if so, restores the MUC state from cache.
      * @private
@@ -131,7 +135,7 @@ const ChatRoomMixin = {
      * @returns { Boolean } Returns `true` if we're still joined, otherwise returns `false`.
      */
     async restoreFromCache () {
-        if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED && (await this.isJoined())) {
+        if (this.isEntered() && (await this.isJoined())) {
             // We've restored the room from cache and we're still joined.
             await new Promise(resolve => this.features.fetch({ 'success': resolve, 'error': resolve }));
             await this.fetchOccupants().catch(e => log.error(e));
@@ -154,7 +158,7 @@ const ChatRoomMixin = {
      *  model (if available).
      */
     async join (nick, password) {
-        if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
+        if (this.isEntered()) {
             // We have restored a groupchat from session storage,
             // so we don't send out a presence stanza again.
             return this;
@@ -293,7 +297,7 @@ const ChatRoomMixin = {
     onOccupantRemoved (occupant) {
         if (
             _converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.EXITED) &&
-            this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED &&
+            this.isEntered() &&
             occupant.get('show') === 'online'
         ) {
             this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.EXITED);
@@ -339,7 +343,7 @@ const ChatRoomMixin = {
     },
 
     async onConnectionStatusChanged () {
-        if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
+        if (this.isEntered()) {
             if (this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
                 try {
                     await this.leave();
@@ -491,8 +495,7 @@ const ChatRoomMixin = {
      * @param { XMLElement } stanza
      */
     handleMessageFromMUCHost (stanza) {
-        const conn_status = this.session.get('connection_status');
-        if (conn_status === converse.ROOMSTATUS.ENTERED) {
+        if (this.isEntered()) {
             // We're not interested in activity indicators when already joined to the room
             return;
         }
@@ -513,7 +516,7 @@ const ChatRoomMixin = {
      * @param { XMLElement } stanza
      */
     handleForwardedMentions (stanza) {
-        if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
+        if (this.isEntered()) {
             // Avoid counting mentions twice
             return;
         }
@@ -1023,7 +1026,7 @@ const ChatRoomMixin = {
         if (
             !api.settings.get('send_chat_state_notifications') ||
             !this.get('chat_state') ||
-            this.session.get('connection_status') !== converse.ROOMSTATUS.ENTERED ||
+            !this.isEntered() ||
             (this.features.get('moderated') && this.getOwnRole() === 'visitor')
         ) {
             return;
@@ -1404,6 +1407,10 @@ const ChatRoomMixin = {
         return this.occupants.getOwnOccupant();
     },
 
+    /**
+     * Send a presence stanza to update the user's nickname in this MUC.
+     * @param { String } nick
+     */
     async setNickname (nick) {
         if (
             api.settings.get('auto_register_muc_nickname') &&

+ 10 - 0
src/plugins/muc-views/heading.js

@@ -1,5 +1,6 @@
 import { ElementView } from '@converse/skeletor/src/element.js';
 import MUCInviteModal from 'modals/muc-invite.js';
+import NicknameModal from './modals/nickname.js';
 import RoomDetailsModal from 'modals/muc-details.js';
 import debounce from 'lodash-es/debounce';
 import tpl_muc_head from './templates/muc-head.js';
@@ -107,6 +108,15 @@ export default class MUCHeading extends ElementView {
             });
         }
 
+        buttons.push({
+            'i18n_text': __('Nickname'),
+            'i18n_title': __("Change the nickname you're using in this groupchat"),
+            'handler': ev => api.modal.show(NicknameModal, { 'model': this.model }, ev),
+            'a_class': 'open-nickname-modal',
+            'icon_class': 'fa-smile',
+            'name': 'nickname'
+        });
+
         if (this.model.invitesAllowed()) {
             buttons.push({
                 'i18n_text': __('Invite'),

+ 15 - 0
src/plugins/muc-views/modals/nickname.js

@@ -0,0 +1,15 @@
+import tpl_nickname from "./templates/nickname.js";
+import BaseModal from "plugins/modal/base.js";
+
+export default BaseModal.extend({
+    id: 'change-nickname-modal',
+
+    initialize (attrs) {
+        this.model = attrs.model;
+        BaseModal.prototype.initialize.apply(this, arguments);
+    },
+
+    toHTML () {
+        return tpl_nickname(this);
+    },
+});

+ 21 - 0
src/plugins/muc-views/modals/templates/nickname.js

@@ -0,0 +1,21 @@
+import { __ } from 'i18n';
+import { html } from "lit";
+import { modal_header_close_button } from "plugins/modal/templates/buttons.js"
+
+export default (modal) => {
+    const jid = modal.model.get('jid');
+    const i18n_heading = __('Change your nickname');
+    return html`
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title" id="converse-modtools-modal-label">
+                        ${i18n_heading}</h5>
+                    ${modal_header_close_button}
+                </div>
+                <div class="modal-body d-flex flex-column">
+                    <converse-muc-nickname-form jid="${jid}"></converse-muc-nickname-form>
+                </div>
+            </div>
+        </div>`;
+}

+ 21 - 1
src/plugins/muc-views/nickname-form.js

@@ -19,7 +19,27 @@ class MUCNicknameForm extends CustomElement {
     }
 
     render () {
-        return tpl_muc_nickname_form(this.model);
+        return tpl_muc_nickname_form(this);
+    }
+
+    submitNickname (ev) {
+        ev.preventDefault();
+        const nick = ev.target.nick.value.trim();
+        if (!nick) {
+            return;
+        }
+        if (this.model.isEntered()) {
+            this.model.setNickname(nick);
+            this.closeModal();
+        } else {
+            this.model.join(nick);
+        }
+    }
+
+    closeModal () {
+        const evt = document.createEvent('Event');
+        evt.initEvent('hide.bs.modal', true, true);
+        this.dispatchEvent(evt);
     }
 }
 

+ 4 - 2
src/plugins/muc-views/templates/muc-bottom-panel.js

@@ -1,6 +1,6 @@
 import '../message-form.js';
+import '../nickname-form.js';
 import 'shared/chat/toolbar.js';
-import tpl_muc_nickname_form from './muc-nickname-form.js';
 import { __ } from 'i18n';
 import { api, converse } from "@converse/headless/core";
 import { html } from "lit";
@@ -45,7 +45,9 @@ export default (o) => {
             ${(o.can_edit) ? tpl_can_edit(o) : html`<span class="muc-bottom-panel muc-bottom-panel--muted">${i18n_not_allowed}</span>`}`;
     } else if (conn_status == converse.ROOMSTATUS.NICKNAME_REQUIRED) {
         if (api.settings.get('muc_show_logs_before_join')) {
-            return html`<span class="muc-bottom-panel muc-bottom-panel--nickname">${tpl_muc_nickname_form(o.model)}</span>`;
+            return html`<span class="muc-bottom-panel muc-bottom-panel--nickname">
+                <converse-muc-nickname-form jid="${o.model.get('jid')}"></converse-muc-nickname-form>
+            </span>`;
         }
     } else {
         return '';

+ 5 - 11
src/plugins/muc-views/templates/muc-nickname-form.js

@@ -2,24 +2,18 @@ import { __ } from 'i18n';
 import { api } from "@converse/headless/core";
 import { html } from "lit";
 
-function submitNickname (ev, model) {
-    ev.preventDefault();
-    const nick = ev.target.nick.value.trim();
-    nick && model.join(nick);
-}
-
-export default (model) => {
+export default (el) => {
     const i18n_nickname =  __('Nickname');
-    const i18n_join = __('Enter groupchat');
+    const i18n_join = el.model?.isEntered() ? __('Change nickname') : __('Enter groupchat');
     const i18n_heading = api.settings.get('muc_show_logs_before_join') ?
         __('Choose a nickname to enter') :
         __('Please choose your nickname');
 
-    const validation_message = model.get('nickname_validation_message');
+    const validation_message = el.model?.get('nickname_validation_message');
 
     return html`
         <div class="chatroom-form-container muc-nickname-form"
-                @submit=${ev => submitNickname(ev, model)}>
+                @submit=${ev => el.submitNickname(ev)}>
             <form class="converse-form chatroom-form converse-centered-form">
                 <fieldset class="form-group">
                     <label>${i18n_heading}</label>
@@ -27,7 +21,7 @@ export default (model) => {
                     <input type="text"
                         required="required"
                         name="nick"
-                        value="${model.get('nick') || ''}"
+                        value="${el.model?.get('nick') || ''}"
                         class="form-control ${validation_message ? 'error': ''}"
                         placeholder="${i18n_nickname}"/>
                 </fieldset>

+ 31 - 3
src/plugins/muc-views/tests/nickname.js

@@ -2,7 +2,36 @@
 
 const { $pres, $iq, Strophe, sizzle, u }  = converse.env;
 
-fdescribe("A MUC", function () {
+describe("A MUC", function () {
+
+    it("allows you to change your nickname via a modal",
+            mock.initConverse([], {'view_mode': 'fullscreen'}, async function (_converse) {
+
+        const muc_jid = 'lounge@montague.lit';
+        const nick = 'romeo';
+        await mock.openAndEnterChatRoom(_converse, muc_jid, nick);
+
+        const view = _converse.chatboxviews.get(muc_jid);
+        const dropdown_item = view.querySelector(".open-nickname-modal");
+        dropdown_item.click();
+
+        const modal = _converse.api.modal.get('change-nickname-modal');
+        await u.waitUntil(() => u.isVisible(modal.el));
+
+        const input = modal.el.querySelector('input[name="nick"]');
+        expect(input.value).toBe(nick);
+
+        const newnick = 'loverboy';
+        input.value = newnick;
+        modal.el.querySelector('input[type="submit"]')?.click();
+
+        await u.waitUntil(() => !u.isVisible(modal.el));
+
+        const { sent_stanzas } = _converse.connection;
+        const sent_stanza = sent_stanzas.pop()
+        expect(Strophe.serialize(sent_stanza).toLocaleString()).toBe(
+            `<presence from="${_converse.jid}" id="${sent_stanza.getAttribute('id')}" to="${muc_jid}/${newnick}" xmlns="jabber:client"/>`);
+    }));
 
     it("informs users if their nicknames have been changed.",
             mock.initConverse([], {}, async function (_converse) {
@@ -44,9 +73,8 @@ fdescribe("A MUC", function () {
          */
         const __ = _converse.__;
         await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'oldnick');
-        const view = _converse.chatboxviews.get('lounge@montague.lit');
-        expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
 
+        const view = _converse.chatboxviews.get('lounge@montague.lit');
         await u.waitUntil(() => view.querySelectorAll('li .occupant-nick').length, 500);
         let occupants = view.querySelector('.occupant-list');
         expect(occupants.childElementCount).toBe(1);

+ 2 - 2
webpack.html

@@ -38,8 +38,8 @@
             muc_domain: 'conference.chat.example.org',
             muc_respect_autojoin: true,
             view_mode: 'fullscreen',
-            // websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
-            websocket_url: 'wss://conversejs.org/xmpp-websocket',
+            websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
+            // websocket_url: 'wss://conversejs.org/xmpp-websocket',
             // bosh_service_url: 'http://chat.example.org:5280/http-bind',
             allow_user_defined_connection_url: true,
             muc_show_logs_before_join: true,

+ 1 - 1
webpack/webpack.serve.js

@@ -9,7 +9,7 @@ module.exports = merge(common, {
     devtool: "inline-source-map",
     devServer: {
         static: [ path.resolve(__dirname, '../') ],
-        port: 3004
+        port: 3003
     },
     plugins: [
         new HTMLWebpackPlugin({