Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--appinfo/info.xml2
-rw-r--r--src/App.vue55
-rw-r--r--src/components/ConversationIcon.vue2
-rw-r--r--src/components/LeftSidebar/CirclesList/CirclesList.vue95
-rw-r--r--src/components/LeftSidebar/ContactsList/ContactsList.vue87
-rw-r--r--src/components/LeftSidebar/ConversationsList/ConversationsList.vue4
-rw-r--r--src/components/LeftSidebar/LeftSidebar.vue46
-rw-r--r--src/components/LeftSidebar/NewGroupConversation/SetContacts/SetContacts.vue3
-rw-r--r--src/components/ParticipantOptionsList.vue (renamed from src/components/LeftSidebar/GroupsList/GroupsList.vue)51
-rw-r--r--src/components/RightSidebar/Participants/CurrentParticipants/CurrentParticipants.vue15
-rw-r--r--src/components/RightSidebar/Participants/ParticipantsTab.vue153
-rw-r--r--src/router/router.js9
-rw-r--r--src/services/conversationsService.js31
-rw-r--r--src/utils/signaling.js6
-rw-r--r--src/views/NotFoundView.vue18
15 files changed, 283 insertions, 294 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 3d90d5927..67f1f83fb 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]></description>
- <version>8.0.0-alpha.3</version>
+ <version>8.0.0-alpha.4</version>
<licence>agpl</licence>
<author>Daniel Calviño Sánchez</author>
diff --git a/src/App.vue b/src/App.vue
index e3b155a28..fe55017b1 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -174,6 +174,20 @@ export default {
document.addEventListener('visibilitychange', this.changeWindowVisibility)
this.onResize()
+
+ EventBus.$on('conversationsReceived', (params) => {
+ if (this.$route.name === 'conversation'
+ && !this.$store.getters.conversations[this.token]) {
+ if (!params.singleConversation) {
+ console.info('Conversations received, but the current conversation is not in the list, trying to get potential public conversation manually')
+ this.fetchSingleConversation(this.token)
+ } else {
+ console.info('Conversation received, but the current conversation is not in the list. Redirecting to /apps/spreed')
+ this.$router.push('/apps/spreed/not-found')
+ }
+ }
+ })
+
/**
* Listens to the conversationsReceived globalevent, emitted by the conversationsList
* component each time a new batch of conversations is received and processed in
@@ -192,13 +206,6 @@ export default {
}
})
- EventBus.$on('conversationsReceived', () => {
- if (this.$route.name === 'conversation'
- && !this.$store.getters.conversations[this.token]) {
- this.$router.push('/apps/spreed')
- }
- })
-
/**
* Global before guard, this is called whenever a navigation is triggered.
*/
@@ -296,18 +303,28 @@ export default {
},
async fetchSingleConversation(token) {
- /** Fetches the conversations from the server and then adds them one by one
- * to the store.
- */
- const response = await fetchConversation(token)
- // this.$store.dispatch('purgeConversationsStore')
- this.$store.dispatch('addConversation', response.data.ocs.data)
-
- /**
- * Emits a global event that is used in App.vue to update the page title once the
- * ( if the current route is a conversation and once the conversations are received)
- */
- EventBus.$emit('conversationsReceived')
+ try {
+ /**
+ * Fetches the conversations from the server and then adds them one by one
+ * to the store.
+ */
+ const response = await fetchConversation(token)
+
+ // this.$store.dispatch('purgeConversationsStore')
+ this.$store.dispatch('addConversation', response.data.ocs.data)
+
+ /**
+ * Emits a global event that is used in App.vue to update the page title once the
+ * ( if the current route is a conversation and once the conversations are received)
+ */
+ EventBus.$emit('conversationsReceived', {
+ singleConversation: true,
+ })
+ } catch (exception) {
+ console.info('Conversation received, but the current conversation is not in the list. Redirecting to /apps/spreed')
+ this.$router.push('/apps/spreed/not-found')
+ this.$store.dispatch('hideSidebar')
+ }
},
},
}
diff --git a/src/components/ConversationIcon.vue b/src/components/ConversationIcon.vue
index bff76a7d8..adbc7a508 100644
--- a/src/components/ConversationIcon.vue
+++ b/src/components/ConversationIcon.vue
@@ -75,6 +75,8 @@ export default {
return 'icon-file'
} else if (this.item.objectType === 'share:password') {
return 'icon-password'
+ } else if (this.item.objectType === 'emails') {
+ return 'icon-mail'
} else if (this.item.type === CONVERSATION.TYPE.CHANGELOG) {
return 'icon-changelog'
} else if (this.item.type === CONVERSATION.TYPE.GROUP) {
diff --git a/src/components/LeftSidebar/CirclesList/CirclesList.vue b/src/components/LeftSidebar/CirclesList/CirclesList.vue
deleted file mode 100644
index 93481d4f2..000000000
--- a/src/components/LeftSidebar/CirclesList/CirclesList.vue
+++ /dev/null
@@ -1,95 +0,0 @@
-<!--
- - @copyright Copyright (c) 2019 Joas Schilling <coding@schilljs.com>
- -
- - @author Joas Schilling <coding@schilljs.com>
- -
- - @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>
- <ul class="contacts-list">
- <AppContentListItem
- v-for="item of circles"
- :key="item.id"
- :title="item.label"
- @click="createAndJoinConversation(item.id)">
- <template v-slot:icon>
- <ConversationIcon
- :item="iconData" />
- </template>
- </AppContentListItem>
- </ul>
-</template>
-
-<script>
-import ConversationIcon from '../../ConversationIcon'
-import AppContentListItem from '../ConversationsList/AppContentListItem/AppContentListItem'
-import { EventBus } from '../../../services/EventBus'
-import { createGroupConversation } from '../../../services/conversationsService'
-import { CONVERSATION } from '../../../constants'
-
-export default {
- name: 'CirclesList',
- components: {
- ConversationIcon,
- AppContentListItem,
- },
- props: {
- circles: {
- type: Array,
- required: true,
- },
- isLoading: {
- type: Boolean,
- default: false,
- },
- },
- computed: {
- iconData() {
- return {
- type: CONVERSATION.TYPE.GROUP,
- }
- },
- },
- methods: {
- /**
- * Create a new conversation with the selected circle.
- * @param {string} circleId the ID of the clicked circle.
- */
- async createAndJoinConversation(circleId) {
- console.debug('circles')
- console.debug(circleId)
- const response = await createGroupConversation(circleId, 'circles')
- const conversation = response.data.ocs.data
- this.$store.dispatch('addConversation', conversation)
- this.$router.push({ name: 'conversation', params: { token: conversation.token } }).catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`))
- console.debug(response)
- EventBus.$emit('resetSearchFilter')
- },
- },
-}
-</script>
-
-<style lang="scss" scoped>
-.ellipsis {
- text-overflow: ellipsis;
-}
-.contacts-list {
- overflow: visible;
- display: block;
-}
-
-</style>
diff --git a/src/components/LeftSidebar/ContactsList/ContactsList.vue b/src/components/LeftSidebar/ContactsList/ContactsList.vue
deleted file mode 100644
index 60c8c448e..000000000
--- a/src/components/LeftSidebar/ContactsList/ContactsList.vue
+++ /dev/null
@@ -1,87 +0,0 @@
-<!--
- - @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me>
- -
- - @author Marco Ambrosini <marcoambrosini@pm.me>
- -
- - @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>
- <ul class="contacts-list">
- <AppContentListItem
- v-for="item of contacts"
- :key="item.id"
- :title="item.label"
- @click="createAndJoinConversation(item.id)">
- <Avatar
- slot="icon"
- :size="44"
- :user="item.id"
- :display-name="item.label" />
- </AppContentListItem>
- </ul>
-</template>
-
-<script>
-import AppContentListItem from '../ConversationsList/AppContentListItem/AppContentListItem'
-import Avatar from '@nextcloud/vue/dist/Components/Avatar'
-import { EventBus } from '../../../services/EventBus'
-import { createOneToOneConversation } from '../../../services/conversationsService'
-
-export default {
- name: 'ContactsList',
- components: {
- Avatar,
- AppContentListItem,
- },
- props: {
- contacts: {
- type: Array,
- required: true,
- },
- isLoading: {
- type: Boolean,
- default: false,
- },
- },
- methods: {
- /**
- * Create a new conversation with the selected user.
- * @param {string} userId the ID of the clicked user.
- */
- async createAndJoinConversation(userId) {
- console.debug(userId)
- const response = await createOneToOneConversation(userId)
- const conversation = response.data.ocs.data
- this.$store.dispatch('addConversation', conversation)
- this.$router.push({ name: 'conversation', params: { token: conversation.token } }).catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`))
- console.debug(response)
- EventBus.$emit('resetSearchFilter')
- },
- },
-}
-</script>
-
-<style lang="scss" scoped>
-.ellipsis {
- text-overflow: ellipsis;
-}
-.contacts-list {
- overflow: visible;
- display: block;
-}
-
-</style>
diff --git a/src/components/LeftSidebar/ConversationsList/ConversationsList.vue b/src/components/LeftSidebar/ConversationsList/ConversationsList.vue
index ca470df81..446024676 100644
--- a/src/components/LeftSidebar/ConversationsList/ConversationsList.vue
+++ b/src/components/LeftSidebar/ConversationsList/ConversationsList.vue
@@ -111,7 +111,9 @@ export default {
* Emits a global event that is used in App.vue to update the page title once the
* ( if the current route is a conversation and once the conversations are received)
*/
- EventBus.$emit('conversationsReceived')
+ EventBus.$emit('conversationsReceived', {
+ singleConversation: false,
+ })
},
},
}
diff --git a/src/components/LeftSidebar/LeftSidebar.vue b/src/components/LeftSidebar/LeftSidebar.vue
index 70e397c20..180427a7a 100644
--- a/src/components/LeftSidebar/LeftSidebar.vue
+++ b/src/components/LeftSidebar/LeftSidebar.vue
@@ -39,7 +39,9 @@
<Caption
:title="t('spreed', 'Contacts')" />
<li v-if="searchResultsUsers.length !== 0">
- <ContactsList :contacts="searchResultsUsers" />
+ <ParticipantOptionsList
+ :items="searchResultsUsers"
+ @click="createAndJoinConversation" />
</li>
</template>
@@ -47,7 +49,9 @@
<Caption
:title="t('spreed', 'Groups')" />
<li v-if="searchResultsGroups.length !== 0">
- <GroupsList :groups="searchResultsGroups" />
+ <ParticipantOptionsList
+ :items="searchResultsGroups"
+ @click="createAndJoinConversation" />
</li>
</template>
@@ -55,7 +59,9 @@
<Caption
:title="t('spreed', 'Circles')" />
<li v-if="searchResultsCircles.length !== 0">
- <CirclesList :circles="searchResultsCircles" />
+ <ParticipantOptionsList
+ :items="searchResultsCircles"
+ @click="createAndJoinConversation" />
</li>
</template>
@@ -71,15 +77,16 @@
<script>
import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation'
import Caption from '../Caption'
-import CirclesList from './CirclesList/CirclesList'
-import ContactsList from './ContactsList/ContactsList'
import ConversationsList from './ConversationsList/ConversationsList'
-import GroupsList from './GroupsList/GroupsList'
+import ParticipantOptionsList from '../ParticipantOptionsList'
import Hint from '../Hint'
import SearchBox from './SearchBox/SearchBox'
import debounce from 'debounce'
import { EventBus } from '../../services/EventBus'
-import { searchPossibleConversations } from '../../services/conversationsService'
+import {
+ createGroupConversation, createOneToOneConversation,
+ searchPossibleConversations,
+} from '../../services/conversationsService'
import { CONVERSATION } from '../../constants'
import NewGroupConversation from './NewGroupConversation/NewGroupConversation'
@@ -90,10 +97,8 @@ export default {
components: {
AppNavigation,
Caption,
- ContactsList,
- CirclesList,
ConversationsList,
- GroupsList,
+ ParticipantOptionsList,
Hint,
SearchBox,
NewGroupConversation,
@@ -147,7 +152,7 @@ export default {
} else {
return t('spreed', 'Groups')
}
- } else if (!this.searchResultsGroups.length) {
+ } else {
if (this.isCirclesEnabled && !this.searchResultsCircles.length) {
return t('spreed', 'Circles')
}
@@ -187,6 +192,25 @@ export default {
this.contactsLoading = false
},
+ /**
+ * Create a new conversation with the selected group/user/circle
+ * @param {Object} item The autocomplete suggestion to start a conversation with
+ * @param {string} item.id The ID of the target
+ * @param {string} item.source The source of the target
+ */
+ async createAndJoinConversation(item) {
+ let response
+ if (item.source === 'users') {
+ response = await createOneToOneConversation(item.id)
+ } else {
+ response = await createGroupConversation(item.id, item.source)
+ }
+ const conversation = response.data.ocs.data
+ this.$store.dispatch('addConversation', conversation)
+ this.$router.push({ name: 'conversation', params: { token: conversation.token } }).catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`))
+ EventBus.$emit('resetSearchFilter')
+ },
+
hasOneToOneConversationWith(userId) {
return !!this.conversationsList.find(conversation => conversation.type === CONVERSATION.TYPE.ONE_TO_ONE && conversation.name === userId)
},
diff --git a/src/components/LeftSidebar/NewGroupConversation/SetContacts/SetContacts.vue b/src/components/LeftSidebar/NewGroupConversation/SetContacts/SetContacts.vue
index 618d41018..639928130 100644
--- a/src/components/LeftSidebar/NewGroupConversation/SetContacts/SetContacts.vue
+++ b/src/components/LeftSidebar/NewGroupConversation/SetContacts/SetContacts.vue
@@ -31,7 +31,7 @@
@input="handleInput">
<template v-if="isSearching">
<Caption
- :title="t('spreed', `Select participants`)" />
+ :title="t('spreed', 'Select participants')" />
<ParticipantsList
:add-on-click="false"
height="250px"
@@ -79,7 +79,6 @@ export default {
},
computed: {
-
isSearching() {
return this.searchText !== ''
},
diff --git a/src/components/LeftSidebar/GroupsList/GroupsList.vue b/src/components/ParticipantOptionsList.vue
index dd56d7320..4a62ddcfc 100644
--- a/src/components/LeftSidebar/GroupsList/GroupsList.vue
+++ b/src/components/ParticipantOptionsList.vue
@@ -22,33 +22,32 @@
<template>
<ul class="contacts-list">
<AppContentListItem
- v-for="item of groups"
+ v-for="item of items"
:key="item.id"
:title="item.label"
- @click="createAndJoinConversation(item.id)">
- <template v-slot:icon>
+ @click="onClick(item)">
+ <template
+ v-slot:icon>
<ConversationIcon
- :item="iconData" />
+ :item="iconData(item)" />
</template>
</AppContentListItem>
</ul>
</template>
<script>
-import ConversationIcon from '../../ConversationIcon'
-import AppContentListItem from '../ConversationsList/AppContentListItem/AppContentListItem'
-import { EventBus } from '../../../services/EventBus'
-import { createGroupConversation } from '../../../services/conversationsService'
-import { CONVERSATION } from '../../../constants'
+import ConversationIcon from './ConversationIcon'
+import AppContentListItem from './LeftSidebar/ConversationsList/AppContentListItem/AppContentListItem'
+import { CONVERSATION } from '../constants'
export default {
- name: 'GroupsList',
+ name: 'ParticipantOptionsList',
components: {
ConversationIcon,
AppContentListItem,
},
props: {
- groups: {
+ items: {
type: Array,
required: true,
},
@@ -57,26 +56,25 @@ export default {
default: false,
},
},
- computed: {
- iconData() {
+ methods: {
+ // forward click event
+ onClick(item) {
+ this.$emit('click', item)
+ },
+ iconData(item) {
+ if (item.source === 'users') {
+ return {
+ type: CONVERSATION.TYPE.ONE_TO_ONE,
+ displayName: item.label,
+ name: item.id,
+ }
+ }
return {
type: CONVERSATION.TYPE.GROUP,
+ objectType: item.source,
}
},
},
- methods: {
- /**
- * Create a new conversation with the selected group.
- * @param {string} groupId the ID of the clicked group.
- */
- async createAndJoinConversation(groupId) {
- const response = await createGroupConversation(groupId, 'groups')
- const conversation = response.data.ocs.data
- this.$store.dispatch('addConversation', conversation)
- this.$router.push({ name: 'conversation', params: { token: conversation.token } }).catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`))
- EventBus.$emit('resetSearchFilter')
- },
- },
}
</script>
@@ -88,5 +86,4 @@ export default {
overflow: visible;
display: block;
}
-
</style>
diff --git a/src/components/RightSidebar/Participants/CurrentParticipants/CurrentParticipants.vue b/src/components/RightSidebar/Participants/CurrentParticipants/CurrentParticipants.vue
index cd41708cc..c06cdc746 100644
--- a/src/components/RightSidebar/Participants/CurrentParticipants/CurrentParticipants.vue
+++ b/src/components/RightSidebar/Participants/CurrentParticipants/CurrentParticipants.vue
@@ -35,6 +35,13 @@ export default {
ParticipantsList,
},
+ props: {
+ searchText: {
+ type: String,
+ default: '',
+ },
+ },
+
computed: {
token() {
return this.$store.getters.getToken()
@@ -46,7 +53,13 @@ export default {
* @returns {array}
*/
participantsList() {
- const participants = this.$store.getters.participantsList(this.token)
+ let participants = this.$store.getters.participantsList(this.token)
+
+ if (this.searchText !== '') {
+ const lowerSearchText = this.searchText.toLowerCase()
+ participants = participants.filter(participant => participant.displayName.toLowerCase().indexOf(lowerSearchText) !== -1 || participant.userId.toLowerCase().indexOf(lowerSearchText) !== -1)
+ }
+
return participants.slice().sort(this.sortParticipants)
},
},
diff --git a/src/components/RightSidebar/Participants/ParticipantsTab.vue b/src/components/RightSidebar/Participants/ParticipantsTab.vue
index 0110192b6..00addc57d 100644
--- a/src/components/RightSidebar/Participants/ParticipantsTab.vue
+++ b/src/components/RightSidebar/Participants/ParticipantsTab.vue
@@ -26,33 +26,46 @@
v-model="searchText"
:placeholder-text="t('spreed', 'Add participants to the conversation')"
@input="handleInput" />
- <CurrentParticipants />
+ <Caption v-if="isSearching"
+ :title="t('spreed', 'Participants')" />
+ <CurrentParticipants
+ :search-text="searchText" />
<template v-if="isSearching">
- <Caption
- :title="t('spreed', 'Add participants')" />
- <ParticipantsList
- v-if="addableUsers.length !== 0"
- :items="addableUsers"
- @refreshCurrentParticipants="getParticipants" />
- <Hint v-else-if="contactsLoading" :hint="t('spreed', 'Loading')" />
- <Hint v-else :hint="t('spreed', 'No search results')" />
+ <template v-if="addableUsers.length !== 0">
+ <Caption
+ :title="t('spreed', 'Add contacts')" />
+ <ParticipantOptionsList
+ :items="addableUsers"
+ @click="addParticipants" />
+ </template>
- <Caption
- :title="t('spreed', 'Add groups')" />
- <ParticipantsList
- v-if="addableGroups.length !== 0"
- :items="addableGroups"
- @refreshCurrentParticipants="getParticipants" />
- <Hint v-else-if="contactsLoading" :hint="t('spreed', 'Loading')" />
- <Hint v-else :hint="t('spreed', 'No search results')" />
+ <template v-if="addableGroups.length !== 0">
+ <Caption
+ :title="t('spreed', 'Add groups')" />
+ <ParticipantOptionsList
+ :items="addableGroups"
+ @click="addParticipants" />
+ </template>
+
+ <template v-if="addableEmails.length !== 0">
+ <Caption
+ :title="t('spreed', 'Add emails')" />
+ <ParticipantOptionsList
+ :items="addableEmails"
+ @click="addParticipants" />
+ </template>
+
+ <template v-if="addableCircles.length !== 0">
+ <Caption
+ :title="t('spreed', 'Add circles')" />
+ <ParticipantOptionsList
+ :items="addableCircles"
+ @click="addParticipants" />
+ </template>
- <Caption
- :title="t('spreed', 'Add circles')" />
- <ParticipantsList
- v-if="addableCircles.length !== 0"
- :items="addableCircles"
- @refreshCurrentParticipants="getParticipants" />
- <Hint v-else-if="contactsLoading" :hint="t('spreed', 'Loading')" />
+ <Caption v-if="sourcesWithoutResults"
+ :title="sourcesWithoutResultsList" />
+ <Hint v-if="contactsLoading" :hint="t('spreed', 'Loading')" />
<Hint v-else :hint="t('spreed', 'No search results')" />
</template>
</div>
@@ -62,23 +75,26 @@
import Caption from '../../Caption'
import CurrentParticipants from './CurrentParticipants/CurrentParticipants'
import Hint from '../../Hint'
-import ParticipantsList from './ParticipantsList/ParticipantsList'
+import ParticipantOptionsList from '../../ParticipantOptionsList'
import SearchBox from '../../LeftSidebar/SearchBox/SearchBox'
import debounce from 'debounce'
import { EventBus } from '../../../services/EventBus'
import { CONVERSATION, WEBINAR } from '../../../constants'
import { searchPossibleConversations } from '../../../services/conversationsService'
-import { fetchParticipants } from '../../../services/participantsService'
+import {
+ addParticipant,
+ fetchParticipants,
+} from '../../../services/participantsService'
import isInLobby from '../../../mixins/isInLobby'
export default {
name: 'ParticipantsTab',
components: {
CurrentParticipants,
+ ParticipantOptionsList,
SearchBox,
Caption,
Hint,
- ParticipantsList,
},
mixins: [
@@ -97,6 +113,7 @@ export default {
searchText: '',
searchResults: [],
contactsLoading: false,
+ isCirclesEnabled: true, // FIXME
}
},
@@ -125,6 +142,44 @@ export default {
isSearching() {
return this.searchText !== ''
},
+
+ sourcesWithoutResults() {
+ return !this.addableUsers.length
+ || !this.addableGroups.length
+ || (this.isCirclesEnabled && !this.addableCircles.length)
+ },
+
+ sourcesWithoutResultsList() {
+ if (!this.addableUsers.length) {
+ if (!this.addableGroups.length) {
+ if (this.isCirclesEnabled && !this.addableCircles.length) {
+ return t('spreed', 'Add contacts, groups or circles')
+ } else {
+ return t('spreed', 'Add contacts or groups')
+ }
+ } else {
+ if (this.isCirclesEnabled && !this.addableCircles.length) {
+ return t('spreed', 'Add contacts or circles')
+ } else {
+ return t('spreed', 'Add contacts')
+ }
+ }
+ } else {
+ if (!this.addableGroups.length) {
+ if (this.isCirclesEnabled && !this.addableCircles.length) {
+ return t('spreed', 'Add groups or circles')
+ } else {
+ return t('spreed', 'Add groups')
+ }
+ } else {
+ if (this.isCirclesEnabled && !this.addableCircles.length) {
+ return t('spreed', 'Add circles')
+ }
+ }
+ }
+ return t('spreed', 'Add other sources')
+ },
+
addableUsers() {
if (this.searchResults !== []) {
const searchResultUsers = this.searchResults.filter(item => item.source === 'users')
@@ -148,6 +203,12 @@ export default {
}
return []
},
+ addableEmails() {
+ if (this.searchResults !== []) {
+ return this.searchResults.filter((item) => item.source === 'emails')
+ }
+ return []
+ },
addableCircles() {
if (this.searchResults !== []) {
return this.searchResults.filter((item) => item.source === 'circles')
@@ -157,9 +218,8 @@ export default {
},
beforeMount() {
- this.getParticipants()
-
EventBus.$on('routeChange', this.onRouteChange)
+ EventBus.$on('joinedConversation', this.onJoinedConversation)
// FIXME this works only temporary until signaling is fixed to be only on the calls
// Then we have to search for another solution. Maybe the room list which we update
@@ -169,15 +229,22 @@ export default {
beforeDestroy() {
EventBus.$off('routeChange', this.onRouteChange)
+ EventBus.$off('joinedConversation', this.onJoinedConversation)
EventBus.$off('Signaling::participantListChanged', this.getParticipants)
},
methods: {
/**
- * If the route changes, the search filter is reset and we get participants again
+ * If the route changes, the search filter is reset
*/
onRouteChange() {
this.searchText = ''
+ },
+
+ /**
+ * If the conversation has been joined, we get the participants
+ */
+ onJoinedConversation() {
this.$nextTick(() => {
this.getParticipants()
})
@@ -200,16 +267,32 @@ export default {
async fetchSearchResults() {
try {
- const response = await searchPossibleConversations(this.searchText)
+ const response = await searchPossibleConversations(this.searchText, this.token)
this.searchResults = response.data.ocs.data
this.getParticipants()
this.contactsLoading = false
- } catch (exeption) {
- console.error(exeption)
+ } catch (exception) {
+ console.error(exception)
OCP.Toast.error(t('spreed', 'An error occurred while performing the search'))
}
},
+ /**
+ * Add the selected group/user/circle to the conversation
+ * @param {Object} item The autocomplete suggestion to start a conversation with
+ * @param {string} item.id The ID of the target
+ * @param {string} item.source The source of the target
+ */
+ async addParticipants(item) {
+ try {
+ await addParticipant(this.token, item.id, item.source)
+ this.searchText = ''
+ this.getParticipants()
+ } catch (exception) {
+ console.debug(exception)
+ }
+ },
+
async getParticipants() {
if (this.token === '' || this.isInLobby) {
return
@@ -227,8 +310,8 @@ export default {
participant,
})
})
- } catch (exeption) {
- console.error(exeption)
+ } catch (exception) {
+ console.error(exception)
OCP.Toast.error(t('spreed', 'An error occurred while fetching the participants'))
}
},
diff --git a/src/router/router.js b/src/router/router.js
index ee7f194c3..c67a23e5b 100644
--- a/src/router/router.js
+++ b/src/router/router.js
@@ -23,8 +23,9 @@
import Vue from 'vue'
import Router from 'vue-router'
import { generateUrl } from '@nextcloud/router'
-import WelcomeView from '../views/WelcomeView.vue'
import MainView from '../views/MainView.vue'
+import NotFoundView from '../views/NotFoundView.vue'
+import WelcomeView from '../views/WelcomeView.vue'
Vue.use(Router)
@@ -42,6 +43,12 @@ export default new Router({
props: true,
},
{
+ path: '/apps/spreed/not-found',
+ name: 'notfound',
+ component: NotFoundView,
+ props: true,
+ },
+ {
path: '/call/:token',
name: 'conversation',
component: MainView,
diff --git a/src/services/conversationsService.js b/src/services/conversationsService.js
index b3b7a044a..c9fa47644 100644
--- a/src/services/conversationsService.js
+++ b/src/services/conversationsService.js
@@ -41,22 +41,35 @@ const fetchConversations = async function() {
* @param {string} token The token of the conversation to be fetched.
*/
const fetchConversation = async function(token) {
- try {
- const response = await axios.get(generateOcsUrl('apps/spreed/api/v1', 2) + `room/${token}`)
- return response
- } catch (error) {
- console.debug('Error while fetching a conversation: ', error)
- }
+ const response = await axios.get(generateOcsUrl('apps/spreed/api/v1', 2) + `room/${token}`)
+ return response
}
/**
* Fetch possible conversations
* @param {string} searchText The string that will be used in the search query.
+ * @param {string} [token] The token of the conversation (if any)
*/
-const searchPossibleConversations = async function(searchText) {
+const searchPossibleConversations = async function(searchText, token) {
+ token = token || 'new'
+ const shareTypes = [
+ SHARE.TYPE.USER,
+ SHARE.TYPE.GROUP,
+ SHARE.TYPE.CIRCLE,
+ ]
+ if (token !== 'new') {
+ shareTypes.push(SHARE.TYPE.EMAIL)
+ }
+
try {
- const response = await axios.get(generateOcsUrl('core/autocomplete', 2) + `get` + `?format=json` + `&search=${searchText}` + `&itemType=call` + `&itemId=new` + `&shareTypes[]=${SHARE.TYPE.USER}&shareTypes[]=${SHARE.TYPE.GROUP}&shareTypes[]=${SHARE.TYPE.CIRCLE}`)
- return response
+ return await axios.get(generateOcsUrl('core/autocomplete', 2) + `get`, {
+ params: {
+ search: searchText,
+ itemType: 'call',
+ itemId: token,
+ shareTypes,
+ },
+ })
} catch (error) {
console.debug('Error while searching possible conversations: ', error)
}
diff --git a/src/utils/signaling.js b/src/utils/signaling.js
index ee2b4d11c..a468483f3 100644
--- a/src/utils/signaling.js
+++ b/src/utils/signaling.js
@@ -215,11 +215,7 @@ Signaling.Base.prototype.joinRoom = function(token, password) {
this._joinRoomSuccess(token, result.ocs.data.sessionId)
}.bind(this),
error: function(result) {
- reject(new Error())
- if (result.status === 404 || result.status === 503) {
- // Room not found or maintenance mode
- OC.redirect(OC.generateUrl('apps/spreed'))
- }
+ reject(result)
if (result.status === 403) {
// This should not happen anymore since we ask for the password before
diff --git a/src/views/NotFoundView.vue b/src/views/NotFoundView.vue
new file mode 100644
index 000000000..6f1478f26
--- /dev/null
+++ b/src/views/NotFoundView.vue
@@ -0,0 +1,18 @@
+<template>
+ <div id="emptycontent">
+ <div id="emptycontent-icon" class="icon-search" />
+ <h2>{{ t('spreed', 'The conversation does not exist') }}</h2>
+ <p class="emptycontent-additional">
+ {{ t('spreed','Join a conversation or start a new one!') }}
+ </p>
+ </div>
+</template>
+
+<script>
+export default {
+ name: 'NotFoundView',
+}
+</script>
+
+<style lang="scss" scoped>
+</style>