Refactor template syntax to use @if directives for conditional rendering

- Updated panel.component.html to replace *ngIf with @if for chat, participants, background effects, settings, activities, and external panels.
- Modified participants-panel.component.html to use @if for local and remote participants rendering.
- Changed settings-panel.component.html to utilize @if for menu options based on visibility conditions.
- Refactored pre-join.component.html to implement @if for participant name input and error message display.
- Adjusted session.component.html to use @if for toolbar template rendering.
- Updated audio-devices.component.html and video-devices.component.html to replace *ngIf with @if for dropdown icons.
- Refactored stream.component.html to use @if for participant name and audio wave display.
- Modified toolbar-media-buttons.component.html and toolbar-panel-buttons.component.html to implement @if for button visibility.
- Updated toolbar.component.html to use @if for recording time display.
- Refactored videoconference.component.html to replace *ngIf with @if for pre-join and template rendering.
ov-components_modernization
Carlos Santos 2025-12-11 17:21:28 +01:00
parent 264db1facc
commit 53927b05a7
19 changed files with 606 additions and 506 deletions

View File

@ -1,30 +1,37 @@
<mat-toolbar class="header"> {{ title || ('ADMIN.DASHBOARD_TITLE' | translate) }} </mat-toolbar> <mat-toolbar class="header"> {{ title || ('ADMIN.DASHBOARD_TITLE' | translate) }} </mat-toolbar>
<div class="center-container"> <div class="center-container">
<div *ngIf="loading" class="outer"> @if (loading) {
<div class="outer">
<div class="middle"> <div class="middle">
<div class="inner"> <div class="inner">
<mat-spinner [diameter]="50"></mat-spinner> <mat-spinner [diameter]="50"></mat-spinner>
</div> </div>
</div> </div>
</div> </div>
}
<mat-card *ngIf="!loading" class="card-container" appearance="outlined"> @if (!loading) {
<mat-card class="card-container" appearance="outlined">
<mat-card-content> <mat-card-content>
<form [formGroup]="loginForm" (ngSubmit)="login()"> <form [formGroup]="loginForm" (ngSubmit)="login()">
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>{{ 'ADMIN.USERNAME' | translate }}</mat-label> <mat-label>{{ 'ADMIN.USERNAME' | translate }}</mat-label>
<input matInput formControlName="username" type="text" name="username" /> <input matInput formControlName="username" type="text" name="username" />
<mat-error *ngIf="loginForm.get('username')?.hasError('required')"> @if (loginForm.get('username')?.hasError('required')) {
<mat-error>
{{ 'ADMIN.USERNAME_REQUIRED' | translate }} {{ 'ADMIN.USERNAME_REQUIRED' | translate }}
</mat-error> </mat-error>
}
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>{{ 'ADMIN.PASSWORD' | translate }}</mat-label> <mat-label>{{ 'ADMIN.PASSWORD' | translate }}</mat-label>
<input matInput formControlName="password" type="password" name="password" autocomplete="current-password" /> <input matInput formControlName="password" type="password" name="password" autocomplete="current-password" />
<mat-error *ngIf="loginForm.get('password')?.hasError('required')"> @if (loginForm.get('password')?.hasError('required')) {
<mat-error>
{{ 'ADMIN.PASSWORD_REQUIRED' | translate }} {{ 'ADMIN.PASSWORD_REQUIRED' | translate }}
</mat-error> </mat-error>
}
</mat-form-field> </mat-form-field>
<div class="col-12 d-flex text-center"> <div class="col-12 d-flex text-center">
<button mat-flat-button disableRipple class="form-btn" type="submit"> <button mat-flat-button disableRipple class="form-btn" type="submit">
@ -34,4 +41,5 @@
</form> </form>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
}
</div> </div>

View File

@ -1,8 +1,8 @@
<div class="container" [ngClass]="{ withCaptions: captionsEnabled, withMargin: localParticipant.isMinimized }"> <div class="container" [ngClass]="{ withCaptions: captionsEnabled, withMargin: localParticipant.isMinimized }">
<div id="layout" class="layout" #layout> <div id="layout" class="layout" #layout>
@for (track of localParticipant.tracks; track track) {
<div <div
#localLayoutElement #localLayoutElement
*ngFor="let track of localParticipant.tracks; trackBy: trackParticipantElement"
[ngClass]="{ [ngClass]="{
local_participant: true, local_participant: true,
OV_root: !track.isAudioTrack && !track.isMinimized, OV_root: !track.isAudioTrack && !track.isMinimized,
@ -20,14 +20,15 @@
> >
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: track }"></ng-container> <ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: track }"></ng-container>
</div> </div>
}
<!-- Render additional layout elements injected via ovAdditionalLayoutElement --> <!-- Render additional layout elements injected via ovAdditionalLayoutElement -->
@if (layoutAdditionalElementsTemplate) { @if (layoutAdditionalElementsTemplate) {
<ng-container *ngTemplateOutlet="layoutAdditionalElementsTemplate"></ng-container> <ng-container *ngTemplateOutlet="layoutAdditionalElementsTemplate"></ng-container>
} }
@for (track of remoteParticipants | tracks; track track) {
<div <div
*ngFor="let track of remoteParticipants | tracks; trackBy: trackParticipantElement"
class="remote-participant" class="remote-participant"
[id]="'participant-' + track.participant.identity" [id]="'participant-' + track.participant.identity"
[ngClass]="{ [ngClass]="{
@ -40,6 +41,7 @@
> >
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: track }"></ng-container> <ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: track }"></ng-container>
</div> </div>
}
</div> </div>
<!-- <ov-captions *ngIf="captionsEnabled" class="OV_ignored"></ov-captions> --> <!-- <ov-captions *ngIf="captionsEnabled" class="OV_ignored"></ov-captions> -->

