Fixes #3123: Contacts do not show up online until chat is opened with them.
The issue was that nothing was listening to the new `presenceChanged` event.
This commit is contained in:
parent
9ba339a6d9
commit
cb1f929045
|
@ -9,6 +9,7 @@
|
|||
- Generate TypeScript declaration files into `dist/types`
|
||||
- Removed documentation about the no longer implemented `fullname` option.
|
||||
- Updated translations
|
||||
- #3123: Contacts do not show up online until chat is opened with them.
|
||||
- #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
|
||||
|
||||
|
|
4
Makefile
4
Makefile
|
@ -58,6 +58,10 @@ serve: node_modules dist
|
|||
serve_bg: node_modules
|
||||
$(HTTPSERVE) -p $(HTTPSERVE_PORT) -c-1 -s &
|
||||
|
||||
certs:
|
||||
mkdir certs
|
||||
cd certs && openssl req -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out chat.example.org.crt -keyout chat.example.org.key
|
||||
|
||||
########################################################################
|
||||
## Translation machinery
|
||||
|
||||
|
|
|
@ -377,9 +377,7 @@ const RosterContacts = Collection.extend({
|
|||
_converse.xmppstatus.save({'status': show}, {'silent': true});
|
||||
|
||||
const status_message = presence.querySelector('status')?.textContent;
|
||||
if (status_message) {
|
||||
_converse.xmppstatus.save({'status_message': status_message});
|
||||
}
|
||||
if (status_message) _converse.xmppstatus.save({ status_message });
|
||||
}
|
||||
if (_converse.jid === jid && presence_type === 'unavailable') {
|
||||
// XXX: We've received an "unavailable" presence from our
|
||||
|
@ -412,11 +410,11 @@ const RosterContacts = Collection.extend({
|
|||
return; // Ignore MUC
|
||||
}
|
||||
|
||||
const status_message = presence.querySelector('status')?.textContent;
|
||||
const contact = this.get(bare_jid);
|
||||
|
||||
if (contact && (status_message !== contact.get('status'))) {
|
||||
contact.save({'status': status_message});
|
||||
if (contact) {
|
||||
const status = presence.querySelector('status')?.textContent;
|
||||
if (contact.get('status') !== status) contact.save({status});
|
||||
}
|
||||
|
||||
if (presence_type === 'subscribed' && contact) {
|
||||
|
|
|
@ -30,7 +30,7 @@ export const Presence = Model.extend({
|
|||
const hpr = this.getHighestPriorityResource();
|
||||
const show = hpr?.attributes?.show || 'offline';
|
||||
if (this.get('show') !== show) {
|
||||
this.save({'show': show});
|
||||
this.save({ show });
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -51,17 +51,17 @@ export const Presence = Model.extend({
|
|||
* @param { Element } presence: The presence stanza
|
||||
*/
|
||||
addResource (presence) {
|
||||
const jid = presence.getAttribute('from'),
|
||||
name = Strophe.getResourceFromJid(jid),
|
||||
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop(),
|
||||
priority = presence.querySelector('priority')?.textContent ?? 0,
|
||||
resource = this.resources.get(name),
|
||||
settings = {
|
||||
'name': name,
|
||||
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
|
||||
'show': presence.querySelector('show')?.textContent ?? 'online',
|
||||
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
|
||||
};
|
||||
const jid = presence.getAttribute('from');
|
||||
const name = Strophe.getResourceFromJid(jid);
|
||||
const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop();
|
||||
const priority = presence.querySelector('priority')?.textContent;
|
||||
const resource = this.resources.get(name);
|
||||
const settings = {
|
||||
name,
|
||||
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
|
||||
'show': presence.querySelector('show')?.textContent ?? 'online',
|
||||
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
|
||||
};
|
||||
if (resource) {
|
||||
resource.save(settings);
|
||||
} else {
|
||||
|
@ -78,9 +78,7 @@ export const Presence = Model.extend({
|
|||
*/
|
||||
removeResource (name) {
|
||||
const resource = this.resources.get(name);
|
||||
if (resource) {
|
||||
resource.destroy();
|
||||
}
|
||||
resource?.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ export default {
|
|||
/**
|
||||
* Send out a presence stanza
|
||||
* @method _converse.api.user.presence.send
|
||||
* @param { String } type
|
||||
* @param { String } to
|
||||
* @param { String } [type]
|
||||
* @param { String } [to]
|
||||
* @param { String } [status] - An optional status message
|
||||
* @param { Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder } [child_nodes]
|
||||
* Nodes(s) to be added as child nodes of the `presence` XML element.
|
||||
|
|
|
@ -357,7 +357,7 @@ async function getLoginCredentialsFromBrowser () {
|
|||
if (!jid) return null;
|
||||
|
||||
try {
|
||||
const creds = await navigator.credentials.get({'password': true});
|
||||
const creds = await navigator.credentials.get({ password: true});
|
||||
if (creds && creds.type == 'password' && isValidJID(creds.id)) {
|
||||
// XXX: We don't actually compare `creds.id` with `jid` because
|
||||
// the user might have been presented a list of credentials with
|
||||
|
@ -431,7 +431,7 @@ export async function attemptNonPreboundSession (credentials, automatic) {
|
|||
* The user's plaintext password is not stored, nor any material from which
|
||||
* the user's plaintext password could be recovered.
|
||||
*
|
||||
* @param { String } JID - The XMPP address for which to fetch the SCRAM keys
|
||||
* @param { String } jid - The XMPP address for which to fetch the SCRAM keys
|
||||
* @returns { Promise } A promise which resolves once we've fetched the previously
|
||||
* used login keys.
|
||||
*/
|
||||
|
@ -444,6 +444,13 @@ export async function savedLoginInfo (jid) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param { Object } [credentials]
|
||||
* @param { string } credentials.password
|
||||
* @param { Object } credentials.password
|
||||
* @param { string } credentials.password.ck
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
async function connect (credentials) {
|
||||
const { api } = _converse;
|
||||
if ([ANONYMOUS, EXTERNAL].includes(api.settings.get("authentication"))) {
|
||||
|
|
|
@ -15,10 +15,11 @@ export default class RosterContact extends CustomElement {
|
|||
}
|
||||
|
||||
initialize () {
|
||||
this.listenTo(this.model, "change", () => this.requestUpdate());
|
||||
this.listenTo(this.model, "highlight", () => this.requestUpdate());
|
||||
this.listenTo(this.model, 'change', () => this.requestUpdate());
|
||||
this.listenTo(this.model, 'highlight', () => this.requestUpdate());
|
||||
this.listenTo(this.model, 'vcard:add', () => this.requestUpdate());
|
||||
this.listenTo(this.model, 'vcard:change', () => this.requestUpdate());
|
||||
this.listenTo(this.model, 'presenceChanged', () => this.requestUpdate());
|
||||
}
|
||||
|
||||
render () {
|
||||
|
|
|
@ -812,6 +812,37 @@ describe("The Contacts Roster", function () {
|
|||
expect(true).toBe(true);
|
||||
}));
|
||||
|
||||
it("will have their online statuses shown correctly",
|
||||
mock.initConverse(
|
||||
[], {},
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
const icon_el = document.querySelector('converse-roster-contact converse-icon');
|
||||
expect(icon_el.getAttribute('color')).toBe('var(--subdued-color)');
|
||||
|
||||
let pres = $pres({from: 'mercutio@montague.lit/resource'});
|
||||
_converse.connection._dataRecv(mock.createRequest(pres));
|
||||
await u.waitUntil(() => icon_el.getAttribute('color') === 'var(--chat-status-online)');
|
||||
|
||||
pres = $pres({from: 'mercutio@montague.lit/resource'}).c('show', 'away');
|
||||
_converse.connection._dataRecv(mock.createRequest(pres));
|
||||
await u.waitUntil(() => icon_el.getAttribute('color') === 'var(--chat-status-away)');
|
||||
|
||||
pres = $pres({from: 'mercutio@montague.lit/resource'}).c('show', 'xa');
|
||||
_converse.connection._dataRecv(mock.createRequest(pres));
|
||||
await u.waitUntil(() => icon_el.getAttribute('color') === 'var(--subdued-color)');
|
||||
|
||||
pres = $pres({from: 'mercutio@montague.lit/resource'}).c('show', 'dnd');
|
||||
_converse.connection._dataRecv(mock.createRequest(pres));
|
||||
await u.waitUntil(() => icon_el.getAttribute('color') === 'var(--chat-status-busy)');
|
||||
|
||||
pres = $pres({from: 'mercutio@montague.lit/resource', type: 'unavailable'});
|
||||
_converse.connection._dataRecv(mock.createRequest(pres));
|
||||
await u.waitUntil(() => icon_el.getAttribute('color') === 'var(--subdued-color)');
|
||||
}));
|
||||
|
||||
it("can be added to the roster and they will be sorted alphabetically",
|
||||
mock.initConverse(
|
||||
[], {},
|
||||
|
|
|
@ -40,7 +40,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: 'ws://chat.example.org:5381/xmpp-websocket',
|
||||
// websocket_url: 'wss://chat.example.org:5381/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,
|
||||
|
|
|
@ -12,7 +12,12 @@ module.exports = merge(common, {
|
|||
devtool: "inline-source-map",
|
||||
devServer: {
|
||||
static: [ path.resolve(__dirname, '../') ],
|
||||
port: 3003
|
||||
port: 3003,
|
||||
// https: {
|
||||
// key: './certs/chat.example.org.key',
|
||||
// cert: './certs/chat.example.org.crt',
|
||||
// requestCert: true,
|
||||
// },
|
||||
},
|
||||
plugins: [
|
||||
new HTMLWebpackPlugin({
|
||||
|
|
Loading…
Reference in New Issue