[IMP] mail, im_livechat: use ui service instead of store.isSmall
Since [1], the ui service is reactive which removes the needs for the `mail.store` service to maintain a reactive version of `isSmall`. This PR replaces the use of `store.isSmall` as well as `env.isSmall` by the ui service. 1: https://github.com/odoo/odoo/pull/121343 closes odoo/odoo#121590 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -27,7 +27,7 @@ patch(ThreadService.prototype, "im_livechat", {
|
||||
*/
|
||||
setDiscussThread(thread, pushState) {
|
||||
this._super(thread, pushState);
|
||||
if (this.store.isSmall && thread.type === "livechat") {
|
||||
if (this.ui.isSmall && thread.type === "livechat") {
|
||||
this.store.discuss.activeTab = "livechat";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ Model({
|
||||
globalWindowInnerHeight: this.messaging.browser.innerHeight,
|
||||
globalWindowInnerWidth: this.messaging.browser.innerWidth,
|
||||
isMobileDevice: isMobileOS(),
|
||||
isSmall: this.env.isSmall,
|
||||
isSmall: this.env.services.ui.isSmall,
|
||||
sizeClass: this.env.services.ui.size,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { Component, useExternalListener, useRef, useState } from "@odoo/owl";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
@@ -41,6 +42,7 @@ export class AttachmentViewer extends Component {
|
||||
scale: 1,
|
||||
angle: 0,
|
||||
});
|
||||
this.ui = useState(useService("ui"));
|
||||
}
|
||||
|
||||
onImageLoaded() {
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
</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"/>
|
||||
</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': env.isSmall }" t-att-src="state.attachment.defaultSource"/>
|
||||
<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': env.isSmall }" t-on-click.stop="" controls="controls">
|
||||
<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">
|
||||
<source t-att-data-type="state.attachment.mimetype" t-att-src="state.attachment.defaultSource"/>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
@@ -73,6 +73,7 @@ export class Composer extends Component {
|
||||
this.messageService = useState(useService("mail.message"));
|
||||
/** @type {import("@mail/core/thread_service").ThreadService} */
|
||||
this.threadService = useService("mail.thread");
|
||||
this.ui = useState(useService("ui"));
|
||||
this.ref = useRef("textarea");
|
||||
this.fakeTextarea = useRef("fakeTextarea");
|
||||
this.state = useState({
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
</div>
|
||||
<div class="o-mail-Composer-actions d-flex bg-view"
|
||||
t-att-class="{
|
||||
'ms-1' : compact and store.isSmall,
|
||||
'mx-1 me-2' : compact and !store.isSmall,
|
||||
'ms-1' : compact and ui.isSmall,
|
||||
'mx-1 me-2' : compact and !ui.isSmall,
|
||||
'ms-3' : normal,
|
||||
'mx-3 border-top p-1' : extended,
|
||||
}"
|
||||
@@ -96,14 +96,14 @@
|
||||
</t>
|
||||
|
||||
<t t-name="mail.Composer.sendButton" owl="1">
|
||||
<button t-if="!compact or store.isSmall" class="o-mail-Composer-send btn btn-link o-last border-start-0"
|
||||
<button t-if="!compact or ui.isSmall" class="o-mail-Composer-send btn btn-link o-last border-start-0"
|
||||
t-att-class="{'mt-2': extended, 'rounded-0 rounded-end-3': !extended and !compact, 'align-self-stretch': !extended, 'border-start-0': env.inDiscussApp}"
|
||||
t-on-click="sendMessage"
|
||||
type="button"
|
||||
t-att-disabled="isSendButtonDisabled"
|
||||
aria-label="Send"
|
||||
>
|
||||
<t t-if="store.isSmall"><i class="fa fa-paper-plane-o"/></t>
|
||||
<t t-if="ui.isSmall"><i class="fa fa-paper-plane-o"/></t>
|
||||
<t t-else="">Send</t>
|
||||
</button>
|
||||
</t>
|
||||
|
||||
@@ -14,7 +14,7 @@ export class Store {
|
||||
|
||||
setup(env) {
|
||||
this.env = env;
|
||||
this.isSmall = env.isSmall;
|
||||
this.discuss.activeTab = this.env.services.ui.isSmall ? "mailbox" : "all";
|
||||
}
|
||||
|
||||
async updateBusSubscription() {
|
||||
@@ -149,10 +149,8 @@ export const storeService = {
|
||||
start(env, services) {
|
||||
const res = reactive(new Store(env, services));
|
||||
onChange(res, "threads", () => res.updateBusSubscription());
|
||||
res.discuss.activeTab = res.isSmall ? "mailbox" : "all";
|
||||
services.ui.bus.addEventListener("resize", () => {
|
||||
res.isSmall = services.ui.isSmall;
|
||||
if (!res.isSmall) {
|
||||
if (!services.ui.isSmall) {
|
||||
res.discuss.activeTab = "all";
|
||||
} else {
|
||||
res.discuss.activeTab = res.threads[res.discuss.threadLocalId]?.type ?? "all";
|
||||
|
||||
@@ -39,6 +39,7 @@ export class ThreadService {
|
||||
this.rpc = services.rpc;
|
||||
this.notificationService = services.notification;
|
||||
this.router = services.router;
|
||||
this.ui = services.ui;
|
||||
/** @type {import("@mail/core/persona_service").PersonaService} */
|
||||
this.personaService = services["mail.persona"];
|
||||
/** @type {import("@mail/core/message_service").MessageService} */
|
||||
@@ -637,7 +638,7 @@ export class ThreadService {
|
||||
typeof thread.id === "string"
|
||||
? `mail.box_${thread.id}`
|
||||
: `discuss.channel_${thread.id}`;
|
||||
this.store.discuss.activeTab = !this.store.isSmall
|
||||
this.store.discuss.activeTab = !this.ui.isSmall
|
||||
? "all"
|
||||
: thread.model === "mail.box"
|
||||
? "mailbox"
|
||||
@@ -1101,6 +1102,7 @@ export const threadService = {
|
||||
"router",
|
||||
"mail.persona",
|
||||
"mail.message",
|
||||
"ui",
|
||||
],
|
||||
start(env, services) {
|
||||
return new ThreadService(env, services);
|
||||
|
||||
@@ -24,6 +24,7 @@ export class MessageReactionMenu extends Component {
|
||||
this.threadService = useService("mail.thread");
|
||||
this.root = useRef("root");
|
||||
this.store = useStore();
|
||||
this.ui = useState(useService("ui"));
|
||||
this.messageService = useService("mail.message");
|
||||
this.state = useState({
|
||||
reaction: this.props.message.reactions[0],
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</t>
|
||||
</div>
|
||||
<div class="d-flex overflow-auto flex-column flex-grow-1 bg-view p-2 h-100">
|
||||
<div t-foreach="state.reaction.personas" t-as="persona" t-key="persona.id" class="o-mail-MessageReactionMenu-persona d-flex p-1 align-items-center" t-att-class="{ 'o-isDeviceSmall': store.isSmall }">
|
||||
<div t-foreach="state.reaction.personas" t-as="persona" t-key="persona.id" class="o-mail-MessageReactionMenu-persona d-flex p-1 align-items-center" t-att-class="{ 'o-isDeviceSmall': ui.isSmall }">
|
||||
<img class="rounded-circle o_object_fit_cover o-mail-MessageReactionMenu-avatar" t-att-src="threadService.avatarUrl(persona, props.message.originThread)"/>
|
||||
<span class="d-flex flex-grow-1 align-items-center">
|
||||
<span class="mx-2 text-truncate fs-6" t-esc="persona.name"/>
|
||||
|
||||
@@ -14,6 +14,7 @@ export class MessageReactions extends Component {
|
||||
this.user = useService("user");
|
||||
this.messaging = useService("mail.messaging");
|
||||
this.store = useStore();
|
||||
this.ui = useService("ui");
|
||||
this.messageService = useState(useService("mail.message"));
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ export class MessageReactions extends Component {
|
||||
}
|
||||
|
||||
onContextMenu(ev) {
|
||||
if (this.store.isSmall) {
|
||||
if (this.ui.isSmall) {
|
||||
ev.preventDefault();
|
||||
this.props.openReactionMenu();
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export class Thread extends Component {
|
||||
this.state = useState({ isReplyingTo: false, showJumpPresent: false });
|
||||
/** @type {import("@mail/core/thread_service").ThreadService} */
|
||||
this.threadService = useState(useService("mail.thread"));
|
||||
this.ui = useState(useService("ui"));
|
||||
if (!this.env.inChatter || !this.props.hasScrollAdjust) {
|
||||
useAutoScroll("messages", () => {
|
||||
if (this.env.messageHighlight?.highlightedMessageId) {
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
<t t-name="mail.NotificationMessage" owl="1">
|
||||
<div class="mx-auto text-500 small px-3 text-center" t-on-click="onClickNotification" t-att-class="{
|
||||
'o-mail-Thread-noPinnedPanel': env.inChatWindow and !env.isSmall,
|
||||
'o-mail-Thread-noPinnedPanel': env.inChatWindow and !ui.isSmall,
|
||||
'mt-2': prevMsg and !prevMsg.isNotification,
|
||||
}">
|
||||
<i t-if="msg.notificationIcon" t-attf-class="{{ msg.notificationIcon }} me-1"/>
|
||||
|
||||
@@ -19,6 +19,7 @@ export class ChannelInvitation extends Component {
|
||||
this.notification = useService("notification");
|
||||
this.threadService = useState(useService("mail.thread"));
|
||||
this.personaService = useService("mail.persona");
|
||||
this.ui = useService("ui");
|
||||
this.inputRef = useRef("input");
|
||||
this.searchStr = "";
|
||||
this.state = useState({
|
||||
@@ -107,7 +108,7 @@ export class ChannelInvitation extends Component {
|
||||
}
|
||||
);
|
||||
}
|
||||
if (this.env.isSmall) {
|
||||
if (this.ui.isSmall) {
|
||||
this.props.chatState.activeMode = "";
|
||||
} else {
|
||||
this.props.close();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates xml:space="preserve">
|
||||
<t t-inherit="mail.ChatWindow" t-inherit-mode="extension">
|
||||
<xpath expr="//*[@t-ref='startCall']" position="after">
|
||||
<div t-if="env.isSmall and thread and thread.type === 'channel'" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" t-att-title="state.activeMode === 'add-users' ? 'Stop adding users' : 'Add users'" t-on-click.stop="toggleAddUsers">
|
||||
<div t-if="ui.isSmall and thread and thread.type === 'channel'" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" t-att-title="state.activeMode === 'add-users' ? 'Stop adding users' : 'Add users'" t-on-click.stop="toggleAddUsers">
|
||||
<i class="fa fa-lg fa-user-plus"/>
|
||||
</div>
|
||||
</xpath>
|
||||
|
||||
@@ -62,6 +62,7 @@ export class Discuss extends Component {
|
||||
this.state = useState({ activeMode: this.MODES.NONE });
|
||||
this.orm = useService("orm");
|
||||
this.effect = useService("effect");
|
||||
this.ui = useState(useService("ui"));
|
||||
this.prevInboxCounter = this.store.discuss.inbox.counter;
|
||||
useChildSubEnv({
|
||||
inDiscussApp: true,
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.Discuss" owl="1">
|
||||
<div class="o-mail-Discuss d-flex h-100 flex-grow-1 border-top" t-att-class="{ 'flex-column align-items-center bg-view': store.isSmall }" t-ref="root">
|
||||
<div t-if="store.isSmall and store.discuss.activeTab === 'mailbox'" class="w-100 border-bottom" t-call="mail.Discuss.mobileTopbar" t-ref="mobileTopbar"/>
|
||||
<div t-if="thread and !(store.isSmall and ['chat', 'channel'].includes(store.discuss.activeTab))" class="o-mail-Discuss-content d-flex flex-column h-100 w-100 bg-view overflow-auto" t-ref="content">
|
||||
<div class="o-mail-Discuss d-flex h-100 flex-grow-1 border-top" t-att-class="{ 'flex-column align-items-center bg-view': ui.isSmall }" t-ref="root">
|
||||
<div t-if="ui.isSmall and store.discuss.activeTab === 'mailbox'" class="w-100 border-bottom" t-call="mail.Discuss.mobileTopbar" t-ref="mobileTopbar"/>
|
||||
<div t-if="thread and !(ui.isSmall and ['chat', 'channel'].includes(store.discuss.activeTab))" class="o-mail-Discuss-content d-flex flex-column h-100 w-100 bg-view overflow-auto" t-ref="content">
|
||||
<div class="o-mail-Discuss-header px-3 bg-view border-bottom d-flex flex-shrink-0 align-items-center" style="height:46px;">
|
||||
<div t-if="thread and thread.model === 'discuss.channel'" class="o-mail-Discuss-threadAvatar position-relative align-self-center align-items-center mt-2 mb-2 me-2">
|
||||
<img class="rounded" t-att-src="channelAvatar" alt="Thread Image"/>
|
||||
@@ -48,7 +48,7 @@
|
||||
<button t-if="thread.allowCalls and thread.type !== 'mailbox' and thread !== rtc.state.channel" class="btn rounded px-2 shadow-none" title="Start a Call" t-on-click="() => this.rtc.toggleCall(this.thread)" t-ref="startCall">
|
||||
<i class="fa fa-lg fa-phone text-700"/>
|
||||
</button>
|
||||
<div t-if="props.public and !store.isSmall" class="d-flex align-items-center">
|
||||
<div t-if="props.public and !ui.isSmall" class="d-flex align-items-center">
|
||||
<img class="o-mail-Discuss-selfAvatar mx-1 rounded-circle o_object_fit_cover flex-shrink-0" alt="Avatar" t-att-src="threadService.avatarUrl(store.self, thread)"/>
|
||||
<div class="lead fw-bold flex-shrink-1 text-dark">
|
||||
<t t-if="store.user" t-esc="store.user.name"/>
|
||||
@@ -72,7 +72,7 @@
|
||||
<PinnedMessagesPanel t-if="state.activeMode === MODES.PINNED_MESSAGES and thread.model === 'discuss.channel'" thread="thread"/>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="!thread and (!store.isSmall or store.discuss.activeTab === 'mailbox')" class="d-flex flex-grow-1 align-items-center justify-content-center w-100 bg-view">
|
||||
<div t-if="!thread and (!ui.isSmall or store.discuss.activeTab === 'mailbox')" class="d-flex flex-grow-1 align-items-center justify-content-center w-100 bg-view">
|
||||
<h4 class="text-muted"><b><i>No conversation selected.</i></b></h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { Component, onWillStart, onWillUpdateProps, useSubEnv } from "@odoo/owl";
|
||||
import { Component, onWillStart, onWillUpdateProps, useState, useSubEnv } from "@odoo/owl";
|
||||
import { Message } from "@mail/core_ui/message";
|
||||
import { MessageConfirmDialog } from "@mail/core_ui/message_confirm_dialog";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
@@ -18,6 +18,8 @@ export class PinnedMessagesPanel extends Component {
|
||||
this.messageService = useService("mail.message");
|
||||
this.store = useService("mail.store");
|
||||
this.rpc = useService("rpc");
|
||||
this.ui = useState(useService("ui"));
|
||||
|
||||
onWillStart(() => {
|
||||
this.threadService.fetchPinnedMessages(this.props.thread);
|
||||
});
|
||||
@@ -38,7 +40,7 @@ export class PinnedMessagesPanel extends Component {
|
||||
* @param {Message} message
|
||||
*/
|
||||
async onClickJump(message) {
|
||||
if (this.env.isSmall) {
|
||||
if (this.ui.isSmall) {
|
||||
this.env.pinMenu.close();
|
||||
// Give the time to the pin menu to close before scrolling
|
||||
// to the message.
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
<div class="card m-3 mt-0" t-foreach="props.thread.pinnedMessages" t-as="message" t-key="message.id">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-end">
|
||||
<button class="o-mail-PinnedMessagesPanel-jump btn rounded bg-400 badge opacity-0 flex-shrink-0" t-att-class="{ 'opacity-100 py-1 px-2': env.isSmall }" t-on-click="() => this.onClickJump(message)">Jump</button>
|
||||
<button class="btn ms-2 p-0 text-700 shadow-none" t-att-class="{ 'fs-5 align-self-end': env.isSmall }" title="Unpin"
|
||||
<button class="o-mail-PinnedMessagesPanel-jump btn rounded bg-400 badge opacity-0 flex-shrink-0" t-att-class="{ 'opacity-100 py-1 px-2': ui.isSmall }" t-on-click="() => this.onClickJump(message)">Jump</button>
|
||||
<button class="btn ms-2 p-0 text-700 shadow-none" t-att-class="{ 'fs-5 align-self-end': ui.isSmall }" title="Unpin"
|
||||
t-on-click="() => this.onClickUnpin(message)">
|
||||
<i class="d-block fa fa-times"/>
|
||||
</button>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useMessaging } from "@mail/core/messaging_hook";
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
import { Layout } from "@web/search/layout";
|
||||
import { standardViewProps } from "@web/views/standard_view_props";
|
||||
@@ -41,6 +41,7 @@ export class ActivityController extends Component {
|
||||
this.action = useService("action");
|
||||
this.messaging = useMessaging();
|
||||
this.activity = useService("mail.activity");
|
||||
this.ui = useState(useService("ui"));
|
||||
}
|
||||
|
||||
scheduleActivity() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mail.ActivityController" owl="1">
|
||||
<div t-att-class="props.className + (env.isSmall ? ' o_action_delegate_scroll': '')">
|
||||
<div t-att-class="props.className + (ui.isSmall ? ' o_action_delegate_scroll': '')">
|
||||
<Layout display="props.display">
|
||||
<t t-set-slot="control-panel-additional-actions">
|
||||
<CogMenu/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useMessaging, useStore } from "@mail/core/messaging_hook";
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
import { Dropdown } from "@web/core/dropdown/dropdown";
|
||||
import { registry } from "@web/core/registry";
|
||||
@@ -18,6 +18,7 @@ export class ActivityMenu extends Component {
|
||||
this.store = useStore();
|
||||
this.action = useService("action");
|
||||
this.userId = useService("user").userId;
|
||||
this.ui = useState(useService("ui"));
|
||||
this.fetchSystrayActivities();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ActivityMenu" owl="1">
|
||||
<Dropdown position="'bottom-end'" autoOpen="false" beforeOpen.bind="onBeforeOpen" menuClass="`p-0 ${ store.isSmall ? 'start-0 w-100 mh-100 bottom-0 d-flex flex-column' : ''}`">
|
||||
<Dropdown position="'bottom-end'" autoOpen="false" beforeOpen.bind="onBeforeOpen" menuClass="`p-0 ${ ui.isSmall ? 'start-0 w-100 mh-100 bottom-0 d-flex flex-column' : ''}`">
|
||||
<t t-set-slot="toggler">
|
||||
<i class="fa fa-lg fa-clock-o" role="img" aria-label="Activities"></i>
|
||||
<span t-if="store.activityCounter" class="o-mail-ActivityMenu-counter badge rounded-pill"><t t-esc="store.activityCounter"/></span>
|
||||
</t>
|
||||
<t t-set-slot="default">
|
||||
<div class="o-mail-ActivityMenu bg-view flex-grow-1 d-flex flex-column" t-att-class="{ 'overflow-auto w-100 mh-100': store.isSmall }">
|
||||
<div class="o-mail-ActivityMenu bg-view flex-grow-1 d-flex flex-column" t-att-class="{ 'overflow-auto w-100 mh-100': ui.isSmall }">
|
||||
<div t-if="store.activityGroups.length === 0" class="o-mail-ActivityMenu-empty align-items-center text-muted p-2 opacity-50 d-flex justify-content-center">
|
||||
<span>Congratulations, you're done with your activities.</span>
|
||||
</div>
|
||||
|
||||
@@ -57,6 +57,7 @@ export class ChatWindow extends Component {
|
||||
activeMode: "",
|
||||
});
|
||||
this.action = useService("action");
|
||||
this.ui = useState(useService("ui"));
|
||||
this.contentRef = useRef("content");
|
||||
useChildSubEnv({
|
||||
inChatWindow: true,
|
||||
@@ -77,10 +78,10 @@ export class ChatWindow extends Component {
|
||||
}
|
||||
|
||||
get style() {
|
||||
const maxHeight = !this.store.isSmall ? "max-height: 95vh;" : "";
|
||||
const maxHeight = !this.ui.isSmall ? "max-height: 95vh;" : "";
|
||||
const textDirection = localization.direction;
|
||||
const offsetFrom = textDirection === "rtl" ? "left" : "right";
|
||||
const visibleOffset = this.store.isSmall ? 0 : this.props.right;
|
||||
const visibleOffset = this.ui.isSmall ? 0 : this.props.right;
|
||||
const oppositeFrom = offsetFrom === "right" ? "left" : "right";
|
||||
return `${offsetFrom}: ${visibleOffset}px; ${oppositeFrom}: auto; ${maxHeight}`;
|
||||
}
|
||||
@@ -112,7 +113,7 @@ export class ChatWindow extends Component {
|
||||
}
|
||||
|
||||
toggleFold() {
|
||||
if (this.store.isSmall) {
|
||||
if (this.ui.isSmall) {
|
||||
return;
|
||||
}
|
||||
if (this.props.chatWindow.hidden) {
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
<div class="o-mail-ChatWindow"
|
||||
t-att-style="style"
|
||||
t-att-class="{
|
||||
'w-100 h-100': store.isSmall,
|
||||
'w-100 h-100': ui.isSmall,
|
||||
'o-folded': props.chatWindow.folded,
|
||||
'position-absolute bottom-0 overflow-hidden d-flex flex-column': !props.chatWindow.hidden,
|
||||
'rounded-top-3': !props.chatWindow.hidden and !store.isSmall,
|
||||
'rounded-top-3': !props.chatWindow.hidden and !ui.isSmall,
|
||||
}"
|
||||
t-on-keydown.stop="onKeydown"
|
||||
tabindex="1"
|
||||
>
|
||||
<div class="o-mail-ChatWindow-header d-flex align-items-center flex-shrink-0 bg-100" t-on-click="toggleFold" t-att-class="{ 'cursor-pointer': !store.isSmall, 'border-bottom': !props.chatWindow.folded }">
|
||||
<div t-if="store.isSmall" class="cursor-pointer d-flex align-items-center h-100 px-3 py-0 me-2" title="Close conversation" t-on-click="() => this.close()">
|
||||
<div class="o-mail-ChatWindow-header d-flex align-items-center flex-shrink-0 bg-100" t-on-click="toggleFold" t-att-class="{ 'cursor-pointer': !ui.isSmall, 'border-bottom': !props.chatWindow.folded }">
|
||||
<div t-if="ui.isSmall" class="cursor-pointer d-flex align-items-center h-100 px-3 py-0 me-2" title="Close conversation" t-on-click="() => this.close()">
|
||||
<i class="oi oi-arrow-left"/>
|
||||
</div>
|
||||
<div t-if="thread and thread.model === 'discuss.channel'" class="o-mail-ChatWindow-threadAvatar position-relative align-self-center align-items-center my-0" t-att-class="{ 'ms-3': !store.isSmall, 'me-1': thread.type === 'chat', 'me-2': thread.type !== 'chat' }">
|
||||
<div t-if="thread and thread.model === 'discuss.channel'" class="o-mail-ChatWindow-threadAvatar position-relative align-self-center align-items-center my-0" t-att-class="{ 'ms-3': !ui.isSmall, 'me-1': thread.type === 'chat', 'me-2': thread.type !== 'chat' }">
|
||||
<img class="rounded" t-att-src="thread.imgUrl" alt="Thread Image"/>
|
||||
</div>
|
||||
<t t-else="">
|
||||
@@ -32,14 +32,14 @@
|
||||
(<t t-out="thread.message_unread_counter"/>)
|
||||
</div>
|
||||
<div class="flex-grow-1"/>
|
||||
<div t-if="thread?.model === 'discuss.channel' and env.isSmall" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" title="Pinned messages" t-on-click.stop="togglePinMenu">
|
||||
<div t-if="thread?.model === 'discuss.channel' and ui.isSmall" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" title="Pinned messages" t-on-click.stop="togglePinMenu">
|
||||
<i class="fa fa-lg fa-thumb-tack"/>
|
||||
</div>
|
||||
<div t-if="thread and thread.allowCalls and thread !== rtc.state.channel and !props.chatWindow.hidden" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" title="Start a Call" t-on-click.stop="() => this.rtc.toggleCall(this.props.chatWindow.thread)" t-ref="startCall">
|
||||
<i class="fa fa-phone"/>
|
||||
</div>
|
||||
<t t-if="thread">
|
||||
<div t-if="!env.isSmall" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" title="Open in Discuss" t-on-click.stop="expand" t-ref="expand">
|
||||
<div t-if="!ui.isSmall" class="o-mail-ChatWindow-command d-flex align-items-center h-100 px-3 py-0 m-0 opacity-75 opacity-100-hover" title="Open in Discuss" t-on-click.stop="expand" t-ref="expand">
|
||||
<i class="fa fa-expand"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -45,6 +45,7 @@ export class ChatWindowContainer extends Component {
|
||||
this.messaging = useMessaging();
|
||||
this.store = useStore();
|
||||
this.chatWindowService = useState(useService("mail.chat_window"));
|
||||
this.ui = useState(useService("ui"));
|
||||
this.hiddenMenuRef = useRef("hiddenMenu");
|
||||
useEffect(
|
||||
() => this.setHiddenMenuOffset(),
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.ChatWindowContainer" owl="1">
|
||||
<div class="o-mail-ChatWindowContainer" t-if="!store.discuss.isActive or store.isSmall">
|
||||
<div t-if="chatWindowService.hidden.length > 0 and !store.isSmall" t-ref="hiddenMenu" class="o-mail-ChatWindow-hiddenMenuContainer position-fixed bottom-0">
|
||||
<div class="o-mail-ChatWindowContainer" t-if="!store.discuss.isActive or ui.isSmall">
|
||||
<div t-if="chatWindowService.hidden.length > 0 and !ui.isSmall" t-ref="hiddenMenu" class="o-mail-ChatWindow-hiddenMenuContainer position-fixed bottom-0">
|
||||
<t t-call="mail.ChatWindowHiddenMenu"/>
|
||||
</div>
|
||||
<t t-foreach="chatWindowService.visible" t-as="chatWindow" t-key="chatWindow.threadLocalId">
|
||||
|
||||
@@ -16,6 +16,7 @@ export class ChatWindowService {
|
||||
/** @type {import("@mail/core/store_service").Store} */
|
||||
this.store = services["mail.store"];
|
||||
this.orm = services.orm;
|
||||
this.ui = services.ui;
|
||||
}
|
||||
|
||||
openNewMessage() {
|
||||
@@ -42,12 +43,12 @@ export class ChatWindowService {
|
||||
}
|
||||
|
||||
get maxVisible() {
|
||||
const startGap = this.store.isSmall
|
||||
const startGap = this.ui.isSmall
|
||||
? 0
|
||||
: this.hidden.length > 0
|
||||
? CHAT_WINDOW_END_GAP_WIDTH + CHAT_WINDOW_HIDDEN_WIDTH
|
||||
: CHAT_WINDOW_END_GAP_WIDTH;
|
||||
const endGap = this.store.isSmall ? 0 : CHAT_WINDOW_END_GAP_WIDTH;
|
||||
const endGap = this.ui.isSmall ? 0 : CHAT_WINDOW_END_GAP_WIDTH;
|
||||
const available = browser.innerWidth - startGap - endGap;
|
||||
const maxAmountWithoutHidden = Math.floor(
|
||||
available / (CHAT_WINDOW_WIDTH + CHAT_WINDOW_INBETWEEN_WIDTH)
|
||||
@@ -56,7 +57,7 @@ export class ChatWindowService {
|
||||
}
|
||||
|
||||
notifyState(chatWindow) {
|
||||
if (this.env.isSmall) {
|
||||
if (this.ui.isSmall) {
|
||||
return;
|
||||
}
|
||||
if (chatWindow.thread?.model === "discuss.channel") {
|
||||
@@ -142,7 +143,7 @@ export class ChatWindowService {
|
||||
}
|
||||
|
||||
async close(chatWindow, { escape = false } = {}) {
|
||||
if (this.store.isSmall && !this.store.discuss.isActive) {
|
||||
if (this.ui.isSmall && !this.store.discuss.isActive) {
|
||||
// If we are in mobile and discuss is not open, it means the
|
||||
// chat window was opened from the messaging menu. In that
|
||||
// case it should be re-opened to simulate it was always
|
||||
@@ -171,7 +172,7 @@ export class ChatWindowService {
|
||||
}
|
||||
|
||||
export const chatWindowService = {
|
||||
dependencies: ["mail.store", "orm"],
|
||||
dependencies: ["mail.store", "orm", "ui"],
|
||||
start(env, services) {
|
||||
return new ChatWindowService(env, services);
|
||||
},
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<templates xml:space="preserve">
|
||||
<t t-inherit="mail.Discuss" t-inherit-mode="extension">
|
||||
<xpath expr="//*[@t-ref='mobileTopbar']" position="before">
|
||||
<Sidebar t-if="!props.public and !store.isSmall"/>
|
||||
<Sidebar t-if="!props.public and !ui.isSmall"/>
|
||||
</xpath>
|
||||
<xpath expr="//*[@t-ref='root']" position="inside">
|
||||
<MessagingMenu t-if="store.isSmall"/>
|
||||
<MessagingMenu t-if="ui.isSmall"/>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -31,6 +31,7 @@ export class MessagingMenu extends Component {
|
||||
/** @type {import("@mail/core/thread_service").ThreadService} */
|
||||
this.threadService = useState(useService("mail.thread"));
|
||||
this.action = useService("action");
|
||||
this.ui = useState(useService("ui"));
|
||||
this.state = useState({
|
||||
addingChat: false,
|
||||
addingChannel: false,
|
||||
@@ -201,7 +202,7 @@ export class MessagingMenu extends Component {
|
||||
}
|
||||
|
||||
onClickNewMessage() {
|
||||
if (this.store.isSmall && this.env.inDiscussApp) {
|
||||
if (this.ui.isSmall && this.env.inDiscussApp) {
|
||||
this.state.addingChat = true;
|
||||
} else {
|
||||
this.chatWindowService.openNewMessage();
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.MessagingMenu" owl="1">
|
||||
<Dropdown t-if="!env.inDiscussApp" autoOpen="false" beforeOpen.bind="beforeOpen" onStateChanged="state => this.state.isOpen = state.open" position="'bottom-end'" menuClass="`p-0 ${ store.isSmall ? 'start-0 w-100 mh-100 bottom-0 d-flex flex-column' : ''}`">
|
||||
<Dropdown t-if="!env.inDiscussApp" autoOpen="false" beforeOpen.bind="beforeOpen" onStateChanged="state => this.state.isOpen = state.open" position="'bottom-end'" menuClass="`p-0 ${ ui.isSmall ? 'start-0 w-100 mh-100 bottom-0 d-flex flex-column' : ''}`">
|
||||
<t t-set-slot="toggler">
|
||||
<i class="fa fa-lg fa-comments" role="img" aria-label="Messages" t-on-click="() => store.discuss.activeTab = store.isSmall and store.discuss.activeTab === 'mailbox' ? 'all' : store.discuss.activeTab"></i>
|
||||
<i class="fa fa-lg fa-comments" role="img" aria-label="Messages" t-on-click="() => store.discuss.activeTab = ui.isSmall and store.discuss.activeTab === 'mailbox' ? 'all' : store.discuss.activeTab"></i>
|
||||
<span t-if="counter" class="o-mail-MessagingMenu-counter badge rounded-pill"><t t-esc="counter"/></span>
|
||||
</t>
|
||||
<t t-set-slot="default">
|
||||
@@ -16,17 +16,17 @@
|
||||
|
||||
<t t-name="mail.MessagingMenu.content" owl="1">
|
||||
<t t-set="failures" t-value="store.notificationGroups"/>
|
||||
<div class="o-mail-MessagingMenu d-flex flex-column flex-grow-1" t-att-class="{ 'overflow-auto w-100 mh-100': store.isSmall }">
|
||||
<div t-att-class="{'flex-row-reverse text-uppercase': store.isSmall,
|
||||
<div class="o-mail-MessagingMenu d-flex flex-column flex-grow-1" t-att-class="{ 'overflow-auto w-100 mh-100': ui.isSmall }">
|
||||
<div t-att-class="{'flex-row-reverse text-uppercase': ui.isSmall,
|
||||
'o-mail-MessagingMenu-header border-bottom d-flex flex-shrink-0 p-2': !env.inDiscussApp,
|
||||
}">
|
||||
<t t-if="!store.isSmall">
|
||||
<t t-if="!ui.isSmall">
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'all' ? 'fw-bolder' : 'text-500'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'all'">All</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'chat' ? 'fw-bolder' : 'text-500'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'chat'">Chats</button>
|
||||
<button class="btn btn-link" t-att-class="store.discuss.activeTab === 'channel' ? 'fw-bolder' : 'text-500'" type="button" role="tab" t-on-click="() => store.discuss.activeTab = 'channel'">Channels</button>
|
||||
</t>
|
||||
<div class="flex-grow-1"></div>
|
||||
<t t-if="!store.discuss.isActive or store.isSmall and store.discuss.activeTab !== 'mailbox'">
|
||||
<t t-if="!store.discuss.isActive or ui.isSmall and store.discuss.activeTab !== 'mailbox'">
|
||||
<t t-if="env.inDiscussApp and ['chat', 'channel'].includes(store.discuss.activeTab)">
|
||||
<div t-if="state.addingChat or state.addingChannel" class="p-4 border-bottom bg-light" t-ref="selector">
|
||||
<t t-if="state.addingChat" t-set="category" t-value="store.discuss.chats"/>
|
||||
@@ -103,7 +103,7 @@
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="store.isSmall" class="o-mail-MessagingMenu-navbar d-flex border-top bg-view shadow-lg w-100">
|
||||
<div t-if="ui.isSmall" class="o-mail-MessagingMenu-navbar d-flex border-top bg-view shadow-lg w-100">
|
||||
<button t-foreach="tabs" t-key="tab.id" t-as="tab" class="o-mail-MessagingMenu-tab btn d-flex flex-column align-items-center flex-grow-1 flex-basis-0 p-2" t-att-class="{
|
||||
'text-primary fw-bolder': store.discuss.activeTab === tab.id,
|
||||
'border-end': !tab_last,
|
||||
|
||||
@@ -9,6 +9,7 @@ patch(Messaging.prototype, "mail/web", {
|
||||
this._super(env, services, initialThreadLocalId);
|
||||
/** @type {import("@mail/chat/chat_window_service").ChatWindow} */
|
||||
this.chatWindowService = services["mail.chat_window"];
|
||||
this.ui = services["ui"];
|
||||
},
|
||||
initMessagingCallback(data) {
|
||||
this.loadFailures();
|
||||
@@ -29,7 +30,7 @@ patch(Messaging.prototype, "mail/web", {
|
||||
const channel = this.store.threads[createLocalId("discuss.channel", notif.payload.id)];
|
||||
const message = this.store.messages[notif.payload.message.id];
|
||||
if (
|
||||
!this.store.isSmall &&
|
||||
!this.ui.isSmall &&
|
||||
channel.correspondent !== this.store.odoobot &&
|
||||
!message.isSelfAuthored
|
||||
) {
|
||||
@@ -39,5 +40,5 @@ patch(Messaging.prototype, "mail/web", {
|
||||
});
|
||||
|
||||
patch(messagingService, "mail/web", {
|
||||
dependencies: [...messagingService.dependencies, "mail.chat_window"],
|
||||
dependencies: [...messagingService.dependencies, "mail.chat_window", "ui"],
|
||||
});
|
||||
|
||||
@@ -170,7 +170,7 @@ patch(ThreadService.prototype, "mail/web", {
|
||||
thread.suggestedRecipients = recipients;
|
||||
},
|
||||
open(thread, replaceNewMessageChatWindow) {
|
||||
if (!this.store.discuss.isActive || this.store.isSmall) {
|
||||
if (!this.store.discuss.isActive || this.ui.isSmall) {
|
||||
const chatWindow = this.chatWindowService.insert({
|
||||
folded: false,
|
||||
thread,
|
||||
|
||||
@@ -763,7 +763,7 @@ QUnit.test(
|
||||
900,
|
||||
"should not have enough space to open 3 chat windows simultaneously"
|
||||
);
|
||||
const { env, openDiscuss } = await start();
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss();
|
||||
// open, from systray menu, chat windows of channels with id 1, 2, 3
|
||||
await click(".o_menu_systray i[aria-label='Messages']");
|
||||
@@ -773,7 +773,10 @@ QUnit.test(
|
||||
await click(".o_menu_systray i[aria-label='Messages']");
|
||||
await click(".o-mail-NotificationItem-name:contains(Channel-3)");
|
||||
// simulate resize to go into mobile
|
||||
await afterNextRender(() => (env.services["mail.store"].isSmall = true));
|
||||
await afterNextRender(() => {
|
||||
patchUiSize({ size: SIZES.SM });
|
||||
window.dispatchEvent(new UIEvent("resize"));
|
||||
});
|
||||
assert.containsNone($, ".o-mail-ChatWindowHiddenToggler");
|
||||
}
|
||||
);
|
||||
|
||||
@@ -418,7 +418,7 @@ async function start(param0 = {}) {
|
||||
await afterNextRender(async () => {
|
||||
webClient = await getWebClientReady({ ...param0, messagingBus });
|
||||
});
|
||||
if (webClient.env.isSmall) {
|
||||
if (webClient.env.services.ui.isSmall) {
|
||||
target.style.width = "100%";
|
||||
}
|
||||
const openView = async (action, options) => {
|
||||
|
||||
Reference in New Issue
Block a user