diff options
| author | Alexandre Kühn <aku@odoo.com> | 2018-06-12 15:46:46 +0200 |
|---|---|---|
| committer | Alexandre Kühn <aku@odoo.com> | 2018-07-02 15:06:54 +0200 |
| commit | cd34f6de727d5b3858420cff3c9d3c5995c4e75c (patch) | |
| tree | 8d25861f98a038b71728355375d634a5c178290e /addons/mail/static/src/xml | |
| parent | ee9c1f52c316c83775969a3c9038e7e9f560f7e7 (diff) | |
[REF] mail: JS mail refactoring
----------------------
Summary
----------------------
The purpose of this commit is to improve the JS code of the `mail` module.
It applies the new coding guidelines and makes some changes on the design
of some modules, such as the old ChatManager module.
Here is a short summary of the changes that have been made:
1. New coding guidelines
- snake_case to camelCase
- prefix private attributes and methods with '_'
- jsdoc on most methods
- one class per module
2. Rename/Merge some classes
- 'chat manager' becomes 'mail manager' (internal) and 'mail service' (external)
- 'chat window manager' is now included in mail manager
- 'thread' widget now named 'thread widget'
3. New model abstraction for mail objects:
- modules 'mail.model.*'
- modeling:
0..1 0..1 * *
ThreadWindow <------> Thread <-------> Message
/ \
/ \
Thread With Cache Document Thread
/ | \
/ | \
Mailbox Channel Support Channel
|
|
DM
- Thread: the superclass of threads.
- ThreadWindow: the window component of a thread.
- Message: mail objects representing messages.
- Thread With Cache: threads that can be used with search view (Discuss app compatible).
- Document Thread: represents the thread part of a chatter.
- Mailbox: represents what was previously called 'static' channel, e.g. 'Inbox'.
- Channel: mail objects representing channels, including livechat.
- DM: special kind of Channel for 1:1 communication in the backend.
- Support Channel: special channel for im_support module.
This new modeling approach let us easily add features on all threads, such as the
possibility to put any thread in a small window.
----------------------
Known issues
----------------------
[Already Present in Master]
1. When the Discuss app is in the background with 'Inbox' as the selected
Thread, when clicking on a document thread preview in the messaging menu
of the systray, the rainbow man appears.
2. When a document with the chatter is in the background, when receiving an
inbox notification from this document thread, the document thread is
automatically marked as read, which removes the notification right away.
3. Sometimes, opening a DM window from the "blank" thread window does not work.
4. Reply-to feature on Inbox is not working: no message is sent in the document
thread.
5. On the first login of admin user with demo data, the inbox counter is wrong
(it displays 6, instead of 3).
Explanation after investigation:
> On page load, it fetches the correct number of Inbox messages (3),
but the server notifies of 3 needaction messages right away,
so it wrongly assumes these are new needaction messages.
> Not possible for web client to detect that these messages should not
increment the Inbox counter while keeping same API.
6. Notifications for new document thread messages only work when the user sets
'handle with Odoo' for the Notification Management in the preferences.
> due to notifications on the longpoll bus for document thread
messages that come from needaction notifications.
> requires server-side changes to send notification on the longpoll
bus to mentionned user.
[New]
7. When receiving a message on a unjoined channel, thread window flickers
('open' > 'close' > 'open')
Explanation after investigation:
> JS logic:
a) On auto-join, ask server to join the channel and get channel infos.
b) The info tells the channel is not detached, but JS code makes decision
to detach it, and tells server the channel is now detached.
c) From (a), server notifies on longpoll bus the state of channel, which is
not detached. The web client thinks that the window state of the channel
has been changed somewhere else, and the channel is now closed.
d) From (b), server notifies on longpoll bus the state of channel, which is
detached. The web client opens the thread window of this channel.
> The flicker didn't occur before refactoring because the web client was only
updating the model of channel when it receives the longpoll notification.
> Server behaviour on 1st longpoll notification is necessary for cross-tab
synchronization for channel window state.
> New design implies that model and view should be synchronized, hence the
issue now.
> Solution: remove server-side thread window synchronization and replace with
client-side synchronization.
----------------------
Hacks
----------------------
The module `im_livechat` now uses mail objects that are compatible with Message
and Window objects:
Modeling for messages:
AbstractMessage
/ \
/ \
LivechatMessage Message
- AbstractMessage: message compatible with the thread widget.
- LivechatMessage: message used by im_livechat.
- Message: message used with the mail manager.
Modeling for thread windows:
AbstractThreadWindow
/ \
/ \
LivechatWindow ThreadWindow
- AbstractThreadWindow: behaviour share between all types of thread windows.
- LivechatWindow: window used by im_livechat.
- ThreadWindow: window used with mail_manager.
The reason for these hacks are twofold:
1. Use the thread widget in the frontend and livechat external lib bundles.
2. Do not have a dependency with the mail manager in the frontend and external
lib bundles.
Diffstat (limited to 'addons/mail/static/src/xml')
| -rw-r--r-- | addons/mail/static/src/xml/abstract_thread_window.xml | 79 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/activity.xml | 4 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/chat_window.xml | 57 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/chatter.xml | 28 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/composer.xml | 26 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/discuss.xml | 316 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/extended_chat_window.xml | 19 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/systray.xml | 56 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/thread.xml | 215 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/thread_window.xml | 25 | ||||
| -rw-r--r-- | addons/mail/static/src/xml/web_kanban_activity.xml | 4 |
11 files changed, 477 insertions, 352 deletions
diff --git a/addons/mail/static/src/xml/abstract_thread_window.xml b/addons/mail/static/src/xml/abstract_thread_window.xml new file mode 100644 index 00000000000..6857c638c45 --- /dev/null +++ b/addons/mail/static/src/xml/abstract_thread_window.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <!-- + @param {mail.AbstractThreadWindow} widget + --> + <t t-name="mail.AbstractThreadWindow"> + <div class="o_thread_window o_in_home_menu"> + <div class="o_thread_window_header"> + <t t-call="mail.AbstractThreadWindowHeaderContent"> + <t t-set="status" t-value="widget.getStatus()"/> + <t t-set="title" t-value="widget.getTitle()"/> + <t t-set="unreadCounter" t-value="widget.getUnreadCounter()"/> + </t> + </div> + <div class="o_chat_content"> + </div> + <div t-if='widget.hasThread()' class="o_thread_composer o_chat_mini_composer"> + <input class="o_composer_text_field" t-att-placeholder="widget.options.placeholder"/> + </div> + </div> + </t> + + <!-- + @param {boolean} [open=false] tell whether the hidden thread window + dropdown menu is open or not. + @param {mail.ThreadWindow[]} threadWindows list of thread windows + that are hidden, i.e. put in the hidden window dropdown menu. + @param {integer} [unreadCounter] the total unread counter of all + threads which are linked to the thread windows that are put in the + hidden thread window dropdown menu. + --> + <t t-name="mail.HiddenThreadWindowsDropdown"> + <div t-attf-class="o_thread_window o_in_home_menu o_thread_window_dropdown dropup #{open ? 'open' : ''}"> + <span role="alert" class="o_thread_window_dropdown_toggler dropdown-toggle" data-toggle="dropdown" title="Unread messages"> + <i class="fa fa-comments-o" role="img" aria-label="Messages"/> <t t-esc="threadWindows.length"/> + <span class="o_total_unread_counter"> + <t t-if="unreadCounter"><t t-esc="unreadCounter"/></t> + </span> + </span> + <ul class="dropdown-menu" role="menu"> + <t t-foreach="threadWindows" t-as="threadWindow"> + <li role="menuitem" class="o_thread_window_header" t-att-data-thread-id="threadWindow.getID()"> + <t t-call="mail.AbstractThreadWindowHeaderContent"> + <t t-set="status" t-value="threadWindow.getStatus()"/> + <t t-set="title" t-value="threadWindow.getTitle()"/> + <t t-set="unreadCounter" t-value="threadWindow.getUnreadCounter()"/> + </t> + </li> + </t> + </ul> + </div> + </t> + + <!-- + @param {string} [status] e.g. 'online', 'offline'. + @param {string} title the title of the thread window, e.g. the record + name of the document. + @param {integer} [unreadCounter] the number of unread messages on the + thread. + @param {Object} widget + @param {function} widget.isMobile function without any param that + states whether it should render for desktop or mobile screen. + --> + <t t-name="mail.AbstractThreadWindowHeaderContent"> + <span t-if="widget.isMobile()"> + <a href="#" class="o_thread_window_close fa fa-1x fa-arrow-left mr4" aria-label="Close chat window" title="Close chat window"/> + </span> + <span class="o_thread_window_title"> + <t t-if="status" t-call="mail.UserStatus"/> + <t t-esc="title"/> + <span t-if="unreadCounter"> (<t t-esc="unreadCounter"/>)</span> + </span> + <span t-if="!widget.isMobile()" class="o_thread_window_buttons"> + <a href="#" class="o_thread_window_close fa fa-close"/> + </span> + </t> + +</templates> diff --git a/addons/mail/static/src/xml/activity.xml b/addons/mail/static/src/xml/activity.xml index 9d2f0d72ed8..d96db53ac41 100644 --- a/addons/mail/static/src/xml/activity.xml +++ b/addons/mail/static/src/xml/activity.xml @@ -40,13 +40,13 @@ </dd> <dt>Created on</dt> <dd class="mb8"> - <t t-esc="activity.create_date.format(datetime_format)"/> + <t t-esc="activity.create_date.format(datetimeFormat)"/> </dd> <dt>Assigned to</dt> <dd class="mb8"> <img t-attf-src="/web/image#{activity.user_id[0] >= 0 ? ('/res.users/' + activity.user_id[0] + '/image_small') : ''}" height="18" width="18" class="img-circle mr4" t-att-title="activity.user_id[1]" t-att-alt="activity.user_id[1]"/> <b><t t-esc="activity.user_id[1]"/></b> - <em>, due on </em><span t-attf-class="o_activity_color_#{activity.state}"><t t-esc="activity.date_deadline.format(date_format)"/></span> + <em>, due on </em><span t-attf-class="o_activity_color_#{activity.state}"><t t-esc="activity.date_deadline.format(dateFormat)"/></span> </dd> </dl> </div> diff --git a/addons/mail/static/src/xml/chat_window.xml b/addons/mail/static/src/xml/chat_window.xml deleted file mode 100644 index 7d0fce19ee6..00000000000 --- a/addons/mail/static/src/xml/chat_window.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<templates xml:space="preserve"> - - <t t-name="mail.ChatWindow"> - <div class="o_chat_window o_in_home_menu"> - <div class="o_chat_header"> - <t t-call="mail.ChatWindowHeaderContent"> - <t t-set="status" t-value="widget.status"/> - <t t-set="title" t-value="widget.title"/> - <t t-set="unread_counter" t-value="widget.unread_msgs"/> - </t> - </div> - <div class="o_chat_content"> - </div> - <div t-if='!widget.options.input_less' class="o_chat_composer o_chat_mini_composer"> - <input class="o_composer_text_field" t-att-placeholder="widget.options.placeholder"/> - </div> - </div> - </t> - - <t t-name="mail.ChatWindowsDropdown"> - <div t-attf-class="o_chat_window o_in_home_menu o_chat_window_dropdown dropup #{open ? 'open' : ''}"> - <span role="alert" class="o_chat_window_dropdown_toggler dropdown-toggle" data-toggle="dropdown" title="Unread messages"> - <i class="fa fa-comments-o" role="img" aria-label="Messages"/> <t t-esc="sessions.length"/> - <span class="o_total_unread_counter"> - <t t-if="unread_counter"><t t-esc="unread_counter"/></t> - </span> - </span> - <ul class="dropdown-menu" role="menu"> - <t t-foreach="sessions" t-as="session"> - <li role="menuitem" class="o_chat_header" t-att-data-session-id="session.id"> - <t t-call="mail.ChatWindowHeaderContent"> - <t t-set="status" t-value="session.window.status"/> - <t t-set="title" t-value="session.window.title"/> - <t t-set="unread_counter" t-value="session.window.unread_msgs"/> - </t> - </li> - </t> - </ul> - </div> - </t> - - <t t-name="mail.ChatWindowHeaderContent"> - <span t-if="widget.isMobile"> - <a href="#" class="o_chat_window_close fa fa-1x fa-arrow-left mr4" aria-label="Close chat window" title="Close chat window"/> - </span> - <span class="o_chat_title"> - <t t-if="status" t-call="mail.chat.UserStatus"/> - <t t-esc="title"/> - <span t-if="unread_counter"> (<t t-esc="unread_counter"/>)</span> - </span> - <span t-if="!widget.isMobile" class="o_chat_window_buttons"> - <a href="#" class="o_chat_window_close fa fa-close" aria-label="Close chat window" title="Close chat window"/> - </span> - </t> - -</templates> diff --git a/addons/mail/static/src/xml/chatter.xml b/addons/mail/static/src/xml/chatter.xml index e6cd3995bc7..7a5959789cf 100644 --- a/addons/mail/static/src/xml/chatter.xml +++ b/addons/mail/static/src/xml/chatter.xml @@ -3,28 +3,30 @@ <!-- Chatter composer + + @param {mail.composer.Chatter} widget --> - <t t-name="mail.chatter.ChatComposer" t-extend="mail.ChatComposer"> + <t t-name="mail.chatter.Composer" t-extend="mail.Composer"> <!-- Insert information before the composer --> <t t-jquery=".o_composer_container" t-operation="before"> <!-- New message composer --> - <t t-if="!widget.options.is_log"> + <t t-if="!widget.options.isLog"> <!-- Text for followers --> <small class="o_chatter_composer_info"> <b class="text-muted">To: </b> <em class="text-muted">Followers of </em> <b> - <t t-if="widget.options.record_name"> -  "<t t-esc="widget.options.record_name"/>" + <t t-if="widget.options.recordName"> +  "<t t-esc="widget.options.recordName"/>" </t> - <t t-if="!widget.options.record_name"> + <t t-if="!widget.options.recordName"> this document </t> </b> </small> <!-- List of followers --> - <div class="o_composer_suggested_partners"> - <t t-foreach='widget.suggested_partners' t-as='recipient'> + <div class="o_composer_suggestedPartners"> + <t t-foreach='widget.suggestedPartners' t-as='recipient'> <div t-attf-title="Add as recipient and follower (reason: #{recipient.reason})"> <div class="o_checkbox"> <input type="checkbox" @@ -48,15 +50,19 @@ <!-- Chatter (mail_thread widget) buttons + + @param {boolean} [logNoteButton] + @param {boolean} [newMessageButton] + @param {boolean} [scheduleActivityButton] --> - <t t-name="mail.Chatter.Buttons"> - <button t-if="new_message_btn" type="button" class="btn btn-sm btn-link o_chatter_button_new_message" title="Send a message" disabled="disabled"> + <t t-name="mail.chatter.Buttons"> + <button t-if="newMessageButton" type="button" class="btn btn-sm btn-link o_chatter_button_new_message" title="Send a message" disabled="disabled"> Send message </button> - <button t-if="log_note_btn" class="btn btn-sm btn-link o_chatter_button_log_note" title="Log a note. Followers will not be notified." disabled="disabled"> + <button t-if="logNoteButton" class="btn btn-sm btn-link o_chatter_button_log_note" title="Log a note. Followers will not be notified." disabled="disabled"> Log note </button> - <button t-if="schedule_activity_btn" class="btn btn-sm btn-link o_chatter_button_schedule_activity" title="Log or schedule an activity" disabled="disabled"> + <button t-if="scheduleActivityButton" class="btn btn-sm btn-link o_chatter_button_schedule_activity" title="Log or schedule an activity" disabled="disabled"> <i class="fa fa-clock-o" role="img" aria-label="Dates" title="Dates"/> <t t-if="isMobile">Activity</t><t t-else="">Schedule activity</t> </button> </t> diff --git a/addons/mail/static/src/xml/composer.xml b/addons/mail/static/src/xml/composer.xml index 752b9febdf9..f7baafeb777 100644 --- a/addons/mail/static/src/xml/composer.xml +++ b/addons/mail/static/src/xml/composer.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> - <div t-name="mail.ChatComposer" t-attf-class="o_chat_composer #{widget.extended ? 'o_chat_composer_extended' : (widget.notInline ? '' : 'o_chat_inline_composer')} #{widget.isMini ? 'o_chat_mini_composer' : ''}"> + <div t-name="mail.Composer" t-attf-class="o_thread_composer #{widget.extended ? 'o_thread_composer_extended' : (widget.notInline ? '' : 'o_chat_inline_composer')} #{widget.isMini ? 'o_chat_mini_composer' : ''}"> <div class="o_composer_container"> <img t-if="widget.avatarURL" height="36" width="36" class="o_thread_message_sidebar o_chatter_avatar img-circle" t-att-src="widget.avatarURL" alt="Avatar"/> <div t-if="widget.extended" class="o_composer_subject"> @@ -20,11 +20,11 @@ <div class="o_composer_attachments_list"/> </div> <div class="o_composer_send"> - <button tabindex="3" class="btn btn-sm btn-primary o_composer_button_send hidden-xs" type="button"><t t-esc="widget.options.send_text"/></button> + <button tabindex="3" class="btn btn-sm btn-primary o_composer_button_send hidden-xs" type="button"><t t-esc="widget.options.sendText"/></button> </div> <span class="hide"> <t t-call="HiddenInputFile"> - <t t-set="fileupload_id" t-value="widget.fileupload_id"/> + <t t-set="fileupload_id" t-value="widget.fileuploadID"/> <t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t> <t t-set="multi_upload" t-value="true"/> <input type="hidden" name="model" value="mail.compose.message"/> @@ -34,15 +34,19 @@ </span> </div> - <div t-name="mail.ChatComposer.emojis" class="o_mail_emoji_container"> + <div t-name="mail.Composer.emojis" class="o_mail_emoji_container"> <t t-foreach="emojis" t-as="emoji"> - <button t-att-data-emoji="emoji.source" class="btn btn-link o_mail_emoji" t-att-title="emoji.description" t-att-aria-label="emoji.description"> - <t t-raw="emoji.unicode_source"/> + <button t-att-data-emoji="emoji.sources[0]" class="btn btn-link o_mail_emoji" t-att-title="emoji.description" t-att-aria-label="emoji.description"> + <t t-raw="emoji.unicode"/> </button> </t> </div> - <t t-name="mail.AbstractMentionSuggestions"> + <!-- + @param {Object[]} suggestions + @param {boolean} [suggestions.divider=false] + --> + <t t-name="mail.MentionSuggestions"> <ul class="dropdown-menu" role="menu"> <t t-foreach="suggestions" t-as="suggestion"> <li role="separator" t-if="suggestion.divider" class="divider"/> @@ -52,7 +56,7 @@ </t> </ul> </t> - <t t-name="mail.MentionPartnerSuggestions" t-extend="mail.AbstractMentionSuggestions"> + <t t-name="mail.MentionSuggestions.Partner" t-extend="mail.MentionSuggestions"> <t t-jquery=".o_mention_proposition a" t-operation="append"> <span class="o_mention_name"><t t-esc="suggestion.name"/></span> <t t-if="suggestion.email"> @@ -60,18 +64,18 @@ </t> </t> </t> - <t t-name="mail.MentionChannelSuggestions" t-extend="mail.AbstractMentionSuggestions"> + <t t-name="mail.MentionSuggestions.Channel" t-extend="mail.MentionSuggestions"> <t t-jquery=".o_mention_proposition a" t-operation="append"> <span class="o_mention_name"><t t-esc="suggestion.name"/></span> </t> </t> - <t t-name="mail.MentionCannedResponseSuggestions" t-extend="mail.AbstractMentionSuggestions"> + <t t-name="mail.MentionSuggestions.CannedResponse" t-extend="mail.MentionSuggestions"> <t t-jquery=".o_mention_proposition a" t-operation="append"> <span class="o_mention_name"><t t-esc="suggestion.source"/></span> <span class="o_mention_info"><t t-esc="suggestion.substitution"/></span> </t> </t> - <t t-name="mail.MentionCommandSuggestions" t-extend="mail.AbstractMentionSuggestions"> + <t t-name="mail.MentionSuggestions.Command" t-extend="mail.MentionSuggestions"> <t t-jquery=".o_mention_proposition a" t-operation="append"> <span class="o_mention_name">/<t t-esc="suggestion.name"/></span> <span class="o_mention_info"><t t-esc="suggestion.help"/></span> diff --git a/addons/mail/static/src/xml/discuss.xml b/addons/mail/static/src/xml/discuss.xml index 7901a870ea3..493d3c918aa 100644 --- a/addons/mail/static/src/xml/discuss.xml +++ b/addons/mail/static/src/xml/discuss.xml @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> - <!-- Discuss --> + <!-- + Discuss Main template + + @param {mail.discuss} widget + --> <t t-name="mail.discuss"> - <div class="o_mail_chat"> - <div class="o_mail_chat_sidebar"/> - <div class="o_mail_chat_content"> - <t t-if="widget.notification_bar"> + <div class="o_mail_discuss"> + <div class="o_mail_discuss_sidebar"/> + <div class="o_mail_discuss_content"> + <t t-if="widget.displayNotificationBar"> <div class="o_mail_annoying_notification_bar"> <span class="o_mail_request_permission">Odoo needs your permission to <a href="#"> enable desktop notifications</a>.</span> <span class="fa fa-close" role="button" aria-label="Close" title="Close"></span> @@ -15,145 +19,184 @@ </div> </div> </t> - <t t-name="mail.chat.Sidebar"> - <div class="o_mail_chat_sidebar"> - <div t-attf-class="o_mail_chat_title_main o_mail_chat_channel_item #{(active_channel_id === 'channel_inbox') ? 'o_active': ''}" - data-channel-id="channel_inbox"> - <span class="o_channel_name fa fa-inbox mr8">Inbox</span> - <t t-set="counter" t-value="needaction_counter"/> - <t t-call="mail.chat.SidebarNeedaction"/> + + <!-- + @param {string|integer} activeThreadID + @param {integer} moderationCounter + @param {integer} needactionCounter + @param {integer} starredCounter + --> + <t t-name="mail.discuss.Sidebar"> + <div class="o_mail_discuss_sidebar"> + <div t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID === 'mailbox_inbox') ? 'o_active': ''}" + data-thread-id="mailbox_inbox"> + <span class="o_thread_name fa fa-inbox mr8">Inbox</span> + <t t-set="counter" t-value="needactionCounter"/> + <t t-call="mail.discuss.SidebarCounter"/> </div> - <div t-attf-class="o_mail_chat_title_main o_mail_chat_title_starred o_mail_chat_channel_item #{(active_channel_id === 'channel_starred') ? 'o_active': ''}" - data-channel-id="channel_starred"> - <span class="o_channel_name fa fa-star-o mr8">Starred</span> - <t t-set="counter" t-value="starred_counter"/> - <t t-call="mail.chat.SidebarNeedaction"/> + <div t-attf-class="o_mail_discuss_title_main o_mail_mailbox_title_starred o_mail_discuss_item #{(activeThreadID === 'mailbox_starred') ? 'o_active': ''}" + data-thread-id="mailbox_starred"> + <span class="o_thread_name fa fa-star-o mr8">Starred</span> + <t t-set="counter" t-value="starredCounter"/> + <t t-call="mail.discuss.SidebarCounter"/> </div> - <div t-if="isModerator" t-attf-class="o_mail_chat_title_main o_mail_chat_channel_item #{(active_channel_id == 'channel_moderation') ? 'o_active': ''}" - data-channel-id="channel_moderation"> - <span class="o_channel_name"> <i class="fa fa-envelope mr8"/>Moderation Queue</span> + <div t-if="isModerator" t-attf-class="o_mail_discuss_title_main o_mail_discuss_item #{(activeThreadID == 'mailbox_moderation') ? 'o_active': ''}" + data-thread-id="mailbox_moderation"> + <span class="o_thread_name"> <i class="fa fa-envelope mr8"/>Moderation Queue</span> <t t-set="counter" t-value="moderationCounter"/> - <t t-call="mail.chat.SidebarNeedaction"/> + <t t-call="mail.discuss.SidebarCounter"/> </div> <hr class="mb8"/> - <t t-set="channel_type" t-value="'public'"/> - <t t-call="mail.chat.SidebarTitle"> - <t t-set="channel_title">Channels</t> - <t t-set="channel_icon" t-value="fa-users"/> + <t t-set="type" t-value="'public'"/> + <t t-call="mail.discuss.SidebarTitle"> + <t t-set="title">Channels</t> + <t t-set="icon" t-value="fa-users"/> </t> - <t t-call="mail.chat.SidebarItems"> - <t t-set="display_hash" t-value="true"/> - <t t-set="input_placeholder">Add a channel</t> + <t t-call="mail.discuss.SidebarItems"> + <t t-set="displayHash" t-value="true"/> + <t t-set="inputPlaceholder">Add a channel</t> </t> - <t t-set="channel_type" t-value="'dm'"/> - <t t-call="mail.chat.SidebarTitle"> - <t t-set="channel_title">Direct Messages</t> - <t t-set="channel_icon" t-value="fa-user"/> + <t t-set="type" t-value="'dm'"/> + <t t-call="mail.discuss.SidebarTitle"> + <t t-set="title">Direct Messages</t> + <t t-set="icon" t-value="fa-user"/> </t> - <t t-call="mail.chat.SidebarItems"> - <t t-set="display_status" t-value="true"/> - <t t-set="input_placeholder">User name</t> + <t t-call="mail.discuss.SidebarItems"> + <t t-set="displayStatus" t-value="true"/> + <t t-set="inputPlaceholder">User name</t> </t> - <t t-set="channel_type" t-value="'private'"/> - <t t-call="mail.chat.SidebarTitle"> - <t t-set="channel_title">Private Channels</t> - <t t-set="channel_icon" t-value="fa-eye-slash"/> + <t t-set="type" t-value="'private'"/> + <t t-call="mail.discuss.SidebarTitle"> + <t t-set="title">Private Channels</t> + <t t-set="icon" t-value="fa-eye-slash"/> </t> - <t t-call="mail.chat.SidebarItems"> - <t t-set="input_placeholder">Add a private channel</t> - <t t-set="display_hash" t-value="true"/> + <t t-call="mail.discuss.SidebarItems"> + <t t-set="inputPlaceholder">Add a private channel</t> + <t t-set="displayHash" t-value="true"/> </t> </div> </t> - <t t-name="mail.chat.SidebarNeedaction"> + + <!-- + @param {integer} counter + --> + <t t-name="mail.discuss.SidebarCounter"> <span t-attf-class="o_mail_sidebar_needaction badge #{(!counter ? 'hide' : '')}"> <t t-esc="counter"/> </span> </t> - <t t-name="mail.chat.SidebarTitle"> - <!-- Don't display the channel type if there is no channel of such type - and the 'add a channel' option is disabled for this type --> - <t t-if="disable_add_channel"> + + <!-- + Don't display the thread type if there is no thread of such type + and the 'add a channel' option is disabled for this type + + @param {mail.model.Thread[]} threads + @param {boolean} [disableAddThread] whether the 'Add thread' action should disabled or not for the considered discuss item category + @param {string} [icon] classname to add on title to display icon + @param {string} title the displayed type of the discuss items (e.g. "Private Channels") + @param {string} type the type of the thread + --> + <t t-name="mail.discuss.SidebarTitle"> + <t t-if="disableAddThread"> <t t-set="empty" t-value="true"/> - <t t-foreach="channels" t-as="channel"> - <t t-if="channel.type === channel_type"> + <t t-foreach="threads" t-as="thread"> + <t t-if="thread.getType() === type"> <t t-set="empty" t-value="false"/> </t> </t> </t> - <t t-if="!disable_add_channel || !empty"> + <t t-if="!disableAddThread || !empty"> <div class="o_mail_sidebar_title"> - <h4 t-att-class="channel_type == 'public' ? 'o_mail_open_channels' : ''"> - <i t-if="channel_icon" t-attf-class="mr4 fa-fw fa #{channel_icon}" role="img" aria-label="Channel" title="Channel"></i> - <b><t t-esc="channel_title"/></b> + <h4 t-att-class="type == 'public' ? 'o_mail_open_channels' : ''"> + <i t-if="icon" t-attf-class="mr4 fa-fw fa #{icon}" role="img" aria-label="Channel" title="Channel"></i> + <b><t t-esc="title"/></b> </h4> - <span t-if="!disable_add_channel" class="fa fa-plus o_add" title="Add" t-attf-data-type="#{channel_type}" role="img" aria-label="Add"/> + <span t-if="!disableAddThread" class="fa fa-plus o_add" title="Add" t-attf-data-type="#{type}" role="img" aria-label="Add"/> </div> </t> </t> - <t t-name="mail.chat.SidebarItems"> - <div t-if="!disable_add_channel" class="o_mail_add_channel" t-attf-data-type="#{channel_type}"> - <span t-if="display_hash">#</span> - <input type="text" class="o_input" t-attf-placeholder="#{input_placeholder}"/> + + <!-- + @param {string|integer} activeThreadID + @param {mail.model.Thread[]} threads + @param {boolean} disableAddThread whether the 'Add thread' action should be disable or not for the considered discuss item category + @param {boolean} [displayHash] whether the hash ('#') character should be displayed as a prefix for item's name + @param {boolean} [displayStatus] whether the item should display its status (should be the case only for chats) + @param {string} inputPlaceholder text placeholder on showing input in a sidebar category + --> + <t t-name="mail.discuss.SidebarItems"> + <div t-if="!disableAddThread" class="o_mail_add_thread" t-attf-data-type="#{type}"> + <span t-if="displayHash">#</span> + <input type="text" class="o_input" t-attf-placeholder="#{inputPlaceholder}"/> </div> - <t t-foreach="channels" t-as="channel"> - <t t-set="counter" t-value="channel.is_chat ? channel.unread_counter : channel.needaction_counter"/> - <div t-if="channel.type === channel_type" t-att-data-channel-id="channel.id" t-att-title="channel.name" - t-attf-class="o_mail_chat_channel_item #{channel.unread_counter ? ' o_unread_message' : ''} #{(active_channel_id == channel.id) ? 'o_active': ''}"> - <span class="o_channel_name"> - <span t-if="display_status"> - <t t-call="mail.chat.UserStatus"> - <t t-set="status" t-value="channel.status"/> + <t t-foreach="threads" t-as="thread"> + <t t-set="counter" t-value="thread.getType() === 'mailbox' ? thread.getMailboxCounter() : thread.isChat() ? thread.getUnreadCounter() : thread.getNeedactionCounter()"/> + <div t-if="thread.getType() === type" t-att-data-thread-id="thread.getID()" t-att-title="thread.getName()" + t-attf-class="o_mail_discuss_item #{thread.hasUnreadMessages() ? ' o_unread_message' : ''} #{(activeThreadID == thread.getID()) ? 'o_active': ''}"> + <span class="o_thread_name"> + <span t-if="displayStatus"> + <t t-call="mail.UserStatus"> + <t t-set="status" t-value="thread.getStatus()"/> </t> </span> - <span t-if="display_hash" class="o_mail_hash">#</span> - <t t-esc="channel.name"/> - <i t-if="channel.mass_mailing" class="fa fa-envelope-o" title="Sends messages by email" role="img" aria-label="Send by mail"/> + <span t-if="displayHash" class="o_mail_hash">#</span> + <t t-esc="thread.getName()"/> + <i t-if="thread.isMassMailing()" class="fa fa-envelope-o" title="Sends messages by email" role="img" aria-label="Send by mail"/> </span> <span t-attf-class="fa fa-cog o_mail_channel_settings" - title="Channel settings" role="img" aria-label="Channel settings" t-att-data-channel-id="channel.id"/> - <t t-call="mail.chat.SidebarNeedaction"/> - <span t-if="! channel.group_based_subscription" t-attf-class="fa fa-times o_mail_partner_unpin #{counter ? 'hide' : ''}" - title="Leave this channel" role="img" aria-label="Leave this channel" t-att-data-channel-id="channel.id"/> + title="Channel settings" role="img" aria-label="Channel settings" t-att-data-thread-id="thread.getID()"/> + <t t-call="mail.discuss.SidebarCounter"/> + <span t-if="!thread.isGroupBasedSubscription()" t-attf-class="fa fa-times o_mail_partner_unpin #{counter ? 'hide' : ''}" + title="Leave this channel" role="img" aria-label="Leave this channel" t-att-data-thread-id="thread.getID()"/> </div> </t> </t> - <t t-name="mail.chat.UserStatus"> + <!-- + @param {string} status + --> + <t t-name="mail.UserStatus"> <i t-if="status == 'online'" class="o_mail_user_status o_user_online fa fa-circle" title="Online" role="img" aria-label="User is online"/> <i t-if="status == 'away'" class="o_mail_user_status o_user_idle fa fa-circle" title="Idle" role="img" aria-label="User is idle"/> <i t-if="status == 'offline'" class="o_mail_user_status fa fa-circle-o" title="Offline" role="img" aria-label="User is offline"/> </t> - <t t-name="mail.chat.MessageSentSnackbar"> + <!-- + @param {string} documentName + --> + <t t-name="mail.discuss.MessageSentSnackbar"> <div class="alert o_mail_snackbar" data-dismiss="alert" role="alert"> - Message sent in "<t t-esc="record_name"/>". + Message sent in "<t t-esc="documentName"/>". </div> </t> - <!-- Buttons of the Control Panel --> - <t t-name="mail.chat.ControlButtons"> + <!-- + Buttons of the Control Panel + + @param {boolean} [disableAddThread] whether the 'Add channel' action should be disabled or not for the considered discuss item category + --> + <t t-name="mail.discuss.ControlButtons"> <div> - <button type="button" class="btn btn-primary btn-sm o_mail_chat_button_invite hidden-xs" title="Invite people">Invite</button> - <button type="button" class="btn btn-default btn-sm o_mail_chat_button_mark_read hidden-xs" title="Mark all as read">Mark all read</button> - <button type="button" class="btn btn-default btn-sm o_mail_chat_button_unstar_all" title="Unstar all messages">Unstar all</button> - <button type="button" class="btn btn-default btn-sm o_mail_chat_button_dm visible-xs" title="New Message">New Message</button> - <button t-if="!disable_add_channel" type="button" class="btn btn-default btn-sm o_mail_chat_button_public o_mail_chat_button_private visible-xs" title="New Channel">New Channel</button> - <button type="button" class="btn btn-default btn-sm o_hidden o_mail_chat_button_select_all" title="Select all messages to moderate">Select All</button> - <button type="button" class="btn btn-default btn-sm o_hidden o_mail_chat_button_unselect_all" title="Unselect all messages to moderate">Unselect All</button> - <button type="button" data-decision="accept" class="btn btn-default btn-sm o_mail_chat_button_moderate_all" title="Accept selected messages">Accept</button> - <button type="button" data-decision="reject" class="btn btn-default btn-sm o_mail_chat_button_moderate_all" title="Reject selected messages">Reject</button> - <button type="button" data-decision="discard" class="btn btn-default btn-sm o_mail_chat_button_moderate_all" title="Discard selected messages">Discard</button> + <button type="button" class="btn btn-primary btn-sm o_mail_discuss_button_invite hidden-xs" title="Invite people">Invite</button> + <button type="button" class="btn btn-default btn-sm o_mail_discuss_button_mark_all_read hidden-xs" title="Mark all as read">Mark all read</button> + <button type="button" class="btn btn-default btn-sm o_mail_discuss_button_unstar_all" title="Unstar all messages">Unstar all</button> + <button type="button" class="btn btn-default btn-sm o_mail_discuss_button_dm visible-xs" title="New Message">New Message</button> + <button t-if="!disableAddThread" type="button" class="btn btn-default btn-sm o_mail_discuss_button_public o_mail_discuss_button_private visible-xs" title="New Channel">New Channel</button> + <button type="button" class="btn btn-default btn-sm o_hidden o_mail_discuss_button_select_all" title="Select all messages to moderate">Select All</button> + <button type="button" class="btn btn-default btn-sm o_hidden o_mail_discuss_button_unselect_all" title="Unselect all messages to moderate">Unselect All</button> + <button type="button" data-decision="accept" class="btn btn-default btn-sm o_mail_discuss_button_moderate_all" title="Accept selected messages">Accept</button> + <button type="button" data-decision="reject" class="btn btn-default btn-sm o_mail_discuss_button_moderate_all" title="Reject selected messages">Reject</button> + <button type="button" data-decision="discard" class="btn btn-default btn-sm o_mail_discuss_button_moderate_all" title="Discard selected messages">Discard</button> </div> </t> <!-- Invite dialog's content --> <div t-name="mail.PartnerInviteDialog"> - <input type="text" class="o_input o_mail_chat_partner_invite_input" id="mail_search_partners"/> + <input type="text" class="o_input o_mail_discuss_partner_invite_input" id="mail_search_partners"/> </div> <div t-name="mail.ModeratorRejectMessageDialog"> @@ -163,17 +206,18 @@ <!-- Mobile templates --> <t t-name="mail.discuss_mobile"> - <div class="o_mail_chat"> - <div class="o_mail_chat_mobile_inbox_buttons"> - <button type="button" class="btn btn-primary btn-sm visible-xs-inline o_channel_inbox_item" title="Inbox" data-type="channel_inbox"> + <div class="o_mail_discuss"> + <div class="o_mail_discuss_mobile_mailboxes_buttons"> + <button type="button" class="btn btn-primary btn-sm visible-xs-inline o_mailbox_inbox_item" title="Inbox" data-type="mailbox_inbox"> Inbox - </button><button type="button" class="btn btn-default btn-sm visible-xs-inline o_channel_inbox_item" title="Starred" data-type="channel_starred"> + </button> + <button type="button" class="btn btn-default btn-sm visible-xs-inline o_mailbox_inbox_item" title="Starred" data-type="mailbox_starred"> Starred </button> </div> - <div class="o_mail_chat_content"/> + <div class="o_mail_discuss_content"/> <div class="o_mail_mobile_tabs"> - <div class="o_mail_mobile_tab" data-type="channel_inbox"> + <div class="o_mail_mobile_tab" data-type="mailbox_inbox"> <span class="o_tab_title fa fa-inbox">Inbox</span> </div> <div class="o_mail_mobile_tab" data-type="dm"> @@ -189,51 +233,71 @@ </div> </t> - <t t-name="mail.chat.MobileTabPane"> - <div class="o_mail_chat_tab_pane" t-att-data-type="type"> - <div t-if="!disable_add_channel" class="o_mail_add_channel" t-att-data-type="type"> + <!-- + @param {boolean} [disableAddThread] whether the 'Add thread' action should be disabled or not for the considered discuss item category + @param {Object[]} previews valid objects for preview rendering (see mail.Preview template) + --> + <t t-name="mail.discuss.MobileTabPane"> + <div class="o_mail_discuss_tab_pane" t-att-data-type="type"> + <div t-if="!disableAddThread" class="o_mail_add_thread" t-att-data-type="type"> <span t-if="type == 'private' || type == 'public'">#</span> - <t t-set="input_placeholder" t-if="type == 'private' || type == 'public'">Add a channel</t> - <t t-set="input_placeholder" t-if="type == 'dm'">Open chat</t> - <input type="text" t-attf-placeholder="#{input_placeholder}"/> + <t t-set="inputPlaceholder" t-if="type == 'private' || type == 'public'">Add a channel</t> + <t t-set="inputPlaceholder" t-if="type == 'dm'">Open chat</t> + <input type="text" t-attf-placeholder="#{inputPlaceholder}"/> </div> - <t t-foreach="channels" t-as="channel"> - <t t-call="mail.chat.ChannelPreview"/> + <t t-foreach="previews" t-as="preview"> + <t t-call="mail.Preview"/> </t> </div> </t> - <!-- The ChannelPreview template is used by the discuss in mobile, and by the systray menu --> - <t t-name="mail.chat.ChannelPreview"> - <div t-attf-class="o_mail_channel_preview #{channel.unread_counter ? 'o_channel_unread' : ''}" - t-att-data-channel_id="channel.id" t-att-data-res_id="channel.res_id" t-att-data-res_model="channel.model"> - <div t-attf-class="o_mail_channel_image #{channel.model? 'o_mail_channel_app' : ''}"> - <img class="o_mail_channel_image" t-att-src="channel.image_src" alt="Channel"/> - <i t-if="channel.status === 'online'" class="o_mail_user_status o_user_online fa fa-circle" title="Online" role="img" aria-label="User is online"/> - <i t-if="channel.status === 'away'" class="o_mail_user_status o_user_idle fa fa-circle" title="Idle" role="img" aria-label="User is idle"/> + <!-- + The preview template is used by the discuss in mobile, and by the systray menu + In order to show preview of threads + + FIXME: it is shared with systray, so it should not be in discuss.xml + + @param {Object} preview a valid object for preview rendering + @param {integer} [preview.documentID] + @param {string} [preview.documentModel] + @param {string|integer} preview.id + @param {string} [preview.imageSRC] + @param {boolean} [preview.isAuthor] + @param {moment} [preview.date] + @param {string} [preview.author] + @param {string} [preview.body] + @param {string} [preview.title] + @param {string} [preview.status] + @param {integer} [preview.unreadCounter] + --> + <t t-name="mail.Preview"> + <div t-attf-class="o_mail_preview #{preview.unreadCounter > 0 ? 'o_preview_unread' : ''}" + t-att-data-preview-id="preview.id" t-att-data-document-id="preview.documentID" t-att-data-document-model="preview.documentModel"> + <div t-attf-class="o_mail_preview_image #{preview.documentModel ? 'o_mail_preview_app' : ''}"> + <img class="o_mail_preview_image" t-att-src="preview.imageSRC" alt="Channel"/> + <i t-if="preview.status === 'online'" class="o_mail_user_status o_user_online fa fa-circle" title="Online" role="img" aria-label="User is online"/> + <i t-if="preview.status === 'away'" class="o_mail_user_status o_user_idle fa fa-circle" title="Idle" role="img" aria-label="User is idle"/> </div> - <div class="o_channel_info"> - <div class="o_channel_title"> - <span class="o_channel_name"> - <t t-esc="channel.name"/> + <div class="o_preview_info"> + <div class="o_preview_title"> + <span class="o_preview_name"> + <t t-esc="preview.title"/> </span> - <span class="o_channel_counter"> - <t t-if="channel.unread_counter">&nbsp;(<t t-esc="channel.unread_counter"/>)</t> + <span class="o_preview_counter"> + <t t-if="preview.unreadCounter > 0">&nbsp;(<t t-esc="preview.unreadCounter"/>)</t> </span> - <span class="o_last_message_date"> <t t-esc="channel.last_message_date"/> </span> + <span class="o_last_message_date"> <t t-esc="preview.date ? preview.date.fromNow() : ''"/> </span> </div> - <div t-if="channel.last_message" class="o_last_message_preview"> - <t t-if="channel.last_message.is_author"> + <div t-if="preview.body" class="o_last_message_preview"> + <t t-if="preview.isAuthor"> <span class="fa fa-mail-reply" role="img" aria-label="Reply"/> You: </t> - <t t-else=""> - <t t-if="channel.last_message.displayed_author"> - <t t-esc="channel.last_message.displayed_author"/>: - </t> + <t t-elif="preview.author"> + <t t-esc="preview.author"/>: </t> - <t t-raw="channel.last_message_preview"/> + <t t-raw="preview.body"/> </div> - <span t-att-data-channel_id="channel.id" t-att-data-res_id="channel.res_id" t-att-data-model="channel.model" title="Mark as Read" class="o_discuss_icon o_mail_channel_mark_read fa fa-check" t-if="channel.unread_counter"/> + <span title="Mark as Read" class="o_discuss_icon o_mail_preview_mark_as_read fa fa-check" t-if="preview.unreadCounter"/> </div> </div> </t> diff --git a/addons/mail/static/src/xml/extended_chat_window.xml b/addons/mail/static/src/xml/extended_chat_window.xml deleted file mode 100644 index c5188feadfa..00000000000 --- a/addons/mail/static/src/xml/extended_chat_window.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<templates xml:space="preserve"> - - <t t-name="mail.ExtendedChatWindow" t-extend="mail.ChatWindow"> - <t t-jquery=".o_chat_header" t-operation="after"> - <div t-if="widget.options.thread_less" class="o_chat_search_input"> - <span> To: </span> - <input type="text" placeholder="User name"/> - </div> - </t> - </t> - - <t t-extend="mail.ChatWindowHeaderContent"> - <t t-jquery=".o_chat_window_buttons" t-operation="prepend"> - <a href="#" class="o_chat_window_expand fa fa-expand" title="Open in Discuss" aria-label="Open in discuss"/> - </t> - </t> - -</templates> diff --git a/addons/mail/static/src/xml/systray.xml b/addons/mail/static/src/xml/systray.xml index 6350e07d2da..ae1d7f2879f 100644 --- a/addons/mail/static/src/xml/systray.xml +++ b/addons/mail/static/src/xml/systray.xml @@ -1,18 +1,21 @@ <?xml version="1.0" encoding="UTF-8"?> <templates> - <t t-name="mail.chat.MessagingMenu"> - <li class="o_mail_navbar_item"> + <!-- + @param {mail.systray.MessagingMenu} widget + --> + <t t-name="mail.systray.MessagingMenu"> + <li class="o_mail_systray_item"> <a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="Conversations" href="#"> <i class="fa fa-comments" role="img" aria-label="Messages"/> <span class="o_notification_counter badge"/> </a> - <ul class="o_mail_navbar_dropdown dropdown-menu" role="menu"> - <li role="menuitem" t-if="widget.isMobile"> - <div class="o_mail_navbar_mobile_header"> + <ul class="o_mail_systray_dropdown dropdown-menu" role="menu"> + <li role="menuitem" t-if="widget.isMobile()"> + <div class="o_mail_systray_mobile_header"> <button type="button" class="btn btn-default btn-sm o_new_message"> New message </button> </div> </li> - <li role="menuitem" t-if="!widget.isMobile" class="o_mail_navbar_dropdown_top"> + <li role="menuitem" t-if="!widget.isMobile()" class="o_mail_systray_dropdown_top"> <div> <button type="button" class="btn btn-sm btn-link o_filter_button active"> All </button> <button type="button" class="btn btn-sm btn-link o_filter_button" data-filter='chat'> Chat </button> @@ -20,8 +23,8 @@ </div> <button type="button" class="btn btn-sm btn-link o_new_message"> New message </button> </li> - <li role="menuitem" class="o_mail_navbar_dropdown_channels"/> - <li role="menuitem" t-if="widget.isMobile"> + <li role="menuitem" class="o_mail_systray_dropdown_items"/> + <li role="menuitem" t-if="widget.isMobile()"> <div class="o_mail_mobile_tabs"> <div class="o_mail_mobile_tab o_filter_button active"> <span class="o_tab_title fa fa-envelope">All</span> @@ -38,31 +41,38 @@ </li> </t> - <t t-name="mail.chat.ChannelsPreview"> - <t t-if="_.isEmpty(channels)"> + <!-- + @param {Object} previews valid list of objects for preview rendering + (see mail.Preview template) + --> + <t t-name="mail.systray.MessagingMenu.Previews"> + <t t-if="_.isEmpty(previews)"> <li class="text-center o_no_activity mt16"> - <span>No discussion yet...</span> + <span>No conversation yet...</span> </li> </t> - <t t-foreach="channels" t-as="channel"> - <t t-call="mail.chat.ChannelPreview"/> + <t t-foreach="previews" t-as="preview"> + <t t-call="mail.Preview"/> </t> </t> - <t t-name="mail.chat.ActivityMenuPreview"> + <!-- + @param {Object[]} activities + --> + <t t-name="mail.systray.ActivityMenu.Previews"> <t t-if="_.isEmpty(activities)"> <li class="text-center o_no_activity"> <span>No activities planned.</span> </li> </t> <t t-foreach="activities" t-as="activity"> - <div class="o_mail_channel_preview" t-att-data-res_model="activity.model" t-att-data-model_name="activity.name" data-filter='my'> - <div class="o_mail_channel_image o_mail_channel_app"> + <div class="o_mail_preview" t-att-data-res_model="activity.model" t-att-data-model_name="activity.name" data-filter='my'> + <div class="o_mail_preview_image o_mail_preview_app"> <img t-att-src="activity.icon" alt="Activity"/> </div> - <div class="o_channel_info"> - <div class="o_channel_title"> - <span class="o_channel_name"> + <div class="o_preview_info"> + <div class="o_preview_title"> + <span class="o_preview_name"> <t t-esc="activity.name"/> </span> </div> @@ -79,13 +89,13 @@ </t> </t> - <t t-name="mail.chat.ActivityMenu"> - <li class="o_mail_navbar_item"> + <t t-name="mail.systray.ActivityMenu"> + <li class="o_mail_systray_item"> <a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="Activities" href="#"> <i class="fa fa-clock-o" role="img" aria-label="Activities"/> <span class="o_notification_counter badge"/> </a> - <ul class="o_mail_navbar_dropdown dropdown-menu" role="menu"> - <li role="menuitem" class="o_mail_navbar_dropdown_channels"/> + <ul class="o_mail_systray_dropdown dropdown-menu" role="menu"> + <li role="menuitem" class="o_mail_systray_dropdown_items"/> </ul> </li> </t> diff --git a/addons/mail/static/src/xml/thread.xml b/addons/mail/static/src/xml/thread.xml index 60779ea6563..ac544d3b7bb 100644 --- a/addons/mail/static/src/xml/thread.xml +++ b/addons/mail/static/src/xml/thread.xml @@ -1,18 +1,21 @@ <?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> - <t t-name="mail.ChatThread"> + <!-- + @param {mail.model.AbstractMessage[]} messages + --> + <t t-name="mail.widget.Thread"> <t t-if="messages.length"> - <t t-if="options.display_load_more and options.display_order == ORDER.ASC" - t-call="mail.ChatThread.LoadMore"/> - <t t-call="mail.ChatThread.Content"/> - <t t-if="options.display_load_more and options.display_order == ORDER.DESC" - t-call="mail.ChatThread.LoadMore"/> + <t t-if="options.displayLoadMore and options.displayOrder == ORDER.ASC" + t-call="mail.widget.Thread.LoadMore"/> + <t t-call="mail.widget.Thread.Content"/> + <t t-if="options.displayLoadMore and options.displayOrder == ORDER.DESC" + t-call="mail.widget.Thread.LoadMore"/> </t> - <t t-if="options.display_empty_channel"> + <t t-if="options.displayEmptyChannel"> <t t-call="mail.EmptyChannel" /> </t> - <div t-if="options.display_no_match" class="o_mail_no_content"> + <div t-if="options.displayNoMatchFound" class="o_mail_no_content"> <div class="o_thread_title">No matches found</div> <div>No message matches your search. Try to change your search filters.</div> </div> @@ -64,7 +67,7 @@ <div class="modal o_modal_fullscreen" tabindex="-1" data-keyboard="false" role="dialog"> <t t-call="DocumentViewer.Content"/> - <t t-if="widget.attachment.length != 1"> + <t t-if="widget.attachment.length !== 1"> <a class="arrow arrow-left move_previous" href="#"> <span class="fa fa-chevron-left" role="img" aria-label="Previous" title="Previous"/> </a> @@ -93,149 +96,159 @@ </html> </t> - <!-- When a channel is empty--> + <!-- + When a channel is empty + + @param {mail.widget.Thread} widget + --> <div t-name="mail.EmptyChannel" class="o_mail_no_content"> - <t t-if="options.channel_id==='channel_inbox'"> + <t t-if="options.threadID === 'mailbox_inbox'"> <div class="o_thread_title">Congratulations, your inbox is empty</div> <div>New messages appear here.</div> </t> - <t t-if="options.channel_id==='channel_starred'"> + <t t-if="options.threadID === 'mailbox_starred'"> <div class="o_thread_title">No starred message</div> - <div>You can mark any message as 'starred', and it shows up in this channel.</div> + <div>You can mark any message as 'starred', and it shows up in this mailbox.</div> </t> - <t t-if="options.channel_id==='channel_moderation'"> + <t t-if="options.threadID === 'mailbox_moderation'"> <div class="o_thread_title">You have no message to moderate</div> <div>Pending moderation messages appear here.</div> </t> </div> - <t t-name="mail.ChatThread.Content"> - <t t-if="options.messages_separator_position == 'top'" t-call="mail.MessagesSeparator"/> + <!-- + @param {mail.model.AbstractMessage[]} messages + --> + <t t-name="mail.widget.Thread.Content"> + <t t-if="options.messagesSeparatorPosition == 'top'" t-call="mail.MessagesSeparator"/> <t t-set="current_day" t-value="0"/> <t t-foreach="messages" t-as="message"> - <div t-if="current_day != message.day" class="o_thread_date_separator"> + <div t-if="current_day !== message.getDateDay()" class="o_thread_date_separator"> <span class="o_thread_date"> - <t t-esc="message.day"/> + <t t-esc="message.getDateDay()"/> </span> - <t t-set="current_day" t-value="message.day"/> + <t t-set="current_day" t-value="message.getDateDay()"/> </div> - <t t-call="mail.ChatThread.Message"/> + <t t-call="mail.widget.Thread.Message"/> </t> </t> - <t t-name="mail.ChatThread.Message"> - <div t-att-class="'o_thread_message' + (message.expanded ? ' o_message_expanded ' : ' ') + (message.is_selected ? 'o_thread_selected_message' : '') + (message.is_discussion or message.is_notification ? ' o_mail_discussion' : ' o_mail_not_discussion')" t-att-data-message-id="message.id"> - <div t-if="options.display_avatar" class="o_thread_message_sidebar"> - <t t-if="!message.mailto and message.author_id[0]"> - <img t-if="message.avatar_src and message.display_author" + <!-- + @param {mail.model.AbstractMessage} message + --> + <t t-name="mail.widget.Thread.Message"> + <div t-att-class="'o_thread_message' + (message.getID() === options.selectedMessageID ? 'o_thread_selected_message ' : ' ') + (message.isDiscussion() or message.isNotification() ? ' o_mail_discussion' : ' o_mail_not_discussion')" t-att-data-message-id="message.getID()"> + <div t-if="options.displayAvatars" class="o_thread_message_sidebar"> + <t t-if="message.hasAuthor()"> + <img t-if="message.shouldDisplayAuthor()" alt="" - t-att-src="message.avatar_src" + t-att-src="message.getAvatarSource()" data-oe-model="res.partner" - t-att-data-oe-id="message.author_redirect ? message.author_id[0] : ''" - t-attf-class="o_thread_message_avatar img-circle #{message.author_redirect ? 'o_mail_redirect' : ''}"/> + t-att-data-oe-id="message.shouldRedirectToAuthor() ? message.getAuthorID() : ''" + t-attf-class="o_thread_message_avatar img-circle #{message.shouldRedirectToAuthor() ? 'o_mail_redirect' : ''}"/> </t> - <t t-if="message.mailto || !message.author_id[0]"> - <img t-if="message.avatar_src and message.display_author" + <t t-else=""> + <img t-if="message.shouldDisplayAuthor()" alt="" - t-att-src="message.avatar_src" + t-att-src="message.getAvatarSource()" class="o_thread_message_avatar img-circle"/> </t> - <span t-if="!message.display_author" t-att-title="message.date.format(date_format)" class="o_thread_message_side_date"> - <t t-esc="message.date.format('hh:mm')"/> + <span t-if="!message.shouldDisplayAuthor()" t-att-title="message.getDate().format(dateFormat)" class="o_thread_message_side_date"> + <t t-esc="message.getDate().format('hh:mm')"/> </span> - <i t-if="!message.display_author and options.display_stars and message.message_type != 'notification'" - t-att-class="'fa o_thread_message_star o_thread_icon ' + (message.is_starred ? 'fa-star' : 'fa-star-o')" - t-att-data-message-id="message.id" title="Mark as Todo" role="img" aria-label="Mark as todo"/> + <i t-if="!message.shouldDisplayAuthor() and options.displayStars and message.getType() !== 'notification'" + t-att-class="'fa o_thread_message_star o_thread_icon ' + (message.isStarred() ? 'fa-star' : 'fa-star-o')" + t-att-data-message-id="message.getID()" title="Mark as Todo" role="img" aria-label="Mark as todo"/> </div> <div class="o_thread_message_core"> - <p t-if="message.display_author" class="o_mail_info text-muted"> - <input t-if="message.needsModeration and message.isModerator" type="checkbox" class="moderation_checkbox" t-att-data-message-id="message.id"/> - <t t-if="message.is_note"> + <p t-if="message.shouldDisplayAuthor()" class="o_mail_info text-muted"> + <input t-if="message.needsModeration() and message.needsModerationByUser()" type="checkbox" class="moderation_checkbox" t-att-data-message-id="message.getID()"/> + <t t-if="message.isNote()"> Note by </t> - <strong t-if="message.mailto"> - <a class="text-muted" t-attf-href="mailto:#{message.mailto}?subject=Re: #{message.subject}"> - <t t-esc="message.mailto"/> - </a> + <strong t-if="message.hasAuthor()" + data-oe-model="res.partner" t-att-data-oe-id="message.shouldRedirectToAuthor() ? message.getAuthorID() : ''" + t-attf-class="o_thread_author #{message.shouldRedirectToAuthor() ? 'o_mail_redirect' : ''}"> + <t t-esc="message.getDisplayedAuthor()"/> </strong> - <strong t-if="!message.mailto and message.author_id[0]" - data-oe-model="res.partner" t-att-data-oe-id="message.author_redirect ? message.author_id[0] : ''" - t-attf-class="o_thread_author #{message.author_redirect ? 'o_mail_redirect' : ''}"> - <t t-esc="message.displayed_author"/> + <strong t-elif="message.hasEmailFrom()"> + <a class="text-muted" t-attf-href="mailto:#{message.getEmailFrom()}?subject=Re: #{message.hasSubject() ? message.getSubject() : ''}"> + <t t-esc="message.getEmailFrom()"/> + </a> </strong> - <strong t-if="!message.mailto and !message.author_id[0]" class="o_thread_author"> - <t t-esc="message.displayed_author"/> + <strong t-else="" class="o_thread_author"> + <t t-esc="message.getDisplayedAuthor()"/> </strong> - - <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small> - <t t-if="message.model and (message.model != 'mail.channel') and options.display_document_link"> - on <a t-att-href="message.url" t-att-data-oe-model="message.model" t-att-data-oe-id="message.res_id" class="o_document_link"><t t-esc="message.record_name"/></a> + - <small class="o_mail_timestamp" t-att-title="message.getDate().format(dateFormat)"><t t-esc="message.getTimeElapsed()"/></small> + <t t-if="message.isLinkedToDocumentThread() and options.displayDocumentLinks"> + on <a t-att-href="message.getURL()" t-att-data-oe-model="message.getDocumentModel()" t-att-data-oe-id="message.getDocumentID()" class="o_document_link"><t t-esc="message.getDocumentName()"/></a> </t> - <t t-if="message.origin_id and (message.origin_id !== options.channel_id)"> - (from <a t-att-data-oe-id="message.origin_id" href="#">#<t t-esc="message.origin_name"/></a>) + <t t-if="message.originatesFromChannel() and (message.getOriginChannelID() !== options.threadID)"> + (from <a t-att-data-oe-id="message.getOriginChannelID()" href="#">#<t t-esc="message.getOriginChannelName()"/></a>) </t> - <span t-if="options.display_email_icon and message.customer_email_data and message.customer_email_data.length" class="o_thread_tooltip_container"> + <span t-if="options.displayEmailIcons and message.hasCustomerEmailData()" class="o_thread_tooltip_container"> <t t-set="fatype" t-value="''"/> - <t t-if="message.customer_email_status === 'sent' or message.customer_email_status === 'ready'"> + <t t-if="message.getCustomerEmailStatus() === 'sent' or message.getCustomerEmailStatus() === 'ready'"> <t t-set="fatype" t-value="'-o'"/> </t> - <i t-att-class="'o_thread_tooltip o_thread_message_email o_thread_message_email_' + message.customer_email_status + ' fa fa-envelope'+ fatype" t-att-data-message-id="message.id"/> + <i t-att-class="'o_thread_tooltip o_thread_message_email o_thread_message_email_' + message.getCustomerEmailStatus() + ' fa fa-envelope-o'" t-att-data-message-id="message.getID()"/> <span class="o_thread_tooltip_content"> - <t t-foreach="message.customer_email_data" t-as="customer"> + <t t-foreach="message.getCustomerEmailData()" t-as="customerEmailData"> <span> - <t t-if="customer[2] === 'sent'"><i class='fa fa-check' title="Sent" role="img" aria-label="Sent"/></t> - <t t-if="customer[2] === 'bounce'"><i class='fa fa-exclamation' title="Bounced" role="img" aria-label="Bounced"/></t> - <t t-if="customer[2] === 'exception'"><i class='fa fa-exclamation' title="Error" role="img" aria-label="Error"/></t> - <t t-if="customer[2] === 'ready'"><i class='fa fa-send-o' title="Ready" role="img" aria-label="Ready"/></t> - <t t-if="customer[2] === 'canceled'"><i class='fa fa-trash-o' title="Canceled" role="img" aria-label="Canceled"/></t> - <t t-esc="customer[1]"/> + <t t-if="customerEmailData[2] === 'sent'"><i class='fa fa-check' title="Sent" role="img" aria-label="Sent"/></t> + <t t-if="customerEmailData[2] === 'bounce'"><i class='fa fa-exclamation' title="Bounced" role="img" aria-label="Bounced"/></t> + <t t-if="customerEmailData[2] === 'exception'"><i class='fa fa-exclamation' title="Error" role="img" aria-label="Error"/></t> + <t t-if="customerEmailData[2] === 'ready'"><i class='fa fa-send-o' title="Ready" role="img" aria-label="Ready"/></t> + <t t-if="customerEmailData[2] === 'canceled'"><i class='fa fa-trash-o' title="Canceled" role="img" aria-label="Canceled"/></t> + <t t-esc="customerEmailData[1]"/> </span> <br /> </t> </span> </span> <span t-attf-class="o_thread_icons"> - <i t-if="options.display_stars and !message.is_system_notification and !message.needsModeration" - t-att-class="'fa fa-lg o_thread_icon o_thread_message_star ' + (message.is_starred ? 'fa-star' : 'fa-star-o')" - t-att-data-message-id="message.id" title="Mark as Todo" role="img" aria-label="Mark as Todo"/> - <i t-if="message.record_name && message.model != 'mail.channel' && options.display_reply_icon" + <i t-if="options.displayStars and !message.isSystemNotification() and !message.needsModeration()" + t-att-class="'fa fa-lg o_thread_icon o_thread_message_star ' + (message.isStarred() ? 'fa-star' : 'fa-star-o')" + t-att-data-message-id="message.getID()" title="Mark as Todo" role="img" aria-label="Mark as Todo"/> + <i t-if="message.originatesFromChannel() and options.displayReplyIcons" class="fa fa-reply o_thread_icon o_thread_message_reply" - t-att-data-message-id="message.id" title="Reply" role="img" aria-label="Reply"/> - <i t-if="message.is_needaction && options.display_needactions" + t-att-data-message-id="message.getID()" title="Reply" role="img" aria-label="Reply"/> + <i t-if="message.isNeedaction() and options.displayMarkAsRead" class="fa fa-check o_thread_icon o_thread_message_needaction" - t-att-data-message-id="message.id" title="Mark as Read" role="img" aria-label="Mark as Read"/> + t-att-data-message-id="message.getID()" title="Mark as Read" role="img" aria-label="Mark as Read"/> - <t t-if="message.needsModeration and message.model === 'mail.channel'"> - <t t-if="message.isModerator"> + <t t-if="message.needsModeration() and message.originatesFromChannel()"> + <t t-if="message.needsModerationByUser()"> <t t-call="mail.MessageModerationDecision"/> </t> - <t t-elif="message.is_author"> + <t t-elif="message.isAuthor()"> <t t-call="mail.MessageModerationFeedback"/> </t> </t> </span> </p> <div class="o_thread_message_content"> - <t t-if="message.tracking_value_ids and message.tracking_value_ids.length > 0"> - <t t-if="message.subtype_description"> - <p><t t-esc="message.subtype_description"/></p> + <t t-if="message.hasTrackingValues()"> + <t t-if="message.hasSubtypeDescription()"> + <p><t t-esc="message.getSubtypeDescription()"/></p> </t> - <t t-call="mail.ChatThread.MessageTracking"/> + <t t-call="mail.widget.Thread.MessageTracking"/> </t> - <p t-if="options.display_subject and message.display_subject" class="o_mail_subject">Subject: <t t-esc="message.subject"/></p> - <t t-if="!(message.tracking_value_ids and message.tracking_value_ids.length > 0)"> - <t t-raw="message.body"/> + <p t-if="options.displaySubjectOnMessages and message.shouldDisplaySubject()" class="o_mail_subject">Subject: <t t-esc="message.getSubject()"/></p> + <t t-if="!(message.hasTrackingValues())"> + <t t-raw="message.getBody()"/> </t> - <t t-if="message.attachment_ids.length > 0"> - <div t-if="message.images.length > 0" class="o_attachments_previews"> - <t t-foreach="message.images" t-as="attachment"> + <t t-if="message.hasAttachments()"> + <div t-if="message.hasImageAttachments()" class="o_attachments_previews"> + <t t-foreach="message.getImageAttachments()" t-as="attachment"> <t t-call="mail.AttachmentPreview"/> </t> </div> - <div t-if="message.others.length > 0" class="o_attachments_list"> - <t t-foreach="message.others" t-as="attachment"> + <div t-if="message.hasNonImageAttachments()" class="o_attachments_list"> + <t t-foreach="message.getNonImageAttachments()" t-as="attachment"> <t t-call="mail.Attachment"/> </t> </div> @@ -243,47 +256,47 @@ </div> </div> </div> - <t t-if="options.messages_separator_position == message.id"> + <t t-if="options.messagesSeparatorPosition == message.getID()"> <t t-call="mail.MessagesSeparator"/> </t> </t> + <!-- + @param {mail.model.Message} message + --> <t t-name="mail.MessageModerationDecision"> <i class="o_thread_icon ml4 o_thread_message_moderation text-success" - t-att-data-message-id="message.id" data-decision="accept" title="Accept"><b>Accept |</b></i> + t-att-data-message-id="message.getID()" data-decision="accept" title="Accept"><b>Accept |</b></i> <i class="o_thread_icon ml4 o_thread_message_moderation text-danger" - t-att-data-message-id="message.id" data-decision="reject" title="Remove message with explanation"><b>Reject |</b></i> + t-att-data-message-id="message.getID()" data-decision="reject" title="Remove message with explanation"><b>Reject |</b></i> <i class="o_thread_icon ml4 o_thread_message_moderation text-danger" - t-att-data-message-id="message.id" data-decision="discard" title="Remove message without explanation"><b>Discard |</b></i> + t-att-data-message-id="message.getID()" data-decision="discard" title="Remove message without explanation"><b>Discard |</b></i> <i class="o_thread_icon ml4 o_thread_message_moderation text-success" - t-att-data-message-id="message.id" data-decision="allow" title="Add this email address to white list of people"><b>Always Allow |</b></i> + t-att-data-message-id="message.getID()" data-decision="allow" title="Add this email address to white list of people"><b>Always Allow |</b></i> <i class="o_thread_icon ml4 o_thread_message_moderation text-danger" - t-att-data-message-id="message.id" data-decision="ban" title="Ban this email address"><b>Ban</b></i> + t-att-data-message-id="message.getID()" data-decision="ban" title="Ban this email address"><b>Ban</b></i> </t> <t t-name="mail.MessageModerationFeedback"> <i class="text-danger" title="Your message is pending moderation"><b>Pending moderation</b></i> </t> - - - <t t-name="mail.MessagesSeparator"> <div class="o_thread_new_messages_separator"> <span class="o_thread_separator_label">New messages</span> </div> </t> - <t t-name="mail.ChatThread.MessageTracking"> + <t t-name="mail.widget.Thread.MessageTracking"> <ul class="o_mail_thread_message_tracking"> - <t t-foreach='message.tracking_value_ids' t-as='value'> + <t t-foreach='message.getTrackingValues()' t-as='value'> <li> <t t-esc="value.changed_field"/>: <t t-if="value.old_value"> <span> <t t-esc="value.old_value || ((value.field_type !== 'boolean') and '')"/> </span> - <span t-if="value.old_value != value.new_value" class="fa fa-long-arrow-right" role="img" aria-label="Changed" title="Changed"/> + <span t-if="value.old_value !== value.new_value" class="fa fa-long-arrow-right" role="img" aria-label="Changed" title="Changed"/> </t> - <span t-if="value.old_value != value.new_value"> + <span t-if="value.old_value !== value.new_value"> <t t-esc="value.new_value || ((value.field_type !== 'boolean') and '')"/> </span> </li> @@ -291,7 +304,7 @@ </ul> </t> - <t t-name="mail.ChatComposer.Attachments"> + <t t-name="mail.composer.Attachments"> <div t-if="attachments.length > 0" class="o_attachments o_attachments_list"> <t t-foreach="attachments" t-as='attachment'> <t t-call="mail.Attachment"> @@ -357,7 +370,7 @@ </div> </t> - <t t-name="mail.ChatThread.LoadMore"> + <t t-name="mail.widget.Thread.LoadMore"> <div class="o_thread_show_more"> <t t-if="options.loadMoreOnScroll"> <span><i class="fa fa-spinner fa-spin" role="img" aria-label="Please wait" title="Please wait"/> Loading older messages... </span> diff --git a/addons/mail/static/src/xml/thread_window.xml b/addons/mail/static/src/xml/thread_window.xml new file mode 100644 index 00000000000..5ed41cd66f8 --- /dev/null +++ b/addons/mail/static/src/xml/thread_window.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <!-- + @param {mail.ThreadWindow} widget + --> + <t t-name="mail.ThreadWindow" t-extend="mail.AbstractThreadWindow"> + <t t-jquery=".o_thread_window_header" t-operation="after"> + <div t-if="!widget.hasThread()" class="o_thread_search_input"> + <span> To: </span> + <input type="text" placeholder="User name"/> + </div> + </t> + </t> + + <!-- + @param {string} expandTitle text to display on mouse hover (title attribute) + --> + <t t-extend="mail.AbstractThreadWindowHeaderContent"> + <t t-jquery=".o_thread_window_buttons" t-operation="prepend"> + <a href="#" class="o_thread_window_expand fa fa-expand" t-att-title="expandTitle" t-att-aira-label="expandTitle"/> + </t> + </t> + +</templates> diff --git a/addons/mail/static/src/xml/web_kanban_activity.xml b/addons/mail/static/src/xml/web_kanban_activity.xml index b078e280722..4493e4b9903 100644 --- a/addons/mail/static/src/xml/web_kanban_activity.xml +++ b/addons/mail/static/src/xml/web_kanban_activity.xml @@ -3,8 +3,8 @@ <t t-name="mail.KanbanActivity"> <div class="o_kanban_inline_block dropdown o_kanban_selection o_mail_activity"> - <a role="button" class="dropdown-toggle o_activity_btn" data-toggle="dropdown"> - <span class="fa fa-clock-o fa-lg fa-fw" t-att-title="widget.selection[widget.activity_state]" role="img" t-att-aria-label="widget.selection[widget.activity_state]"/> + <a class="dropdown-toggle o_activity_btn" data-toggle="dropdown"> + <span class="fa fa-clock-o fa-lg fa-fw" t-att-title="widget.selection[widget.activityState]" role="img" t-att-aria-label="widget.selection[widget.activity_state]"/> </a> <ul class="dropdown-menu o_activity" role="menu"> </ul> |
