diff options
-rw-r--r-- | appinfo/routes.php | 5 | ||||
-rw-r--r-- | lib/Controller/PageController.php | 12 | ||||
-rw-r--r-- | src/App.vue | 8 | ||||
-rw-r--r-- | src/FilesSidebarCallViewApp.vue | 8 | ||||
-rw-r--r-- | src/PublicShareAuthSidebar.vue | 2 | ||||
-rw-r--r-- | src/PublicShareSidebar.vue | 8 | ||||
-rw-r--r-- | src/mixins/duplicateSessionHandler.js | 55 | ||||
-rw-r--r-- | src/router/router.js | 7 | ||||
-rw-r--r-- | src/utils/signaling.js | 50 | ||||
-rw-r--r-- | src/views/SessionConflictView.vue | 15 |
10 files changed, 119 insertions, 51 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php index 22074e938..f8e7c6145 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -33,6 +33,11 @@ return [ 'url' => '/not-found', 'verb' => 'GET', ], + [ + 'name' => 'Page#duplicateSession', + 'url' => '/duplicate-session', + 'verb' => 'GET', + ], [ 'name' => 'Page#showCall', diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index b75843c55..d3246e1d0 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -152,7 +152,17 @@ class PageController extends Controller { * @return Response */ public function notFound(): Response { - return new RedirectResponse($this->url->linkToRouteAbsolute('spreed.Page.index')); + return $this->index(); + } + + /** + * @PublicPage + * @NoCSRFRequired + * + * @return Response + */ + public function duplicateSession(): Response { + return $this->index(); } /** diff --git a/src/App.vue b/src/App.vue index 943839506..75e415673 100644 --- a/src/App.vue +++ b/src/App.vue @@ -31,7 +31,7 @@ </AppContent> <RightSidebar :show-chat-in-sidebar="isInCall" /> - <PreventUnload :when="isInCall" /> + <PreventUnload :when="warnLeaving" /> </Content> </template> @@ -56,6 +56,7 @@ import { } from './utils/webrtc/index' import { emit } from '@nextcloud/event-bus' import browserCheck from './mixins/browserCheck' +import duplicateSessionHandler from './mixins/duplicateSessionHandler' import talkHashCheck from './mixins/talkHashCheck' import { generateUrl } from '@nextcloud/router' @@ -72,6 +73,7 @@ export default { mixins: [ browserCheck, talkHashCheck, + duplicateSessionHandler, ], data: function() { @@ -116,6 +118,10 @@ export default { return this.participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED }, + warnLeaving() { + return !this.isLeavingAfterSessionConflict && this.isInCall + }, + /** * Keeps a list for all last message ids * @returns {object} Map with token => lastMessageId diff --git a/src/FilesSidebarCallViewApp.vue b/src/FilesSidebarCallViewApp.vue index fc1af2a25..052cbbfd5 100644 --- a/src/FilesSidebarCallViewApp.vue +++ b/src/FilesSidebarCallViewApp.vue @@ -26,7 +26,7 @@ v-show="isInCall" :token="token" :is-sidebar="true" /> - <PreventUnload :when="isInCall" /> + <PreventUnload :when="warnLeaving" /> </div> </template> @@ -35,6 +35,7 @@ import { PARTICIPANT } from './constants' import CallView from './components/CallView/CallView' import PreventUnload from 'vue-prevent-unload' import browserCheck from './mixins/browserCheck' +import duplicateSessionHandler from './mixins/duplicateSessionHandler' import talkHashCheck from './mixins/talkHashCheck' export default { @@ -48,6 +49,7 @@ export default { mixins: [ browserCheck, + duplicateSessionHandler, talkHashCheck, ], @@ -113,6 +115,10 @@ export default { return participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED }, + + warnLeaving() { + return !this.isLeavingAfterSessionConflict && this.isInCall + }, }, watch: { diff --git a/src/PublicShareAuthSidebar.vue b/src/PublicShareAuthSidebar.vue index fd46d399d..1d7dc5149 100644 --- a/src/PublicShareAuthSidebar.vue +++ b/src/PublicShareAuthSidebar.vue @@ -49,6 +49,7 @@ import { } from './services/participantsService' import { signalingKill } from './utils/webrtc/index' import browserCheck from './mixins/browserCheck' +import duplicateSessionHandler from './mixins/duplicateSessionHandler' import talkHashCheck from './mixins/talkHashCheck' export default { @@ -62,6 +63,7 @@ export default { mixins: [ browserCheck, + duplicateSessionHandler, talkHashCheck, ], diff --git a/src/PublicShareSidebar.vue b/src/PublicShareSidebar.vue index 9edb2377a..d5cb407e2 100644 --- a/src/PublicShareSidebar.vue +++ b/src/PublicShareSidebar.vue @@ -35,7 +35,7 @@ <CallView v-if="isInCall" :token="token" :is-sidebar="true" /> - <PreventUnload :when="isInCall" /> + <PreventUnload :when="warnLeaving" /> <CallButton class="call-button" /> <ChatView :token="token" /> </template> @@ -59,6 +59,7 @@ import { } from './services/participantsService' import { signalingKill } from './utils/webrtc/index' import browserCheck from './mixins/browserCheck' +import duplicateSessionHandler from './mixins/duplicateSessionHandler' import talkHashCheck from './mixins/talkHashCheck' export default { @@ -74,6 +75,7 @@ export default { mixins: [ browserCheck, + duplicateSessionHandler, talkHashCheck, ], @@ -119,6 +121,10 @@ export default { return participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED }, + + warnLeaving() { + return !this.isLeavingAfterSessionConflict && this.isInCall + }, }, beforeMount() { diff --git a/src/mixins/duplicateSessionHandler.js b/src/mixins/duplicateSessionHandler.js new file mode 100644 index 000000000..604eccb23 --- /dev/null +++ b/src/mixins/duplicateSessionHandler.js @@ -0,0 +1,55 @@ +/** + * @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/>. + * + */ + +import { generateUrl } from '@nextcloud/router' +import { EventBus } from '../services/EventBus' +import SessionStorage from '../services/SessionStorage' + +const talkHashCheck = { + data() { + return { + isLeavingAfterSessionConflict: false, + } + }, + + beforeDestroy() { + EventBus.$off('duplicateSessionDetected', this.duplicateSessionTriggered) + }, + + beforeMount() { + EventBus.$on('duplicateSessionDetected', this.duplicateSessionTriggered) + }, + + methods: { + duplicateSessionTriggered() { + this.isLeavingAfterSessionConflict = true + SessionStorage.removeItem('joined_conversation') + this.$nextTick(() => { + // Need to delay until next tick, otherwise the PreventUnload is still being triggered + // Putting the window in front with the warning and irritating the user + window.location = generateUrl('/apps/spreed/duplicate-session') + }) + }, + }, +} + +export default talkHashCheck diff --git a/src/router/router.js b/src/router/router.js index 9c144af18..8bf321154 100644 --- a/src/router/router.js +++ b/src/router/router.js @@ -25,6 +25,7 @@ import Router from 'vue-router' import { getRootUrl, generateUrl } from '@nextcloud/router' import MainView from '../views/MainView.vue' import NotFoundView from '../views/NotFoundView.vue' +import SessionConflictView from '../views/SessionConflictView.vue' import WelcomeView from '../views/WelcomeView.vue' Vue.use(Router) @@ -55,6 +56,12 @@ export default new Router({ props: true, }, { + path: '/apps/spreed/duplicate-session', + name: 'duplicatesession', + component: SessionConflictView, + props: true, + }, + { path: '/call/:token', name: 'conversation', component: MainView, diff --git a/src/utils/signaling.js b/src/utils/signaling.js index 66846b8d8..1157ce71c 100644 --- a/src/utils/signaling.js +++ b/src/utils/signaling.js @@ -39,7 +39,6 @@ import { showError, showWarning, } from '@nextcloud/dialogs' -import SessionStorage from '../services/SessionStorage' const Signaling = { Base: {}, @@ -446,30 +445,11 @@ Signaling.Internal.prototype._startPullingMessages = function() { console.error('Session was killed but the conversation still exists') this._trigger('pullMessagesStoppedOnFail') - OC.dialogs.confirmDestructive( - t('spreed', 'You joined the conversation in another window or device. This is currently not supported by Nextcloud Talk. What do you want to do?'), - t('spreed', 'Duplicate session'), - { - type: OC.dialogs.YES_NO_BUTTONS, - confirm: t('spreed', 'Restart here'), - confirmClasses: 'error', - cancel: t('spreed', 'Leave this page'), - }, - decision => { - if (!decision) { - // Cancel - SessionStorage.removeItem('joined_conversation') - window.location = generateUrl('/apps/spreed') - } else { - // Confirm - window.location = generateUrl('call/' + token) - } - } - ) + EventBus.$emit('duplicateSessionDetected') } else if (error.response && (error.response.status === 404 || error.response.status === 403)) { // Conversation was deleted or the user was removed console.error('Conversation was not found anymore') - OC.redirect(generateUrl('/apps/spreed/not-found')) + window.location = generateUrl('/apps/spreed/not-found') } else if (token) { if (this.pullMessagesFails === 1) { this.pullMessageErrorToast = showError(t('spreed', 'Lost connection to signaling server. Trying to reconnect.'), { @@ -1070,7 +1050,6 @@ Signaling.Standalone.prototype.processEvent = function(data) { } Signaling.Standalone.prototype.processRoomEvent = function(data) { - let showSessionConflictDialog = false let i let joinedUsers = [] let leftSessionIds = [] @@ -1093,7 +1072,7 @@ Signaling.Standalone.prototype.processRoomEvent = function(data) { if (this.settings.userId && joinedUsers[i].userid === this.settings.userId) { if (this.ownSessionJoined && joinedUsers[i].sessionid !== this.sessionId) { console.error('Duplicated session detected for the same user.') - showSessionConflictDialog = true + EventBus.$emit('duplicateSessionDetected') } else if (joinedUsers[i].sessionid === this.sessionId) { // We are ignoring joins before we found our own message, // as otherwise you get the warning for your own old session immediately @@ -1127,29 +1106,6 @@ Signaling.Standalone.prototype.processRoomEvent = function(data) { console.error('Unknown room event', data) break } - - if (showSessionConflictDialog) { - OC.dialogs.confirmDestructive( - t('spreed', 'You joined the conversation in another window or device. This is currently not supported by Nextcloud Talk. What do you want to do?'), - t('spreed', 'Duplicate session'), - { - type: OC.dialogs.YES_NO_BUTTONS, - confirm: t('spreed', 'Restart here'), - confirmClasses: 'error', - cancel: t('spreed', 'Leave this page'), - }, - decision => { - if (!decision) { - // Cancel - SessionStorage.removeItem('joined_conversation') - window.location = generateUrl('/apps/spreed') - } else { - // Confirm - window.location = generateUrl('call/' + this.currentRoomToken) - } - } - ) - } } Signaling.Standalone.prototype.processRoomMessageEvent = function(data) { diff --git a/src/views/SessionConflictView.vue b/src/views/SessionConflictView.vue new file mode 100644 index 000000000..4b5974608 --- /dev/null +++ b/src/views/SessionConflictView.vue @@ -0,0 +1,15 @@ +<template> + <div id="emptycontent"> + <div id="emptycontent-icon" class="icon-info" /> + <h2>{{ t('spreed', 'Duplicate session') }}</h2> + <p class="emptycontent-additional"> + {{ t('spreed','You joined the conversation in another window or device. This is currently not supported by Nextcloud Talk so this session was closed.') }} + </p> + </div> +</template> + +<script> +export default { + name: 'SessionConflictView', +} +</script> |