ov-components: add disconnected state handling and translations for multiple languages

master
Carlos Santos 2026-01-15 18:03:09 +01:00
parent 4a5afac382
commit 8dd058fd50
12 changed files with 71 additions and 43 deletions

View File

@ -30,6 +30,14 @@
<mat-icon class="error-icon">error</mat-icon> <mat-icon class="error-icon">error</mat-icon>
<span>{{ componentState.error?.message }}</span> <span>{{ componentState.error?.message }}</span>
</div> </div>
} @else if (componentState.state === 'DISCONNECTED') {
<!-- Disconnected state -->
<div id="spinner">
<mat-icon class="info-icon">check_circle</mat-icon>
<span>{{ 'ROOM.DISCONNECTED' | translate }}</span>
<p class="subtitle">{{ 'ROOM.DISCONNECTED_SUBTITLE' | translate }}</p>
</div>
} @else if (componentState.isRoomReady) { } @else if (componentState.isRoomReady) {
<!-- VideoConference --> <!-- VideoConference -->

View File

@ -1,8 +1,6 @@
import { animate, style, transition, trigger } from '@angular/animations'; import { animate, style, transition, trigger } from '@angular/animations';
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
ContentChild, ContentChild,
EventEmitter, EventEmitter,
@ -11,7 +9,16 @@ import {
TemplateRef, TemplateRef,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { Room } from 'livekit-client';
import { Subject, filter, skip, take, takeUntil } from 'rxjs'; import { Subject, filter, skip, take, takeUntil } from 'rxjs';
import {
LayoutAdditionalElementsDirective,
LeaveButtonDirective,
ParticipantPanelAfterLocalParticipantDirective,
PreJoinDirective,
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from '../../directives/template/internals.directive';
import { import {
ActivitiesPanelDirective, ActivitiesPanelDirective,
AdditionalPanelsDirective, AdditionalPanelsDirective,
@ -26,29 +33,17 @@ import {
ToolbarAdditionalPanelButtonsDirective, ToolbarAdditionalPanelButtonsDirective,
ToolbarDirective ToolbarDirective
} from '../../directives/template/openvidu-components-angular.directive'; } from '../../directives/template/openvidu-components-angular.directive';
import { ILogger } from '../../models/logger.model'; import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../models/broadcasting.model';
import { VideoconferenceState, VideoconferenceStateInfo } from '../../models/videoconference-state.model';
import { ActionService } from '../../services/action/action.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { DeviceService } from '../../services/device/device.service';
import { LoggerService } from '../../services/logger/logger.service';
import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { StorageService } from '../../services/storage/storage.service';
import {
TemplateManagerService,
TemplateConfiguration,
ExternalDirectives,
DefaultTemplates
} from '../../services/template/template-manager.service';
import { Room } from 'livekit-client';
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
import { CustomDevice } from '../../models/device.model'; import { CustomDevice } from '../../models/device.model';
import { LangOption } from '../../models/lang.model';
import { ILogger } from '../../models/logger.model';
import { import {
ActivitiesPanelStatusEvent, ActivitiesPanelStatusEvent,
ChatPanelStatusEvent, ChatPanelStatusEvent,
ParticipantsPanelStatusEvent, ParticipantsPanelStatusEvent,
SettingsPanelStatusEvent SettingsPanelStatusEvent
} from '../../models/panel.model'; } from '../../models/panel.model';
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
import { import {
RecordingDeleteRequestedEvent, RecordingDeleteRequestedEvent,
RecordingDownloadClickedEvent, RecordingDownloadClickedEvent,
@ -56,18 +51,21 @@ import {
RecordingStartRequestedEvent, RecordingStartRequestedEvent,
RecordingStopRequestedEvent RecordingStopRequestedEvent
} from '../../models/recording.model'; } from '../../models/recording.model';
import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../models/broadcasting.model'; import { VideoconferenceState, VideoconferenceStateInfo } from '../../models/videoconference-state.model';
import { LangOption } from '../../models/lang.model'; import { ActionService } from '../../services/action/action.service';
import { import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
LayoutAdditionalElementsDirective, import { DeviceService } from '../../services/device/device.service';
ParticipantPanelAfterLocalParticipantDirective,
PreJoinDirective,
LeaveButtonDirective,
SettingsPanelGeneralAdditionalElementsDirective,
ToolbarMoreOptionsAdditionalMenuItemsDirective
} from '../../directives/template/internals.directive';
import { OpenViduThemeService } from '../../services/theme/theme.service';
import { E2eeService } from '../../services/e2ee/e2ee.service'; import { E2eeService } from '../../services/e2ee/e2ee.service';
import { LoggerService } from '../../services/logger/logger.service';
import { OpenViduService } from '../../services/openvidu/openvidu.service';
import { StorageService } from '../../services/storage/storage.service';
import {
DefaultTemplates,
ExternalDirectives,
TemplateConfiguration,
TemplateManagerService
} from '../../services/template/template-manager.service';
import { OpenViduThemeService } from '../../services/theme/theme.service';
/** /**
* The **VideoconferenceComponent** is the parent of all OpenVidu components. * The **VideoconferenceComponent** is the parent of all OpenVidu components.
@ -982,14 +980,16 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
* @internal * @internal
*/ */
_onParticipantLeft(event: ParticipantLeftEvent) { _onParticipantLeft(event: ParticipantLeftEvent) {
// Reset to disconnected state to allow prejoin to show again if needed this.onParticipantLeft.emit(event);
// Reset to disconnected state
// Set showPrejoin to false to prevent prejoin from showing and creating tracks
// This avoids the race condition where tracks are created before navigation
this.updateComponentState({ this.updateComponentState({
state: VideoconferenceState.DISCONNECTED, state: VideoconferenceState.DISCONNECTED,
isRoomReady: false, isRoomReady: false,
showPrejoin: this.libService.showPrejoin() showPrejoin: false
}); });
this.onParticipantLeft.emit(event);
} }
private subscribeToVideconferenceDirectives() { private subscribeToVideconferenceDirectives() {

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "正在加入房间...", "JOINING": "正在加入房间...",
"LANDSCAPE_WARNING": "请将设备旋转到纵向以获得最佳体验" "LANDSCAPE_WARNING": "请将设备旋转到纵向以获得最佳体验",
"DISCONNECTED": "您已离开房间",
"DISCONNECTED_SUBTITLE": "会话已结束"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "设置你的绰号", "NICKNAME_SECTION": "设置你的绰号",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Raum beitreten...", "JOINING": "Raum beitreten...",
"LANDSCAPE_WARNING": "Bitte drehen Sie Ihr Gerät für eine bessere Erfahrung in den Hochformatmodus" "LANDSCAPE_WARNING": "Bitte drehen Sie Ihr Gerät für eine bessere Erfahrung in den Hochformatmodus",
"DISCONNECTED": "Sie haben den Raum verlassen",
"DISCONNECTED_SUBTITLE": "Die Sitzung wurde beendet"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Legen Sie Ihren Spitznamen fest", "NICKNAME_SECTION": "Legen Sie Ihren Spitznamen fest",

View File

@ -35,7 +35,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Joining room...", "JOINING": "Joining room...",
"LANDSCAPE_WARNING": "Please rotate your device to portrait for a better experience" "LANDSCAPE_WARNING": "Please rotate your device to portrait for a better experience",
"DISCONNECTED": "You have left the room",
"DISCONNECTED_SUBTITLE": "The session has ended"
}, },
"TOOLBAR": { "TOOLBAR": {
"MUTE_AUDIO": "Mute your audio", "MUTE_AUDIO": "Mute your audio",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Uniéndose a la sala...", "JOINING": "Uniéndose a la sala...",
"LANDSCAPE_WARNING": "Por favor, gira tu dispositivo a modo retrato para una mejor experiencia" "LANDSCAPE_WARNING": "Por favor, gira tu dispositivo a modo retrato para una mejor experiencia",
"DISCONNECTED": "Has salido de la sala",
"DISCONNECTED_SUBTITLE": "La sesión ha finalizado"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Elige tu nombre", "NICKNAME_SECTION": "Elige tu nombre",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Rejoindre la salle...", "JOINING": "Rejoindre la salle...",
"LANDSCAPE_WARNING": "Veuillez faire pivoter votre appareil en mode portrait pour une meilleure expérience" "LANDSCAPE_WARNING": "Veuillez faire pivoter votre appareil en mode portrait pour une meilleure expérience",
"DISCONNECTED": "Vous avez quitté la salle",
"DISCONNECTED_SUBTITLE": "La session est terminée"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Définir votre surnom", "NICKNAME_SECTION": "Définir votre surnom",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "कक्ष में शामिल हो रहा है...", "JOINING": "कक्ष में शामिल हो रहा है...",
"LANDSCAPE_WARNING": "कृपया बेहतर अनुभव के लिए अपने डिवाइस को पोर्ट्रेट मोड में घुमाएँ" "LANDSCAPE_WARNING": "कृपया बेहतर अनुभव के लिए अपने डिवाइस को पोर्ट्रेट मोड में घुमाएं",
"DISCONNECTED": "आपने कमरा छोड़ दिया है",
"DISCONNECTED_SUBTITLE": "सत्र समाप्त हो गया है"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "अपना निकनेम सेट करें", "NICKNAME_SECTION": "अपना निकनेम सेट करें",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Unendosi alla stanza...", "JOINING": "Unendosi alla stanza...",
"LANDSCAPE_WARNING": "Per una migliore esperienza, ruota il dispositivo in modalità verticale" "LANDSCAPE_WARNING": "Per una migliore esperienza, ruota il dispositivo in modalità verticale",
"DISCONNECTED": "Hai lasciato la stanza",
"DISCONNECTED_SUBTITLE": "La sessione è terminata"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Imposta il tuo soprannome", "NICKNAME_SECTION": "Imposta il tuo soprannome",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "ルームに参加しています...", "JOINING": "ルームに参加しています...",
"LANDSCAPE_WARNING": "より良い体験のために、デバイスを縦向きに回転させてください" "LANDSCAPE_WARNING": "より良い体験のために、デバイスを縦向きに回転させてください",
"DISCONNECTED": "ルームから退出しました",
"DISCONNECTED_SUBTITLE": "セッションが終了しました"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "ニックネームを設定してください", "NICKNAME_SECTION": "ニックネームを設定してください",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "De ruimte betreden...", "JOINING": "De ruimte betreden...",
"LANDSCAPE_WARNING": "Draai uw apparaat voor een betere ervaring naar portretmodus" "LANDSCAPE_WARNING": "Draai uw apparaat voor een betere ervaring naar portretmodus",
"DISCONNECTED": "U heeft de kamer verlaten",
"DISCONNECTED_SUBTITLE": "De sessie is beëindigd"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Stel je bijnaam in", "NICKNAME_SECTION": "Stel je bijnaam in",

View File

@ -23,7 +23,9 @@
}, },
"ROOM": { "ROOM": {
"JOINING": "Entrando na sala...", "JOINING": "Entrando na sala...",
"LANDSCAPE_WARNING": "Por favor, gire seu dispositivo para o modo retrato para uma melhor experiência" "LANDSCAPE_WARNING": "Por favor, gire seu dispositivo para o modo retrato para uma melhor experiência",
"DISCONNECTED": "Você saiu da sala",
"DISCONNECTED_SUBTITLE": "A sessão foi encerrada"
}, },
"PREJOIN": { "PREJOIN": {
"NICKNAME_SECTION": "Defina seu apelido", "NICKNAME_SECTION": "Defina seu apelido",