[IMP] mail: Call settings menu as right sidebar
This commit changes layout of call settings menu. Before this commit, it was available in call action list while in a call, as a dialog, in More > Settings. With this commit, it's now available as an button in thread topbar and chat window header, next to member list button (`fa-cog`). It can be accessed even while not in a call. Task-2929713 closes odoo/odoo#96954 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
committed by
Alexandre Kühn
parent
14b960239b
commit
3723b222b3
@@ -5,7 +5,7 @@
|
||||
<t t-if="callActionListView">
|
||||
<div class="o_CallActionList d-flex justify-content-between" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_CallActionList_buttons d-flex align-items-center flex-wrap">
|
||||
<t t-if="callActionListView.channel.rtc and messaging.rtc.currentRtcSession">
|
||||
<t t-if="callActionListView.thread.rtc and messaging.rtc.currentRtcSession">
|
||||
<button class="o_CallActionList_button btn d-flex m-1 border-0 rounded-circle bg-800 shadow-none"
|
||||
t-att-class="{ 'o-isActive': !messaging.rtc.currentRtcSession.isMute, 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall }"
|
||||
t-att-aria-label="callActionListView.microphoneButtonTitle"
|
||||
@@ -62,11 +62,36 @@
|
||||
</div>
|
||||
</button>
|
||||
</t>
|
||||
<Popover position="'top'" popoverClass="'o_CallActionList_popover'">
|
||||
<t t-if="!callActionListView.callView.isFullScreen">
|
||||
<button class="o_CallActionList_button btn d-flex m-1 border-0 rounded-circle bg-800 shadow-none"
|
||||
aria-label="More"
|
||||
title="More"
|
||||
aria-label="Activate Full Screen"
|
||||
title="Activate Full Screen"
|
||||
t-att-class="{ 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall }"
|
||||
t-on-click="callActionListView.callView.activateFullScreen"
|
||||
>
|
||||
<div class="o_CallActionList_buttonIconWrapper fa-stack" t-att-class="{ 'o-isSmall': callActionListView.isSmall }">
|
||||
<i class="fa fa-arrows-alt fa-stack-1x" t-att-class="{ 'fa-lg': !callActionListView.isSmall }"/>
|
||||
</div>
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="callActionListView.callView.isFullScreen">
|
||||
<button class="o_CallActionList_button btn d-flex m-1 border-0 rounded-circle bg-800 shadow-none"
|
||||
aria-label="Deactivate Full Screen"
|
||||
title="Deactivate Full Screen"
|
||||
t-att-class="{ 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall }"
|
||||
t-on-click="callActionListView.callView.deactivateFullScreen"
|
||||
>
|
||||
<div class="o_CallActionList_buttonIconWrapper fa-stack" t-att-class="{ 'o-isSmall': callActionListView.isSmall }">
|
||||
<i class="fa fa-compress fa-stack-1x" t-att-class="{ 'fa-lg': !callActionListView.isSmall }"/>
|
||||
</div>
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="messaging.modelManager.isDebug">
|
||||
<Popover position="'top'" popoverClass="'o_CallActionList_popover'">
|
||||
<button class="o_CallActionList_button btn d-flex m-1 border-0 rounded-circle bg-800 shadow-none"
|
||||
aria-label="More"
|
||||
title="More"
|
||||
t-att-class="{ 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall }"
|
||||
>
|
||||
<div class="o_CallActionList_buttonIconWrapper fa-stack" t-att-class="{ 'o-isSmall': callActionListView.isSmall }">
|
||||
<i class="fa fa-ellipsis-h fa-stack-1x" t-att-class="{ 'fa-lg': !callActionListView.isSmall }"/>
|
||||
@@ -76,26 +101,27 @@
|
||||
<CallOptionMenu record="callActionListView.callOptionMenu"/>
|
||||
</t>
|
||||
</Popover>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="!callActionListView.channel">
|
||||
<t t-if="!callActionListView.thread">
|
||||
<button class="o_CallActionList_button o_CallActionList_callToggle btn btn-success d-flex m-1 border-0 rounded-circle shadow-none"
|
||||
t-att-class="{ 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall }"
|
||||
aria-label="Join Video Call"
|
||||
title="Join Video Call"
|
||||
t-att-disabled="callActionListView.channel.hasPendingRtcRequest"
|
||||
t-att-disabled="callActionListView.thread.hasPendingRtcRequest"
|
||||
t-on-click="callActionListView.onClickToggleVideoCall">
|
||||
<div class="o_CallActionList_buttonIconWrapper fa-stack" t-att-class="{ 'o-isSmall': callActionListView.isSmall }">
|
||||
<i class="fa fa-video-camera fa-stack-1x" t-att-class="{ 'fa-lg': !callActionListView.isSmall }"/>
|
||||
</div>
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="callActionListView.channel">
|
||||
<t t-if="callActionListView.channel.rtcInvitingSession and !callActionListView.channel.rtc">
|
||||
<t t-if="callActionListView.thread">
|
||||
<t t-if="callActionListView.thread.rtcInvitingSession and !callActionListView.thread.rtc">
|
||||
<button class="o_CallActionList_button o_CallActionList_callToggle o-isActive btn btn-danger d-flex m-1 border-0 rounded-circle shadow-none"
|
||||
t-att-class="{ 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall }"
|
||||
aria-label="Reject"
|
||||
title="Reject"
|
||||
t-att-disabled="callActionListView.channel.hasPendingRtcRequest"
|
||||
t-att-disabled="callActionListView.thread.hasPendingRtcRequest"
|
||||
t-on-click="callActionListView.onClickRejectCall">
|
||||
<div class="o_CallActionList_buttonIconWrapper fa-stack" t-att-class="{ 'o-isSmall': callActionListView.isSmall }">
|
||||
<i class="fa fa-phone fa-stack-1x" t-att-class="{ 'fa-lg': !callActionListView.isSmall }"/>
|
||||
@@ -104,8 +130,8 @@
|
||||
</t>
|
||||
<button class="o_CallActionList_button o_CallActionList_callToggle btn d-flex m-1 border-0 rounded-circle shadow-none"
|
||||
t-att-aria-label="callActionListView.callButtonTitle"
|
||||
t-att-class="{ 'o-isActive btn-danger': !!callActionListView.channel.rtc, 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall, 'btn-success': !callActionListView.channel.rtc }"
|
||||
t-att-disabled="callActionListView.channel.hasPendingRtcRequest"
|
||||
t-att-class="{ 'o-isActive btn-danger': !!callActionListView.thread.rtc, 'o-isSmall p-2': callActionListView.isSmall, 'p-3': !callActionListView.isSmall, 'btn-success': !callActionListView.thread.rtc }"
|
||||
t-att-disabled="callActionListView.thread.hasPendingRtcRequest"
|
||||
t-att-title="callActionListView.callButtonTitle"
|
||||
t-on-click="callActionListView.onClickToggleAudioCall">
|
||||
<div class="o_CallActionList_buttonIconWrapper fa-stack" t-att-class="{ 'o-isSmall': callActionListView.isSmall }">
|
||||
|
||||
@@ -4,37 +4,10 @@
|
||||
<t t-name="mail.CallOptionMenu" owl="1">
|
||||
<t t-if="callOptionMenu">
|
||||
<div class="o_CallOptionMenu d-flex flex-column" t-attf-class="{{ className }}" t-ref="root">
|
||||
<t t-if="callOptionMenu.callActionListView.channel.videoCount > 0">
|
||||
<div class="o_CallOptionMenu_button btn d-flex align-items-center border-0 rounded">
|
||||
<label class="o_CallOptionMenu_checkBoxLabel cursor-pointer d-flex align-items-center mb-0 text-800 fw-normal">
|
||||
<input class="o_CallOptionMenu_checkBox mx-2 p-1" type="checkbox" t-on-change="callOptionMenu.onChangeVideoFilterCheckbox" t-att-checked="callOptionMenu.callView.filterVideoGrid"/>
|
||||
<span class="o_CallOptionMenu_buttonText py-1 ps-1 text-truncate">Video only</span>
|
||||
</label>
|
||||
</div>
|
||||
<hr class="o_CallOptionMenu_separator w-100 my-1 bg-300"/>
|
||||
</t>
|
||||
<t t-if="callOptionMenu.callView.isFullScreen">
|
||||
<button class="o_CallOptionMenu_button btn d-flex align-items-center border-0 rounded text-800 fw-normal" t-on-click="callOptionMenu.onClickDeactivateFullScreen">
|
||||
<i class="o_CallOptionMenu_buttonIcon fa fa-lg fa-compress m-1 p-1"/>
|
||||
<span class="o_CallOptionMenu_buttonText text-truncate">Exit full screen</span>
|
||||
</button>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<button class="o_CallOptionMenu_button btn d-flex align-items-center border-0 rounded text-800 fw-normal" t-on-click="callOptionMenu.onClickActivateFullScreen">
|
||||
<i class="o_CallOptionMenu_buttonIcon fa fa-lg fa-arrows-alt m-1 p-1"/>
|
||||
<span class="o_CallOptionMenu_buttonText text-truncate">Full screen</span>
|
||||
</button>
|
||||
</t>
|
||||
<button class="o_CallOptionMenu_button btn d-flex align-items-center border-0 rounded text-800 fw-normal" t-on-click="callOptionMenu.onClickOptions">
|
||||
<i class="o_CallOptionMenu_buttonIcon fa fa-lg fa-cog m-1 p-1"/>
|
||||
<span class="o_CallOptionMenu_buttonText text-truncate">Settings</span>
|
||||
<button class="o_CallOptionMenu_button btn d-flex align-items-center border-0 rounded text-800 fw-normal" t-on-click="callOptionMenu.onClickDownloadLogs">
|
||||
<i class="o_CallOptionMenu_buttonIcon fa fa-lg fa-file-text-o m-1 p-1"/>
|
||||
<span class="o_CallOptionMenu_buttonText text-truncate">Download logs</span>
|
||||
</button>
|
||||
<t t-if="messaging.modelManager.isDebug">
|
||||
<button class="o_CallOptionMenu_button btn d-flex align-items-center border-0 rounded text-800 fw-normal" t-on-click="callOptionMenu.onClickDownloadLogs">
|
||||
<i class="o_CallOptionMenu_buttonIcon fa fa-lg fa-file-text-o m-1 p-1"/>
|
||||
<span class="o_CallOptionMenu_buttonText text-truncate">Download logs</span>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
// Layout
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
.o_CallSettingsMenu_categoryTitle {
|
||||
padding: map-get($spacers, 2) 0;
|
||||
}
|
||||
|
||||
.o_CallSettingsMenu {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.o_CallSettingsMenu_option {
|
||||
min-height: 40px;
|
||||
padding: map-get($spacers, 2) map-get($spacers, 3);
|
||||
}
|
||||
|
||||
.o_CallSettingsMenu_optionInputGroupInput {
|
||||
|
||||
@@ -2,68 +2,78 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.CallSettingsMenu" owl="1">
|
||||
<div class="o_CallSettingsMenu d-flex flex-column ms-2 overflow-auto" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_CallSettingsMenu_option d-flex align-items-center flex-wrap m-2 p-1">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Input device" aria-label="Input device">
|
||||
<span class="o_CallSettingsMenuoptionName me-2 text-truncate fw-bolder">Input device</span>
|
||||
<div>
|
||||
<select name="inputDevice" class="o_CallSettingsMenu_optionDeviceSelect form-select" t-att-value="messaging.userSetting.audioInputDeviceId" t-on-change="callSettingsMenu.onChangeSelectAudioInput">
|
||||
<option value="">Browser default</option>
|
||||
<t t-if="state.userDevices" t-foreach="state.userDevices" t-as="device" t-key="device_index">
|
||||
<CallSettingsMenuDevice device="device"/>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="o_CallSettingsMenu_option d-flex align-items-center flex-wrap m-2 p-1">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Use Push-to-talk" aria-label="Use Push-to-talk">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate fw-bolder">Use Push-to-talk</span>
|
||||
<input type="checkbox" aria-label="toggle push-to-talk" title="toggle push-to-talk" t-on-change="callSettingsMenu.onChangePushToTalk" t-att-checked="messaging.userSetting.usePushToTalk ? 'checked' : ''"/>
|
||||
</label>
|
||||
</div>
|
||||
<t t-if="messaging.userSetting.usePushToTalk">
|
||||
<div class="o_CallSettingsMenu_option d-flex align-items-center flex-wrap m-2 p-1">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Push-to-talk key" aria-label="Push-to-talk key">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate fw-bolder">Push-to-talk key</span>
|
||||
<span class="o_CallSettingsMenu_optionPushToTalkGroup d-flex">
|
||||
<t t-if="messaging.userSetting.pushToTalkKey">
|
||||
<span class="o_CallSettingsMenu_optionPushToTalkGroupKey ms-1 px-3 border border-2 rounded fs-3 fw-bolder" t-attf-class="{{ callSettingsMenu.isRegisteringKey ? 'o-isRegistering border-danger' : 'border-primary' }}" t-esc="messaging.userSetting.pushToTalkKeyToString()"/>
|
||||
</t>
|
||||
<button class="o_CallSettingsMenu_button btn btn-link px-2 py-0 text-black" t-on-click="callSettingsMenu.onClickRegisterKeyButton">
|
||||
<t t-if="callSettingsMenu.isRegisteringKey">
|
||||
<i title="Cancel" aria-label="Cancel" class="fa fa-2x fa-times-circle"/>
|
||||
<div class="o_CallSettingsMenu" t-attf-class="{{ className }}" t-ref="root">
|
||||
<div class="o_CallSettingsMenu_category d-flex flex-column overflow-auto">
|
||||
<div class="o_CallSettingsMenu_categoryTitle fw-bolder text-700 text-truncate text-uppercase">Voice Settings</div>
|
||||
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Input device" aria-label="Input device">
|
||||
<span class="o_CallSettingsMenuoptionName me-2 text-truncate">Input device</span>
|
||||
<div>
|
||||
<select name="inputDevice" class="o_CallSettingsMenu_optionDeviceSelect form-select" t-att-value="messaging.userSetting.audioInputDeviceId" t-on-change="callSettingsMenu.onChangeSelectAudioInput">
|
||||
<option value="">Browser default</option>
|
||||
<t t-if="state.userDevices" t-foreach="state.userDevices" t-as="device" t-key="device_index">
|
||||
<CallSettingsMenuDevice device="device"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<i title="Register new key" aria-label="Register new key" class="fa fa-2x fa-keyboard-o"/>
|
||||
</t>
|
||||
</button>
|
||||
</span>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Enable Push-to-talk" aria-label="Enable Push-to-talk">
|
||||
<input type="checkbox" aria-label="toggle push-to-talk" title="toggle push-to-talk" t-on-change="callSettingsMenu.onChangePushToTalk" t-att-checked="messaging.userSetting.usePushToTalk ? 'checked' : ''"/>
|
||||
<span class="o_CallSettingsMenu_optionName ms-2 text-truncate">Enable Push-to-talk</span>
|
||||
</label>
|
||||
</div>
|
||||
<t t-if="messaging.userSetting.usePushToTalk">
|
||||
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Push-to-talk key" aria-label="Push-to-talk key">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Push-to-talk key</span>
|
||||
<span class="o_CallSettingsMenu_optionPushToTalkGroup d-flex">
|
||||
<t t-if="messaging.userSetting.pushToTalkKey">
|
||||
<span class="o_CallSettingsMenu_optionPushToTalkGroupKey ms-1 px-3 border border-2 rounded fs-3" t-attf-class="{{ callSettingsMenu.userSetting.isRegisteringKey ? 'o-isRegistering border-danger' : 'border-primary' }}" t-esc="messaging.userSetting.pushToTalkKeyToString()"/>
|
||||
</t>
|
||||
<button class="o_CallSettingsMenu_button btn btn-link px-2 py-0 text-black" t-on-click="callSettingsMenu.onClickRegisterKeyButton">
|
||||
<t t-if="callSettingsMenu.userSetting.isRegisteringKey">
|
||||
<i title="Cancel" aria-label="Cancel" class="fa fa-2x fa-times-circle"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<i title="Register new key" aria-label="Register new key" class="fa fa-2x fa-keyboard-o"/>
|
||||
</t>
|
||||
</button>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
<div t-if="callSettingsMenu.isRegisteringKey">Press a key to register it as the push-to-talk shortcut</div>
|
||||
<div class="o_CallSettingsMenu_option d-flex align-items-center flex-wrap m-2 p-1">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Delay after releasing push-to-talk" aria-label="Delay after releasing push-to-talk">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate fw-bolder">Delay after releasing push-to-talk</span>
|
||||
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
|
||||
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="1" max="2000" step="1" t-att-value="messaging.userSetting.voiceActiveDuration" t-on-change="callSettingsMenu.onChangeDelay"/>
|
||||
<span class="o_CallSettingsMenu_optionInputGroupValue p-1"><t t-esc="messaging.userSetting.voiceActiveDuration"/>ms</span>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="callSettingsMenu.userSetting.isRegisteringKey">Press a key to register it as the push-to-talk shortcut</div>
|
||||
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Delay after releasing push-to-talk" aria-label="Delay after releasing push-to-talk">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Delay after releasing push-to-talk</span>
|
||||
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
|
||||
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="1" max="2000" step="1" t-att-value="messaging.userSetting.voiceActiveDuration" t-on-change="callSettingsMenu.onChangeDelay"/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Voice detection threshold" aria-label="Voice detection threshold">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Voice detection threshold</span>
|
||||
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
|
||||
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="0.001" max="1" step="0.001" t-att-value="messaging.userSetting.voiceActivationThreshold" t-on-change="callSettingsMenu.onChangeThreshold"/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_CallSettingsMenu_category d-flex flex-column overflow-auto">
|
||||
<div class="o_CallSettingsMenu_categoryTitle fw-bolder text-700 text-truncate text-uppercase">Video Settings</div>
|
||||
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Show video participants only" aria-label="Show video participants only">
|
||||
<input type="checkbox" aria-label="toggle push-to-talk" title="Show video participants only" t-on-change="callSettingsMenu.onChangeVideoFilterCheckbox" t-att-checked="callSettingsMenu.thread.channel.showOnlyVideo ? 'checked' : ''"/>
|
||||
<span class="o_CallSettingsMenu_optionName ms-2 text-truncate">Show video participants only</span>
|
||||
</label>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="o_CallSettingsMenu_option d-flex align-items-center flex-wrap m-2 p-1">
|
||||
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Minimum activity for voice detection" aria-label="Minimum activity for voice detection">
|
||||
<span class="o_CallSettingsMenu_optionName me-2 text-truncate fw-bolder">Minimum activity for voice detection</span>
|
||||
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
|
||||
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="0.001" max="1" step="0.001" t-att-value="messaging.userSetting.voiceActivationThreshold" t-on-change="callSettingsMenu.onChangeThreshold"/>
|
||||
<span class="o_CallSettingsMenu_optionInputGroupValue p-1"><t t-esc="messaging.userSetting.voiceActivationThreshold"/></span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -9,15 +9,6 @@
|
||||
<CallMainView record="callView.callMainView"/>
|
||||
<CallSidebarView t-if="callView.callSidebarView" record="callView.callSidebarView"/>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<t t-if="messaging.userSetting.callSettingsMenu.isOpen">
|
||||
<Dialog size="'small'" title="callView.settingsTitle" onClosed="callView.onRtcSettingsDialogClosed">
|
||||
<CallSettingsMenu record="messaging.userSetting.callSettingsMenu"/>
|
||||
<t t-set-slot="buttons">
|
||||
<!-- Explicit No buttons -->
|
||||
</t>
|
||||
</Dialog>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
<t t-if="chatWindow.channelMemberListView">
|
||||
<ChannelMemberList record="chatWindow.channelMemberListView" className="'bg-white'"/>
|
||||
</t>
|
||||
<t t-if="chatWindow.callSettingsMenu">
|
||||
<CallSettingsMenu record="chatWindow.callSettingsMenu" className="'bg-white'"/>
|
||||
</t>
|
||||
<t t-if="chatWindow.channelInvitationForm">
|
||||
<ChannelInvitationForm className="'o_ChatWindow_channelInvitationForm'" record="chatWindow.channelInvitationForm"/>
|
||||
</t>
|
||||
|
||||
@@ -55,6 +55,16 @@
|
||||
<i class="fa fa-users"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="chatWindow.thread and chatWindow.thread.hasCallFeature and !chatWindow.isCallSettingsMenuOpen">
|
||||
<div class="o_ChatWindowHeader_command o_ChatWindowHeader_commandShowCallSettingsMenu cursor-pointer d-flex align-items-center h-100 px-3 py-0 m-0" t-att-class="{ 'o-isDeviceSmall': messaging.device.isSmall, 'opacity-50 opacity-100-hover': !messaging.device.isSmall}" title="Show Member List" t-on-click="chatWindow.onClickShowCallSettingsMenu">
|
||||
<i class="fa fa-gear"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="chatWindow.thread and chatWindow.thread.hasCallFeature and chatWindow.isCallSettingsMenuOpen">
|
||||
<div class="o_ChatWindowHeader_command cursor-pointer d-flex align-items-center h-100 px-3 py-0 m-0" t-att-class="{ 'o-isDeviceSmall': messaging.device.isSmall, 'opacity-50 opacity-100-hover': !messaging.device.isSmall }" title="Hide Member List" t-on-click="chatWindow.onClickHideCallSettingsMenu">
|
||||
<i class="fa fa-gear"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="chatWindow.isExpandable">
|
||||
<div class="o_ChatWindowHeader_command o_ChatWindowHeader_commandExpand cursor-pointer d-flex align-items-center h-100 px-3 py-0 m-0" t-att-class="{ 'o-isDeviceSmall': messaging.device.isSmall, 'opacity-50 opacity-100-hover': !messaging.device.isSmall }" t-on-click="chatWindow.onClickExpand" title="Open in Discuss">
|
||||
<i class="fa fa-expand"/>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
width: $o-mail-chat-sidebar-width;
|
||||
}
|
||||
|
||||
.o_ThreadView_callSettingsMenu {
|
||||
width: $o-mail-chat-sidebar-width;
|
||||
}
|
||||
|
||||
.o_ThreadView_core {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
<t t-if="threadView.channelMemberListView">
|
||||
<ChannelMemberList className="'o_ThreadView_channelMemberList flex-shrink-0 border-start'" record="threadView.channelMemberListView"/>
|
||||
</t>
|
||||
<t t-if="threadView.isCallSettingsMenuOpen">
|
||||
<CallSettingsMenu className="'o_ThreadView_callSettingsMenu flex-shrink-0 border-start'" record="threadView.callSettingsMenu"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -19,8 +19,17 @@
|
||||
// Style
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
.o_ThreadViewTopbar_button:hover {
|
||||
background-color: $gray-300;
|
||||
.o_ThreadViewTopbar_button {
|
||||
|
||||
&:not(.o-isActive):hover {
|
||||
background-color: $gray-300;
|
||||
}
|
||||
&.o-isActive {
|
||||
background-color: $gray-300;
|
||||
}
|
||||
&.o-isActive:hover {
|
||||
background-color: $gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
// use selector specificity to override bootstrap style
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="threadViewTopbar.thread and threadViewTopbar.thread.hasInviteFeature">
|
||||
<button class="o_ThreadViewTopbar_inviteButton o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" t-attf-class="{{ threadViewTopbar.invitePopoverView ? '' : '' }}" title="Add users" t-on-click="threadViewTopbar.onClickInviteButton" t-ref="inviteButton">
|
||||
<i class="fa fa-lg fa-user-plus" t-attf-class="{{ threadViewTopbar.invitePopoverView ? 'text-500' : 'text-700' }}"/>
|
||||
<button class="o_ThreadViewTopbar_inviteButton o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" t-attf-class="{{ threadViewTopbar.invitePopoverView ? 'o-isActive' : '' }}" title="Add users" t-on-click="threadViewTopbar.onClickInviteButton" t-ref="inviteButton">
|
||||
<i class="fa fa-lg fa-user-plus text-700"/>
|
||||
</button>
|
||||
</t>
|
||||
<!-- FIXME: guests should be able to see members but there currently is no route for it, so hide it for now -->
|
||||
@@ -69,8 +69,18 @@
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="threadViewTopbar.thread and threadViewTopbar.thread.hasMemberListFeature and threadViewTopbar.threadView.hasMemberList and threadViewTopbar.threadView.isMemberListOpened">
|
||||
<button class="o_ThreadViewTopbar_hideMemberListButton o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" title="Hide Member List" t-on-click="threadViewTopbar.onClickHideMemberList">
|
||||
<i class="fa fa-lg fa-users text-500"/>
|
||||
<button class="o_ThreadViewTopbar_hideMemberListButton o_ThreadViewTopbar_button o-isActive btn px-2 border-none rounded shadow-none" title="Hide Member List" t-on-click="threadViewTopbar.onClickHideMemberList">
|
||||
<i class="fa fa-lg fa-users text-700"/>
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="threadViewTopbar.thread and threadViewTopbar.thread.hasCallFeature and !threadViewTopbar.threadView.isCallSettingsMenuOpen">
|
||||
<button class="o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" title="Show Call Settings" t-on-click="threadViewTopbar.onClickShowCallSettingsMenu">
|
||||
<i class="fa fa-lg fa-gear text-700"/>
|
||||
</button>
|
||||
</t>
|
||||
<t t-if="threadViewTopbar.thread and threadViewTopbar.thread.hasCallFeature and threadViewTopbar.threadView.isCallSettingsMenuOpen">
|
||||
<button class="o_ThreadViewTopbar_button o-isActive btn px-2 border-none rounded shadow-none" title="Hide Call Settings" t-on-click="threadViewTopbar.onClickHideCallSettingsMenu">
|
||||
<i class="fa fa-lg fa-gear text-700"/>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
@@ -42,10 +42,10 @@ registerModel({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
async onClickRejectCall(ev) {
|
||||
if (this.channel.hasPendingRtcRequest) {
|
||||
if (this.thread.hasPendingRtcRequest) {
|
||||
return;
|
||||
}
|
||||
await this.channel.leaveCall();
|
||||
await this.thread.leaveCall();
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
@@ -57,19 +57,19 @@ registerModel({
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
async onClickToggleAudioCall(ev) {
|
||||
if (this.channel.hasPendingRtcRequest) {
|
||||
if (this.thread.hasPendingRtcRequest) {
|
||||
return;
|
||||
}
|
||||
await this.channel.toggleCall();
|
||||
await this.thread.toggleCall();
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
async onClickToggleVideoCall(ev) {
|
||||
if (this.channel.hasPendingRtcRequest) {
|
||||
if (this.thread.hasPendingRtcRequest) {
|
||||
return;
|
||||
}
|
||||
await this.channel.toggleCall({
|
||||
await this.thread.toggleCall({
|
||||
startWithVideo: true,
|
||||
});
|
||||
},
|
||||
@@ -78,10 +78,10 @@ registerModel({
|
||||
* @returns {string|FieldCommand}
|
||||
*/
|
||||
_computeCallButtonTitle() {
|
||||
if (!this.channel) {
|
||||
if (!this.thread) {
|
||||
return clear();
|
||||
}
|
||||
if (this.channel.rtc) {
|
||||
if (this.thread.rtc) {
|
||||
return this.env._t("Disconnect");
|
||||
} else {
|
||||
return this.env._t("Join Call");
|
||||
@@ -160,10 +160,6 @@ registerModel({
|
||||
compute: '_computeCameraButtonTitle',
|
||||
default: '',
|
||||
}),
|
||||
channel: one('Thread', {
|
||||
related: 'callMainView.channel',
|
||||
required: true,
|
||||
}),
|
||||
headphoneButtonTitle: attr({
|
||||
compute: '_computeHeadphoneButtonTitle',
|
||||
default: '',
|
||||
@@ -183,5 +179,9 @@ registerModel({
|
||||
compute: '_computeScreenSharingButtonTitle',
|
||||
default: '',
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
related: 'callMainView.thread',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -168,10 +168,6 @@ registerModel({
|
||||
identifying: true,
|
||||
inverse: 'callMainView',
|
||||
}),
|
||||
channel: one('Thread', {
|
||||
related: 'callView.channel',
|
||||
required: true,
|
||||
}),
|
||||
hasSidebarButton: attr({
|
||||
compute: '_computeHasSidebarButton',
|
||||
}),
|
||||
@@ -197,6 +193,10 @@ registerModel({
|
||||
inverse: 'callMainViewAsShowOverlay',
|
||||
isCausal: true,
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
related: 'callView.thread',
|
||||
required: true,
|
||||
}),
|
||||
tileContainerRef: attr(),
|
||||
tileHeight: attr({
|
||||
default: 0,
|
||||
|
||||
@@ -2,22 +2,10 @@
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
registerModel({
|
||||
name: 'CallOptionMenu',
|
||||
recordMethods: {
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onChangeVideoFilterCheckbox(ev) {
|
||||
const filterVideoGrid = ev.target.checked;
|
||||
const activeRtcSession = this.callView.activeRtcSession;
|
||||
if (filterVideoGrid && activeRtcSession && !activeRtcSession.videoStream) {
|
||||
this.callView.update({ activeRtcSession: clear() });
|
||||
}
|
||||
this.callView.update({ filterVideoGrid });
|
||||
},
|
||||
/**
|
||||
* Creates and download a file that contains the logs of the current RTC call.
|
||||
*
|
||||
@@ -38,27 +26,6 @@ registerModel({
|
||||
window.URL.revokeObjectURL(url);
|
||||
this.component.trigger('o-popover-close');
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickActivateFullScreen(ev) {
|
||||
this.callView.activateFullScreen();
|
||||
this.component.trigger('o-popover-close');
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickDeactivateFullScreen(ev) {
|
||||
this.callView.deactivateFullScreen();
|
||||
this.component.trigger('o-popover-close');
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickOptions(ev) {
|
||||
this.messaging.userSetting.callSettingsMenu.toggle();
|
||||
this.component.trigger('o-popover-close');
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
import { one } from '@mail/model/model_field';
|
||||
|
||||
registerModel({
|
||||
name: 'CallSettingsMenu',
|
||||
identifyingMode: 'xor',
|
||||
lifecycleHooks: {
|
||||
_created() {
|
||||
browser.addEventListener('keydown', this._onKeyDown);
|
||||
@@ -26,7 +28,7 @@ registerModel({
|
||||
},
|
||||
onChangePushToTalk() {
|
||||
if (this.userSetting.usePushToTalk) {
|
||||
this.update({
|
||||
this.userSetting.update({
|
||||
isRegisteringKey: false,
|
||||
});
|
||||
}
|
||||
@@ -44,16 +46,45 @@ registerModel({
|
||||
onChangeThreshold(ev) {
|
||||
this.userSetting.setThresholdValue(parseFloat(ev.target.value));
|
||||
},
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onChangeVideoFilterCheckbox(ev) {
|
||||
const showOnlyVideo = ev.target.checked;
|
||||
this.thread.channel.update({ showOnlyVideo });
|
||||
if (!this.callView) {
|
||||
return;
|
||||
}
|
||||
const activeRtcSession = this.callView.activeRtcSession;
|
||||
if (showOnlyVideo && activeRtcSession && !activeRtcSession.videoStream) {
|
||||
this.callView.update({ activeRtcSession: clear() });
|
||||
}
|
||||
},
|
||||
onClickRegisterKeyButton() {
|
||||
this.update({
|
||||
this.userSetting.update({
|
||||
isRegisteringKey: !this.isRegisteringKey,
|
||||
});
|
||||
},
|
||||
toggle() {
|
||||
this.update({ isOpen: !this.isOpen });
|
||||
_computeCallView() {
|
||||
if (this.threadViewOwner) {
|
||||
return this.threadViewOwner.callView;
|
||||
}
|
||||
if (this.chatWindowOwner && this.chatWindowOwner.threadView) {
|
||||
return this.chatWindowOwner.threadView.callView;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
_computeThread() {
|
||||
if (this.threadViewOwner) {
|
||||
return this.threadViewOwner.thread;
|
||||
}
|
||||
if (this.chatWindowOwner) {
|
||||
return this.chatWindowOwner.thread;
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
_onKeyDown(ev) {
|
||||
if (!this.isRegisteringKey) {
|
||||
if (!this.userSetting.isRegisteringKey) {
|
||||
return;
|
||||
}
|
||||
ev.stopPropagation();
|
||||
@@ -61,29 +92,33 @@ registerModel({
|
||||
this.userSetting.setPushToTalkKey(ev);
|
||||
},
|
||||
_onKeyUp(ev) {
|
||||
if (!this.isRegisteringKey) {
|
||||
if (!this.userSetting.isRegisteringKey) {
|
||||
return;
|
||||
}
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.update({
|
||||
this.userSetting.update({
|
||||
isRegisteringKey: false,
|
||||
});
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
isOpen: attr({
|
||||
default: false,
|
||||
callView: one('CallView', {
|
||||
compute: '_computeCallView',
|
||||
}),
|
||||
/**
|
||||
* true if listening to keyboard input to register the push to talk key.
|
||||
*/
|
||||
isRegisteringKey: attr({
|
||||
default: false,
|
||||
}),
|
||||
userSetting: one('UserSetting', {
|
||||
chatWindowOwner: one('ChatWindow', {
|
||||
identifying: true,
|
||||
inverse: 'callSettingsMenu',
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
compute: '_computeThread',
|
||||
}),
|
||||
threadViewOwner: one('ThreadView', {
|
||||
identifying: true,
|
||||
inverse: 'callSettingsMenu',
|
||||
}),
|
||||
userSetting: one('UserSetting', {
|
||||
related: 'messaging.userSetting',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -84,9 +84,12 @@ registerModel({
|
||||
return clear();
|
||||
},
|
||||
_computeFilteredChannelMembers() {
|
||||
if (!this.channel) {
|
||||
return clear();
|
||||
}
|
||||
const channelMembers = [];
|
||||
for (const channelMember of this.channel.channel.callParticipants) {
|
||||
if (this.filterVideoGrid && !channelMember.isStreaming) {
|
||||
for (const channelMember of this.channel.callParticipants) {
|
||||
if (this.channel.showOnlyVideo && this.thread.videoCount > 0 && !channelMember.isStreaming) {
|
||||
continue;
|
||||
}
|
||||
channelMembers.push(channelMember);
|
||||
@@ -97,7 +100,7 @@ registerModel({
|
||||
* @private
|
||||
*/
|
||||
_computeIsMinimized() {
|
||||
if (!this.threadView) {
|
||||
if (!this.threadView || !this.thread) {
|
||||
return true;
|
||||
}
|
||||
if (this.isFullScreen || this.threadView.compact) {
|
||||
@@ -106,7 +109,7 @@ registerModel({
|
||||
if (this.activeRtcSession) {
|
||||
return false;
|
||||
}
|
||||
return !this.channel.rtc || this.channel.videoCount === 0;
|
||||
return !this.thread.rtc || this.thread.videoCount === 0;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -127,14 +130,16 @@ registerModel({
|
||||
*/
|
||||
_onChangeRtcChannel() {
|
||||
this.deactivateFullScreen();
|
||||
this.update({ filterVideoGrid: false });
|
||||
if (!this.thread && !this.thread.rtc) {
|
||||
this.channel.update({ showOnlyVideo: false });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onChangeVideoCount() {
|
||||
if (this.channel.videoCount === 0) {
|
||||
this.update({ filterVideoGrid: false });
|
||||
if (this.thread.videoCount === 0) {
|
||||
this.channel.update({ showOnlyVideo: false });
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -172,19 +177,12 @@ registerModel({
|
||||
inverse: 'callView',
|
||||
isCausal: true,
|
||||
}),
|
||||
channel: one('Thread', {
|
||||
related: 'threadView.thread',
|
||||
required: true,
|
||||
channel: one('Channel', {
|
||||
related: 'thread.channel',
|
||||
}),
|
||||
filteredChannelMembers: many('ChannelMember', {
|
||||
compute: '_computeFilteredChannelMembers',
|
||||
}),
|
||||
/**
|
||||
* Determines whether we only display the videos or all the participants
|
||||
*/
|
||||
filterVideoGrid: attr({
|
||||
default: false,
|
||||
}),
|
||||
/**
|
||||
* Determines if the viewer should be displayed fullScreen.
|
||||
*/
|
||||
@@ -222,6 +220,10 @@ registerModel({
|
||||
settingsTitle: attr({
|
||||
compute: '_computeSettingsTitle',
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
related: 'threadView.thread',
|
||||
required: true,
|
||||
}),
|
||||
/**
|
||||
* ThreadView on which the call view is attached.
|
||||
*/
|
||||
@@ -232,11 +234,11 @@ registerModel({
|
||||
},
|
||||
onChanges: [
|
||||
{
|
||||
dependencies: ['channel.rtc'],
|
||||
dependencies: ['thread.rtc'],
|
||||
methodName: '_onChangeRtcChannel',
|
||||
},
|
||||
{
|
||||
dependencies: ['channel.videoCount'],
|
||||
dependencies: ['thread.videoCount'],
|
||||
methodName: '_onChangeVideoCount',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -104,6 +104,12 @@ registerModel({
|
||||
inverse: 'channelAsOnlineMember',
|
||||
sort: '_sortMembers',
|
||||
}),
|
||||
/**
|
||||
* Determines whether we only display the participants who broadcast a video or all of them.
|
||||
*/
|
||||
showOnlyVideo: attr({
|
||||
default: false,
|
||||
}),
|
||||
thread: one('Thread', {
|
||||
compute: '_computeThread',
|
||||
inverse: 'channel',
|
||||
|
||||
@@ -194,6 +194,13 @@ registerModel({
|
||||
this.fold();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickHideCallSettingsMenu(ev) {
|
||||
markEventHandled(ev, 'ChatWindow.onClickCommand');
|
||||
this.update({ isCallSettingsMenuOpen: false });
|
||||
},
|
||||
/**
|
||||
* Handles click on the "stop adding users" button.
|
||||
*
|
||||
@@ -240,6 +247,16 @@ registerModel({
|
||||
this.channelInvitationForm.searchPartnersToInvite();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
onClickShowCallSettingsMenu(ev) {
|
||||
markEventHandled(ev, 'ChatWindow.onClickCommand');
|
||||
this.update({
|
||||
isCallSettingsMenuOpen: true,
|
||||
isMemberListOpened: false,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
@@ -247,6 +264,7 @@ registerModel({
|
||||
markEventHandled(ev, 'ChatWindow.onClickShowMemberList');
|
||||
this.update({
|
||||
channelInvitationForm: clear(),
|
||||
isCallSettingsMenuOpen: false,
|
||||
isMemberListOpened: true,
|
||||
});
|
||||
},
|
||||
@@ -342,6 +360,16 @@ registerModel({
|
||||
this.thread.notifyFoldStateToServer('open');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
*/
|
||||
_computeCallSettingsMenu() {
|
||||
if (this.isCallSettingsMenuOpen) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {FieldCommand}
|
||||
@@ -404,7 +432,7 @@ registerModel({
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_computeHasThreadView() {
|
||||
return this.isVisible && !this.isFolded && !!this.thread && !this.isMemberListOpened && !this.channelInvitationForm;
|
||||
return this.isVisible && !this.isFolded && !!this.thread && !this.isMemberListOpened && !this.channelInvitationForm && !this.isCallSettingsMenuOpen;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -559,6 +587,14 @@ registerModel({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
* Model for the component with the controls for RTC related settings.
|
||||
*/
|
||||
callSettingsMenu: one('CallSettingsMenu', {
|
||||
compute: '_computeCallSettingsMenu',
|
||||
inverse: 'chatWindowOwner',
|
||||
isCausal: true,
|
||||
}),
|
||||
/**
|
||||
* Determines the channel invitation form displayed by this chat window
|
||||
* (if any). Only makes sense if hasInviteFeature is true.
|
||||
@@ -609,6 +645,9 @@ registerModel({
|
||||
hasThreadView: attr({
|
||||
compute: '_computeHasThreadView',
|
||||
}),
|
||||
isCallSettingsMenuOpen: attr({
|
||||
default: false,
|
||||
}),
|
||||
/**
|
||||
* Determine whether the chat window should be programmatically
|
||||
* focused by observed component of chat window. Those components
|
||||
|
||||
@@ -311,6 +311,9 @@ registerModel({
|
||||
if (this.disconnectAudioMonitor) {
|
||||
this.disconnectAudioMonitor();
|
||||
}
|
||||
if (!this.currentRtcSession) {
|
||||
return;
|
||||
}
|
||||
if (this.messaging.userSetting.usePushToTalk || !this.channel || !this.audioTrack) {
|
||||
this.currentRtcSession.update({ isTalking: false });
|
||||
await this._updateLocalAudioTrackEnabledState();
|
||||
@@ -1023,7 +1026,7 @@ registerModel({
|
||||
if (this.currentRtcSession.isMute) {
|
||||
return;
|
||||
}
|
||||
if (this.messaging.userSetting.callSettingsMenu.isRegisteringKey) {
|
||||
if (this.messaging.userSetting.isRegisteringKey) {
|
||||
return;
|
||||
}
|
||||
this.messaging.browser.clearTimeout(this.pushToTalkTimeout);
|
||||
|
||||
@@ -82,6 +82,12 @@ registerModel({
|
||||
messageListViewMessageViewItem.messageView.startEditing();
|
||||
}
|
||||
},
|
||||
_computeCallSettingsMenu() {
|
||||
if (this.isCallSettingsMenuOpen) {
|
||||
return {};
|
||||
}
|
||||
return clear();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -322,6 +328,14 @@ registerModel({
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
/**
|
||||
* Model for the component with the controls for RTC related settings.
|
||||
*/
|
||||
callSettingsMenu: one('CallSettingsMenu', {
|
||||
compute: '_computeCallSettingsMenu',
|
||||
inverse: 'threadViewOwner',
|
||||
isCausal: true,
|
||||
}),
|
||||
channelMemberListView: one('ChannelMemberListView', {
|
||||
compute: '_computeChannelMemberListView',
|
||||
inverse: 'threadViewOwner',
|
||||
@@ -383,6 +397,9 @@ registerModel({
|
||||
hasTopbar: attr({
|
||||
related: 'threadViewer.hasTopbar',
|
||||
}),
|
||||
isCallSettingsMenuOpen: attr({
|
||||
default: false,
|
||||
}),
|
||||
isComposerFocused: attr({
|
||||
related: 'composerView.isFocused',
|
||||
}),
|
||||
|
||||
@@ -26,6 +26,12 @@ registerModel({
|
||||
startWithVideo: true,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onClickHideCallSettingsMenu(ev) {
|
||||
this.threadView.update({ isCallSettingsMenuOpen: false });
|
||||
},
|
||||
/**
|
||||
* Handles click on the "hide member list" button.
|
||||
*
|
||||
@@ -64,13 +70,26 @@ registerModel({
|
||||
}
|
||||
await this.thread.toggleCall();
|
||||
},
|
||||
/**
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onClickShowCallSettingsMenu(ev) {
|
||||
// FIXME maybe find another way to prevent both being open at the same time?
|
||||
this.threadView.update({
|
||||
isCallSettingsMenuOpen: true,
|
||||
isMemberListOpened: false,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Handles click on the "show member list" button.
|
||||
*
|
||||
* @param {Event} ev
|
||||
*/
|
||||
onClickShowMemberList(ev) {
|
||||
this.threadView.update({ isMemberListOpened: true });
|
||||
this.threadView.update({
|
||||
isCallSettingsMenuOpen: false,
|
||||
isMemberListOpened: true,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Handles click on the "thread name" of this top bar.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
|
||||
import { registerModel } from '@mail/model/model_core';
|
||||
import { attr, one } from '@mail/model/model_field';
|
||||
import { attr } from '@mail/model/model_field';
|
||||
import { clear } from '@mail/model/model_field_command';
|
||||
|
||||
/**
|
||||
@@ -282,15 +282,13 @@ registerModel({
|
||||
audioInputDeviceId: attr({
|
||||
default: '',
|
||||
}),
|
||||
/**
|
||||
* Model for the component with the controls for RTC related settings.
|
||||
*/
|
||||
callSettingsMenu: one('CallSettingsMenu', {
|
||||
default: {},
|
||||
inverse: 'userSetting',
|
||||
isCausal: true,
|
||||
}),
|
||||
globalSettingsTimeout: attr(),
|
||||
/**
|
||||
* true if listening to keyboard input to register the push to talk key.
|
||||
*/
|
||||
isRegisteringKey: attr({
|
||||
default: false,
|
||||
}),
|
||||
/**
|
||||
* String that encodes the push-to-talk key with its modifiers.
|
||||
*/
|
||||
|
||||
@@ -409,7 +409,7 @@ QUnit.test('new message autocomplete should automatically select first result',
|
||||
});
|
||||
|
||||
QUnit.test('chat window: basic rendering', async function (assert) {
|
||||
assert.expect(14);
|
||||
assert.expect(15);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const mailChannelId1 = pyEnv['mail.channel'].create({ name: "General" });
|
||||
@@ -450,8 +450,8 @@ QUnit.test('chat window: basic rendering', async function (assert) {
|
||||
);
|
||||
assert.strictEqual(
|
||||
chatWindowHeader.querySelectorAll(`:scope .o_ChatWindowHeader_command`).length,
|
||||
5,
|
||||
"should have 5 commands in header part"
|
||||
6,
|
||||
"should have 6 commands in header part"
|
||||
);
|
||||
assert.strictEqual(
|
||||
chatWindowHeader.querySelectorAll(`:scope .o_ChatWindowHeader_commandPhone`).length,
|
||||
@@ -468,6 +468,11 @@ QUnit.test('chat window: basic rendering', async function (assert) {
|
||||
1,
|
||||
"should have command to show the member list"
|
||||
);
|
||||
assert.strictEqual(
|
||||
chatWindowHeader.querySelectorAll(`:scope .o_ChatWindowHeader_commandShowCallSettingsMenu`).length,
|
||||
1,
|
||||
"should have command to show the call settings menu"
|
||||
);
|
||||
assert.strictEqual(
|
||||
chatWindowHeader.querySelectorAll(`:scope .o_ChatWindowHeader_commandExpand`).length,
|
||||
1,
|
||||
|
||||
Reference in New Issue
Block a user