[FIX] mail: several discuss improvements (following MILK redesign)
closes odoo/odoo#121948 Forward-port-of: #121683 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com> Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
<t t-inherit="mail.ImStatus" t-inherit-mode="extension">
|
||||
<xpath expr="//*[@name='icon']" position="replace">
|
||||
<i t-if="props.persona.im_status === 'leave_online'" class="fa fa-plane text-success" title="Online" role="img" aria-label="User is online"/>
|
||||
<i t-if="props.persona.im_status === 'leave_away'" class="fa fa-plane text-warning" title="Idle" role="img" aria-label="User is idle"/>
|
||||
<i t-if="props.persona.im_status === 'leave_offline'" class="fa fa-plane text-700" title="Out of office" role="img" aria-label="User is out of office"/>
|
||||
<i t-elif="props.persona.im_status === 'leave_away'" class="fa fa-plane text-warning" title="Idle" role="img" aria-label="User is idle"/>
|
||||
<i t-elif="props.persona.im_status === 'leave_offline'" class="fa fa-plane text-700" title="Out of office" role="img" aria-label="User is out of office"/>
|
||||
<t t-else="">$0</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
<t t-name="im_livechat.Discuss" t-inherit="mail.Discuss" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//*[contains(@class, 'o-mail-Discuss-threadAvatar')]" position="attributes">
|
||||
<attribute name="t-if">thread and thread.model === 'discuss.channel' and thread.type !== 'livechat'</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -118,6 +118,6 @@ class BinaryController(http.Controller):
|
||||
raise NotFound()
|
||||
return (
|
||||
request.env["ir.binary"]
|
||||
._get_image_stream_from(attachment_sudo, width=width, height=height)
|
||||
._get_image_stream_from(attachment_sudo, width=int(width), height=int(height))
|
||||
.get_response(as_attachment=kwargs.get("download"))
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ class ChannelController(http.Controller):
|
||||
except UserError:
|
||||
raise NotFound()
|
||||
|
||||
@http.route("/discuss/channel/update_avatar", methods=["POST"], type="json", auth="public")
|
||||
@http.route("/discuss/channel/update_avatar", methods=["POST"], type="json")
|
||||
def discuss_channel_avatar_update(self, channel_id, data):
|
||||
channel = request.env["discuss.channel"].browse(int(channel_id)).exists()
|
||||
if not channel or not data:
|
||||
|
||||
@@ -130,6 +130,13 @@ msgstr ""
|
||||
msgid "%s and %s are typing..."
|
||||
msgstr ""
|
||||
|
||||
#. module: mail
|
||||
#. odoo-javascript
|
||||
#: code:addons/mail/static/src/core_ui/message_seen_indicator.js:0
|
||||
#, python-format
|
||||
msgid "Seen by everyone"
|
||||
msgstr ""
|
||||
|
||||
#. module: mail
|
||||
#. odoo-javascript
|
||||
#: code:addons/mail/static/src/core_ui/message_reactions.js:0
|
||||
|
||||
@@ -115,8 +115,6 @@ class Channel(models.Model):
|
||||
for channel in self:
|
||||
if channel.channel_type == 'channel':
|
||||
channel.is_editable = self.sudo(False).env.is_admin() or channel.create_uid.id == self.env.user.id
|
||||
elif channel.channel_type == 'chat':
|
||||
channel.is_editable = True
|
||||
elif channel.channel_type == 'group':
|
||||
channel.is_editable = channel.is_member and not self.sudo(False).env.user._is_public()
|
||||
else:
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
</div>
|
||||
<div class="o-mail-Composer-actions d-flex bg-view"
|
||||
t-att-class="{
|
||||
'ms-1' : compact and ui.isSmall,
|
||||
'mx-1 me-2' : compact and !ui.isSmall,
|
||||
'ms-3' : normal,
|
||||
'mx-3 border-top p-1' : extended,
|
||||
'ms-1': compact and ui.isSmall,
|
||||
'mx-1 me-2': compact and !ui.isSmall,
|
||||
'ms-3': normal,
|
||||
'mx-3 border-top p-1': extended,
|
||||
}"
|
||||
>
|
||||
<div class="d-flex flex-grow-1 align-items-center" >
|
||||
|
||||
@@ -1089,6 +1089,18 @@ export class ThreadService {
|
||||
}
|
||||
return DEFAULT_AVATAR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} threadId
|
||||
* @param {string} data base64 representation of the binary
|
||||
* @returns
|
||||
*/
|
||||
async notifyThreadAvatarToServer(threadId, data) {
|
||||
return this.rpc("/discuss/channel/update_avatar", {
|
||||
channel_id: threadId,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const threadService = {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
}
|
||||
|
||||
button:hover, .focus {
|
||||
background-color: $o-gray-300 !important;
|
||||
background-color: $o-gray-200 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<small t-if="isActive" class="o-mail-Message-date text-muted opacity-50 ms-2">
|
||||
<t t-esc="messageService.dateSimple(message)"/>
|
||||
</small>
|
||||
<div t-att-class="{ 'position-absolute bottom-0 mb-1': !env.inChatter }">
|
||||
<div t-else="" class="ms-2">
|
||||
<MessageSeenIndicator
|
||||
t-if="props.message.isSelfAuthored and props.thread"
|
||||
message="props.message"
|
||||
@@ -134,21 +134,21 @@
|
||||
'flex-row-reverse': env.inChatWindow and isAlignedRight,
|
||||
}"
|
||||
>
|
||||
<button t-if="canAddReaction" class="btn px-1 py-0" tabindex="1" title="Add a Reaction" aria-label="Add a Reaction" t-ref="emoji-picker"><i class="fa fa-lg fa-smile-o"/></button>
|
||||
<button t-if="canReplyTo" class="btn px-1 py-0" t-on-click.stop="onClickReplyTo" tabindex="1" title="Reply" aria-label="Reply"><i class="fa fa-lg fa-reply"/></button>
|
||||
<button t-if="canToggleStar" class="btn px-1 py-0" t-on-click="() => messageService.toggleStar(props.message)" tabindex="0" title="Mark as Todo" aria-label="Mark as Todo"><i class="fa fa-lg" t-att-class="message.isStarred ? 'fa-star o-mail-Message-starred' : 'fa-star-o'"/></button>
|
||||
<Dropdown t-if="editable or deletable or isInInbox or props.thread?.model === 'discuss.channel'" onStateChanged="state => this.state.expandOptions = state.open" position="props.thread?.newestMessage === props.message ? 'top-start' : 'bottom-start'" togglerClass="`btn p-0 ${ state.expandOptions ? 'bg-200' : '' }`" menuClass="'d-flex flex-column py-0 o-mail-Message-moreMenu'" class="'d-flex'" title="expandText">
|
||||
<button t-if="canAddReaction" class="btn px-1 py-0 rounded-0" tabindex="1" title="Add a Reaction" aria-label="Add a Reaction" t-ref="emoji-picker"><i class="fa fa-lg fa-smile-o"/></button>
|
||||
<button t-if="canReplyTo" class="btn px-1 py-0 rounded-0" t-on-click.stop="onClickReplyTo" tabindex="1" title="Reply" aria-label="Reply"><i class="fa fa-lg fa-reply"/></button>
|
||||
<button t-if="canToggleStar" class="btn px-1 py-0 rounded-0" t-on-click="() => messageService.toggleStar(props.message)" tabindex="0" title="Mark as Todo" aria-label="Mark as Todo"><i class="fa fa-lg" t-att-class="message.isStarred ? 'fa-star o-mail-Message-starred' : 'fa-star-o'"/></button>
|
||||
<Dropdown t-if="editable or deletable or isInInbox or props.thread?.model === 'discuss.channel'" onStateChanged="state => this.state.expandOptions = state.open" position="props.thread?.newestMessage === props.message ? 'top-start' : 'bottom-start'" togglerClass="`btn p-0 ${ state.expandOptions ? 'bg-200' : '' }`" menuClass="'d-flex flex-column py-0 o-mail-Message-moreMenu'" class="'d-flex rounded-0'" title="expandText">
|
||||
<t t-set-slot="toggler">
|
||||
<i class="btn px-1 py-0 fa fa-lg fa-ellipsis-h" t-att-class="{ 'order-1': props.isInChatWindow }" tabindex="1"/>
|
||||
<i class="btn px-1 py-0 fa fa-lg fa-ellipsis-h rounded-0" t-att-class="{ 'order-1': props.isInChatWindow }" tabindex="1"/>
|
||||
</t>
|
||||
<t t-set-slot="default">
|
||||
<DropdownItem t-if="message.reactions.length" class="'px-2 d-flex align-items-center'" title="'Reactions'" onSelected="() => this.openReactionMenu()"><i class="fa fa-lg fa-smile-o pe-2"/>View Reactions</DropdownItem>
|
||||
<DropdownItem t-if="isInInbox" class="'px-2 d-flex align-items-center'" title="'Mark as Read'" onSelected="() => messageService.setDone(props.message)"><i class="fa fa-lg fa-check pe-2"/>Mark as Read</DropdownItem>
|
||||
<DropdownItem t-if="canUnfollow" class="'px-2 d-flex align-items-center'" title="'Unfollow'" onSelected="() => messageService.unfollow(props.message)"><i class="fa fa-lg fa-user-times pe-2"/>Unfollow</DropdownItem>
|
||||
<DropdownItem t-if="store.user and props.thread.model === 'discuss.channel'" class="'px-2 d-flex align-items-center'" title="pinOptionText" onSelected="() => this.onClickPin()"><i class="fa fa-lg fa-thumb-tack pe-2"/><t t-esc="pinOptionText"/></DropdownItem>
|
||||
<DropdownItem t-if="props.thread.model === 'discuss.channel'" class="'px-2 d-flex align-items-center'" title="'Mark as Unread'" onSelected="() => this.onClickMarkAsUnread()"><i class="fa fa-lg fa-eye-slash pe-2"/>Mark as Unread</DropdownItem>
|
||||
<DropdownItem t-if="editable" class="'px-2 d-flex align-items-center'" title="'Edit'" onSelected="() => this.onClickEdit()"><i class="fa fa-lg fa-pencil pe-2"/>Edit</DropdownItem>
|
||||
<DropdownItem t-if="deletable" class="'px-2 d-flex align-items-center'" title="'Delete'" onSelected="() => this.onClickDelete()"><i class="fa fa-lg fa-trash pe-2"/>Delete</DropdownItem>
|
||||
<DropdownItem t-if="message.reactions.length" class="'px-2 d-flex align-items-center rounded-0'" title="'Reactions'" onSelected="() => this.openReactionMenu()"><i class="fa fa-lg fa-smile-o pe-2"/>View Reactions</DropdownItem>
|
||||
<DropdownItem t-if="isInInbox" class="'px-2 d-flex align-items-center rounded-0'" title="'Mark as Read'" onSelected="() => messageService.setDone(props.message)"><i class="fa fa-lg fa-check pe-2"/>Mark as Read</DropdownItem>
|
||||
<DropdownItem t-if="canUnfollow" class="'px-2 d-flex align-items-center rounded-0'" title="'Unfollow'" onSelected="() => messageService.unfollow(props.message)"><i class="fa fa-lg fa-user-times pe-2"/>Unfollow</DropdownItem>
|
||||
<DropdownItem t-if="store.user and props.thread.model === 'discuss.channel'" class="'px-2 d-flex align-items-center rounded-0'" title="pinOptionText" onSelected="() => this.onClickPin()"><i class="fa fa-lg fa-thumb-tack pe-2"/><t t-esc="pinOptionText"/></DropdownItem>
|
||||
<DropdownItem t-if="props.thread.model === 'discuss.channel'" class="'px-2 d-flex align-items-center rounded-0'" title="'Mark as Unread'" onSelected="() => this.onClickMarkAsUnread()"><i class="fa fa-lg fa-eye-slash pe-2"/>Mark as Unread</DropdownItem>
|
||||
<DropdownItem t-if="editable" class="'px-2 d-flex align-items-center rounded-0'" title="'Edit'" onSelected="() => this.onClickEdit()"><i class="fa fa-lg fa-pencil pe-2"/>Edit</DropdownItem>
|
||||
<DropdownItem t-if="deletable" class="'px-2 d-flex align-items-center rounded-0'" title="'Delete'" onSelected="() => this.onClickDelete()"><i class="fa fa-lg fa-trash pe-2"/>Delete</DropdownItem>
|
||||
</t>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}">
|
||||
<small class="position-relative d-block text-small mb-1" t-attf-class="{{ env.inChatWindow and props.alignedRight ? 'justify-content-end pe-5': 'ps-5' }}" t-on-click="() => this.props.onClick?.()">
|
||||
<span class="o-mail-MessageInReply-corner position-absolute bottom-0 top-50 pe-4 border-top text-300" t-attf-class="{{ env.inChatWindow and props.alignedRight ? 'o-isRightAlign border-end' : 'o-isLeftAlign border-start' }}" t-att-class="{ 'ms-n2': store.discuss.isActive }"/>
|
||||
<span t-if="!message.parentMessage.isEmpty" class="d-inline-flex align-items-center text-muted opacity-75" t-att-class="{ 'cursor-pointer opacity-100-hover': props.onClick }" t-attf-class="{{ env.inChatWindow and props.alignedRight ? 'pe-3': 'ps-3' }}">
|
||||
<span t-if="!message.parentMessage.isEmpty" class="d-inline-flex align-items-center text-muted opacity-75" t-att-class="{ 'cursor-pointer opacity-100-hover': props.onClick }">
|
||||
<img class="o-mail-MessageInReply-avatar me-2 rounded" t-att-src="authorAvatarUrl" t-att-title="message.parentMessage.author.name" alt="Avatar"/>
|
||||
<span class="o-mail-MessageInReply-content overflow-hidden">
|
||||
<b>@<t t-out="message.parentMessage.author.name"/></b>:
|
||||
|
||||
@@ -27,6 +27,15 @@ export class MessageSeenIndicator extends Component {
|
||||
return otherDidNotSee.length === 0;
|
||||
}
|
||||
|
||||
get hasEveryoneReceived() {
|
||||
return !([...this.props.thread.seenInfos].some((seenInfo) => {
|
||||
return (
|
||||
seenInfo.partner.id !== this.props.message.author.id &&
|
||||
(!seenInfo.lastFetchedMessage || seenInfo.lastFetchedMessage.id < this.props.message.id)
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
get isMessagePreviousToLastSelfMessageSeenByEveryone() {
|
||||
if (!this.props.thread.lastSelfMessageSeenByEveryone) {
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mail.MessageSeenIndicator" owl="1">
|
||||
<span class="o-mail-MessageSeenIndicator position-relative d-flex opacity-75-hover opacity-50" t-att-class="{ 'o-all-seen text-odoo': hasEveryoneSeen }" t-attf-class="{{ props.className }}" t-att-title="text">
|
||||
<span class="o-mail-MessageSeenIndicator position-relative d-flex opacity-50" t-att-class="{ 'o-all-seen text-primary': hasEveryoneSeen }" t-attf-class="{{ props.className }}">
|
||||
<t t-if="!isMessagePreviousToLastSelfMessageSeenByEveryone">
|
||||
<i t-if="hasSomeoneFetched or hasSomeoneSeen" class="fa fa-check ps-1"/>
|
||||
<i t-if="hasSomeoneSeen" class="o-second fa fa-check position-absolute"/>
|
||||
|
||||
@@ -13,6 +13,7 @@ $o-mail-Message-sidebarWidth: 48px !default;
|
||||
$o-mail-NavigableList-zIndex: 11;
|
||||
$o-mail-Chatter-minWidth: 530px !default;
|
||||
$o-mail-Discuss-inspector: 250px !default;
|
||||
$o-mail-Discuss-headerHeight: 48px !default;
|
||||
|
||||
@mixin o-mail-AttachmentViewer-arrow {
|
||||
background-color: rgba(black, 0.4);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.o-mail-Thread-date hr {
|
||||
opacity: $hr-opacity / 2;
|
||||
}
|
||||
|
||||
.o-mail-Thread-newMessage {
|
||||
transition: opacity 0.5s;
|
||||
|
||||
@@ -25,3 +29,7 @@
|
||||
background-color: mix(map-get($theme-colors, 'info'), $o-webclient-background-color, 5%);
|
||||
z-index: $o-mail-NavigableList-zIndex - 2;
|
||||
}
|
||||
|
||||
.o-mail-NotificationMessage p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
<t t-foreach="props.order === 'asc' ? props.thread.nonEmptyMessages : [...props.thread.nonEmptyMessages].reverse()" t-as="msg" t-key="msg.id">
|
||||
<t t-if="msg.dateDay !== currentDay">
|
||||
<div class="o-mail-Thread-date d-flex align-items-center fw-bolder pt-4">
|
||||
<hr class="flex-grow-1"/>
|
||||
<span class="px-3"><t t-esc="msg.dateDay"/></span>
|
||||
<hr class="flex-grow-1"/>
|
||||
<hr class="ms-3 flex-grow-1"/>
|
||||
<span class="px-3 text-muted"><t t-esc="msg.dateDay"/></span>
|
||||
<hr class="me-3 flex-grow-1"/>
|
||||
</div>
|
||||
<t t-set="currentDay" t-value="msg.dateDay"/>
|
||||
</t>
|
||||
<Transition t-if="props.thread.isChannel" visible="props.thread.seen_message_id === (prevMsg?.id ?? false)" name="'o-fade'" t-slot-scope="transition">
|
||||
<div class="o-mail-Thread-newMessage d-flex align-items-center fw-bolder" t-att-class="{ 'opacity-0': transition.className.includes('o-fade-leave') }">
|
||||
<hr class="flex-grow-1 border opacity-50"/><span class="px-3">New messages</span>
|
||||
<hr class="ms-3 flex-grow-1 border opacity-50"/><span class="px-3 me-3">New messages</span>
|
||||
</div>
|
||||
</Transition>
|
||||
<t t-if="msg.isNotification">
|
||||
@@ -114,7 +114,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="mail.NotificationMessage" owl="1">
|
||||
<div class="mx-auto text-500 small px-3 text-center" t-on-click="onClickNotification" t-att-class="{
|
||||
<div class="o-mail-NotificationMessage mx-auto text-500 small px-3 text-center" t-on-click="onClickNotification" t-att-class="{
|
||||
'o-mail-Thread-noPinnedPanel': env.inChatWindow and !ui.isSmall,
|
||||
'mt-2': prevMsg and !prevMsg.isNotification,
|
||||
}">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="discuss.CallSettings" owl="1">
|
||||
<div class="o-discuss-CallSettings py-2 user-select-none bg-light" t-attf-class="{{ props.className }}" t-ref="root">
|
||||
<div class="o-discuss-CallSettings py-2 user-select-none bg-view" t-attf-class="{{ props.className }}" t-ref="root">
|
||||
<div class="d-flex flex-column px-3 overflow-auto">
|
||||
<div class="py-2 fw-bolder text-700 text-truncate text-uppercase">Voice Settings</div>
|
||||
<div class="mb-3 d-flex align-items-center flex-wrap">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.o-discuss-ChannelMember:hover {
|
||||
@extend .bg-300;
|
||||
.o-discuss-ChannelMember.cursor-pointer:hover {
|
||||
@extend .bg-200;
|
||||
}
|
||||
|
||||
.o-discuss-ChannelMember-avatar {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="discuss.ChannelMemberList" owl="1">
|
||||
<div class="o-discuss-ChannelMemberList d-flex flex-column overflow-auto bg-light" t-att-class="props.className">
|
||||
<div class="o-discuss-ChannelMemberList d-flex flex-column overflow-auto bg-view" t-att-class="props.className">
|
||||
<t t-if="props.thread.onlineMembers.length > 0">
|
||||
<h6 class="m-2">
|
||||
Online -
|
||||
@@ -26,11 +26,11 @@
|
||||
</t>
|
||||
|
||||
<t t-name="discuss.channel_member" owl="1">
|
||||
<div class="o-discuss-ChannelMember d-flex align-items-center mx-2 p-2 bg-light" t-att-class="{ 'cursor-pointer': canOpenChatWith(member) }" t-on-click.stop="() => this.openChatAvatar(member)">
|
||||
<div class="o-discuss-ChannelMember d-flex align-items-center p-2 bg-view" t-att-class="{ 'cursor-pointer': canOpenChatWith(member) }" t-on-click.stop="() => this.openChatAvatar(member)">
|
||||
<div class="o-bg-inherit o-discuss-ChannelMember-avatar position-relative d-flex ms-4 flex-shrink-0">
|
||||
<img class="w-100 h-100 rounded-circle o_object_fit_cover o_redirect"
|
||||
t-att-src="threadService.avatarUrl(member.persona, props.thread)"/>
|
||||
<ImStatus persona="member.persona"/>
|
||||
<ImStatus persona="member.persona" className="'position-absolute top-100 start-100 translate-middle mt-n1 ms-n1'"/>
|
||||
</div>
|
||||
<span class="ms-2 text-truncate" t-esc="member.persona.name"/>
|
||||
<span class="ms-auto">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="mail.AutoresizeInput" owl="1">
|
||||
<input
|
||||
class="o-mail-AutoresizeInput text-truncate px-1 border-1 bg-view border-transparent"
|
||||
class="o-mail-AutoresizeInput text-truncate px-1 border-1 border-transparent"
|
||||
t-attf-class="{{ props.className }}"
|
||||
t-attf-placeholder="{{ props.placeholder }}"
|
||||
t-att-disabled="!props.enabled"
|
||||
|
||||
@@ -109,7 +109,9 @@ export class Discuss extends Component {
|
||||
|
||||
get channelAvatar() {
|
||||
return this.props.public
|
||||
? url(`/discuss/channel/${this.thread.id}/avatar_128`)
|
||||
? url(
|
||||
`/discuss/channel/${this.thread.id}/avatar_128?unique=${this.thread?.avatarCacheKey}`
|
||||
)
|
||||
: this.thread.imgUrl;
|
||||
}
|
||||
|
||||
@@ -121,7 +123,7 @@ export class Discuss extends Component {
|
||||
}
|
||||
|
||||
async onFileUploaded(file) {
|
||||
await this.threadService.updateAvatar(this.thread.id, file.data);
|
||||
await this.threadService.notifyThreadAvatarToServer(this.thread.id, file.data);
|
||||
this.notification.add(_t("The avatar has been updated!"), { type: "success" });
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
.o-mail-Thread {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.o-mail-Discuss-header button:not(.btn-secondary):hover {
|
||||
background-color: $gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
.o-mail-Discuss-selfAvatar {
|
||||
@@ -17,15 +13,19 @@
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.o-mail-Discuss-header button {
|
||||
&:not(.o-isActive):hover {
|
||||
background-color: $gray-300;
|
||||
}
|
||||
&.o-isActive {
|
||||
background-color: $gray-300;
|
||||
}
|
||||
&.o-isActive:hover {
|
||||
background-color: $gray-400;
|
||||
.o-mail-Discuss-header {
|
||||
height: $o-mail-Discuss-headerHeight;
|
||||
|
||||
button {
|
||||
&:not(.o-isActive):hover {
|
||||
background-color: $gray-200;
|
||||
}
|
||||
&.o-isActive {
|
||||
background-color: $gray-300;
|
||||
}
|
||||
&.o-isActive:hover {
|
||||
background-color: $gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<div class="o-mail-Discuss d-flex h-100 flex-grow-1 border-top" t-att-class="{ 'flex-column align-items-center bg-view': ui.isSmall }" t-ref="root">
|
||||
<div t-if="ui.isSmall and store.discuss.activeTab === 'mailbox'" class="w-100 border-bottom" t-call="mail.Discuss.mobileTopbar" t-ref="mobileTopbar"/>
|
||||
<div t-if="thread and !(ui.isSmall and ['chat', 'channel'].includes(store.discuss.activeTab))" class="o-mail-Discuss-content d-flex flex-column h-100 w-100 bg-view overflow-auto" t-ref="content">
|
||||
<div class="o-mail-Discuss-header px-3 bg-view border-bottom d-flex flex-shrink-0 align-items-center" style="height:46px;">
|
||||
<div t-if="thread and thread.model === 'discuss.channel'" class="o-mail-Discuss-threadAvatar position-relative align-self-center align-items-center mt-2 mb-2 me-2">
|
||||
<div class="o-mail-Discuss-header px-3 bg-view border-bottom d-flex flex-shrink-0 align-items-center">
|
||||
<div t-if="thread and ['channel', 'group', 'chat'].includes(thread.type)" class="o-mail-Discuss-threadAvatar position-relative align-self-center align-items-center mt-2 mb-2 me-2">
|
||||
<img class="rounded" t-att-src="channelAvatar" alt="Thread Image"/>
|
||||
<FileUploader t-if="thread.is_editable and thread.type !== 'chat'" acceptedFileExtensions="'.bmp, .jpg, .jpeg, .png, .svg'" showUploadingText="false" multiUpload="false" onUploaded="(data) => this.onFileUploaded(data)">
|
||||
<FileUploader t-if="thread.is_editable" acceptedFileExtensions="'.bmp, .jpg, .jpeg, .png, .svg'" showUploadingText="false" multiUpload="false" onUploaded="(data) => this.onFileUploaded(data)">
|
||||
<t t-set-slot="toggler">
|
||||
<a href="#" class="position-absolute z-index-1 h-100 w-100 rounded start-0 bottom-0" title="Upload Avatar">
|
||||
<i class="position-absolute top-50 start-50 fa fa-sm fa-pencil text-white"/>
|
||||
@@ -22,8 +22,8 @@
|
||||
<ImStatus t-if="thread and thread.type === 'chat' and thread.correspondent" className="'me-1'" persona="thread.correspondent"/>
|
||||
<div class="d-flex flex-grow-1 align-self-center align-items-center h-100 py-2">
|
||||
<AutoresizeInput
|
||||
className="'o-mail-Discuss-threadName lead fw-bold flex-shrink-1 text-dark py-0'"
|
||||
enabled="thread.is_editable"
|
||||
className="'o-mail-Discuss-threadName lead fw-bold flex-shrink-1 text-dark bg-view py-0'"
|
||||
enabled="thread.is_editable or thread.type === 'chat'"
|
||||
onValidate.bind="renameThread"
|
||||
value="thread.displayName"
|
||||
/>
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="flex-shrink-0 mx-2 py-2 border-start"/>
|
||||
<t t-set="autogrowDescriptionPlaceholder">Add a description</t>
|
||||
<AutoresizeInput
|
||||
className="'o-mail-Discuss-threadDescription flex-shrink-1 py-1'"
|
||||
className="'o-mail-Discuss-threadDescription flex-shrink-1 py-1 bg-view'"
|
||||
enabled="thread.is_editable"
|
||||
onValidate.bind="updateThreadDescription"
|
||||
placeholder="thread.is_editable ? autogrowDescriptionPlaceholder : ''"
|
||||
@@ -40,8 +40,8 @@
|
||||
</t>
|
||||
</div>
|
||||
<div class="o-mail-Discuss-header flex-shrink-0 d-flex align-items-center ms-1">
|
||||
<button t-if="thread.id === 'inbox'" class="btn btn-secondary" t-att-disabled="thread.isEmpty" t-on-click="markAllAsRead">Mark all read</button>
|
||||
<button t-if="thread.id === 'starred'" class="btn btn-secondary" t-att-disabled="thread.isEmpty" t-on-click="messageService.unstarAll">Unstar all</button>
|
||||
<button t-if="thread.id === 'inbox'" class="btn btn-link" t-att-disabled="thread.isEmpty" t-on-click="markAllAsRead">Mark all read</button>
|
||||
<button t-if="thread.id === 'starred'" class="btn btn-link" t-att-disabled="thread.isEmpty" t-on-click="messageService.unstarAll">Unstar all</button>
|
||||
<button t-if="thread.model === 'discuss.channel'" class="btn rounded px-2 shadow-none" t-att-class="{ 'o-isActive': state.activeMode === MODES.PINNED_MESSAGES }" title="Pinned messages" t-on-click.stop="togglePinMenu">
|
||||
<i class="fa fa-lg fa-thumb-tack text-700"/>
|
||||
</button>
|
||||
@@ -63,8 +63,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto bg-100 d-flex flex-grow-1">
|
||||
<div class="bg-view d-flex flex-column flex-grow-1">
|
||||
<div class="overflow-auto bg-view d-flex flex-grow-1">
|
||||
<div class="d-flex flex-column flex-grow-1">
|
||||
<Call t-if="Object.keys(thread.rtcSessions).length > 0" thread="thread"/>
|
||||
<Thread thread="thread" t-key="thread.localId" messageEdition="messageEdition" messageToReplyTo="messageToReplyTo"/>
|
||||
<Composer t-if="thread.type !== 'mailbox' or messageToReplyTo.thread === thread" t-key="thread.localId" composer="thread.composer" autofocus="true" messageEdition="messageEdition" messageToReplyTo="messageToReplyTo" onDiscardCallback="() => messageToReplyTo.cancel()" dropzoneRef="contentRef"/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mail.PinnedMessagesPanel" owl="1">
|
||||
<div class="o-mail-PinnedMessagesPanel overflow-auto d-flex flex-column flex-shrink-0 bg-100 position-relative border-start" t-att-class="{
|
||||
<div class="o-mail-PinnedMessagesPanel overflow-auto d-flex flex-column flex-shrink-0 bg-view position-relative border-start" t-att-class="{
|
||||
'flex-grow-1': env.inChatWindow,
|
||||
}">
|
||||
<p class="o-mail-PinnedMessagesPanel-title position-sticky top-0 py-3 mb-0 fw-bolder text-center text-uppercase text-700 bg-100">
|
||||
<p class="o-mail-PinnedMessagesPanel-title position-sticky top-0 py-3 mb-0 fw-bolder text-center text-uppercase text-700">
|
||||
Pinned messages
|
||||
</p>
|
||||
<div class="d-flex flex-column" t-att-class="{ 'justify-content-center flex-grow-1': props.thread.pinnedMessages.length === 0 }" t-ref="message-list">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
width: 30px;
|
||||
font-size: 1rem;
|
||||
&:hover {
|
||||
background-color: $gray-300;
|
||||
background-color: $gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="mail.EmojiPicker.tab" owl="1">
|
||||
<span class="o-mail-Emoji text-center p-1 cursor-pointer" t-att-class="{'o-isActive bg-300': category.sortId === state.categoryId}" t-att-title="category.name" t-att-data-id="category.sortId" t-on-click="selectCategory">
|
||||
<span class="o-mail-Emoji text-center p-1 cursor-pointer" t-att-class="{'o-isActive bg-200': category.sortId === state.categoryId}" t-att-title="category.name" t-att-data-id="category.sortId" t-on-click="selectCategory">
|
||||
<t t-esc="category.title"/>
|
||||
</span>
|
||||
</t>
|
||||
@@ -71,7 +71,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="mail.EmojiPicker.emoji" owl="1">
|
||||
<span class="o-mail-Emoji cursor-pointer d-flex justify-content-center fs-2" t-att-class="{ 'bg-300': state.activeEmojiIndex === itemIndex }" t-att-title="emoji.name" t-att-data-codepoints="emoji.codepoints" t-att-data-index="itemIndex" t-att-data-category="recentCategory.sortId" t-on-click="selectEmoji">
|
||||
<span class="o-mail-Emoji cursor-pointer d-flex justify-content-center fs-2" t-att-class="{ 'bg-200': state.activeEmojiIndex === itemIndex }" t-att-title="emoji.name" t-att-data-codepoints="emoji.codepoints" t-att-data-index="itemIndex" t-att-data-category="recentCategory.sortId" t-on-click="selectEmoji">
|
||||
<span t-esc="emoji.codepoints"/>
|
||||
</span>
|
||||
</t>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
outline: none;
|
||||
|
||||
&.o-folded {
|
||||
height: 48px;
|
||||
height: $o-mail-Discuss-headerHeight;
|
||||
}
|
||||
|
||||
.o-mail-ChatWindow-command:hover {
|
||||
@@ -15,12 +15,10 @@
|
||||
}
|
||||
|
||||
.o-mail-ChatWindow-header {
|
||||
height: 48px;
|
||||
height: $o-mail-Discuss-headerHeight;
|
||||
|
||||
.o-mail-ChatWindow-threadAvatar {
|
||||
img {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
.o-mail-ChatWindow-threadAvatar img {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<ThreadIcon t-if="thread" thread="thread" className="'ms-3 me-1 my-0'"/>
|
||||
</t>
|
||||
<ImStatus t-if="thread and thread.type === 'chat' and thread.correspondent" className="'me-1'" persona="thread.correspondent"/>
|
||||
<span class="o-mail-ChatWindow-name text-truncate" t-att-title="props.chatWindow.displayName" t-esc="props.chatWindow.displayName" t-att-class="{
|
||||
<span class="o-mail-ChatWindow-name text-truncate fw-bold" t-att-title="props.chatWindow.displayName" t-esc="props.chatWindow.displayName" t-att-class="{
|
||||
'ms-3 me-1 my-0': !thread,
|
||||
}"
|
||||
/>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
.o-mail-DiscussSidebar-item:hover {
|
||||
background-color: $o-gray-300 !important;
|
||||
background-color: $o-gray-200 !important;
|
||||
|
||||
.o-mail-DiscussCategoryItem-commands {
|
||||
display: flex !important;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
Start a meeting
|
||||
</button>
|
||||
</div>
|
||||
<hr class="w-100"/>
|
||||
<hr class="w-100 opacity-0"/>
|
||||
<div class="d-flex flex-column flex-grow-0">
|
||||
<t t-call="mail.Mailbox">
|
||||
<t t-set="mailbox" t-value="store.discuss.inbox"/>
|
||||
@@ -20,7 +20,7 @@
|
||||
<t t-set="mailbox" t-value="store.discuss.history"/>
|
||||
</t>
|
||||
</div>
|
||||
<hr class="w-100"/>
|
||||
<hr class="w-100 opacity-0"/>
|
||||
<input t-if="hasQuickSearch" class="form-control mx-4 mb-2 rounded-3 w-auto" placeholder="Quick search..." t-model="state.quickSearchVal"/>
|
||||
<t t-call="mail.DiscussCategory">
|
||||
<t t-set="category" t-value="store.discuss.channels"/>
|
||||
@@ -35,11 +35,11 @@
|
||||
<button class="o-mail-DiscussSidebar-item btn d-flex align-items-center py-1 px-0 border-0 rounded-0 fw-normal text-reset"
|
||||
t-att-class="{
|
||||
'o-bg-inherit': mailbox.localId !== store.discuss.threadLocalId,
|
||||
'o-active bg-300': mailbox.localId === store.discuss.threadLocalId,
|
||||
'o-active bg-200': mailbox.localId === store.discuss.threadLocalId,
|
||||
}"
|
||||
t-on-click="(ev) => this.openThread(ev, mailbox)"
|
||||
>
|
||||
<ThreadIcon className="'ms-4 me-2'" thread="mailbox"/>
|
||||
<ThreadIcon className="'ms-4 me-2 o-bg-inherit'" thread="mailbox"/>
|
||||
<div class="me-2 text-truncate">
|
||||
<t t-esc="mailbox.name"/>
|
||||
</div>
|
||||
@@ -54,7 +54,7 @@
|
||||
<t t-set="hover_class" t-value="'btn p-0 text-start text-700 opacity-100-hover opacity-75'"/>
|
||||
<div class="o-mail-DiscussCategory d-flex align-items-center my-1" t-att-class="category.extraClass">
|
||||
<div t-attf-class="d-flex align-items-baseline mx-1 {{ hover_class }}" t-on-click="() => this.toggleCategory(category)">
|
||||
<i class="o-mail-DiscussCategory-icon small" t-att-class="category.isOpen ? 'oi oi-chevron-down' : 'oi oi-chevron-right'"/>
|
||||
<i class="o-mail-DiscussCategory-icon small me-1" t-att-class="category.isOpen ? 'oi oi-chevron-down' : 'oi oi-chevron-right'"/>
|
||||
<span class="btn-sm p-0 text-uppercase fw-bolder"><t t-esc="category.name"/></span>
|
||||
</div>
|
||||
<div class="flex-grow-1"/>
|
||||
@@ -85,14 +85,14 @@
|
||||
<button class="o-mail-DiscussCategoryItem o-mail-DiscussSidebar-item btn btn-secondary d-flex align-items-center w-100 px-0 py-2 border-0 rounded-0 text-reset"
|
||||
t-att-class="{
|
||||
'o-bg-inherit': threadLocalId !== store.discuss.threadLocalId,
|
||||
'o-active bg-300': threadLocalId === store.discuss.threadLocalId,
|
||||
'o-active bg-200': threadLocalId === store.discuss.threadLocalId,
|
||||
'o-unread': thread.message_unread_counter > 0,
|
||||
}"
|
||||
t-on-click="(ev) => this.openThread(ev, thread)"
|
||||
>
|
||||
<div class="o-bg-inherit position-relative d-flex ms-4 flex-shrink-0" style="width:30px;height:30px">
|
||||
<img class="w-100 h-100 rounded" t-att-src="thread.imgUrl" alt="Thread Image"/>
|
||||
<ThreadIcon t-if="thread.type === 'chat' or (thread.type === 'channel' and !thread.authorizedGroupFullName)" thread="thread" size="'small'" className="'o-mail-DiscussCategoryItem-threadIcon position-absolute bottom-0 end-0 p-1 me-n1 mb-n1 d-flex align-items-center justify-content-center rounded-circle bg-100'"/>
|
||||
<ThreadIcon t-if="thread.type === 'chat' or (thread.type === 'channel' and !thread.authorizedGroupFullName)" thread="thread" size="'small'" className="'o-mail-DiscussCategoryItem-threadIcon position-absolute bottom-0 end-0 p-1 me-n1 mb-n1 d-flex align-items-center justify-content-center rounded-circle o-bg-inherit'"/>
|
||||
</div>
|
||||
<span class="ms-3 me-2 text-truncate" t-att-class="{ 'o-item-unread fw-bolder': thread.message_unread_counter > 0 }">
|
||||
<t t-esc="thread.displayName"/>
|
||||
@@ -102,7 +102,7 @@
|
||||
<i t-if="thread.type === 'channel'" t-attf-class="fa fa-cog {{ hover_class }}" title="Channel settings" t-on-click.stop="() => this.openSettings(thread)" role="img"/>
|
||||
<div t-if="threadService.canLeave(thread)" class="fa fa-times ms-1" t-attf-class="{{ hover_class }}"
|
||||
t-on-click.stop="() => this.leaveChannel(thread)" title="Leave this channel" role="img"/>
|
||||
<div t-if="threadService.canUnpin(thread)" t-attf-class="fa fa-times ms-1" t-on-click.stop="() => threadService.unpin(thread)" title="Unpin Conversation" role="img"/>
|
||||
<div t-if="threadService.canUnpin(thread)" t-attf-class="fa fa-times ms-1 {{ hover_class }}" t-on-click.stop="() => threadService.unpin(thread)" title="Unpin Conversation" role="img"/>
|
||||
</div>
|
||||
<div t-if="Object.keys(thread.rtcSessions).length > 0" class="fa fa-volume-up ms-1 me-3" t-att-class="{ 'text-danger': rtc.state.channel?.id === thread.id }"/>
|
||||
<div t-if="counter > 0" t-attf-class="o-mail-DiscussCategoryItem-counter badge rounded-pill text-bg-primary ms-1 me-3">
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
width: 450px;
|
||||
min-height: 50px;
|
||||
max-height: Min(calc(100vh - 140px), 630px);
|
||||
// z-index: 1055;
|
||||
// left: auto;
|
||||
// right: 0;
|
||||
|
||||
hr {
|
||||
opacity: $hr-opacity / 2;
|
||||
}
|
||||
}
|
||||
|
||||
.o-mail-MessagingMenu-navbar {
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
'o-mail-MessagingMenu-header border-bottom d-flex flex-shrink-0 p-2': !env.inDiscussApp,
|
||||
}">
|
||||
<t t-if="!ui.isSmall">
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'all' ? 'fw-bolder' : 'text-500'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'all'">All</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'chat' ? 'fw-bolder' : 'text-500'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'chat'">Chats</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'channel' ? 'fw-bolder' : 'text-500'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'channel'">Channels</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'all' ? 'fw-bolder' : 'text-muted'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'all'">All</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'chat' ? 'fw-bolder' : 'text-muted'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'chat'">Chats</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'channel' ? 'fw-bolder' : 'text-muted'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'channel'">Channels</button>
|
||||
</t>
|
||||
<div class="flex-grow-1"></div>
|
||||
<t t-if="!store.discuss.isActive or ui.isSmall and store.discuss.activeTab !== 'mailbox'">
|
||||
@@ -49,30 +49,33 @@
|
||||
<div class="d-flex justify-content-center py-4 px-2 text-muted" t-if="!hasPreviews">
|
||||
No conversation yet...
|
||||
</div>
|
||||
<NotificationItem t-if="notificationRequest.isShown"
|
||||
body="notificationRequest.body"
|
||||
displayName="notificationRequest.displayName"
|
||||
iconSrc="notificationRequest.iconSrc"
|
||||
isLast="notificationRequest.isLast"
|
||||
onClick="() => notification.requestPermission()"
|
||||
>
|
||||
<t t-set-slot="icon">
|
||||
<ImStatus persona="notificationRequest.partner" className="'position-absolute top-100 start-100 translate-middle mt-n1 ms-n1'"/>
|
||||
</t>
|
||||
</NotificationItem>
|
||||
<t t-if="store.discuss.activeTab === 'all'">
|
||||
<t t-if="notificationRequest.isShown">
|
||||
<NotificationItem
|
||||
t-foreach="failures" t-as="failure" t-key="failure.id"
|
||||
body="failure.body"
|
||||
counter="failure.notifications.length > 1 ? failure.notifications.length : undefined"
|
||||
datetime="failure.datetime"
|
||||
displayName="failure.modelName"
|
||||
iconSrc="failure.iconSrc"
|
||||
isLast="threads.length === 0 and failure_last"
|
||||
hasMarkAsReadButton="true"
|
||||
onClick="(isMarkAsRead) => isMarkAsRead ? this.cancelNotifications(failure) : this.onClickFailure(failure)"
|
||||
onSwipeRight="hasTouch() ? { action: () => this.cancelNotifications(failure), icon: 'fa-times-circle', bgColor: 'bg-warning' } : undefined"
|
||||
/>
|
||||
body="notificationRequest.body"
|
||||
displayName="notificationRequest.displayName"
|
||||
iconSrc="notificationRequest.iconSrc"
|
||||
onClick="() => notification.requestPermission()"
|
||||
>
|
||||
<t t-set-slot="icon">
|
||||
<ImStatus persona="notificationRequest.partner" className="'position-absolute top-100 start-100 translate-middle mt-n1 ms-n1'"/>
|
||||
</t>
|
||||
</NotificationItem>
|
||||
<hr t-if="!notificationRequest.isLast" class="my-0"/>
|
||||
</t>
|
||||
<t t-if="store.discuss.activeTab === 'all'">
|
||||
<t t-foreach="failures" t-as="failure" t-key="failure.id">
|
||||
<NotificationItem
|
||||
body="failure.body"
|
||||
counter="failure.notifications.length > 1 ? failure.notifications.length : undefined"
|
||||
datetime="failure.datetime"
|
||||
displayName="failure.modelName"
|
||||
iconSrc="failure.iconSrc"
|
||||
hasMarkAsReadButton="true"
|
||||
onClick="(isMarkAsRead) => isMarkAsRead ? this.cancelNotifications(failure) : this.onClickFailure(failure)"
|
||||
onSwipeRight="hasTouch() ? { action: () => this.cancelNotifications(failure), icon: 'fa-times-circle', bgColor: 'bg-warning' } : undefined"
|
||||
/>
|
||||
<hr t-if="threads.length !== 0 or !failure_last" class="my-0"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-foreach="threads" t-as="thread" t-key="thread.localId">
|
||||
<t t-set="message" t-value="thread.isChatChannel or (thread.type === 'channel' and !thread.hasNeedactionMessages) ? thread.newestMessage : thread.newestNeedactionMessage"/>
|
||||
@@ -83,7 +86,6 @@
|
||||
displayName="thread.displayName"
|
||||
iconSrc="thread.imgUrl"
|
||||
hasMarkAsReadButton="thread.isUnread"
|
||||
isLast="thread_last"
|
||||
onClick="(isMarkAsRead) => this.onClickThread(isMarkAsRead, thread)"
|
||||
onSwipeRight="hasTouch() and thread.isUnread ? { action: () => this.markAsRead(thread), icon: 'fa-check-circle', bgColor: 'bg-success' } : undefined"
|
||||
onSwipeLeft="hasTouch() and threadService.canUnpin(thread) ? { action: () => this.threadService.unpin(thread), icon: 'fa-times-circle', bgColor: 'bg-danger' } : undefined"
|
||||
@@ -100,6 +102,7 @@
|
||||
</t>
|
||||
</t>
|
||||
</NotificationItem>
|
||||
<hr t-if="!thread_last" class="my-0"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,6 @@ export class NotificationItem extends Component {
|
||||
"displayName",
|
||||
"hasMarkAsReadButton?",
|
||||
"iconSrc?",
|
||||
"isLast",
|
||||
"onClick",
|
||||
"onSwipeLeft?",
|
||||
"onSwipeRight?",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<t t-name="mail.NotificationItem" owl="1">
|
||||
<ActionSwiper onLeftSwipe="props.onSwipeLeft ? props.onSwipeLeft : undefined" onRightSwipe="props.onSwipeRight ? props.onSwipeRight : undefined">
|
||||
<div class="o-mail-NotificationItem d-flex cursor-pointer align-items-center p-1" t-att-class="{ 'border-bottom': !props.isLast, 'o-muted': !props.hasMarkAsReadButton }" t-on-click="onClick">
|
||||
<div class="o-mail-NotificationItem d-flex cursor-pointer align-items-center p-1" t-att-class="{ 'o-muted': !props.hasMarkAsReadButton }" t-on-click="onClick">
|
||||
<div class="position-relative o-bg-inherit m-1 flex-shrink-0" style="width:40px;height:40px;">
|
||||
<img class="o_avatar w-100 h-100 rounded" alt="Notification Item Image" t-att-src="props.iconSrc"/>
|
||||
<t t-slot="icon"/>
|
||||
@@ -24,7 +24,7 @@
|
||||
<t t-if="props.body" t-esc="props.body" name="notificationBody"/>
|
||||
</div>
|
||||
<div class="flex-grow-1"/>
|
||||
<i t-if="props.hasMarkAsReadButton" class="o-mail-NotificationItem-markAsRead fa fa-check d-flex ms-2 text-600 opacity-50 opacity-100-hover" title="Mark As Read" t-ref="markAsRead"/>
|
||||
<i t-if="props.hasMarkAsReadButton" class="o-mail-NotificationItem-markAsRead fa fa-check d-flex ms-2 text-600 opacity-50 opacity-100-hover cursor-pointer" title="Mark As Read" t-ref="markAsRead"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -199,13 +199,6 @@ patch(ThreadService.prototype, "mail/web", {
|
||||
}
|
||||
delete this.store.followers[follower.id];
|
||||
},
|
||||
|
||||
async updateAvatar(threadId, data) {
|
||||
return this.rpc("/discuss/channel/update_avatar", {
|
||||
channel_id: threadId,
|
||||
data,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
patch(threadService, "mail/web", {
|
||||
|
||||
@@ -49,22 +49,22 @@ QUnit.test("Basic keyboard navigation", async (assert) => {
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
await click("button[aria-label='Emojis']");
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-300"); // bg-300 means active
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-200"); // bg-200 means active
|
||||
await afterNextRender(() => triggerHotkey("ArrowRight"));
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=1].bg-300");
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=1].bg-200");
|
||||
await afterNextRender(() => triggerHotkey("ArrowDown"));
|
||||
assert.containsOnce(
|
||||
$,
|
||||
`.o-mail-EmojiPicker-content .o-mail-Emoji[data-index=${EMOJI_PER_ROW + 1}].bg-300`
|
||||
`.o-mail-EmojiPicker-content .o-mail-Emoji[data-index=${EMOJI_PER_ROW + 1}].bg-200`
|
||||
);
|
||||
await afterNextRender(() => triggerHotkey("ArrowLeft"));
|
||||
assert.containsOnce(
|
||||
$,
|
||||
`.o-mail-EmojiPicker-content .o-mail-Emoji[data-index=${EMOJI_PER_ROW}].bg-300`
|
||||
`.o-mail-EmojiPicker-content .o-mail-Emoji[data-index=${EMOJI_PER_ROW}].bg-200`
|
||||
);
|
||||
await afterNextRender(() => triggerHotkey("ArrowUp"));
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-300");
|
||||
const codepoints = $(".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-300").data(
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-200");
|
||||
const codepoints = $(".o-mail-EmojiPicker-content .o-mail-Emoji[data-index=0].bg-200").data(
|
||||
"codepoints"
|
||||
);
|
||||
await afterNextRender(() => triggerHotkey("Enter"));
|
||||
@@ -148,7 +148,7 @@ QUnit.test("first category should be highlight by default", async (assert) => {
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
await click("button[aria-label='Emojis']");
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-header .o-mail-Emoji:eq(0).bg-300");
|
||||
assert.containsOnce($, ".o-mail-EmojiPicker-header .o-mail-Emoji:eq(0).bg-200");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
|
||||
@@ -633,9 +633,6 @@ patch(MockServer.prototype, "mail/models/discuss_channel", {
|
||||
case "channel":
|
||||
is_editable = channel.create_uid === this.pyEnv.currentPartnerId;
|
||||
break;
|
||||
case "chat":
|
||||
is_editable = true;
|
||||
break;
|
||||
case "group":
|
||||
is_editable = memberOfCurrentUser;
|
||||
break;
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<div class="oe_button_box" name="button_box"/>
|
||||
<field name="avatar_128" invisible="1"/>
|
||||
<field name="is_editable" invisible="1"/>
|
||||
<field name="image_128" widget="image" class="oe_avatar" options="{'size': [90, 90], 'preview_image':'avatar_128'}"/>
|
||||
<field name="image_128" widget="image" class="oe_avatar" options="{'size': [90, 90], 'preview_image':'avatar_128'}" attrs="{'readonly': [('is_editable', '!=', 'True')]}"/>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Group Name"/>
|
||||
|
||||
@@ -508,7 +508,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'group_based_subscription': False,
|
||||
'id': self.channel_chat_1.id,
|
||||
'invitedMembers': [('insert', [])],
|
||||
'is_editable': True,
|
||||
'is_editable': False,
|
||||
'is_minimized': False,
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_1.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
@@ -594,7 +594,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'group_based_subscription': False,
|
||||
'id': self.channel_chat_2.id,
|
||||
'invitedMembers': [('insert', [])],
|
||||
'is_editable': True,
|
||||
'is_editable': False,
|
||||
'is_minimized': False,
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_2.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
@@ -680,7 +680,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'group_based_subscription': False,
|
||||
'id': self.channel_chat_3.id,
|
||||
'invitedMembers': [('insert', [])],
|
||||
'is_editable': True,
|
||||
'is_editable': False,
|
||||
'is_minimized': False,
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_3.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
@@ -766,7 +766,7 @@ class TestDiscussFullPerformance(TransactionCase):
|
||||
'group_based_subscription': False,
|
||||
'id': self.channel_chat_4.id,
|
||||
'invitedMembers': [('insert', [])],
|
||||
'is_editable': True,
|
||||
'is_editable': False,
|
||||
'is_minimized': False,
|
||||
'is_pinned': True,
|
||||
'last_interest_dt': self.channel_chat_4.channel_member_ids.filtered(lambda m: m.partner_id == self.users[0].partner_id).last_interest_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
|
||||
Reference in New Issue
Block a user