Compare commits
No commits in common. "main-chapril-rebranding" and "v10.0.0" have entirely different histories.
main-chapr
...
v10.0.0
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@babel/eslint-parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2020,
|
"ecmaVersion": 2017,
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"allowImportExportEverywhere": true
|
"allowImportExportEverywhere": true
|
||||||
},
|
},
|
||||||
@ -10,12 +10,8 @@
|
|||||||
"jasmine": true,
|
"jasmine": true,
|
||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"plugins": ["@typescript-eslint"],
|
"plugins": [],
|
||||||
"extends": [
|
"extends": ["eslint:recommended"],
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended"
|
|
||||||
],
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"Uint8Array": true,
|
"Uint8Array": true,
|
||||||
"Promise": true,
|
"Promise": true,
|
||||||
|
2
.github/codeql-config.yml
vendored
2
.github/codeql-config.yml
vendored
@ -1,2 +0,0 @@
|
|||||||
paths-ignore:
|
|
||||||
- '**/tests/*.js'
|
|
43
.github/workflows/codeql.yml
vendored
43
.github/workflows/codeql.yml
vendored
@ -1,43 +0,0 @@
|
|||||||
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
3
.github/workflows/karma-tests.yml
vendored
@ -8,7 +8,6 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -18,7 +17,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18.x]
|
node-version: [16.x]
|
||||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,9 +1,6 @@
|
|||||||
# Distribution directory
|
# Distribution directory
|
||||||
dist
|
dist
|
||||||
|
|
||||||
# conversejs/media repo checkout
|
|
||||||
media
|
|
||||||
|
|
||||||
# Editor fluff
|
# Editor fluff
|
||||||
*~
|
*~
|
||||||
.sw?
|
.sw?
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
"quoteProps": "preserve",
|
"quoteProps": "preserve",
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
|
36541
3rdparty/libsignal-protocol.js
vendored
36541
3rdparty/libsignal-protocol.js
vendored
File diff suppressed because one or more lines are too long
1
3rdparty/libsignal-protocol.min.js
vendored
1
3rdparty/libsignal-protocol.min.js
vendored
File diff suppressed because one or more lines are too long
56
CHANGES.md
56
CHANGES.md
@ -1,61 +1,5 @@
|
|||||||
# Changelog
|
# 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)
|
## 10.0.0 (2022-10-30)
|
||||||
|
|
||||||
- Update to Strophe.js 1.6.0 which adds support for SCRAM-SHA-256 and SCRAM-SHA-512
|
- Update to Strophe.js 1.6.0 which adds support for SCRAM-SHA-256 and SCRAM-SHA-512
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* An XMPP chat client that runs in the browser.
|
* An XMPP chat client that runs in the browser.
|
||||||
*
|
*
|
||||||
* Version: 10.1.6
|
* Version: 10.0.0
|
||||||
*
|
*
|
||||||
* Copyright: JC Brand 2013-2018
|
* Copyright: JC Brand 2013-2018
|
||||||
* Except for 3rd party dependencies.
|
* Except for 3rd party dependencies.
|
||||||
|
73
Makefile
73
Makefile
@ -3,11 +3,13 @@ BOOTSTRAP = ./node_modules/
|
|||||||
BUILDDIR = ./docs
|
BUILDDIR = ./docs
|
||||||
KARMA ?= ./node_modules/.bin/karma
|
KARMA ?= ./node_modules/.bin/karma
|
||||||
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss
|
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss
|
||||||
|
ESLINT ?= ./node_modules/.bin/eslint
|
||||||
HTTPSERVE ?= ./node_modules/.bin/http-server
|
HTTPSERVE ?= ./node_modules/.bin/http-server
|
||||||
HTTPSERVE_PORT ?= 8000
|
HTTPSERVE_PORT ?= 8000
|
||||||
INKSCAPE ?= inkscape
|
INKSCAPE ?= inkscape
|
||||||
INSTALL ?= install
|
INSTALL ?= install
|
||||||
JSDOC ?= ./node_modules/.bin/jsdoc
|
JSDOC ?= ./node_modules/.bin/jsdoc
|
||||||
|
LERNA ?= ./node_modules/.bin/lerna
|
||||||
OXIPNG ?= oxipng
|
OXIPNG ?= oxipng
|
||||||
PAPER =
|
PAPER =
|
||||||
RJS ?= ./node_modules/.bin/r.js
|
RJS ?= ./node_modules/.bin/r.js
|
||||||
@ -58,14 +60,13 @@ serve: node_modules dist
|
|||||||
serve_bg: node_modules
|
serve_bg: node_modules
|
||||||
$(HTTPSERVE) -p $(HTTPSERVE_PORT) -c-1 -s &
|
$(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
|
## Translation machinery
|
||||||
|
|
||||||
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
|
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=10.0.0 dist/converse-no-dependencies.js -c
|
||||||
|
|
||||||
src/i18n/converse.pot: dist/converse-no-dependencies.js
|
src/i18n/converse.pot: dist/converse-no-dependencies.js
|
||||||
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
|
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
|
||||||
@ -82,9 +83,10 @@ po:
|
|||||||
########################################################################
|
########################################################################
|
||||||
## Release management
|
## Release management
|
||||||
|
|
||||||
.PHONY: version
|
.PHONY: release
|
||||||
version:
|
release:
|
||||||
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/shared/constants.js
|
find ./src -name "*~" -exec rm {} \;
|
||||||
|
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)";/' src/headless/core.js
|
||||||
$(SED) -i '/Version:/s/:.*/: $(VERSION)/' COPYRIGHT
|
$(SED) -i '/Version:/s/:.*/: $(VERSION)/' COPYRIGHT
|
||||||
$(SED) -i '/Project-Id-Version:/s/:.*/: Converse.js $(VERSION)\n"/' src/i18n/converse.pot
|
$(SED) -i '/Project-Id-Version:/s/:.*/: Converse.js $(VERSION)\n"/' src/i18n/converse.pot
|
||||||
$(SED) -i '/"version":/s/:.*/: "$(VERSION)",/' manifest.json
|
$(SED) -i '/"version":/s/:.*/: "$(VERSION)",/' manifest.json
|
||||||
@ -99,33 +101,19 @@ version:
|
|||||||
make pot
|
make pot
|
||||||
make po
|
make po
|
||||||
make dist
|
make dist
|
||||||
|
npm pack
|
||||||
release-checkout:
|
cd src/headless && npm pack
|
||||||
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
|
.PHONY: postrelease
|
||||||
postrelease:
|
postrelease:
|
||||||
$(SED) -i '/^export const VERSION_NAME =/s/=.*/= "v$(VERSION)dev";/' src/headless/shared/constants.js
|
$(SED) -i '/^_converse.VERSION_NAME =/s/=.*/= "v$(VERSION)dev";/' src/headless/core.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
|
## Install dependencies
|
||||||
|
|
||||||
|
$(LERNA):
|
||||||
|
npm install lerna
|
||||||
|
|
||||||
${NVM_DIR}/nvm.sh:
|
${NVM_DIR}/nvm.sh:
|
||||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
|
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
|
||||||
source ~/.bashrc
|
source ~/.bashrc
|
||||||
@ -137,9 +125,12 @@ nvm: ${NVM_DIR}/nvm.sh
|
|||||||
node: .nvmrc
|
node: .nvmrc
|
||||||
. $(HOME)/.nvm/nvm.sh && nvm install
|
. $(HOME)/.nvm/nvm.sh && nvm install
|
||||||
|
|
||||||
node_modules: package.json src/headless/package.json
|
package-lock.json: package.json
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
node_modules: $(LERNA) package.json package-lock.json src/headless/package.json src/headless/package-lock.json
|
||||||
|
npm run lerna
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
npm run clean
|
npm run clean
|
||||||
@ -156,9 +147,6 @@ devserver: node_modules
|
|||||||
########################################################################
|
########################################################################
|
||||||
## Builds
|
## 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
|
dist/converse.js:: node_modules
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
@ -205,16 +193,7 @@ 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
|
dist:: node_modules src/* | dist/website.css dist/website.min.css
|
||||||
npm run headless
|
npm run headless
|
||||||
# Ideally this should just be `npm run build`.
|
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
|
.PHONY: install
|
||||||
install:: dist
|
install:: dist
|
||||||
@ -223,24 +202,20 @@ install:: dist
|
|||||||
cdn:: node_modules
|
cdn:: node_modules
|
||||||
npm run cdn
|
npm run cdn
|
||||||
|
|
||||||
.PHONY: types
|
|
||||||
types:: node_modules
|
|
||||||
npm run types
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
.PHONY: eslint
|
.PHONY: eslint
|
||||||
eslint: node_modules
|
eslint: node_modules
|
||||||
npm run lint
|
$(ESLINT) src/**/*.js
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: eslint | dist/converse.js dist/converse.css
|
check: eslint | dist/converse.js dist/converse.css
|
||||||
npm run test -- $(ARGS)
|
$(KARMA) start karma.conf.js $(ARGS)
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
npm run test -- $(ARGS)
|
$(KARMA) start karma.conf.js $(ARGS)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
## Documentation
|
## Documentation
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
# 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?}/
|
|
||||||
```
|
|
14
README.md
14
README.md
@ -166,19 +166,14 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
|
|||||||
|
|
||||||
## Sponsors
|
## 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>
|
<p>
|
||||||
<a href="https://blokt.com?utm_source=conversejs" target="_blank" rel="noopener">
|
<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">
|
<img alt="Blokt Crypto & Privacy" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/blokt.png" width="200">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="https://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener">
|
<a href="https://www.codefirst.co.uk?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img alt="Prime Sound" src="https://raw.githubusercontent.com/conversejs/media/main/logos/primesound.png" width="200">
|
<img alt="Codefirst" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/codefirst.png" width="200">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -186,3 +181,8 @@ We accept donations via [Patreon](https://www.patreon.com/jcbrand) and [Liberapa
|
|||||||
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
|
<img alt="KeyCDN" src="https://raw.githubusercontent.com/conversejs/converse.js/541613d1fea8aef364af00180f60e959162e5e4b/logo/keycdn.png" width="200">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</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>
|
||||||
|
23
RELEASE.md
23
RELEASE.md
@ -1,18 +1,19 @@
|
|||||||
# Release checklist
|
# Release checklist
|
||||||
|
|
||||||
1. Merge weblate translations: https://hosted.weblate.org/projects/conversejs/translations/#repository
|
1. Check that weblate translations are all merged in
|
||||||
2. Run `make check` to check that all tests pass.
|
2. Run `make check` to check that all tests pass.
|
||||||
3. Run `make version VERSION=10.1.6`
|
3. Run `make release VERSION=10.0.1`
|
||||||
4. Do a `git diff` to check if things look sane.
|
4. Do a `git diff` to check if things look sane.
|
||||||
5. Do a quick manual test with the `dist` files (via `index.html`)
|
5. Do a quick manual test with the `dist` files (via `index.html`)
|
||||||
6. `git commit -am "Release 10.1.6"`
|
6. `git commit -am "Release 10.0.1"`
|
||||||
7. `git tag -s v10.1.6 -m "Release 10.1.6"`
|
7. `git tag -s v10.0.1 -m "Release 10.0.1"`
|
||||||
8. `git push && git push origin v10.1.6`
|
8. Run `git push && git push origin v10.0.1`
|
||||||
9. `make publish BRANCH=v10.1.6`
|
9. Update https://conversejs.org
|
||||||
10. Update release page on Github
|
|
||||||
* Upload tar files
|
|
||||||
11. Update https://conversejs.org
|
|
||||||
* `cd /home/conversejs/converse.js`
|
* `cd /home/conversejs/converse.js`
|
||||||
* `make deploy VERSION=10.1.6`
|
* `git clone --branch v10.0.1 git@github.com:conversejs/converse.js.git 10.0.1`
|
||||||
|
* `cd 10.0.1 && nvm install && ASSET_PATH=https://cdn.conversejs.org/10.0.1/dist/ make dist && make doc`
|
||||||
|
* `cd .. && git pull && nvm install && ASSET_PATH=https://cdn.conversejs.org/dist/ make dist && make doc`
|
||||||
|
10. Update release page on Github
|
||||||
|
11. Run `npm publish && cd src/headless/ && npm publish`
|
||||||
12. Update the repository on weblate
|
12. Update the repository on weblate
|
||||||
13. Decide on next release number and run `make postrelease VERSION=10.1.7`
|
13. Decide on next release number and run `make postrelease VERSION=9.1.2`
|
||||||
|
23
converse-logs/README.md
Normal file
23
converse-logs/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# 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.
|
||||||
|
|
5
converse-logs/converse-logs.js
Normal file
5
converse-logs/converse-logs.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
define("transcripts", [
|
||||||
|
"tpl!../../converse-logs/missing_messages",
|
||||||
|
], function () {
|
||||||
|
return arguments;
|
||||||
|
});
|
@ -10,9 +10,6 @@
|
|||||||
<category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-client"/>
|
<category rdf:resource="https://linkmauve.fr/ns/xmpp-doap#category-client"/>
|
||||||
<programming-language>JavaScript</programming-language>
|
<programming-language>JavaScript</programming-language>
|
||||||
<os>Browser</os>
|
<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"/>
|
<schema:logo rdf:resource="https://raw.githubusercontent.com/conversejs/converse.js/master/logo/conversejs-filled.svg"/>
|
||||||
<repository>
|
<repository>
|
||||||
<GitRepository>
|
<GitRepository>
|
||||||
|
8
dev.html
8
dev.html
@ -28,22 +28,18 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
converse.initialize({
|
converse.initialize({
|
||||||
i18n: 'af',
|
|
||||||
theme: 'dracula',
|
|
||||||
auto_away: 300,
|
auto_away: 300,
|
||||||
enable_smacks: true,
|
enable_smacks: true,
|
||||||
loglevel: 'debug',
|
loglevel: 'debug',
|
||||||
reuse_scram_keys: true,
|
|
||||||
prune_messages_above: 100,
|
prune_messages_above: 100,
|
||||||
message_archiving: 'always',
|
message_archiving: 'always',
|
||||||
muc_respect_autojoin: true,
|
muc_respect_autojoin: true,
|
||||||
muc_show_logs_before_join: true,
|
muc_show_logs_before_join: true,
|
||||||
notify_all_room_messages: ['discuss@conference.conversejs.org'],
|
notify_all_room_messages: ['discuss@conference.conversejs.org'],
|
||||||
view_mode: 'fullscreen',
|
view_mode: 'fullscreen',
|
||||||
// websocket_url: 'wss://conversejs.org/xmpp-websocket',
|
websocket_url: 'wss://conversejs.org/xmpp-websocket',
|
||||||
websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
|
// websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
|
||||||
whitelisted_plugins: ['converse-debug'],
|
whitelisted_plugins: ['converse-debug'],
|
||||||
// connection_options: { worker: '/dist/shared-connection-worker.js' }
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -2,21 +2,25 @@
|
|||||||
<h4 class="sidebar-title">Sponsored by</h4>
|
<h4 class="sidebar-title">Sponsored by</h4>
|
||||||
</span>
|
</span>
|
||||||
<ul class="sponsors-list">
|
<ul class="sponsors-list">
|
||||||
<li><a href="https://bairesdev.com/sponsoring-open-source-projects/?utm_source=conversejs" target="_blank" rel="noopener">
|
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
||||||
<img style="width: 10em" src="/media/logos/bairesdev-primary.png" alt="BairesDev">
|
<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">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="https://blokt.com/?utm_source=conversejs" target="_blank" rel="noopener">
|
<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">
|
<img style="width: 9em" src="/logo/blokt.png" alt="Blokt Crypto & Privacy">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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">
|
<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">
|
<img style="width: 9em" src="/media/logos/primesound.png" alt="Prime Sound">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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>
|
</ul>
|
||||||
<span class="centered-text-container patreon-link-container"><a href="https://conversejs.org#sponsors">Become a sponsor</a></span>
|
<span class="centered-text-container patreon-link-container"><a href="https://conversejs.org#sponsors">Become a sponsor</a></span>
|
||||||
|
@ -48,9 +48,9 @@ copyright = u'2018, JC Brand'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '10.1.6'
|
version = '10.0.0'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '10.1.6'
|
release = '10.0.0'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -407,6 +407,7 @@ in to their XMPP account.
|
|||||||
So currently if EITHER ``keepalive`` or ``auto_login`` is ``true`` and
|
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.
|
`authentication`_ is set to ``login``, then Converse will try to log the user in.
|
||||||
|
|
||||||
|
|
||||||
auto_away
|
auto_away
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -693,7 +694,7 @@ appear in another.
|
|||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
converse.initialize({
|
converse.initialize({
|
||||||
connection_options: { worker: '/dist/shared-connection-worker.js' }
|
connection_options: { 'worker': true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -925,6 +926,12 @@ filter_url_query_params
|
|||||||
|
|
||||||
Accepts a string or array of strings. Any query strings from URLs that match this setting will be removed.
|
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
|
geouri_regex
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -1813,27 +1820,6 @@ 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.
|
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`:
|
||||||
|
|
||||||
roomconfig_whitelist
|
roomconfig_whitelist
|
||||||
@ -2083,13 +2069,6 @@ themselves).
|
|||||||
In order to support all browsers we need both an MP3 and an Ogg file. Make sure
|
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``.
|
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
|
sticky_controlbox
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -56,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:
|
To load a specific version of Converse you can put the version in the URL:
|
||||||
|
|
||||||
* https://cdn.conversejs.org/10.1.6/dist/converse.min.js
|
* https://cdn.conversejs.org/10.0.0/dist/converse.min.js
|
||||||
* https://cdn.conversejs.org/10.1.6/dist/converse.min.css
|
* https://cdn.conversejs.org/10.0.0/dist/converse.min.css
|
||||||
|
|
||||||
You can include these two URLs inside the *<head>* element of your website
|
You can include these two URLs inside the *<head>* element of your website
|
||||||
via the *script* and *link* tags:
|
via the *script* and *link* tags:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/10.1.6/dist/converse.min.css">
|
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/10.0.0/dist/converse.min.css">
|
||||||
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js" charset="utf-8"></script>
|
<script src="https://cdn.conversejs.org/10.0.0/dist/converse.min.js" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
Option 2: Download the builds from Github
|
Option 2: Download the builds from Github
|
||||||
|
@ -202,4 +202,5 @@ Storing the SASL SCRAM-SHA1 hash in IndexedDB
|
|||||||
Another suggestion that's been suggested is to store the SCRAM-SHA1 computed
|
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.
|
``clientKey`` in localStorage and to use that upon page reload to log the user in again.
|
||||||
|
|
||||||
This has been implemented since version 10, see documentation on `reuse_scram_keys <https://conversejs.org/docs/html/configuration.html#reuse-scram-keys>`_
|
We might implement this feature in core Converse.js eventually.
|
||||||
|
As always, contributions welcome!
|
||||||
|
@ -240,11 +240,11 @@
|
|||||||
<div class="sponsors">
|
<div class="sponsors">
|
||||||
<h2>Converse is supported by:</h2>
|
<h2>Converse is supported by:</h2>
|
||||||
<ul >
|
<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://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://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://primesound.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="width: 10em" src="/media/logos/primesound.png" alt="Prime Sound"></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@
|
|||||||
|
|
||||||
<!-- These files are NOT needed when using converse.js in your own project. -->
|
<!-- 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 rel="shortcut icon" type="image/ico" href="images/favicon.ico"/>
|
||||||
<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.0.0/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" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.0.0/css/website.min.css" />
|
||||||
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
|
<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="/src/website.js"></script>
|
||||||
<script type="text/javascript" src="analytics.js"></script>
|
<script type="text/javascript" src="analytics.js"></script>
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
|
|
||||||
<![if gte IE 11]>
|
<![if gte IE 11]>
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/css/converse.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.0.0/css/converse.min.css" />
|
||||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||||
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
|
<script src="https://cdn.conversejs.org/10.0.0/dist/converse.min.js"></script>
|
||||||
<![endif]>
|
<![endif]>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -66,7 +66,7 @@
|
|||||||
<table id="jslicense-labels1" style="width: 100%">
|
<table id="jslicense-labels1" style="width: 100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://cdn.conversejs.org/10.1.6/dist/converse.min.js">converse.min.js</a>
|
<a href="https://cdn.conversejs.org/10.0.0/dist/converse.min.js">converse.min.js</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://www.mozilla.org/en-US/MPL/2.0/">MPL-2.0</a>
|
<a href="https://www.mozilla.org/en-US/MPL/2.0/">MPL-2.0</a>
|
||||||
|
@ -103,9 +103,6 @@ module.exports = function(config) {
|
|||||||
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/omemo/tests/muc.js", type: 'module' },
|
{ pattern: "src/plugins/omemo/tests/muc.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/omemo/tests/omemo.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/push/tests/push.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },
|
{ pattern: "src/plugins/roomslist/tests/roomslist.js", type: 'module' },
|
||||||
|
7
lerna.json
Normal file
7
lerna.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
".",
|
||||||
|
"src/*"
|
||||||
|
],
|
||||||
|
"version": "4.0.3"
|
||||||
|
}
|
BIN
logo/codefirst.png
Normal file
BIN
logo/codefirst.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@ -1,34 +1,19 @@
|
|||||||
<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;"
|
<svg class="converse-svg-logo"
|
||||||
xml:space="preserve" version="1.1"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 376 311" height="20%" width="10rem"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
<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"/>
|
viewBox="0 0 364 364">
|
||||||
<metadata id="metadata114">
|
<title>Converse</title>
|
||||||
<rdf:RDF>
|
<g class="cls-1" id="g904">
|
||||||
<cc:Work rdf:about="">
|
<g data-name="Layer 2">
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<g data-name="Layer 7">
|
||||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
<path
|
||||||
<dc:title/>
|
class="cls-3"
|
||||||
<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"/>
|
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" />
|
||||||
<dc:creator>
|
<path
|
||||||
<cc:Agent>
|
class="cls-4"
|
||||||
<dc:title>Antoine BARDELLI</dc:title>
|
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" />
|
||||||
</cc:Agent>
|
</g>
|
||||||
</dc:creator>
|
</g>
|
||||||
</cc:Work>
|
</g>
|
||||||
</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>
|
</svg>
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,19 +0,0 @@
|
|||||||
<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>
|
|
Before Width: | Height: | Size: 1.0 KiB |
@ -1,46 +1,108 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
id="chapril-logo"
|
class="converse-svg-logo"
|
||||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"
|
viewBox="0 0 364 364"
|
||||||
class="converse-svg-logo"
|
version="1.1"
|
||||||
xml:space="preserve" version="1.1"
|
id="svg13"
|
||||||
viewBox="0 0 376 311" height="20%" width="6rem"
|
sodipodi:docname="conversejs-with-byline.svg"
|
||||||
sodipodi:docname="chapril-logo.svg" inkscape:version="0.92.1 r15371">
|
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
||||||
<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
|
||||||
<metadata id="metadata114">
|
id="metadata19">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work rdf:about="">
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
<dc:type
|
||||||
<dc:title/>
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<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:title>Converse</dc:title>
|
||||||
<dc:creator>
|
|
||||||
<cc:Agent>
|
|
||||||
<dc:title>Antoine BARDELLI</dc:title>
|
|
||||||
</cc:Agent>
|
|
||||||
</dc:creator>
|
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<defs id="defs112"/>
|
<defs
|
||||||
<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)">
|
id="defs17">
|
||||||
<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"/>
|
<inkscape:perspective
|
||||||
<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"/>
|
sodipodi:type="inkscape:persp3d"
|
||||||
<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"/>
|
inkscape:vp_x="0 : 182 : 1"
|
||||||
<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"/>
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
<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"/>
|
inkscape:vp_z="364 : 182 : 1"
|
||||||
<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"/>
|
inkscape:persp3d-origin="182 : 121.33333 : 1"
|
||||||
<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"/>
|
id="perspective2147" />
|
||||||
<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"/>
|
</defs>
|
||||||
<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"/>
|
<sodipodi:namedview
|
||||||
<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"/>
|
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>
|
||||||
</g>
|
</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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 5.5 KiB |
@ -2,7 +2,7 @@
|
|||||||
"short_name": "Converse",
|
"short_name": "Converse",
|
||||||
"name": "Converse Chat",
|
"name": "Converse Chat",
|
||||||
"description": "Messaging Freedom",
|
"description": "Messaging Freedom",
|
||||||
"version": "10.1.6",
|
"version": "10.0.0",
|
||||||
"categories": ["social"],
|
"categories": ["social"],
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
<script type="text/javascript" src="analytics.js"></script>
|
<script type="text/javascript" src="analytics.js"></script>
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.1.6/dist/converse.min.css" />
|
<link type="text/css" rel="stylesheet" media="screen" href="https://cdn.conversejs.org/10.0.0/dist/converse.min.css" />
|
||||||
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>
|
||||||
<script src="https://cdn.conversejs.org/10.1.6/dist/converse.min.js"></script>
|
<script src="https://cdn.conversejs.org/10.0.0/dist/converse.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="page-top" data-spy="scroll" class="converse-website">
|
<body id="page-top" data-spy="scroll" class="converse-website">
|
||||||
@ -241,9 +241,11 @@
|
|||||||
<h2>Converse is supported by:</h2>
|
<h2>Converse is supported by:</h2>
|
||||||
<ul >
|
<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://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://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://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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
24697
package-lock.json
generated
24697
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
66
package.json
66
package.json
@ -1,46 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "converse.js",
|
"name": "converse.js",
|
||||||
"version": "10.1.6",
|
"version": "10.0.0",
|
||||||
"description": "Browser based XMPP chat client",
|
"description": "Browser based XMPP chat client",
|
||||||
"browser": "dist/converse.js",
|
"browser": "dist/converse.js",
|
||||||
"module": "src/index.js",
|
"module": "src/converse.js",
|
||||||
"workspaces": [
|
|
||||||
"src/headless"
|
|
||||||
],
|
|
||||||
"files": [
|
"files": [
|
||||||
|
"*.js",
|
||||||
|
"*.json",
|
||||||
"CHANGES.md",
|
"CHANGES.md",
|
||||||
"LICENSE.txt",
|
"LICENSE.txt",
|
||||||
"README.md",
|
"README.md",
|
||||||
"COPYRIGHT",
|
"COPYRIGHT",
|
||||||
"dist/",
|
"dist/",
|
||||||
"docs/**/*.md",
|
|
||||||
"docs/**/*.rst",
|
"docs/**/*.rst",
|
||||||
|
"docs/**/*.md",
|
||||||
"sass/**/*.scss",
|
"sass/**/*.scss",
|
||||||
"src/**/*.html",
|
"src/*.js",
|
||||||
"src/**/*.js",
|
|
||||||
"src/**/*.json",
|
|
||||||
"src/**/*.md",
|
|
||||||
"src/**/*.po",
|
|
||||||
"src/**/*.pot",
|
"src/**/*.pot",
|
||||||
"src/**/*.scss",
|
"src/**/*.po",
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.html",
|
||||||
"src/**/*.svg",
|
"src/**/*.svg",
|
||||||
|
"src/**/*.md",
|
||||||
"src/**/*.txt",
|
"src/**/*.txt",
|
||||||
"3rdparty/*.js"
|
"src/**/*.json"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack/webpack.build.js",
|
"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",
|
"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",
|
"clean": "rm -rf node_modules dist *.zip src/headless/dist src/headless/node_modules",
|
||||||
"dev": "webpack --config webpack/webpack.build.js --mode=development",
|
"dev": "webpack --config webpack/webpack.build.js --mode=development",
|
||||||
"headless": "webpack --config webpack/webpack.headless.js",
|
"headless": "webpack --config webpack/webpack.headless.js",
|
||||||
"headless-dev": "webpack --config webpack/webpack.headless.js --mode=development",
|
"headless-dev": "webpack --config webpack/webpack.headless.js --mode=development",
|
||||||
|
"lerna": "lerna bootstrap --hoist --ignore-scripts",
|
||||||
"nodeps": "webpack --config webpack/webpack.nodeps.js",
|
"nodeps": "webpack --config webpack/webpack.nodeps.js",
|
||||||
|
"prepare": "npm run lerna && npm run build",
|
||||||
"serve": "webpack serve --config webpack/webpack.serve.js",
|
"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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -69,13 +65,13 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.17.10",
|
"@babel/cli": "^7.17.10",
|
||||||
"@babel/core": "^7.18.5",
|
"@babel/core": "^7.18.5",
|
||||||
|
"@babel/eslint-parser": "^7.18.9",
|
||||||
"@babel/preset-env": "^7.18.2",
|
"@babel/preset-env": "^7.18.2",
|
||||||
"@converse/headless": "file:src/headless",
|
"@converse/headless": "file:src/headless",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
|
||||||
"autoprefixer": "^10.4.5",
|
"autoprefixer": "^10.4.5",
|
||||||
"babel-loader": "^9.1.0",
|
"babel-loader": "^8.2.5",
|
||||||
"bootstrap.native-loader": "2.0.0",
|
"bootstrap.native-loader": "2.0.0",
|
||||||
"clean-css-cli": "^5.6.2",
|
"clean-css-cli": "^5.6.0",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"eslint": "^8.21.0",
|
"eslint": "^8.21.0",
|
||||||
@ -84,50 +80,48 @@
|
|||||||
"http-server": "^14.1.0",
|
"http-server": "^14.1.0",
|
||||||
"imports-loader": "^4.0.0",
|
"imports-loader": "^4.0.0",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jsdoc": "^4.0.0",
|
"jsdoc": "^3.6.7",
|
||||||
"karma": "^6.3.19",
|
"karma": "^6.3.19",
|
||||||
"karma-chrome-launcher": "^3.1.1",
|
"karma-chrome-launcher": "^3.1.1",
|
||||||
"karma-cli": "^2.0.0",
|
"karma-cli": "^2.0.0",
|
||||||
"karma-jasmine": "^5.0.0",
|
"karma-jasmine": "^5.0.0",
|
||||||
"karma-jasmine-html-reporter": "^2.0.0",
|
"karma-jasmine-html-reporter": "^1.7.0",
|
||||||
"karma-webpack": "^5.0.0",
|
"karma-webpack": "^5.0.0",
|
||||||
|
"lerna": "^5.6.2",
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"po-loader": "0.7.0",
|
"po-loader": "0.7.0",
|
||||||
"po2json": "^1.0.0-beta-3",
|
"po2json": "^1.0.0-beta-3",
|
||||||
"postcss": "^8.4.16",
|
"postcss": "^8.4.14",
|
||||||
|
"postcss-clean": "1.2.0",
|
||||||
"postcss-loader": "^7.0.1",
|
"postcss-loader": "^7.0.1",
|
||||||
"prettierx": "^0.19.0",
|
"prettierx": "^0.19.0",
|
||||||
"sass": "^1.51.0",
|
"sass": "^1.51.0",
|
||||||
"sass-loader": "^13.1.0",
|
"sass-loader": "^12.6.0",
|
||||||
"style-loader": "^3.1.0",
|
"style-loader": "^3.1.0",
|
||||||
"tsc": "^2.0.4",
|
"webpack": "^5.72.0",
|
||||||
"typescript": "^4.9.5",
|
"webpack-cli": "^4.7.2",
|
||||||
"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-dev-server": "^4.8.1",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@converse/openpromise": "^0.0.1",
|
"@converse/openpromise": "^0.0.1",
|
||||||
"@converse/skeletor": "^0.0.8",
|
"@converse/skeletor": "0.0.7",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"bootstrap.native": "^2.0.27",
|
"bootstrap.native": "^2.0.27",
|
||||||
"client-compress": "^2.2.2",
|
"client-compress": "^2.2.2",
|
||||||
"dayjs": "^1.11.8",
|
"dayjs": "1.11.6",
|
||||||
"dompurify": "^2.3.1",
|
"dompurify": "^2.3.1",
|
||||||
"favico.js-slevomat": "^0.3.11",
|
"favico.js-slevomat": "^0.3.11",
|
||||||
"gifuct-js": "^2.1.2",
|
"filesize": "^7.0.0",
|
||||||
"jed": "1.1.1",
|
"jed": "1.1.1",
|
||||||
"lit": "^2.4.0",
|
"lit": "^2.4.0",
|
||||||
"localforage-webextensionstorage-driver": "^3.0.0",
|
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"pluggable.js": "^3.0.1",
|
"pluggable.js": "3.0.1",
|
||||||
"sizzle": "^2.3.5",
|
"sizzle": "^2.3.5",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
"strophe.js": "^1.6.2",
|
"strophe.js": "1.6.0",
|
||||||
"urijs": "^1.19.10"
|
"urijs": "^1.19.10"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
@ -2,5 +2,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
require('autoprefixer'),
|
require('autoprefixer'),
|
||||||
|
require('postcss-clean')
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "@converse/headless";
|
import "@converse/headless/headless";
|
||||||
import "./i18n/index.js";
|
import "./i18n/index.js";
|
||||||
import "shared/registry.js";
|
import "shared/registry.js";
|
||||||
import { CustomElement } from 'shared/components/element';
|
import { CustomElement } from 'shared/components/element';
|
@ -49,7 +49,7 @@ const converse = {
|
|||||||
*
|
*
|
||||||
* @memberOf converse
|
* @memberOf converse
|
||||||
* @method load
|
* @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
|
* @example
|
||||||
* converse.load({assets_path: '/path/to/assets/'});
|
* converse.load({assets_path: '/path/to/assets/'});
|
||||||
*/
|
*/
|
||||||
@ -57,7 +57,7 @@ const converse = {
|
|||||||
if (settings.assets_path) {
|
if (settings.assets_path) {
|
||||||
__webpack_public_path__ = settings.assets_path; // eslint-disable-line no-undef
|
__webpack_public_path__ = settings.assets_path; // eslint-disable-line no-undef
|
||||||
}
|
}
|
||||||
require('./index.js');
|
require('./converse.js');
|
||||||
Object.keys(plugins).forEach(name => converse.plugins.add(name, plugins[name]));
|
Object.keys(plugins).forEach(name => converse.plugins.add(name, plugins[name]));
|
||||||
return converse;
|
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 main distribution of Converse relies on the headless build.
|
||||||
|
|
||||||
The file [src/headless/index.js](https://github.com/jcbrand/converse.js/blob/master/src/headless/index.js)
|
The file [src/headless/headless.js](https://github.com/jcbrand/converse.js/blob/master/src/headless/headless.js)
|
||||||
is used to determine which plugins are included in the build.
|
is used to determine which plugins are included in the build.
|
||||||
|
@ -2,16 +2,753 @@
|
|||||||
* @copyright The Converse.js contributors
|
* @copyright The Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import './shared/constants.js';
|
import URI from 'urijs';
|
||||||
import _converse from './shared/_converse';
|
import _converse from '@converse/headless/shared/_converse';
|
||||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||||
import api from './shared/api/index.js';
|
import connection_api from '@converse/headless/shared/connection/api.js';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import i18n from './shared/i18n';
|
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';
|
||||||
|
|
||||||
export { converse } from './shared/api/public.js';
|
|
||||||
export { _converse };
|
export { _converse };
|
||||||
export { i18n };
|
export { i18n };
|
||||||
export { api };
|
|
||||||
|
import {
|
||||||
|
attemptNonPreboundSession,
|
||||||
|
cleanup,
|
||||||
|
getConnectionServiceURL,
|
||||||
|
initClientConfig,
|
||||||
|
initPlugins,
|
||||||
|
initSessionStorage,
|
||||||
|
registerGlobalEventHandlers,
|
||||||
|
setUserJID,
|
||||||
|
} from './utils/init.js';
|
||||||
|
|
||||||
dayjs.extend(advancedFormat);
|
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 = "v10.0.0";
|
||||||
|
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
_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,
|
||||||
|
stx: u.stx,
|
||||||
|
u,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
* --------------------
|
* --------------------
|
||||||
* Any of the following components may be removed if they're not needed.
|
* 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/bookmarks/index.js"; // XEP-0199 XMPP Ping
|
||||||
import "./plugins/bosh.js"; // XEP-0206 BOSH
|
import "./plugins/bosh.js"; // XEP-0206 BOSH
|
||||||
import "./plugins/caps/index.js"; // XEP-0115 Entity Capabilities
|
import "./plugins/caps/index.js"; // XEP-0115 Entity Capabilities
|
||||||
import "./plugins/chat/index.js"; // RFC-6121 Instant messaging
|
import "./plugins/chat/index.js"; // RFC-6121 Instant messaging
|
||||||
import "./plugins/chatboxes/index.js";
|
import "./plugins/chatboxes/index.js";
|
||||||
import "./plugins/disco/index.js"; // XEP-0030 Service discovery
|
import "./plugins/disco/index.js"; // XEP-0030 Service discovery
|
||||||
import "./plugins/adhoc/index.js"; // XEP-0050 Ad Hoc Commands
|
|
||||||
import "./plugins/headlines/index.js"; // Support for headline messages
|
import "./plugins/headlines/index.js"; // Support for headline messages
|
||||||
import "./plugins/mam/index.js"; // XEP-0313 Message Archive Management
|
import "./plugins/mam/index.js"; // XEP-0313 Message Archive Management
|
||||||
import "./plugins/muc/index.js"; // XEP-0045 Multi-user chat
|
import "./plugins/muc/index.js"; // XEP-0045 Multi-user chat
|
@ -1,4 +1,4 @@
|
|||||||
import { isElement } from './utils/core.js';
|
import isElement from 'lodash-es/isElement';
|
||||||
|
|
||||||
const LEVELS = {
|
const LEVELS = {
|
||||||
'debug': 0,
|
'debug': 0,
|
||||||
@ -8,26 +8,24 @@ const LEVELS = {
|
|||||||
'fatal': 4
|
'fatal': 4
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
||||||
const logger = Object.assign({
|
const logger = Object.assign({
|
||||||
'debug': console?.log ? console.log.bind(console) : function noop () {},
|
'debug': console?.log ? console.log.bind(console) : function noop () {},
|
||||||
'error': 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 () {},
|
'info': console?.log ? console.log.bind(console) : function noop () {},
|
||||||
'warn': console?.log ? console.log.bind(console) : function noop () {}
|
'warn': console?.log ? console.log.bind(console) : function noop () {}
|
||||||
}, console);
|
}, console);
|
||||||
/* eslint-enable @typescript-eslint/no-empty-function */
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The log namespace
|
* The log namespace
|
||||||
* @namespace log
|
* @namespace log
|
||||||
*/
|
*/
|
||||||
export default {
|
const log = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The the log-level, which determines how verbose the logging is.
|
* The the log-level, which determines how verbose the logging is.
|
||||||
* @method log#setLogLevel
|
* @method log#setLogLevel
|
||||||
* @param { number } level - The loglevel which allows for filtering of log messages
|
* @param { integer } level - The loglevel which allows for filtering of log messages
|
||||||
*/
|
*/
|
||||||
setLogLevel (level) {
|
setLogLevel (level) {
|
||||||
if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
|
if (!['debug', 'info', 'warn', 'error', 'fatal'].includes(level)) {
|
||||||
@ -44,7 +42,7 @@ export default {
|
|||||||
* logged as well.
|
* logged as well.
|
||||||
* @method log#log
|
* @method log#log
|
||||||
* @param { string | Error } message - The message to be logged
|
* @param { string | Error } message - The message to be logged
|
||||||
* @param { number } level - The loglevel which allows for filtering of log messages
|
* @param { integer } level - The loglevel which allows for filtering of log messages
|
||||||
*/
|
*/
|
||||||
log (message, level, style='') {
|
log (message, level, style='') {
|
||||||
if (LEVELS[level] < LEVELS[this.loglevel]) {
|
if (LEVELS[level] < LEVELS[this.loglevel]) {
|
||||||
@ -95,3 +93,5 @@ export default {
|
|||||||
this.log(message, 'fatal', style);
|
this.log(message, 'fatal', style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default log;
|
||||||
|
142
src/headless/package-lock.json
generated
142
src/headless/package-lock.json
generated
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "@converse/headless",
|
"name": "@converse/headless",
|
||||||
"version": "10.1.0",
|
"version": "9.1.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@converse/headless",
|
"name": "@converse/headless",
|
||||||
"version": "10.1.0",
|
"version": "9.1.1",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@converse/openpromise": "^0.0.1",
|
"@converse/openpromise": "^0.0.1",
|
||||||
"@converse/skeletor": "0.0.8",
|
"@converse/skeletor": "0.0.7",
|
||||||
"dayjs": "1.11.8",
|
"dayjs": "1.11.6",
|
||||||
"dompurify": "^2.3.1",
|
"dompurify": "^2.3.1",
|
||||||
"filesize": "^7.0.0",
|
"filesize": "^7.0.0",
|
||||||
"localforage-webextensionstorage-driver": "^3.0.0",
|
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||||
@ -23,37 +23,29 @@
|
|||||||
"urijs": "^1.19.10"
|
"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": {
|
"node_modules/@converse/openpromise": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
||||||
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@converse/skeletor": {
|
"node_modules/@converse/skeletor": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.7.tgz",
|
||||||
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
|
"integrity": "sha512-JqK1lND0R1l9UEH/0cA7AGcP7EzB+NdPoMmCYNHBaSBins7ir+HHsGxw9eIHWJMPNXIN4ERG2oKwUTwVLryxmA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@converse/localforage-getitems": "1.4.3",
|
|
||||||
"lit-html": "^2.0.0-rc.2",
|
"lit-html": "^2.0.0-rc.2",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"localforage-driver-memory": "^1.0.5",
|
"localforage-driver-memory": "^1.0.5",
|
||||||
|
"localforage-getitems": "github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||||
"localforage-setitems": "^1.4.0",
|
"localforage-setitems": "^1.4.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mergebounce": "0.1.1"
|
"mergebounce": "0.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/trusted-types": {
|
"node_modules/@types/trusted-types": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
||||||
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
|
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
|
||||||
},
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.3",
|
"version": "0.8.3",
|
||||||
@ -70,9 +62,9 @@
|
|||||||
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
||||||
},
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@ -153,9 +145,9 @@
|
|||||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||||
},
|
},
|
||||||
"node_modules/dompurify": {
|
"node_modules/dompurify": {
|
||||||
"version": "2.4.4",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz",
|
||||||
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
|
"integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA=="
|
||||||
},
|
},
|
||||||
"node_modules/filesize": {
|
"node_modules/filesize": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
@ -267,9 +259,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lit-html": {
|
"node_modules/lit-html": {
|
||||||
"version": "2.6.1",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.4.0.tgz",
|
||||||
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
|
"integrity": "sha512-G6qXu4JNUpY6aaF2VMfaszhO9hlWw0hOTRFDmuMheg/nDYGB+2RztUSOyrzALAbr8Nh0Y7qjhYkReh3rPnplVg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/trusted-types": "^2.0.2"
|
"@types/trusted-types": "^2.0.2"
|
||||||
}
|
}
|
||||||
@ -302,6 +294,14 @@
|
|||||||
"localforage": "^1.5.0"
|
"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": {
|
"node_modules/localforage-setitems": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
||||||
@ -379,9 +379,9 @@
|
|||||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.15.0",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.2.3.tgz",
|
||||||
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
|
"integrity": "sha512-qfadtkY5kl0F5e4dXVdj2D+GtOdifasXHFMiL1SMf9ADQDv5Eti6xReef9FKj+iQPR2pvtqWna57s/PjARY4fg==",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@ -395,9 +395,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sizzle": {
|
"node_modules/sizzle": {
|
||||||
"version": "2.3.10",
|
"version": "2.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.7.tgz",
|
||||||
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
|
"integrity": "sha512-f3gf191yiPkCbcRR9IjHajmlrGN/MCXxAzNzIwVW0qZic2wbGKXn+KjbrVfqCaA3zEgCawqwtj2Y5LiBvOUddg=="
|
||||||
},
|
},
|
||||||
"node_modules/sprintf-js": {
|
"node_modules/sprintf-js": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@ -439,16 +439,16 @@
|
|||||||
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.12.1",
|
"version": "8.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||||
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
"utf-8-validate": ">=5.0.2"
|
"utf-8-validate": "^5.0.2"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"bufferutil": {
|
"bufferutil": {
|
||||||
@ -461,37 +461,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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": {
|
"@converse/openpromise": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@converse/openpromise/-/openpromise-0.0.1.tgz",
|
||||||
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
"integrity": "sha512-oA1TKrm6H838isYZJxMWXpXyOUezkD49eMJ6bkI+FfL2MsVuOV3ZbhBV+c07mLSknKXO7pUbWTVa5f7bXJXYjQ=="
|
||||||
},
|
},
|
||||||
"@converse/skeletor": {
|
"@converse/skeletor": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@converse/skeletor/-/skeletor-0.0.7.tgz",
|
||||||
"integrity": "sha512-8/wAenuk7QKOHaOsk89e5zFyQZz5HhsuqWBzrzDxmepiBVlRvnxjVdB6619IFyW0VWf0ezcm5Rl4JndUx2sbqg==",
|
"integrity": "sha512-JqK1lND0R1l9UEH/0cA7AGcP7EzB+NdPoMmCYNHBaSBins7ir+HHsGxw9eIHWJMPNXIN4ERG2oKwUTwVLryxmA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@converse/localforage-getitems": "1.4.3",
|
|
||||||
"lit-html": "^2.0.0-rc.2",
|
"lit-html": "^2.0.0-rc.2",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"localforage-driver-memory": "^1.0.5",
|
"localforage-driver-memory": "^1.0.5",
|
||||||
|
"localforage-getitems": "github:conversejs/localForage-getItems#0f129c5c9bb0d23f8dbb64c9dfbb003c8cdf7285",
|
||||||
"localforage-setitems": "^1.4.0",
|
"localforage-setitems": "^1.4.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mergebounce": "0.1.1"
|
"mergebounce": "0.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/trusted-types": {
|
"@types/trusted-types": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
||||||
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
|
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
|
||||||
},
|
},
|
||||||
"@xmldom/xmldom": {
|
"@xmldom/xmldom": {
|
||||||
"version": "0.8.3",
|
"version": "0.8.3",
|
||||||
@ -505,9 +497,9 @@
|
|||||||
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
||||||
},
|
},
|
||||||
"anymatch": {
|
"anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@ -566,9 +558,9 @@
|
|||||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||||
},
|
},
|
||||||
"dompurify": {
|
"dompurify": {
|
||||||
"version": "2.4.4",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz",
|
||||||
"integrity": "sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ=="
|
"integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA=="
|
||||||
},
|
},
|
||||||
"filesize": {
|
"filesize": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
@ -646,9 +638,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lit-html": {
|
"lit-html": {
|
||||||
"version": "2.6.1",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.4.0.tgz",
|
||||||
"integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==",
|
"integrity": "sha512-G6qXu4JNUpY6aaF2VMfaszhO9hlWw0hOTRFDmuMheg/nDYGB+2RztUSOyrzALAbr8Nh0Y7qjhYkReh3rPnplVg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/trusted-types": "^2.0.2"
|
"@types/trusted-types": "^2.0.2"
|
||||||
}
|
}
|
||||||
@ -676,6 +668,14 @@
|
|||||||
"tslib": "^1.6.0"
|
"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": {
|
"localforage-setitems": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/localforage-setitems/-/localforage-setitems-1.4.0.tgz",
|
||||||
@ -738,18 +738,18 @@
|
|||||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||||
},
|
},
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"version": "3.15.0",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.2.3.tgz",
|
||||||
"integrity": "sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==",
|
"integrity": "sha512-qfadtkY5kl0F5e4dXVdj2D+GtOdifasXHFMiL1SMf9ADQDv5Eti6xReef9FKj+iQPR2pvtqWna57s/PjARY4fg==",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sizzle": {
|
"sizzle": {
|
||||||
"version": "2.3.10",
|
"version": "2.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/sizzle/-/sizzle-2.3.7.tgz",
|
||||||
"integrity": "sha512-kPGev+SiByuzi/YPDTqCwdKLWCaN9+14ve86yH0gP6Efue04xjLYWJrcLC6y1buFyIVXkwHNXPsOTEd1MYVPbQ=="
|
"integrity": "sha512-f3gf191yiPkCbcRR9IjHajmlrGN/MCXxAzNzIwVW0qZic2wbGKXn+KjbrVfqCaA3zEgCawqwtj2Y5LiBvOUddg=="
|
||||||
},
|
},
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@ -786,9 +786,9 @@
|
|||||||
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.12.1",
|
"version": "8.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||||
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
|
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@converse/headless",
|
"name": "@converse/headless",
|
||||||
"version": "10.1.6",
|
"version": "10.0.0",
|
||||||
"description": "Converse.js Headless build",
|
"description": "Converse.js Headless build",
|
||||||
"author": "JC Brand <jc@opkode.com>",
|
"author": "cmrd Senya <senya@riseup.net>",
|
||||||
"contributors": [
|
|
||||||
"cmrd Senya <senya@riseup.net>"
|
|
||||||
],
|
|
||||||
"homepage": "https://conversejs.org",
|
"homepage": "https://conversejs.org",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"main": "dist/converse-headless.js",
|
"main": "dist/converse-headless.min.js",
|
||||||
"module": "index.js",
|
"module": "headless.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"converse.js",
|
"converse.js",
|
||||||
"XMPP",
|
"XMPP",
|
||||||
@ -30,19 +27,19 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/conversejs/converse.js/issues"
|
"url": "https://github.com/conversejs/converse.js/issues"
|
||||||
},
|
},
|
||||||
|
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@converse/openpromise": "^0.0.1",
|
"@converse/openpromise": "^0.0.1",
|
||||||
"@converse/skeletor": "^0.0.8",
|
"@converse/skeletor": "0.0.7",
|
||||||
"dayjs": "^1.11.8",
|
"dayjs": "1.11.6",
|
||||||
"dompurify": "^2.3.1",
|
"dompurify": "^2.3.1",
|
||||||
"filesize": "^10.0.7",
|
"filesize": "^7.0.0",
|
||||||
"localforage-webextensionstorage-driver": "^3.0.0",
|
"localforage-webextensionstorage-driver": "^3.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"pluggable.js": "3.0.1",
|
"pluggable.js": "3.0.1",
|
||||||
"sizzle": "^2.3.5",
|
"sizzle": "^2.3.5",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
"strophe.js": "^1.6.2",
|
"strophe.js": "1.6.0",
|
||||||
"urijs": "^1.19.10"
|
"urijs": "^1.19.10"
|
||||||
},
|
}
|
||||||
"devDependencies": {}
|
|
||||||
}
|
}
|
||||||
|
61
src/headless/plugins/adhoc.js
Normal file
61
src/headless/plugins/adhoc.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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;
|
@ -1,109 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,22 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -136,7 +136,7 @@ converse.plugins.add('converse-bosh', {
|
|||||||
tokens: {
|
tokens: {
|
||||||
/**
|
/**
|
||||||
* @method api.tokens.get
|
* @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.
|
* @returns 'string' A token, either the RID or SID token depending on what's asked for.
|
||||||
* @example _converse.api.tokens.get('rid');
|
* @example _converse.api.tokens.get('rid');
|
||||||
*/
|
*/
|
||||||
|
@ -31,13 +31,13 @@ async function createCapsNode () {
|
|||||||
'hash': "sha-1",
|
'hash': "sha-1",
|
||||||
'node': "https://conversejs.org",
|
'node': "https://conversejs.org",
|
||||||
'ver': await generateVerificationString()
|
'ver': await generateVerificationString()
|
||||||
}).tree();
|
}).nodeTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a stanza, adds a XEP-0115 CAPS element
|
* Given a stanza, adds a XEP-0115 CAPS element
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
export async function addCapsNode (stanza) {
|
export async function addCapsNode (stanza) {
|
||||||
const caps_el = await createCapsNode();
|
const caps_el = await createCapsNode();
|
||||||
|
@ -13,7 +13,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* @method api.chats.create
|
* @method api.chats.create
|
||||||
* @param {string|string[]} jid|jids An jid or array of jids
|
* @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) {
|
async create (jids, attrs) {
|
||||||
if (typeof jids === 'string') {
|
if (typeof jids === 'string') {
|
||||||
@ -44,9 +44,9 @@ export default {
|
|||||||
*
|
*
|
||||||
* @method api.chats.open
|
* @method api.chats.open
|
||||||
* @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
* @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 {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} [attrs.minimized] - Should the chat be created in minimized state.
|
||||||
* @param { Boolean } [force=false] - By default, a minimized
|
* @param {Boolean} [force=false] - By default, a minimized
|
||||||
* chat won't be maximized (in `overlayed` view mode) and in
|
* chat won't be maximized (in `overlayed` view mode) and in
|
||||||
* `fullscreen` view mode a newly opened chat won't replace
|
* `fullscreen` view mode a newly opened chat won't replace
|
||||||
* another chat already in the foreground.
|
* another chat already in the foreground.
|
||||||
@ -102,8 +102,8 @@ export default {
|
|||||||
*
|
*
|
||||||
* @method api.chats.get
|
* @method api.chats.get
|
||||||
* @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
* @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 {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 {Boolean} [create=false] - Whether the chat should be created if it's not found.
|
||||||
* @returns { Promise<_converse.ChatBox> }
|
* @returns { Promise<_converse.ChatBox> }
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
@ -7,8 +7,7 @@ import { getOpenPromise } from '@converse/openpromise';
|
|||||||
const { Strophe, sizzle, u } = converse.env;
|
const { Strophe, sizzle, u } = converse.env;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixin which turns a `ModelWithContact` model into a non-MUC message.
|
* Mixin which turns a `ModelWithContact` model into a non-MUC message. These can be either `chat` messages or `headline` messages.
|
||||||
* These can be either `chat`, `normal` or `headline` messages.
|
|
||||||
* @mixin
|
* @mixin
|
||||||
* @namespace _converse.Message
|
* @namespace _converse.Message
|
||||||
* @memberOf _converse
|
* @memberOf _converse
|
||||||
@ -48,8 +47,9 @@ const MessageMixin = {
|
|||||||
this.initialized.resolve();
|
this.initialized.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
setContact () {
|
setContact () {
|
||||||
if (['chat', 'normal'].includes(this.get('type'))) {
|
if (this.get('type') === 'chat') {
|
||||||
ModelWithContact.prototype.initialize.apply(this, arguments);
|
ModelWithContact.prototype.initialize.apply(this, arguments);
|
||||||
this.setRosterContact(Strophe.getBareJidFromJid(this.get('from')));
|
this.setRosterContact(Strophe.getBareJidFromJid(this.get('from')));
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ const MessageMixin = {
|
|||||||
if (this.get('is_encrypted')) {
|
if (this.get('is_encrypted')) {
|
||||||
const { __ } = _converse;
|
const { __ } = _converse;
|
||||||
return this.get('plaintext') || this.get('body') || __('Undecryptable OMEMO message');
|
return this.get('plaintext') || this.get('body') || __('Undecryptable OMEMO message');
|
||||||
} else if (['groupchat', 'chat', 'normal'].includes(this.get('type'))) {
|
} else if (['groupchat', 'chat'].includes(this.get('type'))) {
|
||||||
return this.get('body');
|
return this.get('body');
|
||||||
} else {
|
} else {
|
||||||
return this.get('message');
|
return this.get('message');
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import ModelWithContact from './model-with-contact.js';
|
import ModelWithContact from './model-with-contact.js';
|
||||||
|
import filesize from "filesize";
|
||||||
import isMatch from "lodash-es/isMatch";
|
import isMatch from "lodash-es/isMatch";
|
||||||
import isObject from "lodash-es/isObject";
|
import isObject from "lodash-es/isObject";
|
||||||
import log from '@converse/headless/log';
|
import log from '@converse/headless/log';
|
||||||
import pick from "lodash-es/pick";
|
import pick from "lodash-es/pick";
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { TimeoutError } from '../../shared/errors.js';
|
|
||||||
import { _converse, api, converse } from "../../core.js";
|
import { _converse, api, converse } from "../../core.js";
|
||||||
import { debouncedPruneHistory, handleCorrection } 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 { getMediaURLsMetadata } from '@converse/headless/shared/parsers.js';
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
import { initStorage } from '@converse/headless/utils/storage.js';
|
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||||
@ -369,7 +368,7 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async createMessageFromError (error) {
|
async createMessageFromError (error) {
|
||||||
if (error instanceof TimeoutError) {
|
if (error instanceof _converse.TimeoutError) {
|
||||||
const msg = await this.createMessage({
|
const msg = await this.createMessage({
|
||||||
'type': 'error',
|
'type': 'error',
|
||||||
'message': error.message,
|
'message': error.message,
|
||||||
|
@ -32,7 +32,7 @@ const { Strophe, sizzle } = converse.env;
|
|||||||
/**
|
/**
|
||||||
* Parses a passed in message stanza and returns an object of attributes.
|
* Parses a passed in message stanza and returns an object of attributes.
|
||||||
* @method st#parseMessage
|
* @method st#parseMessage
|
||||||
* @param { Element } stanza - The message stanza
|
* @param { XMLElement } stanza - The message stanza
|
||||||
* @param { _converse } _converse
|
* @param { _converse } _converse
|
||||||
* @returns { (MessageAttributes|Error) }
|
* @returns { (MessageAttributes|Error) }
|
||||||
*/
|
*/
|
||||||
@ -178,7 +178,7 @@ export async function parseMessage (stanza) {
|
|||||||
'thread': stanza.querySelector('thread')?.textContent,
|
'thread': stanza.querySelector('thread')?.textContent,
|
||||||
'time': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : now,
|
'time': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : now,
|
||||||
'to': stanza.getAttribute('to'),
|
'to': stanza.getAttribute('to'),
|
||||||
'type': stanza.getAttribute('type') || 'normal'
|
'type': stanza.getAttribute('type')
|
||||||
},
|
},
|
||||||
getErrorAttributes(stanza),
|
getErrorAttributes(stanza),
|
||||||
getOutOfBandAttributes(stanza),
|
getOutOfBandAttributes(stanza),
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import log from '@converse/headless/log.js';
|
|
||||||
import { _converse, api, converse } from '@converse/headless/core.js';
|
import { _converse, api, converse } from '@converse/headless/core.js';
|
||||||
import { isArchived, isHeadline, isServerMessage, } from '@converse/headless/shared/parsers';
|
import { isServerMessage, } from '@converse/headless/shared/parsers';
|
||||||
import { parseMessage } from './parsers.js';
|
import { parseMessage } from './parsers.js';
|
||||||
import { shouldClearCache } from '@converse/headless/utils/core.js';
|
import log from '@converse/headless/log.js';
|
||||||
|
|
||||||
const { Strophe, u } = converse.env;
|
const { Strophe, sizzle, u } = converse.env;
|
||||||
|
|
||||||
export function openChat (jid) {
|
export function openChat (jid) {
|
||||||
if (!u.isValidJID(jid)) {
|
if (!u.isValidJID(jid)) {
|
||||||
@ -14,7 +13,7 @@ export function openChat (jid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function onClearSession () {
|
export async function onClearSession () {
|
||||||
if (shouldClearCache()) {
|
if (_converse.shouldClearCache()) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
|
_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ 'silent': true }))
|
||||||
);
|
);
|
||||||
@ -61,22 +60,43 @@ export function autoJoinChats () {
|
|||||||
export function registerMessageHandlers () {
|
export function registerMessageHandlers () {
|
||||||
_converse.connection.addHandler(
|
_converse.connection.addHandler(
|
||||||
stanza => {
|
stanza => {
|
||||||
if (
|
if (sizzle(`message > result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop()) {
|
||||||
['groupchat', 'error'].includes(stanza.getAttribute('type')) ||
|
// MAM messages are handled in converse-mam.
|
||||||
isHeadline(stanza) ||
|
// We shouldn't get MAM messages here because
|
||||||
isServerMessage(stanza) ||
|
// they shouldn't have a `type` attribute.
|
||||||
isArchived(stanza)
|
log.warn(`Received a MAM message with type "chat".`);
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return _converse.handleMessageStanza(stanza) || true;
|
_converse.handleMessageStanza(stanza);
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
'message',
|
'message',
|
||||||
|
'chat'
|
||||||
);
|
);
|
||||||
|
|
||||||
_converse.connection.addHandler(
|
_converse.connection.addHandler(
|
||||||
stanza => handleErrorMessage(stanza) || true,
|
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;
|
||||||
|
},
|
||||||
null,
|
null,
|
||||||
'message',
|
'message',
|
||||||
'error'
|
'error'
|
||||||
@ -89,8 +109,6 @@ export function registerMessageHandlers () {
|
|||||||
* @param { MessageAttributes } attrs - The message attributes
|
* @param { MessageAttributes } attrs - The message attributes
|
||||||
*/
|
*/
|
||||||
export async function handleMessageStanza (stanza) {
|
export async function handleMessageStanza (stanza) {
|
||||||
stanza = stanza.tree?.() ?? stanza;
|
|
||||||
|
|
||||||
if (isServerMessage(stanza)) {
|
if (isServerMessage(stanza)) {
|
||||||
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
|
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
|
||||||
const from = stanza.getAttribute('from');
|
const from = stanza.getAttribute('from');
|
||||||
@ -98,7 +116,7 @@ export async function handleMessageStanza (stanza) {
|
|||||||
}
|
}
|
||||||
let attrs;
|
let attrs;
|
||||||
try {
|
try {
|
||||||
attrs = await parseMessage(stanza);
|
attrs = await parseMessage(stanza, _converse);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return log.error(e);
|
return log.error(e);
|
||||||
}
|
}
|
||||||
@ -114,7 +132,7 @@ export async function handleMessageStanza (stanza) {
|
|||||||
* @typedef { Object } MessageData
|
* @typedef { Object } MessageData
|
||||||
* An object containing the original message stanza, as well as the
|
* An object containing the original message stanza, as well as the
|
||||||
* parsed attributes.
|
* parsed attributes.
|
||||||
* @property { Element } stanza
|
* @property { XMLElement } stanza
|
||||||
* @property { MessageAttributes } stanza
|
* @property { MessageAttributes } stanza
|
||||||
* @property { ChatBox } chatbox
|
* @property { ChatBox } chatbox
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,7 @@ const ChatBoxes = Collection.extend({
|
|||||||
* @event _converse#chatBoxesFetched
|
* @event _converse#chatBoxesFetched
|
||||||
* @type { object }
|
* @type { object }
|
||||||
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox
|
* @property { _converse.ChatBox | _converse.ChatRoom } chatbox
|
||||||
* @property { Element } stanza
|
* @property { XMLElement } stanza
|
||||||
* @example _converse.api.listen.on('chatBoxesFetched', obj => { ... });
|
* @example _converse.api.listen.on('chatBoxesFetched', obj => { ... });
|
||||||
* @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
|
* @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... });
|
||||||
*/
|
*/
|
||||||
|
@ -24,8 +24,8 @@ export default {
|
|||||||
stream: {
|
stream: {
|
||||||
/**
|
/**
|
||||||
* @method api.disco.stream.getFeature
|
* @method api.disco.stream.getFeature
|
||||||
* @param { String } name The feature name
|
* @param {String} name The feature name
|
||||||
* @param { String } xmlns The XML namespace
|
* @param {String} xmlns The XML namespace
|
||||||
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
|
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
|
||||||
*/
|
*/
|
||||||
async getFeature (name, xmlns) {
|
async getFeature (name, xmlns) {
|
||||||
@ -57,10 +57,10 @@ export default {
|
|||||||
* Lets you add new identities for this client (i.e. instance of Converse)
|
* Lets you add new identities for this client (i.e. instance of Converse)
|
||||||
* @method api.disco.own.identities.add
|
* @method api.disco.own.identities.add
|
||||||
*
|
*
|
||||||
* @param { String } category - server, client, gateway, directory, etc.
|
* @param {String} category - server, client, gateway, directory, etc.
|
||||||
* @param { String } type - phone, pc, web, etc.
|
* @param {String} type - phone, pc, web, etc.
|
||||||
* @param { String } name - "Converse"
|
* @param {String} name - "Converse"
|
||||||
* @param { String } lang - en, el, de, etc.
|
* @param {String} lang - en, el, de, etc.
|
||||||
*
|
*
|
||||||
* @example _converse.api.disco.own.identities.clear();
|
* @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)
|
* Lets you register new disco features for this client (i.e. instance of Converse)
|
||||||
* @method api.disco.own.features.add
|
* @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");
|
* @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
|
||||||
*/
|
*/
|
||||||
add (name) {
|
add (name) {
|
||||||
@ -134,8 +134,8 @@ export default {
|
|||||||
* Query for information about an XMPP entity
|
* Query for information about an XMPP entity
|
||||||
*
|
*
|
||||||
* @method api.disco.info
|
* @method api.disco.info
|
||||||
* @param { string } jid The Jabber ID of the entity to query
|
* @param {string} jid The Jabber ID of the entity to query
|
||||||
* @param { string } [node] A specific node identifier associated with the JID
|
* @param {string} [node] A specific node identifier associated with the JID
|
||||||
* @returns {promise} Promise which resolves once we have a result from the server.
|
* @returns {promise} Promise which resolves once we have a result from the server.
|
||||||
*/
|
*/
|
||||||
info (jid, node) {
|
info (jid, node) {
|
||||||
@ -155,8 +155,8 @@ export default {
|
|||||||
* Query for items associated with an XMPP entity
|
* Query for items associated with an XMPP entity
|
||||||
*
|
*
|
||||||
* @method api.disco.items
|
* @method api.disco.items
|
||||||
* @param { string } jid The Jabber ID of the entity to query for 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} [node] A specific node identifier associated with the JID
|
||||||
* @returns {promise} Promise which resolves once we have a result from the server.
|
* @returns {promise} Promise which resolves once we have a result from the server.
|
||||||
*/
|
*/
|
||||||
items (jid, node) {
|
items (jid, node) {
|
||||||
@ -184,8 +184,8 @@ export default {
|
|||||||
* Get the corresponding `DiscoEntity` instance.
|
* Get the corresponding `DiscoEntity` instance.
|
||||||
*
|
*
|
||||||
* @method api.disco.entities.get
|
* @method api.disco.entities.get
|
||||||
* @param { string } jid The Jabber ID of the entity
|
* @param {string} jid The Jabber ID of the entity
|
||||||
* @param { boolean } [create] Whether the entity should be created if it doesn't exist.
|
* @param {boolean} [create] Whether the entity should be created if it doesn't exist.
|
||||||
* @example _converse.api.disco.entities.get(jid);
|
* @example _converse.api.disco.entities.get(jid);
|
||||||
*/
|
*/
|
||||||
async get (jid, create=false) {
|
async get (jid, create=false) {
|
||||||
@ -195,29 +195,19 @@ export default {
|
|||||||
}
|
}
|
||||||
if (_converse.disco_entities === undefined) {
|
if (_converse.disco_entities === undefined) {
|
||||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||||
log.warn(`Tried to look up entity ${jid} but _converse.disco_entities has been torn down`);
|
const msg = `Tried to look up entity ${jid} but _converse.disco_entities has been torn down`;
|
||||||
|
log.warn(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const entity = _converse.disco_entities.get(jid);
|
const entity = _converse.disco_entities.get(jid);
|
||||||
if (entity || !create) {
|
if (entity || !create) {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
return api.disco.entities.create({ jid });
|
return api.disco.entities.create(jid);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return any disco items advertised on this entity
|
* Create a new disco entity. It's identity and features
|
||||||
*
|
|
||||||
* @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
|
* will automatically be fetched from cache or from the
|
||||||
* XMPP server.
|
* XMPP server.
|
||||||
*
|
*
|
||||||
@ -225,17 +215,14 @@ export default {
|
|||||||
* `ignore_cache: true` in the options parameter.
|
* `ignore_cache: true` in the options parameter.
|
||||||
*
|
*
|
||||||
* @method api.disco.entities.create
|
* @method api.disco.entities.create
|
||||||
* @param { object } data
|
* @param {string} jid The Jabber ID of the entity
|
||||||
* @param { string } data.jid - The Jabber ID of the entity
|
* @param {object} [options] Additional options
|
||||||
* @param { string } data.parent_jid - The Jabber ID of the parent entity
|
* @param {boolean} [options.ignore_cache]
|
||||||
* @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
|
* 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 (data, options) {
|
create (jid, options) {
|
||||||
return _converse.disco_entities.create(data, options);
|
return _converse.disco_entities.create({'jid': jid}, options);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -248,11 +235,11 @@ export default {
|
|||||||
* Return a given feature of a disco entity
|
* Return a given feature of a disco entity
|
||||||
*
|
*
|
||||||
* @method api.disco.features.get
|
* @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`
|
* supported. In the XML stanza, this is the `var`
|
||||||
* attribute of the `<feature>` element. For
|
* attribute of the `<feature>` element. For
|
||||||
* example: `http://jabber.org/protocol/muc`
|
* 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
|
* (and its associated items) which should be queried
|
||||||
* @returns {promise} A promise which resolves with a list containing
|
* @returns {promise} A promise which resolves with a list containing
|
||||||
* _converse.Entity instances representing the entity
|
* _converse.Entity instances representing the entity
|
||||||
@ -262,56 +249,22 @@ export default {
|
|||||||
* api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
|
* api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
|
||||||
*/
|
*/
|
||||||
async get (feature, jid) {
|
async get (feature, jid) {
|
||||||
if (!jid) throw new TypeError('You need to provide an entity JID');
|
if (!jid) {
|
||||||
|
throw new TypeError('You need to provide an entity JID');
|
||||||
const entity = await api.disco.entities.get(jid, true);
|
}
|
||||||
|
await api.waitUntil('discoInitialized');
|
||||||
|
let entity = await api.disco.entities.get(jid, true);
|
||||||
|
|
||||||
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
if (_converse.disco_entities === undefined && !api.connection.connected()) {
|
||||||
// Happens during tests when disco lookups happen asynchronously after teardown.
|
// Happens during tests when disco lookups happen asynchronously after teardown.
|
||||||
log.warn(`Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`);
|
const msg = `Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`;
|
||||||
return [];
|
log.warn(msg);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
entity = await entity.waitUntilFeaturesDiscovered;
|
||||||
const promises = [
|
const promises = [...entity.items.map(i => i.hasFeature(feature)), entity.hasFeature(feature)];
|
||||||
entity.getFeature(feature),
|
|
||||||
...api.disco.entities.items(jid).map(i => i.getFeature(feature))
|
|
||||||
];
|
|
||||||
const result = await Promise.all(promises);
|
const result = await Promise.all(promises);
|
||||||
return result.filter(isObject);
|
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);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -319,11 +272,11 @@ export default {
|
|||||||
* Used to determine whether an entity supports a given feature.
|
* Used to determine whether an entity supports a given feature.
|
||||||
*
|
*
|
||||||
* @method api.disco.supports
|
* @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`
|
* supported. In the XML stanza, this is the `var`
|
||||||
* attribute of the `<feature>` element. For
|
* attribute of the `<feature>` element. For
|
||||||
* example: `http://jabber.org/protocol/muc`
|
* 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
|
* (and its associated items) which should be queried
|
||||||
* @returns {promise} A promise which resolves with `true` or `false`.
|
* @returns {promise} A promise which resolves with `true` or `false`.
|
||||||
* @example
|
* @example
|
||||||
@ -333,15 +286,16 @@ export default {
|
|||||||
* // The feature is not supported
|
* // The feature is not supported
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
supports (feature, jid) {
|
async supports (feature, jid) {
|
||||||
return api.disco.features.has(feature, jid);
|
const features = await api.disco.features.get(feature, jid) || [];
|
||||||
|
return features.length > 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the features, fields and identities associated with a
|
* Refresh the features, fields and identities associated with a
|
||||||
* disco entity by refetching them from the server
|
* disco entity by refetching them from the server
|
||||||
* @method api.disco.refresh
|
* @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
|
* @returns {promise} A promise which resolves once the features have been refreshed
|
||||||
* @example
|
* @example
|
||||||
* await api.disco.refresh('room@conference.example.org');
|
* await api.disco.refresh('room@conference.example.org');
|
||||||
@ -362,7 +316,7 @@ export default {
|
|||||||
entity.queryInfo();
|
entity.queryInfo();
|
||||||
} else {
|
} else {
|
||||||
// Create it if it doesn't exist
|
// 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;
|
return entity.waitUntilFeaturesDiscovered;
|
||||||
},
|
},
|
||||||
@ -379,7 +333,7 @@ export default {
|
|||||||
* Return all the features associated with a disco entity
|
* Return all the features associated with a disco entity
|
||||||
*
|
*
|
||||||
* @method api.disco.getFeatures
|
* @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
|
* @returns {promise} A promise which resolves with the returned features
|
||||||
* @example
|
* @example
|
||||||
* const features = await api.disco.getFeatures('room@conference.example.org');
|
* const features = await api.disco.getFeatures('room@conference.example.org');
|
||||||
@ -401,7 +355,7 @@ export default {
|
|||||||
* See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
|
* See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
|
||||||
*
|
*
|
||||||
* @method api.disco.getFields
|
* @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
|
* @example
|
||||||
* const fields = await api.disco.getFields('room@conference.example.org');
|
* const fields = await api.disco.getFields('room@conference.example.org');
|
||||||
*/
|
*/
|
||||||
@ -424,15 +378,15 @@ export default {
|
|||||||
* XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
|
* XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
|
||||||
*
|
*
|
||||||
* @method api.disco.getIdentity
|
* @method api.disco.getIdentity
|
||||||
* @param { string } The identity category.
|
* @param {string} The identity category.
|
||||||
* In the XML stanza, this is the `category`
|
* In the XML stanza, this is the `category`
|
||||||
* attribute of the `<identity>` element.
|
* attribute of the `<identity>` element.
|
||||||
* For example: 'pubsub'
|
* For example: 'pubsub'
|
||||||
* @param { string } type The identity type.
|
* @param {string} type The identity type.
|
||||||
* In the XML stanza, this is the `type`
|
* In the XML stanza, this is the `type`
|
||||||
* attribute of the `<identity>` element.
|
* attribute of the `<identity>` element.
|
||||||
* For example: 'pep'
|
* 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
|
* @returns {promise} A promise which resolves with a map indicating
|
||||||
* whether an identity with a given type is provided by the entity.
|
* whether an identity with a given type is provided by the entity.
|
||||||
* @example
|
* @example
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import log from '@converse/headless/log.js';
|
import log from "@converse/headless/log.js";
|
||||||
import sizzle from 'sizzle';
|
import sizzle from "sizzle";
|
||||||
import { Collection } from '@converse/skeletor/src/collection';
|
import { Collection } from "@converse/skeletor/src/collection";
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
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';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
|
|
||||||
const { Strophe } = converse.env;
|
const { Strophe } = converse.env;
|
||||||
@ -19,7 +19,7 @@ const { Strophe } = converse.env;
|
|||||||
const DiscoEntity = Model.extend({
|
const DiscoEntity = Model.extend({
|
||||||
idAttribute: 'jid',
|
idAttribute: 'jid',
|
||||||
|
|
||||||
initialize (_, options) {
|
async initialize (_, options) {
|
||||||
this.waitUntilFeaturesDiscovered = getOpenPromise();
|
this.waitUntilFeaturesDiscovered = getOpenPromise();
|
||||||
|
|
||||||
this.dataforms = new Collection();
|
this.dataforms = new Collection();
|
||||||
@ -29,12 +29,17 @@ const DiscoEntity = Model.extend({
|
|||||||
this.features = new Collection();
|
this.features = new Collection();
|
||||||
id = `converse.features-${this.get('jid')}`;
|
id = `converse.features-${this.get('jid')}`;
|
||||||
this.features.browserStorage = _converse.createStore(id, 'session');
|
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();
|
this.fields = new Collection();
|
||||||
id = `converse.fields-${this.get('jid')}`;
|
id = `converse.fields-${this.get('jid')}`;
|
||||||
this.fields.browserStorage = _converse.createStore(id, 'session');
|
this.fields.browserStorage = _converse.createStore(id, 'session');
|
||||||
this.listenTo(this.fields, 'add', this.onFieldAdded);
|
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.identities = new Collection();
|
this.identities = new Collection();
|
||||||
id = `converse.identities-${this.get('jid')}`;
|
id = `converse.identities-${this.get('jid')}`;
|
||||||
@ -54,7 +59,7 @@ const DiscoEntity = Model.extend({
|
|||||||
await this.waitUntilFeaturesDiscovered;
|
await this.waitUntilFeaturesDiscovered;
|
||||||
return this.identities.findWhere({
|
return this.identities.findWhere({
|
||||||
'category': category,
|
'category': category,
|
||||||
'type': type,
|
'type': type
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -62,12 +67,12 @@ const DiscoEntity = Model.extend({
|
|||||||
* Returns a Promise which resolves with a map indicating
|
* Returns a Promise which resolves with a map indicating
|
||||||
* whether a given feature is supported.
|
* whether a given feature is supported.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.DiscoEntity#getFeature
|
* @method _converse.DiscoEntity#hasFeature
|
||||||
* @param { String } feature - The feature that might be supported.
|
* @param { String } feature - The feature that might be supported.
|
||||||
*/
|
*/
|
||||||
async getFeature (feature) {
|
async hasFeature (feature) {
|
||||||
await this.waitUntilFeaturesDiscovered;
|
await this.waitUntilFeaturesDiscovered
|
||||||
if (this.features.findWhere({ 'var': feature })) {
|
if (this.features.findWhere({'var': feature})) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -101,7 +106,7 @@ const DiscoEntity = Model.extend({
|
|||||||
} else {
|
} else {
|
||||||
const store_id = this.features.browserStorage.name;
|
const store_id = this.features.browserStorage.name;
|
||||||
const result = await this.features.browserStorage.store.getItem(store_id);
|
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();
|
this.queryInfo();
|
||||||
} else {
|
} else {
|
||||||
this.features.fetch({
|
this.features.fetch({
|
||||||
@ -109,9 +114,9 @@ const DiscoEntity = Model.extend({
|
|||||||
success: () => {
|
success: () => {
|
||||||
this.waitUntilFeaturesDiscovered.resolve(this);
|
this.waitUntilFeaturesDiscovered.resolve(this);
|
||||||
this.trigger('featuresDiscovered');
|
this.trigger('featuresDiscovered');
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
this.identities.fetch({ add: true });
|
this.identities.fetch({add: true});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -130,27 +135,22 @@ const DiscoEntity = Model.extend({
|
|||||||
|
|
||||||
onDiscoItems (stanza) {
|
onDiscoItems (stanza) {
|
||||||
sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => {
|
sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => {
|
||||||
if (item.getAttribute('node')) {
|
if (item.getAttribute("node")) {
|
||||||
// XXX: Ignore nodes for now.
|
// XXX: Ignore nodes for now.
|
||||||
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
|
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const jid = item.getAttribute('jid');
|
const jid = item.getAttribute('jid');
|
||||||
const entity = _converse.disco_entities.get(jid);
|
if (this.items.get(jid) === undefined) {
|
||||||
if (entity) {
|
const entities = _converse.disco_entities;
|
||||||
entity.set({ parent_jids: [this.get('jid')] });
|
const entity = entities.get(jid) || entities.create({ jid, name: item.getAttribute('name') });
|
||||||
} else {
|
this.items.create(entity);
|
||||||
api.disco.entities.create({
|
|
||||||
jid,
|
|
||||||
'parent_jids': [this.get('jid')],
|
|
||||||
'name': item.getAttribute('name'),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async queryForItems () {
|
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
|
// Don't fetch features and items if this is not a
|
||||||
// server or a conference component.
|
// server or a conference component.
|
||||||
return;
|
return;
|
||||||
@ -159,12 +159,12 @@ const DiscoEntity = Model.extend({
|
|||||||
this.onDiscoItems(stanza);
|
this.onDiscoItems(stanza);
|
||||||
},
|
},
|
||||||
|
|
||||||
async onInfo (stanza) {
|
onInfo (stanza) {
|
||||||
Array.from(stanza.querySelectorAll('identity')).forEach(identity => {
|
Array.from(stanza.querySelectorAll('identity')).forEach(identity => {
|
||||||
this.identities.create({
|
this.identities.create({
|
||||||
'category': identity.getAttribute('category'),
|
'category': identity.getAttribute('category'),
|
||||||
'type': identity.getAttribute('type'),
|
'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 => {
|
sizzle('field', form).forEach(field => {
|
||||||
data[field.getAttribute('var')] = {
|
data[field.getAttribute('var')] = {
|
||||||
'value': field.querySelector('value')?.textContent,
|
'value': field.querySelector('value')?.textContent,
|
||||||
'type': field.getAttribute('type'),
|
'type': field.getAttribute('type')
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.dataforms.create(data);
|
this.dataforms.create(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (stanza.querySelector(`feature[var="${Strophe.NS.DISCO_ITEMS}"]`)) {
|
if (stanza.querySelector(`feature[var="${Strophe.NS.DISCO_ITEMS}"]`)) {
|
||||||
await this.queryForItems();
|
this.queryForItems();
|
||||||
}
|
}
|
||||||
Array.from(stanza.querySelectorAll('feature')).forEach(feature => {
|
Array.from(stanza.querySelectorAll('feature')).forEach(feature => {
|
||||||
this.features.create({
|
this.features.create({
|
||||||
'var': feature.getAttribute('var'),
|
'var': feature.getAttribute('var'),
|
||||||
'from': stanza.getAttribute('from'),
|
'from': stanza.getAttribute('from')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -194,13 +194,13 @@ const DiscoEntity = Model.extend({
|
|||||||
this.fields.create({
|
this.fields.create({
|
||||||
'var': field.getAttribute('var'),
|
'var': field.getAttribute('var'),
|
||||||
'value': field.querySelector('value')?.textContent,
|
'value': field.querySelector('value')?.textContent,
|
||||||
'from': stanza.getAttribute('from'),
|
'from': stanza.getAttribute('from')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.waitUntilFeaturesDiscovered.resolve(this);
|
this.waitUntilFeaturesDiscovered.resolve(this);
|
||||||
this.trigger('featuresDiscovered');
|
this.trigger('featuresDiscovered');
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default DiscoEntity;
|
export default DiscoEntity;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
describe("Service Discovery", function () {
|
describe("Service Discovery", function () {
|
||||||
|
|
||||||
describe("Whenever a server is queried for its features", function () {
|
describe("Whenever converse.js queries a server for its features", function () {
|
||||||
|
|
||||||
it("stores the features it receives",
|
it("stores the features it receives",
|
||||||
mock.initConverse(
|
mock.initConverse(
|
||||||
@ -76,12 +76,23 @@ describe("Service Discovery", function () {
|
|||||||
'var': 'jabber:iq:version'});
|
'var': 'jabber:iq:version'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_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 () {
|
await u.waitUntil(function () {
|
||||||
// Converse.js sees that the entity has a disco#items feature,
|
// Converse.js sees that the entity has a disco#items feature,
|
||||||
// so it will make a query for it.
|
// 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;
|
return IQ_stanzas.filter(iq => iq.querySelector('query[xmlns="http://jabber.org/protocol/disco#items"]')).length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* <iq type='result'
|
/* <iq type='result'
|
||||||
* from='catalog.shakespeare.lit'
|
* from='catalog.shakespeare.lit'
|
||||||
* to='romeo@montague.net/orchard'
|
* to='romeo@montague.net/orchard'
|
||||||
@ -108,8 +119,9 @@ describe("Service Discovery", function () {
|
|||||||
* </query>
|
* </query>
|
||||||
* </iq>
|
* </iq>
|
||||||
*/
|
*/
|
||||||
stanza = IQ_stanzas.find(iq => iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]'));
|
stanza = IQ_stanzas.find(function (iq) {
|
||||||
|
return iq.querySelector('iq[to="montague.lit"] query[xmlns="http://jabber.org/protocol/disco#items"]');
|
||||||
|
});
|
||||||
const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
|
const items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)];
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'type': 'result',
|
'type': 'result',
|
||||||
@ -140,19 +152,9 @@ describe("Service Discovery", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
await u.waitUntil(() => _converse.disco_entities);
|
||||||
const entities = await _converse.api.disco.entities.get()
|
entities = _converse.disco_entities;
|
||||||
expect(entities.length).toBe(5); // We have an extra entity, which is the user's JID
|
expect(entities.length).toBe(5);
|
||||||
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([
|
expect(entities.map(e => e.get('jid'))).toEqual([
|
||||||
'montague.lit',
|
'montague.lit',
|
||||||
'romeo@montague.lit',
|
'romeo@montague.lit',
|
||||||
@ -160,14 +162,11 @@ describe("Service Discovery", function () {
|
|||||||
'plays.shakespeare.lit',
|
'plays.shakespeare.lit',
|
||||||
'words.shakespeare.lit'
|
'words.shakespeare.lit'
|
||||||
]);
|
]);
|
||||||
const { api, domain } = _converse;
|
|
||||||
let entity = entities.get(_converse.domain);
|
let entity = entities.get(_converse.domain);
|
||||||
expect(api.disco.entities.items(domain).length).toBe(3);
|
expect(entity.items.length).toBe(3);
|
||||||
|
expect(entity.items.pluck('jid').includes('people.shakespeare.lit')).toBeTruthy();
|
||||||
expect(api.disco.entities.items(domain).map(e => e.get('jid'))).toEqual(
|
expect(entity.items.pluck('jid').includes('plays.shakespeare.lit')).toBeTruthy();
|
||||||
['people.shakespeare.lit', 'plays.shakespeare.lit', 'words.shakespeare.lit']
|
expect(entity.items.pluck('jid').includes('words.shakespeare.lit')).toBeTruthy();
|
||||||
)
|
|
||||||
|
|
||||||
expect(entity.identities.where({'category': 'conference'}).length).toBe(1);
|
expect(entity.identities.where({'category': 'conference'}).length).toBe(1);
|
||||||
expect(entity.identities.where({'category': 'directory'}).length).toBe(1);
|
expect(entity.identities.where({'category': 'directory'}).length).toBe(1);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export async function initializeDisco () {
|
|||||||
const collection = await _converse.disco_entities.fetchEntities();
|
const collection = await _converse.disco_entities.fetchEntities();
|
||||||
if (collection.length === 0 || !collection.get(_converse.domain)) {
|
if (collection.length === 0 || !collection.get(_converse.domain)) {
|
||||||
// If we don't have an entity for our own XMPP server, create one.
|
// If we don't have an entity for our own XMPP server, create one.
|
||||||
api.disco.entities.create({'jid': _converse.domain}, {'ignore_cache': true});
|
_converse.disco_entities.create({'jid': _converse.domain});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Triggered once the `converse-disco` plugin has been initialized and the
|
* Triggered once the `converse-disco` plugin has been initialized and the
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
* @copyright 2022, the Converse.js contributors
|
* @copyright 2022, the Converse.js contributors
|
||||||
* @license Mozilla Public License (MPLv2)
|
* @license Mozilla Public License (MPLv2)
|
||||||
*/
|
*/
|
||||||
import './utils.js';
|
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api, converse } from "../../core.js";
|
import { _converse, api, converse } from "../../core.js";
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
@ -86,8 +85,8 @@ converse.plugins.add('converse-emoji', {
|
|||||||
async initialize () {
|
async initialize () {
|
||||||
if (!converse.emojis.initialized) {
|
if (!converse.emojis.initialized) {
|
||||||
converse.emojis.initialized = true;
|
converse.emojis.initialized = true;
|
||||||
const module = await import(/*webpackChunkName: "emojis" */ './emoji.json');
|
const { default: json } = await import(/*webpackChunkName: "emojis" */ './emoji.json');
|
||||||
const json = converse.emojis.json = module.default;
|
converse.emojis.json = json;
|
||||||
converse.emojis.by_sn = Object.keys(json).reduce((result, cat) => Object.assign(result, json[cat]), {});
|
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 = Object.values(converse.emojis.by_sn);
|
||||||
converse.emojis.list.sort((a, b) => a.sn < b.sn ? -1 : (a.sn > b.sn ? 1 : 0));
|
converse.emojis.list.sort((a, b) => a.sn < b.sn ? -1 : (a.sn > b.sn ? 1 : 0));
|
||||||
|
@ -163,7 +163,7 @@ function shortnamesToUnicode (str) {
|
|||||||
* Determines whether the passed in string is just a single emoji shortname;
|
* Determines whether the passed in string is just a single emoji shortname;
|
||||||
* @namespace u
|
* @namespace u
|
||||||
* @method u.isOnlyEmojis
|
* @method u.isOnlyEmojis
|
||||||
* @param { String } text - A string which migh be just an emoji shortname
|
* @param { String } shortname - A string which migh be just an emoji shortname
|
||||||
* @returns { Boolean }
|
* @returns { Boolean }
|
||||||
*/
|
*/
|
||||||
function isOnlyEmojis (text) {
|
function isOnlyEmojis (text) {
|
||||||
|
@ -15,8 +15,8 @@ export default {
|
|||||||
*
|
*
|
||||||
* @method api.headlines.get
|
* @method api.headlines.get
|
||||||
* @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
|
* @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 {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 {Boolean} [create=false] - Whether the chat should be created if it's not found.
|
||||||
* @returns { Promise<_converse.HeadlinesFeed> }
|
* @returns { Promise<_converse.HeadlinesFeed> }
|
||||||
*/
|
*/
|
||||||
async get (jids, attrs={}, create=false) {
|
async get (jids, attrs={}, create=false) {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import HeadlinesFeed from './feed.js';
|
import HeadlinesFeed from './feed.js';
|
||||||
import headlines_api from './api.js';
|
import headlines_api from './api.js';
|
||||||
import { _converse, api, converse } from "../../core.js";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
import { onHeadlineMessage } from './utils.js';
|
import { onHeadlineMessage } from './utils.js';
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { parseMessage } from '@converse/headless/plugins/chat/parsers';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler method for all incoming messages of type "headline".
|
* Handler method for all incoming messages of type "headline".
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
export async function onHeadlineMessage (stanza) {
|
export async function onHeadlineMessage (stanza) {
|
||||||
if (isHeadline(stanza) || isServerMessage(stanza)) {
|
if (isHeadline(stanza) || isServerMessage(stanza)) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
import { RSM } from '@converse/headless/shared/rsm';
|
||||||
import log from '@converse/headless/log';
|
import log from '@converse/headless/log';
|
||||||
import sizzle from "sizzle";
|
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";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
|
|
||||||
const { Strophe, $iq, dayjs } = converse.env;
|
const { Strophe, $iq, dayjs } = converse.env;
|
||||||
@ -271,7 +270,7 @@ export default {
|
|||||||
const { __ } = _converse;
|
const { __ } = _converse;
|
||||||
const err_msg = __("Timeout while trying to fetch archived messages.");
|
const err_msg = __("Timeout while trying to fetch archived messages.");
|
||||||
log.error(err_msg);
|
log.error(err_msg);
|
||||||
error = new TimeoutError(err_msg);
|
error = new _converse.TimeoutError(err_msg);
|
||||||
return { messages, error };
|
return { messages, error };
|
||||||
|
|
||||||
} else if (u.isErrorStanza(iq_result)) {
|
} else if (u.isErrorStanza(iq_result)) {
|
||||||
|
@ -100,7 +100,7 @@ export async function handleMAMResult (model, result, query, options, should_pag
|
|||||||
/**
|
/**
|
||||||
* @typedef { Object } MAMOptions
|
* @typedef { Object } MAMOptions
|
||||||
* A map of MAM related options that may be passed to fetchArchivedMessages
|
* A map of MAM related options that may be passed to fetchArchivedMessages
|
||||||
* @param { number } [options.max] - The maximum number of items to return.
|
* @param { integer } [options.max] - The maximum number of items to return.
|
||||||
* Defaults to "archived_messages_page_size"
|
* Defaults to "archived_messages_page_size"
|
||||||
* @param { string } [options.after] - The XEP-0359 stanza ID of a message
|
* @param { string } [options.after] - The XEP-0359 stanza ID of a message
|
||||||
* after which messages should be returned. Implies forward paging.
|
* 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.
|
* Fetch XEP-0313 archived messages based on the passed in criteria.
|
||||||
* @param { ChatBox | ChatRoom } model
|
* @param { _converse.ChatBox | _converse.ChatRoom } model
|
||||||
* @param { MAMOptions } [options]
|
* @param { MAMOptions } [options]
|
||||||
* @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether
|
* @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether
|
||||||
* this function should recursively page through the entire result set if a limited
|
* 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 by that user
|
* Given an occupant model, see which affiliations may be assigned to that user.
|
||||||
* @param { Model } occupant
|
* @param { Model } occupant
|
||||||
* @returns { Array<('owner'|'admin'|'member'|'outcast'|'none')> } - An array of assignable affiliations
|
* @returns { Array<('owner'|'admin'|'member'|'outcast'|'none')> } - An array of assignable affiliations
|
||||||
*/
|
*/
|
||||||
@ -54,9 +54,9 @@ export function getAssignableAffiliations (occupant) {
|
|||||||
if (!Array.isArray(disabled)) {
|
if (!Array.isArray(disabled)) {
|
||||||
disabled = disabled ? AFFILIATIONS : [];
|
disabled = disabled ? AFFILIATIONS : [];
|
||||||
}
|
}
|
||||||
if (occupant?.get('affiliation') === 'owner') {
|
if (occupant.get('affiliation') === 'owner') {
|
||||||
return AFFILIATIONS.filter(a => !disabled.includes(a));
|
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));
|
return AFFILIATIONS.filter(a => !['owner', 'admin', ...disabled].includes(a));
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import log from '../../log';
|
import log from '../../log';
|
||||||
|
import u from '../../utils/form';
|
||||||
import { Strophe } from 'strophe.js/src/strophe';
|
import { Strophe } from 'strophe.js/src/strophe';
|
||||||
import { _converse, api, converse } from '../../core.js';
|
import { _converse, api } from '../../core.js';
|
||||||
|
|
||||||
const { u } = converse.env;
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -23,7 +22,7 @@ export default {
|
|||||||
* @method api.rooms.create
|
* @method api.rooms.create
|
||||||
* @param {(string[]|string)} jid|jids The JID or array of
|
* @param {(string[]|string)} jid|jids The JID or array of
|
||||||
* JIDs of the chatroom(s) to create
|
* 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.
|
* @returns {Promise} Promise which resolves with the Model representing the chat.
|
||||||
*/
|
*/
|
||||||
create (jids, attrs = {}) {
|
create (jids, attrs = {}) {
|
||||||
@ -45,24 +44,24 @@ export default {
|
|||||||
* Similar to {@link api.chats.open}, but for groupchats.
|
* Similar to {@link api.chats.open}, but for groupchats.
|
||||||
*
|
*
|
||||||
* @method api.rooms.open
|
* @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).
|
* currently open rooms will be returned).
|
||||||
* @param { string } attrs A map containing any extra room attributes.
|
* @param {string} attrs A map containing any extra room attributes.
|
||||||
* @param { string } [attrs.nick] The current user's nickname for the MUC
|
* @param {string} [attrs.nick] The current user's nickname for the MUC
|
||||||
* @param { boolean } [attrs.auto_configure] A boolean, indicating
|
* @param {boolean} [attrs.auto_configure] A boolean, indicating
|
||||||
* whether the room should be configured automatically or not.
|
* whether the room should be configured automatically or not.
|
||||||
* If set to `true`, then it makes sense to pass in configuration settings.
|
* 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
|
* configured automatically. Currently it doesn't make sense to specify
|
||||||
* `roomconfig` values if `auto_configure` is set to `false`.
|
* `roomconfig` values if `auto_configure` is set to `false`.
|
||||||
* For a list of configuration values that can be passed in, refer to these values
|
* 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).
|
* 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.
|
* 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.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.bring_to_foreground] A boolean indicating whether the room should be
|
||||||
* brought to the foreground and therefore replace the currently shown chat.
|
* brought to the foreground and therefore replace the currently shown chat.
|
||||||
* If there is no chat currently open, then this option is ineffective.
|
* 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
|
* room won't be maximized (in `overlayed` view mode) and in
|
||||||
* `fullscreen` view mode a newly opened room won't replace
|
* `fullscreen` view mode a newly opened room won't replace
|
||||||
* another chat already in the foreground.
|
* another chat already in the foreground.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import log from '../../log';
|
||||||
import { Strophe } from 'strophe.js/src/strophe';
|
import { Strophe } from 'strophe.js/src/strophe';
|
||||||
import { _converse, api } from '../../core.js';
|
import { _converse, api } from '../../core.js';
|
||||||
|
|
||||||
@ -19,7 +20,6 @@ const ChatRoomMessageMixin = {
|
|||||||
this.on('change:type', () => this.setOccupant());
|
this.on('change:type', () => this.setOccupant());
|
||||||
this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage());
|
this.on('change:is_ephemeral', () => this.setTimerForEphemeralMessage());
|
||||||
|
|
||||||
this.chatbox = this.collection?.chatbox;
|
|
||||||
this.setTimerForEphemeralMessage();
|
this.setTimerForEphemeralMessage();
|
||||||
this.setOccupant();
|
this.setOccupant();
|
||||||
/**
|
/**
|
||||||
@ -53,20 +53,24 @@ const ChatRoomMessageMixin = {
|
|||||||
return (
|
return (
|
||||||
['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) &&
|
['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) &&
|
||||||
this.get(`stanza_id ${this.get('from_muc')}`) &&
|
this.get(`stanza_id ${this.get('from_muc')}`) &&
|
||||||
this.chatbox.canModerateMessages()
|
this.collection.chatbox.canModerateMessages()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
checkValidity () {
|
checkValidity () {
|
||||||
const result = _converse.Message.prototype.checkValidity.call(this);
|
const result = _converse.Message.prototype.checkValidity.call(this);
|
||||||
!result && this.chatbox.debouncedRejoin();
|
!result && this.collection.chatbox.debouncedRejoin();
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
onOccupantRemoved () {
|
onOccupantRemoved () {
|
||||||
this.stopListening(this.occupant);
|
this.stopListening(this.occupant);
|
||||||
delete this.occupant;
|
delete this.occupant;
|
||||||
this.listenTo(this.chatbox.occupants, 'add', this.onOccupantAdded);
|
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);
|
||||||
},
|
},
|
||||||
|
|
||||||
onOccupantAdded (occupant) {
|
onOccupantAdded (occupant) {
|
||||||
@ -77,6 +81,10 @@ const ChatRoomMessageMixin = {
|
|||||||
} else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) {
|
} else if (occupant.get('nick') !== Strophe.getResourceFromJid(this.get('from'))) {
|
||||||
return;
|
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;
|
this.occupant = occupant;
|
||||||
if (occupant.get('jid')) {
|
if (occupant.get('jid')) {
|
||||||
@ -85,40 +93,32 @@ const ChatRoomMessageMixin = {
|
|||||||
|
|
||||||
this.trigger('occupantAdded');
|
this.trigger('occupantAdded');
|
||||||
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
||||||
this.stopListening(this.chatbox.occupants, 'add', this.onOccupantAdded);
|
this.stopListening(chatbox.occupants, 'add', this.onOccupantAdded);
|
||||||
},
|
|
||||||
|
|
||||||
getOccupant() {
|
|
||||||
if (this.occupant) return this.occupant;
|
|
||||||
|
|
||||||
this.setOccupant();
|
|
||||||
return this.occupant;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setOccupant () {
|
setOccupant () {
|
||||||
if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) {
|
if (this.get('type') !== 'groupchat' || this.isEphemeral() || this.occupant) {
|
||||||
return;
|
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 nick = Strophe.getResourceFromJid(this.get('from'));
|
||||||
const occupant_id = this.get('occupant_id');
|
const occupant_id = this.get('occupant_id');
|
||||||
|
this.occupant = chatbox.occupants.findOccupant({ nick, occupant_id });
|
||||||
|
|
||||||
this.occupant = this.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' });
|
||||||
if (!this.occupant) {
|
const jid = `${chatbox.get('jid')}/${nick}`;
|
||||||
this.occupant = this.chatbox.occupants.create({
|
api.user.presence.send('probe', jid);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
if (this.occupant) {
|
||||||
|
this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved);
|
||||||
|
} else {
|
||||||
|
this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
|
import invoke from 'lodash-es/invoke';
|
||||||
import isElement from 'lodash-es/isElement';
|
import isElement from 'lodash-es/isElement';
|
||||||
import log from '../../log';
|
import log from '../../log';
|
||||||
import p from '../../utils/parse-helpers';
|
import p from '../../utils/parse-helpers';
|
||||||
import pick from 'lodash-es/pick';
|
import pick from 'lodash-es/pick';
|
||||||
import sizzle from 'sizzle';
|
import sizzle from 'sizzle';
|
||||||
|
import u from '../../utils/form';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
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 { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
|
||||||
import { TimeoutError } from '../../shared/errors.js';
|
|
||||||
import { _converse, api, converse } from '../../core.js';
|
import { _converse, api, converse } from '../../core.js';
|
||||||
import { computeAffiliationsDelta, setAffiliations, getAffiliationList } from './affiliations/utils.js';
|
import { computeAffiliationsDelta, setAffiliations, getAffiliationList } from './affiliations/utils.js';
|
||||||
|
import { handleCorrection } from '@converse/headless/shared/chat/utils.js';
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
import { handleCorrection } from '../../shared/chat/utils.js';
|
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||||
import { initStorage } from '../../utils/storage.js';
|
import { isArchived, getMediaURLsMetadata } from '@converse/headless/shared/parsers.js';
|
||||||
import { isArchived, getMediaURLsMetadata } from '../../shared/parsers.js';
|
import { isUniView, getUniqueId, safeSave } from '@converse/headless/utils/core.js';
|
||||||
import { isUniView, getUniqueId, safeSave } from '../../utils/core.js';
|
|
||||||
import { parseMUCMessage, parseMUCPresence } from './parsers.js';
|
import { parseMUCMessage, parseMUCPresence } from './parsers.js';
|
||||||
import { sendMarker } from '../../shared/actions.js';
|
import { sendMarker } from '@converse/headless/shared/actions.js';
|
||||||
|
import { ROOMSTATUS } from './constants.js';
|
||||||
const { u } = converse.env;
|
|
||||||
|
|
||||||
const OWNER_COMMANDS = ['owner'];
|
const OWNER_COMMANDS = ['owner'];
|
||||||
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
|
const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke'];
|
||||||
@ -130,15 +129,6 @@ const ChatRoomMixin = {
|
|||||||
return this.session.get('connection_status') === 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';
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether we're still joined and if so, restores the MUC state from cache.
|
* Checks whether we're still joined and if so, restores the MUC state from cache.
|
||||||
* @private
|
* @private
|
||||||
@ -146,23 +136,18 @@ const ChatRoomMixin = {
|
|||||||
* @returns { Boolean } Returns `true` if we're still joined, otherwise returns `false`.
|
* @returns { Boolean } Returns `true` if we're still joined, otherwise returns `false`.
|
||||||
*/
|
*/
|
||||||
async restoreFromCache () {
|
async restoreFromCache () {
|
||||||
if (this.isEntered()) {
|
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 }));
|
||||||
await this.fetchOccupants().catch(e => log.error(e));
|
await this.fetchOccupants().catch(e => log.error(e));
|
||||||
|
await this.fetchMessages().catch(e => log.error(e));
|
||||||
if (this.isRAICandidate()) {
|
return true;
|
||||||
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
|
} else {
|
||||||
this.enableRAI();
|
this.session.save('connection_status', ROOMSTATUS.DISCONNECTED);
|
||||||
return true;
|
this.clearOccupantsCache();
|
||||||
} else if (await this.isJoined()) {
|
return false;
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,7 +211,7 @@ const ChatRoomMixin = {
|
|||||||
* *Hook* which allows plugins to update an outgoing MUC join presence stanza
|
* *Hook* which allows plugins to update an outgoing MUC join presence stanza
|
||||||
* @event _converse#constructedMUCPresence
|
* @event _converse#constructedMUCPresence
|
||||||
* @param { _converse.ChatRoom } - The MUC from which this message stanza is being sent.
|
* @param { _converse.ChatRoom } - The MUC from which this message stanza is being sent.
|
||||||
* @param { Element } stanza - The stanza which will be sent out
|
* @param { XMLElement } stanza - The stanza which will be sent out
|
||||||
*/
|
*/
|
||||||
stanza = await api.hook('constructedMUCPresence', this, stanza);
|
stanza = await api.hook('constructedMUCPresence', this, stanza);
|
||||||
return stanza;
|
return stanza;
|
||||||
@ -291,7 +276,10 @@ const ChatRoomMixin = {
|
|||||||
const roomstatus = ROOMSTATUS;
|
const roomstatus = ROOMSTATUS;
|
||||||
const conn_status = this.session.get('connection_status');
|
const conn_status = this.session.get('connection_status');
|
||||||
if (this.get('hidden')) {
|
if (this.get('hidden')) {
|
||||||
if (conn_status === roomstatus.ENTERED && this.isRAICandidate()) {
|
if (conn_status === roomstatus.ENTERED &&
|
||||||
|
api.settings.get('muc_subscribe_to_rai') &&
|
||||||
|
this.getOwnAffiliation() !== 'none') {
|
||||||
|
|
||||||
this.sendMarkerForLastMessage('received', true);
|
this.sendMarkerForLastMessage('received', true);
|
||||||
await this.leave();
|
await this.leave();
|
||||||
this.enableRAI();
|
this.enableRAI();
|
||||||
@ -359,7 +347,7 @@ const ChatRoomMixin = {
|
|||||||
|
|
||||||
async onConnectionStatusChanged () {
|
async onConnectionStatusChanged () {
|
||||||
if (this.isEntered()) {
|
if (this.isEntered()) {
|
||||||
if (this.isRAICandidate()) {
|
if (this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') {
|
||||||
try {
|
try {
|
||||||
await this.leave();
|
await this.leave();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -456,7 +444,7 @@ const ChatRoomMixin = {
|
|||||||
|
|
||||||
async handleErrorMessageStanza (stanza) {
|
async handleErrorMessageStanza (stanza) {
|
||||||
const { __ } = _converse;
|
const { __ } = _converse;
|
||||||
const attrs = await parseMUCMessage(stanza, this);
|
const attrs = await parseMUCMessage(stanza, this, _converse);
|
||||||
if (!(await this.shouldShowErrorMessage(attrs))) {
|
if (!(await this.shouldShowErrorMessage(attrs))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -507,7 +495,7 @@ const ChatRoomMixin = {
|
|||||||
* Handles incoming message stanzas from the service that hosts this MUC
|
* Handles incoming message stanzas from the service that hosts this MUC
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#handleMessageFromMUCHost
|
* @method _converse.ChatRoom#handleMessageFromMUCHost
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
handleMessageFromMUCHost (stanza) {
|
handleMessageFromMUCHost (stanza) {
|
||||||
if (this.isEntered()) {
|
if (this.isEntered()) {
|
||||||
@ -528,7 +516,7 @@ const ChatRoomMixin = {
|
|||||||
* Handles XEP-0452 MUC Mention Notification messages
|
* Handles XEP-0452 MUC Mention Notification messages
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#handleForwardedMentions
|
* @method _converse.ChatRoom#handleForwardedMentions
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
handleForwardedMentions (stanza) {
|
handleForwardedMentions (stanza) {
|
||||||
if (this.isEntered()) {
|
if (this.isEntered()) {
|
||||||
@ -547,7 +535,7 @@ const ChatRoomMixin = {
|
|||||||
'num_unread': this.get('num_unread') + mentions.length
|
'num_unread': this.get('num_unread') + mentions.length
|
||||||
});
|
});
|
||||||
mentions.forEach(async stanza => {
|
mentions.forEach(async stanza => {
|
||||||
const attrs = await parseMUCMessage(stanza, this);
|
const attrs = await parseMUCMessage(stanza, this, _converse);
|
||||||
const data = { stanza, attrs, 'chatbox': this };
|
const data = { stanza, attrs, 'chatbox': this };
|
||||||
api.trigger('message', data);
|
api.trigger('message', data);
|
||||||
});
|
});
|
||||||
@ -558,11 +546,9 @@ const ChatRoomMixin = {
|
|||||||
* Parses an incoming message stanza and queues it for processing.
|
* Parses an incoming message stanza and queues it for processing.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#handleMessageStanza
|
* @method _converse.ChatRoom#handleMessageStanza
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
async handleMessageStanza (stanza) {
|
async handleMessageStanza (stanza) {
|
||||||
stanza = stanza.tree?.() ?? stanza;
|
|
||||||
|
|
||||||
const type = stanza.getAttribute('type');
|
const type = stanza.getAttribute('type');
|
||||||
if (type === 'error') {
|
if (type === 'error') {
|
||||||
return this.handleErrorMessageStanza(stanza);
|
return this.handleErrorMessageStanza(stanza);
|
||||||
@ -588,7 +574,7 @@ const ChatRoomMixin = {
|
|||||||
*/
|
*/
|
||||||
let attrs;
|
let attrs;
|
||||||
try {
|
try {
|
||||||
attrs = await parseMUCMessage(stanza, this);
|
attrs = await parseMUCMessage(stanza, this, _converse);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return log.error(e);
|
return log.error(e);
|
||||||
}
|
}
|
||||||
@ -709,9 +695,9 @@ const ChatRoomMixin = {
|
|||||||
* or error message within a specific timeout period.
|
* or error message within a specific timeout period.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#sendTimedMessage
|
* @method _converse.ChatRoom#sendTimedMessage
|
||||||
* @param { _converse.Message|Element } message
|
* @param { _converse.Message|XMLElement } message
|
||||||
* @returns { Promise<Element>|Promise<TimeoutError> } Returns a promise
|
* @returns { Promise<XMLElement>|Promise<_converse.TimeoutError> } Returns a promise
|
||||||
* which resolves with the reflected message stanza or with an error stanza or {@link TimeoutError}.
|
* which resolves with the reflected message stanza or with an error stanza or {@link _converse.TimeoutError}.
|
||||||
*/
|
*/
|
||||||
sendTimedMessage (el) {
|
sendTimedMessage (el) {
|
||||||
if (typeof el.tree === 'function') {
|
if (typeof el.tree === 'function') {
|
||||||
@ -724,10 +710,9 @@ const ChatRoomMixin = {
|
|||||||
el.setAttribute('id', id);
|
el.setAttribute('id', id);
|
||||||
}
|
}
|
||||||
const promise = getOpenPromise();
|
const promise = getOpenPromise();
|
||||||
const timeout = api.settings.get('stanza_timeout');
|
const timeoutHandler = _converse.connection.addTimedHandler(_converse.STANZA_TIMEOUT, () => {
|
||||||
const timeoutHandler = _converse.connection.addTimedHandler(timeout, () => {
|
|
||||||
_converse.connection.deleteHandler(handler);
|
_converse.connection.deleteHandler(handler);
|
||||||
const err = new TimeoutError('Timeout Error: No response from server');
|
const err = new _converse.TimeoutError('Timeout Error: No response from server');
|
||||||
promise.resolve(err);
|
promise.resolve(err);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -770,14 +755,14 @@ const ChatRoomMixin = {
|
|||||||
message.set({
|
message.set({
|
||||||
'retracted': new Date().toISOString(),
|
'retracted': new Date().toISOString(),
|
||||||
'retracted_id': origin_id,
|
'retracted_id': origin_id,
|
||||||
'retraction_id': stanza.tree().getAttribute('id'),
|
'retraction_id': stanza.nodeTree.getAttribute('id'),
|
||||||
'editable': false
|
'editable': false
|
||||||
});
|
});
|
||||||
const result = await this.sendTimedMessage(stanza);
|
const result = await this.sendTimedMessage(stanza);
|
||||||
|
|
||||||
if (u.isErrorStanza(result)) {
|
if (u.isErrorStanza(result)) {
|
||||||
log.error(result);
|
log.error(result);
|
||||||
} else if (result instanceof TimeoutError) {
|
} else if (result instanceof _converse.TimeoutError) {
|
||||||
log.error(result);
|
log.error(result);
|
||||||
message.save({
|
message.save({
|
||||||
editable,
|
editable,
|
||||||
@ -794,7 +779,7 @@ const ChatRoomMixin = {
|
|||||||
* Retract someone else's message in this groupchat.
|
* Retract someone else's message in this groupchat.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#retractOtherMessage
|
* @method _converse.ChatRoom#retractOtherMessage
|
||||||
* @param { _converse.ChatRoomMessage } message - The message which we're retracting.
|
* @param { _converse.Message } message - The message which we're retracting.
|
||||||
* @param { string } [reason] - The reason for retracting the message.
|
* @param { string } [reason] - The reason for retracting the message.
|
||||||
* @example
|
* @example
|
||||||
* const room = await api.rooms.get(jid);
|
* const room = await api.rooms.get(jid);
|
||||||
@ -829,7 +814,7 @@ const ChatRoomMixin = {
|
|||||||
* Sends an IQ stanza to the XMPP server to retract a message in this groupchat.
|
* Sends an IQ stanza to the XMPP server to retract a message in this groupchat.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#sendRetractionIQ
|
* @method _converse.ChatRoom#sendRetractionIQ
|
||||||
* @param { _converse.ChatRoomMessage } message - The message which we're retracting.
|
* @param { _converse.Message } message - The message which we're retracting.
|
||||||
* @param { string } [reason] - The reason for retracting the message.
|
* @param { string } [reason] - The reason for retracting the message.
|
||||||
*/
|
*/
|
||||||
sendRetractionIQ (message, reason) {
|
sendRetractionIQ (message, reason) {
|
||||||
@ -1244,7 +1229,7 @@ const ChatRoomMixin = {
|
|||||||
* 'roomconfig' data.
|
* 'roomconfig' data.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#autoConfigureChatRoom
|
* @method _converse.ChatRoom#autoConfigureChatRoom
|
||||||
* @returns { Promise<Element> }
|
* @returns { Promise<XMLElement> }
|
||||||
* Returns a promise which resolves once a response IQ has
|
* Returns a promise which resolves once a response IQ has
|
||||||
* been received.
|
* been received.
|
||||||
*/
|
*/
|
||||||
@ -1263,7 +1248,7 @@ const ChatRoomMixin = {
|
|||||||
* has been received.
|
* has been received.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#fetchRoomConfiguration
|
* @method _converse.ChatRoom#fetchRoomConfiguration
|
||||||
* @returns { Promise<Element> }
|
* @returns { Promise<XMLElement> }
|
||||||
*/
|
*/
|
||||||
fetchRoomConfiguration () {
|
fetchRoomConfiguration () {
|
||||||
return api.sendIQ($iq({ 'to': this.get('jid'), 'type': 'get' }).c('query', { xmlns: Strophe.NS.MUC_OWNER }));
|
return api.sendIQ($iq({ 'to': this.get('jid'), 'type': 'get' }).c('query', { xmlns: Strophe.NS.MUC_OWNER }));
|
||||||
@ -1274,7 +1259,7 @@ const ChatRoomMixin = {
|
|||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#sendConfiguration
|
* @method _converse.ChatRoom#sendConfiguration
|
||||||
* @param { Array } config - The groupchat configuration
|
* @param { Array } config - The groupchat configuration
|
||||||
* @returns { Promise<Element> } - A promise which resolves with
|
* @returns { Promise<XMLElement> } - A promise which resolves with
|
||||||
* the `result` stanza received from the XMPP server.
|
* the `result` stanza received from the XMPP server.
|
||||||
*/
|
*/
|
||||||
sendConfiguration (config = []) {
|
sendConfiguration (config = []) {
|
||||||
@ -1716,7 +1701,7 @@ const ChatRoomMixin = {
|
|||||||
* Given a presence stanza, update the occupant model based on its contents.
|
* Given a presence stanza, update the occupant model based on its contents.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#updateOccupantsOnPresence
|
* @method _converse.ChatRoom#updateOccupantsOnPresence
|
||||||
* @param { Element } pres - The presence stanza
|
* @param { XMLElement } pres - The presence stanza
|
||||||
*/
|
*/
|
||||||
updateOccupantsOnPresence (pres) {
|
updateOccupantsOnPresence (pres) {
|
||||||
const data = parseMUCPresence(pres, this);
|
const data = parseMUCPresence(pres, this);
|
||||||
@ -1903,7 +1888,7 @@ const ChatRoomMixin = {
|
|||||||
* the `from` attribute. Doesn't check the `type` attribute.
|
* the `from` attribute. Doesn't check the `type` attribute.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#isOwnMessage
|
* @method _converse.ChatRoom#isOwnMessage
|
||||||
* @param { Object|Element|_converse.Message } msg
|
* @param { Object|XMLElement|_converse.Message } msg
|
||||||
* @returns { boolean }
|
* @returns { boolean }
|
||||||
*/
|
*/
|
||||||
isOwnMessage (msg) {
|
isOwnMessage (msg) {
|
||||||
@ -1943,10 +1928,6 @@ const ChatRoomMixin = {
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async isJoined () {
|
async isJoined () {
|
||||||
if (!this.isEntered()) {
|
|
||||||
log.info(`isJoined: not pinging MUC ${this.get('jid')} since we're not entered`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!api.connection.connected()) {
|
if (!api.connection.connected()) {
|
||||||
await new Promise(resolve => api.listen.once('reconnected', resolve));
|
await new Promise(resolve => api.listen.once('reconnected', resolve));
|
||||||
}
|
}
|
||||||
@ -1971,14 +1952,10 @@ const ChatRoomMixin = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether we're still joined and re-join if not
|
* Check whether we're still joined and re-join if not
|
||||||
|
* @async
|
||||||
* @method _converse.ChatRoom#rejoinIfNecessary
|
* @method _converse.ChatRoom#rejoinIfNecessary
|
||||||
*/
|
*/
|
||||||
async 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())) {
|
if (!(await this.isJoined())) {
|
||||||
this.rejoin();
|
this.rejoin();
|
||||||
return true;
|
return true;
|
||||||
@ -2151,7 +2128,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
|
* @param {String|Array<String>} states - The state or states representing the type of notificcation
|
||||||
*/
|
*/
|
||||||
removeNotification (actor, states) {
|
removeNotification (actor, states) {
|
||||||
@ -2175,8 +2152,8 @@ const ChatRoomMixin = {
|
|||||||
*
|
*
|
||||||
* The state can be a XEP-0085 Chat State or a XEP-0045 join/leave
|
* The state can be a XEP-0085 Chat State or a XEP-0045 join/leave
|
||||||
* state.
|
* state.
|
||||||
* @param { String } actor - The nickname of the actor that causes the notification
|
* @param {String} actor - The nickname of the actor that causes the notification
|
||||||
* @param { String } state - The state representing the type of notificcation
|
* @param {String} state - The state representing the type of notificcation
|
||||||
*/
|
*/
|
||||||
updateNotifications (actor, state) {
|
updateNotifications (actor, state) {
|
||||||
const actors_per_state = this.notifications.toJSON();
|
const actors_per_state = this.notifications.toJSON();
|
||||||
@ -2224,7 +2201,7 @@ const ChatRoomMixin = {
|
|||||||
/**
|
/**
|
||||||
* Given {@link MessageAttributes} look for XEP-0316 Room Notifications and create info
|
* Given {@link MessageAttributes} look for XEP-0316 Room Notifications and create info
|
||||||
* messages for them.
|
* messages for them.
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
handleMEPNotification (attrs) {
|
handleMEPNotification (attrs) {
|
||||||
if (attrs.from !== this.get('jid') || !attrs.activities) {
|
if (attrs.from !== this.get('jid') || !attrs.activities) {
|
||||||
@ -2321,7 +2298,7 @@ const ChatRoomMixin = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a presence stanza that disconnects the user from the MUC
|
* Handle a presence stanza that disconnects the user from the MUC
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
handleDisconnection (stanza) {
|
handleDisconnection (stanza) {
|
||||||
const is_self = stanza.querySelector("status[code='110']") !== null;
|
const is_self = stanza.querySelector("status[code='110']") !== null;
|
||||||
@ -2343,7 +2320,7 @@ const ChatRoomMixin = {
|
|||||||
// each <x/> element pertains to a single user.
|
// each <x/> element pertains to a single user.
|
||||||
const item = x.querySelector('item');
|
const item = x.querySelector('item');
|
||||||
const reason = item ? item.querySelector('reason')?.textContent : undefined;
|
const reason = item ? item.querySelector('reason')?.textContent : undefined;
|
||||||
const actor = item ? item.querySelector('actor')?.getAttribute('nick') : undefined;
|
const actor = item ? invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined;
|
||||||
const message = _converse.muc.disconnect_messages[codes[0]];
|
const message = _converse.muc.disconnect_messages[codes[0]];
|
||||||
const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
|
const status = codes.includes('301') ? ROOMSTATUS.BANNED : ROOMSTATUS.DISCONNECTED;
|
||||||
this.setDisconnectionState(message, reason, actor, status);
|
this.setDisconnectionState(message, reason, actor, status);
|
||||||
@ -2456,7 +2433,7 @@ const ChatRoomMixin = {
|
|||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#createInfoMessage
|
* @method _converse.ChatRoom#createInfoMessage
|
||||||
* @param { string } code - The MUC status code
|
* @param { string } code - The MUC status code
|
||||||
* @param { Element } stanza - The original stanza that contains the code
|
* @param { XMLElement } stanza - The original stanza that contains the code
|
||||||
* @param { Boolean } is_self - Whether this stanza refers to our own presence
|
* @param { Boolean } is_self - Whether this stanza refers to our own presence
|
||||||
*/
|
*/
|
||||||
createInfoMessage (code, stanza, is_self) {
|
createInfoMessage (code, stanza, is_self) {
|
||||||
@ -2478,15 +2455,14 @@ const ChatRoomMixin = {
|
|||||||
} else if (is_self && code in _converse.muc.new_nickname_messages) {
|
} 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
|
// XXX: Side-effect of setting the nick. Should ideally be refactored out of this method
|
||||||
let nick;
|
let nick;
|
||||||
if (code === '210') {
|
if (is_self && code === '210') {
|
||||||
nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
|
nick = Strophe.getResourceFromJid(stanza.getAttribute('from'));
|
||||||
} else if (code === '303') {
|
} else if (is_self && code === '303') {
|
||||||
nick = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop().getAttribute('nick');
|
nick = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop().getAttribute('nick');
|
||||||
}
|
}
|
||||||
this.save('nick', nick);
|
this.save('nick', nick);
|
||||||
data.message = __(_converse.muc.new_nickname_messages[code], nick);
|
data.message = __(_converse.muc.new_nickname_messages[code], nick);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.message) {
|
if (data.message) {
|
||||||
if (code === '201' && this.messages.findWhere(data)) {
|
if (code === '201' && this.messages.findWhere(data)) {
|
||||||
return;
|
return;
|
||||||
@ -2499,7 +2475,7 @@ const ChatRoomMixin = {
|
|||||||
* Create info messages based on a received presence or message stanza
|
* Create info messages based on a received presence or message stanza
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#createInfoMessages
|
* @method _converse.ChatRoom#createInfoMessages
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
createInfoMessages (stanza) {
|
createInfoMessages (stanza) {
|
||||||
const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
|
const codes = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code'));
|
||||||
@ -2518,7 +2494,7 @@ const ChatRoomMixin = {
|
|||||||
* implied by) the server.
|
* implied by) the server.
|
||||||
* @param { String } reason - The reason provided for the disconnection
|
* @param { String } reason - The reason provided for the disconnection
|
||||||
* @param { String } actor - The person (if any) responsible for this disconnection
|
* @param { String } actor - The person (if any) responsible for this disconnection
|
||||||
* @param { number } status - The status code (see `ROOMSTATUS`)
|
* @param { Integer } status - The status code (see `ROOMSTATUS`)
|
||||||
*/
|
*/
|
||||||
setDisconnectionState (message, reason, actor, status=ROOMSTATUS.DISCONNECTED) {
|
setDisconnectionState (message, reason, actor, status=ROOMSTATUS.DISCONNECTED) {
|
||||||
this.session.save({
|
this.session.save({
|
||||||
@ -2555,7 +2531,7 @@ const ChatRoomMixin = {
|
|||||||
* `connection_status` value for this {@link _converse.ChatRoom} as
|
* `connection_status` value for this {@link _converse.ChatRoom} as
|
||||||
* well as any additional output that can be shown to the user.
|
* well as any additional output that can be shown to the user.
|
||||||
* @private
|
* @private
|
||||||
* @param { Element } stanza - The presence stanza
|
* @param { XMLElement } stanza - The presence stanza
|
||||||
*/
|
*/
|
||||||
onErrorPresence (stanza) {
|
onErrorPresence (stanza) {
|
||||||
const __ = _converse.__;
|
const __ = _converse.__;
|
||||||
@ -2620,7 +2596,7 @@ const ChatRoomMixin = {
|
|||||||
* Listens for incoming presence stanzas from the service that hosts this MUC
|
* Listens for incoming presence stanzas from the service that hosts this MUC
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#onPresenceFromMUCHost
|
* @method _converse.ChatRoom#onPresenceFromMUCHost
|
||||||
* @param { Element } stanza - The presence stanza
|
* @param { XMLElement } stanza - The presence stanza
|
||||||
*/
|
*/
|
||||||
onPresenceFromMUCHost (stanza) {
|
onPresenceFromMUCHost (stanza) {
|
||||||
if (stanza.getAttribute('type') === 'error') {
|
if (stanza.getAttribute('type') === 'error') {
|
||||||
@ -2639,7 +2615,7 @@ const ChatRoomMixin = {
|
|||||||
* Handles incoming presence stanzas coming from the MUC
|
* Handles incoming presence stanzas coming from the MUC
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#onPresence
|
* @method _converse.ChatRoom#onPresence
|
||||||
* @param { Element } stanza
|
* @param { XMLElement } stanza
|
||||||
*/
|
*/
|
||||||
onPresence (stanza) {
|
onPresence (stanza) {
|
||||||
if (stanza.getAttribute('type') === 'error') {
|
if (stanza.getAttribute('type') === 'error') {
|
||||||
@ -2672,7 +2648,7 @@ const ChatRoomMixin = {
|
|||||||
* user is the groupchat's owner.
|
* user is the groupchat's owner.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#onOwnPresence
|
* @method _converse.ChatRoom#onOwnPresence
|
||||||
* @param { Element } pres - The stanza
|
* @param { XMLElement } pres - The stanza
|
||||||
*/
|
*/
|
||||||
async onOwnPresence (stanza) {
|
async onOwnPresence (stanza) {
|
||||||
await this.occupants.fetched;
|
await this.occupants.fetched;
|
||||||
|
@ -10,9 +10,9 @@ class ChatRoomOccupant extends Model {
|
|||||||
|
|
||||||
defaults () { // eslint-disable-line class-methods-use-this
|
defaults () { // eslint-disable-line class-methods-use-this
|
||||||
return {
|
return {
|
||||||
hats: [],
|
'hats': [],
|
||||||
show: 'offline',
|
'show': 'offline',
|
||||||
states: []
|
'states': []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import ChatRoomOccupant from './occupant.js';
|
import ChatRoomOccupant from './occupant.js';
|
||||||
|
import u from '../../utils/form';
|
||||||
import { Collection } from '@converse/skeletor/src/collection.js';
|
import { Collection } from '@converse/skeletor/src/collection.js';
|
||||||
import { MUC_ROLE_WEIGHTS } from './constants.js';
|
import { MUC_ROLE_WEIGHTS } from './constants.js';
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { Strophe } from 'strophe.js/src/strophe.js';
|
import { Strophe } from 'strophe.js/src/strophe.js';
|
||||||
import { _converse, api, converse } from '../../core.js';
|
import { _converse, api } from '../../core.js';
|
||||||
import { getAffiliationList } from './affiliations/utils.js';
|
import { getAffiliationList } from './affiliations/utils.js';
|
||||||
import { getAutoFetchedAffiliationLists } from './utils.js';
|
import { getAutoFetchedAffiliationLists } from './utils.js';
|
||||||
import { getUniqueId } from '@converse/headless/utils/core.js';
|
import { getUniqueId } from '@converse/headless/utils/core.js';
|
||||||
|
|
||||||
const { u } = converse.env;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of {@link _converse.ChatRoomOccupant} instances, representing participants in a MUC.
|
* A list of {@link _converse.ChatRoomOccupant} instances, representing participants in a MUC.
|
||||||
|
@ -27,7 +27,7 @@ const { NS } = Strophe;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a message stanza for XEP-0317 MEP notification data
|
* Parses a message stanza for XEP-0317 MEP notification data
|
||||||
* @param { Element } stanza - The message stanza
|
* @param { XMLElement } stanza - The message stanza
|
||||||
* @returns { Array } Returns an array of objects representing <activity> elements.
|
* @returns { Array } Returns an array of objects representing <activity> elements.
|
||||||
*/
|
*/
|
||||||
export function getMEPActivities (stanza) {
|
export function getMEPActivities (stanza) {
|
||||||
@ -61,7 +61,7 @@ export function getMEPActivities (stanza) {
|
|||||||
* Note, this function doesn't check whether this is actually a MAM archived stanza.
|
* Note, this function doesn't check whether this is actually a MAM archived stanza.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param { Element } stanza - The message stanza
|
* @param { XMLElement } stanza - The message stanza
|
||||||
* @returns { Object }
|
* @returns { Object }
|
||||||
*/
|
*/
|
||||||
function getJIDFromMUCUserData (stanza) {
|
function getJIDFromMUCUserData (stanza) {
|
||||||
@ -71,8 +71,8 @@ function getJIDFromMUCUserData (stanza) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param { Element } stanza - The message stanza
|
* @param { XMLElement } stanza - The message stanza
|
||||||
* @param { Element } original_stanza - The original stanza, that contains the
|
* @param { XMLElement } original_stanza - The original stanza, that contains the
|
||||||
* message stanza, if it was contained, otherwise it's the message stanza itself.
|
* message stanza, if it was contained, otherwise it's the message stanza itself.
|
||||||
* @returns { Object }
|
* @returns { Object }
|
||||||
*/
|
*/
|
||||||
@ -140,8 +140,8 @@ function getSender (attrs, chatbox) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a passed in message stanza and returns an object of attributes.
|
* Parses a passed in message stanza and returns an object of attributes.
|
||||||
* @param { Element } stanza - The message stanza
|
* @param { XMLElement } stanza - The message stanza
|
||||||
* @param { Element } original_stanza - The original stanza, that contains the
|
* @param { XMLElement } original_stanza - The original stanza, that contains the
|
||||||
* message stanza, if it was contained, otherwise it's the message stanza itself.
|
* message stanza, if it was contained, otherwise it's the message stanza itself.
|
||||||
* @param { _converse.ChatRoom } chatbox
|
* @param { _converse.ChatRoom } chatbox
|
||||||
* @param { _converse } _converse
|
* @param { _converse } _converse
|
||||||
@ -342,7 +342,7 @@ export function parseMemberListIQ (iq) {
|
|||||||
/**
|
/**
|
||||||
* Parses a passed in MUC presence stanza and returns an object of attributes.
|
* Parses a passed in MUC presence stanza and returns an object of attributes.
|
||||||
* @method parseMUCPresence
|
* @method parseMUCPresence
|
||||||
* @param { Element } stanza - The presence stanza
|
* @param { XMLElement } stanza - The presence stanza
|
||||||
* @param { _converse.ChatRoom } chatbox
|
* @param { _converse.ChatRoom } chatbox
|
||||||
* @returns { MUCPresenceAttributes }
|
* @returns { MUCPresenceAttributes }
|
||||||
*/
|
*/
|
||||||
|
@ -124,19 +124,19 @@ describe("A MUC message", function () {
|
|||||||
const muc_jid = 'lounge@montague.lit';
|
const muc_jid = 'lounge@montague.lit';
|
||||||
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||||
const received_stanza = u.toStanza(`
|
const received_stanza = u.toStanza(`
|
||||||
<message to='${_converse.jid}' from='${muc_jid}/mallory' type='groupchat' id='${_converse.connection.getUniqueId()}' >
|
<message to='${_converse.jid}' from='${muc_jid}/mallory' type='groupchat' id='${_converse.connection.getUniqueId()}' >
|
||||||
<reply xmlns='urn:xmpp:reply:0' id='${_converse.connection.getUniqueId()}' to='${_converse.jid}'/>
|
<reply xmlns='urn:xmpp:reply:0' id='${_converse.connection.getUniqueId()}' to='${_converse.jid}'/>
|
||||||
<fallback xmlns='urn:xmpp:feature-fallback:0' for='urn:xmpp:reply:0'>
|
<fallback xmlns='urn:xmpp:feature-fallback:0' for='urn:xmpp:reply:0'>
|
||||||
<body start='0' end='10'/>
|
<body start='0' end='10'/>
|
||||||
</fallback>
|
</fallback>
|
||||||
<active xmlns='http://jabber.org/protocol/chatstates'/>
|
<active xmlns='http://jabber.org/protocol/chatstates'/>
|
||||||
<body>> ping
|
<body>> ping
|
||||||
pong</body>
|
pong</body>
|
||||||
<request xmlns='urn:xmpp:receipts'/>
|
<request xmlns='urn:xmpp:receipts'/>
|
||||||
</message>
|
</message>
|
||||||
`);
|
`);
|
||||||
await model.handleMessageStanza(received_stanza);
|
await model.handleMessageStanza(received_stanza);
|
||||||
await u.waitUntil(() => model.messages.last());
|
await u.waitUntil(() => model.messages.last());
|
||||||
expect(model.messages.last().get('body')).toBe('> ping\n pong');
|
expect(model.messages.last().get('body')).toBe('> ping\npong');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -83,14 +83,7 @@ describe("A MUC occupant", function () {
|
|||||||
await u.waitUntil(() => model.messages.length);
|
await u.waitUntil(() => model.messages.length);
|
||||||
let message = model.messages.at(0);
|
let message = model.messages.at(0);
|
||||||
expect(message.get('occupant_id')).toBe("dd72603deec90a38ba552f7c68cbcc61bca202cd");
|
expect(message.get('occupant_id')).toBe("dd72603deec90a38ba552f7c68cbcc61bca202cd");
|
||||||
expect(message.occupant).not.toBeUndefined();
|
expect(message.occupant).toBeUndefined();
|
||||||
|
|
||||||
let occupant = message.occupant;
|
|
||||||
expect(occupant.getDisplayName()).toBe('3rdwitch');
|
|
||||||
expect(occupant.get('nick')).toBe('3rdwitch');
|
|
||||||
expect(occupant.get('jid')).toBe(undefined);
|
|
||||||
expect(occupant.get('occupant_id')).toBe("dd72603deec90a38ba552f7c68cbcc61bca202cd");
|
|
||||||
|
|
||||||
expect(message.getDisplayName()).toBe('3rdwitch');
|
expect(message.getDisplayName()).toBe('3rdwitch');
|
||||||
|
|
||||||
const presence = u.toStanza(`
|
const presence = u.toStanza(`
|
||||||
@ -105,7 +98,7 @@ describe("A MUC occupant", function () {
|
|||||||
</presence>`);
|
</presence>`);
|
||||||
_converse.connection._dataRecv(mock.createRequest(presence));
|
_converse.connection._dataRecv(mock.createRequest(presence));
|
||||||
|
|
||||||
occupant = await u.waitUntil(() => model.getOccupantByNickname('thirdwitch'));
|
const occupant = await u.waitUntil(() => model.getOccupantByNickname('thirdwitch'));
|
||||||
expect(occupant.get('occupant_id')).toBe('dd72603deec90a38ba552f7c68cbcc61bca202cd');
|
expect(occupant.get('occupant_id')).toBe('dd72603deec90a38ba552f7c68cbcc61bca202cd');
|
||||||
expect(model.occupants.findWhere({'occupant_id': "dd72603deec90a38ba552f7c68cbcc61bca202cd"})).toBe(occupant);
|
expect(model.occupants.findWhere({'occupant_id': "dd72603deec90a38ba552f7c68cbcc61bca202cd"})).toBe(occupant);
|
||||||
|
|
||||||
|
@ -85,9 +85,10 @@ export async function openChatRoom (jid, settings) {
|
|||||||
* See XEP-0249: Direct MUC invitations.
|
* See XEP-0249: Direct MUC invitations.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatRoom#onDirectMUCInvitation
|
* @method _converse.ChatRoom#onDirectMUCInvitation
|
||||||
* @param { Element } message - The message stanza containing the invitation.
|
* @param { XMLElement } message - The message stanza containing the invitation.
|
||||||
*/
|
*/
|
||||||
export async function onDirectMUCInvitation (message) {
|
export async function onDirectMUCInvitation (message) {
|
||||||
|
const { __ } = _converse;
|
||||||
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
|
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
|
||||||
from = Strophe.getBareJidFromJid(message.getAttribute('from')),
|
from = Strophe.getBareJidFromJid(message.getAttribute('from')),
|
||||||
room_jid = x_el.getAttribute('jid'),
|
room_jid = x_el.getAttribute('jid'),
|
||||||
@ -98,20 +99,21 @@ export async function onDirectMUCInvitation (message) {
|
|||||||
result = true;
|
result = true;
|
||||||
} else {
|
} else {
|
||||||
// Invite request might come from someone not your roster list
|
// Invite request might come from someone not your roster list
|
||||||
const contact = _converse.roster.get(from)?.getDisplayName() ?? from;
|
let contact = _converse.roster.get(from);
|
||||||
|
contact = contact ? contact.getDisplayName() : from;
|
||||||
/**
|
if (!reason) {
|
||||||
* *Hook* which is used to gather confirmation whether a direct MUC
|
result = await api.confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid));
|
||||||
* invitation should be accepted or not.
|
} else {
|
||||||
*
|
result = await api.confirm(
|
||||||
* It's meant for consumers of `@converse/headless` to subscribe to
|
__(
|
||||||
* this hook and then ask the user to confirm.
|
'%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"',
|
||||||
*
|
contact,
|
||||||
* @event _converse#confirmDirectMUCInvitation
|
room_jid,
|
||||||
*/
|
reason
|
||||||
result = await api.hook('confirmDirectMUCInvitation', { contact, reason, jid: room_jid }, false);
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
|
const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') });
|
||||||
if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
|
if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) {
|
||||||
|
@ -10,7 +10,7 @@ export default {
|
|||||||
* @method api.ping
|
* @method api.ping
|
||||||
* @param { String } [jid] - The JID of the service to ping
|
* @param { String } [jid] - The JID of the service to ping
|
||||||
* If the ping is sent out to the user's bare JID and no response is received it will attempt to reconnect.
|
* If the ping is sent out to the user's bare JID and no response is received it will attempt to reconnect.
|
||||||
* @param { number } [timeout] - The amount of time in
|
* @param { Integer } [timeout] - The amount of time in
|
||||||
* milliseconds to wait for a response. The default is 10000;
|
* milliseconds to wait for a response. The default is 10000;
|
||||||
* @returns { Boolean | null }
|
* @returns { Boolean | null }
|
||||||
* Whether the pinged entity responded with a non-error IQ stanza.
|
* Whether the pinged entity responded with a non-error IQ stanza.
|
||||||
|
@ -32,12 +32,12 @@ converse.plugins.add('converse-pubsub', {
|
|||||||
* Publshes an item to a PubSub node
|
* Publshes an item to a PubSub node
|
||||||
*
|
*
|
||||||
* @method _converse.api.pubsub.publish
|
* @method _converse.api.pubsub.publish
|
||||||
* @param { string } jid The JID of the pubsub service where the node resides.
|
* @param {string} jid The JID of the pubsub service where the node resides.
|
||||||
* @param { string } node The node being published to
|
* @param {string} node The node being published to
|
||||||
* @param {Strophe.Builder} item The Strophe.Builder representation of the XML element being published
|
* @param {Strophe.Builder} item The Strophe.Builder representation of the XML element being published
|
||||||
* @param { object } options An object representing the publisher options
|
* @param {object} options An object representing the publisher options
|
||||||
* (see https://xmpp.org/extensions/xep-0060.html#publisher-publish-options)
|
* (see https://xmpp.org/extensions/xep-0060.html#publisher-publish-options)
|
||||||
* @param { boolean } strict_options Indicates whether the publisher
|
* @param {boolean} strict_options Indicates whether the publisher
|
||||||
* options are a strict requirement or not. If they're NOT
|
* options are a strict requirement or not. If they're NOT
|
||||||
* strict, then Converse will publish to the node even if
|
* strict, then Converse will publish to the node even if
|
||||||
* the publish options precondication cannot be met.
|
* the publish options precondication cannot be met.
|
||||||
@ -74,7 +74,7 @@ converse.plugins.add('converse-pubsub', {
|
|||||||
|
|
||||||
// The publish-options precondition couldn't be
|
// The publish-options precondition couldn't be
|
||||||
// met. We re-publish but without publish-options.
|
// met. We re-publish but without publish-options.
|
||||||
const el = stanza.tree();
|
const el = stanza.nodeTree;
|
||||||
el.querySelector('publish-options').outerHTML = '';
|
el.querySelector('publish-options').outerHTML = '';
|
||||||
log.warn(`PubSub: Republishing without publish options. ${el.outerHTML}`);
|
log.warn(`PubSub: Republishing without publish options. ${el.outerHTML}`);
|
||||||
await api.sendIQ(el);
|
await api.sendIQ(el);
|
||||||
|
@ -55,8 +55,8 @@ export default {
|
|||||||
* Add a contact.
|
* Add a contact.
|
||||||
*
|
*
|
||||||
* @method _converse.api.contacts.add
|
* @method _converse.api.contacts.add
|
||||||
* @param { string } jid The JID of the contact to be added
|
* @param {string} jid The JID of the contact to be added
|
||||||
* @param { string } [name] A custom name to show the user by in the roster
|
* @param {string} [name] A custom name to show the user by in the roster
|
||||||
* @example
|
* @example
|
||||||
* _converse.api.contacts.add('buddy@example.com')
|
* _converse.api.contacts.add('buddy@example.com')
|
||||||
* @example
|
* @example
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import '@converse/headless/plugins/status/api.js';
|
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
@ -94,12 +93,21 @@ const RosterContact = Model.extend({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a presence subscription request to this roster contact
|
* Send a presence subscription request to this roster contact
|
||||||
|
* @private
|
||||||
* @method _converse.RosterContacts#subscribe
|
* @method _converse.RosterContacts#subscribe
|
||||||
* @param { String } message - An optional message to explain the
|
* @param { String } message - An optional message to explain the
|
||||||
* reason for the subscription request.
|
* reason for the subscription request.
|
||||||
*/
|
*/
|
||||||
subscribe (message) {
|
subscribe (message) {
|
||||||
api.user.presence.send('subscribe', this.get('jid'), message);
|
const pres = $pres({to: this.get('jid'), type: "subscribe"});
|
||||||
|
if (message && message !== "") {
|
||||||
|
pres.c("status").t(message).up();
|
||||||
|
}
|
||||||
|
const nick = _converse.xmppstatus.getNickname() || _converse.xmppstatus.getFullname();
|
||||||
|
if (nick) {
|
||||||
|
pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
|
||||||
|
}
|
||||||
|
api.send(pres);
|
||||||
this.save('ask', "subscribe"); // ask === 'subscribe' Means we have asked to subscribe to them.
|
this.save('ask', "subscribe"); // ask === 'subscribe' Means we have asked to subscribe to them.
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
@ -127,6 +135,7 @@ const RosterContact = Model.extend({
|
|||||||
* send notification of the subscription state change to the user.
|
* send notification of the subscription state change to the user.
|
||||||
* @private
|
* @private
|
||||||
* @method _converse.RosterContacts#ackUnsubscribe
|
* @method _converse.RosterContacts#ackUnsubscribe
|
||||||
|
* @param { String } jid - The Jabber ID of the user who is unsubscribing
|
||||||
*/
|
*/
|
||||||
ackUnsubscribe () {
|
ackUnsubscribe () {
|
||||||
api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')}));
|
api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')}));
|
||||||
|
@ -112,7 +112,7 @@ const RosterContacts = Collection.extend({
|
|||||||
* @method _converse.RosterContacts#addAndSubscribe
|
* @method _converse.RosterContacts#addAndSubscribe
|
||||||
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
|
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
|
||||||
* @param { String } name - The name of that user
|
* @param { String } name - The name of that user
|
||||||
* @param { Array<String> } groups - Any roster groups the user might belong to
|
* @param { Array.String } groups - Any roster groups the user might belong to
|
||||||
* @param { String } message - An optional message to explain the reason for the subscription request.
|
* @param { String } message - An optional message to explain the reason for the subscription request.
|
||||||
* @param { Object } attributes - Any additional attributes to be stored on the user's model.
|
* @param { Object } attributes - Any additional attributes to be stored on the user's model.
|
||||||
*/
|
*/
|
||||||
@ -128,7 +128,9 @@ const RosterContacts = Collection.extend({
|
|||||||
* @method _converse.RosterContacts#sendContactAddIQ
|
* @method _converse.RosterContacts#sendContactAddIQ
|
||||||
* @param { String } jid - The Jabber ID of the user being added
|
* @param { String } jid - The Jabber ID of the user being added
|
||||||
* @param { String } name - The name of that user
|
* @param { String } name - The name of that user
|
||||||
* @param { Array<String> } groups - Any roster groups the user might belong to
|
* @param { Array.String } groups - Any roster groups the user might belong to
|
||||||
|
* @param { Function } callback - A function to call once the IQ is returned
|
||||||
|
* @param { Function } errback - A function to call if an error occurred
|
||||||
*/
|
*/
|
||||||
sendContactAddIQ (jid, name, groups) {
|
sendContactAddIQ (jid, name, groups) {
|
||||||
name = name ? name : null;
|
name = name ? name : null;
|
||||||
@ -146,7 +148,7 @@ const RosterContacts = Collection.extend({
|
|||||||
* @method _converse.RosterContacts#addContactToRoster
|
* @method _converse.RosterContacts#addContactToRoster
|
||||||
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
|
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
|
||||||
* @param { String } name - The name of that user
|
* @param { String } name - The name of that user
|
||||||
* @param { Array<String> } groups - Any roster groups the user might belong to
|
* @param { Array.String } groups - Any roster groups the user might belong to
|
||||||
* @param { Object } attributes - Any additional attributes to be stored on the user's model.
|
* @param { Object } attributes - Any additional attributes to be stored on the user's model.
|
||||||
*/
|
*/
|
||||||
async addContactToRoster (jid, name, groups, attributes) {
|
async addContactToRoster (jid, name, groups, attributes) {
|
||||||
@ -188,7 +190,7 @@ const RosterContacts = Collection.extend({
|
|||||||
* Handle roster updates from the XMPP server.
|
* Handle roster updates from the XMPP server.
|
||||||
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
|
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
|
||||||
* @method _converse.RosterContacts#onRosterPush
|
* @method _converse.RosterContacts#onRosterPush
|
||||||
* @param { Element } iq - The IQ stanza received from the XMPP server.
|
* @param { XMLElement } IQ - The IQ stanza received from the XMPP server.
|
||||||
*/
|
*/
|
||||||
onRosterPush (iq) {
|
onRosterPush (iq) {
|
||||||
const id = iq.getAttribute('id');
|
const id = iq.getAttribute('id');
|
||||||
@ -224,7 +226,7 @@ const RosterContacts = Collection.extend({
|
|||||||
/**
|
/**
|
||||||
* When the roster receives a push event from server (i.e. new entry in your contacts roster).
|
* When the roster receives a push event from server (i.e. new entry in your contacts roster).
|
||||||
* @event _converse#rosterPush
|
* @event _converse#rosterPush
|
||||||
* @type { Element }
|
* @type { XMLElement }
|
||||||
* @example _converse.api.listen.on('rosterPush', iq => { ... });
|
* @example _converse.api.listen.on('rosterPush', iq => { ... });
|
||||||
*/
|
*/
|
||||||
api.trigger('rosterPush', iq);
|
api.trigger('rosterPush', iq);
|
||||||
@ -277,7 +279,7 @@ const RosterContacts = Collection.extend({
|
|||||||
* See also the `cachedRoster` event further up, which gets called instead of
|
* See also the `cachedRoster` event further up, which gets called instead of
|
||||||
* `roster` if its already in `sessionStorage`.
|
* `roster` if its already in `sessionStorage`.
|
||||||
* @event _converse#roster
|
* @event _converse#roster
|
||||||
* @type { Element }
|
* @type { XMLElement }
|
||||||
* @example _converse.api.listen.on('roster', iq => { ... });
|
* @example _converse.api.listen.on('roster', iq => { ... });
|
||||||
* @example _converse.api.waitUntil('roster').then(iq => { ... });
|
* @example _converse.api.waitUntil('roster').then(iq => { ... });
|
||||||
*/
|
*/
|
||||||
@ -287,7 +289,7 @@ const RosterContacts = Collection.extend({
|
|||||||
/**
|
/**
|
||||||
* Update or create RosterContact models based on the given `item` XML
|
* Update or create RosterContact models based on the given `item` XML
|
||||||
* node received in the resulting IQ stanza from the server.
|
* node received in the resulting IQ stanza from the server.
|
||||||
* @param { Element } item
|
* @param { XMLElement } item
|
||||||
*/
|
*/
|
||||||
updateContact (item) {
|
updateContact (item) {
|
||||||
const jid = item.getAttribute('jid');
|
const jid = item.getAttribute('jid');
|
||||||
@ -377,7 +379,9 @@ const RosterContacts = Collection.extend({
|
|||||||
_converse.xmppstatus.save({'status': show}, {'silent': true});
|
_converse.xmppstatus.save({'status': show}, {'silent': true});
|
||||||
|
|
||||||
const status_message = presence.querySelector('status')?.textContent;
|
const status_message = presence.querySelector('status')?.textContent;
|
||||||
if (status_message) _converse.xmppstatus.save({ status_message });
|
if (status_message) {
|
||||||
|
_converse.xmppstatus.save({'status_message': status_message});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_converse.jid === jid && presence_type === 'unavailable') {
|
if (_converse.jid === jid && presence_type === 'unavailable') {
|
||||||
// XXX: We've received an "unavailable" presence from our
|
// XXX: We've received an "unavailable" presence from our
|
||||||
@ -410,11 +414,11 @@ const RosterContacts = Collection.extend({
|
|||||||
return; // Ignore MUC
|
return; // Ignore MUC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const status_message = presence.querySelector('status')?.textContent;
|
||||||
const contact = this.get(bare_jid);
|
const contact = this.get(bare_jid);
|
||||||
|
|
||||||
if (contact) {
|
if (contact && (status_message !== contact.get('status'))) {
|
||||||
const status = presence.querySelector('status')?.textContent;
|
contact.save({'status': status_message});
|
||||||
if (contact.get('status') !== status) contact.save({status});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (presence_type === 'subscribed' && contact) {
|
if (presence_type === 'subscribed' && contact) {
|
||||||
|
@ -30,7 +30,7 @@ export const Presence = Model.extend({
|
|||||||
const hpr = this.getHighestPriorityResource();
|
const hpr = this.getHighestPriorityResource();
|
||||||
const show = hpr?.attributes?.show || 'offline';
|
const show = hpr?.attributes?.show || 'offline';
|
||||||
if (this.get('show') !== show) {
|
if (this.get('show') !== show) {
|
||||||
this.save({ show });
|
this.save({'show': show});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -48,20 +48,20 @@ export const Presence = Model.extend({
|
|||||||
* from the passed in presence stanza.
|
* from the passed in presence stanza.
|
||||||
* Also updates the presence if the resource has higher priority (and is newer).
|
* Also updates the presence if the resource has higher priority (and is newer).
|
||||||
* @private
|
* @private
|
||||||
* @param { Element } presence: The presence stanza
|
* @param { XMLElement } presence: The presence stanza
|
||||||
*/
|
*/
|
||||||
addResource (presence) {
|
addResource (presence) {
|
||||||
const jid = presence.getAttribute('from');
|
const jid = presence.getAttribute('from'),
|
||||||
const name = Strophe.getResourceFromJid(jid);
|
name = Strophe.getResourceFromJid(jid),
|
||||||
const delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop();
|
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, presence).pop(),
|
||||||
const priority = presence.querySelector('priority')?.textContent;
|
priority = presence.querySelector('priority')?.textContent ?? 0,
|
||||||
const resource = this.resources.get(name);
|
resource = this.resources.get(name),
|
||||||
const settings = {
|
settings = {
|
||||||
name,
|
'name': name,
|
||||||
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
|
'priority': isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10),
|
||||||
'show': presence.querySelector('show')?.textContent ?? 'online',
|
'show': presence.querySelector('show')?.textContent ?? 'online',
|
||||||
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
|
'timestamp': delay ? dayjs(delay.getAttribute('stamp')).toISOString() : (new Date()).toISOString()
|
||||||
};
|
};
|
||||||
if (resource) {
|
if (resource) {
|
||||||
resource.save(settings);
|
resource.save(settings);
|
||||||
} else {
|
} else {
|
||||||
@ -78,7 +78,9 @@ export const Presence = Model.extend({
|
|||||||
*/
|
*/
|
||||||
removeResource (name) {
|
removeResource (name) {
|
||||||
const resource = this.resources.get(name);
|
const resource = this.resources.get(name);
|
||||||
resource?.destroy();
|
if (resource) {
|
||||||
|
resource.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
import { RosterFilter } from '@converse/headless/plugins/roster/filter.js';
|
import { RosterFilter } from '@converse/headless/plugins/roster/filter.js';
|
||||||
import { STATUS_WEIGHTS } from "../../shared/constants";
|
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
import { _converse, api, converse } from "@converse/headless/core";
|
||||||
import { initStorage } from '@converse/headless/utils/storage.js';
|
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||||
import { shouldClearCache } from '@converse/headless/utils/core.js';
|
|
||||||
|
|
||||||
const { $pres } = converse.env;
|
const { $pres } = converse.env;
|
||||||
|
|
||||||
@ -90,7 +88,7 @@ async function clearPresences () {
|
|||||||
*/
|
*/
|
||||||
export async function onClearSession () {
|
export async function onClearSession () {
|
||||||
await clearPresences();
|
await clearPresences();
|
||||||
if (shouldClearCache()) {
|
if (_converse.shouldClearCache()) {
|
||||||
if (_converse.rostergroups) {
|
if (_converse.rostergroups) {
|
||||||
await _converse.rostergroups.clearStore();
|
await _converse.rostergroups.clearStore();
|
||||||
delete _converse.rostergroups;
|
delete _converse.rostergroups;
|
||||||
@ -199,12 +197,12 @@ export function rejectPresenceSubscription (jid, message) {
|
|||||||
export function contactsComparator (contact1, contact2) {
|
export function contactsComparator (contact1, contact2) {
|
||||||
const status1 = contact1.presence.get('show') || 'offline';
|
const status1 = contact1.presence.get('show') || 'offline';
|
||||||
const status2 = contact2.presence.get('show') || 'offline';
|
const status2 = contact2.presence.get('show') || 'offline';
|
||||||
if (STATUS_WEIGHTS[status1] === STATUS_WEIGHTS[status2]) {
|
if (_converse.STATUS_WEIGHTS[status1] === _converse.STATUS_WEIGHTS[status2]) {
|
||||||
const name1 = (contact1.getDisplayName()).toLowerCase();
|
const name1 = (contact1.getDisplayName()).toLowerCase();
|
||||||
const name2 = (contact2.getDisplayName()).toLowerCase();
|
const name2 = (contact2.getDisplayName()).toLowerCase();
|
||||||
return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
|
return name1 < name2 ? -1 : (name1 > name2? 1 : 0);
|
||||||
} else {
|
} else {
|
||||||
return STATUS_WEIGHTS[status1] < STATUS_WEIGHTS[status2] ? -1 : 1;
|
return _converse.STATUS_WEIGHTS[status1] < _converse.STATUS_WEIGHTS[status2] ? -1 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,13 +49,14 @@ describe("XEP-0198 Stream Management", function () {
|
|||||||
`<iq id="${IQ_stanzas[1].getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
|
`<iq id="${IQ_stanzas[1].getAttribute('id')}" type="get" xmlns="jabber:client"><query xmlns="jabber:iq:roster"/></iq>`);
|
||||||
await mock.waitForRoster(_converse, 'current', 1);
|
await mock.waitForRoster(_converse, 'current', 1);
|
||||||
|
|
||||||
expect(Strophe.serialize(IQ_stanzas[2])).toBe(
|
const omemo_iq = IQ_stanzas[2];
|
||||||
`<iq from="romeo@montague.lit/orchard" id="${IQ_stanzas[2].getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
expect(Strophe.serialize(omemo_iq)).toBe(
|
||||||
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
|
`<iq from="romeo@montague.lit" id="${omemo_iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
|
||||||
|
|
||||||
expect(Strophe.serialize(IQ_stanzas[3])).toBe(
|
expect(Strophe.serialize(IQ_stanzas[3])).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${IQ_stanzas[3].getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit/orchard" id="${IQ_stanzas[3].getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
|
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
|
||||||
|
|
||||||
expect(Strophe.serialize(IQ_stanzas[4])).toBe(
|
expect(Strophe.serialize(IQ_stanzas[4])).toBe(
|
||||||
`<iq from="romeo@montague.lit/orchard" id="${IQ_stanzas[4].getAttribute('id')}" type="set" xmlns="jabber:client"><enable xmlns="urn:xmpp:carbons:2"/></iq>`);
|
`<iq from="romeo@montague.lit/orchard" id="${IQ_stanzas[4].getAttribute('id')}" type="set" xmlns="jabber:client"><enable xmlns="urn:xmpp:carbons:2"/></iq>`);
|
||||||
@ -136,13 +137,13 @@ describe("XEP-0198 Stream Management", function () {
|
|||||||
|
|
||||||
iq = IQ_stanzas.pop();
|
iq = IQ_stanzas.pop();
|
||||||
expect(Strophe.serialize(iq)).toBe(
|
expect(Strophe.serialize(iq)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
|
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
|
||||||
|
|
||||||
iq = IQ_stanzas.pop();
|
iq = IQ_stanzas.pop();
|
||||||
expect(Strophe.serialize(iq)).toBe(
|
expect(Strophe.serialize(iq)).toBe(
|
||||||
`<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq.getAttribute('id')}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<query xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub></iq>`);
|
||||||
|
|
||||||
expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0);
|
expect(IQ_stanzas.filter(iq => sizzle('query[xmlns="jabber:iq:roster"]', iq).pop()).length).toBe(0);
|
||||||
}));
|
}));
|
||||||
|
@ -1,8 +1,38 @@
|
|||||||
import { _converse, api } from '../../core';
|
import { _converse, api } from '@converse/headless/core';
|
||||||
import { STATUS_WEIGHTS } from '../../shared/constants';
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* @namespace _converse.api.user.presence
|
||||||
|
* @memberOf _converse.api.user
|
||||||
|
*/
|
||||||
|
presence: {
|
||||||
|
/**
|
||||||
|
* Send out a presence stanza
|
||||||
|
* @method _converse.api.user.presence.send
|
||||||
|
* @param { String } type
|
||||||
|
* @param { String } to
|
||||||
|
* @param { String } [status] - An optional status message
|
||||||
|
* @param { Element[]|Strophe.Builder[]|Element|Strophe.Builder } [child_nodes]
|
||||||
|
* Nodes(s) to be added as child nodes of the `presence` XML element.
|
||||||
|
*/
|
||||||
|
async send (type, to, status, child_nodes) {
|
||||||
|
await api.waitUntil('statusInitialized');
|
||||||
|
if (child_nodes && !Array.isArray(child_nodes)) {
|
||||||
|
child_nodes = [child_nodes];
|
||||||
|
}
|
||||||
|
const model = _converse.xmppstatus
|
||||||
|
const presence = await model.constructPresence(type, to, status);
|
||||||
|
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
|
||||||
|
api.send(presence);
|
||||||
|
|
||||||
|
if (['away', 'chat', 'dnd', 'online', 'xa', undefined].includes(type)) {
|
||||||
|
const mucs = await api.rooms.get();
|
||||||
|
mucs.forEach(muc => muc.sendStatusPresence(type, status, child_nodes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set and get the user's chat status, also called their *availability*.
|
* Set and get the user's chat status, also called their *availability*.
|
||||||
* @namespace _converse.api.user.status
|
* @namespace _converse.api.user.status
|
||||||
@ -25,15 +55,15 @@ export default {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @method _converse.api.user.status.set
|
* @method _converse.api.user.status.set
|
||||||
* @param { string } value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa')
|
* @param {string} value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa')
|
||||||
* @param { string } [message] A custom status message
|
* @param {string} [message] A custom status message
|
||||||
*
|
*
|
||||||
* @example _converse.api.user.status.set('dnd');
|
* @example _converse.api.user.status.set('dnd');
|
||||||
* @example _converse.api.user.status.set('dnd', 'In a meeting');
|
* @example _converse.api.user.status.set('dnd', 'In a meeting');
|
||||||
*/
|
*/
|
||||||
async set (value, message) {
|
async set (value, message) {
|
||||||
const data = {'status': value};
|
const data = {'status': value};
|
||||||
if (!Object.keys(STATUS_WEIGHTS).includes(value)) {
|
if (!Object.keys(_converse.STATUS_WEIGHTS).includes(value)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
|
'Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'
|
||||||
);
|
);
|
||||||
@ -55,7 +85,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* @method _converse.api.user.status.message.get
|
* @method _converse.api.user.status.message.get
|
||||||
* @returns { Promise<string> } The status message
|
* @returns {string} The status message
|
||||||
* @example const message = _converse.api.user.status.message.get()
|
* @example const message = _converse.api.user.status.message.get()
|
||||||
*/
|
*/
|
||||||
async get () {
|
async get () {
|
||||||
@ -65,7 +95,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* @method _converse.api.user.status.message.set
|
* @method _converse.api.user.status.message.set
|
||||||
* @param { string } status The status message
|
* @param {string} status The status message
|
||||||
* @example _converse.api.user.status.message.set('In a meeting');
|
* @example _converse.api.user.status.message.set('In a meeting');
|
||||||
*/
|
*/
|
||||||
async set (status) {
|
async set (status) {
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import XMPPStatus from './status.js';
|
import XMPPStatus from './status.js';
|
||||||
import status_api from './api.js';
|
import status_api from './api.js';
|
||||||
import { _converse, api, converse } from '@converse/headless/core';
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
import { shouldClearCache } from '@converse/headless/utils/core.js';
|
|
||||||
import {
|
import {
|
||||||
addStatusToMUCJoinPresence,
|
addStatusToMUCJoinPresence,
|
||||||
initStatus,
|
initStatus,
|
||||||
@ -53,7 +52,7 @@ converse.plugins.add('converse-status', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
api.listen.on('clearSession', () => {
|
api.listen.on('clearSession', () => {
|
||||||
if (shouldClearCache() && _converse.xmppstatus) {
|
if (_converse.shouldClearCache() && _converse.xmppstatus) {
|
||||||
_converse.xmppstatus.destroy();
|
_converse.xmppstatus.destroy();
|
||||||
delete _converse.xmppstatus;
|
delete _converse.xmppstatus;
|
||||||
api.promises.add(['statusInitialized']);
|
api.promises.add(['statusInitialized']);
|
||||||
|
@ -5,11 +5,10 @@ import { _converse, api, converse } from '@converse/headless/core';
|
|||||||
|
|
||||||
const { Strophe, $pres } = converse.env;
|
const { Strophe, $pres } = converse.env;
|
||||||
|
|
||||||
export default class XMPPStatus extends Model {
|
const XMPPStatus = Model.extend({
|
||||||
|
defaults () {
|
||||||
defaults () { // eslint-disable-line class-methods-use-this
|
|
||||||
return { "status": api.settings.get("default_state") }
|
return { "status": api.settings.get("default_state") }
|
||||||
}
|
},
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
this.on('change', item => {
|
this.on('change', item => {
|
||||||
@ -20,72 +19,54 @@ export default class XMPPStatus extends Model {
|
|||||||
api.user.presence.send(this.get('status'), null, this.get('status_message'));
|
api.user.presence.send(this.get('status'), null, this.get('status_message'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
getDisplayName () {
|
getNickname () {
|
||||||
return this.getFullname() || this.getNickname() || _converse.bare_jid;
|
|
||||||
}
|
|
||||||
|
|
||||||
getNickname () { // eslint-disable-line class-methods-use-this
|
|
||||||
return api.settings.get('nickname');
|
return api.settings.get('nickname');
|
||||||
}
|
},
|
||||||
|
|
||||||
getFullname () { // eslint-disable-line class-methods-use-this
|
getFullname () {
|
||||||
return ''; // Gets overridden in converse-vcard
|
// Gets overridden in converse-vcard
|
||||||
}
|
return '';
|
||||||
|
},
|
||||||
|
|
||||||
/** Constructs a presence stanza
|
|
||||||
* @param { string } [type]
|
|
||||||
* @param { string } [to] - The JID to which this presence should be sent
|
|
||||||
* @param { string } [status_message]
|
|
||||||
*/
|
|
||||||
async constructPresence (type, to=null, status_message) {
|
async constructPresence (type, to=null, status_message) {
|
||||||
type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state"));
|
type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state"));
|
||||||
status_message = typeof status_message === 'string' ? status_message : this.get('status_message');
|
status_message = typeof status_message === 'string' ? status_message : this.get('status_message');
|
||||||
|
|
||||||
let presence;
|
let presence;
|
||||||
|
const attrs = {to};
|
||||||
if (type === 'subscribe') {
|
if ((type === 'unavailable') ||
|
||||||
presence = $pres({ to, type });
|
|
||||||
const { xmppstatus } = _converse;
|
|
||||||
const nick = xmppstatus.getNickname();
|
|
||||||
if (nick) presence.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
|
|
||||||
|
|
||||||
} else if ((type === 'unavailable') ||
|
|
||||||
(type === 'probe') ||
|
(type === 'probe') ||
|
||||||
(type === 'error') ||
|
(type === 'error') ||
|
||||||
(type === 'unsubscribe') ||
|
(type === 'unsubscribe') ||
|
||||||
(type === 'unsubscribed') ||
|
(type === 'unsubscribed') ||
|
||||||
|
(type === 'subscribe') ||
|
||||||
(type === 'subscribed')) {
|
(type === 'subscribed')) {
|
||||||
presence = $pres({ to, type });
|
attrs['type'] = type;
|
||||||
|
presence = $pres(attrs);
|
||||||
} else if (type === 'offline') {
|
} else if (type === 'offline') {
|
||||||
presence = $pres({ to, type: 'unavailable' });
|
attrs['type'] = 'unavailable';
|
||||||
|
presence = $pres(attrs);
|
||||||
} else if (type === 'online') {
|
} else if (type === 'online') {
|
||||||
presence = $pres({ to });
|
presence = $pres(attrs);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
presence = $pres({ to }).c('show').t(type).up();
|
presence = $pres(attrs).c('show').t(type).up();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status_message) presence.c('status').t(status_message).up();
|
if (status_message) {
|
||||||
|
presence.c('status').t(status_message).up();
|
||||||
|
}
|
||||||
|
|
||||||
const priority = api.settings.get("priority");
|
const priority = api.settings.get("priority");
|
||||||
presence.c('priority').t(isNaN(Number(priority)) ? 0 : priority).up();
|
presence.c('priority').t(isNaN(Number(priority)) ? 0 : priority).up();
|
||||||
|
if (_converse.idle) {
|
||||||
const { idle, idle_seconds } = _converse;
|
|
||||||
if (idle) {
|
|
||||||
const idle_since = new Date();
|
const idle_since = new Date();
|
||||||
idle_since.setSeconds(idle_since.getSeconds() - idle_seconds);
|
idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds);
|
||||||
presence.c('idle', { xmlns: Strophe.NS.IDLE, since: idle_since.toISOString() });
|
presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* *Hook* which allows plugins to modify a presence stanza
|
|
||||||
* @event _converse#constructedPresence
|
|
||||||
*/
|
|
||||||
presence = await api.hook('constructedPresence', null, presence);
|
presence = await api.hook('constructedPresence', null, presence);
|
||||||
return presence;
|
return presence;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
export default XMPPStatus;
|
||||||
|
@ -22,8 +22,8 @@ export default {
|
|||||||
* for the passed in JID.
|
* for the passed in JID.
|
||||||
*
|
*
|
||||||
* @method _converse.api.vcard.set
|
* @method _converse.api.vcard.set
|
||||||
* @param { string } jid The JID for which the VCard should be set
|
* @param {string} jid The JID for which the VCard should be set
|
||||||
* @param { object } data A map of VCard keys and values
|
* @param {object} data A map of VCard keys and values
|
||||||
* @example
|
* @example
|
||||||
* let jid = _converse.bare_jid;
|
* let jid = _converse.bare_jid;
|
||||||
* _converse.api.vcard.set( jid, {
|
* _converse.api.vcard.set( jid, {
|
||||||
@ -67,7 +67,7 @@ export default {
|
|||||||
* @param {Model|string} model Either a `Model` instance, or a string JID.
|
* @param {Model|string} model Either a `Model` instance, or a string JID.
|
||||||
* If a `Model` instance is passed in, then it must have either a `jid`
|
* If a `Model` instance is passed in, then it must have either a `jid`
|
||||||
* attribute or a `muc_jid` attribute.
|
* attribute or a `muc_jid` attribute.
|
||||||
* @param { boolean } [force] A boolean indicating whether the vcard should be
|
* @param {boolean} [force] A boolean indicating whether the vcard should be
|
||||||
* fetched from the server even if it's been fetched before.
|
* fetched from the server even if it's been fetched before.
|
||||||
* @returns {promise} A Promise which resolves with the VCard data for a particular JID or for
|
* @returns {promise} A Promise which resolves with the VCard data for a particular JID or for
|
||||||
* a `Model` instance which represents an entity with a JID (such as a roster contact,
|
* a `Model` instance which represents an entity with a JID (such as a roster contact,
|
||||||
@ -106,8 +106,8 @@ export default {
|
|||||||
* returned VCard data.
|
* returned VCard data.
|
||||||
*
|
*
|
||||||
* @method _converse.api.vcard.update
|
* @method _converse.api.vcard.update
|
||||||
* @param { Model } model A `Model` instance
|
* @param {Model} model A `Model` instance
|
||||||
* @param { boolean } [force] A boolean indicating whether the vcard should be
|
* @param {boolean} [force] A boolean indicating whether the vcard should be
|
||||||
* fetched again even if it's been fetched before.
|
* fetched again even if it's been fetched before.
|
||||||
* @returns {promise} A promise which resolves once the update has completed.
|
* @returns {promise} A promise which resolves once the update has completed.
|
||||||
* @example
|
* @example
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import log from "@converse/headless/log";
|
import log from "@converse/headless/log";
|
||||||
import { _converse, api, converse } from "../../core.js";
|
import { _converse, api, converse } from "../../core.js";
|
||||||
import { initStorage } from '@converse/headless/utils/storage.js';
|
import { initStorage } from '@converse/headless/utils/storage.js';
|
||||||
import { shouldClearCache } from '@converse/headless/utils/core.js';
|
|
||||||
|
|
||||||
const { Strophe, $iq, u } = converse.env;
|
const { Strophe, $iq, u } = converse.env;
|
||||||
|
|
||||||
@ -164,7 +163,7 @@ export async function initVCardCollection () {
|
|||||||
|
|
||||||
|
|
||||||
export function clearVCardsSession () {
|
export function clearVCardsSession () {
|
||||||
if (shouldClearCache()) {
|
if (_converse.shouldClearCache()) {
|
||||||
api.promises.add('VCardsInitialized');
|
api.promises.add('VCardsInitialized');
|
||||||
if (_converse.vcards) {
|
if (_converse.vcards) {
|
||||||
_converse.vcards.clearStore();
|
_converse.vcards.clearStore();
|
||||||
|
@ -1,36 +1,11 @@
|
|||||||
import i18n from './i18n.js';
|
import i18n from '@converse/headless/shared/i18n';
|
||||||
import log from '../log.js';
|
import log from '@converse/headless/log';
|
||||||
import pluggable from 'pluggable.js/src/pluggable.js';
|
import { CONNECTION_STATUS } from '@converse/headless/shared/constants';
|
||||||
import { Events } from '@converse/skeletor/src/events.js';
|
|
||||||
import { Router } from '@converse/skeletor/src/router.js';
|
import { Router } from '@converse/skeletor/src/router.js';
|
||||||
import { createStore, getDefaultStore } from '../utils/storage.js';
|
import { TimeoutError } from '@converse/headless/shared/errors';
|
||||||
import { getInitSettings } from './settings/utils.js';
|
import { createStore, getDefaultStore } from '@converse/headless/utils/storage.js';
|
||||||
|
import { getInitSettings } from '@converse/headless/shared/settings/utils.js';
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
import { getOpenPromise } from '@converse/openpromise';
|
||||||
import { shouldClearCache } from '../utils/core.js';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ACTIVE,
|
|
||||||
ANONYMOUS,
|
|
||||||
CHATROOMS_TYPE,
|
|
||||||
CLOSED,
|
|
||||||
COMPOSING,
|
|
||||||
CONTROLBOX_TYPE,
|
|
||||||
DEFAULT_IMAGE,
|
|
||||||
DEFAULT_IMAGE_TYPE,
|
|
||||||
EXTERNAL,
|
|
||||||
FAILURE,
|
|
||||||
GONE,
|
|
||||||
HEADLINES_TYPE,
|
|
||||||
INACTIVE,
|
|
||||||
LOGIN,
|
|
||||||
LOGOUT,
|
|
||||||
OPENED,
|
|
||||||
PAUSED,
|
|
||||||
PREBIND,
|
|
||||||
PRIVATE_CHAT_TYPE,
|
|
||||||
SUCCESS,
|
|
||||||
VERSION_NAME
|
|
||||||
} from './constants';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,51 +16,67 @@ import {
|
|||||||
*/
|
*/
|
||||||
const _converse = {
|
const _converse = {
|
||||||
log,
|
log,
|
||||||
|
CONNECTION_STATUS,
|
||||||
shouldClearCache, // TODO: Should be moved to utils with next major release
|
|
||||||
VERSION_NAME,
|
|
||||||
|
|
||||||
templates: {},
|
templates: {},
|
||||||
promises: {
|
promises: {
|
||||||
'initialized': getOpenPromise()
|
'initialized': getOpenPromise()
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: remove constants in next major release
|
STATUS_WEIGHTS: {
|
||||||
ANONYMOUS,
|
'offline': 6,
|
||||||
CLOSED,
|
'unavailable': 5,
|
||||||
EXTERNAL,
|
'xa': 4,
|
||||||
LOGIN,
|
'away': 3,
|
||||||
LOGOUT,
|
'dnd': 2,
|
||||||
OPENED,
|
'chat': 1, // We currently don't differentiate between "chat" and "online"
|
||||||
PREBIND,
|
'online': 1
|
||||||
|
},
|
||||||
|
ANONYMOUS: 'anonymous',
|
||||||
|
CLOSED: 'closed',
|
||||||
|
EXTERNAL: 'external',
|
||||||
|
LOGIN: 'login',
|
||||||
|
LOGOUT: 'logout',
|
||||||
|
OPENED: 'opened',
|
||||||
|
PREBIND: 'prebind',
|
||||||
|
|
||||||
SUCCESS,
|
/**
|
||||||
FAILURE,
|
* @constant
|
||||||
|
* @type { integer }
|
||||||
|
*/
|
||||||
|
STANZA_TIMEOUT: 20000,
|
||||||
|
|
||||||
DEFAULT_IMAGE_TYPE,
|
SUCCESS: 'success',
|
||||||
DEFAULT_IMAGE,
|
FAILURE: 'failure',
|
||||||
|
|
||||||
INACTIVE,
|
// Generated from css/images/user.svg
|
||||||
ACTIVE,
|
DEFAULT_IMAGE_TYPE: 'image/svg+xml',
|
||||||
COMPOSING,
|
DEFAULT_IMAGE: "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCI+CiA8cmVjdCB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgZmlsbD0iIzU1NSIvPgogPGNpcmNsZSBjeD0iNjQiIGN5PSI0MSIgcj0iMjQiIGZpbGw9IiNmZmYiLz4KIDxwYXRoIGQ9Im0yOC41IDExMiB2LTEyIGMwLTEyIDEwLTI0IDI0LTI0IGgyMyBjMTQgMCAyNCAxMiAyNCAyNCB2MTIiIGZpbGw9IiNmZmYiLz4KPC9zdmc+Cg==",
|
||||||
PAUSED,
|
|
||||||
GONE,
|
|
||||||
|
|
||||||
PRIVATE_CHAT_TYPE,
|
|
||||||
CHATROOMS_TYPE,
|
|
||||||
HEADLINES_TYPE,
|
|
||||||
CONTROLBOX_TYPE,
|
|
||||||
|
|
||||||
// Set as module attr so that we can override in tests.
|
|
||||||
// TODO: replace with config settings
|
|
||||||
TIMEOUTS: {
|
TIMEOUTS: {
|
||||||
|
// Set as module attr so that we can override in tests.
|
||||||
PAUSED: 10000,
|
PAUSED: 10000,
|
||||||
INACTIVE: 90000
|
INACTIVE: 90000
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// XEP-0085 Chat states
|
||||||
|
// https://xmpp.org/extensions/xep-0085.html
|
||||||
|
INACTIVE: 'inactive',
|
||||||
|
ACTIVE: 'active',
|
||||||
|
COMPOSING: 'composing',
|
||||||
|
PAUSED: 'paused',
|
||||||
|
GONE: 'gone',
|
||||||
|
|
||||||
|
// Chat types
|
||||||
|
PRIVATE_CHAT_TYPE: 'chatbox',
|
||||||
|
CHATROOMS_TYPE: 'chatroom',
|
||||||
|
HEADLINES_TYPE: 'headline',
|
||||||
|
CONTROLBOX_TYPE: 'controlbox',
|
||||||
|
|
||||||
default_connection_options: {'explicitResourceBinding': true},
|
default_connection_options: {'explicitResourceBinding': true},
|
||||||
router: new Router(),
|
router: new Router(),
|
||||||
|
|
||||||
|
TimeoutError: TimeoutError,
|
||||||
|
|
||||||
isTestEnv: () => {
|
isTestEnv: () => {
|
||||||
return getInitSettings()['bosh_service_url'] === 'montague.lit/http-bind';
|
return getInitSettings()['bosh_service_url'] === 'montague.lit/http-bind';
|
||||||
},
|
},
|
||||||
@ -121,10 +112,4 @@ const _converse = {
|
|||||||
'___': str => str
|
'___': str => str
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make _converse an event emitter
|
|
||||||
Object.assign(_converse, Events);
|
|
||||||
|
|
||||||
// Make _converse pluggable
|
|
||||||
pluggable.enable(_converse, '_converse', 'pluggable');
|
|
||||||
|
|
||||||
export default _converse;
|
export default _converse;
|
||||||
|
@ -1,132 +0,0 @@
|
|||||||
import _converse from '../_converse.js';
|
|
||||||
import isFunction from '../../utils/core.js';
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import _converse from '../_converse.js';
|
|
||||||
import connection_api from '../connection/api.js';
|
|
||||||
import events_api from '../api/events.js';
|
|
||||||
import promise_api from '../api/promise.js';
|
|
||||||
import send_api from '../api/send.js';
|
|
||||||
import user_api from '../api/user.js';
|
|
||||||
import { settings_api } from '../settings/api.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ### 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
|
|
||||||
*/
|
|
||||||
const api = _converse.api = {
|
|
||||||
connection: connection_api,
|
|
||||||
settings: settings_api,
|
|
||||||
...send_api,
|
|
||||||
...user_api,
|
|
||||||
...events_api,
|
|
||||||
...promise_api,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default api;
|
|
@ -1,35 +0,0 @@
|
|||||||
import { _converse, api } from '../../core.js';
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* @namespace _converse.api.user.presence
|
|
||||||
* @memberOf _converse.api.user
|
|
||||||
*/
|
|
||||||
presence: {
|
|
||||||
/**
|
|
||||||
* Send out a presence stanza
|
|
||||||
* @method _converse.api.user.presence.send
|
|
||||||
* @param { String } [type]
|
|
||||||
* @param { String } [to]
|
|
||||||
* @param { String } [status] - An optional status message
|
|
||||||
* @param { Array<Element>|Array<Strophe.Builder>|Element|Strophe.Builder } [child_nodes]
|
|
||||||
* Nodes(s) to be added as child nodes of the `presence` XML element.
|
|
||||||
*/
|
|
||||||
async send (type, to, status, child_nodes) {
|
|
||||||
await api.waitUntil('statusInitialized');
|
|
||||||
if (child_nodes && !Array.isArray(child_nodes)) {
|
|
||||||
child_nodes = [child_nodes];
|
|
||||||
}
|
|
||||||
const model = _converse.xmppstatus
|
|
||||||
const presence = await model.constructPresence(type, to, status);
|
|
||||||
child_nodes?.map(c => c?.tree() ?? c).forEach(c => presence.cnode(c).up());
|
|
||||||
api.send(presence);
|
|
||||||
|
|
||||||
if (['away', 'chat', 'dnd', 'online', 'xa', undefined].includes(type)) {
|
|
||||||
const mucs = await api.rooms.get();
|
|
||||||
mucs.forEach(muc => muc.sendStatusPresence(type, status, child_nodes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
import _converse from '@converse/headless/shared/_converse.js';
|
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
|
||||||
import { waitUntil, isFunction } from '../../utils/core.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 waitUntil(condition);
|
|
||||||
} else {
|
|
||||||
const promise = _converse.promises[condition];
|
|
||||||
if (promise === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,212 +0,0 @@
|
|||||||
import ConnectionFeedback from './../connection/feedback.js';
|
|
||||||
import URI from 'urijs';
|
|
||||||
import _converse from '../_converse.js';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import i18n from '../i18n';
|
|
||||||
import log from '../../log.js';
|
|
||||||
import sizzle from 'sizzle';
|
|
||||||
import u, { setUnloadEvent } from '../../utils/core.js';
|
|
||||||
import { ANONYMOUS, CHAT_STATES, KEYCODES, VERSION_NAME } from '../constants.js';
|
|
||||||
import { Collection } from "@converse/skeletor/src/collection";
|
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
|
||||||
import { Strophe, $build, $iq, $msg, $pres } from 'strophe.js/src/strophe';
|
|
||||||
import { TimeoutError } from '../errors.js';
|
|
||||||
import { filesize } from 'filesize';
|
|
||||||
import { html } from 'lit';
|
|
||||||
import { initAppSettings } from '../settings/utils.js';
|
|
||||||
import { sprintf } from 'sprintf-js';
|
|
||||||
import { stx } from '../../utils/stanza.js';
|
|
||||||
|
|
||||||
import {
|
|
||||||
cleanup,
|
|
||||||
initClientConfig,
|
|
||||||
initPlugins,
|
|
||||||
initSessionStorage,
|
|
||||||
registerGlobalEventHandlers,
|
|
||||||
} from '../../utils/init.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ### 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
|
|
||||||
*/
|
|
||||||
export const converse = Object.assign(window.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) {
|
|
||||||
const { api } = _converse;
|
|
||||||
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") === 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 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") && 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 { Error } converse.env.TimeoutError
|
|
||||||
* @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,
|
|
||||||
TimeoutError,
|
|
||||||
URI,
|
|
||||||
VERSION_NAME,
|
|
||||||
dayjs,
|
|
||||||
filesize,
|
|
||||||
html,
|
|
||||||
log,
|
|
||||||
sizzle,
|
|
||||||
sprintf,
|
|
||||||
stx,
|
|
||||||
u,
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,81 +0,0 @@
|
|||||||
import _converse from '../../shared/_converse.js';
|
|
||||||
import log from '../../log.js';
|
|
||||||
import { Strophe } from 'strophe.js/src/strophe';
|
|
||||||
import { TimeoutError } from '../errors.js';
|
|
||||||
import { toStanza } from '../../utils/stanza.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* Allows you to send XML stanzas.
|
|
||||||
* @method _converse.api.send
|
|
||||||
* @param { Element | Stanza } 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) {
|
|
||||||
const { api } = _converse;
|
|
||||||
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 = toStanza(stanza);
|
|
||||||
} else if (stanza?.tree) {
|
|
||||||
stanza = stanza.tree();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 { Element } stanza
|
|
||||||
* @param { number } [timeout] - The default timeout value is taken from
|
|
||||||
* the `stanza_timeout` configuration setting.
|
|
||||||
* @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, reject=true) {
|
|
||||||
const { api, connection } = _converse;
|
|
||||||
|
|
||||||
let promise;
|
|
||||||
stanza = stanza.tree?.() ?? stanza;
|
|
||||||
if (['get', 'set'].includes(stanza.getAttribute('type'))) {
|
|
||||||
timeout = timeout || api.settings.get('stanza_timeout');
|
|
||||||
if (reject) {
|
|
||||||
promise = new Promise((resolve, reject) => 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) => connection.sendIQ(stanza, resolve, resolve, timeout));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_converse.connection.sendIQ(stanza);
|
|
||||||
promise = Promise.resolve();
|
|
||||||
}
|
|
||||||
api.trigger('send', stanza);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
import _converse from '../_converse.js';
|
|
||||||
import presence_api from './presence.js';
|
|
||||||
import u, { replacePromise } from '../../utils/core.js';
|
|
||||||
import { attemptNonPreboundSession, initConnection, setUserJID } from '../../utils/init.js';
|
|
||||||
import { getOpenPromise } from '@converse/openpromise';
|
|
||||||
import { user_settings_api } from '../settings/api.js';
|
|
||||||
import { LOGOUT, PREBIND } from '../constants.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
/**
|
|
||||||
* This grouping collects API functions related to the current logged in user.
|
|
||||||
*
|
|
||||||
* @namespace _converse.api.user
|
|
||||||
* @memberOf _converse.api
|
|
||||||
*/
|
|
||||||
user: {
|
|
||||||
settings: user_settings_api,
|
|
||||||
...presence_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 { Promise<void> }
|
|
||||||
*/
|
|
||||||
async login (jid, password, automatic=false) {
|
|
||||||
const { api } = _converse;
|
|
||||||
jid = jid || api.settings.get('jid');
|
|
||||||
if (!_converse.connection?.jid || (jid && !u.isSameDomain(_converse.connection.jid, jid))) {
|
|
||||||
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") === 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 () {
|
|
||||||
const { api } = _converse;
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
// Remove the session JID, otherwise the user would just be logged
|
|
||||||
// in again upon reload. See #2759
|
|
||||||
localStorage.removeItem('conversejs-session-jid');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered once the user has logged out.
|
|
||||||
* @event _converse#logout
|
|
||||||
*/
|
|
||||||
api.trigger('logout');
|
|
||||||
promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
_converse.connection.setDisconnectionCause(LOGOUT, undefined, true);
|
|
||||||
if (_converse.connection !== undefined) {
|
|
||||||
api.listen.once('disconnected', () => complete());
|
|
||||||
_converse.connection.disconnect();
|
|
||||||
} else {
|
|
||||||
complete();
|
|
||||||
}
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import _converse from '../_converse';
|
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
|
||||||
import { Strophe } from 'strophe.js/src/strophe';
|
|
||||||
|
|
||||||
export default Model.extend({
|
|
||||||
defaults: {
|
|
||||||
'connection_status': Strophe.Status.DISCONNECTED,
|
|
||||||
'message': ''
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
const { api } = _converse;
|
|
||||||
this.on('change', () => api.trigger('connfeedback', _converse.connfeedback));
|
|
||||||
}
|
|
||||||
});
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user