Merge pull request #12693 from nextcloud/backport/12610/stable28

[stable28] fix(sidebar): use open state to remove always working focus trap on mobile
This commit is contained in:
Grigorii K. Shartsev 2024-11-07 08:53:57 -05:00 коммит произвёл GitHub
Родитель 19ff854291 736b8849d2
Коммит f471eca3d5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
2 изменённых файлов: 125 добавлений и 106 удалений

Просмотреть файл

@ -21,14 +21,33 @@
-->
<template>
<NcAppSidebar v-show="opened"
<NcAppSidebar v-if="token"
:open="opened"
:name="name"
:title="name"
:active="activeTab"
:class="'active-tab-' + activeTab"
:toggle-classes="{ 'chat-button-sidebar-toggle': isInCall }"
:toggle-attrs="isInCall ? inCallToggleAttrs : undefined"
@update:open="handleUpdateOpen"
@update:active="handleUpdateActive"
@closed="handleClosed"
@close="handleClose">
<!-- Use a custom icon when sidebar is used for chat messages during the call -->
<template #toggle-icon>
<template v-if="isInCall">
<MessageText :size="20" />
<NcCounterBubble v-if="unreadMessagesCounter > 0"
class="chat-button__unread-messages-counter"
:type="hasUnreadMentions ? 'highlighted' : 'outlined'">
{{ unreadMessagesCounter }}
</NcCounterBubble>
</template>
<template v-else>
<!-- Use the old icon on older versions -->
<MenuIcon :size="20" />
</template>
</template>
<template #description>
<LobbyStatus v-if="canFullModerate && hasLobbyEnabled" :token="token" />
</template>
@ -106,13 +125,17 @@ import CogIcon from 'vue-material-design-icons/Cog.vue'
import DotsCircle from 'vue-material-design-icons/DotsCircle.vue'
import FolderMultipleImage from 'vue-material-design-icons/FolderMultipleImage.vue'
import InformationOutline from 'vue-material-design-icons/InformationOutline.vue'
import MenuIcon from 'vue-material-design-icons/Menu.vue'
import Message from 'vue-material-design-icons/Message.vue'
import MessageText from 'vue-material-design-icons/MessageText.vue'
import { showMessage } from '@nextcloud/dialogs'
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
import NcAppSidebar from '@nextcloud/vue/dist/Components/NcAppSidebar.js'
import NcAppSidebarTab from '@nextcloud/vue/dist/Components/NcAppSidebarTab.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
import BreakoutRoomsTab from './BreakoutRooms/BreakoutRoomsTab.vue'
import LobbyStatus from './LobbyStatus.vue'
@ -135,6 +158,7 @@ export default {
NcAppSidebar,
NcAppSidebarTab,
NcButton,
NcCounterBubble,
ParticipantsTab,
SetGuestUsername,
SharedItemsTab,
@ -146,6 +170,8 @@ export default {
FolderMultipleImage,
InformationOutline,
Message,
MessageText,
MenuIcon,
},
mixins: [
@ -163,6 +189,7 @@ export default {
return {
activeTab: 'participants',
contactsLoading: false,
unreadNotificationHandle: null,
}
},
@ -171,7 +198,7 @@ export default {
return this.$store.getters.getSidebarStatus
},
opened() {
return !!this.token && !this.isInLobby && this.show
return !this.isInLobby && this.show
},
token() {
return this.$store.getters.getToken()
@ -277,6 +304,21 @@ export default {
breakoutRoomsText() {
return t('spreed', 'Breakout rooms')
},
unreadMessagesCounter() {
return this.conversation.unreadMessages
},
hasUnreadMentions() {
return this.conversation.unreadMention
},
inCallToggleAttrs() {
return {
'data-theme-dark': true,
'aria-label': t('spreed', 'Open chat'),
title: t('spreed', 'Open chat')
}
},
},
watch: {
@ -311,6 +353,27 @@ export default {
},
},
unreadMessagesCounter(newValue, oldValue) {
if (!this.isInCall || this.opened) {
return
}
// new messages arrived
if (newValue > 0 && oldValue === 0 && !this.hasUnreadMentions) {
this.notifyUnreadMessages(t('spreed', 'You have new unread messages in the chat.'))
}
},
hasUnreadMentions(newValue) {
if (!this.isInCall || this.opened) {
return
}
if (newValue) {
this.notifyUnreadMessages(t('spreed', 'You have been mentioned in the chat.'))
}
},
isInCall(newValue) {
if (newValue) {
// Set 'chat' tab as active, and switch to it if sidebar is open
@ -318,6 +381,9 @@ export default {
return
}
// discard notification if the call ends
this.notifyUnreadMessages(null)
// If 'chat' tab wasn't active, leave it as is
if (this.activeTab !== 'chat') {
return
@ -358,11 +424,28 @@ export default {
},
methods: {
openSidebar() {
// In call by default open on chat
if (this.isInCall) {
this.activeTab = 'chat'
}
this.$store.dispatch('showSidebar')
BrowserStorage.setItem('sidebarOpen', 'true')
},
handleClose() {
this.$store.dispatch('hideSidebar')
BrowserStorage.setItem('sidebarOpen', 'false')
},
handleUpdateOpen(open) {
if (open) {
this.openSidebar()
} else {
this.handleClose()
}
},
handleUpdateActive(active) {
this.activeTab = active
},
@ -374,6 +457,21 @@ export default {
handleClosed() {
emit('files:sidebar:closed', {})
},
notifyUnreadMessages(message) {
if (this.unreadNotificationHandle) {
this.unreadNotificationHandle.hideToast()
this.unreadNotificationHandle = null
}
if (message) {
this.unreadNotificationHandle = showMessage(message, {
onClick: () => {
this.activeTab = 'chat'
this.openSidebar()
},
})
}
},
},
}
</script>
@ -414,4 +512,26 @@ export default {
height: 100%;
}
.chat-button__unread-messages-counter {
position: absolute;
bottom: 2px;
right: 2px;
pointer-events: none;
&.counter-bubble__counter--highlighted {
color: var(--color-primary-text);
}
}
</style>
<style lang="scss">
/*
* NcAppSidebar toggle it rendered on the page outside the sidebar element, so we need global styles here.
* It is _quite_ safe, as chat-button-sidebar-toggle class is defined here manually, not an internal class.
*/
.chat-button-sidebar-toggle {
position: relative;
// Allow unread counter to overflow rounded button
overflow: visible !important;
}
</style>

Просмотреть файл

@ -100,40 +100,6 @@
<CallButton class="top-bar__button" />
<!-- sidebar toggle -->
<template v-if="showOpenSidebarButton">
<!-- in chat: open last tab -->
<NcButton v-if="!isInCall"
:aria-label="t('spreed', 'Open sidebar')"
:title="t('spreed', 'Open sidebar')"
class="top-bar__button dark-hover"
close-after-click="true"
type="tertiary"
@click="openSidebar">
<template #icon>
<MenuIcon :size="20" />
</template>
</NcButton>
<!-- in call: open chat tab -->
<NcButton v-else
:aria-label="t('spreed', 'Open chat')"
:title="t('spreed', 'Open chat')"
class="top-bar__button chat-button dark-hover"
type="tertiary"
@click="openSidebar('chat')">
<template #icon>
<MessageText :size="20"
fill-color="#ffffff" />
<NcCounterBubble v-if="unreadMessagesCounter > 0"
class="chat-button__unread-messages-counter"
:type="hasUnreadMentions ? 'highlighted' : 'outlined'">
{{ unreadMessagesCounter }}
</NcCounterBubble>
</template>
</NcButton>
</template>
<!-- Breakout rooms editor -->
<BreakoutRoomsEditor v-if="showBreakoutRoomsEditor"
:token="token"
@ -143,15 +109,11 @@
<script>
import AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'
import MenuIcon from 'vue-material-design-icons/Menu.vue'
import MessageText from 'vue-material-design-icons/MessageText.vue'
import { getCapabilities } from '@nextcloud/capabilities'
import { showMessage } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
import richEditor from '@nextcloud/vue/dist/Mixins/richEditor.js'
@ -184,13 +146,10 @@ export default {
ConversationIcon,
TopBarMediaControls,
NcButton,
NcCounterBubble,
TopBarMenu,
ReactionMenu,
// Icons
AccountMultiple,
MenuIcon,
MessageText,
},
mixins: [
@ -215,7 +174,6 @@ export default {
data: () => {
return {
unreadNotificationHandle: null,
showBreakoutRoomsEditor: false,
localCallParticipantModel,
localMediaModel,
@ -227,10 +185,6 @@ export default {
return this.$store.getters.getMainContainerSelector()
},
showOpenSidebarButton() {
return !this.$store.getters.getSidebarStatus
},
isOneToOneConversation() {
return this.conversation.type === CONVERSATION.TYPE.ONE_TO_ONE
},
@ -251,13 +205,6 @@ export default {
return getStatusMessage(this.conversation)
},
unreadMessagesCounter() {
return this.conversation.unreadMessages
},
hasUnreadMentions() {
return this.conversation.unreadMention
},
renderedDescription() {
return this.renderContent(this.conversation.description)
},
@ -314,34 +261,6 @@ export default {
},
watch: {
unreadMessagesCounter(newValue, oldValue) {
if (!this.isInCall || !this.showOpenSidebarButton) {
return
}
// new messages arrived
if (newValue > 0 && oldValue === 0 && !this.hasUnreadMentions) {
this.notifyUnreadMessages(t('spreed', 'You have new unread messages in the chat.'))
}
},
hasUnreadMentions(newValue) {
if (!this.isInCall || !this.showOpenSidebarButton) {
return
}
if (newValue) {
this.notifyUnreadMessages(t('spreed', 'You have been mentioned in the chat.'))
}
},
isInCall(newValue) {
if (!newValue) {
// discard notification if the call ends
this.notifyUnreadMessages(null)
}
},
isOneToOneConversation: {
immediate: true,
// Group conversations have mixin in RightSidebar, so should work only for one-to-one
@ -373,16 +292,6 @@ export default {
},
methods: {
notifyUnreadMessages(message) {
if (this.unreadNotificationHandle) {
this.unreadNotificationHandle.hideToast()
this.unreadNotificationHandle = null
}
if (message) {
this.unreadNotificationHandle = showMessage(message)
}
},
openSidebar(activeTab) {
if (typeof activeTab === 'string') {
emit('spreed:select-active-sidebar-tab', activeTab)
@ -410,7 +319,9 @@ export default {
display: flex;
z-index: 10;
justify-content: flex-end;
padding: 8px;
padding: calc(2 * var(--default-grid-baseline));
// Reserve space for the sidebar toggle button
padding-right: calc(2 * var(--default-grid-baseline) + var(--app-sidebar-offset));
background-color: var(--color-main-background);
border-bottom: 1px solid var(--color-border);
@ -454,18 +365,6 @@ export default {
}
}
.chat-button {
position: relative;
overflow: visible;
&__unread-messages-counter {
position: absolute;
top: 24px;
right: 2px;
pointer-events: none;
color: var(--color-primary-element);
}
}
&--authorised {
.conversation-icon {
margin-left: calc(var(--default-clickable-area) + var(--default-grid-baseline));