summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/models/thread_view
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-08-28 10:16:37 +0000
committerSébastien Theys <seb@odoo.com>2020-08-28 13:43:54 +0000
commitf9ee1cf5f904116e4d1d70a8eaf4ee1d58b18a3a (patch)
tree89425d0677083bb9489e42d47521c4bcf1e4ff44 /addons/mail/static/src/models/thread_view
parent98e12901c97a67dc87f21647473754cd6f384d4a (diff)
[FIX] mail, *: rename thread viewer to thread view
The "viewer" should be the "parent" not the view itself. This distinction will become important in a future commit to allow viewers to not display the view in some situations. Rename done as a standalone commit to ease diff readability. Part of task-2310623 closes odoo/odoo#56737 X-original-commit: 1b811e6a972d72de02965adda67c5b039bee023f Related: odoo/enterprise#12756 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com> Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Diffstat (limited to 'addons/mail/static/src/models/thread_view')
-rw-r--r--addons/mail/static/src/models/thread_view/thread_view.js303
1 files changed, 303 insertions, 0 deletions
diff --git a/addons/mail/static/src/models/thread_view/thread_view.js b/addons/mail/static/src/models/thread_view/thread_view.js
new file mode 100644
index 00000000000..f0075314561
--- /dev/null
+++ b/addons/mail/static/src/models/thread_view/thread_view.js
@@ -0,0 +1,303 @@
+odoo.define('mail/static/src/models/thread_view/thread_view.js', function (require) {
+'use strict';
+
+const { registerNewModel } = require('mail/static/src/model/model_core.js');
+const { RecordDeletedError } = require('mail/static/src/model/model_errors.js');
+const { attr, many2many, many2one, one2one } = require('mail/static/src/model/model_field.js');
+
+function factory(dependencies) {
+
+ class ThreadView extends dependencies['mail.model'] {
+
+ /**
+ * @override
+ */
+ _willDelete() {
+ this._stopLoading();
+ return super._willDelete(...arguments);
+ }
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ /**
+ * This function register a hint for the component related to this
+ * record. Hints are information on changes around this viewer that
+ * make require adjustment on the component. For instance, if this
+ * ThreadView initiated a thread cache load and it now has become
+ * loaded, then it may need to auto-scroll to last message.
+ *
+ * @param {string} hintType name of the hint. Used to determine what's
+ * the broad type of adjustement the component has to do.
+ * @param {any} [hintData] data of the hint. Used to fine-tune
+ * adjustments on the component.
+ */
+ addComponentHint(hintType, hintData) {
+ const hint = { data: hintData, type: hintType };
+ this.update({
+ componentHintList: this.componentHintList.concat([hint]),
+ });
+ }
+
+ /**
+ * @param {mail.thread_cache} threadCache
+ */
+ handleThreadCacheLoaded(threadCache) {
+ if (threadCache !== this.threadCache) {
+ return;
+ }
+ this._stopLoading();
+ }
+
+ /**
+ * @param {Object} hint
+ */
+ markComponentHintProcessed(hint) {
+ let filterFun;
+ switch (hint.type) {
+ case 'current-partner-just-posted-message':
+ filterFun = h => h.type !== hint.type && h.messageId !== hint.data.messageId;
+ break;
+ default:
+ filterFun = h => h.type !== hint.type;
+ break;
+ }
+ this.update({
+ componentHintList: this.componentHintList.filter(filterFun),
+ });
+ }
+
+ /**
+ * @param {mail.message} message
+ */
+ handleVisibleMessage(message) {
+ if (!this.lastVisibleMessage || this.lastVisibleMessage.id < message.id) {
+ this.update({ lastVisibleMessage: [['link', message]] });
+ }
+ }
+
+ /**
+ * @param {string} scrollTop
+ */
+ saveThreadCacheScrollPositionsAsInitial(scrollTop) {
+ if (!this.threadCache) {
+ return;
+ }
+ this.update({
+ threadCacheInitialScrollPositions: Object.assign({}, this.threadCacheInitialScrollPositions, {
+ [this.threadCache.localId]: scrollTop,
+ }),
+ });
+ }
+
+ //----------------------------------------------------------------------
+ // Private
+ //----------------------------------------------------------------------
+
+ /**
+ * @private
+ * @returns {mail.thread_cache|undefined}
+ */
+ _computeThreadCache() {
+ if (!this.thread) {
+ return [];
+ }
+ return [['link', this.thread.cache(this.stringifiedDomain)]];
+ }
+
+ /**
+ * Not a real field, used to trigger `thread.markAsSeen` when one of
+ * the dependencies changes.
+ *
+ * @private
+ * @returns {boolean}
+ */
+ _computeThreadShouldBeSetAsSeen() {
+ // FIXME condition should not be on "composer is focused" but "threadView is active"
+ // See task-2277543
+ const lastMessageIsVisible = this.lastVisibleMessage &&
+ this.lastVisibleMessage === this.lastMessage;
+ if (lastMessageIsVisible && this.hasComposerFocus && this.thread) {
+ this.thread.markAsSeen().catch(e => {
+ // prevent crash when executing compute during destroy
+ if (!(e instanceof RecordDeletedError)) {
+ throw e;
+ }
+ });
+ }
+ return true;
+ }
+
+ /**
+ * @private
+ * @returns {integer|undefined}
+ */
+ _computeThreadCacheInitialScrollPosition() {
+ if (!this.threadCache) {
+ return undefined;
+ }
+ return this.threadCacheInitialScrollPositions[this.threadCache.localId];
+ }
+
+ /**
+ * @private
+ */
+ _prepareLoading() {
+ this._isPreparingLoading = true;
+ this._loaderTimeout = setTimeout(() => {
+ this.isShowingLoading = true;
+ this._isPreparingLoading = false;
+ }, 400);
+ }
+
+ /**
+ * @private
+ */
+ _stopLoading() {
+ clearTimeout(this._loaderTimeout);
+ this._loaderTimeout = null;
+ this.isShowingLoading = false;
+ this._isPreparingLoading = false;
+ }
+
+ /**
+ * @override
+ */
+ _updateAfter(previous) {
+ if (this.thread && this.thread !== previous.thread) {
+ this._stopLoading();
+ if (!this.threadCache.isLoaded && !this.threadCache.isLoading) {
+ this.threadCache.loadMessages();
+ }
+ }
+ if (this.threadCache !== previous.threadCache) {
+ this._stopLoading();
+ this.addComponentHint('change-of-thread-cache');
+ }
+
+ if (
+ this.thread && this.threadCache && this.threadCache.isLoading &&
+ !this.isShowingLoading && !this._isPreparingLoading
+ ) {
+ this._prepareLoading();
+ }
+ }
+
+ /**
+ * @override
+ */
+ _updateBefore() {
+ return {
+ thread: this.thread,
+ threadCache: this.threadCache,
+ };
+ }
+
+ }
+
+ ThreadView.fields = {
+ chatWindow: one2one('mail.chat_window', {
+ inverse: 'threadView',
+ }),
+ checkedMessages: many2many('mail.message', {
+ related: 'threadCache.checkedMessages',
+ }),
+ /**
+ * List of component hints. Hints contain information that help
+ * components make UI/UX decisions based on their UI state.
+ * For instance, on receiving new messages and the last message
+ * is visible, it should auto-scroll to this new last message.
+ *
+ * Format of a component hint:
+ *
+ * {
+ * type: {string} the name of the component hint. Useful
+ * for components to dispatch behaviour
+ * based on its type.
+ * data: {Object} data related to the component hint.
+ * For instance, if hint suggests to scroll
+ * to a certain message, data may contain
+ * message id.
+ * }
+ */
+ componentHintList: attr({
+ default: [],
+ }),
+ composer: many2one('mail.composer', {
+ related: 'thread.composer',
+ }),
+ hasComposerFocus: attr({
+ related: 'composer.hasFocus',
+ }),
+ lastMessage: many2one('mail.message', {
+ related: 'thread.lastMessage',
+ }),
+ /**
+ * Most recent message in this ThreadView that has been shown to the
+ * current partner.
+ */
+ lastVisibleMessage: many2one('mail.message'),
+ messages: many2many('mail.message', {
+ related: 'threadCache.messages',
+ }),
+ stringifiedDomain: attr({
+ default: '[]',
+ }),
+ thread: many2one('mail.thread', {
+ inverse: 'threadViews',
+ }),
+ threadCache: many2one('mail.thread_cache', {
+ compute: '_computeThreadCache',
+ dependencies: [
+ 'stringifiedDomain',
+ 'thread',
+ 'threadCaches',
+ ],
+ }),
+ threadCacheInitialScrollPosition: attr({
+ compute: '_computeThreadCacheInitialScrollPosition',
+ dependencies: [
+ 'threadCache',
+ 'threadCacheInitialScrollPositions',
+ ],
+ }),
+ /**
+ * List of saved initial scroll positions of thread caches.
+ * Useful to restore scroll position on changing back to this
+ * thread cache. Note that this is only applied when opening
+ * the thread cache, because scroll position may change fast so
+ * save is already throttled
+ */
+ threadCacheInitialScrollPositions: attr({
+ default: {},
+ }),
+ threadCaches: many2many('mail.thread_cache', {
+ related: 'thread.caches',
+ }),
+ /**
+ * Not a real field, used to trigger `thread.markAsSeen` when one of
+ * the dependencies changes.
+ */
+ threadShouldBeSetAsSeen: attr({
+ compute: '_computeThreadShouldBeSetAsSeen',
+ dependencies: [
+ 'hasComposerFocus',
+ 'lastMessage',
+ 'lastVisibleMessage',
+ 'threadCache',
+ ],
+ }),
+ uncheckedMessages: many2many('mail.message', {
+ related: 'threadCache.uncheckedMessages',
+ }),
+ };
+
+ ThreadView.modelName = 'mail.thread_view';
+
+ return ThreadView;
+}
+
+registerNewModel('mail.thread_view', factory);
+
+});