diff options
author | Maksim Sukharev <antreesy.web@gmail.com> | 2023-08-03 09:07:46 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-03 09:07:46 +0200 |
commit | d739457bfca94ed3d049dbcbd9e1f57b0fcbee00 (patch) | |
tree | 61804164c8a233ea20acc847a18be2c9168a359d | |
parent | db952ee33d672335942d83edf483b194ab4f6680 (diff) | |
parent | a89b55124fed73b748111e5064e3fbe9fa7776ea (diff) |
Merge pull request #10095 from nextcloud/backport/9955/stable27
[stable27] feat(OpenConversationsList) - Make list of open conversations discoverable and usable
-rw-r--r-- | src/collections.js | 2 | ||||
-rw-r--r-- | src/components/LeftSidebar/LeftSidebar.spec.js | 10 | ||||
-rw-r--r-- | src/components/LeftSidebar/LeftSidebar.vue | 65 | ||||
-rw-r--r-- | src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue | 23 | ||||
-rw-r--r-- | src/components/LeftSidebar/OpenConversationsList/OpenConversationsList.vue | 79 | ||||
-rw-r--r-- | src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/Forwarder.vue | 2 | ||||
-rw-r--r-- | src/components/RoomSelector.spec.js (renamed from src/views/RoomSelector.spec.js) | 18 | ||||
-rw-r--r-- | src/components/RoomSelector.vue (renamed from src/views/RoomSelector.vue) | 55 | ||||
-rw-r--r-- | src/deck.js | 2 | ||||
-rw-r--r-- | src/maps.js | 2 |
10 files changed, 197 insertions, 61 deletions
diff --git a/src/collections.js b/src/collections.js index 40f458d29..037381e5d 100644 --- a/src/collections.js +++ b/src/collections.js @@ -41,7 +41,7 @@ import Vue from 'vue' container.id = 'spreed-room-select' const body = document.getElementById('body-user') body.appendChild(container) - const RoomSelector = () => import('./views/RoomSelector.vue') + const RoomSelector = () => import('./components/RoomSelector.vue') const ComponentVM = new Vue({ render: h => h(RoomSelector, { props: { diff --git a/src/components/LeftSidebar/LeftSidebar.spec.js b/src/components/LeftSidebar/LeftSidebar.spec.js index ea73f4faa..ace133521 100644 --- a/src/components/LeftSidebar/LeftSidebar.spec.js +++ b/src/components/LeftSidebar/LeftSidebar.spec.js @@ -13,7 +13,7 @@ import router from '../../__mocks__/router.js' import { searchPossibleConversations, searchListedConversations } from '../../services/conversationsService.js' import { EventBus } from '../../services/EventBus.js' import storeConfig from '../../store/storeConfig.js' -import { findNcListItems } from '../../test-helpers.js' +import { findNcListItems, findNcActionButton } from '../../test-helpers.js' jest.mock('@nextcloud/initial-state', () => ({ loadState: jest.fn(), @@ -709,16 +709,16 @@ describe('LeftSidebar.vue', () => { loadStateSettings.start_conversations = true const wrapper = mountComponent() - const buttonEl = wrapper.findComponent({ name: 'NewGroupConversation' }) - expect(buttonEl.exists()).toBeTruthy() + const newConversationbutton = findNcActionButton(wrapper, 'Create a new conversation') + expect(newConversationbutton.exists()).toBeTruthy() }) test('does not show new conversation button if user cannot start conversations', () => { loadStateSettings.start_conversations = false const wrapper = mountComponent() - const buttonEl = wrapper.findComponent({ name: 'NewGroupConversation' }) - expect(buttonEl.exists()).toBeFalsy() + const newConversationbutton = findNcActionButton(wrapper, 'Create a new conversation') + expect(newConversationbutton.exists()).toBeFalsy() }) }) diff --git a/src/components/LeftSidebar/LeftSidebar.vue b/src/components/LeftSidebar/LeftSidebar.vue index 1d1b67a2c..c66964f75 100644 --- a/src/components/LeftSidebar/LeftSidebar.vue +++ b/src/components/LeftSidebar/LeftSidebar.vue @@ -35,8 +35,8 @@ @keydown.enter.native="handleEnter" @abort-search="abortSearch" /> - <!-- Options --> - <div class="options" + <!-- Filters --> + <div class="filters" :class="{'hidden-visually': isFocused}"> <NcActions class="filter-actions" :primary="isFiltered !== null"> @@ -75,10 +75,36 @@ </NcActions> </div> - <!-- New Conversation --> - <NewGroupConversation v-if="canStartConversations" - ref="newGroupConversation" - class="new-conversation__button" /> + <!-- Actions --> + <div class="actions"> + <NcActions class="conversations-actions"> + <template #icon> + <DotsVertical :size="20" /> + </template> + <NcActionButton v-if="canStartConversations" + close-after-click + @click="showModalNewConversation"> + <template #icon> + <Plus :size="20" /> + </template> + {{ t('spreed','Create a new conversation') }} + </NcActionButton> + + <NcActionButton close-after-click + @click="showModalListConversations"> + <template #icon> + <List :size="20" /> + </template> + {{ t('spreed','Join open conversations') }} + </NcActionButton> + </NcActions> + </div> + + <!-- All open conversations list --> + <OpenConversationsList ref="openConversationsList" /> + + <!-- New Conversation dialog--> + <NewGroupConversation ref="newGroupConversation" /> </div> <template #list> @@ -96,7 +122,7 @@ <LoadingPlaceholder type="conversations" /> </template> <Hint v-else-if="noMatchFound" - :hint="t('spreed', 'No matches')" /> + :hint="t('spreed', 'No matches found')" /> <template v-if="isSearching"> <template v-if="!listedConversationsLoading && searchResultsListedConversations.length > 0"> <NcAppNavigationCaption :title="t('spreed', 'Open conversations')" /> @@ -182,9 +208,12 @@ import debounce from 'debounce' import AtIcon from 'vue-material-design-icons/At.vue' +import DotsVertical from 'vue-material-design-icons/DotsVertical.vue' import FilterIcon from 'vue-material-design-icons/Filter.vue' import FilterRemoveIcon from 'vue-material-design-icons/FilterRemove.vue' +import List from 'vue-material-design-icons/FormatListBulleted.vue' import MessageBadge from 'vue-material-design-icons/MessageBadge.vue' +import Plus from 'vue-material-design-icons/Plus.vue' import { showError } from '@nextcloud/dialogs' import { emit } from '@nextcloud/event-bus' @@ -203,6 +232,7 @@ import Hint from '../Hint.vue' import LoadingPlaceholder from '../LoadingPlaceholder.vue' import Conversation from './ConversationsList/Conversation.vue' import NewGroupConversation from './NewGroupConversation/NewGroupConversation.vue' +import OpenConversationsList from './OpenConversationsList/OpenConversationsList.vue' import SearchBox from './SearchBox/SearchBox.vue' import { CONVERSATION } from '../../constants.js' @@ -225,6 +255,7 @@ export default { Hint, SearchBox, NewGroupConversation, + OpenConversationsList, Conversation, LoadingPlaceholder, NcListItem, @@ -235,6 +266,9 @@ export default { MessageBadge, FilterIcon, FilterRemoveIcon, + Plus, + List, + DotsVertical, }, mixins: [ @@ -383,6 +417,15 @@ export default { getFocusableList() { return this.$el.querySelectorAll('li.acli_wrapper .acli') }, + + showModalNewConversation() { + this.$refs.newGroupConversation.showModal() + }, + + showModalListConversations() { + this.$refs.openConversationsList.showModal() + }, + setIsFocused(event) { if (event.relatedTarget?.className.includes('input-field__clear-button') || this.searchText !== '') { return @@ -757,19 +800,19 @@ export default { } -.options{ +.filters { position: absolute; right : 52px; // New conversation button's width display: flex; height: var(--default-clickable-area); } -.new-conversation__button{ +.actions { position: absolute; - right: 1px; + right: 5px; } -.filter-actions__button--active{ +.filter-actions__button--active { background-color: var(--color-primary-element-light); border-radius: 6px; :deep(.action-button__longtext){ diff --git a/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue b/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue index 3a5a02958..2f4e8a446 100644 --- a/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue +++ b/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue @@ -21,15 +21,6 @@ <template> <div class="wrapper"> - <NcButton type="tertiary" - class="toggle" - :aria-label="t('spreed', 'Create a new group conversation')" - :title="t('spreed', 'Create a new group conversation')" - @click="showModal"> - <template #icon> - <Plus :size="20" /> - </template> - </NcButton> <!-- New group form --> <NcModal v-if="modal" :container="container" @@ -169,8 +160,6 @@ <script> -import Plus from 'vue-material-design-icons/Plus.vue' - import { getCapabilities } from '@nextcloud/capabilities' import { showError } from '@nextcloud/dialogs' @@ -218,7 +207,6 @@ export default { NcModal, NcPasswordField, NcTextField, - Plus, SetContacts, }, @@ -305,7 +293,7 @@ export default { }) }, }, - expose: ['showModalForItem'], + expose: ['showModalForItem', 'showModal'], methods: { showModal() { @@ -494,15 +482,6 @@ export default { margin: 50px auto 0 auto; } } -.toggle { - height: 44px; - width: 44px; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - margin: 0 var(--default-grid-baseline); -} .new-group-conversation { &__header { diff --git a/src/components/LeftSidebar/OpenConversationsList/OpenConversationsList.vue b/src/components/LeftSidebar/OpenConversationsList/OpenConversationsList.vue new file mode 100644 index 000000000..e47e0a78c --- /dev/null +++ b/src/components/LeftSidebar/OpenConversationsList/OpenConversationsList.vue @@ -0,0 +1,79 @@ +<!-- + - @copyright Copyright (c) 2023 + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. +--> + +<template> + <RoomSelector v-if="modal" + :container="container" + list-open-conversations + show-postable-only + :dialog-title="dialogTitle" + @close="closeModal" + @select="openConversation" /> +</template> + +<script> + +import RoomSelector from '../../RoomSelector.vue' + +export default { + + name: 'OpenConversationsList', + + components: { + RoomSelector, + }, + + data() { + return { + modal: false, + } + }, + + computed: { + container() { + return this.$store.getters.getMainContainerSelector() + }, + + dialogTitle() { + return t('spreed', 'Join open conversations') + }, + + }, + + expose: ['showModal'], + + methods: { + showModal() { + this.modal = true + }, + + closeModal() { + this.modal = false + }, + + openConversation(token) { + this.$router.push({ name: 'conversation', params: { token } }) + .catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`)) + this.closeModal() + }, + }, + +} + +</script> diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/Forwarder.vue b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/Forwarder.vue index cdf3b5d2b..e9f1e807f 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/Forwarder.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/Forwarder.vue @@ -78,7 +78,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' import NcModal from '@nextcloud/vue/dist/Components/NcModal.js' -import RoomSelector from '../../../../../views/RoomSelector.vue' +import RoomSelector from '../../../../RoomSelector.vue' export default { name: 'Forwarder', diff --git a/src/views/RoomSelector.spec.js b/src/components/RoomSelector.spec.js index e1124d445..a97e47484 100644 --- a/src/views/RoomSelector.spec.js +++ b/src/components/RoomSelector.spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils' +import flushPromises from 'flush-promises' import axios from '@nextcloud/axios' import { generateOcsUrl } from '@nextcloud/router' @@ -85,11 +86,12 @@ describe('RoomSelector.vue', () => { const wrapper = shallowMount(RoomSelector) expect(axios.get).toHaveBeenCalledWith( - generateOcsUrl('/apps/spreed/api/v4/room') + generateOcsUrl('/apps/spreed/api/v4/room'), + { params: { includeStatus: true } } ) // need to wait for re-render, otherwise the list is not rendered yet - await wrapper.vm.$nextTick() + await flushPromises() const list = wrapper.findAll('li') expect(list.length).toBe(3) @@ -103,13 +105,13 @@ describe('RoomSelector.vue', () => { showPostableOnly: true, }, }) - expect(axios.get).toHaveBeenCalledWith( - generateOcsUrl('/apps/spreed/api/v4/room') + generateOcsUrl('/apps/spreed/api/v4/room'), + { params: { includeStatus: true } } ) // need to wait for re-render, otherwise the list is not rendered yet - await wrapper.vm.$nextTick() + await flushPromises() const list = wrapper.findAll('li') expect(list.length).toBe(2) @@ -120,10 +122,10 @@ describe('RoomSelector.vue', () => { const wrapper = shallowMount(RoomSelector) expect(axios.get).toHaveBeenCalledWith( - generateOcsUrl('/apps/spreed/api/v4/room') + generateOcsUrl('/apps/spreed/api/v4/room'), + { params: { includeStatus: true } } ) - - await wrapper.vm.$nextTick() + await flushPromises() const eventHandler = jest.fn() wrapper.vm.$root.$on('select', eventHandler) diff --git a/src/views/RoomSelector.vue b/src/components/RoomSelector.vue index c392ebf6f..a2b5630e0 100644 --- a/src/views/RoomSelector.vue +++ b/src/components/RoomSelector.vue @@ -53,8 +53,13 @@ <span>{{ room.displayName }}</span> </li> </ul> - <div v-else-if="!loading"> - {{ t('spreed', 'No conversations found') }} + <div v-else-if="!loading" class="no-match-message"> + <h2 class="no-match-title"> + {{ noMatchFoundTitle }} + </h2> + <p v-if="noMatchFoundSubtitle" class="subtitle"> + {{ noMatchFoundSubtitle }} + </p> </div> </div> <div id="modal-buttons"> @@ -73,16 +78,14 @@ <script> import Magnify from 'vue-material-design-icons/Magnify.vue' -import axios from '@nextcloud/axios' -import { generateOcsUrl } from '@nextcloud/router' - import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcModal from '@nextcloud/vue/dist/Components/NcModal.js' import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' -import ConversationIcon from '../components/ConversationIcon.vue' +import ConversationIcon from './ConversationIcon.vue' import { CONVERSATION } from '../constants.js' +import { searchListedConversations, fetchConversations } from '../services/conversationsService.js' export default { name: 'RoomSelector', @@ -108,6 +111,7 @@ export default { type: String, default: '', }, + /** * Whether to only show conversations to which * the user can post messages. @@ -116,6 +120,11 @@ export default { type: Boolean, default: false, }, + + listOpenConversations: { + type: Boolean, + default: false, + }, }, emits: ['close', 'select'], data() { @@ -142,6 +151,18 @@ export default { return roomsTemp.filter(room => room.displayName.toLowerCase().includes(this.searchText.toLowerCase())) } }, + + noMatchFoundTitle() { + return this.listOpenConversations + ? t('spreed', 'No open conversations found') + : t('spreed', 'No conversations found') + }, + + noMatchFoundSubtitle() { + return this.listOpenConversations + ? t('spreed', 'Either there are no open conversations or you joined all of them.') + : t('spreed', 'Check spelling or use complete words.') + }, }, beforeMount() { this.fetchRooms() @@ -151,11 +172,13 @@ export default { } }, methods: { - fetchRooms() { - axios.get(generateOcsUrl('/apps/spreed/api/v4/room')).then((response) => { - this.rooms = response.data.ocs.data.sort(this.sortConversations) - this.loading = false - }) + async fetchRooms() { + const response = this.listOpenConversations + ? await searchListedConversations({ searchText: '' }, {}) + : await fetchConversations({}) + + this.rooms = response.data.ocs.data.sort(this.sortConversations) + this.loading = false }, sortConversations(conversation1, conversation2) { if (conversation1.isFavorite !== conversation2.isFavorite) { @@ -221,6 +244,16 @@ export default { height: 100%; } +.no-match-message{ + padding: 40px 0; + text-align: center; + +} + +.no-match-title{ + font-weight: normal; +} + li { padding: 6px; border: 1px solid transparent; diff --git a/src/deck.js b/src/deck.js index 7a4203c61..720eac7b7 100644 --- a/src/deck.js +++ b/src/deck.js @@ -27,7 +27,7 @@ import { showSuccess, showError } from '@nextcloud/dialogs' import { |