Turn converse-roster
and converse-roster-filter
into Lit elements
This commit is contained in:
parent
8de4671603
commit
d2a35d4ce1
@ -1,10 +1,9 @@
|
|||||||
import debounce from "lodash-es/debounce";
|
import debounce from "lodash-es/debounce";
|
||||||
import tpl_roster_filter from "./templates/roster_filter.js";
|
import tpl_roster_filter from "./templates/roster_filter.js";
|
||||||
import { ElementView } from '@converse/skeletor/src/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core";
|
||||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||||
import { render } from 'lit';
|
|
||||||
|
|
||||||
export const RosterFilter = Model.extend({
|
export const RosterFilter = Model.extend({
|
||||||
initialize () {
|
initialize () {
|
||||||
@ -16,8 +15,12 @@ export const RosterFilter = Model.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export class RosterFilterView extends ElementView {
|
export class RosterFilterView extends CustomElement {
|
||||||
tagName = 'span';
|
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
const model = new _converse.RosterFilter();
|
const model = new _converse.RosterFilter();
|
||||||
@ -30,24 +33,19 @@ export class RosterFilterView extends ElementView {
|
|||||||
this.model.save({'filter_text': this.querySelector('.roster-filter').value});
|
this.model.save({'filter_text': this.querySelector('.roster-filter').value});
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|
||||||
this.listenTo(this.model, 'change', this.render);
|
this.listenTo(_converse, 'rosterContactsFetched', this.requestUpdate);
|
||||||
this.listenTo(
|
this.listenTo(_converse.presences, 'change:show', this.requestUpdate);
|
||||||
this.model,
|
this.listenTo(_converse.roster, "add", this.requestUpdate);
|
||||||
'change',
|
this.listenTo(_converse.roster, "destroy", this.requestUpdate);
|
||||||
() => this.dispatchEvent(new CustomEvent('update', { 'detail': this.model.changed }))
|
this.listenTo(_converse.roster, "remove", this.requestUpdate);
|
||||||
);
|
this.listenTo(this.model, 'change', this.dispatchUpdateEvent);
|
||||||
|
this.listenTo(this.model, 'change', this.requestUpdate);
|
||||||
this.listenTo(_converse.roster, "add", this.render);
|
|
||||||
this.listenTo(_converse.roster, "destroy", this.render);
|
|
||||||
this.listenTo(_converse.roster, "remove", this.render);
|
|
||||||
_converse.presences.on('change:show', this.render, this);
|
|
||||||
|
|
||||||
this.model.fetch();
|
this.model.fetch();
|
||||||
this.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
render(tpl_roster_filter(
|
return tpl_roster_filter(
|
||||||
Object.assign(this.model.toJSON(), {
|
Object.assign(this.model.toJSON(), {
|
||||||
visible: this.shouldBeVisible(),
|
visible: this.shouldBeVisible(),
|
||||||
changeChatStateFilter: ev => this.changeChatStateFilter(ev),
|
changeChatStateFilter: ev => this.changeChatStateFilter(ev),
|
||||||
@ -55,8 +53,11 @@ export class RosterFilterView extends ElementView {
|
|||||||
clearFilter: ev => this.clearFilter(ev),
|
clearFilter: ev => this.clearFilter(ev),
|
||||||
liveFilter: ev => this.liveFilter(ev),
|
liveFilter: ev => this.liveFilter(ev),
|
||||||
submitFilter: ev => this.submitFilter(ev),
|
submitFilter: ev => this.submitFilter(ev),
|
||||||
})), this);
|
}));
|
||||||
return this;
|
}
|
||||||
|
|
||||||
|
dispatchUpdateEvent () {
|
||||||
|
this.dispatchEvent(new CustomEvent('update', { 'detail': this.model.changed }));
|
||||||
}
|
}
|
||||||
|
|
||||||
changeChatStateFilter (ev) {
|
changeChatStateFilter (ev) {
|
||||||
@ -96,7 +97,7 @@ export class RosterFilterView extends ElementView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldBeVisible () {
|
shouldBeVisible () {
|
||||||
return _converse.roster && _converse.roster.length >= 5 || this.isActive();
|
return _converse.roster?.length >= 5 || this.isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFilter (ev) {
|
clearFilter (ev) {
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import debounce from 'lodash-es/debounce';
|
|
||||||
import tpl_roster from "./templates/roster.js";
|
import tpl_roster from "./templates/roster.js";
|
||||||
import { ElementView } from "@converse/skeletor/src/element";
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api } from "@converse/headless/core";
|
import { _converse, api } from "@converse/headless/core";
|
||||||
import { render } from 'lit';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,21 +9,22 @@ import { render } from 'lit';
|
|||||||
* @namespace _converse.RosterView
|
* @namespace _converse.RosterView
|
||||||
* @memberOf _converse
|
* @memberOf _converse
|
||||||
*/
|
*/
|
||||||
export default class RosterView extends ElementView {
|
export default class RosterView extends CustomElement {
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
async initialize () {
|
async initialize () {
|
||||||
await api.waitUntil('rosterInitialized')
|
await api.waitUntil('rosterInitialized')
|
||||||
this.debouncedRender = debounce(this.render, 100);
|
this.listenTo(_converse, 'rosterContactsFetched', this.requestUpdate);
|
||||||
this.listenTo(_converse, 'rosterContactsFetched', this.render);
|
this.listenTo(_converse.presences, 'change:show', this.requestUpdate);
|
||||||
this.listenTo(_converse.roster, "add", this.debouncedRender);
|
this.listenTo(_converse.roster, 'add', this.requestUpdate);
|
||||||
this.listenTo(_converse.roster, "destroy", this.debouncedRender);
|
this.listenTo(_converse.roster, 'destroy', this.requestUpdate);
|
||||||
this.listenTo(_converse.roster, "remove", this.debouncedRender);
|
this.listenTo(_converse.roster, 'remove', this.requestUpdate);
|
||||||
this.listenTo(_converse.roster, 'change', this.renderIfRelevantChange);
|
this.listenTo(_converse.roster, 'change', this.requestUpdate);
|
||||||
this.listenTo(_converse.roster.state, "change", this.render);
|
this.listenTo(_converse.roster.state, 'change', this.requestUpdate);
|
||||||
_converse.presences.on('change:show', () => this.debouncedRender());
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
this.listenToRosterFilter();
|
|
||||||
/**
|
/**
|
||||||
* Triggered once the _converse.RosterView instance has been created and initialized.
|
* Triggered once the _converse.RosterView instance has been created and initialized.
|
||||||
* @event _converse#rosterViewInitialized
|
* @event _converse#rosterViewInitialized
|
||||||
@ -34,21 +33,17 @@ export default class RosterView extends ElementView {
|
|||||||
api.trigger('rosterViewInitialized');
|
api.trigger('rosterViewInitialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
firstUpdated () {
|
||||||
render(tpl_roster(this), this);
|
this.listenToRosterFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderIfRelevantChange (model) {
|
render () {
|
||||||
const attrs = ['ask', 'requesting', 'groups', 'num_unread'];
|
return tpl_roster(this);
|
||||||
const changed = model.changed || {};
|
|
||||||
if (Object.keys(changed).filter(m => attrs.includes(m)).length) {
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listenToRosterFilter () {
|
listenToRosterFilter () {
|
||||||
this.filter_view = this.querySelector('converse-roster-filter');
|
this.filter_view = this.querySelector('converse-roster-filter');
|
||||||
this.filter_view.addEventListener('update', () => this.render());
|
this.filter_view.addEventListener('update', () => this.requestUpdate());
|
||||||
}
|
}
|
||||||
|
|
||||||
showAddContactModal (ev) { // eslint-disable-line class-methods-use-this
|
showAddContactModal (ev) { // eslint-disable-line class-methods-use-this
|
||||||
@ -58,14 +53,14 @@ export default class RosterView extends ElementView {
|
|||||||
async syncContacts (ev) { // eslint-disable-line class-methods-use-this
|
async syncContacts (ev) { // eslint-disable-line class-methods-use-this
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.syncing_contacts = true;
|
this.syncing_contacts = true;
|
||||||
this.render();
|
this.requestUpdate();
|
||||||
|
|
||||||
_converse.roster.data.save('version', null);
|
_converse.roster.data.save('version', null);
|
||||||
await _converse.roster.fetchFromServer();
|
await _converse.roster.fetchFromServer();
|
||||||
api.user.presence.send();
|
api.user.presence.send();
|
||||||
|
|
||||||
this.syncing_contacts = false;
|
this.syncing_contacts = false;
|
||||||
this.render();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ describe("The Protocol", function () {
|
|||||||
stanza = $iq({'type': 'result', 'id': roster_fetch_stanza.getAttribute('id')});
|
stanza = $iq({'type': 'result', 'id': roster_fetch_stanza.getAttribute('id')});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
await u.waitUntil(() => _converse.roster.create.calls.count());
|
await u.waitUntil(() => _converse.roster.create.calls.count(), 1000);
|
||||||
|
|
||||||
// A contact should now have been created
|
// A contact should now have been created
|
||||||
expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy();
|
expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy();
|
||||||
@ -206,7 +206,7 @@ describe("The Protocol", function () {
|
|||||||
// contact in the roster.
|
// contact in the roster.
|
||||||
await u.waitUntil(() => {
|
await u.waitUntil(() => {
|
||||||
const header = sizzle('a:contains("Pending contacts")', rosterview).pop();
|
const header = sizzle('a:contains("Pending contacts")', rosterview).pop();
|
||||||
const contacts = Array.from(header.parentElement.querySelectorAll('li')).filter(u.isVisible);
|
const contacts = Array.from(header?.parentElement.querySelectorAll('li') ?? []).filter(u.isVisible);
|
||||||
return contacts.length;
|
return contacts.length;
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
@ -286,7 +286,8 @@ describe("The Protocol", function () {
|
|||||||
expect(u.hasClass('to', contacts[0])).toBeTruthy();
|
expect(u.hasClass('to', contacts[0])).toBeTruthy();
|
||||||
expect(u.hasClass('both', contacts[0])).toBeFalsy();
|
expect(u.hasClass('both', contacts[0])).toBeFalsy();
|
||||||
expect(u.hasClass('current-xmpp-contact', contacts[0])).toBeTruthy();
|
expect(u.hasClass('current-xmpp-contact', contacts[0])).toBeTruthy();
|
||||||
expect(contacts[0].textContent.trim()).toBe('Nicky');
|
|
||||||
|
await u.waitUntil(() => contacts[0].textContent.trim() === 'Nicky');
|
||||||
|
|
||||||
expect(contact.presence.get('show')).toBe('offline');
|
expect(contact.presence.get('show')).toBe('offline');
|
||||||
|
|
||||||
|
@ -139,7 +139,6 @@ describe("The Contacts Roster", function () {
|
|||||||
const rosterview = document.querySelector('converse-roster');
|
const rosterview = document.querySelector('converse-roster');
|
||||||
const filter = rosterview.querySelector('.roster-filter');
|
const filter = rosterview.querySelector('.roster-filter');
|
||||||
const roster = rosterview.querySelector('.roster-contacts');
|
const roster = rosterview.querySelector('.roster-contacts');
|
||||||
rosterview.filter_view.delegateEvents();
|
|
||||||
|
|
||||||
await u.waitUntil(() => (sizzle('li', roster).filter(u.isVisible).length === 17), 800);
|
await u.waitUntil(() => (sizzle('li', roster).filter(u.isVisible).length === 17), 800);
|
||||||
filter.value = "la";
|
filter.value = "la";
|
||||||
@ -251,7 +250,6 @@ describe("The Contacts Roster", function () {
|
|||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
await mock.waitForRoster(_converse, 'current');
|
await mock.waitForRoster(_converse, 'current');
|
||||||
const rosterview = document.querySelector('converse-roster');
|
const rosterview = document.querySelector('converse-roster');
|
||||||
rosterview.filter_view.delegateEvents();
|
|
||||||
const roster = rosterview.querySelector('.roster-contacts');
|
const roster = rosterview.querySelector('.roster-contacts');
|
||||||
|
|
||||||
const button = rosterview.querySelector('span[data-type="groups"]');
|
const button = rosterview.querySelector('span[data-type="groups"]');
|
||||||
@ -299,7 +297,7 @@ describe("The Contacts Roster", function () {
|
|||||||
const isHidden = (el) => u.hasClass('hidden', el);
|
const isHidden = (el) => u.hasClass('hidden', el);
|
||||||
await u.waitUntil(() => !isHidden(rosterview.querySelector('.roster-filter-form .clear-input')), 900);
|
await u.waitUntil(() => !isHidden(rosterview.querySelector('.roster-filter-form .clear-input')), 900);
|
||||||
rosterview.querySelector('.clear-input').click();
|
rosterview.querySelector('.clear-input').click();
|
||||||
expect(document.querySelector('.roster-filter').value).toBe("");
|
await u.waitUntil(() => document.querySelector('.roster-filter').value == '');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Disabling for now, because since recently this test consistently
|
// Disabling for now, because since recently this test consistently
|
||||||
@ -460,6 +458,7 @@ describe("The Contacts Roster", function () {
|
|||||||
|
|
||||||
const contact = _converse.roster.get('groupchanger@montague.lit');
|
const contact = _converse.roster.get('groupchanger@montague.lit');
|
||||||
contact.set({'groups': ['secondgroup']});
|
contact.set({'groups': ['secondgroup']});
|
||||||
|
await u.waitUntil(() => sizzle('.roster-group[data-group="secondgroup"] a.group-toggle', rosterview).length);
|
||||||
group_titles = await u.waitUntil(() => {
|
group_titles = await u.waitUntil(() => {
|
||||||
const toggles = sizzle('.roster-group[data-group="secondgroup"] a.group-toggle', rosterview);
|
const toggles = sizzle('.roster-group[data-group="secondgroup"] a.group-toggle', rosterview);
|
||||||
if (toggles.reduce((result, t) => result && u.isVisible(t), true)) {
|
if (toggles.reduce((result, t) => result && u.isVisible(t), true)) {
|
||||||
@ -688,8 +687,12 @@ describe("The Contacts Roster", function () {
|
|||||||
// Check that they are sorted alphabetically
|
// Check that they are sorted alphabetically
|
||||||
const el = await u.waitUntil(() => rosterview.querySelector(`ul[data-group="Pending contacts"]`));
|
const el = await u.waitUntil(() => rosterview.querySelector(`ul[data-group="Pending contacts"]`));
|
||||||
const spans = el.querySelectorAll('.pending-xmpp-contact span');
|
const spans = el.querySelectorAll('.pending-xmpp-contact span');
|
||||||
const t = Array.from(spans).reduce((result, value) => result + value.textContent?.trim(), '');
|
|
||||||
expect(t).toEqual(mock.pend_names.slice(0,i+1).sort().join(''));
|
await u.waitUntil(
|
||||||
|
() => Array.from(spans).reduce((result, value) => result + value.textContent?.trim(), '') ===
|
||||||
|
mock.pend_names.slice(0,i+1).sort().join('')
|
||||||
|
);
|
||||||
|
expect(true).toBe(true);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -729,8 +732,8 @@ describe("The Contacts Roster", function () {
|
|||||||
requesting: false,
|
requesting: false,
|
||||||
subscription: 'both'
|
subscription: 'both'
|
||||||
});
|
});
|
||||||
const el = rosterview.querySelector(`ul[data-group="My contacts"]`);
|
await u.waitUntil(() => u.hasClass('collapsed', rosterview.querySelector(`ul[data-group="My contacts"]`)) === true);
|
||||||
expect(u.hasClass('collapsed', el)).toBe(true);
|
expect(true).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("can be added to the roster and they will be sorted alphabetically",
|
it("can be added to the roster and they will be sorted alphabetically",
|
||||||
|
Loading…
Reference in New Issue
Block a user