Skip to content

Commit

Permalink
Show a validation error in add-muc modal when no default MUC domain i…
Browse files Browse the repository at this point in the history
…s found
  • Loading branch information
jcbrand committed Feb 12, 2025
1 parent a3f4a61 commit 68243ab
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 22 deletions.
3 changes: 2 additions & 1 deletion src/headless/plugins/muc/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ const { Strophe, sizzle, u } = converse.env;
* @returns {Promise<string|undefined>}
*/
export async function getDefaultMUCService () {
let muc_service = api.settings.get('muc_domain');
let muc_service = api.settings.get('muc_domain') || _converse.session.get('default_muc_service');
if (!muc_service) {
const domain = _converse.session.get('domain');
const items = await api.disco.entities.items(domain);
for (const item of items) {
if (await api.disco.features.has(Strophe.NS.MUC, item.get('jid'))) {
muc_service = item.get('jid');
_converse.session.save({ default_muc_service: muc_service });
break;
}
}
Expand Down
18 changes: 14 additions & 4 deletions src/plugins/muc-views/modals/add-muc.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default class AddMUCModal extends BaseModal {
return s
.trim()
.replace(/\s+/g, '-')
.replace(/\u0142/g, "l")
.replace(/\u0142/g, 'l')
.replace(/[^\x00-\x7F]/g, (c) => c.normalize('NFD').replace(/[\u0300-\u036f]/g, ''))
.replace(/[^a-zA-Z0-9-]/g, '-')
.replace(/-+/g, '-')
Expand All @@ -75,7 +75,7 @@ export default class AddMUCModal extends BaseModal {
ev.preventDefault();

const autocomplete_el = /** @type {AutoCompleteComponent} */ (this.querySelector('converse-autocomplete'));
if (autocomplete_el.onChange().error_message) return;
if ((await autocomplete_el.onChange()).error_message) return;

const { escapeNode, getNodeFromJid, getDomainFromJid } = Strophe;
const form = /** @type {HTMLFormElement} */ (ev.target);
Expand Down Expand Up @@ -109,9 +109,9 @@ export default class AddMUCModal extends BaseModal {

/**
* @param {string} jid
* @return {string}
* @return {Promise<string>}
*/
validateMUCJID(jid) {
async validateMUCJID(jid) {
if (jid.length === 0) {
return __('Invalid groupchat address, it cannot be empty.');
}
Expand All @@ -130,6 +130,16 @@ export default class AddMUCModal extends BaseModal {
return __('Invalid groupchat address, it cannot start or end with an @ sign.');
}

if (!jid.includes('@')) {
const muc_service = await u.muc.getDefaultMUCService();
if (!muc_service) {
return __(
"No default groupchat service found. "+
"You'll need to specify the full address, for example [email protected]"
);
}
}

const policy = api.settings.get('muc_roomid_policy');
if (policy && api.settings.get('muc_domain')) {
if (api.settings.get('locked_muc_domain') || !u.isValidJID(jid)) {
Expand Down
5 changes: 0 additions & 5 deletions src/plugins/muc-views/modals/templates/add-muc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,10 @@ const nickname_input = () => {
*/
export default (el) => {
const i18n_join = __('Join');
const muc_domain = api.settings.get('muc_domain');

let placeholder = '';
let label_name;
if (api.settings.get('locked_muc_domain')) {
label_name = __('Groupchat name');
} else {
placeholder = muc_domain ? `name@${muc_domain}` : __('[email protected]');
label_name = __('Groupchat name or address');
}

Expand All @@ -58,7 +54,6 @@ export default (el) => {
class="add-muc-autocomplete"
min_chars="3"
name="chatroom"
placeholder="${placeholder}"
position="below"
required
></converse-autocomplete>`
Expand Down
40 changes: 33 additions & 7 deletions src/plugins/muc-views/tests/muc-add-modal.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/*global mock, converse */
const { Promise, sizzle, u } = converse.env;


describe('The "Groupchats" Add modal', function () {

beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza }));
Expand All @@ -12,9 +11,6 @@ describe('The "Groupchats" Add modal', function () {

let label_name = modal.querySelector('label[for="chatroom"]');
expect(label_name.textContent.trim()).toBe('Groupchat name or address:');
const name_input = modal.querySelector('input[name="chatroom"]');
expect(name_input.placeholder).toBe('[email protected]');

const label_nick = modal.querySelector('label[for="nickname"]');
expect(label_nick.textContent.trim()).toBe('Nickname:');
const nick_input = modal.querySelector('input[name="nickname"]');
Expand All @@ -39,7 +35,6 @@ describe('The "Groupchats" Add modal', function () {
const label_name = modal.querySelector('label[for="chatroom"]');
expect(label_name.textContent.trim()).toBe('Groupchat name or address:');
let name_input = modal.querySelector('input[name="chatroom"]');
expect(name_input.placeholder).toBe('[email protected]');
name_input.value = 'lounge';
let nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
Expand All @@ -66,7 +61,7 @@ describe('The "Groupchats" Add modal', function () {
})
);

it('only uses the muc_domain if locked_muc_domain is true', mock.initConverse(
it('uses the muc_domain if locked_muc_domain is true', mock.initConverse(
['chatBoxesFetched'], { muc_domain: 'muc.example.org', locked_muc_domain: true },
async function (_converse) {
const modal = await mock.openAddMUCModal(_converse);
Expand Down Expand Up @@ -104,7 +99,7 @@ describe('The "Groupchats" Add modal', function () {
})
);

fit("lets you create a MUC with only the name",
it("lets you create a MUC with only the name",
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
const { domain } = _converse;
await mock.waitUntilDiscoConfirmed(
Expand Down Expand Up @@ -210,6 +205,37 @@ describe('The "Groupchats" Add modal', function () {
})
);

it("shows a validation error when only the name was specified and there's no default MUC service",
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
const { domain } = _converse;
await mock.waitUntilDiscoConfirmed(
_converse,
domain,
[{ category: 'server', type: 'IM' }],
[],
);

const nick = 'max';
const modal = await mock.openAddMUCModal(_converse);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());

const name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'The Lounge';

const nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = nick;

modal.querySelector('form input[type="submit"]').click();

await u.waitUntil(() => name_input.classList.contains('error'));
expect(name_input.classList.contains('is-invalid')).toBe(true);
expect(modal.querySelector('.invalid-feedback')?.textContent).toBe(
"No default groupchat service found. "+
"You'll need to specify the full address, for example [email protected]"
);
})
);

it("normalizes the MUC name when creating the corresponding JID",
mock.initConverse(['chatBoxesFetched'], {muc_domain: 'montague.lit'}, async function (_converse) {
const modal = await mock.openAddMUCModal(_converse);
Expand Down
4 changes: 2 additions & 2 deletions src/shared/autocomplete/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ export default class AutoCompleteComponent extends CustomElement {
this.auto_complete.evaluate(ev);
}

onChange() {
async onChange() {
const input = this.querySelector('input');
this.error_message = this.validate?.(input.value);
this.error_message = await this.validate?.(input.value);
if (this.error_message) this.requestUpdate();
return this;
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/plugins/muc-views/modals/add-muc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export default class AddMUCModal extends BaseModal {
openChatRoom(ev: Event): Promise<void>;
/**
* @param {string} jid
* @return {string}
* @return {Promise<string>}
*/
validateMUCJID(jid: string): string;
validateMUCJID(jid: string): Promise<string>;
}
import BaseModal from 'plugins/modal/modal.js';
//# sourceMappingURL=add-muc.d.ts.map
2 changes: 1 addition & 1 deletion src/types/shared/autocomplete/component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default class AutoCompleteComponent extends CustomElement {
onKeyDown(ev: KeyboardEvent): void;
/** @param {KeyboardEvent} ev */
onKeyUp(ev: KeyboardEvent): void;
onChange(): this;
onChange(): Promise<this>;
}
import { CustomElement } from 'shared/components/element.js';
import AutoComplete from './autocomplete.js';
Expand Down

0 comments on commit 68243ab

Please sign in to comment.