View File

@ -8,8 +8,8 @@
<div class="activities-body-container"> <div class="activities-body-container">
<mat-accordion [multi]="false"> <mat-accordion [multi]="false">
@if (showRecordingActivity) {
<ov-recording-activity <ov-recording-activity
*ngIf="showRecordingActivity"
id="recording-activity" id="recording-activity"
[expanded]="expandedPanel === 'recording'" [expanded]="expandedPanel === 'recording'"
(onRecordingStartRequested)="onRecordingStartRequested.emit($event)" (onRecordingStartRequested)="onRecordingStartRequested.emit($event)"
@ -20,13 +20,15 @@
(onViewRecordingClicked)="onViewRecordingClicked.emit($event)" (onViewRecordingClicked)="onViewRecordingClicked.emit($event)"
(onViewRecordingsClicked)="onViewRecordingsClicked.emit()" (onViewRecordingsClicked)="onViewRecordingsClicked.emit()"
></ov-recording-activity> ></ov-recording-activity>
}
@if (showBroadcastingActivity) {
<ov-broadcasting-activity <ov-broadcasting-activity
*ngIf="showBroadcastingActivity"
id="broadcasting-activity" id="broadcasting-activity"
[expanded]="expandedPanel === 'broadcasting'" [expanded]="expandedPanel === 'broadcasting'"
(onBroadcastingStartRequested)="onBroadcastingStartRequested.emit($event)" (onBroadcastingStartRequested)="onBroadcastingStartRequested.emit($event)"
(onBroadcastingStopRequested)="onBroadcastingStopRequested.emit($event)" (onBroadcastingStopRequested)="onBroadcastingStopRequested.emit($event)"
></ov-broadcasting-activity> ></ov-broadcasting-activity>
}
</mat-accordion> </mat-accordion>
</div> </div>
</div> </div>

View File

@ -73,7 +73,7 @@
/> />
<button <button
mat-icon-button mat-icon-button
*ngIf="broadcastingStatus !== broadcastingStatusEnum.STARTED" @if (broadcastingStatus !== broadcastingStatusEnum.STARTED) {
id="broadcasting-btn" id="broadcasting-btn"
[disabled]=" [disabled]="
!broadcastUrl || !broadcastUrl ||
@ -82,19 +82,21 @@
" "
(click)="startBroadcasting()" (click)="startBroadcasting()"
matTooltip="{{ 'PANEL.STREAMING.START' | translate }}" matTooltip="{{ 'PANEL.STREAMING.START' | translate }}"
}
> >
<mat-icon>play_circle</mat-icon> <mat-icon>play_circle</mat-icon>
</button> </button>
@if (broadcastingStatus === broadcastingStatusEnum.STARTED) {
<button <button
mat-icon-button mat-icon-button
*ngIf="broadcastingStatus === broadcastingStatusEnum.STARTED"
id="stop-broadcasting-btn" id="stop-broadcasting-btn"
(click)="stopBroadcasting()" (click)="stopBroadcasting()"
matTooltip="{{ 'PANEL.STREAMING.STOP' | translate }}" matTooltip="{{ 'PANEL.STREAMING.STOP' | translate }}"
> >
<mat-icon>stop_circle</mat-icon> <mat-icon>stop_circle</mat-icon>
</button> </button>
}
</div> </div>
<div> <div>

View File

@ -11,17 +11,23 @@
</div> </div>
<div class="messages-container" #chatScroll> <div class="messages-container" #chatScroll>
<div *ngFor="let data of messageList" class="message" [ngClass]="data.isLocal ? 'right' : 'left'"> @for (data of messageList; track data) {
<div class="message" [ngClass]="data.isLocal ? 'right' : 'left'">
<div class="msg-detail"> <div class="msg-detail">
<div class="participant-name-container"> <div class="participant-name-container">
<p *ngIf="data.isLocal">{{ 'PANEL.CHAT.YOU' | translate }}</p> @if (data.isLocal) {
<p *ngIf="!data.isLocal">{{ data.participantName }}</p> <p>{{ 'PANEL.CHAT.YOU' | translate }}</p>
}
@if (!data.isLocal) {
<p>{{ data.participantName }}</p>
}
</div> </div>
<div class="chat-message"> <div class="chat-message">
<p [innerHTML]="data.message | linkify"></p> <p [innerHTML]="data.message | linkify"></p>
</div> </div>
</div> </div>
</div> </div>
}
</div> </div>
<div class="input-container"> <div class="input-container">

