[IMP] mail: merge some models (reduce amount of models)
- [IMP] mail: merge chatter top bar with chatter - [IMP] mail: merge attachment box into chatter - [IMP] mail: merge activityBoxView into chatter - [IMP] mail: merge follow/unfollow button into chatter - [IMP] mail: merge emoji picker header into emoji picker - [IMP] mail: merge emoji category bar into emoji picker - [IMP] mail: merge emoji header list action into emoji picker - [IMP] mail: merge emoji search bar into emoji picker - [IMP] mail: merge emoji grid loading screen into emoji grid - [IMP] mail: merge emoji grid no search into emoji grid - [IMP] mail: merge emoji grid row section into emoji grid row - [IMP] mail: merge emoji grid item into emoji view closes odoo/odoo#105099 Related: odoo/enterprise#33681 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
// ------------------------------------------------------------------
|
||||
// Layout
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
.o_ActivityBoxView_activityList {
|
||||
max-width: var(--Chatter-max-width, none);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Style
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
.o_ActivityBoxView_titleLine {
|
||||
border-top: $border-width $border-color dashed;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ActivityBoxView" owl="1">
|
||||
<div class="o_ActivityBoxView" t-attf-class="{{ className }}" t-ref="root">
|
||||
<a href="#" role="button" class="o_ActivityBoxView_title btn d-flex align-items-center mt-4 p-0 w-100 fw-bold" t-att-aria-expanded="isActivityListVisible ? 'true' : 'false'" t-on-click="onClickActivityBoxTitle">
|
||||
<hr class="o_ActivityBoxView_titleLine w-auto flex-grow-1 me-3" />
|
||||
<span class="o_ActivityBoxView_titleText">
|
||||
<i class="fa fa-fw" t-att-class="isActivityListVisible ? 'fa-caret-down' : 'fa-caret-right'"/>
|
||||
Planned activities
|
||||
</span>
|
||||
<span t-if="!isActivityListVisible" class="o_ActivityBoxView_titleBadges ms-2">
|
||||
<span t-if="chatter.thread.overdueActivities.length > 0" class="o_ActivityBoxView_titleBadge me-1 badge text-bg-danger" t-esc="chatter.thread.overdueActivities.length"/>
|
||||
<span t-if="chatter.thread.todayActivities.length > 0" class="o_ActivityBoxView_titleBadge me-1 badge text-bg-warning" t-esc="chatter.thread.todayActivities.length"/>
|
||||
<span t-if="chatter.thread.futureActivities.length > 0" class="o_ActivityBoxView_titleBadge me-1 badge text-bg-success" t-esc="chatter.thread.futureActivities.length"/>
|
||||
</span>
|
||||
<hr class="o_ActivityBoxView_titleLine w-auto flex-grow-1 ms-3"/>
|
||||
</a>
|
||||
<div t-if="isActivityListVisible" class="o_ActivityBoxView_activityList">
|
||||
<ActivityView t-foreach="activityViews" t-as="activityView" t-key="activityView.localId" className="'o_ActivityBoxView_activity'" record="activityView"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,7 +0,0 @@
|
||||
// ------------------------------------------------------------------
|
||||
// Style
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
.o_AttachmentBoxView_dashedLine {
|
||||
border-top: $border-width dashed $border-color;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.AttachmentBoxView" owl="1">
|
||||
<div class="o_AttachmentBoxView position-relative" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_AttachmentBoxView_title d-flex align-items-center">
|
||||
<hr class="o_AttachmentBoxView_dashedLine flex-grow-1"/>
|
||||
<span class="o_AttachmentBoxView_titleText p-3 fw-bold">
|
||||
Files
|
||||
</span>
|
||||
<hr class="o_AttachmentBoxView_dashedLine flex-grow-1"/>
|
||||
</div>
|
||||
<div class="o_AttachmentBoxView_content d-flex flex-column">
|
||||
<AttachmentList t-if="attachmentList"
|
||||
className="'o_attachmentBox_attachmentList'"
|
||||
record="attachmentList"
|
||||
/>
|
||||
<button class="o_AttachmentBoxView_buttonAdd btn btn-link" type="button" t-on-click="onClickAddAttachment" t-att-disabled="!chatter.hasWriteAccess">
|
||||
<i class="fa fa-plus-square"/>
|
||||
Attach files
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
+21
@@ -31,3 +31,24 @@
|
||||
.o_ChatterTopbar_buttonClose {
|
||||
width: $o-statusbar-height;
|
||||
}
|
||||
|
||||
.o_ActivityBoxView_activityList {
|
||||
max-width: var(--Chatter-max-width, none);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Style
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
.o_ActivityBoxView_titleLine {
|
||||
border-top: $border-width $border-color dashed;
|
||||
}
|
||||
|
||||
|
||||
.o_AttachmentBoxView_dashedLine {
|
||||
border-top: $border-width dashed $border-color;
|
||||
}
|
||||
|
||||
.o_FollowButtonView_unfollow:focus {
|
||||
background-color: $gray-200;
|
||||
}
|
||||
@@ -8,11 +8,91 @@
|
||||
record="dropZoneView"
|
||||
/>
|
||||
<div class="o_Chatter_fixedPanel">
|
||||
<ChatterTopbar
|
||||
t-if="topbar"
|
||||
className="'o_Chatter_topbar'"
|
||||
record="topbar"
|
||||
/>
|
||||
<div t-if="hasTopBar" class="o_ChatterTopbar justify-content-between d-flex">
|
||||
<div class="o_ChatterTopbar_actions flex-fill d-flex border-transparent">
|
||||
<div class="o_ChatterTopbar_controllers d-flex pe-2" t-if="threadView">
|
||||
<button class="o_ChatterTopbar_button o_ChatterTopbar_buttonSendMessage btn text-nowrap me-2"
|
||||
type="button"
|
||||
t-att-class="{
|
||||
'o-active btn-odoo': composerView and !composerView.composer.isLog,
|
||||
'btn-odoo': !composerView,
|
||||
'btn-light': composerView and composerView.composer.isLog,
|
||||
}"
|
||||
t-att-disabled="!hasWriteAccess"
|
||||
data-hotkey="m"
|
||||
t-on-click="onClickSendMessage"
|
||||
>
|
||||
Send message
|
||||
</button>
|
||||
<button class="o_ChatterTopbar_button o_ChatterTopbar_buttonLogNote btn text-nowrap"
|
||||
type="button"
|
||||
t-att-class="{
|
||||
'o-active btn-odoo': composerView and composerView.composer.isLog,
|
||||
'btn-light': composerView and !composerView.composer.isLog or !composerView,
|
||||
}"
|
||||
t-att-disabled="!hasWriteAccess"
|
||||
t-on-click="onClickLogNote"
|
||||
data-hotkey="shift+m"
|
||||
>
|
||||
Log note
|
||||
</button>
|
||||
</div>
|
||||
<div class="o_ChatterTopbar_tools position-relative d-flex flex-grow-1 border-bottom"
|
||||
t-att-class="{
|
||||
'border-start ps-2': hasActivities,
|
||||
}"
|
||||
>
|
||||
<button t-if="hasActivities" class="o_ChatterTopbar_button o_ChatterTopbar_buttonScheduleActivity btn btn-light text-nowrap" type="button" t-att-disabled="!hasWriteAccess" t-on-click="onClickScheduleActivity" data-hotkey="shift+a">
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
<span>Activities</span>
|
||||
</button>
|
||||
<div class="flex-grow-1 border-start pe-2"
|
||||
t-att-class="{
|
||||
'ms-2': hasActivities,
|
||||
}"
|
||||
/>
|
||||
<div class="o_ChatterTopbar_rightSection flex-grow-1 flex-shrink-0 justify-content-end d-flex">
|
||||
<button t-if="thread.allAttachments.length === 0" class="o_ChatterTopbar_button o_ChatterTopbar_buttonAddAttachments btn btn-light btn-primary" type="button" t-att-disabled="!hasWriteAccess" t-on-click="onClickButtonAddAttachments" >
|
||||
<i class="fa fa-paperclip fa-lg me-1" role="img" aria-label="Attachments"/>
|
||||
<i t-if="isShowingAttachmentsLoading" class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin" aria-label="Attachment counter loading..."/>
|
||||
</button>
|
||||
<button t-if="thread.allAttachments.length > 0" class="o_ChatterTopbar_button o_ChatterTopbar_buttonToggleAttachments btn btn-light btn-primary" type="button" t-att-disabled="!hasReadAccess" t-att-aria-expanded="attachmentBoxView ? 'true' : 'false'" t-on-click="onClickButtonToggleAttachments">
|
||||
<i class="fa fa-paperclip fa-lg me-1" role="img" aria-label="Attachments"/>
|
||||
<span t-if="!isShowingAttachmentsLoading" class="o_ChatterTopbar_buttonCount o_ChatterTopbar_buttonAttachmentsCount" t-esc="attachmentButtonText"/>
|
||||
<i t-if="isShowingAttachmentsLoading" class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin" aria-label="Attachment counter loading..."/>
|
||||
</button>
|
||||
<t t-if="hasFollowers and thread">
|
||||
<FollowerListMenuView
|
||||
className="'o_ChatterTopbar_followerListMenu'"
|
||||
record="followerListMenuView"
|
||||
/>
|
||||
<div t-if="hasFollowButton" class="o_ChatterTopbar_followButton o_FollowButtonView d-flex">
|
||||
<button t-if="thread and thread.isCurrentPartnerFollowing" class="o_FollowButtonView_unfollow btn px-0" t-att-class="{ 'o_ChatterTopbar_button': chatter, 'o-following text-success': !isUnfollowButtonHighlighted, 'o-unfollow text-warning': isUnfollowButtonHighlighted }" t-att-disabled="isFollowButtonDisabled" t-on-click="onClickUnfollow" t-on-mouseenter="onMouseEnterUnfollow" t-on-mouseleave="onMouseleaveUnfollow">
|
||||
<div class="position-relative">
|
||||
<!-- Hidden element used to set the button maximum size -->
|
||||
<span class="invisible">
|
||||
<i class="fa fa-fw fa fa-eye-slash"/> <t t-out="followingText.length > unfollowingText.length ? followingText : unfollowingText"></t>
|
||||
</span>
|
||||
<span t-if="isUnfollowButtonHighlighted" class="o_FollowButtonView_text position-absolute end-0 top-0 bottom-0 text-warning">
|
||||
<i class="fa fa-fw fa-times"/> <span t-out="unfollowingText"/>
|
||||
</span>
|
||||
<span t-else="" class="o_FollowButtonView_text d-block position-absolute end-0 top-0 bottom-0">
|
||||
<i class="fa fa-fw fa-check"/> <span t-out="followingText"/>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
<button t-else="" class="o_FollowButtonView_follow btn btn-link text-600 pe-0" t-att-disabled="isFollowButtonDisabled" t-on-click="onClickFollow" t-att-class="{ 'o_ChatterTopbar_button': chatter }">
|
||||
Follow
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button t-if="hasTopbarCloseButton" class="o_ChatterTopbar_buttonClose btn btn-dark flex-shrink-0 rounded-bottom-3" aria-label="Close" t-on-click="onClickChatterTopbarClose">
|
||||
<i class="oi oi-large oi-close"/>
|
||||
</button>
|
||||
</div>
|
||||
<ComposerView t-if="composerView"
|
||||
className="'o_Chatter_composer border-bottom'"
|
||||
classNameObj="{ 'o-bordered': hasExternalBorder }"
|
||||
@@ -20,14 +100,43 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="o_Chatter_scrollPanel overflow-auto" t-on-scroll="onScrollScrollPanel" t-ref="scrollPanel">
|
||||
<AttachmentBoxView t-if="attachmentBoxView"
|
||||
className="'o_Chatter_attachmentBox'"
|
||||
record="attachmentBoxView"
|
||||
/>
|
||||
<ActivityBoxView t-if="activityBoxView"
|
||||
className="'o_Chatter_activityBox'"
|
||||
record="activityBoxView"
|
||||
/>
|
||||
<div t-if="hasAttachmentBox" class="o_Chatter_attachmentBox o_AttachmentBoxView position-relative">
|
||||
<div class="o_AttachmentBoxView_title d-flex align-items-center">
|
||||
<hr class="o_AttachmentBoxView_dashedLine flex-grow-1"/>
|
||||
<span class="o_AttachmentBoxView_titleText p-3 fw-bold">
|
||||
Files
|
||||
</span>
|
||||
<hr class="o_AttachmentBoxView_dashedLine flex-grow-1"/>
|
||||
</div>
|
||||
<div class="o_AttachmentBoxView_content d-flex flex-column">
|
||||
<AttachmentList t-if="attachmentList"
|
||||
className="'o_attachmentBox_attachmentList'"
|
||||
record="attachmentList"
|
||||
/>
|
||||
<button class="o_AttachmentBoxView_buttonAdd btn btn-link" type="button" t-on-click="onClickButtonAddAttachments" t-att-disabled="!hasWriteAccess">
|
||||
<i class="fa fa-plus-square"/>
|
||||
Attach files
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="hasActivityBox" class="o_Chatter_activityBox o_ActivityBoxView">
|
||||
<a href="#" role="button" class="o_ActivityBoxView_title btn d-flex align-items-center mt-4 p-0 w-100 fw-bold" t-att-aria-expanded="isActivityListVisible ? 'true' : 'false'" t-on-click="onClickActivityBoxTitle">
|
||||
<hr class="o_ActivityBoxView_titleLine w-auto flex-grow-1 me-3" />
|
||||
<span class="o_ActivityBoxView_titleText">
|
||||
<i class="fa fa-fw" t-att-class="isActivityListVisible ? 'fa-caret-down' : 'fa-caret-right'"/>
|
||||
Planned activities
|
||||
</span>
|
||||
<span t-if="!isActivityListVisible" class="o_ActivityBoxView_titleBadges ms-2">
|
||||
<span t-if="thread.overdueActivities.length > 0" class="o_ActivityBoxView_titleBadge me-1 badge text-bg-danger" t-esc="thread.overdueActivities.length"/>
|
||||
<span t-if="thread.todayActivities.length > 0" class="o_ActivityBoxView_titleBadge me-1 badge text-bg-warning" t-esc="thread.todayActivities.length"/>
|
||||
<span t-if="thread.futureActivities.length > 0" class="o_ActivityBoxView_titleBadge me-1 badge text-bg-success" t-esc="thread.futureActivities.length"/>
|
||||
</span>
|
||||
<hr class="o_ActivityBoxView_titleLine w-auto flex-grow-1 ms-3"/>
|
||||
</a>
|
||||
<div t-if="isActivityListVisible" class="o_ActivityBoxView_activityList">
|
||||
<ActivityView t-foreach="activityViews" t-as="activityView" t-key="activityView.localId" className="'o_ActivityBoxView_activity'" record="activityView"/>
|
||||
</div>
|
||||
</div>
|
||||
<ThreadView t-if="threadView"
|
||||
className="'o_Chatter_thread'"
|
||||
record="threadView"
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ChatterTopbar" owl="1">
|
||||
<div class="o_ChatterTopbar justify-content-between d-flex" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_ChatterTopbar_actions flex-fill d-flex border-transparent">
|
||||
<div class="o_ChatterTopbar_controllers d-flex pe-2" t-if="chatter.threadView">
|
||||
<button class="o_ChatterTopbar_button o_ChatterTopbar_buttonSendMessage btn text-nowrap me-2"
|
||||
type="button"
|
||||
t-att-class="{
|
||||
'o-active btn-odoo': chatter.composerView and !chatter.composerView.composer.isLog,
|
||||
'btn-odoo': !chatter.composerView,
|
||||
'btn-light': chatter.composerView and chatter.composerView.composer.isLog,
|
||||
}"
|
||||
t-att-disabled="!chatter.hasWriteAccess"
|
||||
data-hotkey="m"
|
||||
t-on-click="chatter.onClickSendMessage"
|
||||
>
|
||||
Send message
|
||||
</button>
|
||||
<button class="o_ChatterTopbar_button o_ChatterTopbar_buttonLogNote btn text-nowrap"
|
||||
type="button"
|
||||
t-att-class="{
|
||||
'o-active btn-odoo': chatter.composerView and chatter.composerView.composer.isLog,
|
||||
'btn-light': chatter.composerView and !chatter.composerView.composer.isLog or !chatter.composerView,
|
||||
}"
|
||||
t-att-disabled="!chatter.hasWriteAccess"
|
||||
t-on-click="chatter.onClickLogNote"
|
||||
data-hotkey="shift+m"
|
||||
>
|
||||
Log note
|
||||
</button>
|
||||
</div>
|
||||
<div class="o_ChatterTopbar_tools position-relative d-flex flex-grow-1 border-bottom"
|
||||
t-att-class="{
|
||||
'border-start ps-2': chatter.hasActivities,
|
||||
}"
|
||||
>
|
||||
<button t-if="chatter.hasActivities" class="o_ChatterTopbar_button o_ChatterTopbar_buttonScheduleActivity btn btn-light text-nowrap" type="button" t-att-disabled="!chatter.hasWriteAccess" t-on-click="chatter.onClickScheduleActivity" data-hotkey="shift+a">
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
<span>Activities</span>
|
||||
</button>
|
||||
<div class="flex-grow-1 border-start pe-2"
|
||||
t-att-class="{
|
||||
'ms-2': chatter.hasActivities,
|
||||
}"
|
||||
/>
|
||||
<div class="o_ChatterTopbar_rightSection flex-grow-1 flex-shrink-0 justify-content-end d-flex">
|
||||
<button t-if="chatter.thread.allAttachments.length === 0" class="o_ChatterTopbar_button o_ChatterTopbar_buttonAddAttachments btn btn-light btn-primary" type="button" t-att-disabled="!chatter.hasWriteAccess" t-on-click="chatter.onClickButtonAddAttachments" >
|
||||
<i class="fa fa-paperclip fa-lg me-1" role="img" aria-label="Attachments"/>
|
||||
<i t-if="chatter.isShowingAttachmentsLoading" class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin" aria-label="Attachment counter loading..."/>
|
||||
</button>
|
||||
<button t-if="chatter.thread.allAttachments.length > 0" class="o_ChatterTopbar_button o_ChatterTopbar_buttonToggleAttachments btn btn-light btn-primary" type="button" t-att-disabled="!chatter.hasReadAccess" t-att-aria-expanded="chatter.attachmentBoxView ? 'true' : 'false'" t-on-click="chatter.onClickButtonToggleAttachments">
|
||||
<i class="fa fa-paperclip fa-lg me-1" role="img" aria-label="Attachments"/>
|
||||
<span t-if="!chatter.isShowingAttachmentsLoading" class="o_ChatterTopbar_buttonCount o_ChatterTopbar_buttonAttachmentsCount" t-esc="attachmentButtonText"/>
|
||||
<i t-if="chatter.isShowingAttachmentsLoading" class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin" aria-label="Attachment counter loading..."/>
|
||||
</button>
|
||||
<t t-if="chatter.hasFollowers and chatter.thread">
|
||||
<FollowerListMenuView
|
||||
className="'o_ChatterTopbar_followerListMenu'"
|
||||
record="chatter.followerListMenuView"
|
||||
/>
|
||||
<FollowButtonView t-if="chatter.followButtonView"
|
||||
className="'o_ChatterTopbar_followButton'"
|
||||
record="chatter.followButtonView"
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button t-if="chatter.hasTopbarCloseButton" class="o_ChatterTopbar_buttonClose btn btn-dark flex-shrink-0 rounded-bottom-3" aria-label="Close" t-on-click="chatter.onClickChatterTopbarClose">
|
||||
<i class="oi oi-large oi-close"/>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,3 +0,0 @@
|
||||
.o_EmojiCategoryBarView {
|
||||
background-color: var(--EmojiCategoryBarView-background-color, #{$o-gray-200});
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiCategoryBarView" owl="1">
|
||||
<div class="o_EmojiCategoryBarView d-flex flex-shrink-0 overflow-auto border-end" t-attf-class="{{ className }}" t-ref="root">
|
||||
<EmojiCategoryView t-foreach="emojiCategoryViews" t-as="emojiCategoryView" t-key="emojiCategoryView.localId" record="emojiCategoryView"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiGridItemView" owl="1">
|
||||
<div class="o_EmojiGridItemView d-flex justify-content-center" t-attf-style="width: {{width}}px;">
|
||||
<EmojiView t-if="emojiView" className="'o_EmojiGrid_emoji w-100 h-100'" record="emojiView"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiGridLoadingScreen" owl="1">
|
||||
<div class="o_EmojiGridLoadingScreen d-flex flex-column justify-content-center align-items-center w-100 h-100" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_EmojiGridLoadingScreen_spinner fa fa-circle-o-notch fa-spin fs-1"/>
|
||||
<span class="o_EmojiGridLoadingScreen_text text-muted mt-4" t-out="text"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
<t t-name="mail.EmojiGridRowView" owl="1">
|
||||
<div class="o_EmojiGridRowView">
|
||||
<EmojiGridSectionView t-if="sectionView" record="sectionView"/>
|
||||
<div t-if="hasSection" class="o_EmojiGridSectionView d-flex justify-content-center align-items-end mx-2 text-truncate">
|
||||
<span t-out="category.name"/>
|
||||
</div>
|
||||
<t t-else="">
|
||||
<EmojiGridItemView t-foreach="items" t-as="item" t-key="item" className="'o_EmojiGridRowView_item'" record="item"/>
|
||||
<EmojiView t-foreach="items" t-as="item" t-key="item" className="'o_EmojiGridRowView_item'" record="item"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
|
||||
.o_EmojiGridSearchNoContentView_image {
|
||||
filter: grayscale(1);
|
||||
font-size: 5rem;
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiGridSearchNoContentView" owl="1">
|
||||
<div class="o_EmojiGridSearchNoContentView d-flex flex-column justify-content-center align-items-center w-100 h-100" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_EmojiGridSearchNoContentView_image">😢</div>
|
||||
<span class="o_EmojiGridSearchNoContentView_text text-muted" t-out="text"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiGridSectionView" owl="1">
|
||||
<div class="o_EmojiGridSectionView d-flex justify-content-center align-items-end mx-2 text-truncate">
|
||||
<span t-out="category.name"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -3,9 +3,15 @@
|
||||
|
||||
<t t-name="mail.EmojiGridView" owl="1">
|
||||
<div class="o_EmojiGridView o_EmojiGridView_container" t-attf-style="--rowHeight: {{rowHeight}}px; --itemWidth: {{itemWidth}}px; height: {{height}}px; width: {{width}}px;" t-on-scroll="onScroll" t-ref="containerRef">
|
||||
<EmojiGridLoadingScreen t-if="!this.messaging.emojiRegistry.isLoaded" record="loadingScreenView"/>
|
||||
<EmojiGridSearchNoContentView t-if="searchNoContentView" record="searchNoContentView"/>
|
||||
<div t-if="!searchNoContentView" class="o_EmojiGridView_list" t-attf-style="height: {{listHeight}}px; min-height: {{listHeight}}px;" t-ref="listRef">
|
||||
<div t-if="!this.messaging.emojiRegistry.isLoaded" class="o_EmojiGridLoadingScreen d-flex flex-column justify-content-center align-items-center w-100 h-100">
|
||||
<div class="o_EmojiGridLoadingScreen_spinner fa fa-circle-o-notch fa-spin fs-1"/>
|
||||
<span class="o_EmojiGridLoadingScreen_text text-muted mt-4" t-out="loadingText"/>
|
||||
</div>
|
||||
<div t-if="hasNoSearchResult" class="o_EmojiGridSearchNoContentView d-flex flex-column justify-content-center align-items-center w-100 h-100">
|
||||
<div class="o_EmojiGridSearchNoContentView_image">😢</div>
|
||||
<span class="o_EmojiGridSearchNoContentView_text text-muted" t-out="noSearchResultText"/>
|
||||
</div>
|
||||
<div t-else="" class="o_EmojiGridView_list" t-attf-style="height: {{listHeight}}px; min-height: {{listHeight}}px;" t-ref="listRef">
|
||||
<div t-if="lastRenderedRowIndex >= firstRenderedRowIndex" class="o_EmojiGridView_viewBlock" t-attf-style="top: {{ distanceFromTop - distanceInRowOffset }}px; width: {{ width }}px;" t-ref="viewBlockRef">
|
||||
<EmojiGridRowView t-foreach="renderedRows" t-as="row" t-key="row" record="row"/>
|
||||
</div>
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiPickerHeaderActionListView" owl="1">
|
||||
<div class="o_EmojiPickerHeaderActionListView d-flex" t-attf-class="{{ className }}" t-ref="root">
|
||||
<EmojiPickerHeaderActionView t-foreach="actionViews" t-as="actionView" t-key="actionView" record="actionView"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiPickerHeaderView" owl="1">
|
||||
<div class="o_EmojiPickerHeaderView d-flex bg-100 border-bottom" t-attf-class="{{ className }}" t-ref="root">
|
||||
<EmojiCategoryBarView record="emojiCategoryBarView"/>
|
||||
<span class="flex-grow-1"/>
|
||||
<EmojiPickerHeaderActionListView t-if="actionListView" record="actionListView"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
+9
@@ -13,3 +13,12 @@
|
||||
.o_EmojiSearchBarView_searchInput:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.o_EmojiCategoryBarView {
|
||||
background-color: var(--EmojiCategoryBarView-background-color, #{$o-gray-200});
|
||||
}
|
||||
|
||||
.o_EmojiGridSearchNoContentView_image {
|
||||
filter: grayscale(1);
|
||||
font-size: 5rem;
|
||||
}
|
||||
@@ -3,8 +3,21 @@
|
||||
|
||||
<t t-name="mail.EmojiPickerView" owl="1">
|
||||
<div class="o_EmojiPickerView d-flex flex-column" t-attf-class="{{ className }}" t-ref="root">
|
||||
<EmojiPickerHeaderView record="headerView"/>
|
||||
<EmojiSearchBarView record="emojiSearchBarView"/>
|
||||
<div class="o_EmojiPickerHeaderView d-flex bg-100 border-bottom">
|
||||
<div class="o_EmojiCategoryBarView d-flex flex-shrink-0 overflow-auto border-end">
|
||||
<EmojiCategoryView t-foreach="emojiCategoryViews" t-as="emojiCategoryView" t-key="emojiCategoryView.localId" record="emojiCategoryView"/>
|
||||
</div>
|
||||
<span class="flex-grow-1"/>
|
||||
<div class="o_EmojiPickerHeaderActionListView d-flex">
|
||||
<EmojiPickerHeaderActionView t-foreach="actionViews" t-as="actionView" t-key="actionView" record="actionView"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_EmojiSearchBarView overflow-auto d-flex flex-shrink-0 p-2 flex-column border-bottom">
|
||||
<div class="o_EmojiSearchBarView_inputContainer d-flex flex-grow-1 align-items-center">
|
||||
<input class="o_EmojiSearchBarView_searchInput o_input form-control overflow-auto flex-fill" t-att-placeholder="placeholder" t-on-focusin="onFocusinInput" t-on-focusout="onFocusoutInput" t-on-input="onInput" t-ref="input"/>
|
||||
<i class="o_EmojiSearchBarView_icon oi oi-search ps-2" title="Search..." role="img" aria-label="Search..."/>
|
||||
</div>
|
||||
</div>
|
||||
<EmojiGridView record="emojiGridView"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiSearchBarView" owl="1">
|
||||
<div class="o_EmojiSearchBarView overflow-auto d-flex flex-shrink-0 p-2 flex-column border-bottom" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_EmojiSearchBarView_inputContainer d-flex flex-grow-1 align-items-center">
|
||||
<input class="o_EmojiSearchBarView_searchInput o_input form-control overflow-auto flex-fill" t-att-placeholder="placeholder" t-on-focusin="onFocusinInput" t-on-focusout="onFocusoutInput" t-on-input="onInput" t-ref="input"/>
|
||||
<i class="o_EmojiSearchBarView_icon oi oi-search ps-2" title="Search..." role="img" aria-label="Search..."/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -2,7 +2,9 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.EmojiView" owl="1">
|
||||
<span class="o_EmojiView p-1 fs-3 cursor-pointer" t-att-class="emojiGridViewAsHovered ? 'bg-200' : ''" t-esc="emoji.codepoints" t-on-click="onClick" t-on-mouseenter="onMouseenter" t-on-mouseleave="onMouseleave" t-att-title="emoji.name" t-att-data-source="emoji.sources[0]" t-att-data-codepoints="emoji.codepoints" t-attf-class="{{ className }}" t-ref="root"/>
|
||||
<div class="o_EmojiViewItem d-flex justify-content-center" t-attf-style="width: {{width}}px;">
|
||||
<span class="o_EmojiView p-1 fs-3 cursor-pointer" t-att-class="emojiGridViewAsHovered ? 'bg-200' : ''" t-esc="emoji.codepoints" t-on-click="onClick" t-on-mouseenter="onMouseenter" t-on-mouseleave="onMouseleave" t-att-title="emoji.name" t-att-data-source="emoji.sources[0]" t-att-data-codepoints="emoji.codepoints" t-attf-class="{{ className }}" t-ref="root"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.o_FollowButtonView_unfollow:focus {
|
||||
background-color: $gray-200;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.FollowButtonView" owl="1">
|
||||
<div class="o_FollowButtonView d-flex" t-attf-class="{{ className }}" t-ref="root">
|
||||
<button t-if="chatterOwner.thread and chatterOwner.thread.isCurrentPartnerFollowing" class="o_FollowButtonView_unfollow btn px-0" t-att-class="{ 'o_ChatterTopbar_button': chatterOwner, 'o-following text-success': !isUnfollowButtonHighlighted, 'o-unfollow text-warning': isUnfollowButtonHighlighted }" t-att-disabled="isDisabled" t-on-click="onClickUnfollow" t-on-mouseenter="onMouseEnterUnfollow" t-on-mouseleave="onMouseleaveUnfollow">
|
||||
<div class="position-relative">
|
||||
<!-- Hidden element used to set the button maximum size -->
|
||||
<span class="invisible">
|
||||
<i class="fa fa-fw fa fa-eye-slash"/> <t t-out="followingText.length > unfollowingText.length ? followingText : unfollowingText"></t>
|
||||
</span>
|
||||
<span t-if="isUnfollowButtonHighlighted" class="o_FollowButtonView_text position-absolute end-0 top-0 bottom-0 text-warning">
|
||||
<i class="fa fa-fw fa-times"/> <span t-out="unfollowingText"/>
|
||||
</span>
|
||||
<span t-else="" class="o_FollowButtonView_text d-block position-absolute end-0 top-0 bottom-0">
|
||||
<i class="fa fa-fw fa-check"/> <span t-out="followingText"/>
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
<button t-else="" class="o_FollowButtonView_follow btn btn-link text-600 pe-0" t-att-disabled="isDisabled" t-on-click="onClickFollow" t-att-class="{ 'o_ChatterTopbar_button': chatterOwner }">
|
||||
Follow
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,29 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'ActivityBoxView',
|
||||
template: 'mail.ActivityBoxView',
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on activity box title.
|
||||
*/
|
||||
onClickActivityBoxTitle(ev) {
|
||||
ev.preventDefault();
|
||||
this.update({ isActivityListVisible: !this.isActivityListVisible });
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
activityViews: many('ActivityView', { inverse: 'activityBoxView',
|
||||
compute() {
|
||||
return this.chatter.thread.activities.map(activity => {
|
||||
return { activity };
|
||||
});
|
||||
},
|
||||
}),
|
||||
chatter: one('Chatter', { identifying: true, inverse: 'activityBoxView' }),
|
||||
isActivityListVisible: attr({ default: true }),
|
||||
},
|
||||
});
|
||||
@@ -39,7 +39,7 @@ registerModel({
|
||||
* Handles click on this "Done" button.
|
||||
*/
|
||||
async onClickDone() {
|
||||
const chatter = this.activityViewOwner && this.activityViewOwner.activityBoxView.chatter;
|
||||
const chatter = this.activityViewOwner && this.activityViewOwner.chatterOwner;
|
||||
const reloadFunc = this.reloadFunc;
|
||||
const webRecord = this.webRecord;
|
||||
const thread = this.activity.thread;
|
||||
@@ -60,7 +60,7 @@ registerModel({
|
||||
* Handles click on this "Done & Schedule Next" button.
|
||||
*/
|
||||
async onClickDoneAndScheduleNext() {
|
||||
const chatter = this.activityViewOwner && this.activityViewOwner.activityBoxView.chatter;
|
||||
const chatter = this.activityViewOwner && this.activityViewOwner.chatterOwner;
|
||||
const reloadFunc = this.reloadFunc;
|
||||
const webRecord = this.webRecord;
|
||||
const thread = this.activity.thread;
|
||||
|
||||
@@ -34,7 +34,7 @@ registerModel({
|
||||
* Handles the click on the cancel button
|
||||
*/
|
||||
async onClickCancel() {
|
||||
const { chatter } = this.activityBoxView; // save value before deleting activity
|
||||
const chatter = this.chatterOwner; // save value before deleting activity
|
||||
await this.activity.deleteServerRecord();
|
||||
if (chatter.exists() && chatter.component) {
|
||||
chatter.reloadParentView();
|
||||
@@ -51,7 +51,7 @@ registerModel({
|
||||
* Handles the click on the edit button
|
||||
*/
|
||||
async onClickEdit() {
|
||||
const { chatter } = this.activityBoxView;
|
||||
const chatter = this.chatterOwner;
|
||||
await this.activity.edit();
|
||||
if (chatter.exists() && chatter.component) {
|
||||
chatter.reloadParentView();
|
||||
@@ -70,7 +70,6 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
activity: one('Activity', { identifying: true, inverse: 'activityViews' }),
|
||||
activityBoxView: one('ActivityBoxView', { identifying: true, inverse: 'activityViews' }),
|
||||
/**
|
||||
* Determines whether the details are visible.
|
||||
*/
|
||||
@@ -86,6 +85,7 @@ registerModel({
|
||||
return sprintf(this.env._t("for %s"), this.activity.assignee.nameOrDisplayName);
|
||||
},
|
||||
}),
|
||||
chatterOwner: one('Chatter', { identifying: true, inverse: 'activityViews' }),
|
||||
clockWatcher: one('ClockWatcher', { default: { clock: { frequency: 60 * 1000 } }, inverse: 'activityViewOwner' }),
|
||||
/**
|
||||
* Compute the label for "when" the activity is due.
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useComponentToModel } from '@mail/component_hooks/use_component_to_model';
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'AttachmentBoxView',
|
||||
template: 'mail.AttachmentBoxView',
|
||||
componentSetup() {
|
||||
useComponentToModel({ fieldName: 'component' });
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles click on the "add attachment" button.
|
||||
*/
|
||||
onClickAddAttachment() {
|
||||
this.fileUploader.openBrowserFileUploader();
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
* Determines the attachment list that will be used to display the attachments.
|
||||
*/
|
||||
attachmentList: one('AttachmentList', { inverse: 'attachmentBoxViewOwner',
|
||||
compute() {
|
||||
return (this.chatter.thread && this.chatter.thread.allAttachments.length > 0)
|
||||
? {}
|
||||
: clear();
|
||||
},
|
||||
}),
|
||||
chatter: one('Chatter', { identifying: true, inverse: 'attachmentBoxView' }),
|
||||
/**
|
||||
* States the OWL component displaying this attachment box.
|
||||
*/
|
||||
component: attr(),
|
||||
fileUploader: one('FileUploader', { default: {}, inverse: 'attachmentBoxView', readonly: true, required: true }),
|
||||
},
|
||||
});
|
||||
@@ -54,17 +54,15 @@ registerModel({
|
||||
compute() {
|
||||
if (
|
||||
this.dialogOwner.attachmentCardOwnerAsAttachmentDeleteConfirm &&
|
||||
this.dialogOwner.attachmentCardOwnerAsAttachmentDeleteConfirm.attachmentList.attachmentBoxViewOwner &&
|
||||
this.dialogOwner.attachmentCardOwnerAsAttachmentDeleteConfirm.attachmentList.attachmentBoxViewOwner.chatter
|
||||
this.dialogOwner.attachmentCardOwnerAsAttachmentDeleteConfirm.attachmentList.chatterOwner
|
||||
) {
|
||||
return this.dialogOwner.attachmentCardOwnerAsAttachmentDeleteConfirm.attachmentList.attachmentBoxViewOwner.chatter;
|
||||
return this.dialogOwner.attachmentCardOwnerAsAttachmentDeleteConfirm.attachmentList.chatterOwner;
|
||||
}
|
||||
if (
|
||||
this.dialogOwner.attachmentImageOwnerAsAttachmentDeleteConfirm &&
|
||||
this.dialogOwner.attachmentImageOwnerAsAttachmentDeleteConfirm.attachmentList.attachmentBoxViewOwner &&
|
||||
this.dialogOwner.attachmentImageOwnerAsAttachmentDeleteConfirm.attachmentList.attachmentBoxViewOwner.chatter
|
||||
this.dialogOwner.attachmentImageOwnerAsAttachmentDeleteConfirm.attachmentList.chatterOwner
|
||||
) {
|
||||
return this.dialogOwner.attachmentImageOwnerAsAttachmentDeleteConfirm.attachmentList.attachmentBoxViewOwner.chatter;
|
||||
return this.dialogOwner.attachmentImageOwnerAsAttachmentDeleteConfirm.attachmentList.chatterOwner;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
|
||||
@@ -90,7 +90,7 @@ registerModel({
|
||||
if (this.attachmentList.composerViewOwner) {
|
||||
return 50;
|
||||
}
|
||||
if (this.attachmentList.attachmentBoxViewOwner) {
|
||||
if (this.attachmentList.chatterOwner) {
|
||||
return 160;
|
||||
}
|
||||
if (this.attachmentList.messageViewOwner) {
|
||||
|
||||
@@ -28,9 +28,9 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
* Link with a AttachmentBoxView to handle attachments.
|
||||
* Link with a chatter to handle attachments.
|
||||
*/
|
||||
attachmentBoxViewOwner: one('AttachmentBoxView', { identifying: true, inverse: 'attachmentList' }),
|
||||
chatterOwner: one('Chatter', { identifying: true, inverse: 'attachmentList' }),
|
||||
/**
|
||||
* States the attachment cards that are displaying this nonImageAttachments.
|
||||
*/
|
||||
@@ -56,8 +56,8 @@ registerModel({
|
||||
if (this.messageViewOwner) {
|
||||
return this.messageViewOwner.message.attachments;
|
||||
}
|
||||
if (this.attachmentBoxViewOwner) {
|
||||
return this.attachmentBoxViewOwner.chatter.thread.allAttachments;
|
||||
if (this.chatterOwner) {
|
||||
return this.chatterOwner.thread.allAttachments;
|
||||
}
|
||||
if (this.composerViewOwner && this.composerViewOwner.composer) {
|
||||
return this.composerViewOwner.composer.attachments;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useComponentToModel } from '@mail/component_hooks/use_component_to_model';
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear, insert, link } from '@mail/model/model_field_command';
|
||||
|
||||
const getThreadNextTemporaryId = (function () {
|
||||
@@ -40,6 +40,13 @@ registerModel({
|
||||
isShowingAttachmentsLoading: true,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Handles click on activity box title.
|
||||
*/
|
||||
onClickActivityBoxTitle(ev) {
|
||||
ev.preventDefault();
|
||||
this.update({ isActivityListVisible: !this.isActivityListVisible });
|
||||
},
|
||||
/**
|
||||
* Handles click on the attach button.
|
||||
*/
|
||||
@@ -50,7 +57,7 @@ registerModel({
|
||||
* Handles click on the attachments button.
|
||||
*/
|
||||
onClickButtonToggleAttachments() {
|
||||
this.update({ attachmentBoxView: this.attachmentBoxView ? clear() : {} });
|
||||
this.update({ hasAttachmentBox: this.hasAttachmentBox ? clear() : true });
|
||||
},
|
||||
/**
|
||||
* Handles click on top bar close button.
|
||||
@@ -60,6 +67,15 @@ registerModel({
|
||||
onClickChatterTopbarClose(ev) {
|
||||
this.component.trigger('o-close-chatter');
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickFollow(ev) {
|
||||
if (!this.exists() || !this.thread) {
|
||||
return;
|
||||
}
|
||||
this.thread.follow();
|
||||
},
|
||||
/**
|
||||
* Handles click on "log note" button.
|
||||
*
|
||||
@@ -95,6 +111,34 @@ registerModel({
|
||||
this.showSendMessage();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickUnfollow(ev) {
|
||||
if (!this.exists() || !this.thread) {
|
||||
return;
|
||||
}
|
||||
this.thread.unfollow();
|
||||
this.reloadParentView({ fieldNames: ['message_follower_ids'] });
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onMouseEnterUnfollow(ev) {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isUnfollowButtonHighlighted: true });
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onMouseleaveUnfollow(ev) {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isUnfollowButtonHighlighted: false });
|
||||
},
|
||||
/**
|
||||
* Handles scroll on this scroll panel.
|
||||
*
|
||||
@@ -106,8 +150,8 @@ registerModel({
|
||||
}
|
||||
this.threadView.messageListView.onScroll();
|
||||
},
|
||||
openAttachmentBoxView() {
|
||||
this.update({ attachmentBoxView: {} });
|
||||
openAttachmentBox() {
|
||||
this.update({ hasAttachmentBox: true });
|
||||
},
|
||||
/**
|
||||
* Open a dialog to add partners as followers.
|
||||
@@ -186,7 +230,7 @@ registerModel({
|
||||
this.thread.delete();
|
||||
}
|
||||
this.update({
|
||||
attachmentBoxView: this.isAttachmentBoxVisibleInitially ? {} : clear(),
|
||||
hasAttachmentBox: this.isAttachmentBoxVisibleInitially ? true : clear(),
|
||||
thread: insert({
|
||||
// If the thread was considered to have the activity
|
||||
// mixin once, it will have it forever.
|
||||
@@ -205,7 +249,7 @@ registerModel({
|
||||
});
|
||||
const nextId = getThreadNextTemporaryId();
|
||||
this.update({
|
||||
attachmentBoxView: clear(),
|
||||
hasAttachmentBox: clear(),
|
||||
thread: insert({
|
||||
areAttachmentsLoaded: true,
|
||||
id: nextId,
|
||||
@@ -240,15 +284,41 @@ registerModel({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
activityBoxView: one('ActivityBoxView', { inverse: 'chatter',
|
||||
activityViews: many('ActivityView', { inverse: 'chatterOwner',
|
||||
compute() {
|
||||
if (this.thread && this.thread.hasActivities && this.thread.activities.length > 0) {
|
||||
return {};
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
return clear();
|
||||
return this.thread.activities.map(activity => {
|
||||
return { activity };
|
||||
});
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* Determines the attachment list that will be used to display the attachments.
|
||||
*/
|
||||
attachmentList: one('AttachmentList', { inverse: 'chatterOwner',
|
||||
compute() {
|
||||
return (this.thread && this.thread.allAttachments.length > 0)
|
||||
? {}
|
||||
: clear();
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* Determines the label on the attachment button of the topbar.
|
||||
*/
|
||||
attachmentButtonText: attr({ default: "",
|
||||
compute() {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
const attachments = this.thread.allAttachments;
|
||||
if (attachments.length === 0) {
|
||||
return clear();
|
||||
}
|
||||
return attachments.length;
|
||||
},
|
||||
}),
|
||||
attachmentBoxView: one('AttachmentBoxView', { inverse: 'chatter' }),
|
||||
attachmentsLoaderTimer: one('Timer', { inverse: 'chatterOwnerAsAttachmentsLoader' }),
|
||||
/**
|
||||
* States the OWL Chatter component of this chatter.
|
||||
@@ -275,14 +345,6 @@ registerModel({
|
||||
return this.thread ? {} : clear();
|
||||
},
|
||||
}),
|
||||
followButtonView: one('FollowButtonView', { inverse: 'chatterOwner',
|
||||
compute() {
|
||||
if (this.hasFollowers && this.thread && (!this.thread.channel || this.thread.channel.channel_type !== 'chat')) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
followerListMenuView: one('FollowerListMenuView', { inverse: 'chatterOwner',
|
||||
compute() {
|
||||
if (this.hasFollowers && this.thread) {
|
||||
@@ -291,11 +353,36 @@ registerModel({
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* Text displayed by the follow button when not already followed.
|
||||
*/
|
||||
followingText: attr({
|
||||
compute() {
|
||||
return this.env._t("Following");
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* Determines whether `this` should display an activity box.
|
||||
*/
|
||||
hasActivities: attr({ default: true }),
|
||||
hasActivityBox: attr({ default: false,
|
||||
compute() {
|
||||
if (this.thread && this.thread.hasActivities && this.thread.activities.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
hasAttachmentBox: attr({ default: false }),
|
||||
hasExternalBorder: attr({ default: true }),
|
||||
hasFollowButton: attr({ default: false,
|
||||
compute() {
|
||||
if (this.hasFollowers && this.thread && (!this.thread.channel || this.thread.channel.channel_type !== 'chat')) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* Determines whether `this` should display followers menu.
|
||||
*/
|
||||
@@ -328,6 +415,11 @@ registerModel({
|
||||
return Boolean(this.thread && this.hasMessageList);
|
||||
},
|
||||
}),
|
||||
hasTopBar: attr({ default: false,
|
||||
compute() {
|
||||
return this.thread ? true : clear();
|
||||
},
|
||||
}),
|
||||
hasWriteAccess: attr({
|
||||
compute() {
|
||||
return Boolean(this.thread && !this.thread.isTemporary && this.thread.hasWriteAccess);
|
||||
@@ -340,10 +432,16 @@ registerModel({
|
||||
* of this record.
|
||||
*/
|
||||
id: attr({ identifying: true }),
|
||||
isActivityListVisible: attr({ default: true }),
|
||||
/**
|
||||
* Determiners whether the attachment box is visible initially.
|
||||
*/
|
||||
isAttachmentBoxVisibleInitially: attr({ default: false }),
|
||||
isFollowButtonDisabled: attr({
|
||||
compute() {
|
||||
return !this.hasReadAccess;
|
||||
},
|
||||
}),
|
||||
isInFormSheetBg: attr({ default: false }),
|
||||
isPreparingAttachmentsLoading: attr({ default: false,
|
||||
compute() {
|
||||
@@ -351,6 +449,7 @@ registerModel({
|
||||
},
|
||||
}),
|
||||
isShowingAttachmentsLoading: attr({ default: false }),
|
||||
isUnfollowButtonHighlighted: attr({ default: false }),
|
||||
scrollPanelRef: attr({ ref: 'scrollPanel' }),
|
||||
/**
|
||||
* Determines whether the view should reload after file changed in this chatter,
|
||||
@@ -392,9 +491,12 @@ registerModel({
|
||||
};
|
||||
},
|
||||
}),
|
||||
topbar: one('ChatterTopbar', { inverse: 'chatter',
|
||||
/**
|
||||
* Text displayed by the follow button when already followed.
|
||||
*/
|
||||
unfollowingText: attr({
|
||||
compute() {
|
||||
return this.thread ? {} : clear();
|
||||
return this.env._t("Unfollow");
|
||||
},
|
||||
}),
|
||||
useDragVisibleDropZone: one('UseDragVisibleDropZone', { default: {}, inverse: 'chatterOwner', readonly: true, required: true }),
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'ChatterTopbar',
|
||||
template: 'mail.ChatterTopbar',
|
||||
fields: {
|
||||
/**
|
||||
* Determines the label on the attachment button of the topbar.
|
||||
*/
|
||||
attachmentButtonText: attr({ default: "",
|
||||
compute() {
|
||||
if (!this.chatter || !this.chatter.thread) {
|
||||
return clear();
|
||||
}
|
||||
const attachments = this.chatter.thread.allAttachments;
|
||||
if (attachments.length === 0) {
|
||||
return clear();
|
||||
}
|
||||
return attachments.length;
|
||||
},
|
||||
}),
|
||||
chatter: one('Chatter', { identifying: true, inverse: 'topbar' }),
|
||||
},
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { many, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiCategoryBarView',
|
||||
template: 'mail.EmojiCategoryBarView',
|
||||
fields: {
|
||||
emojiCategoryViews: many('EmojiCategoryView', { inverse: 'emojiCategoryBarViewOwner',
|
||||
compute() {
|
||||
if (!this.emojiPickerView) {
|
||||
return clear();
|
||||
}
|
||||
return this.emojiPickerView.categories.map(category => ({ viewCategory: category }));
|
||||
},
|
||||
}),
|
||||
emojiPickerHeaderViewOwner: one('EmojiPickerHeaderView', { identifying: true, inverse: 'emojiCategoryBarView' }),
|
||||
emojiPickerView: one('EmojiPickerView', { related: 'emojiPickerHeaderViewOwner.emojiPickerViewOwner' }),
|
||||
},
|
||||
});
|
||||
@@ -11,8 +11,8 @@ registerModel({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClick() {
|
||||
this.emojiPickerView.emojiSearchBarView.reset();
|
||||
this.emojiPickerView.emojiGridView.update({ categorySelectedByUser: this.viewCategory });
|
||||
this.emojiPickerViewOwner.reset();
|
||||
this.emojiPickerViewOwner.emojiGridView.update({ categorySelectedByUser: this.viewCategory });
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -35,8 +35,7 @@ registerModel({
|
||||
},
|
||||
fields: {
|
||||
category: one('EmojiCategory', { related: 'viewCategory.category' }),
|
||||
emojiCategoryBarViewOwner: one('EmojiCategoryBarView', { identifying: true, inverse: 'emojiCategoryViews' }),
|
||||
emojiPickerView: one('EmojiPickerView', { related: 'emojiCategoryBarViewOwner.emojiPickerView' }),
|
||||
emojiPickerViewOwner: one('EmojiPickerView', { identifying: true, inverse: 'emojiCategoryViews' }),
|
||||
isActive: attr({
|
||||
compute() {
|
||||
return Boolean(this.viewCategory.emojiPickerViewAsActive);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiGridItemView',
|
||||
template: 'mail.EmojiGridItemView',
|
||||
fields: {
|
||||
emojiOrEmojiInCategory: one('EmojiOrEmojiInCategory', { identifying: true, inverse: 'emojiGridItemViews' }),
|
||||
emojiGridRowViewOwner: one('EmojiGridRowView', { identifying: true, inverse: 'items' }),
|
||||
emojiView: one('EmojiView', { inverse: 'emojiGridItemViewOwner',
|
||||
compute() {
|
||||
if (this.emojiOrEmojiInCategory.emoji) {
|
||||
return { emoji: this.emojiOrEmojiInCategory.emoji };
|
||||
}
|
||||
if (this.emojiOrEmojiInCategory.emojiInCategory) {
|
||||
return { emoji: this.emojiOrEmojiInCategory.emojiInCategory.emoji };
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
width: attr({ default: 0,
|
||||
compute() {
|
||||
if (!this.emojiGridRowViewOwner.emojiGridViewOwner) {
|
||||
return clear();
|
||||
}
|
||||
return this.emojiGridRowViewOwner.emojiGridViewOwner.itemWidth;
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiGridLoadingScreen',
|
||||
template: 'mail.EmojiGridLoadingScreen',
|
||||
fields: {
|
||||
emojiGridViewOwner: one('EmojiGridView', { identifying: true, inverse: 'loadingScreenView' }),
|
||||
text: attr({
|
||||
compute() {
|
||||
return this.env._t("Loading...");
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -8,17 +8,18 @@ registerModel({
|
||||
name: 'EmojiGridRowView',
|
||||
template: 'mail.EmojiGridRowView',
|
||||
fields: {
|
||||
category: one('EmojiCategory', { related: 'viewCategory.category' }),
|
||||
emojiGridViewOwner: one('EmojiGridView', { related: 'emojiGridViewRowRegistryOwner.emojiGridViewOwner' }),
|
||||
index: attr({ identifying: true }),
|
||||
items: many('EmojiGridItemView', { inverse: 'emojiGridRowViewOwner' }),
|
||||
sectionView: one('EmojiGridSectionView', { inverse: 'emojiGridRowViewOwner',
|
||||
hasSection: attr({ default: false,
|
||||
compute() {
|
||||
if (this.viewCategory) {
|
||||
return {};
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
index: attr({ identifying: true }),
|
||||
items: many('EmojiView', { inverse: 'emojiGridRowViewOwner' }),
|
||||
emojiGridViewRowRegistryOwner: one('EmojiGridViewRowRegistry', { identifying: true, inverse: 'rows' }),
|
||||
viewCategory: one('EmojiPickerView.Category', { inverse: 'emojiGridRowView' }),
|
||||
},
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiGridSearchNoContentView',
|
||||
template: 'mail.EmojiGridSearchNoContentView',
|
||||
fields: {
|
||||
emojiGridViewOwner: one('EmojiGridView', { identifying: true, inverse: 'searchNoContentView' }),
|
||||
text: attr({
|
||||
compute() {
|
||||
return this.env._t("No emoji match your search");
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiGridSectionView',
|
||||
template: 'mail.EmojiGridSectionView',
|
||||
fields: {
|
||||
category: one('EmojiCategory', { related: 'viewCategory.category' }),
|
||||
emojiGridRowViewOwner: one('EmojiGridRowView', { identifying: true, inverse: 'sectionView' }),
|
||||
viewCategory: one('EmojiPickerView.Category', { related: 'emojiGridRowViewOwner.viewCategory' }),
|
||||
},
|
||||
});
|
||||
@@ -24,7 +24,7 @@ registerModel({
|
||||
onComponentUpdate() {
|
||||
if (
|
||||
this.categorySelectedByUser &&
|
||||
this.emojiPickerViewOwner.emojiSearchBarView.currentSearch === ""
|
||||
this.emojiPickerViewOwner.currentSearch === ""
|
||||
) {
|
||||
this.doJumpToCategorySelectedByUser();
|
||||
}
|
||||
@@ -53,7 +53,7 @@ registerModel({
|
||||
* Filters emoji according to the current search terms.
|
||||
*/
|
||||
_filterEmoji(emoji) {
|
||||
return (emoji._isStringInEmojiKeywords(this.emojiPickerViewOwner.emojiSearchBarView.currentSearch));
|
||||
return (emoji._isStringInEmojiKeywords(this.emojiPickerViewOwner.currentSearch));
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
@@ -96,6 +96,15 @@ registerModel({
|
||||
);
|
||||
},
|
||||
}),
|
||||
hasNoSearchResult: attr({
|
||||
compute() {
|
||||
if (this.emojiPickerViewOwner.currentSearch !== "" && this.rows.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
default: false,
|
||||
}),
|
||||
height: attr({
|
||||
compute() {
|
||||
return this.rowHeight * 9.5;
|
||||
@@ -121,15 +130,17 @@ registerModel({
|
||||
},
|
||||
}),
|
||||
listRef: attr({ ref: 'listRef' }),
|
||||
loadingScreenView: one('EmojiGridLoadingScreen', { inverse: 'emojiGridViewOwner',
|
||||
loadingText: attr({
|
||||
compute() {
|
||||
if (!this.messaging.emojiRegistry.isLoaded) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
return this.env._t("Loading...");
|
||||
},
|
||||
}),
|
||||
nonSearchRowRegistry: one('EmojiGridViewRowRegistry', { default: {}, inverse: 'emojiGridViewOwnerAsNonSearch' }),
|
||||
noSearchResultText: attr({
|
||||
compute() {
|
||||
return this.env._t("No emoji match your search");
|
||||
},
|
||||
}),
|
||||
onScrollThrottle: one('Throttle', { inverse: 'emojiGridViewAsOnScroll',
|
||||
compute() {
|
||||
return { func: () => this.update({ scrollRecomputeCount: increment() }) };
|
||||
@@ -159,7 +170,7 @@ registerModel({
|
||||
rowHeight: attr({ default: 30 }),
|
||||
rows: many('EmojiGridRowView', {
|
||||
compute() {
|
||||
if (this.emojiPickerViewOwner.emojiSearchBarView.currentSearch !== "") {
|
||||
if (this.emojiPickerViewOwner.currentSearch !== "") {
|
||||
return this.searchRowRegistry.rows;
|
||||
}
|
||||
return this.nonSearchRowRegistry.rows;
|
||||
@@ -188,14 +199,6 @@ registerModel({
|
||||
}),
|
||||
scrollbarThresholdWidth: attr({ default: 15 }),
|
||||
scrollRecomputeCount: attr({ default: 0 }),
|
||||
searchNoContentView: one('EmojiGridSearchNoContentView', { inverse: 'emojiGridViewOwner',
|
||||
compute() {
|
||||
if (this.emojiPickerViewOwner.emojiSearchBarView.currentSearch !== "" && this.rows.length === 0) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
searchRowRegistry: one('EmojiGridViewRowRegistry', { default: {}, inverse: 'emojiGridViewOwnerAsSearch' }),
|
||||
viewBlockRef: attr({ ref: 'viewBlockRef' }),
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,7 @@ registerModel({
|
||||
return value;
|
||||
},
|
||||
computeSearchRows() {
|
||||
if (this.emojiGridViewOwner.emojiPickerViewOwner.emojiSearchBarView.currentSearch === "") {
|
||||
if (this.emojiGridViewOwner.emojiPickerViewOwner.currentSearch === "") {
|
||||
return clear();
|
||||
}
|
||||
const emojis = this.messaging.emojiRegistry.allEmojis.filter(this.emojiGridViewOwner._filterEmoji);
|
||||
|
||||
@@ -9,6 +9,6 @@ registerModel({
|
||||
fields: {
|
||||
emoji: one('Emoji', { identifying: true, inverse: 'emojiOrEmojiInCategory' }),
|
||||
emojiInCategory: one('EmojiInCategory', { identifying: true, inverse: 'emojiOrEmojiInCategory' }),
|
||||
emojiGridItemViews: many('EmojiGridItemView', { inverse: 'emojiOrEmojiInCategory' }),
|
||||
emojiViews: many('EmojiView', { inverse: 'emojiOrEmojiInCategory' }),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { many, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiPickerHeaderActionListView',
|
||||
template: 'mail.EmojiPickerHeaderActionListView',
|
||||
fields: {
|
||||
__dummyActionView: one('EmojiPickerHeaderActionView', { inverse: '__ownerAsDummy' }),
|
||||
actionViews: many('EmojiPickerHeaderActionView', { inverse: 'owner',
|
||||
sort: [['smaller-first', 'sequence']],
|
||||
}),
|
||||
emojiPickerView: one('EmojiPickerView', { related: 'owner.emojiPickerViewOwner' }),
|
||||
owner: one('EmojiPickerHeaderView', { identifying: true, inverse: 'actionListView' }),
|
||||
},
|
||||
});
|
||||
@@ -10,7 +10,7 @@ registerModel({
|
||||
identifyingMode: 'xor',
|
||||
fields: {
|
||||
// dummy identifying field, so that it works without defining one initially in mail
|
||||
__ownerAsDummy: one('EmojiPickerHeaderActionListView', { identifying: true, inverse: '__dummyActionView' }),
|
||||
__ownerAsDummy: one('EmojiPickerView', { identifying: true, inverse: '__dummyActionView', }),
|
||||
content: one('Record', { required: true,
|
||||
compute() {
|
||||
return clear();
|
||||
@@ -21,7 +21,7 @@ registerModel({
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
owner: one('EmojiPickerHeaderActionListView', { inverse: 'actionViews', required: true,
|
||||
owner: one('EmojiPickerView', { inverse: 'actionViews', required: true,
|
||||
compute() {
|
||||
return clear();
|
||||
},
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiPickerHeaderView',
|
||||
template: 'mail.EmojiPickerHeaderView',
|
||||
fields: {
|
||||
actionListView: one('EmojiPickerHeaderActionListView', { default: {}, isCausal: true, inverse: 'owner' }),
|
||||
emojiCategoryBarView: one('EmojiCategoryBarView', { default: {}, inverse: 'emojiPickerHeaderViewOwner', readonly: true, required: true }),
|
||||
emojiPickerViewOwner: one('EmojiPickerView', { identifying: true, inverse: 'headerView' }),
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useComponentToModel } from '@mail/component_hooks/use_component_to_model';
|
||||
import { useUpdateToModel } from '@mail/component_hooks/use_update_to_model';
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, many, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
@@ -10,6 +11,7 @@ registerModel({
|
||||
template: 'mail.EmojiPickerView',
|
||||
componentSetup() {
|
||||
useComponentToModel({ fieldName: 'component' });
|
||||
useUpdateToModel({ methodName: 'onComponentUpdate', modelName: 'EmojiPickerView' });
|
||||
},
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
@@ -17,13 +19,69 @@ registerModel({
|
||||
return;
|
||||
}
|
||||
this.messaging.emojiRegistry.loadEmojiData();
|
||||
if (!this.messaging.device.isSmall) {
|
||||
this.update({ isDoFocus: true });
|
||||
}
|
||||
},
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles OWL update on the search bar.
|
||||
*/
|
||||
onComponentUpdate() {
|
||||
this._handleFocus();
|
||||
},
|
||||
onFocusinInput() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isFocused: true });
|
||||
},
|
||||
onFocusoutInput() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isFocused: false });
|
||||
},
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
onInput() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ currentSearch: this.inputRef.el.value });
|
||||
},
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
reset() {
|
||||
this.update({ currentSearch: "" });
|
||||
this.inputRef.el.value = "";
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_handleFocus() {
|
||||
if (this.isDoFocus) {
|
||||
if (!this.inputRef.el) {
|
||||
return;
|
||||
}
|
||||
this.update({ isDoFocus: false });
|
||||
this.inputRef.el.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
__dummyActionView: one('EmojiPickerHeaderActionView', { inverse: '__ownerAsDummy' }),
|
||||
actionViews: many('EmojiPickerHeaderActionView', {
|
||||
inverse: 'owner',
|
||||
sort: [['smaller-first', 'sequence']],
|
||||
}),
|
||||
activeCategoryByGridViewScroll: one('EmojiPickerView.Category'),
|
||||
activeCategory: one('EmojiPickerView.Category', {
|
||||
compute() {
|
||||
if (this.emojiSearchBarView.currentSearch !== "") {
|
||||
if (this.currentSearch !== "") {
|
||||
return clear();
|
||||
}
|
||||
if (this.activeCategoryByGridViewScroll) {
|
||||
@@ -41,6 +99,8 @@ registerModel({
|
||||
return this.messaging.emojiRegistry.allCategories.map(category => ({ category }));
|
||||
},
|
||||
}),
|
||||
component: attr(),
|
||||
currentSearch: attr({ default: "" }),
|
||||
defaultActiveCategory: one('EmojiPickerView.Category', {
|
||||
compute() {
|
||||
if (this.categories.length === 0) {
|
||||
@@ -49,10 +109,20 @@ registerModel({
|
||||
return this.categories[0];
|
||||
},
|
||||
}),
|
||||
emojiCategoryViews: many('EmojiCategoryView', { inverse: 'emojiPickerViewOwner',
|
||||
compute() {
|
||||
return this.categories.map(category => ({ viewCategory: category }));
|
||||
},
|
||||
}),
|
||||
emojiGridView: one('EmojiGridView', { default: {}, inverse: 'emojiPickerViewOwner', readonly: true, required: true }),
|
||||
emojiSearchBarView: one('EmojiSearchBarView', { default: {}, inverse: 'emojiPickerView', readonly: true }),
|
||||
headerView: one('EmojiPickerHeaderView', { default: {}, inverse: 'emojiPickerViewOwner', readonly: true, required: true }),
|
||||
inputRef: attr({ ref: 'input' }),
|
||||
isDoFocus: attr({ default: false }),
|
||||
isFocused: attr({ default: false }),
|
||||
placeholder: attr({ required: true,
|
||||
compute() {
|
||||
return this.env._t("Search an emoji");
|
||||
},
|
||||
}),
|
||||
popoverViewOwner: one('PopoverView', { identifying: true, inverse: 'emojiPickerView' }),
|
||||
component: attr(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useUpdateToModel } from '@mail/component_hooks/use_update_to_model';
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'EmojiSearchBarView',
|
||||
template: 'mail.EmojiSearchBarView',
|
||||
componentSetup() {
|
||||
useUpdateToModel({ methodName: 'onComponentUpdate', modelName: 'EmojiSearchBarView' });
|
||||
},
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
if (!this.messaging.device.isSmall) {
|
||||
this.update({ isDoFocus: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
recordMethods: {
|
||||
/**
|
||||
* Handles OWL update on this EmojiSearchBarView component.
|
||||
*/
|
||||
onComponentUpdate() {
|
||||
this._handleFocus();
|
||||
},
|
||||
onFocusinInput() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isFocused: true });
|
||||
},
|
||||
onFocusoutInput() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isFocused: false });
|
||||
},
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
onInput() {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({
|
||||
currentSearch: this.inputRef.el.value,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
reset() {
|
||||
this.update({ currentSearch: "" });
|
||||
this.inputRef.el.value = "";
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_handleFocus() {
|
||||
if (this.isDoFocus) {
|
||||
if (!this.inputRef.el) {
|
||||
return;
|
||||
}
|
||||
this.update({ isDoFocus: false });
|
||||
this.inputRef.el.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
currentSearch: attr({ default: "" }),
|
||||
emojiPickerView: one("EmojiPickerView", { identifying: true, inverse: "emojiSearchBarView" }),
|
||||
inputRef: attr({ ref: 'input' }),
|
||||
isDoFocus: attr({ default: false }),
|
||||
isFocused: attr({ default: false }),
|
||||
placeholder: attr({ required: true,
|
||||
compute() {
|
||||
return this.env._t("Search an emoji");
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { one } from '@mail/model/model_field';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
@@ -12,7 +12,7 @@ registerModel({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClick(ev) {
|
||||
if (!this.emojiGridItemViewOwner.emojiGridRowViewOwner) {
|
||||
if (!this.emojiGridRowViewOwner) {
|
||||
return;
|
||||
}
|
||||
if (this.emojiPickerViewOwner.popoverViewOwner.messageActionViewOwnerAsReaction) {
|
||||
@@ -31,7 +31,7 @@ registerModel({
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ emojiGridViewAsHovered: this.emojiGridItemViewOwner.emojiGridRowViewOwner.emojiGridViewOwner });
|
||||
this.update({ emojiGridViewAsHovered: this.emojiGridRowViewOwner.emojiGridViewOwner });
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -44,13 +44,32 @@ registerModel({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
emoji: one('Emoji', { identifying: true, inverse: 'emojiViews' }),
|
||||
emojiGridItemViewOwner: one('EmojiGridItemView', { identifying: true, inverse: 'emojiView' }),
|
||||
emoji: one('Emoji', { inverse: 'emojiViews',
|
||||
compute() {
|
||||
if (this.emojiOrEmojiInCategory.emoji) {
|
||||
return this.emojiOrEmojiInCategory.emoji;
|
||||
}
|
||||
if (this.emojiOrEmojiInCategory.emojiInCategory) {
|
||||
return this.emojiOrEmojiInCategory.emojiInCategory.emoji;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
}),
|
||||
emojiGridViewAsHovered: one('EmojiGridView', { inverse: 'hoveredEmojiView' }),
|
||||
emojiOrEmojiInCategory: one('EmojiOrEmojiInCategory', { identifying: true, inverse: 'emojiViews' }),
|
||||
emojiGridRowViewOwner: one('EmojiGridRowView', { identifying: true, inverse: 'items' }),
|
||||
emojiPickerViewOwner: one('EmojiPickerView', {
|
||||
compute() {
|
||||
return this.emojiGridItemViewOwner.emojiGridRowViewOwner.emojiGridViewOwner.emojiPickerViewOwner;
|
||||
return this.emojiGridRowViewOwner.emojiGridViewOwner.emojiPickerViewOwner;
|
||||
}
|
||||
})
|
||||
}),
|
||||
width: attr({ default: 0,
|
||||
compute() {
|
||||
if (!this.emojiGridRowViewOwner.emojiGridViewOwner) {
|
||||
return clear();
|
||||
}
|
||||
return this.emojiGridRowViewOwner.emojiGridViewOwner.itemWidth;
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -43,8 +43,8 @@ registerModel({
|
||||
if (this.fileInput && this.fileInput.el) {
|
||||
this.fileInput.el.value = '';
|
||||
}
|
||||
if (this.chatterOwner && !this.chatterOwner.attachmentBoxView) {
|
||||
this.chatterOwner.openAttachmentBoxView();
|
||||
if (this.chatterOwner && !this.chatterOwner.hasAttachmentBox) {
|
||||
this.chatterOwner.openAttachmentBox();
|
||||
}
|
||||
this.messaging.messagingBus.trigger('o-file-uploader-upload', { files });
|
||||
},
|
||||
@@ -100,8 +100,7 @@ registerModel({
|
||||
const thread = this.thread; // save before async
|
||||
const chatter = (
|
||||
(this.chatterOwner) ||
|
||||
(this.attachmentBoxView && this.attachmentBoxView.chatter) ||
|
||||
(this.activityView && this.activityView.activityBoxView.chatter)
|
||||
(this.activityView && this.activityView.chatterOwner)
|
||||
); // save before async
|
||||
const activity = (
|
||||
this.activityView && this.activityView.activity ||
|
||||
@@ -167,7 +166,6 @@ registerModel({
|
||||
fields: {
|
||||
activityListViewItemOwner: one('ActivityListViewItem', { identifying: true, inverse: 'fileUploader' }),
|
||||
activityView: one('ActivityView', { identifying: true, inverse: 'fileUploader' }),
|
||||
attachmentBoxView: one('AttachmentBoxView', { identifying: true, inverse: 'fileUploader' }),
|
||||
chatterOwner: one('Chatter', { identifying: true, inverse: 'fileUploader' }),
|
||||
composerView: one('ComposerView', { identifying: true, inverse: 'fileUploader' }),
|
||||
fileInput: attr({
|
||||
@@ -192,9 +190,6 @@ registerModel({
|
||||
if (this.activityListViewItemOwner) {
|
||||
return this.activityListViewItemOwner.activity.thread;
|
||||
}
|
||||
if (this.attachmentBoxView) {
|
||||
return this.attachmentBoxView.chatter.thread;
|
||||
}
|
||||
if (this.chatterOwner) {
|
||||
return this.chatterOwner.thread;
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'FollowButtonView',
|
||||
template: 'mail.FollowButtonView',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickFollow(ev) {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!this.chatterOwner || !this.chatterOwner.thread) {
|
||||
return;
|
||||
}
|
||||
this.chatterOwner.thread.follow();
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickUnfollow(ev) {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!this.chatterOwner || !this.chatterOwner.thread) {
|
||||
return;
|
||||
}
|
||||
this.chatterOwner.thread.unfollow();
|
||||
this.chatterOwner.reloadParentView({ fieldNames: ['message_follower_ids'] });
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onMouseEnterUnfollow(ev) {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isUnfollowButtonHighlighted: true });
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onMouseleaveUnfollow(ev) {
|
||||
if (!this.exists()) {
|
||||
return;
|
||||
}
|
||||
this.update({ isUnfollowButtonHighlighted: false });
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
chatterOwner: one('Chatter', { identifying: true, inverse: 'followButtonView' }),
|
||||
followingText: attr({
|
||||
compute() {
|
||||
return this.env._t("Following");
|
||||
},
|
||||
}),
|
||||
isDisabled: attr({
|
||||
compute() {
|
||||
if (!this.chatterOwner) {
|
||||
return clear();
|
||||
}
|
||||
return !this.chatterOwner.hasReadAccess;
|
||||
},
|
||||
}),
|
||||
isUnfollowButtonHighlighted: attr({ default: false }),
|
||||
unfollowingText: attr({
|
||||
compute() {
|
||||
return this.env._t("Unfollow");
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -750,7 +750,7 @@ QUnit.test('activity upload document is available', async function (assert) {
|
||||
"should have activity upload button"
|
||||
);
|
||||
assert.ok(
|
||||
messaging.models['Chatter'].all()[0].activityBoxView.activityViews[0].fileUploader,
|
||||
messaging.models['Chatter'].all()[0].activityViews[0].fileUploader,
|
||||
"should have a file uploader"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ QUnit.test('base empty rendering', async function (assert) {
|
||||
"should have a button add"
|
||||
);
|
||||
assert.ok(
|
||||
messaging.models['Chatter'].all()[0].attachmentBoxView.fileUploader,
|
||||
messaging.models['Chatter'].all()[0].fileUploader,
|
||||
"should have a file uploader"
|
||||
);
|
||||
assert.strictEqual(
|
||||
@@ -90,7 +90,7 @@ QUnit.test('base non-empty rendering', async function (assert) {
|
||||
"should have a button add"
|
||||
);
|
||||
assert.ok(
|
||||
messaging.models['Chatter'].all()[0].attachmentBoxView.fileUploader,
|
||||
messaging.models['Chatter'].all()[0].fileUploader,
|
||||
"should have a file uploader"
|
||||
);
|
||||
assert.strictEqual(
|
||||
|
||||
@@ -9,7 +9,7 @@ registerPatch({
|
||||
* Handles click on the "grant access" button.
|
||||
*/
|
||||
async onGrantAccess(ev) {
|
||||
const { chatter } = this.activityBoxView; // save value before deleting activity
|
||||
const chatter = this.chatterOwner; // save value before deleting activity
|
||||
await this.messaging.rpc({
|
||||
model: 'slide.channel',
|
||||
method: 'action_grant_access',
|
||||
@@ -25,7 +25,7 @@ registerPatch({
|
||||
* Handles click on the "refuse access" button.
|
||||
*/
|
||||
async onRefuseAccess(ev) {
|
||||
const { chatter } = this.activityBoxView; // save value before deleting activity
|
||||
const chatter = this.chatterOwner; // save value before deleting activity
|
||||
await this.messaging.rpc({
|
||||
model: 'slide.channel',
|
||||
method: 'action_refuse_access',
|
||||
|
||||
Reference in New Issue
Block a user