[IMP] mail: make emoji picker available in knowledge

Introduce header actions so that emoji picker in knowledge
can have an action to remove emoji.

Task-2980528

closes odoo/odoo#100010

Related: odoo/enterprise#31231
Signed-off-by: Louis Wicket (wil) <wil@odoo.com>
This commit is contained in:
Alexandre Kühn
2022-09-16 14:25:29 +02:00
parent f9d4c74de9
commit e975a9849e
20 changed files with 226 additions and 120 deletions
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="mail.EmojiCategoryBarView" owl="1">
<div class="o_EmojiCategoryBarView d-flex flex-shrink-0 overflow-auto bg-100 border-bottom" t-attf-class="{{ className }}" t-ref="root">
<div class="o_EmojiCategoryBarView d-flex flex-shrink-0 overflow-auto" t-attf-class="{{ className }}" t-ref="root">
<t t-foreach="emojiCategoryBarView.emojiCategoryViews" t-as="emojiCategoryView" t-key="emojiCategoryView.localId">
<EmojiCategoryView record="emojiCategoryView"/>
</t>
@@ -0,0 +1,23 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class EmojiPickerHeaderActionListView extends Component {
/**
* @returns {EmojiPickerHeaderActionListView}
*/
get emojiPickerHeaderActionListView() {
return this.props.record;
}
}
Object.assign(EmojiPickerHeaderActionListView, {
props: { record: Object },
template: 'mail.EmojiPickerHeaderActionListView',
});
registerMessagingComponent(EmojiPickerHeaderActionListView);
@@ -0,0 +1,10 @@
<?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="emojiPickerHeaderActionListView.actionViews" t-as="actionView" t-key="actionView" record="actionView"/>
</div>
</t>
</templates>
@@ -0,0 +1,23 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class EmojiPickerHeaderActionView extends Component {
/**
* @returns {EmojiPickerHeaderActionView}
*/
get emojiPickerHeaderActionView() {
return this.props.record;
}
}
Object.assign(EmojiPickerHeaderActionView, {
props: { record: Object },
template: 'mail.EmojiPickerHeaderActionView',
});
registerMessagingComponent(EmojiPickerHeaderActionView);
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.EmojiPickerHeaderActionView" owl="1">
<div class="o_EmojiPickerHeaderActionView d-flex" t-attf-class="{{ className }}" t-ref="root">
<t
t-component="constructor.components[emojiPickerHeaderActionView.contentComponentName]"
t-props="{
className: `o_EmojiPickerHeaderActionView_content`,
record: emojiPickerHeaderActionView.content,
}"
/>
</div>
</t>
</templates>
@@ -0,0 +1,23 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class EmojiPickerHeaderView extends Component {
/**
* @returns {EmojiPickerHeaderView}
*/
get emojiPickerHeaderView() {
return this.props.record;
}
}
Object.assign(EmojiPickerHeaderView, {
props: { record: Object },
template: 'mail.EmojiPickerHeaderView',
});
registerMessagingComponent(EmojiPickerHeaderView);
@@ -0,0 +1,12 @@
<?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="emojiPickerHeaderView.emojiCategoryBarView"/>
<span class="flex-grow-1"/>
<EmojiPickerHeaderActionListView t-if="emojiPickerHeaderView.actionListView" record="emojiPickerHeaderView.actionListView"/>
</div>
</t>
</templates>
@@ -3,7 +3,7 @@
<t t-name="mail.EmojiPickerView" owl="1">
<div class="o_EmojiPickerView d-flex flex-column" t-attf-class="{{ className }}" t-ref="root">
<EmojiCategoryBarView record="emojiPickerView.emojiCategoryBarView"/>
<EmojiPickerHeaderView record="emojiPickerView.headerView"/>
<EmojiSearchBarView record="emojiPickerView.emojiSearchBarView"/>
<EmojiGridView record="emojiPickerView.emojiGridView"/>
</div>
@@ -1,16 +0,0 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class LegacyEmoji extends Component {}
Object.assign(LegacyEmoji, {
props: {
emojiView: Object,
},
template: 'mail.LegacyEmoji',
});
registerMessagingComponent(LegacyEmoji);
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.LegacyEmoji" owl="1">
<span class="o_LegacyEmoji p-1 fs-5 cursor-pointer" t-on-click="props.emojiView.emojiListView.onClickEmoji" t-att-title="props.emojiView.emoji.name" t-att-data-source="props.emojiView.emoji.sources[0]" t-att-data-unicode="props.emojiView.emoji.unicode" t-attf-class="{{ className }}" t-ref="root">
<t t-esc="props.emojiView.emoji.unicode"/>
</span>
</t>
</templates>
@@ -1,57 +0,0 @@
/** @odoo-module **/
import { useUpdate } from '@mail/component_hooks/use_update';
import { registerMessagingComponent } from '@mail/utils/messaging_component';
import { LegacyComponent } from '@web/legacy/legacy_component';
export class LegacyEmojiList extends LegacyComponent {
/**
* @override
*/
setup() {
super.setup();
useUpdate({ func: () => this._update() });
}
get emojiListView() {
return this.props.record;
}
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
*/
_update() {
this.trigger('o-popover-compute');
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
close() {
this.trigger('o-popover-close');
}
/**
* Returns whether the given node is self or a children of self.
*
* @param {Node} node
* @returns {boolean}
*/
contains(node) {
return Boolean(this.root.el && this.root.el.contains(node));
}
}
Object.assign(LegacyEmojiList, {
props: { record: Object },
template: 'mail.LegacyEmojiList',
});
registerMessagingComponent(LegacyEmojiList);
@@ -1,9 +0,0 @@
// ------------------------------------------------------------------
// Layout
// ------------------------------------------------------------------
.o_LegacyEmojiList {
// Could be replaced by "d-grid" class after migration to BS5
display: grid;
grid-template-columns: repeat(9, 1fr);
}
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.LegacyEmojiList" owl="1">
<div class="o_LegacyEmojiList px-1" t-attf-class="{{ className }}" t-ref="root">
<t t-if="emojiListView">
<t t-foreach="emojiListView.emojiViews" t-as="emojiView" t-key="emojiView.localId">
<LegacyEmoji emojiView="emojiView"/>
</t>
</t>
</div>
</t>
</templates>
@@ -2,19 +2,26 @@
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',
fields: {
emojiCategoryViews: many('EmojiCategoryView', {
compute() {
return this.emojiPickerViewOwner.categories.map(category => ({ viewCategory: category }));
if (!this.emojiPickerView) {
return clear();
}
return this.emojiPickerView.categories.map(category => ({ viewCategory: category }));
},
inverse: 'emojiCategoryBarViewOwner',
}),
emojiPickerViewOwner: one('EmojiPickerView', {
emojiPickerHeaderViewOwner: one('EmojiPickerHeaderView', {
identifying: true,
inverse: 'emojiCategoryBarView',
}),
emojiPickerView: one('EmojiPickerView', {
related: 'emojiPickerHeaderViewOwner.emojiPickerViewOwner',
}),
},
});
@@ -10,16 +10,16 @@ registerModel({
* @param {MouseEvent} ev
*/
onClick() {
this.emojiCategoryBarViewOwner.emojiPickerViewOwner.emojiSearchBarView.reset();
this.emojiPickerView.emojiSearchBarView.reset();
let categoryRowScrollPosition = Math.max(
0,
// Index of the beginning of the category
(this.emojiCategoryBarViewOwner.emojiPickerViewOwner.emojiGridView.rowHeight * this.viewCategory.emojiGridRowView.index)
(this.emojiPickerView.emojiGridView.rowHeight * this.viewCategory.emojiGridRowView.index)
-
// Cancels the amount of buffer rows
(this.emojiCategoryBarViewOwner.emojiPickerViewOwner.emojiGridView.rowHeight * this.emojiCategoryBarViewOwner.emojiPickerViewOwner.emojiGridView.topBufferAmount)
(this.emojiPickerView.emojiGridView.rowHeight * this.emojiPickerView.emojiGridView.topBufferAmount)
);
this.emojiCategoryBarViewOwner.emojiPickerViewOwner.emojiGridView.containerRef.el.scrollTo({ top: categoryRowScrollPosition });
this.emojiPickerView.emojiGridView.containerRef.el.scrollTo({ top: categoryRowScrollPosition });
},
/**
* @param {MouseEvent} ev
@@ -42,6 +42,9 @@ registerModel({
identifying: true,
inverse: 'emojiCategoryViews',
}),
emojiPickerView: one('EmojiPickerView', {
related: 'emojiCategoryBarViewOwner.emojiPickerView',
}),
isActive: attr({
compute() {
return Boolean(this.viewCategory.emojiPickerViewAsActive);
@@ -19,6 +19,9 @@ registerModel({
});
},
onScroll() {
if (!this.exists()) {
return;
}
this.onScrollThrottle.do();
},
_onChangeScrollRecomputeCount() {
@@ -0,0 +1,28 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
registerModel({
name: 'EmojiPickerHeaderActionListView',
fields: {
__dummyActionView: one('EmojiPickerHeaderActionView', {
inverse: '__ownerAsDummy',
}),
actionViews: many('EmojiPickerHeaderActionView', {
inverse: 'owner',
sort() {
return [
['smaller-first', 'sequence'],
];
},
}),
emojiPickerView: one('EmojiPickerView', {
related: 'owner.emojiPickerViewOwner',
}),
owner: one('EmojiPickerHeaderView', {
identifying: true,
inverse: 'actionListView',
}),
},
});
@@ -0,0 +1,39 @@
/** @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: 'EmojiPickerHeaderActionView',
identifyingMode: 'xor',
fields: {
// dummy identifying field, so that it works without defining one initially in mail
__ownerAsDummy: one('EmojiPickerHeaderActionListView', {
identifying: true,
inverse: '__dummyActionView',
}),
content: one('Record', {
compute() {
return clear();
},
required: true,
}),
contentComponentName: attr({
compute() {
return clear();
},
required: true,
}),
owner: one('EmojiPickerHeaderActionListView', {
compute() {
return clear();
},
required: true,
inverse: 'actionViews',
}),
sequence: attr({
default: 0,
}),
},
});
@@ -0,0 +1,25 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
registerModel({
name: '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',
}),
},
});
@@ -37,12 +37,6 @@ registerModel({
return this.categories[0];
},
}),
emojiCategoryBarView: one('EmojiCategoryBarView', {
default: {},
inverse: 'emojiPickerViewOwner',
readonly: true,
required: true,
}),
emojiGridView: one('EmojiGridView', {
default: {},
inverse: 'emojiPickerViewOwner',
@@ -54,6 +48,12 @@ registerModel({
inverse: 'emojiPickerView',
readonly: true,
}),
headerView: one('EmojiPickerHeaderView', {
default: {},
inverse: 'emojiPickerViewOwner',
readonly: true,
required: true,
}),
popoverViewOwner: one('PopoverView', {
identifying: true,
inverse: 'emojiPickerView',