View File

@ -1,30 +1,29 @@
<!-- CHAT panel --> <!-- CHAT panel -->
<ng-container *ngIf="isChatPanelOpened"> @if (isChatPanelOpened) {
<ng-container *ngTemplateOutlet="chatPanelTemplate"></ng-container> <ng-container *ngTemplateOutlet="chatPanelTemplate"></ng-container>
</ng-container> }
<!-- PARTICIPANTS panel --> <!-- PARTICIPANTS panel -->
<ng-container *ngIf="isParticipantsPanelOpened"> @if (isParticipantsPanelOpened) {
<ng-container *ngTemplateOutlet="participantsPanelTemplate"></ng-container> <ng-container *ngTemplateOutlet="participantsPanelTemplate"></ng-container>
</ng-container> }
<!-- Background effects panel --> <!-- Background effects panel -->
<ng-container *ngIf="isBackgroundEffectsPanelOpened"> @if (isBackgroundEffectsPanelOpened) {
<ng-container *ngTemplateOutlet="backgroundEffectsPanelTemplate"></ng-container> <ng-container *ngTemplateOutlet="backgroundEffectsPanelTemplate"></ng-container>
</ng-container> }
<!-- Settings panel --> <!-- Settings panel -->
<ng-container *ngIf="isSettingsPanelOpened"> @if (isSettingsPanelOpened) {
<ng-container *ngTemplateOutlet="settingsPanelTemplate"></ng-container> <ng-container *ngTemplateOutlet="settingsPanelTemplate"></ng-container>
</ng-container> }
<!-- Activities panel --> <!-- Activities panel -->
<ng-container *ngIf="isActivitiesPanelOpened"> @if (isActivitiesPanelOpened) {
<ng-container *ngTemplateOutlet="activitiesPanelTemplate"></ng-container> <ng-container *ngTemplateOutlet="activitiesPanelTemplate"></ng-container>
</ng-container> }
<!-- External additional panels --> <!-- External additional panels -->
<ng-container *ngIf="additionalPanelsTemplate && isExternalPanelOpened"> @if (additionalPanelsTemplate && isExternalPanelOpened) {
<ng-container *ngTemplateOutlet="additionalPanelsTemplate"></ng-container> <ng-container *ngTemplateOutlet="additionalPanelsTemplate"></ng-container>
</ng-container> }

View File

@ -7,17 +7,25 @@
</div> </div>
<div class="scrollable"> <div class="scrollable">
<div class="local-participant-container" *ngIf="localParticipant"> @if (localParticipant) {
<div class="local-participant-container">
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: localParticipant }"></ng-container> <ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: localParticipant }"></ng-container>
<mat-divider *ngIf="true"></mat-divider> @if (true) {
<mat-divider></mat-divider>
}
</div> </div>
}
<ng-container *ngTemplateOutlet="participantPanelAfterLocalParticipantTemplate"></ng-container> <ng-container *ngTemplateOutlet="participantPanelAfterLocalParticipantTemplate"></ng-container>
<div class="remote-participants-container" id="remote-participants-container" *ngIf="remoteParticipants.length > 0"> @if (remoteParticipants.length > 0) {
<div *ngFor="let participant of this.remoteParticipants" id="remote-participant-item"> <div class="remote-participants-container" id="remote-participants-container">
@for (participant of this.remoteParticipants; track participant.identity) {
<div id="remote-participant-item">
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: participant }"></ng-container> <ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: participant }"></ng-container>
</div> </div>
}
</div> </div>
}
</div> </div>
</div> </div>

View File

