Compare commits
379 Commits
v9.1.1-cha
...
main-chapr
Author | SHA1 | Date | |
---|---|---|---|
a461444bed | |||
1307f87912 | |||
23cf858a02 | |||
b49146b36f | |||
8d67dce865 | |||
543807aa50 | |||
|
82a8c3f9fc | ||
|
3e680e88d8 | ||
|
005f5374f0 | ||
|
79bb8e76ce | ||
|
61192f91d9 | ||
|
e31d4c7bac | ||
|
c30569dfd3 | ||
|
5e02b9bd5d | ||
|
9114db8764 | ||
|
bc7621c25d | ||
|
ae518aa2c3 | ||
|
e4a4b2819c | ||
|
5310021b67 | ||
|
e18fdd56b1 | ||
|
94963662e7 | ||
|
4f14d50f5d | ||
|
699ab71f21 | ||
|
daeb641530 | ||
|
6e71555ee4 | ||
|
27d61411ab | ||
|
3328e1c94c | ||
|
c63c787a6d | ||
|
0efeead71a | ||
|
5822fade32 | ||
|
621612d126 | ||
|
536350155d | ||
|
841a57fd66 | ||
|
175595098a | ||
|
03655b45b2 | ||
|
fa9ae16ead | ||
|
cb1f929045 | ||
|
9ba339a6d9 | ||
|
bc452596a7 | ||
|
2b732cdb94 | ||
|
1e4937bf41 | ||
|
8b28ffecfe | ||
|
80bf81db6f | ||
|
7fdbcd0e64 | ||
|
975a2c2305 | ||
|
abad4d054b | ||
|
b5b6d1f8b2 | ||
|
64aef7f70d | ||
|
4f0f210e21 | ||
|
c4299e92be | ||
|
0cc03e7e7a | ||
|
f9f246282b | ||
|
3e4a8b23fe | ||
|
cdda140137 | ||
|
0a15b003dc | ||
|
23539ecf47 | ||
|
efa448956e | ||
|
743f336dc5 | ||
|
8a0386fdcc | ||
|
b647f5463f | ||
|
46f2e40f91 | ||
|
8b1b4b6446 | ||
|
9407d00c67 | ||
|
f0d584aed6 | ||
|
2768c2a3ec | ||
|
8fb1716681 | ||
|
507cfa9ef2 | ||
|
5ef7807a68 | ||
|
952d3cf440 | ||
|
c9a95faa35 | ||
|
02185cee5f | ||
|
40290f27e7 | ||
|
bbfcb74eaa | ||
|
a2e618a8f6 | ||
|
ebbb39a9aa | ||
|
2d66a974d0 | ||
|
fa12dae7bc | ||
|
df25e16438 | ||
|
c39d61f62c | ||
|
01ce344d82 | ||
|
bb428fa1d7 | ||
|
5a710e4601 | ||
|
f91f4cb7ab | ||
|
f2ac748e06 | ||
|
27a47c6ee8 | ||
|
0a338feb40 | ||
|
d74daccfbb | ||
|
5c3acc8a4d | ||
|
48e7987f38 | ||
|
4d2a8e9f8d | ||
|
3d0e9a7374 | ||
|
fb054c40be | ||
|
c3c666e568 | ||
|
6b365458f5 | ||
|
885a3d92b5 | ||
|
6df80507f3 | ||
|
16754c15aa | ||
|
70a10efd49 | ||
|
9a63629b40 | ||
|
dcfc0d50f4 | ||
|
fde55bea2c | ||
|
3530ccc35d | ||
|
05c5cd1046 | ||
|
c38c706079 | ||
|
e1e93c2ec9 | ||
|
16b442ac19 | ||
|
0122bd7bab | ||
|
56259fd22e | ||
|
85181053e6 | ||
|
26062df4e5 | ||
|
2fb9fec8f8 | ||
|
d4cb67dc5f | ||
|
ad7b4ae343 | ||
|
57f489f61b | ||
|
6ce8879e9c | ||
|
1ee4cce2fd | ||
|
b69e5b5482 | ||
|
3f6ad0d950 | ||
|
9899351597 | ||
|
0b3bcbfe40 | ||
|
5e3139f563 | ||
|
7b8b32638c | ||
|
8035084e8e | ||
|
19466ddf62 | ||
|
bb7ed5315f | ||
|
6719aeba45 | ||
|
45844447bf | ||
|
82314eb1df | ||
|
d9c86ed575 | ||
|
89d231a5dd | ||
|
72406b4aac | ||
|
e08b58c3d3 | ||
|
387c992381 | ||
|
4e98383e65 | ||
|
dae84028c2 | ||
|
5e5bdc78ec | ||
|
15c10376b0 | ||
|
ccc165facc | ||
|
aa299af3ec | ||
|
b16e3efbd4 | ||
|
5db3e8ca51 | ||
|
0fcdb2a594 | ||
|
5029d93523 | ||
|
bab11b682b | ||
|
9343488864 | ||
|
782c8c97fa | ||
|
d2f7756313 | ||
|
07371b0852 | ||
|
25aa0303c6 | ||
|
6186f05a3e | ||
|
dd4f1e6b85 | ||
|
366390935f | ||
|
7eb07fa02f | ||
|
fbe2e41de7 | ||
|
cfc2d93b9b | ||
|
12170461cd | ||
|
ab079036da | ||
|
8001cf3809 | ||
|
d93a47d246 | ||
|
76fb90319b | ||
|
9ca1114670 | ||
|
251784c21c | ||
|
8c3ce2892b | ||
|
6c44fb2a2f | ||
|
465aa38222 | ||
|
6fc83db9df | ||
|
a486fe25b8 | ||
|
87c172acd0 | ||
|
968ec8182c | ||
|
451ed18261 | ||
|
078936fb8a | ||
|
0227b2e06c | ||
|
44c22bb984 | ||
|
da12005420 | ||
|
de65409e79 | ||
|
9ab99a0859 | ||
|
77fdbb5aa2 | ||
|
a8f4bc4617 | ||
|
18cd864e09 | ||
|
192861f9c9 | ||
|
4a43b46432 | ||
|
95bf14385b | ||
|
0fe2df24ad | ||
|
5a368a9c0f | ||
|
73b9cbea13 | ||
|
2dee891c63 | ||
|
97b87bcdc1 | ||
|
c10f18ad1c | ||
|
4d687b022a | ||
|
53d562481e | ||
|
7539fc1a6f | ||
|
f740332e95 | ||
|
4abc9c45d3 | ||
|
756942d2bd | ||
|
fdcab413f5 | ||
|
3c6dd5d0ae | ||
|
3004d7bff5 | ||
|
6494b34da3 | ||
|
447fe8ba08 | ||
|
8ab0b718de | ||
|
97e5bb9b11 | ||
|
5f38a914b1 | ||
|
fa132567da | ||
|
5ea00b8008 | ||
|
a76393f216 | ||
|
739d79e90c | ||
|
e63ba2075f | ||
|
8ca265d8d5 | ||
|
78a7841afb | ||
|
4733e7f65f | ||
|
8c84e276ff | ||
|
a251608fc5 | ||
|
3af6168270 | ||
|
34c17a7662 | ||
|
87aba699f1 | ||
|
defe5cdcb0 | ||
|
038a38cae0 | ||
|
8aaf792c6c | ||
|
659a69e7b7 | ||
|
ec70d4bc56 | ||
|
b0b8a3f89d | ||
|
72be204f77 | ||
|
d5deed0871 | ||
|
040d5bae9d | ||
|
b2ad6e844c | ||
|
b4fb710d61 | ||
|
a5be19ad50 | ||
|
6250be89ed | ||
|
e355814da7 | ||
|
1ecffd61af | ||
|
d887ea4b7b | ||
|
b2943351a1 | ||
|
0b514fbc3e | ||
|
0d743d428f | ||
|
0b809049e2 | ||
|
8b64482fd4 | ||
|
996e47b275 | ||
|
c3ff3370bd | ||
|
83726f0821 | ||
|
1ef2c3b3d4 | ||
|
0af4b185ac | ||
|
a3a90a9be8 | ||
|
7861af7bf9 | ||
|
e90cf6453e | ||
|
dbd36a0d75 | ||
|
c6bc5e1438 | ||
|
cc6a20b3e6 | ||
|
40024f4599 | ||
|
18d8b69f00 | ||
|
62cd0afa37 | ||
|
eb9fd74c77 | ||
|
6c9f0400c8 | ||
|
210d4cb87d | ||
|
7b6301d7f3 | ||
|
63cc8411a5 | ||
|
21c41f9265 | ||
|
83351fb98f | ||
|
4531dd4363 | ||
|
31b10aa0b0 | ||
|
ef16a52ced | ||
|
1a8ae3dcbe | ||
|
34a4a70ae2 | ||
|
f791169f47 | ||
|
782de0165f | ||
|
e555469e3f | ||
|
b4eb6c0994 | ||
|
74cda3f4b4 | ||
|
aebd98cf0a | ||
|
983d5ea09c | ||
|
3720092c09 | ||
|
36cb7a644e | ||
|
6293efd8ee | ||
|
5760379e0a | ||
|
a4ee3085b7 | ||
|
bd60861c1e | ||
|
fbe86e5af8 | ||
|
927add0707 | ||
|
8378ce739b | ||
|
f9e6c3ff95 | ||
|
98ae55790d | ||
|
bb0ad432b2 | ||
|
55e3417df3 | ||
|
92f71bafb2 | ||
|
f1734dbb40 | ||
|
0489e0f902 | ||
|
0aa66fee99 | ||
|
ed1099490c | ||
|
a74bbd093f | ||
|
8ae4b6abd4 | ||
|
ca3c8fc10b | ||
|
e8b9681a15 | ||
|
51516e38f6 | ||
|
de72d6bae3 | ||
|
0d9561e666 | ||
|
97be0bd8ac | ||
|
7f99b24e28 | ||
|
17e5804be7 | ||
|
fd9e41a917 | ||
|
760f4f7dd1 | ||
|
5231262bda | ||
|
34927b5b77 | ||
|
7028286855 | ||
|
342c75775b | ||
|
f1cc8c85f4 | ||
|
c5588e3c49 | ||
|
3d19def3e7 | ||
|
53521c2236 | ||
|
22170450b4 | ||
|
f014db8b7a | ||
|
fe9345b7fc | ||
|
d2bdce51e8 | ||
|
cac92c3ebc | ||
|
603f8349e8 | ||
|
5f6e70289b | ||
|
c716551fc8 | ||
|
78634cdec3 | ||
|
45e989f048 | ||
|
0cfe2a18af | ||
|
71d1383604 | ||
|
0df1c1880e | ||
|
06a2aac022 | ||
|
d8ea42a845 | ||
|
085de8405f | ||
|
990aefc6cb | ||
|
eb29d962f5 | ||
|
605c02901a | ||
|
368bcf258f | ||
|
07efd601da | ||
|
4aab83c4af | ||
|
f05a41f6a5 | ||
|
4d9c0d3f9a | ||
|
35bdcf58cd | ||
|
3ca478da32 | ||
|
5d3da20bd9 | ||
|
a95c070c2b | ||
|
c14c2b3b59 | ||
|
b7e793df02 | ||
|
c245599e65 | ||
|
7e86c561c7 | ||
|
25891ef0ee | ||
|
ba7c6d7a50 | ||
|
7e2dd55c16 | ||
|
b8579ccc90 | ||
|
73a7b209ce | ||
|
8dc8b1d564 | ||
|
d2b4f2e0f6 | ||
|
320f11f795 | ||
|
4237e5b3ae | ||
|
8671afc4b1 | ||
|
6d39fae71a | ||
|
0316f073e0 | ||
|
1744dbc420 | ||
|
eb8ebea920 | ||
|
6c13cfaf30 | ||
|
a228cf244c | ||
|
3b124cfdce | ||
|
4c8bc187b2 | ||
|
1ad6de2dd6 | ||
|
795a9a7e3e | ||
|
87ecf061f1 | ||
|
858a6051ac | ||
|
52693bfc0b | ||
|
79063b9993 | ||
|
22840f8a50 | ||
|
92f74b888b | ||
|
ac241e5ed1 | ||
|
6573d080e4 | ||
|
12a0d0e3cc | ||
|
2a7bfd13b5 | ||
|
ab7e879261 | ||
|
a57853156e | ||
|
a3c0f90476 | ||
|
b5bcc05947 | ||
|
bd579d8613 | ||
|
0c02fbe4cf | ||
|
0b172be3d0 | ||
|
7e9861ba8f | ||
|
0c9fd691ac | ||
|
984386aa64 |
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module",
|
||||
"allowImportExportEverywhere": true
|
||||
},
|
||||
|
@ -10,8 +10,12 @@
|
|||
"jasmine": true,
|
||||
"es6": true
|
||||
},
|
||||
"plugins": [],
|
||||
"extends": ["eslint:recommended"],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"globals": {
|
||||
"Uint8Array": true,
|
||||
"Promise": true,
|
||||
|
|
6
.github/CONTRIBUTING.md
vendored
|
@ -4,9 +4,9 @@ Thanks for contributing to [Converse.js](https://conversejs.org)
|
|||
|
||||
## Support questions
|
||||
|
||||
The Github issue tracker is used for bugs reports and feature requests, not for general tech support.
|
||||
The Github issue tracker is used for bug reports and feature requests, not for general tech support.
|
||||
|
||||
For support, you can join your [XMPP webchat](https://inverse.chat/#converse/room?jid=discuss@conference.conversejs.org).
|
||||
For support, you can join our [XMPP webchat](https://inverse.chat/#converse/room?jid=discuss@conference.conversejs.org).
|
||||
Instead of the webchat, you can also open the room in your XMPP client, [click here](xmpp://discuss@conference.conversejs.org?join).
|
||||
|
||||
You can also ask questions on [StackOverflow](https://stackoverflow.com/questions/tagged/converse.js)
|
||||
|
@ -23,6 +23,6 @@ Please read the [style guide](https://conversejs.org/docs/html/style_guide.html)
|
|||
|
||||
Add a test for any bug fixed or feature added.
|
||||
|
||||
Tests are can be find in various `./test` folders in the Converse source code.
|
||||
Tests can be found in various `./tests` folders in the Converse source code.
|
||||
|
||||
To run the tests, you can run `make check` on Linux and Mac, or `./node_modules/bin/karma start karma.conf` on Windows.
|
||||
|
|
2
.github/codeql-config.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
paths-ignore:
|
||||
- '**/tests/*.js'
|
43
.github/workflows/codeql.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "11 18 * * 6"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ javascript ]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: ./.github/codeql-config.yml
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
if: ${{ matrix.language == 'javascript' }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
3
.github/workflows/karma-tests.yml
vendored
|
@ -8,6 +8,7 @@ on:
|
|||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -17,7 +18,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
node-version: [18.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
|
|
3
.gitignore
vendored
|
@ -1,6 +1,9 @@
|
|||
# Distribution directory
|
||||
dist
|
||||
|
||||
# conversejs/media repo checkout
|
||||
media
|
||||
|
||||
# Editor fluff
|
||||
*~
|
||||
.sw?
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"arrowParens": "avoid",
|
||||
"printWidth": 120,
|
||||
"quoteProps": "preserve",
|
||||
"singleQuote": true,
|
||||
|
|
36541
3rdparty/libsignal-protocol.js
vendored
Normal file
1
3rdparty/libsignal-protocol.min.js
vendored
Normal file
85
CHANGES.md
|
@ -1,5 +1,88 @@
|
|||
# Changelog
|
||||
|
||||
## 10.1.6 (2023-08-31)
|
||||
|
||||
- #3246: Badge color not responsive to dark theme
|
||||
- Fix a GIF rendering bug that causes a memory overflow
|
||||
|
||||
## 10.1.5 (2023-06-29)
|
||||
|
||||
- #3209: Fix error when importing the `converse` global with bootstrap modal API
|
||||
- #3207: `.po` translation files weren't included in previous release
|
||||
- Updated Galician and Portuguese translations
|
||||
|
||||
## 10.1.4 (2023-06-25)
|
||||
|
||||
- Fix `dist` directory not included in NPM package
|
||||
|
||||
## 10.1.3 (2023-06-23)
|
||||
|
||||
- Add the ability to set roles and affiliations via the MUC occupant modal
|
||||
- Fix `isOnlyEmojis is not a function` when using only `@converse/headless`
|
||||
- Fix `autojoin` checkbox state in MUC bookmark form
|
||||
- Remove call to `api.confirm` in `@converse/headless`
|
||||
- Generate TypeScript declaration files into `dist/types`
|
||||
- Removed documentation about the no longer implemented `fullname` option.
|
||||
- Updated translations
|
||||
- #3123: Contacts do not show up online until chat is opened with them.
|
||||
- #3156: Add function to prevent drag stutter effect over iframes when resize is called in overlay mode
|
||||
- #3165: Use configured nickname in profile view in the control box
|
||||
|
||||
- New config option [stanza_timeout](https://conversejs.org/docs/html/configuration.html#stanza-timeout)
|
||||
|
||||
## 10.1.2 (2023-02-17)
|
||||
|
||||
- #1490: Busy-loop when fetching registration form fails
|
||||
- #1556: Can't switch to registration form afrer logout
|
||||
- #3137: Various UI/UX bugfixes regarding the registration form
|
||||
- XEP-0437: Room Activity Indicators (RAI) optimizations
|
||||
|
||||
## 10.1.1 (2023-02-15)
|
||||
|
||||
- #1851: Sort open groupchats alphabetically
|
||||
- #2240: Ad-Hoc command result form not shown
|
||||
- #3128: Second bookmarked room shows info of the first one
|
||||
- Bugfix. Uyghur translations weren't loading
|
||||
|
||||
## 10.1.0 (2023-01-07)
|
||||
|
||||
- #326: Add the ability to reset your password
|
||||
- #2759: Don't automatically log in again if the user manually logged out
|
||||
- #2816: Chat highlight behaves odd
|
||||
- #2925: File upload is not always enabled
|
||||
- #3001: Add option to save SCRAM details and to use them to stay logged in upon reload
|
||||
- Add a "Add to Contacts" button in MUC occupant modals
|
||||
- Updated translations and add support for Uyghur
|
||||
|
||||
- New config option [reuse_scram_keys](https://conversejs.org/docs/html/configuration.html#reuse-scram-keys)
|
||||
|
||||
## 10.0.0 (2022-10-30)
|
||||
|
||||
- Update to Strophe.js 1.6.0 which adds support for SCRAM-SHA-256 and SCRAM-SHA-512
|
||||
- Don't automatically convert OpenStreetMap URLs into `geo:` URIs in sent messages
|
||||
- Remove the `allow_chat_pending_contacts` config option.
|
||||
- Show roster contacts with `subscription` set to `none`
|
||||
- Remove the `converse-carbons` plugin and make carbons part of the `converse-chat` plugin.
|
||||
- Remove the `message_carbons` configuration setting. Carbons are now always enabled.
|
||||
- Move the `converse-oauth` plugin to the [community-plugins](https://github.com/conversejs/community-plugins)
|
||||
- Don't apply message corrections when the MUC occupant-id doesn't match.
|
||||
- Update `nick` attribute on ChatRoom when user nickname changes
|
||||
- Restrict editing of MUC messages to ones with the same XEP-0421 occupant ID
|
||||
- Remove unfurls for links removed in a subsequent message correction
|
||||
- Bookmarks now appear in a modal and the `hide_open_bookmarks` config setting has been removed.
|
||||
- #1004: Stop using fonts to render icons and use SVG instead
|
||||
- #2797: Weird unicode characters rendering outside of line-height
|
||||
- #2870: Fix for multiple URLs to be linkified when sent together in chat and adds a test for this.
|
||||
- #2879: Quotes, lines not aligned to the first line
|
||||
- #2925: Fix missing disco-items in browser storage.
|
||||
- #2936: Fix documentation about enable_smacks option, which is true by default.
|
||||
- #2939: Data forms with a field named "username" are not displayed
|
||||
- #3005: Fix MUC messages with a fallback body not rendering.
|
||||
- #3007: Fix links becoming text when a message is edited
|
||||
- #3018: Fix MUC icons not functioning.
|
||||
- #3028: Fix encrypted media from Conversations/Quicksy not properly decrypting
|
||||
|
||||
|
||||
## 9.1.1 (2022-05-05)
|
||||
|
||||
- GIFs don't render inside unfurls and cause a TypeError
|
||||
|
@ -274,7 +357,7 @@ Soon we'll deprecate the latter, so prepare now.
|
|||
|
||||
## 6.0.0 (2020-01-09)
|
||||
|
||||
- [enable_smacks](https://conversejs.org/docs/html/configuration.html#enable-smacks) is not set to `true` by default.
|
||||
- [enable_smacks](https://conversejs.org/docs/html/configuration.html#enable-smacks) is now set to `true` by default.
|
||||
- Refactor some presence and status handling code from `converse-core` into `@converse/headless/converse-status`.
|
||||
- It's now possible to navigate the emoji-picker via the keyboard arrow keys.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
* An XMPP chat client that runs in the browser.
|
||||
*
|
||||
* Version: 9.1.1
|
||||
* Version: 10.1.6
|
||||
*
|
||||
* Copyright: JC Brand 2013-2018
|
||||
* Except for 3rd party dependencies.
|
||||
|
|
85
Makefile
|
@ -1,16 +1,13 @@
|
|||
# You can set these variables from the command line.
|
||||
BABEL ?= node_modules/.bin/babel
|
||||
BOOTSTRAP = ./node_modules/
|
||||
BUILDDIR = ./docs
|
||||
KARMA ?= ./node_modules/.bin/karma
|
||||
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss
|
||||
ESLINT ?= ./node_modules/.bin/eslint
|
||||
HTTPSERVE ?= ./node_modules/.bin/http-server
|
||||
HTTPSERVE_PORT ?= 8000
|
||||
INKSCAPE ?= inkscape
|
||||
INSTALL ?= install
|
||||
JSDOC ?= ./node_modules/.bin/jsdoc
|
||||
LERNA ?= ./node_modules/.bin/lerna
|
||||
OXIPNG ?= oxipng
|
||||
PAPER =
|
||||
RJS ?= ./node_modules/.bin/r.js
|
||||
|
@ -61,13 +58,14 @@ serve: node_modules dist
|
|||
serve_bg: node_modules
|
||||
$(HTTPSERVE) -p $(HTTPSERVE_PORT) -c-1 -s &
|
||||
|
||||
certs:
|
||||
mkdir certs
|
||||
cd certs && openssl req -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out chat.example.org.crt -keyout chat.example.org.key
|
||||
|
||||
########################################################################
|
||||
## Translation machinery
|
||||
|
||||
dist/converse-no-dependencies.js: src webpack/webpack.common.js webpack/webpack.nodeps.js @converse/headless node_modules
|
||||
npm run nodeps
|
||||
|
||||
GETTEXT = $(XGETTEXT) --from-code=UTF-8 --language=JavaScript --keyword=__ --keyword=___ --keyword=i18n_ --force-po --output=src/i18n/converse.pot --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=9.1.1 dist/converse-no-dependencies.js -c
|
||||
GETTEXT = $(XGETTEXT) --from-code=UTF-8 --language=JavaScript --keyword=__ --keyword=___ --keyword=i18n_ --force-po --output=src/i18n/converse.pot --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=10.1.6 dist/converse-no-dependencies.js -c
|
||||
|
||||
src/i18n/converse.pot: dist/converse-no-dependencies.js
|
||||
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
|
||||
|
@ -84,9 +82,9 @@ po:
|
|||
########################################################################
|
||||
## Release management
|
||||
|
||||
.PHONY: release
|
||||
release:
|
||||
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/core.js
|
||||
.PHONY: version
|
||||
version:
|
||||
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/shared/constants.js
|
||||
$(SED) -i '/Version:/s/:.*/: $(VERSION)/' COPYRIGHT
|
||||
$(SED) -i '/Project-Id-Version:/s/:.*/: Converse.js $(VERSION)\n"/' src/i18n/converse.pot
|
||||
$(SED) -i '/"version":/s/:.*/: "$(VERSION)",/' manifest.json
|
||||
|
@ -101,19 +99,33 @@ release:
|
|||
make pot
|
||||
make po
|
||||
make dist
|
||||
npm pack
|
||||
cd src/headless && npm pack
|
||||
|
||||
release-checkout:
|
||||
git clone git@github.com:conversejs/converse.js.git --depth 1 --branch $(BRANCH) release-$(BRANCH)
|
||||
cd release-$(BRANCH) && make dist
|
||||
|
||||
.PHONY: publish
|
||||
publish:
|
||||
make release-checkout
|
||||
cd release-$(BRANCH) && npm pack && npm publish
|
||||
cd release-$(BRANCH)/src/headless && npm pack && npm publish
|
||||
find ./release-$(BRANCH)/ -name "converse.js-*.tgz" -exec mv {} . \;
|
||||
find ./release-$(BRANCH)/src/headless -name "converse-headless-*.tgz" -exec mv {} . \;
|
||||
rm -rf release-$(BRANCH)
|
||||
|
||||
.PHONY: postrelease
|
||||
postrelease:
|
||||
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)dev";/' src/headless/core.js
|
||||
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)dev";/' src/headless/shared/constants.js
|
||||
|
||||
.PHONY: deploy
|
||||
deploy:
|
||||
git clone --branch v$(VERSION) git@github.com:conversejs/converse.js.git --depth 1 $(VERSION)
|
||||
cd $(VERSION) && make node && ASSET_PATH=https://cdn.conversejs.org/$(VERSION)/dist/ make dist && make doc
|
||||
cd .. && git pull && make node && ASSET_PATH=https://cdn.conversejs.org/dist/ make dist && make doc
|
||||
|
||||
########################################################################
|
||||
## Install dependencies
|
||||
|
||||
$(LERNA):
|
||||
npm install lerna
|
||||
|
||||
${NVM_DIR}/nvm.sh:
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
|
||||
source ~/.bashrc
|
||||
|
@ -125,12 +137,9 @@ nvm: ${NVM_DIR}/nvm.sh
|
|||
node: .nvmrc
|
||||
. $(HOME)/.nvm/nvm.sh && nvm install
|
||||
|
||||
package-lock.json: package.json
|
||||
node_modules: package.json src/headless/package.json
|
||||
npm install
|
||||
|
||||
node_modules: $(LERNA) package.json package-lock.json src/headless/package.json src/headless/package-lock.json
|
||||
npm run lerna
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
npm run clean
|
||||
|
@ -147,6 +156,9 @@ devserver: node_modules
|
|||
########################################################################
|
||||
## Builds
|
||||
|
||||
dist/converse-no-dependencies.js: src webpack/webpack.common.js webpack/webpack.nodeps.js @converse/headless node_modules
|
||||
npm run nodeps
|
||||
|
||||
dist/converse.js:: node_modules
|
||||
npm run build
|
||||
|
||||
|
@ -193,7 +205,16 @@ src/headless/dist/converse-headless.min.js: src webpack/webpack.common.js node_m
|
|||
|
||||
dist:: node_modules src/* | dist/website.css dist/website.min.css
|
||||
npm run headless
|
||||
npm run build
|
||||
# Ideally this should just be `npm run build`.
|
||||
# The additional steps are necessary to properly generate JSON chunk files
|
||||
# from the .po files. The nodeps config uses preset-env with IE11.
|
||||
# Somehow this is necessary.
|
||||
npm run nodeps
|
||||
$(eval TMPD := $(shell mktemp -d))
|
||||
mv dist/locales $(TMPD) && \
|
||||
npm run build && \
|
||||
mv $(TMPD)/locales/*-po.js dist/locales/ && \
|
||||
rm -rf $(TMPD)
|
||||
|
||||
.PHONY: install
|
||||
install:: dist
|
||||
|
@ -202,23 +223,24 @@ install:: dist
|
|||
cdn:: node_modules
|
||||
npm run cdn
|
||||
|
||||
.PHONY: types
|
||||
types:: node_modules
|
||||
npm run types
|
||||
|
||||
########################################################################
|
||||
## Tests
|
||||
|
||||
.PHONY: eslint
|
||||
eslint: node_modules
|
||||
$(ESLINT) src/*.js
|
||||
$(ESLINT) src/utils/*.js
|
||||
$(ESLINT) src/headless/*.js
|
||||
$(ESLINT) src/headless/utils/*.js
|
||||
npm run lint
|
||||
|
||||
.PHONY: check
|
||||
check: eslint | dist/converse.js dist/converse.css
|
||||
$(KARMA) start karma.conf.js $(ARGS)
|
||||
npm run test -- $(ARGS)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(KARMA) start karma.conf.js $(ARGS)
|
||||
npm run test -- $(ARGS)
|
||||
|
||||
########################################################################
|
||||
## Documentation
|
||||
|
@ -226,13 +248,10 @@ test:
|
|||
./bin/activate:
|
||||
python3 -m venv .
|
||||
|
||||
.installed.cfg: requirements.txt buildout.cfg
|
||||
./bin/pip install -r requirements.txt
|
||||
.PHONY: docsdev
|
||||
docsdev: ./bin/activate requirements.txt
|
||||
./bin/pip install --upgrade pip==21.3.1
|
||||
./bin/pip install --upgrade setuptools==51.3.3
|
||||
./bin/buildout -v
|
||||
|
||||
docsdev: ./bin/activate .installed.cfg
|
||||
./bin/pip install -r requirements.txt
|
||||
|
||||
.PHONY: html
|
||||
html: doc
|
||||
|
|
17
README.chapril.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Construire une version Chapril de ConverseJS
|
||||
|
||||
```
|
||||
cd .../conversejs
|
||||
# La première fois, installer nvm (attention, ça va modifier le .bashrc, entre autres choses)
|
||||
make nvm
|
||||
|
||||
export V=7.0.4
|
||||
git rebase v${V?}
|
||||
# [... Résoudre les conflits]
|
||||
git checkout -b v${V?}-chapril
|
||||
nvm install
|
||||
make dist
|
||||
# [... Tester les livrables présents dans dist/, et si tout est ok :]
|
||||
make version VERSION=${V?}-chapril
|
||||
rsync -av dist/ chapril-xmpp:/var/www/xmpp.chapril.org/public_html/dist-custom-chapril-${V?}/
|
||||
```
|
25
README.md
|
@ -166,28 +166,23 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
|
|||
|
||||
## Sponsors
|
||||
|
||||
<p>
|
||||
<a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="BairesDev" src="https://raw.githubusercontent.com/conversejs/media/main/logos/bairesdev-primary.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="Blokt Crypto & Privacy" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/blokt.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="Codefirst" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/codefirst.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://originalenergie.de/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="Original Energie" src="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/originalenergie-black.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="Prime Sound" src="https://raw.githubusercontent.com/conversejs/media/main/logos/primesound.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
|
||||
</a>
|
||||
</p>
|
||||
|
|
23
RELEASE.md
|
@ -1,19 +1,18 @@
|
|||
# Release checklist
|
||||
|
||||
1. Check that weblate translations are all merged in
|
||||
1. Merge weblate translations: https://hosted.weblate.org/projects/conversejs/translations/#repository
|
||||
2. Run `make check` to check that all tests pass.
|
||||
3. Run `make release VERSION=9.1.1`
|
||||
3. Run `make version VERSION=10.1.6`
|
||||
4. Do a `git diff` to check if things look sane.
|
||||
5. Do a quick manual test with the `dist` files (via `index.html`)
|
||||
6. `git commit -am "Release 9.1.1"`
|
||||
7. `git tag -s v9.1.1 -m "Release 9.1.1"`
|
||||
8. Run `git push && git push origin v9.1.1`
|
||||
9. Update https://conversejs.org
|
||||
* `cd /home/conversejs/converse.js`
|
||||
* `git clone --branch v9.1.1 git@github.com:conversejs/converse.js.git 9.1.1`
|
||||
* `cd 9.1.1 && nvm install && ASSET_PATH=https://cdn.conversejs.org/9.1.1/dist/ make dist && make doc`
|
||||
* `cd .. && git pull && nvm install && ASSET_PATH=https://cdn.conversejs.org/dist/ make dist && make doc`
|
||||
6. `git commit -am "Release 10.1.6"`
|
||||
7. `git tag -s v10.1.6 -m "Release 10.1.6"`
|
||||
8. `git push && git push origin v10.1.6`
|
||||
9. `make publish BRANCH=v10.1.6`
|
||||
10. Update release page on Github
|
||||
11. Run `npm publish && cd src/headless/ && npm publish`
|
||||
* Upload tar files
|
||||
11. Update https://conversejs.org
|
||||
* `cd /home/conversejs/converse.js`
|
||||
* `make deploy VERSION=10.1.6`
|
||||
12. Update the repository on weblate
|
||||
13. Decide on next release number and run `make postrelease VERSION=9.1.2`
|
||||
13. Decide on next release number and run `make postrelease VERSION=10.1.7`
|
||||
|
|
10
babel.config.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"browsers": [">1%", "not ie 11", "not op_mini all", "not dead"]
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
15
buildout.cfg
|
@ -1,15 +0,0 @@
|
|||
[buildout]
|
||||
parts =
|
||||
sphinx
|
||||
|
||||
versions = versions
|
||||
|
||||
[sphinx]
|
||||
recipe = zc.recipe.egg
|
||||
eggs =
|
||||
Sphinx
|
||||
sphinx-bootstrap-theme
|
||||
|
||||
[versions]
|
||||
Sphinx = 4.5.0
|
||||
docutils = 0.17.1
|
|
@ -1,23 +0,0 @@
|
|||
# How to use saved Chrome/Chromium logs to replay events
|
||||
|
||||
**NOTE**: This feature is very experimental and in many cases doesn't work
|
||||
without data massaging and ugly hacks.
|
||||
|
||||
It's possible to save the log output from Chrome/Chromium (I haven't tried this
|
||||
yet with any other browser) and then to replay that log output in the browser.
|
||||
|
||||
This can be a very helpful technique to track down bugs.
|
||||
|
||||
To do this, follow the following steps:
|
||||
|
||||
1. Save the log file (right click and then click "Save as" in the browser's console).
|
||||
2. Rename the log file, making sure it ends in `.html`
|
||||
3. Move the log file to the `converse-logs` directory in the converse.js repo.
|
||||
4. Add `<log>` to the top of the log file and `</log>` to the bottom of the log file.
|
||||
5. In `converse-logs/converse-logs.js`, add a new entry for the log file (don't
|
||||
include the `.html` part of the file name.
|
||||
6. Make sure that `spec/transcripts` is "required"-ed in `tests/main.js`
|
||||
6. Open `tests.html` in your browser.
|
||||
|
||||
Your logs will run first, and then all the other tests will run afterwards.
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
define("transcripts", [
|
||||
"tpl!../../converse-logs/missing_messages",
|
||||
], function () {
|
||||
return arguments;
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rdf:RDF xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xmpp="https://linkmauve.fr/ns/xmpp-doap#" xml:lang="en">
|
||||
<rdf:RDF xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xmpp="https://linkmauve.fr/ns/xmpp-doap#" xmlns:schema="https://schema.org/" xml:lang="en">
|
||||
<Project xmlns="http://usefulinc.com/ns/doap#">
|
||||
<name>Converse.js</name>
|
||||
<shortdesc>Browser based XMPP chat client</shortdesc>
|
||||
|
@ -9,6 +9,11 @@
|
|||
<support-forum rdf:resource="xmpp:discuss@conference.conversejs.org?join"/>
|
||||
<category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-client"/>
|
||||
<programming-language>JavaScript</programming-language>
|
||||
<os>Browser</os>
|
||||
<os>Linux</os>
|
||||
<os>macOS</os>
|
||||
<os>Windows</os>
|
||||
<schema:logo rdf:resource="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/conversejs-filled.svg"/>
|
||||
<repository>
|
||||
<GitRepository>
|
||||
<browse rdf:resource="https://github.com/conversejs/converse.js"/>
|
||||
|
|
8
dev.html
|
@ -28,18 +28,22 @@
|
|||
});
|
||||
|
||||
converse.initialize({
|
||||
i18n: 'af',
|
||||
theme: 'dracula',
|
||||
auto_away: 300,
|
||||
enable_smacks: true,
|
||||
loglevel: 'debug',
|
||||
reuse_scram_keys: true,
|
||||
prune_messages_above: 100,
|
||||
message_archiving: 'always',
|
||||
muc_respect_autojoin: true,
|
||||
muc_show_logs_before_join: true,
|
||||
notify_all_room_messages: ['discuss@conference.conversejs.org'],
|
||||
view_mode: 'fullscreen',
|
||||
websocket_url: 'wss://conversejs.org/xmpp-websocket',
|
||||
// websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
|
||||
// websocket_url: 'wss://conversejs.org/xmpp-websocket',
|
||||
websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
|
||||
whitelisted_plugins: ['converse-debug'],
|
||||
// connection_options: { worker: '/dist/shared-connection-worker.js' }
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -2,25 +2,21 @@
|
|||
<h4 class="sidebar-title">Sponsored by</h4>
|
||||
</span>
|
||||
<ul class="sponsors-list">
|
||||
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="height: 2.5em" src="/logo/keycdn.png" alt="KeyCDN">
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="https://www.codefirst.co.uk/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="width: 9em" src="/logo/codefirst.png" alt="Codefirst">
|
||||
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="width: 10em" src="/media/logos/bairesdev-primary.png" alt="BairesDev">
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="https://blokt.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="width: 9em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy">
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="https://originalenergie.de/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="width: 9em" src="/logo/originalenergie-black.png" alt="Original Energie">
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="width: 9em" src="/media/logos/primesound.png" alt="Prime Sound">
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||
<img style="height: 2.5em" src="/logo/keycdn.png" alt="KeyCDN">
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="centered-text-container patreon-link-container"><a href="https://conversejs.org#sponsors">Become a sponsor</a></span>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/api/index.rst">Edit me on GitHub</a></div>
|
||||
|
||||
============================================
|
||||
The API documentation (generated with JSDoc)
|
||||
|
|
|
@ -48,9 +48,9 @@ copyright = u'2018, JC Brand'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '9.1.1'
|
||||
version = '10.1.6'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '9.1.1'
|
||||
release = '10.1.6'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
|
@ -150,13 +150,6 @@ This setting is only applicable if the ``converse-bookmarks`` plugin is loaded.
|
|||
|
||||
See also: `allow_public_bookmarks`_
|
||||
|
||||
allow_chat_pending_contacts
|
||||
---------------------------
|
||||
|
||||
* Default: ``false``
|
||||
|
||||
Allow the user to chat with pending contacts.
|
||||
|
||||
allow_contact_removal
|
||||
---------------------
|
||||
|
||||
|
@ -414,7 +407,6 @@ in to their XMPP account.
|
|||
So currently if EITHER ``keepalive`` or ``auto_login`` is ``true`` and
|
||||
`authentication`_ is set to ``login``, then Converse will try to log the user in.
|
||||
|
||||
|
||||
auto_away
|
||||
---------
|
||||
|
||||
|
@ -701,7 +693,7 @@ appear in another.
|
|||
.. code-block:: javascript
|
||||
|
||||
converse.initialize({
|
||||
connection_options: { 'worker': true }
|
||||
connection_options: { worker: '/dist/shared-connection-worker.js' }
|
||||
});
|
||||
|
||||
|
||||
|
@ -760,6 +752,8 @@ loglevel
|
|||
|
||||
You can also set this value by changing a URL fragment `#converse?loglevel=debug`
|
||||
|
||||
.. _`dark_theme`:
|
||||
|
||||
dark_theme
|
||||
----------
|
||||
|
||||
|
@ -907,7 +901,7 @@ The app servers are specified with the `push_app_servers`_ option.
|
|||
enable_smacks
|
||||
-------------
|
||||
|
||||
* Default: ``false``
|
||||
* Default: ``true``
|
||||
|
||||
Determines whether `XEP-0198 Stream Management <https://xmpp.org/extensions/xep-0198.html>`_
|
||||
support is turned on or not.
|
||||
|
@ -931,12 +925,6 @@ filter_url_query_params
|
|||
|
||||
Accepts a string or array of strings. Any query strings from URLs that match this setting will be removed.
|
||||
|
||||
fullname
|
||||
--------
|
||||
|
||||
If you are using prebinding, can specify the fullname of the currently
|
||||
logged in user, otherwise the user's vCard will be fetched.
|
||||
|
||||
geouri_regex
|
||||
------------
|
||||
|
||||
|
@ -967,22 +955,6 @@ hide_offline_users
|
|||
|
||||
If set to ``true``, then offline users aren't shown in the roster.
|
||||
|
||||
hide_open_bookmarks
|
||||
-------------------
|
||||
|
||||
* Default: ``false`` (``true`` when the `view_mode`_ is set to ``fullscreen``).
|
||||
|
||||
This setting applies to the ``converse-bookmarks`` plugin and specfically the
|
||||
list of bookmarks shown in the ``Rooms`` tab of the control box.
|
||||
|
||||
By default all bookmarks are shown in that list, if this setting is set to
|
||||
``true``, then only bookmarks for rooms not currently open (i.e. that the
|
||||
current user hasn't joined), are shown.
|
||||
|
||||
Makes sense to set this to ``true`` when also using the non-core
|
||||
``converse-roomslist`` plugin, which shows a list of currently open (i.e.
|
||||
"joined") rooms.
|
||||
|
||||
.. _`i18n`:
|
||||
|
||||
i18n
|
||||
|
@ -1207,22 +1179,6 @@ from the XMPP server.
|
|||
|
||||
Used in conjunction with ``message_archiving`` and in context of `XEP-0313: Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_.
|
||||
|
||||
message_carbons
|
||||
---------------
|
||||
|
||||
* Default: ``true``
|
||||
|
||||
Support for `XEP-0280: Message Carbons <https://xmpp.org/extensions/xep-0280.html>`_
|
||||
|
||||
In order to keep all IM clients for a user engaged in a conversation,
|
||||
outbound messages are carbon-copied to all interested resources.
|
||||
|
||||
This is especially important with Converse, where each browser
|
||||
tab serves as a separate IM client.
|
||||
|
||||
XEP-0280 requires server support, so make sure that message carbons are enabled
|
||||
on your server.
|
||||
|
||||
|
||||
message_limit
|
||||
-------------
|
||||
|
@ -1857,6 +1813,27 @@ Based on the OGP metadata Converse will render a URL preview (also known as an
|
|||
the ``show_images_inline``, ``embed_audio`` and ``embed_videos`` settings.
|
||||
|
||||
|
||||
reuse_scram_keys
|
||||
----------------
|
||||
|
||||
* Default: ``false``
|
||||
|
||||
Most XMPP servers enable the Salted Challenge Response Authentication Mechanism
|
||||
or SCRAM for short. This allows the user and the server to mutually
|
||||
authenticate *without* the need to transmit the user's password in plaintext.
|
||||
|
||||
Assuming the server does not alter the user's password or the
|
||||
storage parameters, we can authenticate with the same SCRAM key multiple times.
|
||||
|
||||
This opens an opportunity: we can store the user's login credentials in the
|
||||
browser without storing the sensitive plaintext password, or the
|
||||
need to set up complicated third party backends, like OAuth.
|
||||
|
||||
Enabling this option will let Converse save a user's SCRAM keys upon successful
|
||||
login, and next time Converse is loaded the user will be automatically logged in
|
||||
with those SCRAM keys.
|
||||
|
||||
|
||||
.. _`roomconfig_whitelist`:
|
||||
|
||||
roomconfig_whitelist
|
||||
|
@ -2106,6 +2083,13 @@ themselves).
|
|||
In order to support all browsers we need both an MP3 and an Ogg file. Make sure
|
||||
to name your files ``msg_received.ogg`` and ``msg_received.mp3``.
|
||||
|
||||
stanza_timeout
|
||||
--------------
|
||||
|
||||
* Default: ``20000`` (20 seconds)
|
||||
|
||||
The time to wait, in milliseconds, for a response stanza (for example to an IQ
|
||||
request), before a timeout error is thrown and Converse stops waiting.
|
||||
|
||||
sticky_controlbox
|
||||
-----------------
|
||||
|
@ -2163,6 +2147,8 @@ If set to ``false``, this feature is disabled.
|
|||
|
||||
If set to ``a resource name``, Converse will synchronize only with a client that has that particular resource assigned to it.
|
||||
|
||||
.. _`theme`:
|
||||
|
||||
theme
|
||||
-----
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/dependencies.rst">Edit me on GitHub</a></div>
|
||||
|
||||
=============================
|
||||
Starting up a dev environment
|
||||
=============================
|
||||
.. _`development`:
|
||||
|
||||
============================
|
||||
Setting up a dev environment
|
||||
============================
|
||||
|
||||
Installing the 3rd party dependencies
|
||||
=====================================
|
||||
|
@ -88,3 +90,50 @@ which requires all other dependent JavaScript code to also be open sourced under
|
|||
license. You might not be willing to adhere to those terms, which is why you
|
||||
need to decide for yourself whether you're going to load libsignal or not.
|
||||
|
||||
|
||||
.. _`webserver`:
|
||||
|
||||
Setting up a webserver
|
||||
======================
|
||||
|
||||
When making changes to Converse, either development or theming changes,
|
||||
you'll want to preview them in your browser.
|
||||
|
||||
For this, you'll need to serve the development files via a web server,
|
||||
so that you can see your local changes in the browser.
|
||||
|
||||
Manually starting a web server
|
||||
------------------------------
|
||||
|
||||
To both set up the development environment and also start up a web browser to
|
||||
serve the files for you, you can run::
|
||||
|
||||
make serve
|
||||
|
||||
.. note::
|
||||
To run the "make" commands, you'll need `GNUMake <https://www.gnu.org/software/make>`_
|
||||
installed on your computer. If you use GNU/Linux or \*BSD, it should be installed or
|
||||
available via your package manager. For Mac, you'll need to install XCode and in
|
||||
Windows you can use `Chocolatey <https://chocolatey.org/>`_.
|
||||
|
||||
After running ``make serve`` you can open http://localhost:8000 in your webbrowser to see the Converse website.
|
||||
|
||||
When developing or changing the theme, you'll want to load all the
|
||||
unminified JS and CSS resources as separate files. To do this, open http://localhost:8000/dev.html instead.
|
||||
|
||||
You might want to open `dev.html <https://github.com/conversejs/converse.js/blob/master/dev.html>`_ in your text editor or IDE as well, to see
|
||||
how ``converse.initialize`` is called and to potentially change any of the
|
||||
settings.
|
||||
|
||||
Starting a web server with live reloading
|
||||
-----------------------------------------
|
||||
|
||||
Alternatively, if you want to have live reloading whenever any of the source files change, you
|
||||
can run ``make devserver`` (which will use `webpack-dev-server <https://github.com/webpack/webpack-dev-server>`_).
|
||||
|
||||
Instead of ``dev.html`` being used, `webpack.html <https://github.com/conversejs/converse.js/blob/master/webpack.html>`_
|
||||
is now being used as the HTML template, and you'll need to modify that file if
|
||||
you want to change the settings passed to ``converse.initialize``.
|
||||
|
||||
If you're running ``make devserver``, you need to open http://localhost:8080.
|
||||
|
||||
|
|
|
@ -23,11 +23,10 @@ to fix a bug or to add new functionality.
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
dependencies
|
||||
style_guide
|
||||
webserver
|
||||
setup_dev_environment
|
||||
plugin_development
|
||||
api/index
|
||||
testing
|
||||
other_frameworks
|
||||
builds
|
||||
style_guide
|
||||
|
|
|
@ -35,6 +35,12 @@ End to end message encryption (`XEP-0384 OMEMO <https://xmpp.org/extensions/xep-
|
|||
of uploaded files. Files will be uploaded WITHOUT ENCRYPTION, even when
|
||||
OMEMO is enabled.
|
||||
|
||||
.. note::
|
||||
For end-to-end encryption via OMEMO, you'll need to load `libsignal-protocol.js
|
||||
<https://github.com/signalapp/libsignal-protocol-javascript>`_ separately in
|
||||
your page. Take a look at the section on :ref:`libsignal <dependency-libsignal>` and the
|
||||
:ref:`security considerations around OMEMO <feature-omemo>`.
|
||||
|
||||
Converse supports OMEMO encryption based on the
|
||||
`Signal Protocol <https://github.com/signalapp/libsignal-protocol-javascript>`_.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/other_frameworks.rst">Edit me on GitHub</a></div>
|
||||
|
||||
Integrating converse.js into other frameworks
|
||||
=============================================
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/plugin_development.rst">Edit me on GitHub</a></div>
|
||||
|
||||
.. _`writing-a-plugin`:
|
||||
|
||||
|
@ -16,7 +16,7 @@ and is itself composed out of plugins.
|
|||
There are only a few files that are included in the default build of Converse
|
||||
which aren't plugins.
|
||||
|
||||
An important one is `converse-core.js <https://github.com/conversejs/converse.js/blob/master/src/headless/converse-core.js>`_,
|
||||
An important one is `core.js <https://github.com/conversejs/.js/blob/master/src/headless/core.js>`_,
|
||||
which is responsible for bootstrapping the plugin architecture,
|
||||
setting up and maintaining the connection to the XMPP
|
||||
server and declaring the public (`window.converse </docs/html/api/converse.html>`_) and protected (`_converse.api </docs/html/api/-_converse.api.html>`_) APIs.
|
||||
|
@ -180,11 +180,59 @@ The code for it could look something like this:
|
|||
These dependencies are closured so that they don't pollute the global
|
||||
namespace, that's why you need to access them in such a way inside the module.
|
||||
|
||||
Overrides
|
||||
---------
|
||||
Overriding templates
|
||||
--------------------
|
||||
|
||||
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_
|
||||
templates and templates are imported as separate files.
|
||||
|
||||
It's possible to configure your module bundler (e.g. Webpack) in such as way that a
|
||||
different file is loaded when a template is imported.
|
||||
|
||||
This allows you to create your own templates that are used instead of the ones
|
||||
that would have originally been imported.
|
||||
|
||||
With Webpack (which Converse uses internally), you can specify an
|
||||
``alias`` for the template you want to override. This alias then points to your
|
||||
own custom template.
|
||||
|
||||
For example, in your webpack config file, you could add the following to the
|
||||
``config`` object that gets exported:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js'],
|
||||
modules: [
|
||||
path.join(__dirname, 'node_modules'),
|
||||
path.join(__dirname, 'node_modules/converse.js/src')
|
||||
],
|
||||
alias: {
|
||||
'plugins/profile/templates/profile.js$': path.resolve(__dirname, 'templates/custom-profile.js')
|
||||
}
|
||||
}
|
||||
|
||||
This will override the template that gets imported at the path ``plugins/profile/templates/profile.js``
|
||||
with your own template at the path ``templates/custom-profile.js`` (relative to your webpack config file).
|
||||
|
||||
|
||||
Object and class Overrides
|
||||
--------------------------
|
||||
|
||||
.. note:: Using the `overrides` feature from pluggable.js is discouraged. It's
|
||||
much better to use events, promises and `hooks`_ to modify the behaviour of
|
||||
Converse.
|
||||
|
||||
The pluggable.js `overrides` will only work on objects and classes that are
|
||||
set as attributes on the `_converse` object, which doesn't apply to many
|
||||
newer classes and objects, such as the web components. For these clasess,
|
||||
overrides won't work at all.
|
||||
|
||||
This section is left here for completeness, because in some special cases
|
||||
overrides are still used.
|
||||
|
||||
Plugins can override core code or code from other plugins. You can specify
|
||||
overrides in the object passed to ``converse.plugins.add``.
|
||||
overrides in the object passed to ``converse.plugins.add``.
|
||||
|
||||
In an override you can still call the overridden function, by calling
|
||||
``this.__super__.methodName.apply(this, arguments);`` where ``methodName`` is
|
||||
|
@ -242,7 +290,7 @@ monkey patching which pollutes the call stack and can make your code fragile
|
|||
and prone to bugs when Converse gets updated. Too much use of ``overrides``
|
||||
is therefore a "code smell" which should ideally be avoided.
|
||||
|
||||
A better approach is to listen to the events emitted by Converse, and to add
|
||||
A better approach is to use the events and `hooks`_ emitted by Converse, and to add
|
||||
your code in event handlers. This is however not always possible, in which case
|
||||
the overrides are a powerful tool.
|
||||
|
||||
|
@ -260,40 +308,6 @@ For example:
|
|||
Object.assign(_converse.ChatBoxView.prototype, { doSomething });
|
||||
|
||||
|
||||
Overriding a template
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_
|
||||
templates.
|
||||
|
||||
It's not possible to override a template with the plugin's ``overrides``
|
||||
feature, instead you should configure a new path to your own template via your
|
||||
module bundler.
|
||||
|
||||
For example, with Webpack (which Converse uses internall), you can specify an
|
||||
``alias`` for the template you want to override. This alias then points to your
|
||||
own custom template.
|
||||
|
||||
For example, in your webpack config file, you could add the following to the
|
||||
``config`` object that gets exported:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
resolve: {
|
||||
extensions: ['.js'],
|
||||
modules: [
|
||||
path.join(__dirname, 'node_modules'),
|
||||
path.join(__dirname, 'node_modules/converse.js/src')
|
||||
],
|
||||
alias: {
|
||||
'plugins/profile/templates/profile.js$': path.resolve(__dirname, 'templates/custom-profile.js')
|
||||
}
|
||||
}
|
||||
|
||||
This will override the template that gets imported at the path ``plugins/profile/templates/profile.js``
|
||||
with your own template at the path ``templates/custom-profile.js`` (relative to
|
||||
your webpack config file).
|
||||
|
||||
|
||||
.. _`dependencies`:
|
||||
|
||||
|
@ -515,8 +529,8 @@ The `Actions <https://github.com/conversejs/community-plugins/tree/master/packag
|
|||
``like`` or ``dislike`` to chat messages.
|
||||
|
||||
|
||||
A full example plugin
|
||||
---------------------
|
||||
An example plugin
|
||||
-----------------
|
||||
|
||||
Below follows a documented example of a plugin. This is the same code that gets
|
||||
generated by `generator-conversejs <https://github.com/jcbrand/generator-conversejs>`_.
|
||||
|
@ -590,8 +604,8 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
|
|||
* _converse.api.promises.add('myPromise');
|
||||
*
|
||||
* Your plugin should then, when appropriate, resolve the
|
||||
* promise by calling `_converse.api.emit`, which will also
|
||||
* emit an event with the same name as the promise.
|
||||
* promise by calling `_converse.api.trigger`, which will also
|
||||
* trigger an event with the same name as the promise.
|
||||
* For example:
|
||||
*
|
||||
* _converse.api.trigger('operationCompleted');
|
||||
|
@ -605,53 +619,22 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
|
|||
*
|
||||
* _converse.api.waitUntil('operationCompleted', function { ... });
|
||||
*/
|
||||
},
|
||||
|
||||
/* If you want to override some function or a model or
|
||||
* view defined elsewhere in Converse, then you do that under
|
||||
* the "overrides" namespace.
|
||||
*/
|
||||
overrides: {
|
||||
/* For example, the private *_converse* object has a
|
||||
* method "onConnected". You can override that method as follows:
|
||||
|
||||
/* In your plugin, you can also listen for hooks.
|
||||
* Hooks allow you to add or modify data and properties used by
|
||||
* Converse.
|
||||
*
|
||||
* For example, the getToolbarButtons hook allows you to add new buttons to the chat toolbar.
|
||||
* https://conversejs.org/docs/html/api/-_converse.html#event:getToolbarButtons
|
||||
*/
|
||||
onConnected: function () {
|
||||
// Overrides the onConnected method in Converse
|
||||
|
||||
// Top-level functions in "overrides" are bound to the
|
||||
// inner "_converse" object.
|
||||
const _converse = this;
|
||||
|
||||
// Your custom code can come here ...
|
||||
|
||||
// You can access the original function being overridden
|
||||
// via the __super__ attribute.
|
||||
// Make sure to pass on the arguments supplied to this
|
||||
// function and also to apply the proper "this" object.
|
||||
_converse.__super__.onConnected.apply(this, arguments);
|
||||
|
||||
// Your custom code can come here ...
|
||||
},
|
||||
|
||||
/* Override Converse's XMPPStatus model so that we can override the
|
||||
* function that sends out the presence stanza.
|
||||
*/
|
||||
XMPPStatus: {
|
||||
sendPresence: function (type, status_message, jid) {
|
||||
// The "_converse" object is available via the __super__
|
||||
// attribute.
|
||||
const _converse = this.__super__._converse;
|
||||
|
||||
// Custom code can come here ...
|
||||
|
||||
// You can call the original overridden method, by
|
||||
// accessing it via the __super__ attribute.
|
||||
// When calling it, you need to apply the proper
|
||||
// context as reference by the "this" variable.
|
||||
this.__super__.sendPresence.apply(this, arguments);
|
||||
|
||||
// Custom code can come here ...
|
||||
}
|
||||
}
|
||||
api.listen.on('getToolbarButtons', (toolbar_el, buttons) => {
|
||||
buttons.push(html`
|
||||
<button class="my-button" @click=${alert('hello world!')}>
|
||||
<converse-icon class="fa fa-eye" size="1em" color="blue"></converse-icon>
|
||||
</button>
|
||||
`);
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -21,8 +21,9 @@ Let your XMPP server serve Converse for you
|
|||
If you run your own XMPP server, you might first want to check whether it has
|
||||
a plugin or module for hosting Converse.
|
||||
|
||||
* OpenFire has the `inverse <https://www.igniterealtime.org/projects/openfire/plugin-archive.jsp?plugin=inverse>`_ plugin.
|
||||
* Prosody has `mod_conversejs <https://modules.prosody.im/mod_conversejs.html>`_.
|
||||
* `Openfire <http://www.igniterealtime.org/projects/openfire/>`_ has the `inverse <https://www.igniterealtime.org/projects/openfire/plugin-archive.jsp?plugin=inverse>`_ plugin.
|
||||
* `Prosody <https://prosody.im/>`_ has `mod_conversejs <https://modules.prosody.im/mod_conversejs.html>`_.
|
||||
* `ejabberd <http://www.ejabberd.im/>`_ has `mod_conversejs <https://docs.ejabberd.im/admin/configuration/modules/#mod-conversejs>`_.
|
||||
|
||||
|
||||
Serving Converse yourself
|
||||
|
@ -55,16 +56,16 @@ might break when a new backwards-incompatible version of Converse is released.
|
|||
|
||||
To load a specific version of Converse you can put the version in the URL:
|
||||
|
||||
* https://cdn.conversejs.org/9.1.1/dist/converse.min.js
|
||||
* https://cdn.conversejs.org/9.1.1/dist/converse.min.css
|
||||
* https://cdn.conversejs.org/10.1.6/dist/converse.min.js
|
||||
* https://cdn.conversejs.org/10.1.6/dist/converse.min.css
|
||||
|
||||
You can include these two URLs inside the *<head>* element of your website
|
||||
via the *script* and *link* tags:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/9.1.1/dist/converse.min.css">
|
||||
<script src="https://cdn.conversejs.org/9.1.1/dist/converse.min.js" charset="utf-8"></script>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/10.1.6/dist/converse.min.css">
|
||||
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js" charset="utf-8"></script>
|
||||
|
||||
|
||||
Option 2: Download the builds from Github
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/security.rst">Edit me on GitHub</a></div>
|
||||
|
||||
=======================
|
||||
Security considerations
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/setup.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/session.rst">Edit me on GitHub</a></div>
|
||||
|
||||
==================
|
||||
Session Management
|
||||
|
@ -202,5 +202,4 @@ Storing the SASL SCRAM-SHA1 hash in IndexedDB
|
|||
Another suggestion that's been suggested is to store the SCRAM-SHA1 computed
|
||||
``clientKey`` in localStorage and to use that upon page reload to log the user in again.
|
||||
|
||||
We might implement this feature in core Converse.js eventually.
|
||||
As always, contributions welcome!
|
||||
This has been implemented since version 10, see documentation on `reuse_scram_keys <https://conversejs.org/docs/html/configuration.html#reuse-scram-keys>`_
|
||||
|
|
140
docs/source/setup_dev_environment.rst
Normal file
|
@ -0,0 +1,140 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/setup_dev_environment.rst">Edit me on GitHub</a></div>
|
||||
|
||||
.. _`setup_dev_environment`:
|
||||
|
||||
============================
|
||||
Setting up a dev environment
|
||||
============================
|
||||
|
||||
Installing the 3rd party dependencies
|
||||
=====================================
|
||||
|
||||
To develop and customize Converse, you'll first need to check out Converse's Git
|
||||
repository:
|
||||
|
||||
::
|
||||
|
||||
git clone https://github.com/conversejs/converse.js.git
|
||||
cd converse.js
|
||||
|
||||
|
||||
We use development tools which depend on Node.js and NPM (the Node package manager).
|
||||
|
||||
It's recommended that you use `NVM <https://github.com/nvm-sh/nvm>`_ (the Node version manager)
|
||||
to make sure you have the right version of Node.
|
||||
|
||||
Refer to the `NVM Github page <https://github.com/nvm-sh/nvm#install--update-script>`_ for instructions on how to install it.
|
||||
|
||||
Once NVM is installed, you can run the following inside your checkout of the Converse Git repository:
|
||||
|
||||
::
|
||||
|
||||
nvm install
|
||||
|
||||
.. note::
|
||||
You will always have to first run ``nvm install`` in a new terminal session in order to use the
|
||||
recommended version of Node before working on Converse.
|
||||
|
||||
|
||||
To set up the Converse development environment, you now run ``make dev``.
|
||||
|
||||
::
|
||||
|
||||
make dev
|
||||
|
||||
Alternatively, if you're using Windows, or don't have GNU Make installed, you can run the
|
||||
following:
|
||||
|
||||
::
|
||||
|
||||
npm install
|
||||
npm run lerna
|
||||
|
||||
This will install the Node development tools and Converse's dependencies.
|
||||
|
||||
The front-end dependencies are those JavaScript files on which
|
||||
Converse directly depends and which will be loaded in the browser as part of
|
||||
the bundle in ``dist/converse.js`` (or ``dist/converse.min.js``).
|
||||
|
||||
To see the 3rd party dependencies (not just the front-end dependencies, but
|
||||
also ones necessary for development tasks like making builds), take a look at
|
||||
the list under the ``devDependencies`` in `package.json <https://github.com/jcbrand/converse.js/blob/master/package.json>`_.
|
||||
|
||||
.. note::
|
||||
After running ```make dev```, you should now have a new *node_modules* directory
|
||||
which contains all the external dependencies of Converse.
|
||||
If this directory does NOT exist, something must have gone wrong.
|
||||
Double-check the output of ```make dev``` to see if there are any errors
|
||||
listed. For support, you can ask in our chatroom: `dicuss@conference.conversejs.org <xmpp:discuss@conference.conversejs.org>`_.
|
||||
|
||||
If you don't have an XMPP client installed, follow this link to
|
||||
`conversejs.org <https://conversejs.org/fullscreen#converse/room?jid=discuss@conference.conversejs.org>`_
|
||||
where you can log in and be taken directly to the chatroom.
|
||||
|
||||
|
||||
.. _`dependency-libsignal`:
|
||||
|
||||
Libsignal
|
||||
---------
|
||||
|
||||
If you want OMEMO encryption, you need to load `libsignal <https://github.com/signalapp/libsignal-protocol-javascript>`_ separately in your page.
|
||||
|
||||
For example::
|
||||
|
||||
<script src="3rdparty/libsignal-protocol-javascript/dist/libsignal-protocol.js"></script>
|
||||
|
||||
The reason libsignal needs to be loaded separately is because it's released
|
||||
under the `GPLv3 <https://github.com/signalapp/libsignal-protocol-javascript/blob/master/LICENSE>`_
|
||||
which requires all other dependent JavaScript code to also be open sourced under the same
|
||||
license. You might not be willing to adhere to those terms, which is why you
|
||||
need to decide for yourself whether you're going to load libsignal or not.
|
||||
|
||||
|
||||
.. _`webserver`:
|
||||
|
||||
Setting up a webserver
|
||||
======================
|
||||
|
||||
When making changes to Converse, either development or theming changes,
|
||||
you'll want to preview them in your browser.
|
||||
|
||||
For this, you'll need to serve the development files via a web server,
|
||||
so that you can see your local changes in the browser.
|
||||
|
||||
Manually starting a web server
|
||||
------------------------------
|
||||
|
||||
To both set up the development environment and also start up a web browser to
|
||||
serve the files for you, you can run::
|
||||
|
||||
make serve
|
||||
|
||||
.. note::
|
||||
To run the "make" commands, you'll need `GNUMake <https://www.gnu.org/software/make>`_
|
||||
installed on your computer. If you use GNU/Linux or \*BSD, it should be installed or
|
||||
available via your package manager. For Mac, you'll need to install XCode and in
|
||||
Windows you can use `Chocolatey <https://chocolatey.org/>`_.
|
||||
|
||||
After running ``make serve`` you can open http://localhost:8000 in your webbrowser to see the Converse website.
|
||||
|
||||
When developing or changing the theme, you'll want to load all the
|
||||
unminified JS and CSS resources as separate files. To do this, open http://localhost:8000/dev.html instead.
|
||||
|
||||
You might want to open `dev.html <https://github.com/conversejs/converse.js/blob/master/dev.html>`_ in your text editor or IDE as well, to see
|
||||
how ``converse.initialize`` is called and to potentially change any of the
|
||||
settings.
|
||||
|
||||
Starting a web server with live reloading
|
||||
-----------------------------------------
|
||||
|
||||
Alternatively, if you want to have live reloading whenever any of the source files change, you
|
||||
can run ``make devserver`` (which will use `webpack-dev-server <https://github.com/webpack/webpack-dev-server>`_).
|
||||
|
||||
Instead of ``dev.html`` being used, `webpack.html <https://github.com/conversejs/converse.js/blob/master/webpack.html>`_
|
||||
is now being used as the HTML template, and you'll need to modify that file if
|
||||
you want to change the settings passed to ``converse.initialize``.
|
||||
|
||||
If you're running ``make devserver``, you need to open http://localhost:8080.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/style_guide.rst">Edit me on GitHub</a></div>
|
||||
|
||||
Software Style Guide
|
||||
====================
|
||||
|
|
|
@ -11,19 +11,109 @@ Theming
|
|||
Setting up your environment
|
||||
===========================
|
||||
|
||||
In order to theme Converse, you first need to set up a :ref:`development` environment
|
||||
and then you'll also want to follow the guide to :ref:`webserver`.
|
||||
In order to theme Converse, you first need to follow the steps for :ref:`setup_dev_environment`, including :ref:`webserver`.
|
||||
|
||||
Creating a custom theme
|
||||
=======================
|
||||
|
||||
Converse can be themed via CSS custom properties (aka CSS variables) and it has
|
||||
some themes available in its source repository.
|
||||
|
||||
A theme is a CSS file with a specific rule that defines the theme's CSS properties.
|
||||
The rule has a specific selector that must include (and determines) the theme name.
|
||||
|
||||
Inside this CSS rule, various CSS variables are assigned values.
|
||||
The CSS variables mainly refer to the colors that comprise the theme.
|
||||
If you don't specify a value for a specific CSS variable, then the value from
|
||||
the ``classic`` theme is used, as defined in `classic.scss <https://github.com/conversejs/converse.js/tree/master/src/shared/styles/themes/classic.scss>`_.
|
||||
|
||||
The native theme files can be found in `shared/styles/themes <https://github.com/conversejs/converse.js/tree/master/src/shared/styles/themes>`_.
|
||||
|
||||
Note, the Converse theme files have a ``.scss`` extension because they are compiled
|
||||
by the Sass compiler into normal CSS files. It's however not necessary to use
|
||||
Sass, basic CSS files will also suffice.
|
||||
|
||||
The theme that Converse uses can be set via the :ref:`theme` configuration
|
||||
setting (and the :ref:`dark_theme` configuration setting for dark mode).
|
||||
|
||||
How are themes applied?
|
||||
-----------------------
|
||||
|
||||
When you set a value for the :ref:`theme` configuration setting, Converse will add
|
||||
a class ``theme-${api.settings.get('theme')}`` on the ``converse-root`` DOM
|
||||
element.
|
||||
|
||||
So, for example, if you set the ``theme`` setting to ``"dracula"``, then the
|
||||
``converse-root`` element will get the class ``theme-dracula``.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
converse.initialize({ theme: "dracula" });
|
||||
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<converse-root class="conversejs theme-dracula"></converse-root>
|
||||
|
||||
|
||||
The apply a theme, there then needs to be a CSS rule with a selector that matches the
|
||||
``theme-dracula`` class on the ``converse-root`` element.
|
||||
|
||||
If you take a look at the theme file `dracula.scss <https://github.com/conversejs/converse.js/tree/master/src/shared/styles/themes/dracula.scss>`_
|
||||
you'll see that it defines a CSS rule with the selector
|
||||
``.conversejs.theme-dracula``.
|
||||
|
||||
This selector matches any DOM element with both the classes ``.conversejs`` and
|
||||
``.theme-dracula``. The ``converse-root`` element will already have the class
|
||||
``.conversejs`` and it will have the class ``.theme-dracula`` if the ``theme``
|
||||
(or ``dark_theme`` in dark mode) configuration setting is set to ``"dracula"``.
|
||||
|
||||
This is how themes are applied, by defining a CSS selector that matches the
|
||||
class ``.theme-${name}`` (where ``name`` is a variable containing the name of
|
||||
the theme), and then setting the ``theme`` (and/or ``dark_theme``) configuration
|
||||
setting.
|
||||
|
||||
To create your own theme, you can create a similar CSS rule that matches
|
||||
your theme's name and then you set the ``theme`` configuration setting to that
|
||||
name. This CSS rule can be in any CSS file that is loaded in your website, or
|
||||
you can even put it in the DOM as an inline style.
|
||||
|
||||
Modifying the CSS
|
||||
=================
|
||||
|
||||
To create a new theme with different colors, it should be enough to create a
|
||||
theme file that sets the various CSS variables (as described above).
|
||||
|
||||
For other CSS-related changes, you can make a specific
|
||||
CSS rule with that matches the element you want to change.
|
||||
|
||||
Sometimes it might however be neccessary to modify the core CSS files from
|
||||
Converse, for example if you're developing new features or fixing styling bugs.
|
||||
|
||||
The CSS files are generated from `Sass <http://sass-lang.com>`_ files that end in ``.scss`` and
|
||||
which are distributed throughout the source code.
|
||||
|
||||
The CSS that is relevant to a particular plugin
|
||||
is usually inside the ``./styles`` directory inside the relevant plugin directory.
|
||||
|
||||
For example: `src/plugins/controlbox/styles <https://github.com/conversejs/converse.js/tree/master/src/plugins/controlbox/styles>`_.
|
||||
|
||||
If you're running ``make watch``, then the CSS will automatically be
|
||||
regenerated when you've changed any of the ``.scss``.
|
||||
|
||||
You can also manually generate the CSS::
|
||||
|
||||
make css
|
||||
|
||||
Modifying the HTML templates of Converse
|
||||
========================================
|
||||
|
||||
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_ as HTML
|
||||
Converse uses `lit-html <https://lit.dev/docs/libraries/standalone-templates/>`_ as HTML
|
||||
templating library, and the HTML source code is contained in JavaScript ``.js``
|
||||
files in various ``./template`` directories in the source code.
|
||||
|
||||
Some top-level templates are found in the ``./src/templates`` directory, but
|
||||
usually the templates that are relevant to a specific plugin will be find
|
||||
inside that plugin's subdirectory.
|
||||
Some top-level templates are also in the ``./src/templates`` directory, but
|
||||
the templates that are relevant to a specific plugin should be inside that plugin's subdirectory.
|
||||
|
||||
For example: `src/plugins/chatview/templates <https://github.com/conversejs/converse.js/tree/master/src/plugins/chatview/templates>`_.
|
||||
|
||||
|
@ -53,19 +143,3 @@ two of my own custom templates.
|
|||
'./templates/message.js': path.resolve(__dirname, 'path/to/my/custom/chat_message.js'),
|
||||
}
|
||||
}
|
||||
|
||||
Modifying the CSS
|
||||
=================
|
||||
|
||||
The CSS files are generated from `Sass <http://sass-lang.com>`_ files that end in ``.scss`` and
|
||||
which are distributed throughout the source code.
|
||||
|
||||
Similarly to the template files, the CSS that is relevant to a particular plugin
|
||||
is usually inside the ``./styles`` directory inside the relevant plugin
|
||||
directory.
|
||||
|
||||
For example: `src/plugins/controlbox/styles <https://github.com/conversejs/converse.js/tree/master/src/plugins/controlbox/styles>`_.
|
||||
|
||||
To generate the CSS you can run::
|
||||
|
||||
make css
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/setup.rst">Edit me on GitHub</a></div>
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/troubleshooting.rst">Edit me on GitHub</a></div>
|
||||
|
||||
=============================
|
||||
Troubleshooting and debugging
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
.. raw:: html
|
||||
|
||||
<div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/webserver.rst">Edit me on GitHub</a></div>
|
||||
|
||||
|
||||
.. _`webserver`:
|
||||
|
||||
Setting up a webserver
|
||||
======================
|
||||
|
||||
When making changes to Converse, either development or theming changes,
|
||||
you'll want to preview them in your browser.
|
||||
|
||||
For this, you'll need to serve the development files via a web server,
|
||||
so that you can see your local changes in the browser.
|
||||
|
||||
Manually starting a web server
|
||||
------------------------------
|
||||
|
||||
To both set up the development environment and also start up a web browser to
|
||||
serve the files for you, you can run::
|
||||
|
||||
make serve
|
||||
|
||||
.. note::
|
||||
To run the "make" commands, you'll need `GNUMake <https://www.gnu.org/software/make>`_
|
||||
installed on your computer. If you use GNU/Linux or \*BSD, it should be installed or
|
||||
available via your package manager. For Mac, you'll need to install XCode and in
|
||||
Windows you can use `Chocolatey <https://chocolatey.org/>`_.
|
||||
|
||||
After running ``make serve`` you can open http://localhost:8000 in your webbrowser to see the Converse website.
|
||||
|
||||
When developing or changing the theme, you'll want to load all the
|
||||
unminified JS and CSS resources as separate files. To do this, open http://localhost:8000/dev.html instead.
|
||||
|
||||
You might want to open `dev.html <https://github.com/conversejs/converse.js/blob/master/dev.html>`_ in your text editor or IDE as well, to see
|
||||
how ``converse.initialize`` is called and to potentially change any of the
|
||||
settings.
|
||||
|
||||
Starting a web server with live reloading
|
||||
-----------------------------------------
|
||||
|
||||
Alternatively, if you want to have live reloading whenever any of the source files change, you
|
||||
can run ``make devserver`` (which will use `webpack-dev-server <https://github.com/webpack/webpack-dev-server>`_).
|
||||
|
||||
Instead of ``dev.html`` being used, `webpack.html <https://github.com/conversejs/converse.js/blob/master/webpack.html>`_
|
||||
is now being used as the HTML template, and you'll need to modify that file if
|
||||
you want to change the settings passed to ``converse.initialize``.
|
||||
|
||||
If you're running ``make devserver``, you need to open http://localhost:8080.
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
<head>
|
||||
<title>Converse</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="description" content="Converse XMPP/Jabber Chat"/>
|
||||
<meta name="author" content="JC Brand" />
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<title>Converse</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Converse XMPP/Jabber Chat"/>
|
||||
<meta name="author" content="JC Brand" />
|
||||
|
@ -239,12 +240,11 @@
|
|||
<div class="sponsors">
|
||||
<h2>Converse is supported by:</h2>
|
||||
<ul >
|
||||
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 13em" src="/media/logos/bairesdev-primary.png" alt="BairesDev"></a></li>
|
||||
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
||||
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
|
||||
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
|
||||
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
|
||||
<li><a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em; padding-top: 0.5em" src="/logo/codefirst.png" alt="Codefirst"></a></li>
|
||||
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
||||
<li><a href="https://originalenergie.de/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/logo/originalenergie.png" alt="Original Energie"></a></li>
|
||||
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,17 +11,17 @@
|
|||
|
||||
<!-- These files are NOT needed when using converse.js in your own project. -->
|
||||
<link rel="shortcut icon" type="image/ico" href="images/favicon.ico"/>
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/9.1.1/css/font-awesome.min.css" />
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/9.1.1/css/website.min.css" />
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/font-awesome.min.css" />
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/website.min.css" />
|
||||
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
||||
<script type="text/javascript" src="/src/website.js"></script>
|
||||
<script type="text/javascript" src="analytics.js"></script>
|
||||
<!-- *********************************************************************** -->
|
||||
|
||||
<![if gte IE 11]>
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/9.1.1/css/converse.min.css" />
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/converse.min.css" />
|
||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||
<script src="https://cdn.conversejs.org/9.1.1/dist/converse.min.js"></script>
|
||||
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
|
||||
<![endif]>
|
||||
</head>
|
||||
|
||||
|
@ -66,7 +66,7 @@
|
|||
<table id="jslicense-labels1" style="width: 100%">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://cdn.conversejs.org/9.1.1/dist/converse.min.js">converse.min.js</a>
|
||||
<a href="https://cdn.conversejs.org/10.1.6/dist/converse.min.js">converse.min.js</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://www.mozilla.org/en-US/MPL/2.0/">MPL-2.0</a>
|
||||
|
|
|
@ -10,8 +10,8 @@ module.exports = function(config) {
|
|||
files: [
|
||||
{ pattern: 'dist/*.js.map', included: false },
|
||||
{ pattern: 'dist/*.css.map', included: false },
|
||||
{ pattern: "dist/icons.js", served: true },
|
||||
{ pattern: "dist/emojis.js", served: true },
|
||||
"src/shared/tests/tests.css",
|
||||
"node_modules/lodash/lodash.min.js",
|
||||
"dist/converse.js",
|
||||
"dist/converse.css",
|
||||
|
@ -42,6 +42,8 @@ module.exports = function(config) {
|
|||
{ pattern: "src/headless/tests/converse.js", type: 'module' },
|
||||
{ pattern: "src/headless/tests/eventemitter.js", type: 'module' },
|
||||
{ pattern: "src/modals/tests/user-details-modal.js", type: 'module' },
|
||||
{ pattern: "src/plugins/adhoc-views/tests/adhoc.js", type: 'module' },
|
||||
{ pattern: "src/plugins/bookmark-views/tests/bookmarks-list.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' },
|
||||
|
@ -82,13 +84,12 @@ module.exports = function(config) {
|
|||
{ pattern: "src/plugins/muc-views/tests/mep.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/modtools.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-add-modal.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-list-modal.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-api.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-list-modal.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-mentions.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-messages.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc-registration.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muc.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/muclist.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/nickname.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/occupants.js", type: 'module' },
|
||||
{ pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
|
||||
|
@ -102,9 +103,14 @@ module.exports = function(config) {
|
|||
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
||||
{ pattern: "src/plugins/omemo/tests/muc.js", type: 'module' },
|
||||
{ pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' },
|
||||
{ pattern: "src/plugins/profile/tests/password-reset.js", type: 'module' },
|
||||
{ pattern: "src/plugins/profile/tests/profile.js", type: 'module' },
|
||||
{ pattern: "src/plugins/profile/tests/status.js", type: 'module' },
|
||||
{ pattern: "src/plugins/push/tests/push.js", type: 'module' },
|
||||
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
||||
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },
|
||||
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
||||
{ pattern: "src/plugins/rosterview/tests/add-contact-modal.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' },
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"packages": [
|
||||
".",
|
||||
"src/*"
|
||||
],
|
||||
"version": "4.0.3"
|
||||
}
|
Before Width: | Height: | Size: 12 KiB |
|
@ -1,19 +1,34 @@
|
|||
<svg class="converse-svg-logo"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 364 364">
|
||||
<title>Converse</title>
|
||||
<g class="cls-1" id="g904">
|
||||
<g data-name="Layer 2">
|
||||
<g data-name="Layer 7">
|
||||
<path
|
||||
class="cls-3"
|
||||
d="M221.46,103.71c0,18.83-29.36,18.83-29.12,0C192.1,84.88,221.46,84.88,221.46,103.71Z" />
|
||||
<path
|
||||
class="cls-4"
|
||||
d="M179.9,4.15A175.48,175.48,0,1,0,355.38,179.63,175.48,175.48,0,0,0,179.9,4.15Zm-40.79,264.5c-.23-17.82,27.58-17.82,27.58,0S138.88,286.48,139.11,268.65ZM218.6,168.24A79.65,79.65,0,0,1,205.15,174a12.76,12.76,0,0,0-6.29,4.65L167.54,222a1.36,1.36,0,0,1-2.46-.8v-35.8a2.58,2.58,0,0,0-3.06-2.53c-15.43,3-30.23,7.7-42.73,19.94-38.8,38-29.42,105.69,16.09,133.16a162.25,162.25,0,0,1-91.47-67.27C-3.86,182.26,34.5,47.25,138.37,25.66c46.89-9.75,118.25,5.16,123.73,62.83C265.15,120.64,246.56,152.89,218.6,168.24Z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg108" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
|
||||
xml:space="preserve" version="1.1"
|
||||
viewBox="0 0 376 311" height="20%" width="10rem"
|
||||
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview16" showgrid="false" inkscape:zoom="0.75884244" inkscape:cx="188" inkscape:cy="155.5" inkscape:window-x="0" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg108"/>
|
||||
<metadata id="metadata114">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
<cc:license rdf:resource="GFDL version 1.3 ou ultérieure, Creative Commons By Sa version 2.0 ou ultérieure, Licence Art Libre version 1.3 ou ultérieure"/>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Antoine BARDELLI</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs id="defs112"/>
|
||||
<g id="g250" style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" transform="translate(4.6079614e-7,-4.4571451e-6)">
|
||||
<path id="path124" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 96.70711,209.69029 c -2.742,1.114 -5.399,1.8 -7.885,2.057 -0.771,0.085 -1.629,0.171 -2.4,0.171 -2.571,0 -5.314,-0.428 -8.228,-1.286 -2.486,-0.857 -4.628,-2.314 -6.514,-4.371 -1.886,-1.8 -3.343,-3.942 -4.371,-6.428 -0.943,-2.486 -1.457,-5.057 -1.543,-7.714 0,-0.086 0,-0.257 0,-0.343 0,-2.485 0.514,-4.971 1.543,-7.371 1.028,-2.314 2.485,-4.457 4.371,-6.428 1.886,-2.057 4.028,-3.514 6.514,-4.371 2.828,-1.029 5.571,-1.543 8.142,-1.543 0.257,0 0.6,0 0.857,0 3.686,0.171 6.857,1.028 9.686,2.657 -0.086,1.457 -0.343,3.514 -0.6,6.343 -2.743,-1.372 -5.4,-2.143 -7.971,-2.315 -1.972,-0.171 -3.943,0.086 -6,0.772 -1.629,0.514 -3.171,1.457 -4.457,2.914 -1.286,1.371 -2.228,2.828 -2.914,4.457 -0.772,1.628 -1.114,3.342 -1.114,5.142 0.085,1.886 0.428,3.6 1.114,5.229 0.686,1.714 1.628,3.171 2.914,4.457 1.286,1.285 2.828,2.228 4.457,2.914 2.486,0.685 4.543,1.028 6.343,1.028 3.342,-0.343 5.999,-1.2 8.056,-2.657 0,1.457 0,3.686 0,6.686 z"/>
|
||||
<path id="path126" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 137.33411,184.91929 c 0,1.8 0,3.515 0,5.314 0,2.657 0,5.4 -0.086,8.057 0,4.457 0,8.743 0,12.771 -1.714,0 -4.285,0 -7.799,0 0,-3.857 0,-11.571 0.085,-23.142 0.086,-1.114 -0.171,-2.228 -0.6,-3.257 -0.428,-1.028 -1.028,-1.971 -1.8,-2.742 -0.942,-0.943 -2.142,-1.715 -3.599,-2.143 -1.029,-0.257 -2.229,-0.429 -3.6,-0.343 -1.115,0.086 -2.229,0.343 -3.257,0.686 -1.029,0.428 -2.143,1.028 -3.343,1.8 -0.6,0.6 -1.029,1.2 -1.457,1.8 v 27.341 h -8.057 v -62.997 h 8.057 v 29.313 c 0.943,-1.114 1.8,-1.971 2.571,-2.486 1.629,-0.942 3,-1.542 4.2,-1.885 1.971,-0.429 3.771,-0.6 5.571,-0.6 2.143,0.085 3.943,0.343 5.4,0.943 1.543,0.6 2.914,1.542 4.028,2.742 2.4,2.572 3.6,5.486 3.686,8.828 z"/>
|
||||
<path id="path128" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 169.38911,146.86429 c 4.714,10.714 14.228,32.056 28.37,64.111 -1.371,0 -4.114,0 -8.313,0 -1.286,-2.657 -3.772,-7.971 -7.543,-15.942 -4.028,0 -12.085,0 -24.17,0 -1.114,2.657 -3.514,7.971 -7.028,15.942 -1.457,0 -4.2,0 -8.4,0 4.543,-10.713 13.542,-32.055 27.084,-64.111 z m 9.171,40.884 c -1.457,-3.686 -4.542,-10.885 -9.085,-21.77 -1.457,3.599 -4.285,10.885 -8.571,21.77 3,0 8.828,0 17.656,0 z"/>
|
||||
<path id="path130" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 212.15911,199.83329 c 0.257,0.429 0.6,0.771 0.857,1.114 1.114,1.457 2.657,2.743 4.457,3.686 1.457,0.685 3.085,1.028 4.8,1.028 0.085,0 0.257,0 0.428,0 1.8,0.086 3.514,-0.257 5.143,-1.028 1.628,-0.6 3.171,-1.629 4.457,-2.914 1.285,-1.286 2.228,-2.743 2.914,-4.457 0.685,-1.543 1.028,-3.343 1.028,-5.314 0.086,-1.715 -0.257,-3.429 -1.028,-5.057 -0.6,-1.629 -1.629,-3.171 -2.914,-4.457 -1.286,-1.286 -2.743,-2.229 -4.457,-2.914 -1.115,-0.515 -2.572,-0.772 -4.2,-0.772 -0.172,0 -0.343,0 -0.429,0 -2.657,0.086 -4.714,0.429 -5.999,1.2 -3.429,1.8 -5.143,3.857 -5.143,6.171 0,3 0,7.629 0.086,13.714 z m 0,8.314 c -0.086,3.343 -0.086,10.028 -0.086,20.056 -1.286,0 -4.028,0 -8.057,0 v -54.769 c 2.657,0 5.4,0 8.057,0 0.086,1.286 0.086,2.4 0.086,3.257 0.857,-1.114 2.314,-2.142 4.457,-3.085 2.4,-1.029 4.971,-1.543 7.714,-1.543 2.657,0 5.228,0.514 7.628,1.543 2.485,1.028 4.714,2.485 6.514,4.371 1.971,1.886 3.428,4.028 4.371,6.428 1.028,2.486 1.543,4.971 1.543,7.543 0,0.085 0,0.085 0,0.171 0,2.657 -0.515,5.228 -1.543,7.714 -1.029,2.486 -2.486,4.628 -4.371,6.428 -1.886,1.972 -4.029,3.429 -6.514,4.371 -2.486,1.029 -5.057,1.543 -7.628,1.543 -2.657,-0.085 -5.229,-0.6 -7.714,-1.543 -1.372,-0.514 -2.829,-1.371 -4.457,-2.485 z"/>
|
||||
<path id="path132" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 271.04211,180.46329 c 0,-0.086 -0.686,-0.172 -2.143,-0.429 -2.743,-0.086 -5.228,0.857 -7.371,2.828 -0.857,0.772 -1.543,1.886 -2.057,3.343 -0.343,1.371 -0.515,2.829 -0.6,4.457 0,4.543 0,11.399 0,20.399 -1.286,0 -3.943,0 -7.971,0 0,-6.257 0,-18.856 -0.086,-37.627 1.286,0 3.943,0 7.8,0 0.085,0.772 0.085,2.4 0.085,4.8 0,0.514 0,1.543 -0.085,3.171 1.028,-4.199 3.085,-6.856 6.085,-7.971 1.2,-0.343 2.4,-0.514 3.686,-0.514 0.857,-0.086 1.714,0 2.657,0.171 0,1.629 0,4.115 0,7.372 z"/>
|
||||
<path id="path134" style="clip-rule:evenodd;fill:#005184;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 277.47011,163.23529 c 0,-1.372 0.514,-2.4 1.457,-3.171 0.943,-0.772 1.971,-1.2 3.085,-1.2 0.086,0 0.172,0 0.258,0 1.114,0 2.057,0.342 2.999,1.028 0.943,0.771 1.372,1.8 1.372,3.257 0,1.457 -0.429,2.571 -1.372,3.428 -0.942,0.858 -2.057,1.286 -3.257,1.286 -1.114,0 -2.142,-0.428 -3.085,-1.2 -0.943,-0.771 -1.457,-1.971 -1.457,-3.428 z"/>
|
||||
<path style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" id="path136" d="m 278.15611,173.34929 c 1.371,0 4.028,0 8.056,0 0,6.342 0,18.942 0,37.883 -1.285,0 -3.942,0 -7.885,0 0,-6.256 -0.086,-18.941 -0.171,-37.883 z"/>
|
||||
<path id="path138" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 295.55511,146.26429 v 64.797 c 1.285,0 3.942,0 7.885,0 0,-10.799 0,-32.398 0,-64.797 -1.286,0 -3.943,0 -7.885,0 z"/>
|
||||
<path id="path144" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 79.90511,45.008286 c 0.902,0.305 0.903,0.306 1.74,0.76 0,0 2.36,1.6 3.541,2.399 16.192,11.056 46.905,36.807 46.905,36.807 l -0.62,0.933 c 0,0 -30.962,-17.269 -47.812,-25.837 -0.154,2.104 -0.309,4.208 -0.465,6.312 l -0.126,1.689 c -0.635,7.993 -1.295,15.97 -2,23.945 l -0.096,1.076 c -0.824,8.583004 -1.124,17.369004 -3.069,25.573004 -1.802,7.601 -10.27,13.477 -15.485,20.114 -10.856,13.816 -19.361,29.582 -21.64,47.603 -2.579,20.389 5.773,40.853 21.062,55.815 5.92,5.794 12.534,10.853 19.323,15.617 l -3.768,5.852 c -20.581,-12.139 -40.182,-28.618 -47.181,-51.981 -6.615,-22.081 -1.608,-47.917 10.144,-67.278 6.689,-11.02 14.834,-21.098 23.413,-30.201 0,0 0.585,-2.521 0.874,-4.27 3.14,-19.016004 5.383,-38.057004 7.92,-57.227004 l 0.274,-2.074 0.28,-1.686 c 0.307,-0.8 0.337,-1.033 0.841,-1.737 1.119,-1.562 2.062,-2.818 5.945,-2.204 z"/>
|
||||
<path id="path146" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 300.65411,44.858286 c 2.74,0.486 2.778,0.936 3.448,1.587 2.555,2.484 2.191,7.238 2.75,11.471 2.467,18.612 4.172,37.353 7.871,55.935004 0,0 3.607,4.013 6.264,7.099 11.064,12.849 21.019,26.411 26.284,43.975 6.27,20.92 3.585,41.82 -7.633,60.315 -9.349,15.414 -24.268,26.668 -39.964,35.962 l -0.593,-1.853 c 17.279,-12.08 34.133,-26.972 39.318,-48.24 3.677,-15.083 -0.809,-32.515 -6.571,-47.292 -5.094,-13.065 -14.589,-24.209 -23.233,-34.394 -1.789,-2.107 -3.603,-4.192 -5.472,-6.229 0,0 -2.204,-3.069 -2.806,-6.739 -2.582,-15.749 -3.416,-31.540004 -4.699,-47.326004 0,0 -0.34,-4.424 -0.589,-7.754 l -0.098,-1.322 -30.582,18.428 -15.221,9.11 -0.801,0.477 -3.955,-6.151 0.767,-0.531 14.605,-10.067 37.28,-25.534 c 0,0 1.172,-0.939 3.63,-0.927 z m 14.362,70.312004 c 0.178,0.639 -0.049,-0.186 0,0 z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 8.9 KiB |
19
logo/conversejs-transparent.svg
Normal file
|
@ -0,0 +1,19 @@
|
|||
<svg class="converse-svg-logo"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 364 364">
|
||||
<title>Converse</title>
|
||||
<g class="cls-1" id="g904">
|
||||
<g data-name="Layer 2">
|
||||
<g data-name="Layer 7">
|
||||
<path
|
||||
class="cls-3"
|
||||
d="M221.46,103.71c0,18.83-29.36,18.83-29.12,0C192.1,84.88,221.46,84.88,221.46,103.71Z" />
|
||||
<path
|
||||
class="cls-4"
|
||||
d="M179.9,4.15A175.48,175.48,0,1,0,355.38,179.63,175.48,175.48,0,0,0,179.9,4.15Zm-40.79,264.5c-.23-17.82,27.58-17.82,27.58,0S138.88,286.48,139.11,268.65ZM218.6,168.24A79.65,79.65,0,0,1,205.15,174a12.76,12.76,0,0,0-6.29,4.65L167.54,222a1.36,1.36,0,0,1-2.46-.8v-35.8a2.58,2.58,0,0,0-3.06-2.53c-15.43,3-30.23,7.7-42.73,19.94-38.8,38-29.42,105.69,16.09,133.16a162.25,162.25,0,0,1-91.47-67.27C-3.86,182.26,34.5,47.25,138.37,25.66c46.89-9.75,118.25,5.16,123.73,62.83C265.15,120.64,246.56,152.89,218.6,168.24Z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
|
@ -1,108 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
class="converse-svg-logo"
|
||||
viewBox="0 0 364 364"
|
||||
version="1.1"
|
||||
id="svg13"
|
||||
sodipodi:docname="conversejs-with-byline.svg"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
||||
<metadata
|
||||
id="metadata19">
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="chapril-logo"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
|
||||
class="converse-svg-logo"
|
||||
xml:space="preserve" version="1.1"
|
||||
viewBox="0 0 376 311" height="20%" width="6rem"
|
||||
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview16" showgrid="false" inkscape:zoom="0.75884244" inkscape:cx="188" inkscape:cy="155.5" inkscape:window-x="0" inkscape:window-y="24" inkscape:window-maximized="0" inkscape:current-layer="svg108"/>
|
||||
<metadata id="metadata114">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Converse</dc:title>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
<cc:license rdf:resource="GFDL version 1.3 ou ultérieure, Creative Commons By Sa version 2.0 ou ultérieure, Licence Art Libre version 1.3 ou ultérieure"/>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Antoine BARDELLI</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs17">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 182 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="364 : 182 : 1"
|
||||
inkscape:persp3d-origin="182 : 121.33333 : 1"
|
||||
id="perspective2147" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1434"
|
||||
inkscape:window-height="951"
|
||||
id="namedview15"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.8338154"
|
||||
inkscape:cx="225.17086"
|
||||
inkscape:cy="243.79827"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg13" />
|
||||
<title
|
||||
id="title2">Converse</title>
|
||||
<g
|
||||
class="cls-1"
|
||||
id="g904"
|
||||
transform="matrix(0.2441072,0,0,0.2441072,12.20969,55.55023)">
|
||||
<g
|
||||
data-name="Layer 2"
|
||||
id="g10">
|
||||
<g
|
||||
data-name="Layer 7"
|
||||
id="g8">
|
||||
<path
|
||||
class="cls-3"
|
||||
d="m 221.46,103.71 c 0,18.83 -29.36,18.83 -29.12,0 -0.24,-18.83 29.12,-18.83 29.12,0 z"
|
||||
id="path4"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
class="cls-4"
|
||||
d="M 179.9,4.15 C 108.92504,4.15 44.938239,46.904566 17.778836,112.4757 -9.3805118,178.0467 5.6365472,253.52014 55.823205,303.70679 106.00986,353.89345 181.4833,368.91051 247.0543,341.75116 312.62543,314.59176 355.38,250.60496 355.38,179.63 355.38,82.715072 276.81493,4.15 179.9,4.15 Z m -40.79,264.5 c -0.23,-17.82 27.58,-17.82 27.58,0 0,17.82 -27.81,17.83 -27.58,0 z M 218.6,168.24 c -4.29711,2.32859 -8.79944,4.25673 -13.45,5.76 -2.53177,0.85328 -12.23498,3.26952 -13.79313,5.4398 C 180.90809,189.252 165.08,221.2 165.08,221.2 v -35.8 c -0.003,-1.6153 -1.4729,-2.83052 -3.06,-2.53 -15.43,3 -30.23,7.7 -42.73,19.94 -38.8,38 -29.025098,103.71549 16.4849,131.18549 C 98.17801,323.32071 65.725789,295.74404 44.332966,263.03587 -3.4370336,176.59587 35.058475,51.159326 138.92848,29.569326 185.81848,19.819326 256.62,30.82 262.1,88.49 c 3.05,32.15 -15.54,64.4 -43.5,79.75 z"
|
||||
id="path6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssscsccccccccccccc" />
|
||||
</g>
|
||||
</g>
|
||||
<defs id="defs112"/>
|
||||
<g id="g250" style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" transform="translate(4.6079614e-7,-4.4571451e-6)">
|
||||
<path id="path124" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 96.70711,209.69029 c -2.742,1.114 -5.399,1.8 -7.885,2.057 -0.771,0.085 -1.629,0.171 -2.4,0.171 -2.571,0 -5.314,-0.428 -8.228,-1.286 -2.486,-0.857 -4.628,-2.314 -6.514,-4.371 -1.886,-1.8 -3.343,-3.942 -4.371,-6.428 -0.943,-2.486 -1.457,-5.057 -1.543,-7.714 0,-0.086 0,-0.257 0,-0.343 0,-2.485 0.514,-4.971 1.543,-7.371 1.028,-2.314 2.485,-4.457 4.371,-6.428 1.886,-2.057 4.028,-3.514 6.514,-4.371 2.828,-1.029 5.571,-1.543 8.142,-1.543 0.257,0 0.6,0 0.857,0 3.686,0.171 6.857,1.028 9.686,2.657 -0.086,1.457 -0.343,3.514 -0.6,6.343 -2.743,-1.372 -5.4,-2.143 -7.971,-2.315 -1.972,-0.171 -3.943,0.086 -6,0.772 -1.629,0.514 -3.171,1.457 -4.457,2.914 -1.286,1.371 -2.228,2.828 -2.914,4.457 -0.772,1.628 -1.114,3.342 -1.114,5.142 0.085,1.886 0.428,3.6 1.114,5.229 0.686,1.714 1.628,3.171 2.914,4.457 1.286,1.285 2.828,2.228 4.457,2.914 2.486,0.685 4.543,1.028 6.343,1.028 3.342,-0.343 5.999,-1.2 8.056,-2.657 0,1.457 0,3.686 0,6.686 z"/>
|
||||
<path id="path126" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 137.33411,184.91929 c 0,1.8 0,3.515 0,5.314 0,2.657 0,5.4 -0.086,8.057 0,4.457 0,8.743 0,12.771 -1.714,0 -4.285,0 -7.799,0 0,-3.857 0,-11.571 0.085,-23.142 0.086,-1.114 -0.171,-2.228 -0.6,-3.257 -0.428,-1.028 -1.028,-1.971 -1.8,-2.742 -0.942,-0.943 -2.142,-1.715 -3.599,-2.143 -1.029,-0.257 -2.229,-0.429 -3.6,-0.343 -1.115,0.086 -2.229,0.343 -3.257,0.686 -1.029,0.428 -2.143,1.028 -3.343,1.8 -0.6,0.6 -1.029,1.2 -1.457,1.8 v 27.341 h -8.057 v -62.997 h 8.057 v 29.313 c 0.943,-1.114 1.8,-1.971 2.571,-2.486 1.629,-0.942 3,-1.542 4.2,-1.885 1.971,-0.429 3.771,-0.6 5.571,-0.6 2.143,0.085 3.943,0.343 5.4,0.943 1.543,0.6 2.914,1.542 4.028,2.742 2.4,2.572 3.6,5.486 3.686,8.828 z"/>
|
||||
<path id="path128" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 169.38911,146.86429 c 4.714,10.714 14.228,32.056 28.37,64.111 -1.371,0 -4.114,0 -8.313,0 -1.286,-2.657 -3.772,-7.971 -7.543,-15.942 -4.028,0 -12.085,0 -24.17,0 -1.114,2.657 -3.514,7.971 -7.028,15.942 -1.457,0 -4.2,0 -8.4,0 4.543,-10.713 13.542,-32.055 27.084,-64.111 z m 9.171,40.884 c -1.457,-3.686 -4.542,-10.885 -9.085,-21.77 -1.457,3.599 -4.285,10.885 -8.571,21.77 3,0 8.828,0 17.656,0 z"/>
|
||||
<path id="path130" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 212.15911,199.83329 c 0.257,0.429 0.6,0.771 0.857,1.114 1.114,1.457 2.657,2.743 4.457,3.686 1.457,0.685 3.085,1.028 4.8,1.028 0.085,0 0.257,0 0.428,0 1.8,0.086 3.514,-0.257 5.143,-1.028 1.628,-0.6 3.171,-1.629 4.457,-2.914 1.285,-1.286 2.228,-2.743 2.914,-4.457 0.685,-1.543 1.028,-3.343 1.028,-5.314 0.086,-1.715 -0.257,-3.429 -1.028,-5.057 -0.6,-1.629 -1.629,-3.171 -2.914,-4.457 -1.286,-1.286 -2.743,-2.229 -4.457,-2.914 -1.115,-0.515 -2.572,-0.772 -4.2,-0.772 -0.172,0 -0.343,0 -0.429,0 -2.657,0.086 -4.714,0.429 -5.999,1.2 -3.429,1.8 -5.143,3.857 -5.143,6.171 0,3 0,7.629 0.086,13.714 z m 0,8.314 c -0.086,3.343 -0.086,10.028 -0.086,20.056 -1.286,0 -4.028,0 -8.057,0 v -54.769 c 2.657,0 5.4,0 8.057,0 0.086,1.286 0.086,2.4 0.086,3.257 0.857,-1.114 2.314,-2.142 4.457,-3.085 2.4,-1.029 4.971,-1.543 7.714,-1.543 2.657,0 5.228,0.514 7.628,1.543 2.485,1.028 4.714,2.485 6.514,4.371 1.971,1.886 3.428,4.028 4.371,6.428 1.028,2.486 1.543,4.971 1.543,7.543 0,0.085 0,0.085 0,0.171 0,2.657 -0.515,5.228 -1.543,7.714 -1.029,2.486 -2.486,4.628 -4.371,6.428 -1.886,1.972 -4.029,3.429 -6.514,4.371 -2.486,1.029 -5.057,1.543 -7.628,1.543 -2.657,-0.085 -5.229,-0.6 -7.714,-1.543 -1.372,-0.514 -2.829,-1.371 -4.457,-2.485 z"/>
|
||||
<path id="path132" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 271.04211,180.46329 c 0,-0.086 -0.686,-0.172 -2.143,-0.429 -2.743,-0.086 -5.228,0.857 -7.371,2.828 -0.857,0.772 -1.543,1.886 -2.057,3.343 -0.343,1.371 -0.515,2.829 -0.6,4.457 0,4.543 0,11.399 0,20.399 -1.286,0 -3.943,0 -7.971,0 0,-6.257 0,-18.856 -0.086,-37.627 1.286,0 3.943,0 7.8,0 0.085,0.772 0.085,2.4 0.085,4.8 0,0.514 0,1.543 -0.085,3.171 1.028,-4.199 3.085,-6.856 6.085,-7.971 1.2,-0.343 2.4,-0.514 3.686,-0.514 0.857,-0.086 1.714,0 2.657,0.171 0,1.629 0,4.115 0,7.372 z"/>
|
||||
<path id="path134" style="clip-rule:evenodd;fill:#005184;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 277.47011,163.23529 c 0,-1.372 0.514,-2.4 1.457,-3.171 0.943,-0.772 1.971,-1.2 3.085,-1.2 0.086,0 0.172,0 0.258,0 1.114,0 2.057,0.342 2.999,1.028 0.943,0.771 1.372,1.8 1.372,3.257 0,1.457 -0.429,2.571 -1.372,3.428 -0.942,0.858 -2.057,1.286 -3.257,1.286 -1.114,0 -2.142,-0.428 -3.085,-1.2 -0.943,-0.771 -1.457,-1.971 -1.457,-3.428 z"/>
|
||||
<path style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41420996" id="path136" d="m 278.15611,173.34929 c 1.371,0 4.028,0 8.056,0 0,6.342 0,18.942 0,37.883 -1.285,0 -3.942,0 -7.885,0 0,-6.256 -0.086,-18.941 -0.171,-37.883 z"/>
|
||||
<path id="path138" style="clip-rule:evenodd;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 295.55511,146.26429 v 64.797 c 1.285,0 3.942,0 7.885,0 0,-10.799 0,-32.398 0,-64.797 -1.286,0 -3.943,0 -7.885,0 z"/>
|
||||
<path id="path144" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 79.90511,45.008286 c 0.902,0.305 0.903,0.306 1.74,0.76 0,0 2.36,1.6 3.541,2.399 16.192,11.056 46.905,36.807 46.905,36.807 l -0.62,0.933 c 0,0 -30.962,-17.269 -47.812,-25.837 -0.154,2.104 -0.309,4.208 -0.465,6.312 l -0.126,1.689 c -0.635,7.993 -1.295,15.97 -2,23.945 l -0.096,1.076 c -0.824,8.583004 -1.124,17.369004 -3.069,25.573004 -1.802,7.601 -10.27,13.477 -15.485,20.114 -10.856,13.816 -19.361,29.582 -21.64,47.603 -2.579,20.389 5.773,40.853 21.062,55.815 5.92,5.794 12.534,10.853 19.323,15.617 l -3.768,5.852 c -20.581,-12.139 -40.182,-28.618 -47.181,-51.981 -6.615,-22.081 -1.608,-47.917 10.144,-67.278 6.689,-11.02 14.834,-21.098 23.413,-30.201 0,0 0.585,-2.521 0.874,-4.27 3.14,-19.016004 5.383,-38.057004 7.92,-57.227004 l 0.274,-2.074 0.28,-1.686 c 0.307,-0.8 0.337,-1.033 0.841,-1.737 1.119,-1.562 2.062,-2.818 5.945,-2.204 z"/>
|
||||
<path id="path146" style="clip-rule:evenodd;fill:#005184;fill-rule:nonzero;stroke-linejoin:round;stroke-miterlimit:1.41420996" d="m 300.65411,44.858286 c 2.74,0.486 2.778,0.936 3.448,1.587 2.555,2.484 2.191,7.238 2.75,11.471 2.467,18.612 4.172,37.353 7.871,55.935004 0,0 3.607,4.013 6.264,7.099 11.064,12.849 21.019,26.411 26.284,43.975 6.27,20.92 3.585,41.82 -7.633,60.315 -9.349,15.414 -24.268,26.668 -39.964,35.962 l -0.593,-1.853 c 17.279,-12.08 34.133,-26.972 39.318,-48.24 3.677,-15.083 -0.809,-32.515 -6.571,-47.292 -5.094,-13.065 -14.589,-24.209 -23.233,-34.394 -1.789,-2.107 -3.603,-4.192 -5.472,-6.229 0,0 -2.204,-3.069 -2.806,-6.739 -2.582,-15.749 -3.416,-31.540004 -4.699,-47.326004 0,0 -0.34,-4.424 -0.589,-7.754 l -0.098,-1.322 -30.582,18.428 -15.221,9.11 -0.801,0.477 -3.955,-6.151 0.767,-0.531 14.605,-10.067 37.28,-25.534 c 0,0 1.172,-0.939 3.63,-0.927 z m 14.362,70.312004 c 0.178,0.639 -0.049,-0.186 0,0 z"/>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.63063431px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.10960984px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="110.04511"
|
||||
y="98.826035"
|
||||
id="text861"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan859"
|
||||
x="110.04511"
|
||||
y="98.826035"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.63063431px;font-family:Baumans;-inkscape-font-specification:'Baumans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:2.10960984px">converse<tspan
|
||||
style="fill:#a2a2a2;fill-opacity:1;stroke-width:2.10960984px"
|
||||
id="tspan867">.js</tspan></tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.49652481px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.77068853px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="124.58434"
|
||||
y="128.44286"
|
||||
id="text865"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan863"
|
||||
x="124.58434"
|
||||
y="128.44286"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.49652481px;font-family:Muli;-inkscape-font-specification:'Muli, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#484848;fill-opacity:1;stroke-width:0.77068853px">messaging freedom</tspan></text>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.8 KiB |
|
@ -2,7 +2,7 @@
|
|||
"short_name": "Converse",
|
||||
"name": "Converse Chat",
|
||||
"description": "Messaging Freedom",
|
||||
"version": "9.1.1",
|
||||
"version": "10.1.6",
|
||||
"categories": ["social"],
|
||||
"icons": [
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<title>Converse.js | Mobile</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Converse: An XMPP chat client which can be integrated into any website" />
|
||||
<meta name="author" content="JC Brand" />
|
||||
|
@ -18,9 +19,9 @@
|
|||
<script type="text/javascript" src="analytics.js"></script>
|
||||
<!-- *********************************************************************** -->
|
||||
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/9.1.1/dist/converse.min.css" />
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/dist/converse.min.css" />
|
||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||
<script src="https://cdn.conversejs.org/9.1.1/dist/converse.min.js"></script>
|
||||
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body id="page-top" data-spy="scroll" class="converse-website">
|
||||
|
@ -240,11 +241,9 @@
|
|||
<h2>Converse is supported by:</h2>
|
||||
<ul >
|
||||
<li><a href="https://www.keycdn.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
|
||||
<li><a href="https://wikisuite.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 4em" src="/logo/wikisuite-white.png" alt="WikiSuite"></a></li>
|
||||
<li><a href="https://weblate.org?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 2.6em" src="/logo/weblate-button.svg" alt="Weblate"></a></li>
|
||||
<li><a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em; padding-top: 0.5em" src="/logo/codefirst.png" alt="Codefirst"></a></li>
|
||||
<li><a href="https://www.b1-systems.de?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 5em" src="/logo/b1-systems.svg" alt="B1 Systems"></a></li>
|
||||
<li><a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 12em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy"></a></li>
|
||||
<li><a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
41269
package-lock.json
generated
90
package.json
|
@ -1,42 +1,46 @@
|
|||
{
|
||||
"name": "converse.js",
|
||||
"version": "9.1.1",
|
||||
"version": "10.1.6",
|
||||
"description": "Browser based XMPP chat client",
|
||||
"browser": "dist/converse.js",
|
||||
"module": "src/converse.js",
|
||||
"module": "src/index.js",
|
||||
"workspaces": [
|
||||
"src/headless"
|
||||
],
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.json",
|
||||
"CHANGES.md",
|
||||
"LICENSE.txt",
|
||||
"README.md",
|
||||
"COPYRIGHT",
|
||||
"dist/",
|
||||
"docs/**/*.rst",
|
||||
"docs/**/*.md",
|
||||
"docs/**/*.rst",
|
||||
"sass/**/*.scss",
|
||||
"src/*.js",
|
||||
"src/**/*.pot",
|
||||
"src/**/*.po",
|
||||
"src/**/*.js",
|
||||
"src/**/*.html",
|
||||
"src/**/*.svg",
|
||||
"src/**/*.js",
|
||||
"src/**/*.json",
|
||||
"src/**/*.md",
|
||||
"src/**/*.po",
|
||||
"src/**/*.pot",
|
||||
"src/**/*.scss",
|
||||
"src/**/*.svg",
|
||||
"src/**/*.txt",
|
||||
"src/**/*.json"
|
||||
"3rdparty/*.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack/webpack.build.js",
|
||||
"lint": "eslint src/**/*.js",
|
||||
"test": "karma start karma.conf",
|
||||
"cdn": "ASSET_PATH=https://cdn.conversejs.org/dist/ npm run build",
|
||||
"clean": "rm -rf node_modules dist *.zip src/headless/dist src/headless/node_modules",
|
||||
"dev": "webpack --config webpack/webpack.build.js --mode=development",
|
||||
"headless": "webpack --config webpack/webpack.headless.js",
|
||||
"headless-dev": "webpack --config webpack/webpack.headless.js --mode=development",
|
||||
"lerna": "lerna bootstrap --hoist --ignore-scripts",
|
||||
"nodeps": "webpack --config webpack/webpack.nodeps.js",
|
||||
"prepare": "npm run lerna && npm run build",
|
||||
"serve": "webpack serve --config webpack/webpack.serve.js",
|
||||
"watch": "webpack --watch --config webpack/webpack.build.js --mode=development"
|
||||
"watch": "webpack --watch --config webpack/webpack.build.js --mode=development",
|
||||
"types": "tsc --declaration --emitDeclarationOnly --allowJs",
|
||||
"check:types": "tsc --noEmit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -63,54 +67,70 @@
|
|||
"browser": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.14.5",
|
||||
"@babel/core": "^7.17.9",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@converse/headless": "file:src/headless",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
||||
"autoprefixer": "^10.4.5",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.2.5",
|
||||
"babel-loader": "^9.1.0",
|
||||
"bootstrap.native-loader": "2.0.0",
|
||||
"clean-css-cli": "^5.6.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"clean-css-cli": "^5.6.2",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint": "^8.21.0",
|
||||
"fast-text-encoding": "^1.0.3",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"http-server": "^14.1.0",
|
||||
"imports-loader": "^3.0.0",
|
||||
"imports-loader": "^4.0.0",
|
||||
"install": "^0.13.0",
|
||||
"jsdoc": "^3.6.7",
|
||||
"jsdoc": "^4.0.0",
|
||||
"karma": "^6.3.19",
|
||||
"karma-chrome-launcher": "^3.1.1",
|
||||
"karma-cli": "^2.0.0",
|
||||
"karma-jasmine": "^5.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.7.0",
|
||||
"karma-jasmine-html-reporter": "^2.0.0",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"lerna": "^4.0.0",
|
||||
"mini-css-extract-plugin": "^2.6.0",
|
||||
"minimist": "^1.2.6",
|
||||
"po-loader": "0.6.1",
|
||||
"postcss": "^8.4.12",
|
||||
"postcss-clean": "1.2.0",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"po-loader": "0.7.0",
|
||||
"po2json": "^1.0.0-beta-3",
|
||||
"postcss": "^8.4.16",
|
||||
"postcss-loader": "^7.0.1",
|
||||
"prettierx": "^0.19.0",
|
||||
"sass": "^1.51.0",
|
||||
"sass-loader": "^12.6.0",
|
||||
"sass-loader": "^13.1.0",
|
||||
"style-loader": "^3.1.0",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"tsc": "^2.0.4",
|
||||
"typescript": "^4.9.5",
|
||||
"typescript-eslint-parser": "^22.0.0",
|
||||
"uglify-js": "^3.17.4",
|
||||
"webpack": "^5.86.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.8.1",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "5.14.0",
|
||||
"@converse/openpromise": "^0.0.1",
|
||||
"@converse/skeletor": "^0.0.8",
|
||||
"bootstrap": "^4.6.0",
|
||||
"bootstrap.native": "^2.0.27",
|
||||
"client-compress": "^2.2.2",
|
||||
"dayjs": "^1.11.8",
|
||||
"dompurify": "^2.3.1",
|
||||
"favico.js-slevomat": "^0.3.11",
|
||||
"gifuct-js": "^2.1.2",
|
||||
"jed": "1.1.1",
|
||||
"lit": "^2.2.2"
|
||||
"lit": "^2.4.0",
|
||||
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pluggable.js": "^3.0.1",
|
||||
"sizzle": "^2.3.5",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"strophe.js": "^1.6.2",
|
||||
"urijs": "^1.19.10"
|
||||
},
|
||||
"resolutions": {
|
||||
"autoprefixer": "10.4.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,5 @@
|
|||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer'),
|
||||
require('postcss-clean')
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
zc.buildout==2.13.6
|
||||
Sphinx==4.5.0
|
||||
docutils==0.17.1
|
||||
sphinx-bootstrap-theme==0.8.1
|
||||
|
|
|
@ -49,7 +49,7 @@ const converse = {
|
|||
*
|
||||
* @memberOf converse
|
||||
* @method load
|
||||
* @param {object} settings A map of configuration-settings that are needed at load time.
|
||||
* @param { object } settings A map of configuration-settings that are needed at load time.
|
||||
* @example
|
||||
* converse.load({assets_path: '/path/to/assets/'});
|
||||
*/
|
||||
|
@ -57,7 +57,7 @@ const converse = {
|
|||
if (settings.assets_path) {
|
||||
__webpack_public_path__ = settings.assets_path; // eslint-disable-line no-undef
|
||||
}
|
||||
require('./converse.js');
|
||||
require('./index.js');
|
||||
Object.keys(plugins).forEach(name => converse.plugins.add(name, plugins[name]));
|
||||
return converse;
|
||||
}
|
||||
|
|
|
@ -13,5 +13,5 @@ It's also installable with NPM/Yarn as [@converse/headless](https://www.npmjs.co
|
|||
|
||||
The main distribution of Converse relies on the headless build.
|
||||
|
||||
The file [src/headless/headless.js](https://github.com/jcbrand/converse.js/blob/master/src/headless/headless.js)
|
||||
The file [src/headless/index.js](https://github.com/jcbrand/converse.js/blob/master/src/headless/index.js)
|
||||
is used to determine which plugins are included in the build.
|
||||
|
|
|
@ -2,766 +2,16 @@
|
|||
* @copyright The Converse.js contributors
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import URI from 'urijs';
|
||||
import _converse from '@converse/headless/shared/_converse';
|
||||
import './shared/constants.js';
|
||||
import _converse from './shared/_converse';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import connection_api from '@converse/headless/shared/connection/api.js';
|
||||
import api from './shared/api/index.js';
|
||||
import dayjs from 'dayjs';
|
||||
import i18n from '@converse/headless/shared/i18n';
|
||||
import invoke from 'lodash-es/invoke';
|
||||
import isFunction from 'lodash-es/isFunction';
|
||||
import log from '@converse/headless/log.js';
|
||||
import pluggable from 'pluggable.js/src/pluggable.js';
|
||||
import sizzle from 'sizzle';
|
||||
import u, { setUnloadEvent, replacePromise } from '@converse/headless/utils/core.js';
|
||||
import { CHAT_STATES, KEYCODES } from './shared/constants.js';
|
||||
import { Collection } from "@converse/skeletor/src/collection";
|
||||
import { Connection, MockConnection } from '@converse/headless/shared/connection/index.js';
|
||||
import { Events } from '@converse/skeletor/src/events.js';
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
|
||||
import { TimeoutError } from '@converse/headless/shared/errors';
|
||||
import { getOpenPromise } from '@converse/openpromise';
|
||||
import { html } from 'lit';
|
||||
import { initAppSettings } from '@converse/headless/shared/settings/utils.js';
|
||||
import { settings_api, user_settings_api } from '@converse/headless/shared/settings/api.js';
|
||||
import { sprintf } from 'sprintf-js';
|
||||
import i18n from './shared/i18n';
|
||||
|
||||
export { converse } from './shared/api/public.js';
|
||||
export { _converse };
|
||||
export { i18n };
|
||||
|
||||
import {
|
||||
attemptNonPreboundSession,
|
||||
cleanup,
|
||||
getConnectionServiceURL,
|
||||
initClientConfig,
|
||||
initPlugins,
|
||||
initSessionStorage,
|
||||
registerGlobalEventHandlers,
|
||||
setUserJID,
|
||||
} from './utils/init.js';
|
||||
export { api };
|
||||
|
||||
dayjs.extend(advancedFormat);
|
||||
|
||||
// Add Strophe Namespaces
|
||||
Strophe.addNamespace('ACTIVITY', 'http://jabber.org/protocol/activity');
|
||||
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');
|
||||
Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
|
||||
Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
|
||||
Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0');
|
||||
Strophe.addNamespace('MENTIONS', 'urn:xmpp:mmn:0');
|
||||
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
|
||||
Strophe.addNamespace('MODERATE', 'urn:xmpp:message-moderate:0');
|
||||
Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
|
||||
Strophe.addNamespace('OCCUPANTID', 'urn:xmpp:occupant-id:0');
|
||||
Strophe.addNamespace('OMEMO', 'eu.siacs.conversations.axolotl');
|
||||
Strophe.addNamespace('OUTOFBAND', 'jabber:x:oob');
|
||||
Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub');
|
||||
Strophe.addNamespace('RAI', 'urn:xmpp:rai:0');
|
||||
Strophe.addNamespace('RECEIPTS', 'urn:xmpp:receipts');
|
||||
Strophe.addNamespace('REFERENCE', 'urn:xmpp:reference:0');
|
||||
Strophe.addNamespace('REGISTER', 'jabber:iq:register');
|
||||
Strophe.addNamespace('RETRACT', 'urn:xmpp:message-retract:0');
|
||||
Strophe.addNamespace('ROSTERX', 'http://jabber.org/protocol/rosterx');
|
||||
Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm');
|
||||
Strophe.addNamespace('SID', 'urn:xmpp:sid:0');
|
||||
Strophe.addNamespace('SPOILER', 'urn:xmpp:spoiler:0');
|
||||
Strophe.addNamespace('STANZAS', 'urn:ietf:params:xml:ns:xmpp-stanzas');
|
||||
Strophe.addNamespace('STYLING', 'urn:xmpp:styling:0');
|
||||
Strophe.addNamespace('VCARD', 'vcard-temp');
|
||||
Strophe.addNamespace('VCARDUPDATE', 'vcard-temp:x:update');
|
||||
Strophe.addNamespace('XFORM', 'jabber:x:data');
|
||||
Strophe.addNamespace('XHTML', 'http://www.w3.org/1999/xhtml');
|
||||
|
||||
_converse.VERSION_NAME = "v9.1.1";
|
||||
|
||||
Object.assign(_converse, Events);
|
||||
|
||||
// Make converse pluggable
|
||||
pluggable.enable(_converse, '_converse', 'pluggable');
|
||||
|
||||
|
||||
/**
|
||||
* ### The private API
|
||||
*
|
||||
* The private API methods are only accessible via the closured {@link _converse}
|
||||
* object, which is only available to plugins.
|
||||
*
|
||||
* These methods are kept private (i.e. not global) because they may return
|
||||
* sensitive data which should be kept off-limits to other 3rd-party scripts
|
||||
* that might be running in the page.
|
||||
*
|
||||
* @namespace _converse.api
|
||||
* @memberOf _converse
|
||||
*/
|
||||
export const api = _converse.api = {
|
||||
|
||||
connection: connection_api,
|
||||
settings: settings_api,
|
||||
|
||||
/**
|
||||
* Lets you trigger events, which can be listened to via
|
||||
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}
|
||||
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
|
||||
*
|
||||
* Some events also double as promises and can be waited on via {@link _converse.api.waitUntil}.
|
||||
*
|
||||
* @method _converse.api.trigger
|
||||
* @param {string} name - The event name
|
||||
* @param {...any} [argument] - Argument to be passed to the event handler
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.synchronous] - Whether the event is synchronous or not.
|
||||
* When a synchronous event is fired, a promise will be returned
|
||||
* by {@link _converse.api.trigger} which resolves once all the
|
||||
* event handlers' promises have been resolved.
|
||||
*/
|
||||
async trigger (name) {
|
||||
if (!_converse._events) {
|
||||
return;
|
||||
}
|
||||
const args = Array.from(arguments);
|
||||
const options = args.pop();
|
||||
if (options && options.synchronous) {
|
||||
const events = _converse._events[name] || [];
|
||||
const event_args = args.splice(1);
|
||||
await Promise.all(events.map(e => e.callback.apply(e.ctx, event_args)));
|
||||
} else {
|
||||
_converse.trigger.apply(_converse, arguments);
|
||||
}
|
||||
const promise = _converse.promises[name];
|
||||
if (promise !== undefined) {
|
||||
promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers a hook which can be intercepted by registered listeners via
|
||||
* {@link _converse.api.listen.on} or {@link _converse.api.listen.once}.
|
||||
* (see [_converse.api.listen](http://localhost:8000/docs/html/api/-_converse.api.listen.html)).
|
||||
* A hook is a special kind of event which allows you to intercept a data
|
||||
* structure in order to modify it, before passing it back.
|
||||
* @async
|
||||
* @param {string} name - The hook name
|
||||
* @param {...any} context - The context to which the hook applies (could be for example, a {@link _converse.ChatBox)).
|
||||
* @param {...any} data - The data structure to be intercepted and modified by the hook listeners.
|
||||
* @returns {Promise<any>} - A promise that resolves with the modified data structure.
|
||||
*/
|
||||
hook (name, context, data) {
|
||||
const events = _converse._events[name] || [];
|
||||
if (events.length) {
|
||||
// Create a chain of promises, with each one feeding its output to
|
||||
// the next. The first input is a promise with the original data
|
||||
// sent to this hook.
|
||||
return events.reduce((o, e) => o.then(d => e.callback(context, d)), Promise.resolve(data));
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This grouping collects API functions related to the current logged in user.
|
||||
*
|
||||
* @namespace _converse.api.user
|
||||
* @memberOf _converse.api
|
||||
*/
|
||||
user: {
|
||||
settings: user_settings_api,
|
||||
|
||||
/**
|
||||
* @method _converse.api.user.jid
|
||||
* @returns {string} The current user's full JID (Jabber ID)
|
||||
* @example _converse.api.user.jid())
|
||||
*/
|
||||
jid () {
|
||||
return _converse.connection.jid;
|
||||
},
|
||||
|
||||
/**
|
||||
* Logs the user in.
|
||||
*
|
||||
* If called without any parameters, Converse will try
|
||||
* to log the user in by calling the `prebind_url` or `credentials_url` depending
|
||||
* on whether prebinding is used or not.
|
||||
*
|
||||
* @method _converse.api.user.login
|
||||
* @param {string} [jid]
|
||||
* @param {string} [password]
|
||||
* @param {boolean} [automatic=false] - An internally used flag that indicates whether
|
||||
* this method was called automatically once the connection has been
|
||||
* initialized. It's used together with the `auto_login` configuration flag
|
||||
* to determine whether Converse should try to log the user in if it
|
||||
* fails to restore a previous auth'd session.
|
||||
* @returns {void}
|
||||
*/
|
||||
async login (jid, password, automatic=false) {
|
||||
jid = jid || api.settings.get('jid');
|
||||
if (!_converse.connection?.jid || (jid && !u.isSameDomain(_converse.connection.jid, jid))) {
|
||||
await _converse.initConnection();
|
||||
}
|
||||
if (api.settings.get("connection_options")?.worker && (await _converse.connection.restoreWorkerSession())) {
|
||||
return;
|
||||
}
|
||||
if (jid) {
|
||||
jid = await setUserJID(jid);
|
||||
}
|
||||
|
||||
// See whether there is a BOSH session to re-attach to
|
||||
const bosh_plugin = _converse.pluggable.plugins['converse-bosh'];
|
||||
if (bosh_plugin?.enabled()) {
|
||||
if (await _converse.restoreBOSHSession()) {
|
||||
return;
|
||||
} else if (api.settings.get("authentication") === _converse.PREBIND && (!automatic || api.settings.get("auto_login"))) {
|
||||
return _converse.startNewPreboundBOSHSession();
|
||||
}
|
||||
}
|
||||
password = password || api.settings.get("password");
|
||||
const credentials = (jid && password) ? { jid, password } : null;
|
||||
attemptNonPreboundSession(credentials, automatic);
|
||||
},
|
||||
|
||||
/**
|
||||
* Logs the user out of the current XMPP session.
|
||||
* @method _converse.api.user.logout
|
||||
* @example _converse.api.user.logout();
|
||||
*/
|
||||
async logout () {
|
||||
/**
|
||||
* Triggered before the user is logged out
|
||||
* @event _converse#beforeLogout
|
||||
*/
|
||||
await api.trigger('beforeLogout', {'synchronous': true});
|
||||
|
||||
const promise = getOpenPromise();
|
||||
const complete = () => {
|
||||
// Recreate all the promises
|
||||
Object.keys(_converse.promises).forEach(replacePromise);
|
||||
delete _converse.jid
|
||||
/**
|
||||
* Triggered once the user has logged out.
|
||||
* @event _converse#logout
|
||||
*/
|
||||
api.trigger('logout');
|
||||
promise.resolve();
|
||||
}
|
||||
|
||||
_converse.connection.setDisconnectionCause(_converse.LOGOUT, undefined, true);
|
||||
if (_converse.connection !== undefined) {
|
||||
api.listen.once('disconnected', () => complete());
|
||||
_converse.connection.disconnect();
|
||||
} else {
|
||||
complete();
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converse and its plugins trigger various events which you can listen to via the
|
||||
* {@link _converse.api.listen} namespace.
|
||||
*
|
||||
* Some of these events are also available as [ES2015 Promises](http://es6-features.org/#PromiseUsage)
|
||||
* although not all of them could logically act as promises, since some events
|
||||
* might be fired multpile times whereas promises are to be resolved (or
|
||||
* rejected) only once.
|
||||
*
|
||||
* Events which are also promises include:
|
||||
*
|
||||
* * [cachedRoster](/docs/html/events.html#cachedroster)
|
||||
* * [chatBoxesFetched](/docs/html/events.html#chatBoxesFetched)
|
||||
* * [pluginsInitialized](/docs/html/events.html#pluginsInitialized)
|
||||
* * [roster](/docs/html/events.html#roster)
|
||||
* * [rosterContactsFetched](/docs/html/events.html#rosterContactsFetched)
|
||||
* * [rosterGroupsFetched](/docs/html/events.html#rosterGroupsFetched)
|
||||
* * [rosterInitialized](/docs/html/events.html#rosterInitialized)
|
||||
*
|
||||
* The various plugins might also provide promises, and they do this by using the
|
||||
* `promises.add` api method.
|
||||
*
|
||||
* @namespace _converse.api.promises
|
||||
* @memberOf _converse.api
|
||||
*/
|
||||
promises: {
|
||||
/**
|
||||
* By calling `promises.add`, a new [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
|
||||
* is made available for other code or plugins to depend on via the
|
||||
* {@link _converse.api.waitUntil} method.
|
||||
*
|
||||
* Generally, it's the responsibility of the plugin which adds the promise to
|
||||
* also resolve it.
|
||||
*
|
||||
* This is done by calling {@link _converse.api.trigger}, which not only resolves the
|
||||
* promise, but also emits an event with the same name (which can be listened to
|
||||
* via {@link _converse.api.listen}).
|
||||
*
|
||||
* @method _converse.api.promises.add
|
||||
* @param {string|array} [name|names] The name or an array of names for the promise(s) to be added
|
||||
* @param {boolean} [replace=true] Whether this promise should be replaced with a new one when the user logs out.
|
||||
* @example _converse.api.promises.add('foo-completed');
|
||||
*/
|
||||
add (promises, replace=true) {
|
||||
promises = Array.isArray(promises) ? promises : [promises];
|
||||
promises.forEach(name => {
|
||||
const promise = getOpenPromise();
|
||||
promise.replace = replace;
|
||||
_converse.promises[name] = promise;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converse emits events to which you can subscribe to.
|
||||
*
|
||||
* The `listen` namespace exposes methods for creating event listeners
|
||||
* (aka handlers) for these events.
|
||||
*
|
||||
* @namespace _converse.api.listen
|
||||
* @memberOf _converse
|
||||
*/
|
||||
listen: {
|
||||
/**
|
||||
* Lets you listen to an event exactly once.
|
||||
* @method _converse.api.listen.once
|
||||
* @param {string} name The event's name
|
||||
* @param {function} callback The callback method to be called when the event is emitted.
|
||||
* @param {object} [context] The value of the `this` parameter for the callback.
|
||||
* @example _converse.api.listen.once('message', function (messageXML) { ... });
|
||||
*/
|
||||
once: _converse.once.bind(_converse),
|
||||
|
||||
/**
|
||||
* Lets you subscribe to an event.
|
||||
* Every time the event fires, the callback method specified by `callback` will be called.
|
||||
* @method _converse.api.listen.on
|
||||
* @param {string} name The event's name
|
||||
* @param {function} callback The callback method to be called when the event is emitted.
|
||||
* @param {object} [context] The value of the `this` parameter for the callback.
|
||||
* @example _converse.api.listen.on('message', function (messageXML) { ... });
|
||||
*/
|
||||
on: _converse.on.bind(_converse),
|
||||
|
||||
/**
|
||||
* To stop listening to an event, you can use the `not` method.
|
||||
* @method _converse.api.listen.not
|
||||
* @param {string} name The event's name
|
||||
* @param {function} callback The callback method that is to no longer be called when the event fires
|
||||
* @example _converse.api.listen.not('message', function (messageXML);
|
||||
*/
|
||||
not: _converse.off.bind(_converse),
|
||||
|
||||
/**
|
||||
* Subscribe to an incoming stanza
|
||||
* Every a matched stanza is received, the callback method specified by
|
||||
* `callback` will be called.
|
||||
* @method _converse.api.listen.stanza
|
||||
* @param {string} name The stanza's name
|
||||
* @param {object} options Matching options (e.g. 'ns' for namespace, 'type' for stanza type, also 'id' and 'from');
|
||||
* @param {function} handler The callback method to be called when the stanza appears
|
||||
*/
|
||||
stanza (name, options, handler) {
|
||||
if (isFunction(options)) {
|
||||
handler = options;
|
||||
options = {};
|
||||
} else {
|
||||
options = options || {};
|
||||
}
|
||||
_converse.connection.addHandler(
|
||||
handler,
|
||||
options.ns,
|
||||
name,
|
||||
options.type,
|
||||
options.id,
|
||||
options.from,
|
||||
options
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Wait until a promise is resolved or until the passed in function returns
|
||||
* a truthy value.
|
||||
* @method _converse.api.waitUntil
|
||||
* @param {string|function} condition - The name of the promise to wait for,
|
||||
* or a function which should eventually return a truthy value.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
waitUntil (condition) {
|
||||
if (isFunction(condition)) {
|
||||
return u.waitUntil(condition);
|
||||
} else {
|
||||
const promise = _converse.promises[condition];
|
||||
if (promise === undefined) {
|
||||
return null;
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows you to send XML stanzas.
|
||||
* @method _converse.api.send
|
||||
* @param {XMLElement} stanza
|
||||
* @return {void}
|
||||
* @example
|
||||
* const msg = converse.env.$msg({
|
||||
* 'from': 'juliet@example.com/balcony',
|
||||
* 'to': 'romeo@example.net',
|
||||
* 'type':'chat'
|
||||
* });
|
||||
* _converse.api.send(msg);
|
||||
*/
|
||||
send (stanza) {
|
||||
if (!api.connection.connected()) {
|
||||
log.warn("Not sending stanza because we're not connected!");
|
||||
log.warn(Strophe.serialize(stanza));
|
||||
return;
|
||||
}
|
||||
if (typeof stanza === 'string') {
|
||||
stanza = u.toStanza(stanza);
|
||||
} else if (stanza?.nodeTree) {
|
||||
stanza = stanza.nodeTree;
|
||||
}
|
||||
|
||||
if (stanza.tagName === 'iq') {
|
||||
return api.sendIQ(stanza);
|
||||
} else {
|
||||
_converse.connection.send(stanza);
|
||||
api.trigger('send', stanza);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Send an IQ stanza
|
||||
* @method _converse.api.sendIQ
|
||||
* @param {XMLElement} stanza
|
||||
* @param {Integer} [timeout=_converse.STANZA_TIMEOUT]
|
||||
* @param {Boolean} [reject=true] - Whether an error IQ should cause the promise
|
||||
* to be rejected. If `false`, the promise will resolve instead of being rejected.
|
||||
* @returns {Promise} A promise which resolves (or potentially rejected) once we
|
||||
* receive a `result` or `error` stanza or once a timeout is reached.
|
||||
* If the IQ stanza being sent is of type `result` or `error`, there's
|
||||
* nothing to wait for, so an already resolved promise is returned.
|
||||
*/
|
||||
sendIQ (stanza, timeout=_converse.STANZA_TIMEOUT, reject=true) {
|
||||
let promise;
|
||||
stanza = stanza?.nodeTree ?? stanza;
|
||||
if (['get', 'set'].includes(stanza.getAttribute('type'))) {
|
||||
timeout = timeout || _converse.STANZA_TIMEOUT;
|
||||
if (reject) {
|
||||
promise = new Promise((resolve, reject) => _converse.connection.sendIQ(stanza, resolve, reject, timeout));
|
||||
promise.catch(e => {
|
||||
if (e === null) {
|
||||
throw new TimeoutError(
|
||||
`Timeout error after ${timeout}ms for the following IQ stanza: ${Strophe.serialize(stanza)}`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise = new Promise(resolve => _converse.connection.sendIQ(stanza, resolve, resolve, timeout));
|
||||
}
|
||||
} else {
|
||||
_converse.connection.sendIQ(stanza);
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
api.trigger('send', stanza);
|
||||
return promise;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_converse.shouldClearCache = () => (
|
||||
!_converse.config.get('trusted') ||
|
||||
api.settings.get('clear_cache_on_logout') ||
|
||||
_converse.isTestEnv()
|
||||
);
|
||||
|
||||
|
||||
export function clearSession () {
|
||||
_converse.session?.destroy();
|
||||
delete _converse.session;
|
||||
_converse.shouldClearCache() && _converse.api.user.settings.clear();
|
||||
/**
|
||||
* Synchronouse event triggered once the user session has been cleared,
|
||||
* for example when the user has logged out or when Converse has
|
||||
* disconnected for some other reason.
|
||||
* @event _converse#clearSession
|
||||
*/
|
||||
return _converse.api.trigger('clearSession', {'synchronous': true});
|
||||
}
|
||||
|
||||
|
||||
_converse.initConnection = function () {
|
||||
const api = _converse.api;
|
||||
|
||||
if (! api.settings.get('bosh_service_url')) {
|
||||
if (api.settings.get("authentication") === _converse.PREBIND) {
|
||||
throw new Error("authentication is set to 'prebind' but we don't have a BOSH connection");
|
||||
}
|
||||
}
|
||||
|
||||
const XMPPConnection = _converse.isTestEnv() ? MockConnection : Connection;
|
||||
_converse.connection = new XMPPConnection(
|
||||
getConnectionServiceURL(),
|
||||
Object.assign(
|
||||
_converse.default_connection_options,
|
||||
api.settings.get("connection_options"),
|
||||
{'keepalive': api.settings.get("keepalive")}
|
||||
)
|
||||
);
|
||||
|
||||
setUpXMLLogging();
|
||||
/**
|
||||
* Triggered once the `Connection` constructor has been initialized, which
|
||||
* will be responsible for managing the connection to the XMPP server.
|
||||
*
|
||||
* @event _converse#connectionInitialized
|
||||
*/
|
||||
api.trigger('connectionInitialized');
|
||||
}
|
||||
|
||||
|
||||
function setUpXMLLogging () {
|
||||
const lmap = {}
|
||||
lmap[Strophe.LogLevel.DEBUG] = 'debug';
|
||||
lmap[Strophe.LogLevel.INFO] = 'info';
|
||||
lmap[Strophe.LogLevel.WARN] = 'warn';
|
||||
lmap[Strophe.LogLevel.ERROR] = 'error';
|
||||
lmap[Strophe.LogLevel.FATAL] = 'fatal';
|
||||
|
||||
Strophe.log = (level, msg) => log.log(msg, lmap[level]);
|
||||
Strophe.error = (msg) => log.error(msg);
|
||||
|
||||
_converse.connection.xmlInput = body => log.debug(body.outerHTML, 'color: darkgoldenrod');
|
||||
_converse.connection.xmlOutput = body => log.debug(body.outerHTML, 'color: darkcyan');
|
||||
}
|
||||
|
||||
_converse.saveWindowState = function (ev) {
|
||||
// XXX: eventually we should be able to just use
|
||||
// document.visibilityState (when we drop support for older
|
||||
// browsers).
|
||||
let state;
|
||||
const event_map = {
|
||||
'focus': "visible",
|
||||
'focusin': "visible",
|
||||
'pageshow': "visible",
|
||||
'blur': "hidden",
|
||||
'focusout': "hidden",
|
||||
'pagehide': "hidden"
|
||||
};
|
||||
ev = ev || document.createEvent('Events');
|
||||
if (ev.type in event_map) {
|
||||
state = event_map[ev.type];
|
||||
} else {
|
||||
state = document.hidden ? "hidden" : "visible";
|
||||
}
|
||||
_converse.windowState = state;
|
||||
/**
|
||||
* Triggered when window state has changed.
|
||||
* Used to determine when a user left the page and when came back.
|
||||
* @event _converse#windowStateChanged
|
||||
* @type { object }
|
||||
* @property{ string } state - Either "hidden" or "visible"
|
||||
* @example _converse.api.listen.on('windowStateChanged', obj => { ... });
|
||||
*/
|
||||
api.trigger('windowStateChanged', {state});
|
||||
}
|
||||
|
||||
_converse.ConnectionFeedback = Model.extend({
|
||||
defaults: {
|
||||
'connection_status': Strophe.Status.DISCONNECTED,
|
||||
'message': ''
|
||||
},
|
||||
initialize () {
|
||||
this.on('change', () => api.trigger('connfeedback', _converse.connfeedback));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export const converse = window.converse || {};
|
||||
|
||||
|
||||
/**
|
||||
* ### The Public API
|
||||
*
|
||||
* This namespace contains public API methods which are are
|
||||
* accessible on the global `converse` object.
|
||||
* They are public, because any JavaScript in the
|
||||
* page can call them. Public methods therefore don’t expose any sensitive
|
||||
* or closured data. To do that, you’ll need to create a plugin, which has
|
||||
* access to the private API method.
|
||||
*
|
||||
* @global
|
||||
* @namespace converse
|
||||
*/
|
||||
Object.assign(converse, {
|
||||
|
||||
CHAT_STATES,
|
||||
|
||||
keycodes: KEYCODES,
|
||||
|
||||
/**
|
||||
* Public API method which initializes Converse.
|
||||
* This method must always be called when using Converse.
|
||||
* @async
|
||||
* @memberOf converse
|
||||
* @method initialize
|
||||
* @param {object} config A map of [configuration-settings](https://conversejs.org/docs/html/configuration.html#configuration-settings).
|
||||
* @example
|
||||
* converse.initialize({
|
||||
* auto_list_rooms: false,
|
||||
* auto_subscribe: false,
|
||||
* bosh_service_url: 'https://bind.example.com',
|
||||
* hide_muc_server: false,
|
||||
* i18n: 'en',
|
||||
* play_sounds: true,
|
||||
* show_controlbox_by_default: true,
|
||||
* debug: false,
|
||||
* roster_groups: true
|
||||
* });
|
||||
*/
|
||||
async initialize (settings) {
|
||||
await cleanup(_converse);
|
||||
|
||||
setUnloadEvent();
|
||||
initAppSettings(settings);
|
||||
_converse.strict_plugin_dependencies = settings.strict_plugin_dependencies; // Needed by pluggable.js
|
||||
log.setLogLevel(api.settings.get("loglevel"));
|
||||
|
||||
if (api.settings.get("authentication") === _converse.ANONYMOUS) {
|
||||
if (api.settings.get("auto_login") && !api.settings.get('jid')) {
|
||||
throw new Error("Config Error: you need to provide the server's " +
|
||||
"domain via the 'jid' option when using anonymous " +
|
||||
"authentication with auto_login.");
|
||||
}
|
||||
}
|
||||
_converse.router.route(
|
||||
/^converse\?loglevel=(debug|info|warn|error|fatal)$/, 'loglevel',
|
||||
l => log.setLogLevel(l)
|
||||
);
|
||||
_converse.connfeedback = new _converse.ConnectionFeedback();
|
||||
|
||||
/* When reloading the page:
|
||||
* For new sessions, we need to send out a presence stanza to notify
|
||||
* the server/network that we're online.
|
||||
* When re-attaching to an existing session we don't need to again send out a presence stanza,
|
||||
* because it's as if "we never left" (see onConnectStatusChanged).
|
||||
* https://github.com/conversejs/converse.js/issues/521
|
||||
*/
|
||||
_converse.send_initial_presence = true;
|
||||
|
||||
await initSessionStorage(_converse);
|
||||
await initClientConfig(_converse);
|
||||
await i18n.initialize();
|
||||
initPlugins(_converse);
|
||||
|
||||
// Register all custom elements
|
||||
// XXX: api.elements is defined in the UI part of Converse, outside of @converse/headless.
|
||||
// This line should probably be moved to the UI code as part of a larger refactoring.
|
||||
api.elements?.register();
|
||||
|
||||
registerGlobalEventHandlers(_converse);
|
||||
|
||||
try {
|
||||
!History.started && _converse.router.history.start();
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
const plugins = _converse.pluggable.plugins
|
||||
if (api.settings.get("auto_login") || api.settings.get("keepalive") && invoke(plugins['converse-bosh'], 'enabled')) {
|
||||
await api.user.login(null, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered once converse.initialize has finished.
|
||||
* @event _converse#initialized
|
||||
*/
|
||||
api.trigger('initialized');
|
||||
|
||||
if (_converse.isTestEnv()) {
|
||||
return _converse;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposes methods for adding and removing plugins. You'll need to write a plugin
|
||||
* if you want to have access to the private API methods defined further down below.
|
||||
*
|
||||
* For more information on plugins, read the documentation on [writing a plugin](/docs/html/plugin_development.html).
|
||||
* @namespace plugins
|
||||
* @memberOf converse
|
||||
*/
|
||||
plugins: {
|
||||
/**
|
||||
* Registers a new plugin.
|
||||
* @method converse.plugins.add
|
||||
* @param {string} name The name of the plugin
|
||||
* @param {object} plugin The plugin object
|
||||
* @example
|
||||
* const plugin = {
|
||||
* initialize: function () {
|
||||
* // Gets called as soon as the plugin has been loaded.
|
||||
*
|
||||
* // Inside this method, you have access to the private
|
||||
* // API via `_covnerse.api`.
|
||||
*
|
||||
* // The private _converse object contains the core logic
|
||||
* // and data-structures of Converse.
|
||||
* }
|
||||
* }
|
||||
* converse.plugins.add('myplugin', plugin);
|
||||
*/
|
||||
add (name, plugin) {
|
||||
plugin.__name__ = name;
|
||||
if (_converse.pluggable.plugins[name] !== undefined) {
|
||||
throw new TypeError(
|
||||
`Error: plugin with name "${name}" has already been ` + 'registered!'
|
||||
);
|
||||
} else {
|
||||
_converse.pluggable.plugins[name] = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
/**
|
||||
* Utility methods and globals from bundled 3rd party libraries.
|
||||
* @typedef ConverseEnv
|
||||
* @property {function} converse.env.$build - Creates a Strophe.Builder, for creating stanza objects.
|
||||
* @property {function} converse.env.$iq - Creates a Strophe.Builder with an <iq/> element as the root.
|
||||
* @property {function} converse.env.$msg - Creates a Strophe.Builder with an <message/> element as the root.
|
||||
* @property {function} converse.env.$pres - Creates a Strophe.Builder with an <presence/> element as the root.
|
||||
* @property {function} converse.env.Promise - The Promise implementation used by Converse.
|
||||
* @property {function} converse.env.Strophe - The [Strophe](http://strophe.im/strophejs) XMPP library used by Converse.
|
||||
* @property {function} converse.env.f - And instance of Lodash with its methods wrapped to produce immutable auto-curried iteratee-first data-last methods.
|
||||
* @property {function} converse.env.sizzle - [Sizzle](https://sizzlejs.com) CSS selector engine.
|
||||
* @property {function} converse.env.sprintf
|
||||
* @property {object} converse.env._ - The instance of [lodash-es](http://lodash.com) used by Converse.
|
||||
* @property {object} converse.env.dayjs - [DayJS](https://github.com/iamkun/dayjs) date manipulation library.
|
||||
* @property {object} converse.env.utils - Module containing common utility methods used by Converse.
|
||||
* @memberOf converse
|
||||
*/
|
||||
'env': {
|
||||
$build,
|
||||
$iq,
|
||||
$msg,
|
||||
$pres,
|
||||
'utils': u,
|
||||
Collection,
|
||||
Model,
|
||||
Promise,
|
||||
Strophe,
|
||||
URI,
|
||||
dayjs,
|
||||
html,
|
||||
log,
|
||||
sizzle,
|
||||
sprintf,
|
||||
u,
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
* --------------------
|
||||
* Any of the following components may be removed if they're not needed.
|
||||
*/
|
||||
import "./plugins/adhoc.js"; // XEP-0050 Ad Hoc Commands
|
||||
import "./plugins/bookmarks/index.js"; // XEP-0199 XMPP Ping
|
||||
import "./plugins/bosh.js"; // XEP-0206 BOSH
|
||||
import "./plugins/caps/index.js"; // XEP-0115 Entity Capabilities
|
||||
import "./plugins/carbons.js"; // XEP-0280 Message Carbons
|
||||
import "./plugins/chat/index.js"; // RFC-6121 Instant messaging
|
||||
import "./plugins/chatboxes/index.js";
|
||||
import "./plugins/disco/index.js"; // XEP-0030 Service discovery
|
||||
import "./plugins/headlines.js"; // Support for headline messages
|
||||
import "./plugins/adhoc/index.js"; // XEP-0050 Ad Hoc Commands
|
||||
import "./plugins/headlines/index.js"; // Support for headline messages
|
||||
import "./plugins/mam/index.js"; // XEP-0313 Message Archive Management
|
||||
import "./plugins/muc/index.js"; // XEP-0045 Multi-user chat
|
||||
import "./plugins/ping/index.js"; // XEP-0199 XMPP Ping
|
|
@ -1,4 +1,4 @@
|
|||
import isElement from 'lodash-es/isElement';
|
||||
import { isElement } from './utils/core.js';
|
||||
|
||||
const LEVELS = {
|
||||
'debug': 0,
|
||||
|
@ -8,24 +8,26 @@ const LEVELS = {
|
|||
'fatal': 4
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
const logger = Object.assign({
|
||||
'debug': console?.log ? console.log.bind(console) : function noop () {},
|
||||
'error': console?.log ? console.log.bind(console) : function noop () {},
|
||||
'info': console?.log ? console.log.bind(console) : function noop () {},
|
||||
'warn': console?.log ? console.log.bind(console) : function noop () {}
|
||||
}, console);
|
||||
/* eslint-enable @typescript-eslint/no-empty-function */
|
||||
|
||||
|
||||
/**
|
||||
* The log namespace
|
||||
* @namespace log
|
||||
*/
|
||||
const log = {
|
||||
export default {
|
||||
|
||||
/**
|
||||
* The the log-level, which determines how verbose the logging is.
|
||||
* @method log#setLogLevel
|
||||
* @param { integer } level - The loglevel which allows for filtering of log messages
|
||||
* @param { number } level - The loglevel which allows for filtering of log messages
|
||||
*/
|
||||
setLogLevel (level) {
|
||||
if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
|
||||
|
@ -42,7 +44,7 @@ const log = {
|
|||
* logged as well.
|
||||
* @method log#log
|
||||
* @param { string | Error } message - The message to be logged
|
||||
* @param { integer } level - The loglevel which allows for filtering of log messages
|
||||
* @param { number } level - The loglevel which allows for filtering of log messages
|
||||
*/
|
||||
log (message, level, style='') {
|
||||
if (LEVELS[level] < LEVELS[this.loglevel]) {
|
||||
|
@ -93,5 +95,3 @@ const log = {
|
|||
this.log(message, 'fatal', style);
|
||||
}
|
||||
}
|
||||
|
||||
export default log;
|
||||
|
|
221
src/headless/package-lock.json
generated
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"name": "@converse/headless",
|
||||
"version": "9.1.0",
|
||||
"version": "10.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@converse/headless",
|
||||
"version": "9.1.0",
|
||||
"version": "10.1.0",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@converse/openpromise": "^0.0.1",
|
||||
"@converse/skeletor": "0.0.7",
|
||||
"dayjs": "1.11.1",
|
||||
"@converse/skeletor": "0.0.8",
|
||||
"dayjs": "1.11.8",
|
||||
"dompurify": "^2.3.1",
|
||||
"filesize": "^7.0.0",
|
||||
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||
|
@ -19,52 +19,60 @@
|
|||
"pluggable.js": "3.0.1",
|
||||
"sizzle": "^2.3.5",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"strophe.js": "1.5.0",
|
||||
"strophe.js": "1.6.0",
|
||||
"urijs": "^1.19.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@converse/localforage-getitems": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@converse/localforage-getitems/-/localforage-getitems-1.4.3.tgz",
|
||||
"integrity": "sha512-ofch1Zv9+CxU4xYxBq+QmsJFKLi6FZ69REPoTc56eeWN6ps0/+g5gD4/gwukEFhAuE8jsBpjcrnizXsa4WsMxQ==",
|
||||
"dependencies": {
|
||||
"localforage": ">=1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@converse/openpromise": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
||||
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
||||
},
|
||||
"node_modules/@converse/skeletor": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.7.tgz",
|
||||
"integrity": "sha512-JqK1lND0R1l9UEH/0cA7AGcP7EzB+NdPoMmCYNHBaSBins7ir+HHsGxw9eIHWJMPNXIN4ERG2oKwUTwVLryxmA==",
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
|
||||
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
|
||||
"dependencies": {
|
||||
"@converse/localforage-getitems": "1.4.3",
|
||||
"lit-html": "^2.0.0-rc.2",
|
||||
"localforage": "^1.10.0",
|
||||
"localforage-driver-memory": "^1.0.5",
|
||||
"localforage-getitems": "github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||
"localforage-setitems": "^1.4.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mergebounce": "0.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
||||
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
|
||||
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.3.tgz",
|
||||
"integrity": "sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
|
@ -76,7 +84,7 @@
|
|||
"node_modules/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=",
|
||||
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
|
||||
"dependencies": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
|
@ -131,13 +139,13 @@
|
|||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz",
|
||||
"integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA=="
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz",
|
||||
"integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
|
@ -145,9 +153,9 @@
|
|||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.6.tgz",
|
||||
"integrity": "sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg=="
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
|
||||
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
|
||||
},
|
||||
"node_modules/filesize": {
|
||||
"version": "7.0.0",
|
||||
|
@ -195,7 +203,7 @@
|
|||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
|
@ -211,7 +219,7 @@
|
|||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -253,15 +261,15 @@
|
|||
"node_modules/lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
|
||||
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lit-html": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.2.tgz",
|
||||
"integrity": "sha512-cJofCRXuizwyaiGt9pJjJOcauezUlSB6t87VBXsPwRhbzF29MgD8GH6fZ0BuZdXAAC02IRONZBd//VPUuU8QbQ==",
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
|
||||
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
|
||||
"dependencies": {
|
||||
"@types/trusted-types": "^2.0.2"
|
||||
}
|
||||
|
@ -294,18 +302,10 @@
|
|||
"localforage": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/localforage-getitems": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "git+ssh://git@github.com/conversejs/localForage-getItems.git#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||
"integrity": "sha512-J1Q2IqJgwWNJOOX8Q0UULrBEXZExmLwuGWXp/bsjWpuA5LL8RDJcmbUX/3CD9DqjvxvNTnutw2/973CD/EqXMQ==",
|
||||
"dependencies": {
|
||||
"localforage": ">=1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/localforage-setitems": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
||||
"integrity": "sha1-NrhZDVB9+1yAQDPih+zljYiZbV8=",
|
||||
"integrity": "sha512-uU2ZydBZZkwaP0s1m5NMJXKOQJ0pfqFjDfHdv/SU9E1SWZpr4U3qpyJLZSZJvCvaAeBEL9Bvzd0/CWC9Jtk3wg==",
|
||||
"dependencies": {
|
||||
"localforage": ">=1.4.0"
|
||||
}
|
||||
|
@ -379,24 +379,25 @@
|
|||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.70.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
|
||||
"integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
|
||||
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
"node": ">=14.18.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/sizzle": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.6.tgz",
|
||||
"integrity": "sha512-abtd95IkbcMAaYk1Lux4k9Xz6wnQqyLy2aco9HGJ8jVaCDEcc+ug0hW8RdV6aIre3ycWXxPdcX0u7QL/1UaSoA=="
|
||||
"version": "2.3.10",
|
||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
|
||||
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
|
@ -404,15 +405,15 @@
|
|||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||
},
|
||||
"node_modules/strophe.js": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
|
||||
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
|
||||
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
|
||||
"dependencies": {
|
||||
"abab": "^2.0.3",
|
||||
"karma-rollup-preprocessor": "^7.0.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@xmldom/xmldom": "0.8.2",
|
||||
"@xmldom/xmldom": "0.8.3",
|
||||
"ws": "^8.5.0"
|
||||
}
|
||||
},
|
||||
|
@ -438,16 +439,16 @@
|
|||
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
|
@ -460,45 +461,53 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@converse/localforage-getitems": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@converse/localforage-getitems/-/localforage-getitems-1.4.3.tgz",
|
||||
"integrity": "sha512-ofch1Zv9+CxU4xYxBq+QmsJFKLi6FZ69REPoTc56eeWN6ps0/+g5gD4/gwukEFhAuE8jsBpjcrnizXsa4WsMxQ==",
|
||||
"requires": {
|
||||
"localforage": ">=1.4.0"
|
||||
}
|
||||
},
|
||||
"@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": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.7.tgz",
|
||||
"integrity": "sha512-JqK1lND0R1l9UEH/0cA7AGcP7EzB+NdPoMmCYNHBaSBins7ir+HHsGxw9eIHWJMPNXIN4ERG2oKwUTwVLryxmA==",
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
|
||||
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
|
||||
"requires": {
|
||||
"@converse/localforage-getitems": "1.4.3",
|
||||
"lit-html": "^2.0.0-rc.2",
|
||||
"localforage": "^1.10.0",
|
||||
"localforage-driver-memory": "^1.0.5",
|
||||
"localforage-getitems": "github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||
"localforage-setitems": "^1.4.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mergebounce": "0.1.1"
|
||||
}
|
||||
},
|
||||
"@types/trusted-types": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
||||
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
|
||||
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
|
||||
},
|
||||
"@xmldom/xmldom": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
||||
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ==",
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.3.tgz",
|
||||
"integrity": "sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ==",
|
||||
"optional": true
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
||||
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
|
@ -507,7 +516,7 @@
|
|||
"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=",
|
||||
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
|
||||
"requires": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
|
@ -547,9 +556,9 @@
|
|||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz",
|
||||
"integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA=="
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz",
|
||||
"integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.1",
|
||||
|
@ -557,9 +566,9 @@
|
|||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.6.tgz",
|
||||
"integrity": "sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg=="
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
|
||||
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
|
||||
},
|
||||
"filesize": {
|
||||
"version": "7.0.0",
|
||||
|
@ -591,7 +600,7 @@
|
|||
"immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
|
@ -604,7 +613,7 @@
|
|||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.3",
|
||||
|
@ -631,15 +640,15 @@
|
|||
"lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
|
||||
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
|
||||
"requires": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"lit-html": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.2.tgz",
|
||||
"integrity": "sha512-cJofCRXuizwyaiGt9pJjJOcauezUlSB6t87VBXsPwRhbzF29MgD8GH6fZ0BuZdXAAC02IRONZBd//VPUuU8QbQ==",
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
|
||||
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
|
||||
"requires": {
|
||||
"@types/trusted-types": "^2.0.2"
|
||||
}
|
||||
|
@ -667,18 +676,10 @@
|
|||
"tslib": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"localforage-getitems": {
|
||||
"version": "git+ssh://git@github.com/conversejs/localForage-getItems.git#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||
"integrity": "sha512-J1Q2IqJgwWNJOOX8Q0UULrBEXZExmLwuGWXp/bsjWpuA5LL8RDJcmbUX/3CD9DqjvxvNTnutw2/973CD/EqXMQ==",
|
||||
"from": "localforage-getitems@github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||
"requires": {
|
||||
"localforage": ">=1.4.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=",
|
||||
"integrity": "sha512-uU2ZydBZZkwaP0s1m5NMJXKOQJ0pfqFjDfHdv/SU9E1SWZpr4U3qpyJLZSZJvCvaAeBEL9Bvzd0/CWC9Jtk3wg==",
|
||||
"requires": {
|
||||
"localforage": ">=1.4.0"
|
||||
}
|
||||
|
@ -737,18 +738,18 @@
|
|||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.70.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
|
||||
"integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
|
||||
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"sizzle": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.6.tgz",
|
||||
"integrity": "sha512-abtd95IkbcMAaYk1Lux4k9Xz6wnQqyLy2aco9HGJ8jVaCDEcc+ug0hW8RdV6aIre3ycWXxPdcX0u7QL/1UaSoA=="
|
||||
"version": "2.3.10",
|
||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
|
||||
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
|
@ -756,11 +757,11 @@
|
|||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||
},
|
||||
"strophe.js": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.5.0.tgz",
|
||||
"integrity": "sha512-H5tE/tZxPR5xP3jhXyQwsjnMSwQMf7vrn9r1OkufTApyGHYe8WjzhsfxtL3AFhVu7vFjXPPZBrmUOTm1ccYgOA==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/strophe.js/-/strophe.js-1.6.0.tgz",
|
||||
"integrity": "sha512-LE2B6nEJNUbF2Cl/p1tLIsXVJ9l86B/Z12HYYiO3n92VwYkhJ/5vJ+1ZMdwP9eN9GP8a3nbqfS5zE9umcK0FdA==",
|
||||
"requires": {
|
||||
"@xmldom/xmldom": "0.8.2",
|
||||
"@xmldom/xmldom": "0.8.3",
|
||||
"abab": "^2.0.3",
|
||||
"karma-rollup-preprocessor": "^7.0.8",
|
||||
"ws": "^8.5.0"
|
||||
|
@ -785,9 +786,9 @@
|
|||
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
||||
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
||||
"optional": true,
|
||||
"requires": {}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
{
|
||||
"name": "@converse/headless",
|
||||
"version": "9.1.1",
|
||||
"version": "10.1.6",
|
||||
"description": "Converse.js Headless build",
|
||||
"author": "cmrd Senya <senya@riseup.net>",
|
||||
"author": "JC Brand <jc@opkode.com>",
|
||||
"contributors": [
|
||||
"cmrd Senya <senya@riseup.net>"
|
||||
],
|
||||
"homepage": "https://conversejs.org",
|
||||
"license": "MPL-2.0",
|
||||
"main": "dist/converse-headless.min.js",
|
||||
"module": "headless.js",
|
||||
"main": "dist/converse-headless.js",
|
||||
"module": "index.js",
|
||||
"keywords": [
|
||||
"converse.js",
|
||||
"XMPP",
|
||||
|
@ -27,19 +30,19 @@
|
|||
"bugs": {
|
||||
"url": "https://github.com/conversejs/converse.js/issues"
|
||||
},
|
||||
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
||||
"dependencies": {
|
||||
"@converse/openpromise": "^0.0.1",
|
||||
"@converse/skeletor": "0.0.7",
|
||||
"dayjs": "1.11.1",
|
||||
"@converse/skeletor": "^0.0.8",
|
||||
"dayjs": "^1.11.8",
|
||||
"dompurify": "^2.3.1",
|
||||
"filesize": "^7.0.0",
|
||||
"filesize": "^10.0.7",
|
||||
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pluggable.js": "3.0.1",
|
||||
"sizzle": "^2.3.5",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"strophe.js": "1.5.0",
|
||||
"strophe.js": "^1.6.2",
|
||||
"urijs": "^1.19.10"
|
||||
}
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import { converse } from "../core.js";
|
||||
import log from "@converse/headless/log";
|
||||
import sizzle from 'sizzle';
|
||||
import { getAttributes } from '@converse/headless/shared/parsers';
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
let _converse, api;
|
||||
|
||||
Strophe.addNamespace('ADHOC', 'http://jabber.org/protocol/commands');
|
||||
|
||||
|
||||
function parseForCommands (stanza) {
|
||||
const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
|
||||
return items.map(getAttributes)
|
||||
}
|
||||
|
||||
|
||||
const adhoc_api = {
|
||||
/**
|
||||
* The XEP-0050 Ad-Hoc Commands API
|
||||
*
|
||||
* This API lets you discover ad-hoc commands available for an entity in the XMPP network.
|
||||
*
|
||||
* @namespace api.adhoc
|
||||
* @memberOf api
|
||||
*/
|
||||
adhoc: {
|
||||
/**
|
||||
* @method api.adhoc.getCommands
|
||||
* @param { String } to_jid
|
||||
*/
|
||||
async getCommands (to_jid) {
|
||||
let commands = [];
|
||||
try {
|
||||
commands = parseForCommands(await api.disco.items(to_jid, Strophe.NS.ADHOC));
|
||||
} catch (e) {
|
||||
if (e === null) {
|
||||
log.error(`Error: timeout while fetching ad-hoc commands for ${to_jid}`);
|
||||
} else {
|
||||
log.error(`Error while fetching ad-hoc commands for ${to_jid}`);
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
converse.plugins.add('converse-adhoc', {
|
||||
|
||||
dependencies: ["converse-disco"],
|
||||
|
||||
initialize () {
|
||||
_converse = this._converse;
|
||||
api = _converse.api;
|
||||
Object.assign(api, adhoc_api);
|
||||
}
|
||||
});
|
||||
|
||||
export default adhoc_api;
|
109
src/headless/plugins/adhoc/api.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
import log from '@converse/headless/log';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { getCommandFields, parseForCommands } from './utils.js';
|
||||
|
||||
const { Strophe, $iq, u, stx } = converse.env;
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* The XEP-0050 Ad-Hoc Commands API
|
||||
*
|
||||
* This API lets you discover ad-hoc commands available for an entity in the XMPP network.
|
||||
*
|
||||
* @namespace api.adhoc
|
||||
* @memberOf api
|
||||
*/
|
||||
adhoc: {
|
||||
/**
|
||||
* @method api.adhoc.getCommands
|
||||
* @param { String } to_jid
|
||||
*/
|
||||
async getCommands (to_jid) {
|
||||
try {
|
||||
return parseForCommands(await api.disco.items(to_jid, Strophe.NS.ADHOC));
|
||||
} catch (e) {
|
||||
if (e === null) {
|
||||
log.error(`Error: timeout while fetching ad-hoc commands for ${to_jid}`);
|
||||
} else {
|
||||
log.error(`Error while fetching ad-hoc commands for ${to_jid}`);
|
||||
log.error(e);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @method api.adhoc.fetchCommandForm
|
||||
*/
|
||||
async fetchCommandForm (command) {
|
||||
const node = command.node;
|
||||
const jid = command.jid;
|
||||
const stanza = $iq({
|
||||
'type': 'set',
|
||||
'to': jid
|
||||
}).c('command', {
|
||||
'xmlns': Strophe.NS.ADHOC,
|
||||
'node': node,
|
||||
'action': 'execute'
|
||||
});
|
||||
try {
|
||||
return getCommandFields(await api.sendIQ(stanza), jid);
|
||||
|
||||
} catch (e) {
|
||||
if (e === null) {
|
||||
log.error(`Error: timeout while trying to execute command for ${jid}`);
|
||||
} else {
|
||||
log.error(`Error while trying to execute command for ${jid}`);
|
||||
log.error(e);
|
||||
}
|
||||
const { __ } = _converse;
|
||||
return {
|
||||
instructions: __('An error occurred while trying to fetch the command form'),
|
||||
fields: []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @method api.adhoc.runCommand
|
||||
* @param { String } jid
|
||||
* @param { String } sessionid
|
||||
* @param { 'execute' | 'cancel' | 'prev' | 'next' | 'complete' } action
|
||||
* @param { String } node
|
||||
* @param { Array<{ string: string }> } inputs
|
||||
*/
|
||||
async runCommand (jid, sessionid, node, action, inputs) {
|
||||
const iq =
|
||||
stx`<iq type="set" to="${jid}" xmlns="jabber:client">
|
||||
<command sessionid="${sessionid}" node="${node}" action="${action}" xmlns="${Strophe.NS.ADHOC}">
|
||||
${ !['cancel', 'prev'].includes(action) ? stx`
|
||||
<x xmlns="${Strophe.NS.XFORM}" type="submit">
|
||||
${ inputs.reduce((out, { name, value }) => out + `<field var="${name}"><value>${value}</value></field>`, '') }
|
||||
</x>` : '' }
|
||||
</command>
|
||||
</iq>`;
|
||||
|
||||
const result = await api.sendIQ(iq, null, false);
|
||||
if (result === null) {
|
||||
log.warn(`A timeout occurred while trying to run an ad-hoc command`);
|
||||
const { __ } = _converse;
|
||||
return {
|
||||
status: 'error',
|
||||
note: __('A timeout occurred'),
|
||||
}
|
||||
} else if (u.isErrorStanza(result)) {
|
||||
log.error('Error while trying to execute an ad-hoc command');
|
||||
log.error(result);
|
||||
}
|
||||
|
||||
const command = result.querySelector('command');
|
||||
const status = command?.getAttribute('status');
|
||||
return {
|
||||
status,
|
||||
...(status === 'executing' ? getCommandFields(result) : {}),
|
||||
note: result.querySelector('note')?.textContent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
src/headless/plugins/adhoc/index.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import adhoc_api from './api.js';
|
||||
import { converse } from "@converse/headless/core";
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
|
||||
Strophe.addNamespace('ADHOC', 'http://jabber.org/protocol/commands');
|
||||
|
||||
|
||||
converse.plugins.add('converse-adhoc', {
|
||||
|
||||
dependencies: ["converse-disco"],
|
||||
|
||||
initialize () {
|
||||
Object.assign(this._converse.api, adhoc_api);
|
||||
}
|
||||
});
|
22
src/headless/plugins/adhoc/utils.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import sizzle from 'sizzle';
|
||||
import { converse } from "@converse/headless/core";
|
||||
import { getAttributes } from '@converse/headless/shared/parsers';
|
||||
|
||||
const { Strophe, u } = converse.env;
|
||||
|
||||
export function parseForCommands (stanza) {
|
||||
const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
|
||||
return items.map(getAttributes)
|
||||
}
|
||||
|
||||
export function getCommandFields (iq, jid) {
|
||||
const cmd_el = sizzle(`command[xmlns="${Strophe.NS.ADHOC}"]`, iq).pop();
|
||||
const data = {
|
||||
sessionid: cmd_el.getAttribute('sessionid'),
|
||||
instructions: sizzle('x[type="form"][xmlns="jabber:x:data"] instructions', cmd_el).pop()?.textContent,
|
||||
fields: sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el)
|
||||
.map(f => u.xForm2TemplateResult(f, cmd_el, { domain: jid })),
|
||||
actions: Array.from(cmd_el.querySelector('actions')?.children).map((a) => a.nodeName.toLowerCase()) ?? []
|
||||
}
|
||||
return data;
|
||||
}
|
|
@ -8,7 +8,6 @@ import "@converse/headless/plugins/muc/index.js";
|
|||
import Bookmark from './model.js';
|
||||
import Bookmarks from './collection.js';
|
||||
import { Collection } from "@converse/skeletor/src/collection.js";
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { _converse, api, converse } from "@converse/headless/core.js";
|
||||
import { initBookmarks, getNicknameFromBookmark, handleBookmarksPush } from './utils.js';
|
||||
|
||||
|
@ -57,12 +56,6 @@ converse.plugins.add('converse-bookmarks', {
|
|||
_converse.Bookmark = Bookmark;
|
||||
_converse.Bookmarks = Collection.extend(Bookmarks);
|
||||
|
||||
_converse.BookmarksList = Model.extend({
|
||||
defaults: {
|
||||
"toggle-state": _converse.OPENED
|
||||
}
|
||||
});
|
||||
|
||||
api.listen.on('addClientFeatures', () => {
|
||||
if (api.settings.get('allow_bookmarks')) {
|
||||
api.disco.own.features.add(Strophe.NS.BOOKMARKS + '+notify')
|
||||
|
|
|
@ -136,7 +136,7 @@ converse.plugins.add('converse-bosh', {
|
|||
tokens: {
|
||||
/**
|
||||
* @method api.tokens.get
|
||||
* @param {string} [id] The type of token to return ('rid' or 'sid').
|
||||
* @param { string } [id] The type of token to return ('rid' or 'sid').
|
||||
* @returns 'string' A token, either the RID or SID token depending on what's asked for.
|
||||
* @example _converse.api.tokens.get('rid');
|
||||
*/
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import { api, converse } from '@converse/headless/core';
|
||||
import { createCapsNode } from './utils.js';
|
||||
import { addCapsNode } from './utils.js';
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
|
||||
|
@ -15,7 +15,7 @@ converse.plugins.add('converse-caps', {
|
|||
dependencies: ['converse-status'],
|
||||
|
||||
initialize () {
|
||||
api.listen.on('constructedPresence', (_, p) => (p.root().cnode(createCapsNode()).up() && p));
|
||||
api.listen.on('constructedMUCPresence', (_, p) => (p.root().cnode(createCapsNode()).up() && p));
|
||||
api.listen.on('constructedPresence', (_, p) => addCapsNode(p));
|
||||
api.listen.on('constructedMUCPresence', (_, p) => addCapsNode(p));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import SHA1 from 'strophe.js/src/sha1';
|
||||
import { _converse, converse } from '@converse/headless/core';
|
||||
import { _converse, converse } from '@converse/headless/core.js';
|
||||
import { arrayBufferToBase64, stringToArrayBuffer } from '@converse/headless/utils/arraybuffer.js';
|
||||
|
||||
const { Strophe, $build } = converse.env;
|
||||
|
||||
|
@ -7,7 +7,7 @@ function propertySort (array, property) {
|
|||
return array.sort((a, b) => { return a[property] > b[property] ? -1 : 1 });
|
||||
}
|
||||
|
||||
function generateVerificationString () {
|
||||
async function generateVerificationString () {
|
||||
const identities = _converse.api.disco.own.identities.get();
|
||||
const features = _converse.api.disco.own.features.get();
|
||||
|
||||
|
@ -20,14 +20,27 @@ function generateVerificationString () {
|
|||
let S = identities.reduce((result, id) => `${result}${id.category}/${id.type}/${id?.lang ?? ''}/${id.name}<`, "");
|
||||
features.sort();
|
||||
S = features.reduce((result, feature) => `${result}${feature}<`, S);
|
||||
return SHA1.b64_sha1(S);
|
||||
|
||||
const ab = await crypto.subtle.digest('SHA-1', stringToArrayBuffer(S));
|
||||
return arrayBufferToBase64(ab);
|
||||
}
|
||||
|
||||
export function createCapsNode () {
|
||||
async function createCapsNode () {
|
||||
return $build("c", {
|
||||
'xmlns': Strophe.NS.CAPS,
|
||||
'hash': "sha-1",
|
||||
'node': "https://conversejs.org",
|
||||
'ver': generateVerificationString()
|
||||
}).nodeTree;
|
||||
'ver': await generateVerificationString()
|
||||
}).tree();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a stanza, adds a XEP-0115 CAPS element
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
export async function addCapsNode (stanza) {
|
||||
const caps_el = await createCapsNode();
|
||||
stanza.root().cnode(caps_el).up();
|
||||
return stanza;
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* @module converse-carbons
|
||||
* @copyright The Converse.js contributors
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
* @description Implements support for XEP-0280 Message Carbons
|
||||
*/
|
||||
|
||||
import log from '@converse/headless/log.js';
|
||||
import { Strophe } from 'strophe.js/src/strophe';
|
||||
import { _converse, api, converse } from "../core.js";
|
||||
|
||||
const { u } = converse.env;
|
||||
|
||||
|
||||
/**
|
||||
* Ask the XMPP server to enable Message Carbons
|
||||
* See [XEP-0280](https://xmpp.org/extensions/xep-0280.html#enabling)
|
||||
*/
|
||||
async function enableCarbons (reconnecting) {
|
||||
if (reconnecting && _converse.session.get('carbons_enabled')) {
|
||||
if (_converse.session.get('smacks_enabled')) {
|
||||
// No need to re-enable carbons when resuming a XEP-0198 stream
|
||||
return;
|
||||
}
|
||||
_converse.session.set({'carbons_enabled': false})
|
||||
}
|
||||
|
||||
if (!api.settings.get("message_carbons") || _converse.session?.get('carbons_enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const iq = new Strophe.Builder('iq', {
|
||||
'from': _converse.connection.jid,
|
||||
'type': 'set'
|
||||
}).c('enable', {xmlns: Strophe.NS.CARBONS});
|
||||
|
||||
const result = await api.sendIQ(iq, null, false);
|
||||
if (result === null) {
|
||||
log.warn(`A timeout occurred while trying to enable carbons`);
|
||||
} else if (u.isErrorStanza(result)) {
|
||||
log.warn('An error occurred while trying to enable message carbons.');
|
||||
log.error(result);
|
||||
} else {
|
||||
_converse.session.set({'carbons_enabled': true});
|
||||
log.debug('Message carbons have been enabled.');
|
||||
}
|
||||
_converse.session.save(); // Gather multiple sets into one save
|
||||
}
|
||||
|
||||
|
||||
converse.plugins.add('converse-carbons', {
|
||||
|
||||
initialize () {
|
||||
api.settings.extend({
|
||||
message_carbons: true
|
||||
});
|
||||
|
||||
api.listen.on('connected', () => enableCarbons());
|
||||
api.listen.on('reconnected', () => enableCarbons(true));
|
||||
}
|
||||
});
|
|
@ -13,7 +13,7 @@ export default {
|
|||
/**
|
||||
* @method api.chats.create
|
||||
* @param {string|string[]} jid|jids An jid or array of jids
|
||||
* @param {object} [attrs] An object containing configuration attributes.
|
||||
* @param { object } [attrs] An object containing configuration attributes.
|
||||
*/
|
||||
async create (jids, attrs) {
|
||||
if (typeof jids === 'string') {
|
||||
|
@ -44,9 +44,9 @@ export default {
|
|||
*
|
||||
* @method api.chats.open
|
||||
* @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||
* @param {Boolean} [attrs.minimized] - Should the chat be created in minimized state.
|
||||
* @param {Boolean} [force=false] - By default, a minimized
|
||||
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||
* @param { Boolean } [attrs.minimized] - Should the chat be created in minimized state.
|
||||
* @param { Boolean } [force=false] - By default, a minimized
|
||||
* chat won't be maximized (in `overlayed` view mode) and in
|
||||
* `fullscreen` view mode a newly opened chat won't replace
|
||||
* another chat already in the foreground.
|
||||
|
@ -102,8 +102,8 @@ export default {
|
|||
*
|
||||
* @method api.chats.get
|
||||
* @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
|
||||
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||
* @param { Boolean } [create=false] - Whether the chat should be created if it's not found.
|
||||
* @returns { Promise<_converse.ChatBox> }
|
||||
*
|
||||
* @example
|
||||
|
|
|
@ -6,24 +6,18 @@ import ChatBox from './model.js';
|
|||
import MessageMixin from './message.js';
|
||||
import ModelWithContact from './model-with-contact.js';
|
||||
import chat_api from './api.js';
|
||||
import { Collection } from "@converse/skeletor/src/collection";
|
||||
import { Collection } from '@converse/skeletor/src/collection';
|
||||
import { _converse, api, converse } from '../../core.js';
|
||||
import { autoJoinChats, handleMessageStanza, onClearSession, openChat, registerMessageHandlers } from './utils.js';
|
||||
|
||||
import {
|
||||
autoJoinChats,
|
||||
enableCarbons,
|
||||
handleMessageStanza,
|
||||
onClearSession,
|
||||
openChat,
|
||||
registerMessageHandlers,
|
||||
} from './utils.js';
|
||||
|
||||
converse.plugins.add('converse-chat', {
|
||||
/* Optional dependencies are other plugins which might be
|
||||
* overridden or relied upon, and therefore need to be loaded before
|
||||
* this plugin. They are called "optional" because they might not be
|
||||
* available, in which case any overrides applicable to them will be
|
||||
* ignored.
|
||||
*
|
||||
* It's possible however to make optional dependencies non-optional.
|
||||
* If the setting "strict_plugin_dependencies" is set to true,
|
||||
* an error will be raised if the plugin is not found.
|
||||
*
|
||||
* NB: These plugins need to have already been loaded via require.js.
|
||||
*/
|
||||
dependencies: ['converse-chatboxes', 'converse-disco'],
|
||||
|
||||
initialize () {
|
||||
|
@ -40,14 +34,14 @@ converse.plugins.add('converse-chat', {
|
|||
'filter_by_resource': false,
|
||||
'prune_messages_above': undefined,
|
||||
'pruning_behavior': 'unscrolled',
|
||||
'send_chat_markers': ["received", "displayed", "acknowledged"],
|
||||
'send_chat_markers': ['received', 'displayed', 'acknowledged'],
|
||||
'send_chat_state_notifications': true,
|
||||
});
|
||||
|
||||
_converse.Message = ModelWithContact.extend(MessageMixin);
|
||||
_converse.Messages = Collection.extend({
|
||||
model: _converse.Message,
|
||||
comparator: 'time'
|
||||
comparator: 'time',
|
||||
});
|
||||
|
||||
Object.assign(_converse, { ChatBox, handleMessageStanza });
|
||||
|
@ -58,5 +52,8 @@ converse.plugins.add('converse-chat', {
|
|||
api.listen.on('chatBoxesFetched', autoJoinChats);
|
||||
api.listen.on('presencesInitialized', registerMessageHandlers);
|
||||
api.listen.on('clearSession', onClearSession);
|
||||
}
|
||||
|
||||
api.listen.on('connected', () => enableCarbons());
|
||||
api.listen.on('reconnected', () => enableCarbons());
|
||||
},
|
||||
});
|
||||
|
|
|
@ -7,7 +7,8 @@ import { getOpenPromise } from '@converse/openpromise';
|
|||
const { Strophe, sizzle, u } = converse.env;
|
||||
|
||||
/**
|
||||
* Mixin which turns a `ModelWithContact` model into a non-MUC message. These can be either `chat` messages or `headline` messages.
|
||||
* Mixin which turns a `ModelWithContact` model into a non-MUC message.
|
||||
* These can be either `chat`, `normal` or `headline` messages.
|
||||
* @mixin
|
||||
* @namespace _converse.Message
|
||||
* @memberOf _converse
|
||||
|
@ -47,9 +48,8 @@ const MessageMixin = {
|
|||
this.initialized.resolve();
|
||||
},
|
||||
|
||||
|
||||
setContact () {
|
||||
if (this.get('type') === 'chat') {
|
||||
if (['chat', 'normal'].includes(this.get('type'))) {
|
||||
ModelWithContact.prototype.initialize.apply(this, arguments);
|
||||
this.setRosterContact(Strophe.getBareJidFromJid(this.get('from')));
|
||||
}
|
||||
|
@ -143,12 +143,11 @@ const MessageMixin = {
|
|||
}
|
||||
const date = dayjs(this.get('time'));
|
||||
return this.get('from') === prev_model.get('from') &&
|
||||
!this.isMeCommand() &&
|
||||
!prev_model.isMeCommand() &&
|
||||
this.get('type') !== 'info' &&
|
||||
prev_model.get('type') !== 'info' &&
|
||||
!this.isMeCommand() && !prev_model.isMeCommand() &&
|
||||
!!this.get('is_encrypted') === !!prev_model.get('is_encrypted') &&
|
||||
this.get('type') === prev_model.get('type') && this.get('type') !== 'info' &&
|
||||
date.isBefore(dayjs(prev_model.get('time')).add(10, 'minutes')) &&
|
||||
!!this.get('is_encrypted') === !!prev_model.get('is_encrypted');
|
||||
(this.get('type') === 'groupchat' ? this.get('occupant_id') === prev_model.get('occupant_id') : true);
|
||||
},
|
||||
|
||||
getDisplayName () {
|
||||
|
@ -165,7 +164,7 @@ const MessageMixin = {
|
|||
if (this.get('is_encrypted')) {
|
||||
const { __ } = _converse;
|
||||
return this.get('plaintext') || this.get('body') || __('Undecryptable OMEMO message');
|
||||
} else if (['groupchat', 'chat'].includes(this.get('type'))) {
|
||||
} else if (['groupchat', 'chat', 'normal'].includes(this.get('type'))) {
|
||||
return this.get('body');
|
||||
} else {
|
||||
return this.get('message');
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import ModelWithContact from './model-with-contact.js';
|
||||
import filesize from "filesize";
|
||||
import isMatch from "lodash-es/isMatch";
|
||||
import isObject from "lodash-es/isObject";
|
||||
import log from '@converse/headless/log';
|
||||
import pick from "lodash-es/pick";
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { TimeoutError } from '../../shared/errors.js';
|
||||
import { _converse, api, converse } from "../../core.js";
|
||||
import { debouncedPruneHistory } from '@converse/headless/shared/chat/utils.js';
|
||||
import { debouncedPruneHistory, handleCorrection } from '@converse/headless/shared/chat/utils.js';
|
||||
import { filesize } from "filesize";
|
||||
import { getMediaURLsMetadata } from '@converse/headless/shared/parsers.js';
|
||||
import { getOpenPromise } from '@converse/openpromise';
|
||||
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||
import { isUniView } from '@converse/headless/utils/core.js';
|
||||
import { isUniView, isEmptyMessage } from '../../utils/core.js';
|
||||
import { parseMessage } from './parsers.js';
|
||||
import { sendMarker } from '@converse/headless/shared/actions.js';
|
||||
|
||||
|
@ -91,17 +92,6 @@ const ChatBox = ModelWithContact.extend({
|
|||
initMessages () {
|
||||
this.messages = this.getMessagesCollection();
|
||||
this.messages.fetched = getOpenPromise();
|
||||
this.messages.fetched.then(() => {
|
||||
this.pruneHistoryWhenScrolledDown();
|
||||
/**
|
||||
* Triggered whenever a { @link _converse.ChatBox } or ${ @link _converse.ChatRoom }
|
||||
* has fetched its messages from the local cache.
|
||||
* @event _converse#afterMessagesFetched
|
||||
* @type { _converse.ChatBox| _converse.ChatRoom }
|
||||
* @example _converse.api.listen.on('afterMessagesFetched', (chat) => { ... });
|
||||
*/
|
||||
api.trigger('afterMessagesFetched', this);
|
||||
});
|
||||
this.messages.chatbox = this;
|
||||
initStorage(this.messages, this.getMessagesCacheKey());
|
||||
|
||||
|
@ -131,12 +121,13 @@ const ChatBox = ModelWithContact.extend({
|
|||
},
|
||||
|
||||
afterMessagesFetched () {
|
||||
this.pruneHistoryWhenScrolledDown();
|
||||
/**
|
||||
* Triggered whenever a `_converse.ChatBox` instance has fetched its messages from
|
||||
* `sessionStorage` but **NOT** from the server.
|
||||
* Triggered whenever a { @link _converse.ChatBox } or ${ @link _converse.ChatRoom }
|
||||
* has fetched its messages from the local cache.
|
||||
* @event _converse#afterMessagesFetched
|
||||
* @type {_converse.ChatBox | _converse.ChatRoom}
|
||||
* @example _converse.api.listen.on('afterMessagesFetched', view => { ... });
|
||||
* @type { _converse.ChatBox| _converse.ChatRoom }
|
||||
* @example _converse.api.listen.on('afterMessagesFetched', (chat) => { ... });
|
||||
*/
|
||||
api.trigger('afterMessagesFetched', this);
|
||||
},
|
||||
|
@ -234,7 +225,7 @@ const ChatBox = ModelWithContact.extend({
|
|||
this.notifications.set('chat_state', attrs.chat_state);
|
||||
}
|
||||
if (u.shouldCreateMessage(attrs)) {
|
||||
const msg = this.handleCorrection(attrs) || await this.createMessage(attrs);
|
||||
const msg = await handleCorrection(this, attrs) || await this.createMessage(attrs);
|
||||
this.notifications.set({'chat_state': null});
|
||||
this.handleUnreadMessage(msg);
|
||||
}
|
||||
|
@ -257,7 +248,7 @@ const ChatBox = ModelWithContact.extend({
|
|||
onMessageAdded (message) {
|
||||
if (api.settings.get('prune_messages_above') &&
|
||||
(api.settings.get('pruning_behavior') === 'scrolled' || !this.ui.get('scrolled')) &&
|
||||
!u.isEmptyMessage(message)
|
||||
!isEmptyMessage(message)
|
||||
) {
|
||||
debouncedPruneHistory(this);
|
||||
}
|
||||
|
@ -378,7 +369,7 @@ const ChatBox = ModelWithContact.extend({
|
|||
},
|
||||
|
||||
async createMessageFromError (error) {
|
||||
if (error instanceof _converse.TimeoutError) {
|
||||
if (error instanceof TimeoutError) {
|
||||
const msg = await this.createMessage({
|
||||
'type': 'error',
|
||||
'message': error.message,
|
||||
|
@ -605,47 +596,6 @@ const ChatBox = ModelWithContact.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether the passed in message attributes represent a
|
||||
* message which corrects a previously received message, or an
|
||||
* older message which has already been corrected.
|
||||
* In both cases, update the corrected message accordingly.
|
||||
* @private
|
||||
* @method _converse.ChatBox#handleCorrection
|
||||
* @param { object } attrs - Attributes representing a received
|
||||
* message, as returned by {@link parseMessage}
|
||||
* @returns { _converse.Message|undefined } Returns the corrected
|
||||
* message or `undefined` if not applicable.
|
||||
*/
|
||||
handleCorrection (attrs) {
|
||||
if (!attrs.replace_id || !attrs.from) {
|
||||
return;
|
||||
}
|
||||
const message = this.messages.findWhere({'msgid': attrs.replace_id, 'from': attrs.from});
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
const older_versions = message.get('older_versions') || {};
|
||||
if ((attrs.time < message.get('time')) && message.get('edited')) {
|
||||
// This is an older message which has been corrected afterwards
|
||||
older_versions[attrs.time] = attrs['message'];
|
||||
message.save({'older_versions': older_versions});
|
||||
} else {
|
||||
// This is a correction of an earlier message we already received
|
||||
if (Object.keys(older_versions).length) {
|
||||
older_versions[message.get('edited')] = message.getMessageText();
|
||||
} else {
|
||||
older_versions[message.get('time')] = message.getMessageText();
|
||||
}
|
||||
attrs = Object.assign(attrs, { older_versions });
|
||||
delete attrs['msgid']; // We want to keep the msgid of the original message
|
||||
delete attrs['id']; // Delete id, otherwise a new cache entry gets created
|
||||
attrs['time'] = message.get('time');
|
||||
message.save(attrs);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an already cached message (if it exists) based on the
|
||||
* passed in attributes map.
|
||||
|
@ -886,10 +836,11 @@ const ChatBox = ModelWithContact.extend({
|
|||
},
|
||||
|
||||
async getOutgoingMessageAttributes (attrs) {
|
||||
await api.emojis.initialize();
|
||||
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;
|
||||
const body = text ? u.shortnamesToUnicode(text) : undefined;
|
||||
attrs = Object.assign({}, attrs, {
|
||||
'from': _converse.bare_jid,
|
||||
'fullname': _converse.xmppstatus.get('fullname'),
|
||||
|
@ -932,17 +883,14 @@ const ChatBox = ModelWithContact.extend({
|
|||
* @param { String } send_time - time when the message was sent
|
||||
*/
|
||||
setEditable (attrs, send_time) {
|
||||
if (attrs.is_headline || u.isEmptyMessage(attrs) || attrs.sender !== 'me') {
|
||||
if (attrs.is_headline || isEmptyMessage(attrs) || attrs.sender !== 'me') {
|
||||
return;
|
||||
}
|
||||
if (api.settings.get('allow_message_corrections') === 'all') {
|
||||
attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
||||
} else if ((api.settings.get('allow_message_corrections') === 'last') && (send_time > this.get('time_sent'))) {
|
||||
this.set({'time_sent': send_time});
|
||||
const msg = this.messages.findWhere({'editable': true});
|
||||
if (msg) {
|
||||
msg.save({'editable': false});
|
||||
}
|
||||
this.messages.findWhere({'editable': true})?.save({'editable': false});
|
||||
attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
||||
}
|
||||
},
|
||||
|
@ -980,19 +928,19 @@ const ChatBox = ModelWithContact.extend({
|
|||
const older_versions = message.get('older_versions') || {};
|
||||
const edited_time = message.get('edited') || message.get('time');
|
||||
older_versions[edited_time] = message.getMessageText();
|
||||
const plaintext = attrs.is_encrypted ? attrs.message : undefined;
|
||||
|
||||
message.save({
|
||||
'body': attrs.body,
|
||||
'message': attrs.body,
|
||||
'correcting': false,
|
||||
'edited': (new Date()).toISOString(),
|
||||
'is_only_emojis': attrs.is_only_emojis,
|
||||
'origin_id': u.getUniqueId(),
|
||||
'received': undefined,
|
||||
'references': attrs.references,
|
||||
older_versions,
|
||||
plaintext,
|
||||
...pick(attrs, ['body', 'is_only_emojis', 'media_urls', 'references', 'is_encrypted']),
|
||||
...{
|
||||
'correcting': false,
|
||||
'edited': (new Date()).toISOString(),
|
||||
'message': attrs.body,
|
||||
'ogp_metadata': [],
|
||||
'origin_id': u.getUniqueId(),
|
||||
'received': undefined,
|
||||
older_versions,
|
||||
plaintext: attrs.is_encrypted ? attrs.message : undefined,
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setEditable(attrs, (new Date()).toISOString());
|
||||
|
|
|
@ -32,7 +32,7 @@ const { Strophe, sizzle } = converse.env;
|
|||
/**
|
||||
* Parses a passed in message stanza and returns an object of attributes.
|
||||
* @method st#parseMessage
|
||||
* @param { XMLElement } stanza - The message stanza
|
||||
* @param { Element } stanza - The message stanza
|
||||
* @param { _converse } _converse
|
||||
* @returns { (MessageAttributes|Error) }
|
||||
*/
|
||||
|
@ -178,7 +178,7 @@ export async function parseMessage (stanza) {
|
|||
'thread': stanza.querySelector('thread')?.textContent,
|
||||
'time': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : now,
|
||||
'to': stanza.getAttribute('to'),
|
||||
'type': stanza.getAttribute('type')
|
||||
'type': stanza.getAttribute('type') || 'normal'
|
||||
},
|
||||
getErrorAttributes(stanza),
|
||||
getOutOfBandAttributes(stanza),
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||
import { isServerMessage, } from '@converse/headless/shared/parsers';
|
||||
import { parseMessage } from './parsers.js';
|
||||
import log from '@converse/headless/log.js';
|
||||
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||
import { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers';
|
||||
import { parseMessage } from './parsers.js';
|
||||
import { shouldClearCache } from '@converse/headless/utils/core.js';
|
||||
|
||||
const { Strophe, sizzle, u } = converse.env;
|
||||
const { Strophe, u } = converse.env;
|
||||
|
||||
export function openChat (jid) {
|
||||
if (!u.isValidJID(jid)) {
|
||||
|
@ -13,7 +14,7 @@ export function openChat (jid) {
|
|||
}
|
||||
|
||||
export async function onClearSession () {
|
||||
if (_converse.shouldClearCache()) {
|
||||
if (shouldClearCache()) {
|
||||
await Promise.all(
|
||||
_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
|
||||
);
|
||||
|
@ -28,7 +29,7 @@ async function handleErrorMessage (stanza) {
|
|||
return;
|
||||
}
|
||||
const chatbox = await api.chatboxes.get(from_jid);
|
||||
if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
if (chatbox?.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
chatbox?.handleErrorMessageStanza(stanza);
|
||||
}
|
||||
}
|
||||
|
@ -47,56 +48,35 @@ export function autoJoinChats () {
|
|||
}
|
||||
});
|
||||
/**
|
||||
* Triggered once any private chats have been automatically joined as
|
||||
* specified by the `auto_join_private_chats` setting.
|
||||
* See: https://conversejs.org/docs/html/configuration.html#auto-join-private-chats
|
||||
* @event _converse#privateChatsAutoJoined
|
||||
* @example _converse.api.listen.on('privateChatsAutoJoined', () => { ... });
|
||||
* @example _converse.api.waitUntil('privateChatsAutoJoined').then(() => { ... });
|
||||
*/
|
||||
* Triggered once any private chats have been automatically joined as
|
||||
* specified by the `auto_join_private_chats` setting.
|
||||
* See: https://conversejs.org/docs/html/configuration.html#auto-join-private-chats
|
||||
* @event _converse#privateChatsAutoJoined
|
||||
* @example _converse.api.listen.on('privateChatsAutoJoined', () => { ... });
|
||||
* @example _converse.api.waitUntil('privateChatsAutoJoined').then(() => { ... });
|
||||
*/
|
||||
api.trigger('privateChatsAutoJoined');
|
||||
}
|
||||
|
||||
export function registerMessageHandlers () {
|
||||
_converse.connection.addHandler(
|
||||
stanza => {
|
||||
if (sizzle(`message > result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop()) {
|
||||
// MAM messages are handled in converse-mam.
|
||||
// We shouldn't get MAM messages here because
|
||||
// they shouldn't have a `type` attribute.
|
||||
log.warn(`Received a MAM message with type "chat".`);
|
||||
if (
|
||||
['groupchat', 'error'].includes(stanza.getAttribute('type')) ||
|
||||
isHeadline(stanza) ||
|
||||
isServerMessage(stanza) ||
|
||||
isArchived(stanza)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
_converse.handleMessageStanza(stanza);
|
||||
return true;
|
||||
return _converse.handleMessageStanza(stanza) || true;
|
||||
},
|
||||
null,
|
||||
'message',
|
||||
'chat'
|
||||
);
|
||||
|
||||
_converse.connection.addHandler(
|
||||
stanza => {
|
||||
// Message receipts are usually without the `type` attribute. See #1353
|
||||
if (stanza.getAttribute('type') !== null) {
|
||||
// TODO: currently Strophe has no way to register a handler
|
||||
// for stanzas without a `type` attribute.
|
||||
// We could update it to accept null to mean no attribute,
|
||||
// but that would be a backward-incompatible change
|
||||
return true; // Gets handled above.
|
||||
}
|
||||
_converse.handleMessageStanza(stanza);
|
||||
return true;
|
||||
},
|
||||
Strophe.NS.RECEIPTS,
|
||||
'message'
|
||||
);
|
||||
|
||||
_converse.connection.addHandler(
|
||||
stanza => {
|
||||
handleErrorMessage(stanza);
|
||||
return true;
|
||||
},
|
||||
stanza => handleErrorMessage(stanza) || true,
|
||||
null,
|
||||
'message',
|
||||
'error'
|
||||
|
@ -106,10 +86,11 @@ export function registerMessageHandlers () {
|
|||
|
||||
/**
|
||||
* Handler method for all incoming single-user chat "message" stanzas.
|
||||
* @private
|
||||
* @param { MessageAttributes } attrs - The message attributes
|
||||
*/
|
||||
export async function handleMessageStanza (stanza) {
|
||||
stanza = stanza.tree?.() ?? stanza;
|
||||
|
||||
if (isServerMessage(stanza)) {
|
||||
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
|
||||
const from = stanza.getAttribute('from');
|
||||
|
@ -117,7 +98,7 @@ export async function handleMessageStanza (stanza) {
|
|||
}
|
||||
let attrs;
|
||||
try {
|
||||
attrs = await parseMessage(stanza, _converse);
|
||||
attrs = await parseMessage(stanza);
|
||||
} catch (e) {
|
||||
return log.error(e);
|
||||
}
|
||||
|
@ -133,7 +114,7 @@ export async function handleMessageStanza (stanza) {
|
|||
* @typedef { Object } MessageData
|
||||
* An object containing the original message stanza, as well as the
|
||||
* parsed attributes.
|
||||
* @property { XMLElement } stanza
|
||||
* @property { Element } stanza
|
||||
* @property { MessageAttributes } stanza
|
||||
* @property { ChatBox } chatbox
|
||||
*/
|
||||
|
@ -146,3 +127,33 @@ export async function handleMessageStanza (stanza) {
|
|||
*/
|
||||
api.trigger('message', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the XMPP server to enable Message Carbons
|
||||
* See [XEP-0280](https://xmpp.org/extensions/xep-0280.html#enabling)
|
||||
* @param { Boolean } reconnecting
|
||||
*/
|
||||
export async function enableCarbons () {
|
||||
const domain = Strophe.getDomainFromJid(_converse.bare_jid);
|
||||
const supported = await api.disco.supports(Strophe.NS.CARBONS, domain);
|
||||
|
||||
if (!supported) {
|
||||
log.warn("Not enabling carbons because it's not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
const iq = new Strophe.Builder('iq', {
|
||||
'from': _converse.connection.jid,
|
||||
'type': 'set'
|
||||
}).c('enable', {xmlns: Strophe.NS.CARBONS});
|
||||
|
||||
const result = await api.sendIQ(iq, null, false);
|
||||
if (result === null) {
|
||||
log.warn(`A timeout occurred while trying to enable carbons`);
|
||||
} else if (u.isErrorStanza(result)) {
|
||||
log.warn('An error occurred while trying to enable message carbons.');
|
||||
log.error(result);
|
||||
} else {
|
||||
log.debug('Message carbons have been enabled.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ const ChatBoxes = Collection.extend({
|
|||
* @event _converse#chatBoxesFetched
|
||||
* @type { object }
|
||||
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox
|
||||
* @property { XMLElement } stanza
|
||||
* @property { Element } stanza
|
||||
* @example _converse.api.listen.on('chatBoxesFetched', obj => { ... });
|
||||
* @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
|
||||
*/
|
||||
|
|
|
@ -24,8 +24,8 @@ export default {
|
|||
stream: {
|
||||
/**
|
||||
* @method api.disco.stream.getFeature
|
||||
* @param {String} name The feature name
|
||||
* @param {String} xmlns The XML namespace
|
||||
* @param { String } name The feature name
|
||||
* @param { String } xmlns The XML namespace
|
||||
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
|
||||
*/
|
||||
async getFeature (name, xmlns) {
|
||||
|
@ -57,10 +57,10 @@ export default {
|
|||
* Lets you add new identities for this client (i.e. instance of Converse)
|
||||
* @method api.disco.own.identities.add
|
||||
*
|
||||
* @param {String} category - server, client, gateway, directory, etc.
|
||||
* @param {String} type - phone, pc, web, etc.
|
||||
* @param {String} name - "Converse"
|
||||
* @param {String} lang - en, el, de, etc.
|
||||
* @param { String } category - server, client, gateway, directory, etc.
|
||||
* @param { String } type - phone, pc, web, etc.
|
||||
* @param { String } name - "Converse"
|
||||
* @param { String } lang - en, el, de, etc.
|
||||
*
|
||||
* @example _converse.api.disco.own.identities.clear();
|
||||
*/
|
||||
|
@ -102,7 +102,7 @@ export default {
|
|||
/**
|
||||
* Lets you register new disco features for this client (i.e. instance of Converse)
|
||||
* @method api.disco.own.features.add
|
||||
* @param {String} name - e.g. http://jabber.org/protocol/caps
|
||||
* @param { String } name - e.g. http://jabber.org/protocol/caps
|
||||
* @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
|
||||
*/
|
||||
add (name) {
|
||||
|
@ -134,8 +134,8 @@ export default {
|
|||
* Query for information about an XMPP entity
|
||||
*
|
||||
* @method api.disco.info
|
||||
* @param {string} jid The Jabber ID of the entity to query
|
||||
* @param {string} [node] A specific node identifier associated with the JID
|
||||
* @param { string } jid The Jabber ID of the entity to query
|
||||
* @param { string } [node] A specific node identifier associated with the JID
|
||||
* @returns {promise} Promise which resolves once we have a result from the server.
|
||||
*/
|
||||
info (jid, node) {
|
||||
|
@ -155,8 +155,8 @@ export default {
|
|||
* Query for items associated with an XMPP entity
|
||||
*
|
||||
* @method api.disco.items
|
||||
* @param {string} jid The Jabber ID of the entity to query for items
|
||||
* @param {string} [node] A specific node identifier associated with the JID
|
||||
* @param { string } jid The Jabber ID of the entity to query for items
|
||||
* @param { string } [node] A specific node identifier associated with the JID
|
||||
* @returns {promise} Promise which resolves once we have a result from the server.
|
||||
*/
|
||||
items (jid, node) {
|
||||
|
@ -184,8 +184,8 @@ export default {
|
|||
* Get the corresponding `DiscoEntity` instance.
|
||||
*
|
||||
* @method api.disco.entities.get
|
||||
* @param {string} jid The Jabber ID of the entity
|
||||
* @param {boolean} [create] Whether the entity should be created if it doesn't exist.
|
||||
* @param { string } jid The Jabber ID of the entity
|
||||
* @param { boolean } [create] Whether the entity should be created if it doesn't exist.
|
||||
* @example _converse.api.disco.entities.get(jid);
|
||||
*/
|
||||
async get (jid, create=false) {
|
||||
|
@ -195,19 +195,29 @@ export default {
|
|||
}
|
||||
if (_converse.disco_entities === undefined) {
|
||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||
const msg = `Tried to look up entity ${jid} but _converse.disco_entities has been torn down`;
|
||||
log.warn(msg);
|
||||
log.warn(`Tried to look up entity ${jid} but _converse.disco_entities has been torn down`);
|
||||
return;
|
||||
}
|
||||
const entity = _converse.disco_entities.get(jid);
|
||||
if (entity || !create) {
|
||||
return entity;
|
||||
}
|
||||
return api.disco.entities.create(jid);
|
||||
return api.disco.entities.create({ jid });
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new disco entity. It's identity and features
|
||||
* Return any disco items advertised on this entity
|
||||
*
|
||||
* @method api.disco.entities.items
|
||||
* @param { string } jid The Jabber ID of the entity for which we want to fetch items
|
||||
* @example api.disco.entities.items(jid);
|
||||
*/
|
||||
items (jid) {
|
||||
return _converse.disco_entities.filter(e => e.get('parent_jids')?.includes(jid));
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new disco entity. It's identity and features
|
||||
* will automatically be fetched from cache or from the
|
||||
* XMPP server.
|
||||
*
|
||||
|
@ -215,14 +225,17 @@ export default {
|
|||
* `ignore_cache: true` in the options parameter.
|
||||
*
|
||||
* @method api.disco.entities.create
|
||||
* @param {string} jid The Jabber ID of the entity
|
||||
* @param {object} [options] Additional options
|
||||
* @param {boolean} [options.ignore_cache]
|
||||
* @param { object } data
|
||||
* @param { string } data.jid - The Jabber ID of the entity
|
||||
* @param { string } data.parent_jid - The Jabber ID of the parent entity
|
||||
* @param { string } data.name
|
||||
* @param { object } [options] - Additional options
|
||||
* @param { boolean } [options.ignore_cache]
|
||||
* If true, fetch all features from the XMPP server instead of restoring them from cache
|
||||
* @example _converse.api.disco.entities.create(jid, {'ignore_cache': true});
|
||||
* @example _converse.api.disco.entities.create({ jid }, {'ignore_cache': true});
|
||||
*/
|
||||
create (jid, options) {
|
||||
return _converse.disco_entities.create({'jid': jid}, options);
|
||||
create (data, options) {
|
||||
return _converse.disco_entities.create(data, options);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -235,11 +248,11 @@ export default {
|
|||
* Return a given feature of a disco entity
|
||||
*
|
||||
* @method api.disco.features.get
|
||||
* @param {string} feature The feature that might be
|
||||
* @param { string } feature The feature that might be
|
||||
* supported. In the XML stanza, this is the `var`
|
||||
* attribute of the `<feature>` element. For
|
||||
* example: `http://jabber.org/protocol/muc`
|
||||
* @param {string} jid The JID of the entity
|
||||
* @param { string } jid The JID of the entity
|
||||
* (and its associated items) which should be queried
|
||||
* @returns {promise} A promise which resolves with a list containing
|
||||
* _converse.Entity instances representing the entity
|
||||
|
@ -249,22 +262,56 @@ export default {
|
|||
* api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
|
||||
*/
|
||||
async get (feature, jid) {
|
||||
if (!jid) {
|
||||
throw new TypeError('You need to provide an entity JID');
|
||||
}
|
||||
await api.waitUntil('discoInitialized');
|
||||
let entity = await api.disco.entities.get(jid, true);
|
||||
if (!jid) throw new TypeError('You need to provide an entity JID');
|
||||
|
||||
const entity = await api.disco.entities.get(jid, true);
|
||||
|
||||
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||
const msg = `Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`;
|
||||
log.warn(msg);
|
||||
return;
|
||||
log.warn(`Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`);
|
||||
return [];
|
||||
}
|
||||
entity = await entity.waitUntilFeaturesDiscovered;
|
||||
const promises = [...entity.items.map(i => i.hasFeature(feature)), entity.hasFeature(feature)];
|
||||
|
||||
const promises = [
|
||||
entity.getFeature(feature),
|
||||
...api.disco.entities.items(jid).map(i => i.getFeature(feature))
|
||||
];
|
||||
const result = await Promise.all(promises);
|
||||
return result.filter(isObject);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if an entity with the given JID, or if one of its
|
||||
* associated items, supports a given feature.
|
||||
*
|
||||
* @method api.disco.features.has
|
||||
* @param { string } feature The feature that might be
|
||||
* supported. In the XML stanza, this is the `var`
|
||||
* attribute of the `<feature>` element. For
|
||||
* example: `http://jabber.org/protocol/muc`
|
||||
* @param { string } jid The JID of the entity
|
||||
* (and its associated items) which should be queried
|
||||
* @returns {Promise} A promise which resolves with a boolean
|
||||
* @example
|
||||
* api.disco.features.has(Strophe.NS.MAM, _converse.bare_jid);
|
||||
*/
|
||||
async has (feature, jid) {
|
||||
if (!jid) throw new TypeError('You need to provide an entity JID');
|
||||
|
||||
const entity = await api.disco.entities.get(jid, true);
|
||||
|
||||
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||
log.warn(`Tried to check if ${jid} supports feature ${feature}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await entity.getFeature(feature)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const result = await Promise.all(api.disco.entities.items(jid).map(i => i.getFeature(feature)));
|
||||
return result.map(isObject).includes(true);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -272,11 +319,11 @@ export default {
|
|||
* Used to determine whether an entity supports a given feature.
|
||||
*
|
||||
* @method api.disco.supports
|
||||
* @param {string} feature The feature that might be
|
||||
* @param { string } feature The feature that might be
|
||||
* supported. In the XML stanza, this is the `var`
|
||||
* attribute of the `<feature>` element. For
|
||||
* example: `http://jabber.org/protocol/muc`
|
||||
* @param {string} jid The JID of the entity
|
||||
* @param { string } jid The JID of the entity
|
||||
* (and its associated items) which should be queried
|
||||
* @returns {promise} A promise which resolves with `true` or `false`.
|
||||
* @example
|
||||
|
@ -286,16 +333,15 @@ export default {
|
|||
* // The feature is not supported
|
||||
* }
|
||||
*/
|
||||
async supports (feature, jid) {
|
||||
const features = await api.disco.features.get(feature, jid) || [];
|
||||
return features.length > 0;
|
||||
supports (feature, jid) {
|
||||
return api.disco.features.has(feature, jid);
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh the features, fields and identities associated with a
|
||||
* disco entity by refetching them from the server
|
||||
* @method api.disco.refresh
|
||||
* @param {string} jid The JID of the entity whose features are refreshed.
|
||||
* @param { string } jid The JID of the entity whose features are refreshed.
|
||||
* @returns {promise} A promise which resolves once the features have been refreshed
|
||||
* @example
|
||||
* await api.disco.refresh('room@conference.example.org');
|
||||
|
@ -316,7 +362,7 @@ export default {
|
|||
entity.queryInfo();
|
||||
} else {
|
||||
// Create it if it doesn't exist
|
||||
entity = await api.disco.entities.create(jid, {'ignore_cache': true});
|
||||
entity = await api.disco.entities.create({ jid }, {'ignore_cache': true});
|
||||
}
|
||||
return entity.waitUntilFeaturesDiscovered;
|
||||
},
|
||||
|
@ -333,7 +379,7 @@ export default {
|
|||
* Return all the features associated with a disco entity
|
||||
*
|
||||
* @method api.disco.getFeatures
|
||||
* @param {string} jid The JID of the entity whose features are returned.
|
||||
* @param { string } jid The JID of the entity whose features are returned.
|
||||
* @returns {promise} A promise which resolves with the returned features
|
||||
* @example
|
||||
* const features = await api.disco.getFeatures('room@conference.example.org');
|
||||
|
@ -355,7 +401,7 @@ export default {
|
|||
* See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
|
||||
*
|
||||
* @method api.disco.getFields
|
||||
* @param {string} jid The JID of the entity whose fields are returned.
|
||||
* @param { string } jid The JID of the entity whose fields are returned.
|
||||
* @example
|
||||
* const fields = await api.disco.getFields('room@conference.example.org');
|
||||
*/
|
||||
|
@ -378,15 +424,15 @@ export default {
|
|||
* XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
|
||||
*
|
||||
* @method api.disco.getIdentity
|
||||
* @param {string} The identity category.
|
||||
* @param { string } The identity category.
|
||||
* In the XML stanza, this is the `category`
|
||||
* attribute of the `<identity>` element.
|
||||
* For example: 'pubsub'
|
||||
* @param {string} type The identity type.
|
||||
* @param { string } type The identity type.
|
||||
* In the XML stanza, this is the `type`
|
||||
* attribute of the `<identity>` element.
|
||||
* For example: 'pep'
|
||||
* @param {string} jid The JID of the entity which might have the identity
|
||||
* @param { string } jid The JID of the entity which might have the identity
|
||||
* @returns {promise} A promise which resolves with a map indicating
|
||||
* whether an identity with a given type is provided by the entity.
|
||||
* @example
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import log from "@converse/headless/log.js";
|
||||
import sizzle from "sizzle";
|
||||
import { Collection } from "@converse/skeletor/src/collection";
|
||||
import log from '@converse/headless/log.js';
|
||||
import sizzle from 'sizzle';
|
||||
import { Collection } from '@converse/skeletor/src/collection';
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { _converse, api, converse } from "@converse/headless/core.js";
|
||||
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||
import { getOpenPromise } from '@converse/openpromise';
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
|
@ -19,7 +19,7 @@ const { Strophe } = converse.env;
|
|||
const DiscoEntity = Model.extend({
|
||||
idAttribute: 'jid',
|
||||
|
||||
async initialize (_, options) {
|
||||
initialize (_, options) {
|
||||
this.waitUntilFeaturesDiscovered = getOpenPromise();
|
||||
|
||||
this.dataforms = new Collection();
|
||||
|
@ -29,17 +29,12 @@ const DiscoEntity = Model.extend({
|
|||
this.features = new Collection();
|
||||
id = `converse.features-${this.get('jid')}`;
|
||||
this.features.browserStorage = _converse.createStore(id, 'session');
|
||||
this.listenTo(this.features, 'add', this.onFeatureAdded)
|
||||
this.listenTo(this.features, 'add', this.onFeatureAdded);
|
||||
|
||||
this.fields = new Collection();
|
||||
id = `converse.fields-${this.get('jid')}`;
|
||||
this.fields.browserStorage = _converse.createStore(id, 'session');
|
||||
this.listenTo(this.fields, 'add', this.onFieldAdded)
|
||||
|
||||
this.items = new _converse.DiscoEntities();
|
||||
id = `converse.disco-items-${this.get('jid')}`;
|
||||
this.items.browserStorage = _converse.createStore(id, 'session');
|
||||
await new Promise(f => this.items.fetch({'success': f, 'error': f}));
|
||||
this.listenTo(this.fields, 'add', this.onFieldAdded);
|
||||
|
||||
this.identities = new Collection();
|
||||
id = `converse.identities-${this.get('jid')}`;
|
||||
|
@ -59,7 +54,7 @@ const DiscoEntity = Model.extend({
|
|||
await this.waitUntilFeaturesDiscovered;
|
||||
return this.identities.findWhere({
|
||||
'category': category,
|
||||
'type': type
|
||||
'type': type,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -67,12 +62,12 @@ const DiscoEntity = Model.extend({
|
|||
* Returns a Promise which resolves with a map indicating
|
||||
* whether a given feature is supported.
|
||||
* @private
|
||||
* @method _converse.DiscoEntity#hasFeature
|
||||
* @method _converse.DiscoEntity#getFeature
|
||||
* @param { String } feature - The feature that might be supported.
|
||||
*/
|
||||
async hasFeature (feature) {
|
||||
await this.waitUntilFeaturesDiscovered
|
||||
if (this.features.findWhere({'var': feature})) {
|
||||
async getFeature (feature) {
|
||||
await this.waitUntilFeaturesDiscovered;
|
||||
if (this.features.findWhere({ 'var': feature })) {
|
||||
return this;
|
||||
}
|
||||
},
|
||||
|
@ -106,7 +101,7 @@ const DiscoEntity = Model.extend({
|
|||
} else {
|
||||
const store_id = this.features.browserStorage.name;
|
||||
const result = await this.features.browserStorage.store.getItem(store_id);
|
||||
if (result && result.length === 0 || result === null) {
|
||||
if ((result && result.length === 0) || result === null) {
|
||||
this.queryInfo();
|
||||
} else {
|
||||
this.features.fetch({
|
||||
|
@ -114,9 +109,9 @@ const DiscoEntity = Model.extend({
|
|||
success: () => {
|
||||
this.waitUntilFeaturesDiscovered.resolve(this);
|
||||
this.trigger('featuresDiscovered');
|
||||
}
|
||||
},
|
||||
});
|
||||
this.identities.fetch({add: true});
|
||||
this.identities.fetch({ add: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -135,22 +130,27 @@ const DiscoEntity = Model.extend({
|
|||
|
||||
onDiscoItems (stanza) {
|
||||
sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => {
|
||||
if (item.getAttribute("node")) {
|
||||
if (item.getAttribute('node')) {
|
||||
// XXX: Ignore nodes for now.
|
||||
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
|
||||
return;
|
||||
}
|
||||
const jid = item.getAttribute('jid');
|
||||
if (this.items.get(jid) === undefined) {
|
||||
const entities = _converse.disco_entities;
|
||||
const entity = entities.get(jid) || entities.create({ jid, name: item.getAttribute('name') });
|
||||
this.items.add(entity);
|
||||
const entity = _converse.disco_entities.get(jid);
|
||||
if (entity) {
|
||||
entity.set({ parent_jids: [this.get('jid')] });
|
||||
} else {
|
||||
api.disco.entities.create({
|
||||
jid,
|
||||
'parent_jids': [this.get('jid')],
|
||||
'name': item.getAttribute('name'),
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async queryForItems () {
|
||||
if (this.identities.where({'category': 'server'}).length === 0) {
|
||||
if (this.identities.where({ 'category': 'server' }).length === 0) {
|
||||
// Don't fetch features and items if this is not a
|
||||
// server or a conference component.
|
||||
return;
|
||||
|
@ -159,12 +159,12 @@ const DiscoEntity = Model.extend({
|
|||
this.onDiscoItems(stanza);
|
||||
},
|
||||
|
||||
onInfo (stanza) {
|
||||
async onInfo (stanza) {
|
||||
Array.from(stanza.querySelectorAll('identity')).forEach(identity => {
|
||||
this.identities.create({
|
||||
'category': identity.getAttribute('category'),
|
||||
'type': identity.getAttribute('type'),
|
||||
'name': identity.getAttribute('name')
|
||||
'name': identity.getAttribute('name'),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -173,19 +173,19 @@ const DiscoEntity = Model.extend({
|
|||
sizzle('field', form).forEach(field => {
|
||||
data[field.getAttribute('var')] = {
|
||||
'value': field.querySelector('value')?.textContent,
|
||||
'type': field.getAttribute('type')
|
||||
'type': field.getAttribute('type'),
|
||||
};
|
||||
});
|
||||
this.dataforms.create(data);
|
||||
});
|
||||
|
||||
if (stanza.querySelector(`feature[var="${Strophe.NS.DISCO_ITEMS}"]`)) {
|
||||
this.queryForItems();
|
||||
await this.queryForItems();
|
||||
}
|
||||
Array.from(stanza.querySelectorAll('feature')).forEach(feature => {
|
||||
this.features.create({
|
||||
'var': feature.getAttribute('var'),
|
||||
'from': stanza.getAttribute('from')
|
||||
'from': stanza.getAttribute('from'),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -194,13 +194,13 @@ const DiscoEntity = Model.extend({
|
|||
this.fields.create({
|
||||
'var': field.getAttribute('var'),
|
||||
'value': field.querySelector('value')?.textContent,
|
||||
'from': stanza.getAttribute('from')
|
||||
'from': stanza.getAttribute('from'),
|
||||
});
|
||||
});
|
||||
|
||||
this.waitUntilFeaturesDiscovered.resolve(this);
|
||||
this.trigger('featuresDiscovered');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default DiscoEntity;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
describe("Service Discovery", function () {
|
||||
|
||||
describe("Whenever converse.js queries a server for its features", function () {
|
||||
describe("Whenever a server is queried for its features", function () {
|
||||
|
||||
it("stores the features it receives",
|
||||
mock.initConverse(
|
||||
|
@ -76,23 +76,12 @@ describe("Service Discovery", function () {
|
|||
'var': 'jabber:iq:version'});
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
|
||||
let entities = await _converse.api.disco.entities.get()
|
||||
expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID
|
||||
expect(entities.get(_converse.domain).features.length).toBe(5);
|
||||
expect(entities.get(_converse.domain).identities.length).toBe(3);
|
||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:version'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:time'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:register'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where(
|
||||
{'var': 'http://jabber.org/protocol/disco#items'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where(
|
||||
{'var': 'http://jabber.org/protocol/disco#info'}).length).toBe(1);
|
||||
|
||||
await u.waitUntil(function () {
|
||||
// Converse.js sees that the entity has a disco#items feature,
|
||||
// so it will make a query for it.
|
||||
return IQ_stanzas.filter(iq => iq.querySelector('query[xmlns="http://jabber.org/protocol/disco#items"]')).length > 0;
|
||||
});
|
||||
|
||||
/* <iq type='result'
|
||||
* from='catalog.shakespeare.lit'
|
||||
* to='romeo@montague.net/orchard'
|
||||
|
@ -119,9 +108,8 @@ describe("Service Discovery", function () {
|
|||
* </query>
|
||||
* </iq>
|
||||
*/
|
||||
stanza = IQ_stanzas.find(function (iq) {
|
||||
return iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]');
|
||||
});
|
||||
stanza = IQ_stanzas.find(iq => iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]'));
|
||||
|
||||
const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
|
||||
stanza = $iq({
|
||||
'type': 'result',
|
||||
|
@ -152,9 +140,19 @@ describe("Service Discovery", function () {
|
|||
});
|
||||
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
await u.waitUntil(() => _converse.disco_entities);
|
||||
entities = _converse.disco_entities;
|
||||
expect(entities.length).toBe(5);
|
||||
|
||||
const entities = await _converse.api.disco.entities.get()
|
||||
expect(entities.length).toBe(5); // We have an extra entity, which is the user's JID
|
||||
expect(entities.get(_converse.domain).features.length).toBe(5);
|
||||
expect(entities.get(_converse.domain).identities.length).toBe(3);
|
||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:version'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:time'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where({'var': 'jabber:iq:register'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where(
|
||||
{'var': 'http://jabber.org/protocol/disco#items'}).length).toBe(1);
|
||||
expect(entities.get('montague.lit').features.where(
|
||||
{'var': 'http://jabber.org/protocol/disco#info'}).length).toBe(1);
|
||||
|
||||
expect(entities.map(e => e.get('jid'))).toEqual([
|
||||
'montague.lit',
|
||||
'romeo@montague.lit',
|
||||
|
@ -162,11 +160,14 @@ describe("Service Discovery", function () {
|
|||
'plays.shakespeare.lit',
|
||||
'words.shakespeare.lit'
|
||||
]);
|
||||
const { api, domain } = _converse;
|
||||
let entity = entities.get(_converse.domain);
|
||||
expect(entity.items.length).toBe(3);
|
||||
expect(entity.items.pluck('jid').includes('people.shakespeare.lit')).toBeTruthy();
|
||||
expect(entity.items.pluck('jid').includes('plays.shakespeare.lit')).toBeTruthy();
|
||||
expect(entity.items.pluck('jid').includes('words.shakespeare.lit')).toBeTruthy();
|
||||
expect(api.disco.entities.items(domain).length).toBe(3);
|
||||
|
||||
expect(api.disco.entities.items(domain).map(e => e.get('jid'))).toEqual(
|
||||
['people.shakespeare.lit', 'plays.shakespeare.lit', 'words.shakespeare.lit']
|
||||
)
|
||||
|
||||
expect(entity.identities.where({'category': 'conference'}).length).toBe(1);
|
||||
expect(entity.identities.where({'category': 'directory'}).length).toBe(1);
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ function addClientFeatures () {
|
|||
api.disco.own.features.add(Strophe.NS.CHATSTATES);
|
||||
api.disco.own.features.add(Strophe.NS.DISCO_INFO);
|
||||
api.disco.own.features.add(Strophe.NS.ROSTERX); // Limited support
|
||||
if (api.settings.get("message_carbons")) {
|
||||
api.disco.own.features.add(Strophe.NS.CARBONS);
|
||||
}
|
||||
api.disco.own.features.add(Strophe.NS.CARBONS);
|
||||
/**
|
||||
* Triggered in converse-disco once the core disco features of
|
||||
* Converse have been added.
|
||||
|
@ -71,7 +69,7 @@ export async function initializeDisco () {
|
|||
const collection = await _converse.disco_entities.fetchEntities();
|
||||
if (collection.length === 0 || !collection.get(_converse.domain)) {
|
||||
// If we don't have an entity for our own XMPP server, create one.
|
||||
_converse.disco_entities.create({'jid': _converse.domain});
|
||||
api.disco.entities.create({'jid': _converse.domain}, {'ignore_cache': true});
|
||||
}
|
||||
/**
|
||||
* Triggered once the `converse-disco` plugin has been initialized and the
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* @copyright 2022, the Converse.js contributors
|
||||
* @license Mozilla Public License (MPLv2)
|
||||
*/
|
||||
import './utils.js';
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { _converse, api, converse } from "../../core.js";
|
||||
import { getOpenPromise } from '@converse/openpromise';
|
||||
|
@ -85,8 +86,8 @@ converse.plugins.add('converse-emoji', {
|
|||
async initialize () {
|
||||
if (!converse.emojis.initialized) {
|
||||
converse.emojis.initialized = true;
|
||||
const { default: json } = await import(/*webpackChunkName: "emojis" */ './emoji.json');
|
||||
converse.emojis.json = json;
|
||||
const module = await import(/*webpackChunkName: "emojis" */ './emoji.json');
|
||||
const json = converse.emojis.json = module.default;
|
||||
converse.emojis.by_sn = Object.keys(json).reduce((result, cat) => Object.assign(result, json[cat]), {});
|
||||
converse.emojis.list = Object.values(converse.emojis.by_sn);
|
||||
converse.emojis.list.sort((a, b) => a.sn < b.sn ? -1 : (a.sn > b.sn ? 1 : 0));
|
||||
|
|
|
@ -94,7 +94,7 @@ export function getShortnameReferences (text) {
|
|||
if (!converse.emojis.initialized) {
|
||||
throw new Error(
|
||||
'getShortnameReferences called before emojis are initialized. '+
|
||||
'To avoid this problem, first await the converse.emojis.initilaized_promise.'
|
||||
'To avoid this problem, first await the converse.emojis.initialized_promise'
|
||||
);
|
||||
}
|
||||
const references = [...text.matchAll(converse.emojis.shortnames_regex)].filter(ref => ref[0].length > 0);
|
||||
|
@ -163,7 +163,7 @@ function shortnamesToUnicode (str) {
|
|||
* Determines whether the passed in string is just a single emoji shortname;
|
||||
* @namespace u
|
||||
* @method u.isOnlyEmojis
|
||||
* @param { String } shortname - A string which migh be just an emoji shortname
|
||||
* @param { String } text - A string which migh be just an emoji shortname
|
||||
* @returns { Boolean }
|
||||
*/
|
||||
function isOnlyEmojis (text) {
|
||||
|
@ -207,4 +207,3 @@ Object.assign(u, {
|
|||
isOnlyEmojis,
|
||||
shortnamesToUnicode,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
/**
|
||||
* @module converse-headlines
|
||||
* @copyright 2022, the Converse.js contributors
|
||||
* @description XEP-0045 Multi-User Chat Views
|
||||
*/
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { isHeadline, isServerMessage } from '@converse/headless/shared/parsers';
|
||||
import { parseMessage } from '@converse/headless/plugins/chat/parsers';
|
||||
|
||||
|
||||
converse.plugins.add('converse-headlines', {
|
||||
/* Plugin dependencies are other plugins which might be
|
||||
* overridden or relied upon, and therefore need to be loaded before
|
||||
* this plugin.
|
||||
*
|
||||
* If the setting "strict_plugin_dependencies" is set to true,
|
||||
* an error will be raised if the plugin is not found. By default it's
|
||||
* false, which means these plugins are only loaded opportunistically.
|
||||
*
|
||||
* NB: These plugins need to have already been loaded via require.js.
|
||||
*/
|
||||
dependencies: ["converse-chat"],
|
||||
|
||||
overrides: {
|
||||
// Overrides mentioned here will be picked up by converse.js's
|
||||
// plugin architecture they will replace existing methods on the
|
||||
// relevant objects or classes.
|
||||
//
|
||||
// New functions which don't exist yet can also be added.
|
||||
|
||||
ChatBoxes: {
|
||||
model (attrs, options) {
|
||||
const { _converse } = this.__super__;
|
||||
if (attrs.type == _converse.HEADLINES_TYPE) {
|
||||
return new _converse.HeadlinesBox(attrs, options);
|
||||
} else {
|
||||
return this.__super__.model.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by converse.js's plugin machinery.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shows headline messages
|
||||
* @class
|
||||
* @namespace _converse.HeadlinesBox
|
||||
* @memberOf _converse
|
||||
*/
|
||||
_converse.HeadlinesBox = _converse.ChatBox.extend({
|
||||
defaults () {
|
||||
return {
|
||||
'bookmarked': false,
|
||||
'hidden': ['mobile', 'fullscreen'].includes(api.settings.get("view_mode")),
|
||||
'message_type': 'headline',
|
||||
'num_unread': 0,
|
||||
'time_opened': this.get('time_opened') || (new Date()).getTime(),
|
||||
'type': _converse.HEADLINES_TYPE
|
||||
}
|
||||
},
|
||||
|
||||
async initialize () {
|
||||
this.set({'box_id': `box-${this.get('jid')}`});
|
||||
this.initUI();
|
||||
this.initMessages();
|
||||
await this.fetchMessages();
|
||||
/**
|
||||
* Triggered once a {@link _converse.HeadlinesBox} has been created and initialized.
|
||||
* @event _converse#headlinesBoxInitialized
|
||||
* @type { _converse.HeadlinesBox }
|
||||
* @example _converse.api.listen.on('headlinesBoxInitialized', model => { ... });
|
||||
*/
|
||||
api.trigger('headlinesBoxInitialized', this);
|
||||
}
|
||||
});
|
||||
|
||||
async function onHeadlineMessage (stanza) {
|
||||
// Handler method for all incoming messages of type "headline".
|
||||
if (isHeadline(stanza) || isServerMessage(stanza)) {
|
||||
const from_jid = stanza.getAttribute('from');
|
||||
|
||||
await api.waitUntil('rosterInitialized')
|
||||
if (from_jid.includes('@') &&
|
||||
!_converse.roster.get(from_jid) &&
|
||||
!api.settings.get("allow_non_roster_messaging")) {
|
||||
return;
|
||||
}
|
||||
if (stanza.querySelector('body') === null) {
|
||||
// Avoid creating a chat box if we have nothing to show inside it.
|
||||
return;
|
||||
}
|
||||
const chatbox = _converse.chatboxes.create({
|
||||
'id': from_jid,
|
||||
'jid': from_jid,
|
||||
'type': _converse.HEADLINES_TYPE,
|
||||
'from': from_jid
|
||||
});
|
||||
const attrs = await parseMessage(stanza, _converse);
|
||||
await chatbox.createMessage(attrs);
|
||||
api.trigger('message', {chatbox, stanza, attrs});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************ BEGIN Event Handlers ************************/
|
||||
function registerHeadlineHandler () {
|
||||
_converse.connection.addHandler(message => (onHeadlineMessage(message) || true), null, 'message');
|
||||
}
|
||||
api.listen.on('connected', registerHeadlineHandler);
|
||||
api.listen.on('reconnected', registerHeadlineHandler);
|
||||
/************************ END Event Handlers ************************/
|
||||
|
||||
|
||||
/************************ BEGIN API ************************/
|
||||
Object.assign(api, {
|
||||
/**
|
||||
* The "headlines" namespace, which is used for headline-channels
|
||||
* which are read-only channels containing messages of type
|
||||
* "headline".
|
||||
*
|
||||
* @namespace api.headlines
|
||||
* @memberOf api
|
||||
*/
|
||||
headlines: {
|
||||
/**
|
||||
* Retrieves a headline-channel or all headline-channels.
|
||||
*
|
||||
* @method api.headlines.get
|
||||
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
|
||||
* @returns { Promise<_converse.HeadlinesBox> }
|
||||
*/
|
||||
async get (jids, attrs={}, create=false) {
|
||||
async function _get (jid) {
|
||||
let model = await api.chatboxes.get(jid);
|
||||
if (!model && create) {
|
||||
model = await api.chatboxes.create(jid, attrs, _converse.HeadlinesBox);
|
||||
} else {
|
||||
model = (model && model.get('type') === _converse.HEADLINES_TYPE) ? model : null;
|
||||
if (model && Object.keys(attrs).length) {
|
||||
model.save(attrs);
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
if (jids === undefined) {
|
||||
const chats = await api.chatboxes.get();
|
||||
return chats.filter(c => (c.get('type') === _converse.HEADLINES_TYPE));
|
||||
} else if (typeof jids === 'string') {
|
||||
return _get(jids);
|
||||
}
|
||||
return Promise.all(jids.map(jid => _get(jid)));
|
||||
}
|
||||
}
|
||||
});
|
||||
/************************ END API ************************/
|
||||
}
|
||||
});
|
44
src/headless/plugins/headlines/api.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { _converse, api } from "@converse/headless/core";
|
||||
|
||||
export default {
|
||||
/**
|
||||
* The "headlines" namespace, which is used for headline-channels
|
||||
* which are read-only channels containing messages of type
|
||||
* "headline".
|
||||
*
|
||||
* @namespace api.headlines
|
||||
* @memberOf api
|
||||
*/
|
||||
headlines: {
|
||||
/**
|
||||
* Retrieves a headline-channel or all headline-channels.
|
||||
*
|
||||
* @method api.headlines.get
|
||||
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
||||
* @param { Object } [attrs] - Attributes to be set on the _converse.ChatBox model.
|
||||
* @param { Boolean } [create=false] - Whether the chat should be created if it's not found.
|
||||
* @returns { Promise<_converse.HeadlinesFeed> }
|
||||
*/
|
||||
async get (jids, attrs={}, create=false) {
|
||||
async function _get (jid) {
|
||||
let model = await api.chatboxes.get(jid);
|
||||
if (!model && create) {
|
||||
model = await api.chatboxes.create(jid, attrs, _converse.HeadlinesFeed);
|
||||
} else {
|
||||
model = (model && model.get('type') === _converse.HEADLINES_TYPE) ? model : null;
|
||||
if (model && Object.keys(attrs).length) {
|
||||
model.save(attrs);
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
if (jids === undefined) {
|
||||
const chats = await api.chatboxes.get();
|
||||
return chats.filter(c => (c.get('type') === _converse.HEADLINES_TYPE));
|
||||
} else if (typeof jids === 'string') {
|
||||
return _get(jids);
|
||||
}
|
||||
return Promise.all(jids.map(jid => _get(jid)));
|
||||
}
|
||||
}
|
||||
};
|
31
src/headless/plugins/headlines/feed.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import ChatBox from '@converse/headless/plugins/chat/model.js';
|
||||
import { _converse, api } from '../../core.js';
|
||||
|
||||
|
||||
export default class HeadlinesFeed extends ChatBox {
|
||||
|
||||
defaults () {
|
||||
return {
|
||||
'bookmarked': false,
|
||||
'hidden': ['mobile', 'fullscreen'].includes(api.settings.get("view_mode")),
|
||||
'message_type': 'headline',
|
||||
'num_unread': 0,
|
||||
'time_opened': this.get('time_opened') || (new Date()).getTime(),
|
||||
'type': _converse.HEADLINES_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
async initialize () {
|
||||
this.set({'box_id': `box-${this.get('jid')}`});
|
||||
this.initUI();
|
||||
this.initMessages();
|
||||
await this.fetchMessages();
|
||||
/**
|
||||
* Triggered once a { @link _converse.HeadlinesFeed } has been created and initialized.
|
||||
* @event _converse#headlinesFeedInitialized
|
||||
* @type { _converse.HeadlinesFeed }
|
||||
* @example _converse.api.listen.on('headlinesFeedInitialized', model => { ... });
|
||||
*/
|
||||
api.trigger('headlinesFeedInitialized', this);
|
||||
}
|
||||
}
|
49
src/headless/plugins/headlines/index.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* @module converse-headlines
|
||||
* @copyright 2022, the Converse.js contributors
|
||||
*/
|
||||
import HeadlinesFeed from './feed.js';
|
||||
import headlines_api from './api.js';
|
||||
import { _converse, api, converse } from "../../core.js";
|
||||
import { onHeadlineMessage } from './utils.js';
|
||||
|
||||
|
||||
converse.plugins.add('converse-headlines', {
|
||||
dependencies: ["converse-chat"],
|
||||
|
||||
overrides: {
|
||||
// Overrides mentioned here will be picked up by converse.js's
|
||||
// plugin architecture they will replace existing methods on the
|
||||
// relevant objects or classes.
|
||||
|
||||
ChatBoxes: {
|
||||
model (attrs, options) {
|
||||
const { _converse } = this.__super__;
|
||||
if (attrs.type == _converse.HEADLINES_TYPE) {
|
||||
return new _converse.HeadlinesFeed(attrs, options);
|
||||
} else {
|
||||
return this.__super__.model.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
initialize () {
|
||||
/**
|
||||
* Shows headline messages
|
||||
* @class
|
||||
* @namespace _converse.HeadlinesFeed
|
||||
* @memberOf _converse
|
||||
*/
|
||||
_converse.HeadlinesFeed = HeadlinesFeed;
|
||||
|
||||
function registerHeadlineHandler () {
|
||||
_converse.connection.addHandler(m => (onHeadlineMessage(m) || true), null, 'message');
|
||||
}
|
||||
api.listen.on('connected', registerHeadlineHandler);
|
||||
api.listen.on('reconnected', registerHeadlineHandler);
|
||||
|
||||
Object.assign(api, headlines_api);
|
||||
}
|
||||
});
|
33
src/headless/plugins/headlines/utils.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { _converse, api } from "@converse/headless/core";
|
||||
import { isHeadline, isServerMessage } from '@converse/headless/shared/parsers';
|
||||
import { parseMessage } from '@converse/headless/plugins/chat/parsers';
|
||||
|
||||
/**
|
||||
* Handler method for all incoming messages of type "headline".
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
export async function onHeadlineMessage (stanza) {
|
||||
if (isHeadline(stanza) || isServerMessage(stanza)) {
|
||||
const from_jid = stanza.getAttribute('from');
|
||||
|
||||
await api.waitUntil('rosterInitialized')
|
||||
if (from_jid.includes('@') &&
|
||||
!_converse.roster.get(from_jid) &&
|
||||
!api.settings.get("allow_non_roster_messaging")) {
|
||||
return;
|
||||
}
|
||||
if (stanza.querySelector('body') === null) {
|
||||
// Avoid creating a chat box if we have nothing to show inside it.
|
||||
return;
|
||||
}
|
||||
const chatbox = _converse.chatboxes.create({
|
||||
'id': from_jid,
|
||||
'jid': from_jid,
|
||||
'type': _converse.HEADLINES_TYPE,
|
||||
'from': from_jid
|
||||
});
|
||||
const attrs = await parseMessage(stanza, _converse);
|
||||
await chatbox.createMessage(attrs);
|
||||
api.trigger('message', {chatbox, stanza, attrs});
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { RSM } from '@converse/headless/shared/rsm';
|
||||
import log from '@converse/headless/log';
|
||||
import sizzle from "sizzle";
|
||||
import { RSM } from '@converse/headless/shared/rsm';
|
||||
import { TimeoutError } from '../../shared/errors.js';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
|
||||
const { Strophe, $iq, dayjs } = converse.env;
|
||||
|
@ -270,7 +271,7 @@ export default {
|
|||
const { __ } = _converse;
|
||||
const err_msg = __("Timeout while trying to fetch archived messages.");
|
||||
log.error(err_msg);
|
||||
error = new _converse.TimeoutError(err_msg);
|
||||
error = new TimeoutError(err_msg);
|
||||
return { messages, error };
|
||||
|
||||
} else if (u.isErrorStanza(iq_result)) {
|
||||
|
|
|
@ -48,19 +48,13 @@ converse.plugins.add('converse-mam', {
|
|||
api.listen.on('enteredNewRoom', muc => muc.features.get('mam_enabled') && fetchNewestMessages(muc));
|
||||
|
||||
api.listen.on('chatReconnected', chat => {
|
||||
// XXX: For MUCs, we listen to enteredNewRoom instead
|
||||
if (chat.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
fetchNewestMessages(chat);
|
||||
}
|
||||
});
|
||||
|
||||
api.listen.on('afterMessagesFetched', chat => {
|
||||
// XXX: We don't want to query MAM every time this is triggered
|
||||
// since it's not necessary when the chat is restored from cache.
|
||||
// (given that BOSH or SMACKS will ensure that you get messages
|
||||
// sent during the reload).
|
||||
// With MUCs we can listen for `enteredNewRoom`.
|
||||
if (chat.get('type') === _converse.PRIVATE_CHAT_TYPE && !_converse.connection.restored) {
|
||||
if (chat.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||
fetchNewestMessages(chat);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -100,7 +100,7 @@ export async function handleMAMResult (model, result, query, options, should_pag
|
|||
/**
|
||||
* @typedef { Object } MAMOptions
|
||||
* A map of MAM related options that may be passed to fetchArchivedMessages
|
||||
* @param { integer } [options.max] - The maximum number of items to return.
|
||||
* @param { number } [options.max] - The maximum number of items to return.
|
||||
* Defaults to "archived_messages_page_size"
|
||||
* @param { string } [options.after] - The XEP-0359 stanza ID of a message
|
||||
* after which messages should be returned. Implies forward paging.
|
||||
|
@ -117,7 +117,7 @@ export async function handleMAMResult (model, result, query, options, should_pag
|
|||
|
||||
/**
|
||||
* Fetch XEP-0313 archived messages based on the passed in criteria.
|
||||
* @param { _converse.ChatBox | _converse.ChatRoom } model
|
||||
* @param { ChatBox | ChatRoom } model
|
||||
* @param { MAMOptions } [options]
|
||||
* @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether
|
||||
* this function should recursively page through the entire result set if a limited
|
||||
|
|
|
@ -45,7 +45,7 @@ export async function getAffiliationList (affiliation, muc_jid) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given an occupant model, see which affiliations may be assigned to that user.
|
||||
* Given an occupant model, see which affiliations may be assigned by that user
|
||||
* @param { Model } occupant
|
||||
* @returns { Array<('owner'|'admin'|'member'|'outcast'|'none')> } - An array of assignable affiliations
|
||||
*/
|
||||
|
@ -54,9 +54,9 @@ export function getAssignableAffiliations (occupant) {
|
|||
if (!Array.isArray(disabled)) {
|
||||
disabled = disabled ? AFFILIATIONS : [];
|
||||
}
|
||||
if (occupant.get('affiliation') === 'owner') {
|
||||
if (occupant?.get('affiliation') === 'owner') {
|
||||
return AFFILIATIONS.filter(a => !disabled.includes(a));
|
||||
} else if (occupant.get('affiliation') === 'admin') {
|
||||
} else if (occupant?.get('affiliation') === 'admin') {
|
||||
return AFFILIATIONS.filter(a => !['owner', 'admin', ...disabled].includes(a));
|
||||
} else {
|
||||
return [];
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import log from '../../log';
|
||||
import u from '../../utils/form';
|
||||
import { Strophe } from 'strophe.js/src/strophe';
|
||||
import { _converse, api } from '../../core.js';
|
||||
import { _converse, api, converse } from '../../core.js';
|
||||
|
||||
const { u } = converse.env;
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -22,7 +23,7 @@ export default {
|
|||
* @method api.rooms.create
|
||||
* @param {(string[]|string)} jid|jids The JID or array of
|
||||
* JIDs of the chatroom(s) to create
|
||||
* @param {object} [attrs] attrs The room attributes
|
||||
* @param { object } [attrs] attrs The room attributes
|
||||
* @returns {Promise} Promise which resolves with the Model representing the chat.
|
||||
*/
|
||||
create (jids, attrs = {}) {
|
||||
|
@ -44,24 +45,24 @@ export default {
|
|||
* Similar to {@link api.chats.open}, but for groupchats.
|
||||
*
|
||||
* @method api.rooms.open
|
||||
* @param {string} jid The room JID or JIDs (if not specified, all
|
||||
* @param { string } jid The room JID or JIDs (if not specified, all
|
||||
* currently open rooms will be returned).
|
||||
* @param {string} attrs A map containing any extra room attributes.
|
||||
* @param {string} [attrs.nick] The current user's nickname for the MUC
|
||||
* @param {boolean} [attrs.auto_configure] A boolean, indicating
|
||||
* @param { string } attrs A map containing any extra room attributes.
|
||||
* @param { string } [attrs.nick] The current user's nickname for the MUC
|
||||
* @param { boolean } [attrs.auto_configure] A boolean, indicating
|
||||
* whether the room should be configured automatically or not.
|
||||
* If set to `true`, then it makes sense to pass in configuration settings.
|
||||
* @param {object} [attrs.roomconfig] A map of configuration settings to be used when the room gets
|
||||
* @param { object } [attrs.roomconfig] A map of configuration settings to be used when the room gets
|
||||
* configured automatically. Currently it doesn't make sense to specify
|
||||
* `roomconfig` values if `auto_configure` is set to `false`.
|
||||
* For a list of configuration values that can be passed in, refer to these values
|
||||
* in the [XEP-0045 MUC specification](https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner).
|
||||
* The values should be named without the `muc#roomconfig_` prefix.
|
||||
* @param {boolean} [attrs.minimized] A boolean, indicating whether the room should be opened minimized or not.
|
||||
* @param {boolean} [attrs.bring_to_foreground] A boolean indicating whether the room should be
|
||||
* @param { boolean } [attrs.minimized] A boolean, indicating whether the room should be opened minimized or not.
|
||||
* @param { boolean } [attrs.bring_to_foreground] A boolean indicating whether the room should be
|
||||
* brought to the foreground and therefore replace the currently shown chat.
|
||||
* If there is no chat currently open, then this option is ineffective.
|
||||
* @param {Boolean} [force=false] - By default, a minimized
|
||||
* @param { Boolean } [force=false] - By default, a minimized
|
||||
* room won't be maximized (in `overlayed` view mode) and in
|
||||
* `fullscreen` view mode a newly opened room won't replace
|
||||
* another chat already in the foreground.
|
||||
|
|
|
@ -1,6 +1,84 @@
|
|||
export const ROLES = ['moderator', 'participant', 'visitor'];
|
||||
export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
|
||||
|
||||
export const MUC_ROLE_WEIGHTS = {
|
||||
'moderator': 1,
|
||||
'participant': 2,
|
||||
'visitor': 3,
|
||||
'none': 2
|
||||
'none': 2,
|
||||
};
|
||||
|
||||
export const AFFILIATION_CHANGES = {
|
||||
OWNER: 'owner',
|
||||
ADMIN: 'admin',
|
||||
MEMBER: 'member',
|
||||
EXADMIN: 'exadmin',
|
||||
EXOWNER: 'exowner',
|
||||
EXOUTCAST: 'exoutcast',
|
||||
EXMEMBER: 'exmember',
|
||||
};
|
||||
|
||||
export const AFFILIATION_CHANGES_LIST = Object.values(AFFILIATION_CHANGES);
|
||||
export const MUC_TRAFFIC_STATES = { ENTERED: 'entered', EXITED: 'exited' };
|
||||
export const MUC_TRAFFIC_STATES_LIST = Object.values(MUC_TRAFFIC_STATES);
|
||||
export const MUC_ROLE_CHANGES = { OP: 'op', DEOP: 'deop', VOICE: 'voice', MUTE: 'mute' };
|
||||
export const MUC_ROLE_CHANGES_LIST = Object.values(MUC_ROLE_CHANGES);
|
||||
|
||||
export const INFO_CODES = {
|
||||
'visibility_changes': ['100', '102', '103', '172', '173', '174'],
|
||||
'self': ['110'],
|
||||
'non_privacy_changes': ['104', '201'],
|
||||
'muc_logging_changes': ['170', '171'],
|
||||
'nickname_changes': ['210', '303'],
|
||||
'disconnected': ['301', '307', '321', '322', '332', '333'],
|
||||
'affiliation_changes': [...AFFILIATION_CHANGES_LIST],
|
||||
'join_leave_events': [...MUC_TRAFFIC_STATES_LIST],
|
||||
'role_changes': [...MUC_ROLE_CHANGES_LIST],
|
||||
};
|
||||
|
||||
export const ROOMSTATUS = {
|
||||
CONNECTED: 0,
|
||||
CONNECTING: 1,
|
||||
NICKNAME_REQUIRED: 2,
|
||||
PASSWORD_REQUIRED: 3,
|
||||
DISCONNECTED: 4,
|
||||
ENTERED: 5,
|
||||
DESTROYED: 6,
|
||||
BANNED: 7,
|
||||
CLOSING: 8,
|
||||
};
|
||||
|
||||
export const ROOM_FEATURES = [
|
||||
'passwordprotected',
|
||||
'unsecured',
|
||||
'hidden',
|
||||
'publicroom',
|
||||
'membersonly',
|
||||
'open',
|
||||
'persistent',
|
||||
'temporary',
|
||||
'nonanonymous',
|
||||
'semianonymous',
|
||||
'moderated',
|
||||
'unmoderated',
|
||||
'mam_enabled',
|
||||
];
|
||||
|
||||
export const MUC_NICK_CHANGED_CODE = '303';
|
||||
|
||||
// No longer used in code, but useful as reference.
|
||||
//
|
||||
// const ROOM_FEATURES_MAP = {
|
||||
// 'passwordprotected': 'unsecured',
|
||||
// 'unsecured': 'passwordprotected',
|
||||
// 'hidden': 'publicroom',
|
||||
// 'publicroom': 'hidden',
|
||||
// 'membersonly': 'open',
|
||||
// 'open': 'membersonly',
|
||||
// 'persistent': 'temporary',
|
||||
// 'temporary': 'persistent',
|
||||
// 'nonanonymous': 'semianonymous',
|
||||
// 'semianonymous': 'nonanonymous',
|
||||
// 'moderated': 'unmoderated',
|
||||
// 'unmoderated': 'moderated'
|
||||
// };
|
||||
|
|
|
@ -29,38 +29,34 @@ import {
|
|||
routeToRoom,
|
||||
} from './utils.js';
|
||||
import { computeAffiliationsDelta } from './affiliations/utils.js';
|
||||
import {
|
||||
AFFILIATION_CHANGES,
|
||||
AFFILIATION_CHANGES_LIST,
|
||||
INFO_CODES,
|
||||
MUC_NICK_CHANGED_CODE,
|
||||
MUC_ROLE_CHANGES,
|
||||
MUC_ROLE_CHANGES_LIST,
|
||||
MUC_TRAFFIC_STATES,
|
||||
MUC_TRAFFIC_STATES_LIST,
|
||||
ROOMSTATUS,
|
||||
ROOM_FEATURES,
|
||||
} from './constants.js';
|
||||
|
||||
export const ROLES = ['moderator', 'participant', 'visitor'];
|
||||
export const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none'];
|
||||
|
||||
converse.AFFILIATION_CHANGES = {
|
||||
OWNER: 'owner',
|
||||
ADMIN: 'admin',
|
||||
MEMBER: 'member',
|
||||
EXADMIN: 'exadmin',
|
||||
EXOWNER: 'exowner',
|
||||
EXOUTCAST: 'exoutcast',
|
||||
EXMEMBER: 'exmember',
|
||||
};
|
||||
converse.AFFILIATION_CHANGES_LIST = Object.values(converse.AFFILIATION_CHANGES);
|
||||
converse.MUC_TRAFFIC_STATES = { ENTERED: 'entered', EXITED: 'exited' };
|
||||
converse.MUC_TRAFFIC_STATES_LIST = Object.values(converse.MUC_TRAFFIC_STATES);
|
||||
converse.MUC_ROLE_CHANGES = { OP: 'op', DEOP: 'deop', VOICE: 'voice', MUTE: 'mute' };
|
||||
converse.MUC_ROLE_CHANGES_LIST = Object.values(converse.MUC_ROLE_CHANGES);
|
||||
converse.AFFILIATION_CHANGES = AFFILIATION_CHANGES;
|
||||
converse.AFFILIATION_CHANGES_LIST = AFFILIATION_CHANGES_LIST;
|
||||
converse.MUC_TRAFFIC_STATES = MUC_TRAFFIC_STATES;
|
||||
converse.MUC_TRAFFIC_STATES_LIST = MUC_TRAFFIC_STATES_LIST;
|
||||
converse.MUC_ROLE_CHANGES = MUC_ROLE_CHANGES;
|
||||
converse.MUC_ROLE_CHANGES_LIST = MUC_ROLE_CHANGES_LIST;
|
||||
|
||||
converse.MUC = {};
|
||||
converse.MUC = { INFO_CODES };
|
||||
|
||||
converse.MUC.INFO_CODES = {
|
||||
'visibility_changes': ['100', '102', '103', '172', '173', '174'],
|
||||
'self': ['110'],
|
||||
'non_privacy_changes': ['104', '201'],
|
||||
'muc_logging_changes': ['170', '171'],
|
||||
'nickname_changes': ['210', '303'],
|
||||
'disconnected': ['301', '307', '321', '322', '332', '333'],
|
||||
'affiliation_changes': [...converse.AFFILIATION_CHANGES_LIST],
|
||||
'join_leave_events': [...converse.MUC_TRAFFIC_STATES_LIST],
|
||||
'role_changes': [...converse.MUC_ROLE_CHANGES_LIST],
|
||||
};
|
||||
converse.MUC_NICK_CHANGED_CODE = MUC_NICK_CHANGED_CODE;
|
||||
converse.ROOM_FEATURES = ROOM_FEATURES;
|
||||
converse.ROOMSTATUS = ROOMSTATUS;
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
|
||||
|
@ -73,66 +69,8 @@ Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + '#user');
|
|||
Strophe.addNamespace('MUC_HATS', 'xmpp:prosody.im/protocol/hats:1');
|
||||
Strophe.addNamespace('CONFINFO', 'urn:ietf:params:xml:ns:conference-info');
|
||||
|
||||
converse.MUC_NICK_CHANGED_CODE = '303';
|
||||
|
||||
converse.ROOM_FEATURES = [
|
||||
'passwordprotected',
|
||||
'unsecured',
|
||||
'hidden',
|
||||
'publicroom',
|
||||
'membersonly',
|
||||
'open',
|
||||
'persistent',
|
||||
'temporary',
|
||||
'nonanonymous',
|
||||
'semianonymous',
|
||||
'moderated',
|
||||
'unmoderated',
|
||||
'mam_enabled',
|
||||
];
|
||||
|
||||
// No longer used in code, but useful as reference.
|
||||
//
|
||||
// const ROOM_FEATURES_MAP = {
|
||||
// 'passwordprotected': 'unsecured',
|
||||
// 'unsecured': 'passwordprotected',
|
||||
// 'hidden': 'publicroom',
|
||||
// 'publicroom': 'hidden',
|
||||
// 'membersonly': 'open',
|
||||
// 'open': 'membersonly',
|
||||
// 'persistent': 'temporary',
|
||||
// 'temporary': 'persistent',
|
||||
// 'nonanonymous': 'semianonymous',
|
||||
// 'semianonymous': 'nonanonymous',
|
||||
// 'moderated': 'unmoderated',
|
||||
// 'unmoderated': 'moderated'
|
||||
// };
|
||||
|
||||
converse.ROOMSTATUS = {
|
||||
CONNECTED: 0,
|
||||
CONNECTING: 1,
|
||||
NICKNAME_REQUIRED: 2,
|
||||
PASSWORD_REQUIRED: 3,
|
||||
DISCONNECTED: 4,
|
||||
ENTERED: 5,
|
||||
DESTROYED: 6,
|
||||
BANNED: 7,
|
||||
CLOSING: 8
|
||||
};
|
||||
|
||||
converse.plugins.add('converse-muc', {
|
||||
/* Optional dependencies are other plugins which might be
|
||||
* overridden or relied upon, and therefore need to be loaded before
|
||||
* this plugin. They are called "optional" because they might not be
|
||||
* available, in which case any overrides applicable to them will be
|
||||
* ignored.
|
||||
*
|
||||
* It's possible however to make optional dependencies non-optional.
|
||||
* If the setting "strict_plugin_dependencies" is set to true,
|
||||
* an error will be raised if the plugin is not found.
|
||||
*
|
||||
* NB: These plugins need to have already been loaded via require.js.
|
||||
*/
|
||||
dependencies: ['converse-chatboxes', 'converse-chat', 'converse-disco'],
|
||||
|
||||
overrides: {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import log from '../../log';
|
||||
import { Strophe } from 'strophe.js/src/strophe';
|
||||
import { _converse, api } from '../../core.js';
|
||||
|
||||
|
@ -20,6 +19,7 @@ const ChatRoomMessageMixin = {
|
|||
this.on('change:type', () => this.setOccupant());
|
||||
this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage());
|
||||
|
||||
this.chatbox = this.collection?.chatbox;
|
||||
this.setTimerForEphemeralMessage();
|
||||
this.setOccupant();
|
||||
/**
|
||||
|
@ -53,24 +53,20 @@ const ChatRoomMessageMixin = {
|
|||
return (
|
||||
['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) &&
|
||||
this.get(`stanza_id ${this.get('from_muc')}`) &&
|
||||
this.collection.chatbox.canModerateMessages()
|
||||
this.chatbox.canModerateMessages()
|
||||
);
|
||||
},
|
||||
|
||||
checkValidity () {
|
||||
const result = _converse.Message.prototype.checkValidity.call(this);
|
||||
!result && this.collection.chatbox.debouncedRejoin();
|
||||
!result && this.chatbox.debouncedRejoin();
|
||||
return result;
|
||||
},
|
||||
|
||||
onOccupantRemoved () {
|
||||
this.stopListening(this.occupant);
|
||||
delete this.occupant;
|
||||
const chatbox = this?.collection?.chatbox;
|
||||
if (!chatbox) {
|
||||
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
|
||||
}
|
||||
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
|
||||
this.listenTo(this.chatbox.occupants, 'add', this.onOccupantAdded);
|
||||
},
|
||||
|
||||
onOccupantAdded (occupant) {
|
||||
|
@ -81,10 +77,6 @@ const ChatRoomMessageMixin = {
|
|||
} else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) {
|
||||
return;
|
||||
}
|
||||
const chatbox = this?.collection?.chatbox;
|
||||
if (!chatbox) {
|
||||
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
|
||||
}
|
||||
|
||||
this.occupant = occupant;
|
||||
if (occupant.get('jid')) {
|
||||
|
@ -93,32 +85,40 @@ const ChatRoomMessageMixin = {
|
|||
|
||||
this.trigger('occupantAdded');
|
||||
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
||||
this.stopListening(chatbox.occupants, 'add', this.onOccupantAdded);
|
||||
this.stopListening(this.chatbox.occupants, 'add', this.onOccupantAdded);
|
||||
},
|
||||
|
||||
getOccupant() {
|
||||
if (this.occupant) return this.occupant;
|
||||
|
||||
this.setOccupant();
|
||||
return this.occupant;
|
||||
},
|
||||
|
||||
setOccupant () {
|
||||
if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) {
|
||||
return;
|
||||
}
|
||||
const chatbox = this?.collection?.chatbox;
|
||||
if (!chatbox) {
|
||||
return log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`);
|
||||
}
|
||||
|
||||
const nick = Strophe.getResourceFromJid(this.get('from'));
|
||||
const occupant_id = this.get('occupant_id');
|
||||
this.occupant = chatbox.occupants.findOccupant({ nick, occupant_id });
|
||||
|
||||
if (!this.occupant && api.settings.get('muc_send_probes')) {
|
||||
this.occupant = chatbox.occupants.create({ nick, occupant_id, 'type': 'unavailable' });
|
||||
const jid = `${chatbox.get('jid')}/${nick}`;
|
||||
api.user.presence.send('probe', jid);
|
||||
this.occupant = this.chatbox.occupants.findOccupant({ nick, occupant_id });
|
||||
|
||||
if (!this.occupant) {
|
||||
this.occupant = this.chatbox.occupants.create({
|
||||
nick,
|
||||
occupant_id,
|
||||
jid: this.get('from_real_jid'),
|
||||
});
|
||||
|
||||
if (api.settings.get('muc_send_probes')) {
|
||||
const jid = `${this.chatbox.get('jid')}/${nick}`;
|
||||
api.user.presence.send('probe', jid);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.occupant) {
|
||||
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
||||
} else {
|
||||
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
|
||||
}
|
||||
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import debounce from 'lodash-es/debounce';
|
||||
import invoke from 'lodash-es/invoke';
|
||||
import isElement from 'lodash-es/isElement';
|
||||
import log from '../../log';
|
||||
import p from '../../utils/parse-helpers';
|
||||
import pick from 'lodash-es/pick';
|
||||
import sizzle from 'sizzle';
|
||||
import u from '../../utils/form';
|
||||
import zipObject from 'lodash-es/zipObject';
|
||||
import { Model } from '@converse/skeletor/src/model.js';
|
||||
import { ROOMSTATUS } from './constants.js';
|
||||
import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
|
||||
import { TimeoutError } from '../../shared/errors.js';
|
||||
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/utils/storage.js';
|
||||
import { isArchived, getMediaURLsMetadata } from '@converse/headless/shared/parsers';
|
||||
import { isUniView, getUniqueId, safeSave } from '@converse/headless/utils/core.js';
|
||||
import { handleCorrection } from '../../shared/chat/utils.js';
|
||||
import { initStorage } from '../../utils/storage.js';
|
||||
import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js';
|
||||
import { isUniView, getUniqueId, safeSave } from '../../utils/core.js';
|
||||
import { parseMUCMessage, parseMUCPresence } from './parsers.js';
|
||||
import { sendMarker } from '@converse/headless/shared/actions';
|
||||
import { sendMarker } from '../../shared/actions.js';
|
||||
|
||||
const { u } = converse.env;
|
||||
|
||||
const OWNER_COMMANDS = ['owner'];
|
||||
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
|
||||
|
@ -47,7 +49,7 @@ const ACTION_INFO_CODES = ['301', '303', '333', '307', '321', '322'];
|
|||
const MUCSession = Model.extend({
|
||||
defaults () {
|
||||
return {
|
||||
'connection_status': converse.ROOMSTATUS.DISCONNECTED
|
||||
'connection_status': ROOMSTATUS.DISCONNECTED
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -125,7 +127,16 @@ const ChatRoomMixin = {
|
|||
},
|
||||
|
||||
isEntered () {
|
||||
return this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED;
|
||||
return this.session.get('connection_status') === ROOMSTATUS.ENTERED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether this MUC qualifies for subscribing to XEP-0437 Room Activity Indicators (RAI)
|
||||
* @method _converse.ChatRoom#isRAICandidate
|
||||
* @returns { Boolean }
|
||||
*/
|
||||
isRAICandidate () {
|
||||
return this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none';
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -135,18 +146,23 @@ const ChatRoomMixin = {
|
|||
* @returns { Boolean } Returns `true` if we're still joined, otherwise returns `false`.
|
||||
*/
|
||||
async restoreFromCache () {
|
||||
if (this.isEntered() && (await this.isJoined())) {
|
||||
// We've restored the room from cache and we're still joined.
|
||||
await new Promise(r => this.features.fetch({ 'success': r, 'error': r }));
|
||||
await new Promise(r => this.config.fetch({ 'success': r, 'error': r }));
|
||||
if (this.isEntered()) {
|
||||
await this.fetchOccupants().catch(e => log.error(e));
|
||||
await this.fetchMessages().catch(e => log.error(e));
|
||||
return true;
|
||||
} else {
|
||||
this.session.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
|
||||
this.clearOccupantsCache();
|
||||
return false;
|
||||
|
||||
if (this.isRAICandidate()) {
|
||||
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
|
||||
this.enableRAI();
|
||||
return true;
|
||||
} else if (await this.isJoined()) {
|
||||
await new Promise(r => this.config.fetch({ 'success': r, 'error': r }));
|
||||
await new Promise(r => this.features.fetch({ 'success': r, 'error': r }));
|
||||
await this.fetchMessages().catch(e => log.error(e));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
|
||||
this.clearOccupantsCache();
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -165,11 +181,11 @@ const ChatRoomMixin = {
|
|||
return this;
|
||||
}
|
||||
// Set this early, so we don't rejoin in onHiddenChange
|
||||
this.session.save('connection_status', converse.ROOMSTATUS.CONNECTING);
|
||||
this.session.save('connection_status', ROOMSTATUS.CONNECTING);
|
||||
await this.refreshDiscoInfo();
|
||||
nick = await this.getAndPersistNickname(nick);
|
||||
if (!nick) {
|
||||
safeSave(this.session, { 'connection_status': converse.ROOMSTATUS.NICKNAME_REQUIRED });
|
||||
safeSave(this.session, { 'connection_status': ROOMSTATUS.NICKNAME_REQUIRED });
|
||||
if (api.settings.get('muc_show_logs_before_join')) {
|
||||
await this.fetchMessages();
|
||||
}
|
||||
|
@ -185,7 +201,7 @@ const ChatRoomMixin = {
|
|||
* @method _converse.ChatRoom#rejoin
|
||||
*/
|
||||
rejoin () {
|
||||
this.session.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
|
||||
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
|
||||
this.registerHandlers();
|
||||
this.clearOccupantsCache();
|
||||
return this.join();
|
||||
|
@ -210,7 +226,7 @@ const ChatRoomMixin = {
|
|||
* *Hook* which allows plugins to update an outgoing MUC join presence stanza
|
||||
* @event _converse#constructedMUCPresence
|
||||
* @param { _converse.ChatRoom } - The MUC from which this message stanza is being sent.
|
||||
* @param { XMLElement } stanza - The stanza which will be sent out
|
||||
* @param { Element } stanza - The stanza which will be sent out
|
||||
*/
|
||||
stanza = await api.hook('constructedMUCPresence', this, stanza);
|
||||
return stanza;
|
||||
|
@ -272,13 +288,10 @@ const ChatRoomMixin = {
|
|||
* @method _converse.ChatRoom#onHiddenChange
|
||||
*/
|
||||
async onHiddenChange () {
|
||||
const roomstatus = converse.ROOMSTATUS;
|
||||
const roomstatus = ROOMSTATUS;
|
||||
const conn_status = this.session.get('connection_status');
|
||||
if (this.get('hidden')) {
|
||||
if (conn_status === roomstatus.ENTERED &&
|
||||
api.settings.get('muc_subscribe_to_rai') &&
|
||||
this.getOwnAffiliation() !== 'none') {
|
||||
|
||||
if (conn_status === roomstatus.ENTERED && this.isRAICandidate()) {
|
||||
this.sendMarkerForLastMessage('received', true);
|
||||
await this.leave();
|
||||
this.enableRAI();
|
||||
|
@ -294,7 +307,7 @@ const ChatRoomMixin = {
|
|||
onOccupantAdded (occupant) {
|
||||
if (
|
||||
_converse.isInfoVisible(converse.MUC_TRAFFIC_STATES.ENTERED) &&
|
||||
this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED &&
|
||||
this.session.get('connection_status') === ROOMSTATUS.ENTERED &&
|
||||
occupant.get('show') === 'online'
|
||||
) {
|
||||
this.updateNotifications(occupant.get('nick'), converse.MUC_TRAFFIC_STATES.ENTERED);
|
||||
|
@ -346,7 +359,7 @@ const ChatRoomMixin = {
|
|||
|
||||
async onConnectionStatusChanged () {
|
||||
if (this.isEntered()) {
|
||||
if (this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
|
||||
if (this.isRAICandidate()) {
|
||||
try {
|
||||
await this.leave();
|
||||
} catch (e) {
|
||||
|
@ -380,10 +393,10 @@ const ChatRoomMixin = {
|
|||
this.features = new Model(
|
||||
Object.assign(
|
||||
{ id },
|
||||
zipObject(
|
||||
converse.ROOM_FEATURES,
|
||||
converse.ROOM_FEATURES.map(() => false)
|
||||
)
|
||||
converse.ROOM_FEATURES.reduce((acc, feature) => {
|
||||
acc[feature] = false;
|
||||
return acc;
|
||||
}, {})
|
||||
)
|
||||
);
|
||||
this.features.browserStorage = _converse.createStore(id, 'session');
|
||||
|
@ -443,7 +456,7 @@ const ChatRoomMixin = {
|
|||
|
||||
async handleErrorMessageStanza (stanza) {
|
||||
const { __ } = _converse;
|
||||
const attrs = await parseMUCMessage(stanza, this, _converse);
|
||||
const attrs = await parseMUCMessage(stanza, this);
|
||||
if (!(await this.shouldShowErrorMessage(attrs))) {
|
||||
return;
|
||||
}
|
||||
|
@ -494,7 +507,7 @@ const ChatRoomMixin = {
|
|||
* Handles incoming message stanzas from the service that hosts this MUC
|
||||
* @private
|
||||
* @method _converse.ChatRoom#handleMessageFromMUCHost
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
handleMessageFromMUCHost (stanza) {
|
||||
if (this.isEntered()) {
|
||||
|
@ -515,7 +528,7 @@ const ChatRoomMixin = {
|
|||
* Handles XEP-0452 MUC Mention Notification messages
|
||||
* @private
|
||||
* @method _converse.ChatRoom#handleForwardedMentions
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
handleForwardedMentions (stanza) {
|
||||
if (this.isEntered()) {
|
||||
|
@ -534,7 +547,7 @@ const ChatRoomMixin = {
|
|||
'num_unread': this.get('num_unread') + mentions.length
|
||||
});
|
||||
mentions.forEach(async stanza => {
|
||||
const attrs = await parseMUCMessage(stanza, this, _converse);
|
||||
const attrs = await parseMUCMessage(stanza, this);
|
||||
const data = { stanza, attrs, 'chatbox': this };
|
||||
api.trigger('message', data);
|
||||
});
|
||||
|
@ -545,9 +558,11 @@ const ChatRoomMixin = {
|
|||
* Parses an incoming message stanza and queues it for processing.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#handleMessageStanza
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
async handleMessageStanza (stanza) {
|
||||
stanza = stanza.tree?.() ?? stanza;
|
||||
|
||||
const type = stanza.getAttribute('type');
|
||||
if (type === 'error') {
|
||||
return this.handleErrorMessageStanza(stanza);
|
||||
|
@ -573,7 +588,7 @@ const ChatRoomMixin = {
|
|||
*/
|
||||
let attrs;
|
||||
try {
|
||||
attrs = await parseMUCMessage(stanza, this, _converse);
|
||||
attrs = await parseMUCMessage(stanza, this);
|
||||
} catch (e) {
|
||||
return log.error(e);
|
||||
}
|
||||
|
@ -694,9 +709,9 @@ const ChatRoomMixin = {
|
|||
* or error message within a specific timeout period.
|
||||
* @private
|
||||
* @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 with an error stanza or {@link _converse.TimeoutError}.
|
||||
* @param { _converse.Message|Element } message
|
||||
* @returns { Promise<Element>|Promise<TimeoutError> } Returns a promise
|
||||
* which resolves with the reflected message stanza or with an error stanza or {@link TimeoutError}.
|
||||
*/
|
||||
sendTimedMessage (el) {
|
||||
if (typeof el.tree === 'function') {
|
||||
|
@ -709,9 +724,10 @@ const ChatRoomMixin = {
|
|||
el.setAttribute('id', id);
|
||||
}
|
||||
const promise = getOpenPromise();
|
||||
const timeoutHandler = _converse.connection.addTimedHandler(_converse.STANZA_TIMEOUT, () => {
|
||||
const timeout = api.settings.get('stanza_timeout');
|
||||
const timeoutHandler = _converse.connection.addTimedHandler(timeout, () => {
|
||||
_converse.connection.deleteHandler(handler);
|
||||
const err = new _converse.TimeoutError('Timeout Error: No response from server');
|
||||
const err = new TimeoutError('Timeout Error: No response from server');
|
||||
promise.resolve(err);
|
||||
return false;
|
||||
});
|
||||
|
@ -754,14 +770,14 @@ const ChatRoomMixin = {
|
|||
message.set({
|
||||
'retracted': new Date().toISOString(),
|
||||
'retracted_id': origin_id,
|
||||
'retraction_id': stanza.nodeTree.getAttribute('id'),
|
||||
'retraction_id': stanza.tree().getAttribute('id'),
|
||||
'editable': false
|
||||
});
|
||||
const result = await this.sendTimedMessage(stanza);
|
||||
|
||||
if (u.isErrorStanza(result)) {
|
||||
log.error(result);
|
||||
} else if (result instanceof _converse.TimeoutError) {
|
||||
} else if (result instanceof TimeoutError) {
|
||||
log.error(result);
|
||||
message.save({
|
||||
editable,
|
||||
|
@ -778,7 +794,7 @@ const ChatRoomMixin = {
|
|||
* Retract someone else's message in this groupchat.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#retractOtherMessage
|
||||
* @param { _converse.Message } message - The message which we're retracting.
|
||||
* @param { _converse.ChatRoomMessage } message - The message which we're retracting.
|
||||
* @param { string } [reason] - The reason for retracting the message.
|
||||
* @example
|
||||
* const room = await api.rooms.get(jid);
|
||||
|
@ -813,7 +829,7 @@ const ChatRoomMixin = {
|
|||
* Sends an IQ stanza to the XMPP server to retract a message in this groupchat.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#sendRetractionIQ
|
||||
* @param { _converse.Message } message - The message which we're retracting.
|
||||
* @param { _converse.ChatRoomMessage } message - The message which we're retracting.
|
||||
* @param { string } [reason] - The reason for retracting the message.
|
||||
*/
|
||||
sendRetractionIQ (message, reason) {
|
||||
|
@ -882,12 +898,15 @@ const ChatRoomMixin = {
|
|||
'error': (_, e) => { log.error(e); resolve(); }
|
||||
}));
|
||||
}
|
||||
safeSave(this.session, { 'connection_status': converse.ROOMSTATUS.DISCONNECTED });
|
||||
safeSave(this.session, { 'connection_status': ROOMSTATUS.DISCONNECTED });
|
||||
},
|
||||
|
||||
async close (ev) {
|
||||
safeSave(this.session, { 'connection_status': converse.ROOMSTATUS.CLOSING });
|
||||
this.sendMarkerForLastMessage('received', true);
|
||||
const { ENTERED, CLOSING } = ROOMSTATUS;
|
||||
const was_entered = this.session.get('connection_status') === ENTERED;
|
||||
|
||||
safeSave(this.session, { 'connection_status': CLOSING });
|
||||
was_entered && this.sendMarkerForLastMessage('received', true);
|
||||
await this.unregisterNickname();
|
||||
await this.leave();
|
||||
|
||||
|
@ -938,6 +957,13 @@ const ChatRoomMixin = {
|
|||
return this.occupants.findOccupant({ nick });
|
||||
},
|
||||
|
||||
getReferenceURIFromNickname (nickname) {
|
||||
const muc_jid = this.get('jid');
|
||||
const occupant = this.getOccupant(nickname);
|
||||
const uri = (this.features.get('nonanonymous') && occupant?.get('jid')) || `${muc_jid}/${nickname}`;
|
||||
return encodeURI(`xmpp:${uri}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a text message, look for `@` mentions and turn them into
|
||||
* XEP-0372 references
|
||||
|
@ -951,13 +977,6 @@ const ChatRoomMixin = {
|
|||
|
||||
const getMatchingNickname = p.findFirstMatchInArray(this.getAllKnownNicknames());
|
||||
|
||||
const uriFromNickname = nickname => {
|
||||
const jid = this.get('jid');
|
||||
const occupant = this.getOccupant(nickname) || this.getOccupant(jid);
|
||||
const uri = (this.features.get('nonanonymous') && occupant?.get('jid')) || `${jid}/${nickname}`;
|
||||
return encodeURI(`xmpp:${uri}`);
|
||||
};
|
||||
|
||||
const matchToReference = match => {
|
||||
let at_sign_index = match[0].indexOf('@');
|
||||
if (match[0][at_sign_index + 1] === '@') {
|
||||
|
@ -968,7 +987,7 @@ const ChatRoomMixin = {
|
|||
const end = begin + match[0].length - at_sign_index;
|
||||
const value = getMatchingNickname(match[1]);
|
||||
const type = 'mention';
|
||||
const uri = uriFromNickname(value);
|
||||
const uri = this.getReferenceURIFromNickname(value);
|
||||
return { begin, end, value, type, uri };
|
||||
};
|
||||
|
||||
|
@ -981,13 +1000,14 @@ const ChatRoomMixin = {
|
|||
},
|
||||
|
||||
async getOutgoingMessageAttributes (attrs) {
|
||||
await api.emojis.initialize();
|
||||
const is_spoiler = this.get('composing_spoiler');
|
||||
let text = '', references;
|
||||
if (attrs?.body) {
|
||||
[text, references] = this.parseTextForReferences(attrs.body);
|
||||
}
|
||||
const origin_id = getUniqueId();
|
||||
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
||||
const body = text ? u.shortnamesToUnicode(text) : undefined;
|
||||
attrs = Object.assign({}, attrs, {
|
||||
body,
|
||||
is_spoiler,
|
||||
|
@ -1167,13 +1187,12 @@ const ChatRoomMixin = {
|
|||
*/
|
||||
async getDiscoInfoFeatures () {
|
||||
const features = await api.disco.getFeatures(this.get('jid'));
|
||||
const attrs = Object.assign(
|
||||
zipObject(
|
||||
converse.ROOM_FEATURES,
|
||||
converse.ROOM_FEATURES.map(() => false)
|
||||
),
|
||||
{ 'fetched': new Date().toISOString() }
|
||||
);
|
||||
|
||||
const attrs = converse.ROOM_FEATURES.reduce((acc, feature) => {
|
||||
acc[feature] = false;
|
||||
return acc;
|
||||
}, { 'fetched': new Date().toISOString() });
|
||||
|
||||
features.each(feature => {
|
||||
const fieldname = feature.get('var');
|
||||
if (!fieldname.startsWith('muc_')) {
|
||||
|
@ -1225,7 +1244,7 @@ const ChatRoomMixin = {
|
|||
* 'roomconfig' data.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#autoConfigureChatRoom
|
||||
* @returns { Promise<XMLElement> }
|
||||
* @returns { Promise<Element> }
|
||||
* Returns a promise which resolves once a response IQ has
|
||||
* been received.
|
||||
*/
|
||||
|
@ -1244,7 +1263,7 @@ const ChatRoomMixin = {
|
|||
* has been received.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#fetchRoomConfiguration
|
||||
* @returns { Promise<XMLElement> }
|
||||
* @returns { Promise<Element> }
|
||||
*/
|
||||
fetchRoomConfiguration () {
|
||||
return api.sendIQ($iq({ 'to': this.get('jid'), 'type': 'get' }).c('query', { xmlns: Strophe.NS.MUC_OWNER }));
|
||||
|
@ -1255,7 +1274,7 @@ const ChatRoomMixin = {
|
|||
* @private
|
||||
* @method _converse.ChatRoom#sendConfiguration
|
||||
* @param { Array } config - The groupchat configuration
|
||||
* @returns { Promise<XMLElement> } - A promise which resolves with
|
||||
* @returns { Promise<Element> } - A promise which resolves with
|
||||
* the `result` stanza received from the XMPP server.
|
||||
*/
|
||||
sendConfiguration (config = []) {
|
||||
|
@ -1284,7 +1303,7 @@ const ChatRoomMixin = {
|
|||
if (!args.startsWith('@')) {
|
||||
args = '@' + args;
|
||||
}
|
||||
const [text, references] = this.parseTextForReferences(args); // eslint-disable-line no-unused-vars
|
||||
const [_text, references] = this.parseTextForReferences(args); // eslint-disable-line no-unused-vars
|
||||
if (!references.length) {
|
||||
const message = __("Error: couldn't find a groupchat participant based on your arguments");
|
||||
this.createMessage({ message, 'type': 'error' });
|
||||
|
@ -1697,7 +1716,7 @@ const ChatRoomMixin = {
|
|||
* Given a presence stanza, update the occupant model based on its contents.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#updateOccupantsOnPresence
|
||||
* @param { XMLElement } pres - The presence stanza
|
||||
* @param { Element } pres - The presence stanza
|
||||
*/
|
||||
updateOccupantsOnPresence (pres) {
|
||||
const data = parseMUCPresence(pres, this);
|
||||
|
@ -1726,6 +1745,19 @@ const ChatRoomMixin = {
|
|||
'resource': Strophe.getResourceFromJid(jid) || occupant?.attributes?.resource
|
||||
}
|
||||
|
||||
if (data.is_me) {
|
||||
let modified = false;
|
||||
if (data.states.includes(converse.MUC_NICK_CHANGED_CODE)) {
|
||||
modified = true;
|
||||
this.set('nick', data.nick);
|
||||
}
|
||||
if (this.features.get(Strophe.NS.OCCUPANTID) && this.get('occupant-id') !== data.occupant_id) {
|
||||
modified = true;
|
||||
this.set('occupant_id', data.occupant_id);
|
||||
}
|
||||
modified && this.save();
|
||||
}
|
||||
|
||||
if (occupant) {
|
||||
occupant.save(attributes);
|
||||
} else {
|
||||
|
@ -1871,7 +1903,7 @@ const ChatRoomMixin = {
|
|||
* the `from` attribute. Doesn't check the `type` attribute.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#isOwnMessage
|
||||
* @param { Object|XMLElement|_converse.Message } msg
|
||||
* @param { Object|Element|_converse.Message } msg
|
||||
* @returns { boolean }
|
||||
*/
|
||||
isOwnMessage (msg) {
|
||||
|
@ -1887,8 +1919,10 @@ const ChatRoomMixin = {
|
|||
},
|
||||
|
||||
getUpdatedMessageAttributes (message, attrs) {
|
||||
const new_attrs = _converse.ChatBox.prototype.getUpdatedMessageAttributes.call(this, message, attrs);
|
||||
new_attrs['from_muc'] = attrs['from_muc'];
|
||||
const new_attrs = {
|
||||
..._converse.ChatBox.prototype.getUpdatedMessageAttributes.call(this, message, attrs),
|
||||
...pick(attrs, ['from_muc', 'occupant_id']),
|
||||
}
|
||||
|
||||
if (this.isOwnMessage(attrs)) {
|
||||
const stanza_id_keys = Object.keys(attrs).filter(k => k.startsWith('stanza_id'));
|
||||
|
@ -1909,22 +1943,14 @@ const ChatRoomMixin = {
|
|||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async isJoined () {
|
||||
const jid = this.get('jid');
|
||||
const ping = $iq({
|
||||
'to': `${jid}/${this.get('nick')}`,
|
||||
'type': 'get'
|
||||
}).c('ping', { 'xmlns': Strophe.NS.PING });
|
||||
try {
|
||||
await api.sendIQ(ping);
|
||||
} catch (e) {
|
||||
if (e === null) {
|
||||
log.warn(`isJoined: Timeout error while checking whether we're joined to MUC: ${jid}`);
|
||||
} else {
|
||||
log.warn(`isJoined: Apparently we're no longer connected to MUC: ${jid}`);
|
||||
}
|
||||
if (!this.isEntered()) {
|
||||
log.info(`isJoined: not pinging MUC ${this.get('jid')} since we're not entered`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (!api.connection.connected()) {
|
||||
await new Promise(resolve => api.listen.once('reconnected', resolve));
|
||||
}
|
||||
return api.ping(`${this.get('jid')}/${this.get('nick')}`)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1936,7 +1962,7 @@ const ChatRoomMixin = {
|
|||
* Nodes(s) to be added as child nodes of the `presence` XML element.
|
||||
*/
|
||||
async sendStatusPresence (type, status, child_nodes) {
|
||||
if (this.session.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
|
||||
if (this.session.get('connection_status') === ROOMSTATUS.ENTERED) {
|
||||
const presence = await _converse.xmppstatus.constructPresence(type, this.getRoomJIDAndNick(), status);
|
||||
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
|
||||
api.send(presence);
|
||||
|
@ -1945,10 +1971,14 @@ const ChatRoomMixin = {
|
|||
|
||||
/**
|
||||
* Check whether we're still joined and re-join if not
|
||||
* @async
|
||||
* @method _converse.ChatRoom#rejoinIfNecessary
|
||||
*/
|
||||
async rejoinIfNecessary () {
|
||||
if (this.isRAICandidate()) {
|
||||
log.debug(`rejoinIfNecessary: not rejoining hidden MUC "${this.get('jid')}" since we're using RAI`);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(await this.isJoined())) {
|
||||
this.rejoin();
|
||||
return true;
|
||||
|
@ -2121,7 +2151,7 @@ const ChatRoomMixin = {
|
|||
},
|
||||
|
||||
/**
|
||||
* @param {String} actor - The nickname of the actor that caused the notification
|
||||
* @param { String } actor - The nickname of the actor that caused the notification
|
||||
* @param {String|Array<String>} states - The state or states representing the type of notificcation
|
||||
*/
|
||||
removeNotification (actor, states) {
|
||||
|
@ -2145,8 +2175,8 @@ const ChatRoomMixin = {
|
|||
*
|
||||
* The state can be a XEP-0085 Chat State or a XEP-0045 join/leave
|
||||
* state.
|
||||
* @param {String} actor - The nickname of the actor that causes the notification
|
||||
* @param {String} state - The state representing the type of notificcation
|
||||
* @param { String } actor - The nickname of the actor that causes the notification
|
||||
* @param { String } state - The state representing the type of notificcation
|
||||
*/
|
||||
updateNotifications (actor, state) {
|
||||
const actors_per_state = this.notifications.toJSON();
|
||||
|
@ -2194,7 +2224,7 @@ const ChatRoomMixin = {
|
|||
/**
|
||||
* Given {@link MessageAttributes} look for XEP-0316 Room Notifications and create info
|
||||
* messages for them.
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
handleMEPNotification (attrs) {
|
||||
if (attrs.from !== this.get('jid') || !attrs.activities) {
|
||||
|
@ -2267,7 +2297,7 @@ const ChatRoomMixin = {
|
|||
this.updateNotifications(attrs.nick, attrs.chat_state);
|
||||
}
|
||||
if (u.shouldCreateGroupchatMessage(attrs)) {
|
||||
const msg = this.handleCorrection(attrs) || (await this.createMessage(attrs));
|
||||
const msg = await handleCorrection(this, attrs) || (await this.createMessage(attrs));
|
||||
this.removeNotification(attrs.nick, ['composing', 'paused']);
|
||||
this.handleUnreadMessage(msg);
|
||||
}
|
||||
|
@ -2276,7 +2306,7 @@ const ChatRoomMixin = {
|
|||
handleModifyError (pres) {
|
||||
const text = pres.querySelector('error text')?.textContent;
|
||||
if (text) {
|
||||
if (this.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING) {
|
||||
if (this.session.get('connection_status') === ROOMSTATUS.CONNECTING) {
|
||||
this.setDisconnectionState(text);
|
||||
} else {
|
||||
const attrs = {
|
||||
|
@ -2291,7 +2321,7 @@ const ChatRoomMixin = {
|
|||
|
||||
/**
|
||||
* Handle a presence stanza that disconnects the user from the MUC
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
handleDisconnection (stanza) {
|
||||
const is_self = stanza.querySelector("status[code='110']") !== null;
|
||||
|
@ -2313,9 +2343,9 @@ const ChatRoomMixin = {
|
|||
// each <x/> element pertains to a single user.
|
||||
const item = x.querySelector('item');
|
||||
const reason = item ? item.querySelector('reason')?.textContent : undefined;
|
||||
const actor = item ? invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
|
||||
const actor = item ? item.querySelector('actor')?.getAttribute('nick') : undefined;
|
||||
const message = _converse.muc.disconnect_messages[codes[0]];
|
||||
const status = codes.includes('301') ? converse.ROOMSTATUS.BANNED : converse.ROOMSTATUS.DISCONNECTED;
|
||||
const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
|
||||
this.setDisconnectionState(message, reason, actor, status);
|
||||
},
|
||||
|
||||
|
@ -2426,7 +2456,7 @@ const ChatRoomMixin = {
|
|||
* @private
|
||||
* @method _converse.ChatRoom#createInfoMessage
|
||||
* @param { string } code - The MUC status code
|
||||
* @param { XMLElement } stanza - The original stanza that contains the code
|
||||
* @param { Element } stanza - The original stanza that contains the code
|
||||
* @param { Boolean } is_self - Whether this stanza refers to our own presence
|
||||
*/
|
||||
createInfoMessage (code, stanza, is_self) {
|
||||
|
@ -2448,14 +2478,15 @@ const ChatRoomMixin = {
|
|||
} else if (is_self && code in _converse.muc.new_nickname_messages) {
|
||||
// XXX: Side-effect of setting the nick. Should ideally be refactored out of this method
|
||||
let nick;
|
||||
if (is_self && code === '210') {
|
||||
if (code === '210') {
|
||||
nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
|
||||
} else if (is_self && code === '303') {
|
||||
} else if (code === '303') {
|
||||
nick = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop().getAttribute('nick');
|
||||
}
|
||||
this.save('nick', nick);
|
||||
data.message = __(_converse.muc.new_nickname_messages[code], nick);
|
||||
}
|
||||
|
||||
if (data.message) {
|
||||
if (code === '201' && this.messages.findWhere(data)) {
|
||||
return;
|
||||
|
@ -2468,7 +2499,7 @@ const ChatRoomMixin = {
|
|||
* Create info messages based on a received presence or message stanza
|
||||
* @private
|
||||
* @method _converse.ChatRoom#createInfoMessages
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
createInfoMessages (stanza) {
|
||||
const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
|
||||
|
@ -2487,9 +2518,9 @@ const ChatRoomMixin = {
|
|||
* implied by) the server.
|
||||
* @param { String } reason - The reason provided for the disconnection
|
||||
* @param { String } actor - The person (if any) responsible for this disconnection
|
||||
* @param { Integer } status - The status code (see `converse.ROOMSTATUS`)
|
||||
* @param { number } status - The status code (see `ROOMSTATUS`)
|
||||
*/
|
||||
setDisconnectionState (message, reason, actor, status=converse.ROOMSTATUS.DISCONNECTED) {
|
||||
setDisconnectionState (message, reason, actor, status=ROOMSTATUS.DISCONNECTED) {
|
||||
this.session.save({
|
||||
'connection_status': status,
|
||||
'disconnection_actor': actor,
|
||||
|
@ -2515,7 +2546,7 @@ const ChatRoomMixin = {
|
|||
'The nickname you chose is reserved or ' + 'currently in use, please choose a different one.'
|
||||
)
|
||||
});
|
||||
this.session.save({ 'connection_status': converse.ROOMSTATUS.NICKNAME_REQUIRED });
|
||||
this.session.save({ 'connection_status': ROOMSTATUS.NICKNAME_REQUIRED });
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2524,7 +2555,7 @@ const ChatRoomMixin = {
|
|||
* `connection_status` value for this {@link _converse.ChatRoom} as
|
||||
* well as any additional output that can be shown to the user.
|
||||
* @private
|
||||
* @param { XMLElement } stanza - The presence stanza
|
||||
* @param { Element } stanza - The presence stanza
|
||||
*/
|
||||
onErrorPresence (stanza) {
|
||||
const __ = _converse.__;
|
||||
|
@ -2537,7 +2568,7 @@ const ChatRoomMixin = {
|
|||
} else if (error_type === 'auth') {
|
||||
if (sizzle(`not-authorized[xmlns="${Strophe.NS.STANZAS}"]`, error).length) {
|
||||
this.save({ 'password_validation_message': reason || __('Password incorrect') });
|
||||
this.session.save({ 'connection_status': converse.ROOMSTATUS.PASSWORD_REQUIRED });
|
||||
this.session.save({ 'connection_status': ROOMSTATUS.PASSWORD_REQUIRED });
|
||||
}
|
||||
if (error.querySelector('registration-required')) {
|
||||
const message = __('You are not on the member list of this groupchat.');
|
||||
|
@ -2547,7 +2578,7 @@ const ChatRoomMixin = {
|
|||
_converse.muc.disconnect_messages[301],
|
||||
reason,
|
||||
null,
|
||||
converse.ROOMSTATUS.BANNED
|
||||
ROOMSTATUS.BANNED
|
||||
);
|
||||
}
|
||||
} else if (error_type === 'cancel') {
|
||||
|
@ -2563,7 +2594,7 @@ const ChatRoomMixin = {
|
|||
?.textContent.replace(/^xmpp:/, '')
|
||||
.replace(/\?join$/, '');
|
||||
this.save({ moved_jid, 'destroyed_reason': reason });
|
||||
this.session.save({ 'connection_status': converse.ROOMSTATUS.DESTROYED });
|
||||
this.session.save({ 'connection_status': ROOMSTATUS.DESTROYED });
|
||||
} else if (error.querySelector('conflict')) {
|
||||
this.onNicknameClash(stanza);
|
||||
} else if (error.querySelector('item-not-found')) {
|
||||
|
@ -2589,7 +2620,7 @@ const ChatRoomMixin = {
|
|||
* Listens for incoming presence stanzas from the service that hosts this MUC
|
||||
* @private
|
||||
* @method _converse.ChatRoom#onPresenceFromMUCHost
|
||||
* @param { XMLElement } stanza - The presence stanza
|
||||
* @param { Element } stanza - The presence stanza
|
||||
*/
|
||||
onPresenceFromMUCHost (stanza) {
|
||||
if (stanza.getAttribute('type') === 'error') {
|
||||
|
@ -2597,7 +2628,7 @@ const ChatRoomMixin = {
|
|||
if (error?.getAttribute('type') === 'wait' && error?.querySelector('resource-constraint')) {
|
||||
// If we get a <resource-constraint> error, we assume it's in context of XEP-0437 RAI.
|
||||
// We remove this MUC's host from the list of enabled domains and rejoin the MUC.
|
||||
if (this.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
|
||||
if (this.session.get('connection_status') === ROOMSTATUS.DISCONNECTED) {
|
||||
this.rejoin();
|
||||
}
|
||||
}
|
||||
|
@ -2608,7 +2639,7 @@ const ChatRoomMixin = {
|
|||
* Handles incoming presence stanzas coming from the MUC
|
||||
* @private
|
||||
* @method _converse.ChatRoom#onPresence
|
||||
* @param { XMLElement } stanza
|
||||
* @param { Element } stanza
|
||||
*/
|
||||
onPresence (stanza) {
|
||||
if (stanza.getAttribute('type') === 'error') {
|
||||
|
@ -2619,9 +2650,9 @@ const ChatRoomMixin = {
|
|||
this.onOwnPresence(stanza);
|
||||
if (
|
||||
this.getOwnRole() !== 'none' &&
|
||||
this.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING
|
||||
this.session.get('connection_status') === ROOMSTATUS.CONNECTING
|
||||
) {
|
||||
this.session.save('connection_status', converse.ROOMSTATUS.CONNECTED);
|
||||
this.session.save('connection_status', ROOMSTATUS.CONNECTED);
|
||||
}
|
||||
} else {
|
||||
this.updateOccupantsOnPresence(stanza);
|
||||
|
@ -2641,51 +2672,39 @@ const ChatRoomMixin = {
|
|||
* user is the groupchat's owner.
|
||||
* @private
|
||||
* @method _converse.ChatRoom#onOwnPresence
|
||||
* @param { XMLElement } pres - The stanza
|
||||
* @param { Element } pres - The stanza
|
||||
*/
|
||||
async onOwnPresence (stanza) {
|
||||
await this.occupants.fetched;
|
||||
|
||||
if (stanza.getAttribute('type') === 'unavailable') {
|
||||
this.handleDisconnection(stanza);
|
||||
return;
|
||||
}
|
||||
|
||||
const old_status = this.session.get('connection_status');
|
||||
if (stanza.getAttribute('type') !== 'unavailable' &&
|
||||
old_status !== converse.ROOMSTATUS.ENTERED &&
|
||||
old_status !== converse.ROOMSTATUS.CLOSING
|
||||
if (old_status !== ROOMSTATUS.ENTERED &&
|
||||
old_status !== ROOMSTATUS.CLOSING
|
||||
) {
|
||||
// Set connection_status before creating the occupant, but
|
||||
// only trigger afterwards, so that plugins can access the
|
||||
// occupant in their event handlers.
|
||||
this.session.save('connection_status', converse.ROOMSTATUS.ENTERED, { 'silent': true });
|
||||
this.session.save('connection_status', ROOMSTATUS.ENTERED, { 'silent': true });
|
||||
this.updateOccupantsOnPresence(stanza);
|
||||
this.session.trigger('change:connection_status', this.session, old_status);
|
||||
} else {
|
||||
this.updateOccupantsOnPresence(stanza);
|
||||
}
|
||||
|
||||
if (stanza.getAttribute('type') === 'unavailable') {
|
||||
this.handleDisconnection(stanza);
|
||||
return;
|
||||
} else {
|
||||
const locked_room = stanza.querySelector("status[code='201']");
|
||||
if (locked_room) {
|
||||
if (this.get('auto_configure')) {
|
||||
this.autoConfigureChatRoom().then(() => this.refreshDiscoInfo());
|
||||
} else if (api.settings.get('muc_instant_rooms')) {
|
||||
// Accept default configuration
|
||||
this.sendConfiguration().then(() => this.refreshDiscoInfo());
|
||||
} else {
|
||||
this.session.save({ 'view': converse.MUC.VIEWS.CONFIG });
|
||||
return;
|
||||
}
|
||||
} else if (!this.features.get('fetched')) {
|
||||
// The features for this groupchat weren't fetched.
|
||||
// That must mean it's a new groupchat without locking
|
||||
// (in which case Prosody doesn't send a 201 status),
|
||||
// otherwise the features would have been fetched in
|
||||
// the "initialize" method already.
|
||||
if (this.getOwnAffiliation() === 'owner' && this.get('auto_configure')) {
|
||||
this.autoConfigureChatRoom().then(() => this.refreshDiscoInfo());
|
||||
} else {
|
||||
this.getDiscoInfo();
|
||||
}
|
||||
const locked_room = stanza.querySelector("status[code='201']");
|
||||
if (locked_room) {
|
||||
if (this.get('auto_configure')) {
|
||||
await this.autoConfigureChatRoom().then(() => this.refreshDiscoInfo());
|
||||
} else if (api.settings.get('muc_instant_rooms')) {
|
||||
// Accept default configuration
|
||||
await this.sendConfiguration().then(() => this.refreshDiscoInfo());
|
||||
} else {
|
||||
this.session.save({ 'view': converse.MUC.VIEWS.CONFIG });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -10,9 +10,9 @@ class ChatRoomOccupant extends Model {
|
|||
|
||||
defaults () { // eslint-disable-line class-methods-use-this
|
||||
return {
|
||||
'hats': [],
|
||||
'show': 'offline',
|
||||
'states': []
|
||||
hats: [],
|
||||
show: 'offline',
|
||||
states: []
|
||||
}
|
||||
}
|
||||
|
||||
|
|