Skip to content

Commit

Permalink
[REF] web,mail: formView design
Browse files Browse the repository at this point in the history
- ButtonBox is moved to the ControlPanel's actions section (center).
- StatusBarField, ButtonBox and PercentPieField design.
- Chatter is moved below the ControlPanel (aka "in the view")

task-2818586

Part-of: odoo#116641
Co-authored-by: Pierre Paridans <[email protected]>
Co-authored-by: Pierre Rousseau <[email protected]>
Co-authored-by: luvi <[email protected]>
  • Loading branch information
4 people committed May 12, 2023
1 parent c964329 commit 824024f
Show file tree
Hide file tree
Showing 33 changed files with 404 additions and 561 deletions.
2 changes: 1 addition & 1 deletion addons/mail/static/src/core_ui/thread.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<t t-name="mail.Thread" owl="1">
<t t-if="env.inChatter" t-call="mail.Thread.jumpPresent"/> <!-- chatter has its own scrollable, this ensures proper sticky showing -->
<div class="o-mail-Thread flex-grow-1 bg-view" t-att-class="{ 'overflow-auto': props.hasScrollAdjust, 'pb-4': !state.showJumpPresent }" t-ref="messages">
<div class="o-mail-Thread flex-grow-1" t-att-class="{ 'overflow-auto': props.hasScrollAdjust, 'pb-4': !state.showJumpPresent }" t-ref="messages">
<t t-if="!props.thread.isEmpty or props.thread.loadOlder or props.thread.hasLoadingFailed" name="content">
<div class="d-flex flex-column position-relative" t-att-class="{'justify-content-end': !env.inChatter and props.thread.type !== 'mailbox'}" style="min-height:100%">
<span class="position-absolute w-100 invisible" t-att-class="props.order === 'asc' ? 'bottom-0' : 'top-0'" t-ref="present-treshold" t-att-style="`height: Min(${PRESENT_THRESHOLD}px, 100%)`"/>
Expand Down
132 changes: 44 additions & 88 deletions addons/mail/static/src/views/form/form_compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { registry } from "@web/core/registry";
import { SIZES } from "@web/core/ui/ui_service";
import { patch } from "@web/core/utils/patch";
import { append, createElement, setAttributes } from "@web/core/utils/xml";
import { ViewCompiler, getModifier } from "@web/views/view_compiler";
import { FormCompiler } from "@web/views/form/form_compiler";