@ -24,10 +24,12 @@
[matTooltipDisabled]="!shouldHideMenuText" [matTooltipDisabled]="!shouldHideMenuText"
> >
<mat-icon matListItemIcon>manage_accounts</mat-icon> <mat-icon matListItemIcon>manage_accounts</mat-icon>
<div *ngIf="!shouldHideMenuText" class="option-text">{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div> @if (!shouldHideMenuText) {
<div class="option-text">{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div>
}
</mat-list-option> </mat-list-option>
@if (showCameraButton) {
<mat-list-option <mat-list-option
*ngIf="showCameraButton"
class="option" class="option"
id="video-opt" id="video-opt"
[selected]="selectedOption === settingsOptions.VIDEO" [selected]="selectedOption === settingsOptions.VIDEO"
@ -36,10 +38,13 @@
[matTooltipDisabled]="!shouldHideMenuText" [matTooltipDisabled]="!shouldHideMenuText"
> >
<mat-icon matListItemIcon>videocam</mat-icon> <mat-icon matListItemIcon>videocam</mat-icon>
<div *ngIf="!shouldHideMenuText" class="option-text">{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div> @if (!shouldHideMenuText) {
<div class="option-text">{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div>
}
</mat-list-option> </mat-list-option>
}
@if (showMicrophoneButton) {
<mat-list-option <mat-list-option
*ngIf="showMicrophoneButton"
class="option" class="option"
id="audio-opt" id="audio-opt"
[selected]="selectedOption === settingsOptions.AUDIO" [selected]="selectedOption === settingsOptions.AUDIO"
@ -48,8 +53,11 @@
[matTooltipDisabled]="!shouldHideMenuText" [matTooltipDisabled]="!shouldHideMenuText"
> >
<mat-icon matListItemIcon>mic</mat-icon> <mat-icon matListItemIcon>mic</mat-icon>
<div *ngIf="!shouldHideMenuText" class="option-text">{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div> @if (!shouldHideMenuText) {
<div class="option-text">{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div>
}
</mat-list-option> </mat-list-option>
}
<!-- <mat-list-option <!-- <mat-list-option
*ngIf="showCaptions" *ngIf="showCaptions"
class="option" class="option"
@ -66,7 +74,8 @@
</div> </div>
<div class="item-content" [class.full-width]="isVerticalLayout"> <div class="item-content" [class.full-width]="isVerticalLayout">
<div *ngIf="selectedOption === settingsOptions.GENERAL" class="general-settings"> @if (selectedOption === settingsOptions.GENERAL) {
<div class="general-settings">
<div class="nickname-section"> <div class="nickname-section">
<mat-label class="input-label">{{ 'PREJOIN.NICKNAME' | translate }}</mat-label> <mat-label class="input-label">{{ 'PREJOIN.NICKNAME' | translate }}</mat-label>
<div class="nickname-input-container"> <div class="nickname-input-container">
@ -100,18 +109,23 @@
</div> </div>
} }
</div> </div>
<div *ngIf="showCameraButton && selectedOption === settingsOptions.VIDEO" class="video-settings"> }
@if (showCameraButton && selectedOption === settingsOptions.VIDEO) {
<div class="video-settings">
<ov-video-devices-select <ov-video-devices-select
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)" (onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
(onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)" (onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)"
></ov-video-devices-select> ></ov-video-devices-select>
</div> </div>
<div *ngIf="showMicrophoneButton && selectedOption === settingsOptions.AUDIO" class="audio-settings"> }
@if (showMicrophoneButton && selectedOption === settingsOptions.AUDIO) {
<div class="audio-settings">
<ov-audio-devices-select <ov-audio-devices-select
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)" (onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)" (onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
></ov-audio-devices-select> ></ov-audio-devices-select>
</div> </div>
}
<!-- <div *ngIf="selectedOption === settingsOptions.CAPTIONS && showCaptions" class="captions-settings"> <!-- <div *ngIf="selectedOption === settingsOptions.CAPTIONS && showCaptions" class="captions-settings">
<ov-captions-settings></ov-captions-settings> <ov-captions-settings></ov-captions-settings>
</div> --> </div> -->

View File

@ -41,7 +41,8 @@
<!-- Video Controls Overlay --> <!-- Video Controls Overlay -->
<div class="video-overlay"> <div class="video-overlay">
<div class="device-controls"> <div class="device-controls">
<div class="control-group" *ngIf="showCameraButton"> @if (showCameraButton) {
<div class="control-group">
<ov-video-devices-select <ov-video-devices-select
[compact]="true" [compact]="true"
(onVideoDeviceChanged)="videoDeviceChanged($event)" (onVideoDeviceChanged)="videoDeviceChanged($event)"
@ -51,8 +52,10 @@
> >
</ov-video-devices-select> </ov-video-devices-select>
</div> </div>
}
<div class="control-group" *ngIf="showMicrophoneButton"> @if (showMicrophoneButton) {
<div class="control-group">
<ov-audio-devices-select <ov-audio-devices-select
[compact]="true" [compact]="true"
(onAudioDeviceChanged)="audioDeviceChanged($event)" (onAudioDeviceChanged)="audioDeviceChanged($event)"
@ -62,6 +65,7 @@
> >
</ov-audio-devices-select> </ov-audio-devices-select>
</div> </div>
}
</div> </div>
<!-- Virtual Background Button --> <!-- Virtual Background Button -->
@ -93,7 +97,8 @@
<!-- Configuration Section --> <!-- Configuration Section -->
<div class="configuration-section"> <div class="configuration-section">
<!-- Participant Name Input --> <!-- Participant Name Input -->
<div class="participant-name-container input-section" *ngIf="showParticipantName"> @if (showParticipantName) {
<div class="participant-name-container input-section">
<ov-participant-name-input <ov-participant-name-input
[isPrejoinPage]="true" [isPrejoinPage]="true"
[error]="!!_error" [error]="!!_error"
@ -103,12 +108,15 @@
> >
</ov-participant-name-input> </ov-participant-name-input>
</div> </div>
}
<!-- Error Message --> <!-- Error Message -->
<div *ngIf="!!_error" class="error-message" id="token-error"> @if (!!_error) {
<div class="error-message" id="token-error">
<mat-icon class="error-icon">error_outline</mat-icon> <mat-icon class="error-icon">error_outline</mat-icon>
<span class="error-text">{{ _error }}</span> <span class="error-text">{{ _error }}</span>
</div> </div>
}
<!-- Join Button --> <!-- Join Button -->
<div class="join-section"> <div class="join-section">

View File

@ -29,8 +29,10 @@
</mat-sidenav-content> </mat-sidenav-content>
</mat-sidenav-container> </mat-sidenav-container>
<div id="footer-container" *ngIf="toolbarTemplate"> @if (toolbarTemplate) {
<div id="footer-container">
<ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container> <ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container>
</div> </div>
}
</div> </div>
} }

View File

@ -56,7 +56,9 @@
> >
<mat-icon class="device-icon">mic</mat-icon> <mat-icon class="device-icon">mic</mat-icon>
<span class="selected-device-name">{{ microphoneSelected?.label || 'No microphone selected' }}</span> <span class="selected-device-name">{{ microphoneSelected?.label || 'No microphone selected' }}</span>
<mat-icon class="dropdown-icon" *ngIf="microphones.length > 1">expand_more</mat-icon> @if (microphones.length > 1) {
<mat-icon class="dropdown-icon">expand_more</mat-icon>
}
</button> </button>
</div> </div>
} @else { } @else {
@ -91,7 +93,9 @@
(click)="onMicrophoneSelected({ value: microphone })" (click)="onMicrophoneSelected({ value: microphone })"
[class.selected]="microphone.device === microphoneSelected.device" [class.selected]="microphone.device === microphoneSelected.device"
> >
<mat-icon *ngIf="microphone.device === microphoneSelected.device">check</mat-icon> @if (microphone.device === microphoneSelected.device) {
<mat-icon>check</mat-icon>
}
<span>{{ microphone.label }}</span> <span>{{ microphone.label }}</span>
</button> </button>
} }

