Merge branch 'master' into muppdev
This commit is contained in:
commit
da940b0752
|
@ -3,6 +3,7 @@
|
|||
## 8.0.0 (Unreleased)
|
||||
|
||||
- #1083: Add support for XEP-0393 Message Styling
|
||||
- #1182: Add support for XEP-0454 OMEMO Media sharing
|
||||
- #2275: Allow punctuation to immediately precede a mention
|
||||
- #2348: `auto_join_room` not showing the room in `fullscreen` `view_mode`.
|
||||
- #2400: Fixes infinite loop bug when appending .png to allowed image urls
|
||||
|
|
1
Makefile
1
Makefile
|
@ -73,6 +73,7 @@ GETTEXT = $(XGETTEXT) --from-code=UTF-8 --language=JavaScript --keyword=__ --key
|
|||
src/i18n/converse.pot: dist/converse-no-dependencies.js
|
||||
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
|
||||
rm dist/converse-no-dependencies.js
|
||||
rm dist/tmp.css
|
||||
|
||||
.PHONY: pot
|
||||
pot: src/i18n/converse.pot
|
||||
|
|
|
@ -108,7 +108,7 @@ In embedded mode, Converse can be embedded into an element in the DOM.
|
|||
- [XEP-0424](https://xmpp.org/extensions/xep-0424.html) Message Retractions
|
||||
- [XEP-0425](https://xmpp.org/extensions/xep-0425.html) Message Moderation
|
||||
- [XEP-0437](https://xmpp.org/extensions/xep-0437.html) Room Activity Indicators
|
||||
|
||||
- [XEP-0454](https://xmpp.org/extensions/xep-0454.html) OMEMO Media sharing
|
||||
|
||||
## Integration into other servers and frameworks
|
||||
|
||||
|
|
|
@ -800,7 +800,7 @@ embed_audio
|
|||
|
||||
If set to ``false``, audio files won't be embedded in chats, instead only their links will be shown.
|
||||
|
||||
It also accepts an array strings of whitelisted domain names to only render videos that belong to those domains.
|
||||
It also accepts an array strings of whitelisted domain names to only render audio files that belong to those domains.
|
||||
E.g. ``['conversejs.org']``
|
||||
|
||||
|
||||
|
@ -1513,7 +1513,7 @@ muc_show_ogp_unfurls
|
|||
Supports showing extra metadata (picture and description) for URLs contained in
|
||||
groupchat messages.
|
||||
|
||||
The metadat must come from the MUC itself, metadata sent from participants
|
||||
The metadata must come from the MUC itself, metadata sent from participants
|
||||
themselves will not be shown.
|
||||
|
||||
For Prosody XMPP server, `mod_ogp <https://modules.prosody.im/mod_ogp.html>`_ can be used.
|
||||
|
|
|
@ -39,7 +39,6 @@ module.exports = function(config) {
|
|||
{ pattern: "src/headless/plugins/status/tests/status.js", type: 'module' },
|
||||
{ pattern: "src/headless/tests/converse.js", type: 'module' },
|
||||
{ pattern: "src/headless/tests/eventemitter.js", type: 'module' },
|
||||
{ pattern: "src/headless/tests/persistence.js", type: 'module' },
|
||||
{ pattern: "src/plugins/bookmark-views/tests/bookmarks.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/chatbox.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/corrections.js", type: 'module' },
|
||||
|
@ -54,6 +53,8 @@ module.exports = function(config) {
|
|||
{ pattern: "src/plugins/chatview/tests/oob.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/receipts.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/spoilers.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/styling.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/unreads.js", type: 'module' },
|
||||
{ pattern: "src/plugins/chatview/tests/xss.js", type: 'module' },
|
||||
{ pattern: "src/plugins/controlbox/tests/controlbox.js", type: 'module' },
|
||||
{ pattern: "src/plugins/controlbox/tests/login.js", type: 'module' },
|
||||
|
@ -68,6 +69,7 @@ module.exports = function(config) {
|
|||
{ pattern: "src/plugins/muc-views/tests/hats.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/http-file-upload.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/markers.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/me-messages.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/mentions.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/modtools.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-api.js", type: 'module' },
|
||||
|
@ -79,16 +81,20 @@ module.exports = function(config) {
|
|||
{ pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/retractions.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/styling.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/toolbar.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/unfurls.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/xss.js", type: 'module' },
|
||||
{ pattern: "src/plugins/notifications/tests/notification.js", type: 'module' },
|
||||
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
||||
{ pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' },
|
||||
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
||||
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
||||
{ pattern: "src/plugins/rosterview/tests/presence.js", type: 'module' },
|
||||
{ pattern: "src/plugins/rosterview/tests/protocol.js", type: 'module' },
|
||||
{ pattern: "src/plugins/rosterview/tests/roster.js", type: 'module' },
|
||||
{ pattern: "src/shared/chat/tests/styling.js", type: 'module' },
|
||||
|
||||
// For some reason this test causes issues when its run earlier
|
||||
{ pattern: "src/headless/tests/persistence.js", type: 'module' },
|
||||
],
|
||||
|
||||
proxies: {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
45
package.json
45
package.json
|
@ -63,56 +63,50 @@
|
|||
"browser": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.10.3",
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/preset-env": "^7.12.7",
|
||||
"@babel/cli": "^7.14.5",
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@converse/headless": "file:src/headless",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"bootstrap.native-loader": "2.0.0",
|
||||
"clean-css-cli": "^4.3.0",
|
||||
"copy-webpack-plugin": "^6.3.2",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"css-loader": "^3.5.3",
|
||||
"eslint": "^7.3.0",
|
||||
"exports-loader": "^0.7.0",
|
||||
"fast-text-encoding": "^1.0.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"http-server": "^0.12.3",
|
||||
"imports-loader": "^0.8.0",
|
||||
"install": "^0.13.0",
|
||||
"jasmine": "^3.5.0",
|
||||
"jsdoc": "^3.6.6",
|
||||
"karma": "^6.3.2",
|
||||
"jasmine": "^3.7.0",
|
||||
"jsdoc": "^3.6.7",
|
||||
"karma": "^6.3.4",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-cli": "^2.0.0",
|
||||
"karma-jasmine": "^3.1.1",
|
||||
"karma-jasmine": "^4.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.5.4",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"lerna": "^3.22.1",
|
||||
"mini-css-extract-plugin": "^1.5.1",
|
||||
"lerna": "^4.0.0",
|
||||
"mini-css-extract-plugin": "^1.6.2",
|
||||
"minimist": "^1.2.3",
|
||||
"npm": "^6.14.9",
|
||||
"npm": "^7.19.0",
|
||||
"po-loader": "^0.5.0",
|
||||
"po2json": "^1.0.0-beta",
|
||||
"postcss-clean": "^1.1.0",
|
||||
"postcss-clean": "^1.2.2",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prettierx": "^0.12.1",
|
||||
"prettierx": "^0.18.2",
|
||||
"run-headless-chromium": "^0.1.1",
|
||||
"sass": "^1.32.12",
|
||||
"sass-loader": "^11.0.1",
|
||||
"sinon": "^9.2.4",
|
||||
"style-loader": "^0.23.1",
|
||||
"webpack": "^5.36.1",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"webpack-dev-server": "^4.0.0-beta.2",
|
||||
"webpack-merge": "^5.7.3"
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^4.0.0-beta.3",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "5.14.0",
|
||||
|
@ -120,8 +114,7 @@
|
|||
"bootstrap.native": "^2.0.27",
|
||||
"favico.js-slevomat": "^0.3.11",
|
||||
"jed": "1.1.1",
|
||||
"lit": "^2.0.0-rc.1",
|
||||
"urijs": "^1.19.6",
|
||||
"lit": "^2.0.0-rc.2",
|
||||
"xss": "^1.0.8"
|
||||
}
|
||||
}
|
||||
|
|
70
spec/mock.js
70
spec/mock.js
|
@ -15,7 +15,7 @@ mock.initConverse = function (promise_names=[], settings=null, func) {
|
|||
settings = null;
|
||||
}
|
||||
|
||||
return async done => {
|
||||
return async () => {
|
||||
if (_converse && _converse.api.connection.connected()) {
|
||||
await _converse.api.user.logout();
|
||||
}
|
||||
|
@ -28,11 +28,10 @@ mock.initConverse = function (promise_names=[], settings=null, func) {
|
|||
await initConverse(settings);
|
||||
await Promise.all((promise_names || []).map(_converse.api.waitUntil));
|
||||
try {
|
||||
await func(done, _converse);
|
||||
await func(_converse);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
fail(e);
|
||||
await done();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -673,3 +672,68 @@ const initConverse = async (settings) => {
|
|||
window.converse_disable_effects = true;
|
||||
return _converse;
|
||||
}
|
||||
|
||||
|
||||
mock.deviceListFetched = async function deviceListFetched (_converse, jid) {
|
||||
const selector = `iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.devicelist"]`;
|
||||
const stanza = await u.waitUntil(
|
||||
() => Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop()
|
||||
);
|
||||
await u.waitUntil(() => _converse.devicelists.get(jid));
|
||||
return stanza;
|
||||
}
|
||||
|
||||
mock.ownDeviceHasBeenPublished = function ownDeviceHasBeenPublished (_converse) {
|
||||
return Array.from(_converse.connection.IQ_stanzas).filter(
|
||||
iq => iq.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]')
|
||||
).pop();
|
||||
}
|
||||
|
||||
mock.bundleHasBeenPublished = function bundleHasBeenPublished (_converse) {
|
||||
const selector = 'publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]';
|
||||
return Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop();
|
||||
}
|
||||
|
||||
mock.bundleFetched = function bundleFetched (_converse, jid, device_id) {
|
||||
return Array.from(_converse.connection.IQ_stanzas).filter(
|
||||
iq => iq.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.bundles:${device_id}"]`)
|
||||
).pop();
|
||||
}
|
||||
|
||||
mock.initializedOMEMO = async function initializedOMEMO (_converse) {
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.bare_jid,
|
||||
[{'category': 'pubsub', 'type': 'pep'}],
|
||||
['http://jabber.org/protocol/pubsub#publish-options']
|
||||
);
|
||||
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, _converse.bare_jid));
|
||||
let stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '482886413b977930064a5888b92134fe'});
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
iq_stanza = await u.waitUntil(() => mock.ownDeviceHasBeenPublished(_converse))
|
||||
|
||||
stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
|
||||
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse))
|
||||
|
||||
stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await _converse.api.waitUntil('OMEMOInitialized');
|
||||
}
|
||||
|
|
24
spec/push.js
24
spec/push.js
|
@ -1,4 +1,4 @@
|
|||
/*global mock, converse, _ */
|
||||
/*global mock, converse */
|
||||
|
||||
const $iq = converse.env.$iq;
|
||||
const Strophe = converse.env.Strophe;
|
||||
|
@ -18,7 +18,7 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'jid': 'push-5@client.example',
|
||||
'node': 'yxs32uqsflafdk3iuqo'
|
||||
}]
|
||||
}, async function (done, _converse) {
|
||||
}, async function (_converse) {
|
||||
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
expect(_converse.session.get('push_enabled')).toBeFalsy();
|
||||
|
@ -33,7 +33,7 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
[{'category': 'account', 'type':'registered'}],
|
||||
['urn:xmpp:push:0'], [], 'info');
|
||||
const stanza = await u.waitUntil(() =>
|
||||
_.filter(IQ_stanzas, iq => iq.querySelector('iq[type="set"] enable[xmlns="urn:xmpp:push:0"]')).pop()
|
||||
IQ_stanzas.filter(iq => iq.querySelector('iq[type="set"] enable[xmlns="urn:xmpp:push:0"]')).pop()
|
||||
);
|
||||
expect(Strophe.serialize(stanza)).toEqual(
|
||||
`<iq id="${stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
||||
|
@ -46,7 +46,6 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'id': stanza.getAttribute('id')
|
||||
})));
|
||||
await u.waitUntil(() => _converse.session.get('push_enabled'));
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be enabled for a MUC domain",
|
||||
|
@ -57,7 +56,7 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'jid': 'push-5@client.example',
|
||||
'node': 'yxs32uqsflafdk3iuqo'
|
||||
}]
|
||||
}, async function (done, _converse) {
|
||||
}, async function (_converse) {
|
||||
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
|
@ -80,16 +79,14 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
|
||||
await u.waitUntil(() => _converse.session.get('push_enabled'));
|
||||
expect(_converse.session.get('push_enabled').length).toBe(1);
|
||||
expect(_.includes(_converse.session.get('push_enabled'), 'romeo@montague.lit')).toBe(true);
|
||||
expect(_converse.session.get('push_enabled').includes('romeo@montague.lit')).toBe(true);
|
||||
|
||||
mock.openAndEnterChatRoom(_converse, 'coven@chat.shakespeare.lit', 'oldhag');
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, 'chat.shakespeare.lit',
|
||||
[{'category': 'account', 'type':'registered'}],
|
||||
['urn:xmpp:push:0'], [], 'info');
|
||||
iq = await u.waitUntil(() => _.filter(
|
||||
IQ_stanzas,
|
||||
iq => sizzle(`iq[type="set"][to="chat.shakespeare.lit"] enable[xmlns="${Strophe.NS.PUSH}"]`, iq).length
|
||||
iq = await u.waitUntil(() => IQ_stanzas.filter(iq => sizzle(`iq[type="set"][to="chat.shakespeare.lit"] enable[xmlns="${Strophe.NS.PUSH}"]`, iq).length
|
||||
).pop());
|
||||
|
||||
expect(Strophe.serialize(iq)).toEqual(
|
||||
|
@ -102,8 +99,7 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'type': 'result',
|
||||
'id': iq.getAttribute('id')
|
||||
})));
|
||||
await u.waitUntil(() => _.includes(_converse.session.get('push_enabled'), 'chat.shakespeare.lit'));
|
||||
done();
|
||||
await u.waitUntil(() => _converse.session.get('push_enabled').includes('chat.shakespeare.lit'));
|
||||
}));
|
||||
|
||||
it("can be disabled",
|
||||
|
@ -114,7 +110,7 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'node': 'yxs32uqsflafdk3iuqo',
|
||||
'disable': true
|
||||
}]
|
||||
}, async function (done, _converse) {
|
||||
}, async function (_converse) {
|
||||
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
expect(_converse.session.get('push_enabled')).toBeFalsy();
|
||||
|
@ -136,7 +132,6 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'id': stanza.getAttribute('id')
|
||||
})));
|
||||
await u.waitUntil(() => _converse.session.get('push_enabled'))
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
|
@ -147,7 +142,7 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'node': 'yxs32uqsflafdk3iuqo',
|
||||
'secret': 'eruio234vzxc2kla-91'
|
||||
}]
|
||||
}, async function (done, _converse) {
|
||||
}, async function (_converse) {
|
||||
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
expect(_converse.session.get('push_enabled')).toBeFalsy();
|
||||
|
@ -179,6 +174,5 @@ describe("XEP-0357 Push Notifications", function () {
|
|||
'id': stanza.getAttribute('id')
|
||||
})));
|
||||
await u.waitUntil(() => _converse.session.get('push_enabled'))
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ const u = converse.env.utils;
|
|||
describe("The User Details Modal", function () {
|
||||
|
||||
it("can be used to remove a contact",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
_converse.api.trigger('rosterContactsFetched');
|
||||
|
@ -30,11 +30,10 @@ describe("The User Details Modal", function () {
|
|||
show_modal_button.click();
|
||||
remove_contact_button = modal.el.querySelector('button.remove-contact');
|
||||
expect(remove_contact_button === null).toBeTruthy();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows an alert when an error happened while removing the contact",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
_converse.api.trigger('rosterContactsFetched');
|
||||
|
@ -71,6 +70,5 @@ describe("The User Details Modal", function () {
|
|||
|
||||
remove_contact_button = modal.el.querySelector('button.remove-contact');
|
||||
expect(u.isVisible(remove_contact_button)).toBeTruthy();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import Storage from '@converse/skeletor/src/storage.js';
|
||||
import URI from 'urijs';
|
||||
import _converse from '@converse/headless/shared/_converse';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import dayjs from 'dayjs';
|
||||
|
@ -48,6 +49,7 @@ Strophe.addNamespace('CARBONS', 'urn:xmpp:carbons:2');
|
|||
Strophe.addNamespace('CHATSTATES', 'http://jabber.org/protocol/chatstates');
|
||||
Strophe.addNamespace('CSI', 'urn:xmpp:csi:0');
|
||||
Strophe.addNamespace('DELAY', 'urn:xmpp:delay');
|
||||
Strophe.addNamespace('EME', 'urn:xmpp:eme:0');
|
||||
Strophe.addNamespace('FASTEN', 'urn:xmpp:fasten:0');
|
||||
Strophe.addNamespace('FORWARD', 'urn:xmpp:forward:0');
|
||||
Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
|
||||
|
@ -1415,6 +1417,7 @@ Object.assign(converse, {
|
|||
Model,
|
||||
Promise,
|
||||
Strophe,
|
||||
URI,
|
||||
dayjs,
|
||||
html,
|
||||
log,
|
||||
|
|
|
@ -1,77 +1,178 @@
|
|||
{
|
||||
"name": "@converse/headless",
|
||||
"version": "6.0.0",
|
||||
"version": "8.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@converse/openpromise": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
||||
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
||||
},
|
||||
"@converse/skeletor": {
|
||||
"version": "github:conversejs/skeletor#f354bc530493a17d031f6f9c524cc34e073908e3",
|
||||
"from": "github:conversejs/skeletor#f354bc530493a17d031f6f9c524cc34e073908e3",
|
||||
"requires": {
|
||||
"lit-html": "^2.0.0-rc.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mergebounce": "0.0.2"
|
||||
}
|
||||
},
|
||||
"@types/trusted-types": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz",
|
||||
"integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw=="
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"requires": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.10.5",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.5.tgz",
|
||||
"integrity": "sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g=="
|
||||
},
|
||||
"filesize": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz",
|
||||
"integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==",
|
||||
"dev": true
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-6.3.0.tgz",
|
||||
"integrity": "sha512-ytx0ruGpDHKWVoiui6+BY/QMNngtDQ/pJaFwfBpQif0J63+E8DLdFyqS3NkKQn7vIruUEpoGD9JUJSg7Kp+I0g=="
|
||||
},
|
||||
"immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
|
||||
"dev": true
|
||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
|
||||
},
|
||||
"lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"lit-html": {
|
||||
"version": "2.0.0-rc.3",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.0-rc.3.tgz",
|
||||
"integrity": "sha512-Y6P8LlAyQuqvzq6l/Nc4z5/P5M/rVLYKQIRxcNwSuGajK0g4kbcBFQqZmgvqKG+ak+dHZjfm2HUw9TF5N/pkCw==",
|
||||
"requires": {
|
||||
"@types/trusted-types": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"localforage": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz",
|
||||
"integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==",
|
||||
"dev": true,
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz",
|
||||
"integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==",
|
||||
"requires": {
|
||||
"lie": "3.1.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
"localforage-driver-commons": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/localforage-driver-commons/-/localforage-driver-commons-1.0.3.tgz",
|
||||
"integrity": "sha512-K9PiNNXcyX98lQVyCADjv+QKxFD71y0DtVUhqMjwCkFY/d/g7GdJLPN9U92M7RUvfkL8mzPhC+mWEKo9tur5oQ=="
|
||||
},
|
||||
"localforage-driver-memory": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/localforage-driver-memory/-/localforage-driver-memory-1.0.5.tgz",
|
||||
"integrity": "sha512-m4v478ixdT3hA7gKv+pAxDIWgMKiUV2GuYem5jnpOBQFVJbrHU7jmNlrj8a0MfD9qff3i48E3Yfip5Eu1AN6Qg==",
|
||||
"requires": {
|
||||
"localforage-driver-commons": "^1.0.1",
|
||||
"tslib": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"localforage-setitems": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
||||
"integrity": "sha1-NrhZDVB9+1yAQDPih+zljYiZbV8=",
|
||||
"requires": {
|
||||
"localforage": ">=1.4.0"
|
||||
}
|
||||
},
|
||||
"localforage-webextensionstorage-driver": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage-webextensionstorage-driver/-/localforage-webextensionstorage-driver-2.0.0.tgz",
|
||||
"integrity": "sha512-gB9q+NOn3D62x8Akn7nykh2H0ArNehYflZ3sgGZNc8eB6Yf0HnK30vwpe0xXTLYMIe15XeRNiiZd8qwTFnGYSw==",
|
||||
"requires": {
|
||||
"babel-runtime": "^6.22.0"
|
||||
}
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
|
||||
"integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==",
|
||||
"dev": true
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"mergebounce": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mergebounce/-/mergebounce-0.0.2.tgz",
|
||||
"integrity": "sha512-1nxx6ljFJkx26WlwQLzbaBQc6lDg7mqdHPhIDixpOW+7Idx6DdPBrUZCwinihWbw33B1/YhZbdLU7dAf1vyC6w==",
|
||||
"requires": {
|
||||
"@converse/openpromise": "0.0.1",
|
||||
"lodash-es": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"pluggable.js": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-2.0.1.tgz",
|
||||
"integrity": "sha512-SBt6v6Tbp20Jf8hU0cpcc/+HBHGMY8/Q+yA6Ih0tBQE8tfdZ6U4PRG0iNvUUjLx/hVyOP53n0UfGBymlfaaXCg==",
|
||||
"dev": true,
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pluggable.js/-/pluggable.js-3.0.1.tgz",
|
||||
"integrity": "sha512-DQC51A6aKLk6anvyvQfukNcVzGHOI5B04DerHioqLSF7ptI+Nla2hHzG4PGxq8tKqOGwQHnXnj9qxcFM3VViEQ==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.11"
|
||||
"lodash-es": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
},
|
||||
"sizzle": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.6.tgz",
|
||||
"integrity": "sha512-abtd95IkbcMAaYk1Lux4k9Xz6wnQqyLy2aco9HGJ8jVaCDEcc+ug0hW8RdV6aIre3ycWXxPdcX0u7QL/1UaSoA=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||
},
|
||||
"strophe.js": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.4.2.tgz",
|
||||
"integrity": "sha512-jkyZQCZLm7Zgmra0zJKxpHPNIUncYj/e/eYfgxFoc5gwrWeHWigNBs0q7wtqhCiqG6Qxcf22PUpcyBq8cK+9ew==",
|
||||
"requires": {
|
||||
"abab": "^2.0.3",
|
||||
"ws": "^7.0.0",
|
||||
"xmldom": "0.5.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"xmldom": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz",
|
||||
"integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
||||
"devDependencies": {},
|
||||
"dependencies": {
|
||||
"@converse/skeletor": "conversejs/skeletor#f354bc530493a17d031f6f9c524cc34e073908e3",
|
||||
"dayjs": "1.10.4",
|
||||
"filesize": "^6.1.0",
|
||||
"@converse/skeletor": "0.0.5",
|
||||
"dayjs": "1.10.5",
|
||||
"filesize": "^6.3.0",
|
||||
"localforage": "^1.9.0",
|
||||
"localforage-driver-memory": "^1.0.5",
|
||||
"localforage-setitems": "^1.4.0",
|
||||
|
@ -48,6 +48,7 @@
|
|||
"pluggable.js": "3.0.1",
|
||||
"sizzle": "^2.3.5",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"strophe.js": "1.4.2"
|
||||
"strophe.js": "1.4.2",
|
||||
"urijs": "^1.19.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ describe("A sent presence stanza", function () {
|
|||
afterEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = original_timeout));
|
||||
|
||||
it("includes a entity capabilities node",
|
||||
mock.initConverse([], {}, async (done, _converse) => {
|
||||
mock.initConverse([], {}, async (_converse) => {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
_converse.api.disco.own.identities.clear();
|
||||
|
@ -27,10 +27,9 @@ describe("A sent presence stanza", function () {
|
|||
`<priority>0</priority>`+
|
||||
`<c hash="sha-1" node="https://conversejs.org" ver="QgayPKawpkPSDYmwT/WM94uAlu0=" xmlns="http://jabber.org/protocol/caps"/>`+
|
||||
`</presence>`)
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a given priority", mock.initConverse(['statusInitialized'], {}, async (done, _converse) => {
|
||||
it("has a given priority", mock.initConverse(['statusInitialized'], {}, async (_converse) => {
|
||||
const { api } = _converse;
|
||||
let pres = await _converse.xmppstatus.constructPresence('online', null, 'Hello world');
|
||||
expect(pres.toLocaleString()).toBe(
|
||||
|
@ -62,6 +61,5 @@ describe("A sent presence stanza", function () {
|
|||
`<c hash="sha-1" node="https://conversejs.org" ver="PxXfr6uz8ClMWIga0OB/MhKNH/M=" xmlns="http://jabber.org/protocol/caps"/>`+
|
||||
`</presence>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -224,15 +224,23 @@ const MessageMixin = {
|
|||
|
||||
uploadFile () {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = () => {
|
||||
xhr.onreadystatechange = async () => {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
log.info('Status: ' + xhr.status);
|
||||
if (xhr.status === 200 || xhr.status === 201) {
|
||||
this.save({
|
||||
let attrs = {
|
||||
'upload': _converse.SUCCESS,
|
||||
'oob_url': this.get('get'),
|
||||
'message': this.get('get')
|
||||
});
|
||||
'message': this.get('get'),
|
||||
'body': this.get('get'),
|
||||
};
|
||||
/**
|
||||
* *Hook* which allows plugins to change the attributes
|
||||
* saved on the message once a file has been uploaded.
|
||||
* @event _converse#afterFileUploaded
|
||||
*/
|
||||
attrs = await api.hook('afterFileUploaded', this, attrs);
|
||||
this.save(attrs);
|
||||
} else {
|
||||
xhr.onerror();
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { _converse, api, converse } from "../../core.js";
|
|||
import { getOpenPromise } from '@converse/openpromise';
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
import { debouncedPruneHistory, pruneHistory } from '@converse/headless/shared/chat/utils.js';
|
||||
import { getMediaURLs } from '@converse/headless/shared/parsers';
|
||||
import { parseMessage } from './parsers.js';
|
||||
import { sendMarker } from '@converse/headless/shared/actions';
|
||||
|
||||
|
@ -56,8 +57,8 @@ const ChatBox = ModelWithContact.extend({
|
|||
}
|
||||
this.set({'box_id': `box-${jid}`});
|
||||
this.initNotifications();
|
||||
this.initMessages();
|
||||
this.initUI();
|
||||
this.initMessages();
|
||||
|
||||
if (this.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
this.presence = _converse.presences.findWhere({'jid': jid}) || _converse.presences.create({'jid': jid});
|
||||
|
@ -241,9 +242,16 @@ const ChatBox = ModelWithContact.extend({
|
|||
}
|
||||
},
|
||||
|
||||
onMessageUploadChanged (message) {
|
||||
async onMessageUploadChanged (message) {
|
||||
if (message.get('upload') === _converse.SUCCESS) {
|
||||
api.send(this.createMessageStanza(message));
|
||||
const attrs = {
|
||||
'body': message.get('message'),
|
||||
'spoiler_hint': message.get('spoiler_hint'),
|
||||
'oob_url': message.get('oob_url')
|
||||
|
||||
}
|
||||
await this.sendMessage(attrs);
|
||||
message.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -338,9 +346,10 @@ const ChatBox = ModelWithContact.extend({
|
|||
},
|
||||
|
||||
pruneHistoryWhenScrolledDown () {
|
||||
if (!this.ui.get('scrolled') &&
|
||||
if (
|
||||
api.settings.get('prune_messages_above') &&
|
||||
api.settings.get('pruning_behavior') === 'unscrolled'
|
||||
api.settings.get('pruning_behavior') === 'unscrolled' &&
|
||||
!this.ui.get('scrolled')
|
||||
) {
|
||||
pruneHistory(this);
|
||||
}
|
||||
|
@ -841,11 +850,12 @@ const ChatBox = ModelWithContact.extend({
|
|||
return stanza;
|
||||
},
|
||||
|
||||
getOutgoingMessageAttributes (text, spoiler_hint) {
|
||||
const is_spoiler = this.get('composing_spoiler');
|
||||
getOutgoingMessageAttributes (attrs) {
|
||||
const is_spoiler = !!this.get('composing_spoiler');
|
||||
const origin_id = u.getUniqueId();
|
||||
const text = attrs?.body;
|
||||
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
||||
return {
|
||||
return Object.assign({}, attrs, {
|
||||
'from': _converse.bare_jid,
|
||||
'fullname': _converse.xmppstatus.get('fullname'),
|
||||
'id': origin_id,
|
||||
|
@ -855,13 +865,12 @@ const ChatBox = ModelWithContact.extend({
|
|||
'msgid': origin_id,
|
||||
'nickname': this.get('nickname'),
|
||||
'sender': 'me',
|
||||
'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
|
||||
'time': (new Date()).toISOString(),
|
||||
'type': this.get('message_type'),
|
||||
body,
|
||||
is_spoiler,
|
||||
origin_id
|
||||
}
|
||||
}, getMediaURLs(text));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -910,15 +919,14 @@ const ChatBox = ModelWithContact.extend({
|
|||
* @private
|
||||
* @method _converse.ChatBox#sendMessage
|
||||
* @memberOf _converse.ChatBox
|
||||
* @param { String } text - The chat message text
|
||||
* @param { String } spoiler_hint - An optional hint, if the message being sent is a spoiler
|
||||
* @param { Object } [attrs] - A map of attributes to be saved on the message
|
||||
* @returns { _converse.Message }
|
||||
* @example
|
||||
* const chat = api.chats.get('buddy1@example.com');
|
||||
* chat.sendMessage('hello world');
|
||||
* const chat = api.chats.get('buddy1@example.org');
|
||||
* chat.sendMessage({'body': 'hello world'});
|
||||
*/
|
||||
async sendMessage (text, spoiler_hint) {
|
||||
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
|
||||
async sendMessage (attrs) {
|
||||
attrs = this.getOutgoingMessageAttributes(attrs);
|
||||
let message = this.messages.findWhere('correcting')
|
||||
if (message) {
|
||||
const older_versions = message.get('older_versions') || {};
|
||||
|
@ -1001,6 +1009,13 @@ const ChatBox = ModelWithContact.extend({
|
|||
return;
|
||||
}
|
||||
Array.from(files).forEach(async file => {
|
||||
/**
|
||||
* *Hook* which allows plugins to transform files before they'll be
|
||||
* uploaded. The main use-case is to encrypt the files.
|
||||
* @event _converse#beforeFileUpload
|
||||
*/
|
||||
file = await api.hook('beforeFileUpload', this, file);
|
||||
|
||||
if (!window.isNaN(max_file_size) && window.parseInt(file.size) > max_file_size) {
|
||||
return this.createMessage({
|
||||
'message': __('The size of your file, %1$s, exceeds the maximum allowed by your server, which is %2$s.',
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
getCorrectionAttributes,
|
||||
getEncryptionAttributes,
|
||||
getErrorAttributes,
|
||||
getMediaURLs,
|
||||
getOutOfBandAttributes,
|
||||
getReceiptId,
|
||||
getReferences,
|
||||
|
@ -215,5 +216,10 @@ export async function parseMessage (stanza, _converse) {
|
|||
* *Hook* which allows plugins to add additional parsing
|
||||
* @event _converse#parseMessage
|
||||
*/
|
||||
return api.hook('parseMessage', stanza, attrs);
|
||||
attrs = await api.hook('parseMessage', stanza, attrs);
|
||||
|
||||
// We call this after the hook, to allow plugins to decrypt encrypted
|
||||
// messages, since we need to parse the message text to determine whether
|
||||
// there are media urls.
|
||||
return Object.assign(attrs, getMediaURLs(attrs.is_encrypted ? attrs.plaintext : attrs.body));
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ describe("The \"chats\" API", function() {
|
|||
|
||||
it("has a method 'get' which returns the promise that resolves to a chat model", mock.initConverse(
|
||||
['rosterInitialized', 'chatBoxesInitialized'], {},
|
||||
async (done, _converse) => {
|
||||
async (_converse) => {
|
||||
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
@ -28,17 +28,16 @@ describe("The \"chats\" API", function() {
|
|||
expect(chat.get('box_id')).toBe(`box-${jid}`);
|
||||
|
||||
// Test for multiple JIDs
|
||||
mock.openChatBoxFor(_converse, jid2);
|
||||
await mock.openChatBoxFor(_converse, jid2);
|
||||
await u.waitUntil(() => _converse.chatboxes.length == 3);
|
||||
const list = await _converse.api.chats.get([jid, jid2]);
|
||||
expect(Array.isArray(list)).toBeTruthy();
|
||||
expect(list[0].get('box_id')).toBe(`box-${jid}`);
|
||||
expect(list[1].get('box_id')).toBe(`box-${jid2}`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a method 'open' which opens and returns a promise that resolves to a chat model", mock.initConverse(
|
||||
['chatBoxesInitialized'], {}, async (done, _converse) => {
|
||||
['chatBoxesInitialized'], {}, async (_converse) => {
|
||||
|
||||
await mock.openControlBox(_converse);
|
||||
await mock.waitForRoster(_converse, 'current', 2);
|
||||
|
@ -62,6 +61,5 @@ describe("The \"chats\" API", function() {
|
|||
expect(Array.isArray(list)).toBeTruthy();
|
||||
expect(list[0].get('box_id')).toBe(`box-${jid}`);
|
||||
expect(list[1].get('box_id')).toBe(`box-${jid2}`);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
import DiscoEntities from './entities.js';
|
||||
import DiscoEntity from './entity.js';
|
||||
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||
import { initializeDisco, initStreamFeatures, notifyStreamFeaturesAdded, populateStreamFeatures } from './utils.js';
|
||||
import {
|
||||
clearSession,
|
||||
initStreamFeatures,
|
||||
initializeDisco,
|
||||
notifyStreamFeaturesAdded,
|
||||
populateStreamFeatures
|
||||
} from './utils.js';
|
||||
import disco_api from './api.js';
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
|
@ -46,15 +52,10 @@ converse.plugins.add('converse-disco', {
|
|||
}
|
||||
});
|
||||
|
||||
api.listen.on('clearSession', () => {
|
||||
if (_converse.shouldClearCache() && _converse.disco_entities) {
|
||||
Array.from(_converse.disco_entities.models).forEach(e => e.features.clearStore());
|
||||
Array.from(_converse.disco_entities.models).forEach(e => e.identities.clearStore());
|
||||
Array.from(_converse.disco_entities.models).forEach(e => e.dataforms.clearStore());
|
||||
Array.from(_converse.disco_entities.models).forEach(e => e.fields.clearStore());
|
||||
_converse.disco_entities.clearStore();
|
||||
delete _converse.disco_entities;
|
||||
}
|
||||
});
|
||||
// All disco entities stored in sessionStorage and are refetched
|
||||
// upon login or reconnection and then stored with new ids, so to
|
||||
// avoid sessionStorage filling up, we remove them.
|
||||
api.listen.on('will-reconnect', clearSession);
|
||||
api.listen.on('clearSession', clearSession);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ describe("Service Discovery", function () {
|
|||
it("stores the features it receives",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const { u, $iq } = converse.env;
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
|
@ -161,7 +161,6 @@ describe("Service Discovery", function () {
|
|||
expect(entities.get(_converse.domain).items.pluck('jid').includes('words.shakespeare.lit')).toBeTruthy();
|
||||
expect(entities.get(_converse.domain).identities.where({'category': 'conference'}).length).toBe(1);
|
||||
expect(entities.get(_converse.domain).identities.where({'category': 'directory'}).length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -169,15 +168,15 @@ describe("Service Discovery", function () {
|
|||
it("emits the serviceDiscovered event",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
function (done, _converse) {
|
||||
function (_converse) {
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
sinon.spy(_converse.api, "trigger");
|
||||
spyOn(_converse.api, "trigger").and.callThrough();
|
||||
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
|
||||
expect(_converse.api.trigger.called).toBe(true);
|
||||
expect(_converse.api.trigger.args[0][0]).toBe('serviceDiscovered');
|
||||
expect(_converse.api.trigger.args[0][1].get('var')).toBe(Strophe.NS.MAM);
|
||||
done();
|
||||
expect(_converse.api.trigger).toHaveBeenCalled();
|
||||
const last_call = _converse.api.trigger.calls.all().pop();
|
||||
expect(last_call.args[0]).toBe('serviceDiscovered');
|
||||
expect(last_call.args[1].get('var')).toBe(Strophe.NS.MAM);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -112,7 +112,7 @@ export function populateStreamFeatures () {
|
|||
// Strophe.js sets the <stream:features> element on the
|
||||
// Strophe.Connection instance (_converse.connection).
|
||||
//
|
||||
// Once this is done, we populate the _converse.stream_features collection
|
||||
// Once this is we populate the _converse.stream_features collection
|
||||
// and trigger streamFeaturesAdded.
|
||||
initStreamFeatures();
|
||||
Array.from(_converse.connection.features.childNodes).forEach(feature => {
|
||||
|
@ -123,3 +123,12 @@ export function populateStreamFeatures () {
|
|||
});
|
||||
notifyStreamFeaturesAdded();
|
||||
}
|
||||
|
||||
export function clearSession () {
|
||||
_converse.disco_entities?.forEach(e => e.features.clearStore());
|
||||
_converse.disco_entities?.forEach(e => e.identities.clearStore());
|
||||
_converse.disco_entities?.forEach(e => e.dataforms.clearStore());
|
||||
_converse.disco_entities?.forEach(e => e.fields.clearStore());
|
||||
_converse.disco_entities.clearStore();
|
||||
delete _converse.disco_entities;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ converse.plugins.add('converse-headlines', {
|
|||
|
||||
async initialize () {
|
||||
this.set({'box_id': `box-${this.get('jid')}`});
|
||||
this.initUI();
|
||||
this.initMessages();
|
||||
await this.fetchMessages();
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@ import { _converse, api, converse } from '../../core.js';
|
|||
import { computeAffiliationsDelta, setAffiliations, getAffiliationList } from './affiliations/utils.js';
|
||||
import { getOpenPromise } from '@converse/openpromise';
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
import { isArchived } from '@converse/headless/shared/parsers';
|
||||
import { isArchived, getMediaURLs } from '@converse/headless/shared/parsers';
|
||||
import { parseMUCMessage, parseMUCPresence } from './parsers.js';
|
||||
import { sendMarker } from '@converse/headless/shared/actions';
|
||||
|
||||
|
@ -438,10 +438,14 @@ const ChatRoomMixin = {
|
|||
};
|
||||
if (attrs.msgid === message.get('retraction_id')) {
|
||||
// The error message refers to a retraction
|
||||
new_attrs.retracted = undefined;
|
||||
new_attrs.retraction_id = undefined;
|
||||
new_attrs.retracted_id = undefined;
|
||||
|
||||
if (!attrs.error) {
|
||||
if (attrs.error_condition === 'forbidden') {
|
||||
new_attrs.error = __("You're not allowed to retract your message.");
|
||||
|
||||
} else if (attrs.error_condition === 'not-acceptable') {
|
||||
new_attrs.error = __(
|
||||
"Your retraction was not delivered because you're not present in the groupchat."
|
||||
|
@ -665,8 +669,7 @@ const ChatRoomMixin = {
|
|||
* @method _converse.ChatRoom#sendTimedMessage
|
||||
* @param { _converse.Message|XMLElement } message
|
||||
* @returns { Promise<XMLElement>|Promise<_converse.TimeoutError> } Returns a promise
|
||||
* which resolves with the reflected message stanza or rejects
|
||||
* with an error stanza or with a {@link _converse.TimeoutError}.
|
||||
* which resolves with the reflected message stanza or with an error stanza or {@link _converse.TimeoutError}.
|
||||
*/
|
||||
sendTimedMessage (el) {
|
||||
if (typeof el.tree === 'function') {
|
||||
|
@ -681,23 +684,15 @@ const ChatRoomMixin = {
|
|||
const promise = getOpenPromise();
|
||||
const timeoutHandler = _converse.connection.addTimedHandler(_converse.STANZA_TIMEOUT, () => {
|
||||
_converse.connection.deleteHandler(handler);
|
||||
promise.reject(new _converse.TimeoutError('Timeout Error: No response from server'));
|
||||
const err = new _converse.TimeoutError('Timeout Error: No response from server');
|
||||
promise.resolve(err);
|
||||
return false;
|
||||
});
|
||||
const handler = _converse.connection.addHandler(
|
||||
stanza => {
|
||||
timeoutHandler && _converse.connection.deleteTimedHandler(timeoutHandler);
|
||||
if (stanza.getAttribute('type') === 'groupchat') {
|
||||
promise.resolve(stanza);
|
||||
} else {
|
||||
promise.reject(stanza);
|
||||
}
|
||||
},
|
||||
null,
|
||||
'message',
|
||||
['error', 'groupchat'],
|
||||
id
|
||||
);
|
||||
}, null, 'message', ['error', 'groupchat'], id);
|
||||
api.send(el);
|
||||
return promise;
|
||||
},
|
||||
|
@ -735,17 +730,20 @@ const ChatRoomMixin = {
|
|||
'retraction_id': stanza.nodeTree.getAttribute('id'),
|
||||
'editable': false
|
||||
});
|
||||
try {
|
||||
await this.sendTimedMessage(stanza);
|
||||
} catch (e) {
|
||||
const result = await this.sendTimedMessage(stanza);
|
||||
|
||||
if (u.isErrorStanza(result)) {
|
||||
log.error(result);
|
||||
} else if (result instanceof _converse.TimeoutError) {
|
||||
log.error(result);
|
||||
message.save({
|
||||
editable,
|
||||
'error_type': 'timeout',
|
||||
'error': __('A timeout happened while while trying to retract your message.'),
|
||||
'retracted': undefined,
|
||||
'retracted_id': undefined
|
||||
'retracted_id': undefined,
|
||||
'retraction_id': undefined
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -961,12 +959,15 @@ const ChatRoomMixin = {
|
|||
return [updated_message, updated_references];
|
||||
},
|
||||
|
||||
getOutgoingMessageAttributes (original_message, spoiler_hint) {
|
||||
getOutgoingMessageAttributes (attrs) {
|
||||
const is_spoiler = this.get('composing_spoiler');
|
||||
const [text, references] = this.parseTextForReferences(original_message);
|
||||
let text = '', references;
|
||||
if (attrs?.body) {
|
||||
[text, references] = this.parseTextForReferences(attrs.body);
|
||||
}
|
||||
const origin_id = u.getUniqueId();
|
||||
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
||||
return {
|
||||
return Object.assign({}, attrs, {
|
||||
body,
|
||||
is_spoiler,
|
||||
origin_id,
|
||||
|
@ -979,9 +980,8 @@ const ChatRoomMixin = {
|
|||
'message': body,
|
||||
'nick': this.get('nick'),
|
||||
'sender': 'me',
|
||||
'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
|
||||
'type': 'groupchat'
|
||||
};
|
||||
}, getMediaURLs(text));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
getCorrectionAttributes,
|
||||
getEncryptionAttributes,
|
||||
getErrorAttributes,
|
||||
getMediaURLs,
|
||||
getOpenGraphMetadata,
|
||||
getOutOfBandAttributes,
|
||||
getReceiptId,
|
||||
|
@ -184,9 +185,10 @@ export async function parseMUCMessage (stanza, chatbox, _converse) {
|
|||
getOpenGraphMetadata(stanza),
|
||||
getRetractionAttributes(stanza, original_stanza),
|
||||
getModerationAttributes(stanza),
|
||||
getEncryptionAttributes(stanza, _converse)
|
||||
getEncryptionAttributes(stanza, _converse),
|
||||
);
|
||||
|
||||
|
||||
await api.emojis.initialize();
|
||||
attrs = Object.assign(
|
||||
{
|
||||
|
@ -213,11 +215,17 @@ export async function parseMUCMessage (stanza, chatbox, _converse) {
|
|||
}
|
||||
// We prefer to use one of the XEP-0359 unique and stable stanza IDs as the Model id, to avoid duplicates.
|
||||
attrs['id'] = attrs['origin_id'] || attrs[`stanza_id ${attrs.from_muc || attrs.from}`] || u.getUniqueId();
|
||||
|
||||
/**
|
||||
* *Hook* which allows plugins to add additional parsing
|
||||
* @event _converse#parseMUCMessage
|
||||
*/
|
||||
return api.hook('parseMUCMessage', stanza, attrs);
|
||||
attrs = await api.hook('parseMUCMessage', stanza, attrs);
|
||||
|
||||
// We call this after the hook, to allow plugins to decrypt encrypted
|
||||
// messages, since we need to parse the message text to determine whether
|
||||
// there are media urls.
|
||||
return Object.assign(attrs, getMediaURLs(attrs.is_encrypted ? attrs.plaintext : attrs.body));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@ const Strophe = converse.env.Strophe;
|
|||
describe('The MUC Affiliations API', function () {
|
||||
|
||||
it('can be used to set affiliations in MUCs without having to join them first',
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
const { api } = _converse;
|
||||
const user_jid = 'annoyingguy@montague.lit';
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
|
@ -36,7 +36,6 @@ describe('The MUC Affiliations API', function () {
|
|||
`</query>` +
|
||||
`</iq>`);
|
||||
|
||||
done();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -8,26 +8,26 @@ describe("A Groupchat Message", function () {
|
|||
mock.initConverse(
|
||||
['chatBoxesFetched'],
|
||||
{'prune_messages_above': 3},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
expect(model.ui.get('scrolled')).toBeFalsy();
|
||||
|
||||
model.sendMessage('1st message');
|
||||
model.sendMessage('2nd message');
|
||||
model.sendMessage('3rd message');
|
||||
model.sendMessage({'body': '1st message'});
|
||||
model.sendMessage({'body': '2nd message'});
|
||||
model.sendMessage({'body': '3rd message'});
|
||||
await u.waitUntil(() => model.messages.length === 3);
|
||||
// Make sure pruneHistory fires
|
||||
await new Promise(resolve => setTimeout(resolve, 550));
|
||||
|
||||
model.sendMessage('4th message');
|
||||
model.sendMessage({'body': '4th message'});
|
||||
await u.waitUntil(() => model.messages.length === 4);
|
||||
await u.waitUntil(() => model.messages.length === 3, 550);
|
||||
|
||||
model.ui.set('scrolled', true);
|
||||
model.sendMessage('5th message');
|
||||
model.sendMessage('6th message');
|
||||
model.sendMessage({'body': '5th message'});
|
||||
model.sendMessage({'body': '6th message'});
|
||||
await u.waitUntil(() => model.messages.length === 5);
|
||||
|
||||
// Wait long enough to be sure the debounced pruneHistory method didn't fire.
|
||||
|
@ -47,6 +47,5 @@ describe("A Groupchat Message", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await u.waitUntil(() => model.messages.length === 4);
|
||||
await u.waitUntil(() => model.messages.length === 3, 550);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ describe("Chatrooms", function () {
|
|||
|
||||
it("allows you to automatically register your nickname when joining a room",
|
||||
mock.initConverse(['chatBoxesFetched'], {'auto_register_muc_nickname': true},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const muc_jid = 'coven@chat.shakespeare.lit';
|
||||
const room = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
|
@ -47,12 +47,11 @@ describe("Chatrooms", function () {
|
|||
`</x>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("allows you to automatically deregister your nickname when closing a room",
|
||||
mock.initConverse(['chatBoxesFetched'], {'auto_register_muc_nickname': 'unregister'},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const muc_jid = 'coven@chat.shakespeare.lit';
|
||||
const room = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
|
@ -96,7 +95,6 @@ describe("Chatrooms", function () {
|
|||
}).c('query', {'xmlns': 'jabber:iq:register'});
|
||||
_converse.connection._dataRecv(mock.createRequest(result));
|
||||
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ describe("XMPP Ping", function () {
|
|||
describe("An IQ stanza", function () {
|
||||
|
||||
it("is returned when converse.js gets pinged",
|
||||
mock.initConverse(['statusInitialized'], {}, (done, _converse) => {
|
||||
mock.initConverse(['statusInitialized'], {}, (_converse) => {
|
||||
const ping = u.toStanza(`
|
||||
<iq from="${_converse.domain}"
|
||||
to="${_converse.jid}" id="s2c1" type="get">
|
||||
|
@ -19,17 +19,15 @@ describe("XMPP Ping", function () {
|
|||
const sent_stanza = _converse.connection.IQ_stanzas.pop();
|
||||
expect(Strophe.serialize(sent_stanza)).toBe(
|
||||
`<iq id="s2c1" to="${_converse.domain}" type="result" xmlns="jabber:client"/>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is sent out when converse.js pings a server", mock.initConverse((done, _converse) => {
|
||||
it("is sent out when converse.js pings a server", mock.initConverse((_converse) => {
|
||||
_converse.api.ping();
|
||||
const sent_stanza = _converse.connection.IQ_stanzas.pop();
|
||||
expect(Strophe.serialize(sent_stanza)).toBe(
|
||||
`<iq id="${sent_stanza.getAttribute('id')}" to="montague.lit" type="get" xmlns="jabber:client">`+
|
||||
`<ping xmlns="urn:xmpp:ping"/>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { getOpenPromise } from '@converse/openpromise';
|
||||
import { rejectPresenceSubscription } from './utils.js';
|
||||
|
||||
const { Strophe, $iq, $pres } = converse.env;
|
||||
|
||||
|
@ -11,6 +12,7 @@ const { Strophe, $iq, $pres } = converse.env;
|
|||
const RosterContact = Model.extend({
|
||||
defaults: {
|
||||
'chat_state': undefined,
|
||||
'groups': [],
|
||||
'image': _converse.DEFAULT_IMAGE,
|
||||
'image_type': _converse.DEFAULT_IMAGE_TYPE,
|
||||
'num_unread': 0,
|
||||
|
@ -24,7 +26,6 @@ const RosterContact = Model.extend({
|
|||
const bare_jid = Strophe.getBareJidFromJid(jid).toLowerCase();
|
||||
attributes.jid = bare_jid;
|
||||
this.set(Object.assign({
|
||||
'groups': [],
|
||||
'id': bare_jid,
|
||||
'jid': bare_jid,
|
||||
'user_id': Strophe.getNodeFromJid(jid)
|
||||
|
@ -147,7 +148,7 @@ const RosterContact = Model.extend({
|
|||
* @param { String } message - Optional message to send to the person being unauthorized
|
||||
*/
|
||||
unauthorize (message) {
|
||||
_converse.rejectPresenceSubscription(this.get('jid'), message);
|
||||
rejectPresenceSubscription(this.get('jid'), message);
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ import { Model } from "@converse/skeletor/src/model";
|
|||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
import { rejectPresenceSubscription } from './utils.js';
|
||||
|
||||
const { Strophe, $iq, sizzle } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
const { Strophe, $iq, sizzle, u } = converse.env;
|
||||
|
||||
|
||||
const RosterContacts = Collection.extend({
|
||||
|
@ -299,7 +299,7 @@ const RosterContacts = Collection.extend({
|
|||
const contact = this.get(jid);
|
||||
const subscription = item.getAttribute("subscription");
|
||||
const ask = item.getAttribute("ask");
|
||||
const groups = Array.from(item.getElementsByTagName('group')).map(e => e.textContent);
|
||||
const groups = [...new Set(sizzle('group', item).map(e => e.textContent))];
|
||||
if (!contact) {
|
||||
if ((subscription === "none" && ask === null) || (subscription === "remove")) {
|
||||
return; // We're lazy when adding contacts.
|
||||
|
@ -355,7 +355,7 @@ const RosterContacts = Collection.extend({
|
|||
contact = this.get(bare_jid);
|
||||
|
||||
if (!api.settings.get('allow_contact_requests')) {
|
||||
_converse.rejectPresenceSubscription(
|
||||
rejectPresenceSubscription(
|
||||
jid,
|
||||
__("This client does not allow presence subscriptions")
|
||||
);
|
||||
|
|
|
@ -2,41 +2,34 @@
|
|||
* @copyright The Converse.js contributors
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import "@converse/headless/plugins/status";
|
||||
import '@converse/headless/plugins/status';
|
||||
import RosterContact from './contact.js';
|
||||
import RosterContacts from './contacts.js';
|
||||
import invoke from 'lodash-es/invoke';
|
||||
import log from "@converse/headless/log";
|
||||
import roster_api from './api.js';
|
||||
import { Presence, Presences } from './presence.js';
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { clearPresences, initRoster, updateUnreadCounter } from './utils.js';
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
|
||||
const { $pres } = converse.env;
|
||||
import { _converse, api, converse } from '@converse/headless/core';
|
||||
import {
|
||||
onChatBoxesInitialized,
|
||||
onClearSession,
|
||||
onPresencesInitialized,
|
||||
onRosterContactsFetched,
|
||||
onStatusInitialized,
|
||||
unregisterPresenceHandler,
|
||||
} from './utils.js';
|
||||
|
||||
|
||||
converse.plugins.add('converse-roster', {
|
||||
|
||||
dependencies: ['converse-status'],
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by converse.js's plugin machinery.
|
||||
*/
|
||||
api.settings.extend({
|
||||
'allow_contact_requests': true,
|
||||
'auto_subscribe': false,
|
||||
'synchronize_availability': true,
|
||||
'synchronize_availability': true
|
||||
});
|
||||
|
||||
api.promises.add([
|
||||
'cachedRoster',
|
||||
'roster',
|
||||
'rosterContactsFetched',
|
||||
'rosterInitialized',
|
||||
]);
|
||||
api.promises.add(['cachedRoster', 'roster', 'rosterContactsFetched', 'rosterInitialized']);
|
||||
|
||||
// API methods only available to plugins
|
||||
Object.assign(_converse.api, roster_api);
|
||||
|
@ -47,161 +40,18 @@ converse.plugins.add('converse-roster', {
|
|||
_converse.HEADER_UNGROUPED = __('Ungrouped');
|
||||
_converse.HEADER_UNREAD = __('New messages');
|
||||
|
||||
|
||||
_converse.registerPresenceHandler = function () {
|
||||
_converse.unregisterPresenceHandler();
|
||||
_converse.presence_ref = _converse.connection.addHandler(presence => {
|
||||
_converse.roster.presenceHandler(presence);
|
||||
return true;
|
||||
}, null, 'presence', null);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reject or cancel another user's subscription to our presence updates.
|
||||
* @method rejectPresenceSubscription
|
||||
* @private
|
||||
* @memberOf _converse
|
||||
* @param { String } jid - The Jabber ID of the user whose subscription is being canceled
|
||||
* @param { String } message - An optional message to the user
|
||||
*/
|
||||
_converse.rejectPresenceSubscription = function (jid, message) {
|
||||
const pres = $pres({to: jid, type: "unsubscribed"});
|
||||
if (message && message !== "") { pres.c("status").t(message); }
|
||||
api.send(pres);
|
||||
};
|
||||
|
||||
|
||||
_converse.sendInitialPresence = function () {
|
||||
if (_converse.send_initial_presence) {
|
||||
api.user.presence.send();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fetch all the roster groups, and then the roster contacts.
|
||||
* Emit an event after fetching is done in each case.
|
||||
* @private
|
||||
* @method _converse.populateRoster
|
||||
* @param { Bool } ignore_cache - If set to to true, the local cache
|
||||
* will be ignored it's guaranteed that the XMPP server
|
||||
* will be queried for the roster.
|
||||
*/
|
||||
_converse.populateRoster = async function (ignore_cache=false) {
|
||||
if (ignore_cache) {
|
||||
_converse.send_initial_presence = true;
|
||||
}
|
||||
try {
|
||||
await _converse.roster.fetchRosterContacts();
|
||||
api.trigger('rosterContactsFetched');
|
||||
} catch (reason) {
|
||||
log.error(reason);
|
||||
} finally {
|
||||
_converse.sendInitialPresence();
|
||||
}
|
||||
};
|
||||
|
||||
_converse.Presence = Presence;
|
||||
_converse.Presences = Presences;
|
||||
_converse.RosterContact = RosterContact;
|
||||
_converse.RosterContacts = RosterContacts;
|
||||
|
||||
_converse.unregisterPresenceHandler = function () {
|
||||
if (_converse.presence_ref !== undefined) {
|
||||
_converse.connection.deleteHandler(_converse.presence_ref);
|
||||
delete _converse.presence_ref;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************** Event Handlers ********************/
|
||||
api.listen.on('chatBoxesInitialized', () => {
|
||||
_converse.chatboxes.on('change:num_unread', updateUnreadCounter);
|
||||
|
||||
_converse.chatboxes.on('add', chatbox => {
|
||||
if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
chatbox.setRosterContact(chatbox.get('jid'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
api.listen.on('beforeTearDown', () => _converse.unregisterPresenceHandler());
|
||||
|
||||
api.waitUntil('rosterContactsFetched').then(() => {
|
||||
_converse.roster.on('add', (contact) => {
|
||||
/* When a new contact is added, check if we already have a
|
||||
* chatbox open for it, and if so attach it to the chatbox.
|
||||
*/
|
||||
const chatbox = _converse.chatboxes.findWhere({'jid': contact.get('jid')});
|
||||
if (chatbox) {
|
||||
chatbox.setRosterContact(contact.get('jid'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
api.listen.on('beforeTearDown', () => unregisterPresenceHandler());
|
||||
api.listen.on('chatBoxesInitialized', onChatBoxesInitialized);
|
||||
api.listen.on('clearSession', onClearSession);
|
||||
api.listen.on('presencesInitialized', onPresencesInitialized);
|
||||
api.listen.on('statusInitialized', onStatusInitialized);
|
||||
api.listen.on('streamResumptionFailed', () => _converse.session.set('roster_cached', false));
|
||||
|
||||
api.listen.on('clearSession', async () => {
|
||||
await clearPresences();
|
||||
if (_converse.shouldClearCache()) {
|
||||
if (_converse.rostergroups) {
|
||||
await _converse.rostergroups.clearStore();
|
||||
delete _converse.rostergroups;
|
||||
}
|
||||
if (_converse.roster) {
|
||||
invoke(_converse, 'roster.data.destroy');
|
||||
await _converse.roster.clearStore();
|
||||
delete _converse.roster;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
api.listen.on('statusInitialized', async reconnecting => {
|
||||
if (reconnecting) {
|
||||
// When reconnecting and not resuming a previous session,
|
||||
// we clear all cached presence data, since it might be stale
|
||||
// and we'll receive new presence updates
|
||||
!_converse.connection.hasResumed() && await clearPresences();
|
||||
} else {
|
||||
_converse.presences = new _converse.Presences();
|
||||
const id = `converse.presences-${_converse.bare_jid}`;
|
||||
initStorage(_converse.presences, id, 'session');
|
||||
// We might be continuing an existing session, so we fetch
|
||||
// cached presence data.
|
||||
_converse.presences.fetch();
|
||||
}
|
||||
/**
|
||||
* Triggered once the _converse.Presences collection has been
|
||||
* initialized and its cached data fetched.
|
||||
* Returns a boolean indicating whether this event has fired due to
|
||||
* Converse having reconnected.
|
||||
* @event _converse#presencesInitialized
|
||||
* @type { bool }
|
||||
* @example _converse.api.listen.on('presencesInitialized', reconnecting => { ... });
|
||||
*/
|
||||
api.trigger('presencesInitialized', reconnecting);
|
||||
});
|
||||
|
||||
|
||||
api.listen.on('presencesInitialized', async (reconnecting) => {
|
||||
if (reconnecting) {
|
||||
/**
|
||||
* Similar to `rosterInitialized`, but instead pertaining to reconnection.
|
||||
* This event indicates that the roster and its groups are now again
|
||||
* available after Converse.js has reconnected.
|
||||
* @event _converse#rosterReadyAfterReconnection
|
||||
* @example _converse.api.listen.on('rosterReadyAfterReconnection', () => { ... });
|
||||
*/
|
||||
api.trigger('rosterReadyAfterReconnection');
|
||||
} else {
|
||||
await initRoster();
|
||||
}
|
||||
_converse.roster.onConnected();
|
||||
_converse.registerPresenceHandler();
|
||||
_converse.populateRoster(!_converse.connection.restored);
|
||||
});
|
||||
api.waitUntil('rosterContactsFetched').then(onRosterContactsFetched);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
describe("A received presence stanza", function () {
|
||||
|
||||
it("has its priority taken into account",
|
||||
mock.initConverse([], {}, async (done, _converse) => {
|
||||
mock.initConverse([], {}, async (_converse) => {
|
||||
|
||||
const u = converse.env.utils;
|
||||
mock.openControlBox(_converse);
|
||||
|
@ -177,6 +177,5 @@ describe("A received presence stanza", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(_converse.roster.get(contact_jid).presence.get('show')).toBe('offline');
|
||||
expect(contact.presence.resources.length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { _converse, api } from "@converse/headless/core";
|
||||
import log from "@converse/headless/log";
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
|
||||
const { $pres } = converse.env;
|
||||
|
||||
export async function initRoster () {
|
||||
|
||||
async function initRoster () {
|
||||
// Initialize the Bakcbone collections that represent the contats
|
||||
// roster and the roster groups.
|
||||
await api.waitUntil('VCardsInitialized');
|
||||
|
@ -28,19 +31,167 @@ export async function initRoster () {
|
|||
}
|
||||
|
||||
|
||||
export function updateUnreadCounter (chatbox) {
|
||||
const contact = _converse.roster && _converse.roster.findWhere({'jid': chatbox.get('jid')});
|
||||
if (contact !== undefined) {
|
||||
contact.save({'num_unread': chatbox.get('num_unread')});
|
||||
/**
|
||||
* Fetch all the roster groups, and then the roster contacts.
|
||||
* Emit an event after fetching is done in each case.
|
||||
* @private
|
||||
* @param { Bool } ignore_cache - If set to to true, the local cache
|
||||
* will be ignored it's guaranteed that the XMPP server
|
||||
* will be queried for the roster.
|
||||
*/
|
||||
async function populateRoster (ignore_cache=false) {
|
||||
if (ignore_cache) {
|
||||
_converse.send_initial_presence = true;
|
||||
}
|
||||
try {
|
||||
await _converse.roster.fetchRosterContacts();
|
||||
api.trigger('rosterContactsFetched');
|
||||
} catch (reason) {
|
||||
log.error(reason);
|
||||
} finally {
|
||||
_converse.send_initial_presence && api.user.presence.send();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function clearPresences () {
|
||||
function updateUnreadCounter (chatbox) {
|
||||
const contact = _converse.roster?.findWhere({'jid': chatbox.get('jid')});
|
||||
contact?.save({'num_unread': chatbox.get('num_unread')});
|
||||
}
|
||||
|
||||
function registerPresenceHandler () {
|
||||
unregisterPresenceHandler();
|
||||
_converse.presence_ref = _converse.connection.addHandler(presence => {
|
||||
_converse.roster.presenceHandler(presence);
|
||||
return true;
|
||||
}, null, 'presence', null);
|
||||
}
|
||||
|
||||
export function unregisterPresenceHandler () {
|
||||
if (_converse.presence_ref !== undefined) {
|
||||
_converse.connection.deleteHandler(_converse.presence_ref);
|
||||
delete _converse.presence_ref;
|
||||
}
|
||||
}
|
||||
|
||||
async function clearPresences () {
|
||||
await _converse.presences?.clearStore();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Roster specific event handler for the clearSession event
|
||||
*/
|
||||
export async function onClearSession () {
|
||||
await clearPresences();
|
||||
if (_converse.shouldClearCache()) {
|
||||
if (_converse.rostergroups) {
|
||||
await _converse.rostergroups.clearStore();
|
||||
delete _converse.rostergroups;
|
||||
}
|
||||
if (_converse.roster) {
|
||||
_converse.roster.data?.destroy();
|
||||
await _converse.roster.clearStore();
|
||||
delete _converse.roster;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Roster specific event handler for the presencesInitialized event
|
||||
* @param { Boolean } reconnecting
|
||||
*/
|
||||
export async function onPresencesInitialized (reconnecting) {
|
||||
if (reconnecting) {
|
||||
/**
|
||||
* Similar to `rosterInitialized`, but instead pertaining to reconnection.
|
||||
* This event indicates that the roster and its groups are now again
|
||||
* available after Converse.js has reconnected.
|
||||
* @event _converse#rosterReadyAfterReconnection
|
||||
* @example _converse.api.listen.on('rosterReadyAfterReconnection', () => { ... });
|
||||
*/
|
||||
api.trigger('rosterReadyAfterReconnection');
|
||||
} else {
|
||||
await initRoster();
|
||||
}
|
||||
_converse.roster.onConnected();
|
||||
registerPresenceHandler();
|
||||
populateRoster(!_converse.connection.restored);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Roster specific event handler for the statusInitialized event
|
||||
* @param { Boolean } reconnecting
|
||||
*/
|
||||
export async function onStatusInitialized (reconnecting) {
|
||||
if (reconnecting) {
|
||||
// When reconnecting and not resuming a previous session,
|
||||
// we clear all cached presence data, since it might be stale
|
||||
// and we'll receive new presence updates
|
||||
!_converse.connection.hasResumed() && (await clearPresences());
|
||||
} else {
|
||||
_converse.presences = new _converse.Presences();
|
||||
const id = `converse.presences-${_converse.bare_jid}`;
|
||||
initStorage(_converse.presences, id, 'session');
|
||||
// We might be continuing an existing session, so we fetch
|
||||
// cached presence data.
|
||||
_converse.presences.fetch();
|
||||
}
|
||||
/**
|
||||
* Triggered once the _converse.Presences collection has been
|
||||
* initialized and its cached data fetched.
|
||||
* Returns a boolean indicating whether this event has fired due to
|
||||
* Converse having reconnected.
|
||||
* @event _converse#presencesInitialized
|
||||
* @type { bool }
|
||||
* @example _converse.api.listen.on('presencesInitialized', reconnecting => { ... });
|
||||
*/
|
||||
api.trigger('presencesInitialized', reconnecting);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Roster specific event handler for the chatBoxesInitialized event
|
||||
*/
|
||||
export function onChatBoxesInitialized () {
|
||||
_converse.chatboxes.on('change:num_unread', updateUnreadCounter);
|
||||
|
||||
_converse.chatboxes.on('add', chatbox => {
|
||||
if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
chatbox.setRosterContact(chatbox.get('jid'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Roster specific handler for the rosterContactsFetched promise
|
||||
*/
|
||||
export function onRosterContactsFetched () {
|
||||
_converse.roster.on('add', contact => {
|
||||
// When a new contact is added, check if we already have a
|
||||
// chatbox open for it, and if so attach it to the chatbox.
|
||||
const chatbox = _converse.chatboxes.findWhere({ 'jid': contact.get('jid') });
|
||||
chatbox?.setRosterContact(contact.get('jid'));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reject or cancel another user's subscription to our presence updates.
|
||||
* @function rejectPresenceSubscription
|
||||
* @param { String } jid - The Jabber ID of the user whose subscription is being canceled
|
||||
* @param { String } message - An optional message to the user
|
||||
*/
|
||||
export function rejectPresenceSubscription (jid, message) {
|
||||
const pres = $pres({to: jid, type: "unsubscribed"});
|
||||
if (message && message !== "") { pres.c("status").t(message); }
|
||||
api.send(pres);
|
||||
}
|
||||
|
||||
|
||||
export function contactsComparator (contact1, contact2) {
|
||||
const status1 = contact1.presence.get('show') || 'offline';
|
||||
const status2 = contact2.presence.get('show') || 'offline';
|
||||
|
|
|
@ -16,7 +16,7 @@ describe("XEP-0198 Stream Management", function () {
|
|||
'show_controlbox_by_default': true,
|
||||
'smacks_max_unacked_stanzas': 2
|
||||
},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await _converse.api.user.login('romeo@montague.lit/orchard', 'secret');
|
||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||
|
@ -119,7 +119,6 @@ describe("XEP-0198 Stream Management", function () {
|
|||
expect(Strophe.serialize(iq)).toBe(`<iq id="${iq.getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
|
||||
|
||||
expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
|
@ -131,7 +130,7 @@ describe("XEP-0198 Stream Management", function () {
|
|||
'show_controlbox_by_default': true,
|
||||
'smacks_max_unacked_stanzas': 2
|
||||
},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await _converse.api.user.login('romeo@montague.lit/orchard', 'secret');
|
||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||
|
@ -173,7 +172,6 @@ describe("XEP-0198 Stream Management", function () {
|
|||
// Check that the roster gets fetched
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await new Promise(resolve => _converse.api.listen.once('reconnected', resolve));
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
|
@ -187,7 +185,7 @@ describe("XEP-0198 Stream Management", function () {
|
|||
'show_controlbox_by_default': true,
|
||||
'smacks_max_unacked_stanzas': 2
|
||||
},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const key = "converse-test-session/converse.session-romeo@montague.lit-converse.session-romeo@montague.lit";
|
||||
sessionStorage.setItem(
|
||||
|
@ -266,6 +264,5 @@ describe("XEP-0198 Stream Management", function () {
|
|||
await u.waitUntil(() => muc.messages.length);
|
||||
expect(muc.messages.at(0).get('message')).toBe('First message')
|
||||
delete _converse.no_connection_on_bind;
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ const u = converse.env.utils;
|
|||
describe("The XMPPStatus model", function () {
|
||||
|
||||
it("won't send <show>online</show> when setting a custom status message",
|
||||
mock.initConverse(async (done, _converse) => {
|
||||
mock.initConverse(async (_converse) => {
|
||||
|
||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||
await _converse.api.user.status.set('online');
|
||||
|
@ -18,6 +18,5 @@ describe("The XMPPStatus model", function () {
|
|||
expect(stanza.querySelectorAll('show').length).toBe(0);
|
||||
expect(stanza.querySelectorAll('priority').length).toBe(1);
|
||||
expect(stanza.querySelector('priority').textContent).toBe('0');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
import URI from 'urijs';
|
||||
import dayjs from 'dayjs';
|
||||
import log from '@converse/headless/log';
|
||||
import sizzle from 'sizzle';
|
||||
import { Strophe } from 'strophe.js/src/strophe';
|
||||
import { _converse, api } from '@converse/headless/core';
|
||||
import { decodeHTMLEntities } from '@converse/headless/shared/utils';
|
||||
import { rejectMessage } from '@converse/headless/shared/actions';
|
||||
import {
|
||||
isAudioDomainAllowed,
|
||||
isAudioURL,
|
||||
isImageDomainAllowed,
|
||||
isImageURL,
|
||||
isVideoDomainAllowed,
|
||||
isVideoURL
|
||||
} from '@converse/headless/utils/url.js';
|
||||
|
||||
const { NS } = Strophe;
|
||||
|
||||
|
@ -48,13 +58,29 @@ export function getStanzaIDs (stanza, original_stanza) {
|
|||
}
|
||||
|
||||
export function getEncryptionAttributes (stanza, _converse) {
|
||||
const eme_tag = sizzle(`encryption[xmlns="${Strophe.NS.EME}"]`, stanza).pop();
|
||||
const namespace = eme_tag?.getAttribute('namespace');
|
||||
const attrs = {};
|
||||
|
||||
if (namespace) {
|
||||
attrs.is_encrypted = true;
|
||||
attrs.encryption_namespace = namespace;
|
||||
if (namespace !== Strophe.NS.OMEMO) {
|
||||
// Found an encrypted message, but it's not OMEMO
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
|
||||
const encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).pop();
|
||||
const attrs = { 'is_encrypted': !!encrypted };
|
||||
if (!eme_tag) {
|
||||
attrs.is_encrypted = !!encrypted;
|
||||
}
|
||||
|
||||
if (!encrypted || api.settings.get('clear_cache_on_logout')) {
|
||||
return attrs;
|
||||
}
|
||||
const header = encrypted.querySelector('header');
|
||||
attrs['encrypted'] = { 'device_id': header.getAttribute('sid') };
|
||||
attrs.encrypted = { 'device_id': header.getAttribute('sid') };
|
||||
|
||||
const device_id = _converse.omemo_store?.get('device_id');
|
||||
const key = device_id && sizzle(`key[rid="${device_id}"]`, encrypted).pop();
|
||||
|
@ -150,6 +176,34 @@ export function getOpenGraphMetadata (stanza) {
|
|||
return {};
|
||||
}
|
||||
|
||||
|
||||
export function getMediaURLs (text) {
|
||||
const objs = [];
|
||||
if (!text) {
|
||||
return {};
|
||||
}
|
||||
const parse_options = { 'start': /\b(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi };
|
||||
try {
|
||||
URI.withinString(
|
||||
text,
|
||||
(url, start, end) => {
|
||||
objs.push({ url, start, end });
|
||||
return url;
|
||||
},
|
||||
parse_options
|
||||
);
|
||||
} catch (error) {
|
||||
log.debug(error);
|
||||
}
|
||||
const media_urls = objs.filter(o => {
|
||||
return (isImageURL(o.url) && isImageDomainAllowed(o.url)) ||
|
||||
(isVideoURL(o.url) && isVideoDomainAllowed(o.url)) ||
|
||||
(isAudioURL(o.url) && isAudioDomainAllowed(o.url));
|
||||
}).map(o => ({ 'start': o.start, 'end': o.end }));
|
||||
return media_urls.length ? { media_urls } : {};
|
||||
}
|
||||
|
||||
|
||||
export function getSpoilerAttributes (stanza) {
|
||||
const spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, stanza).pop();
|
||||
return {
|
||||
|
|
|
@ -13,19 +13,23 @@ export function getDefaultStore () {
|
|||
}
|
||||
}
|
||||
|
||||
function storeUsesIndexedDB (store) {
|
||||
return store === 'persistent' && api.settings.get('persistent_store') === 'IndexedDB';
|
||||
}
|
||||
|
||||
export function createStore (id, store) {
|
||||
const name = store || getDefaultStore();
|
||||
const s = _converse.storage[name];
|
||||
if (typeof s === 'undefined') {
|
||||
throw new TypeError(`createStore: Could not find store for ${id}`);
|
||||
}
|
||||
return new Storage(id, s, api.settings.get('persistent_store') === 'IndexedDB');
|
||||
return new Storage(id, s, storeUsesIndexedDB(store));
|
||||
}
|
||||
|
||||
export function initStorage (model, id, type) {
|
||||
const store = type || getDefaultStore();
|
||||
model.browserStorage = _converse.createStore(id, store);
|
||||
if (store === 'persistent' && api.settings.get('persistent_store') === 'IndexedDB') {
|
||||
if (storeUsesIndexedDB(store)) {
|
||||
const flush = () => model.browserStorage.flush();
|
||||
window.addEventListener(_converse.unloadevent, flush);
|
||||
model.on('destroy', () => window.removeEventListener(_converse.unloadevent, flush));
|
||||
|
|
|
@ -6,7 +6,7 @@ describe("Converse", function() {
|
|||
|
||||
describe("Authentication", function () {
|
||||
|
||||
it("needs either a bosh_service_url a websocket_url or both", mock.initConverse(async (done, _converse) => {
|
||||
it("needs either a bosh_service_url a websocket_url or both", mock.initConverse(async (_converse) => {
|
||||
const url = _converse.bosh_service_url;
|
||||
const connection = _converse.connection;
|
||||
_converse.api.settings.set('bosh_service_url', undefined);
|
||||
|
@ -17,7 +17,6 @@ describe("Converse", function() {
|
|||
_converse.api.settings.set('bosh_service_url', url);
|
||||
_converse.connection = connection;
|
||||
expect(e.message).toBe("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
|
||||
done();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
@ -25,7 +24,7 @@ describe("Converse", function() {
|
|||
describe("A chat state indication", function () {
|
||||
|
||||
it("are sent out when the client becomes or stops being idle",
|
||||
mock.initConverse(['discoInitialized'], {}, (done, _converse) => {
|
||||
mock.initConverse(['discoInitialized'], {}, (_converse) => {
|
||||
|
||||
spyOn(_converse, 'sendCSI').and.callThrough();
|
||||
let sent_stanza;
|
||||
|
@ -47,14 +46,13 @@ describe("Converse", function() {
|
|||
_converse.onUserActivity();
|
||||
expect(_converse.sendCSI).toHaveBeenCalledWith('active');
|
||||
expect(Strophe.serialize(sent_stanza)).toBe('<active xmlns="urn:xmpp:csi:0"/>');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("Automatic status change", function () {
|
||||
|
||||
it("happens when the client is idle for long enough",
|
||||
mock.initConverse(['initialized'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['initialized'], {}, async (_converse) => {
|
||||
let i = 0;
|
||||
// Usually initialized by registerIntervalHandler
|
||||
_converse.idle_seconds = 0;
|
||||
|
@ -120,7 +118,6 @@ describe("Converse", function() {
|
|||
_converse.onUserActivity();
|
||||
expect(await _converse.api.user.status.get()).toBe('dnd');
|
||||
expect(_converse.auto_changed_status).toBe(false);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -129,15 +126,14 @@ describe("Converse", function() {
|
|||
describe("The \"status\" API", function () {
|
||||
|
||||
it("has a method for getting the user's availability",
|
||||
mock.initConverse(['statusInitialized'], {}, async(done, _converse) => {
|
||||
mock.initConverse(['statusInitialized'], {}, async(_converse) => {
|
||||
_converse.xmppstatus.set('status', 'online');
|
||||
expect(await _converse.api.user.status.get()).toBe('online');
|
||||
_converse.xmppstatus.set('status', 'dnd');
|
||||
expect(await _converse.api.user.status.get()).toBe('dnd');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a method for setting the user's availability", mock.initConverse(async (done, _converse) => {
|
||||
it("has a method for setting the user's availability", mock.initConverse(async (_converse) => {
|
||||
await _converse.api.user.status.set('away');
|
||||
expect(await _converse.xmppstatus.get('status')).toBe('away');
|
||||
await _converse.api.user.status.set('dnd');
|
||||
|
@ -149,39 +145,35 @@ describe("Converse", function() {
|
|||
const promise = _converse.api.user.status.set('invalid')
|
||||
promise.catch(e => {
|
||||
expect(e.message).toBe('Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1');
|
||||
done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("allows setting the status message as well", mock.initConverse(async (done, _converse) => {
|
||||
it("allows setting the status message as well", mock.initConverse(async (_converse) => {
|
||||
await _converse.api.user.status.set('away', "I'm in a meeting");
|
||||
expect(_converse.xmppstatus.get('status')).toBe('away');
|
||||
expect(_converse.xmppstatus.get('status_message')).toBe("I'm in a meeting");
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a method for getting the user's status message",
|
||||
mock.initConverse(['statusInitialized'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['statusInitialized'], {}, async (_converse) => {
|
||||
await _converse.xmppstatus.set('status_message', undefined);
|
||||
expect(await _converse.api.user.status.message.get()).toBe(undefined);
|
||||
await _converse.xmppstatus.set('status_message', "I'm in a meeting");
|
||||
expect(await _converse.api.user.status.message.get()).toBe("I'm in a meeting");
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a method for setting the user's status message",
|
||||
mock.initConverse(['statusInitialized'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['statusInitialized'], {}, async (_converse) => {
|
||||
_converse.xmppstatus.set('status_message', undefined);
|
||||
await _converse.api.user.status.message.set("I'm in a meeting");
|
||||
expect(_converse.xmppstatus.get('status_message')).toBe("I'm in a meeting");
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("The \"tokens\" API", function () {
|
||||
|
||||
it("has a method for retrieving the next RID", mock.initConverse((done, _converse) => {
|
||||
it("has a method for retrieving the next RID", mock.initConverse((_converse) => {
|
||||
mock.createContacts(_converse, 'current');
|
||||
const old_connection = _converse.connection;
|
||||
_converse.connection._proto.rid = '1234';
|
||||
|
@ -190,10 +182,9 @@ describe("Converse", function() {
|
|||
expect(_converse.api.tokens.get('rid')).toBe(null);
|
||||
// Restore the connection
|
||||
_converse.connection = old_connection;
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a method for retrieving the SID", mock.initConverse((done, _converse) => {
|
||||
it("has a method for retrieving the SID", mock.initConverse((_converse) => {
|
||||
mock.createContacts(_converse, 'current');
|
||||
const old_connection = _converse.connection;
|
||||
_converse.connection._proto.sid = '1234';
|
||||
|
@ -202,14 +193,13 @@ describe("Converse", function() {
|
|||
expect(_converse.api.tokens.get('sid')).toBe(null);
|
||||
// Restore the connection
|
||||
_converse.connection = old_connection;
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("The \"contacts\" API", function () {
|
||||
|
||||
it("has a method 'get' which returns wrapped contacts",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
let contact = await _converse.api.contacts.get('non-existing@jabber.org');
|
||||
|
@ -228,11 +218,10 @@ describe("Converse", function() {
|
|||
// Check that all JIDs are returned if you call without any parameters
|
||||
list = await _converse.api.contacts.get();
|
||||
expect(list.length).toBe(mock.cur_names.length);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("has a method 'add' with which contacts can be added",
|
||||
mock.initConverse(['rosterInitialized'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['rosterInitialized'], {}, async (_converse) => {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
try {
|
||||
|
@ -251,13 +240,12 @@ describe("Converse", function() {
|
|||
spyOn(_converse.roster, 'addAndSubscribe');
|
||||
await _converse.api.contacts.add("newcontact@example.org");
|
||||
expect(_converse.roster.addAndSubscribe).toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("The \"settings\" API", function() {
|
||||
it("has methods 'get' and 'set' to set configuration settings",
|
||||
mock.initConverse(null, {'play_sounds': true}, (done, _converse) => {
|
||||
mock.initConverse(null, {'play_sounds': true}, (_converse) => {
|
||||
|
||||
expect(Object.keys(_converse.api.settings)).toEqual(["extend", "update", "get", "set"]);
|
||||
expect(_converse.api.settings.get("play_sounds")).toBe(true);
|
||||
|
@ -269,11 +257,10 @@ describe("Converse", function() {
|
|||
expect(typeof _converse.api.settings.get("non_existing")).toBe("undefined");
|
||||
_converse.api.settings.set("non_existing", true);
|
||||
expect(typeof _converse.api.settings.get("non_existing")).toBe("undefined");
|
||||
done();
|
||||
}));
|
||||
|
||||
it("extended via settings.extend don't override settings passed in via converse.initialize",
|
||||
mock.initConverse([], {'emoji_categories': {"travel": ":rocket:"}}, (done, _converse) => {
|
||||
mock.initConverse([], {'emoji_categories': {"travel": ":rocket:"}}, (_converse) => {
|
||||
|
||||
expect(_converse.api.settings.get('emoji_categories')?.travel).toBe(':rocket:');
|
||||
|
||||
|
@ -283,7 +270,6 @@ describe("Converse", function() {
|
|||
|
||||
expect(_converse.api.settings.get('emoji_categories')?.travel).toBe(':rocket:');
|
||||
expect(_converse.api.settings.get('emoji_categories')?.food).toBe(undefined);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("only overrides the passed in properties",
|
||||
|
@ -292,7 +278,7 @@ describe("Converse", function() {
|
|||
'root': document.createElement('div').attachShadow({ 'mode': 'open' }),
|
||||
'emoji_categories': { 'travel': ':rocket:' },
|
||||
},
|
||||
(done, _converse) => {
|
||||
(_converse) => {
|
||||
expect(_converse.api.settings.get('emoji_categories')?.travel).toBe(':rocket:');
|
||||
|
||||
// Test that the extend command doesn't override user-provided site
|
||||
|
@ -303,7 +289,6 @@ describe("Converse", function() {
|
|||
|
||||
expect(_converse.api.settings.get('emoji_categories').travel).toBe(':rocket:');
|
||||
expect(_converse.api.settings.get('emoji_categories').food).toBe(undefined);
|
||||
done();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -311,7 +296,7 @@ describe("Converse", function() {
|
|||
});
|
||||
|
||||
describe("The \"plugins\" API", function() {
|
||||
it("only has a method 'add' for registering plugins", mock.initConverse((done, _converse) => {
|
||||
it("only has a method 'add' for registering plugins", mock.initConverse((_converse) => {
|
||||
expect(Object.keys(converse.plugins)).toEqual(["add"]);
|
||||
// Cheating a little bit. We clear the plugins to test more easily.
|
||||
const _old_plugins = _converse.pluggable.plugins;
|
||||
|
@ -321,17 +306,15 @@ describe("Converse", function() {
|
|||
converse.plugins.add('plugin2', {});
|
||||
expect(Object.keys(_converse.pluggable.plugins)).toEqual(['plugin1', 'plugin2']);
|
||||
_converse.pluggable.plugins = _old_plugins;
|
||||
done();
|
||||
}));
|
||||
|
||||
describe("The \"plugins.add\" method", function() {
|
||||
it("throws an error when multiple plugins attempt to register with the same name",
|
||||
mock.initConverse((done, _converse) => { // eslint-disable-line no-unused-vars
|
||||
mock.initConverse((_converse) => { // eslint-disable-line no-unused-vars
|
||||
|
||||
converse.plugins.add('myplugin', {});
|
||||
const error = new TypeError('Error: plugin with name "myplugin" has already been registered!');
|
||||
expect(() => converse.plugins.add('myplugin', {})).toThrow(error);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
describe("The _converse Event Emitter", function() {
|
||||
|
||||
it("allows you to subscribe to emitted events", mock.initConverse((done, _converse) => {
|
||||
it("allows you to subscribe to emitted events", mock.initConverse((_converse) => {
|
||||
this.callback = function () {};
|
||||
spyOn(this, 'callback');
|
||||
_converse.on('connected', this.callback);
|
||||
|
@ -12,10 +12,9 @@ describe("The _converse Event Emitter", function() {
|
|||
expect(this.callback.calls.count(), 2);
|
||||
_converse.api.trigger('connected');
|
||||
expect(this.callback.calls.count(), 3);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("allows you to listen once for an emitted event", mock.initConverse((done, _converse) => {
|
||||
it("allows you to listen once for an emitted event", mock.initConverse((_converse) => {
|
||||
this.callback = function () {};
|
||||
spyOn(this, 'callback');
|
||||
_converse.once('connected', this.callback);
|
||||
|
@ -25,10 +24,9 @@ describe("The _converse Event Emitter", function() {
|
|||
expect(this.callback.calls.count(), 1);
|
||||
_converse.api.trigger('connected');
|
||||
expect(this.callback.calls.count(), 1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("allows you to stop listening or subscribing to an event", mock.initConverse((done, _converse) => {
|
||||
it("allows you to stop listening or subscribing to an event", mock.initConverse((_converse) => {
|
||||
this.callback = function () {};
|
||||
this.anotherCallback = function () {};
|
||||
this.neverCalled = function () {};
|
||||
|
@ -56,6 +54,5 @@ describe("The _converse Event Emitter", function() {
|
|||
expect(this.callback.calls.count(), 1);
|
||||
expect(this.anotherCallback.calls.count(), 3);
|
||||
expect(this.neverCalled).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
describe("The persistent store", function() {
|
||||
|
||||
it("is unique to the user based on their JID",
|
||||
mock.initConverse(['discoInitialized'], {'persistent_store': 'IndexedDB'}, (done, _converse) => {
|
||||
mock.initConverse([], {'persistent_store': 'IndexedDB'}, (_converse) => {
|
||||
|
||||
expect(_converse.storage.persistent.config().storeName).toBe(_converse.bare_jid);
|
||||
expect(_converse.storage.persistent.config().description).toBe('indexedDB instance');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import { converse } from '@converse/headless/core.js';
|
||||
|
||||
const { u } = converse.env;
|
||||
|
||||
|
||||
export function appendArrayBuffer (buffer1, buffer2) {
|
||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
||||
tmp.set(new Uint8Array(buffer1), 0);
|
||||
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
|
||||
return tmp.buffer;
|
||||
}
|
||||
|
||||
export function arrayBufferToHex (ab) {
|
||||
// https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex#40031979
|
||||
return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
|
||||
export function arrayBufferToString (ab) {
|
||||
return new TextDecoder("utf-8").decode(ab);
|
||||
}
|
||||
|
||||
export function stringToArrayBuffer (string) {
|
||||
const bytes = new TextEncoder("utf-8").encode(string);
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
export function arrayBufferToBase64 (ab) {
|
||||
return btoa((new Uint8Array(ab)).reduce((data, byte) => data + String.fromCharCode(byte), ''));
|
||||
}
|
||||
|
||||
export function base64ToArrayBuffer (b64) {
|
||||
const binary_string = window.atob(b64),
|
||||
len = binary_string.length,
|
||||
bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i)
|
||||
}
|
||||
return bytes.buffer
|
||||
}
|
||||
|
||||
export function hexToArrayBuffer (hex) {
|
||||
const typedArray = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16)))
|
||||
return typedArray.buffer
|
||||
}
|
||||
|
||||
|
||||
Object.assign(u, { arrayBufferToHex, arrayBufferToString, stringToArrayBuffer, arrayBufferToBase64, base64ToArrayBuffer });
|
|
@ -439,42 +439,6 @@ u.formatFingerprint = function (fp) {
|
|||
return fp;
|
||||
};
|
||||
|
||||
u.appendArrayBuffer = function (buffer1, buffer2) {
|
||||
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
||||
tmp.set(new Uint8Array(buffer1), 0);
|
||||
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
|
||||
return tmp.buffer;
|
||||
};
|
||||
|
||||
u.arrayBufferToHex = function (ab) {
|
||||
// https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex#40031979
|
||||
return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');
|
||||
};
|
||||
|
||||
u.arrayBufferToString = function (ab) {
|
||||
return new TextDecoder("utf-8").decode(ab);
|
||||
};
|
||||
|
||||
u.stringToArrayBuffer = function (string) {
|
||||
const bytes = new TextEncoder("utf-8").encode(string);
|
||||
return bytes.buffer;
|
||||
};
|
||||
|
||||
u.arrayBufferToBase64 = function (ab) {
|
||||
return btoa((new Uint8Array(ab)).reduce((data, byte) => data + String.fromCharCode(byte), ''));
|
||||
};
|
||||
|
||||
u.base64ToArrayBuffer = function (b64) {
|
||||
const binary_string = window.atob(b64),
|
||||
len = binary_string.length,
|
||||
bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i)
|
||||
}
|
||||
return bytes.buffer
|
||||
};
|
||||
|
||||
u.getRandomInt = function (max) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
};
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import URI from 'urijs';
|
||||
import log from '@converse/headless/log';
|
||||
import { api } from '@converse/headless/core';
|
||||
|
||||
function checkTLS (uri) {
|
||||
return (
|
||||
window.location.protocol === 'http:' ||
|
||||
(window.location.protocol === 'https:' && uri.protocol().toLowerCase() === 'https')
|
||||
);
|
||||
}
|
||||
|
||||
export function getURI (url) {
|
||||
try {
|
||||
return url instanceof URI ? url : new URI(url);
|
||||
} catch (error) {
|
||||
log.debug(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function checkFileTypes (types, url) {
|
||||
const uri = getURI(url);
|
||||
if (uri === null || !checkTLS(uri)) {
|
||||
return false;
|
||||
}
|
||||
const filename = uri.filename().toLowerCase();
|
||||
return !!types.filter(ext => filename.endsWith(ext)).length;
|
||||
}
|
||||
|
||||
function isDomainAllowed (whitelist, url) {
|
||||
const uri = getURI(url);
|
||||
const subdomain = uri.subdomain();
|
||||
const domain = uri.domain();
|
||||
const fulldomain = `${subdomain ? `${subdomain}.` : ''}${domain}`;
|
||||
return whitelist.includes(domain) || whitelist.includes(fulldomain);
|
||||
}
|
||||
|
||||
export function filterQueryParamsFromURL (url) {
|
||||
const paramsArray = api.settings.get('filter_url_query_params');
|
||||
if (!paramsArray) return url;
|
||||
const parsed_uri = getURI(url);
|
||||
return parsed_uri.removeQuery(paramsArray).toString();
|
||||
}
|
||||
|
||||
export function isAudioDomainAllowed (url) {
|
||||
const embed_audio = api.settings.get('embed_audio');
|
||||
if (!Array.isArray(embed_audio)) {
|
||||
return embed_audio;
|
||||
}
|
||||
try {
|
||||
return isDomainAllowed(embed_audio, url);
|
||||
} catch (error) {
|
||||
log.debug(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isVideoDomainAllowed (url) {
|
||||
const embed_videos = api.settings.get('embed_videos');
|
||||
if (!Array.isArray(embed_videos)) {
|
||||
return embed_videos;
|
||||
}
|
||||
try {
|
||||
return isDomainAllowed(embed_videos, url);
|
||||
} catch (error) {
|
||||
log.debug(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isImageDomainAllowed (url) {
|
||||
const show_images_inline = api.settings.get('show_images_inline');
|
||||
if (!Array.isArray(show_images_inline)) {
|
||||
return show_images_inline;
|
||||
}
|
||||
try {
|
||||
return isDomainAllowed(show_images_inline, url);
|
||||
} catch (error) {
|
||||
log.debug(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isURLWithImageExtension (url) {
|
||||
return checkFileTypes(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg'], url);
|
||||
}
|
||||
|
||||
export function isAudioURL (url) {
|
||||
return checkFileTypes(['.ogg', '.mp3', '.m4a'], url);
|
||||
}
|
||||
|
||||
export function isVideoURL (url) {
|
||||
return checkFileTypes(['.mp4', '.webm'], url);
|
||||
}
|
||||
|
||||
export function isImageURL (url) {
|
||||
const regex = api.settings.get('image_urls_regex');
|
||||
return regex?.test(url) || isURLWithImageExtension(url);
|
||||
}
|
||||
|
||||
export function isEncryptedFileURL (url) {
|
||||
return url.startsWith('aesgcm://');
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -7,8 +7,8 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 0.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2021-01-20 09:32+0000\n"
|
||||
"Last-Translator: Juanro49 <juanrobertogarciasanchez@gmail.com>\n"
|
||||
"PO-Revision-Date: 2021-05-23 22:31+0000\n"
|
||||
"Last-Translator: juliojulian <majuga@publicar.uy>\n"
|
||||
"Language-Team: Spanish <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/es/>\n"
|
||||
"Language: es\n"
|
||||
|
@ -16,7 +16,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.5-dev\n"
|
||||
"X-Generator: Weblate 4.7-dev\n"
|
||||
"plural_forms: nplurals=2; plural=(n != 1);\n"
|
||||
"lang: es\n"
|
||||
"Language-Code: es\n"
|
||||
|
@ -77,9 +77,8 @@ msgstr "%1$s se ha marchado"
|
|||
|
||||
#: dist/converse-no-dependencies.js:51675
|
||||
#: dist/converse-no-dependencies.js:58125
|
||||
#, fuzzy
|
||||
msgid "You're not allowed to retract your message."
|
||||
msgstr "Lo sentimos, no tienes permisos para retractarte de este mensaje."
|
||||
msgstr "No tienes permisos para editar este mensaje."
|
||||
|
||||
#: dist/converse-no-dependencies.js:51677
|
||||
#: dist/converse-no-dependencies.js:58129
|
||||
|
|
|
@ -7,8 +7,8 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 0.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2021-01-26 23:32+0000\n"
|
||||
"Last-Translator: Vincent Finance <linuxmario@linuxmario.net>\n"
|
||||
"PO-Revision-Date: 2021-05-20 18:33+0000\n"
|
||||
"Last-Translator: lilim <lionel@les-miquelots.net>\n"
|
||||
"Language-Team: French <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/fr/>\n"
|
||||
"Language: fr\n"
|
||||
|
@ -16,7 +16,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 4.5-dev\n"
|
||||
"X-Generator: Weblate 4.7-dev\n"
|
||||
"plural_forms: nplurals=2; plural=(n != 1);\n"
|
||||
"lang: fr\n"
|
||||
"Language-Code: fr\n"
|
||||
|
@ -762,7 +762,6 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:76031
|
||||
#: dist/converse-no-dependencies.js:76122
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Be aware that other XMPP/Jabber clients (and servers) may not yet support "
|
||||
"retractions and that this message may not be removed everywhere."
|
||||
|
@ -774,7 +773,6 @@ msgstr ""
|
|||
#: dist/converse-no-dependencies.js:76032
|
||||
#: dist/converse-no-dependencies.js:76129
|
||||
#: dist/converse-no-dependencies.js:76166
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to retract this message?"
|
||||
msgstr "Voulez-vous vraiment retirer ce message ?"
|
||||
|
||||
|
@ -797,12 +795,10 @@ msgid "Sorry, you're not allowed to retract this message."
|
|||
msgstr "Désolé, vous n'êtes pas autorisé a retirer ce message."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid "You are about to retract this message."
|
||||
msgstr "Vous êtes sur le point de retirer ce message."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"You may optionally include a message, explaining the reason for the "
|
||||
"retraction."
|
||||
|
@ -811,7 +807,6 @@ msgstr ""
|
|||
"du retrait."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
#, fuzzy
|
||||
msgid "Message Retraction"
|
||||
msgstr "Versions du message"
|
||||
|
||||
|
@ -820,9 +815,8 @@ msgid "Optional reason"
|
|||
msgstr "Raison facultative"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76208
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to retract this message"
|
||||
msgstr "Désolé, vous n'êtes pas autorisé a retirer ce message."
|
||||
msgstr "Désolé, vous n'êtes pas autorisé a retirer ce message"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76270
|
||||
msgid "Cancel Editing"
|
||||
|
@ -838,19 +832,19 @@ msgstr "Retirer"
|
|||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Show URL previews"
|
||||
msgstr ""
|
||||
msgstr "Montrer les prévisualisations d'URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Hide URL previews"
|
||||
msgstr ""
|
||||
msgstr "Cacher les prévisualisations d'URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Show URL preview"
|
||||
msgstr ""
|
||||
msgstr "Montrer la prévisualisation de l'URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Hide URL preview"
|
||||
msgstr ""
|
||||
msgstr "Cacher la prévisualisation de l'URL"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:76429
|
||||
|
@ -1248,7 +1242,7 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:94078
|
||||
msgid "Loading configuration form"
|
||||
msgstr ""
|
||||
msgstr "Chargement du formulaire de configuration"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
msgid "Sorry, an error occurred while trying to submit the config form."
|
||||
|
@ -1506,7 +1500,6 @@ msgid "Allow muted user to post messages"
|
|||
msgstr "Autoriser les utilisateurs muets à poster des messages"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96345
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The conversation has moved to a new address. Click the link below to enter."
|
||||
msgstr ""
|
||||
|
@ -1518,9 +1511,9 @@ msgid "This groupchat no longer exists"
|
|||
msgstr "Ce salon n’existe plus"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96355
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "The following reason was given: \"%1$s\""
|
||||
msgstr "La raison indiquée est : « %1$s »."
|
||||
msgstr "La raison indiquée est : « %1$s »"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96851
|
||||
#, javascript-format
|
||||
|
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 4.0.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 10:11+0100\n"
|
||||
"PO-Revision-Date: 2021-04-09 13:31+0200\n"
|
||||
"PO-Revision-Date: 2021-04-25 05:32+0000\n"
|
||||
"Last-Translator: Xosé M <xosem@disroot.org>\n"
|
||||
"Language-Team: Galician <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/gl/>\n"
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.5.2-dev\n"
|
||||
"X-Generator: Weblate 4.7-dev\n"
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:42989
|
||||
|
@ -778,7 +778,7 @@ msgstr "Razón (optativo)"
|
|||
|
||||
#: dist/converse-no-dependencies.js:76184
|
||||
msgid "Sorry, you're not allowed to retract this message"
|
||||
msgstr "Desculpa, pero non tes permiso para eliminar esta mensaxe."
|
||||
msgstr "Desculpa, pero non tes permiso para eliminar esta mensaxe"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76270
|
||||
msgid "Cancel Editing"
|
||||
|
@ -1461,7 +1461,7 @@ msgstr "Esta conversa en grupo xa non existe"
|
|||
#: dist/converse-no-dependencies.js:96194
|
||||
#, javascript-format
|
||||
msgid "The following reason was given: \"%1$s\""
|
||||
msgstr "A razón do cambio é: \"%1$s\"."
|
||||
msgstr "A razón do cambio é: \"%1$s\""
|
||||
|
||||
#: dist/converse-no-dependencies.js:96851
|
||||
#, javascript-format
|
||||
|
|
|
@ -9,8 +9,8 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 0.9.6\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2020-11-18 18:41+0100\n"
|
||||
"Last-Translator: Michal Biesiada <blade-14@o2.pl>\n"
|
||||
"PO-Revision-Date: 2021-05-20 18:33+0000\n"
|
||||
"Last-Translator: G <emgrzegorz@interia.pl>\n"
|
||||
"Language-Team: Polish <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/pl/>\n"
|
||||
"Language: pl\n"
|
||||
|
@ -19,7 +19,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.4-dev\n"
|
||||
"X-Generator: Weblate 4.7-dev\n"
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:42989
|
||||
|
@ -32,9 +32,8 @@ msgid "An error occurred while connecting to the chat server."
|
|||
msgstr "Wystąpił błąd w czasie łączenia się z serwerem."
|
||||
|
||||
#: dist/converse-no-dependencies.js:43246
|
||||
#, fuzzy
|
||||
msgid "Your XMPP address and/or password is incorrect. Please try again."
|
||||
msgstr "Twój Jabber ID i/lub hasło jest nieprawidłowe. Spróbuj ponownie."
|
||||
msgstr "Twój XMPP adres i/lub hasło jest nieprawidłowe. Spróbuj ponownie."
|
||||
|
||||
#: dist/converse-no-dependencies.js:43258
|
||||
#, javascript-format
|
||||
|
@ -69,26 +68,22 @@ msgstr "%1$s odszedł od klawiatury"
|
|||
|
||||
#: dist/converse-no-dependencies.js:51675
|
||||
#: dist/converse-no-dependencies.js:58125
|
||||
#, fuzzy
|
||||
msgid "You're not allowed to retract your message."
|
||||
msgstr "Przepraszam, nie wolno ci wycofać tej wiadomości."
|
||||
msgstr "Nie wolno tobie wycofać tej wiadomości."
|
||||
|
||||
#: dist/converse-no-dependencies.js:51677
|
||||
#: dist/converse-no-dependencies.js:58129
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error occurred while trying to retract your message."
|
||||
msgstr "Przepraszam, coś poszło nie tak podczas próby cofnięcia wiadomości."
|
||||
msgstr "Przepraszam, pojawił się błąd podczas próby cofnięcia wiadomości."
|
||||
|
||||
#: dist/converse-no-dependencies.js:51682
|
||||
#, fuzzy
|
||||
msgid "You're not allowed to send a message."
|
||||
msgstr "Nie możesz wysyłać wiadomości w tym pokoju"
|
||||
msgstr "Nie możesz wysyłać wiadomości"
|
||||
|
||||
#: dist/converse-no-dependencies.js:51684
|
||||
#: dist/converse-no-dependencies.js:58138
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error occurred while trying to send your message."
|
||||
msgstr "Wystąpił błąd w czasie próby usunięcia urządzenia."
|
||||
msgstr "Przepraszam, wystąpił błąd podczas próby wysłania twojej wiadomości."
|
||||
|
||||
#: dist/converse-no-dependencies.js:51955
|
||||
#, javascript-format
|
||||
|
@ -122,9 +117,8 @@ msgstr ""
|
|||
"serwer, który wynosi %2$s."
|
||||
|
||||
#: dist/converse-no-dependencies.js:53208
|
||||
#, fuzzy
|
||||
msgid "Undecryptable OMEMO message"
|
||||
msgstr "Nieszyfrowalny komunikat OMEMO"
|
||||
msgstr "Nierozszyfrowalna wiadomość OMEMO"
|
||||
|
||||
#: dist/converse-no-dependencies.js:53272
|
||||
msgid "Sorry, could not determine upload URL."
|
||||
|
@ -213,15 +207,13 @@ msgid "A timeout happened while while trying to retract your message."
|
|||
msgstr "Podczas próby cofnięcia komunikatu pojawił się limit czasu"
|
||||
|
||||
#: dist/converse-no-dependencies.js:59253
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error happened while running the command."
|
||||
msgstr "Wystąpił błąd w trakcie próby zapisania Twoich danych profilowych."
|
||||
msgstr "Przepraszam, ale wystąpił błąd w trakcie uruchomienia polecenia."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59253
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
#, fuzzy
|
||||
msgid "Check your browser's developer console for details."
|
||||
msgstr "Możesz sprawdzić konsolę dewelopera przeglądarki pod kątem błędów."
|
||||
msgstr "Sprawdzić konsolę dewelopera przeglądarki po szczegóły."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59278
|
||||
#: dist/converse-no-dependencies.js:59301
|
||||
|
@ -340,19 +332,19 @@ msgid "%1$s have stopped typing"
|
|||
msgstr "%1$s przestał pisać"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60543
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have gone away"
|
||||
msgstr "%1$s odszedł od klawiatury"
|
||||
msgstr "%1$s oddalił się"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60545
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have entered the groupchat"
|
||||
msgstr "%1$s wszedł do pokoju"
|
||||
msgstr "%1$s wszedł na czat groupowy"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60547
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have left the groupchat"
|
||||
msgstr "%1$s opuścił pokój"
|
||||
msgstr "%1$s opuścił czat groupowy"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60549
|
||||
#, fuzzy, javascript-format
|
||||
|
@ -375,9 +367,9 @@ msgid "%1$s have been muted"
|
|||
msgstr "%1$s został wyciszony"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60859
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s has been banned by %2$s"
|
||||
msgstr "%1$s został zbanowany"
|
||||
msgstr "%1$s został zbanowany przez %2$s"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60859
|
||||
#, javascript-format
|
||||
|
@ -390,9 +382,9 @@ msgid "%1$s's nickname has changed"
|
|||
msgstr "%1$s zmienił pseudonim"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60863
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s has been kicked out by %2$s"
|
||||
msgstr "%1$s został wyrzucony"
|
||||
msgstr "%1$s został wykopany przez %2$s"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60863
|
||||
#, javascript-format
|
||||
|
@ -539,10 +531,8 @@ msgid "You have been banned from this groupchat"
|
|||
msgstr "Zostałeś zablokowany na tym czacie grupowym"
|
||||
|
||||
#: dist/converse-no-dependencies.js:62490
|
||||
#, fuzzy
|
||||
msgid "You have exited this groupchat due to a technical problem"
|
||||
msgstr ""
|
||||
"Zostałeś usunięty z tego czatu grupowego z powodu zmiany przynależności"
|
||||
msgstr "Wyszedłeś z tego czatu grupowego z powodu problemu technicznego"
|
||||
|
||||
#: dist/converse-no-dependencies.js:62491
|
||||
msgid "You have been kicked from this groupchat"
|
||||
|
@ -616,9 +606,8 @@ msgstr ""
|
|||
"aby poprosić o nie ponownie."
|
||||
|
||||
#: dist/converse-no-dependencies.js:65180
|
||||
#, fuzzy
|
||||
msgid "Timeout while trying to fetch archived messages."
|
||||
msgstr "Podczas próby cofnięcia komunikatu pojawił się limit czasu"
|
||||
msgstr "Limit czasu podczas próby pobrania archiwalnych wiadomości."
|
||||
|
||||
#: dist/converse-no-dependencies.js:65195
|
||||
#, fuzzy
|
||||
|
@ -742,9 +731,9 @@ msgid "Download audio file \"%1$s\""
|
|||
msgstr "Pobierz plik audio \"%1$s\""
|
||||
|
||||
#: dist/converse-no-dependencies.js:74156
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Download image file \"%1$s\""
|
||||
msgstr "Pobierz zdjęcie \"%1$s\""
|
||||
msgstr "Pobierz obraz pliku \"%1$s\""
|
||||
|
||||
#: dist/converse-no-dependencies.js:74164
|
||||
#, javascript-format
|
||||
|
@ -771,7 +760,6 @@ msgstr ""
|
|||
#: dist/converse-no-dependencies.js:76032
|
||||
#: dist/converse-no-dependencies.js:76129
|
||||
#: dist/converse-no-dependencies.js:76166
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to retract this message?"
|
||||
msgstr "Jesteś pewien, że chcesz wycofać tę wiadomość?"
|
||||
|
||||
|
@ -792,28 +780,24 @@ msgid "Sorry, you're not allowed to retract this message."
|
|||
msgstr "Przepraszam, nie wolno ci wycofać tej wiadomości."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid "You are about to retract this message."
|
||||
msgstr "Masz zamiar wycofać tę wiadomość."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"You may optionally include a message, explaining the reason for the "
|
||||
"retraction."
|
||||
msgstr "Opcjonalnie możesz dołączyć wiadomość wyjaśniającą powód zwinięcia."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
#, fuzzy
|
||||
msgid "Message Retraction"
|
||||
msgstr "Wersja wiadomości"
|
||||
msgstr "Wycofanie wiadomości"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
msgid "Optional reason"
|
||||
msgstr "Opcjonalny powód"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76208
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to retract this message"
|
||||
msgstr "Przepraszam, nie wolno ci wycofać tej wiadomości."
|
||||
|
||||
|
@ -824,32 +808,32 @@ msgstr "Zmiana ustawień"
|
|||
|
||||
#: dist/converse-no-dependencies.js:76270
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Edycja"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76299
|
||||
msgid "Retract"
|
||||
msgstr ""
|
||||
msgstr "Wycofaj"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Show URL previews"
|
||||
msgstr ""
|
||||
msgstr "Pokaż podgląd URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Hide URL previews"
|
||||
msgstr ""
|
||||
msgstr "Ukryj podgląd URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Show URL preview"
|
||||
msgstr ""
|
||||
msgstr "Pokaż podgląd URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Hide URL preview"
|
||||
msgstr ""
|
||||
msgstr "Ukryj podgląd URL"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:76429
|
||||
msgid "Image: "
|
||||
msgstr ""
|
||||
msgstr "Obraz: "
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:77991
|
||||
|
@ -869,9 +853,8 @@ msgid "OMEMO Fingerprints"
|
|||
msgstr "Odciski palców OMEMO"
|
||||
|
||||
#: dist/converse-no-dependencies.js:78232
|
||||
#, fuzzy
|
||||
msgid "No OMEMO-enabled devices found"
|
||||
msgstr "Pozostałe urządzenia z funkcją OMEMO"
|
||||
msgstr "Nie znaleziono urządzeń z włączonym OMEMO"
|
||||
|
||||
#: dist/converse-no-dependencies.js:78243
|
||||
msgid "Remove as contact"
|
||||
|
@ -942,7 +925,7 @@ msgstr "Jesteś pewien, że chcesz usunąć ten kontakt?"
|
|||
|
||||
#: dist/converse-no-dependencies.js:79208
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
msgstr "Ponów"
|
||||
|
||||
#: dist/converse-no-dependencies.js:79215
|
||||
msgid "Uploading file:"
|
||||
|
@ -963,9 +946,8 @@ msgid "Show more"
|
|||
msgstr "Pokaż więcej"
|
||||
|
||||
#: dist/converse-no-dependencies.js:79359
|
||||
#, fuzzy
|
||||
msgid "Show less"
|
||||
msgstr "Pokaż użytkowników"
|
||||
msgstr "Pokaż mniej"
|
||||
|
||||
#: dist/converse-no-dependencies.js:80828
|
||||
msgid "Search results"
|
||||
|
@ -992,14 +974,12 @@ msgid "Message characters remaining"
|
|||
msgstr "Pozostałe znaki wiadomości"
|
||||
|
||||
#: dist/converse-no-dependencies.js:82222
|
||||
#, fuzzy
|
||||
msgid "Hide participants"
|
||||
msgstr "Uczestnicy"
|
||||
msgstr "Ukryj uczestników"
|
||||
|
||||
#: dist/converse-no-dependencies.js:82224
|
||||
#, fuzzy
|
||||
msgid "Show participants"
|
||||
msgstr "Uczestnicy"
|
||||
msgstr "Pokaż uczestników"
|
||||
|
||||
#: dist/converse-no-dependencies.js:82247
|
||||
msgid "Choose a file to send"
|
||||
|
@ -1020,17 +1000,15 @@ msgstr "Jesteś pewien, że chcesz wyczyścić wiadomości z tej rozmowy?"
|
|||
#: dist/converse-no-dependencies.js:82962
|
||||
#: dist/converse-no-dependencies.js:97658
|
||||
msgid "Details"
|
||||
msgstr ""
|
||||
msgstr "Detale"
|
||||
|
||||
#: dist/converse-no-dependencies.js:82963
|
||||
#, fuzzy
|
||||
msgid "See more information about this person"
|
||||
msgstr "Pokaż więcej informacji o pokoju"
|
||||
msgstr "Pokaż więcej informacji o tej osobie"
|
||||
|
||||
#: dist/converse-no-dependencies.js:82976
|
||||
#, fuzzy
|
||||
msgid "Close and end this conversation"
|
||||
msgstr "Zakończ szyfrowaną rozmowę"
|
||||
msgstr "Zamknij i zakoń tę rozmowę"
|
||||
|
||||
#: dist/converse-no-dependencies.js:83478
|
||||
msgid "Hidden message"
|
||||
|
@ -1051,6 +1029,8 @@ msgstr "Masz nieprzeczytane wiadomości"
|
|||
#: dist/converse-no-dependencies.js:83639
|
||||
msgid "Sorry, the connection has been lost, and your message could not be sent"
|
||||
msgstr ""
|
||||
"Przepraszam, ale połączenie zostało utracone i twoja wiadomość nie mogła "
|
||||
"zostać wysłana"
|
||||
|
||||
#. eslint-disable-line class-methods-use-this
|
||||
#: dist/converse-no-dependencies.js:84684
|
||||
|
@ -1091,9 +1071,8 @@ msgid "This is a trusted device"
|
|||
msgstr "To jest zaufane urządzenie"
|
||||
|
||||
#: dist/converse-no-dependencies.js:87541
|
||||
#, fuzzy
|
||||
msgid "Password"
|
||||
msgstr "Hasło:"
|
||||
msgstr "Hasło"
|
||||
|
||||
#: dist/converse-no-dependencies.js:87547
|
||||
msgid "Create an account"
|
||||
|
@ -1139,18 +1118,16 @@ msgid "Toggle chat"
|
|||
msgstr "Przełącz czat"
|
||||
|
||||
#: dist/converse-no-dependencies.js:89998
|
||||
#, fuzzy
|
||||
msgid "Close these announcements"
|
||||
msgstr "Ogłoszenia"
|
||||
msgstr "Zamknij ogłoszenia"
|
||||
|
||||
#: dist/converse-no-dependencies.js:90595
|
||||
msgid "Announcements"
|
||||
msgstr "Ogłoszenia"
|
||||
|
||||
#: dist/converse-no-dependencies.js:90599
|
||||
#, fuzzy
|
||||
msgid "Click to open this server message"
|
||||
msgstr "Kliknij, aby otworzyć ten czat grupowy"
|
||||
msgstr "Kliknij, aby otworzyć tę wiadomość z serwera"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:90801
|
||||
|
@ -1159,19 +1136,16 @@ msgstr "Kliknij, aby przywrócić ten czat"
|
|||
|
||||
#: dist/converse-no-dependencies.js:91053
|
||||
#: dist/converse-no-dependencies.js:91071
|
||||
#, fuzzy
|
||||
msgid "Minimize"
|
||||
msgstr "Zminimalizowane"
|
||||
msgstr "Zminimalizuj"
|
||||
|
||||
#: dist/converse-no-dependencies.js:91054
|
||||
#, fuzzy
|
||||
msgid "Minimize this chat"
|
||||
msgstr "Zminimalizuj to okno czatu"
|
||||
msgstr "Zminimalizuj ten czat"
|
||||
|
||||
#: dist/converse-no-dependencies.js:91072
|
||||
#, fuzzy
|
||||
msgid "Minimize this groupchat"
|
||||
msgstr "Zminimalizuj to okno czatu"
|
||||
msgstr "Zminimalizuj ten czat grupowy"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:91421
|
||||
|
@ -1181,17 +1155,17 @@ msgstr "Zminimalizowane"
|
|||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:92962
|
||||
msgid "Hide"
|
||||
msgstr ""
|
||||
msgstr "Ukryj"
|
||||
|
||||
#: dist/converse-no-dependencies.js:92964
|
||||
msgid "Execute"
|
||||
msgstr ""
|
||||
msgstr "Uruchom"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93293
|
||||
msgid ""
|
||||
"Couldn't find a participant with that nickname. They might have left the "
|
||||
"groupchat."
|
||||
msgstr ""
|
||||
msgstr "Nie znaleziono uczestnika o tym nicku. Być moze opuścij czat grupowy."
|
||||
|
||||
#. e.g. Your nickname is "coolguy69"
|
||||
#: dist/converse-no-dependencies.js:93422
|
||||
|
@ -1206,7 +1180,7 @@ msgstr "Błąd: nieprawidłowa liczba argumentów"
|
|||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:93540
|
||||
msgid "On which entity do you want to run commands?"
|
||||
msgstr ""
|
||||
msgstr "Na której jednostce chcesz wykonać polecenie?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93542
|
||||
msgid ""
|
||||
|
@ -1215,18 +1189,16 @@ msgid ""
|
|||
msgstr ""
|
||||
|
||||
#: dist/converse-no-dependencies.js:93544
|
||||
#, fuzzy
|
||||
msgid "Commands found"
|
||||
msgstr "Pokoje na %1$s"
|
||||
msgstr "Znaleziono polecenia"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93546
|
||||
msgid "List available commands"
|
||||
msgstr ""
|
||||
msgstr "Lista dostępnych poleceń"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93550
|
||||
#, fuzzy
|
||||
msgid "No commands found"
|
||||
msgstr "Pokoje na %1$s"
|
||||
msgstr "Nie znaleziono polecenia"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93838
|
||||
#, fuzzy
|
||||
|
@ -1235,20 +1207,19 @@ msgstr "Wystąpił błąd w czasie próby usunięcia urządzenia."
|
|||
|
||||
#: dist/converse-no-dependencies.js:93849
|
||||
msgid "The specified entity doesn't support ad-hoc commands"
|
||||
msgstr ""
|
||||
msgstr "Wskazana jednostka nie obsługuje poleceń ad-hoc"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93964
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Sorry, an error occurred while trying to execute the command. See the "
|
||||
"developer console for details"
|
||||
msgstr ""
|
||||
"Ups, podczas wykonywania polecenia wystąpił błąd. Szczegółowe informacje "
|
||||
"można znaleźć w konsoli deweloperskiej przeglądarki."
|
||||
"Przepraszam, podczas wykonywania polecenia wystąpił błąd. Szczegółowe "
|
||||
"informacje można znaleźć na konsoli dewelopera."
|
||||
|
||||
#: dist/converse-no-dependencies.js:94078
|
||||
msgid "Loading configuration form"
|
||||
msgstr ""
|
||||
msgstr "Wczytywanie formularza konfiguracyjnego"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
#, fuzzy
|
||||
|
@ -1349,7 +1320,7 @@ msgstr "Nie znaleziono użytkowników z tą rolą."
|
|||
|
||||
#: dist/converse-no-dependencies.js:94965
|
||||
msgid "Type here to filter the search results"
|
||||
msgstr ""
|
||||
msgstr "Wpisz tutaj aby odfiltrować rezultaty przeszukiwań"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94969
|
||||
msgid "Show users"
|
||||
|
@ -1384,9 +1355,8 @@ msgid "Timeout error while trying to set the affiliation"
|
|||
msgstr "Przepraszam, coś poszło nie tak podczas próby ustalenia przynależności"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95315
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to make that change"
|
||||
msgstr "Nie możesz dokonać tej zmiany"
|
||||
msgstr "Przepraszam, ale nie możesz dokonać tej zmiany"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95317
|
||||
msgid "Sorry, something went wrong while trying to set the affiliation"
|
||||
|
@ -1506,10 +1476,9 @@ msgid "Allow muted user to post messages"
|
|||
msgstr "Pozwól uciszonemu człowiekowi na rozmowę"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96345
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The conversation has moved to a new address. Click the link below to enter."
|
||||
msgstr "Rozmowa została przeniesiona. Kliknij poniżej aby wejść."
|
||||
msgstr "Rozmowa została przeniesiona pod nowy adres. Kliknij poniżej aby wejść."
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:96353
|
||||
|
@ -1517,7 +1486,7 @@ msgid "This groupchat no longer exists"
|
|||
msgstr "Ten czat grupowy już nie istnieje"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96355
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "The following reason was given: \"%1$s\""
|
||||
msgstr "Podany powód to: \"%1$s\"."
|
||||
|
||||
|
@ -1538,19 +1507,16 @@ msgid "Invite"
|
|||
msgstr "Zaproś"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96916
|
||||
#, fuzzy
|
||||
msgid "Invite someone to this groupchat"
|
||||
msgstr "Usuń ten czat grupowy"
|
||||
msgstr "Zaproś kogoś na ten czat grupowy"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96918
|
||||
#, fuzzy
|
||||
msgid "user@example.org"
|
||||
msgstr "np. użytkownik@przykładowa-domena.pl"
|
||||
msgstr "użytkownik@example.org"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96924
|
||||
#, fuzzy
|
||||
msgid "Optional reason for the invitation"
|
||||
msgstr "Opcjonalny powód"
|
||||
msgstr "Opcjonalny powód zaproszenia"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97156
|
||||
msgid "Topic"
|
||||
|
@ -1701,42 +1667,36 @@ msgstr "Informacje o czacie grupowym dla %1$s"
|
|||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:97327
|
||||
#, fuzzy
|
||||
msgid "Hide the groupchat topic"
|
||||
msgstr "Wejdź do pokoju"
|
||||
msgstr "Ukryj temat czatu grupowego"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97329
|
||||
#, fuzzy
|
||||
msgid "This groupchat is bookmarked"
|
||||
msgstr "Ten człowiek jest moderatorem"
|
||||
msgstr "Ten czat grupowy został zapamiętany"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97659
|
||||
#, fuzzy
|
||||
msgid "Show more information about this groupchat"
|
||||
msgstr "Pokaż więcej informacji na temat tego czatu grupowego"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97670
|
||||
#, fuzzy
|
||||
msgid "Configure"
|
||||
msgstr "Potwierdź"
|
||||
msgstr "Konfiguracja"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97671
|
||||
msgid "Configure this groupchat"
|
||||
msgstr "Skonfiguruj ten pokój"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97684
|
||||
#, fuzzy
|
||||
msgid "Invite someone to join this groupchat"
|
||||
msgstr "Każdy może dołączyć do tego czatu grupowego"
|
||||
msgstr "Zaproś kogoś aby przyłączył się do czatu grupowego"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97698
|
||||
#, fuzzy
|
||||
msgid "Show topic"
|
||||
msgstr "Pokaż pokoje"
|
||||
msgstr "Pokaż temat"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97698
|
||||
msgid "Hide topic"
|
||||
msgstr ""
|
||||
msgstr "Ukryj temat"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97699
|
||||
msgid "Show the topic message in the heading"
|
||||
|
@ -1757,23 +1717,20 @@ msgid "Moderate this groupchat"
|
|||
msgstr "Opuść ten pokój"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97729
|
||||
#, fuzzy
|
||||
msgid "Destroy"
|
||||
msgstr "Zniszcz pokój"
|
||||
msgstr "Zniszcz"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97743
|
||||
msgid "Leave"
|
||||
msgstr ""
|
||||
msgstr "Opuść"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97744
|
||||
#, fuzzy
|
||||
msgid "Leave and close this groupchat"
|
||||
msgstr "Opuść ten pokój"
|
||||
msgstr "Opuść i zamknij ten czat grupowy"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97753
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to leave this groupchat?"
|
||||
msgstr "Jesteś pewny, że chcesz wyjść z grupowego czatu %1$s?"
|
||||
msgstr "Jesteś pewny, że chcesz opuścić ten czat grupowy?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:98120
|
||||
msgid "This user is a moderator."
|
||||
|
@ -1818,14 +1775,12 @@ msgid "Participants"
|
|||
msgstr "Uczestnicy"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99003
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to destroy this groupchat?"
|
||||
msgstr "Jesteś pewny, że chcesz wyjść z grupowego czatu %1$s?"
|
||||
msgstr "Jesteś pewny, że chcesz usunąć ten czat grupowy?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99006
|
||||
#, fuzzy
|
||||
msgid "Please enter the XMPP address of this groupchat to confirm"
|
||||
msgstr "Proszę wprowadzić dostawcę XMPP, aby zarejestrować się:"
|
||||
msgstr "Proszę wprowadzić adres XMPP tego czatu grupowego aby potwierdzić"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:99008
|
||||
|
@ -1834,9 +1789,8 @@ msgid "name@example.org"
|
|||
msgstr "np. użytkownik@przykładowa-domena.pl"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99012
|
||||
#, fuzzy
|
||||
msgid "Optional reason for destroying this groupchat"
|
||||
msgstr "Jesteś pewny, że chcesz wyjść z grupowego czatu %1$s?"
|
||||
msgstr "Dodatkowy powód do usunięcia tego czatu grupowego"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99016
|
||||
msgid "Optional XMPP address for a new groupchat that replaces this one"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,8 +8,8 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 0.6.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2020-12-19 10:29+0000\n"
|
||||
"Last-Translator: LL Magical <lolayami2004@gmail.com>\n"
|
||||
"PO-Revision-Date: 2021-05-04 18:32+0000\n"
|
||||
"Last-Translator: bashl <esqueleto777@disroot.org>\n"
|
||||
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
|
||||
"conversejs/translations/pt_BR/>\n"
|
||||
"Language: pt_BR\n"
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 4.4-dev\n"
|
||||
"X-Generator: Weblate 4.7-dev\n"
|
||||
"domain: converse\n"
|
||||
"lang: pt_BR\n"
|
||||
"plural_forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
@ -770,9 +770,8 @@ msgid "Sorry, you're not allowed to retract this message."
|
|||
msgstr "Desculpe, você não está autorizado a retrair esta mensagem."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid "You are about to retract this message."
|
||||
msgstr "Você está a ponto de retirar esta mensagem."
|
||||
msgstr "Você está a prestes a retirar esta mensagem."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
|
@ -811,19 +810,19 @@ msgstr "Retirar"
|
|||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Show URL previews"
|
||||
msgstr ""
|
||||
msgstr "Mostrar pré-visualizações da URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Hide URL previews"
|
||||
msgstr ""
|
||||
msgstr "Ocultar pré-visualizações da URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Show URL preview"
|
||||
msgstr ""
|
||||
msgstr "Mostrar pré-visualização da URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Hide URL preview"
|
||||
msgstr ""
|
||||
msgstr "Ocultar pré-visualização da URL"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:76429
|
||||
|
@ -1215,7 +1214,7 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:94078
|
||||
msgid "Loading configuration form"
|
||||
msgstr ""
|
||||
msgstr "Carregando configuração de formulário"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
msgid "Sorry, an error occurred while trying to submit the config form."
|
||||
|
@ -1470,10 +1469,11 @@ msgid "Allow muted user to post messages"
|
|||
msgstr "Permitir que o usuário mudo publique mensagens"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96345
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The conversation has moved to a new address. Click the link below to enter."
|
||||
msgstr "Esta conversa foi movida. Clique abaixo para entrar de novo."
|
||||
msgstr ""
|
||||
"Esta conversa foi movida para um novo endereço. Clique abaixo para entrar "
|
||||
"novamente."
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:96353
|
||||
|
@ -1499,7 +1499,7 @@ msgstr "A razão dada é: \"%1$s\"."
|
|||
#: dist/converse-no-dependencies.js:96914
|
||||
#: dist/converse-no-dependencies.js:97683
|
||||
msgid "Invite"
|
||||
msgstr "convite"
|
||||
msgstr "Convite"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96916
|
||||
msgid "Invite someone to this groupchat"
|
||||
|
|
|
@ -8,8 +8,8 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 3.3.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2020-12-14 01:29+0000\n"
|
||||
"Last-Translator: Sergiu <adinfinitvm@wail.ch>\n"
|
||||
"PO-Revision-Date: 2021-06-19 21:33+0000\n"
|
||||
"Last-Translator: dhruva dhruva <dhruva.gurmukhi@slmail.me>\n"
|
||||
"Language-Team: Romanian <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/ro/>\n"
|
||||
"Language: ro\n"
|
||||
|
@ -18,7 +18,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
|
||||
"20)) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.4-dev\n"
|
||||
"X-Generator: Weblate 4.7\n"
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:42989
|
||||
|
@ -27,16 +27,14 @@ msgstr "Conexiunea s-a întrerupt, se încearcă reconectarea."
|
|||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:43239
|
||||
#, fuzzy
|
||||
msgid "An error occurred while connecting to the chat server."
|
||||
msgstr "S-a produs o eroare în timpul conexiunii la serverul de discuții."
|
||||
msgstr "S-a produs o eroare în timpul conectării la serverul de discuții."
|
||||
|
||||
#: dist/converse-no-dependencies.js:43246
|
||||
#, fuzzy
|
||||
msgid "Your XMPP address and/or password is incorrect. Please try again."
|
||||
msgstr ""
|
||||
"ID-ul dumneavoastră Jabber sau parola sunt incorecte. Vă rugăm să încercați "
|
||||
"din nou."
|
||||
"Adresa dumneavoastră de XMPP sau parola sunt incorecte. Vă rugăm să "
|
||||
"încercați din nou."
|
||||
|
||||
#: dist/converse-no-dependencies.js:43258
|
||||
#, javascript-format
|
||||
|
@ -72,9 +70,8 @@ msgstr "%1$s a plecat"
|
|||
|
||||
#: dist/converse-no-dependencies.js:51675
|
||||
#: dist/converse-no-dependencies.js:58125
|
||||
#, fuzzy
|
||||
msgid "You're not allowed to retract your message."
|
||||
msgstr "Nu aveți permisiunea de a crea noi grupuri de discuții."
|
||||
msgstr "Nu aveți permisiunea să vă retrageți mesajul."
|
||||
|
||||
#: dist/converse-no-dependencies.js:51677
|
||||
#: dist/converse-no-dependencies.js:58129
|
||||
|
@ -125,9 +122,8 @@ msgstr ""
|
|||
"permisă de server, care este %2$s."
|
||||
|
||||
#: dist/converse-no-dependencies.js:53208
|
||||
#, fuzzy
|
||||
msgid "Undecryptable OMEMO message"
|
||||
msgstr "Mesajul OMEMO nu poate fi criptat"
|
||||
msgstr "Mesaj OMEMO nedecriptabil"
|
||||
|
||||
#: dist/converse-no-dependencies.js:53272
|
||||
msgid "Sorry, could not determine upload URL."
|
||||
|
@ -154,43 +150,43 @@ msgstr "Ne pare rău, nu am putut încărca fișierul dumneavoastră."
|
|||
|
||||
#: dist/converse-no-dependencies.js:55882
|
||||
msgid "Smileys and emotions"
|
||||
msgstr ""
|
||||
msgstr "Zâmbăreți și emoticoane"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55883
|
||||
msgid "People"
|
||||
msgstr ""
|
||||
msgstr "Persoane"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55884
|
||||
msgid "Activities"
|
||||
msgstr ""
|
||||
msgstr "Activități"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55885
|
||||
msgid "Travel"
|
||||
msgstr ""
|
||||
msgstr "Călătorie"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55886
|
||||
msgid "Objects"
|
||||
msgstr ""
|
||||
msgstr "Obiecte"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55887
|
||||
msgid "Animals and nature"
|
||||
msgstr ""
|
||||
msgstr "Animale și natură"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55888
|
||||
msgid "Food and drink"
|
||||
msgstr ""
|
||||
msgstr "Mâncăruri și băuturi"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55889
|
||||
msgid "Symbols"
|
||||
msgstr ""
|
||||
msgstr "Simboluri"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55890
|
||||
msgid "Flags"
|
||||
msgstr ""
|
||||
msgstr "Steaguri"
|
||||
|
||||
#: dist/converse-no-dependencies.js:55891
|
||||
msgid "Stickers"
|
||||
msgstr ""
|
||||
msgstr "Autocolante"
|
||||
|
||||
#: dist/converse-no-dependencies.js:58127
|
||||
msgid ""
|
||||
|
@ -210,37 +206,33 @@ msgstr ""
|
|||
msgid ""
|
||||
"Your message was not delivered because you're not present in the groupchat."
|
||||
msgstr ""
|
||||
"Mesajul dvs. nu a fost livrat deoarece nu sunteți prezent în grupul de chat."
|
||||
|
||||
#: dist/converse-no-dependencies.js:58502
|
||||
#, fuzzy
|
||||
msgid "A timeout happened while while trying to retract your message."
|
||||
msgstr "S-a produs o eroare în timpul conexiunii la serverul de discuții."
|
||||
msgstr "A apărut o expirare de timp când încercați să vă retrageți mesajul."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59253
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error happened while running the command."
|
||||
msgstr ""
|
||||
"Din păcate, a avut loc o eroare în timpul rulării acestei comenzi. Pentru "
|
||||
"detalii verificați consola de dezvoltare a navigatorului dvs."
|
||||
msgstr "Din păcate, s-a produs o eroare în timpul rulării comenzii."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59253
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
#, fuzzy
|
||||
msgid "Check your browser's developer console for details."
|
||||
msgstr ""
|
||||
"Din păcate, a avut loc o eroare în timpul rulării acestei comenzi. Pentru "
|
||||
"detalii verificați consola de dezvoltare a navigatorului dvs."
|
||||
msgstr "Pentru detalii verificați consola de dezvoltare a navigatorului dvs."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59278
|
||||
#: dist/converse-no-dependencies.js:59301
|
||||
#, fuzzy
|
||||
msgid "Error: couldn't find a groupchat participant based on your arguments"
|
||||
msgstr "Eroare: %1$s nu se găsește în acest grup de discuții"
|
||||
msgstr ""
|
||||
"Eroare: nu s-a putut găsi un participant în grupul de discuții pe baza "
|
||||
"argumentelor dvs"
|
||||
|
||||
#: dist/converse-no-dependencies.js:59288
|
||||
#, fuzzy
|
||||
msgid "Error: found multiple groupchat participant based on your arguments"
|
||||
msgstr "Eroare: %1$s nu se găsește în acest grup de discuții"
|
||||
msgstr ""
|
||||
"Eroare: am găsit mai mulți participanți la grupul de discuții pe baza "
|
||||
"argumentelor dvs"
|
||||
|
||||
#: dist/converse-no-dependencies.js:59316
|
||||
#, javascript-format
|
||||
|
@ -263,13 +255,15 @@ msgstr "Interzis: nu aveți autorizația necesară pentru a face acest lucru."
|
|||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:59877
|
||||
msgid "You're not allowed to register yourself in this groupchat."
|
||||
msgstr ""
|
||||
msgstr "Nu ai permisiunea să te înregistrezi în acest grup de discuții."
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:59879
|
||||
msgid ""
|
||||
"You're not allowed to register in this groupchat because it's members-only."
|
||||
msgstr ""
|
||||
"Nu aveți voie să vă înregistrați în acest grup de discuții, deoarece este "
|
||||
"doar pentru membri."
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:59924
|
||||
|
@ -277,12 +271,16 @@ msgid ""
|
|||
"Can't register your nickname in this groupchat, it doesn't support "
|
||||
"registration."
|
||||
msgstr ""
|
||||
"Nu vă puteți înregistra porecla în acest grup de discuții, nu acceptă "
|
||||
"înregistrarea."
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:59926
|
||||
msgid ""
|
||||
"Can't register your nickname in this groupchat, invalid data form supplied."
|
||||
msgstr ""
|
||||
"Nu vă puteți înregistra porecla în acest grup de discuții, formular invalid "
|
||||
"de date furnizat."
|
||||
|
||||
#: dist/converse-no-dependencies.js:60130
|
||||
#, javascript-format
|
||||
|
|
|
@ -8,8 +8,8 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 0.10\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2020-11-18 18:28+0100\n"
|
||||
"Last-Translator: Andrey <andrey@mailbox.org>\n"
|
||||
"PO-Revision-Date: 2021-04-14 08:39+0000\n"
|
||||
"Last-Translator: member7me <zegucdx5@mail.ru>\n"
|
||||
"Language-Team: Russian <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/ru/>\n"
|
||||
"Language: ru\n"
|
||||
|
@ -18,7 +18,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.3.1\n"
|
||||
"X-Generator: Weblate 4.6-dev\n"
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:42989
|
||||
|
@ -200,27 +200,27 @@ msgid "A timeout happened while while trying to retract your message."
|
|||
msgstr "Истекло время ожидания при попытке отозвать ваше сообщение."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59253
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error happened while running the command."
|
||||
msgstr ""
|
||||
"Извините, произошла ошибка при попытке сохранить данные вашего профиля."
|
||||
msgstr "К сожалению, при выполнении команды произошла ошибка."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59253
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
#, fuzzy
|
||||
msgid "Check your browser's developer console for details."
|
||||
msgstr "Проверьте консоль вашего браузера для деталей об ошибках."
|
||||
msgstr ""
|
||||
"Для получения деталей ошибки, проверьте консоль разработчика в браузере."
|
||||
|
||||
#: dist/converse-no-dependencies.js:59278
|
||||
#: dist/converse-no-dependencies.js:59301
|
||||
#, fuzzy
|
||||
msgid "Error: couldn't find a groupchat participant based on your arguments"
|
||||
msgstr "Ошибка: не удалось найти участника группового чата \"%1$s\""
|
||||
msgstr ""
|
||||
"Ошибка: не удалось найти участника группового чата на основании ваших "
|
||||
"аргументов"
|
||||
|
||||
#: dist/converse-no-dependencies.js:59288
|
||||
#, fuzzy
|
||||
msgid "Error: found multiple groupchat participant based on your arguments"
|
||||
msgstr "Все участники группового чата могут видеть ваш XMPP адрес"
|
||||
msgstr ""
|
||||
"Ошибка: на основе ваших аргументов обнаружено несколько участников "
|
||||
"группового чата"
|
||||
|
||||
#: dist/converse-no-dependencies.js:59316
|
||||
#, javascript-format
|
||||
|
@ -302,9 +302,9 @@ msgid "%1$s is no longer a moderator"
|
|||
msgstr "%1$s больше не модератор"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60524
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s has been given a voice"
|
||||
msgstr "%1$s снова получил право голоса"
|
||||
msgstr "%1$s получил право голоса"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60526
|
||||
#, javascript-format
|
||||
|
@ -317,49 +317,49 @@ msgid "%1$s and %2$s"
|
|||
msgstr "%1$s и %2$s"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60539
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s are typing"
|
||||
msgstr "%1$s набирает текст"
|
||||
msgstr "%1$s печатает"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60541
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have stopped typing"
|
||||
msgstr "%1$s прекратил печатать"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60543
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have gone away"
|
||||
msgstr "%1$s отошёл"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60545
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have entered the groupchat"
|
||||
msgstr "%1$s вошёл в комнату"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60547
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have left the groupchat"
|
||||
msgstr "%1$s покинул комнату"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60549
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s are now moderators"
|
||||
msgstr "%1$s теперь модератор"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60551
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s are no longer moderators"
|
||||
msgstr "%1$s больше не модератор"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60553
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have been given voices"
|
||||
msgstr "%1$s снова получил право голоса"
|
||||
msgstr "%1$s были даны голоса"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60555
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%1$s have been muted"
|
||||
msgstr "%1$s был приглушён"
|
||||
msgstr "%1$s отключены"
|
||||
|
||||
#: dist/converse-no-dependencies.js:60859
|
||||
#, javascript-format
|
||||
|
@ -597,14 +597,13 @@ msgstr ""
|
|||
"перезагрузить страницу, чтобы запросить их снова."
|
||||
|
||||
#: dist/converse-no-dependencies.js:65180
|
||||
#, fuzzy
|
||||
msgid "Timeout while trying to fetch archived messages."
|
||||
msgstr "При попытке отозвать сообщение произошел таймаут"
|
||||
msgstr "Тайм-аут при попытке получить архивные сообщения."
|
||||
|
||||
#: dist/converse-no-dependencies.js:65195
|
||||
#, fuzzy
|
||||
msgid "An error occurred while querying for archived messages."
|
||||
msgstr "Извините, произошла ошибка при попытке удаления устройств."
|
||||
msgstr ""
|
||||
"Извините, произошла ошибка при попытке получения заархивированных сообщений."
|
||||
|
||||
#: dist/converse-no-dependencies.js:67158
|
||||
#, javascript-format
|
||||
|
@ -721,7 +720,7 @@ msgid "Download audio file \"%1$s\""
|
|||
msgstr "Скачать аудиофайл \"%1$s\""
|
||||
|
||||
#: dist/converse-no-dependencies.js:74156
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Download image file \"%1$s\""
|
||||
msgstr "Скачать изображение \"%1$s\""
|
||||
|
||||
|
@ -734,6 +733,8 @@ msgstr "Скачать файл \"%1$s\""
|
|||
msgid ""
|
||||
"You have an unsent message which will be lost if you continue. Are you sure?"
|
||||
msgstr ""
|
||||
"У вас есть неотправленное сообщение, в случае, если вы продолжите, оно будет "
|
||||
"потеряно. Вы уверены?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76031
|
||||
#: dist/converse-no-dependencies.js:76122
|
||||
|
@ -741,13 +742,14 @@ msgid ""
|
|||
"Be aware that other XMPP/Jabber clients (and servers) may not yet support "
|
||||
"retractions and that this message may not be removed everywhere."
|
||||
msgstr ""
|
||||
"Имейте в виду, что другие XMPP клиенты (и серверы) могут еще не поддерживать "
|
||||
"удаление и что это сообщение может быть не удалено везде."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76032
|
||||
#: dist/converse-no-dependencies.js:76129
|
||||
#: dist/converse-no-dependencies.js:76166
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to retract this message?"
|
||||
msgstr "Вы уверены, что хотите удалить этот контакт?"
|
||||
msgstr "Вы уверены, что хотите удалить это сообщение?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76039
|
||||
#: dist/converse-no-dependencies.js:76136
|
||||
|
@ -755,45 +757,37 @@ msgstr "Вы уверены, что хотите удалить этот кон
|
|||
#: dist/converse-no-dependencies.js:97755
|
||||
#: dist/converse-no-dependencies.js:99021
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
msgstr "Подтвердить"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76088
|
||||
msgid "A timeout occurred while trying to retract the message"
|
||||
msgstr "При попытке отозвать сообщение произошел таймаут"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76092
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to retract this message."
|
||||
msgstr "Вам не разрешено создавать новые комнаты."
|
||||
msgstr "Извините, вы не можете удалить это сообщение."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid "You are about to retract this message."
|
||||
msgstr "Вы собираетесь отозвать это сообщение."
|
||||
msgstr "Вы собираетесь удалить это сообщение."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"You may optionally include a message, explaining the reason for the "
|
||||
"retraction."
|
||||
msgstr ""
|
||||
"Вы собираетесь пригласить %1$s в комнату \"%2$s\". По желанию вы можете "
|
||||
"добавить сообщение с объяснением причины приглашения."
|
||||
msgstr "По желанию вы можете добавить сообщение с объяснением причины отказа."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
#, fuzzy
|
||||
msgid "Message Retraction"
|
||||
msgstr "Версии сообщения"
|
||||
msgstr "Удаление сообщения"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
#, fuzzy
|
||||
msgid "Optional reason"
|
||||
msgstr "Опционная подсказка"
|
||||
msgstr "Необязательная причина"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76208
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to retract this message"
|
||||
msgstr "Вам не разрешено создавать новые комнаты."
|
||||
msgstr "Извините, вы не можете удалить это сообщение"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76270
|
||||
msgid "Cancel Editing"
|
||||
|
@ -809,19 +803,19 @@ msgstr "Отозвать"
|
|||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Show URL previews"
|
||||
msgstr ""
|
||||
msgstr "Показать превью URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Hide URL previews"
|
||||
msgstr ""
|
||||
msgstr "Скрыть превью URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Show URL preview"
|
||||
msgstr ""
|
||||
msgstr "Показать превью URL"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Hide URL preview"
|
||||
msgstr ""
|
||||
msgstr "Скрыть превью URL"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:76429
|
||||
|
@ -1109,18 +1103,16 @@ msgid "Toggle chat"
|
|||
msgstr "Включить чат"
|
||||
|
||||
#: dist/converse-no-dependencies.js:89998
|
||||
#, fuzzy
|
||||
msgid "Close these announcements"
|
||||
msgstr "Покинуть эту комнату"
|
||||
msgstr "Закрыть эти объявления"
|
||||
|
||||
#: dist/converse-no-dependencies.js:90595
|
||||
msgid "Announcements"
|
||||
msgstr ""
|
||||
msgstr "Объявления"
|
||||
|
||||
#: dist/converse-no-dependencies.js:90599
|
||||
#, fuzzy
|
||||
msgid "Click to open this server message"
|
||||
msgstr "Зайти в чат"
|
||||
msgstr "Нажмите, чтобы открыть это сообщение сервера"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:90801
|
||||
|
@ -1129,19 +1121,16 @@ msgstr "Кликните, чтобы развернуть чат"
|
|||
|
||||
#: dist/converse-no-dependencies.js:91053
|
||||
#: dist/converse-no-dependencies.js:91071
|
||||
#, fuzzy
|
||||
msgid "Minimize"
|
||||
msgstr "Свёрнуто"
|
||||
msgstr "Свернуть"
|
||||
|
||||
#: dist/converse-no-dependencies.js:91054
|
||||
#, fuzzy
|
||||
msgid "Minimize this chat"
|
||||
msgstr "Свернуть окно чата"
|
||||
|
||||
#: dist/converse-no-dependencies.js:91072
|
||||
#, fuzzy
|
||||
msgid "Minimize this groupchat"
|
||||
msgstr "Свернуть окно чата"
|
||||
msgstr "Свернуть этот групповой чат"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:91421
|
||||
|
@ -1151,23 +1140,24 @@ msgstr "Свёрнуто"
|
|||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:92962
|
||||
msgid "Hide"
|
||||
msgstr ""
|
||||
msgstr "Скрыть"
|
||||
|
||||
#: dist/converse-no-dependencies.js:92964
|
||||
msgid "Execute"
|
||||
msgstr ""
|
||||
msgstr "Выполнить"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93293
|
||||
msgid ""
|
||||
"Couldn't find a participant with that nickname. They might have left the "
|
||||
"groupchat."
|
||||
msgstr ""
|
||||
"Не удалось найти участника с таким ником. Он мог покинуть групповой чат."
|
||||
|
||||
#. e.g. Your nickname is "coolguy69"
|
||||
#: dist/converse-no-dependencies.js:93422
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Your nickname is \"%1$s\""
|
||||
msgstr "Ваш псевдоним был изменён на: %1$s"
|
||||
msgstr "Ваш ник был изменён на: %1$s"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93454
|
||||
msgid "Error: invalid number of arguments"
|
||||
|
@ -1176,13 +1166,15 @@ msgstr "Ошибка: неверное количество аргументов
|
|||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:93540
|
||||
msgid "On which entity do you want to run commands?"
|
||||
msgstr ""
|
||||
msgstr "На чем вы хотите запускать команды?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93542
|
||||
msgid ""
|
||||
"Certain XMPP services and entities allow privileged users to execute ad-hoc "
|
||||
"commands on them."
|
||||
msgstr ""
|
||||
"Некоторые XMPP службы и объекты позволяют привилегированным пользователям "
|
||||
"выполнять ad-hoc команды."
|
||||
|
||||
#: dist/converse-no-dependencies.js:93544
|
||||
msgid "Commands found"
|
||||
|
@ -1190,20 +1182,19 @@ msgstr "Найдены команды"
|
|||
|
||||
#: dist/converse-no-dependencies.js:93546
|
||||
msgid "List available commands"
|
||||
msgstr ""
|
||||
msgstr "Список доступных команд"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93550
|
||||
msgid "No commands found"
|
||||
msgstr "Комманд не найдено"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93838
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error occurred while looking for commands on that entity."
|
||||
msgstr "Извините, произошла ошибка при попытке удаления устройств."
|
||||
msgstr "Извините, произошла ошибка."
|
||||
|
||||
#: dist/converse-no-dependencies.js:93849
|
||||
msgid "The specified entity doesn't support ad-hoc commands"
|
||||
msgstr ""
|
||||
msgstr "Указанный объект не поддерживает ad-hoc команды"
|
||||
|
||||
#: dist/converse-no-dependencies.js:93964
|
||||
msgid ""
|
||||
|
@ -1215,12 +1206,11 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:94078
|
||||
msgid "Loading configuration form"
|
||||
msgstr ""
|
||||
msgstr "Загрузка формы конфигурации"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
#, fuzzy
|
||||
msgid "Sorry, an error occurred while trying to submit the config form."
|
||||
msgstr "Извините, произошла ошибка при попытке удаления устройств."
|
||||
msgstr "Извините, произошла ошибка при добавлении конфиг формы."
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:94447
|
||||
|
@ -1320,7 +1310,7 @@ msgstr "Пользователи с этой ролью не найдены."
|
|||
|
||||
#: dist/converse-no-dependencies.js:94965
|
||||
msgid "Type here to filter the search results"
|
||||
msgstr ""
|
||||
msgstr "Введите здесь, чтобы отфильтровать результаты поиска"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94969
|
||||
msgid "Show users"
|
||||
|
@ -1349,46 +1339,40 @@ msgstr ""
|
|||
"администраторы и владельцы автоматически получают роль модератора."
|
||||
|
||||
#: dist/converse-no-dependencies.js:95313
|
||||
#, fuzzy
|
||||
msgid "Timeout error while trying to set the affiliation"
|
||||
msgstr "Извините, что-то пошло не так при попытке обновления"
|
||||
msgstr "Ошибка тайм-аута"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95315
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to make that change"
|
||||
msgstr "Вам не разрешено вносить это изменение"
|
||||
msgstr "Извините, вам не разрешено вносить это изменение"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95317
|
||||
#, fuzzy
|
||||
msgid "Sorry, something went wrong while trying to set the affiliation"
|
||||
msgstr "Извините, что-то пошло не так при попытке обновления"
|
||||
msgstr "Извините, что-то пошло не так при попытке установить принадлежность"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95324
|
||||
msgid "Affiliation changed"
|
||||
msgstr ""
|
||||
msgstr "Принадлежность изменена"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95359
|
||||
#, fuzzy
|
||||
msgid "Role changed"
|
||||
msgstr "Включить чат"
|
||||
msgstr "Роль изменена"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95372
|
||||
msgid "You're not allowed to make that change"
|
||||
msgstr "Вам не разрешено вносить это изменение"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95374
|
||||
#, fuzzy
|
||||
msgid "Sorry, something went wrong while trying to set the role"
|
||||
msgstr "Извините, что-то пошло не так при попытке обновления"
|
||||
msgstr "Извините, что-то пошло не так при попытке установить роль"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95419
|
||||
msgid "Enter groupchat"
|
||||
msgstr "Войти в комнату"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95421
|
||||
#, fuzzy
|
||||
msgid "Choose a nickname to enter"
|
||||
msgstr "Выберите файл для отправки"
|
||||
msgstr "Выберите ник для входа"
|
||||
|
||||
#: dist/converse-no-dependencies.js:95421
|
||||
msgid "Please choose your nickname"
|
||||
|
@ -1408,17 +1392,15 @@ msgstr "Дать права администратора"
|
|||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
msgid "Ban user by changing their affiliation to outcast"
|
||||
msgstr ""
|
||||
msgstr "Забанить пользователя, изменив его принадлежность на выбывшего"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
#, fuzzy
|
||||
msgid "Clear the chat area"
|
||||
msgstr "Закрыть это окно чата"
|
||||
msgstr "Очистить окно чата"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
#, fuzzy
|
||||
msgid "Close this groupchat"
|
||||
msgstr "Покинуть эту комнату"
|
||||
msgstr "Закрыть групповой чат"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
msgid "Change user role to participant"
|
||||
|
@ -1443,7 +1425,7 @@ msgstr "Сделать пользователя участником"
|
|||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
msgid "Opens up the moderator tools GUI"
|
||||
msgstr ""
|
||||
msgstr "Открывает GUI интерфейс инструментов модератора"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
msgid "Remove user's ability to post messages"
|
||||
|
@ -1462,13 +1444,12 @@ msgid "Grant ownership of this groupchat"
|
|||
msgstr "Предоставить права владельца на этот чат"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
#, fuzzy
|
||||
msgid "Register your nickname"
|
||||
msgstr "Изменить свой псевдоним"
|
||||
msgstr "Зарегистрируйте свой ник"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
msgid "Revoke the user's current affiliation"
|
||||
msgstr ""
|
||||
msgstr "Отменить текущую принадлежность пользователя"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96121
|
||||
msgid "Set groupchat subject"
|
||||
|
@ -1483,10 +1464,9 @@ msgid "Allow muted user to post messages"
|
|||
msgstr "Разрешить заглушенным пользователям отправлять сообщения"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96345
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The conversation has moved to a new address. Click the link below to enter."
|
||||
msgstr "Беседа перемещена. Нажмите ниже чтобы войти."
|
||||
msgstr "Беседа перемещена в новый адрес. Нажмите ниже, чтобы войти."
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:96353
|
||||
|
@ -1494,9 +1474,9 @@ msgid "This groupchat no longer exists"
|
|||
msgstr "Эта комната больше не существует"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96355
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "The following reason was given: \"%1$s\""
|
||||
msgstr "Причиной является: \"%1$s\"."
|
||||
msgstr "Была указана следующая причина: \"%1$s\""
|
||||
|
||||
#: dist/converse-no-dependencies.js:96851
|
||||
#, javascript-format
|
||||
|
@ -1515,19 +1495,16 @@ msgid "Invite"
|
|||
msgstr "Пригласить"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96916
|
||||
#, fuzzy
|
||||
msgid "Invite someone to this groupchat"
|
||||
msgstr "Покинуть эту комнату"
|
||||
msgstr "Пригласите кого-нибудь в этот групповой чат"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96918
|
||||
#, fuzzy
|
||||
msgid "user@example.org"
|
||||
msgstr "например, name@example.org"
|
||||
msgstr "name@example.org"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96924
|
||||
#, fuzzy
|
||||
msgid "Optional reason for the invitation"
|
||||
msgstr "Опционная подсказка"
|
||||
msgstr "Необязательная причина приглашения"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97156
|
||||
msgid "Topic"
|
||||
|
@ -1678,78 +1655,68 @@ msgstr "Информация конференции от %1$s"
|
|||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:97327
|
||||
#, fuzzy
|
||||
msgid "Hide the groupchat topic"
|
||||
msgstr "Войти в комнату"
|
||||
msgstr "Скрыть тему группового чата"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97329
|
||||
#, fuzzy
|
||||
msgid "This groupchat is bookmarked"
|
||||
msgstr "Эта комната модерируется"
|
||||
msgstr "Этот групповой чат добавлен в закладки"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97659
|
||||
#, fuzzy
|
||||
msgid "Show more information about this groupchat"
|
||||
msgstr "Показать больше информации об этом чате"
|
||||
msgstr "Показать дополнительную информацию об этом групповом чате"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97670
|
||||
msgid "Configure"
|
||||
msgstr ""
|
||||
msgstr "Настроить"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97671
|
||||
msgid "Configure this groupchat"
|
||||
msgstr "Настроить комнату"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97684
|
||||
#, fuzzy
|
||||
msgid "Invite someone to join this groupchat"
|
||||
msgstr "Каждый может присоединиться к этой комнате"
|
||||
msgstr "Предложите кому-нибудь присоединиться к этому групповому чату"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97698
|
||||
#, fuzzy
|
||||
msgid "Show topic"
|
||||
msgstr "Показать список групп"
|
||||
msgstr "Показать тему"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97698
|
||||
msgid "Hide topic"
|
||||
msgstr ""
|
||||
msgstr "Скрыть тему"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97699
|
||||
msgid "Show the topic message in the heading"
|
||||
msgstr ""
|
||||
msgstr "Показывать темы сообщения в заголовке"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97699
|
||||
msgid "Hide the topic in the heading"
|
||||
msgstr ""
|
||||
msgstr "Скрыть тему в заголовке"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97716
|
||||
#, fuzzy
|
||||
msgid "Moderate"
|
||||
msgstr "Модерируемая"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97717
|
||||
#, fuzzy
|
||||
msgid "Moderate this groupchat"
|
||||
msgstr "Покинуть эту комнату"
|
||||
msgstr "Модерировать этот групповой чат"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97729
|
||||
#, fuzzy
|
||||
msgid "Destroy"
|
||||
msgstr "Временный чат"
|
||||
msgstr "Уничтожить"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97743
|
||||
msgid "Leave"
|
||||
msgstr ""
|
||||
msgstr "Покинуть"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97744
|
||||
#, fuzzy
|
||||
msgid "Leave and close this groupchat"
|
||||
msgstr "Покинуть эту комнату"
|
||||
msgstr "Выйти и закрыть этот групповой чат"
|
||||
|
||||
#: dist/converse-no-dependencies.js:97753
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to leave this groupchat?"
|
||||
msgstr "Вы уверены, что хотите покинуть комнату \"%1$s\"?"
|
||||
msgstr "Вы уверены, что хотите покинуть этот групповой чат?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:98120
|
||||
msgid "This user is a moderator."
|
||||
|
@ -1794,9 +1761,8 @@ msgid "Participants"
|
|||
msgstr "Участники"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99003
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to destroy this groupchat?"
|
||||
msgstr "Вы уверены, что хотите покинуть комнату \"%1$s\"?"
|
||||
msgstr "Вы уверены, что хотите уничтожить этот групповой чат?"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99006
|
||||
msgid "Please enter the XMPP address of this groupchat to confirm"
|
||||
|
@ -1814,12 +1780,11 @@ msgstr "Необязательная причина для удаления эт
|
|||
|
||||
#: dist/converse-no-dependencies.js:99016
|
||||
msgid "Optional XMPP address for a new groupchat that replaces this one"
|
||||
msgstr ""
|
||||
msgstr "Дополнительный XMPP адрес для нового группового чата, заменяющего этот"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99017
|
||||
#, fuzzy
|
||||
msgid "replacement@example.org"
|
||||
msgstr "например, name@example.org"
|
||||
msgstr "replace@example.org"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99575
|
||||
msgid "has gone offline"
|
||||
|
@ -1852,7 +1817,6 @@ msgid "%1$s says"
|
|||
msgstr "%1$s говорит"
|
||||
|
||||
#: dist/converse-no-dependencies.js:99649
|
||||
#, fuzzy
|
||||
msgid "Encrypted message received"
|
||||
msgstr "Получено зашифрованное сообщение"
|
||||
|
||||
|
@ -1909,9 +1873,8 @@ msgid "Device without a fingerprint"
|
|||
msgstr "Устройство без отпечатка"
|
||||
|
||||
#: dist/converse-no-dependencies.js:100394
|
||||
#, fuzzy
|
||||
msgid "Checkbox for selecting the following device"
|
||||
msgstr "Флаг для выбора следующих отпечатков"
|
||||
msgstr "Установите флажок для выбора следующего устройства"
|
||||
|
||||
#: dist/converse-no-dependencies.js:100404
|
||||
msgid "Other OMEMO-enabled devices"
|
||||
|
@ -1960,12 +1923,11 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:100447
|
||||
msgid "OMEMO"
|
||||
msgstr ""
|
||||
msgstr "OMEMO"
|
||||
|
||||
#: dist/converse-no-dependencies.js:100449
|
||||
#, fuzzy
|
||||
msgid "Profile"
|
||||
msgstr "Ваш профиль"
|
||||
msgstr "Профиль"
|
||||
|
||||
#: dist/converse-no-dependencies.js:100556
|
||||
msgid "Sorry, an error happened while trying to save your profile data."
|
||||
|
@ -1982,13 +1944,12 @@ msgstr "Подробнее"
|
|||
|
||||
#: dist/converse-no-dependencies.js:100651
|
||||
msgid "Commands"
|
||||
msgstr ""
|
||||
msgstr "Команды"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:100657
|
||||
#, fuzzy
|
||||
msgid "Settings"
|
||||
msgstr "Изменить настройки"
|
||||
msgstr "Настройки"
|
||||
|
||||
#: dist/converse-no-dependencies.js:100659
|
||||
#, javascript-format
|
||||
|
@ -2051,11 +2012,12 @@ msgid "Sorry, could not decrypt a received OMEMO message due to an error."
|
|||
msgstr "К сожалению, не удалось расшифровать OMEMO сообщение."
|
||||
|
||||
#: dist/converse-no-dependencies.js:101694
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Sorry, could not decrypt a received OMEMO because we don't have the JID for "
|
||||
"that user."
|
||||
msgstr "К сожалению, не удалось расшифровать OMEMO сообщение."
|
||||
msgstr ""
|
||||
"К сожалению, не удалось расшифровать полученный OMEMO, потому что у нас нет "
|
||||
"JID для этого пользователя."
|
||||
|
||||
#: dist/converse-no-dependencies.js:102242
|
||||
#, javascript-format
|
||||
|
@ -2067,13 +2029,13 @@ msgstr ""
|
|||
"поддерживается в данной комнате."
|
||||
|
||||
#: dist/converse-no-dependencies.js:102323
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Cannot use end-to-end encryption in this groupchat, either the groupchat has "
|
||||
"some anonymity or not all participants support OMEMO."
|
||||
msgstr ""
|
||||
"Невозможно использовать сквозное шифрование в этой комнате, комната частично "
|
||||
"анонимна или не все участники имеют поддержку шифрования OMEMO."
|
||||
"Невозможно использовать end-to-end encryption шифрование в этой комнате, "
|
||||
"комната частично анонимна или не все участники имеют поддержку шифрования "
|
||||
"OMEMO."
|
||||
|
||||
#: dist/converse-no-dependencies.js:102325
|
||||
#, javascript-format
|
||||
|
@ -2089,15 +2051,16 @@ msgid "Messages are being sent in plaintext"
|
|||
msgstr "Сообщения отправляются в виде открытого текста"
|
||||
|
||||
#: dist/converse-no-dependencies.js:102346
|
||||
#, fuzzy
|
||||
msgid "Messages are sent encrypted"
|
||||
msgstr "Ваши сообщения больше не шифруются"
|
||||
msgstr "Сообщения отправляются в зашифрованном виде"
|
||||
|
||||
#: dist/converse-no-dependencies.js:102350
|
||||
msgid ""
|
||||
"This groupchat needs to be members-only and non-anonymous in order to "
|
||||
"support OMEMO encrypted messages"
|
||||
msgstr ""
|
||||
"Чтобы поддерживать зашифрованные сообщения OMEMO, этот групповой чат должен "
|
||||
"быть для пользователей и не анонимным"
|
||||
|
||||
#: dist/converse-no-dependencies.js:102379
|
||||
#, javascript-format
|
||||
|
@ -2137,12 +2100,11 @@ msgstr ""
|
|||
"расшифрованы на этом девайсе."
|
||||
|
||||
#: dist/converse-no-dependencies.js:102629
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Sorry, no devices found to which we can send an OMEMO encrypted message."
|
||||
msgstr ""
|
||||
"Извините, мы не смогли найти ни одно устройство которому можно отправить "
|
||||
"зашифрованное сообщение."
|
||||
"К сожалению, не найдено устройств, на которые можно отправить зашифрованное "
|
||||
"сообщение OMEMO."
|
||||
|
||||
#: dist/converse-no-dependencies.js:102732
|
||||
msgid ""
|
||||
|
@ -2417,9 +2379,9 @@ msgstr "Нажми что-бы удалить %1$s как контакт"
|
|||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:107574
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Click to chat with %1$s (XMPP address: %2$s)"
|
||||
msgstr "Нажмите для чата с %1$s (Идентификатор Jabber: %2$s)"
|
||||
msgstr "Нажмите, чтобы начать чат с %1$s (адрес XMPP: %2$s)"
|
||||
|
||||
#: dist/converse-no-dependencies.js:107839
|
||||
#, javascript-format
|
||||
|
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 6.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 10:11+0100\n"
|
||||
"PO-Revision-Date: 2021-04-09 13:32+0200\n"
|
||||
"PO-Revision-Date: 2021-06-25 10:43+0200\n"
|
||||
"Last-Translator: Kim Alvefur <zash@zash.se>\n"
|
||||
"Language-Team: Swedish <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/sv/>\n"
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.5.2-dev\n"
|
||||
"X-Generator: Weblate 4.7-dev\n"
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:42989
|
||||
|
@ -83,21 +83,6 @@ msgstr "Du har inte behörighet att skicka ett meddelande."
|
|||
msgid "Sorry, an error occurred while trying to send your message."
|
||||
msgstr "Ledsen, ett fel uppstod när meddelandet skulle skickas."
|
||||
|
||||
#: dist/converse-no-dependencies.js:51955
|
||||
#, javascript-format
|
||||
msgid "%1$s has gone offline"
|
||||
msgstr ""
|
||||
|
||||
#: dist/converse-no-dependencies.js:51959
|
||||
#, javascript-format
|
||||
msgid "%1$s is busy"
|
||||
msgstr ""
|
||||
|
||||
#: dist/converse-no-dependencies.js:51961
|
||||
#, javascript-format
|
||||
msgid "%1$s is online"
|
||||
msgstr ""
|
||||
|
||||
#: dist/converse-no-dependencies.js:52840
|
||||
#: dist/converse-no-dependencies.js:52863
|
||||
msgid "Sorry, looks like file upload is not supported by your server."
|
||||
|
@ -1045,7 +1030,7 @@ msgstr "Visa denna meny"
|
|||
#: dist/converse-no-dependencies.js:84722
|
||||
#, javascript-format
|
||||
msgid "%1$s has gone offline"
|
||||
msgstr "%1$s kopplade ifrån"
|
||||
msgstr "%1$s har kopplat ner"
|
||||
|
||||
#: dist/converse-no-dependencies.js:84726
|
||||
#, javascript-format
|
||||
|
@ -1055,7 +1040,7 @@ msgstr "%1$s är upptagen"
|
|||
#: dist/converse-no-dependencies.js:84728
|
||||
#, javascript-format
|
||||
msgid "%1$s is online"
|
||||
msgstr "%1$s är uppkopplad"
|
||||
msgstr "%1$s är online"
|
||||
|
||||
#: dist/converse-no-dependencies.js:87546
|
||||
msgid ""
|
||||
|
|
|
@ -8,7 +8,7 @@ msgstr ""
|
|||
"Project-Id-Version: Converse.js 3.3.2\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-17 12:35+0100\n"
|
||||
"PO-Revision-Date: 2020-12-16 10:29+0000\n"
|
||||
"PO-Revision-Date: 2021-04-10 06:04+0000\n"
|
||||
"Last-Translator: Oğuz Ersen <oguzersen@protonmail.com>\n"
|
||||
"Language-Team: Turkish <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/tr/>\n"
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.4-dev\n"
|
||||
"X-Generator: Weblate 4.6-dev\n"
|
||||
|
||||
#. Strophe
|
||||
#: dist/converse-no-dependencies.js:42989
|
||||
|
@ -727,7 +727,6 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:76031
|
||||
#: dist/converse-no-dependencies.js:76122
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Be aware that other XMPP/Jabber clients (and servers) may not yet support "
|
||||
"retractions and that this message may not be removed everywhere."
|
||||
|
@ -739,7 +738,6 @@ msgstr ""
|
|||
#: dist/converse-no-dependencies.js:76032
|
||||
#: dist/converse-no-dependencies.js:76129
|
||||
#: dist/converse-no-dependencies.js:76166
|
||||
#, fuzzy
|
||||
msgid "Are you sure you want to retract this message?"
|
||||
msgstr "Bu mesajı geri çekmek istediğinizden emin misiniz?"
|
||||
|
||||
|
@ -760,12 +758,10 @@ msgid "Sorry, you're not allowed to retract this message."
|
|||
msgstr "Üzgünüm, bu mesajı geri çekmenize izin verilmiyor."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid "You are about to retract this message."
|
||||
msgstr "Bu mesajı geri çekmek üzeresiniz."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76190
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"You may optionally include a message, explaining the reason for the "
|
||||
"retraction."
|
||||
|
@ -774,18 +770,16 @@ msgstr ""
|
|||
"ekleyebilirsiniz."
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
#, fuzzy
|
||||
msgid "Message Retraction"
|
||||
msgstr "Mesaj versiyonları"
|
||||
msgstr "Mesaj Geri Çekme"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76197
|
||||
msgid "Optional reason"
|
||||
msgstr "İsteğe bağlı neden"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76208
|
||||
#, fuzzy
|
||||
msgid "Sorry, you're not allowed to retract this message"
|
||||
msgstr "Üzgünüm, bu mesajı geri çekmenize izin verilmiyor."
|
||||
msgstr "Üzgünüm, bu mesajı geri çekmenize izin verilmiyor"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76270
|
||||
msgid "Cancel Editing"
|
||||
|
@ -801,19 +795,19 @@ msgstr "Geri çek"
|
|||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Show URL previews"
|
||||
msgstr ""
|
||||
msgstr "URL ön izlemelerini göster"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76316
|
||||
msgid "Hide URL previews"
|
||||
msgstr ""
|
||||
msgstr "URL ön izlemelerini gizle"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Show URL preview"
|
||||
msgstr ""
|
||||
msgstr "URL ön izlemesini göster"
|
||||
|
||||
#: dist/converse-no-dependencies.js:76318
|
||||
msgid "Hide URL preview"
|
||||
msgstr ""
|
||||
msgstr "URL ön izlemesini gizle"
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:76429
|
||||
|
@ -993,7 +987,7 @@ msgstr "Bu kişi hakkında daha fazla bilgi göster"
|
|||
|
||||
#: dist/converse-no-dependencies.js:82976
|
||||
msgid "Close and end this conversation"
|
||||
msgstr "Bu sohbeti kapat ve sonlandır"
|
||||
msgstr "Bu konuşmayı kapat ve sonlandır"
|
||||
|
||||
#: dist/converse-no-dependencies.js:83478
|
||||
msgid "Hidden message"
|
||||
|
@ -1204,7 +1198,7 @@ msgstr ""
|
|||
|
||||
#: dist/converse-no-dependencies.js:94078
|
||||
msgid "Loading configuration form"
|
||||
msgstr ""
|
||||
msgstr "Yapılandırma formu yükleniyor"
|
||||
|
||||
#: dist/converse-no-dependencies.js:94373
|
||||
msgid "Sorry, an error occurred while trying to submit the config form."
|
||||
|
@ -1460,10 +1454,10 @@ msgid "Allow muted user to post messages"
|
|||
msgstr "Sessiz kullanıcının mesaj göndermesine izin ver"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96345
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"The conversation has moved to a new address. Click the link below to enter."
|
||||
msgstr "Konuşma taşındı. Girmek için aşağıya tıklayın."
|
||||
msgstr ""
|
||||
"Konuşma yeni bir adrese taşındı. Girmek için aşağıdaki bağlantıya tıklayın."
|
||||
|
||||
#. harmony default export
|
||||
#: dist/converse-no-dependencies.js:96353
|
||||
|
@ -1471,9 +1465,9 @@ msgid "This groupchat no longer exists"
|
|||
msgstr "Bu grup sohbeti artık mevcut değil"
|
||||
|
||||
#: dist/converse-no-dependencies.js:96355
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "The following reason was given: \"%1$s\""
|
||||
msgstr "Verilen sebep: \"%1$s\"."
|
||||
msgstr "Şu neden verildi: \"%1$s\""
|
||||
|
||||
#: dist/converse-no-dependencies.js:96851
|
||||
#, javascript-format
|
||||
|
|
|
@ -16,7 +16,7 @@ const subject = (o) => {
|
|||
|
||||
|
||||
export default (o) => {
|
||||
const i18n_address = __('Groupchat address (JID)');
|
||||
const i18n_address = __('Groupchat XMPP address');
|
||||
const i18n_archiving = __('Message archiving');
|
||||
const i18n_archiving_help = __('Messages are archived on the server');
|
||||
const i18n_desc = __('Description');
|
||||
|
|
|
@ -91,7 +91,7 @@ export default (o) => {
|
|||
const heading_profile = __('Your Profile');
|
||||
const i18n_email = __('Email');
|
||||
const i18n_fullname = __('Full Name');
|
||||
const i18n_jid = __('XMPP Address (JID)');
|
||||
const i18n_jid = __('XMPP Address');
|
||||
const i18n_nickname = __('Nickname');
|
||||
const i18n_role = __('Role');
|
||||
const i18n_save = __('Save and close');
|
||||
|
|
|
@ -6,7 +6,7 @@ const { Strophe, u, sizzle, $iq } = converse.env;
|
|||
describe("A chat room", function () {
|
||||
|
||||
it("can be bookmarked", mock.initConverse(
|
||||
['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
|
@ -14,9 +14,8 @@ describe("A chat room", function () {
|
|||
[{'category': 'pubsub', 'type': 'pep'}],
|
||||
['http://jabber.org/protocol/pubsub#publish-options']
|
||||
);
|
||||
const { u, $iq } = converse.env;
|
||||
spyOn(_converse.connection, 'getUniqueId').and.callThrough();
|
||||
|
||||
const { u, $iq } = converse.env;
|
||||
const nick = 'JC';
|
||||
const muc_jid = 'theplay@conference.shakespeare.lit';
|
||||
await mock.openChatRoom(_converse, 'theplay', 'conference.shakespeare.lit', 'JC');
|
||||
|
@ -37,6 +36,7 @@ describe("A chat room", function () {
|
|||
cancel_button.click();
|
||||
|
||||
await u.waitUntil(() => view.model.session.get('view') === null);
|
||||
|
||||
expect(u.hasClass('on-button', toggle), false);
|
||||
expect(toggle.title).toBe('Bookmark this groupchat');
|
||||
|
||||
|
@ -128,12 +128,11 @@ describe("A chat room", function () {
|
|||
expect(u.hasClass('on-button', view.querySelector('.toggle-bookmark')), true);
|
||||
// We ignore this IQ stanza... (unless it's an error stanza), so
|
||||
// nothing to test for here.
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const { u } = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -172,13 +171,12 @@ describe("A chat room", function () {
|
|||
'nick': ' Othello'
|
||||
});
|
||||
expect(_converse.chatboxviews.get(jid) === undefined).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
describe("when bookmarked", function () {
|
||||
|
||||
it("will use the nickname from the bookmark", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("will use the nickname from the bookmark", mock.initConverse([], {}, async function (_converse) {
|
||||
const { u } = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.waitUntilBookmarksReturned(_converse);
|
||||
|
@ -195,10 +193,9 @@ describe("A chat room", function () {
|
|||
const room = await room_creation_promise;
|
||||
await u.waitUntil(() => room.getAndPersistNickname.calls.count());
|
||||
expect(room.get('nick')).toBe('Othello');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("displays that it's bookmarked through its bookmark icon", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("displays that it's bookmarked through its bookmark icon", mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const { u } = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -220,10 +217,9 @@ describe("A chat room", function () {
|
|||
await u.waitUntil(() => view.querySelector('.chatbox-title__text .fa-bookmark') !== null);
|
||||
view.model.set('bookmarked', false);
|
||||
await u.waitUntil(() => view.querySelector('.chatbox-title__text .fa-bookmark') === null);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be unbookmarked", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("can be unbookmarked", mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const { u, Strophe } = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -282,14 +278,13 @@ describe("A chat room", function () {
|
|||
`</pubsub>`+
|
||||
`</iq>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("and when autojoin is set", function () {
|
||||
|
||||
it("will be be opened and joined automatically upon login", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
[], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.waitUntilBookmarksReturned(_converse);
|
||||
|
@ -310,7 +305,6 @@ describe("A chat room", function () {
|
|||
'nick': ''
|
||||
});
|
||||
expect(_converse.api.rooms.create).toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -318,7 +312,7 @@ describe("A chat room", function () {
|
|||
describe("Bookmarks", function () {
|
||||
|
||||
it("can be pushed from the XMPP server", mock.initConverse(
|
||||
['connected', 'chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
['connected', 'chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const { $msg, u } = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -403,13 +397,12 @@ describe("Bookmarks", function () {
|
|||
expect(_converse.bookmarks.map(b => b.get('name'))).toEqual(['Second bookmark', 'The Play's the Thing', 'Yet another bookmark']);
|
||||
expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
|
||||
expect(Object.keys(_converse.chatboxviews.getAll()).length).toBe(2);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("can be retrieved from the XMPP server", mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const { Strophe, sizzle, u, $iq } = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -483,13 +476,12 @@ describe("Bookmarks", function () {
|
|||
expect(_converse.bookmarks.models.length).toBe(2);
|
||||
expect(_converse.bookmarks.findWhere({'jid': 'theplay@conference.shakespeare.lit'}).get('autojoin')).toBe(true);
|
||||
expect(_converse.bookmarks.findWhere({'jid': 'another@conference.shakespeare.lit'}).get('autojoin')).toBe(false);
|
||||
done();
|
||||
}));
|
||||
|
||||
describe("The bookmarks list", function () {
|
||||
|
||||
it("shows a list of bookmarks", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
[], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.bare_jid,
|
||||
|
@ -560,11 +552,10 @@ describe("Bookmarks", function () {
|
|||
expect(els[1].textContent).toBe("Bookmark with a very very long name that will be shortened");
|
||||
expect(els[2].textContent).toBe("noname@conference.shakespeare.lit");
|
||||
expect(els[3].textContent).toBe("The Play's the Thing");
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to open a MUC from a bookmark", mock.initConverse(
|
||||
[], {'view_mode': 'fullscreen'}, async function (done, _converse) {
|
||||
[], {'view_mode': 'fullscreen'}, async function (_converse) {
|
||||
|
||||
const api = _converse.api;
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
|
@ -610,11 +601,10 @@ describe("Bookmarks", function () {
|
|||
await u.waitUntil(() => view.querySelector('.list-item.open').getAttribute('data-room-jid') === 'first@conference.shakespeare.lit');
|
||||
expect((await api.rooms.get('first@conference.shakespeare.lit')).get('hidden')).toBe(false);
|
||||
expect((await api.rooms.get('theplay@conference.shakespeare.lit')).get('hidden')).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("remembers the toggle state of the bookmarks list", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
[], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -665,7 +655,6 @@ describe("Bookmarks", function () {
|
|||
expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', chats_el).pop())).toBeFalsy();
|
||||
expect(sizzle(selector, chats_el).filter(u.isVisible).length).toBe(1);
|
||||
expect(bookmarks_el.model.get('toggle-state')).toBe(_converse.OPENED);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -673,7 +662,7 @@ describe("Bookmarks", function () {
|
|||
describe("When hide_open_bookmarks is true and a bookmarked room is opened", function () {
|
||||
|
||||
it("can be closed", mock.initConverse(
|
||||
[], { hide_open_bookmarks: true }, async function (done, _converse) {
|
||||
[], { hide_open_bookmarks: true }, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -704,6 +693,5 @@ describe("When hide_open_bookmarks is true and a bookmarked room is opened", fun
|
|||
const view = _converse.chatboxviews.get(jid);
|
||||
view.close();
|
||||
await u.waitUntil(() => !u.hasClass('hidden', bookmarks_el.querySelector(".available-chatroom")));
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -15,17 +15,26 @@ export default class ChatBottomPanel extends ElementView {
|
|||
'click .toggle-clear': 'clearMessages'
|
||||
};
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
this.debouncedRender = debounce(this.render, 100);
|
||||
}
|
||||
|
||||
async connectedCallback () {
|
||||
super.connectedCallback();
|
||||
this.debouncedRender = debounce(this.render, 100);
|
||||
this.model = _converse.chatboxes.get(this.getAttribute('jid'));
|
||||
await this.initialize();
|
||||
this.render(); // don't call in initialize, since the MUCBottomPanel subclasses it
|
||||
// and we want to render after it has finished as wel.
|
||||
}
|
||||
|
||||
async initialize () {
|
||||
this.model = await api.chatboxes.get(this.getAttribute('jid'));
|
||||
await this.model.initialized;
|
||||
this.listenTo(this.model, 'change:num_unread', this.debouncedRender)
|
||||
this.listenTo(this.model, 'emoji-picker-autocomplete', this.autocompleteInPicker);
|
||||
|
||||
this.addEventListener('focusin', ev => this.emitFocused(ev));
|
||||
this.addEventListener('focusout', ev => this.emitBlurred(ev));
|
||||
this.render();
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -52,10 +61,6 @@ export default class ChatBottomPanel extends ElementView {
|
|||
_converse.chatboxviews.get(this.getAttribute('jid'))?.emitBlurred(ev);
|
||||
}
|
||||
|
||||
getToolbarOptions () { // eslint-disable-line class-methods-use-this
|
||||
return {};
|
||||
}
|
||||
|
||||
onDrop (evt) {
|
||||
if (evt.dataTransfer.files.length == 0) {
|
||||
// There are no files to be dropped, so this isn’t a file
|
||||
|
|
|
@ -61,6 +61,7 @@ export default class ChatView extends BaseChatView {
|
|||
|
||||
afterShown () {
|
||||
this.model.setChatState(_converse.ACTIVE);
|
||||
this.model.clearUnreadMsgCounter();
|
||||
this.maybeFocus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ export default class MessageForm extends ElementView {
|
|||
this.querySelector('converse-emoji-dropdown')?.hideMenu();
|
||||
|
||||
const is_command = this.parseMessageForCommands(message_text);
|
||||
const message = is_command ? null : await this.model.sendMessage(message_text, spoiler_hint);
|
||||
const message = is_command ? null : await this.model.sendMessage({'body': message_text, spoiler_hint});
|
||||
if (is_command || message) {
|
||||
hint_el.value = '';
|
||||
textarea.value = '';
|
||||
|
|
|
@ -185,9 +185,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
video {
|
||||
width: 100%
|
||||
}
|
||||
progress {
|
||||
margin: 0.5em 0;
|
||||
width: 100%
|
||||
|
|
|
@ -35,7 +35,7 @@ export default (o) => {
|
|||
const display_name = o.model.getDisplayName();
|
||||
|
||||
const tpl_dropdown_btns = () => getDropdownButtons(o.heading_buttons_promise)
|
||||
.then(btns => btns.length ? html`<converse-dropdown .items=${btns}></converse-dropdown>` : '');
|
||||
.then(btns => btns.length ? html`<converse-dropdown class="dropleft" .items=${btns}></converse-dropdown>` : '');
|
||||
|
||||
const tpl_standalone_btns = () => getStandaloneButtons(o.heading_buttons_promise)
|
||||
.then(btns => btns.reverse().map(b => until(b, '')));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*global mock, converse, _ */
|
||||
/*global mock, converse */
|
||||
|
||||
const $msg = converse.env.$msg;
|
||||
const Strophe = converse.env.Strophe;
|
||||
|
@ -13,7 +13,7 @@ describe("Chatboxes", function () {
|
|||
|
||||
describe("A Chatbox", function () {
|
||||
|
||||
it("has a /help command to show the available commands", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("has a /help command to show the available commands", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -40,11 +40,10 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length);
|
||||
const msg_txt_sel = 'converse-chat-message:last-child .chat-msg__body';
|
||||
await u.waitUntil(() => view.querySelector(msg_txt_sel).textContent.trim() === 'hello world');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("has a /clear command", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("has a /clear command", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -67,12 +66,11 @@ describe("Chatboxes", function () {
|
|||
});
|
||||
expect(window.confirm).toHaveBeenCalled();
|
||||
await u.waitUntil(() => sizzle('converse-chat-message', view).length === 0);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("is created when you click on a roster item", mock.initConverse(
|
||||
['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -98,12 +96,11 @@ describe("Chatboxes", function () {
|
|||
// Check that new chat boxes are created to the left of the
|
||||
// controlbox (but to the right of all existing chat boxes)
|
||||
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(3);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("opens when a new message is received", mock.initConverse(
|
||||
[], {'allow_non_roster_messaging': true},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -120,10 +117,9 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => message_promise);
|
||||
expect(_converse.chatboxviews.keys().length).toBe(2);
|
||||
expect(_converse.chatboxviews.keys().pop()).toBe(sender_jid);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("doesn't open when a message without body is received", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("doesn't open when a message without body is received", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const stanza = u.toStanza(`
|
||||
|
@ -136,11 +132,10 @@ describe("Chatboxes", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await u.waitUntil(() => message_promise);
|
||||
expect(_converse.chatboxviews.keys().length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is focused if its already open and you click on its corresponding roster item",
|
||||
mock.initConverse(['chatBoxesFetched'], {'auto_focus': true}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'auto_focus': true}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -159,11 +154,10 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => view.focus.calls.count(), 1000);
|
||||
expect(view.focus).toHaveBeenCalled();
|
||||
expect(_converse.chatboxes.length).toEqual(2);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be saved to, and retrieved from, browserStorage",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
spyOn(_converse.minimize, 'trimChats');
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
|
@ -188,15 +182,14 @@ describe("Chatboxes", function () {
|
|||
const attrs = ['id', 'box_id', 'visible'];
|
||||
let new_attrs, old_attrs;
|
||||
for (let i=0; i<attrs.length; i++) {
|
||||
new_attrs = _.map(_.map(newchatboxes.models, 'attributes'), attrs[i]);
|
||||
old_attrs = _.map(_.map(_converse.chatboxes.models, 'attributes'), attrs[i]);
|
||||
expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
|
||||
new_attrs = newchatboxes.models.map(m => m.attributes[i]);
|
||||
old_attrs = _converse.chatboxes.models.map(m => m.attributes[i]);
|
||||
expect(new_attrs).toEqual(old_attrs);
|
||||
}
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be closed by clicking a DOM element with class 'close-chatbox-button'",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -211,11 +204,10 @@ describe("Chatboxes", function () {
|
|||
expect(chatview.model.close).toHaveBeenCalled();
|
||||
await new Promise(resolve => _converse.api.listen.once('chatBoxClosed', resolve));
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will be removed from browserStorage when closed",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -248,13 +240,12 @@ describe("Chatboxes", function () {
|
|||
await new Promise(resolve => _converse.api.listen.on('chatBoxesFetched', resolve));
|
||||
expect(newchatboxes.length).toEqual(1);
|
||||
expect(newchatboxes.models[0].id).toBe("controlbox");
|
||||
done();
|
||||
}));
|
||||
|
||||
describe("A chat toolbar", function () {
|
||||
|
||||
it("shows the remaining character count if a message_limit is configured",
|
||||
mock.initConverse(['chatBoxesFetched'], {'message_limit': 200}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'message_limit': 200}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 3);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -288,12 +279,11 @@ describe("Chatboxes", function () {
|
|||
textarea.value = 'hello world';
|
||||
message_form.onKeyUp(ev);
|
||||
await u.waitUntil(() => counter.textContent === '189');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("does not show a remaining character count if message_limit is zero",
|
||||
mock.initConverse(['chatBoxesFetched'], {'message_limit': 0}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'message_limit': 0}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 3);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -302,12 +292,11 @@ describe("Chatboxes", function () {
|
|||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const counter = view.querySelector('.chat-toolbar .message-limit');
|
||||
expect(counter).toBe(null);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("can contain a button for starting a call",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -333,13 +322,12 @@ describe("Chatboxes", function () {
|
|||
call_button = toolbar.querySelector('.toggle-call');
|
||||
call_button.click();
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('callButtonClicked', jasmine.any(Object));
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A Chat Status Notification", function () {
|
||||
|
||||
it("does not open a new chatbox", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("does not open a new chatbox", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
||||
|
@ -357,13 +345,12 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => _converse.api.trigger.calls.count());
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
expect(_converse.chatboxviews.keys().length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
describe("An active notification", function () {
|
||||
|
||||
it("is sent when the user opens a chat box",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -381,11 +368,10 @@ describe("Chatboxes", function () {
|
|||
expect(stanza.childNodes[0].tagName).toBe('active');
|
||||
expect(stanza.childNodes[1].tagName).toBe('no-store');
|
||||
expect(stanza.childNodes[2].tagName).toBe('no-permanent-store');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is sent when the user maximizes a minimized a chat box", mock.initConverse(
|
||||
['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -409,14 +395,13 @@ describe("Chatboxes", function () {
|
|||
`<no-permanent-store xmlns="urn:xmpp:hints"/>`+
|
||||
`</message>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A composing notification", function () {
|
||||
|
||||
it("is sent as soon as the user starts typing a message which is not a command",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -452,12 +437,11 @@ describe("Chatboxes", function () {
|
|||
});
|
||||
expect(view.model.get('chat_state')).toBe('composing');
|
||||
expect(_converse.api.trigger.calls.count(), 1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is NOT sent out if send_chat_state_notifications doesn't allow it",
|
||||
mock.initConverse(['chatBoxesFetched'], {'send_chat_state_notifications': []},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -477,10 +461,9 @@ describe("Chatboxes", function () {
|
|||
});
|
||||
expect(view.model.get('chat_state')).toBe('composing');
|
||||
expect(_converse.connection.send).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will be shown if received", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("will be shown if received", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
||||
|
@ -525,11 +508,10 @@ describe("Chatboxes", function () {
|
|||
const msg_el = await u.waitUntil(() => view.querySelector('.chat-msg'));
|
||||
await u.waitUntil( () => view.querySelector('.chat-content__notifications').innerText === '');
|
||||
expect(msg_el.querySelector('.chat-msg__text').textContent).toBe('hello world');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is ignored if it's a composing carbon message sent by this user from a different client",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, 'montague.lit', [], ['vcard-temp']);
|
||||
await u.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'));
|
||||
|
@ -560,14 +542,13 @@ describe("Chatboxes", function () {
|
|||
expect(view.model.messages.length).toEqual(0);
|
||||
const el = view.querySelector('.chat-content__notifications');
|
||||
expect(el.textContent).toBe('');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A paused notification", function () {
|
||||
|
||||
it("is sent if the user has stopped typing since 30 seconds",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -624,10 +605,9 @@ describe("Chatboxes", function () {
|
|||
keyCode: 1
|
||||
});
|
||||
expect(view.model.get('chat_state')).toBe('composing');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will be shown if received", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("will be shown if received", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
const rosterview = document.querySelector('converse-roster');
|
||||
|
@ -649,11 +629,10 @@ describe("Chatboxes", function () {
|
|||
const csn = mock.cur_names[1] + ' has stopped typing';
|
||||
await u.waitUntil( () => view.querySelector('.chat-content__notifications').innerText === csn);
|
||||
expect(view.model.messages.length).toEqual(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will not be shown if it's a paused carbon message that this user sent from a different client",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, 'montague.lit', [], ['vcard-temp']);
|
||||
await u.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'));
|
||||
|
@ -681,15 +660,13 @@ describe("Chatboxes", function () {
|
|||
expect(view.model.messages.length).toEqual(0);
|
||||
const el = view.querySelector('.chat-content__notifications');
|
||||
expect(el.textContent).toBe('');
|
||||
done();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("An inactive notification", function () {
|
||||
|
||||
it("is sent if the user has stopped typing since 2 minutes",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||
// Make the timeouts shorter so that we can test
|
||||
|
@ -750,11 +727,10 @@ describe("Chatboxes", function () {
|
|||
`<no-permanent-store xmlns="urn:xmpp:hints"/>`+
|
||||
`</message>`);
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is sent when the user a minimizes a chat box",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -769,11 +745,10 @@ describe("Chatboxes", function () {
|
|||
var stanza = _converse.connection.send.calls.argsFor(0)[0];
|
||||
expect(stanza.getAttribute('to')).toBe(contact_jid);
|
||||
expect(stanza.childNodes[0].tagName).toBe('inactive');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is sent if the user closes a chat box",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -792,11 +767,10 @@ describe("Chatboxes", function () {
|
|||
expect(stanza.childNodes[0].tagName).toBe('inactive');
|
||||
expect(stanza.childNodes[1].tagName).toBe('no-store');
|
||||
expect(stanza.childNodes[2].tagName).toBe('no-permanent-store');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will clear any other chat status notifications",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -829,13 +803,12 @@ describe("Chatboxes", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(msg));
|
||||
|
||||
await u.waitUntil(() => !view.querySelector('.chat-content__notifications').textContent);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A gone notification", function () {
|
||||
|
||||
it("will be shown if received", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("will be shown if received", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current', 3);
|
||||
await mock.openControlBox(_converse);
|
||||
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -852,13 +825,12 @@ describe("Chatboxes", function () {
|
|||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
const csntext = await u.waitUntil(() => view.querySelector('.chat-content__notifications').textContent);
|
||||
expect(csntext).toEqual(mock.cur_names[1] + ' has gone away');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("On receiving a message correction", function () {
|
||||
|
||||
it("will be removed", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("will be removed", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
||||
|
@ -910,7 +882,6 @@ describe("Chatboxes", function () {
|
|||
|
||||
await _converse.handleMessageStanza(edited);
|
||||
await u.waitUntil(() => !view.querySelector('.chat-content__notifications').textContent);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -919,7 +890,7 @@ describe("Chatboxes", function () {
|
|||
describe("Special Messages", function () {
|
||||
|
||||
it("'/clear' can be used to clear messages in a conversation",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -948,155 +919,14 @@ describe("Chatboxes", function () {
|
|||
expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?');
|
||||
await u.waitUntil(() => view.model.messages.length === 0);
|
||||
await u.waitUntil(() => !view.querySelectorAll('.chat-msg__body').length);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A ChatBox's Unread Message Count", function () {
|
||||
|
||||
it("is incremented when the message is received and ChatBoxView is scrolled up",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
|
||||
msg = mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
|
||||
const view = await mock.openChatBoxFor(_converse, sender_jid)
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
view.model.ui.set('scrolled', true);
|
||||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.model.messages.length);
|
||||
expect(view.model.get('num_unread')).toBe(1);
|
||||
const msgid = view.model.messages.last().get('id');
|
||||
expect(view.model.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.length);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not incremented when the message is received and ChatBoxView is scrolled down",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msg = mock.createChatMessage(_converse, sender_jid, 'This message will be read');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
await _converse.handleMessageStanza(msg);
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 2);
|
||||
expect(sent_stanzas[1].querySelector('displayed')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is incremented when message is received, chatbox is scrolled down and the window is not focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = function () {
|
||||
return mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is incremented when message is received, chatbox is scrolled up and the window is not focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.ui.set('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 1);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is cleared when ChatBoxView was scrolled down and the window become focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 1);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
_converse.saveWindowState({'type': 'focus'});
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 2);
|
||||
expect(sent_stanzas[1].querySelector('displayed')).toBeDefined();
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.ui.set('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 1);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
_converse.saveWindowState({'type': 'focus'});
|
||||
await u.waitUntil(() => chatbox.get('num_unread') === 1);
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A RosterView's Unread Message Count", function () {
|
||||
|
||||
it("is updated when message is received and chatbox is scrolled up",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
let msg, indicator_el;
|
||||
|
@ -1117,11 +947,10 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => chatbox.messages.length > 1);
|
||||
indicator_el = sizzle(selector, rosterview).pop();
|
||||
expect(indicator_el.textContent).toBe('2');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is updated when message is received and chatbox is minimized",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -1145,11 +974,10 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => chatbox.messages.length === 2);
|
||||
indicator_el = sizzle(selector, rosterview).pop();
|
||||
expect(indicator_el.textContent).toBe('2');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -1170,11 +998,10 @@ describe("Chatboxes", function () {
|
|||
expect(select_msgs_indicator().textContent).toBe('2');
|
||||
_converse.minimize.maximize(view.model);
|
||||
u.waitUntil(() => typeof select_msgs_indicator() === 'undefined');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is cleared when unread messages are viewed which were received in scrolled-up chatbox",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.openControlBox(_converse);
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
|
@ -1194,11 +1021,10 @@ describe("Chatboxes", function () {
|
|||
const chat_new_msgs_indicator = await u.waitUntil(() => view.querySelector('.new-msgs-indicator'));
|
||||
chat_new_msgs_indicator.click();
|
||||
await u.waitUntil(() => select_msgs_indicator() === undefined);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -1217,7 +1043,6 @@ describe("Chatboxes", function () {
|
|||
expect(select_msgs_indicator().textContent).toBe('1');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
expect(select_msgs_indicator().textContent).toBe('1');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ const { Promise, $msg, Strophe, sizzle, u } = converse.env;
|
|||
describe("A Chat Message", function () {
|
||||
|
||||
it("can be sent as a correction by using the up arrow",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -161,12 +161,11 @@ describe("A Chat Message", function () {
|
|||
expect(view.model.messages.at(0).get('correcting')).toBeFalsy();
|
||||
expect(view.model.messages.at(1).get('correcting')).toBeFalsy();
|
||||
expect(view.model.messages.at(2).get('correcting')).toBeFalsy();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("can be sent as a correction by clicking the pencil icon",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -285,14 +284,13 @@ describe("A Chat Message", function () {
|
|||
['You have an unsent message which will be lost if you continue. Are you sure?']);
|
||||
expect(window.confirm.calls.argsFor(1)).toEqual(
|
||||
['You have an unsent message which will be lost if you continue. Are you sure?']);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
describe("when received from someone else", function () {
|
||||
|
||||
it("can be replaced with a correction",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -346,7 +344,6 @@ describe("A Chat Message", function () {
|
|||
expect(older_msgs.length).toBe(2);
|
||||
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
||||
expect(view.model.messages.models.length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ describe("Emojis", function () {
|
|||
afterEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = original_timeout));
|
||||
|
||||
it("can be opened by clicking a button in the chat toolbar",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
|
@ -25,14 +25,13 @@ describe("Emojis", function () {
|
|||
item.click()
|
||||
expect(view.querySelector('textarea.chat-textarea').value).toBe(':smiley: ');
|
||||
toolbar.querySelector('.toggle-emojis').click(); // Close the panel again
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A Chat Message", function () {
|
||||
|
||||
it("will display larger if it's only emojis",
|
||||
mock.initConverse(['chatBoxesFetched'], {'use_system_emojis': true}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'use_system_emojis': true}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -114,13 +113,12 @@ describe("Emojis", function () {
|
|||
|
||||
message = view.querySelector('.message:last-child .chat-msg__text');
|
||||
expect(u.hasClass('chat-msg__text--larger', message)).toBe(true);
|
||||
done()
|
||||
}));
|
||||
|
||||
it("can render emojis as images",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {'use_system_emojis': false},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const contact_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -163,7 +161,6 @@ describe("Emojis", function () {
|
|||
const sent_stanzas = _converse.connection.sent_stanzas;
|
||||
const sent_stanza = sent_stanzas.filter(s => s.nodeName === 'message').pop();
|
||||
expect(sent_stanza.querySelector('body').innerHTML).toBe('💩 😇');
|
||||
done()
|
||||
}));
|
||||
|
||||
it("can show custom emojis",
|
||||
|
@ -181,7 +178,7 @@ describe("Emojis", function () {
|
|||
"flags": ":flag_ac:",
|
||||
"custom": ':xmpp:'
|
||||
} },
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -208,7 +205,6 @@ describe("Emojis", function () {
|
|||
const body = view.querySelector('converse-chat-message-body');
|
||||
await u.waitUntil(() => body.innerHTML.replace(/<!-.*?->/g, '').trim() ===
|
||||
'Running tests for <img class="emoji" draggable="false" title=":converse:" alt=":converse:" src="/dist/images/custom_emojis/converse.png">');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
|
||||
describe("Discovering support", function () {
|
||||
|
||||
it("is done automatically", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("is done automatically", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], []);
|
||||
let selector = 'iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#info"]';
|
||||
|
@ -133,7 +133,6 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
expect(features.length).toBe(1);
|
||||
expect(features[0].get('jid')).toBe('upload.montague.lit');
|
||||
expect(features[0].dataforms.where({'FORM_TYPE': {value: "urn:xmpp:http:upload:0", type: "hidden"}}).length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -141,7 +140,7 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
describe("A file upload toolbar button", function () {
|
||||
|
||||
it("does not appear in private chats",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 3);
|
||||
mock.openControlBox(_converse);
|
||||
|
@ -155,7 +154,6 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
await mock.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], [], 'items');
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
expect(view.querySelector('.chat-toolbar .fileupload')).toBe(null);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -164,7 +162,7 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
|
||||
describe("A file upload toolbar button", function () {
|
||||
|
||||
it("appears in private chats", mock.initConverse(async (done, _converse) => {
|
||||
it("appears in private chats", mock.initConverse(async (_converse) => {
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.domain,
|
||||
[{'category': 'server', 'type':'IM'}],
|
||||
|
@ -178,12 +176,11 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const el = await u.waitUntil(() => view.querySelector('.chat-toolbar .fileupload'));
|
||||
expect(el).not.toEqual(null);
|
||||
done();
|
||||
}));
|
||||
|
||||
describe("when clicked and a file chosen", function () {
|
||||
|
||||
it("is uploaded and sent out", mock.initConverse(['chatBoxesFetched'], {} ,async (done, _converse) => {
|
||||
it("is uploaded and sent out", mock.initConverse(['chatBoxesFetched'], {} ,async (_converse) => {
|
||||
const base_url = 'https://conversejs.org';
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.domain,
|
||||
|
@ -280,13 +277,12 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
|
||||
expect(view.querySelector('.chat-msg .chat-msg__media').innerHTML.replace(/<!-.*?->/g, '').trim()).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
|
||||
`Download image file "conversejs-filled.svg"</a>`);
|
||||
`Download file "conversejs-filled.svg"</a>`);
|
||||
XMLHttpRequest.prototype.send = send_backup;
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows an error message if the file is too large",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||
const IQ_ids = _converse.connection.IQ_ids;
|
||||
|
@ -398,14 +394,13 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
expect(messages.length).toBe(1);
|
||||
expect(messages[0].textContent.trim()).toBe(
|
||||
'The size of your file, my-juliet.jpg, exceeds the maximum allowed by your server, which is 5 MB.');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("While a file is being uploaded", function () {
|
||||
|
||||
it("shows a progress bar", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("shows a progress bar", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.domain,
|
||||
[{'category': 'server', 'type':'IM'}],
|
||||
|
@ -457,6 +452,7 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
</slot>
|
||||
</iq>`);
|
||||
|
||||
const promise = u.getOpenPromise();
|
||||
spyOn(XMLHttpRequest.prototype, 'send').and.callFake(async () => {
|
||||
const message = view.model.messages.at(0);
|
||||
const el = await u.waitUntil(() => view.querySelector('.chat-content progress'));
|
||||
|
@ -466,9 +462,10 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
message.set('progress', 1);
|
||||
await u.waitUntil(() => view.querySelector('.chat-content progress').getAttribute('value') === '1');
|
||||
expect(view.querySelector('.chat-content .chat-msg__text').textContent).toBe('Uploading file: my-juliet.jpg, 22.91 KB');
|
||||
done();
|
||||
promise.resolve();
|
||||
});
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
return promise;
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ const u = converse.env.utils;
|
|||
describe("A XEP-0333 Chat Marker", function () {
|
||||
|
||||
it("is sent when a markable message is received from a roster contact",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -33,11 +33,10 @@ describe("A XEP-0333 Chat Marker", function () {
|
|||
`to="${contact_jid}" type="chat" xmlns="jabber:client">`+
|
||||
`<received id="${msgid}" xmlns="urn:xmpp:chat-markers:0"/>`+
|
||||
`</message>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not sent when a markable message is received from someone not on the roster",
|
||||
mock.initConverse([], {'allow_non_roster_messaging': true}, async function (done, _converse) {
|
||||
mock.initConverse([], {'allow_non_roster_messaging': true}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const contact_jid = 'someone@montague.lit';
|
||||
|
@ -66,11 +65,10 @@ describe("A XEP-0333 Chat Marker", function () {
|
|||
`<no-permanent-store xmlns="urn:xmpp:hints"/>`+
|
||||
`</message>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is ignored if it's a carbon copy of one that I sent from a different client",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], [Strophe.NS.SID]);
|
||||
|
@ -112,6 +110,5 @@ describe("A XEP-0333 Chat Marker", function () {
|
|||
await u.waitUntil(() => _converse.api.trigger.calls.count(), 500);
|
||||
expect(view.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
expect(view.model.messages.length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -2,62 +2,9 @@
|
|||
|
||||
const { u, sizzle, $msg } = converse.env;
|
||||
|
||||
describe("A Groupchat Message", function () {
|
||||
|
||||
it("supports the /me command", mock.initConverse([], {}, async function (done, _converse) {
|
||||
await mock.waitUntilDiscoConfirmed(_converse, 'montague.lit', [], ['vcard-temp']);
|
||||
await u.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'));
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
if (!view.querySelectorAll('.chat-area').length) {
|
||||
view.renderChatArea();
|
||||
}
|
||||
let message = '/me is tired';
|
||||
const nick = mock.chatroom_names[0];
|
||||
let msg = $msg({
|
||||
'from': 'lounge@montague.lit/'+nick,
|
||||
'id': u.getUniqueId(),
|
||||
'to': 'romeo@montague.lit',
|
||||
'type': 'groupchat'
|
||||
}).c('body').t(message).tree();
|
||||
await view.model.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => sizzle('.chat-msg:last .chat-msg__text', view).pop());
|
||||
await u.waitUntil(() => view.querySelector('.chat-msg__text').textContent.trim() === 'is tired');
|
||||
expect(view.querySelector('.chat-msg__author').textContent.includes('**Dyon van de Wege')).toBeTruthy();
|
||||
|
||||
message = '/me is as well';
|
||||
msg = $msg({
|
||||
from: 'lounge@montague.lit/Romeo Montague',
|
||||
id: u.getUniqueId(),
|
||||
to: 'romeo@montague.lit',
|
||||
type: 'groupchat'
|
||||
}).c('body').t(message).tree();
|
||||
await view.model.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 2);
|
||||
await u.waitUntil(() => Array.from(view.querySelectorAll('.chat-msg__text')).pop().textContent.trim() === 'is as well');
|
||||
expect(sizzle('.chat-msg__author:last', view).pop().textContent.includes('**Romeo Montague')).toBeTruthy();
|
||||
|
||||
// Check rendering of a mention inside a me message
|
||||
const msg_text = "/me mentions romeo";
|
||||
msg = $msg({
|
||||
from: 'lounge@montague.lit/gibson',
|
||||
id: u.getUniqueId(),
|
||||
to: 'romeo@montague.lit',
|
||||
type: 'groupchat'
|
||||
}).c('body').t(msg_text).up()
|
||||
.c('reference', {'xmlns':'urn:xmpp:reference:0', 'begin':'13', 'end':'19', 'type':'mention', 'uri':'xmpp:romeo@montague.lit'}).nodeTree;
|
||||
await view.model.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 3);
|
||||
await u.waitUntil(() => sizzle('.chat-msg__text:last', view).pop().innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'mentions <span class="mention mention--self badge badge-info">romeo</span>');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A Message", function () {
|
||||
|
||||
it("supports the /me command", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("supports the /me command", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.waitUntilDiscoConfirmed(_converse, 'montague.lit', [], ['vcard-temp']);
|
||||
await u.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'));
|
||||
|
@ -105,6 +52,5 @@ describe("A Message", function () {
|
|||
|
||||
expect(sizzle('.chat-msg__text:last', view).pop().textContent).toBe('wrote a 3rd person message');
|
||||
expect(u.isVisible(sizzle('.chat-msg__author:last', view).pop())).toBeTruthy();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ describe("A Chat Message", function () {
|
|||
|
||||
it("will render audio files from their URLs",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const base_url = 'https://conversejs.org';
|
||||
const message = base_url+"/logo/audio.mp3";
|
||||
|
@ -20,6 +20,5 @@ describe("A Chat Message", function () {
|
|||
expect(msg.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="${message}"></audio>`+
|
||||
`<a target="_blank" rel="noopener" href="${message}">${message}</a>`);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ const { sizzle, u } = converse.env;
|
|||
|
||||
describe("A Chat Message", function () {
|
||||
|
||||
it("will render images from their URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("will render images from their URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const base_url = 'https://conversejs.org';
|
||||
let message = base_url+"/logo/conversejs-filled.svg";
|
||||
|
@ -49,11 +49,10 @@ describe("A Chat Message", function () {
|
|||
|
||||
// Check that the Imgur URL gets a .png attached to make it render
|
||||
await u.waitUntil(() => Array.from(view.querySelectorAll('.chat-content .chat-image')).pop().src.endsWith('png'), 1000);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will not render images if show_images_inline is false",
|
||||
mock.initConverse(['chatBoxesFetched'], {'show_images_inline': false}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'show_images_inline': false}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const base_url = 'https://conversejs.org';
|
||||
const message = base_url+"/logo/conversejs-filled.svg";
|
||||
|
@ -65,13 +64,12 @@ describe("A Chat Message", function () {
|
|||
const sel = '.chat-content .chat-msg:last .chat-msg__text';
|
||||
await u.waitUntil(() => sizzle(sel).pop().innerHTML.replace(/<!-.*?->/g, '').trim() === message);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render images from approved URLs only",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {'show_images_inline': ['conversejs.org']},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const base_url = 'https://conversejs.org';
|
||||
|
@ -88,13 +86,12 @@ describe("A Chat Message", function () {
|
|||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-msg').length === 2, 1000);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-image').length === 1, 1000)
|
||||
expect(view.querySelectorAll('.chat-content .chat-image').length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will fall back to rendering images as URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const base_url = 'https://conversejs.org';
|
||||
|
@ -109,7 +106,6 @@ describe("A Chat Message", function () {
|
|||
const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text').pop();
|
||||
await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '').trim() ==
|
||||
`<a target="_blank" rel="noopener" href="https://conversejs.org/logo/non-existing.svg">https://conversejs.org/logo/non-existing.svg</a>`, 1000);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will fall back to rendering URLs that match image_urls_regex as URLs",
|
||||
|
@ -118,7 +114,7 @@ describe("A Chat Message", function () {
|
|||
'show_images_inline': ['twimg.com'],
|
||||
'image_urls_regex': /^https?:\/\/(www.)?(pbs\.twimg\.com\/)/i
|
||||
},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const message = "https://pbs.twimg.com/media/string?format=jpg&name=small";
|
||||
|
@ -132,13 +128,12 @@ describe("A Chat Message", function () {
|
|||
const msg = view.querySelector('.chat-content .chat-msg .chat-msg__text');
|
||||
await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '').trim() ==
|
||||
`<a target="_blank" rel="noopener" href="https://pbs.twimg.com/media/string?format=jpg&name=small">https://pbs.twimg.com/media/string?format=jpg&name=small</a>`, 1000);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will respect a changed setting when re-rendered",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {'show_images_inline': true},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
|
@ -152,6 +147,32 @@ describe("A Chat Message", function () {
|
|||
view.querySelector('converse-chat-message').requestUpdate();
|
||||
await u.waitUntil(() => view.querySelector('converse-chat-message-body .chat-image') === null);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will allow the user to toggle visibility of rendered images",
|
||||
mock.initConverse(['chatBoxesFetched'], {'show_images_inline': true}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
// let message = "https://i.imgur.com/Py9ifJE.mp4";
|
||||
const base_url = 'https://conversejs.org';
|
||||
const message = base_url+"/logo/conversejs-filled.svg";
|
||||
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
await mock.sendMessage(view, message);
|
||||
const sel = '.chat-content .chat-msg:last .chat-msg__text';
|
||||
await u.waitUntil(() => sizzle(sel).pop().innerHTML.replace(/<!-.*?->/g, '').trim() === message);
|
||||
|
||||
const actions_el = view.querySelector('converse-message-actions');
|
||||
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
|
||||
await u.waitUntil(() => view.querySelector('converse-chat-message-body img'));
|
||||
|
||||
actions_el.querySelector('.chat-msg__action-hide-previews').click();
|
||||
await u.waitUntil(() => actions_el.textContent.includes('Show media'));
|
||||
await u.waitUntil(() => !view.querySelector('converse-chat-message-body img'));
|
||||
|
||||
expect(view.querySelector('converse-chat-message-body').innerHTML.replace(/<!-.*?->/g, '').trim())
|
||||
.toBe(`<a target="_blank" rel="noopener" href="${message}">${message}</a>`)
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
const { Strophe, sizzle, u } = converse.env;
|
||||
|
||||
describe("A Chat Message", function () {
|
||||
describe("A chat message containing video URLs", function () {
|
||||
|
||||
it("will render videos from their URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("will render videos from their URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
// let message = "https://i.imgur.com/Py9ifJE.mp4";
|
||||
const base_url = 'https://conversejs.org';
|
||||
|
@ -27,11 +27,10 @@ describe("A Chat Message", function () {
|
|||
expect(msg.innerHTML.replace(/<!-.*?->/g, '').trim()).toEqual(
|
||||
`<video controls="" preload="metadata" src="${Strophe.xmlescape(message)}"></video>`+
|
||||
`<a target="_blank" rel="noopener" href="${Strophe.xmlescape(message)}">${Strophe.xmlescape(message)}</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will not render videos if embed_videos is false",
|
||||
mock.initConverse(['chatBoxesFetched'], {'embed_videos': false}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'embed_videos': false}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
// let message = "https://i.imgur.com/Py9ifJE.mp4";
|
||||
const base_url = 'https://conversejs.org';
|
||||
|
@ -44,13 +43,12 @@ describe("A Chat Message", function () {
|
|||
const sel = '.chat-content .chat-msg:last .chat-msg__text';
|
||||
await u.waitUntil(() => sizzle(sel).pop().innerHTML.replace(/<!-.*?->/g, '').trim() === message);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render videos from approved URLs only",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {'embed_videos': ['conversejs.org']},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
let message = "https://i.imgur.com/Py9ifJE.mp4";
|
||||
|
@ -69,6 +67,32 @@ describe("A Chat Message", function () {
|
|||
expect(msg.innerHTML.replace(/<!-.*?->/g, '').trim()).toEqual(
|
||||
`<video controls="" preload="metadata" src="${message}"></video>`+
|
||||
`<a target="_blank" rel="noopener" href="${message}">${message}</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will allow the user to toggle visibility of rendered videos",
|
||||
mock.initConverse(['chatBoxesFetched'], {'embed_videos': true}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
// let message = "https://i.imgur.com/Py9ifJE.mp4";
|
||||
const base_url = 'https://conversejs.org';
|
||||
const message = base_url+"/logo/conversejs-filled.mp4";
|
||||
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
await mock.sendMessage(view, message);
|
||||
const sel = '.chat-content .chat-msg:last .chat-msg__text';
|
||||
await u.waitUntil(() => sizzle(sel).pop().innerHTML.replace(/<!-.*?->/g, '').trim() === message);
|
||||
|
||||
const actions_el = view.querySelector('converse-message-actions');
|
||||
await u.waitUntil(() => actions_el.textContent.includes('Hide media'));
|
||||
await u.waitUntil(() => view.querySelector('converse-chat-message-body video'));
|
||||
|
||||
actions_el.querySelector('.chat-msg__action-hide-previews').click();
|
||||
await u.waitUntil(() => actions_el.textContent.includes('Show media'));
|
||||
await u.waitUntil(() => !view.querySelector('converse-chat-message-body video'));
|
||||
|
||||
expect(view.querySelector('converse-chat-message-body').innerHTML.replace(/<!-.*?->/g, '').trim())
|
||||
.toBe(`<a target="_blank" rel="noopener" href="${message}">${message}</a>`)
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -7,15 +7,14 @@ describe("A Chat Message", function () {
|
|||
|
||||
it("will be demarcated if it's the first newly received message",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, contact_jid);
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
await _converse.handleMessageStanza(mock.createChatMessage(_converse, contact_jid, 'This message will be read'));
|
||||
const msg_el = await u.waitUntil(() => view.querySelector('converse-chat-message'));
|
||||
expect(msg_el.querySelector('.chat-msg__text').textContent).toBe('This message will be read');
|
||||
await u.waitUntil(() => view.querySelector('converse-chat-message .chat-msg__text')?.textContent === 'This message will be read');
|
||||
expect(view.model.get('num_unread')).toBe(0);
|
||||
|
||||
_converse.windowState = 'hidden';
|
||||
|
@ -26,16 +25,16 @@ describe("A Chat Message", function () {
|
|||
expect(view.model.get('first_unread_id')).toBe(view.model.messages.last().get('id'));
|
||||
|
||||
await u.waitUntil(() => view.querySelectorAll('converse-chat-message').length === 2);
|
||||
await u.waitUntil(() => view.querySelector('converse-chat-message:last-child .chat-msg__text')?.textContent === 'This message will be new');
|
||||
const last_msg_el = view.querySelector('converse-chat-message:last-child');
|
||||
expect(last_msg_el.firstElementChild?.textContent).toBe('New messages');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("is rejected if it's an unencapsulated forwarded message",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 2);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -74,11 +73,10 @@ describe("A Chat Message", function () {
|
|||
'</message>');
|
||||
models = await _converse.api.chats.get();
|
||||
expect(models.length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be received out of order, and will still be displayed in the right order",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -238,11 +236,10 @@ describe("A Chat Message", function () {
|
|||
expect(day.getAttribute('data-isodate')).toEqual(dayjs().startOf('day').toISOString());
|
||||
expect(day.nextElementSibling.querySelector('.chat-msg__text').textContent).toBe('latest message');
|
||||
expect(u.hasClass('chat-msg--followup', el)).toBe(false);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is ignored if it's a malformed headline message",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -250,7 +247,7 @@ describe("A Chat Message", function () {
|
|||
// Ideally we wouldn't have to filter out headline
|
||||
// messages, but Prosody gives them the wrong 'type' :(
|
||||
spyOn(converse.env.log, 'info');
|
||||
sinon.spy(_converse.api.chatboxes, 'get');
|
||||
spyOn(_converse.api.chatboxes, 'get');
|
||||
const msg = $msg({
|
||||
from: 'montague.lit',
|
||||
to: _converse.bare_jid,
|
||||
|
@ -261,15 +258,12 @@ describe("A Chat Message", function () {
|
|||
expect(converse.env.log.info).toHaveBeenCalledWith(
|
||||
"handleMessageStanza: Ignoring incoming server message from JID: montague.lit"
|
||||
);
|
||||
expect(_converse.api.chatboxes.get.called).toBeFalsy();
|
||||
// Remove sinon spies
|
||||
_converse.api.chatboxes.get.restore();
|
||||
done();
|
||||
expect(_converse.api.chatboxes.get).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it("can be a carbon message, as defined in XEP-0280",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const include_nick = false;
|
||||
await mock.waitForRoster(_converse, 'current', 2, include_nick);
|
||||
|
@ -314,11 +308,10 @@ describe("A Chat Message", function () {
|
|||
expect(view.querySelector('.chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
|
||||
await u.waitUntil(() => chatbox.vcard.get('fullname') === 'Juliet Capulet')
|
||||
expect(view.querySelector('span.chat-msg__author').textContent.trim()).toBe('Juliet Capulet');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be a carbon message that this user sent from a different client, as defined in XEP-0280",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, 'montague.lit', [], ['vcard-temp']);
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
|
@ -359,11 +352,10 @@ describe("A Chat Message", function () {
|
|||
// Now check that the message appears inside the chatbox in the DOM
|
||||
const msg_el = await u.waitUntil(() => view.querySelector('.chat-content .chat-msg .chat-msg__text'));
|
||||
expect(msg_el.textContent).toEqual(msgtext);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will be discarded if it's a malicious message meant to look like a carbon copy",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -403,11 +395,10 @@ describe("A Chat Message", function () {
|
|||
// Check that the chatbox for the malicous user is not created
|
||||
chatbox = await _converse.api.chats.get(sender_jid);
|
||||
expect(chatbox).toBe(null);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will indicate when it has a time difference of more than a day between it and its predecessor",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const include_nick = false;
|
||||
await mock.waitForRoster(_converse, 'current', 2, include_nick);
|
||||
|
@ -494,11 +485,10 @@ describe("A Chat Message", function () {
|
|||
expect(view.querySelector('converse-chat-message:last-child .chat-msg__text').textContent).toEqual(message);
|
||||
expect(view.querySelector('converse-chat-message:last-child .chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
|
||||
expect(view.querySelector('converse-chat-message:last-child .chat-msg__author').textContent.trim()).toBe('Juliet Capulet');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is sanitized to prevent Javascript injection attacks",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -512,11 +502,10 @@ describe("A Chat Message", function () {
|
|||
const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
|
||||
expect(msg.textContent).toEqual(message);
|
||||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual('<p>This message contains <em>some</em> <b>markup</b></p>');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can contain hyperlinks, which will be clickable",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -532,12 +521,11 @@ describe("A Chat Message", function () {
|
|||
expect(msg.textContent).toEqual(message);
|
||||
await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'This message contains a hyperlink: <a target="_blank" rel="noopener" href="http://www.opkode.com">www.opkode.com</a>');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will remove url query parameters from hyperlinks as set",
|
||||
mock.initConverse(['chatBoxesFetched'], {'filter_url_query_params': ['utm_medium', 'utm_content', 's']},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -561,10 +549,9 @@ describe("A Chat Message", function () {
|
|||
await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'Another message with a hyperlink with forbidden query params: '+
|
||||
'<a target="_blank" rel="noopener" href="https://www.opkode.com/?id=0&utm_content=1&s=1">https://www.opkode.com/?id=0&utm_content=1&s=1</a>');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render newlines", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("will render newlines", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const view = await mock.openChatBoxFor(_converse, contact_jid);
|
||||
|
@ -609,13 +596,12 @@ describe("A Chat Message", function () {
|
|||
const text = view.querySelector('converse-chat-message:last-child .chat-msg__text').innerHTML.replace(/<!-.*?->/g, '');
|
||||
return text === 'Hey\nHave you heard\n\u200B\nthe news?\n<a target="_blank" rel="noopener" href="https://conversejs.org/">https://conversejs.org</a>';
|
||||
});
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render the message time as configured",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
|
@ -636,13 +622,12 @@ describe("A Chat Message", function () {
|
|||
const msg_time = view.querySelector('.chat-content .chat-msg:last-child .chat-msg__time');
|
||||
const time = dayjs(msg_object.get('time')).format(api.settings.get('time_format'));
|
||||
expect(msg_time.textContent).toBe(time);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will be correctly identified and rendered as a followup message",
|
||||
mock.initConverse(
|
||||
[], {'debounced_content_rendering': false},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -795,7 +780,6 @@ describe("A Chat Message", function () {
|
|||
"Another message within 10 minutes, but from a different person");
|
||||
|
||||
jasmine.clock().uninstall();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
|
@ -804,7 +788,7 @@ describe("A Chat Message", function () {
|
|||
it("will appear inside the chatbox it was sent from",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -818,14 +802,13 @@ describe("A Chat Message", function () {
|
|||
expect(view.model.sendMessage).toHaveBeenCalled();
|
||||
expect(view.model.messages.length, 2);
|
||||
expect(sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop().textContent).toEqual(message);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("will be trimmed of leading and trailing whitespace",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -836,7 +819,6 @@ describe("A Chat Message", function () {
|
|||
expect(view.model.messages.at(0).get('message')).toEqual(message.trim());
|
||||
const message_el = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
|
||||
expect(message_el.textContent).toEqual(message.trim());
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -844,7 +826,7 @@ describe("A Chat Message", function () {
|
|||
describe("when received from someone else", function () {
|
||||
|
||||
it("will open a chatbox and be displayed inside it",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const include_nick = false;
|
||||
await mock.waitForRoster(_converse, 'current', 1, include_nick);
|
||||
|
@ -885,11 +867,10 @@ describe("A Chat Message", function () {
|
|||
expect(view.querySelector('.chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
|
||||
await u.waitUntil(() => chatbox.vcard.get('fullname') === mock.cur_names[0]);
|
||||
expect(view.querySelector('span.chat-msg__author').textContent.trim()).toBe('Mercutio');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will be trimmed of leading and trailing whitespace",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1, false);
|
||||
const rosterview = document.querySelector('converse-roster');
|
||||
|
@ -912,7 +893,6 @@ describe("A Chat Message", function () {
|
|||
expect(msg_obj.get('message')).toEqual(message.trim());
|
||||
const mel = await u.waitUntil(() => view.querySelector('.chat-msg .chat-msg__text'));
|
||||
expect(mel.textContent).toEqual(message.trim());
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
|
@ -920,7 +900,7 @@ describe("A Chat Message", function () {
|
|||
|
||||
it("the VCard for that user is fetched and the chatbox updated with the results",
|
||||
mock.initConverse([], {'allow_non_roster_messaging': true},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
spyOn(_converse.api, "trigger").and.callThrough();
|
||||
|
@ -964,7 +944,6 @@ describe("A Chat Message", function () {
|
|||
await u.waitUntil(() => chatbox.vcard.get('fullname') === mock.cur_names[0])
|
||||
author_el = view.querySelector('.chat-msg__author');
|
||||
expect(author_el.textContent.trim().includes('Mercutio')).toBeTruthy();
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -974,7 +953,7 @@ describe("A Chat Message", function () {
|
|||
it("will open a chatbox and be displayed inside it if allow_non_roster_messaging is true",
|
||||
mock.initConverse(
|
||||
[], {'allow_non_roster_messaging': false},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
||||
|
@ -1021,7 +1000,6 @@ describe("A Chat Message", function () {
|
|||
expect(view.querySelector('.chat-msg .chat-msg__text').textContent).toEqual(message);
|
||||
expect(view.querySelector('.chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
|
||||
expect(view.querySelector('span.chat-msg__author').textContent.trim()).toBe('Mercutio');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -1029,7 +1007,7 @@ describe("A Chat Message", function () {
|
|||
describe("and for which then an error message is received from the server", function () {
|
||||
|
||||
it("will have the error message displayed after itself",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
|
||||
|
@ -1052,7 +1030,7 @@ describe("A Chat Message", function () {
|
|||
await _converse.api.chats.open(sender_jid)
|
||||
let msg_text = 'This message will not be sent, due to an error';
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
const message = await view.model.sendMessage(msg_text);
|
||||
const message = await view.model.sendMessage({'body': msg_text});
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length);
|
||||
let msg_txt = sizzle('.chat-msg:last .chat-msg__text', view).pop().textContent;
|
||||
expect(msg_txt).toEqual(msg_text);
|
||||
|
@ -1061,7 +1039,7 @@ describe("A Chat Message", function () {
|
|||
// not be received, to test that errors appear
|
||||
// after the relevant message.
|
||||
msg_text = 'This message will be sent, and also receive an error';
|
||||
const second_message = await view.model.sendMessage(msg_text);
|
||||
const second_message = await view.model.sendMessage({'body': msg_text});
|
||||
await u.waitUntil(() => sizzle('.chat-msg .chat-msg__text', view).length === 2, 1000);
|
||||
msg_txt = sizzle('.chat-msg:last .chat-msg__text', view).pop().textContent;
|
||||
expect(msg_txt).toEqual(msg_text);
|
||||
|
@ -1120,7 +1098,7 @@ describe("A Chat Message", function () {
|
|||
expect(view.querySelectorAll('.chat-msg__error').length).toEqual(2);
|
||||
|
||||
msg_text = 'This message will be sent, and also receive an error';
|
||||
const third_message = await view.model.sendMessage(msg_text);
|
||||
const third_message = await view.model.sendMessage({'body': msg_text});
|
||||
await u.waitUntil(() => sizzle('converse-chat-message:last-child .chat-msg__text', view).pop()?.textContent === msg_text);
|
||||
|
||||
// A different error message will however render
|
||||
|
@ -1144,13 +1122,12 @@ describe("A Chat Message", function () {
|
|||
expect(el.querySelector('.chat-msg__action-edit')).toBe(null)
|
||||
expect(el.querySelector('.chat-msg__action-retract')).toBe(null)
|
||||
})
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will not show to the user an error message for a CSI message",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
// See #1317
|
||||
// https://github.com/conversejs/converse.js/issues/1317
|
||||
|
@ -1180,15 +1157,14 @@ describe("A Chat Message", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const msg_text = 'This message will show!';
|
||||
await view.model.sendMessage(msg_text);
|
||||
await view.model.sendMessage({'body': msg_text});
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
||||
expect(view.querySelectorAll('.chat-error').length).toEqual(0);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
it("will cause the chat area to be scrolled down only if it was at the bottom originally",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -1218,11 +1194,10 @@ describe("A Chat Message", function () {
|
|||
indicator_el.click();
|
||||
await u.waitUntil(() => !view.querySelector('.new-msgs-indicator'));
|
||||
await u.waitUntil(() => !view.model.get('scrolled'));
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is ignored if it's intended for a different resource and filter_by_resource is set to true",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
|
@ -1264,7 +1239,6 @@ describe("A Chat Message", function () {
|
|||
const last_message = await u.waitUntil(() => sizzle('.chat-content:last .chat-msg__text', view).pop());
|
||||
const msg_txt = last_message.textContent;
|
||||
expect(msg_txt).toEqual(message);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ describe("A Chat Message", function () {
|
|||
it("will render audio from oob mp3 URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -52,13 +52,12 @@ describe("A Chat Message", function () {
|
|||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "").trim()).toEqual(
|
||||
`<audio controls="" src="https://montague.lit/audio.mp3"></audio>`+
|
||||
`<a target="_blank" rel="noopener" href="${url}">${url}</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render video from oob mp4 URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -100,13 +99,12 @@ describe("A Chat Message", function () {
|
|||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<video controls="" preload="metadata" src="${Strophe.xmlescape(url)}"></video>`+
|
||||
`<a target="_blank" rel="noopener" href="${Strophe.xmlescape(url)}">${Strophe.xmlescape(url)}</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render download links for files from oob URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -129,13 +127,12 @@ describe("A Chat Message", function () {
|
|||
const media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "").replace(/<!-.*?->/g, '')).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="https://montague.lit/funny.pdf">Download file "funny.pdf"</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render images from oob URLs",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const base_url = 'https://conversejs.org';
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
|
@ -162,8 +159,7 @@ describe("A Chat Message", function () {
|
|||
const media = view.querySelector('.chat-msg .chat-msg__media');
|
||||
expect(media.innerHTML.replace(/<!-.*?->/g, '').replace(/(\r\n|\n|\r)/gm, "")).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
|
||||
`Download image file "conversejs-filled.svg"</a>`);
|
||||
done();
|
||||
`Download file "conversejs-filled.svg"</a>`);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ describe("A delivery receipt", function () {
|
|||
it("is emitted for a received message which requests it",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -29,13 +29,12 @@ describe("A delivery receipt", function () {
|
|||
expect(sent_messages.length).toBe(2);
|
||||
const receipt = sizzle(`received[xmlns="${Strophe.NS.RECEIPTS}"]`, sent_messages[1]).pop();
|
||||
expect(Strophe.serialize(receipt)).toBe(`<received id="${msg_id}" xmlns="${Strophe.NS.RECEIPTS}"/>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not emitted for a carbon message",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -59,13 +58,12 @@ describe("A delivery receipt", function () {
|
|||
.c('request', {'xmlns': Strophe.NS.RECEIPTS}).tree();
|
||||
await _converse.handleMessageStanza(msg);
|
||||
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not emitted for an archived message",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -96,13 +94,12 @@ describe("A delivery receipt", function () {
|
|||
expect(message_attrs.is_archived).toBe(true);
|
||||
expect(message_attrs.is_valid_receipt_request).toBe(false);
|
||||
expect(view.model.sendReceiptStanza).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be received for a sent message",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -150,6 +147,5 @@ describe("A delivery receipt", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(msg));
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__receipt').length === 2);
|
||||
expect(_converse.handleMessageStanza.calls.count()).toBe(1);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ describe("A spoiler message", function () {
|
|||
afterEach(() => (jasmine.DEFAULT_TIMEOUT_INTERVAL = original_timeout));
|
||||
|
||||
it("can be received with a hint",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -42,11 +42,10 @@ describe("A spoiler message", function () {
|
|||
await u.waitUntil(() => message_content.textContent === spoiler);
|
||||
const spoiler_hint_el = view.querySelector('.spoiler-hint');
|
||||
expect(spoiler_hint_el.textContent).toBe(spoiler_hint);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be received without a hint",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -79,11 +78,10 @@ describe("A spoiler message", function () {
|
|||
await u.waitUntil(() => message_content.textContent === spoiler);
|
||||
const spoiler_hint_el = view.querySelector('.spoiler-hint');
|
||||
expect(spoiler_hint_el.textContent).toBe('');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be sent without a hint",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
mock.openControlBox(_converse);
|
||||
|
@ -156,11 +154,10 @@ describe("A spoiler message", function () {
|
|||
expect(spoiler_toggle.textContent.trim()).toBe('Show less');
|
||||
spoiler_toggle.click();
|
||||
await u.waitUntil(() => Array.from(spoiler_msg_el.classList).includes('hidden'));
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be sent with a hint",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
mock.openControlBox(_converse);
|
||||
|
@ -237,6 +234,5 @@ describe("A spoiler message", function () {
|
|||
expect(spoiler_toggle.textContent.trim()).toBe('Show less');
|
||||
spoiler_toggle.click();
|
||||
await u.waitUntil(() => Array.from(spoiler_msg_el.classList).includes('hidden'));
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ describe("An incoming chat Message", function () {
|
|||
|
||||
it("can have styling disabled via an \"unstyled\" element",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const include_nick = false;
|
||||
await mock.waitForRoster(_converse, 'current', 2, include_nick);
|
||||
|
@ -31,14 +31,13 @@ describe("An incoming chat Message", function () {
|
|||
setTimeout(() => {
|
||||
const msg_el = view.querySelector('converse-chat-message-body');
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
done();
|
||||
}, 500);
|
||||
}));
|
||||
|
||||
|
||||
it("can have styling disabled via the allow_message_styling setting",
|
||||
mock.initConverse(['chatBoxesFetched'], {'allow_message_styling': false},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const include_nick = false;
|
||||
await mock.waitForRoster(_converse, 'current', 2, include_nick);
|
||||
|
@ -62,13 +61,12 @@ describe("An incoming chat Message", function () {
|
|||
setTimeout(() => {
|
||||
const msg_el = view.querySelector('converse-chat-message-body');
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
done();
|
||||
}, 500);
|
||||
}));
|
||||
|
||||
it("can be styled with span XEP-0393 message styling hints",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
let msg_text, msg, msg_el;
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
|
@ -130,7 +128,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 5);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<span class="styling-directive">~</span><del> Hello! <span title=":poop:">💩</span> </del><span class="styling-directive">~</span>');
|
||||
|
||||
|
@ -188,12 +185,11 @@ describe("An incoming chat Message", function () {
|
|||
'<i><a target="_blank" rel="noopener" href="https://converse_js.org/">https://converse_js.org</a></i>'+
|
||||
'<span class="styling-directive">_</span> <span class="styling-directive">_</span><i>please</i><span class="styling-directive">_</span>');
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be styled with block XEP-0393 message styling hints",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
let msg_text, msg, msg_el;
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
|
@ -206,7 +202,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'Here\'s a code block: \n'+
|
||||
'<div class="styling-directive">```</div><code class="block">Inside the code-block, <code>hello</code> we don\'t enable *styling hints* like ~these~\n'+
|
||||
|
@ -218,7 +213,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 2);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<div class="styling-directive">```</div>'+
|
||||
'<code class="block">ignored\n(println "Hello, world!")\n</code>'+
|
||||
|
@ -236,12 +230,11 @@ describe("An incoming chat Message", function () {
|
|||
'```ignored\n (println "Hello, world!")\n ```\n\n'+
|
||||
' This should not show up as monospace, '+
|
||||
'<span class="styling-directive">*</span><b>preformatted</b><span class="styling-directive">*</span> text ^');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be styled with quote XEP-0393 message styling hints",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
let msg_text, msg, msg_el;
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
|
@ -254,7 +247,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 1);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>This is quoted text\nThis is also quoted</blockquote>\nThis is not quoted');
|
||||
|
||||
|
@ -263,7 +255,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 2);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>This is <span class="styling-directive">*</span><b>quoted</b><span class="styling-directive">*</span> text\n'+
|
||||
'This is <span class="styling-directive">`</span><code>also _quoted_</code><span class="styling-directive">`</span></blockquote>\n'+
|
||||
|
@ -274,7 +265,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 3);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') === "<blockquote><blockquote>This is doubly quoted text</blockquote></blockquote>");
|
||||
|
||||
msg_text = `>> This is doubly quoted text`;
|
||||
|
@ -282,7 +272,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 4);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') === "<blockquote><blockquote>This is doubly quoted text</blockquote></blockquote>");
|
||||
|
||||
msg_text = ">```\n>ignored\n> <span></span> (println \"Hello, world!\")\n>```\n> This should show up as monospace, preformatted text ^";
|
||||
|
@ -290,7 +279,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 5);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>'+
|
||||
'<div class="styling-directive">```</div>'+
|
||||
|
@ -304,7 +292,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 6);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>```\n (println "Hello, world!")</blockquote>\n\n'+
|
||||
'The entire blockquote is a preformatted text block, but this line is plaintext!');
|
||||
|
@ -314,7 +301,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 7);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>Also, icons.js is loaded from /dist, instead of dist.</blockquote>\n'+
|
||||
'<a target="_blank" rel="noopener" href="https://conversejs.org/docs/html/configuration.html#assets-path">https://conversejs.org/docs/html/configuration.html#assets-path</a>');
|
||||
|
@ -324,7 +310,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 8);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>Where is it located?</blockquote>\n'+
|
||||
'<a target="_blank" rel="noopener" '+
|
||||
|
@ -335,7 +320,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 9);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>What do you think of it?</blockquote>\n <span title=":poop:">💩</span>');
|
||||
|
||||
|
@ -344,7 +328,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 10);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
'<blockquote>What do you think of it?</blockquote>\n<span class="styling-directive">~</span><del>hello</del><span class="styling-directive">~</span>');
|
||||
|
||||
|
@ -353,7 +336,6 @@ describe("An incoming chat Message", function () {
|
|||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 11);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') === 'hello world > this is not a quote');
|
||||
|
||||
msg_text = '> What do you think of it romeo?\n Did you see this romeo?';
|
||||
|
@ -381,15 +363,15 @@ describe("An incoming chat Message", function () {
|
|||
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 12);
|
||||
msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
`<blockquote>What do you think of it <span class="mention">romeo</span>?</blockquote>\n Did you see this <span class="mention">romeo</span>?`);
|
||||
done();
|
||||
|
||||
expect(true).toBe(true);
|
||||
}));
|
||||
|
||||
it("won't style invalid block quotes",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -420,8 +402,7 @@ describe("An incoming chat Message", function () {
|
|||
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
||||
const msg_el = Array.from(view.querySelectorAll('converse-chat-message-body')).pop();
|
||||
expect(msg_el.innerText).toBe(msg_text);
|
||||
await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') === '```\ncode```');
|
||||
done();
|
||||
expect(true).toBe(true);
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,156 @@
|
|||
/*global mock, converse */
|
||||
|
||||
const { u } = converse.env;
|
||||
|
||||
|
||||
describe("A ChatBox's Unread Message Count", function () {
|
||||
|
||||
it("is incremented when the message is received and ChatBoxView is scrolled up",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
|
||||
msg = mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
|
||||
const view = await mock.openChatBoxFor(_converse, sender_jid)
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
view.model.ui.set('scrolled', true);
|
||||
await _converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => view.model.messages.length);
|
||||
expect(view.model.get('num_unread')).toBe(1);
|
||||
const msgid = view.model.messages.last().get('id');
|
||||
expect(view.model.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.length);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
}));
|
||||
|
||||
it("is not incremented when the message is received and ChatBoxView is scrolled down",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msg = mock.createChatMessage(_converse, sender_jid, 'This message will be read');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
await _converse.handleMessageStanza(msg);
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 2);
|
||||
expect(sent_stanzas[1].querySelector('displayed')).toBeDefined();
|
||||
}));
|
||||
|
||||
it("is incremented when message is received, chatbox is scrolled down and the window is not focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = function () {
|
||||
return mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
}));
|
||||
|
||||
it("is incremented when message is received, chatbox is scrolled up and the window is not focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.ui.set('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 1);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
}));
|
||||
|
||||
it("is cleared when the chat was scrolled down and the window become focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 1);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
_converse.saveWindowState({'type': 'focus'});
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 2);
|
||||
expect(sent_stanzas[1].querySelector('displayed')).toBeDefined();
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
}));
|
||||
|
||||
it("is cleared when the chat was hidden in fullscreen mode and then becomes visible",
|
||||
mock.initConverse(['chatBoxesFetched'], {'view_mode': 'fullscreen'},
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.save({'hidden': true});
|
||||
_converse.handleMessageStanza(mock.createChatMessage(_converse, sender_jid, 'This message will be unread'));
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
chatbox.save({'hidden': false});
|
||||
await u.waitUntil(() => chatbox.get('num_unread') === 0);
|
||||
chatbox.close();
|
||||
}));
|
||||
|
||||
it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const msgFactory = () => mock.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
await mock.openChatBoxFor(_converse, sender_jid);
|
||||
const sent_stanzas = [];
|
||||
spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.ui.set('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
const msg = msgFactory();
|
||||
_converse.handleMessageStanza(msg);
|
||||
await u.waitUntil(() => chatbox.messages.length);
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
const msgid = chatbox.messages.last().get('id');
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'message').length === 1);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
_converse.saveWindowState({'type': 'focus'});
|
||||
await u.waitUntil(() => chatbox.get('num_unread') === 1);
|
||||
expect(chatbox.get('first_unread_id')).toBe(msgid);
|
||||
expect(sent_stanzas[0].querySelector('received')).toBeDefined();
|
||||
}));
|
||||
});
|
|
@ -6,7 +6,7 @@ const u = converse.env.utils;
|
|||
describe("XSS", function () {
|
||||
describe("A Chat Message", function () {
|
||||
|
||||
it("will escape IMG payload XSS attempts", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("will escape IMG payload XSS attempts", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
spyOn(window, 'alert').and.callThrough();
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -59,10 +59,9 @@ describe("XSS", function () {
|
|||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual("><img src=x onerror=alert(String.fromCharCode(88,83,83));>");
|
||||
|
||||
expect(window.alert).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will escape SVG payload XSS attempts", mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
it("will escape SVG payload XSS attempts", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
spyOn(window, 'alert').and.callThrough();
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -114,11 +113,10 @@ describe("XSS", function () {
|
|||
expect(msg.innerHTML.replace(/<!-.*?->/g, '')).toEqual('"><svg/onload=alert(/XSS/)');
|
||||
|
||||
expect(window.alert).not.toHaveBeenCalled();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will have properly escaped URLs",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -170,11 +168,10 @@ describe("XSS", function () {
|
|||
expect(msg.textContent).toEqual(message);
|
||||
await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
|
||||
`<a target="_blank" rel="noopener" href="https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=%213m6%211e1%213m4%211sQ7SdHo_bPLPlLlU8GSGWaQ%212e0%217i13312%218i6656%214m5%213m4%211s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08%218m2%213d52.3773668%214d4.5489388%215m1%211e2">https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2</a>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will avoid malformed and unsafe urls urls from rendering as anchors",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -250,7 +247,6 @@ describe("XSS", function () {
|
|||
await mock.sendMessage(view, good_urls[5].entered);
|
||||
await checkParsedURL(good_urls[5]);
|
||||
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/**
|
||||
* @module converse-controlbox
|
||||
* @copyright 2020, the Converse.js contributors
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
|
@ -7,45 +6,16 @@ import "shared/components/brand-heading";
|
|||
import "../chatview/index.js";
|
||||
import './loginpanel.js';
|
||||
import './navback.js';
|
||||
import ControlBoxMixin from './model.js';
|
||||
import ControlBox from './model.js';
|
||||
import ControlBoxToggle from './toggle.js';
|
||||
import ControlBoxView from './controlbox.js';
|
||||
import controlbox_api from './api.js';
|
||||
import log from '@converse/headless/log';
|
||||
import { _converse, api, converse } from '@converse/headless/core';
|
||||
import { addControlBox } from './utils.js';
|
||||
import { addControlBox, clearSession, disconnect, onChatBoxesFetched } from './utils.js';
|
||||
|
||||
import './styles/_controlbox.scss';
|
||||
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
function disconnect () {
|
||||
/* Upon disconnection, set connected to `false`, so that if
|
||||
* we reconnect, "onConnected" will be called,
|
||||
* to fetch the roster again and to send out a presence stanza.
|
||||
*/
|
||||
const view = _converse.chatboxviews.get('controlbox');
|
||||
view.model.set({ 'connected': false });
|
||||
return view;
|
||||
}
|
||||
|
||||
function clearSession () {
|
||||
const chatboxviews = _converse?.chatboxviews;
|
||||
const view = chatboxviews && chatboxviews.get('controlbox');
|
||||
if (view) {
|
||||
u.safeSave(view.model, { 'connected': false });
|
||||
if (view?.controlbox_pane) {
|
||||
view.controlbox_pane.remove();
|
||||
delete view.controlbox_pane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onChatBoxesFetched () {
|
||||
const controlbox = _converse.chatboxes.get('controlbox') || addControlBox();
|
||||
controlbox.save({ 'connected': true });
|
||||
}
|
||||
|
||||
converse.plugins.add('converse-controlbox', {
|
||||
/* Plugin dependencies are other plugins which might be
|
||||
|
@ -73,9 +43,8 @@ converse.plugins.add('converse-controlbox', {
|
|||
|
||||
ChatBoxes: {
|
||||
model (attrs, options) {
|
||||
const { _converse } = this.__super__;
|
||||
if (attrs && attrs.id == 'controlbox') {
|
||||
return new _converse.ControlBox(attrs, options);
|
||||
return new ControlBox(attrs, options);
|
||||
} else {
|
||||
return this.__super__.model.apply(this, arguments);
|
||||
}
|
||||
|
@ -97,7 +66,7 @@ converse.plugins.add('converse-controlbox', {
|
|||
Object.assign(api, controlbox_api);
|
||||
|
||||
_converse.ControlBoxView = ControlBoxView;
|
||||
_converse.ControlBox = _converse.ChatBox.extend(ControlBoxMixin);
|
||||
_converse.ControlBox = ControlBox;
|
||||
_converse.ControlBoxToggle = ControlBoxToggle;
|
||||
|
||||
/******************** Event Handlers ********************/
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { _converse, api, converse } from '@converse/headless/core';
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
|
||||
const { dayjs } = converse.env;
|
||||
|
||||
/**
|
||||
* Mixin which turns a ChatBox model into a ControlBox model.
|
||||
*
|
||||
* The ControlBox is the section of the chat that contains the open groupchats,
|
||||
* bookmarks and roster.
|
||||
*
|
||||
|
@ -12,7 +11,8 @@ const { dayjs } = converse.env;
|
|||
* `view_mode` it's a left-aligned sidebar.
|
||||
* @mixin
|
||||
*/
|
||||
const ControlBoxMixin = {
|
||||
const ControlBox = Model.extend({
|
||||
|
||||
defaults () {
|
||||
return {
|
||||
'bookmarked': false,
|
||||
|
@ -20,20 +20,12 @@ const ControlBoxMixin = {
|
|||
'chat_state': undefined,
|
||||
'closed': !api.settings.get('show_controlbox_by_default'),
|
||||
'num_unread': 0,
|
||||
'time_opened': this.get('time_opened') || new Date().getTime(),
|
||||
'time_opened': dayjs(0).valueOf(),
|
||||
'type': _converse.CONTROLBOX_TYPE,
|
||||
'url': ''
|
||||
};
|
||||
},
|
||||
|
||||
initialize () {
|
||||
if (this.get('id') === 'controlbox') {
|
||||
this.set({ 'time_opened': dayjs(0).valueOf() });
|
||||
} else {
|
||||
_converse.ChatBox.prototype.initialize.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
validate (attrs) {
|
||||
if (attrs.type === _converse.CONTROLBOX_TYPE) {
|
||||
if (api.settings.get('view_mode') === 'embedded' && api.settings.get('singleton')) {
|
||||
|
@ -55,7 +47,6 @@ const ControlBoxMixin = {
|
|||
onReconnection () {
|
||||
this.save('connected', true);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
export default ControlBoxMixin;
|
||||
export default ControlBox;
|
||||
|
|
|
@ -9,19 +9,18 @@ const sizzle = converse.env.sizzle;
|
|||
describe("The Controlbox", function () {
|
||||
|
||||
it("can be opened by clicking a DOM element with class 'toggle-controlbox'",
|
||||
mock.initConverse([], {}, function (done, _converse) {
|
||||
mock.initConverse([], {}, function (_converse) {
|
||||
|
||||
spyOn(_converse.api, "trigger").and.callThrough();
|
||||
document.querySelector('.toggle-controlbox').click();
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('controlBoxOpened', jasmine.any(Object));
|
||||
const el = document.querySelector("#controlbox");
|
||||
expect(u.isVisible(el)).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("can be closed by clicking a DOM element with class 'close-chatbox-button'",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.openControlBox(_converse);
|
||||
const view = _converse.chatboxviews.get('controlbox');
|
||||
|
@ -32,14 +31,13 @@ describe("The Controlbox", function () {
|
|||
view.querySelector('.close-chatbox-button').click();
|
||||
expect(view.close).toHaveBeenCalled();
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('controlBoxClosed', jasmine.any(Object));
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
describe("The \"Contacts\" section", function () {
|
||||
|
||||
it("can be used to add contact and it checks for case-sensivity",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
spyOn(_converse.api, "trigger").and.callThrough();
|
||||
await mock.waitForRoster(_converse, 'all', 0);
|
||||
|
@ -61,11 +59,10 @@ describe("The Controlbox", function () {
|
|||
await u.waitUntil(() => Array.from(rosterview.querySelectorAll('.roster-group li')).filter(u.isVisible).length, 700);
|
||||
// Checking that only one entry is created because both JID is same (Case sensitive check)
|
||||
expect(Array.from(rosterview.querySelectorAll('li')).filter(u.isVisible).length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows the number of unread mentions received",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'all');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -108,23 +105,21 @@ describe("The Controlbox", function () {
|
|||
chatview.model.set({'minimized': false});
|
||||
expect(el.querySelector('.restore-chat .message-count')).toBe(null);
|
||||
await u.waitUntil(() => rosterview.querySelector('.msgs-indicator') === null);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
describe("The Status Widget", function () {
|
||||
|
||||
it("shows the user's chat status, which is online by default",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
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');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to set the current user's chat status",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.openControlBox(_converse);
|
||||
var cbview = _converse.chatboxviews.get('controlbox');
|
||||
|
@ -146,11 +141,10 @@ describe("The Controlbox", function () {
|
|||
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');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to set a custom status message",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.openControlBox(_converse);
|
||||
const cbview = _converse.chatboxviews.get('controlbox');
|
||||
|
@ -174,7 +168,6 @@ describe("The Controlbox", function () {
|
|||
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);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -182,7 +175,7 @@ describe("The Controlbox", function () {
|
|||
describe("The 'Add Contact' widget", function () {
|
||||
|
||||
it("opens up an add modal when you click on it",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'all');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -210,11 +203,10 @@ describe("The 'Add Contact' widget", function () {
|
|||
`<iq id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
|
||||
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit" name="Someone"/></query>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be configured to not provide search suggestions",
|
||||
mock.initConverse([], {'autocomplete_add_contact': false}, async function (done, _converse) {
|
||||
mock.initConverse([], {'autocomplete_add_contact': false}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'all', 0);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -239,13 +231,12 @@ describe("The 'Add Contact' widget", function () {
|
|||
`<query xmlns="jabber:iq:roster"><item jid="someone@montague.lit"/></query>`+
|
||||
`</iq>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("integrates with xhr_user_search_url to search for contacts",
|
||||
mock.initConverse([], { 'xhr_user_search_url': 'http://example.org/?' },
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'all', 0);
|
||||
|
||||
|
@ -296,14 +287,13 @@ describe("The 'Add Contact' widget", function () {
|
|||
`<query xmlns="jabber:iq:roster"><item jid="marty@mcfly.net" name="Marty McFly"/></query>`+
|
||||
`</iq>`);
|
||||
window.XMLHttpRequest = XMLHttpRequestBackup;
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be configured to not provide search suggestions for XHR search results",
|
||||
mock.initConverse([],
|
||||
{ 'autocomplete_add_contact': false,
|
||||
'xhr_user_search_url': 'http://example.org/?' },
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'all');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -371,6 +361,5 @@ describe("The 'Add Contact' widget", function () {
|
|||
`<query xmlns="jabber:iq:roster"><item jid="marty@mcfly.net" name="Marty McFly"/></query>`+
|
||||
`</iq>`);
|
||||
window.XMLHttpRequest = XMLHttpRequestBackup;
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ describe("The Login Form", function () {
|
|||
['chatBoxesInitialized'],
|
||||
{ auto_login: false,
|
||||
allow_registration: false },
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const cbview = await u.waitUntil(() => _converse.chatboxviews.get('controlbox'));
|
||||
mock.toggleControlBox();
|
||||
|
@ -34,7 +34,6 @@ describe("The Login Form", function () {
|
|||
cbview.querySelector('input[type="submit"]').click();
|
||||
expect(_converse.config.get('trusted')).toBe(false);
|
||||
expect(_converse.getDefaultStore()).toBe('session');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("checkbox can be set to false by default",
|
||||
|
@ -43,7 +42,7 @@ describe("The Login Form", function () {
|
|||
{ auto_login: false,
|
||||
allow_user_trust_override: 'off',
|
||||
allow_registration: false },
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await u.waitUntil(() => _converse.chatboxviews.get('controlbox'))
|
||||
const cbview = _converse.chatboxviews.get('controlbox');
|
||||
|
@ -67,6 +66,5 @@ describe("The Login Form", function () {
|
|||
cbview.querySelector('input[type="submit"]').click();
|
||||
expect(_converse.config.get('trusted')).toBe(true);
|
||||
expect(_converse.getDefaultStore()).toBe('persistent');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -19,3 +19,30 @@ export function navigateToControlBox (jid) {
|
|||
const model = _converse.chatboxes.get(jid);
|
||||
u.safeSave(model, {'hidden': true});
|
||||
}
|
||||
|
||||
export function disconnect () {
|
||||
/* Upon disconnection, set connected to `false`, so that if
|
||||
* we reconnect, "onConnected" will be called,
|
||||
* to fetch the roster again and to send out a presence stanza.
|
||||
*/
|
||||
const view = _converse.chatboxviews.get('controlbox');
|
||||
view.model.set({ 'connected': false });
|
||||
return view;
|
||||
}
|
||||
|
||||
export function clearSession () {
|
||||
const chatboxviews = _converse?.chatboxviews;
|
||||
const view = chatboxviews && chatboxviews.get('controlbox');
|
||||
if (view) {
|
||||
u.safeSave(view.model, { 'connected': false });
|
||||
if (view?.controlbox_pane) {
|
||||
view.controlbox_pane.remove();
|
||||
delete view.controlbox_pane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function onChatBoxesFetched () {
|
||||
const controlbox = _converse.chatboxes.get('controlbox') || addControlBox();
|
||||
controlbox.save({ 'connected': true });
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export default (o) => {
|
|||
<div class="chatbox-title__text" title="${o.jid}">${ o.display_name }</div>
|
||||
</div>
|
||||
<div class="chatbox-title__buttons row no-gutters">
|
||||
${ o.dropdown_btns.length ? html`<converse-dropdown .items=${o.dropdown_btns}></converse-dropdown>` : '' }
|
||||
${ o.dropdown_btns.length ? html`<converse-dropdown class="dropleft" .items=${o.dropdown_btns}></converse-dropdown>` : '' }
|
||||
${ o.standalone_btns.length ? tpl_standalone_btns(o) : '' }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
describe("A headlines box", function () {
|
||||
|
||||
it("will not open nor display non-headline messages",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const { $msg } = converse.env;
|
||||
|
@ -27,11 +27,10 @@ describe("A headlines box", function () {
|
|||
.c('body').t('SORRY FOR THIS ADVERT');
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(_converse.api.headlines.get().length === 0);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will open and display headline messages", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
[], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const { u, $msg} = converse.env;
|
||||
|
@ -64,11 +63,10 @@ describe("A headlines box", function () {
|
|||
const view = _converse.chatboxviews.get('notify.example.com');
|
||||
expect(view.model.get('show_avatar')).toBeFalsy();
|
||||
expect(view.querySelector('img.avatar')).toBe(null);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will show headline messages in the controlbox", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
[], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const { u, $msg} = converse.env;
|
||||
|
@ -101,11 +99,10 @@ describe("A headlines box", function () {
|
|||
await u.waitUntil(() => view.querySelectorAll(".open-headline").length);
|
||||
expect(view.querySelectorAll('.open-headline').length).toBe(1);
|
||||
expect(view.querySelector('.open-headline').text).toBe('notify.example.com');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will remove headline messages from the controlbox if closed", mock.initConverse(
|
||||
[], {}, async function (done, _converse) {
|
||||
[], {}, async function (_converse) {
|
||||
|
||||
const { u, $msg} = converse.env;
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -143,12 +140,11 @@ describe("A headlines box", function () {
|
|||
close_el.click();
|
||||
await u.waitUntil(() => cbview.querySelectorAll(".open-headline").length === 0);
|
||||
expect(cbview.querySelectorAll('.open-headline').length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will not show a headline messages from a full JID if allow_non_roster_messaging is false",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const { $msg } = converse.env;
|
||||
|
@ -163,6 +159,5 @@ describe("A headlines box", function () {
|
|||
.c('body').t('Здравствуйте друзья');
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(_.without('controlbox', _converse.chatboxviews.keys()).length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -18,7 +18,6 @@ class HeadlinesView extends BaseChatView {
|
|||
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
|
||||
this.listenTo(this.model, 'change:hidden', () => this.afterShown());
|
||||
this.listenTo(this.model, 'destroy', this.remove);
|
||||
this.listenTo(this.model, 'show', this.show);
|
||||
this.listenTo(this.model.messages, 'add', this.requestUpdate);
|
||||
this.listenTo(this.model.messages, 'remove', this.requestUpdate);
|
||||
this.listenTo(this.model.messages, 'reset', this.requestUpdate);
|
||||
|
@ -44,7 +43,6 @@ class HeadlinesView extends BaseChatView {
|
|||
_converse.router.navigate('');
|
||||
}
|
||||
await this.model.close(ev);
|
||||
api.trigger('chatBoxClosed', this);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -54,8 +52,8 @@ class HeadlinesView extends BaseChatView {
|
|||
return [];
|
||||
}
|
||||
|
||||
afterShown () { // eslint-disable-line class-methods-use-this
|
||||
return;
|
||||
afterShown () {
|
||||
this.model.clearUnreadMsgCounter();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ describe("Message Archive Management", function () {
|
|||
describe("The XEP-0313 Archive", function () {
|
||||
|
||||
it("is queried when the user scrolls up",
|
||||
mock.initConverse(['discoInitialized'], {'archived_messages_page_size': 2}, async function (done, _converse) {
|
||||
mock.initConverse(['discoInitialized'], {'archived_messages_page_size': 2}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -78,7 +78,6 @@ describe("Message Archive Management", function () {
|
|||
`<set xmlns="http://jabber.org/protocol/rsm"><before>${view.model.messages.at(0).get('stanza_id romeo@montague.lit')}</before><max>2</max></set></query>`+
|
||||
`</iq>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is queried when the user enters a new MUC",
|
||||
|
@ -86,7 +85,7 @@ describe("Message Archive Management", function () {
|
|||
{
|
||||
'archived_messages_page_size': 2,
|
||||
'muc_clear_messages_on_leave': false,
|
||||
}, async function (done, _converse) {
|
||||
}, async function (_converse) {
|
||||
|
||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||
const muc_jid = 'orchard@chat.shakespeare.lit';
|
||||
|
@ -269,7 +268,6 @@ describe("Message Archive Management", function () {
|
|||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
||||
await u.waitUntil(() => Array.from(view.querySelectorAll('.chat-msg__text'))
|
||||
.map(e => e.textContent).join(' ') === "2nd Message 3rd Message 4th Message 5th Message 6th Message", 1000);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("queries for messages since the most recent cached message in a newly entered MUC",
|
||||
|
@ -278,7 +276,7 @@ describe("Message Archive Management", function () {
|
|||
'archived_messages_page_size': 2,
|
||||
'muc_nickname_from_jid': false,
|
||||
'muc_clear_messages_on_leave': false,
|
||||
}, async function (done, _converse) {
|
||||
}, async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||
|
@ -322,7 +320,6 @@ describe("Message Archive Management", function () {
|
|||
`<set xmlns="http://jabber.org/protocol/rsm"><max>2</max></set>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
return done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -332,7 +329,7 @@ describe("Message Archive Management", function () {
|
|||
it("is discarded if it doesn't come from the right sender",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -382,13 +379,12 @@ describe("Message Archive Management", function () {
|
|||
.filter(el => el.textContent === "Thrice the brinded cat hath mew'd.").length, 1000);
|
||||
expect(view.model.messages.length).toBe(1);
|
||||
expect(view.model.messages.at(0).get('message')).toBe("Thrice the brinded cat hath mew'd.");
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not discarded if it comes from the right sender",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -437,13 +433,12 @@ describe("Message Archive Management", function () {
|
|||
expect(view.model.messages.length).toBe(2);
|
||||
expect(view.model.messages.at(0).get('message')).toBe("Meet me at the dance");
|
||||
expect(view.model.messages.at(1).get('message')).toBe("Thrice the brinded cat hath mew'd.");
|
||||
done();
|
||||
}));
|
||||
|
||||
it("updates the is_archived value of an already cached version",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'trek-radio@conference.lightwitch.org', 'romeo');
|
||||
|
||||
|
@ -485,13 +480,12 @@ describe("Message Archive Management", function () {
|
|||
expect(view.model.messages.length).toBe(1);
|
||||
expect(view.model.messages.at(0).get('is_archived')).toBe(true);
|
||||
expect(view.model.messages.at(0).get('stanza_id trek-radio@conference.lightwitch.org')).toBe('45fbbf2a-1059-479d-9283-c8effaf05621');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("isn't shown as duplicate by comparing its stanza id or archive id",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'trek-radio@conference.lightwitch.org', 'jcbrand');
|
||||
const view = _converse.chatboxviews.get('trek-radio@conference.lightwitch.org');
|
||||
|
@ -524,13 +518,12 @@ describe("Message Archive Management", function () {
|
|||
const result = await view.model.getDuplicateMessage.calls.all()[0].returnValue
|
||||
expect(result instanceof _converse.Message).toBe(true);
|
||||
expect(view.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("isn't shown as duplicate by comparing only the archive id",
|
||||
mock.initConverse(
|
||||
['discoInitialized'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'discuss@conference.conversejs.org', 'romeo');
|
||||
const view = _converse.chatboxviews.get('discuss@conference.conversejs.org');
|
||||
|
@ -574,7 +567,6 @@ describe("Message Archive Management", function () {
|
|||
const result = await view.model.getDuplicateMessage.calls.all()[0].returnValue
|
||||
expect(result instanceof _converse.Message).toBe(true);
|
||||
expect(view.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
done();
|
||||
}))
|
||||
});
|
||||
});
|
||||
|
@ -582,7 +574,7 @@ describe("Message Archive Management", function () {
|
|||
describe("The archive.query API", function () {
|
||||
|
||||
it("can be used to query for all archived messages",
|
||||
mock.initConverse(['discoInitialized'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['discoInitialized'], {}, async function (_converse) {
|
||||
|
||||
const sendIQ = _converse.connection.sendIQ;
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
|
@ -596,11 +588,10 @@ describe("Message Archive Management", function () {
|
|||
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
|
||||
expect(Strophe.serialize(sent_stanza)).toBe(
|
||||
`<iq id="${IQ_id}" type="set" xmlns="jabber:client"><query queryid="${queryid}" xmlns="urn:xmpp:mam:2"/></iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to query for all messages to/from a particular JID",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -625,11 +616,10 @@ describe("Message Archive Management", function () {
|
|||
`</x>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to query for archived messages from a chat room",
|
||||
mock.initConverse(['statusInitialized'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['statusInitialized'], {}, async function (_converse) {
|
||||
|
||||
const room_jid = 'coven@chat.shakespeare.lit';
|
||||
_converse.api.archive.query({'with': room_jid, 'groupchat': true});
|
||||
|
@ -650,11 +640,10 @@ describe("Message Archive Management", function () {
|
|||
`</x>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("checks whether returned MAM messages from a MUC room are from the right JID",
|
||||
mock.initConverse(['statusInitialized'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['statusInitialized'], {}, async function (_converse) {
|
||||
|
||||
const room_jid = 'coven@chat.shakespeare.lit';
|
||||
const promise = _converse.api.archive.query({'with': room_jid, 'groupchat': true, 'max':'10'});
|
||||
|
@ -719,11 +708,10 @@ describe("Message Archive Management", function () {
|
|||
|
||||
const result = await promise;
|
||||
expect(result.messages.length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to query for all messages in a certain timespan",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -757,11 +745,10 @@ describe("Message Archive Management", function () {
|
|||
`</query>`+
|
||||
`</iq>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("throws a TypeError if an invalid date is provided",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
try {
|
||||
|
@ -769,11 +756,10 @@ describe("Message Archive Management", function () {
|
|||
} catch (e) {
|
||||
expect(() => {throw e}).toThrow(new TypeError('archive.query: invalid date provided for: start'));
|
||||
}
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to query for all messages after a certain time",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -803,11 +789,10 @@ describe("Message Archive Management", function () {
|
|||
`</query>`+
|
||||
`</iq>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to query for a limited set of results",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -837,11 +822,10 @@ describe("Message Archive Management", function () {
|
|||
`</query>`+
|
||||
`</iq>`
|
||||
);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be used to page through results",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -875,11 +859,10 @@ describe("Message Archive Management", function () {
|
|||
`</set>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("accepts \"before\" with an empty string as value to reverse the order",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -905,11 +888,10 @@ describe("Message Archive Management", function () {
|
|||
`</set>`+
|
||||
`</query>`+
|
||||
`</iq>`);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("returns an object which includes the messages and a _converse.RSM object",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, null, [Strophe.NS.MAM]);
|
||||
let sent_stanza, IQ_id;
|
||||
|
@ -988,14 +970,13 @@ describe("Message Archive Management", function () {
|
|||
expect(result.rsm.result.count).toBe(16);
|
||||
expect(result.rsm.result.first).toBe('23452-4534-1');
|
||||
expect(result.rsm.result.last).toBe('09af3-cc343-b409f');
|
||||
done()
|
||||
}));
|
||||
});
|
||||
|
||||
describe("The default preference", function () {
|
||||
|
||||
it("is set once server support for MAM has been confirmed",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
|
||||
|
@ -1065,7 +1046,6 @@ describe("Message Archive Management", function () {
|
|||
await u.waitUntil(() => feature.save.calls.count());
|
||||
expect(feature.save).toHaveBeenCalled();
|
||||
expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -1074,7 +1054,7 @@ describe("Chatboxes", function () {
|
|||
describe("A Chatbox", function () {
|
||||
|
||||
it("will fetch archived messages once it's opened",
|
||||
mock.initConverse(['discoInitialized'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['discoInitialized'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -1130,11 +1110,10 @@ describe("Chatboxes", function () {
|
|||
.c('last').t('09af3-cc343-b409f').up()
|
||||
.c('count').t('16');
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will show an error message if the MAM query times out",
|
||||
mock.initConverse(['discoInitialized'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['discoInitialized'], {}, async function (_converse) {
|
||||
|
||||
const sendIQ = _converse.connection.sendIQ;
|
||||
|
||||
|
@ -1231,7 +1210,6 @@ describe("Chatboxes", function () {
|
|||
await u.waitUntil(() => view.model.messages.length === 2, 500);
|
||||
err_message = view.querySelector('.message.chat-error');
|
||||
expect(err_message).toBe(null);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ describe("Message Archive Management", function () {
|
|||
'persistent_store': 'localStorage',
|
||||
'mam_request_all_pages': false
|
||||
},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||
const muc_jid = 'orchard@chat.shakespeare.lit';
|
||||
|
@ -152,12 +152,11 @@ describe("Message Archive Management", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(result));
|
||||
await u.waitUntil(() => view.model.messages.length === 4);
|
||||
await u.waitUntil(() => view.querySelector('converse-mam-placeholder') === null);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is not created when there isn't a gap because the cached history is empty",
|
||||
mock.initConverse(['discoInitialized'], {'archived_messages_page_size': 2},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
const sent_IQs = _converse.connection.IQ_stanzas;
|
||||
const muc_jid = 'orchard@chat.shakespeare.lit';
|
||||
|
@ -213,7 +212,6 @@ describe("Message Archive Management", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(result));
|
||||
await u.waitUntil(() => view.model.messages.length === 2);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,11 +8,8 @@ const sizzle = converse.env.sizzle;
|
|||
describe("A chat message", function () {
|
||||
|
||||
it("received for a minimized chat box will increment a counter on its header",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {'view_mode': 'overlayed'}, async function (_converse) {
|
||||
|
||||
if (_converse.view_mode === 'fullscreen') {
|
||||
return done();
|
||||
}
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const contact_name = mock.cur_names[0];
|
||||
const contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -65,7 +62,6 @@ describe("A chat message", function () {
|
|||
expect(count.textContent).toBe('2');
|
||||
document.querySelector("converse-minimized-chat a.restore-chat").click();
|
||||
expect(_converse.chatboxes.filter('minimized').length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -73,7 +69,7 @@ describe("A chat message", function () {
|
|||
describe("A Groupchat", function () {
|
||||
|
||||
it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.openChatRoom(_converse, 'lounge', 'montague.lit', 'romeo');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
@ -89,7 +85,6 @@ describe("A Groupchat", function () {
|
|||
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
|
||||
expect(view.model.get('minimized')).toBeFalsy();
|
||||
expect(_converse.api.trigger.calls.count(), 3);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -97,7 +92,7 @@ describe("A Groupchat", function () {
|
|||
describe("A Chatbox", function () {
|
||||
|
||||
it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -121,11 +116,10 @@ describe("A Chatbox", function () {
|
|||
minimized_chats.querySelector("a.restore-chat").click();
|
||||
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
|
||||
expect(chatview.model.get('minimized')).toBeFalsy();
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("can be opened in minimized mode initially", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("can be opened in minimized mode initially", mock.initConverse([], {}, async function (_converse) {
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
const minimized_chats = document.querySelector("converse-minimized-chats")
|
||||
|
@ -136,11 +130,10 @@ describe("A Chatbox", function () {
|
|||
expect(u.isVisible(minimized_chats.firstElementChild)).toBe(true);
|
||||
expect(minimized_chats.firstElementChild.querySelectorAll('converse-minimized-chat').length).toBe(1);
|
||||
expect(_converse.chatboxes.filter('minimized').length).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("can be trimmed to conserve space", mock.initConverse([], {}, async function (done, _converse) {
|
||||
it("can be trimmed to conserve space", mock.initConverse([], {}, async function (_converse) {
|
||||
spyOn(_converse.minimize, 'trimChats');
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -173,7 +166,6 @@ describe("A Chatbox", function () {
|
|||
const minimized_chats = document.querySelector("converse-minimized-chats")
|
||||
minimized_chats.querySelector("a.restore-chat").click();
|
||||
expect(_converse.minimize.trimChats.calls.count()).toBe(17);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -181,7 +173,7 @@ describe("A Chatbox", function () {
|
|||
describe("A Minimized ChatBoxView's Unread Message Count", function () {
|
||||
|
||||
it("is displayed when scrolled up chatbox is minimized after receiving unread messages",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -199,11 +191,10 @@ describe("A Minimized ChatBoxView's Unread Message Count", function () {
|
|||
const unread_count = selectUnreadMsgCount();
|
||||
expect(u.isVisible(unread_count)).toBeTruthy();
|
||||
expect(unread_count.innerHTML.replace(/<!-.*?->/g, '')).toBe('1');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is incremented when message is received and windows is not focused",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
|
@ -217,11 +208,10 @@ describe("A Minimized ChatBoxView's Unread Message Count", function () {
|
|||
const unread_count = selectUnreadMsgCount();
|
||||
expect(u.isVisible(unread_count)).toBeTruthy();
|
||||
expect(unread_count.innerHTML.replace(/<!-.*?->/g, '')).toBe('1');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will render Openstreetmap-URL from geo-URI",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 1);
|
||||
const message = "geo:37.786971,-122.399677";
|
||||
|
@ -236,7 +226,6 @@ describe("A Minimized ChatBoxView's Unread Message Count", function () {
|
|||
await u.waitUntil(() => msg.innerHTML.replace(/\<!-.*?-\>/g, '') ===
|
||||
'<a target="_blank" rel="noopener" href="https://www.openstreetmap.org/?mlat=37.786971&'+
|
||||
'mlon=-122.399677#map=18/37.786971/-122.399677">https://www.openstreetmap.org/?mlat=37.786971&mlon=-122.399677#map=18/37.786971/-122.399677</a>');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -244,7 +233,7 @@ describe("A Minimized ChatBoxView's Unread Message Count", function () {
|
|||
describe("The Minimized Chats Widget", function () {
|
||||
|
||||
it("shows chats that have been minimized",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -271,11 +260,10 @@ describe("The Minimized Chats Widget", function () {
|
|||
expect(u.isVisible(minimized_chats)).toBe(true);
|
||||
expect(_converse.chatboxes.filter('minimized').length).toBe(2);
|
||||
expect(_converse.chatboxes.filter('minimized').map(c => c.get('jid')).includes(contact_jid)).toBeTruthy();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be toggled to hide or show minimized chats",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current');
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -297,11 +285,10 @@ describe("The Minimized Chats Widget", function () {
|
|||
minimized_chats.querySelector('#toggle-minimized-chats').click();
|
||||
await u.waitUntil(() => u.isVisible(minimized_chats.querySelector('.minimized-chats-flyout')));
|
||||
expect(minimized_chats.minchats.get('collapsed')).toBeTruthy();
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows the number messages received to minimized chats",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 4);
|
||||
await mock.openControlBox(_converse);
|
||||
|
@ -312,11 +299,13 @@ describe("The Minimized Chats Widget", function () {
|
|||
const unread_el = minimized_chats.querySelector('.unread-message-count');
|
||||
expect(u.isVisible(unread_el)).toBe(false);
|
||||
|
||||
const promises = [];
|
||||
let i, contact_jid;
|
||||
for (i=0; i<3; i++) {
|
||||
contact_jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||
mock.openChatBoxFor(_converse, contact_jid);
|
||||
promises.push(mock.openChatBoxFor(_converse, contact_jid));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
await u.waitUntil(() => _converse.chatboxes.length == 4);
|
||||
|
||||
const chatview = _converse.chatboxviews.get(contact_jid);
|
||||
|
@ -372,11 +361,10 @@ describe("The Minimized Chats Widget", function () {
|
|||
id: u.getUniqueId()
|
||||
}).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
|
||||
expect(minimized_chats.querySelector('.unread-message-count').textContent).toBe((i).toString());
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows the number messages received to minimized groupchats",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'kitchen@conference.shakespeare.lit';
|
||||
await mock.openAndEnterChatRoom(_converse, 'kitchen@conference.shakespeare.lit', 'fires');
|
||||
|
@ -395,6 +383,5 @@ describe("The Minimized Chats Widget", function () {
|
|||
const minimized_chats = document.querySelector("converse-minimized-chats")
|
||||
expect(u.isVisible(minimized_chats.querySelector('.unread-message-count'))).toBeTruthy();
|
||||
expect(minimized_chats.querySelector('.unread-message-count').textContent).toBe('1');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import 'shared/autocomplete/index.js';
|
||||
import BottomPanel from 'plugins/chatview/bottom-panel.js';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import tpl_muc_bottom_panel from './templates/muc-bottom-panel.js';
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { render } from 'lit';
|
||||
|
||||
|
@ -16,17 +14,14 @@ export default class MUCBottomPanel extends BottomPanel {
|
|||
'click .send-button': 'sendButtonClicked',
|
||||
}
|
||||
|
||||
async connectedCallback () {
|
||||
// this.model gets set in the super method and we also wait there for this.model.initialized
|
||||
await super.connectedCallback();
|
||||
this.debouncedRender = debounce(this.render, 100);
|
||||
async initialize () {
|
||||
await super.initialize();
|
||||
this.listenTo(this.model, 'change:hidden_occupants', this.debouncedRender);
|
||||
this.listenTo(this.model, 'change:num_unread_general', this.debouncedRender)
|
||||
this.listenTo(this.model.features, 'change:moderated', this.debouncedRender);
|
||||
this.listenTo(this.model.occupants, 'add', this.renderIfOwnOccupant)
|
||||
this.listenTo(this.model.occupants, 'change:role', this.renderIfOwnOccupant);
|
||||
this.listenTo(this.model.session, 'change:connection_status', this.debouncedRender);
|
||||
this.render();
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -35,6 +30,7 @@ export default class MUCBottomPanel extends BottomPanel {
|
|||
render(tpl_muc_bottom_panel({
|
||||
can_edit, entered,
|
||||
'model': this.model,
|
||||
'is_groupchat': true,
|
||||
'viewUnreadMessages': ev => this.viewUnreadMessages(ev)
|
||||
}), this);
|
||||
}
|
||||
|
@ -47,14 +43,6 @@ export default class MUCBottomPanel extends BottomPanel {
|
|||
this.querySelector('converse-message-form')?.onFormSubmitted(ev);
|
||||
}
|
||||
|
||||
getToolbarOptions () {
|
||||
return Object.assign(super.getToolbarOptions(), {
|
||||
'is_groupchat': true,
|
||||
'label_hide_occupants': __('Hide the list of participants'),
|
||||
'show_occupants_toggle': api.settings.get('visible_toolbar_buttons').toggle_occupants
|
||||
});
|
||||
}
|
||||
|
||||
hideOccupants (ev) {
|
||||
ev?.preventDefault?.();
|
||||
ev?.stopPropagation?.();
|
||||
|
|
|
@ -19,7 +19,6 @@ export default class MUCView extends BaseChatView {
|
|||
|
||||
this.listenTo(_converse, 'windowStateChanged', this.onWindowStateChanged);
|
||||
this.listenTo(this.model, 'change:composing_spoiler', this.requestUpdateMessageForm);
|
||||
this.listenTo(this.model, 'show', this.show);
|
||||
this.listenTo(this.model.session, 'change:connection_status', this.onConnectionStatusChanged);
|
||||
this.listenTo(this.model.session, 'change:view', this.requestUpdate);
|
||||
|
||||
|
|
|
@ -23,10 +23,9 @@ const tpl_can_edit = (o) => {
|
|||
.model=${o.model}
|
||||
?composing_spoiler="${o.model.get('composing_spoiler')}"
|
||||
?hidden_occupants="${o.model.get('hidden_occupants')}"
|
||||
?is_groupchat="${o.model.get('is_groupchat')}"
|
||||
?is_groupchat="${o.is_groupchat}"
|
||||
?show_call_button="${show_call_button}"
|
||||
?show_emoji_button="${show_emoji_button}"
|
||||
?show_occupants_toggle="${o.model.get('show_occupants_toggle')}"
|
||||
?show_send_button="${show_send_button}"
|
||||
?show_spoiler_button="${show_spoiler_button}"
|
||||
?show_toolbar="${show_toolbar}"
|
||||
|
|
|
@ -3,7 +3,7 @@ import { __ } from 'i18n';
|
|||
|
||||
export default (o) => {
|
||||
const i18n_desc = __('Description:');
|
||||
const i18n_jid = __('Groupchat Address (JID):');
|
||||
const i18n_jid = __('Groupchat XMPP Address:');
|
||||
const i18n_occ = __('Participants:');
|
||||
const i18n_features = __('Features:');
|
||||
const i18n_requires_auth = __('Requires authentication');
|
||||
|
|
|
@ -20,7 +20,7 @@ export default (o) => {
|
|||
</div>
|
||||
<div class="chatbox-title__buttons row no-gutters">
|
||||
${ o.standalone_btns.length ? tpl_standalone_btns(o) : '' }
|
||||
${ o.dropdown_btns.length ? html`<converse-dropdown .items=${o.dropdown_btns}></converse-dropdown>` : '' }
|
||||
${ o.dropdown_btns.length ? html`<converse-dropdown class="dropleft" .items=${o.dropdown_btns}></converse-dropdown>` : '' }
|
||||
</div>
|
||||
</div>
|
||||
${ show_subject ? html`<p class="chat-head__desc" title="${i18n_hide_topic}">
|
||||
|
|
|
@ -9,7 +9,7 @@ describe("The nickname autocomplete feature", function () {
|
|||
|
||||
it("shows all autocompletion options when the user presses @",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'tom');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
@ -58,12 +58,11 @@ describe("The nickname autocomplete feature", function () {
|
|||
expect(view.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('harry');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('jane');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(4)').textContent).toBe('tom');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows all autocompletion options when the user presses @ right after a new line",
|
||||
mock.initConverse(['chatBoxesFetched'], {},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'tom');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
@ -113,13 +112,12 @@ describe("The nickname autocomplete feature", function () {
|
|||
expect(view.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('harry');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('jane');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(4)').textContent).toBe('tom');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("shows all autocompletion options when the user presses @ right after an allowed character",
|
||||
mock.initConverse(
|
||||
['chatBoxesFetched'], {'opening_mention_characters':['(']},
|
||||
async function (done, _converse) {
|
||||
async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'tom');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
@ -169,11 +167,10 @@ describe("The nickname autocomplete feature", function () {
|
|||
expect(view.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('harry');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('jane');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(4)').textContent).toBe('tom');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("should order by query index position and length", mock.initConverse(
|
||||
['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
['chatBoxesFetched'], {}, async function (_converse) {
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'tom');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
||||
|
@ -217,11 +214,10 @@ describe("The nickname autocomplete feature", function () {
|
|||
await u.waitUntil(() => view.querySelectorAll('.suggestion-box__results li').length === 2);
|
||||
expect(view.querySelector('.suggestion-box__results li:first-child').textContent).toBe('john');
|
||||
expect(view.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('jones');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("autocompletes when the user presses tab",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
@ -328,11 +324,10 @@ describe("The nickname autocomplete feature", function () {
|
|||
message_form.onKeyDown(tab_event);
|
||||
message_form.onKeyUp(tab_event);
|
||||
await u.waitUntil(() => textarea.value === 'hello @z3r0 ');
|
||||
done();
|
||||
}));
|
||||
|
||||
it("autocompletes when the user presses backspace",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
|
@ -368,6 +363,5 @@ describe("The nickname autocomplete feature", function () {
|
|||
await u.waitUntil(() => view.querySelector('.suggestion-box__results').hidden === false);
|
||||
expect(view.querySelectorAll('.suggestion-box__results li').length).toBe(1);
|
||||
expect(view.querySelector('.suggestion-box__results li').textContent).toBe('some1');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ const u = converse.env.utils;
|
|||
describe("The <converse-muc> component", function () {
|
||||
|
||||
it("can be rendered as a standalone component",
|
||||
mock.initConverse([], {'auto_insert': false}, async function (done, _converse) {
|
||||
mock.initConverse([], {'auto_insert': false}, async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
|
@ -33,11 +33,10 @@ describe("The <converse-muc> component", function () {
|
|||
await u.waitUntil(() => muc_el.querySelector('converse-muc-bottom-panel'));
|
||||
body.removeChild(span_el);
|
||||
expect(true).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("will update correctly when the jid property changes",
|
||||
mock.initConverse([], {'auto_insert': false}, async function (done, _converse) {
|
||||
mock.initConverse([], {'auto_insert': false}, async function (_converse) {
|
||||
|
||||
const { api } = _converse;
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
|
@ -54,7 +53,7 @@ describe("The <converse-muc> component", function () {
|
|||
await mock.returnMemberLists(_converse, muc_jid, [], all_affiliations);
|
||||
await model.messages.fetched;
|
||||
|
||||
model.sendMessage('hello from the lounge!');
|
||||
model.sendMessage({'body': 'hello from the lounge!'});
|
||||
|
||||
const span_el = document.createElement('span');
|
||||
span_el.classList.add('conversejs');
|
||||
|
@ -84,11 +83,10 @@ describe("The <converse-muc> component", function () {
|
|||
await mock.returnMemberLists(_converse, muc2_jid, [], all_affiliations);
|
||||
await model.messages.fetched;
|
||||
|
||||
model2.sendMessage('hello from the bar!');
|
||||
model2.sendMessage({'body': 'hello from the bar!'});
|
||||
muc_el.setAttribute('jid', muc2_jid);
|
||||
|
||||
await u.waitUntil(() => muc_el.querySelector('converse-chat-message-body').textContent.trim() === 'hello from the bar!');
|
||||
body.removeChild(span_el);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ const { $msg, $pres, Strophe, u } = converse.env;
|
|||
describe("A Groupchat Message", function () {
|
||||
|
||||
it("can be replaced with a correction",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
|
@ -66,11 +66,10 @@ describe("A Groupchat Message", function () {
|
|||
expect(older_msgs.length).toBe(2);
|
||||
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
||||
expect(older_msgs[1].textContent.includes('But soft, what light through yonder chimney breaks?')).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("keeps the same position in history after a correction",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
|
@ -158,11 +157,10 @@ describe("A Groupchat Message", function () {
|
|||
expect(older_msgs.length).toBe(2);
|
||||
expect(older_msgs[0].textContent.includes('But soft, what light through yonder airlock breaks?')).toBe(true);
|
||||
expect(older_msgs[1].textContent.includes('But soft, what light through yonder chimney breaks?')).toBe(true);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("can be sent as a correction by using the up arrow",
|
||||
mock.initConverse([], {}, async function (done, _converse) {
|
||||
mock.initConverse([], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
|
@ -262,6 +260,5 @@ describe("A Groupchat Message", function () {
|
|||
expect(view.model.messages.at(0).get('correcting')).toBe(false);
|
||||
expect(view.querySelectorAll('.chat-msg').length).toBe(2);
|
||||
await u.waitUntil(() => !u.hasClass('correcting', view.querySelector('.chat-msg')), 500);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -6,13 +6,13 @@ const u = converse.env.utils;
|
|||
describe("Emojis", function () {
|
||||
describe("The emoji picker", function () {
|
||||
it("is opened to autocomplete emojis in the textarea",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
const view = _converse.chatboxviews.get(muc_jid);
|
||||
await u.waitUntil(() => view.querySelector('converse-emoji-dropdown'));
|
||||
await u.waitUntil(() => view.querySelector('converse-emoji-picker'));
|
||||
const textarea = view.querySelector('textarea.chat-textarea');
|
||||
textarea.value = ':gri';
|
||||
|
||||
|
@ -70,17 +70,16 @@ describe("Emojis", function () {
|
|||
await u.waitUntil(() => input.value === ':use');
|
||||
visible_emojis = sizzle('.insert-emoji:not(.hidden)', picker);
|
||||
expect(visible_emojis.length).toBe(0);
|
||||
done();
|
||||
}));
|
||||
|
||||
it("is focused to autocomplete emojis in the textarea",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
const view = _converse.chatboxviews.get(muc_jid);
|
||||
await u.waitUntil(() => view.querySelector('converse-emoji-dropdown'));
|
||||
await u.waitUntil(() => view.querySelector('converse-emoji-picker'));
|
||||
const textarea = view.querySelector('textarea.chat-textarea');
|
||||
textarea.value = ':';
|
||||
// Press tab
|
||||
|
@ -119,18 +118,17 @@ describe("Emojis", function () {
|
|||
emoji = sizzle('.emojis-lists__container--search .insert-emoji:not(.hidden) a', view).pop();
|
||||
emoji.click();
|
||||
await u.waitUntil(() => textarea.value === ':grinning: :grimacing: ');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("properly inserts emojis into the chat textarea",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
const view = _converse.chatboxviews.get(muc_jid);
|
||||
await u.waitUntil(() => view.querySelector('converse-emoji-dropdown'));
|
||||
await u.waitUntil(() => view.querySelector('converse-emoji-picker'));
|
||||
const textarea = view.querySelector('textarea.chat-textarea');
|
||||
textarea.value = ':gri';
|
||||
|
||||
|
@ -164,12 +162,11 @@ describe("Emojis", function () {
|
|||
const emoji = sizzle('.emojis-lists__container--search .insert-emoji:not(.hidden) a', view).pop();
|
||||
emoji.click();
|
||||
expect(textarea.value).toBe(':100: ');
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
it("allows you to search for particular emojis",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.waitForRoster(_converse, 'current', 0);
|
||||
|
@ -222,7 +219,6 @@ describe("Emojis", function () {
|
|||
input.dispatchEvent(new KeyboardEvent('keydown', enter_event));
|
||||
await u.waitUntil(() => input.value === '');
|
||||
expect(view.querySelector('textarea.chat-textarea').value).toBe(':smiley: ');
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ const u = converse.env.utils;
|
|||
describe("A XEP-0317 MUC Hat", function () {
|
||||
|
||||
it("can be included in a presence stanza",
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
|
||||
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||
const muc_jid = 'lounge@montague.lit';
|
||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||
const view = _converse.chatboxviews.get(muc_jid);
|
||||
|
@ -70,6 +70,5 @@ describe("A XEP-0317 MUC Hat", function () {
|
|||
`)));
|
||||
await u.waitUntil(() => view.model.getOccupant("Terry").get('hats').length === 0);
|
||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg .badge').length === 0);
|
||||
done();
|
||||
}));
|
||||
})
|
||||
|
|
|
@ -8,7 +8,7 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
describe("When not supported", function () {
|
||||
describe("A file upload toolbar button", function () {
|
||||
|
||||
it("does not appear in MUC chats", mock.initConverse([], {}, async (done, _converse) => {
|
||||
it("does not appear in MUC chats", mock.initConverse([], {}, async (_converse) => {
|
||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
|
||||
mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.domain,
|
||||
|
@ -19,7 +19,6 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
await u.waitUntil(() => view.querySelector('.chat-toolbar .fileupload') === null);
|
||||
expect(1).toBe(1);
|
||||
done();
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -29,7 +28,7 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
|
||||
describe("A file upload toolbar button", function () {
|
||||
|
||||
it("appears in MUC chats", mock.initConverse(['chatBoxesFetched'], {}, async (done, _converse) => {
|
||||
it("appears in MUC chats", mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.domain,
|
||||
[{'category': 'server', 'type':'IM'}],
|
||||
|
@ -41,12 +40,11 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
await u.waitUntil(() => _converse.chatboxviews.get('lounge@montague.lit').querySelector('.fileupload'));
|
||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||
expect(view.querySelector('.chat-toolbar .fileupload')).not.toBe(null);
|
||||
done();
|
||||
}));
|
||||
|
||||
describe("when clicked and a file chosen", function () {
|
||||
|
||||
it("is uploaded and sent out from a groupchat", mock.initConverse(['chatBoxesFetched'], {} ,async (done, _converse) => {
|
||||
it("is uploaded and sent out from a groupchat", mock.initConverse(['chatBoxesFetched'], {} ,async (_converse) => {
|
||||
const base_url = 'https://conversejs.org';
|
||||
await mock.waitUntilDiscoConfirmed(
|
||||
_converse, _converse.domain,
|
||||
|
@ -145,10 +143,9 @@ describe("XEP-0363: HTTP File Upload", function () {
|
|||
|
||||
expect(view.querySelector('.chat-msg .chat-msg__media').innerHTML.replace(/<!-.*?->/g, '').trim()).toEqual(
|
||||
`<a target="_blank" rel="noopener" href="${base_url}/logo/conversejs-filled.svg">`+
|
||||
`Download image file "conversejs-filled.svg"</a>`);
|
||||
`Download file "conversejs-filled.svg"</a>`);
|
||||
|
||||
XMLHttpRequest.prototype.send = send_backup;
|
||||
done();
|
||||
}));
|
||||
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue