[MOV] mail, mrp, mail, web: move attachment_viewer from mail to web

The `mail` module's `attachment_viewer` is useful for more than `mail`
alone. In order to be able to use it in the `web_editor` module (which
doesn't depend on `mail`), we move it to the `web` module.

task-3109331

Part-of: odoo/odoo#119625
This commit is contained in:
Antoine Guenet
2023-05-23 16:31:21 +02:00
parent ebd31deee5
commit 8aec394f71
14 changed files with 62 additions and 59 deletions
@@ -5,7 +5,7 @@ import { Component } from "@odoo/owl";
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { useService } from "@web/core/utils/hooks";
import { sprintf } from "@web/core/utils/strings";
import { useAttachmentViewer } from "./attachment_viewer_hook";
import { useAttachmentViewer } from "@web/core/attachment_viewer/attachment_viewer_hook";
import { _t } from "@web/core/l10n/translation";
import { url } from "@web/core/utils/urls";
@@ -13,7 +13,7 @@
.arrow {
width: 5%;
@include o-position-absolute(50%, 0);
@include o-mail-AttachmentViewer-arrow;
@include o-AttachmentViewer-arrow;
transition: width 0.3s;
padding-top: 30px;
height: 75px;
@@ -15,7 +15,7 @@ $o-mail-Chatter-minWidth: 530px !default;
$o-mail-Discuss-inspector: 250px !default;
$o-mail-Discuss-headerHeight: 48px !default;
@mixin o-mail-AttachmentViewer-arrow {
@mixin o-AttachmentViewer-arrow {
background-color: rgba(black, 0.4);
color: rgba(map-get($theme-colors, 'light'), 0.7);
+1 -1
View File
@@ -27,7 +27,7 @@
@include o-position-absolute(50%, $grid-gutter-width*0.5);
border-radius: 100%;
padding: 12px 16px 11px 18px;
@include o-mail-AttachmentViewer-arrow;
@include o-AttachmentViewer-arrow;
}
.arrow-left {
@@ -115,7 +115,7 @@ QUnit.test("view attachment", async (assert) => {
await openDiscuss(channelId);
assert.containsOnce($, ".o-mail-AttachmentImage img");
await click(".o-mail-AttachmentImage");
assert.containsOnce($, ".o-mail-AttachmentViewer");
assert.containsOnce($, ".o-AttachmentViewer");
});
QUnit.test("close attachment viewer", async (assert) => {
@@ -140,10 +140,10 @@ QUnit.test("close attachment viewer", async (assert) => {
assert.containsOnce($, ".o-mail-AttachmentImage img");
await click(".o-mail-AttachmentImage");
assert.containsOnce($, ".o-mail-AttachmentViewer");
assert.containsOnce($, ".o-AttachmentViewer");
await click(".o-mail-AttachmentViewer div[aria-label='Close']");
assert.containsNone($, ".o-mail-AttachmentViewer");
await click(".o-AttachmentViewer div[aria-label='Close']");
assert.containsNone($, ".o-AttachmentViewer");
});
QUnit.test(
@@ -177,8 +177,8 @@ QUnit.test(
const { openDiscuss } = await start();
await openDiscuss(channelId);
await click(".o-mail-AttachmentImage");
const image = $(".o-mail-AttachmentViewer-viewImage")[0];
await click(".o-mail-AttachmentViewer div[aria-label='Close']");
const image = $(".o-AttachmentViewer-viewImage")[0];
await click(".o-AttachmentViewer div[aria-label='Close']");
// Simulate image becoming loaded.
let successfulLoad;
try {
@@ -314,10 +314,10 @@ QUnit.test(
click(".o-mail-AttachmentCard:contains(test.odt)").catch(() => {});
await nextTick();
assert.containsNone($, ".o-mail-AttachmentViewer");
assert.containsNone($, ".o-AttachmentViewer");
await click(".o-mail-AttachmentImage[title='test.png']");
assert.containsOnce($, ".o-mail-AttachmentViewer");
assert.containsOnce($, ".o-AttachmentViewer");
}
);
@@ -136,16 +136,16 @@ QUnit.test("view attachments", async (assert) => {
views: [[false, "form"]],
});
await click('.o-mail-AttachmentCard[aria-label="Blah.txt"] .o-mail-AttachmentCard-image');
assert.containsOnce($, ".o-mail-AttachmentViewer");
assert.containsOnce($, ".o-mail-AttachmentViewer-header:contains(Blah.txt)");
assert.containsOnce($, ".o-mail-AttachmentViewer div[aria-label='Next']");
assert.containsOnce($, ".o-AttachmentViewer");
assert.containsOnce($, ".o-AttachmentViewer-header:contains(Blah.txt)");
assert.containsOnce($, ".o-AttachmentViewer div[aria-label='Next']");
await click(".o-mail-AttachmentViewer div[aria-label='Next']");
assert.containsOnce($, ".o-mail-AttachmentViewer-header:contains(Blu.txt)");
assert.containsOnce($, ".o-mail-AttachmentViewer div[aria-label='Next']");
await click(".o-AttachmentViewer div[aria-label='Next']");
assert.containsOnce($, ".o-AttachmentViewer-header:contains(Blu.txt)");
assert.containsOnce($, ".o-AttachmentViewer div[aria-label='Next']");
await click(".o-mail-AttachmentViewer div[aria-label='Next']");
assert.containsOnce($, ".o-mail-AttachmentViewer-header:contains(Blah.txt)");
await click(".o-AttachmentViewer div[aria-label='Next']");
assert.containsOnce($, ".o-AttachmentViewer-header:contains(Blah.txt)");
});
QUnit.test("scroll to attachment box when toggling on", async (assert) => {
@@ -2,7 +2,7 @@
import { CANCEL_GLOBAL_CLICK, KanbanRecord } from "@web/views/kanban/kanban_record";
import { useService } from "@web/core/utils/hooks";
import { useAttachmentViewer } from "@mail/attachments/attachment_viewer_hook";
import { useAttachmentViewer } from "@web/core/attachment_viewer/attachment_viewer_hook";
export class MrpDocumentsKanbanRecord extends KanbanRecord {
setup() {
@@ -231,12 +231,12 @@ QUnit.module('MrpDocumentsKanbanView', {
assert.containsOnce(target, ".o_kanban_previewer");
await click(target.querySelector(".o_kanban_previewer"));
await waitUntil('.o-mail-AttachmentViewer');
assert.containsOnce(target, '.o-mail-AttachmentViewer');
assert.containsOnce(target, '.o-mail-AttachmentViewer-headerButton .fa-times');
await waitUntil('.o-AttachmentViewer');
assert.containsOnce(target, '.o-AttachmentViewer');
assert.containsOnce(target, '.o-AttachmentViewer-headerButton .fa-times');
await click(target, '.o-mail-AttachmentViewer-headerButton .fa-times');
assert.containsNone(target, '.o-mail-AttachmentViewer');
await click(target, '.o-AttachmentViewer-headerButton .fa-times');
assert.containsNone(target, '.o-AttachmentViewer');
});
});
+3
View File
@@ -185,6 +185,7 @@ This module provides the core of the Odoo Web Client.
('remove', 'web/static/src/webclient/navbar/navbar.scss'), # already in assets_common
('remove', 'web/static/src/webclient/clickbot/clickbot.js'), # lazy loaded
('remove', 'web/static/src/views/form/button_box/*.scss'),
('remove', 'web/static/src/core/attachment_viewer/attachment_viewer.dark.scss'),
# remove the report code and whitelist only what's needed
('remove', 'web/static/src/webclient/actions/reports/**/*'),
@@ -399,6 +400,7 @@ This module provides the core of the Odoo Web Client.
'web/static/src/core/**/*',
('remove', 'web/static/src/core/commands/**/*'),
('remove', 'web/static/src/core/debug/debug_menu.js'),
('remove', 'web/static/src/core/attachment_viewer/attachment_viewer.dark.scss'),
'web/static/src/public/error_notifications.js',
'web/static/src/legacy/utils.js',
@@ -481,6 +483,7 @@ This module provides the core of the Odoo Web Client.
# ---------------------------------------------------------------------
"web.dark_mode_assets_common": [
('include', 'web.assets_common'),
'web/static/src/core/attachment_viewer/attachment_viewer.dark.scss',
],
"web.dark_mode_assets_backend": [
('include', 'web.assets_backend'),
@@ -2,6 +2,6 @@
// ============================================================================
// No CSS hacks, variables overrides only
.o-mail-AttachmentViewer-toolbarButton {
--mail-attachment-viewer-toolbarButton-background-color: #{$o-gray-200};
.o-AttachmentViewer-toolbarButton {
--web-attachment-viewer-toolbarButton-background-color: #{$o-gray-200};
}
@@ -9,7 +9,7 @@ import { useService } from "@web/core/utils/hooks";
* @extends {Component<Props, Env>}
*/
export class AttachmentViewer extends Component {
static template = "mail.AttachmentViewer";
static template = "web.AttachmentViewer";
static components = {};
static props = ["attachments", "startIndex", "close", "modal?"];
static defaultProps = {
@@ -1,33 +1,33 @@
.o-mail-AttachmentViewer {
.o-AttachmentViewer {
z-index: -1;
outline: none;
}
.o-mail-AttachmentViewer-navigation {
.o-AttachmentViewer-navigation {
width: 40px;
height: 40px;
}
.o-mail-AttachmentViewer-header {
.o-AttachmentViewer-header {
height: $o-navbar-height;
}
.o-mail-AttachmentViewer-main {
.o-AttachmentViewer-main {
z-index: -1;
padding: ($o-navbar-height * 1.125) 0;
}
.o-mail-AttachmentViewer-zoomer {
.o-AttachmentViewer-zoomer {
padding: ($o-navbar-height * 1.125) 0;
}
.o-mail-AttachmentViewer-headerButton:hover {
.o-AttachmentViewer-headerButton:hover {
background-color: rgba($white, 0.1);
color: lighten($gray-400, 15%);
}
.o-mail-AttachmentViewer-toolbarButton {
background-color: var(--mail-attachment-viewer-toolbarButton-background-color, #{$o-gray-800});
.o-AttachmentViewer-toolbarButton {
background-color: var(--web-attachment-viewer-toolbarButton-background-color, #{$o-gray-800});
color: #fff;
&:hover {
@@ -35,7 +35,7 @@
}
}
.o-mail-AttachmentViewer-view {
.o-AttachmentViewer-view {
background-color: #000000;
box-shadow: 0 0 40px #000000;
outline: none;
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mail.AttachmentViewer" owl="1">
<t t-name="web.AttachmentViewer" owl="1">
<div class="d-flex justify-content-center" t-att-class="{ 'modal fixed-top bottom-0': props.modal }">
<div class="o-mail-AttachmentViewer flex-column align-items-center d-flex w-100 h-100" tabindex="0">
<div class="o-mail-AttachmentViewer-header position-absolute top-0 d-flex w-100 bg-black-75 text-400">
<div class="o-AttachmentViewer flex-column align-items-center d-flex w-100 h-100" tabindex="0">
<div class="o-AttachmentViewer-header position-absolute top-0 d-flex w-100 bg-black-75 text-400">
<div t-if="isViewable" class="d-flex align-items-center ms-4 me-2">
<i t-if="state.attachment.isImage" class="fa fa-picture-o" role="img" title="Image"/>
<i t-if="state.attachment.isPdf" class="fa fa-file-text" role="img" title="PDF file"/>
@@ -15,58 +15,58 @@
<span class="text-truncate" t-out="state.attachment.displayName"/>
</div>
<div class="flex-grow-1"/>
<div class="o-mail-AttachmentViewer-download o-mail-AttachmentViewer-headerButton d-flex align-items-center px-3 cursor-pointer" role="button" title="Download">
<div class="o-AttachmentViewer-download o-AttachmentViewer-headerButton d-flex align-items-center px-3 cursor-pointer" role="button" title="Download">
<a t-att-href="state.attachment.downloadUrl" class="text-reset" download="">
<i class="fa fa-download fa-fw" role="img"/>
<span>Download</span>
</a>
</div>
<div t-on-click.stop="close" class="o-mail-AttachmentViewer-headerButton d-flex align-items-center mb-0 px-3 h4 text-reset cursor-pointer" role="button" title="Close (Esc)" aria-label="Close">
<div t-on-click.stop="close" class="o-AttachmentViewer-headerButton d-flex align-items-center mb-0 px-3 h4 text-reset cursor-pointer" role="button" title="Close (Esc)" aria-label="Close">
<i class="fa fa-fw fa-times" role="img"/>
</div>
</div>
<div t-on-click.stop="close" t-on-mousemove="onMousemoveView" class="o-mail-AttachmentViewer-main position-absolute top-0 bottom-0 start-0 end-0 align-items-center justify-content-center d-flex" t-att-class="{ 'o_with_img overflow-hidden': state.attachment.isImage }">
<div t-if="state.attachment.isImage" class="o-mail-AttachmentViewer-zoomer position-absolute align-items-center justify-content-center d-flex w-100 h-100" t-ref="zoomer">
<div t-on-click.stop="close" t-on-mousemove="onMousemoveView" class="o-AttachmentViewer-main position-absolute top-0 bottom-0 start-0 end-0 align-items-center justify-content-center d-flex" t-att-class="{ 'o_with_img overflow-hidden': state.attachment.isImage }">
<div t-if="state.attachment.isImage" class="o-AttachmentViewer-zoomer position-absolute align-items-center justify-content-center d-flex w-100 h-100" t-ref="zoomer">
<div t-if="!state.imageLoaded" class="position-absolute">
<i class="fa fa-3x fa-circle-o-notch fa-fw fa-spin text-white" role="img" title="Loading"/>
</div>
<img t-on-click.stop="" t-on-load="onImageLoaded" t-on-wheel="onWheelImage" t-on-mousedown.stop="onMousedownImage" t-on-mouseup.stop="onMouseupImage" class="o-mail-AttachmentViewer-view o-mail-AttachmentViewer-viewImage mw-100 mh-100 transition-base" t-att-src="state.attachment.defaultSource" t-att-style="imageStyle" draggable="false" alt="Viewer" t-ref="image"/>
<img t-on-click.stop="" t-on-load="onImageLoaded" t-on-wheel="onWheelImage" t-on-mousedown.stop="onMousedownImage" t-on-mouseup.stop="onMouseupImage" class="o-AttachmentViewer-view o-AttachmentViewer-viewImage mw-100 mh-100 transition-base" t-att-src="state.attachment.defaultSource" t-att-style="imageStyle" draggable="false" alt="Viewer" t-ref="image"/>
</div>
<iframe t-if="state.attachment.isPdf" class="o-mail-AttachmentViewer-view w-75 h-100 border-0" t-ref="iframeViewerPdf" t-att-class="{ 'w-100': ui.isSmall }" t-att-src="state.attachment.defaultSource"/>
<iframe t-if="state.attachment.isText" class="o-mail-AttachmentViewer-view o-isText o_text w-75 h-100 border-0" t-att-src="state.attachment.defaultSource"/>
<iframe t-if="state.attachment.isUrlYoutube" allow="autoplay; encrypted-media" class="o-mail-AttachmentViewer-view w-75 h-100 border-0" t-att-src="state.attachment.defaultSource" height="315" width="560"/>
<video t-if="state.attachment.isVideo" class="o-mail-AttachmentViewer-view w-75 h-75" t-att-class="{ 'w-100 h-100': ui.isSmall }" t-on-click.stop="" controls="controls">
<iframe t-if="state.attachment.isPdf" class="o-AttachmentViewer-view w-75 h-100 border-0" t-ref="iframeViewerPdf" t-att-class="{ 'w-100': ui.isSmall }" t-att-src="state.attachment.defaultSource"/>
<iframe t-if="state.attachment.isText" class="o-AttachmentViewer-view o-isText o_text w-75 h-100 border-0" t-att-src="state.attachment.defaultSource"/>
<iframe t-if="state.attachment.isUrlYoutube" allow="autoplay; encrypted-media" class="o-AttachmentViewer-view w-75 h-100 border-0" t-att-src="state.attachment.defaultSource" height="315" width="560"/>
<video t-if="state.attachment.isVideo" class="o-AttachmentViewer-view w-75 h-75" t-att-class="{ 'w-100 h-100': ui.isSmall }" t-on-click.stop="" controls="controls">
<source t-att-data-type="state.attachment.mimetype" t-att-src="state.attachment.defaultSource"/>
</video>
</div>
<div t-if="state.attachment.isImage" class="position-absolute bottom-0 d-flex" role="toolbar">
<div class="o-mail-AttachmentViewer-toolbarButton p-3 rounded-0" t-on-click="zoomIn" title="Zoom In (+)" role="button">
<div class="o-AttachmentViewer-toolbarButton p-3 rounded-0" t-on-click="zoomIn" title="Zoom In (+)" role="button">
<i class="fa fa-fw fa-plus" role="img"/>
</div>
<div class="o-mail-AttachmentViewer-toolbarButton p-3 rounded-0" t-att-class="{ 'o_disabled opacity-50': state.scale === 1 }" t-on-click="resetZoom" role="button" title="Reset Zoom (0)">
<div class="o-AttachmentViewer-toolbarButton p-3 rounded-0" t-att-class="{ 'o_disabled opacity-50': state.scale === 1 }" t-on-click="resetZoom" role="button" title="Reset Zoom (0)">
<i class="fa fa-fw fa-search" role="img"/>
</div>
<div class="o-mail-AttachmentViewer-toolbarButton p-3 rounded-0" t-att-class="{ 'o_disabled opacity-50': state.scale === minScale }" t-on-click="zoomOut" title="Zoom Out (-)" role="button">
<div class="o-AttachmentViewer-toolbarButton p-3 rounded-0" t-att-class="{ 'o_disabled opacity-50': state.scale === minScale }" t-on-click="zoomOut" title="Zoom Out (-)" role="button">
<i class="fa fa-fw fa-minus" role="img"/>
</div>
<div class="o-mail-AttachmentViewer-toolbarButton p-3 rounded-0" t-on-click="rotate" title="Rotate (r)" role="button">
<div class="o-AttachmentViewer-toolbarButton p-3 rounded-0" t-on-click="rotate" title="Rotate (r)" role="button">
<i class="fa fa-fw fa-repeat" role="img"/>
</div>
<div class="o-mail-AttachmentViewer-toolbarButton p-3 rounded-0" t-on-click="onClickPrint" title="Print" role="button">
<div class="o-AttachmentViewer-toolbarButton p-3 rounded-0" t-on-click="onClickPrint" title="Print" role="button">
<i class="fa fa-fw fa-print" role="img"/>
</div>
<div class="o-mail-AttachmentViewer-download o-mail-AttachmentViewer-toolbarButton p-3 rounded-0 cursor-pointer" title="Download" role="button">
<div class="o-AttachmentViewer-download o-AttachmentViewer-toolbarButton p-3 rounded-0 cursor-pointer" title="Download" role="button">
<a t-att-href="state.attachment.downloadUrl" class="text-reset" download="">
<i class="fa fa-download fa-fw" role="img"/>
</a>
</div>
</div>
<t t-if="props.attachments.length > 1">
<div class="o-mail-AttachmentViewer-navigation position-absolute top-0 bottom-0 start-0 align-items-center justify-content-center d-flex my-auto ms-3 rounded-circle" t-on-click="previous" title="Previous (Left-Arrow)" aria-label="Previous" role="button">
<div class="o-AttachmentViewer-navigation position-absolute top-0 bottom-0 start-0 align-items-center justify-content-center d-flex my-auto ms-3 rounded-circle" t-on-click="previous" title="Previous (Left-Arrow)" aria-label="Previous" role="button">
<span class="oi oi-chevron-left" role="img"/>
</div>
<div class="o-mail-AttachmentViewer-navigation position-absolute top-0 bottom-0 end-0 align-items-center justify-content-center d-flex my-auto me-3 rounded-circle" t-on-click="next" title="Next (Right-Arrow)" aria-label="Next" role="button">
<div class="o-AttachmentViewer-navigation position-absolute top-0 bottom-0 end-0 align-items-center justify-content-center d-flex my-auto me-3 rounded-circle" t-on-click="next" title="Next (Right-Arrow)" aria-label="Next" role="button">
<span class="oi oi-chevron-right" role="img"/>
</div>
</t>
@@ -7,7 +7,7 @@ import { AttachmentViewer } from "./attachment_viewer";
let id = 1;
export function useAttachmentViewer() {
const attachmentViewerId = `mail.attachment_viewer${id++}`;
const attachmentViewerId = `web.attachment_viewer${id++}`;
/**
* @param {import("@mail/attachments/attachment_model").Attachment} attachment
* @param {import("@mail/attachments/attachment_model").Attachment[]} attachments