View File

@ -55,7 +55,9 @@
> >
<mat-icon class="device-icon">videocam</mat-icon> <mat-icon class="device-icon">videocam</mat-icon>
<span class="selected-device-name">{{ cameraSelected?.label || 'No camera selected' }}</span> <span class="selected-device-name">{{ cameraSelected?.label || 'No camera selected' }}</span>
<mat-icon class="dropdown-icon" *ngIf="cameras.length > 1">expand_more</mat-icon> @if (cameras.length > 1) {
<mat-icon class="dropdown-icon">expand_more</mat-icon>
}
</button> </button>
</div> </div>
} @else { } @else {
@ -87,7 +89,9 @@
(click)="onCameraSelected({ value: camera })" (click)="onCameraSelected({ value: camera })"
[class.selected]="camera.device === cameraSelected?.device" [class.selected]="camera.device === cameraSelected?.device"
> >
<mat-icon *ngIf="camera.device === cameraSelected?.device" class="check-icon">check</mat-icon> @if (camera.device === cameraSelected?.device) {
<mat-icon class="check-icon">check</mat-icon>
}
<span>{{ camera.label }}</span> <span>{{ camera.label }}</span>
</button> </button>
} }

View File

@ -1,5 +1,5 @@
@if (_track.participant) {
<div <div
*ngIf="_track.participant"
[ngClass]="{ [ngClass]="{
OV_stream: !_track.isAudioTrack || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks), OV_stream: !_track.isAudioTrack || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks),
'no-size': !showVideo, 'no-size': !showVideo,
@ -11,21 +11,26 @@
(mousemove)="mouseHover($event)" (mousemove)="mouseHover($event)"
#streamContainer #streamContainer
> >
@if ((!isMinimal && showParticipantName && !_track.isAudioTrack) || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks)) {
<div <div
*ngIf="!isMinimal && showParticipantName && !_track.isAudioTrack || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks)"
id="participant-name-container" id="participant-name-container"
class="participant-name" class="participant-name"
[class.fullscreen]="isFullscreen" [class.fullscreen]="isFullscreen"
> >
<div class="participant-name-container"> <div class="participant-name-container">
<span id="participant-name">{{ _track.participant.name }}</span> <span id="participant-name">{{ _track.participant.name }}</span>
<span *ngIf="_track.isScreenTrack" id="participant-name">_SCREEN</span> @if (_track.isScreenTrack) {
<span id="participant-name">_SCREEN</span>
}
</div> </div>
</div> </div>
}
<div *ngIf="!isMinimal && showAudioDetection && _track.participant.isSpeaking && _track.isCameraTrack" id="audio-wave-container"> @if (!isMinimal && showAudioDetection && _track.participant.isSpeaking && _track.isCameraTrack) {
<div id="audio-wave-container">
<ov-audio-wave></ov-audio-wave> <ov-audio-wave></ov-audio-wave>
</div> </div>
}
<ov-media-element <ov-media-element
[track]="_track.track" [track]="_track.track"
@ -39,12 +44,20 @@
@if (!_track.participant.hasEncryptionError) { @if (!_track.participant.hasEncryptionError) {
<div class="status-icons"> <div class="status-icons">
<mat-icon id="status-mic" fontIcon="mic_off" *ngIf="!_track.participant?.isMicrophoneEnabled"></mat-icon> @if (!_track.participant?.isMicrophoneEnabled) {
<mat-icon id="status-muted-forcibly" fontIcon="volume_off" *ngIf="_track.isMutedForcibly"></mat-icon> <mat-icon id="status-mic" fontIcon="mic_off"></mat-icon>
<mat-icon id="status-pinned" fontIcon="push_pin" *ngIf="_track.isPinned"></mat-icon> }
@if (_track.isMutedForcibly) {
<mat-icon id="status-muted-forcibly" fontIcon="volume_off"></mat-icon>
}
@if (_track.isPinned) {
<mat-icon id="status-pinned" fontIcon="push_pin"></mat-icon>
}
</div> </div>
}
<div class="stream-video-controls" *ngIf="!isMinimal && showVideoControls && mouseHovering"> @if (!isMinimal && showVideoControls && mouseHovering && !_track.participant.hasEncryptionError) {
<div class="stream-video-controls">
<div class="flex-container"> <div class="flex-container">
<button <button
mat-icon-button mat-icon-button
@ -52,32 +65,48 @@
(click)="toggleVideoPinned()" (click)="toggleVideoPinned()"
[matTooltip]="_track.isPinned ? ('STREAM.UNPIN' | translate) : ('STREAM.PIN' | translate)" [matTooltip]="_track.isPinned ? ('STREAM.UNPIN' | translate) : ('STREAM.PIN' | translate)"
> >
<mat-icon *ngIf="_track.isPinned" fontSet="material-symbols-outlined" fontIcon="keep">keep_off</mat-icon> @if (_track.isPinned) {
<mat-icon *ngIf="!_track.isPinned" id="status-pinned" fontIcon="push_pin"></mat-icon> <mat-icon fontSet="material-symbols-outlined" fontIcon="keep">keep_off</mat-icon>
}
@if (!_track.isPinned) {
<mat-icon id="status-pinned" fontIcon="push_pin"></mat-icon>
}
</button> </button>
@if (!_track.participant.isLocal) {
<button <button
*ngIf="!_track.participant.isLocal"
mat-icon-button mat-icon-button
id="silence-btn" id="silence-btn"
(click)="toggleMuteForcibly()" (click)="toggleMuteForcibly()"
[class.muted-btn]="_track.isMutedForcibly" [class.muted-btn]="_track.isMutedForcibly"
[matTooltip]="_track.isMutedForcibly ? ('STREAM.UNMUTE_SOUND' | translate) : ('STREAM.MUTE_SOUND' | translate)" [matTooltip]="_track.isMutedForcibly ? ('STREAM.UNMUTE_SOUND' | translate) : ('STREAM.MUTE_SOUND' | translate)"
> >
<mat-icon *ngIf="_track.isMutedForcibly">volume_off</mat-icon> @if (_track.isMutedForcibly) {
<mat-icon *ngIf="!_track.isMutedForcibly">volume_up</mat-icon> <mat-icon>volume_off</mat-icon>
}
@if (!_track.isMutedForcibly) {
<mat-icon>volume_up</mat-icon>
}
</button> </button>
}
@if (_track.participant.isLocal) {
<button <button
*ngIf="_track.participant.isLocal"
mat-icon-button mat-icon-button
id="minimize-btn" id="minimize-btn"
[disabled]="_track.isPinned" [disabled]="_track.isPinned"
(click)="toggleMinimize()" (click)="toggleMinimize()"
[matTooltip]="_track.isMinimized ? ('STREAM.MAXIMIZE' | translate) : ('STREAM.MINIMIZE' | translate)" [matTooltip]="_track.isMinimized ? ('STREAM.MAXIMIZE' | translate) : ('STREAM.MINIMIZE' | translate)"
> >
<mat-icon *ngIf="_track.isMinimized">open_in_full</mat-icon> @if (_track.isMinimized) {
<mat-icon *ngIf="!_track.isMinimized">close_fullscreen</mat-icon> <mat-icon>open_in_full</mat-icon>
}
@if (!_track.isMinimized) {
<mat-icon>close_fullscreen</mat-icon>
}
</button> </button>
}
</div> </div>
</div> </div>
} }
</div> </div>
}

