From f02fcb96a94804fb36bdeab17566cd67bfd1c235 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Thu, 11 Sep 2025 21:37:34 +0200 Subject: [PATCH] frontend: improve leave redirect URL handling --- .../lib/guards/extract-query-params.guard.ts | 32 ++++++++--- .../src/lib/pages/error/error.component.ts | 2 +- .../end-meeting/end-meeting.component.ts | 2 +- .../lib/pages/meeting/meeting.component.ts | 2 +- .../src/lib/services/navigation.service.ts | 53 +++++++++++-------- .../src/lib/services/theme.service.ts | 8 ++- 6 files changed, 65 insertions(+), 34 deletions(-) diff --git a/frontend/projects/shared-meet-components/src/lib/guards/extract-query-params.guard.ts b/frontend/projects/shared-meet-components/src/lib/guards/extract-query-params.guard.ts index 16d1f37..f33485c 100644 --- a/frontend/projects/shared-meet-components/src/lib/guards/extract-query-params.guard.ts +++ b/frontend/projects/shared-meet-components/src/lib/guards/extract-query-params.guard.ts @@ -1,7 +1,13 @@ import { inject } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivateFn } from '@angular/router'; import { ErrorReason } from '@lib/models'; -import { NavigationService, ParticipantService, RoomService, SessionStorageService } from '@lib/services'; +import { + AppDataService, + NavigationService, + ParticipantService, + RoomService, + SessionStorageService +} from '@lib/services'; import { WebComponentProperty } from '@lib/typings/ce/webcomponent/properties.model'; export const extractRoomQueryParamsGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => { @@ -66,14 +72,28 @@ const extractParams = ({ params, queryParams }: ActivatedRouteSnapshot) => ({ /** * Handles the leave redirect URL logic with automatic referrer detection */ -const handleLeaveRedirectUrl = (leaveRedirectUrl: string) => { +const handleLeaveRedirectUrl = (leaveRedirectUrl: string | undefined) => { const navigationService = inject(NavigationService); + const appDataService = inject(AppDataService); + const isEmbeddedMode = appDataService.isEmbeddedMode(); + // Explicit valid URL provided - use as is if (leaveRedirectUrl && isValidUrl(leaveRedirectUrl)) { - // If explicitly provided, use it navigationService.setLeaveRedirectUrl(leaveRedirectUrl); - } else { - // Check if user came from another domain and auto-configure redirect + return; + } + + // Absolute path provided in embedded mode - construct full URL + if (isEmbeddedMode && leaveRedirectUrl?.startsWith('/')) { + // If in embedded mode and a absolute path is provided, construct full URL based on parent origin + const parentUrl = document.referrer; + const parentOrigin = new URL(parentUrl).origin; + navigationService.setLeaveRedirectUrl(parentOrigin + leaveRedirectUrl); + return; + } + + // Auto-detect from referrer (only if no explicit URL provided and not embedded) + if (!leaveRedirectUrl && !isEmbeddedMode) { const autoRedirectUrl = getAutoRedirectUrl(); if (autoRedirectUrl) { navigationService.setLeaveRedirectUrl(autoRedirectUrl); @@ -110,8 +130,6 @@ const getAutoRedirectUrl = (): string | null => { }; const isValidUrl = (url: string) => { - if (!url) return false; - try { new URL(url); return true; diff --git a/frontend/projects/shared-meet-components/src/lib/pages/error/error.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/error/error.component.ts index 492bdfb..c2df746 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/error/error.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/error/error.component.ts @@ -134,7 +134,7 @@ export class ErrorComponent implements OnInit { const redirectTo = this.navService.getLeaveRedirectURL(); if (redirectTo) { // Navigate to the specified redirect URL - await this.navService.redirectTo(redirectTo); + await this.navService.redirectToLeaveUrl(); return; } diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/end-meeting/end-meeting.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/meeting/end-meeting/end-meeting.component.ts index 309f0ac..2aea800 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/end-meeting/end-meeting.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/end-meeting/end-meeting.component.ts @@ -119,7 +119,7 @@ export class EndMeetingComponent implements OnInit { const redirectTo = this.navService.getLeaveRedirectURL(); if (redirectTo) { // Navigate to the specified redirect URL - await this.navService.redirectTo(redirectTo); + await this.navService.redirectToLeaveUrl(); return; } diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts index 42708e7..afeea69 100644 --- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts +++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts @@ -248,7 +248,7 @@ export class MeetingComponent implements OnInit { const redirectTo = this.navigationService.getLeaveRedirectURL(); if (redirectTo) { // Navigate to the specified redirect URL - await this.navigationService.redirectTo(redirectTo); + await this.navigationService.redirectToLeaveUrl(); return; } diff --git a/frontend/projects/shared-meet-components/src/lib/services/navigation.service.ts b/frontend/projects/shared-meet-components/src/lib/services/navigation.service.ts index de877bc..3a3f3df 100644 --- a/frontend/projects/shared-meet-components/src/lib/services/navigation.service.ts +++ b/frontend/projects/shared-meet-components/src/lib/services/navigation.service.ts @@ -1,4 +1,3 @@ -import { Location } from '@angular/common'; import { Injectable } from '@angular/core'; import { Params, Router, UrlTree } from '@angular/router'; import { ErrorReason } from '@lib/models'; @@ -12,7 +11,6 @@ export class NavigationService { constructor( private router: Router, - private location: Location, private sessionStorageService: SessionStorageService, private appDataService: AppDataService ) {} @@ -31,6 +29,31 @@ export class NavigationService { return this.leaveRedirectUrl; } + /** + * Redirects the user to the leave redirect URL if set and valid. + */ + async redirectToLeaveUrl() { + const url = this.getLeaveRedirectURL(); + if (!url) { + console.warn('No leave redirect URL set'); + return; + } + + const isExternalURL = /^https?:\/\//.test(url); + if (!isExternalURL) { + console.error('Leave redirect URL is not a valid external URL:', url); + return; + } + + const isEmbeddedMode = this.appDataService.isEmbeddedMode(); + if (isEmbeddedMode) { + // Change the top window location if in embedded mode + window.top!.location.href = url; + } else { + window.location.href = url; + } + } + /** * Navigates to a specific route * @@ -50,30 +73,16 @@ export class NavigationService { } /** - * Redirects to internal or external URLs + * Redirects to internal URL * * @param url - The URL to redirect to */ async redirectTo(url: string): Promise { - const isExternalURL = /^https?:\/\//.test(url); - if (isExternalURL) { - console.log('Redirecting to external URL:', url); - - if (this.appDataService.isEmbeddedMode()) { - // Change the top window location if in embedded mode - window.top!.location.href = url; - } else { - window.location.href = url; - } - } else { - console.log('Redirecting to internal route:', url); - - try { - let urlTree = this.router.parseUrl(url); - await this.router.navigateByUrl(urlTree, { replaceUrl: true }); - } catch (error) { - console.error('Error navigating to internal route:', error); - } + try { + let urlTree = this.router.parseUrl(url); + await this.router.navigateByUrl(urlTree, { replaceUrl: true }); + } catch (error) { + console.error('Error navigating to internal route:', error); } } diff --git a/frontend/projects/shared-meet-components/src/lib/services/theme.service.ts b/frontend/projects/shared-meet-components/src/lib/services/theme.service.ts index 2baca81..0292b0b 100644 --- a/frontend/projects/shared-meet-components/src/lib/services/theme.service.ts +++ b/frontend/projects/shared-meet-components/src/lib/services/theme.service.ts @@ -16,7 +16,10 @@ export class ThemeService { public readonly isDark = computed(() => this._currentTheme() === 'dark'); public readonly isLight = computed(() => this._currentTheme() === 'light'); - constructor(@Inject(DOCUMENT) private document: Document, protected ovComponentsThemeService: OpenViduThemeService) {} + constructor( + @Inject(DOCUMENT) private document: Document, + protected ovComponentsThemeService: OpenViduThemeService + ) {} /** * Initializes the theme based on: @@ -39,7 +42,8 @@ export class ThemeService { */ public toggleTheme(): void { const newTheme: Theme = this._currentTheme() === 'light' ? 'dark' : 'light'; - this.setTheme(newTheme); } + this.setTheme(newTheme); + } /** * Changes the current theme