odoo.define('mail/static/src/components/notification_list/notification_list.js', function (require) { 'use strict'; const components = { NotificationGroup: require('mail/static/src/components/notification_group/notification_group.js'), NotificationRequest: require('mail/static/src/components/notification_request/notification_request.js'), ThreadNeedactionPreview: require('mail/static/src/components/thread_needaction_preview/thread_needaction_preview.js'), ThreadPreview: require('mail/static/src/components/thread_preview/thread_preview.js'), }; const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js'); const useStore = require('mail/static/src/component_hooks/use_store/use_store.js'); const { Component } = owl; class NotificationList extends Component { /** * @override */ constructor(...args) { super(...args); useShouldUpdateBasedOnProps(); this.storeProps = useStore((...args) => this._useStoreSelector(...args), { compareDepth: { // list + notification object created in useStore notifications: 2, }, }); } mounted() { this._loadPreviews(); } //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- /** * @returns {Object[]} */ get notifications() { const { notifications } = this.storeProps; return notifications; } //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * Load previews of given thread. Basically consists of fetching all missing * last messages of each thread. * * @private */ async _loadPreviews() { const threads = this.notifications .filter(notification => notification.thread && notification.thread.exists()) .map(notification => notification.thread); this.env.models['mail.thread'].loadPreviews(threads); } /** * @private * @param {Object} props */ _useStoreSelector(props) { const threads = this._useStoreSelectorThreads(props); let threadNeedactionNotifications = []; if (props.filter === 'all') { // threads with needactions threadNeedactionNotifications = this.env.models['mail.thread'] .all(t => t.model !== 'mail.box' && t.needactionMessagesAsOriginThread.length > 0) .sort((t1, t2) => { if (t1.needactionMessagesAsOriginThread.length > 0 && t2.needactionMessagesAsOriginThread.length === 0) { return -1; } if (t1.needactionMessagesAsOriginThread.length === 0 && t2.needactionMessagesAsOriginThread.length > 0) { return 1; } if (t1.lastNeedactionMessageAsOriginThread && t2.lastNeedactionMessageAsOriginThread) { return t1.lastNeedactionMessageAsOriginThread.date.isBefore(t2.lastNeedactionMessageAsOriginThread.date) ? 1 : -1; } if (t1.lastNeedactionMessageAsOriginThread) { return -1; } if (t2.lastNeedactionMessageAsOriginThread) { return 1; } return t1.id < t2.id ? -1 : 1; }) .map(thread => { return { thread, type: 'thread_needaction', uniqueId: thread.localId + '_needaction', }; }); } // thread notifications const threadNotifications = threads .sort((t1, t2) => { if (t1.localMessageUnreadCounter > 0 && t2.localMessageUnreadCounter === 0) { return -1; } if (t1.localMessageUnreadCounter === 0 && t2.localMessageUnreadCounter > 0) { return 1; } if (t1.lastMessage && t2.lastMessage) { return t1.lastMessage.date.isBefore(t2.lastMessage.date) ? 1 : -1; } if (t1.lastMessage) { return -1; } if (t2.lastMessage) { return 1; } return t1.id < t2.id ? -1 : 1; }) .map(thread => { return { thread, type: 'thread', uniqueId: thread.localId, }; }); let notifications = threadNeedactionNotifications.concat(threadNotifications); if (props.filter === 'all') { const notificationGroups = this.env.messaging.notificationGroupManager.groups; notifications = Object.values(notificationGroups) .sort((group1, group2) => group1.date.isAfter(group2.date) ? -1 : 1 ).map(notificationGroup => { return { notificationGroup, uniqueId: notificationGroup.localId, }; }).concat(notifications); } // native notification request if (props.filter === 'all' && this.env.messaging.isNotificationPermissionDefault()) { notifications.unshift({ type: 'odoobotRequest', uniqueId: 'odoobotRequest', }); } return { isDeviceMobile: this.env.messaging.device.isMobile, notifications, }; } /** * @private * @param {Object} props * @throws {Error} in case `props.filter` is not supported * @returns {mail.thread[]} */ _useStoreSelectorThreads(props) { if (props.filter === 'mailbox') { return this.env.models['mail.thread'] .all(thread => thread.isPinned && thread.model === 'mail.box') .sort((mailbox1, mailbox2) => { if (mailbox1 === this.env.messaging.inbox) { return -1; } if (mailbox2 === this.env.messaging.inbox) { return 1; } if (mailbox1 === this.env.messaging.starred) { return -1; } if (mailbox2 === this.env.messaging.starred) { return 1; } const mailbox1Name = mailbox1.displayName; const mailbox2Name = mailbox2.displayName; mailbox1Name < mailbox2Name ? -1 : 1; }); } else if (props.filter === 'channel') { return this.env.models['mail.thread'] .all(thread => thread.channel_type === 'channel' && thread.isPinned && thread.model === 'mail.channel' ) .sort((c1, c2) => c1.displayName < c2.displayName ? -1 : 1); } else if (props.filter === 'chat') { return this.env.models['mail.thread'] .all(thread => thread.isChatChannel && thread.isPinned && thread.model === 'mail.channel' ) .sort((c1, c2) => c1.displayName < c2.displayName ? -1 : 1); } else if (props.filter === 'all') { // "All" filter is for channels and chats return this.env.models['mail.thread'] .all(thread => thread.isPinned && thread.model === 'mail.channel') .sort((c1, c2) => c1.displayName < c2.displayName ? -1 : 1); } else { throw new Error(`Unsupported filter ${props.filter}`); } } } Object.assign(NotificationList, { _allowedFilters: ['all', 'mailbox', 'channel', 'chat'], components, defaultProps: { filter: 'all', }, props: { filter: { type: String, validate: prop => NotificationList._allowedFilters.includes(prop), }, }, template: 'mail.NotificationList', }); return NotificationList; });