From dc7d71823db144616eb939a233e2285484886e91 Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Wed, 2 Feb 2022 10:36:15 +0000 Subject: [PATCH] [IMP] mail: add browser key to messaging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes odoo/odoo#83854 Signed-off-by: Sébastien Theys (seb) --- .../notification_alert/notification_alert.js | 2 +- .../notification_request.js | 2 +- .../mail/static/src/models/chatter/chatter.js | 4 +- .../src/models/composer_view/composer_view.js | 2 +- .../mail/static/src/models/device/device.js | 11 +++-- .../src/models/file_uploader/file_uploader.js | 2 +- .../src/models/message_view/message_view.js | 4 +- .../static/src/models/messaging/messaging.js | 12 +++++- .../mail/static/src/models/thread/thread.js | 10 ++--- .../src/models/thread_view/thread_view.js | 6 +-- .../utils/throttle/tests/throttle_tests.js | 28 +++++++------ .../static/src/utils/throttle/throttle.js | 18 ++++----- .../src/utils/timer/tests/timer_tests.js | 40 ++++++++++--------- addons/mail/static/src/utils/timer/timer.js | 12 +++--- 14 files changed, 83 insertions(+), 70 deletions(-) diff --git a/addons/mail/static/src/components/notification_alert/notification_alert.js b/addons/mail/static/src/components/notification_alert/notification_alert.js index d9757053f00..4bdc9566b8d 100644 --- a/addons/mail/static/src/components/notification_alert/notification_alert.js +++ b/addons/mail/static/src/components/notification_alert/notification_alert.js @@ -17,7 +17,7 @@ export class NotificationAlert extends Component { if (!this.messaging) { return false; } - const windowNotification = this.env.browser.Notification; + const windowNotification = this.messaging.browser.Notification; return ( windowNotification && windowNotification.permission !== "granted" && diff --git a/addons/mail/static/src/components/notification_request/notification_request.js b/addons/mail/static/src/components/notification_request/notification_request.js index 43dcd2edeb1..ca1dabfbd0e 100644 --- a/addons/mail/static/src/components/notification_request/notification_request.js +++ b/addons/mail/static/src/components/notification_request/notification_request.js @@ -49,7 +49,7 @@ export class NotificationRequest extends Component { * @private */ _onClick() { - const windowNotification = this.env.browser.Notification; + const windowNotification = this.messaging.browser.Notification; const def = windowNotification && windowNotification.requestPermission(); if (def) { def.then(this._handleResponseNotificationPermission.bind(this)); diff --git a/addons/mail/static/src/models/chatter/chatter.js b/addons/mail/static/src/models/chatter/chatter.js index 1fb120a06c3..c9411e5e869 100644 --- a/addons/mail/static/src/models/chatter/chatter.js +++ b/addons/mail/static/src/models/chatter/chatter.js @@ -231,7 +231,7 @@ registerModel({ */ _prepareAttachmentsLoading() { this._isPreparingAttachmentsLoading = true; - this._attachmentsLoaderTimeout = this.env.browser.setTimeout(() => { + this._attachmentsLoaderTimeout = this.messaging.browser.setTimeout(() => { this.update({ isShowingAttachmentsLoading: true }); this._isPreparingAttachmentsLoading = false; }, this.messaging.loadingBaseDelayDuration); @@ -240,7 +240,7 @@ registerModel({ * @private */ _stopAttachmentsLoading() { - this.env.browser.clearTimeout(this._attachmentsLoaderTimeout); + this.messaging.browser.clearTimeout(this._attachmentsLoaderTimeout); this._attachmentsLoaderTimeout = null; this._isPreparingAttachmentsLoading = false; }, diff --git a/addons/mail/static/src/models/composer_view/composer_view.js b/addons/mail/static/src/models/composer_view/composer_view.js index 3fa80680b7b..58e669fc427 100644 --- a/addons/mail/static/src/models/composer_view/composer_view.js +++ b/addons/mail/static/src/models/composer_view/composer_view.js @@ -197,7 +197,7 @@ registerModel({ return; } // Let event be handled by bubbling handlers first - await new Promise(this.env.browser.setTimeout); + await new Promise(this.messaging.browser.setTimeout); if (isEventHandled(ev, 'MessageActionList.replyTo')) { return; } diff --git a/addons/mail/static/src/models/device/device.js b/addons/mail/static/src/models/device/device.js index bbc286879af..34d207f7192 100644 --- a/addons/mail/static/src/models/device/device.js +++ b/addons/mail/static/src/models/device/device.js @@ -2,6 +2,7 @@ import { registerModel } from '@mail/model/model_core'; import { attr } from '@mail/model/model_field'; +import { browser } from "@web/core/browser/browser"; registerModel({ name: 'Device', @@ -12,7 +13,7 @@ registerModel({ this._onResize = _.debounce(() => this._refresh(), 100); }, _willDelete() { - window.removeEventListener('resize', this._onResize); + browser.removeEventListener('resize', this._onResize); }, }, recordMethods: { @@ -20,17 +21,15 @@ registerModel({ * Called when messaging is started. */ start() { - // TODO FIXME Not using this.env.browser because it's proxified, and - // addEventListener does not work on proxified window. task-2234596 - window.addEventListener('resize', this._onResize); + browser.addEventListener('resize', this._onResize); }, /** * @private */ _refresh() { this.update({ - globalWindowInnerHeight: this.env.browser.innerHeight, - globalWindowInnerWidth: this.env.browser.innerWidth, + globalWindowInnerHeight: this.messaging.browser.innerHeight, + globalWindowInnerWidth: this.messaging.browser.innerWidth, isMobile: this.env.device.isMobile, isMobileDevice: this.messaging.device.isMobileDevice, sizeClass: this.env.device.size_class, diff --git a/addons/mail/static/src/models/file_uploader/file_uploader.js b/addons/mail/static/src/models/file_uploader/file_uploader.js index 2b01e42607b..75c358f0e4a 100644 --- a/addons/mail/static/src/models/file_uploader/file_uploader.js +++ b/addons/mail/static/src/models/file_uploader/file_uploader.js @@ -145,7 +145,7 @@ registerModel({ return; } try { - const response = await this.env.browser.fetch('/mail/attachment/upload', { + const response = await this.messaging.browser.fetch('/mail/attachment/upload', { method: 'POST', body: this._createFormData({ composer, file, thread }), signal: uploadingAttachment.uploadingAbortController.signal, diff --git a/addons/mail/static/src/models/message_view/message_view.js b/addons/mail/static/src/models/message_view/message_view.js index 9666ecec1b7..e0017954358 100644 --- a/addons/mail/static/src/models/message_view/message_view.js +++ b/addons/mail/static/src/models/message_view/message_view.js @@ -13,10 +13,10 @@ registerModel({ * Briefly highlights the message. */ highlight() { - this.env.browser.clearTimeout(this.highlightTimeout); + this.messaging.browser.clearTimeout(this.highlightTimeout); this.update({ isHighlighted: true, - highlightTimeout: this.env.browser.setTimeout(() => { + highlightTimeout: this.messaging.browser.setTimeout(() => { if (!this.exists()) { return; } diff --git a/addons/mail/static/src/models/messaging/messaging.js b/addons/mail/static/src/models/messaging/messaging.js index 6b62f2138ad..e7c07b67546 100644 --- a/addons/mail/static/src/models/messaging/messaging.js +++ b/addons/mail/static/src/models/messaging/messaging.js @@ -155,6 +155,13 @@ registerModel({ } this.userSetting.update({ rtcLayout: 'sidebar' }); }, + /** + * @private + * @returns {Object} browser + */ + _computeBrowser() { + return this.env.browser; + }, /** * @private * @returns {Promise} @@ -184,7 +191,7 @@ registerModel({ * @returns {boolean} */ _computeIsNotificationPermissionDefault() { - const browserNotification = this.env.browser.Notification; + const browserNotification = this.messaging.browser.Notification; return browserNotification ? browserNotification.permission === 'default' : false; }, /** @@ -229,6 +236,9 @@ registerModel({ autofetchPartnerImStatus: attr({ default: true, }), + browser: attr({ + compute: '_computeBrowser', + }), cannedResponses: many('CannedResponse'), chatWindowManager: one('ChatWindowManager', { default: insertAndReplace(), diff --git a/addons/mail/static/src/models/thread/thread.js b/addons/mail/static/src/models/thread/thread.js index db4319ba8b3..bf66eccb074 100644 --- a/addons/mail/static/src/models/thread/thread.js +++ b/addons/mail/static/src/models/thread/thread.js @@ -23,7 +23,7 @@ registerModel({ name: 'Thread', identifyingFields: ['model', 'id'], lifecycleHooks: { - _willCreate() { + _created() { /** * Timer of current partner that was currently typing something, but * there is no change on the input for 5 seconds. This is used @@ -32,7 +32,7 @@ registerModel({ * on the composer for some time. */ this._currentPartnerInactiveTypingTimer = new Timer( - this.env, + this.messaging, () => this.async(() => { if (this.messaging.currentPartner) { return this._onCurrentPartnerInactiveTypingTimeout(); @@ -65,7 +65,7 @@ registerModel({ * has stopped typing something. */ this._currentPartnerLongTypingTimer = new Timer( - this.env, + this.messaging, () => this.async(() => this._onCurrentPartnerLongTypingTimeout()), 50 * 1000 ); @@ -102,7 +102,7 @@ registerModel({ * @see _notifyCurrentPartnerTypingStatus */ this._throttleNotifyCurrentPartnerTypingStatus = throttle( - this.env, + this.messaging, ({ isTyping }) => this.async(() => this._notifyCurrentPartnerTypingStatus({ isTyping })), 2.5 * 1000 ); @@ -1038,7 +1038,7 @@ registerModel({ */ registerOtherMemberTypingMember(partner) { const timer = new Timer( - this.env, + this.messaging, () => this.async(() => this._onOtherMemberLongTypingTimeout(partner)), 60 * 1000 ); diff --git a/addons/mail/static/src/models/thread_view/thread_view.js b/addons/mail/static/src/models/thread_view/thread_view.js index e6c91ef703b..f10c544b3f3 100644 --- a/addons/mail/static/src/models/thread_view/thread_view.js +++ b/addons/mail/static/src/models/thread_view/thread_view.js @@ -14,7 +14,7 @@ registerModel({ this._loaderTimeout = undefined; }, _willDelete() { - this.env.browser.clearTimeout(this._loaderTimeout); + this.messaging.browser.clearTimeout(this._loaderTimeout); }, }, recordMethods: { @@ -229,7 +229,7 @@ registerModel({ this.update({ isPreparingLoading: true }); this.async(() => new Promise(resolve => { - this._loaderTimeout = this.env.browser.setTimeout(resolve, 400); + this._loaderTimeout = this.messaging.browser.setTimeout(resolve, 400); } )).then(() => { const isLoading = this.threadCache @@ -240,7 +240,7 @@ registerModel({ } return; } - this.env.browser.clearTimeout(this._loaderTimeout); + this.messaging.browser.clearTimeout(this._loaderTimeout); this.update({ isLoading: false, isPreparingLoading: false }); }, /** diff --git a/addons/mail/static/src/utils/throttle/tests/throttle_tests.js b/addons/mail/static/src/utils/throttle/tests/throttle_tests.js index 568bb2145b5..df3cc3d846d 100644 --- a/addons/mail/static/src/utils/throttle/tests/throttle_tests.js +++ b/addons/mail/static/src/utils/throttle/tests/throttle_tests.js @@ -15,11 +15,13 @@ QUnit.module('throttle_tests.js', { this.throttles = []; this.start = async params => { - const { env, widget } = await start(Object.assign({}, params, { + const res = await start(Object.assign({}, params, { data: this.data, })); + const { env, widget } = res; this.env = env; this.widget = widget; + return res; }; }, afterEach() { @@ -35,13 +37,13 @@ QUnit.module('throttle_tests.js', { QUnit.test('single call', async function (assert) { assert.expect(6); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasInvokedFunc = false; const throttledFunc = throttle( - this.env, + messaging, () => { hasInvokedFunc = true; return 'func_result'; @@ -83,13 +85,13 @@ QUnit.test('single call', async function (assert) { QUnit.test('2nd (throttled) call', async function (assert) { assert.expect(8); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let funcCalledAmount = 0; const throttledFunc = throttle( - this.env, + messaging, () => { funcCalledAmount++; return `func_result_${funcCalledAmount}`; @@ -142,13 +144,13 @@ QUnit.test('2nd (throttled) call', async function (assert) { QUnit.test('throttled call reinvocation', async function (assert) { assert.expect(11); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let funcCalledAmount = 0; const throttledFunc = throttle( - this.env, + messaging, () => { funcCalledAmount++; return `func_result_${funcCalledAmount}`; @@ -220,12 +222,12 @@ QUnit.test('throttled call reinvocation', async function (assert) { QUnit.test('flush throttled call', async function (assert) { assert.expect(9); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); const throttledFunc = throttle( - this.env, + messaging, () => {}, 1000, ); @@ -276,12 +278,12 @@ QUnit.test('flush throttled call', async function (assert) { QUnit.test('cancel throttled call', async function (assert) { assert.expect(10); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); const throttledFunc = throttle( - this.env, + messaging, () => {}, 1000, { silentCancelationErrors: false } @@ -342,12 +344,12 @@ QUnit.test('cancel throttled call', async function (assert) { QUnit.test('clear throttled call', async function (assert) { assert.expect(9); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); const throttledFunc = throttle( - this.env, + messaging, () => {}, 1000, { silentCancelationErrors: false } diff --git a/addons/mail/static/src/utils/throttle/throttle.js b/addons/mail/static/src/utils/throttle/throttle.js index e1d40efabbd..d108480cc91 100644 --- a/addons/mail/static/src/utils/throttle/throttle.js +++ b/addons/mail/static/src/utils/throttle/throttle.js @@ -97,20 +97,20 @@ class _ThrottleFlushedError extends Error { class Throttle { /** - * @param {Object} env the OWL env + * @param {Messaging} messaging the messaging record. * @param {function} func provided function for making throttled version. * @param {integer} duration duration of the 'cool down' phase, i.e. * the minimum duration between the most recent function call that has * been made and the following function call (of course, assuming no flush * in-between). */ - constructor(env, func, duration) { + constructor(messaging, func, duration) { /** - * Reference to the OWL envirionment. Useful to fine-tune control of + * Reference to the messaging record. Useful to fine-tune control of * time flow in tests. * @see mail/static/src/utils/test_utils.js:start.hasTimeControl */ - this.env = env; + this.messaging = messaging; /** * Unique id of this throttle function. Useful for the ThrottleError * management, in order to determine whether these errors come from @@ -266,7 +266,7 @@ class Throttle { const coolingDownDeferred = makeDeferred(); this._coolingDownDeferred = coolingDownDeferred; this._isCoolingDown = true; - const cooldownTimeoutId = this.env.browser.setTimeout( + const cooldownTimeoutId = this.messaging.browser.setTimeout( () => coolingDownDeferred.resolve(), this._duration ); @@ -283,7 +283,7 @@ class Throttle { unexpectedError = error; } } finally { - this.env.browser.clearTimeout(cooldownTimeoutId); + this.messaging.browser.clearTimeout(cooldownTimeoutId); this._coolingDownDeferred = undefined; this._isCoolingDown = false; } @@ -312,7 +312,7 @@ class Throttle { * - When a cooldown phase ends, any buffered function call will be performed * and another cooldown phase will follow up. * - * @param {Object} env the OWL env + * @param {Messaging} messaging the messaging record. * @param {function} func the function to throttle. * @param {integer} duration duration, in milliseconds, of the cooling down * phase of the throttling. @@ -330,12 +330,12 @@ class Throttle { * of the provided function. */ function throttle( - env, + messaging, func, duration, { silentCancelationErrors = true } = {} ) { - const throttleObj = new Throttle(env, func, duration); + const throttleObj = new Throttle(messaging, func, duration); const callable = async (...args) => { try { // await is important, otherwise errors are not intercepted. diff --git a/addons/mail/static/src/utils/timer/tests/timer_tests.js b/addons/mail/static/src/utils/timer/tests/timer_tests.js index 69e1f765902..7b2c2067651 100644 --- a/addons/mail/static/src/utils/timer/tests/timer_tests.js +++ b/addons/mail/static/src/utils/timer/tests/timer_tests.js @@ -14,11 +14,13 @@ QUnit.module('timer_tests.js', { this.timers = []; this.start = async (params) => { - const { env, widget } = await start(Object.assign({}, params, { + const res = await start(Object.assign({}, params, { data: this.data, })); + const { env, widget } = res; this.env = env; this.widget = widget; + return res; }; }, afterEach() { @@ -34,14 +36,14 @@ QUnit.module('timer_tests.js', { QUnit.test('timer does not timeout on initialization', async function (assert) { assert.expect(3); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 0 ) @@ -68,14 +70,14 @@ QUnit.test('timer does not timeout on initialization', async function (assert) { QUnit.test('timer start (duration: 0ms)', async function (assert) { assert.expect(2); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 0 ) @@ -97,14 +99,14 @@ QUnit.test('timer start (duration: 0ms)', async function (assert) { QUnit.test('timer start observe termination (duration: 0ms)', async function (assert) { assert.expect(6); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => { hasTimedOut = true; return 'timeout_result'; @@ -146,14 +148,14 @@ QUnit.test('timer start observe termination (duration: 0ms)', async function (as QUnit.test('timer start (duration: 1000s)', async function (assert) { assert.expect(5); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 1000 * 1000 ) @@ -193,14 +195,14 @@ QUnit.test('timer start (duration: 1000s)', async function (assert) { QUnit.test('[no cancelation intercept] timer start then immediate clear (duration: 0ms)', async function (assert) { assert.expect(4); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 0 ) @@ -234,14 +236,14 @@ QUnit.test('[no cancelation intercept] timer start then immediate clear (duratio QUnit.test('[no cancelation intercept] timer start then clear before timeout (duration: 1000ms)', async function (assert) { assert.expect(4); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 1000 ) @@ -276,14 +278,14 @@ QUnit.test('[no cancelation intercept] timer start then clear before timeout (du QUnit.test('[no cancelation intercept] timer start then reset before timeout (duration: 1000ms)', async function (assert) { assert.expect(5); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 1000 ) @@ -324,14 +326,14 @@ QUnit.test('[no cancelation intercept] timer start then reset before timeout (du QUnit.test('[with cancelation intercept] timer start then immediate clear (duration: 0ms)', async function (assert) { assert.expect(5); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 0, { silentCancelationErrors: false } @@ -367,14 +369,14 @@ QUnit.test('[with cancelation intercept] timer start then immediate clear (durat QUnit.test('[with cancelation intercept] timer start then immediate reset (duration: 0ms)', async function (assert) { assert.expect(9); - await this.start({ + const { messaging } = await this.start({ hasTimeControl: true, }); let hasTimedOut = false; this.timers.push( new Timer( - this.env, + messaging, () => hasTimedOut = true, 0, { silentCancelationErrors: false } diff --git a/addons/mail/static/src/utils/timer/timer.js b/addons/mail/static/src/utils/timer/timer.js index e2be9c3dc08..80d147f9ee1 100644 --- a/addons/mail/static/src/utils/timer/timer.js +++ b/addons/mail/static/src/utils/timer/timer.js @@ -38,7 +38,7 @@ import { makeDeferred } from '@mail/utils/deferred/deferred'; class Timer { /** - * @param {Object} env the OWL env + * @param {Messaging} messaging the messaging record. * @param {function} onTimeout * @param {integer} duration * @param {Object} [param3={}] @@ -48,8 +48,8 @@ class Timer { * @see TimerClearedError for when timer has been aborted from `.clear()` * or `.reset()`. */ - constructor(env, onTimeout, duration, { silentCancelationErrors = true } = {}) { - this.env = env; + constructor(messaging, onTimeout, duration, { silentCancelationErrors = true } = {}) { + this.messaging = messaging; /** * Determine whether the timer has a pending timeout. */ @@ -90,7 +90,7 @@ class Timer { * when this timer is running. */ clear() { - this.env.browser.clearTimeout(this._timeoutId); + this.messaging.browser.clearTimeout(this._timeoutId); this.isRunning = false; if (!this._timeoutDeferred) { return; @@ -121,7 +121,7 @@ class Timer { this.isRunning = true; const timeoutDeferred = makeDeferred(); this._timeoutDeferred = timeoutDeferred; - const timeoutId = this.env.browser.setTimeout( + const timeoutId = this.messaging.browser.setTimeout( () => { this.isRunning = false; timeoutDeferred.resolve(this._onTimeout()); @@ -143,7 +143,7 @@ class Timer { throw error; } } finally { - this.env.browser.clearTimeout(timeoutId); + this.messaging.browser.clearTimeout(timeoutId); this._timeoutDeferred = undefined; this.isRunning = false; }