View File

@ -11,6 +11,7 @@ import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.servic
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { LayoutService } from '../../services/layout/layout.service'; import { LayoutService } from '../../services/layout/layout.service';
import { ParticipantService } from '../../services/participant/participant.service'; import { ParticipantService } from '../../services/participant/participant.service';
import { TranslatePipe } from '../../pipes/translate.pipe';
/** /**
* The **StreamComponent** is hosted inside of the {@link LayoutComponent}. * The **StreamComponent** is hosted inside of the {@link LayoutComponent}.
@ -21,7 +22,7 @@ import { ParticipantService } from '../../services/participant/participant.servi
templateUrl: './stream.component.html', templateUrl: './stream.component.html',
styleUrls: ['./stream.component.scss'], styleUrls: ['./stream.component.scss'],
standalone: true, standalone: true,
imports: [CommonModule, AppMaterialModule, AudioWaveComponent, MediaElementComponent] imports: [CommonModule, AppMaterialModule, AudioWaveComponent, MediaElementComponent, TranslatePipe]
}) })
export class StreamComponent implements OnInit, OnDestroy { export class StreamComponent implements OnInit, OnDestroy {
/** /**
@ -193,21 +194,18 @@ export class StreamComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((value: boolean) => { .subscribe((value: boolean) => {
this.showParticipantName = value; this.showParticipantName = value;
// this.cd.markForCheck();
}); });
this.libService.displayAudioDetection$ this.libService.displayAudioDetection$
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((value: boolean) => { .subscribe((value: boolean) => {
this.showAudioDetection = value; this.showAudioDetection = value;
// this.cd.markForCheck();
}); });
this.libService.streamVideoControls$ this.libService.streamVideoControls$
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((value: boolean) => { .subscribe((value: boolean) => {
this.showVideoControls = value; this.showVideoControls = value;
// this.cd.markForCheck();
}); });
} }
} }

View File

@ -159,17 +159,22 @@
} }
<!-- Captions button --> <!-- Captions button -->
<!-- <button @if (!isMinimal && showCaptionsButton) {
*ngIf="!isMinimal && showCaptionsButton" <button
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
mat-menu-item mat-menu-item
id="captions-btn" id="captions-btn"
(click)="onCaptionsToggle()" (click)="onCaptionsToggle()"
> >
<mat-icon>closed_caption</mat-icon> <mat-icon>closed_caption</mat-icon>
<span *ngIf="captionsEnabled">{{ 'TOOLBAR.DISABLE_CAPTIONS' | translate }}</span> @if (captionsEnabled) {
<span *ngIf="!captionsEnabled">{{ 'TOOLBAR.ENABLE_CAPTIONS' | translate }}</span> <span>{{ 'TOOLBAR.DISABLE_CAPTIONS' | translate }}</span>
</button> --> }
@if (!captionsEnabled) {
<span>{{ 'TOOLBAR.ENABLE_CAPTIONS' | translate }}</span>
}
</button>
}
<!-- Additional buttons injection inside menu (mobile only) --> <!-- Additional buttons injection inside menu (mobile only) -->
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition === 'afterMenu') { @if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition === 'afterMenu') {

View File

@ -1,6 +1,7 @@
<!-- Responsive container: show individual buttons on larger screens, collapsed menu on smaller screens --> <!-- Responsive container: show individual buttons on larger screens, collapsed menu on smaller screens -->
<ng-container *ngIf="shouldShowCollapsed; else uncollapsedButtons"> @if (shouldShowCollapsed) {
<div class="collapsed-menu-container" *ngIf="visibleButtonsCount > 0"> @if (visibleButtonsCount > 0) {
<div class="collapsed-menu-container">
<button <button
mat-icon-button mat-icon-button
class="fab-menu-trigger" class="fab-menu-trigger"
@ -16,34 +17,36 @@
<!-- Vertical panels menu --> <!-- Vertical panels menu -->
<mat-menu #panelsMenu="matMenu" class="panels-menu"> <mat-menu #panelsMenu="matMenu" class="panels-menu">
<!-- Activities menu item --> <!-- Activities menu item -->
@if (!isMinimal && showActivitiesPanelButton) {
<button <button
mat-menu-item mat-menu-item
class="panel-menu-item" class="panel-menu-item"
*ngIf="!isMinimal && showActivitiesPanelButton"
(click)="onToggleActivities()" (click)="onToggleActivities()"
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
[class.active-menu-item]="isActivitiesOpened" [class.active-menu-item]="isActivitiesOpened"
> >
<mat-icon>category</mat-icon> <mat-icon>category</mat-icon>
</button> </button>
}
<!-- Participants menu item --> <!-- Participants menu item -->
@if (!isMinimal && showParticipantsPanelButton) {
<button <button
mat-menu-item mat-menu-item
class="panel-menu-item" class="panel-menu-item"
*ngIf="!isMinimal && showParticipantsPanelButton"
(click)="onToggleParticipants()" (click)="onToggleParticipants()"
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
[class.active-menu-item]="isParticipantsOpened" [class.active-menu-item]="isParticipantsOpened"
> >
<mat-icon>people</mat-icon> <mat-icon>people</mat-icon>
</button> </button>
}
<!-- Chat menu item --> <!-- Chat menu item -->
@if (!isMinimal && showChatPanelButton) {
<button <button
mat-menu-item mat-menu-item
class="panel-menu-item" class="panel-menu-item"
*ngIf="!isMinimal && showChatPanelButton"
(click)="onToggleChat()" (click)="onToggleChat()"
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
[class.active-menu-item]="isChatOpened" [class.active-menu-item]="isChatOpened"
@ -59,23 +62,22 @@
chat chat
</mat-icon> </mat-icon>
</button> </button>
}
<!-- External additional panel buttons in menu --> <!-- External additional panel buttons in menu -->
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate"> @if (toolbarAdditionalPanelButtonsTemplate) {
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container> <ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
</ng-container> }
</mat-menu> </mat-menu>
</div> </div>
</ng-container> }
} @else {
<!-- Collapsed menu template for small screens -->
<ng-template #uncollapsedButtons>
<!-- Default activities button --> <!-- Default activities button -->
@if (!isMinimal && showActivitiesPanelButton) {
<button <button
mat-icon-button mat-icon-button
id="activities-panel-btn" id="activities-panel-btn"
class="panel-button" class="panel-button"
*ngIf="!isMinimal && showActivitiesPanelButton"
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}" matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
(click)="onToggleActivities()" (click)="onToggleActivities()"
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
@ -83,13 +85,14 @@
> >
<mat-icon>category</mat-icon> <mat-icon>category</mat-icon>
</button> </button>
}
<!-- Default participants button --> <!-- Default participants button -->
@if (!isMinimal && showParticipantsPanelButton) {
<button <button
mat-icon-button mat-icon-button
class="panel-button" class="panel-button"
id="participants-panel-btn" id="participants-panel-btn"
*ngIf="!isMinimal && showParticipantsPanelButton"
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}" matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
(click)="onToggleParticipants()" (click)="onToggleParticipants()"
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
@ -97,13 +100,14 @@
> >
<mat-icon>people</mat-icon> <mat-icon>people</mat-icon>
</button> </button>
}
<!-- Default chat button --> <!-- Default chat button -->
@if (!isMinimal && showChatPanelButton) {
<button <button
mat-icon-button mat-icon-button
class="panel-button" class="panel-button"
id="chat-panel-btn" id="chat-panel-btn"
*ngIf="!isMinimal && showChatPanelButton"
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}" matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
(click)="onToggleChat()" (click)="onToggleChat()"
[disabled]="isConnectionLost" [disabled]="isConnectionLost"
@ -120,9 +124,13 @@
chat chat
</mat-icon> </mat-icon>
</button> </button>
}
<!-- External additional panel buttons --> <!-- External additional panel buttons -->
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate"> @if (toolbarAdditionalPanelButtonsTemplate) {
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container> <ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
</ng-container> }
</ng-template> }
<!-- Collapsed menu template for small screens -->
<ng-template #uncollapsedButtons>

View File

@ -21,7 +21,9 @@
<div id="recording-tag" class="recording-tag" (click)="openRecordingActivityPanel()"> <div id="recording-tag" class="recording-tag" (click)="openRecordingActivityPanel()">
<mat-icon class="blink">radio_button_checked</mat-icon> <mat-icon class="blink">radio_button_checked</mat-icon>
<span class="blink">REC</span> <span class="blink">REC</span>
<span *ngIf="recordingTime"> | {{ recordingTime | date: 'H:mm:ss' }}</span> @if (recordingTime) {
<span> | {{ recordingTime | date: 'H:mm:ss' }}</span>
}
</div> </div>
} }

View File

@ -8,10 +8,9 @@
} @else if (componentState.showPrejoin) { } @else if (componentState.showPrejoin) {
<!-- Prejoin --> <!-- Prejoin -->
<div [@inOutAnimation] id="pre-join-container"> <div [@inOutAnimation] id="pre-join-container">
<ng-container *ngIf="openviduAngularPreJoinTemplate; else defaultPreJoin"> @if (openviduAngularPreJoinTemplate) {
<ng-container *ngTemplateOutlet="openviduAngularPreJoinTemplate"></ng-container> <ng-container *ngTemplateOutlet="openviduAngularPreJoinTemplate"></ng-container>
</ng-container> } @else {
<ng-template #defaultPreJoin>
<ov-pre-join <ov-pre-join
[error]="componentState.error?.tokenError" [error]="componentState.error?.tokenError"
(onReadyToJoin)="_onReadyToJoin()" (onReadyToJoin)="_onReadyToJoin()"
@ -21,7 +20,7 @@
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)" (onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
(onLangChanged)="onLangChanged.emit($event)" (onLangChanged)="onLangChanged.emit($event)"
></ov-pre-join> ></ov-pre-join>
</ng-template> }
</div> </div>
} @else if (componentState.error?.hasError) { } @else if (componentState.error?.hasError) {
<!-- Error --> <!-- Error -->
@ -44,21 +43,21 @@
(onParticipantLeft)="_onParticipantLeft($event)" (onParticipantLeft)="_onParticipantLeft($event)"
> >
<ng-template #toolbar> <ng-template #toolbar>
<ng-container *ngIf="openviduAngularToolbarTemplate"> @if (openviduAngularToolbarTemplate) {
<ng-container *ngTemplateOutlet="openviduAngularToolbarTemplate"></ng-container> <ng-container *ngTemplateOutlet="openviduAngularToolbarTemplate"></ng-container>
</ng-container> }
</ng-template> </ng-template>
<ng-template #panel> <ng-template #panel>
<ng-container *ngIf="openviduAngularPanelTemplate"> @if (openviduAngularPanelTemplate) {
<ng-container *ngTemplateOutlet="openviduAngularPanelTemplate"></ng-container> <ng-container *ngTemplateOutlet="openviduAngularPanelTemplate"></ng-container>
</ng-container> }
</ng-template> </ng-template>
<ng-template #layout> <ng-template #layout>
<ng-container *ngIf="openviduAngularLayoutTemplate"> @if (openviduAngularLayoutTemplate) {
<ng-container *ngTemplateOutlet="openviduAngularLayoutTemplate"></ng-container> <ng-container *ngTemplateOutlet="openviduAngularLayoutTemplate"></ng-container>
</ng-container> }
</ng-template> </ng-template>
</ov-session> </ov-session>
</div> </div>