diff --git a/CHANGES.md b/CHANGES.md index 44063fdf4..c312cc145 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,7 +7,9 @@ - Fix `autojoin` checkbox state in MUC bookmark form - Remove call to `api.confirm` in `@converse/headless` - Generate TypeScript declaration files into `dist/types` +- Removed documentation about the no longer implemented `fullname` option. - #3156: Add function to prevent drag stutter effect over iframes when resize is called in overlay mode +- #3165: Use configured nickname in profile view in the control box - New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#stanza-timeout) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 29f20988b..1f4b6047d 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -925,12 +925,6 @@ filter_url_query_params Accepts a string or array of strings. Any query strings from URLs that match this setting will be removed. -fullname --------- - -If you are using prebinding, can specify the fullname of the currently -logged in user, otherwise the user's vCard will be fetched. - geouri_regex ------------ diff --git a/karma.conf.js b/karma.conf.js index 90fa14b6c..2a6268a3c 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -104,6 +104,8 @@ module.exports = function(config) { { pattern: "src/plugins/omemo/tests/muc.js", type: 'module' }, { pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' }, { pattern: "src/plugins/profile/tests/password-reset.js", type: 'module' }, + { pattern: "src/plugins/profile/tests/profile.js", type: 'module' }, + { pattern: "src/plugins/profile/tests/status.js", type: 'module' }, { pattern: "src/plugins/push/tests/push.js", type: 'module' }, { pattern: "src/plugins/register/tests/register.js", type: 'module' }, { pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' }, diff --git a/src/headless/plugins/status/status.js b/src/headless/plugins/status/status.js index d01927862..2428d5cb2 100644 --- a/src/headless/plugins/status/status.js +++ b/src/headless/plugins/status/status.js @@ -5,11 +5,11 @@ import { _converse, api, converse } from '@converse/headless/core'; const { Strophe, $pres } = converse.env; -const XMPPStatus = Model.extend({ +export default class XMPPStatus extends Model { - defaults () { + defaults () { // eslint-disable-line class-methods-use-this return { "status": api.settings.get("default_state") } - }, + } initialize () { this.on('change', item => { @@ -20,16 +20,19 @@ const XMPPStatus = Model.extend({ api.user.presence.send(this.get('status'), null, this.get('status_message')); } }); - }, + } - getNickname () { + getDisplayName () { + return this.getFullname() || this.getNickname() || _converse.bare_jid; + } + + getNickname () { // eslint-disable-line class-methods-use-this return api.settings.get('nickname'); - }, + } - getFullname () { - // Gets overridden in converse-vcard - return ''; - }, + getFullname () { // eslint-disable-line class-methods-use-this + return ''; // Gets overridden in converse-vcard + } /** Constructs a presence stanza * @param { string } [type] @@ -85,6 +88,4 @@ const XMPPStatus = Model.extend({ presence = await api.hook('constructedPresence', null, presence); return presence; } -}); - -export default XMPPStatus; +} diff --git a/src/plugins/controlbox/tests/controlbox.js b/src/plugins/controlbox/tests/controlbox.js index ebe70cb31..01100c0de 100644 --- a/src/plugins/controlbox/tests/controlbox.js +++ b/src/plugins/controlbox/tests/controlbox.js @@ -2,7 +2,6 @@ const $msg = converse.env.$msg; const u = converse.env.utils; -const Strophe = converse.env.Strophe; describe("The Controlbox", function () { @@ -105,67 +104,4 @@ describe("The Controlbox", function () { await u.waitUntil(() => rosterview.querySelector('.msgs-indicator') === null); })); }); - - describe("The Status Widget", function () { - - it("shows the user's chat status, which is online by default", - mock.initConverse([], {}, async function (_converse) { - mock.openControlBox(_converse); - const view = await u.waitUntil(() => document.querySelector('converse-user-profile')); - expect(u.hasClass('online', view.querySelector('.xmpp-status span:first-child'))).toBe(true); - expect(view.querySelector('.xmpp-status span.online').textContent.trim()).toBe('I am online'); - })); - - it("can be used to set the current user's chat status", - mock.initConverse([], {}, async function (_converse) { - - await mock.openControlBox(_converse); - const cbview = _converse.chatboxviews.get('controlbox'); - cbview.querySelector('.change-status').click() - const modal = _converse.api.modal.get('converse-chat-status-modal'); - await u.waitUntil(() => u.isVisible(modal), 1000); - modal.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd" - modal.querySelector('[type="submit"]').click(); - const sent_stanzas = _converse.connection.sent_stanzas; - const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop()); - expect(Strophe.serialize(sent_presence)).toBe( - ``+ - `dnd`+ - `0`+ - ``+ - ``); - const view = await u.waitUntil(() => document.querySelector('converse-user-profile')); - const first_child = view.querySelector('.xmpp-status span:first-child'); - expect(u.hasClass('online', first_child)).toBe(false); - expect(u.hasClass('dnd', first_child)).toBe(true); - expect(view.querySelector('.xmpp-status span:first-child').textContent.trim()).toBe('I am busy'); - })); - - it("can be used to set a custom status message", - mock.initConverse([], {}, async function (_converse) { - - await mock.openControlBox(_converse); - const cbview = _converse.chatboxviews.get('controlbox'); - cbview.querySelector('.change-status').click() - const modal = _converse.api.modal.get('converse-chat-status-modal'); - - await u.waitUntil(() => u.isVisible(modal), 1000); - const msg = 'I am happy'; - modal.querySelector('input[name="status_message"]').value = msg; - modal.querySelector('[type="submit"]').click(); - const sent_stanzas = _converse.connection.sent_stanzas; - const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop()); - expect(Strophe.serialize(sent_presence)).toBe( - ``+ - `I am happy`+ - `0`+ - ``+ - ``); - - const view = await u.waitUntil(() => document.querySelector('converse-user-profile')); - const first_child = view.querySelector('.xmpp-status span:first-child'); - expect(u.hasClass('online', first_child)).toBe(true); - expect(view.querySelector('.xmpp-status span:first-child').textContent.trim()).toBe(msg); - })); - }); }); diff --git a/src/plugins/profile/templates/profile.js b/src/plugins/profile/templates/profile.js index 18a5e2d4b..a0ab723d3 100644 --- a/src/plugins/profile/templates/profile.js +++ b/src/plugins/profile/templates/profile.js @@ -1,6 +1,6 @@ import 'shared/avatar/avatar.js'; import { __ } from 'i18n'; -import { _converse, api } from "@converse/headless/core"; +import { api } from "@converse/headless/core"; import { getPrettyStatus, logOut } from '../utils.js'; import { html } from "lit"; @@ -21,7 +21,6 @@ function tplUserSettingsButton (o) { export default (el) => { const chat_status = el.model.get('status') || 'offline'; - const fullname = el.model.vcard?.get('fullname') || el.model.vcard?.get('nickname') || _converse.bare_jid; const status_message = el.model.get('status_message') || __("I am %1$s", getPrettyStatus(chat_status)); const i18n_change_status = __('Click to change your chat status'); const show_settings_button = api.settings.get('show_client_info') || api.settings.get('allow_adhoc_commands'); @@ -44,7 +43,7 @@ export default (el) => { nonce=${el.model.vcard?.get('vcard_updated')} height="40" width="40"> - ${fullname} + ${el.model.getDisplayName()} ${show_settings_button ? tplUserSettingsButton(el) : ''} ${api.settings.get('allow_logout') ? tplSignout() : ''} diff --git a/src/plugins/profile/tests/profile.js b/src/plugins/profile/tests/profile.js new file mode 100644 index 000000000..38e2a789d --- /dev/null +++ b/src/plugins/profile/tests/profile.js @@ -0,0 +1,17 @@ +/*global mock, converse */ + +const u = converse.env.utils; + +describe("The Controlbox", function () { + describe("The user profile", function () { + + it("shows the user's configured nickname", + mock.initConverse([], { blacklisted_plugins: ['converse-vcard'], nickname: 'nicky'}, + async function (_converse) { + + mock.openControlBox(_converse); + const el = await u.waitUntil(() => document.querySelector('converse-user-profile .username')); + expect(el.textContent).toBe('nicky'); + })); + }); +}); diff --git a/src/plugins/profile/tests/status.js b/src/plugins/profile/tests/status.js new file mode 100644 index 000000000..18a206962 --- /dev/null +++ b/src/plugins/profile/tests/status.js @@ -0,0 +1,69 @@ +/*global mock, converse */ + +const u = converse.env.utils; +const Strophe = converse.env.Strophe; + +describe("The Controlbox", function () { + describe("The Status Widget", function () { + + it("shows the user's chat status, which is online by default", + mock.initConverse([], {}, async function (_converse) { + mock.openControlBox(_converse); + const view = await u.waitUntil(() => document.querySelector('converse-user-profile')); + expect(u.hasClass('online', view.querySelector('.xmpp-status span:first-child'))).toBe(true); + expect(view.querySelector('.xmpp-status span.online').textContent.trim()).toBe('I am online'); + })); + + it("can be used to set the current user's chat status", + mock.initConverse([], {}, async function (_converse) { + + await mock.openControlBox(_converse); + const cbview = _converse.chatboxviews.get('controlbox'); + cbview.querySelector('.change-status').click() + const modal = _converse.api.modal.get('converse-chat-status-modal'); + await u.waitUntil(() => u.isVisible(modal), 1000); + modal.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd" + modal.querySelector('[type="submit"]').click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop()); + expect(Strophe.serialize(sent_presence)).toBe( + ``+ + `dnd`+ + `0`+ + ``+ + ``); + const view = await u.waitUntil(() => document.querySelector('converse-user-profile')); + const first_child = view.querySelector('.xmpp-status span:first-child'); + expect(u.hasClass('online', first_child)).toBe(false); + expect(u.hasClass('dnd', first_child)).toBe(true); + expect(view.querySelector('.xmpp-status span:first-child').textContent.trim()).toBe('I am busy'); + })); + + it("can be used to set a custom status message", + mock.initConverse([], {}, async function (_converse) { + + await mock.openControlBox(_converse); + const cbview = _converse.chatboxviews.get('controlbox'); + cbview.querySelector('.change-status').click() + const modal = _converse.api.modal.get('converse-chat-status-modal'); + + await u.waitUntil(() => u.isVisible(modal), 1000); + const msg = 'I am happy'; + modal.querySelector('input[name="status_message"]').value = msg; + modal.querySelector('[type="submit"]').click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const sent_presence = await u.waitUntil(() => sent_stanzas.filter(s => Strophe.serialize(s).match('presence')).pop()); + expect(Strophe.serialize(sent_presence)).toBe( + ``+ + `I am happy`+ + `0`+ + ``+ + ``); + + const view = await u.waitUntil(() => document.querySelector('converse-user-profile')); + const first_child = view.querySelector('.xmpp-status span:first-child'); + expect(u.hasClass('online', first_child)).toBe(true); + expect(view.querySelector('.xmpp-status span:first-child').textContent.trim()).toBe(msg); + })); + }); +}); diff --git a/src/shared/tests/mock.js b/src/shared/tests/mock.js index 2fa74c4ae..af781369f 100644 --- a/src/shared/tests/mock.js +++ b/src/shared/tests/mock.js @@ -643,31 +643,8 @@ function clearStores () { window.sessionStorage.removeItem(cache_key+'fetched'); } -const theme = ['dracula', 'classic', 'concord'][Math.floor(Math.random()*3)]; - -async function _initConverse (settings) { - clearStores(); - await clearIndexedDB(); - - _converse = await converse.initialize(Object.assign({ - 'animate': false, - 'auto_subscribe': false, - 'bosh_service_url': 'montague.lit/http-bind', - 'discover_connection_methods': false, - 'enable_smacks': false, - 'i18n': 'en', - 'loglevel': 'debug', - 'no_trimming': true, - 'persistent_store': 'localStorage', - 'play_sounds': false, - 'theme': theme, - 'use_emojione': false, - 'view_mode': view_mode - }, settings || {})); - - window._converse = _converse; - - _converse.api.vcard.get = function (model, force) { +function getMockVcardFetcher (settings) { + return (model, force) => { let jid; if (typeof model === 'string' || model instanceof String) { jid = model; @@ -702,7 +679,37 @@ async function _initConverse (settings) { 'vcard_updated': dayjs().format(), 'vcard_error': undefined }; - }; + } +} + +const theme = ['dracula', 'classic', 'concord'][Math.floor(Math.random()*3)]; + +async function _initConverse (settings) { + clearStores(); + await clearIndexedDB(); + + _converse = await converse.initialize(Object.assign({ + 'animate': false, + 'auto_subscribe': false, + 'bosh_service_url': 'montague.lit/http-bind', + 'discover_connection_methods': false, + 'enable_smacks': false, + 'i18n': 'en', + 'loglevel': 'debug', + 'no_trimming': true, + 'persistent_store': 'localStorage', + 'play_sounds': false, + 'theme': theme, + 'use_emojione': false, + 'view_mode': view_mode + }, settings || {})); + + window._converse = _converse; + + if (_converse.api.vcard) { + _converse.api.vcard.get = getMockVcardFetcher(settings); + } + if (settings?.auto_login !== false) { _converse.api.user.login('romeo@montague.lit/orchard', 'secret'); await _converse.api.waitUntil('afterResourceBinding');