[REF] hr_expense, lunch, mail: change patch to mixin

Before this commit, a patch was used to modify some classes in place
but these classes were declared some lines just before the patch.
This commit replaces those patches by mixins, it's a better way to
provide a same extension to multiple classes as the extension is
done by the language.

Task: 3410198
Part-of: odoo/odoo#125716
This commit is contained in:
Michael (mcm)
2023-08-02 17:29:05 +02:00
parent 9d6b380a24
commit 06adb79b1e
14 changed files with 68 additions and 106 deletions
@@ -4,9 +4,9 @@ import { useBus, useService } from '@web/core/utils/hooks';
const { useRef, useEffect, useState } = owl;
export const ExpenseDocumentDropZone = {
export const ExpenseDocumentDropZone = (T) => class ExpenseDocumentDropZone extends T {
setup() {
this._super();
super.setup();
this.dragState = useState({
showDragZone: false,
});
@@ -31,31 +31,31 @@ export const ExpenseDocumentDropZone = {
},
() => [document.querySelector('.o_content')]
);
},
}
highlight(ev) {
ev.stopPropagation();
ev.preventDefault();
this.dragState.showDragZone = true;
},
}
unhighlight(ev) {
ev.stopPropagation();
ev.preventDefault();
this.dragState.showDragZone = false;
},
}
async onDrop(ev) {
ev.preventDefault();
await this.env.bus.trigger("change_file_input", {
files: ev.dataTransfer.files,
});
},
}
};
export const ExpenseDocumentUpload = {
export const ExpenseDocumentUpload = (T) => class ExpenseDocumentUpload extends T {
setup() {
this._super();
super.setup();
this.actionService = useService('action');
this.notification = useService('notification');
this.orm = useService('orm');
@@ -67,11 +67,11 @@ export const ExpenseDocumentUpload = {
this.fileInput.el.files = ev.detail.files;
await this.onChangeFileInput();
});
},
}
uploadDocument() {
this.fileInput.el.click();
},
}
async onChangeFileInput() {
const params = {
@@ -87,7 +87,7 @@ export const ExpenseDocumentUpload = {
throw new Error(attachments.error);
}
this.onUpload(attachments);
},
}
async onUpload(attachments) {
const attachmentIds = attachments.map((a) => a.id);
@@ -100,5 +100,5 @@ export const ExpenseDocumentUpload = {
const action = await this.orm.call('hr.expense', 'create_expense_from_attachments', ["", attachmentIds]);
this.actionService.doAction(action);
},
}
};
@@ -4,15 +4,15 @@ import { useService } from "@web/core/utils/hooks";
const { onMounted, onPatched, useRef } = owl;
export const ExpenseMobileQRCode = {
export const ExpenseMobileQRCode = (T) => class ExpenseMobileQRCode extends T {
setup() {
this._super();
super.setup();
this.root = useRef('root');
this.actionService = useService('action');
onMounted(this.bindAppsIcons);
onPatched(this.bindAppsIcons);
},
}
bindAppsIcons() {
const apps = this.root.el.querySelectorAll('.o_expense_mobile_app');
@@ -24,7 +24,7 @@ export const ExpenseMobileQRCode = {
for (const app of apps) {
app.addEventListener('click', handler);
}
},
}
handleClick(ev) {
ev.preventDefault();
+2 -6
View File
@@ -1,7 +1,6 @@
/** @odoo-module */
import { registry } from '@web/core/registry';
import { patch } from '@web/core/utils/patch';
import { ExpenseDashboard } from '../components/expense_dashboard';
import { ExpenseMobileQRCode } from '../mixins/qrcode';
@@ -11,12 +10,9 @@ import { kanbanView } from '@web/views/kanban/kanban_view';
import { KanbanController } from '@web/views/kanban/kanban_controller';
import { KanbanRenderer } from '@web/views/kanban/kanban_renderer';
export class ExpenseKanbanController extends KanbanController {}
patch(ExpenseKanbanController.prototype, 'expense_kanban_controller_upload', ExpenseDocumentUpload);
export class ExpenseKanbanController extends ExpenseDocumentUpload(KanbanController) {}
export class ExpenseKanbanRenderer extends KanbanRenderer {}
patch(ExpenseKanbanRenderer.prototype, 'expense_kanban_renderer_qrcode', ExpenseMobileQRCode);
patch(ExpenseKanbanRenderer.prototype, 'expense_kanban_renderer_qrcode_dzone', ExpenseDocumentDropZone);
export class ExpenseKanbanRenderer extends ExpenseDocumentDropZone(ExpenseMobileQRCode(KanbanRenderer)) {}
ExpenseKanbanRenderer.template = 'hr_expense.KanbanRenderer';
export class ExpenseDashboardKanbanRenderer extends ExpenseKanbanRenderer {}
+2 -6
View File
@@ -5,7 +5,6 @@ import { ExpenseMobileQRCode } from '../mixins/qrcode';
import { ExpenseDocumentUpload, ExpenseDocumentDropZone } from '../mixins/document_upload';
import { registry } from '@web/core/registry';
import { patch } from '@web/core/utils/patch';
import { useService } from '@web/core/utils/hooks';
import { listView } from "@web/views/list/list_view";
@@ -14,7 +13,7 @@ import { ListRenderer } from "@web/views/list/list_renderer";
const { onWillStart } = owl;
export class ExpenseListController extends ListController {
export class ExpenseListController extends ExpenseDocumentUpload(ListController) {
setup() {
super.setup();
this.orm = useService('orm');
@@ -81,11 +80,8 @@ export class ExpenseListController extends ListController {
}
}
}
patch(ExpenseListController.prototype, 'expense_list_controller_upload', ExpenseDocumentUpload);
export class ExpenseListRenderer extends ListRenderer {}
patch(ExpenseListRenderer.prototype, 'expense_list_renderer_qrcode', ExpenseMobileQRCode);
patch(ExpenseListRenderer.prototype, 'expense_list_renderer_qrcode_dzone', ExpenseDocumentDropZone);
export class ExpenseListRenderer extends ExpenseDocumentDropZone(ExpenseMobileQRCode(ListRenderer)) {}
ExpenseListRenderer.template = 'hr_expense.ListRenderer';
export class ExpenseDashboardListRenderer extends ExpenseListRenderer {}
@@ -2,13 +2,13 @@
import { useBus, useService } from "@web/core/utils/hooks";
export const LunchRendererMixin = {
export const LunchRendererMixin = (T) => class LunchRendererMixin extends T {
setup() {
this._super(...arguments);
super.setup(...arguments);
this.action = useService("action");
useBus(this.env.bus, 'lunch_open_order', (ev) => this.openOrderLine(ev.detail.productId));
},
}
openOrderLine(productId, orderId) {
let context = {};
@@ -36,5 +36,5 @@ export const LunchRendererMixin = {
this.action.doAction(action, {
onClose: () => this.env.bus.trigger('lunch_update_dashboard')
});
},
}
}
};
+1 -3
View File
@@ -1,6 +1,5 @@
/** @odoo-module */
import { patch } from '@web/core/utils/patch';
import { registry } from '@web/core/registry';
import { kanbanView } from '@web/views/kanban/kanban_view';
@@ -19,8 +18,7 @@ export class LunchKanbanRecord extends KanbanRecord {
}
}
export class LunchKanbanRenderer extends KanbanRenderer {}
patch(LunchKanbanRenderer.prototype, 'lunch_kanban_renderer_mixin', LunchRendererMixin);
export class LunchKanbanRenderer extends LunchRendererMixin(KanbanRenderer) {}
LunchKanbanRenderer.template = 'lunch.KanbanRenderer';
LunchKanbanRenderer.components = {
+1 -3
View File
@@ -1,6 +1,5 @@
/** @odoo-module */
import { patch } from '@web/core/utils/patch';
import { registry } from '@web/core/registry';
import { listView } from '@web/views/list/list_view';
@@ -12,12 +11,11 @@ import { LunchRendererMixin } from '../mixins/lunch_renderer_mixin';
import { LunchSearchModel } from './search_model';
export class LunchListRenderer extends ListRenderer {
export class LunchListRenderer extends LunchRendererMixin(ListRenderer) {
onCellClicked(record, column) {
this.openOrderLine(record.resId);
}
}
patch(LunchListRenderer.prototype, 'lunch_list_renderer_mixin', LunchRendererMixin);
LunchListRenderer.template = 'lunch.ListRenderer';
LunchListRenderer.components = {
+17 -29
View File
@@ -3,36 +3,24 @@
import { escape } from "@web/core/utils/strings";
/**
* This mixin gathers a few methods that are used to handle emojis.
* Adds a span with a CSS class around chains of emojis in the message for styling purposes.
* The input is first passed through 'escape' to prevent unwanted injections into the HTML
*
* It's currently used to format text and wrap the emojis around <span class="o_mail_emoji"> to make them look nicer
* Sequences of emojis are wrapped instead of individual ones to prevent compound emojis
* such as 👩🏿 = 👩 + 🏿 [dark skin tone character] from being separated.
*
* This will only match characters that have a different presentation from normal text, unlike ®
* For alternatives, see: https://www.unicode.org/reports/tr51/#Emoji_Properties_and_Data_Files
*
* @param {String} message a text message to format
*/
export default {
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
export function formatText(message) {
message = escape(message);
message = message.replaceAll(
/(\p{Emoji_Presentation}+)/gu,
"<span class='o_mail_emoji'>$1</span>"
);
message = message.replace(/(?:\r\n|\r|\n)/g, "<br>");
/**
* Adds a span with a CSS class around chains of emojis in the message for styling purposes.
* The input is first passed through 'escape' to prevent unwanted injections into the HTML
*
* Sequences of emojis are wrapped instead of individual ones to prevent compound emojis
* such as 👩🏿 = 👩 + 🏿 [dark skin tone character] from being separated.
*
* This will only match characters that have a different presentation from normal text, unlike ®
* For alternatives, see: https://www.unicode.org/reports/tr51/#Emoji_Properties_and_Data_Files
*
* @param {String} message a text message to format
*/
_formatText(message) {
message = escape(message);
message = message.replaceAll(
/(\p{Emoji_Presentation}+)/gu,
"<span class='o_mail_emoji'>$1</span>"
);
message = message.replace(/(?:\r\n|\r|\n)/g, "<br>");
return message;
},
};
return message;
}
@@ -1,18 +1,16 @@
/* @odoo-module */
import MailEmojisMixin from "@mail/js/emojis_mixin";
import { EmojisFieldCommon } from "@mail/views/web/fields/emojis_field_common/emojis_field_common";
import { useRef } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { patch } from "@web/core/utils/patch";
import { CharField, charField } from "@web/views/fields/char/char_field";
/**
* Extension of the FieldChar that will add emojis support
*/
export class EmojisCharField extends CharField {
export class EmojisCharField extends EmojisFieldCommon(CharField) {
setup() {
super.setup();
this.targetEditElement = useRef("input");
@@ -20,8 +18,6 @@ export class EmojisCharField extends CharField {
}
}
patch(EmojisCharField.prototype, "emojis_char_field_mail_mixin", MailEmojisMixin);
patch(EmojisCharField.prototype, "emojis_char_field_field_mixin", EmojisFieldCommon);
EmojisCharField.template = "mail.EmojisCharField";
EmojisCharField.components = { ...CharField.components };
@@ -7,7 +7,7 @@ import { useRef } from "@odoo/owl";
/*
* Common code for EmojisTextField and EmojisCharField
*/
export const EmojisFieldCommon = {
export const EmojisFieldCommon = (T) => class EmojisFieldCommon extends T {
/**
* Create an emoji textfield view to enable opening an emoji popover
*/
@@ -38,5 +38,5 @@ export const EmojisFieldCommon = {
position: "bottom",
}
);
},
}
};
@@ -1,16 +1,14 @@
/* @odoo-module */
import MailEmojisMixin from "@mail/js/emojis_mixin";
import { EmojisFieldCommon } from "@mail/views/web/fields/emojis_field_common/emojis_field_common";
import { registry } from "@web/core/registry";
import { patch } from "@web/core/utils/patch";
import { TextField, textField } from "@web/views/fields/text/text_field";
/**
* Extension of the FieldText that will add emojis support
*/
export class EmojisTextField extends TextField {
export class EmojisTextField extends EmojisFieldCommon(TextField) {
setup() {
super.setup();
this.targetEditElement = this.textareaRef;
@@ -18,8 +16,6 @@ export class EmojisTextField extends TextField {
}
}
patch(EmojisTextField.prototype, "emojis_char_field_mail_mixin", MailEmojisMixin);
patch(EmojisTextField.prototype, "emojis_text_field_field_mixin", EmojisFieldCommon);
EmojisTextField.template = "mail.EmojisTextField";
EmojisTextField.components = { ...TextField.components };
@@ -5,7 +5,6 @@ import { useAssignUserCommand } from "@mail/views/web/fields/assign_user_command
import { registry } from "@web/core/registry";
import { TagsList } from "@web/core/tags_list/tags_list";
import { patch } from "@web/core/utils/patch";
import { usePopover } from "@web/core/popover/popover_hook";
import { browser } from "@web/core/browser/browser";
import { AvatarCardPopover } from "@mail/discuss/web/avatar_card/avatar_card_popover";
@@ -22,9 +21,9 @@ import {
export class Many2ManyAvatarUserTagsList extends TagsList {}
Many2ManyAvatarUserTagsList.template = "mail.Many2ManyAvatarUserTagsList";
const userChatter = {
const WithUserChatter = (T) => class UserChatterMixin extends T {
setup() {
this._super(...arguments);
super.setup(...arguments);
this.openChat = useOpenChat(this.relation);
if (this.props.withCommand) {
useAssignUserCommand();
@@ -34,11 +33,11 @@ const userChatter = {
});
this.openTimeout = false;
this.lastOpenedId = 0;
},
}
getTagProps(record) {
return {
...this._super(...arguments),
...super.getTagProps(...arguments),
onImageClicked: () => this.openChat(record.resId),
openCard: (ev) => {
if (this.env.isSmall || this.relation !== "res.users") {
@@ -62,15 +61,15 @@ const userChatter = {
delete this.openTimeout;
},
};
},
};
export class Many2ManyTagsAvatarUserField extends Many2ManyTagsAvatarField {
}
}
export class Many2ManyTagsAvatarUserField extends WithUserChatter(Many2ManyTagsAvatarField) {
static components = {
...Many2ManyTagsAvatarField.components,
TagsList: Many2ManyAvatarUserTagsList,
};
}
patch(Many2ManyTagsAvatarUserField.prototype, "mail/fields/web", userChatter);
export const many2ManyTagsAvatarUserField = {
...many2ManyTagsAvatarField,
@@ -84,7 +83,7 @@ export class KanbanMany2ManyAvatarUserTagsList extends KanbanMany2ManyTagsAvatar
static template = "mail.KanbanMany2ManyAvatarUserTagsList";
}
export class KanbanMany2ManyTagsAvatarUserField extends KanbanMany2ManyTagsAvatarField {
export class KanbanMany2ManyTagsAvatarUserField extends WithUserChatter(KanbanMany2ManyTagsAvatarField) {
static template = "mail.KanbanMany2ManyTagsAvatarUserField";
static components = {
...KanbanMany2ManyTagsAvatarField.components,
@@ -94,7 +93,6 @@ export class KanbanMany2ManyTagsAvatarUserField extends KanbanMany2ManyTagsAvata
return !this.props.readonly;
}
}
patch(KanbanMany2ManyTagsAvatarUserField.prototype, "mail/fields/web", userChatter);
export const kanbanMany2ManyTagsAvatarUserField = {
...kanbanMany2ManyTagsAvatarField,
component: KanbanMany2ManyTagsAvatarUserField,
@@ -102,7 +100,7 @@ export const kanbanMany2ManyTagsAvatarUserField = {
};
registry.category("fields").add("kanban.many2many_avatar_user", kanbanMany2ManyTagsAvatarUserField);
export class ListMany2ManyTagsAvatarUserField extends ListMany2ManyTagsAvatarField {
export class ListMany2ManyTagsAvatarUserField extends WithUserChatter(ListMany2ManyTagsAvatarField) {
static template = "mail.ListMany2ManyTagsAvatarUserField";
static components = {
...ListMany2ManyTagsAvatarField.components,
@@ -113,7 +111,6 @@ export class ListMany2ManyTagsAvatarUserField extends ListMany2ManyTagsAvatarFie
return this.props.record.data[this.props.name].records.length === 1 || !this.props.readonly;
}
}
patch(ListMany2ManyTagsAvatarUserField.prototype, "mail/fields/web", userChatter);
export const listMany2ManyTagsAvatarUserField = {
...listMany2ManyTagsAvatarField,
@@ -5,7 +5,6 @@ import { useAssignUserCommand } from "@mail/views/web/fields/assign_user_command
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { patch } from "@web/core/utils/patch";
import {
Many2OneAvatarField,
many2OneAvatarField,
@@ -16,9 +15,9 @@ import { usePopover } from "@web/core/popover/popover_hook";
import { browser } from "@web/core/browser/browser";
import { AvatarCardPopover } from "@mail/discuss/web/avatar_card/avatar_card_popover";
const userChatter = {
const WithUserChatter = (T) => class extends T {
setup() {
this._super(...arguments);
super.setup(...arguments);
this.openChat = useOpenChat(this.relation);
if (this.props.withCommand) {
useAssignUserCommand();
@@ -27,14 +26,14 @@ const userChatter = {
closeOnHoverAway: true,
});
this.openTimeout = false;
},
}
onClickAvatar() {
const id = this.props.record.data[this.props.name][0] ?? false;
if (id !== false) {
this.openChat(id);
}
},
}
openCard(ev) {
if (this.env.isSmall || this.relation !== "res.users") {
@@ -51,15 +50,15 @@ const userChatter = {
});
}
}, 350);
},
}
clearTimeout() {
browser.clearTimeout(this.openTimeout);
delete this.openTimeout;
},
}
};
export class Many2OneAvatarUserField extends Many2OneAvatarField {
export class Many2OneAvatarUserField extends WithUserChatter(Many2OneAvatarField) {
static template = "mail.Many2OneAvatarUserField";
static props = {
...Many2OneAvatarField.props,
@@ -68,7 +67,6 @@ export class Many2OneAvatarUserField extends Many2OneAvatarField {
withCommand: { type: Boolean, optional: true },
};
}
patch(Many2OneAvatarUserField.prototype, "mail/fields/web", userChatter);
export const many2OneAvatarUserField = {
...many2OneAvatarField,
@@ -85,7 +83,7 @@ export const many2OneAvatarUserField = {
registry.category("fields").add("many2one_avatar_user", many2OneAvatarUserField);
export class KanbanMany2OneAvatarUserField extends KanbanMany2OneAvatarField {
export class KanbanMany2OneAvatarUserField extends WithUserChatter(KanbanMany2OneAvatarField) {
static template = "mail.KanbanMany2OneAvatarUserField";
static props = {
...KanbanMany2OneAvatarField.props,
@@ -101,7 +99,6 @@ export class KanbanMany2OneAvatarUserField extends KanbanMany2OneAvatarField {
return props;
}
}
patch(KanbanMany2OneAvatarUserField.prototype, "mail/fields/web", userChatter);
export const kanbanMany2OneAvatarUserField = {
...kanbanMany2OneAvatarField,
@@ -1,6 +1,6 @@
/* @odoo-module */
import EmojiMixin from "@mail/js/emojis_mixin";
import { formatText } from "@mail/js/emojis_mixin";
QUnit.module("emojis mixin");
@@ -8,5 +8,5 @@ QUnit.test("Emoji formatter handles compound emojis", (assert) => {
const testString = "👩🏿test👩🏿👩t👩";
const expectedString =
"<span class='o_mail_emoji'>👩🏿</span>test<span class='o_mail_emoji'>👩🏿👩</span>t<span class='o_mail_emoji'>👩</span>";
assert.deepEqual(EmojiMixin._formatText(testString), expectedString);
assert.deepEqual(formatText(testString), expectedString);
});