summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components/discuss_sidebar
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/discuss_sidebar
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/discuss_sidebar')
-rw-r--r--addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.js308
-rw-r--r--addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.scss110
-rw-r--r--addons/mail/static/src/components/discuss_sidebar/discuss_sidebar.xml81
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>