diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/discuss_sidebar | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/discuss_sidebar')
3 files changed, 499 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.js b/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.js new file mode 100644 index 00000000..d12d0353 --- /dev/null +++ b/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.js @@ -0,0 +1,308 @@ +odoo.define('mail/static/src/components/discuss_sidebar/discuss_sidebar.js', function (require) { +'use strict'; + +const components = { + AutocompleteInput: require('mail/static/src/components/autocomplete_input/autocomplete_input.js'), + DiscussSidebarItem: require('mail/static/src/components/discuss_sidebar_item/discuss_sidebar_item.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 useUpdate = require('mail/static/src/component_hooks/use_update/use_update.js'); + +const { Component } = owl; +const { useRef } = owl.hooks; + +class DiscussSidebar extends Component { + + /** + * @override + */ + constructor(...args) { + super(...args); + useShouldUpdateBasedOnProps(); + useStore( + (...args) => this._useStoreSelector(...args), + { compareDepth: this._useStoreCompareDepth() } + ); + useUpdate({ func: () => this._update() }); + /** + * Reference of the quick search input. Useful to filter channels and + * chats based on this input content. + */ + this._quickSearchInputRef = useRef('quickSearchInput'); + + // bind since passed as props + this._onAddChannelAutocompleteSelect = this._onAddChannelAutocompleteSelect.bind(this); + this._onAddChannelAutocompleteSource = this._onAddChannelAutocompleteSource.bind(this); + this._onAddChatAutocompleteSelect = this._onAddChatAutocompleteSelect.bind(this); + this._onAddChatAutocompleteSource = this._onAddChatAutocompleteSource.bind(this); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {mail.discuss} + */ + get discuss() { + return this.env.messaging && this.env.messaging.discuss; + } + + /** + * @returns {string} + */ + get FIND_OR_CREATE_CHANNEL() { + return this.env._t("Find or create a channel..."); + } + + /** + * @returns {mail.thread[]} + */ + get orderedMailboxes() { + 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; + }); + } + + /** + * Return the list of chats that match the quick search value input. + * + * @returns {mail.thread[]} + */ + get quickSearchPinnedAndOrderedChats() { + const allOrderedAndPinnedChats = this.env.models['mail.thread'] + .all(thread => + thread.channel_type === 'chat' && + thread.isPinned && + thread.model === 'mail.channel' + ) + .sort((c1, c2) => c1.displayName < c2.displayName ? -1 : 1); + if (!this.discuss.sidebarQuickSearchValue) { + return allOrderedAndPinnedChats; + } + const qsVal = this.discuss.sidebarQuickSearchValue.toLowerCase(); + return allOrderedAndPinnedChats.filter(chat => { + const nameVal = chat.displayName.toLowerCase(); + return nameVal.includes(qsVal); + }); + } + + /** + * Return the list of channels that match the quick search value input. + * + * @returns {mail.thread[]} + */ + get quickSearchOrderedAndPinnedMultiUserChannels() { + const allOrderedAndPinnedMultiUserChannels = this.env.models['mail.thread'] + .all(thread => + thread.channel_type === 'channel' && + thread.isPinned && + thread.model === 'mail.channel' + ) + .sort((c1, c2) => { + if (c1.displayName && !c2.displayName) { + return -1; + } else if (!c1.displayName && c2.displayName) { + return 1; + } else if (c1.displayName && c2.displayName && c1.displayName !== c2.displayName) { + return c1.displayName.toLowerCase() < c2.displayName.toLowerCase() ? -1 : 1; + } else { + return c1.id - c2.id; + } + }); + if (!this.discuss.sidebarQuickSearchValue) { + return allOrderedAndPinnedMultiUserChannels; + } + const qsVal = this.discuss.sidebarQuickSearchValue.toLowerCase(); + return allOrderedAndPinnedMultiUserChannels.filter(channel => { + const nameVal = channel.displayName.toLowerCase(); + return nameVal.includes(qsVal); + }); + } + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _update() { + if (!this.discuss) { + return; + } + if (this._quickSearchInputRef.el) { + this._quickSearchInputRef.el.value = this.discuss.sidebarQuickSearchValue; + } + } + + /** + * @private + * @returns {Object} + */ + _useStoreCompareDepth() { + return { + allOrderedAndPinnedChats: 1, + allOrderedAndPinnedMailboxes: 1, + allOrderedAndPinnedMultiUserChannels: 1, + }; + } + + /** + * @private + * @param {Object} props + * @returns {Object} + */ + _useStoreSelector(props) { + const discuss = this.env.messaging.discuss; + return { + allOrderedAndPinnedChats: this.quickSearchPinnedAndOrderedChats, + allOrderedAndPinnedMailboxes: this.orderedMailboxes, + allOrderedAndPinnedMultiUserChannels: this.quickSearchOrderedAndPinnedMultiUserChannels, + allPinnedChannelAmount: + this.env.models['mail.thread'] + .all(thread => + thread.isPinned && + thread.model === 'mail.channel' + ).length, + discussIsAddingChannel: discuss && discuss.isAddingChannel, + discussIsAddingChat: discuss && discuss.isAddingChat, + discussSidebarQuickSearchValue: discuss && discuss.sidebarQuickSearchValue, + }; + } + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Event} ev + * @param {Object} ui + * @param {Object} ui.item + * @param {integer} ui.item.id + */ + _onAddChannelAutocompleteSelect(ev, ui) { + this.discuss.handleAddChannelAutocompleteSelect(ev, ui); + } + + /** + * @private + * @param {Object} req + * @param {string} req.term + * @param {function} res + */ + _onAddChannelAutocompleteSource(req, res) { + this.discuss.handleAddChannelAutocompleteSource(req, res); + } + + /** + * @private + * @param {Event} ev + * @param {Object} ui + * @param {Object} ui.item + * @param {integer} ui.item.id + */ + _onAddChatAutocompleteSelect(ev, ui) { + this.discuss.handleAddChatAutocompleteSelect(ev, ui); + } + + /** + * @private + * @param {Object} req + * @param {string} req.term + * @param {function} res + */ + _onAddChatAutocompleteSource(req, res) { + this.discuss.handleAddChatAutocompleteSource(req, res); + } + + /** + * Called when clicking on add channel icon. + * + * @private + * @param {MouseEvent} ev + */ + _onClickChannelAdd(ev) { + ev.stopPropagation(); + this.discuss.update({ isAddingChannel: true }); + } + + /** + * Called when clicking on channel title. + * + * @private + * @param {MouseEvent} ev + */ + _onClickChannelTitle(ev) { + ev.stopPropagation(); + return this.env.bus.trigger('do-action', { + action: { + name: this.env._t("Public Channels"), + type: 'ir.actions.act_window', + res_model: 'mail.channel', + views: [[false, 'kanban'], [false, 'form']], + domain: [['public', '!=', 'private']] + }, + }); + } + + /** + * Called when clicking on add chat icon. + * + * @private + * @param {MouseEvent} ev + */ + _onClickChatAdd(ev) { + ev.stopPropagation(); + this.discuss.update({ isAddingChat: true }); + } + + /** + * @private + * @param {CustomEvent} ev + */ + _onHideAddingItem(ev) { + ev.stopPropagation(); + this.discuss.clearIsAddingItem(); + } + + /** + * @private + * @param {KeyboardEvent} ev + */ + _onInputQuickSearch(ev) { + ev.stopPropagation(); + this.discuss.update({ + sidebarQuickSearchValue: this._quickSearchInputRef.el.value, + }); + } + +} + +Object.assign(DiscussSidebar, { + components, + props: {}, + template: 'mail.DiscussSidebar', +}); + +return DiscussSidebar; + +}); diff --git a/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.scss b/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.scss new file mode 100644 index 00000000..3e49cddf --- /dev/null +++ b/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.scss @@ -0,0 +1,110 @@ +// ------------------------------------------------------------------ +// Layout +// ------------------------------------------------------------------ + +.o_DiscussSidebar { + display: flex; + flex-flow: column; + width: $o-mail-chat-sidebar-width; + + @include media-breakpoint-up(xl) { + width: $o-mail-chat-sidebar-width + 50px; + } +} + +.o_DiscussSidebar_group { + display: flex; + flex-flow: column; + flex: 0 0 auto; +} + +.o_DiscussSidebar_groupHeader { + display: flex; + align-items: center; + margin: 5px 0; +} + +.o_DiscussSidebar_groupHeaderItem { + margin-left: 3px; + margin-right: 3px; + + &:first-child { + margin-left: $o-mail-discuss-sidebar-active-indicator-margin-right; + } + + &:last-child { + margin-right: $o-mail-discuss-sidebar-scrollbar-width; + } +} + +.o_DiscussSidebar_itemNew { + display: flex; + justify-content: center; +} + +.o_DiscussSidebar_itemNewInput { + flex: 1 1 auto; + margin-left: $o-mail-discuss-sidebar-active-indicator-margin-right + 3px; + margin-right: $o-mail-discuss-sidebar-scrollbar-width; +} + +.o_DiscussSidebar_quickSearch { + border-radius: 10px; + margin: 0 $o-mail-discuss-sidebar-scrollbar-width 10px; + padding: 3px 10px; +} + +.o_DiscussSidebar_separator { + width: 100%; +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_DiscussSidebar { + background-color: gray('900'); + color: gray('300'); +} + +.o_DiscussSidebar_groupHeader { + font-size: $font-size-sm; + text-transform: uppercase; + font-weight: bolder; +} + +.o_DiscussSidebar_groupHeaderItemAdd { + cursor: pointer; + + &:not(:hover) { + color: gray('600'); + } +} + +.o_DiscussSidebar_groupTitle { + + &:not(.o-clickable) { + color: gray('600'); + } + + &.o-clickable { + cursor: pointer; + + &:not(:hover) { + color: gray('600'); + } + } +} + +.o_DiscussSidebar_itemNewInput { + outline: none; +} + +.o_DiscussSidebar_quickSearch { + border: none; + outline: none; +} + +.o_DiscussSidebar_separator { + background-color: gray('600'); +} diff --git a/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.xml b/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.xml new file mode 100644 index 00000000..4f9c10e5 --- /dev/null +++ b/addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <t t-name="mail.DiscussSidebar" owl="1"> + <div name="root" class="o_DiscussSidebar"> + <div class="o_DiscussSidebar_group o_DiscussSidebar_groupMailbox"> + <t t-foreach="orderedMailboxes" t-as="mailbox" t-key="mailbox.localId"> + <DiscussSidebarItem + class="o_DiscussSidebar_item" + threadLocalId="mailbox.localId" + /> + </t> + </div> + <hr class="o_DiscussSidebar_separator"/> + <t t-if="env.models['mail.thread'].all(thread => thread.isPinned and thread.model === 'mail.channel').length > 19"> + <input class="o_DiscussSidebar_quickSearch" t-on-input="_onInputQuickSearch" placeholder="Quick search..." t-ref="quickSearchInput" t-esc="discuss.sidebarQuickSearchValue"/> + </t> + <div class="o_DiscussSidebar_group o_DiscussSidebar_groupChannel"> + <div class="o_DiscussSidebar_groupHeader"> + <div class="o_DiscussSidebar_groupHeaderItem o_DiscussSidebar_groupTitle o-clickable" t-on-click="_onClickChannelTitle"> + Channels + </div> + <div class="o-autogrow"/> + <div class="o_DiscussSidebar_groupHeaderItem o_DiscussSidebar_groupHeaderItemAdd fa fa-plus" t-on-click="_onClickChannelAdd" title="Add or join a channel"/> + </div> + <div class="o_DiscussSidebar_list"> + <t t-if="discuss.isAddingChannel"> + <div class="o_DiscussSidebar_item o_DiscussSidebar_itemNew"> + <AutocompleteInput + class="o_DiscussSidebar_itemNewInput" + customClass="'o_DiscussSidebar_newChannelAutocompleteSuggestions'" + isFocusOnMount="true" + isHtml="true" + placeholder="FIND_OR_CREATE_CHANNEL" + select="_onAddChannelAutocompleteSelect" + source="_onAddChannelAutocompleteSource" + t-on-o-hide="_onHideAddingItem" + /> + </div> + </t> + <t t-foreach="quickSearchOrderedAndPinnedMultiUserChannels" t-as="channel" t-key="channel.localId"> + <DiscussSidebarItem + class="o_DiscussSidebar_item" + threadLocalId="channel.localId" + /> + </t> + </div> + </div> + <div class="o_DiscussSidebar_group o_DiscussSidebar_groupChat"> + <div class="o_DiscussSidebar_groupHeader"> + <div class="o_DiscussSidebar_groupHeaderItem o_DiscussSidebar_groupTitle"> + Direct Messages + </div> + <div class="o-autogrow"/> + <div class="o_DiscussSidebar_groupHeaderItem o_DiscussSidebar_groupHeaderItemAdd fa fa-plus" t-on-click="_onClickChatAdd" title="Start a conversation"/> + </div> + <div class="o_DiscussSidebar_list"> + <t t-if="discuss.isAddingChat"> + <div class="o_DiscussSidebar_item o_DiscussSidebar_itemNew"> + <AutocompleteInput + class="o_DiscussSidebar_itemNewInput" + isFocusOnMount="true" + placeholder="'Find or start a conversation...'" + select="_onAddChatAutocompleteSelect" + source="_onAddChatAutocompleteSource" + t-on-o-hide="_onHideAddingItem" + /> + </div> + </t> + <t t-foreach="quickSearchPinnedAndOrderedChats" t-as="chat" t-key="chat.localId"> + <DiscussSidebarItem + class="o_DiscussSidebar_item" + threadLocalId="chat.localId" + /> + </t> + </div> + </div> + </div> + </t> + +</templates> |
