diff --git a/MusicPlayerV2-GUI/src/components/notificationsModule.vue b/MusicPlayerV2-GUI/src/components/notificationsModule.vue index bddaec1..3769b6e 100644 --- a/MusicPlayerV2-GUI/src/components/notificationsModule.vue +++ b/MusicPlayerV2-GUI/src/components/notificationsModule.vue @@ -40,6 +40,7 @@ priority: string; id: number; redirect?: string; + openInNewTab?: boolean; } interface NotificationList { @@ -58,7 +59,11 @@ const notificationAction = () => { if ( notifications.value[ currentDID.value ] ) { if ( notifications.value[ currentDID.value ].redirect ) { - router.push( notifications.value[ currentDID.value ].redirect ?? '' ); + if ( notifications.value[ currentDID.value ].openInNewTab ) { + window.open( notifications.value[ currentDID.value ].redirect ?? '' ); + } else { + router.push( notifications.value[ currentDID.value ].redirect ?? '' ); + } } } }; @@ -67,11 +72,11 @@ * Create a notification that will be displayed using the internal notification scheduler * @param {string} message The message to show. Can only be plain text (no HTML) * @param {number} showDuration The duration in seconds for which to show the notification - * @param {string} messageType Type of notification to show. Will dictate how it looks: 'ok', 'error', 'info', 'warn', 'progress' + * @param {string} msgType Type of notification to show. Will dictate how it looks: 'ok', 'error', 'info', 'warn', 'progress' * @param {string} priority The priority of the message: 'low', 'normal', 'critical' * @returns {number} */ - const createNotification = ( message: string, showDuration: number, msgType: string, priority: string, redirect?: string ): number => { + const createNotification = ( message: string, showDuration: number, msgType: string, priority: string, redirect?: string, openInNewTab?: boolean ): number => { /* Takes a notification options array that contains: message, showDuration (in seconds), msgType (ok, error, progress, info) and priority (low, normal, critical). Returns a notification ID which can be used to cancel the notification. The component will throttle notifications and display @@ -89,7 +94,7 @@ currentID.value[ 'low' ] += 1; id = currentID.value[ 'low' ]; } - notifications.value[ id ] = { 'message': message, 'showDuration': showDuration, 'messageType': msgType, 'priority': priority, 'id': id, redirect: redirect }; + notifications.value[ id ] = { 'message': message, 'showDuration': showDuration, 'messageType': msgType, 'priority': priority, 'id': id, redirect: redirect, openInNewTab: openInNewTab }; queue.value.push( id ); console.log( 'scheduled notification: ' + id + ' (' + message + ')' ); if ( ( new Date().getTime() - notificationDisplayStartTime.value ) / 1000 >= ( notifications.value[ currentDID.value ] ? notifications.value[ currentDID.value ].showDuration : 0 ) || messageType.value === 'hide' ) { diff --git a/MusicPlayerV2-GUI/src/components/playerView.vue b/MusicPlayerV2-GUI/src/components/playerView.vue index 8899fa9..541087e 100644 --- a/MusicPlayerV2-GUI/src/components/playerView.vue +++ b/MusicPlayerV2-GUI/src/components/playerView.vue @@ -111,9 +111,24 @@ const roomName = ref( '' ); const isShowingWarning = ref( false ); let currentlyOpenPopup = ''; + let logoutErrorNotification = -1; const emits = defineEmits( [ 'playerStateChange' ] ); + document.addEventListener( 'musicplayer:autherror', () => { + localStorage.setItem( 'close-tab', 'true' ); + isConnectedToNotifier.value = false; + logoutErrorNotification = notifications.value.createNotification( 'You appear to have been logged out. Click to log in again!', 600, 'error', 'critical', '/', true ); + } ); + + window.addEventListener( 'storage', () => { + if ( localStorage.getItem( 'login-ok' ) === 'true' ) { + notifications.value.cancelNotification( logoutErrorNotification ); + notifications.value.createNotification( 'Logged in again. You will have to reconnect to the share!', 20, 'ok', 'normal' ); + localStorage.removeItem( 'login-ok' ); + } + } ); + const playPause = () => { isPlaying.value = !isPlaying.value; if ( isPlaying.value ) { @@ -588,6 +603,10 @@ if ( e === 'ERR_CONFLICT' ) { notifications.value.createNotification( 'A share with this name exists already!', 5, 'error', 'normal' ); control( 'start-share' ); + } else if ( e === 'ERR_UNAUTHORIZED' ) { + console.error( e ); + localStorage.setItem( 'close-tab', 'true' ); + logoutErrorNotification = notifications.value.createNotification( 'You appear to have been logged out. Click to log in again!', 20, 'error', 'normal', '/', true ); } else { console.error( e ); notifications.value.createNotification( 'Could not create share!', 5, 'error', 'normal' ); diff --git a/MusicPlayerV2-GUI/src/scripts/connection.ts b/MusicPlayerV2-GUI/src/scripts/connection.ts index bec578f..b5610e3 100644 --- a/MusicPlayerV2-GUI/src/scripts/connection.ts +++ b/MusicPlayerV2-GUI/src/scripts/connection.ts @@ -87,7 +87,7 @@ class SocketConnection { this.reconnectRetryCount += 1; setTimeout( () => { this.connect(); - }, 500 * this.reconnectRetryCount ); + }, 1000 * this.reconnectRetryCount ); } }; } else { diff --git a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts index 2b80bf9..3596683 100644 --- a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts +++ b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts @@ -24,6 +24,7 @@ class NotificationHandler { lastEmitTimestamp: number; openConnectionsCount: number; pendingRequestCount: number; + connectionWasSuccessful: boolean; constructor () { this.socket = io( localStorage.getItem( 'url' ) ?? '', { @@ -38,6 +39,7 @@ class NotificationHandler { this.lastEmitTimestamp = 0; this.pendingRequestCount = 0; this.openConnectionsCount = 0; + this.connectionWasSuccessful = false; } /** @@ -74,6 +76,8 @@ class NotificationHandler { } ); } else if ( res.status === 409 ) { reject( 'ERR_CONFLICT' ); + } else if ( res.status === 403 || res.status === 401 ) { + reject( 'ERR_UNAUTHORIZED' ); } else { reject( 'ERR_ROOM_CREATING' ); } @@ -89,8 +93,10 @@ class NotificationHandler { fetch( localStorage.getItem( 'url' ) + '/socket/joinRoom?room=' + this.roomName, { credentials: 'include' } ).then( res => { if ( res.status === 200 ) { this.eventSource = new EventSource( localStorage.getItem( 'url' ) + '/socket/connection?room=' + this.roomName, { withCredentials: true } ); + this.eventSource.onopen = () => { this.isConnected = true; + this.connectionWasSuccessful = true; this.reconnectRetryCount = 0; console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Connection successfully established!' ); resolve(); @@ -108,22 +114,37 @@ class NotificationHandler { this.isConnected = false; this.eventSource?.close(); this.openConnectionsCount -= 1; - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to connection error!' ); console.debug( e ); + console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to connection error!' ); this.eventSource = undefined; this.reconnectRetryCount += 1; setTimeout( () => { this.sseConnect(); - }, 500 * this.reconnectRetryCount ); + }, 1000 * this.reconnectRetryCount ); } }; + } else if ( res.status === 403 || res.status === 401 || res.status === 404 ) { + document.dispatchEvent( new Event( 'musicplayer:autherror' ) ); + reject( 'ERR_UNAUTHORIZED' ); } else { - reject( 'ERR_ROOM_CONNECTING' ); + reject( 'ERR_ROOM_CONNECTING_STATUS_CODE' ); } } ).catch( () => { - reject( 'ERR_ROOM_CONNECTING' ); + if ( !this.connectionWasSuccessful ) { + reject( 'ERR_ROOM_CONNECTING' ); + } else { + this.openConnectionsCount -= 1; + console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to severe connection error!' ); + + this.eventSource = undefined; + + this.reconnectRetryCount += 1; + setTimeout( () => { + this.sseConnect(); + }, 1000 * this.reconnectRetryCount ); + } } ); } else { resolve(); diff --git a/MusicPlayerV2-GUI/src/views/HomeView.vue b/MusicPlayerV2-GUI/src/views/HomeView.vue index b8cfdd6..8b04216 100644 --- a/MusicPlayerV2-GUI/src/views/HomeView.vue +++ b/MusicPlayerV2-GUI/src/views/HomeView.vue @@ -61,6 +61,11 @@ if ( res.status ) { store.isUserAuth = true; store.username = res.username; + if ( localStorage.getItem( 'close-tab' ) ) { + localStorage.removeItem( 'close-tab' ); + window.close(); + } + localStorage.setItem( 'login-ok', 'true' ); router.push( localStorage.getItem( 'redirect' ) ?? '/app' ); localStorage.removeItem( 'redirect' ); } else {