function compileChatter(node, params) {
Expand Down Expand Up @@ -37,18 +36,19 @@ function compileChatter(node, params) {
break;
}
}
const chatterContainerXml = createElement("Chatter");
const chatterContainerXml = createElement("t");
setAttributes(chatterContainerXml, {
"t-component": "__comp__.mailComponents.Chatter",
hasActivities,
hasFollowers,
hasMessageList,
hasParentReloadOnAttachmentsChanged,
hasParentReloadOnFollowersUpdate,
hasParentReloadOnMessagePosted,
isAttachmentBoxVisibleInitially,
threadId: params.threadId,
threadModel: params.threadModel,
webRecord: params.webRecord,
threadId: "__comp__.props.record.model.root.resId or undefined",
threadModel: "__comp__.props.record.model.root.resModel",
webRecord: "__comp__.props.record.model.root",
saveRecord: "() => __comp__.saveButtonClicked and __comp__.saveButtonClicked()",
});
const chatterContainerHookXml = createElement("div");
Expand All @@ -60,92 +60,24 @@ function compileChatter(node, params) {
function compileAttachmentPreview(node, params) {
const webClientViewAttachmentViewContainerHookXml = createElement("div");
webClientViewAttachmentViewContainerHookXml.classList.add("o_attachment_preview");
const webClientViewAttachmentViewContainerXml = createElement("AttachmentView");
const webClientViewAttachmentViewContainerXml = createElement("t");
setAttributes(webClientViewAttachmentViewContainerXml, {
threadId: params.threadId,
threadModel: params.threadModel,
"t-component": "__comp__.mailComponents.AttachmentView",
threadId: "__comp__.props.record.model.root.resId or undefined",
threadModel: "__comp__.props.record.model.root.resModel",
});
append(webClientViewAttachmentViewContainerHookXml, webClientViewAttachmentViewContainerXml);
return webClientViewAttachmentViewContainerHookXml;
}

export class MailFormCompiler extends ViewCompiler {
setup() {
this.compilers.push({ selector: "t", fn: this.compileT });
this.compilers.push({ selector: "div.oe_chatter", fn: this.compileChatter });
this.compilers.push({
selector: "div.o_attachment_preview",
fn: this.compileAttachmentPreview,
});
}

compile(node, params) {
const res = super.compile(node, params).children[0];
const chatterContainerHookXml = res.querySelector(".o-mail-Form-chatter");
if (chatterContainerHookXml) {
setAttributes(chatterContainerHookXml, {
"t-if": `!__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL}`,
"t-attf-class": "o-aside",
});
const chatterContainerXml = chatterContainerHookXml.querySelector("Chatter");
setAttributes(chatterContainerXml, {
hasMessageListScrollAdjust: "true",
isInFormSheetBg: "false",
});
}
const attachmentViewHookXml = res.querySelector(".o_attachment_preview");
if (attachmentViewHookXml) {
setAttributes(attachmentViewHookXml, {
"t-if": `__comp__.hasAttachmentViewer()`,
});
}
return res;
}

compileT(node, params) {
const compiledRoot = createElement("t");
for (const child of node.childNodes) {
const invisible = getModifier(child, "invisible");
let compiledChild = this.compileNode(child, params, false);
compiledChild = this.applyInvisible(invisible, compiledChild, {
...params,
recordExpr: "__comp__.model.root",
});
append(compiledRoot, compiledChild);
}
return compiledRoot;
}

compileChatter(node) {
return compileChatter(node, {
chatter: "__comp__.chatter",
threadId: "__comp__.model.root.resId or undefined",
threadModel: "__comp__.model.root.resModel",
webRecord: "__comp__.model.root",
});
}

compileAttachmentPreview(node) {
return compileAttachmentPreview(node, {
threadId: "__comp__.model.root.resId or undefined",
threadModel: "__comp__.model.root.resModel",
});
}
}

registry.category("form_compilers").add("chatter_compiler", {
selector: "div.oe_chatter",
fn: (node) =>
compileChatter(node, {
threadId: "__comp__.props.record.resId or undefined",
threadModel: "__comp__.props.record.resModel",
webRecord: "__comp__.props.record",
}),
fn: compileChatter,
});

registry.category("form_compilers").add("attachment_preview_compiler", {
selector: "div.o_attachment_preview",
fn: () => createElement("t"),
fn: compileAttachmentPreview,
});

patch(FormCompiler.prototype, "mail", {
Expand All @@ -156,7 +88,9 @@ patch(FormCompiler.prototype, "mail", {
if (!chatterContainerHookXml) {
return res; // no chatter, keep the result as it is
}
const chatterContainerXml = chatterContainerHookXml.querySelector("Chatter");
const chatterContainerXml = chatterContainerHookXml.querySelector(
"t[t-component='__comp__.mailComponents.Chatter']"
);
setAttributes(chatterContainerXml, {
hasMessageListScrollAdjust: "false",
isInFormSheetBg: "false",
Expand All @@ -170,24 +104,46 @@ patch(FormCompiler.prototype, "mail", {
if (!parentXml) {
return res; // miss-config: a sheet-bg is required for the rest
}
if (params.hasAttachmentViewerInArch) {

const webClientViewAttachmentViewHookXml = res.querySelector(".o_attachment_preview");
if (webClientViewAttachmentViewHookXml) {
// in sheet bg (attachment viewer present)
setAttributes(webClientViewAttachmentViewHookXml, {
"t-if": `__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL}`,
});
const sheetBgChatterContainerHookXml = chatterContainerHookXml.cloneNode(true);
sheetBgChatterContainerHookXml.classList.add("o-isInFormSheetBg");
setAttributes(sheetBgChatterContainerHookXml, {
"t-if": `__comp__.props.hasAttachmentViewer`,
"t-if": `__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL}`,
});
append(formSheetBgXml, sheetBgChatterContainerHookXml);
const sheetBgChatterContainerXml =
sheetBgChatterContainerHookXml.querySelector("Chatter");
const sheetBgChatterContainerXml = sheetBgChatterContainerHookXml.querySelector(
"t[t-component='__comp__.mailComponents.Chatter']"
);
setAttributes(sheetBgChatterContainerXml, {
isInFormSheetBg: "true",
hasMessageListScrollAdjust: "false",
});
}
// after sheet bg (standard position, either aside or below)
if (webClientViewAttachmentViewHookXml) {
setAttributes(chatterContainerHookXml, {
"t-if": `!(__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL})`,
"t-attf-class": `{{ __comp__.uiService.size >= ${SIZES.XXL} and !(__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL}) ? "o-aside" : "" }}`,
});
setAttributes(chatterContainerXml, {
isInFormSheetBg: "__comp__.hasAttachmentViewer()",
hasMessageListScrollAdjust: `__comp__.uiService.size >= ${SIZES.XXL} and !(__comp__.hasAttachmentViewer() and __comp__.uiService.size >= ${SIZES.XXL})`,
});
} else {
setAttributes(chatterContainerXml, {
isInFormSheetBg: "false",
hasMessageListScrollAdjust: `__comp__.uiService.size >= ${SIZES.XXL}`,
});
setAttributes(chatterContainerHookXml, {
"t-attf-class": `{{ __comp__.uiService.size >= ${SIZES.XXL} ? "o-aside" : "mt-4 mt-md-0" }}`,
});
}
// after sheet bg (standard position, below form)
setAttributes(chatterContainerHookXml, {
"t-if": `!__comp__.props.hasAttachmentViewer and __comp__.uiService.size < ${SIZES.XXL}`,
});
append(parentXml, chatterContainerHookXml);
return res;
},
Expand Down
88 changes: 0 additions & 88 deletions addons/mail/static/src/views/form/form_controller.js

This file was deleted.

7 changes: 0 additions & 7 deletions addons/mail/static/src/views/form/form_controller.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@
<templates xml:space="preserve">

<t t-inherit="web.FormView" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_form_view_container')]" position="after">
<t t-if="mailTemplate">
<t t-call="{{ mailTemplate }}" t-call-context="{ __comp__: Object.assign(Object.create(this), { this: this }) }"/>
</t>
</xpath>
<xpath expr="//Layout/t[@t-component='props.Renderer']" position="attributes">
<attribute name="hasAttachmentViewerInArch">hasAttachmentViewerInArch</attribute>
<attribute name="hasAttachmentViewer">hasAttachmentViewer()</attribute>
<attribute name="saveButtonClicked">() => this.saveButtonClicked()</attribute>
</xpath>
</t>
Expand Down
50 changes: 36 additions & 14 deletions addons/mail/static/src/views/form/form_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,51 @@

import { Chatter } from "@mail/web/chatter";
import { AttachmentView } from "@mail/attachments/attachment_view";

import { patch } from "@web/core/utils/patch";
import { FormRenderer } from "@web/views/form/form_renderer";
import { browser } from "@web/core/browser/browser";
import { useService } from "@web/core/utils/hooks";
import { SIZES } from "@web/core/ui/ui_service";
import { useDebounced } from "@web/core/utils/timing";
import { onMounted, onWillUnmount, useState } from "@odoo/owl";

patch(FormRenderer.prototype, "mail", {
get compileParams() {
return {
...this._super(),
hasAttachmentViewerInArch: this.props.hasAttachmentViewerInArch,
saveButtonClicked: this.props.saveButtonClicked,
setup() {
this._super();
this.mailComponents = {
AttachmentView,
Chatter,
};
this.messagingState = useState({
/** @type {import("@mail/core/thread_model").Thread} */
thread: undefined,
});
if (this.env.services["mail.thread"]) {
this.threadService = useService("mail.thread");
}
this.uiService = useService("ui");

this.onResize = useDebounced(this.render, 200);
onMounted(() => browser.addEventListener("resize", this.onResize));
onWillUnmount(() => browser.removeEventListener("resize", this.onResize));
},
/**
* @returns {boolean}
*/
hasAttachmentViewer() {
if (!this.threadService || this.uiService.size < SIZES.XXL || !this.props.record.resId) {
return false;
}
this.messagingState.thread = this.threadService.insert({
id: this.props.record.resId,
model: this.props.record.resModel,
type: "chatter",
});
return this.messagingState.thread.attachmentsInWebClientView.length > 0;
},
});

patch(FormRenderer.props, "mail", {
hasAttachmentViewerInArch: { type: Boolean, optional: true },
// Template props : added by the FormCompiler
hasAttachmentViewer: { type: Boolean, optional: true },
chatter: { type: Object, optional: true },
saveButtonClicked: { type: Function, optional: true },
});

Object.assign(FormRenderer.components, {
AttachmentView,
Chatter,
});
Loading

0 comments on commit 824024f

Please sign in to comment.