summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaksim Sukharev <antreesy.web@gmail.com>2024-06-28 11:33:59 +0200
committerGitHub <noreply@github.com>2024-06-28 11:33:59 +0200
commit1da2c9f671b050aebc24bb87132b44ef5e8f5b9b (patch)
treef080aa1e1376148cb745a00e3d03e96cedcff192
parent6cb8843f6d412cccd47dabf5635d1e2523f061a4 (diff)
parentb71a50cf29a27460d6c622a2368be937e972cc0e (diff)
Merge pull request #12387 from nextcloud/fix/app-sidebar-open
fix(sidebar): use open state to remove always working focus trap on mobile
-rw-r--r--package-lock.json104
-rw-r--r--package.json2
-rw-r--r--src/components/RightSidebar/RightSidebar.vue119
-rw-r--r--src/components/TopBar/TopBar.vue111
4 files changed, 156 insertions, 180 deletions
diff --git a/package-lock.json b/package-lock.json
index c43304b0a..0408e1518 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,7 @@
"@nextcloud/paths": "^2.1.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/upload": "^1.3.0",
- "@nextcloud/vue": "^8.11.3",
+ "@nextcloud/vue": "^8.13.0",
"@vueuse/components": "^10.11.0",
"crypto-js": "^4.2.0",
"debounce": "^2.1.0",
@@ -3807,6 +3807,18 @@
"stylelint-config-recommended-vue": "^1.5.0"
}
},
+ "node_modules/@nextcloud/timezones": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@nextcloud/timezones/-/timezones-0.1.1.tgz",
+ "integrity": "sha512-ldLuLyz605sszetnp6jy6mtlThu4ICKsZThxHIZwn6t4QzjQH3xr+k8mRU7GIvKq9egUFDqBp4gBjxm3/ROZig==",
+ "dependencies": {
+ "ical.js": "^2.0.1"
+ },
+ "engines": {
+ "node": "^20.0.0",
+ "npm": "^10.0.0"
+ }
+ },
"node_modules/@nextcloud/typings": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@nextcloud/typings/-/typings-1.8.0.tgz",
@@ -3851,27 +3863,27 @@
}
},
"node_modules/@nextcloud/vue": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.12.0.tgz",
- "integrity": "sha512-MHL12+XGIDvpsSdrJn79pYKYrTVUouEymc4No91lKTNZTWDN6bciDSprmMs553hECXrqj7sfwxu6sepj0zcR3Q==",
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.13.0.tgz",
+ "integrity": "sha512-FV0GWSbxkaDQ8S/bWc4XcCXzJfpHzJn4xj0pK/jEwSwdSleBdBFsFFGo+wLCAGoXH5Xf5mHE+LtATFh6wnX7VA==",
"dependencies": {
"@floating-ui/dom": "^1.1.0",
"@linusborg/vue-simple-portal": "^0.1.5",
"@nextcloud/auth": "^2.2.1",
"@nextcloud/axios": "^2.4.0",
"@nextcloud/browser-storage": "^0.4.0",
- "@nextcloud/calendar-js": "^7.0.0",
"@nextcloud/capabilities": "^1.1.0",
"@nextcloud/event-bus": "^3.1.0",
"@nextcloud/initial-state": "^2.1.0",
"@nextcloud/l10n": "^3.0.1",
"@nextcloud/logger": "^3.0.1",
"@nextcloud/router": "^3.0.0",
+ "@nextcloud/timezones": "^0.1.1",
"@nextcloud/vue-select": "^3.25.0",
"@vueuse/components": "^10.9.0",
"@vueuse/core": "^10.9.0",
"clone": "^2.1.2",
- "debounce": "2.0.0",
+ "debounce": "2.1.0",
"dompurify": "^3.0.5",
"emoji-mart-vue-fast": "^15.0.1",
"escape-html": "^1.0.3",
@@ -3895,6 +3907,7 @@
"vue": "^2.7.16",
"vue-color": "^2.8.1",
"vue-frag": "^1.4.3",
+ "vue-router": "^3.6.5",
"vue2-datepicker": "^3.11.0"
},
"engines": {
@@ -3930,19 +3943,6 @@
"@floating-ui/utils": "^0.1.3"
}
},
- "node_modules/@nextcloud/vue/node_modules/@nextcloud/calendar-js": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@nextcloud/calendar-js/-/calendar-js-7.0.0.tgz",
- "integrity": "sha512-CvCcO4hFPjMfIB2AKW0QLNYukGoHFS7QQVvIC8khJjzNfVGS6qMJd2oaZtD9Q9w1fLpvwp1X7orcYGYmosDkAA==",
- "engines": {
- "node": "^20.0.0",
- "npm": "^10.0.0"
- },
- "peerDependencies": {
- "ical.js": "^2.0.1",
- "uuid": "^9.0.0"
- }
- },
"node_modules/@nextcloud/vue/node_modules/@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
@@ -3959,17 +3959,6 @@
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "node_modules/@nextcloud/vue/node_modules/debounce": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.0.0.tgz",
- "integrity": "sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/@nextcloud/vue/node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
@@ -4040,19 +4029,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/@nextcloud/vue/node_modules/uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "peer": true,
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/@nextcloud/vue/node_modules/vfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
@@ -10642,8 +10618,7 @@
"node_modules/ical.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ical.js/-/ical.js-2.0.1.tgz",
- "integrity": "sha512-uYYb1CwTXbd9NP/xTtgQZ5ivv6bpUjQu9VM98s3X78L3XRu00uJW5ZtmnLwyxhztpf5fSiRyDpFW7ZNCePlaPw==",
- "peer": true
+ "integrity": "sha512-uYYb1CwTXbd9NP/xTtgQZ5ivv6bpUjQu9VM98s3X78L3XRu00uJW5ZtmnLwyxhztpf5fSiRyDpFW7ZNCePlaPw=="
},
"node_modules/iconv-lite": {
"version": "0.4.24",
@@ -23099,6 +23074,14 @@
"dev": true,
"requires": {}
},
+ "@nextcloud/timezones": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@nextcloud/timezones/-/timezones-0.1.1.tgz",
+ "integrity": "sha512-ldLuLyz605sszetnp6jy6mtlThu4ICKsZThxHIZwn6t4QzjQH3xr+k8mRU7GIvKq9egUFDqBp4gBjxm3/ROZig==",
+ "requires": {
+ "ical.js": "^2.0.1"
+ }
+ },
"@nextcloud/typings": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@nextcloud/typings/-/typings-1.8.0.tgz",
@@ -23131,27 +23114,27 @@
}
},
"@nextcloud/vue": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.12.0.tgz",
- "integrity": "sha512-MHL12+XGIDvpsSdrJn79pYKYrTVUouEymc4No91lKTNZTWDN6bciDSprmMs553hECXrqj7sfwxu6sepj0zcR3Q==",
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.13.0.tgz",
+ "integrity": "sha512-FV0GWSbxkaDQ8S/bWc4XcCXzJfpHzJn4xj0pK/jEwSwdSleBdBFsFFGo+wLCAGoXH5Xf5mHE+LtATFh6wnX7VA==",
"requires": {
"@floating-ui/dom": "^1.1.0",
"@linusborg/vue-simple-portal": "^0.1.5",
"@nextcloud/auth": "^2.2.1",
"@nextcloud/axios": "^2.4.0",
"@nextcloud/browser-storage": "^0.4.0",
- "@nextcloud/calendar-js": "^7.0.0",
"@nextcloud/capabilities": "^1.1.0",
"@nextcloud/event-bus": "^3.1.0",
"@nextcloud/initial-state": "^2.1.0",
"@nextcloud/l10n": "^3.0.1",
"@nextcloud/logger": "^3.0.1",
"@nextcloud/router": "^3.0.0",
+ "@nextcloud/timezones": "^0.1.1",
"@nextcloud/vue-select": "^3.25.0",
"@vueuse/components": "^10.9.0",
"@vueuse/core": "^10.9.0",
"clone": "^2.1.2",
- "debounce": "2.0.0",
+ "debounce": "2.1.0",
"dompurify": "^3.0.5",
"emoji-mart-vue-fast": "^15.0.1",
"escape-html": "^1.0.3",
@@ -23175,6 +23158,7 @@
"vue": "^2.7.16",
"vue-color": "^2.8.1",
"vue-frag": "^1.4.3",
+ "vue-router": "^3.6.5",
"vue2-datepicker": "^3.11.0"
},
"dependencies": {
@@ -23195,12 +23179,6 @@
"@floating-ui/utils": "^0.1.3"
}
},
- "@nextcloud/calendar-js": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@nextcloud/calendar-js/-/calendar-js-7.0.0.tgz",
- "integrity": "sha512-CvCcO4hFPjMfIB2AKW0QLNYukGoHFS7QQVvIC8khJjzNfVGS6qMJd2oaZtD9Q9w1fLpvwp1X7orcYGYmosDkAA==",
- "requires": {}
- },
"@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
@@ -23211,11 +23189,6 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="
},
- "debounce": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.0.0.tgz",
- "integrity": "sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA=="
- },
"is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
@@ -23260,12 +23233,6 @@
"@types/unist": "^3.0.0"
}
},
- "uuid": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
- "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
- "peer": true
- },
"vfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
@@ -28328,8 +28295,7 @@
"ical.js": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ical.js/-/ical.js-2.0.1.tgz",
- "integrity": "sha512-uYYb1CwTXbd9NP/xTtgQZ5ivv6bpUjQu9VM98s3X78L3XRu00uJW5ZtmnLwyxhztpf5fSiRyDpFW7ZNCePlaPw==",
- "peer": true
+ "integrity": "sha512-uYYb1CwTXbd9NP/xTtgQZ5ivv6bpUjQu9VM98s3X78L3XRu00uJW5ZtmnLwyxhztpf5fSiRyDpFW7ZNCePlaPw=="
},
"iconv-lite": {
"version": "0.4.24",
diff --git a/package.json b/package.json
index 8fa9240f0..417afc2a1 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"@nextcloud/paths": "^2.1.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/upload": "^1.3.0",
- "@nextcloud/vue": "^8.11.3",
+ "@nextcloud/vue": "^8.13.0",
"@vueuse/components": "^10.11.0",
"crypto-js": "^4.2.0",
"debounce": "^2.1.0",
diff --git a/src/components/RightSidebar/RightSidebar.vue b/src/components/RightSidebar/RightSidebar.vue
index 435240cd6..8efc1c03a 100644
--- a/src/components/RightSidebar/RightSidebar.vue
+++ b/src/components/RightSidebar/RightSidebar.vue
@@ -4,13 +4,27 @@
-->
<template>
- <NcAppSidebar v-show="opened"
+ <NcAppSidebar v-if="token"
+ :open="opened"
:name="conversation.displayName"
:title="conversation.displayName"
:active.sync="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 v-if="isInCall" #toggle-icon>
+ <MessageText :size="20" />
+ <NcCounterBubble v-if="unreadMessagesCounter > 0"
+ class="chat-button__unread-messages-counter"
+ :type="hasUnreadMentions ? 'highlighted' : 'outlined'">
+ {{ unreadMessagesCounter }}
+ </NcCounterBubble>
+ </template>
<template #description>
<LobbyStatus v-if="canFullModerate && hasLobbyEnabled" :token="token" />
</template>
@@ -94,13 +108,16 @@ 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 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 { t } from '@nextcloud/l10n'
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'
@@ -123,6 +140,7 @@ export default {
NcAppSidebar,
NcAppSidebarTab,
NcButton,
+ NcCounterBubble,
ParticipantsTab,
SetGuestUsername,
SharedItemsTab,
@@ -134,6 +152,7 @@ export default {
FolderMultipleImage,
InformationOutline,
Message,
+ MessageText,
},
props: {
@@ -147,6 +166,7 @@ export default {
return {
activeTab: 'participants',
contactsLoading: false,
+ unreadNotificationHandle: null,
}
},
@@ -155,7 +175,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()
@@ -257,6 +277,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: {
@@ -287,6 +322,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
@@ -294,6 +350,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
@@ -335,11 +394,30 @@ export default {
methods: {
t,
+
+ 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
},
@@ -351,6 +429,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>
@@ -391,4 +484,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>
diff --git a/src/components/TopBar/TopBar.vue b/src/components/TopBar/TopBar.vue
index fee8bc701..14d25316d 100644
--- a/src/components/TopBar/TopBar.vue
+++ b/src/components/TopBar/TopBar.vue
@@ -80,38 +80,6 @@
<CallButton shrink-on-mobile :is-screensharing="!!localMediaModel.attributes.localScreen" />
- <!-- 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')"
- 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="chat-button"
- type="tertiary"
- @click="openSidebar('chat')">
- <template #icon>
- <MessageText :size="20" />
- <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"
@@ -121,15 +89,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 { showMessage } from '@nextcloud/dialogs'
import { emit } from '@nextcloud/event-bus'
import { t, n } from '@nextcloud/l10n'
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'
@@ -163,13 +127,10 @@ export default {
ConversationIcon,
TopBarMediaControls,
NcButton,
- NcCounterBubble,
TopBarMenu,
ReactionMenu,
// Icons
AccountMultiple,
- MenuIcon,
- MessageText,
},
mixins: [richEditor],
@@ -199,7 +160,6 @@ export default {
data: () => {
return {
- unreadNotificationHandle: null,
showBreakoutRoomsEditor: false,
}
},
@@ -209,10 +169,6 @@ export default {
return this.$store.getters.getMainContainerSelector()
},
- showOpenSidebarButton() {
- return !this.$store.getters.getSidebarStatus
- },
-
isOneToOneConversation() {
return this.conversation.type === CONVERSATION.TYPE.ONE_TO_ONE
|| this.conversation.type === CONVERSATION.TYPE.ONE_TO_ONE_FORMER
@@ -238,13 +194,6 @@ export default {
return getStatusMessage(this.conversation)
},
- unread