mirror of https://github.com/OpenVidu/openvidu.git
ov-components: implement custom leave button directive and enhance toolbar for additional button templates
parent
2a9f3a62fa
commit
41152de276
|
@ -63,9 +63,10 @@
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
}
|
}
|
||||||
|
|
||||||
<ng-container *ngIf="toolbarAdditionalButtonsTemplate && additionalButtonsPosition && additionalButtonsPosition === 'beforeMenu'">
|
<!-- Additional buttons injection from parent component (desktop/tablet only) -->
|
||||||
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate"></ng-container>
|
@if (showAdditionalButtonsOutside() && additionalButtonsPosition === 'beforeMenu') {
|
||||||
</ng-container>
|
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate; context: { $implicit: additionalButtonsPosition }"></ng-container>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- More options button -->
|
<!-- More options button -->
|
||||||
@if (showMoreOptionsButtonDirect()) {
|
@if (showMoreOptionsButtonDirect()) {
|
||||||
|
@ -75,35 +76,17 @@
|
||||||
[matMenuTriggerFor]="settingsMenu"
|
[matMenuTriggerFor]="settingsMenu"
|
||||||
[disabled]="isConnectionLost"
|
[disabled]="isConnectionLost"
|
||||||
[class.mobile-btn]="isMobileView()"
|
[class.mobile-btn]="isMobileView()"
|
||||||
[matBadge]="hasActiveFeatures() && isMobileView() ? '!' : ''"
|
|
||||||
matBadgeColor="accent"
|
|
||||||
matBadgeSize="small"
|
|
||||||
[matBadgeHidden]="!hasActiveFeatures() || !isMobileView()"
|
|
||||||
matTooltip="{{ 'TOOLBAR.MORE_OPTIONS' | translate }}"
|
matTooltip="{{ 'TOOLBAR.MORE_OPTIONS' | translate }}"
|
||||||
>
|
>
|
||||||
<mat-icon>more_vert</mat-icon>
|
<mat-icon>more_vert</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #settingsMenu="matMenu" id="more-options-menu">
|
<mat-menu #settingsMenu="matMenu" id="more-options-menu">
|
||||||
|
<!-- Additional buttons injection inside menu (mobile only) -->
|
||||||
<!-- Dynamic mobile buttons -->
|
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition === 'beforeMenu') {
|
||||||
@for (button of buttonsInMoreOptions(); track button.key) {
|
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate; context: { $implicit: additionalButtonsPosition }">
|
||||||
@if (button.show) {
|
</ng-container>
|
||||||
<button
|
|
||||||
mat-menu-item
|
|
||||||
[id]="button.key + '-btn'"
|
|
||||||
[disabled]="button.disabled"
|
|
||||||
(click)="button.action()"
|
|
||||||
>
|
|
||||||
<mat-icon [color]="button.color || ''">{{ button.icon }}</mat-icon>
|
|
||||||
<span>{{ button.label | translate }}</span>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Divider if there are mobile buttons -->
|
|
||||||
@if (buttonsInMoreOptions().length > 0) {
|
|
||||||
<mat-divider class="divider"></mat-divider>
|
|
||||||
}
|
|
||||||
<!-- Fullscreen button -->
|
<!-- Fullscreen button -->
|
||||||
@if (showFullscreenButton) {
|
@if (showFullscreenButton) {
|
||||||
<button mat-menu-item id="fullscreen-btn" (click)="onFullscreenToggle()">
|
<button mat-menu-item id="fullscreen-btn" (click)="onFullscreenToggle()">
|
||||||
|
@ -188,6 +171,12 @@
|
||||||
<span *ngIf="!captionsEnabled">{{ 'TOOLBAR.ENABLE_CAPTIONS' | translate }}</span>
|
<span *ngIf="!captionsEnabled">{{ 'TOOLBAR.ENABLE_CAPTIONS' | translate }}</span>
|
||||||
</button> -->
|
</button> -->
|
||||||
|
|
||||||
|
<!-- Additional buttons injection inside menu (mobile only) -->
|
||||||
|
@if (showAdditionalButtonsInsideMenu() && additionalButtonsPosition === 'afterMenu') {
|
||||||
|
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate; context: { $implicit: additionalButtonsPosition }">
|
||||||
|
</ng-container>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Divider before settings -->
|
<!-- Divider before settings -->
|
||||||
@if (showSettingsButton) {
|
@if (showSettingsButton) {
|
||||||
<mat-divider class="divider"></mat-divider>
|
<mat-divider class="divider"></mat-divider>
|
||||||
|
@ -203,13 +192,16 @@
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- External additional buttons -->
|
<!-- Additional buttons injection from parent component (desktop/tablet only) -->
|
||||||
<ng-container *ngIf="toolbarAdditionalButtonsTemplate && additionalButtonsPosition && additionalButtonsPosition === 'afterMenu'">
|
@if (showAdditionalButtonsOutside() && additionalButtonsPosition === 'afterMenu') {
|
||||||
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate"></ng-container>
|
<ng-container *ngTemplateOutlet="toolbarAdditionalButtonsTemplate"></ng-container>
|
||||||
</ng-container>
|
}
|
||||||
|
|
||||||
<!-- Leave session button -->
|
<!-- Leave session button -->
|
||||||
@if (showLeaveButtonDirect()) {
|
@if (showLeaveButtonDirect()) {
|
||||||
|
@if (toolbarLeaveButtonTemplate) {
|
||||||
|
<ng-container *ngTemplateOutlet="toolbarLeaveButtonTemplate"></ng-container>
|
||||||
|
} @else {
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
(click)="onLeaveClick()"
|
(click)="onLeaveClick()"
|
||||||
|
@ -219,4 +211,5 @@
|
||||||
>
|
>
|
||||||
<mat-icon>call_end</mat-icon>
|
<mat-icon>call_end</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,9 @@
|
||||||
|
|
||||||
// Mobile responsive styles
|
// Mobile responsive styles
|
||||||
&.mobile-btn {
|
&.mobile-btn {
|
||||||
margin: 3px;
|
padding: 8px 0;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
text-align: justify;
|
|
||||||
|
|
||||||
mat-icon {
|
mat-icon {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
@ -59,7 +58,8 @@
|
||||||
background-color: var(--ov-accent-action-color) !important;
|
background-color: var(--ov-accent-action-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#leave-btn {
|
#leave-btn,
|
||||||
|
::ng-deep #leave-btn {
|
||||||
background-color: var(--ov-error-color) !important;
|
background-color: var(--ov-error-color) !important;
|
||||||
border-radius: var(--ov-leave-button-radius) !important;
|
border-radius: var(--ov-leave-button-radius) !important;
|
||||||
width: 65px !important;
|
width: 65px !important;
|
||||||
|
@ -68,7 +68,6 @@
|
||||||
&.mobile-btn {
|
&.mobile-btn {
|
||||||
width: 56px !important;
|
width: 56px !important;
|
||||||
height: 36px !important;
|
height: 36px !important;
|
||||||
margin: 3px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { BroadcastingStatus } from '../../../models/broadcasting.model';
|
||||||
import { ToolbarAdditionalButtonsPosition } from '../../../models/toolbar.model';
|
import { ToolbarAdditionalButtonsPosition } from '../../../models/toolbar.model';
|
||||||
import { ViewportService } from '../../../services/viewport/viewport.service';
|
import { ViewportService } from '../../../services/viewport/viewport.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ov-toolbar-media-buttons',
|
selector: 'ov-toolbar-media-buttons',
|
||||||
templateUrl: './toolbar-media-buttons.component.html',
|
templateUrl: './toolbar-media-buttons.component.html',
|
||||||
|
@ -11,7 +14,6 @@ import { ViewportService } from '../../../services/viewport/viewport.service';
|
||||||
standalone: false
|
standalone: false
|
||||||
})
|
})
|
||||||
export class ToolbarMediaButtonsComponent {
|
export class ToolbarMediaButtonsComponent {
|
||||||
|
|
||||||
// Camera related inputs
|
// Camera related inputs
|
||||||
@Input() showCameraButton: boolean = true;
|
@Input() showCameraButton: boolean = true;
|
||||||
@Input() isCameraEnabled: boolean = true;
|
@Input() isCameraEnabled: boolean = true;
|
||||||
|
@ -66,6 +68,9 @@ export class ToolbarMediaButtonsComponent {
|
||||||
@Input() toolbarAdditionalButtonsTemplate: TemplateRef<any> | null = null;
|
@Input() toolbarAdditionalButtonsTemplate: TemplateRef<any> | null = null;
|
||||||
@Input() additionalButtonsPosition: ToolbarAdditionalButtonsPosition | undefined;
|
@Input() additionalButtonsPosition: ToolbarAdditionalButtonsPosition | undefined;
|
||||||
|
|
||||||
|
// Leave button template
|
||||||
|
@Input() toolbarLeaveButtonTemplate: TemplateRef<any> | null = null;
|
||||||
|
|
||||||
// Status enums for template usage
|
// Status enums for template usage
|
||||||
_recordingStatus = RecordingStatus;
|
_recordingStatus = RecordingStatus;
|
||||||
_broadcastingStatus = BroadcastingStatus;
|
_broadcastingStatus = BroadcastingStatus;
|
||||||
|
@ -79,90 +84,39 @@ export class ToolbarMediaButtonsComponent {
|
||||||
readonly isDesktopView = computed(() => this.viewportService.isDesktop());
|
readonly isDesktopView = computed(() => this.viewportService.isDesktop());
|
||||||
|
|
||||||
// Essential buttons that always stay visible
|
// Essential buttons that always stay visible
|
||||||
readonly showCameraButtonDirect = computed(() =>
|
readonly showCameraButtonDirect = computed(() => this.showCameraButton && !this.isMinimal);
|
||||||
this.showCameraButton && !this.isMinimal
|
|
||||||
);
|
|
||||||
|
|
||||||
readonly showMicrophoneButtonDirect = computed(() =>
|
readonly showMicrophoneButtonDirect = computed(() => this.showMicrophoneButton && !this.isMinimal);
|
||||||
this.showMicrophoneButton && !this.isMinimal
|
|
||||||
);
|
|
||||||
|
|
||||||
// Screenshare button - visible on tablet+ or when already active
|
// Screenshare button - visible on tablet+ or when already active
|
||||||
readonly showScreenshareButtonDirect = computed(() =>
|
readonly showScreenshareButtonDirect = computed(
|
||||||
this.showScreenshareButton &&
|
() => this.showScreenshareButton && !this.isMinimal && (!this.isMobileView() || this.isScreenShareEnabled)
|
||||||
!this.isMinimal &&
|
|
||||||
(!this.isMobileView() || this.isScreenShareEnabled)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// More options button - always visible when not minimal
|
// More options button - always visible when not minimal
|
||||||
readonly showMoreOptionsButtonDirect = computed(() =>
|
readonly showMoreOptionsButtonDirect = computed(() => this.showMoreOptionsButton && !this.isMinimal);
|
||||||
this.showMoreOptionsButton && !this.isMinimal
|
|
||||||
);
|
|
||||||
|
|
||||||
// Leave button - always visible
|
// Leave button - always visible
|
||||||
readonly showLeaveButtonDirect = computed(() =>
|
readonly showLeaveButtonDirect = computed(() => this.showLeaveButton);
|
||||||
this.showLeaveButton
|
|
||||||
);
|
|
||||||
|
|
||||||
// Buttons that should be moved to "More Options" on mobile
|
|
||||||
readonly buttonsInMoreOptions = computed(() => {
|
|
||||||
const buttons: Array<{
|
|
||||||
key: string;
|
|
||||||
show: boolean;
|
|
||||||
label: string;
|
|
||||||
icon: string;
|
|
||||||
action: () => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
active?: boolean;
|
|
||||||
color?: string;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
const isMobile = this.isMobileView();
|
|
||||||
|
|
||||||
// On mobile, screenshare goes to more options when not active
|
|
||||||
if (isMobile && this.showScreenshareButton && !this.isScreenShareEnabled) {
|
|
||||||
buttons.push({
|
|
||||||
key: 'screenshare',
|
|
||||||
show: true,
|
|
||||||
label: 'TOOLBAR.ENABLE_SCREEN',
|
|
||||||
icon: 'screen_share',
|
|
||||||
action: () => this.onScreenShareToggle(),
|
|
||||||
disabled: this.isConnectionLost
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace screenshare option when active on mobile
|
|
||||||
if (isMobile && this.showScreenshareButton && this.isScreenShareEnabled) {
|
|
||||||
buttons.push({
|
|
||||||
key: 'screenshare-replace',
|
|
||||||
show: true,
|
|
||||||
label: 'STREAM.REPLACE_SCREEN',
|
|
||||||
icon: 'picture_in_picture',
|
|
||||||
action: () => this.onScreenTrackReplace(),
|
|
||||||
disabled: this.isConnectionLost
|
|
||||||
});
|
|
||||||
|
|
||||||
buttons.push({
|
|
||||||
key: 'screenshare-stop',
|
|
||||||
show: true,
|
|
||||||
label: 'TOOLBAR.DISABLE_SCREEN',
|
|
||||||
icon: 'stop_screen_share',
|
|
||||||
action: () => this.onScreenShareToggle(),
|
|
||||||
disabled: this.isConnectionLost,
|
|
||||||
color: 'warn'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if there are active features that should show a badge on More Options
|
// Check if there are active features that should show a badge on More Options
|
||||||
readonly hasActiveFeatures = computed(() =>
|
readonly hasActiveFeatures = computed(
|
||||||
|
() =>
|
||||||
this.isScreenShareEnabled ||
|
this.isScreenShareEnabled ||
|
||||||
this.recordingStatus === this._recordingStatus.STARTED ||
|
this.recordingStatus === this._recordingStatus.STARTED ||
|
||||||
this.broadcastingStatus === this._broadcastingStatus.STARTED
|
this.broadcastingStatus === this._broadcastingStatus.STARTED
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check if additional buttons should be shown outside (desktop/tablet) or inside More Options (mobile)
|
||||||
|
readonly showAdditionalButtonsOutside = computed(() => {
|
||||||
|
return !this.isMobileView() && this.toolbarAdditionalButtonsTemplate;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if additional buttons should be shown inside More Options menu (mobile only)
|
||||||
|
readonly showAdditionalButtonsInsideMenu = computed(() => {
|
||||||
|
return this.isMobileView() && this.toolbarAdditionalButtonsTemplate;
|
||||||
|
});
|
||||||
|
|
||||||
// Media button outputs
|
// Media button outputs
|
||||||
@Output() cameraToggled = new EventEmitter<void>();
|
@Output() cameraToggled = new EventEmitter<void>();
|
||||||
@Output() microphoneToggled = new EventEmitter<void>();
|
@Output() microphoneToggled = new EventEmitter<void>();
|
||||||
|
@ -229,6 +183,4 @@ export class ToolbarMediaButtonsComponent {
|
||||||
onLeaveClick(): void {
|
onLeaveClick(): void {
|
||||||
this.leaveClicked.emit();
|
this.leaveClicked.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
[showLeaveButton]="showLeaveButton"
|
[showLeaveButton]="showLeaveButton"
|
||||||
[toolbarAdditionalButtonsTemplate]="toolbarAdditionalButtonsTemplate"
|
[toolbarAdditionalButtonsTemplate]="toolbarAdditionalButtonsTemplate"
|
||||||
[additionalButtonsPosition]="additionalButtonsPosition"
|
[additionalButtonsPosition]="additionalButtonsPosition"
|
||||||
|
[toolbarLeaveButtonTemplate]="toolbarLeaveButtonTemplate"
|
||||||
(cameraToggled)="toggleCamera()"
|
(cameraToggled)="toggleCamera()"
|
||||||
(microphoneToggled)="toggleMicrophone()"
|
(microphoneToggled)="toggleMicrophone()"
|
||||||
(screenShareToggled)="toggleScreenShare()"
|
(screenShareToggled)="toggleScreenShare()"
|
||||||
|
|
|
@ -50,6 +50,7 @@ import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.servic
|
||||||
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
|
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
|
||||||
import { Room, RoomEvent } from 'livekit-client';
|
import { Room, RoomEvent } from 'livekit-client';
|
||||||
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
|
import { ToolbarAdditionalButtonsPosition } from '../../models/toolbar.model';
|
||||||
|
import { LeaveButtonDirective } from '../../directives/template/internals.directive';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The **ToolbarComponent** is hosted inside of the {@link VideoconferenceComponent}.
|
* The **ToolbarComponent** is hosted inside of the {@link VideoconferenceComponent}.
|
||||||
|
@ -66,12 +67,18 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
@ContentChild('toolbarAdditionalButtons', { read: TemplateRef }) toolbarAdditionalButtonsTemplate: TemplateRef<any>;
|
@ContentChild('toolbarAdditionalButtons', { read: TemplateRef }) toolbarAdditionalButtonsTemplate: TemplateRef<any> | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
@ContentChild('toolbarAdditionalPanelButtons', { read: TemplateRef }) toolbarAdditionalPanelButtonsTemplate: TemplateRef<any>;
|
@ContentChild('toolbarLeaveButton', { read: TemplateRef }) toolbarLeaveButtonTemplate: TemplateRef<any> | undefined;
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@ContentChild('toolbarAdditionalPanelButtons', { read: TemplateRef }) toolbarAdditionalPanelButtonsTemplate:
|
||||||
|
| TemplateRef<any>
|
||||||
|
| undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
|
@ -84,6 +91,17 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@ContentChild(LeaveButtonDirective)
|
||||||
|
set externalLeaveButton(externalLeaveButton: LeaveButtonDirective) {
|
||||||
|
this._externalLeaveButton = externalLeaveButton;
|
||||||
|
if (externalLeaveButton) {
|
||||||
|
this.updateTemplatesAndMarkForCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
|
@ -153,12 +171,12 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
@ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger;
|
@ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
room: Room;
|
room!: Room;
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
|
@ -186,11 +204,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
hasVideoDevices: boolean;
|
hasVideoDevices: boolean = true;
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
hasAudioDevices: boolean;
|
hasAudioDevices: boolean = true;
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
|
@ -305,7 +323,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
additionalButtonsPosition: ToolbarAdditionalButtonsPosition;
|
additionalButtonsPosition: ToolbarAdditionalButtonsPosition = ToolbarAdditionalButtonsPosition.BEFORE_MENU;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
|
@ -357,7 +375,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
recordingTime: Date;
|
recordingTime: Date | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -367,6 +385,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
// Store directive references for template setup
|
// Store directive references for template setup
|
||||||
private _externalAdditionalButtons?: ToolbarAdditionalButtonsDirective;
|
private _externalAdditionalButtons?: ToolbarAdditionalButtonsDirective;
|
||||||
|
private _externalLeaveButton?: LeaveButtonDirective;
|
||||||
private _externalAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective;
|
private _externalAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective;
|
||||||
|
|
||||||
private log: ILogger;
|
private log: ILogger;
|
||||||
|
@ -416,7 +435,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
sizeChange(event) {
|
sizeChange(_: Event) {
|
||||||
if (this.currentWindowHeight >= window.innerHeight) {
|
if (this.currentWindowHeight >= window.innerHeight) {
|
||||||
// The user has exit the fullscreen mode
|
// The user has exit the fullscreen mode
|
||||||
this.currentWindowHeight = window.innerHeight;
|
this.currentWindowHeight = window.innerHeight;
|
||||||
|
@ -474,7 +493,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
private setupTemplates(): void {
|
private setupTemplates(): void {
|
||||||
this.templateConfig = this.templateManagerService.setupToolbarTemplates(
|
this.templateConfig = this.templateManagerService.setupToolbarTemplates(
|
||||||
this._externalAdditionalButtons,
|
this._externalAdditionalButtons,
|
||||||
this._externalAdditionalPanelButtons
|
this._externalAdditionalPanelButtons,
|
||||||
|
this._externalLeaveButton
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply templates to component properties for backward compatibility
|
// Apply templates to component properties for backward compatibility
|
||||||
|
@ -492,6 +512,9 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
if (this.templateConfig.toolbarAdditionalPanelButtonsTemplate) {
|
if (this.templateConfig.toolbarAdditionalPanelButtonsTemplate) {
|
||||||
this.toolbarAdditionalPanelButtonsTemplate = this.templateConfig.toolbarAdditionalPanelButtonsTemplate;
|
this.toolbarAdditionalPanelButtonsTemplate = this.templateConfig.toolbarAdditionalPanelButtonsTemplate;
|
||||||
}
|
}
|
||||||
|
if (this.templateConfig.toolbarLeaveButtonTemplate) {
|
||||||
|
this.toolbarLeaveButtonTemplate = this.templateConfig.toolbarLeaveButtonTemplate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -518,11 +541,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
this.microphoneMuteChanging = false;
|
this.microphoneMuteChanging = false;
|
||||||
const isMicrophoneEnabled = this.participantService.isMyMicrophoneEnabled();
|
const isMicrophoneEnabled = this.participantService.isMyMicrophoneEnabled();
|
||||||
await this.participantService.setMicrophoneEnabled(!isMicrophoneEnabled);
|
await this.participantService.setMicrophoneEnabled(!isMicrophoneEnabled);
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
this.log.e('There was an error toggling microphone:', error.code, error.message);
|
this.log.e('There was an error toggling microphone:', (error as any).code, (error as any).message);
|
||||||
this.actionService.openDialog(
|
this.actionService.openDialog(
|
||||||
this.translateService.translate('ERRORS.TOGGLE_MICROPHONE'),
|
this.translateService.translate('ERRORS.TOGGLE_MICROPHONE'),
|
||||||
error?.error || error?.message || error
|
(error as any)?.error || (error as any)?.message || error
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.microphoneMuteChanging = false;
|
this.microphoneMuteChanging = false;
|
||||||
|
@ -541,8 +564,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
await this.participantService.setCameraEnabled(!isCameraEnabled);
|
await this.participantService.setCameraEnabled(!isCameraEnabled);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.log.e('There was an error toggling camera:', error.code, error.message);
|
this.log.e('There was an error toggling camera:', (error as any).code, (error as any).message);
|
||||||
this.actionService.openDialog(this.translateService.translate('ERRORS.TOGGLE_CAMERA'), error?.error || error?.message || error);
|
this.actionService.openDialog(
|
||||||
|
this.translateService.translate('ERRORS.TOGGLE_CAMERA'),
|
||||||
|
(error as any)?.error || (error as any)?.message || error
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.cameraMuteChanging = false;
|
this.cameraMuteChanging = false;
|
||||||
}
|
}
|
||||||
|
@ -579,8 +605,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
this.onRoomDisconnected.emit();
|
this.onRoomDisconnected.emit();
|
||||||
}, false);
|
}, false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.log.e('There was an error disconnecting:', error.code, error.message);
|
this.log.e('There was an error disconnecting:', (error as any).code, (error as any).message);
|
||||||
this.actionService.openDialog(this.translateService.translate('ERRORS.DISCONNECT'), error?.error || error?.message || error);
|
this.actionService.openDialog(
|
||||||
|
this.translateService.translate('ERRORS.DISCONNECT'),
|
||||||
|
(error as any)?.error || (error as any)?.message || error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,10 @@
|
||||||
<ng-template #toolbarAdditionalPanelButtons>
|
<ng-template #toolbarAdditionalPanelButtons>
|
||||||
<ng-container *ngTemplateOutlet="openviduAngularToolbarAdditionalPanelButtonsTemplate"></ng-container>
|
<ng-container *ngTemplateOutlet="openviduAngularToolbarAdditionalPanelButtonsTemplate"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #toolbarLeaveButton>
|
||||||
|
<ng-container *ngTemplateOutlet="openviduAngularToolbarLeaveButtonTemplate"></ng-container>
|
||||||
|
</ng-template>
|
||||||
</ov-toolbar>
|
</ov-toolbar>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,8 @@ import { LangOption } from '../../models/lang.model';
|
||||||
import {
|
import {
|
||||||
LayoutAdditionalElementsDirective,
|
LayoutAdditionalElementsDirective,
|
||||||
ParticipantPanelAfterLocalParticipantDirective,
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
PreJoinDirective
|
PreJoinDirective,
|
||||||
|
LeaveButtonDirective
|
||||||
} from '../../directives/template/internals.directive';
|
} from '../../directives/template/internals.directive';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,6 +125,24 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
return this._externalToolbarAdditionalButtons;
|
return this._externalToolbarAdditionalButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _externalToolbarLeaveButton?: LeaveButtonDirective;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
@ContentChild(LeaveButtonDirective)
|
||||||
|
set externalToolbarLeaveButton(value: LeaveButtonDirective) {
|
||||||
|
this._externalToolbarLeaveButton = value;
|
||||||
|
this.setupTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
get externalToolbarLeaveButton(): LeaveButtonDirective | undefined {
|
||||||
|
return this._externalToolbarLeaveButton;
|
||||||
|
}
|
||||||
|
|
||||||
private _externalToolbarAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective;
|
private _externalToolbarAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,6 +416,12 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
openviduAngularToolbarAdditionalButtonsTemplate: TemplateRef<any>;
|
openviduAngularToolbarAdditionalButtonsTemplate: TemplateRef<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
openviduAngularToolbarLeaveButtonTemplate: TemplateRef<any> | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
@ -744,6 +769,7 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
toolbar: this.externalToolbar,
|
toolbar: this.externalToolbar,
|
||||||
toolbarAdditionalButtons: this.externalToolbarAdditionalButtons,
|
toolbarAdditionalButtons: this.externalToolbarAdditionalButtons,
|
||||||
toolbarAdditionalPanelButtons: this.externalToolbarAdditionalPanelButtons,
|
toolbarAdditionalPanelButtons: this.externalToolbarAdditionalPanelButtons,
|
||||||
|
toolbarLeaveButton: this.externalToolbarLeaveButton,
|
||||||
additionalPanels: this.externalAdditionalPanels,
|
additionalPanels: this.externalAdditionalPanels,
|
||||||
panel: this.externalPanel,
|
panel: this.externalPanel,
|
||||||
chatPanel: this.externalChatPanel,
|
chatPanel: this.externalChatPanel,
|
||||||
|
@ -800,6 +826,9 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
if (this.templateConfig.toolbarAdditionalButtonsTemplate) {
|
if (this.templateConfig.toolbarAdditionalButtonsTemplate) {
|
||||||
assignIfChanged('openviduAngularToolbarAdditionalButtonsTemplate', this.templateConfig.toolbarAdditionalButtonsTemplate);
|
assignIfChanged('openviduAngularToolbarAdditionalButtonsTemplate', this.templateConfig.toolbarAdditionalButtonsTemplate);
|
||||||
}
|
}
|
||||||
|
if (this.templateConfig.toolbarLeaveButtonTemplate) {
|
||||||
|
assignIfChanged('openviduAngularToolbarLeaveButtonTemplate', this.templateConfig.toolbarLeaveButtonTemplate);
|
||||||
|
}
|
||||||
if (this.templateConfig.toolbarAdditionalPanelButtonsTemplate) {
|
if (this.templateConfig.toolbarAdditionalPanelButtonsTemplate) {
|
||||||
assignIfChanged(
|
assignIfChanged(
|
||||||
'openviduAngularToolbarAdditionalPanelButtonsTemplate',
|
'openviduAngularToolbarAdditionalPanelButtonsTemplate',
|
||||||
|
|
|
@ -222,6 +222,33 @@ export class ParticipantPanelAfterLocalParticipantDirective {
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ***ovLeaveButton** directive allows you to replace the default leave button with a custom template.
|
||||||
|
* Use this directive to provide your own button, confirm dialogs, or any custom leave logic while keeping
|
||||||
|
* the internal leave flow intact.
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
* ```html
|
||||||
|
* <ov-videoconference>
|
||||||
|
* <ng-container *ovLeaveButton>
|
||||||
|
* <button class="my-leave-button" (click)="customLeave()">
|
||||||
|
* Leave meeting
|
||||||
|
* </button>
|
||||||
|
* </ng-container>
|
||||||
|
* </ov-videoconference>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[ovToolbarLeaveButton]',
|
||||||
|
standalone: false
|
||||||
|
})
|
||||||
|
export class LeaveButtonDirective {
|
||||||
|
constructor(
|
||||||
|
public template: TemplateRef<any>,
|
||||||
|
public container: ViewContainerRef
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ***ovLayoutAdditionalElements** directive allows you to inject custom HTML or Angular templates
|
* The ***ovLayoutAdditionalElements** directive allows you to inject custom HTML or Angular templates
|
||||||
* as additional layout elements within the videoconference UI.
|
* as additional layout elements within the videoconference UI.
|
||||||
|
|
|
@ -18,7 +18,8 @@ import {
|
||||||
LayoutAdditionalElementsDirective,
|
LayoutAdditionalElementsDirective,
|
||||||
ParticipantPanelAfterLocalParticipantDirective,
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
ParticipantPanelParticipantBadgeDirective,
|
ParticipantPanelParticipantBadgeDirective,
|
||||||
PreJoinDirective
|
PreJoinDirective,
|
||||||
|
LeaveButtonDirective
|
||||||
} from './internals.directive';
|
} from './internals.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -32,6 +33,7 @@ import {
|
||||||
StreamDirective,
|
StreamDirective,
|
||||||
ToolbarDirective,
|
ToolbarDirective,
|
||||||
ToolbarAdditionalButtonsDirective,
|
ToolbarAdditionalButtonsDirective,
|
||||||
|
LeaveButtonDirective,
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
|
@ -51,6 +53,7 @@ import {
|
||||||
StreamDirective,
|
StreamDirective,
|
||||||
ToolbarDirective,
|
ToolbarDirective,
|
||||||
ToolbarAdditionalButtonsDirective,
|
ToolbarAdditionalButtonsDirective,
|
||||||
|
LeaveButtonDirective,
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
|
|
|
@ -18,7 +18,8 @@ import {
|
||||||
import {
|
import {
|
||||||
PreJoinDirective,
|
PreJoinDirective,
|
||||||
ParticipantPanelAfterLocalParticipantDirective,
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
LayoutAdditionalElementsDirective
|
LayoutAdditionalElementsDirective,
|
||||||
|
LeaveButtonDirective
|
||||||
} from '../../directives/template/internals.directive';
|
} from '../../directives/template/internals.directive';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +30,7 @@ export interface TemplateConfiguration {
|
||||||
toolbarTemplate: TemplateRef<any>;
|
toolbarTemplate: TemplateRef<any>;
|
||||||
toolbarAdditionalButtonsTemplate?: TemplateRef<any>;
|
toolbarAdditionalButtonsTemplate?: TemplateRef<any>;
|
||||||
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
|
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
|
||||||
|
toolbarLeaveButtonTemplate?: TemplateRef<any>;
|
||||||
|
|
||||||
// Panel templates
|
// Panel templates
|
||||||
panelTemplate: TemplateRef<any>;
|
panelTemplate: TemplateRef<any>;
|
||||||
|
@ -69,6 +71,7 @@ export interface PanelTemplateConfiguration {
|
||||||
export interface ToolbarTemplateConfiguration {
|
export interface ToolbarTemplateConfiguration {
|
||||||
toolbarAdditionalButtonsTemplate?: TemplateRef<any>;
|
toolbarAdditionalButtonsTemplate?: TemplateRef<any>;
|
||||||
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
|
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
|
||||||
|
toolbarLeaveButtonTemplate?: TemplateRef<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,6 +113,7 @@ export interface ExternalDirectives {
|
||||||
toolbar?: ToolbarDirective;
|
toolbar?: ToolbarDirective;
|
||||||
toolbarAdditionalButtons?: ToolbarAdditionalButtonsDirective;
|
toolbarAdditionalButtons?: ToolbarAdditionalButtonsDirective;
|
||||||
toolbarAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective;
|
toolbarAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective;
|
||||||
|
toolbarLeaveButton?: LeaveButtonDirective;
|
||||||
additionalPanels?: AdditionalPanelsDirective;
|
additionalPanels?: AdditionalPanelsDirective;
|
||||||
panel?: PanelDirective;
|
panel?: PanelDirective;
|
||||||
chatPanel?: ChatPanelDirective;
|
chatPanel?: ChatPanelDirective;
|
||||||
|
@ -179,6 +183,11 @@ export class TemplateManagerService {
|
||||||
this.log.v('Setting EXTERNAL TOOLBAR ADDITIONAL BUTTONS');
|
this.log.v('Setting EXTERNAL TOOLBAR ADDITIONAL BUTTONS');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (externalDirectives.toolbarLeaveButton) {
|
||||||
|
config.toolbarLeaveButtonTemplate = externalDirectives.toolbarLeaveButton.template;
|
||||||
|
this.log.v('Setting EXTERNAL TOOLBAR LEAVE BUTTON');
|
||||||
|
}
|
||||||
|
|
||||||
if (externalDirectives.toolbarAdditionalPanelButtons) {
|
if (externalDirectives.toolbarAdditionalPanelButtons) {
|
||||||
config.toolbarAdditionalPanelButtonsTemplate = externalDirectives.toolbarAdditionalPanelButtons.template;
|
config.toolbarAdditionalPanelButtonsTemplate = externalDirectives.toolbarAdditionalPanelButtons.template;
|
||||||
this.log.v('Setting EXTERNAL TOOLBAR ADDITIONAL PANEL BUTTONS');
|
this.log.v('Setting EXTERNAL TOOLBAR ADDITIONAL PANEL BUTTONS');
|
||||||
|
@ -358,13 +367,15 @@ export class TemplateManagerService {
|
||||||
*/
|
*/
|
||||||
setupToolbarTemplates(
|
setupToolbarTemplates(
|
||||||
externalAdditionalButtons?: ToolbarAdditionalButtonsDirective,
|
externalAdditionalButtons?: ToolbarAdditionalButtonsDirective,
|
||||||
externalAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective
|
externalAdditionalPanelButtons?: ToolbarAdditionalPanelButtonsDirective,
|
||||||
|
externalLeaveButton?: LeaveButtonDirective
|
||||||
): ToolbarTemplateConfiguration {
|
): ToolbarTemplateConfiguration {
|
||||||
this.log.v('Setting up toolbar templates...');
|
this.log.v('Setting up toolbar templates...');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toolbarAdditionalButtonsTemplate: externalAdditionalButtons?.template,
|
toolbarAdditionalButtonsTemplate: externalAdditionalButtons?.template,
|
||||||
toolbarAdditionalPanelButtonsTemplate: externalAdditionalPanelButtons?.template
|
toolbarAdditionalPanelButtonsTemplate: externalAdditionalPanelButtons?.template,
|
||||||
|
toolbarLeaveButtonTemplate: externalLeaveButton?.template
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ export * from './lib/components/toolbar/toolbar-media-buttons/toolbar-media-butt
|
||||||
export * from './lib/components/videoconference/videoconference.component';
|
export * from './lib/components/videoconference/videoconference.component';
|
||||||
export * from './lib/config/openvidu-components-angular.config';
|
export * from './lib/config/openvidu-components-angular.config';
|
||||||
// Directives
|
// Directives
|
||||||
export * from './lib/directives/template/internals.directive';
|
|
||||||
export * from './lib/directives/api/activities-panel.directive';
|
export * from './lib/directives/api/activities-panel.directive';
|
||||||
export * from './lib/directives/api/admin.directive';
|
export * from './lib/directives/api/admin.directive';
|
||||||
export * from './lib/directives/api/api.directive.module';
|
export * from './lib/directives/api/api.directive.module';
|
||||||
|
@ -28,10 +27,12 @@ export * from './lib/directives/api/participant-panel-item.directive';
|
||||||
export * from './lib/directives/api/stream.directive';
|
export * from './lib/directives/api/stream.directive';
|
||||||
export * from './lib/directives/api/toolbar.directive';
|
export * from './lib/directives/api/toolbar.directive';
|
||||||
export * from './lib/directives/api/videoconference.directive';
|
export * from './lib/directives/api/videoconference.directive';
|
||||||
|
|
||||||
|
export * from './lib/directives/template/internals.directive';
|
||||||
export * from './lib/directives/template/openvidu-components-angular.directive';
|
export * from './lib/directives/template/openvidu-components-angular.directive';
|
||||||
export * from './lib/directives/template/openvidu-components-angular.directive.module';
|
export * from './lib/directives/template/openvidu-components-angular.directive.module';
|
||||||
export * from './lib/models/broadcasting.model';
|
|
||||||
// Models
|
// Models
|
||||||
|
export * from './lib/models/broadcasting.model';
|
||||||
export * from './lib/models/panel.model';
|
export * from './lib/models/panel.model';
|
||||||
export * from './lib/models/participant.model';
|
export * from './lib/models/participant.model';
|
||||||
export * from './lib/models/recording.model';
|
export * from './lib/models/recording.model';
|
||||||
|
|
Loading…
Reference in New Issue