Commit 8c1e886a authored by JC Brand's avatar JC Brand

Fixes #2302 Bookmarks get duplicated on server push

parent 15f5b185
...@@ -991,6 +991,34 @@ ...@@ -991,6 +991,34 @@
} }
} }
}, },
"@babel/helper-skip-transparent-expression-wrappers": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz",
"integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==",
"dev": true,
"requires": {
"@babel/types": "^7.12.1"
},
"dependencies": {
"@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
"dev": true
},
"@babel/types": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
"integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"lodash": "^4.17.19",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/helper-split-export-declaration": { "@babel/helper-split-export-declaration": {
"version": "7.10.4", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz",
...@@ -1285,12 +1313,13 @@ ...@@ -1285,12 +1313,13 @@
} }
}, },
"@babel/plugin-proposal-optional-chaining": { "@babel/plugin-proposal-optional-chaining": {
"version": "7.10.4", "version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz",
"integrity": "sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ==", "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4",
"@babel/helper-skip-transparent-expression-wrappers": "^7.12.1",
"@babel/plugin-syntax-optional-chaining": "^7.8.0" "@babel/plugin-syntax-optional-chaining": "^7.8.0"
}, },
"dependencies": { "dependencies": {
......
...@@ -318,7 +318,7 @@ describe("A chat room", function () { ...@@ -318,7 +318,7 @@ describe("A chat room", function () {
describe("Bookmarks", function () { describe("Bookmarks", function () {
it("can be pushed from the XMPP server", mock.initConverse( it("can be pushed from the XMPP server", mock.initConverse(
['rosterGroupsFetched', 'connected'], {}, async function (done, _converse) { ['connected', 'rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) {
const { $msg, u } = converse.env; const { $msg, u } = converse.env;
await mock.waitUntilBookmarksReturned(_converse); await mock.waitUntilBookmarksReturned(_converse);
...@@ -347,23 +347,61 @@ describe("Bookmarks", function () { ...@@ -347,23 +347,61 @@ describe("Bookmarks", function () {
* </event> * </event>
* </message> * </message>
*/ */
const stanza = $msg({ let stanza = $msg({
'from': 'romeo@montague.lit', 'from': 'romeo@montague.lit',
'to': 'romeo@montague.lit/orchard', 'to': _converse.jid,
'type': 'headline', 'type': 'headline',
'id': 'rnfoo1' 'id': u.getUniqueId()
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'}) }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
.c('items', {'node': 'storage:bookmarks'}) .c('items', {'node': 'storage:bookmarks'})
.c('item', {'id': 'current'}) .c('item', {'id': 'current'})
.c('storage', {'xmlns': 'storage:bookmarks'}) .c('storage', {'xmlns': 'storage:bookmarks'})
.c('conference', {'name': 'The Play&apos;s the Thing', .c('conference', {
'name': 'The Play&apos;s the Thing',
'autojoin': 'true', 'autojoin': 'true',
'jid':'theplay@conference.shakespeare.lit'}) 'jid':'theplay@conference.shakespeare.lit'
.c('nick').t('JC'); }).c('nick').t('JC').up().up()
.c('conference', {
'name': 'Another bookmark',
'autojoin': 'false',
'jid':'another@conference.shakespeare.lit'
}).c('nick').t('JC');
_converse.connection._dataRecv(mock.createRequest(stanza)); _converse.connection._dataRecv(mock.createRequest(stanza));
await u.waitUntil(() => _converse.bookmarks.length); await u.waitUntil(() => _converse.bookmarks.length);
expect(_converse.bookmarks.length).toBe(1); expect(_converse.bookmarks.length).toBe(2);
expect(_converse.bookmarks.map(b => b.get('name'))).toEqual(['Another bookmark', 'The Play&apos;s the Thing']);
expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
stanza = $msg({
'from': 'romeo@montague.lit',
'to': _converse.jid,
'type': 'headline',
'id': u.getUniqueId()
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
.c('items', {'node': 'storage:bookmarks'})
.c('item', {'id': 'current'})
.c('storage', {'xmlns': 'storage:bookmarks'})
.c('conference', {
'name': 'The Play&apos;s the Thing',
'autojoin': 'true',
'jid':'theplay@conference.shakespeare.lit'
}).c('nick').t('JC').up().up()
.c('conference', {
'name': 'Second bookmark',
'autojoin': 'false',
'jid':'another@conference.shakespeare.lit'
}).c('nick').t('JC').up().up()
.c('conference', {
'name': 'Yet another bookmark',
'autojoin': 'false',
'jid':'yab@conference.shakespeare.lit'
}).c('nick').t('JC');
_converse.connection._dataRecv(mock.createRequest(stanza));
await u.waitUntil(() => _converse.bookmarks.length === 3);
expect(_converse.bookmarks.map(b => b.get('name'))).toEqual(['Second bookmark', 'The Play&apos;s the Thing', 'Yet another bookmark']);
expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined(); expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
expect(Object.keys(_converse.chatboxviews.getAll()).length).toBe(2);
done(); done();
})); }));
......
...@@ -16,6 +16,17 @@ const u = converse.env.utils; ...@@ -16,6 +16,17 @@ const u = converse.env.utils;
Strophe.addNamespace('BOOKMARKS', 'storage:bookmarks'); Strophe.addNamespace('BOOKMARKS', 'storage:bookmarks');
function handleBookmarksPush (message) {
if (sizzle(`event[xmlns="${Strophe.NS.PUBSUB}#event"] items[node="${Strophe.NS.BOOKMARKS}"]`, message).length) {
api.waitUntil('bookmarksInitialized')
.then(() => _converse.bookmarks.createBookmarksFromStanza(message))
.catch(e => log.fatal(e));
}
return true;
}
converse.plugins.add('converse-bookmarks', { converse.plugins.add('converse-bookmarks', {
/* Plugin dependencies are other plugins which might be /* Plugin dependencies are other plugins which might be
...@@ -92,6 +103,7 @@ converse.plugins.add('converse-bookmarks', { ...@@ -92,6 +103,7 @@ converse.plugins.add('converse-bookmarks', {
} }
_converse.Bookmark = Model.extend({ _converse.Bookmark = Model.extend({
idAttribute: 'jid',
getDisplayName () { getDisplayName () {
return Strophe.xmlunescape(this.get('name')); return Strophe.xmlunescape(this.get('name'));
} }
...@@ -150,9 +162,9 @@ converse.plugins.add('converse-bookmarks', { ...@@ -150,9 +162,9 @@ converse.plugins.add('converse-bookmarks', {
'from': _converse.connection.jid, 'from': _converse.connection.jid,
}) })
.c('pubsub', {'xmlns': Strophe.NS.PUBSUB}) .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('publish', {'node': 'storage:bookmarks'}) .c('publish', {'node': Strophe.NS.BOOKMARKS})
.c('item', {'id': 'current'}) .c('item', {'id': 'current'})
.c('storage', {'xmlns':'storage:bookmarks'}); .c('storage', {'xmlns': Strophe.NS.BOOKMARKS});
this.forEach(model => { this.forEach(model => {
stanza.c('conference', { stanza.c('conference', {
'name': model.get('name'), 'name': model.get('name'),
...@@ -186,7 +198,7 @@ converse.plugins.add('converse-bookmarks', { ...@@ -186,7 +198,7 @@ converse.plugins.add('converse-bookmarks', {
'from': _converse.connection.jid, 'from': _converse.connection.jid,
'type': 'get', 'type': 'get',
}).c('pubsub', {'xmlns': Strophe.NS.PUBSUB}) }).c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('items', {'node': 'storage:bookmarks'}); .c('items', {'node': Strophe.NS.BOOKMARKS});
api.sendIQ(stanza) api.sendIQ(stanza)
.then(iq => this.onBookmarksReceived(deferred, iq)) .then(iq => this.onBookmarksReceived(deferred, iq))
.catch(iq => this.onBookmarksReceivedError(deferred, iq) .catch(iq => this.onBookmarksReceivedError(deferred, iq)
...@@ -208,18 +220,18 @@ converse.plugins.add('converse-bookmarks', { ...@@ -208,18 +220,18 @@ converse.plugins.add('converse-bookmarks', {
}, },
createBookmarksFromStanza (stanza) { createBookmarksFromStanza (stanza) {
const bookmarks = sizzle( const xmlns = Strophe.NS.BOOKMARKS;
`items[node="storage:bookmarks"] item storage[xmlns="storage:bookmarks"] conference`, const sel = `items[node="${xmlns}"] item storage[xmlns="${xmlns}"] conference`;
stanza sizzle(sel, stanza).forEach(el => {
); const jid = el.getAttribute('jid');
bookmarks.forEach(bookmark => { const bookmark = this.get(jid);
const jid = bookmark.getAttribute('jid'); const attrs = {
this.create({
'jid': jid, 'jid': jid,
'name': bookmark.getAttribute('name') || jid, 'name': el.getAttribute('name') || jid,
'autojoin': bookmark.getAttribute('autojoin') === 'true', 'autojoin': el.getAttribute('autojoin') === 'true',
'nick': bookmark.querySelector('nick')?.textContent || '' 'nick': el.querySelector('nick')?.textContent || ''
}); }
bookmark ? bookmark.save(attrs) : this.create(attrs);
}); });
}, },
...@@ -309,14 +321,8 @@ converse.plugins.add('converse-bookmarks', { ...@@ -309,14 +321,8 @@ converse.plugins.add('converse-bookmarks', {
api.listen.on('connected', async () => { api.listen.on('connected', async () => {
// Add a handler for bookmarks pushed from other connected clients // Add a handler for bookmarks pushed from other connected clients
_converse.connection.addHandler(message => { const { connection } = _converse;
if (sizzle('event[xmlns="'+Strophe.NS.PUBSUB+'#event"] items[node="storage:bookmarks"]', message).length) { connection.addHandler(handleBookmarksPush, null, 'message', 'headline', null, _converse.bare_jid);
api.waitUntil('bookmarksInitialized')
.then(() => _converse.bookmarks.createBookmarksFromStanza(message))
.catch(e => log.fatal(e));
}
}, null, 'message', 'headline', null, _converse.bare_jid);
await Promise.all([api.waitUntil('chatBoxesFetched')]); await Promise.all([api.waitUntil('chatBoxesFetched')]);
initBookmarks(); initBookmarks();
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment