From 5e5fadc223caca920ca83ec82cad8b66d23d2e05 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Thu, 16 Jun 2022 19:39:04 +0530 Subject: [PATCH 01/28] jingle: added the initial boilerplate code for the plugin. --- src/converse.js | 1 + src/plugins/jingle/index.js | 14 ++++++++++++++ src/shared/constants.js | 1 + 3 files changed, 16 insertions(+) create mode 100644 src/plugins/jingle/index.js diff --git a/src/converse.js b/src/converse.js index 42eed5b8b6..39eb802e5e 100644 --- a/src/converse.js +++ b/src/converse.js @@ -23,6 +23,7 @@ import "./plugins/controlbox/index.js"; // The control box import "./plugins/dragresize/index.js"; // Allows chat boxes to be resized by dragging them import "./plugins/fullscreen/index.js"; import "./plugins/headlines-view/index.js"; +import "./plugins/jingle/index.js" // Implements the jingle protocol import "./plugins/mam-views/index.js"; import "./plugins/minimize/index.js"; // Allows chat boxes to be minimized import "./plugins/muc-views/index.js"; // Views related to MUC diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js new file mode 100644 index 0000000000..287154f350 --- /dev/null +++ b/src/plugins/jingle/index.js @@ -0,0 +1,14 @@ +/** + * @description Converse.js plugin which adds XEP-0166 Jingle + * @copyright 2022, the Converse.js contributors + * @license Mozilla Public License (MPLv2) + */ + +import { converse } from '@converse/headless/core'; + +converse.plugins.add('jingle', { + + initialize: function () { + + }, +}); diff --git a/src/shared/constants.js b/src/shared/constants.js index bdbed7dcec..be19bc819b 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -11,6 +11,7 @@ export const VIEW_PLUGINS = [ 'converse-dragresize', 'converse-fullscreen', 'converse-headlines-view', + 'converse-jingle', 'converse-mam-views', 'converse-minimize', 'converse-modal', From 387e781d42e8d8b47bfc3740ad7205600f960ef2 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Fri, 17 Jun 2022 13:58:40 +0530 Subject: [PATCH 02/28] (TBR)CallButton: added an unfunctional call button to the toolbar --- src/plugins/chatview/index.js | 2 +- src/shared/chat/toolbar.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/chatview/index.js b/src/plugins/chatview/index.js index ca6fdf98e8..1dff896252 100644 --- a/src/plugins/chatview/index.js +++ b/src/plugins/chatview/index.js @@ -51,7 +51,7 @@ converse.plugins.add('converse-chatview', { 'time_format': 'HH:mm', 'use_system_emojis': true, 'visible_toolbar_buttons': { - 'call': false, + 'call': true, 'clear': true, 'emoji': true, 'spoiler': true diff --git a/src/shared/chat/toolbar.js b/src/shared/chat/toolbar.js index a69fadb4b8..38040af328 100644 --- a/src/shared/chat/toolbar.js +++ b/src/shared/chat/toolbar.js @@ -54,8 +54,8 @@ export class ChatToolbar extends CustomElement { buttons.push(html``); } - if (this.show_call_button) { - const color = this.is_groupchat ? '--muc-toolbar-btn-color' : '--chat-toolbar-btn-color'; + if (!this.is_groupchat) { + const color = '--chat-toolbar-btn-color'; const i18n_start_call = __('Start a call'); buttons.push(html` ` + ); + } + + return buttons; + }); }, }); diff --git a/src/shared/chat/toolbar.js b/src/shared/chat/toolbar.js index 38040af328..0d00489d54 100644 --- a/src/shared/chat/toolbar.js +++ b/src/shared/chat/toolbar.js @@ -54,16 +54,6 @@ export class ChatToolbar extends CustomElement { buttons.push(html``); } - if (!this.is_groupchat) { - const color = '--chat-toolbar-btn-color'; - const i18n_start_call = __('Start a call'); - buttons.push(html` - ` - ); - } - const message_limit = api.settings.get('message_limit'); if (message_limit) { buttons.push(html` From 6d8824334f10b3c57971eeeb28e6f639f7e2b74f Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 20 Jun 2022 13:04:53 +0530 Subject: [PATCH 04/28] test --- src/plugins/jingle/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 55c05f4d0f..fbc6f41bbb 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -9,6 +9,7 @@ import 'plugins/modal/index.js'; import { __ } from 'i18n'; import { html } from "lit"; + converse.plugins.add('converse-jingle', { /* Plugin dependencies are other plugins which might be * overridden or relied upon, and therefore need to be loaded before From fb84f1004337b38fea2a42f9c471bda0b3621e4f Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 20 Jun 2022 14:53:46 +0530 Subject: [PATCH 05/28] revert back code to avoid merge conflicts --- src/plugins/chatview/index.js | 2 +- src/shared/chat/toolbar.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/plugins/chatview/index.js b/src/plugins/chatview/index.js index 1dff896252..ca6fdf98e8 100644 --- a/src/plugins/chatview/index.js +++ b/src/plugins/chatview/index.js @@ -51,7 +51,7 @@ converse.plugins.add('converse-chatview', { 'time_format': 'HH:mm', 'use_system_emojis': true, 'visible_toolbar_buttons': { - 'call': true, + 'call': false, 'clear': true, 'emoji': true, 'spoiler': true diff --git a/src/shared/chat/toolbar.js b/src/shared/chat/toolbar.js index 0d00489d54..a69fadb4b8 100644 --- a/src/shared/chat/toolbar.js +++ b/src/shared/chat/toolbar.js @@ -54,6 +54,16 @@ export class ChatToolbar extends CustomElement { buttons.push(html``); } + if (this.show_call_button) { + const color = this.is_groupchat ? '--muc-toolbar-btn-color' : '--chat-toolbar-btn-color'; + const i18n_start_call = __('Start a call'); + buttons.push(html` + ` + ); + } + const message_limit = api.settings.get('message_limit'); if (message_limit) { buttons.push(html` From d31b4852603011eb75c6a0113eed15cd53ea9d97 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Tue, 21 Jun 2022 17:42:12 +0530 Subject: [PATCH 06/28] (TBR)added a boilerplate modal for jingle call --- src/plugins/jingle/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index fbc6f41bbb..159499293e 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -4,10 +4,11 @@ * @license Mozilla Public License (MPLv2) */ -import { _converse, converse } from '@converse/headless/core'; +import { _converse, converse, api } from '@converse/headless/core'; import 'plugins/modal/index.js'; import { __ } from 'i18n'; import { html } from "lit"; +import AddJingleModal from "./modal/jingle-call-modal.js" converse.plugins.add('converse-jingle', { @@ -23,16 +24,16 @@ converse.plugins.add('converse-jingle', { */ dependencies: ['converse-chatview'], - initialize: function () { + initialize: function (o) { /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ - _converse.api.listen.on('getToolbarButtons', (toolbar_el, buttons) => { + _converse.api.listen.on('getToolbarButtons', (buttons) => { if (!this.is_groupchat) { const color = '--chat-toolbar-btn-color'; const i18n_start_call = __('Start a call'); buttons.push(html` - ` ); From 5c4b40dff50e70ae6072e07454aa84f16d901d7e Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Thu, 23 Jun 2022 17:41:06 +0530 Subject: [PATCH 07/28] added the call button at the chat header. --- src/plugins/chatview/heading.js | 1 + src/plugins/chatview/templates/chat-head.js | 4 ++++ src/plugins/jingle/index.js | 10 +++++----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/chatview/heading.js b/src/plugins/chatview/heading.js index a58ee828ab..e81884bc1b 100644 --- a/src/plugins/chatview/heading.js +++ b/src/plugins/chatview/heading.js @@ -17,6 +17,7 @@ export default class ChatHeading extends CustomElement { initialize () { this.model = _converse.chatboxes.get(this.jid); + this.listenTo(this.model, 'change:jingle_status', this.requestUpdate); this.listenTo(this.model, 'change:status', this.requestUpdate); this.listenTo(this.model, 'vcard:add', this.requestUpdate); this.listenTo(this.model, 'vcard:change', this.requestUpdate); diff --git a/src/plugins/chatview/templates/chat-head.js b/src/plugins/chatview/templates/chat-head.js index bdd5561c79..5ed5d1d5d2 100644 --- a/src/plugins/chatview/templates/chat-head.js +++ b/src/plugins/chatview/templates/chat-head.js @@ -3,6 +3,7 @@ import { _converse } from '@converse/headless/core'; import { getHeadingDropdownItem, getHeadingStandaloneButton } from 'plugins/chatview/utils.js'; import { html } from "lit"; import { until } from 'lit/directives/until.js'; +import { JINGLE_CALL_STATUS } from "../../jingle/constants.js"; async function getStandaloneButtons (promise) { @@ -42,6 +43,9 @@ export default (o) => { ${ (o.type !== _converse.HEADLINES_TYPE) ? html`${ display_name }` : display_name } +
+ ${(o.model.get('jingle_status') === JINGLE_CALL_STATUS.PENDING) ? html`` : ''} +
${ until(tpl_dropdown_btns(), '') } ${ until(tpl_standalone_btns(), '') } diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 159499293e..d7455ac35e 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -4,11 +4,11 @@ * @license Mozilla Public License (MPLv2) */ -import { _converse, converse, api } from '@converse/headless/core'; +import { _converse, converse } from '@converse/headless/core'; import 'plugins/modal/index.js'; import { __ } from 'i18n'; import { html } from "lit"; -import AddJingleModal from "./modal/jingle-call-modal.js" +import { startJingleCall } from "./utils.js"; converse.plugins.add('converse-jingle', { @@ -24,16 +24,16 @@ converse.plugins.add('converse-jingle', { */ dependencies: ['converse-chatview'], - initialize: function (o) { + initialize: function () { /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ - _converse.api.listen.on('getToolbarButtons', (buttons) => { + _converse.api.listen.on('getToolbarButtons', (toolbar_el, buttons) => { if (!this.is_groupchat) { const color = '--chat-toolbar-btn-color'; const i18n_start_call = __('Start a call'); buttons.push(html` - ` ); From 3f975d216385242c79f043903b700543358376cc Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Thu, 23 Jun 2022 17:46:13 +0530 Subject: [PATCH 08/28] (TBR)Jingle modal --- src/plugins/jingle/constants.js | 6 +++++ src/plugins/jingle/modal/jingle-call-modal.js | 18 +++++++++++++ src/plugins/jingle/templates/jingle-call.js | 27 +++++++++++++++++++ src/plugins/jingle/utils.js | 7 +++++ 4 files changed, 58 insertions(+) create mode 100644 src/plugins/jingle/constants.js create mode 100644 src/plugins/jingle/modal/jingle-call-modal.js create mode 100644 src/plugins/jingle/templates/jingle-call.js create mode 100644 src/plugins/jingle/utils.js diff --git a/src/plugins/jingle/constants.js b/src/plugins/jingle/constants.js new file mode 100644 index 0000000000..057265047e --- /dev/null +++ b/src/plugins/jingle/constants.js @@ -0,0 +1,6 @@ + +export const JINGLE_CALL_STATUS = { + PENDING: 0, + ACTIVE: 1, + ENDED: 2 +}; diff --git a/src/plugins/jingle/modal/jingle-call-modal.js b/src/plugins/jingle/modal/jingle-call-modal.js new file mode 100644 index 0000000000..8b96912ef2 --- /dev/null +++ b/src/plugins/jingle/modal/jingle-call-modal.js @@ -0,0 +1,18 @@ +import BootstrapModal from "plugins/modal/base.js"; +import tpl_start_jingle_call from "../templates/jingle-call.js"; + +export default BootstrapModal.extend({ + id: "start-jingle-call-modal", + persistent: true, + + initialize () { + this.items = []; + this.loading_items = false; + + BootstrapModal.prototype.initialize.apply(this, arguments); + }, + + toHTML () { + return tpl_start_jingle_call(); + } +}); diff --git a/src/plugins/jingle/templates/jingle-call.js b/src/plugins/jingle/templates/jingle-call.js new file mode 100644 index 0000000000..8b83ff44ef --- /dev/null +++ b/src/plugins/jingle/templates/jingle-call.js @@ -0,0 +1,27 @@ +import { html } from 'lit'; +import { __ } from 'i18n'; + +const modal_close_button = html``; + +export default () => { + const i18n_modal_title = __('Jingle Call'); + return html` + + `; +} diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js new file mode 100644 index 0000000000..a9be8de8b0 --- /dev/null +++ b/src/plugins/jingle/utils.js @@ -0,0 +1,7 @@ +import { _converse } from "@converse/headless/core"; +import { JINGLE_CALL_STATUS } from "./constants.js"; + +export function startJingleCall(jid) { + const model = _converse.chatboxes.get(jid); + model.save('jingle_status', JINGLE_CALL_STATUS.PENDING); +} From 70a49d3961134d54562998ae318e42482329627f Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 27 Jun 2022 23:18:35 +0530 Subject: [PATCH 09/28] (TBR)added 2 new custom plugins for jingle --- docs/source/configuration.rst | 1 + docs/source/other_frameworks.rst | 1 + src/plugins/chatview/templates/chat-head.js | 3 +- .../jingle/chat-header-notification.js | 30 ++++++++++++++++ src/plugins/jingle/index.js | 14 +++----- src/plugins/jingle/modal/jingle-call-modal.js | 18 ---------- src/plugins/jingle/templates/header-button.js | 10 ++++++ src/plugins/jingle/templates/jingle-call.js | 27 -------------- .../jingle/templates/toolbar-button.js | 14 ++++++++ src/plugins/jingle/toolbar-button.js | 36 +++++++++++++++++++ src/plugins/jingle/utils.js | 7 ---- src/shared/styles/themes/classic.scss | 1 + 12 files changed, 99 insertions(+), 63 deletions(-) create mode 100644 src/plugins/jingle/chat-header-notification.js delete mode 100644 src/plugins/jingle/modal/jingle-call-modal.js create mode 100644 src/plugins/jingle/templates/header-button.js delete mode 100644 src/plugins/jingle/templates/jingle-call.js create mode 100644 src/plugins/jingle/templates/toolbar-button.js create mode 100644 src/plugins/jingle/toolbar-button.js delete mode 100644 src/plugins/jingle/utils.js diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index afe7901d2f..9ced0e4eb3 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -539,6 +539,7 @@ The core, and by default whitelisted, plugins are:: converse-dragresize converse-fullscreen converse-headline + converse-jingle converse-mam converse-minimize converse-muc diff --git a/docs/source/other_frameworks.rst b/docs/source/other_frameworks.rst index 25ac82a038..ce9834dcfb 100644 --- a/docs/source/other_frameworks.rst +++ b/docs/source/other_frameworks.rst @@ -60,6 +60,7 @@ Below is an example code that wraps converse.js as an angular.js service. "converse-vcard", // XEP-0054 VCard-temp "converse-register", // XEP-0077 In-band registration "converse-ping", // XEP-0199 XMPP Ping + "converse-jingle", // XEP-0166 Support for the Jingle Protocol "converse-notification", // HTML5 Notifications "converse-minimize", // Allows chatboxes to be minimized "converse-dragresize", // Allows chatboxes to be resized by dragging them diff --git a/src/plugins/chatview/templates/chat-head.js b/src/plugins/chatview/templates/chat-head.js index 5ed5d1d5d2..9b7ae1d066 100644 --- a/src/plugins/chatview/templates/chat-head.js +++ b/src/plugins/chatview/templates/chat-head.js @@ -3,7 +3,6 @@ import { _converse } from '@converse/headless/core'; import { getHeadingDropdownItem, getHeadingStandaloneButton } from 'plugins/chatview/utils.js'; import { html } from "lit"; import { until } from 'lit/directives/until.js'; -import { JINGLE_CALL_STATUS } from "../../jingle/constants.js"; async function getStandaloneButtons (promise) { @@ -44,7 +43,7 @@ export default (o) => {
- ${(o.model.get('jingle_status') === JINGLE_CALL_STATUS.PENDING) ? html`` : ''} +
${ until(tpl_dropdown_btns(), '') } diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js new file mode 100644 index 0000000000..f094f9b60c --- /dev/null +++ b/src/plugins/jingle/chat-header-notification.js @@ -0,0 +1,30 @@ +import { CustomElement } from 'shared/components/element.js'; +import { _converse, api } from "@converse/headless/core"; +import { JINGLE_CALL_STATUS } from "./constants.js"; +import tpl_header_button from "./templates/header-button.js"; + +export default class ChatHeaderCallNotification extends CustomElement { + + initialize() { + this.model = _converse.chatboxes.get(this.jid); + this.listenTo(this.model, 'change:jingle_status', this.requestUpdate); + } + + render() { + return tpl_header_button(this); + } + + toggleJingleCallStatus() { + const jingle_status = this.model.get('jingle_status'); + if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + return; + } + if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.PENDING); + return; + } + } +} + +api.elements.define('converse-chat-header-call-notification', ChatHeaderCallNotification); diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index d7455ac35e..71d07c5f34 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -4,11 +4,10 @@ * @license Mozilla Public License (MPLv2) */ -import { _converse, converse } from '@converse/headless/core'; +import { _converse, converse } from '@converse/headless/core.js'; import 'plugins/modal/index.js'; -import { __ } from 'i18n'; +import './toolbar-button.js'; import { html } from "lit"; -import { startJingleCall } from "./utils.js"; converse.plugins.add('converse-jingle', { @@ -30,13 +29,10 @@ converse.plugins.add('converse-jingle', { */ _converse.api.listen.on('getToolbarButtons', (toolbar_el, buttons) => { if (!this.is_groupchat) { - const color = '--chat-toolbar-btn-color'; - const i18n_start_call = __('Start a call'); buttons.push(html` - ` - ); + + + `); } return buttons; diff --git a/src/plugins/jingle/modal/jingle-call-modal.js b/src/plugins/jingle/modal/jingle-call-modal.js deleted file mode 100644 index 8b96912ef2..0000000000 --- a/src/plugins/jingle/modal/jingle-call-modal.js +++ /dev/null @@ -1,18 +0,0 @@ -import BootstrapModal from "plugins/modal/base.js"; -import tpl_start_jingle_call from "../templates/jingle-call.js"; - -export default BootstrapModal.extend({ - id: "start-jingle-call-modal", - persistent: true, - - initialize () { - this.items = []; - this.loading_items = false; - - BootstrapModal.prototype.initialize.apply(this, arguments); - }, - - toHTML () { - return tpl_start_jingle_call(); - } -}); diff --git a/src/plugins/jingle/templates/header-button.js b/src/plugins/jingle/templates/header-button.js new file mode 100644 index 0000000000..ce40bbe5b2 --- /dev/null +++ b/src/plugins/jingle/templates/header-button.js @@ -0,0 +1,10 @@ +import { html } from 'lit'; +import { JINGLE_CALL_STATUS } from '../constants'; + +export default (el) => { + return html` +
+ ${ (el.model.get('jingle_status')) === JINGLE_CALL_STATUS.PENDING ? html`Calling...` : html``} +
+ `; +} diff --git a/src/plugins/jingle/templates/jingle-call.js b/src/plugins/jingle/templates/jingle-call.js deleted file mode 100644 index 8b83ff44ef..0000000000 --- a/src/plugins/jingle/templates/jingle-call.js +++ /dev/null @@ -1,27 +0,0 @@ -import { html } from 'lit'; -import { __ } from 'i18n'; - -const modal_close_button = html``; - -export default () => { - const i18n_modal_title = __('Jingle Call'); - return html` - - `; -} diff --git a/src/plugins/jingle/templates/toolbar-button.js b/src/plugins/jingle/templates/toolbar-button.js new file mode 100644 index 0000000000..c1c564e4bd --- /dev/null +++ b/src/plugins/jingle/templates/toolbar-button.js @@ -0,0 +1,14 @@ +import { html } from 'lit'; +import { __ } from "i18n"; +import { JINGLE_CALL_STATUS } from '../constants'; + +export default (el) => { + const call_color = '--chat-toolbar-btn-color'; + const end_call_color = '--chat-toolbar-btn-close-color'; + const i18n_start_call = __('Start a call'); + const jingle_status = el.model.get('jingle_status'); + return html` + ` +} diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js new file mode 100644 index 0000000000..adb9edc5cc --- /dev/null +++ b/src/plugins/jingle/toolbar-button.js @@ -0,0 +1,36 @@ +import { CustomElement } from 'shared/components/element.js'; +import { _converse, api } from "@converse/headless/core"; +import { JINGLE_CALL_STATUS } from "./constants.js"; +import tpl_toolbar_button from "./templates/toolbar-button.js"; + +export default class JingleToolbarButton extends CustomElement { + + static get properties() { + return { + 'jid': { type: String }, + } + } + + initialize() { + this.model = _converse.chatboxes.get(this.jid); + this.listenTo(this.model, 'change:jingle_status', this.requestUpdate); + } + + render() { + return tpl_toolbar_button(this); + } + + toggleJingleCallStatus() { + const jingle_status = this.model.get('jingle_status'); + if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + return; + } + if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.PENDING); + return; + } + } +} + +api.elements.define('converse-jingle-toolbar-button', JingleToolbarButton); diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js deleted file mode 100644 index a9be8de8b0..0000000000 --- a/src/plugins/jingle/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -import { _converse } from "@converse/headless/core"; -import { JINGLE_CALL_STATUS } from "./constants.js"; - -export function startJingleCall(jid) { - const model = _converse.chatboxes.get(jid); - model.save('jingle_status', JINGLE_CALL_STATUS.PENDING); -} diff --git a/src/shared/styles/themes/classic.scss b/src/shared/styles/themes/classic.scss index 69eceb443d..d2bc2f972c 100644 --- a/src/shared/styles/themes/classic.scss +++ b/src/shared/styles/themes/classic.scss @@ -73,6 +73,7 @@ --chat-head-color: var(--green); --chat-head-text-color: white; --chat-toolbar-btn-color: var(--green); + --chat-toolbar-btn-close-color: var(--dark-red); --chat-toolbar-btn-disabled-color: gray; --toolbar-btn-text-color: white; From 309c29de537831aafdc4991ba8a34b23cfe58d8a Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Wed, 29 Jun 2022 14:49:28 +0530 Subject: [PATCH 10/28] added a toggling button in the centre of the chat-header --- src/plugins/chatview/styles/chat-head.scss | 4 ++++ src/plugins/chatview/templates/chat-head.js | 4 +--- .../jingle/chat-header-notification.js | 20 ++++++++++--------- src/plugins/jingle/index.js | 1 + src/plugins/jingle/templates/header-button.js | 18 ++++++++++++++++- .../jingle/templates/toolbar-button.js | 14 ++++++++++--- src/plugins/jingle/toolbar-button.js | 2 +- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/plugins/chatview/styles/chat-head.scss b/src/plugins/chatview/styles/chat-head.scss index f6522a1112..5a67634481 100644 --- a/src/plugins/chatview/styles/chat-head.scss +++ b/src/plugins/chatview/styles/chat-head.scss @@ -64,6 +64,10 @@ flex-wrap: nowrap; padding: 0; } + + .chatbox-call-status { + width: 80%; + } a, a:visited, a:hover, a:not([href]):not([tabindex]) { &.chatbox-btn { diff --git a/src/plugins/chatview/templates/chat-head.js b/src/plugins/chatview/templates/chat-head.js index 9b7ae1d066..07b933e306 100644 --- a/src/plugins/chatview/templates/chat-head.js +++ b/src/plugins/chatview/templates/chat-head.js @@ -41,9 +41,7 @@ export default (o) => {
${ (o.type !== _converse.HEADLINES_TYPE) ? html`${ display_name }` : display_name }
-
-
- +
${ until(tpl_dropdown_btns(), '') } diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index f094f9b60c..ed6dd81e78 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -1,30 +1,32 @@ import { CustomElement } from 'shared/components/element.js'; import { _converse, api } from "@converse/headless/core"; -import { JINGLE_CALL_STATUS } from "./constants.js"; import tpl_header_button from "./templates/header-button.js"; +import { JINGLE_CALL_STATUS } from "./constants.js"; -export default class ChatHeaderCallNotification extends CustomElement { +export default class CallNotification extends CustomElement { + static get properties() { + return { + 'jid': { type: String }, + } + } + initialize() { this.model = _converse.chatboxes.get(this.jid); - this.listenTo(this.model, 'change:jingle_status', this.requestUpdate); + this.listenTo(this.model, 'change:jingle_status', () => this.requestUpdate()); } render() { return tpl_header_button(this); } - toggleJingleCallStatus() { + endCall() { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); return; } - if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { - this.model.save('jingle_status', JINGLE_CALL_STATUS.PENDING); - return; - } } } -api.elements.define('converse-chat-header-call-notification', ChatHeaderCallNotification); +api.elements.define('converse-call-notification', CallNotification); diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 71d07c5f34..db87a48c77 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -6,6 +6,7 @@ import { _converse, converse } from '@converse/headless/core.js'; import 'plugins/modal/index.js'; +import "./chat-header-notification.js"; import './toolbar-button.js'; import { html } from "lit"; diff --git a/src/plugins/jingle/templates/header-button.js b/src/plugins/jingle/templates/header-button.js index ce40bbe5b2..d499258bac 100644 --- a/src/plugins/jingle/templates/header-button.js +++ b/src/plugins/jingle/templates/header-button.js @@ -1,10 +1,26 @@ import { html } from 'lit'; +import { __ } from 'i18n'; import { JINGLE_CALL_STATUS } from '../constants'; +const tpl_active_call = (o) => { + const button = __('Call Inititated'); + return html` +
+ +
+ `; +} + + // ${(jingle_status === JINGLE_CALL_STATUS.ACTIVE) ? html`${tpl_active_call(el)}` : html`` } export default (el) => { + const jingle_status = el.model.get('jingle_status'); return html`
- ${ (el.model.get('jingle_status')) === JINGLE_CALL_STATUS.PENDING ? html`Calling...` : html``} + ${(jingle_status === JINGLE_CALL_STATUS.PENDING) ? html`Calling...` : '' } +
+
+ ${(jingle_status === JINGLE_CALL_STATUS.PENDING) ? tpl_active_call(el) : '' } + ${(jingle_status === JINGLE_CALL_STATUS.ENDED) ? html`Call Ended` : '' }
`; } diff --git a/src/plugins/jingle/templates/toolbar-button.js b/src/plugins/jingle/templates/toolbar-button.js index c1c564e4bd..be0d3d704a 100644 --- a/src/plugins/jingle/templates/toolbar-button.js +++ b/src/plugins/jingle/templates/toolbar-button.js @@ -5,10 +5,18 @@ import { JINGLE_CALL_STATUS } from '../constants'; export default (el) => { const call_color = '--chat-toolbar-btn-color'; const end_call_color = '--chat-toolbar-btn-close-color'; - const i18n_start_call = __('Start a call'); const jingle_status = el.model.get('jingle_status'); + let button_color, i18n_start_call; + if (jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + button_color = end_call_color; + i18n_start_call = __('Stop the call'); + } + else { + button_color = call_color; + i18n_start_call = __('Start a call'); + } return html` - ` } diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index adb9edc5cc..a003789ef9 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -13,7 +13,7 @@ export default class JingleToolbarButton extends CustomElement { initialize() { this.model = _converse.chatboxes.get(this.jid); - this.listenTo(this.model, 'change:jingle_status', this.requestUpdate); + this.listenTo(this.model, 'change:jingle_status', () => this.requestUpdate()); } render() { From 97b737dd066b0308fa15588d09f1eba111d092a3 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Thu, 30 Jun 2022 22:34:49 +0530 Subject: [PATCH 11/28] jingle: added the tests for jingle & styled the header button --- karma.conf.js | 1 + src/plugins/chatview/templates/chat-head.js | 2 +- src/plugins/chatview/tests/chatbox.js | 4 ++-- src/plugins/jingle/chat-header-notification.js | 2 ++ src/plugins/jingle/constants.js | 1 - src/plugins/jingle/index.js | 2 ++ src/plugins/jingle/templates/header-button.js | 2 +- 7 files changed, 9 insertions(+), 5 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 8166e3ae96..39e06d4df8 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -64,6 +64,7 @@ module.exports = function(config) { { pattern: "src/plugins/controlbox/tests/controlbox.js", type: 'module' }, { pattern: "src/plugins/controlbox/tests/login.js", type: 'module' }, { pattern: "src/plugins/headlines-view/tests/headline.js", type: 'module' }, + { pattern: "src/plugins/jingle/tests/jingle.js", type: 'module' }, { pattern: "src/plugins/mam-views/tests/mam.js", type: 'module' }, { pattern: "src/plugins/mam-views/tests/placeholder.js", type: 'module' }, { pattern: "src/plugins/minimize/tests/minchats.js", type: 'module' }, diff --git a/src/plugins/chatview/templates/chat-head.js b/src/plugins/chatview/templates/chat-head.js index 07b933e306..7ba8a3faaf 100644 --- a/src/plugins/chatview/templates/chat-head.js +++ b/src/plugins/chatview/templates/chat-head.js @@ -41,7 +41,7 @@ export default (o) => {
${ (o.type !== _converse.HEADLINES_TYPE) ? html`${ display_name }` : display_name }
- +
${ until(tpl_dropdown_btns(), '') } diff --git a/src/plugins/chatview/tests/chatbox.js b/src/plugins/chatview/tests/chatbox.js index c1ce92c3bf..968413cb10 100644 --- a/src/plugins/chatview/tests/chatbox.js +++ b/src/plugins/chatview/tests/chatbox.js @@ -296,8 +296,8 @@ describe("Chatboxes", function () { })); - it("can contain a button for starting a call", - mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { + fit("can contain a button for starting a call", + mock.initConverse(['chatBoxesFetched'], { blacklisted_plugins: ['converse-jingle']}, async function (_converse) { const { api } = _converse; await mock.waitForRoster(_converse, 'current'); diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index ed6dd81e78..9458f078a0 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -3,6 +3,8 @@ import { _converse, api } from "@converse/headless/core"; import tpl_header_button from "./templates/header-button.js"; import { JINGLE_CALL_STATUS } from "./constants.js"; +import './styles/jingle.scss'; + export default class CallNotification extends CustomElement { static get properties() { diff --git a/src/plugins/jingle/constants.js b/src/plugins/jingle/constants.js index 057265047e..379128c3da 100644 --- a/src/plugins/jingle/constants.js +++ b/src/plugins/jingle/constants.js @@ -1,4 +1,3 @@ - export const JINGLE_CALL_STATUS = { PENDING: 0, ACTIVE: 1, diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index db87a48c77..51a6a35eef 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -8,6 +8,7 @@ import { _converse, converse } from '@converse/headless/core.js'; import 'plugins/modal/index.js'; import "./chat-header-notification.js"; import './toolbar-button.js'; +import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; @@ -28,6 +29,7 @@ converse.plugins.add('converse-jingle', { /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ + _converse.JINGLE_CALL_STATUS = JINGLE_CALL_STATUS; _converse.api.listen.on('getToolbarButtons', (toolbar_el, buttons) => { if (!this.is_groupchat) { buttons.push(html` diff --git a/src/plugins/jingle/templates/header-button.js b/src/plugins/jingle/templates/header-button.js index d499258bac..be9a77b81e 100644 --- a/src/plugins/jingle/templates/header-button.js +++ b/src/plugins/jingle/templates/header-button.js @@ -6,7 +6,7 @@ const tpl_active_call = (o) => { const button = __('Call Inititated'); return html`
- + ${ button }
`; } From 4eb696e232711f008a681def6e9da431f667b412 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Thu, 30 Jun 2022 22:35:40 +0530 Subject: [PATCH 12/28] additional tests for jingle --- src/plugins/jingle/styles/jingle.scss | 9 +++++++++ src/plugins/jingle/tests/jingle.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/plugins/jingle/styles/jingle.scss create mode 100644 src/plugins/jingle/tests/jingle.js diff --git a/src/plugins/jingle/styles/jingle.scss b/src/plugins/jingle/styles/jingle.scss new file mode 100644 index 0000000000..9896066112 --- /dev/null +++ b/src/plugins/jingle/styles/jingle.scss @@ -0,0 +1,9 @@ +.conversejs { + .chatbox { + .chat-head { + .button-color{ + color: var(--chat-head-text-color) !important; + } + } + } +} \ No newline at end of file diff --git a/src/plugins/jingle/tests/jingle.js b/src/plugins/jingle/tests/jingle.js new file mode 100644 index 0000000000..5cc2bc4e17 --- /dev/null +++ b/src/plugins/jingle/tests/jingle.js @@ -0,0 +1,24 @@ +/* global mock */ + +describe("A Call Button", function () { + + it("has been shown in the toolbar", + mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current'); + await mock.openControlBox(_converse); + const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + spyOn(_converse.api, "trigger").and.callThrough(); + // First check that the button does show + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const toolbar = view.querySelector('.chat-toolbar'); + const call_button = toolbar.querySelector('converse-jingle-toolbar-button'); + // Now check that the state changes + // toggleJingleCallStatus + const chatbox = view.model; + call_button.click(); + expect(chatbox.get('jingle_status') === _converse.JINGLE_CALL_STATUS.PENDING); + call_button.click(); + })); +}); From 1f6bd122407dbfc37f9cab52020259509884ec1f Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 4 Jul 2022 12:45:08 +0530 Subject: [PATCH 13/28] tests for chat header written --- src/plugins/jingle/styles/jingle.scss | 2 +- src/plugins/jingle/templates/header-button.js | 2 +- src/plugins/jingle/tests/jingle.js | 33 ++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/plugins/jingle/styles/jingle.scss b/src/plugins/jingle/styles/jingle.scss index 9896066112..2566e15f9a 100644 --- a/src/plugins/jingle/styles/jingle.scss +++ b/src/plugins/jingle/styles/jingle.scss @@ -1,7 +1,7 @@ .conversejs { .chatbox { .chat-head { - .button-color{ + .jingle-call-initiated-button{ color: var(--chat-head-text-color) !important; } } diff --git a/src/plugins/jingle/templates/header-button.js b/src/plugins/jingle/templates/header-button.js index be9a77b81e..584e5c9abd 100644 --- a/src/plugins/jingle/templates/header-button.js +++ b/src/plugins/jingle/templates/header-button.js @@ -6,7 +6,7 @@ const tpl_active_call = (o) => { const button = __('Call Inititated'); return html` `; } diff --git a/src/plugins/jingle/tests/jingle.js b/src/plugins/jingle/tests/jingle.js index 5cc2bc4e17..676175397b 100644 --- a/src/plugins/jingle/tests/jingle.js +++ b/src/plugins/jingle/tests/jingle.js @@ -1,6 +1,8 @@ -/* global mock */ +/* global mock, converse */ +const u = converse.env.utils; +// import { JINGLE_CALL_STATUS } from "../constants.js"; -describe("A Call Button", function () { +describe("A Jingle Status", function () { it("has been shown in the toolbar", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { @@ -13,12 +15,33 @@ describe("A Call Button", function () { await mock.openChatBoxFor(_converse, contact_jid); const view = _converse.chatboxviews.get(contact_jid); const toolbar = view.querySelector('.chat-toolbar'); - const call_button = toolbar.querySelector('converse-jingle-toolbar-button'); + const call_button = toolbar.querySelector('converse-jingle-toolbar-button button'); // Now check that the state changes // toggleJingleCallStatus const chatbox = view.model; call_button.click(); - expect(chatbox.get('jingle_status') === _converse.JINGLE_CALL_STATUS.PENDING); - call_button.click(); + expect(chatbox.get('jingle_status')).toBe(_converse.JINGLE_CALL_STATUS.PENDING); + })); + + fit("has been shown in the chat-header", + mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current'); + await mock.openControlBox(_converse); + const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + spyOn(_converse.api, "trigger").and.callThrough(); + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const chat_head = view.querySelector('.chatbox-title--row'); + const toolbar = view.querySelector('.chat-toolbar'); + const call_button = toolbar.querySelector('converse-jingle-toolbar-button button'); + const chatbox = view.model; + // this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + // console.log + call_button.click(); + const header_notification = chat_head.querySelector('converse-call-notification'); + const call_intialized = await u.waitUntil(() => header_notification.querySelector('.jingle-call-initiated-button')); + call_intialized.click(); + expect(chatbox.get('jingle_status') === _converse.JINGLE_CALL_STATUS.ENDED); })); }); From cbba9cbcc3878a9d83760b5eade1f468cc27904f Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Wed, 6 Jul 2022 11:55:44 +0530 Subject: [PATCH 14/28] (TBR) added the message initation tests --- karma.conf.js | 3 +- src/headless/core.js | 3 + .../jingle/chat-header-notification.js | 15 ++++- .../jingle/tests/message-initiation.js | 58 +++++++++++++++++++ src/plugins/jingle/tests/{jingle.js => ui.js} | 11 +--- src/plugins/jingle/toolbar-button.js | 13 ++++- 6 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 src/plugins/jingle/tests/message-initiation.js rename src/plugins/jingle/tests/{jingle.js => ui.js} (84%) diff --git a/karma.conf.js b/karma.conf.js index 39e06d4df8..9a72df432a 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -64,7 +64,8 @@ module.exports = function(config) { { pattern: "src/plugins/controlbox/tests/controlbox.js", type: 'module' }, { pattern: "src/plugins/controlbox/tests/login.js", type: 'module' }, { pattern: "src/plugins/headlines-view/tests/headline.js", type: 'module' }, - { pattern: "src/plugins/jingle/tests/jingle.js", type: 'module' }, + { pattern: "src/plugins/jingle/tests/ui.js", type: 'module' }, + { pattern: "src/plugins/jingle/tests/message-initiation.js", type: 'module' }, { pattern: "src/plugins/mam-views/tests/mam.js", type: 'module' }, { pattern: "src/plugins/mam-views/tests/placeholder.js", type: 'module' }, { pattern: "src/plugins/minimize/tests/minchats.js", type: 'module' }, diff --git a/src/headless/core.js b/src/headless/core.js index 9bdd1d3221..8c6677e3a3 100644 --- a/src/headless/core.js +++ b/src/headless/core.js @@ -54,6 +54,9 @@ 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('JINGLE', 'urn:xmpp:jingle:1'); +Strophe.addNamespace('JINGLEMESSAGE', 'urn:xmpp:jingle-message:1'); +Strophe.addNamespace('JINGLERTP', 'urn:xmpp:jingle:apps:rtp:1'); Strophe.addNamespace('MAM', 'urn:xmpp:mam:2'); Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0'); Strophe.addNamespace('MENTIONS', 'urn:xmpp:mmn:0'); diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index 9458f078a0..4d55f37e94 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -1,10 +1,12 @@ import { CustomElement } from 'shared/components/element.js'; -import { _converse, api } from "@converse/headless/core"; +import { _converse, api, converse } from "@converse/headless/core"; import tpl_header_button from "./templates/header-button.js"; import { JINGLE_CALL_STATUS } from "./constants.js"; import './styles/jingle.scss'; +const { Strophe, $msg } = converse.env; + export default class CallNotification extends CustomElement { static get properties() { @@ -26,6 +28,17 @@ export default class CallNotification extends CustomElement { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + api.send( + $msg({ + 'from': this.get('jid'), + 'to': this.get('jid'), + 'type': 'chat' + }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + .c('reason', {'xmlns': Strophe.NS.JINGLE}) + .c('cancel', {}).up() + .t('Retracted').up().up() + .c('store', {'xmlns': Strophe.NS.HINTS}) + ); return; } } diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js new file mode 100644 index 0000000000..5f6e1ccb7f --- /dev/null +++ b/src/plugins/jingle/tests/message-initiation.js @@ -0,0 +1,58 @@ +/*global mock, converse */ +const u = converse.env.utils; +const sizzle = converse.env.sizzle; + +const { Strophe } = converse.env; + +describe("A Jingle Message Initiation Request", function () { + + it("is sent when the user clicks the call button", mock.initConverse( + ['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose`, s).length).pop()); + expect(Strophe.serialize(stanza)).toBe( + ``+ + ``+ + ``+ + ``+ + ``+ + ``); + })); + + + it("is ended when the user clicks the toolbar call button again or the chat header end call button", mock.initConverse( + ['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + call_button.click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`reason`, s).length).pop()); + expect(Strophe.serialize(stanza)).toBe( + ``+ + ``+ + ``+ + ``+ + `Retracted`+ + ``+ + ` `+ + ``+ + `` + ); + })); +}); diff --git a/src/plugins/jingle/tests/jingle.js b/src/plugins/jingle/tests/ui.js similarity index 84% rename from src/plugins/jingle/tests/jingle.js rename to src/plugins/jingle/tests/ui.js index 676175397b..e6aa546ccc 100644 --- a/src/plugins/jingle/tests/jingle.js +++ b/src/plugins/jingle/tests/ui.js @@ -1,6 +1,5 @@ /* global mock, converse */ const u = converse.env.utils; -// import { JINGLE_CALL_STATUS } from "../constants.js"; describe("A Jingle Status", function () { @@ -22,10 +21,10 @@ describe("A Jingle Status", function () { call_button.click(); expect(chatbox.get('jingle_status')).toBe(_converse.JINGLE_CALL_STATUS.PENDING); })); - + fit("has been shown in the chat-header", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { - + await mock.waitForRoster(_converse, 'current'); await mock.openControlBox(_converse); const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit'; @@ -33,12 +32,8 @@ describe("A Jingle Status", function () { await mock.openChatBoxFor(_converse, contact_jid); const view = _converse.chatboxviews.get(contact_jid); const chat_head = view.querySelector('.chatbox-title--row'); - const toolbar = view.querySelector('.chat-toolbar'); - const call_button = toolbar.querySelector('converse-jingle-toolbar-button button'); const chatbox = view.model; - // this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); - // console.log - call_button.click(); + chatbox.save('jingle_status', _converse.JINGLE_CALL_STATUS.PENDING); const header_notification = chat_head.querySelector('converse-call-notification'); const call_intialized = await u.waitUntil(() => header_notification.querySelector('.jingle-call-initiated-button')); call_intialized.click(); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index a003789ef9..5ab43242dd 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -1,8 +1,10 @@ import { CustomElement } from 'shared/components/element.js'; -import { _converse, api } from "@converse/headless/core"; +import { converse, _converse, api } from "@converse/headless/core"; import { JINGLE_CALL_STATUS } from "./constants.js"; import tpl_toolbar_button from "./templates/toolbar-button.js"; +const { Strophe, $msg } = converse.env; + export default class JingleToolbarButton extends CustomElement { static get properties() { @@ -24,6 +26,15 @@ export default class JingleToolbarButton extends CustomElement { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + api.send( + $msg({ + 'from': this.get('jid'), + 'to': this.get('jid'), + 'type': 'chat' + }).c('propose', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + .c('description', {'xmlns': Strophe.NS.JINGLERTP, 'media': 'audio'}).up().up() + .c('store', {'xmlns': Strophe.NS.HINTS}) + ); return; } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { From 0e19aeeafce394d24b3a1e2509a8b0707ce3bea8 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Wed, 6 Jul 2022 15:10:05 +0530 Subject: [PATCH 15/28] (TBR)added the Incoming pending & outgoing pending states, xep 0353 tests don't work --- src/headless/core.js | 3 --- src/plugins/jingle/chat-header-notification.js | 6 +++--- src/plugins/jingle/constants.js | 7 ++++--- src/plugins/jingle/index.js | 5 +++++ src/plugins/jingle/templates/header-button.js | 4 ++-- src/plugins/jingle/templates/toolbar-button.js | 2 +- src/plugins/jingle/tests/message-initiation.js | 14 +++++++------- src/plugins/jingle/tests/ui.js | 4 ++-- src/plugins/jingle/toolbar-button.js | 8 ++++---- 9 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/headless/core.js b/src/headless/core.js index 8c6677e3a3..9bdd1d3221 100644 --- a/src/headless/core.js +++ b/src/headless/core.js @@ -54,9 +54,6 @@ 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('JINGLE', 'urn:xmpp:jingle:1'); -Strophe.addNamespace('JINGLEMESSAGE', 'urn:xmpp:jingle-message:1'); -Strophe.addNamespace('JINGLERTP', 'urn:xmpp:jingle:apps:rtp:1'); Strophe.addNamespace('MAM', 'urn:xmpp:mam:2'); Strophe.addNamespace('MARKERS', 'urn:xmpp:chat-markers:0'); Strophe.addNamespace('MENTIONS', 'urn:xmpp:mmn:0'); diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index 4d55f37e94..5fe20feac5 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -26,12 +26,12 @@ export default class CallNotification extends CustomElement { endCall() { const jingle_status = this.model.get('jingle_status'); - if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); api.send( $msg({ - 'from': this.get('jid'), - 'to': this.get('jid'), + 'from': _converse.bare_jid, + 'to': this.jid, 'type': 'chat' }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) .c('reason', {'xmlns': Strophe.NS.JINGLE}) diff --git a/src/plugins/jingle/constants.js b/src/plugins/jingle/constants.js index 379128c3da..6595a0fc5b 100644 --- a/src/plugins/jingle/constants.js +++ b/src/plugins/jingle/constants.js @@ -1,5 +1,6 @@ export const JINGLE_CALL_STATUS = { - PENDING: 0, - ACTIVE: 1, - ENDED: 2 + INCOMING_PENDING: 0, + OUTGOING_PENDING: 1, + ACTIVE: 2, + ENDED: 3 }; diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 51a6a35eef..85d36450e4 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -11,6 +11,11 @@ import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; +const { Strophe } = converse.env; + +Strophe.addNamespace('JINGLE', 'urn:xmpp:jingle:1'); +Strophe.addNamespace('JINGLEMESSAGE', 'urn:xmpp:jingle-message:1'); +Strophe.addNamespace('JINGLERTP', 'urn:xmpp:jingle:apps:rtp:1'); converse.plugins.add('converse-jingle', { /* Plugin dependencies are other plugins which might be diff --git a/src/plugins/jingle/templates/header-button.js b/src/plugins/jingle/templates/header-button.js index 584e5c9abd..35b2273027 100644 --- a/src/plugins/jingle/templates/header-button.js +++ b/src/plugins/jingle/templates/header-button.js @@ -16,10 +16,10 @@ export default (el) => { const jingle_status = el.model.get('jingle_status'); return html`
- ${(jingle_status === JINGLE_CALL_STATUS.PENDING) ? html`Calling...` : '' } + ${(jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING) ? html`Calling...` : '' }
- ${(jingle_status === JINGLE_CALL_STATUS.PENDING) ? tpl_active_call(el) : '' } + ${(jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING) ? tpl_active_call(el) : '' } ${(jingle_status === JINGLE_CALL_STATUS.ENDED) ? html`Call Ended` : '' }
`; diff --git a/src/plugins/jingle/templates/toolbar-button.js b/src/plugins/jingle/templates/toolbar-button.js index be0d3d704a..71c4bb42b2 100644 --- a/src/plugins/jingle/templates/toolbar-button.js +++ b/src/plugins/jingle/templates/toolbar-button.js @@ -7,7 +7,7 @@ export default (el) => { const end_call_color = '--chat-toolbar-btn-close-color'; const jingle_status = el.model.get('jingle_status'); let button_color, i18n_start_call; - if (jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + if (jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { button_color = end_call_color; i18n_start_call = __('Stop the call'); } diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index 5f6e1ccb7f..451075c402 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -16,20 +16,20 @@ describe("A Jingle Message Initiation Request", function () { const call_button = view.querySelector('converse-jingle-toolbar-button button'); call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; - const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose`, s).length).pop()); + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); expect(Strophe.serialize(stanza)).toBe( ``+ - ``+ - ``+ - ``+ - ``+ + ``+ + ``+ + ``+ + ``+ ``); })); - it("is ended when the user clicks the toolbar call button again or the chat header end call button", mock.initConverse( + fit("is ended when the user clicks the toolbar call button again or the chat header end call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -51,7 +51,7 @@ describe("A Jingle Message Initiation Request", function () { `Retracted`+ ``+ ` `+ - ``+ + ``+ `` ); })); diff --git a/src/plugins/jingle/tests/ui.js b/src/plugins/jingle/tests/ui.js index e6aa546ccc..137506bc1b 100644 --- a/src/plugins/jingle/tests/ui.js +++ b/src/plugins/jingle/tests/ui.js @@ -19,7 +19,7 @@ describe("A Jingle Status", function () { // toggleJingleCallStatus const chatbox = view.model; call_button.click(); - expect(chatbox.get('jingle_status')).toBe(_converse.JINGLE_CALL_STATUS.PENDING); + expect(chatbox.get('jingle_status')).toBe(_converse.JINGLE_CALL_STATUS.OUTGOING_PENDING); })); fit("has been shown in the chat-header", @@ -33,7 +33,7 @@ describe("A Jingle Status", function () { const view = _converse.chatboxviews.get(contact_jid); const chat_head = view.querySelector('.chatbox-title--row'); const chatbox = view.model; - chatbox.save('jingle_status', _converse.JINGLE_CALL_STATUS.PENDING); + chatbox.save('jingle_status', _converse.JINGLE_CALL_STATUS.OUTGOING_PENDING); const header_notification = chat_head.querySelector('converse-call-notification'); const call_intialized = await u.waitUntil(() => header_notification.querySelector('.jingle-call-initiated-button')); call_intialized.click(); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index 5ab43242dd..d0ec1b9671 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -24,12 +24,12 @@ export default class JingleToolbarButton extends CustomElement { toggleJingleCallStatus() { const jingle_status = this.model.get('jingle_status'); - if ( jingle_status === JINGLE_CALL_STATUS.PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); api.send( $msg({ - 'from': this.get('jid'), - 'to': this.get('jid'), + 'from': _converse.bare_jid, + 'to': this.jid, 'type': 'chat' }).c('propose', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) .c('description', {'xmlns': Strophe.NS.JINGLERTP, 'media': 'audio'}).up().up() @@ -38,7 +38,7 @@ export default class JingleToolbarButton extends CustomElement { return; } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { - this.model.save('jingle_status', JINGLE_CALL_STATUS.PENDING); + this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); return; } } From ee9da60eb6e0747b9f12a6930ae8fe059dc88a3d Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Sun, 10 Jul 2022 20:59:11 +0530 Subject: [PATCH 16/28] tests: added tests to message initiation --- src/headless/utils/core.js | 2 +- .../jingle/chat-header-notification.js | 22 +++++++-------- src/plugins/jingle/index.js | 4 ++- .../modal/jingle-incoming-call-modal.js | 18 +++++++++++++ src/plugins/jingle/templates/incoming-call.js | 27 +++++++++++++++++++ .../jingle/tests/message-initiation.js | 4 +-- src/plugins/jingle/tests/ui.js | 2 +- src/plugins/jingle/toolbar-button.js | 8 +++--- src/plugins/jingle/utils.js | 19 +++++++++++++ 9 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 src/plugins/jingle/modal/jingle-incoming-call-modal.js create mode 100644 src/plugins/jingle/templates/incoming-call.js create mode 100644 src/plugins/jingle/utils.js diff --git a/src/headless/utils/core.js b/src/headless/utils/core.js index ea172daf79..f12b7ad8c3 100644 --- a/src/headless/utils/core.js +++ b/src/headless/utils/core.js @@ -24,7 +24,7 @@ export function isEmptyMessage (attrs) { return !attrs['oob_url'] && !attrs['file'] && !(attrs['is_encrypted'] && attrs['plaintext']) && - !attrs['message']; + !attrs['message'] && !attrs['jingle_propose']; } /* We distinguish between UniView and MultiView instances. diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index 5fe20feac5..7a7fc86206 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -4,7 +4,6 @@ import tpl_header_button from "./templates/header-button.js"; import { JINGLE_CALL_STATUS } from "./constants.js"; import './styles/jingle.scss'; - const { Strophe, $msg } = converse.env; export default class CallNotification extends CustomElement { @@ -28,17 +27,16 @@ export default class CallNotification extends CustomElement { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); - api.send( - $msg({ - 'from': _converse.bare_jid, - 'to': this.jid, - 'type': 'chat' - }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) - .c('reason', {'xmlns': Strophe.NS.JINGLE}) - .c('cancel', {}).up() - .t('Retracted').up().up() - .c('store', {'xmlns': Strophe.NS.HINTS}) - ); + const stanza = $msg({ + 'from': _converse.bare_jid, + 'to': this.jid, + 'type': 'chat' + }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + .c('reason', {'xmlns': Strophe.NS.JINGLE}) + .c('cancel', {}).up() + .t('Retracted').up().up() + .c('store', {'xmlns': Strophe.NS.HINTS}) + api.send(stanza); return; } } diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 85d36450e4..a78b637334 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -4,12 +4,13 @@ * @license Mozilla Public License (MPLv2) */ -import { _converse, converse } from '@converse/headless/core.js'; +import { _converse, converse, api } from '@converse/headless/core.js'; import 'plugins/modal/index.js'; import "./chat-header-notification.js"; import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; +import { parseJingleMessage } from './utils.js'; const { Strophe } = converse.env; @@ -45,5 +46,6 @@ converse.plugins.add('converse-jingle', { return buttons; }); + api.listen.on('parseMessage', parseJingleMessage); }, }); diff --git a/src/plugins/jingle/modal/jingle-incoming-call-modal.js b/src/plugins/jingle/modal/jingle-incoming-call-modal.js new file mode 100644 index 0000000000..a5040579a4 --- /dev/null +++ b/src/plugins/jingle/modal/jingle-incoming-call-modal.js @@ -0,0 +1,18 @@ +import BootstrapModal from "plugins/modal/base.js"; +import tpl_incoming_call from "../templates/incoming-call.js"; + +export default BootstrapModal.extend({ + id: "start-jingle-call-modal", + persistent: true, + + initialize () { + this.items = []; + this.loading_items = false; + + BootstrapModal.prototype.initialize.apply(this, arguments); + }, + + toHTML () { + return tpl_incoming_call(); + } +}); diff --git a/src/plugins/jingle/templates/incoming-call.js b/src/plugins/jingle/templates/incoming-call.js new file mode 100644 index 0000000000..8b83ff44ef --- /dev/null +++ b/src/plugins/jingle/templates/incoming-call.js @@ -0,0 +1,27 @@ +import { html } from 'lit'; +import { __ } from 'i18n'; + +const modal_close_button = html``; + +export default () => { + const i18n_modal_title = __('Jingle Call'); + return html` + + `; +} diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index 451075c402..acf523ba61 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -4,7 +4,7 @@ const sizzle = converse.env.sizzle; const { Strophe } = converse.env; -describe("A Jingle Message Initiation Request", function () { +fdescribe("A Jingle Message Initiation Request", function () { it("is sent when the user clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { @@ -29,7 +29,7 @@ describe("A Jingle Message Initiation Request", function () { })); - fit("is ended when the user clicks the toolbar call button again or the chat header end call button", mock.initConverse( + it("is ended when the user clicks the toolbar call button again or the chat header end call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); diff --git a/src/plugins/jingle/tests/ui.js b/src/plugins/jingle/tests/ui.js index 137506bc1b..238fef8265 100644 --- a/src/plugins/jingle/tests/ui.js +++ b/src/plugins/jingle/tests/ui.js @@ -22,7 +22,7 @@ describe("A Jingle Status", function () { expect(chatbox.get('jingle_status')).toBe(_converse.JINGLE_CALL_STATUS.OUTGOING_PENDING); })); - fit("has been shown in the chat-header", + it("has been shown in the chat-header", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current'); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index d0ec1b9671..a37d29ba29 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -26,6 +26,10 @@ export default class JingleToolbarButton extends CustomElement { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + return; + } + if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); api.send( $msg({ 'from': _converse.bare_jid, @@ -37,10 +41,6 @@ export default class JingleToolbarButton extends CustomElement { ); return; } - if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { - this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); - return; - } } } diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js new file mode 100644 index 0000000000..53d30c6035 --- /dev/null +++ b/src/plugins/jingle/utils.js @@ -0,0 +1,19 @@ +import { converse, api } from '@converse/headless/core'; +import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; + +const { Strophe, sizzle } = converse.env; + +export function parseJingleMessage(stanza, attrs) { + return { ...attrs, ...{ 'jingle_propose': getJingleProposeType(stanza) }} +} + +function getJingleProposeType(stanza){ + const el = sizzle(`propose[xmlns="${Strophe.NS.JINGLEMESSAGE}"] > description`, stanza).pop(); + return el?.getAttribute('media'); +} + +export function jingleCallInitialized(el) { + el.listenTo(el.model, 'change:omemo_active', () => { + el.querySelector('converse-chat-toolbar').requestUpdate(); + }); +} From 6c0f422afed725c808751ec874006c2891d7fc90 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Fri, 15 Jul 2022 13:16:02 +0530 Subject: [PATCH 17/28] (TBR)propose id is now shared with the retract stanza --- .../jingle/chat-header-notification.js | 21 ++++++++++++++++--- .../jingle/tests/message-initiation.js | 9 ++++---- src/plugins/jingle/toolbar-button.js | 17 ++++++++++++--- src/plugins/jingle/utils.js | 8 +++---- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index 7a7fc86206..c2e318cc9c 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -25,20 +25,35 @@ export default class CallNotification extends CustomElement { endCall() { const jingle_status = this.model.get('jingle_status'); - if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING ) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + const initiator_stanza = this.model.messages.findWhere({ 'media': 'audio' }); const stanza = $msg({ 'from': _converse.bare_jid, 'to': this.jid, 'type': 'chat' - }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': initiator_stanza.id}) .c('reason', {'xmlns': Strophe.NS.JINGLE}) .c('cancel', {}).up() .t('Retracted').up().up() - .c('store', {'xmlns': Strophe.NS.HINTS}) + .c('store', { 'xmlns': Strophe.NS.HINTS }) api.send(stanza); return; } + // if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + // this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + // const stanza = $msg({ + // 'from': _converse.bare_jid, + // 'to': this.jid, + // 'type': 'chat' + // }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + // .c('reason', {'xmlns': Strophe.NS.JINGLE}) + // .c('success', {}).up() + // .t('Success').up().up() + // .c('store', {'xmlns': Strophe.NS.HINTS}) + // api.send(stanza); + // return; + // } } } diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index acf523ba61..6b26c14969 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -20,6 +20,7 @@ fdescribe("A Jingle Message Initiation Request", function () { expect(Strophe.serialize(stanza)).toBe( ``+ ``+ ``+ @@ -29,20 +30,20 @@ fdescribe("A Jingle Message Initiation Request", function () { })); - it("is ended when the user clicks the toolbar call button again or the chat header end call button", mock.initConverse( + it("is ended when the initiator clicks the toolbar call button again or the chat header end call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; await mock.openChatBoxFor(_converse, contact_jid); - const view = _converse.chatboxviews.get(contact_jid); - const call_button = view.querySelector('converse-jingle-toolbar-button button'); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); call_button.click(); call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`reason`, s).length).pop()); expect(Strophe.serialize(stanza)).toBe( - ``+ ``+ diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index a37d29ba29..2ddbe18c6a 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -4,6 +4,7 @@ import { JINGLE_CALL_STATUS } from "./constants.js"; import tpl_toolbar_button from "./templates/toolbar-button.js"; const { Strophe, $msg } = converse.env; +const u = converse.env.utils; export default class JingleToolbarButton extends CustomElement { @@ -30,15 +31,25 @@ export default class JingleToolbarButton extends CustomElement { } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); + const propose_id = u.getUniqueId(); api.send( $msg({ 'from': _converse.bare_jid, 'to': this.jid, - 'type': 'chat' - }).c('propose', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + 'type': 'chat', + 'id': propose_id, + }).c('propose', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) .c('description', {'xmlns': Strophe.NS.JINGLERTP, 'media': 'audio'}).up().up() - .c('store', {'xmlns': Strophe.NS.HINTS}) + .c('store', { 'xmlns': Strophe.NS.HINTS }) ); + const attrs = { + 'from': _converse.bare_jid, + 'to': this.jid, + 'type': 'chat', + 'id': propose_id, + 'media': 'audio' + } + this.model.messages.create(attrs); return; } } diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index 53d30c6035..fb6a309e71 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -1,4 +1,4 @@ -import { converse, api } from '@converse/headless/core'; +import { converse } from '@converse/headless/core'; import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; const { Strophe, sizzle } = converse.env; @@ -12,8 +12,6 @@ function getJingleProposeType(stanza){ return el?.getAttribute('media'); } -export function jingleCallInitialized(el) { - el.listenTo(el.model, 'change:omemo_active', () => { - el.querySelector('converse-chat-toolbar').requestUpdate(); - }); +export function jingleCallInitialized() { + JingleCallModal; } From 322af186360e9b7896dce6aee3a95df557d05374 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 18 Jul 2022 17:14:47 +0530 Subject: [PATCH 18/28] (TBR) corrected message initiation test --- .../jingle/chat-header-notification.js | 28 +++++++++---------- .../jingle/tests/message-initiation.js | 25 ++++++++++------- src/plugins/jingle/toolbar-button.js | 6 ++-- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index c2e318cc9c..05ff737dee 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -40,20 +40,20 @@ export default class CallNotification extends CustomElement { api.send(stanza); return; } - // if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { - // this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); - // const stanza = $msg({ - // 'from': _converse.bare_jid, - // 'to': this.jid, - // 'type': 'chat' - // }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) - // .c('reason', {'xmlns': Strophe.NS.JINGLE}) - // .c('success', {}).up() - // .t('Success').up().up() - // .c('store', {'xmlns': Strophe.NS.HINTS}) - // api.send(stanza); - // return; - // } + if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + const stanza = $msg({ + 'from': _converse.bare_jid, + 'to': this.jid, + 'type': 'chat' + }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) + .c('reason', {'xmlns': Strophe.NS.JINGLE}) + .c('success', {}).up() + .t('Success').up().up() + .c('store', {'xmlns': Strophe.NS.HINTS}) + api.send(stanza); + return; + } } } diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index 6b26c14969..d42438bff3 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -4,9 +4,9 @@ const sizzle = converse.env.sizzle; const { Strophe } = converse.env; -fdescribe("A Jingle Message Initiation Request", function () { +describe("A Jingle Message Initiation Request", function () { - it("is sent when the user clicks the call button", mock.initConverse( + fit("is sent out when the user clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -17,20 +17,23 @@ fdescribe("A Jingle Message Initiation Request", function () { call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); + const propose_id = stanza.querySelector('propose'); expect(Strophe.serialize(stanza)).toBe( - ``+ - ``+ - ``+ + ``+ + ``+ + ``+ ``+ - ``+ + ``+ ``); + expect(view.model.messages.length).toEqual(1); })); - it("is ended when the initiator clicks the toolbar call button again or the chat header end call button", mock.initConverse( + it("is ended when the initiator clicks the call button again", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -38,6 +41,7 @@ fdescribe("A Jingle Message Initiation Request", function () { await mock.openChatBoxFor(_converse, contact_jid); const view = _converse.chatboxviews.get(contact_jid); const call_button = view.querySelector('converse-jingle-toolbar-button button'); + // the first click starts the call, and the other one ends it call_button.click(); call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; @@ -55,5 +59,6 @@ fdescribe("A Jingle Message Initiation Request", function () { ``+ `` ); + expect(view.model.messages.length).toEqual(1); })); }); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index 2ddbe18c6a..549c86aba5 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -32,12 +32,13 @@ export default class JingleToolbarButton extends CustomElement { if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); const propose_id = u.getUniqueId(); + const message_id = u.getUniqueId(); api.send( $msg({ 'from': _converse.bare_jid, 'to': this.jid, 'type': 'chat', - 'id': propose_id, + 'id': message_id, }).c('propose', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) .c('description', {'xmlns': Strophe.NS.JINGLERTP, 'media': 'audio'}).up().up() .c('store', { 'xmlns': Strophe.NS.HINTS }) @@ -46,7 +47,8 @@ export default class JingleToolbarButton extends CustomElement { 'from': _converse.bare_jid, 'to': this.jid, 'type': 'chat', - 'id': propose_id, + 'msg_id': message_id, + 'propose_id': propose_id, 'media': 'audio' } this.model.messages.create(attrs); From bbcded7eaa80568e9da0b8b951fcd9022ca26cc5 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 18 Jul 2022 19:21:46 +0530 Subject: [PATCH 19/28] changed the describe function text --- .../jingle/tests/message-initiation.js | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index d42438bff3..4891ecc8e5 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -6,7 +6,9 @@ const { Strophe } = converse.env; describe("A Jingle Message Initiation Request", function () { - fit("is sent out when the user clicks the call button", mock.initConverse( + describe("from the initiator's perspective", function () { + + fit("is sent out when one clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -61,4 +63,61 @@ describe("A Jingle Message Initiation Request", function () { ); expect(view.model.messages.length).toEqual(1); })); + }); + + describe("from the reciever's perspective", function () { + + fit("is recieved when the initiator clicks the call button", mock.initConverse( + ['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); + const propose_id = stanza.querySelector('propose'); + const initiator_stanza = u.toStanza(` + + + + + + `); + _converse.connection._dataRecv(mock.createRequest(initiator_stanza)); + expect(view.model.messages.length).toEqual(1); + })); + + fit("is recieved when the initiator clicks the call button", mock.initConverse( + ['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); + const propose_id = stanza.querySelector('propose'); + expect(Strophe.serialize(stanza)).toBe( + ``+ + ``+ + ``+ + ``+ + ``+ + ``); + expect(view.model.messages.length).toEqual(1); + })); + + }); }); From c9783853350257f55fc2371c0f396eefecbc43f7 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Tue, 19 Jul 2022 11:52:10 +0530 Subject: [PATCH 20/28] changed the describe function text --- .../jingle/chat-header-notification.js | 3 ++- .../jingle/tests/message-initiation.js | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index 05ff737dee..d5261c2951 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -28,11 +28,12 @@ export default class CallNotification extends CustomElement { if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING ) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); const initiator_stanza = this.model.messages.findWhere({ 'media': 'audio' }); + const initiator_id = initiator_stanza.attributes.propose_id; const stanza = $msg({ 'from': _converse.bare_jid, 'to': this.jid, 'type': 'chat' - }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': initiator_stanza.id}) + }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': initiator_id}) .c('reason', {'xmlns': Strophe.NS.JINGLE}) .c('cancel', {}).up() .t('Retracted').up().up() diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index 4891ecc8e5..e480e6c42e 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -8,7 +8,7 @@ describe("A Jingle Message Initiation Request", function () { describe("from the initiator's perspective", function () { - fit("is sent out when one clicks the call button", mock.initConverse( + it("is sent out when one clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -35,7 +35,7 @@ describe("A Jingle Message Initiation Request", function () { })); - it("is ended when the initiator clicks the call button again", mock.initConverse( + fit("is ended when the initiator clicks the call button again", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -47,12 +47,14 @@ describe("A Jingle Message Initiation Request", function () { call_button.click(); call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; - const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`reason`, s).length).pop()); + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`reason[xmlns="${Strophe.NS.JINGLE}"]`, s).length).pop()); + const propose_id = stanza.querySelector('propose'); expect(Strophe.serialize(stanza)).toBe( - ``+ - ``+ + ` `+ + ``+ ``+ ``+ `Retracted`+ @@ -62,12 +64,12 @@ describe("A Jingle Message Initiation Request", function () { `` ); expect(view.model.messages.length).toEqual(1); - })); + })); }); describe("from the reciever's perspective", function () { - fit("is recieved when the initiator clicks the call button", mock.initConverse( + it("is recieved when the initiator clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -93,7 +95,7 @@ describe("A Jingle Message Initiation Request", function () { expect(view.model.messages.length).toEqual(1); })); - fit("is recieved when the initiator clicks the call button", mock.initConverse( + it("is recieved when the initiator clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); From 1140ad86309757f375042b6956e0e10d44e4973a Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Sun, 24 Jul 2022 21:33:51 +0530 Subject: [PATCH 21/28] (TBR) corrected some tests --- src/headless/plugins/chat/utils.js | 4 +- src/plugins/chatview/tests/chatbox.js | 2 +- .../jingle/chat-header-notification.js | 24 ++- src/plugins/jingle/templates/header-button.js | 4 +- .../jingle/tests/message-initiation.js | 142 +++++++++++------- src/plugins/jingle/toolbar-button.js | 23 +++ src/plugins/jingle/utils.js | 1 + 7 files changed, 135 insertions(+), 65 deletions(-) diff --git a/src/headless/plugins/chat/utils.js b/src/headless/plugins/chat/utils.js index 0f865cee50..c80cf8e3fb 100644 --- a/src/headless/plugins/chat/utils.js +++ b/src/headless/plugins/chat/utils.js @@ -125,8 +125,8 @@ export async function handleMessageStanza (stanza) { return log.error(attrs.message); } // XXX: Need to take XEP-428 into consideration - const has_body = !!(attrs.body || attrs.plaintext) - const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, has_body); + const should_create = !!(attrs.body || attrs.plaintext || attrs.jingle_propose) + const chatbox = await api.chats.get(attrs.contact_jid, { 'nickname': attrs.nick }, should_create); await chatbox?.queueMessage(attrs); /** * @typedef { Object } MessageData diff --git a/src/plugins/chatview/tests/chatbox.js b/src/plugins/chatview/tests/chatbox.js index 968413cb10..28e26e9d5d 100644 --- a/src/plugins/chatview/tests/chatbox.js +++ b/src/plugins/chatview/tests/chatbox.js @@ -296,7 +296,7 @@ describe("Chatboxes", function () { })); - fit("can contain a button for starting a call", + it("can contain a button for starting a call", mock.initConverse(['chatBoxesFetched'], { blacklisted_plugins: ['converse-jingle']}, async function (_converse) { const { api } = _converse; diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index d5261c2951..c3cb4be6c2 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -5,6 +5,7 @@ import { JINGLE_CALL_STATUS } from "./constants.js"; import './styles/jingle.scss'; const { Strophe, $msg } = converse.env; +const u = converse.env.utils; export default class CallNotification extends CustomElement { @@ -28,17 +29,28 @@ export default class CallNotification extends CustomElement { if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING ) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); const initiator_stanza = this.model.messages.findWhere({ 'media': 'audio' }); - const initiator_id = initiator_stanza.attributes.propose_id; - const stanza = $msg({ + const propose_id = initiator_stanza.attributes.propose_id; + const message_id = u.getUniqueId(); + api.send( + $msg({ 'from': _converse.bare_jid, 'to': this.jid, - 'type': 'chat' - }).c('retract', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': initiator_id}) - .c('reason', {'xmlns': Strophe.NS.JINGLE}) + 'type': 'chat', + id: message_id + }).c('retract', { 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) + .c('reason', { 'xmlns': Strophe.NS.JINGLE }) .c('cancel', {}).up() .t('Retracted').up().up() .c('store', { 'xmlns': Strophe.NS.HINTS }) - api.send(stanza); + ); + const attrs = { + 'from': _converse.bare_jid, + 'to': this.jid, + 'type': 'chat', + 'retract_id': propose_id, + 'msg_id': message_id + } + this.model.messages.create(attrs); return; } if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { diff --git a/src/plugins/jingle/templates/header-button.js b/src/plugins/jingle/templates/header-button.js index 35b2273027..062ec35e17 100644 --- a/src/plugins/jingle/templates/header-button.js +++ b/src/plugins/jingle/templates/header-button.js @@ -3,10 +3,10 @@ import { __ } from 'i18n'; import { JINGLE_CALL_STATUS } from '../constants'; const tpl_active_call = (o) => { - const button = __('Call Inititated'); + const button = __('End Call'); return html` `; } diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index e480e6c42e..0e222e664d 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -8,7 +8,7 @@ describe("A Jingle Message Initiation Request", function () { describe("from the initiator's perspective", function () { - it("is sent out when one clicks the call button", mock.initConverse( + fit("is sent out when one clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -26,100 +26,134 @@ describe("A Jingle Message Initiation Request", function () { `to="${contact_jid}" `+ `type="chat" `+ `xmlns="jabber:client">`+ - ``+ + ``+ ``+ - ``+ + ``+ ``+ ``); - expect(view.model.messages.length).toEqual(1); + expect(view.model.messages.length).toEqual(1); })); - fit("is ended when the initiator clicks the call button again", mock.initConverse( + it("is ended when the initiator clicks the call button again", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; await mock.openChatBoxFor(_converse, contact_jid); - const view = _converse.chatboxviews.get(contact_jid); - const call_button = view.querySelector('converse-jingle-toolbar-button button'); + const view = _converse.chatboxviews.get(contact_jid); // the first click starts the call, and the other one ends it + const call_button = view.querySelector('converse-jingle-toolbar-button button'); call_button.click(); call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; - const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`reason[xmlns="${Strophe.NS.JINGLE}"]`, s).length).pop()); - const propose_id = stanza.querySelector('propose'); + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`retract[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); + const retract_id = stanza.querySelector('retract'); expect(Strophe.serialize(stanza)).toBe( ` `+ - ``+ + `to="${contact_jid}" `+ + `type="chat" `+ + `xmlns="jabber:client">`+ + ``+ ``+ ``+ - `Retracted`+ + `Retracted`+ ``+ - ` `+ - ``+ - `` - ); + ``+ + ``+ + ``); + // This needs to be fixed expect(view.model.messages.length).toEqual(1); - })); + })); + }); + + //todo Add a test of the chat header + it("is ended when the initiator clicks the end call header button", mock.initConverse( + ['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + // the first click starts the call, and the other one ends it + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + call_button.click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`retract[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); + const retract_id = stanza.querySelector('retract'); + expect(Strophe.serialize(stanza)).toBe( + ``+ + ``+ + ``+ + ``+ + `Retracted`+ + ``+ + ``+ + ``+ + ``); + // This needs to be fixed + expect(view.model.messages.length).toEqual(1); + })); }); - describe("from the reciever's perspective", function () { + describe("from the receiver's perspective", function () { - it("is recieved when the initiator clicks the call button", mock.initConverse( - ['chatBoxesFetched'], {}, async function (_converse) { + it("is received when the initiator clicks the call button", mock.initConverse( + ['chatBoxesFetched'], { allow_non_roster_messaging: true }, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); - const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; - await mock.openChatBoxFor(_converse, contact_jid); - const view = _converse.chatboxviews.get(contact_jid); - const call_button = view.querySelector('converse-jingle-toolbar-button button'); - call_button.click(); - const sent_stanzas = _converse.connection.sent_stanzas; - const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); - const propose_id = stanza.querySelector('propose'); + const contact_jid = mock.cur_names[0].replace(/ /g, '.').toLowerCase() + '@montague.lit'; + const propose_id = u.getUniqueId(); const initiator_stanza = u.toStanza(` - + `); - _converse.connection._dataRecv(mock.createRequest(initiator_stanza)); - expect(view.model.messages.length).toEqual(1); + _converse.connection._dataRecv(mock.createRequest(initiator_stanza)); + + const view = await u.waitUntil(() => _converse.chatboxviews.get(contact_jid)); + expect(view.model.messages.length).toEqual(1); + const message = view.model.messages.at(0); })); - it("is recieved when the initiator clicks the call button", mock.initConverse( + it("is received when the initiator clicks the end call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { - await mock.waitForRoster(_converse, 'current', 1); - const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; - await mock.openChatBoxFor(_converse, contact_jid); - const view = _converse.chatboxviews.get(contact_jid); - const call_button = view.querySelector('converse-jingle-toolbar-button button'); - call_button.click(); - const sent_stanzas = _converse.connection.sent_stanzas; - const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); - const propose_id = stanza.querySelector('propose'); - expect(Strophe.serialize(stanza)).toBe( - ``+ - ``+ - ``+ - ``+ - ``+ - ``); + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const sent_stanzas = _converse.connection.sent_stanzas; + const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`propose[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); + const propose_id = stanza.querySelector('propose'); + const initiator_stanza = u.toStanza(` + + + + + Retracted + + + + `); + _converse.connection._dataRecv(mock.createRequest(initiator_stanza)); expect(view.model.messages.length).toEqual(1); })); - }); }); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index 549c86aba5..e8feeefddb 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -27,6 +27,29 @@ export default class JingleToolbarButton extends CustomElement { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + const initiator_stanza = this.model.messages.findWhere({ 'media': 'audio' }); + const propose_id = initiator_stanza.attributes.propose_id; + const message_id = u.getUniqueId(); + api.send( + $msg({ + 'from': _converse.bare_jid, + 'to': this.jid, + 'type': 'chat', + id: message_id + }).c('retract', { 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) + .c('reason', { 'xmlns': Strophe.NS.JINGLE }) + .c('cancel', {}).up() + .t('Retracted').up().up() + .c('store', { 'xmlns': Strophe.NS.HINTS }) + ); + const attrs = { + 'from': _converse.bare_jid, + 'to': this.jid, + 'type': 'chat', + 'retract_id': propose_id, + 'msg_id': message_id + } + this.model.messages.create(attrs); return; } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index fb6a309e71..9c7b97034d 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -4,6 +4,7 @@ import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; const { Strophe, sizzle } = converse.env; export function parseJingleMessage(stanza, attrs) { + // editable: false, retracted_id: {if there is a retractions set it to the id}, retracted(timestamp) return { ...attrs, ...{ 'jingle_propose': getJingleProposeType(stanza) }} } From bbb985e008f3abf43c7aaa3a7ec7c501648edcf3 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Tue, 26 Jul 2022 16:34:50 +0530 Subject: [PATCH 22/28] (TBR) fixed the tests and added the On message hook --- src/headless/plugins/chat/model.js | 1 + src/plugins/jingle/index.js | 3 ++- src/plugins/jingle/tests/message-initiation.js | 18 ++++++++---------- src/plugins/jingle/utils.js | 4 ++++ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/headless/plugins/chat/model.js b/src/headless/plugins/chat/model.js index cdbcaf3144..4881bfa9d5 100644 --- a/src/headless/plugins/chat/model.js +++ b/src/headless/plugins/chat/model.js @@ -228,6 +228,7 @@ const ChatBox = ModelWithContact.extend({ !this.handleChatMarker(attrs) && !(await this.handleRetraction(attrs)) ) { + const message_retraction = await api.hook('onMessage', this, { retract: false }); this.setEditable(attrs, attrs.time); if (attrs['chat_state'] && attrs.sender === 'them') { diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index a78b637334..1334ce6305 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -10,7 +10,7 @@ import "./chat-header-notification.js"; import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; -import { parseJingleMessage } from './utils.js'; +import { parseJingleMessage, handleRetraction } from './utils.js'; const { Strophe } = converse.env; @@ -47,5 +47,6 @@ converse.plugins.add('converse-jingle', { return buttons; }); api.listen.on('parseMessage', parseJingleMessage); + api.listen.on('onMessage', handleRetraction); }, }); diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index 0e222e664d..b21a6f4d71 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -8,7 +8,7 @@ describe("A Jingle Message Initiation Request", function () { describe("from the initiator's perspective", function () { - fit("is sent out when one clicks the call button", mock.initConverse( + it("is sent out when one clicks the call button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -66,10 +66,8 @@ describe("A Jingle Message Initiation Request", function () { // This needs to be fixed expect(view.model.messages.length).toEqual(1); })); - }); - //todo Add a test of the chat header - it("is ended when the initiator clicks the end call header button", mock.initConverse( + fit("is ended when the initiator clicks the end call header button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -79,7 +77,8 @@ describe("A Jingle Message Initiation Request", function () { // the first click starts the call, and the other one ends it const call_button = view.querySelector('converse-jingle-toolbar-button button'); call_button.click(); - call_button.click(); + const header_end_call_button = await u.waitUntil(() => view.querySelector('.jingle-call-initiated-button')); + header_end_call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`retract[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); const retract_id = stanza.querySelector('retract'); @@ -104,7 +103,7 @@ describe("A Jingle Message Initiation Request", function () { describe("from the receiver's perspective", function () { - it("is received when the initiator clicks the call button", mock.initConverse( + fit("is received when the initiator clicks the call button", mock.initConverse( ['chatBoxesFetched'], { allow_non_roster_messaging: true }, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -120,11 +119,10 @@ describe("A Jingle Message Initiation Request", function () { `); - _converse.connection._dataRecv(mock.createRequest(initiator_stanza)); + _converse.connection._dataRecv(mock.createRequest(initiator_stanza)); - const view = await u.waitUntil(() => _converse.chatboxviews.get(contact_jid)); - expect(view.model.messages.length).toEqual(1); - const message = view.model.messages.at(0); + const view = await u.waitUntil(() => _converse.chatboxviews.get(contact_jid)); + expect(view.model.messages.length).toEqual(1); })); it("is received when the initiator clicks the end call button", mock.initConverse( diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index 9c7b97034d..aaea2bcd5d 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -16,3 +16,7 @@ function getJingleProposeType(stanza){ export function jingleCallInitialized() { JingleCallModal; } + +export function handleRetraction(context, retracted) { + +} From 1235d421103c56576a905674d4222838d47d5e80 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Thu, 4 Aug 2022 10:33:45 +0530 Subject: [PATCH 23/28] (TBR)added the message retraction feature --- src/headless/plugins/chat/model.js | 8 +- src/headless/plugins/chat/utils.js | 2 +- .../jingle/chat-header-notification.js | 27 +------ .../jingle/tests/message-initiation.js | 14 ++-- src/plugins/jingle/toolbar-button.js | 27 +------ src/plugins/jingle/utils.js | 77 +++++++++++++++++-- 6 files changed, 90 insertions(+), 65 deletions(-) diff --git a/src/headless/plugins/chat/model.js b/src/headless/plugins/chat/model.js index 4881bfa9d5..d3813d0e9b 100644 --- a/src/headless/plugins/chat/model.js +++ b/src/headless/plugins/chat/model.js @@ -228,7 +228,11 @@ const ChatBox = ModelWithContact.extend({ !this.handleChatMarker(attrs) && !(await this.handleRetraction(attrs)) ) { - const message_retraction = await api.hook('onMessage', this, { retract: false }); + debugger + const { handled } = await api.hook('onMessage', this, { handled: false, attrs }); + console.log(handled) + if (handled) return; + this.setEditable(attrs, attrs.time); if (attrs['chat_state'] && attrs.sender === 'them') { @@ -584,7 +588,7 @@ const ChatBox = ModelWithContact.extend({ if (attrs.is_tombstone) { return false; } - const message = this.messages.findWhere({'origin_id': attrs.retracted_id, 'from': attrs.from}); + const message = this.messages.findWhere({ 'origin_id': attrs.retracted_id, 'from': attrs.from }); if (!message) { attrs['dangling_retraction'] = true; await this.createMessage(attrs); diff --git a/src/headless/plugins/chat/utils.js b/src/headless/plugins/chat/utils.js index c80cf8e3fb..3b17fd5ad9 100644 --- a/src/headless/plugins/chat/utils.js +++ b/src/headless/plugins/chat/utils.js @@ -106,7 +106,7 @@ export function registerMessageHandlers () { /** * Handler method for all incoming single-user chat "message" stanzas. - * @param { MessageAttributes } attrs - The message attributes + * @param { XMLElement } stanza - The message stanza */ export async function handleMessageStanza (stanza) { if (isServerMessage(stanza)) { diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index c3cb4be6c2..acad9ec637 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -2,10 +2,11 @@ import { CustomElement } from 'shared/components/element.js'; import { _converse, api, converse } from "@converse/headless/core"; import tpl_header_button from "./templates/header-button.js"; import { JINGLE_CALL_STATUS } from "./constants.js"; +import { retractCall } from './utils.js'; + import './styles/jingle.scss'; const { Strophe, $msg } = converse.env; -const u = converse.env.utils; export default class CallNotification extends CustomElement { @@ -28,29 +29,7 @@ export default class CallNotification extends CustomElement { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING ) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); - const initiator_stanza = this.model.messages.findWhere({ 'media': 'audio' }); - const propose_id = initiator_stanza.attributes.propose_id; - const message_id = u.getUniqueId(); - api.send( - $msg({ - 'from': _converse.bare_jid, - 'to': this.jid, - 'type': 'chat', - id: message_id - }).c('retract', { 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) - .c('reason', { 'xmlns': Strophe.NS.JINGLE }) - .c('cancel', {}).up() - .t('Retracted').up().up() - .c('store', { 'xmlns': Strophe.NS.HINTS }) - ); - const attrs = { - 'from': _converse.bare_jid, - 'to': this.jid, - 'type': 'chat', - 'retract_id': propose_id, - 'msg_id': message_id - } - this.model.messages.create(attrs); + retractCall(this); return; } if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index b21a6f4d71..2d3fea5744 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -4,7 +4,7 @@ const sizzle = converse.env.sizzle; const { Strophe } = converse.env; -describe("A Jingle Message Initiation Request", function () { +fdescribe("A Jingle Message Initiation Request", function () { describe("from the initiator's perspective", function () { @@ -48,14 +48,14 @@ describe("A Jingle Message Initiation Request", function () { call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`retract[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); - const retract_id = stanza.querySelector('retract'); + const jingle_retraction_id = stanza.querySelector('retract'); expect(Strophe.serialize(stanza)).toBe( ``+ - ``+ + ``+ ``+ ``+ `Retracted`+ @@ -67,7 +67,7 @@ describe("A Jingle Message Initiation Request", function () { expect(view.model.messages.length).toEqual(1); })); - fit("is ended when the initiator clicks the end call header button", mock.initConverse( + it("is ended when the initiator clicks the end call header button", mock.initConverse( ['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); @@ -81,14 +81,14 @@ describe("A Jingle Message Initiation Request", function () { header_end_call_button.click(); const sent_stanzas = _converse.connection.sent_stanzas; const stanza = await u.waitUntil(() => sent_stanzas.filter(s => sizzle(`retract[xmlns='${Strophe.NS.JINGLEMESSAGE}']`, s).length).pop()); - const retract_id = stanza.querySelector('retract'); + const jingle_retraction_id = stanza.querySelector('retract'); expect(Strophe.serialize(stanza)).toBe( ``+ - ``+ + ``+ ``+ ``+ `Retracted`+ @@ -103,7 +103,7 @@ describe("A Jingle Message Initiation Request", function () { describe("from the receiver's perspective", function () { - fit("is received when the initiator clicks the call button", mock.initConverse( + it("is received when the initiator clicks the call button", mock.initConverse( ['chatBoxesFetched'], { allow_non_roster_messaging: true }, async function (_converse) { await mock.waitForRoster(_converse, 'current', 1); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index e8feeefddb..f98aa516f8 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -2,6 +2,7 @@ import { CustomElement } from 'shared/components/element.js'; import { converse, _converse, api } from "@converse/headless/core"; import { JINGLE_CALL_STATUS } from "./constants.js"; import tpl_toolbar_button from "./templates/toolbar-button.js"; +import { retractCall } from './utils.js'; const { Strophe, $msg } = converse.env; const u = converse.env.utils; @@ -22,34 +23,12 @@ export default class JingleToolbarButton extends CustomElement { render() { return tpl_toolbar_button(this); } - + // To be done Put this into utils & call the function toggleJingleCallStatus() { const jingle_status = this.model.get('jingle_status'); if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); - const initiator_stanza = this.model.messages.findWhere({ 'media': 'audio' }); - const propose_id = initiator_stanza.attributes.propose_id; - const message_id = u.getUniqueId(); - api.send( - $msg({ - 'from': _converse.bare_jid, - 'to': this.jid, - 'type': 'chat', - id: message_id - }).c('retract', { 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) - .c('reason', { 'xmlns': Strophe.NS.JINGLE }) - .c('cancel', {}).up() - .t('Retracted').up().up() - .c('store', { 'xmlns': Strophe.NS.HINTS }) - ); - const attrs = { - 'from': _converse.bare_jid, - 'to': this.jid, - 'type': 'chat', - 'retract_id': propose_id, - 'msg_id': message_id - } - this.model.messages.create(attrs); + retractCall(this); return; } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index aaea2bcd5d..d531c4b240 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -1,11 +1,20 @@ -import { converse } from '@converse/headless/core'; -import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; +import { converse, api, _converse } from '@converse/headless/core'; +import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; -const { Strophe, sizzle } = converse.env; +const { Strophe, sizzle, $msg } = converse.env; +const u = converse.env.utils; + +/** + * This function merges the incoming attributes and the jingle propose attribute into one. + * It also determines the type of the media i.e. audio or video + * @param { XMLElement } stanza + * @param { Object } attrs + */ export function parseJingleMessage(stanza, attrs) { - // editable: false, retracted_id: {if there is a retractions set it to the id}, retracted(timestamp) - return { ...attrs, ...{ 'jingle_propose': getJingleProposeType(stanza) }} + const jingle_propose_type = getJingleProposeType(stanza); + // To do editable: false, retracted_id: {if there is a retractions set it to the id}, retracted(timestamp) + return { ...attrs, ...{ 'jingle_propose': jingle_propose_type, 'jingle_retraction_id': getJingleRetractionID(stanza) }} } function getJingleProposeType(stanza){ @@ -13,10 +22,64 @@ function getJingleProposeType(stanza){ return el?.getAttribute('media'); } +function getJingleRetractionID(stanza){ + const el = sizzle(`propose[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); + return el?.getAttribute('id'); +} export function jingleCallInitialized() { JingleCallModal; } -export function handleRetraction(context, retracted) { - +/** + * This function simply sends the retraction stanza and modifies the attributes + */ +export function retractCall(context) { + const initiator_message = context.model.messages.findWhere({ 'media': 'audio' }); + const propose_id = initiator_message.attributes.propose_id; + const message_id = u.getUniqueId(); + api.send( + $msg({ + 'from': _converse.bare_jid, + 'to': context.jid, + 'type': 'chat', + id: message_id + }).c('retract', { 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) + .c('reason', { 'xmlns': Strophe.NS.JINGLE }) + .c('cancel', {}).up() + .t('Retracted').up().up() + .c('store', { 'xmlns': Strophe.NS.HINTS }) + ); + const attrs = { + 'from': _converse.bare_jid, + 'to': context.jid, + 'type': 'chat', + 'jingle_retraction_id': propose_id, + 'msg_id': message_id, + } + context.model.messages.create(attrs); +} + +/** + * This is the handler for the 'onMessage' hook + * It inspects the incoming message attributes and checks whether we have is a jingle retraction message + * if it is, then we find the jingle propose message and update it. + * @param { _converse.ChatBox } model + * @param { } data + */ +export async function handleRetraction(model, data) { + const jingle_retraction_id = data.attrs['jingle_retraction_id']; + // const propose_id = data.attrs['propose_id']; + if (jingle_retraction_id) { + //finding the propose message with the same id as the retraction id + const message = model.messages.findWhere({ 'jingle_propose': 'audio' }); + if (message) { + message.save(data.attrs, { 'propose_id': jingle_retraction_id }); + data.handled = true; + } + else { + // It is a dangling retraction; we are waiting for the correct propose message + await _converse.ChatBox.createMessage(data.attrs); + } + } + return data; } From b3ace4d1544e6f18a2bc5c5b10c9de651c541470 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 8 Aug 2022 12:34:43 +0530 Subject: [PATCH 24/28] chat history: this commit adds the chat history of the jingle call status --- src/headless/plugins/chat/model.js | 2 - .../jingle/chat-header-notification.js | 16 ++------ src/plugins/jingle/index.js | 3 +- .../jingle/templates/jingle_chat_history.js | 12 ++++++ src/plugins/jingle/toolbar-button.js | 13 +++++-- src/plugins/jingle/utils.js | 38 ++++++++++++++++++- 6 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 src/plugins/jingle/templates/jingle_chat_history.js diff --git a/src/headless/plugins/chat/model.js b/src/headless/plugins/chat/model.js index d3813d0e9b..412679c474 100644 --- a/src/headless/plugins/chat/model.js +++ b/src/headless/plugins/chat/model.js @@ -228,9 +228,7 @@ const ChatBox = ModelWithContact.extend({ !this.handleChatMarker(attrs) && !(await this.handleRetraction(attrs)) ) { - debugger const { handled } = await api.hook('onMessage', this, { handled: false, attrs }); - console.log(handled) if (handled) return; this.setEditable(attrs, attrs.time); diff --git a/src/plugins/jingle/chat-header-notification.js b/src/plugins/jingle/chat-header-notification.js index acad9ec637..e310f21f26 100644 --- a/src/plugins/jingle/chat-header-notification.js +++ b/src/plugins/jingle/chat-header-notification.js @@ -1,12 +1,11 @@ import { CustomElement } from 'shared/components/element.js'; -import { _converse, api, converse } from "@converse/headless/core"; +import { _converse, api } from "@converse/headless/core"; import tpl_header_button from "./templates/header-button.js"; import { JINGLE_CALL_STATUS } from "./constants.js"; -import { retractCall } from './utils.js'; +import { retractCall, finishCall } from './utils.js'; import './styles/jingle.scss'; -const { Strophe, $msg } = converse.env; export default class CallNotification extends CustomElement { @@ -34,16 +33,7 @@ export default class CallNotification extends CustomElement { } if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); - const stanza = $msg({ - 'from': _converse.bare_jid, - 'to': this.jid, - 'type': 'chat' - }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': this.getAttribute('id')}) - .c('reason', {'xmlns': Strophe.NS.JINGLE}) - .c('success', {}).up() - .t('Success').up().up() - .c('store', {'xmlns': Strophe.NS.HINTS}) - api.send(stanza); + finishCall(this); return; } } diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 1334ce6305..3bbf758678 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -10,7 +10,7 @@ import "./chat-header-notification.js"; import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; -import { parseJingleMessage, handleRetraction } from './utils.js'; +import { parseJingleMessage, handleRetraction, getJingleTemplate } from './utils.js'; const { Strophe } = converse.env; @@ -48,5 +48,6 @@ converse.plugins.add('converse-jingle', { }); api.listen.on('parseMessage', parseJingleMessage); api.listen.on('onMessage', handleRetraction); + api.listen.on('getJingleTemplate', getJingleTemplate); }, }); diff --git a/src/plugins/jingle/templates/jingle_chat_history.js b/src/plugins/jingle/templates/jingle_chat_history.js new file mode 100644 index 0000000000..a32faf747a --- /dev/null +++ b/src/plugins/jingle/templates/jingle_chat_history.js @@ -0,0 +1,12 @@ +import { __ } from 'i18n'; +import { html } from "lit"; +import { JINGLE_CALL_STATUS } from "../constants.js"; + +export default (o) => { + const ended_call = __('Call Ended'); + const pending_call = __('Calling'); + return html` + ${ (o.get('jingle_status') === JINGLE_CALL_STATUS.OUTGOING_PENDING && o.get('jingle_status')!= undefined ) ? html`${pending_call}` : html`${ended_call}` } +`} + + diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index f98aa516f8..7cef4716f6 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -2,7 +2,7 @@ import { CustomElement } from 'shared/components/element.js'; import { converse, _converse, api } from "@converse/headless/core"; import { JINGLE_CALL_STATUS } from "./constants.js"; import tpl_toolbar_button from "./templates/toolbar-button.js"; -import { retractCall } from './utils.js'; +import { retractCall, finishCall } from './utils.js'; const { Strophe, $msg } = converse.env; const u = converse.env.utils; @@ -23,14 +23,18 @@ export default class JingleToolbarButton extends CustomElement { render() { return tpl_toolbar_button(this); } - // To be done Put this into utils & call the function toggleJingleCallStatus() { const jingle_status = this.model.get('jingle_status'); - if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + if ( jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING) { this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); retractCall(this); return; } + if ( jingle_status === JINGLE_CALL_STATUS.ACTIVE) { + this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + finishCall(this); + return; + } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); const propose_id = u.getUniqueId(); @@ -51,7 +55,8 @@ export default class JingleToolbarButton extends CustomElement { 'type': 'chat', 'msg_id': message_id, 'propose_id': propose_id, - 'media': 'audio' + 'media': 'audio', + 'template_hook': 'getJingleTemplate' } this.model.messages.create(attrs); return; diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index d531c4b240..e33ca809b5 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -1,5 +1,6 @@ import { converse, api, _converse } from '@converse/headless/core'; import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; +import tpl_jingle_chat_history from "./templates/jingle_chat_history.js"; const { Strophe, sizzle, $msg } = converse.env; const u = converse.env.utils; @@ -14,7 +15,7 @@ const u = converse.env.utils; export function parseJingleMessage(stanza, attrs) { const jingle_propose_type = getJingleProposeType(stanza); // To do editable: false, retracted_id: {if there is a retractions set it to the id}, retracted(timestamp) - return { ...attrs, ...{ 'jingle_propose': jingle_propose_type, 'jingle_retraction_id': getJingleRetractionID(stanza) }} + return { ...attrs, ...{ 'jingle_propose': jingle_propose_type, 'jingle_retraction_id': getJingleRetractionID(stanza), 'template_hook': (attrs['template_hook']) ? 'getJingleTemplate' : undefined, 'jingle_status': attrs['jingle_status'] }} } function getJingleProposeType(stanza){ @@ -26,6 +27,11 @@ function getJingleRetractionID(stanza){ const el = sizzle(`propose[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); return el?.getAttribute('id'); } + +export function getJingleTemplate(model) { + return tpl_jingle_chat_history(model); +} + export function jingleCallInitialized() { JingleCallModal; } @@ -55,11 +61,40 @@ export function retractCall(context) { 'type': 'chat', 'jingle_retraction_id': propose_id, 'msg_id': message_id, + 'jingle_status': context.model.get('jingle_status'), + 'template_hook': 'getJingleTemplate' } context.model.messages.create(attrs); } /** + * This function simply sends the stanza that ends the call + */ +export function finishCall(context) { + const message_id = u.getUniqueId(); + const stanza = $msg({ + 'from': _converse.bare_jid, + 'to': context.jid, + 'type': 'chat' + }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': context.getAttribute('id')}) + .c('reason', {'xmlns': Strophe.NS.JINGLE}) + .c('success', {}).up() + .t('Success').up().up() + .c('store', { 'xmlns': Strophe.NS.HINTS }) + const attrs = { + 'from': _converse.bare_jid, + 'to': context.jid, + 'type': 'chat', + 'msg_id': message_id, + 'jingle_status': context.model.get('jingle_status'), + 'template_hook': 'getJingleTemplate' + } + context.model.messages.create(attrs); + api.send(stanza); +} + + +/* * This is the handler for the 'onMessage' hook * It inspects the incoming message attributes and checks whether we have is a jingle retraction message * if it is, then we find the jingle propose message and update it. @@ -68,7 +103,6 @@ export function retractCall(context) { */ export async function handleRetraction(model, data) { const jingle_retraction_id = data.attrs['jingle_retraction_id']; - // const propose_id = data.attrs['propose_id']; if (jingle_retraction_id) { //finding the propose message with the same id as the retraction id const message = model.messages.findWhere({ 'jingle_propose': 'audio' }); From 4c5b855c429bf82dba3907062f70ad9949aeb25b Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Wed, 24 Aug 2022 18:55:20 +0200 Subject: [PATCH 25/28] (TRB) Retraction improvements --- karma.conf.js | 1 + src/plugins/jingle/index.js | 2 +- .../jingle/templates/jingle_chat_history.js | 28 +++++++-- .../jingle/tests/jingle-chat-history.js | 20 +++++++ src/plugins/jingle/toolbar-button.js | 2 +- src/plugins/jingle/utils.js | 60 ++++++++++++++----- 6 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 src/plugins/jingle/tests/jingle-chat-history.js diff --git a/karma.conf.js b/karma.conf.js index 9a72df432a..51667be931 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -66,6 +66,7 @@ module.exports = function(config) { { pattern: "src/plugins/headlines-view/tests/headline.js", type: 'module' }, { pattern: "src/plugins/jingle/tests/ui.js", type: 'module' }, { pattern: "src/plugins/jingle/tests/message-initiation.js", type: 'module' }, + { pattern: "src/plugins/jingle/tests/jingle-chat-history.js", type: 'module' }, { pattern: "src/plugins/mam-views/tests/mam.js", type: 'module' }, { pattern: "src/plugins/mam-views/tests/placeholder.js", type: 'module' }, { pattern: "src/plugins/minimize/tests/minchats.js", type: 'module' }, diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 3bbf758678..d920f689a3 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -46,8 +46,8 @@ converse.plugins.add('converse-jingle', { return buttons; }); - api.listen.on('parseMessage', parseJingleMessage); api.listen.on('onMessage', handleRetraction); + api.listen.on('parseMessage', parseJingleMessage); api.listen.on('getJingleTemplate', getJingleTemplate); }, }); diff --git a/src/plugins/jingle/templates/jingle_chat_history.js b/src/plugins/jingle/templates/jingle_chat_history.js index a32faf747a..a6af334de8 100644 --- a/src/plugins/jingle/templates/jingle_chat_history.js +++ b/src/plugins/jingle/templates/jingle_chat_history.js @@ -1,12 +1,28 @@ import { __ } from 'i18n'; import { html } from "lit"; -import { JINGLE_CALL_STATUS } from "../constants.js"; + +const ended_call = __('Call Ended'); +const pending_call = __('Initiated a call'); +const finished_call = __('Finished call'); + +function calling(o) { + return html`

${ pending_call } at ${o.time}
` +} + +function finishedCall(o) { + return html`

${ finished_call } at ${o.time}
` +} + +function retractedCall(o) { + return html`

${ ended_call } at ${o.time}
` +} + export default (o) => { - const ended_call = __('Call Ended'); - const pending_call = __('Calling'); return html` - ${ (o.get('jingle_status') === JINGLE_CALL_STATUS.OUTGOING_PENDING && o.get('jingle_status')!= undefined ) ? html`${pending_call}` : html`${ended_call}` } + ${(o.get('jingle_status') === 'incoming_call' && o.get('jingle_status') != undefined) ? html`${calling(o)}` : html` + ${(o.get('jingle_status') === 'retracted' && o.get('jingle_status') != undefined) ? html`${retractedCall(o)}` : html` + ${ (o.get('jingle_status') === 'call_ended' && o.get('jingle_status') != undefined) ? html`${finishedCall(o)}` : html``} + `} + `} `} - - diff --git a/src/plugins/jingle/tests/jingle-chat-history.js b/src/plugins/jingle/tests/jingle-chat-history.js new file mode 100644 index 0000000000..307c8471f1 --- /dev/null +++ b/src/plugins/jingle/tests/jingle-chat-history.js @@ -0,0 +1,20 @@ +/*global mock, converse */ + +const { u } = converse.env; + +describe("A jingle chat message", function () { + + it("will be displayed on the initiator's", + mock.initConverse(['chatBoxesFetched'], {}, + async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g, '.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const initiator_message = await u.waitUntil(() => view.querySelectorAll('converse-message-history converse-chat-message .chat-msg__text ')?.textContent === 'Initiated a Call at'); + expect(initiator_message).toBe().not(undefined); + })); +}); diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index 7cef4716f6..ed8a36e0b2 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -56,7 +56,7 @@ export default class JingleToolbarButton extends CustomElement { 'msg_id': message_id, 'propose_id': propose_id, 'media': 'audio', - 'template_hook': 'getJingleTemplate' + 'template_hook': 'getJingleTemplate', } this.model.messages.create(attrs); return; diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index e33ca809b5..809b25b10d 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -5,7 +5,6 @@ import tpl_jingle_chat_history from "./templates/jingle_chat_history.js"; const { Strophe, sizzle, $msg } = converse.env; const u = converse.env.utils; - /** * This function merges the incoming attributes and the jingle propose attribute into one. * It also determines the type of the media i.e. audio or video @@ -13,9 +12,39 @@ const u = converse.env.utils; * @param { Object } attrs */ export function parseJingleMessage(stanza, attrs) { + if(isAJingleMessage(stanza) === true) { const jingle_propose_type = getJingleProposeType(stanza); - // To do editable: false, retracted_id: {if there is a retractions set it to the id}, retracted(timestamp) - return { ...attrs, ...{ 'jingle_propose': jingle_propose_type, 'jingle_retraction_id': getJingleRetractionID(stanza), 'template_hook': (attrs['template_hook']) ? 'getJingleTemplate' : undefined, 'jingle_status': attrs['jingle_status'] }} + return { + ...attrs, ...{ + 'jingle_propose': jingle_propose_type, + 'jingle_retraction_id': getJingleRetractionID(stanza), + 'template_hook': 'getJingleTemplate', + 'jingle_status': jingleStatus(stanza) + } + } + } +} + +function isAJingleMessage(stanza) { + if (jingleStatus(stanza) === 'incoming_call' || jingleStatus(stanza) === 'retracted' || jingleStatus(stanza) === 'call_ended') { + return true; + } + else false; +} + +function jingleStatus(stanza) { + const el_propose = sizzle(`propose[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); + const el_retract = sizzle(`retract[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); + const el_finish = sizzle(`finish[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); + if (el_propose) { + return 'incoming_call'; + } + else if (el_retract) { + return 'retracted'; + } + else if (el_finish) { + return 'call_ended'; + } } function getJingleProposeType(stanza){ @@ -40,8 +69,8 @@ export function jingleCallInitialized() { * This function simply sends the retraction stanza and modifies the attributes */ export function retractCall(context) { - const initiator_message = context.model.messages.findWhere({ 'media': 'audio' }); - const propose_id = initiator_message.attributes.propose_id; + const initiator_message = context.model.messages.where({ template_hook: 'getJingleTemplate', media: 'audio' }); + const propose_id = initiator_message; const message_id = u.getUniqueId(); api.send( $msg({ @@ -49,7 +78,8 @@ export function retractCall(context) { 'to': context.jid, 'type': 'chat', id: message_id - }).c('retract', { 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) + }).c('retract', { + 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) .c('reason', { 'xmlns': Strophe.NS.JINGLE }) .c('cancel', {}).up() .t('Retracted').up().up() @@ -61,7 +91,6 @@ export function retractCall(context) { 'type': 'chat', 'jingle_retraction_id': propose_id, 'msg_id': message_id, - 'jingle_status': context.model.get('jingle_status'), 'template_hook': 'getJingleTemplate' } context.model.messages.create(attrs); @@ -93,27 +122,26 @@ export function finishCall(context) { api.send(stanza); } - /* * This is the handler for the 'onMessage' hook - * It inspects the incoming message attributes and checks whether we have is a jingle retraction message + * It inspects the incoming message attributes and checks whether we have a jingle retraction message * if it is, then we find the jingle propose message and update it. * @param { _converse.ChatBox } model * @param { } data */ -export async function handleRetraction(model, data) { +export function handleRetraction(model, data) { const jingle_retraction_id = data.attrs['jingle_retraction_id']; if (jingle_retraction_id) { //finding the propose message with the same id as the retraction id - const message = model.messages.findWhere({ 'jingle_propose': 'audio' }); + const message = model.messages.where({ jingle_propose: 'audio' }); if (message) { - message.save(data.attrs, { 'propose_id': jingle_retraction_id }); + message.save(data.attrs, { has_been_retracted: 'true' }); data.handled = true; } - else { - // It is a dangling retraction; we are waiting for the correct propose message - await _converse.ChatBox.createMessage(data.attrs); - } + } + else { + // It is a dangling retraction; we are waiting for the correct propose message + model.createMessage(data.attrs); } return data; } From 7ef19af538a6eb66bd9961f2a8684b0fd68b49a8 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 29 Aug 2022 14:43:35 +0200 Subject: [PATCH 26/28] (TBR)added a hook for the retraction --- src/plugins/jingle/index.js | 3 +- .../jingle/templates/jingle_chat_history.js | 18 ++++++++- .../jingle/tests/message-initiation.js | 4 +- src/plugins/jingle/toolbar-button.js | 5 ++- src/plugins/jingle/utils.js | 37 +++++++++++-------- 5 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index d920f689a3..30ce3530bd 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -10,7 +10,7 @@ import "./chat-header-notification.js"; import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; -import { parseJingleMessage, handleRetraction, getJingleTemplate } from './utils.js'; +import { parseJingleMessage, handleRetraction, getJingleTemplate, handleAttrs } from './utils.js'; const { Strophe } = converse.env; @@ -47,6 +47,7 @@ converse.plugins.add('converse-jingle', { return buttons; }); api.listen.on('onMessage', handleRetraction); + api.hook('passingAttrs', handleAttrs); api.listen.on('parseMessage', parseJingleMessage); api.listen.on('getJingleTemplate', getJingleTemplate); }, diff --git a/src/plugins/jingle/templates/jingle_chat_history.js b/src/plugins/jingle/templates/jingle_chat_history.js index a6af334de8..0346c15787 100644 --- a/src/plugins/jingle/templates/jingle_chat_history.js +++ b/src/plugins/jingle/templates/jingle_chat_history.js @@ -1,3 +1,4 @@ +import '../../../shared/avatar/avatar.js'; import { __ } from 'i18n'; import { html } from "lit"; @@ -5,8 +6,21 @@ const ended_call = __('Call Ended'); const pending_call = __('Initiated a call'); const finished_call = __('Finished call'); -function calling(o) { - return html`

${ pending_call } at ${o.time}
` +function calling(el) { + return html` +
+ + ${ pending_call} at ${el.get('time')} +
+ End Call +
` } function finishedCall(o) { diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index 2d3fea5744..f94f469d3d 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -4,7 +4,7 @@ const sizzle = converse.env.sizzle; const { Strophe } = converse.env; -fdescribe("A Jingle Message Initiation Request", function () { +describe("A Jingle Message Initiation Request", function () { describe("from the initiator's perspective", function () { @@ -101,7 +101,7 @@ fdescribe("A Jingle Message Initiation Request", function () { })); }); - describe("from the receiver's perspective", function () { + fdescribe("from the receiver's perspective", function () { it("is received when the initiator clicks the call button", mock.initConverse( ['chatBoxesFetched'], { allow_non_roster_messaging: true }, async function (_converse) { diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index ed8a36e0b2..ebb3954875 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -36,9 +36,9 @@ export default class JingleToolbarButton extends CustomElement { return; } if (!jingle_status || jingle_status === JINGLE_CALL_STATUS.ENDED) { - this.model.save('jingle_status', JINGLE_CALL_STATUS.OUTGOING_PENDING); const propose_id = u.getUniqueId(); const message_id = u.getUniqueId(); + this.model.save({ 'jingle_propose_id': propose_id, 'jingle_status': JINGLE_CALL_STATUS.OUTGOING_PENDING }); api.send( $msg({ 'from': _converse.bare_jid, @@ -54,11 +54,12 @@ export default class JingleToolbarButton extends CustomElement { 'to': this.jid, 'type': 'chat', 'msg_id': message_id, - 'propose_id': propose_id, + 'jingle_propose_id': propose_id, 'media': 'audio', 'template_hook': 'getJingleTemplate', } this.model.messages.create(attrs); + api.hook('passingAttrs', attrs); return; } } diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index 809b25b10d..51ad0e1c4c 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -68,18 +68,17 @@ export function jingleCallInitialized() { /** * This function simply sends the retraction stanza and modifies the attributes */ -export function retractCall(context) { - const initiator_message = context.model.messages.where({ template_hook: 'getJingleTemplate', media: 'audio' }); - const propose_id = initiator_message; +export function retractCall(el) { + const jingle_propose_id = el.model.get('jingle_propose_id'); const message_id = u.getUniqueId(); api.send( $msg({ 'from': _converse.bare_jid, - 'to': context.jid, + 'to': el.jid, 'type': 'chat', id: message_id }).c('retract', { - 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': propose_id }) + 'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': jingle_propose_id }) .c('reason', { 'xmlns': Strophe.NS.JINGLE }) .c('cancel', {}).up() .t('Retracted').up().up() @@ -87,41 +86,47 @@ export function retractCall(context) { ); const attrs = { 'from': _converse.bare_jid, - 'to': context.jid, + 'to': el.jid, 'type': 'chat', - 'jingle_retraction_id': propose_id, + 'jingle_retraction_id': jingle_propose_id, 'msg_id': message_id, 'template_hook': 'getJingleTemplate' } - context.model.messages.create(attrs); + el.model.messages.create(attrs); } /** * This function simply sends the stanza that ends the call */ -export function finishCall(context) { +export function finishCall(el) { const message_id = u.getUniqueId(); + const finish_id = u.getUniqueId(); const stanza = $msg({ 'from': _converse.bare_jid, - 'to': context.jid, + 'to': el.jid, 'type': 'chat' - }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': context.getAttribute('id')}) + }).c('finish', {'xmlns': Strophe.NS.JINGLEMESSAGE, 'id': finish_id}) .c('reason', {'xmlns': Strophe.NS.JINGLE}) .c('success', {}).up() .t('Success').up().up() .c('store', { 'xmlns': Strophe.NS.HINTS }) const attrs = { 'from': _converse.bare_jid, - 'to': context.jid, + 'to': el.jid, 'type': 'chat', 'msg_id': message_id, - 'jingle_status': context.model.get('jingle_status'), + 'jingle_status': el.model.get('jingle_status'), 'template_hook': 'getJingleTemplate' } - context.model.messages.create(attrs); + el.model.messages.create(attrs); api.send(stanza); } +var _propose_id; +export function handleAttrs(attrs) { + _propose_id = attrs.jingle_propose_id; +} + /* * This is the handler for the 'onMessage' hook * It inspects the incoming message attributes and checks whether we have a jingle retraction message @@ -129,11 +134,11 @@ export function finishCall(context) { * @param { _converse.ChatBox } model * @param { } data */ -export function handleRetraction(model, data) { +export async function handleRetraction(model, data) { const jingle_retraction_id = data.attrs['jingle_retraction_id']; if (jingle_retraction_id) { //finding the propose message with the same id as the retraction id - const message = model.messages.where({ jingle_propose: 'audio' }); + const message = await model.messages.findWhere({ jingle_retraction_id: _propose_id }); if (message) { message.save(data.attrs, { has_been_retracted: 'true' }); data.handled = true; From bec6455abadb9abf76b9ef41825cc24b5a443be8 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Tue, 30 Aug 2022 18:02:25 +0200 Subject: [PATCH 27/28] The Reciever's side retraction tests pass --- src/plugins/jingle/index.js | 3 +-- src/plugins/jingle/tests/message-initiation.js | 8 ++++---- src/plugins/jingle/toolbar-button.js | 1 - src/plugins/jingle/utils.js | 14 ++++---------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index 30ce3530bd..d920f689a3 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -10,7 +10,7 @@ import "./chat-header-notification.js"; import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; -import { parseJingleMessage, handleRetraction, getJingleTemplate, handleAttrs } from './utils.js'; +import { parseJingleMessage, handleRetraction, getJingleTemplate } from './utils.js'; const { Strophe } = converse.env; @@ -47,7 +47,6 @@ converse.plugins.add('converse-jingle', { return buttons; }); api.listen.on('onMessage', handleRetraction); - api.hook('passingAttrs', handleAttrs); api.listen.on('parseMessage', parseJingleMessage); api.listen.on('getJingleTemplate', getJingleTemplate); }, diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index f94f469d3d..d517882b35 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -111,8 +111,8 @@ describe("A Jingle Message Initiation Request", function () { const propose_id = u.getUniqueId(); const initiator_stanza = u.toStanza(` @@ -139,8 +139,8 @@ describe("A Jingle Message Initiation Request", function () { const propose_id = stanza.querySelector('propose'); const initiator_stanza = u.toStanza(` diff --git a/src/plugins/jingle/toolbar-button.js b/src/plugins/jingle/toolbar-button.js index ebb3954875..80c2a44f54 100644 --- a/src/plugins/jingle/toolbar-button.js +++ b/src/plugins/jingle/toolbar-button.js @@ -59,7 +59,6 @@ export default class JingleToolbarButton extends CustomElement { 'template_hook': 'getJingleTemplate', } this.model.messages.create(attrs); - api.hook('passingAttrs', attrs); return; } } diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index 51ad0e1c4c..754a1afdc9 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -17,7 +17,7 @@ export function parseJingleMessage(stanza, attrs) { return { ...attrs, ...{ 'jingle_propose': jingle_propose_type, - 'jingle_retraction_id': getJingleRetractionID(stanza), + 'jingle_retraction_id': getJingleRetractionId(stanza), 'template_hook': 'getJingleTemplate', 'jingle_status': jingleStatus(stanza) } @@ -52,8 +52,8 @@ function getJingleProposeType(stanza){ return el?.getAttribute('media'); } -function getJingleRetractionID(stanza){ - const el = sizzle(`propose[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); +function getJingleRetractionId(stanza){ + const el = sizzle(`retract[xmlns="${Strophe.NS.JINGLEMESSAGE}"]`, stanza).pop(); return el?.getAttribute('id'); } @@ -122,11 +122,6 @@ export function finishCall(el) { api.send(stanza); } -var _propose_id; -export function handleAttrs(attrs) { - _propose_id = attrs.jingle_propose_id; -} - /* * This is the handler for the 'onMessage' hook * It inspects the incoming message attributes and checks whether we have a jingle retraction message @@ -137,8 +132,7 @@ export function handleAttrs(attrs) { export async function handleRetraction(model, data) { const jingle_retraction_id = data.attrs['jingle_retraction_id']; if (jingle_retraction_id) { - //finding the propose message with the same id as the retraction id - const message = await model.messages.findWhere({ jingle_retraction_id: _propose_id }); + const message = await model.messages.findWhere({ jingle_propose_id: jingle_retraction_id }); if (message) { message.save(data.attrs, { has_been_retracted: 'true' }); data.handled = true; From 8f722107006ec762d03e9d0af09f5fa12b0a0287 Mon Sep 17 00:00:00 2001 From: Sanskar Bajpai Date: Mon, 5 Sep 2022 19:01:42 +0200 Subject: [PATCH 28/28] chat history ui added and tests as well --- src/headless/plugins/chat/model.js | 1 + src/plugins/jingle/index.js | 3 +- src/plugins/jingle/jingle-message-history.js | 30 ++++++++++ .../jingle/templates/jingle-chat-history.js | 58 +++++++++++++++++++ .../jingle/templates/jingle_chat_history.js | 42 -------------- .../jingle/templates/toolbar-button.js | 8 +-- .../jingle/tests/jingle-chat-history.js | 42 +++++++++----- .../jingle/tests/message-initiation.js | 2 +- src/plugins/jingle/tests/ui.js | 3 +- src/plugins/jingle/utils.js | 17 ++---- 10 files changed, 129 insertions(+), 77 deletions(-) create mode 100644 src/plugins/jingle/jingle-message-history.js create mode 100644 src/plugins/jingle/templates/jingle-chat-history.js delete mode 100644 src/plugins/jingle/templates/jingle_chat_history.js diff --git a/src/headless/plugins/chat/model.js b/src/headless/plugins/chat/model.js index 412679c474..cf11f2e2d3 100644 --- a/src/headless/plugins/chat/model.js +++ b/src/headless/plugins/chat/model.js @@ -229,6 +229,7 @@ const ChatBox = ModelWithContact.extend({ !(await this.handleRetraction(attrs)) ) { const { handled } = await api.hook('onMessage', this, { handled: false, attrs }); + debugger if (handled) return; this.setEditable(attrs, attrs.time); diff --git a/src/plugins/jingle/index.js b/src/plugins/jingle/index.js index d920f689a3..5eb13eb9e7 100644 --- a/src/plugins/jingle/index.js +++ b/src/plugins/jingle/index.js @@ -1,5 +1,5 @@ /** - * @description Converse.js plugin which adds XEP-0166 Jingle + * @description Converse.js plugin which uses XEP-0353, XEP-0166 & XEP-0167 * @copyright 2022, the Converse.js contributors * @license Mozilla Public License (MPLv2) */ @@ -11,6 +11,7 @@ import './toolbar-button.js'; import { JINGLE_CALL_STATUS } from './constants.js'; import { html } from "lit"; import { parseJingleMessage, handleRetraction, getJingleTemplate } from './utils.js'; +import './jingle-message-history.js'; const { Strophe } = converse.env; diff --git a/src/plugins/jingle/jingle-message-history.js b/src/plugins/jingle/jingle-message-history.js new file mode 100644 index 0000000000..47dcea61c1 --- /dev/null +++ b/src/plugins/jingle/jingle-message-history.js @@ -0,0 +1,30 @@ +import { CustomElement } from 'shared/components/element.js'; +import { _converse, api } from "@converse/headless/core"; +import tpl_jingle_message_history from "./templates/jingle-chat-history.js"; +import { finishCall } from './utils.js'; +import { JINGLE_CALL_STATUS } from "./constants.js"; + +export default class JingleMessageHistory extends CustomElement { + + static properties = { + 'jid': { type: String } + }; + + initialize() { + this.model = _converse.chatboxes.get(this.jid); + this.listenTo(this.model, 'change:jingle_status', () => this.requestUpdate()); + } + + render() { + const jid = this.jid; + return tpl_jingle_message_history(this); + } + + endCall() { + this.model.save('jingle_status', JINGLE_CALL_STATUS.ENDED); + finishCall(this); + return; + } +} + +api.elements.define('converse-jingle-message', JingleMessageHistory); diff --git a/src/plugins/jingle/templates/jingle-chat-history.js b/src/plugins/jingle/templates/jingle-chat-history.js new file mode 100644 index 0000000000..81c3caffab --- /dev/null +++ b/src/plugins/jingle/templates/jingle-chat-history.js @@ -0,0 +1,58 @@ +import '../../../shared/avatar/avatar.js'; +import { __ } from 'i18n'; +import { html } from "lit"; + +// const ended_call = __('Call Ended'); +const pending_call = __('Initiated a call with'); +// const finished_call = __('Finished call'); + +function calling(el) { + const i18n_end_call = __('End Call'); + return html` +
+
+ + ${pending_call} + + + + + + + + +
` +} + +// function finishedCall() { +// return html`
+// +// +// +// +// ${finishedCall} +//
` +// } + +// function retractedCall() { +// return html`
+// +// +// +// +// ${ended_call} +//
` +// } + + +export default (el) => { + return calling(el); +} diff --git a/src/plugins/jingle/templates/jingle_chat_history.js b/src/plugins/jingle/templates/jingle_chat_history.js deleted file mode 100644 index 0346c15787..0000000000 --- a/src/plugins/jingle/templates/jingle_chat_history.js +++ /dev/null @@ -1,42 +0,0 @@ -import '../../../shared/avatar/avatar.js'; -import { __ } from 'i18n'; -import { html } from "lit"; - -const ended_call = __('Call Ended'); -const pending_call = __('Initiated a call'); -const finished_call = __('Finished call'); - -function calling(el) { - return html` -
- - ${ pending_call} at ${el.get('time')} -
- End Call -
` -} - -function finishedCall(o) { - return html`

${ finished_call } at ${o.time}
` -} - -function retractedCall(o) { - return html`

${ ended_call } at ${o.time}
` -} - - -export default (o) => { - return html` - ${(o.get('jingle_status') === 'incoming_call' && o.get('jingle_status') != undefined) ? html`${calling(o)}` : html` - ${(o.get('jingle_status') === 'retracted' && o.get('jingle_status') != undefined) ? html`${retractedCall(o)}` : html` - ${ (o.get('jingle_status') === 'call_ended' && o.get('jingle_status') != undefined) ? html`${finishedCall(o)}` : html``} - `} - `} -`} diff --git a/src/plugins/jingle/templates/toolbar-button.js b/src/plugins/jingle/templates/toolbar-button.js index 71c4bb42b2..1aabb00277 100644 --- a/src/plugins/jingle/templates/toolbar-button.js +++ b/src/plugins/jingle/templates/toolbar-button.js @@ -6,17 +6,17 @@ export default (el) => { const call_color = '--chat-toolbar-btn-color'; const end_call_color = '--chat-toolbar-btn-close-color'; const jingle_status = el.model.get('jingle_status'); - let button_color, i18n_start_call; + let button_color, i18n_call; if (jingle_status === JINGLE_CALL_STATUS.OUTGOING_PENDING || jingle_status === JINGLE_CALL_STATUS.ACTIVE) { button_color = end_call_color; - i18n_start_call = __('Stop the call'); + i18n_call = __('Stop the call'); } else { button_color = call_color; - i18n_start_call = __('Start a call'); + i18n_call = __('Start a call'); } return html` - ` } diff --git a/src/plugins/jingle/tests/jingle-chat-history.js b/src/plugins/jingle/tests/jingle-chat-history.js index 307c8471f1..89c6b60be3 100644 --- a/src/plugins/jingle/tests/jingle-chat-history.js +++ b/src/plugins/jingle/tests/jingle-chat-history.js @@ -1,20 +1,32 @@ -/*global mock, converse */ +/* global mock */ -const { u } = converse.env; +describe("A jingle chat history message", function () { -describe("A jingle chat message", function () { + it("has been shown in the chat", + mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { - it("will be displayed on the initiator's", - mock.initConverse(['chatBoxesFetched'], {}, - async function (_converse) { + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const jingle_chat_history_component = view.querySelector('converse-jingle-message'); + expect(jingle_chat_history_component).not.toBe(undefined); + })); - await mock.waitForRoster(_converse, 'current', 1); - const contact_jid = mock.cur_names[0].replace(/ /g, '.').toLowerCase() + '@montague.lit'; - await mock.openChatBoxFor(_converse, contact_jid); - const view = _converse.chatboxviews.get(contact_jid); - const call_button = view.querySelector('converse-jingle-toolbar-button button'); - call_button.click(); - const initiator_message = await u.waitUntil(() => view.querySelectorAll('converse-message-history converse-chat-message .chat-msg__text ')?.textContent === 'Initiated a Call at'); - expect(initiator_message).toBe().not(undefined); - })); + fit("has the end call button", + mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { + + await mock.waitForRoster(_converse, 'current', 1); + const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; + await mock.openChatBoxFor(_converse, contact_jid); + const view = _converse.chatboxviews.get(contact_jid); + const call_button = view.querySelector('converse-jingle-toolbar-button button'); + call_button.click(); + const chatbox = view.model; + const end_call_button = view.querySelector('converse-jingle-message button .end-call'); + expect(end_call_button).not.toBe(undefined); + expect(chatbox.get('jingle_status')).toBe(_converse.JINGLE_CALL_STATUS.ENDED); + })); }); diff --git a/src/plugins/jingle/tests/message-initiation.js b/src/plugins/jingle/tests/message-initiation.js index d517882b35..f93a45a841 100644 --- a/src/plugins/jingle/tests/message-initiation.js +++ b/src/plugins/jingle/tests/message-initiation.js @@ -101,7 +101,7 @@ describe("A Jingle Message Initiation Request", function () { })); }); - fdescribe("from the receiver's perspective", function () { + describe("from the receiver's perspective", function () { it("is received when the initiator clicks the call button", mock.initConverse( ['chatBoxesFetched'], { allow_non_roster_messaging: true }, async function (_converse) { diff --git a/src/plugins/jingle/tests/ui.js b/src/plugins/jingle/tests/ui.js index 238fef8265..38bdf2ed84 100644 --- a/src/plugins/jingle/tests/ui.js +++ b/src/plugins/jingle/tests/ui.js @@ -38,5 +38,6 @@ describe("A Jingle Status", function () { const call_intialized = await u.waitUntil(() => header_notification.querySelector('.jingle-call-initiated-button')); call_intialized.click(); expect(chatbox.get('jingle_status') === _converse.JINGLE_CALL_STATUS.ENDED); - })); + })); + }); diff --git a/src/plugins/jingle/utils.js b/src/plugins/jingle/utils.js index 754a1afdc9..09579836c0 100644 --- a/src/plugins/jingle/utils.js +++ b/src/plugins/jingle/utils.js @@ -1,6 +1,6 @@ import { converse, api, _converse } from '@converse/headless/core'; import JingleCallModal from "./modal/jingle-incoming-call-modal.js"; -import tpl_jingle_chat_history from "./templates/jingle_chat_history.js"; +import { html } from 'lit'; const { Strophe, sizzle, $msg } = converse.env; const u = converse.env.utils; @@ -12,8 +12,8 @@ const u = converse.env.utils; * @param { Object } attrs */ export function parseJingleMessage(stanza, attrs) { - if(isAJingleMessage(stanza) === true) { - const jingle_propose_type = getJingleProposeType(stanza); + if (isAJingleMessage(stanza) === true) { + const jingle_propose_type = getJingleProposeType(stanza); return { ...attrs, ...{ 'jingle_propose': jingle_propose_type, @@ -58,7 +58,7 @@ function getJingleRetractionId(stanza){ } export function getJingleTemplate(model) { - return tpl_jingle_chat_history(model); + return html``; } export function jingleCallInitialized() { @@ -84,15 +84,6 @@ export function retractCall(el) { .t('Retracted').up().up() .c('store', { 'xmlns': Strophe.NS.HINTS }) ); - const attrs = { - 'from': _converse.bare_jid, - 'to': el.jid, - 'type': 'chat', - 'jingle_retraction_id': jingle_propose_id, - 'msg_id': message_id, - 'template_hook': 'getJingleTemplate' - } - el.model.messages.create(attrs); } /**