[IMP] mail: improve EmojiPicker (step 1)

Emoji picker now has a bar to filter
emojis per category.

Task-2898475

closes odoo/odoo#94646

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Nemael
2022-07-01 16:35:28 +02:00
committed by Alexandre Kühn
parent 8cce4e3694
commit 68bb383852
20 changed files with 306 additions and 127 deletions
@@ -4,12 +4,17 @@ import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class Emoji extends Component {}
export class Emoji extends Component {
/**
* @returns {EmojiView}
*/
get emojiView() {
return this.props.record;
}
}
Object.assign(Emoji, {
props: {
emojiView: Object,
},
props: { record: Object },
template: 'mail.Emoji',
});
@@ -2,8 +2,8 @@
<templates xml:space="preserve">
<t t-name="mail.Emoji" owl="1">
<span class="o_Emoji p-1 fs-5 o_cursor_pointer" t-on-click="props.emojiView.emojiPickerView.onClickEmoji" t-att-title="props.emojiView.emoji.description" 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 class="o_Emoji p-1 fs-3 o_cursor_pointer" t-att-class="emojiView.isHovered ? 'bg-200' : ''" t-on-click="emojiView.onClick" t-on-mouseenter="emojiView.onMouseenter" t-on-mouseleave="emojiView.onMouseleave" t-att-title="emojiView.emoji.description" t-att-data-source="emojiView.emoji.sources[0]" t-att-data-unicode="emojiView.emoji.unicode" t-attf-class="{{ className }}" t-ref="root">
<t t-esc="emojiView.emoji.unicode"/>
</span>
</t>
@@ -0,0 +1,21 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class EmojiCategoryBarView extends Component {
/**
* @returns {EmojiCategoryBarView}
*/
get emojiCategoryBarView() {
return this.props.record;
}
}
Object.assign(EmojiCategoryBarView, {
props: { record: Object },
template: 'mail.EmojiCategoryBarView',
});
registerMessagingComponent(EmojiCategoryBarView);
@@ -0,0 +1,12 @@
<?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 bg-100 border-bottom" t-attf-class="{{ className }}" t-ref="root">
<t t-foreach="emojiCategoryBarView.emojiCategoryViews" t-as="emojiCategoryView" t-key="emojiCategoryView.localId">
<EmojiCategoryView record="emojiCategoryView"/>
</t>
</div>
</t>
</templates>
@@ -0,0 +1,21 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class EmojiCategoryView extends Component {
/**
* @returns {EmojiCategoryView}
*/
get emojiCategoryView() {
return this.props.record;
}
}
Object.assign(EmojiCategoryView, {
props: { record: Object },
template: 'mail.EmojiCategoryView',
});
registerMessagingComponent(EmojiCategoryView);
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.EmojiCategoryView" owl="1">
<span class="o_EmojiCategoryView px-2 py-1 fs-5 o_cursor_pointer" t-att-class="(emojiCategoryView.isHovered or emojiCategoryView.emojiCategoryBarViewOwnerAsActive) ? 'bg-300' : ''" t-on-click="emojiCategoryView.onClick" t-on-mouseenter="emojiCategoryView.onMouseenter" t-on-mouseleave="emojiCategoryView.onMouseleave" t-attf-class="{{ className }}" t-ref="root">
<t t-esc="emojiCategoryView.emojiCategory.categoryName"/>
</span>
</t>
</templates>
@@ -0,0 +1,21 @@
/** @odoo-module **/
import { registerMessagingComponent } from '@mail/utils/messaging_component';
const { Component } = owl;
export class EmojiGridView extends Component {
/**
* @returns {EmojiGridView}
*/
get emojiGridView() {
return this.props.record;
}
}
Object.assign(EmojiGridView, {
props: { record: Object },
template: 'mail.EmojiGridView',
});
registerMessagingComponent(EmojiGridView);
@@ -0,0 +1,8 @@
// ------------------------------------------------------------------
// Layout
// ------------------------------------------------------------------
.o_EmojiGridView {
display: grid;
grid-template-columns: repeat(9, 1fr);
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.EmojiGridView" owl="1">
<div class="o_EmojiGridView overflow-auto py-2 pl-3 pr-4" t-attf-class="{{ className }}" t-ref="root">
<t t-foreach="emojiGridView.emojiViews" t-as="emojiView" t-key="emojiView.localId">
<Emoji className="'o_EmojiGid_emoji'" record="emojiView"/>
</t>
</div>
</t>
</templates>
@@ -1,52 +1,16 @@
/** @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 EmojiPickerView extends LegacyComponent {
const { Component } = owl;
export class EmojiPickerView extends Component {
/**
* @override
* @returns {EmojiPickerView}
*/
setup() {
super.setup();
useUpdate({ func: () => this._update() });
}
get emojiPickerView() {
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(EmojiPickerView, {
@@ -1,9 +1,3 @@
// ------------------------------------------------------------------
// Layout
// ------------------------------------------------------------------
.o_EmojiPickerView {
// Could be replaced by "d-grid" class after migration to BS5
display: grid;
grid-template-columns: repeat(9, 1fr);
height: Min(80vh, 340px);
}
@@ -2,12 +2,9 @@
<templates xml:space="preserve">
<t t-name="mail.EmojiPickerView" owl="1">
<div class="o_EmojiPickerView px-1" t-attf-class="{{ className }}" t-ref="root">
<t t-if="emojiPickerView">
<t t-foreach="emojiPickerView.emojiViews" t-as="emojiView" t-key="emojiView.localId">
<Emoji emojiView="emojiView"/>
</t>
</t>
<div class="o_EmojiPickerView d-flex flex-column" t-attf-class="{{ className }}" t-ref="root">
<EmojiCategoryBarView record="emojiPickerView.emojiCategoryBarView"/>
<EmojiGridView record="emojiPickerView.emojiGridView"/>
</div>
</t>
@@ -1,21 +1,11 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { attr, many, one } from '@mail/model/model_field';
import { replace } from '@mail/model/model_field_command';
import { attr, many } from '@mail/model/model_field';
registerModel({
name: 'EmojiCategory',
identifyingFields: ['categoryName'],
recordMethods: {
/**
* @private
* @returns {FieldCommand}
*/
_computeEmojiRegistry() {
return replace(this.messaging.emojiRegistry);
},
},
fields: {
allEmojis: many('Emoji', {
inverse: 'emojiCategories',
@@ -26,13 +16,7 @@ registerModel({
}),
emojiCategoryViews: many('EmojiCategoryView', {
inverse: 'emojiCategory',
readonly: true,
isCausal: true,
}),
emojiRegistry: one('EmojiRegistry', {
compute: '_computeEmojiRegistry',
inverse: 'allCategories',
required: true,
}),
},
});
@@ -0,0 +1,66 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
import { insertAndReplace, replace } from '@mail/model/model_field_command';
registerModel({
name: 'EmojiCategoryBarView',
identifyingFields: ['emojiPickerViewOwner'],
recordMethods: {
/**
* @private
* @returns {FieldCommand}
*/
_computeActiveCategoryView() {
if (!this.activeByUserCategoryView) {
return replace(this.defaultActiveCategoryView);
}
return replace(this.activeByUserCategoryView);
},
/**
* @private
* @returns {FieldCommand}
*/
_computeDefaultActiveCategoryView() {
return insertAndReplace({
emojiCategory: insertAndReplace({ categoryName: "all" }),
emojiCategoryBarViewOwner: replace(this),
});
},
/**
* @private
* @returns {FieldCommand}
*/
_computeEmojiCategoryViews() {
return insertAndReplace(
this.messaging.emojiRegistry.allCategories.map(emojiCategory => {
return { emojiCategory: replace(emojiCategory) };
})
);
},
},
fields: {
activeByUserCategoryView: one('EmojiCategoryView', {
inverse: 'emojiCategoryBarViewOwnerAsActiveByUser',
}),
activeCategoryView: one('EmojiCategoryView', {
compute: '_computeActiveCategoryView',
inverse: 'emojiCategoryBarViewOwnerAsActive',
required: true,
}),
defaultActiveCategoryView: one('EmojiCategoryView', {
compute: '_computeDefaultActiveCategoryView',
}),
emojiCategoryViews: many('EmojiCategoryView', {
compute: '_computeEmojiCategoryViews',
inverse: 'emojiCategoryBarViewOwner',
isCausal: true,
}),
emojiPickerViewOwner: one('EmojiPickerView', {
inverse: 'emojiCategoryBarView',
readonly: true,
required: true,
}),
},
});
@@ -1,21 +1,51 @@
/** @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 { replace } from '@mail/model/model_field_command';
registerModel({
name: 'EmojiCategoryView',
identifyingFields: ['emojiCategory', 'emojiPickerView'],
identifyingFields: ['emojiCategoryBarViewOwner', 'emojiCategory'],
recordMethods: {
/**
* @param {MouseEvent} ev
*/
onClick() {
this.update({ emojiCategoryBarViewOwnerAsActiveByUser: replace(this.emojiCategoryBarViewOwner) });
},
/**
* @param {MouseEvent} ev
*/
onMouseenter(ev) {
this.update({ isHovered: true });
},
/**
* @param {MouseEvent} ev
*/
onMouseleave(ev) {
this.update({ isHovered: false });
},
},
fields: {
emojiCategory: one('EmojiCategory', {
inverse: 'emojiCategoryViews',
readonly: true,
required: true,
}),
emojiPickerView: one('EmojiPickerView', {
emojiCategoryBarViewOwner: one('EmojiCategoryBarView', {
inverse: 'emojiCategoryViews',
readonly: true,
required: true,
}),
emojiCategoryBarViewOwnerAsActiveByUser: one('EmojiCategoryBarView', {
inverse: 'activeByUserCategoryView',
}),
emojiCategoryBarViewOwnerAsActive: one('EmojiCategoryBarView', {
inverse: 'activeCategoryView',
}),
isHovered: attr({
default: false,
}),
}
});
@@ -0,0 +1,39 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
import { clear, insertAndReplace, replace } from '@mail/model/model_field_command';
registerModel({
name: 'EmojiGridView',
identifyingFields: ['emojiPickerViewOwner'],
recordMethods: {
/**
* @private
* @returns {FieldCommand}
*/
_computeEmojiViews() {
if (!this.emojiPickerViewOwner.emojiCategoryBarView.activeCategoryView) {
return clear();
}
return insertAndReplace(
this.emojiPickerViewOwner.emojiCategoryBarView.activeCategoryView.emojiCategory.allEmojis.map(emoji => {
return { emoji: replace(emoji) };
})
);
},
},
fields: {
emojiPickerViewOwner: one('EmojiPickerView', {
inverse: 'emojiGridView',
readonly: true,
required: true,
}),
emojiViews: many('EmojiView', {
compute: '_computeEmojiViews',
inverse: 'emojiGridView',
readonly: true,
isCausal: true,
}),
},
});
@@ -1,59 +1,25 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
import { insertAndReplace, replace } from '@mail/model/model_field_command';
import { one } from '@mail/model/model_field';
import { insertAndReplace } from '@mail/model/model_field_command';
registerModel({
name: 'EmojiPickerView',
identifyingFields: ['popoverViewOwner'],
recordMethods: {
/**
* @param {MouseEvent} ev
*/
onClickEmoji(ev) {
if (this.popoverViewOwner.messageActionListOwnerAsReaction) {
this.popoverViewOwner.messageActionListOwnerAsReaction.onClickReaction(ev);
} else if (this.popoverViewOwner.composerViewOwnerAsEmoji) {
this.popoverViewOwner.composerViewOwnerAsEmoji.onClickEmoji(ev);
}
},
/**
* @private
* @returns {FieldCommand}
*/
_computeEmojiViews() {
return insertAndReplace(
this.messaging.emojiRegistry.allEmojis.map(emoji => {
return { emoji: replace(emoji) };
})
);
},
/**
* @private
* @returns {FieldCommand}
*/
_computeEmojiCategoryViews() {
return insertAndReplace(
this.messaging.emojiRegistry.allCategories.map(emojiCategory => {
return { emojiCategory: replace(emojiCategory) };
})
);
},
},
fields: {
emojiCategoryViews: many('EmojiCategoryView', {
compute: '_computeEmojiCategoryViews',
inverse: 'emojiPickerView',
emojiCategoryBarView: one('EmojiCategoryBarView', {
default: insertAndReplace(),
inverse: 'emojiPickerViewOwner',
readonly: true,
required: true,
isCausal: true,
}),
emojiViews: many('EmojiView', {
compute: '_computeEmojiViews',
inverse: 'emojiPickerView',
emojiGridView: one('EmojiGridView', {
default: insertAndReplace(),
inverse: 'emojiPickerViewOwner',
readonly: true,
required: true,
isCausal: true,
}),
popoverViewOwner: one('PopoverView', {
@@ -105,7 +105,6 @@ registerModel({
{ categoryName: "⚽️", allEmojis: insertAndReplace(["🏃", "🚲", "⚽️", "🏈", "🎱", "🎬", "🎤"].map(unicode => ({ unicode }))) },
]),
inverse: 'emojiRegistry',
}),
},
});
+33 -3
View File
@@ -1,21 +1,51 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
import { attr, one } from '@mail/model/model_field';
registerModel({
name: 'EmojiView',
identifyingFields: ['emoji', 'emojiPickerView'],
identifyingFields: ['emojiGridView', 'emoji'],
recordMethods: {
/**
* @param {MouseEvent} ev
*/
onClick(ev) {
if (this.emojiGridView.emojiPickerViewOwner.popoverViewOwner.messageActionListOwnerAsReaction) {
this.emojiGridView.emojiPickerViewOwner.popoverViewOwner.messageActionListOwnerAsReaction.onClickReaction(ev);
return;
}
if (this.emojiGridView.emojiPickerViewOwner.popoverViewOwner.composerViewOwnerAsEmoji) {
this.emojiGridView.emojiPickerViewOwner.popoverViewOwner.composerViewOwnerAsEmoji.onClickEmoji(ev);
return;
}
},
/**
* @param {MouseEvent} ev
*/
onMouseenter(ev) {
this.update({ isHovered: true });
},
/**
* @param {MouseEvent} ev
*/
onMouseleave(ev) {
this.update({ isHovered: false });
},
},
fields: {
emoji: one('Emoji', {
inverse: 'emojiViews',
readonly: true,
required: true,
}),
emojiPickerView: one('EmojiPickerView', {
emojiGridView: one('EmojiGridView', {
inverse: 'emojiViews',
readonly: true,
required: true,
}),
isHovered: attr({
default: false,
}),
}
});
@@ -106,7 +106,7 @@ registerModel({
return 'ChannelInvitationForm';
}
if (this.emojiPickerView) {
return 'EmojiPicker';
return 'EmojiPickerView';
}
return clear();
},