mirror of https://github.com/OpenVidu/openvidu.git
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
parent
264db1facc
commit
53927b05a7
File diff suppressed because one or more lines are too long
|
|
@ -1,37 +1,45 @@
|
||||||
<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="middle">
|
<div class="outer">
|
||||||
<div class="inner">
|
<div class="middle">
|
||||||
<mat-spinner [diameter]="50"></mat-spinner>
|
<div class="inner">
|
||||||
|
<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-content>
|
<mat-card class="card-container" appearance="outlined">
|
||||||
<form [formGroup]="loginForm" (ngSubmit)="login()">
|
<mat-card-content>
|
||||||
<mat-form-field appearance="outline" class="form-field">
|
<form [formGroup]="loginForm" (ngSubmit)="login()">
|
||||||
<mat-label>{{ 'ADMIN.USERNAME' | translate }}</mat-label>
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
<input matInput formControlName="username" type="text" name="username" />
|
<mat-label>{{ 'ADMIN.USERNAME' | translate }}</mat-label>
|
||||||
<mat-error *ngIf="loginForm.get('username')?.hasError('required')">
|
<input matInput formControlName="username" type="text" name="username" />
|
||||||
{{ 'ADMIN.USERNAME_REQUIRED' | translate }}
|
@if (loginForm.get('username')?.hasError('required')) {
|
||||||
</mat-error>
|
<mat-error>
|
||||||
</mat-form-field>
|
{{ 'ADMIN.USERNAME_REQUIRED' | translate }}
|
||||||
<mat-form-field appearance="outline" class="form-field">
|
</mat-error>
|
||||||
<mat-label>{{ 'ADMIN.PASSWORD' | translate }}</mat-label>
|
}
|
||||||
<input matInput formControlName="password" type="password" name="password" autocomplete="current-password" />
|
</mat-form-field>
|
||||||
<mat-error *ngIf="loginForm.get('password')?.hasError('required')">
|
<mat-form-field appearance="outline" class="form-field">
|
||||||
{{ 'ADMIN.PASSWORD_REQUIRED' | translate }}
|
<mat-label>{{ 'ADMIN.PASSWORD' | translate }}</mat-label>
|
||||||
</mat-error>
|
<input matInput formControlName="password" type="password" name="password" autocomplete="current-password" />
|
||||||
</mat-form-field>
|
@if (loginForm.get('password')?.hasError('required')) {
|
||||||
<div class="col-12 d-flex text-center">
|
<mat-error>
|
||||||
<button mat-flat-button disableRipple class="form-btn" type="submit">
|
{{ 'ADMIN.PASSWORD_REQUIRED' | translate }}
|
||||||
{{ 'ADMIN.LOGIN' | translate }}
|
</mat-error>
|
||||||
</button>
|
}
|
||||||
</div>
|
</mat-form-field>
|
||||||
</form>
|
<div class="col-12 d-flex text-center">
|
||||||
</mat-card-content>
|
<button mat-flat-button disableRipple class="form-btn" type="submit">
|
||||||
</mat-card>
|
{{ 'ADMIN.LOGIN' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,47 @@
|
||||||
<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>
|
||||||
<div
|
@for (track of localParticipant.tracks; track track) {
|
||||||
#localLayoutElement
|
<div
|
||||||
*ngFor="let track of localParticipant.tracks; trackBy: trackParticipantElement"
|
#localLayoutElement
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
local_participant: true,
|
local_participant: true,
|
||||||
OV_root: !track.isAudioTrack && !track.isMinimized,
|
OV_root: !track.isAudioTrack && !track.isMinimized,
|
||||||
OV_publisher: !track.isAudioTrack && !track.isMinimized,
|
OV_publisher: !track.isAudioTrack && !track.isMinimized,
|
||||||
OV_minimized: track.isMinimized,
|
OV_minimized: track.isMinimized,
|
||||||
OV_big: track.isPinned,
|
OV_big: track.isPinned,
|
||||||
OV_ignored: track.isAudioTrack && !track.participant.onlyHasAudioTracks,
|
OV_ignored: track.isAudioTrack && !track.participant.onlyHasAudioTracks,
|
||||||
OV_screen: track.isScreenTrack
|
OV_screen: track.isScreenTrack
|
||||||
}"
|
}"
|
||||||
[id]="'participant-' + track.participant.identity"
|
[id]="'participant-' + track.participant.identity"
|
||||||
cdkDrag
|
cdkDrag
|
||||||
cdkDragBoundary=".layout"
|
cdkDragBoundary=".layout"
|
||||||
[cdkDragDisabled]="!track.isMinimized"
|
[cdkDragDisabled]="!track.isMinimized"
|
||||||
[cdkDragFreeDragPosition]="!track.isMinimized ? { x: 0, y: 0 } : null"
|
[cdkDragFreeDragPosition]="!track.isMinimized ? { x: 0, y: 0 } : null"
|
||||||
>
|
>
|
||||||
<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>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div
|
@for (track of remoteParticipants | tracks; track track) {
|
||||||
*ngFor="let track of remoteParticipants | tracks; trackBy: trackParticipantElement"
|
<div
|
||||||
class="remote-participant"
|
class="remote-participant"
|
||||||
[id]="'participant-' + track.participant.identity"
|
[id]="'participant-' + track.participant.identity"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
OV_root: !track.isAudioTrack,
|
OV_root: !track.isAudioTrack,
|
||||||
OV_publisher: !track.isAudioTrack,
|
OV_publisher: !track.isAudioTrack,
|
||||||
OV_big: track.isPinned,
|
OV_big: track.isPinned,
|
||||||
OV_ignored: track.isAudioTrack && !track.participant.onlyHasAudioTracks,
|
OV_ignored: track.isAudioTrack && !track.participant.onlyHasAudioTracks,
|
||||||
OV_screen: track.isScreenTrack
|
OV_screen: track.isScreenTrack
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<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> -->
|
||||||
|
|
|
||||||
|
|
@ -8,25 +8,27 @@
|
||||||
|
|
||||||
<div class="activities-body-container">
|
<div class="activities-body-container">
|
||||||
<mat-accordion [multi]="false">
|
<mat-accordion [multi]="false">
|
||||||
<ov-recording-activity
|
@if (showRecordingActivity) {
|
||||||
*ngIf="showRecordingActivity"
|
<ov-recording-activity
|
||||||
id="recording-activity"
|
id="recording-activity"
|
||||||
[expanded]="expandedPanel === 'recording'"
|
[expanded]="expandedPanel === 'recording'"
|
||||||
(onRecordingStartRequested)="onRecordingStartRequested.emit($event)"
|
(onRecordingStartRequested)="onRecordingStartRequested.emit($event)"
|
||||||
(onRecordingStopRequested)="onRecordingStopRequested.emit($event)"
|
(onRecordingStopRequested)="onRecordingStopRequested.emit($event)"
|
||||||
(onRecordingDeleteRequested)="onRecordingDeleteRequested.emit($event)"
|
(onRecordingDeleteRequested)="onRecordingDeleteRequested.emit($event)"
|
||||||
(onRecordingDownloadClicked)="onRecordingDownloadClicked.emit($event)"
|
(onRecordingDownloadClicked)="onRecordingDownloadClicked.emit($event)"
|
||||||
(onRecordingPlayClicked)="onRecordingPlayClicked.emit($event)"
|
(onRecordingPlayClicked)="onRecordingPlayClicked.emit($event)"
|
||||||
(onViewRecordingClicked)="onViewRecordingClicked.emit($event)"
|
(onViewRecordingClicked)="onViewRecordingClicked.emit($event)"
|
||||||
(onViewRecordingsClicked)="onViewRecordingsClicked.emit()"
|
(onViewRecordingsClicked)="onViewRecordingsClicked.emit()"
|
||||||
></ov-recording-activity>
|
></ov-recording-activity>
|
||||||
<ov-broadcasting-activity
|
}
|
||||||
*ngIf="showBroadcastingActivity"
|
@if (showBroadcastingActivity) {
|
||||||
id="broadcasting-activity"
|
<ov-broadcasting-activity
|
||||||
[expanded]="expandedPanel === 'broadcasting'"
|
id="broadcasting-activity"
|
||||||
(onBroadcastingStartRequested)="onBroadcastingStartRequested.emit($event)"
|
[expanded]="expandedPanel === 'broadcasting'"
|
||||||
(onBroadcastingStopRequested)="onBroadcastingStopRequested.emit($event)"
|
(onBroadcastingStartRequested)="onBroadcastingStartRequested.emit($event)"
|
||||||
></ov-broadcasting-activity>
|
(onBroadcastingStopRequested)="onBroadcastingStopRequested.emit($event)"
|
||||||
|
></ov-broadcasting-activity>
|
||||||
|
}
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -73,28 +73,30 @@
|
||||||
/>
|
/>
|
||||||
<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 ||
|
||||||
broadcastingStatus === broadcastingStatusEnum.STARTING ||
|
broadcastingStatus === broadcastingStatusEnum.STARTING ||
|
||||||
broadcastingStatus === broadcastingStatusEnum.STOPPING
|
broadcastingStatus === broadcastingStatusEnum.STOPPING
|
||||||
"
|
"
|
||||||
(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>
|
||||||
|
|
||||||
<button
|
@if (broadcastingStatus === broadcastingStatusEnum.STARTED) {
|
||||||
mat-icon-button
|
<button
|
||||||
*ngIf="broadcastingStatus === broadcastingStatusEnum.STARTED"
|
mat-icon-button
|
||||||
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>
|
||||||
|
|
|
||||||
|
|
@ -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="msg-detail">
|
<div class="message" [ngClass]="data.isLocal ? 'right' : 'left'">
|
||||||
<div class="participant-name-container">
|
<div class="msg-detail">
|
||||||
<p *ngIf="data.isLocal">{{ 'PANEL.CHAT.YOU' | translate }}</p>
|
<div class="participant-name-container">
|
||||||
<p *ngIf="!data.isLocal">{{ data.participantName }}</p>
|
@if (data.isLocal) {
|
||||||
</div>
|
<p>{{ 'PANEL.CHAT.YOU' | translate }}</p>
|
||||||
<div class="chat-message">
|
}
|
||||||
<p [innerHTML]="data.message | linkify"></p>
|
@if (!data.isLocal) {
|
||||||
|
<p>{{ data.participantName }}</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="chat-message">
|
||||||
|
<p [innerHTML]="data.message | linkify"></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,25 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="scrollable">
|
<div class="scrollable">
|
||||||
<div class="local-participant-container" *ngIf="localParticipant">
|
@if (localParticipant) {
|
||||||
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: localParticipant }"></ng-container>
|
<div class="local-participant-container">
|
||||||
<mat-divider *ngIf="true"></mat-divider>
|
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: localParticipant }"></ng-container>
|
||||||
</div>
|
@if (true) {
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
}
|
||||||
|
</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">
|
||||||
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: participant }"></ng-container>
|
@for (participant of this.remoteParticipants; track participant.identity) {
|
||||||
|
<div id="remote-participant-item">
|
||||||
|
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: participant }"></ng-container>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,32 +24,40 @@
|
||||||
[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) {
|
||||||
</mat-list-option>
|
<div class="option-text">{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div>
|
||||||
<mat-list-option
|
}
|
||||||
*ngIf="showCameraButton"
|
|
||||||
class="option"
|
|
||||||
id="video-opt"
|
|
||||||
[selected]="selectedOption === settingsOptions.VIDEO"
|
|
||||||
[value]="settingsOptions.VIDEO"
|
|
||||||
matTooltip="{{ shouldHideMenuText ? ('PANEL.SETTINGS.VIDEO' | translate) : '' }}"
|
|
||||||
[matTooltipDisabled]="!shouldHideMenuText"
|
|
||||||
>
|
|
||||||
<mat-icon matListItemIcon>videocam</mat-icon>
|
|
||||||
<div *ngIf="!shouldHideMenuText" class="option-text">{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div>
|
|
||||||
</mat-list-option>
|
|
||||||
<mat-list-option
|
|
||||||
*ngIf="showMicrophoneButton"
|
|
||||||
class="option"
|
|
||||||
id="audio-opt"
|
|
||||||
[selected]="selectedOption === settingsOptions.AUDIO"
|
|
||||||
[value]="settingsOptions.AUDIO"
|
|
||||||
matTooltip="{{ shouldHideMenuText ? ('PANEL.SETTINGS.AUDIO' | translate) : '' }}"
|
|
||||||
[matTooltipDisabled]="!shouldHideMenuText"
|
|
||||||
>
|
|
||||||
<mat-icon matListItemIcon>mic</mat-icon>
|
|
||||||
<div *ngIf="!shouldHideMenuText" class="option-text">{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div>
|
|
||||||
</mat-list-option>
|
</mat-list-option>
|
||||||
|
@if (showCameraButton) {
|
||||||
|
<mat-list-option
|
||||||
|
class="option"
|
||||||
|
id="video-opt"
|
||||||
|
[selected]="selectedOption === settingsOptions.VIDEO"
|
||||||
|
[value]="settingsOptions.VIDEO"
|
||||||
|
matTooltip="{{ shouldHideMenuText ? ('PANEL.SETTINGS.VIDEO' | translate) : '' }}"
|
||||||
|
[matTooltipDisabled]="!shouldHideMenuText"
|
||||||
|
>
|
||||||
|
<mat-icon matListItemIcon>videocam</mat-icon>
|
||||||
|
@if (!shouldHideMenuText) {
|
||||||
|
<div class="option-text">{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div>
|
||||||
|
}
|
||||||
|
</mat-list-option>
|
||||||
|
}
|
||||||
|
@if (showMicrophoneButton) {
|
||||||
|
<mat-list-option
|
||||||
|
class="option"
|
||||||
|
id="audio-opt"
|
||||||
|
[selected]="selectedOption === settingsOptions.AUDIO"
|
||||||
|
[value]="settingsOptions.AUDIO"
|
||||||
|
matTooltip="{{ shouldHideMenuText ? ('PANEL.SETTINGS.AUDIO' | translate) : '' }}"
|
||||||
|
[matTooltipDisabled]="!shouldHideMenuText"
|
||||||
|
>
|
||||||
|
<mat-icon matListItemIcon>mic</mat-icon>
|
||||||
|
@if (!shouldHideMenuText) {
|
||||||
|
<div class="option-text">{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div>
|
||||||
|
}
|
||||||
|
</mat-list-option>
|
||||||
|
}
|
||||||
<!-- <mat-list-option
|
<!-- <mat-list-option
|
||||||
*ngIf="showCaptions"
|
*ngIf="showCaptions"
|
||||||
class="option"
|
class="option"
|
||||||
|
|
@ -66,52 +74,58 @@
|
||||||
</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="nickname-section">
|
<div class="general-settings">
|
||||||
<mat-label class="input-label">{{ 'PREJOIN.NICKNAME' | translate }}</mat-label>
|
<div class="nickname-section">
|
||||||
<div class="nickname-input-container">
|
<mat-label class="input-label">{{ 'PREJOIN.NICKNAME' | translate }}</mat-label>
|
||||||
<ov-participant-name-input></ov-participant-name-input>
|
<div class="nickname-input-container">
|
||||||
|
<ov-participant-name-input></ov-participant-name-input>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="language-section">
|
||||||
<div class="language-section">
|
|
||||||
<mat-list>
|
|
||||||
<mat-list-item class="lang-selector">
|
|
||||||
<mat-icon matListItemIcon>translate</mat-icon>
|
|
||||||
<div matListItemTitle>{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}</div>
|
|
||||||
<ov-lang-selector matListItemMeta (onLangChanged)="onLangChanged.emit($event)"></ov-lang-selector>
|
|
||||||
</mat-list-item>
|
|
||||||
</mat-list>
|
|
||||||
</div>
|
|
||||||
@if (showThemeSelector) {
|
|
||||||
<div class="theme-section">
|
|
||||||
<mat-list>
|
<mat-list>
|
||||||
<mat-list-item class="theme-selector">
|
<mat-list-item class="lang-selector">
|
||||||
<mat-icon matListItemIcon class="material-symbols-outlined">routine</mat-icon>
|
<mat-icon matListItemIcon>translate</mat-icon>
|
||||||
<div matListItemTitle>{{ 'PANEL.SETTINGS.THEME' | translate }}</div>
|
<div matListItemTitle>{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}</div>
|
||||||
<ov-theme-selector matListItemMeta></ov-theme-selector>
|
<ov-lang-selector matListItemMeta (onLangChanged)="onLangChanged.emit($event)"></ov-lang-selector>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
</div>
|
</div>
|
||||||
}
|
@if (showThemeSelector) {
|
||||||
<!-- Additional elements injected via directive -->
|
<div class="theme-section">
|
||||||
@if (generalAdditionalElementsTemplate) {
|
<mat-list>
|
||||||
<div class="additional-elements-section">
|
<mat-list-item class="theme-selector">
|
||||||
<ng-container *ngTemplateOutlet="generalAdditionalElementsTemplate"></ng-container>
|
<mat-icon matListItemIcon class="material-symbols-outlined">routine</mat-icon>
|
||||||
</div>
|
<div matListItemTitle>{{ 'PANEL.SETTINGS.THEME' | translate }}</div>
|
||||||
}
|
<ov-theme-selector matListItemMeta></ov-theme-selector>
|
||||||
</div>
|
</mat-list-item>
|
||||||
<div *ngIf="showCameraButton && selectedOption === settingsOptions.VIDEO" class="video-settings">
|
</mat-list>
|
||||||
<ov-video-devices-select
|
</div>
|
||||||
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
|
}
|
||||||
(onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)"
|
<!-- Additional elements injected via directive -->
|
||||||
></ov-video-devices-select>
|
@if (generalAdditionalElementsTemplate) {
|
||||||
</div>
|
<div class="additional-elements-section">
|
||||||
<div *ngIf="showMicrophoneButton && selectedOption === settingsOptions.AUDIO" class="audio-settings">
|
<ng-container *ngTemplateOutlet="generalAdditionalElementsTemplate"></ng-container>
|
||||||
<ov-audio-devices-select
|
</div>
|
||||||
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
|
}
|
||||||
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
|
</div>
|
||||||
></ov-audio-devices-select>
|
}
|
||||||
</div>
|
@if (showCameraButton && selectedOption === settingsOptions.VIDEO) {
|
||||||
|
<div class="video-settings">
|
||||||
|
<ov-video-devices-select
|
||||||
|
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
|
||||||
|
(onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)"
|
||||||
|
></ov-video-devices-select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if (showMicrophoneButton && selectedOption === settingsOptions.AUDIO) {
|
||||||
|
<div class="audio-settings">
|
||||||
|
<ov-audio-devices-select
|
||||||
|
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
|
||||||
|
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
|
||||||
|
></ov-audio-devices-select>
|
||||||
|
</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> -->
|
||||||
|
|
|
||||||
|
|
@ -41,27 +41,31 @@
|
||||||
<!-- 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) {
|
||||||
<ov-video-devices-select
|
<div class="control-group">
|
||||||
[compact]="true"
|
<ov-video-devices-select
|
||||||
(onVideoDeviceChanged)="videoDeviceChanged($event)"
|
[compact]="true"
|
||||||
(onVideoEnabledChanged)="videoEnabledChanged($event)"
|
(onVideoDeviceChanged)="videoDeviceChanged($event)"
|
||||||
(onVideoDevicesLoaded)="onVideoDevicesLoaded($event)"
|
(onVideoEnabledChanged)="videoEnabledChanged($event)"
|
||||||
class="device-selector"
|
(onVideoDevicesLoaded)="onVideoDevicesLoaded($event)"
|
||||||
>
|
class="device-selector"
|
||||||
</ov-video-devices-select>
|
>
|
||||||
</div>
|
</ov-video-devices-select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="control-group" *ngIf="showMicrophoneButton">
|
@if (showMicrophoneButton) {
|
||||||
<ov-audio-devices-select
|
<div class="control-group">
|
||||||
[compact]="true"
|
<ov-audio-devices-select
|
||||||
(onAudioDeviceChanged)="audioDeviceChanged($event)"
|
[compact]="true"
|
||||||
(onAudioEnabledChanged)="audioEnabledChanged($event)"
|
(onAudioDeviceChanged)="audioDeviceChanged($event)"
|
||||||
(onDeviceSelectorClicked)="onDeviceSelectorClicked()"
|
(onAudioEnabledChanged)="audioEnabledChanged($event)"
|
||||||
class="device-selector"
|
(onDeviceSelectorClicked)="onDeviceSelectorClicked()"
|
||||||
>
|
class="device-selector"
|
||||||
</ov-audio-devices-select>
|
>
|
||||||
</div>
|
</ov-audio-devices-select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Virtual Background Button -->
|
<!-- Virtual Background Button -->
|
||||||
|
|
@ -93,22 +97,26 @@
|
||||||
<!-- 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) {
|
||||||
<ov-participant-name-input
|
<div class="participant-name-container input-section">
|
||||||
[isPrejoinPage]="true"
|
<ov-participant-name-input
|
||||||
[error]="!!_error"
|
[isPrejoinPage]="true"
|
||||||
(onNameUpdated)="onParticipantNameChanged($event)"
|
[error]="!!_error"
|
||||||
(onEnterPressed)="onEnterPressed()"
|
(onNameUpdated)="onParticipantNameChanged($event)"
|
||||||
class="name-input"
|
(onEnterPressed)="onEnterPressed()"
|
||||||
>
|
class="name-input"
|
||||||
</ov-participant-name-input>
|
>
|
||||||
</div>
|
</ov-participant-name-input>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Error Message -->
|
<!-- Error Message -->
|
||||||
<div *ngIf="!!_error" class="error-message" id="token-error">
|
@if (!!_error) {
|
||||||
<mat-icon class="error-icon">error_outline</mat-icon>
|
<div class="error-message" id="token-error">
|
||||||
<span class="error-text">{{ _error }}</span>
|
<mat-icon class="error-icon">error_outline</mat-icon>
|
||||||
</div>
|
<span class="error-text">{{ _error }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Join Button -->
|
<!-- Join Button -->
|
||||||
<div class="join-section">
|
<div class="join-section">
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,10 @@
|
||||||
</mat-sidenav-content>
|
</mat-sidenav-content>
|
||||||
</mat-sidenav-container>
|
</mat-sidenav-container>
|
||||||
|
|
||||||
<div id="footer-container" *ngIf="toolbarTemplate">
|
@if (toolbarTemplate) {
|
||||||
<ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container>
|
<div id="footer-container">
|
||||||
</div>
|
<ng-container *ngTemplateOutlet="toolbarTemplate"></ng-container>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,83 +1,112 @@
|
||||||
<div
|
@if (_track.participant) {
|
||||||
*ngIf="_track.participant"
|
|
||||||
[ngClass]="{
|
|
||||||
OV_stream: !_track.isAudioTrack || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks),
|
|
||||||
'no-size': !showVideo,
|
|
||||||
local: _track.participant.isLocal,
|
|
||||||
remote: !_track.participant.isLocal,
|
|
||||||
speaking: !isMinimal && showAudioDetection && _track.participant.isSpeaking && _track.isCameraTrack
|
|
||||||
}"
|
|
||||||
[id]="'stream-' + _track.source + '-' + _track.trackSid"
|
|
||||||
(mousemove)="mouseHover($event)"
|
|
||||||
#streamContainer
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
*ngIf="!isMinimal && showParticipantName && !_track.isAudioTrack || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks)"
|
[ngClass]="{
|
||||||
id="participant-name-container"
|
OV_stream: !_track.isAudioTrack || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks),
|
||||||
class="participant-name"
|
'no-size': !showVideo,
|
||||||
[class.fullscreen]="isFullscreen"
|
local: _track.participant.isLocal,
|
||||||
|
remote: !_track.participant.isLocal,
|
||||||
|
speaking: !isMinimal && showAudioDetection && _track.participant.isSpeaking && _track.isCameraTrack
|
||||||
|
}"
|
||||||
|
[id]="'stream-' + _track.source + '-' + _track.trackSid"
|
||||||
|
(mousemove)="mouseHover($event)"
|
||||||
|
#streamContainer
|
||||||
>
|
>
|
||||||
<div class="participant-name-container">
|
@if ((!isMinimal && showParticipantName && !_track.isAudioTrack) || (_track.isAudioTrack && _track.participant.onlyHasAudioTracks)) {
|
||||||
<span id="participant-name">{{ _track.participant.name }}</span>
|
<div
|
||||||
<span *ngIf="_track.isScreenTrack" id="participant-name">_SCREEN</span>
|
id="participant-name-container"
|
||||||
</div>
|
class="participant-name"
|
||||||
</div>
|
[class.fullscreen]="isFullscreen"
|
||||||
|
>
|
||||||
<div *ngIf="!isMinimal && showAudioDetection && _track.participant.isSpeaking && _track.isCameraTrack" id="audio-wave-container">
|
<div class="participant-name-container">
|
||||||
<ov-audio-wave></ov-audio-wave>
|
<span id="participant-name">{{ _track.participant.name }}</span>
|
||||||
</div>
|
@if (_track.isScreenTrack) {
|
||||||
|
<span id="participant-name">_SCREEN</span>
|
||||||
<ov-media-element
|
}
|
||||||
[track]="_track.track"
|
</div>
|
||||||
[showAvatar]="_track.participant.onlyHasAudioTracks || (_track.isCameraTrack && !_track.participant.isCameraEnabled)"
|
|
||||||
[avatarColor]="_track.participant.colorProfile"
|
|
||||||
[avatarName]="_track.participant.name"
|
|
||||||
[muted]="_track.isMutedForcibly"
|
|
||||||
[isLocal]="_track.participant.isLocal"
|
|
||||||
[hasEncryptionError]="_track.participant.hasEncryptionError"
|
|
||||||
></ov-media-element>
|
|
||||||
|
|
||||||
@if (!_track.participant.hasEncryptionError) {
|
|
||||||
<div class="status-icons">
|
|
||||||
<mat-icon id="status-mic" fontIcon="mic_off" *ngIf="!_track.participant?.isMicrophoneEnabled"></mat-icon>
|
|
||||||
<mat-icon id="status-muted-forcibly" fontIcon="volume_off" *ngIf="_track.isMutedForcibly"></mat-icon>
|
|
||||||
<mat-icon id="status-pinned" fontIcon="push_pin" *ngIf="_track.isPinned"></mat-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stream-video-controls" *ngIf="!isMinimal && showVideoControls && mouseHovering">
|
|
||||||
<div class="flex-container">
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
id="pin-btn"
|
|
||||||
(click)="toggleVideoPinned()"
|
|
||||||
[matTooltip]="_track.isPinned ? ('STREAM.UNPIN' | translate) : ('STREAM.PIN' | translate)"
|
|
||||||
>
|
|
||||||
<mat-icon *ngIf="_track.isPinned" fontSet="material-symbols-outlined" fontIcon="keep">keep_off</mat-icon>
|
|
||||||
<mat-icon *ngIf="!_track.isPinned" id="status-pinned" fontIcon="push_pin"></mat-icon>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
*ngIf="!_track.participant.isLocal"
|
|
||||||
mat-icon-button
|
|
||||||
id="silence-btn"
|
|
||||||
(click)="toggleMuteForcibly()"
|
|
||||||
[class.muted-btn]="_track.isMutedForcibly"
|
|
||||||
[matTooltip]="_track.isMutedForcibly ? ('STREAM.UNMUTE_SOUND' | translate) : ('STREAM.MUTE_SOUND' | translate)"
|
|
||||||
>
|
|
||||||
<mat-icon *ngIf="_track.isMutedForcibly">volume_off</mat-icon>
|
|
||||||
<mat-icon *ngIf="!_track.isMutedForcibly">volume_up</mat-icon>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
*ngIf="_track.participant.isLocal"
|
|
||||||
mat-icon-button
|
|
||||||
id="minimize-btn"
|
|
||||||
[disabled]="_track.isPinned"
|
|
||||||
(click)="toggleMinimize()"
|
|
||||||
[matTooltip]="_track.isMinimized ? ('STREAM.MAXIMIZE' | translate) : ('STREAM.MINIMIZE' | translate)"
|
|
||||||
>
|
|
||||||
<mat-icon *ngIf="_track.isMinimized">open_in_full</mat-icon>
|
|
||||||
<mat-icon *ngIf="!_track.isMinimized">close_fullscreen</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
|
||||||
</div>
|
@if (!isMinimal && showAudioDetection && _track.participant.isSpeaking && _track.isCameraTrack) {
|
||||||
|
<div id="audio-wave-container">
|
||||||
|
<ov-audio-wave></ov-audio-wave>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<ov-media-element
|
||||||
|
[track]="_track.track"
|
||||||
|
[showAvatar]="_track.participant.onlyHasAudioTracks || (_track.isCameraTrack && !_track.participant.isCameraEnabled)"
|
||||||
|
[avatarColor]="_track.participant.colorProfile"
|
||||||
|
[avatarName]="_track.participant.name"
|
||||||
|
[muted]="_track.isMutedForcibly"
|
||||||
|
[isLocal]="_track.participant.isLocal"
|
||||||
|
[hasEncryptionError]="_track.participant.hasEncryptionError"
|
||||||
|
></ov-media-element>
|
||||||
|
|
||||||
|
@if (!_track.participant.hasEncryptionError) {
|
||||||
|
<div class="status-icons">
|
||||||
|
@if (!_track.participant?.isMicrophoneEnabled) {
|
||||||
|
<mat-icon id="status-mic" fontIcon="mic_off"></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>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!isMinimal && showVideoControls && mouseHovering && !_track.participant.hasEncryptionError) {
|
||||||
|
<div class="stream-video-controls">
|
||||||
|
<div class="flex-container">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
id="pin-btn"
|
||||||
|
(click)="toggleVideoPinned()"
|
||||||
|
[matTooltip]="_track.isPinned ? ('STREAM.UNPIN' | translate) : ('STREAM.PIN' | translate)"
|
||||||
|
>
|
||||||
|
@if (_track.isPinned) {
|
||||||
|
<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>
|
||||||
|
@if (!_track.participant.isLocal) {
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
id="silence-btn"
|
||||||
|
(click)="toggleMuteForcibly()"
|
||||||
|
[class.muted-btn]="_track.isMutedForcibly"
|
||||||
|
[matTooltip]="_track.isMutedForcibly ? ('STREAM.UNMUTE_SOUND' | translate) : ('STREAM.MUTE_SOUND' | translate)"
|
||||||
|
>
|
||||||
|
@if (_track.isMutedForcibly) {
|
||||||
|
<mat-icon>volume_off</mat-icon>
|
||||||
|
}
|
||||||
|
@if (!_track.isMutedForcibly) {
|
||||||
|
<mat-icon>volume_up</mat-icon>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
@if (_track.participant.isLocal) {
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
id="minimize-btn"
|
||||||
|
[disabled]="_track.isPinned"
|
||||||
|
(click)="toggleMinimize()"
|
||||||
|
[matTooltip]="_track.isMinimized ? ('STREAM.MAXIMIZE' | translate) : ('STREAM.MINIMIZE' | translate)"
|
||||||
|
>
|
||||||
|
@if (_track.isMinimized) {
|
||||||
|
<mat-icon>open_in_full</mat-icon>
|
||||||
|
}
|
||||||
|
@if (!_track.isMinimized) {
|
||||||
|
<mat-icon>close_fullscreen</mat-icon>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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') {
|
||||||
|
|
|
||||||
|
|
@ -1,128 +1,136 @@
|
||||||
<!-- 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
|
||||||
|
mat-icon-button
|
||||||
|
class="fab-menu-trigger"
|
||||||
|
[matMenuTriggerFor]="panelsMenu"
|
||||||
|
[disabled]="isConnectionLost"
|
||||||
|
[class.active-btn]="isAnyPanelOpened"
|
||||||
|
matTooltip="{{ 'TOOLBAR.PANELS' | translate }}"
|
||||||
|
#menuTrigger="matMenuTrigger"
|
||||||
|
>
|
||||||
|
<mat-icon class="material-symbols-outlined" [class.rotated]="menuTrigger.menuOpen">keyboard_arrow_up</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Vertical panels menu -->
|
||||||
|
<mat-menu #panelsMenu="matMenu" class="panels-menu">
|
||||||
|
<!-- Activities menu item -->
|
||||||
|
@if (!isMinimal && showActivitiesPanelButton) {
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
class="panel-menu-item"
|
||||||
|
(click)="onToggleActivities()"
|
||||||
|
[disabled]="isConnectionLost"
|
||||||
|
[class.active-menu-item]="isActivitiesOpened"
|
||||||
|
>
|
||||||
|
<mat-icon>category</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Participants menu item -->
|
||||||
|
@if (!isMinimal && showParticipantsPanelButton) {
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
class="panel-menu-item"
|
||||||
|
(click)="onToggleParticipants()"
|
||||||
|
[disabled]="isConnectionLost"
|
||||||
|
[class.active-menu-item]="isParticipantsOpened"
|
||||||
|
>
|
||||||
|
<mat-icon>people</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Chat menu item -->
|
||||||
|
@if (!isMinimal && showChatPanelButton) {
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
class="panel-menu-item"
|
||||||
|
(click)="onToggleChat()"
|
||||||
|
[disabled]="isConnectionLost"
|
||||||
|
[class.active-menu-item]="isChatOpened"
|
||||||
|
>
|
||||||
|
<mat-icon
|
||||||
|
matBadge="{{ unreadMessages }}"
|
||||||
|
[matBadgeHidden]="unreadMessages === 0"
|
||||||
|
matBadgePosition="above before"
|
||||||
|
matBadgeSize="small"
|
||||||
|
matBadgeColor="accent"
|
||||||
|
aria-hidden="false"
|
||||||
|
>
|
||||||
|
chat
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- External additional panel buttons in menu -->
|
||||||
|
@if (toolbarAdditionalPanelButtonsTemplate) {
|
||||||
|
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||||
|
}
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
<!-- Default activities button -->
|
||||||
|
@if (!isMinimal && showActivitiesPanelButton) {
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
class="fab-menu-trigger"
|
id="activities-panel-btn"
|
||||||
[matMenuTriggerFor]="panelsMenu"
|
class="panel-button"
|
||||||
|
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
|
||||||
|
(click)="onToggleActivities()"
|
||||||
[disabled]="isConnectionLost"
|
[disabled]="isConnectionLost"
|
||||||
[class.active-btn]="isAnyPanelOpened"
|
[class.active-btn]="isActivitiesOpened"
|
||||||
matTooltip="{{ 'TOOLBAR.PANELS' | translate }}"
|
|
||||||
#menuTrigger="matMenuTrigger"
|
|
||||||
>
|
>
|
||||||
<mat-icon class="material-symbols-outlined" [class.rotated]="menuTrigger.menuOpen">keyboard_arrow_up</mat-icon>
|
<mat-icon>category</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Vertical panels menu -->
|
<!-- Default participants button -->
|
||||||
<mat-menu #panelsMenu="matMenu" class="panels-menu">
|
@if (!isMinimal && showParticipantsPanelButton) {
|
||||||
<!-- Activities menu item -->
|
<button
|
||||||
<button
|
mat-icon-button
|
||||||
mat-menu-item
|
class="panel-button"
|
||||||
class="panel-menu-item"
|
id="participants-panel-btn"
|
||||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
|
||||||
(click)="onToggleActivities()"
|
(click)="onToggleParticipants()"
|
||||||
[disabled]="isConnectionLost"
|
[disabled]="isConnectionLost"
|
||||||
[class.active-menu-item]="isActivitiesOpened"
|
[class.active-btn]="isParticipantsOpened"
|
||||||
|
>
|
||||||
|
<mat-icon>people</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Default chat button -->
|
||||||
|
@if (!isMinimal && showChatPanelButton) {
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="panel-button"
|
||||||
|
id="chat-panel-btn"
|
||||||
|
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
|
||||||
|
(click)="onToggleChat()"
|
||||||
|
[disabled]="isConnectionLost"
|
||||||
|
[class.active-btn]="isChatOpened"
|
||||||
|
>
|
||||||
|
<mat-icon
|
||||||
|
matBadge="{{ unreadMessages }}"
|
||||||
|
[matBadgeHidden]="unreadMessages === 0"
|
||||||
|
matBadgePosition="above before"
|
||||||
|
matBadgeSize="small"
|
||||||
|
matBadgeColor="accent"
|
||||||
|
aria-hidden="false"
|
||||||
>
|
>
|
||||||
<mat-icon>category</mat-icon>
|
chat
|
||||||
</button>
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Participants menu item -->
|
<!-- External additional panel buttons -->
|
||||||
<button
|
@if (toolbarAdditionalPanelButtonsTemplate) {
|
||||||
mat-menu-item
|
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||||
class="panel-menu-item"
|
}
|
||||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
}
|
||||||
(click)="onToggleParticipants()"
|
|
||||||
[disabled]="isConnectionLost"
|
|
||||||
[class.active-menu-item]="isParticipantsOpened"
|
|
||||||
>
|
|
||||||
<mat-icon>people</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Chat menu item -->
|
|
||||||
<button
|
|
||||||
mat-menu-item
|
|
||||||
class="panel-menu-item"
|
|
||||||
*ngIf="!isMinimal && showChatPanelButton"
|
|
||||||
(click)="onToggleChat()"
|
|
||||||
[disabled]="isConnectionLost"
|
|
||||||
[class.active-menu-item]="isChatOpened"
|
|
||||||
>
|
|
||||||
<mat-icon
|
|
||||||
matBadge="{{ unreadMessages }}"
|
|
||||||
[matBadgeHidden]="unreadMessages === 0"
|
|
||||||
matBadgePosition="above before"
|
|
||||||
matBadgeSize="small"
|
|
||||||
matBadgeColor="accent"
|
|
||||||
aria-hidden="false"
|
|
||||||
>
|
|
||||||
chat
|
|
||||||
</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- External additional panel buttons in menu -->
|
|
||||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
|
||||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</mat-menu>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- Collapsed menu template for small screens -->
|
<!-- Collapsed menu template for small screens -->
|
||||||
<ng-template #uncollapsedButtons>
|
<ng-template #uncollapsedButtons>
|
||||||
<!-- Default activities button -->
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
id="activities-panel-btn"
|
|
||||||
class="panel-button"
|
|
||||||
*ngIf="!isMinimal && showActivitiesPanelButton"
|
|
||||||
matTooltip="{{ 'TOOLBAR.ACTIVITIES' | translate }}"
|
|
||||||
(click)="onToggleActivities()"
|
|
||||||
[disabled]="isConnectionLost"
|
|
||||||
[class.active-btn]="isActivitiesOpened"
|
|
||||||
>
|
|
||||||
<mat-icon>category</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Default participants button -->
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
class="panel-button"
|
|
||||||
id="participants-panel-btn"
|
|
||||||
*ngIf="!isMinimal && showParticipantsPanelButton"
|
|
||||||
matTooltip="{{ 'TOOLBAR.PARTICIPANTS' | translate }}"
|
|
||||||
(click)="onToggleParticipants()"
|
|
||||||
[disabled]="isConnectionLost"
|
|
||||||
[class.active-btn]="isParticipantsOpened"
|
|
||||||
>
|
|
||||||
<mat-icon>people</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Default chat button -->
|
|
||||||
<button
|
|
||||||
mat-icon-button
|
|
||||||
class="panel-button"
|
|
||||||
id="chat-panel-btn"
|
|
||||||
*ngIf="!isMinimal && showChatPanelButton"
|
|
||||||
matTooltip="{{ 'TOOLBAR.CHAT' | translate }}"
|
|
||||||
(click)="onToggleChat()"
|
|
||||||
[disabled]="isConnectionLost"
|
|
||||||
[class.active-btn]="isChatOpened"
|
|
||||||
>
|
|
||||||
<mat-icon
|
|
||||||
matBadge="{{ unreadMessages }}"
|
|
||||||
[matBadgeHidden]="unreadMessages === 0"
|
|
||||||
matBadgePosition="above before"
|
|
||||||
matBadgeSize="small"
|
|
||||||
matBadgeColor="accent"
|
|
||||||
aria-hidden="false"
|
|
||||||
>
|
|
||||||
chat
|
|
||||||
</mat-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- External additional panel buttons -->
|
|
||||||
<ng-container *ngIf="toolbarAdditionalPanelButtonsTemplate">
|
|
||||||
<ng-container *ngTemplateOutlet="toolbarAdditionalPanelButtonsTemplate"></ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</ng-template>
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue