mirror of https://github.com/OpenVidu/openvidu.git
ov-components: Add layout additional elements directive for customizable UI extensions
parent
e9ecceeb77
commit
4bf351b2df
|
@ -19,6 +19,11 @@
|
||||||
<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 -->
|
||||||
|
@if (layoutAdditionalElementsTemplate) {
|
||||||
|
<ng-container *ngTemplateOutlet="layoutAdditionalElementsTemplate"></ng-container>
|
||||||
|
}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngFor="let track of remoteParticipants | tracks; trackBy: trackParticipantElement"
|
*ngFor="let track of remoteParticipants | tracks; trackBy: trackParticipantElement"
|
||||||
class="remote-participant"
|
class="remote-participant"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { LayoutAdditionalElementsDirective } from '../../directives/template/internals.directive';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
@ -20,6 +22,7 @@ import { CdkDrag } from '@angular/cdk/drag-drop';
|
||||||
import { PanelService } from '../../services/panel/panel.service';
|
import { PanelService } from '../../services/panel/panel.service';
|
||||||
import { GlobalConfigService } from '../../services/config/global-config.service';
|
import { GlobalConfigService } from '../../services/config/global-config.service';
|
||||||
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
|
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
|
||||||
|
import { LayoutTemplateConfiguration, TemplateManagerService } from '../../services/template/template-manager.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -39,6 +42,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
*/
|
*/
|
||||||
@ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef<any>;
|
@ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@ContentChild('layoutAdditionalElements', { read: TemplateRef }) layoutAdditionalElementsTemplate: TemplateRef<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
|
@ -62,9 +70,27 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
// is inside of the layout component tagged with '*ovLayout' directive
|
// is inside of the layout component tagged with '*ovLayout' directive
|
||||||
if (externalStream) {
|
if (externalStream) {
|
||||||
this.streamTemplate = externalStream.template;
|
this.streamTemplate = externalStream.template;
|
||||||
|
this.updateTemplatesAndMarkForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@ContentChild(LayoutAdditionalElementsDirective) set externalAdditionalElements(
|
||||||
|
externalAdditionalElements: LayoutAdditionalElementsDirective
|
||||||
|
) {
|
||||||
|
if (externalAdditionalElements) {
|
||||||
|
this._externalLayoutAdditionalElements = externalAdditionalElements;
|
||||||
|
this.updateTemplatesAndMarkForCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
templateConfig: LayoutTemplateConfiguration = {};
|
||||||
|
|
||||||
localParticipant: ParticipantModel | undefined;
|
localParticipant: ParticipantModel | undefined;
|
||||||
remoteParticipants: ParticipantModel[] = [];
|
remoteParticipants: ParticipantModel[] = [];
|
||||||
/**
|
/**
|
||||||
|
@ -72,6 +98,9 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
*/
|
*/
|
||||||
captionsEnabled = true;
|
captionsEnabled = true;
|
||||||
|
|
||||||
|
private _externalStream?: StreamDirective;
|
||||||
|
private _externalLayoutAdditionalElements?: LayoutAdditionalElementsDirective;
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
private resizeTimeout: NodeJS.Timeout;
|
private resizeTimeout: NodeJS.Timeout;
|
||||||
|
@ -87,10 +116,13 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
private participantService: ParticipantService,
|
private participantService: ParticipantService,
|
||||||
private globalService: GlobalConfigService,
|
private globalService: GlobalConfigService,
|
||||||
private directiveService: OpenViduComponentsConfigService,
|
private directiveService: OpenViduComponentsConfigService,
|
||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef,
|
||||||
|
private templateManagerService: TemplateManagerService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.setupTemplates();
|
||||||
|
|
||||||
this.subscribeToParticipants();
|
this.subscribeToParticipants();
|
||||||
this.subscribeToCaptions();
|
this.subscribeToCaptions();
|
||||||
}
|
}
|
||||||
|
@ -121,34 +153,55 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setupTemplates() {
|
||||||
|
this.templateConfig = this.templateManagerService.setupLayoutTemplates(
|
||||||
|
this._externalStream,
|
||||||
|
this._externalLayoutAdditionalElements
|
||||||
|
);
|
||||||
|
|
||||||
|
// Apply templates to component properties for backward compatibility
|
||||||
|
this.applyTemplateConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyTemplateConfiguration() {
|
||||||
|
if (this.templateConfig.layoutStreamTemplate) {
|
||||||
|
this.streamTemplate = this.templateConfig.layoutStreamTemplate;
|
||||||
|
}
|
||||||
|
if (this.templateConfig.layoutAdditionalElementsTemplate) {
|
||||||
|
this.layoutAdditionalElementsTemplate = this.templateConfig.layoutAdditionalElementsTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* Updates templates and triggers change detection
|
||||||
|
*/
|
||||||
|
private updateTemplatesAndMarkForCheck(): void {
|
||||||
|
this.setupTemplates();
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
private subscribeToCaptions() {
|
private subscribeToCaptions() {
|
||||||
this.layoutService.captionsTogglingObs
|
this.layoutService.captionsTogglingObs.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
|
||||||
.pipe(takeUntil(this.destroy$))
|
this.captionsEnabled = value;
|
||||||
.subscribe((value: boolean) => {
|
this.cd.markForCheck();
|
||||||
this.captionsEnabled = value;
|
this.layoutService.update();
|
||||||
this.cd.markForCheck();
|
});
|
||||||
this.layoutService.update();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private subscribeToParticipants() {
|
private subscribeToParticipants() {
|
||||||
this.participantService.localParticipant$
|
this.participantService.localParticipant$.pipe(takeUntil(this.destroy$)).subscribe((p) => {
|
||||||
.pipe(takeUntil(this.destroy$))
|
if (p) {
|
||||||
.subscribe((p) => {
|
this.localParticipant = p;
|
||||||
if (p) {
|
if (!this.localParticipant?.isMinimized) {
|
||||||
this.localParticipant = p;
|
this.videoIsAtRight = false;
|
||||||
if (!this.localParticipant?.isMinimized) {
|
|
||||||
this.videoIsAtRight = false;
|
|
||||||
}
|
|
||||||
this.layoutService.update();
|
|
||||||
this.cd.markForCheck();
|
|
||||||
}
|
}
|
||||||
});
|
this.layoutService.update();
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
combineLatest([
|
combineLatest([this.participantService.remoteParticipants$, this.directiveService.layoutRemoteParticipants$])
|
||||||
this.participantService.remoteParticipants$,
|
|
||||||
this.directiveService.layoutRemoteParticipants$
|
|
||||||
])
|
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([serviceParticipants, directiveParticipants]) =>
|
map(([serviceParticipants, directiveParticipants]) =>
|
||||||
directiveParticipants !== undefined ? directiveParticipants : serviceParticipants
|
directiveParticipants !== undefined ? directiveParticipants : serviceParticipants
|
||||||
|
@ -219,9 +272,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cdkDrag.released
|
this.cdkDrag.released.pipe(takeUntil(this.destroy$)).subscribe(handler);
|
||||||
.pipe(takeUntil(this.destroy$))
|
|
||||||
.subscribe(handler);
|
|
||||||
|
|
||||||
if (this.globalService.isProduction()) return;
|
if (this.globalService.isProduction()) return;
|
||||||
// Just for allow E2E testing with drag and drop
|
// Just for allow E2E testing with drag and drop
|
||||||
|
|
|
@ -100,7 +100,7 @@ export class PreJoinComponent implements OnInit, OnDestroy {
|
||||||
this.cdkSrv.setSelector('body');
|
this.cdkSrv.setSelector('body');
|
||||||
|
|
||||||
if (this.shouldRemoveTracksWhenComponentIsDestroyed) {
|
if (this.shouldRemoveTracksWhenComponentIsDestroyed) {
|
||||||
this.tracks.forEach((track) => {
|
this.tracks?.forEach((track) => {
|
||||||
track.stop();
|
track.stop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,6 +173,10 @@
|
||||||
<ng-template #stream let-track>
|
<ng-template #stream let-track>
|
||||||
<ng-container *ngTemplateOutlet="openviduAngularStreamTemplate; context: { $implicit: track }"></ng-container>
|
<ng-container *ngTemplateOutlet="openviduAngularStreamTemplate; context: { $implicit: track }"></ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #layoutAdditionalElements>
|
||||||
|
<ng-container *ngTemplateOutlet="ovLayoutAdditionalElementsTemplate"></ng-container>
|
||||||
|
</ng-template>
|
||||||
</ov-layout>
|
</ov-layout>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
import { animate, style, transition, trigger } from '@angular/animations';
|
import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, OnDestroy, Output, TemplateRef, ViewChild } from '@angular/core';
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
EventEmitter,
|
||||||
|
OnDestroy,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
import { Subject, filter, skip, take, takeUntil } from 'rxjs';
|
import { Subject, filter, skip, take, takeUntil } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
|
@ -23,7 +34,12 @@ import { DeviceService } from '../../services/device/device.service';
|
||||||
import { LoggerService } from '../../services/logger/logger.service';
|
import { LoggerService } from '../../services/logger/logger.service';
|
||||||
import { OpenViduService } from '../../services/openvidu/openvidu.service';
|
import { OpenViduService } from '../../services/openvidu/openvidu.service';
|
||||||
import { StorageService } from '../../services/storage/storage.service';
|
import { StorageService } from '../../services/storage/storage.service';
|
||||||
import { TemplateManagerService, TemplateConfiguration, ExternalDirectives, DefaultTemplates } from '../../services/template/template-manager.service';
|
import {
|
||||||
|
TemplateManagerService,
|
||||||
|
TemplateConfiguration,
|
||||||
|
ExternalDirectives,
|
||||||
|
DefaultTemplates
|
||||||
|
} from '../../services/template/template-manager.service';
|
||||||
import { Room } from 'livekit-client';
|
import { Room } from 'livekit-client';
|
||||||
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
|
import { ParticipantLeftEvent, ParticipantModel } from '../../models/participant.model';
|
||||||
import { CustomDevice } from '../../models/device.model';
|
import { CustomDevice } from '../../models/device.model';
|
||||||
|
@ -42,7 +58,11 @@ import {
|
||||||
} from '../../models/recording.model';
|
} from '../../models/recording.model';
|
||||||
import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../models/broadcasting.model';
|
import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from '../../models/broadcasting.model';
|
||||||
import { LangOption } from '../../models/lang.model';
|
import { LangOption } from '../../models/lang.model';
|
||||||
import { ParticipantPanelAfterLocalParticipantDirective, PreJoinDirective } from '../../directives/template/internals.directive';
|
import {
|
||||||
|
LayoutAdditionalElementsDirective,
|
||||||
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
|
PreJoinDirective
|
||||||
|
} from '../../directives/template/internals.directive';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The **VideoconferenceComponent** is the parent of all OpenVidu components.
|
* The **VideoconferenceComponent** is the parent of all OpenVidu components.
|
||||||
|
@ -54,18 +74,21 @@ import { ParticipantPanelAfterLocalParticipantDirective, PreJoinDirective } from
|
||||||
styleUrls: ['./videoconference.component.scss'],
|
styleUrls: ['./videoconference.component.scss'],
|
||||||
animations: [
|
animations: [
|
||||||
trigger('inOutAnimation', [
|
trigger('inOutAnimation', [
|
||||||
transition(':enter', [style({ opacity: 0 }), animate(`${VideoconferenceComponent.ANIMATION_DURATION_MS}ms ease-out`, style({ opacity: 1 }))])
|
transition(':enter', [
|
||||||
|
style({ opacity: 0 }),
|
||||||
|
animate(`${VideoconferenceComponent.ANIMATION_DURATION_MS}ms ease-out`, style({ opacity: 1 }))
|
||||||
|
])
|
||||||
// transition(':leave', [style({ opacity: 1 }), animate('50ms ease-in', style({ opacity: 0.9 }))])
|
// transition(':leave', [style({ opacity: 1 }), animate('50ms ease-in', style({ opacity: 0.9 }))])
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
standalone: false
|
standalone: false
|
||||||
})
|
})
|
||||||
export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
private static readonly PARTICIPANT_NAME_TIMEOUT_MS = 1000;
|
private static readonly PARTICIPANT_NAME_TIMEOUT_MS = 1000;
|
||||||
private static readonly ANIMATION_DURATION_MS = 300;
|
private static readonly ANIMATION_DURATION_MS = 300;
|
||||||
private static readonly MATERIAL_ICONS_URL = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&icon_names=background_replace,keep_off';
|
private static readonly MATERIAL_ICONS_URL =
|
||||||
|
'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&icon_names=background_replace,keep_off';
|
||||||
private static readonly MATERIAL_ICONS_SELECTOR = 'link[href*="Material+Symbols+Outlined"]';
|
private static readonly MATERIAL_ICONS_SELECTOR = 'link[href*="Material+Symbols+Outlined"]';
|
||||||
private static readonly SPINNER_DIAMETER = 50;
|
private static readonly SPINNER_DIAMETER = 50;
|
||||||
// *** Toolbar ***
|
// *** Toolbar ***
|
||||||
|
@ -133,7 +156,12 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ContentChild(ParticipantPanelAfterLocalParticipantDirective) externalParticipantPanelAfterLocalParticipant: ParticipantPanelAfterLocalParticipantDirective;
|
@ContentChild(ParticipantPanelAfterLocalParticipantDirective)
|
||||||
|
externalParticipantPanelAfterLocalParticipant: ParticipantPanelAfterLocalParticipantDirective;
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
@ContentChild(LayoutAdditionalElementsDirective) externalLayoutAdditionalElements: LayoutAdditionalElementsDirective;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -227,6 +255,10 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
openviduAngularPreJoinTemplate: TemplateRef<any>;
|
openviduAngularPreJoinTemplate: TemplateRef<any>;
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
ovLayoutAdditionalElementsTemplate: TemplateRef<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -530,7 +562,8 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
participantPanelItemElements: this.externalParticipantPanelItemElements,
|
participantPanelItemElements: this.externalParticipantPanelItemElements,
|
||||||
layout: this.externalLayout,
|
layout: this.externalLayout,
|
||||||
stream: this.externalStream,
|
stream: this.externalStream,
|
||||||
preJoin: this.externalPreJoin
|
preJoin: this.externalPreJoin,
|
||||||
|
layoutAdditionalElements: this.externalLayoutAdditionalElements
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultTemplates: DefaultTemplates = {
|
const defaultTemplates: DefaultTemplates = {
|
||||||
|
@ -576,7 +609,8 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
this.openviduAngularAdditionalPanelsTemplate = this.templateConfig.additionalPanelsTemplate;
|
this.openviduAngularAdditionalPanelsTemplate = this.templateConfig.additionalPanelsTemplate;
|
||||||
}
|
}
|
||||||
if (this.templateConfig.participantPanelAfterLocalParticipantTemplate) {
|
if (this.templateConfig.participantPanelAfterLocalParticipantTemplate) {
|
||||||
this.openviduAngularParticipantPanelAfterLocalParticipantTemplate = this.templateConfig.participantPanelAfterLocalParticipantTemplate;
|
this.openviduAngularParticipantPanelAfterLocalParticipantTemplate =
|
||||||
|
this.templateConfig.participantPanelAfterLocalParticipantTemplate;
|
||||||
}
|
}
|
||||||
if (this.templateConfig.participantPanelItemElementsTemplate) {
|
if (this.templateConfig.participantPanelItemElementsTemplate) {
|
||||||
this.openviduAngularParticipantPanelItemElementsTemplate = this.templateConfig.participantPanelItemElementsTemplate;
|
this.openviduAngularParticipantPanelItemElementsTemplate = this.templateConfig.participantPanelItemElementsTemplate;
|
||||||
|
@ -584,6 +618,9 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
if (this.templateConfig.preJoinTemplate) {
|
if (this.templateConfig.preJoinTemplate) {
|
||||||
this.openviduAngularPreJoinTemplate = this.templateConfig.preJoinTemplate;
|
this.openviduAngularPreJoinTemplate = this.templateConfig.preJoinTemplate;
|
||||||
}
|
}
|
||||||
|
if (this.templateConfig.layoutAdditionalElementsTemplate) {
|
||||||
|
this.ovLayoutAdditionalElementsTemplate = this.templateConfig.layoutAdditionalElementsTemplate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -624,7 +661,7 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
this.log.w('No participant name available when requesting token');
|
this.log.w('No participant name available when requesting token');
|
||||||
// Wait a bit and try again in case name is still propagating
|
// Wait a bit and try again in case name is still propagating
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const retryName = this.libService.getCurrentParticipantName()|| this.latestParticipantName;
|
const retryName = this.libService.getCurrentParticipantName() || this.latestParticipantName;
|
||||||
if (retryName) {
|
if (retryName) {
|
||||||
this.log.d(`Retrying token request for participant: ${retryName}`);
|
this.log.d(`Retrying token request for participant: ${retryName}`);
|
||||||
this.onTokenRequested.emit(retryName);
|
this.onTokenRequested.emit(retryName);
|
||||||
|
@ -774,9 +811,11 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
this.storageSrv.setParticipantName(name);
|
this.storageSrv.setParticipantName(name);
|
||||||
|
|
||||||
// If we're waiting for a participant name to proceed with joining, do it now
|
// If we're waiting for a participant name to proceed with joining, do it now
|
||||||
if (this.componentState.state === VideoconferenceState.JOINING &&
|
if (
|
||||||
|
this.componentState.state === VideoconferenceState.JOINING &&
|
||||||
this.componentState.isRoomReady &&
|
this.componentState.isRoomReady &&
|
||||||
!this.componentState.showPrejoin) {
|
!this.componentState.showPrejoin
|
||||||
|
) {
|
||||||
this.log.d('Participant name received, proceeding to join');
|
this.log.d('Participant name received, proceeding to join');
|
||||||
this.updateComponentState({
|
this.updateComponentState({
|
||||||
state: VideoconferenceState.READY_TO_CONNECT,
|
state: VideoconferenceState.READY_TO_CONNECT,
|
||||||
|
@ -786,4 +825,4 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,7 @@ export class PreJoinDirective {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The **ovParticipantPanelAfterLocalParticipant** directive allows you to inject custom HTML or Angular templates
|
* The ***ovParticipantPanelAfterLocalParticipant** directive allows you to inject custom HTML or Angular templates
|
||||||
* immediately after the local participant item in the participant panel.
|
* immediately after the local participant item in the participant panel.
|
||||||
* This enables you to extend the participant panel with additional controls, information, or UI elements.
|
* This enables you to extend the participant panel with additional controls, information, or UI elements.
|
||||||
*
|
*
|
||||||
|
@ -221,4 +221,32 @@ export class ParticipantPanelAfterLocalParticipantDirective {
|
||||||
public template: TemplateRef<any>,
|
public template: TemplateRef<any>,
|
||||||
public container: ViewContainerRef
|
public container: ViewContainerRef
|
||||||
) {}
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ***ovLayoutAdditionalElements** directive allows you to inject custom HTML or Angular templates
|
||||||
|
* as additional layout elements within the videoconference UI.
|
||||||
|
* This enables you to extend the layout with extra controls, banners, or any custom UI.
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
* ```html
|
||||||
|
* <ov-videoconference>
|
||||||
|
* <ng-container *ovLayoutAdditionalElements>
|
||||||
|
* <div class="my-custom-layout-element">
|
||||||
|
* <!-- Your custom HTML here -->
|
||||||
|
* <span>Extra layout element</span>
|
||||||
|
* </div>
|
||||||
|
* </ng-container>
|
||||||
|
* </ov-videoconference>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[ovLayoutAdditionalElements]',
|
||||||
|
standalone: false
|
||||||
|
})
|
||||||
|
export class LayoutAdditionalElementsDirective {
|
||||||
|
constructor(
|
||||||
|
public template: TemplateRef<any>,
|
||||||
|
public container: ViewContainerRef
|
||||||
|
) {}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ import {
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
BackgroundEffectsPanelDirective
|
BackgroundEffectsPanelDirective
|
||||||
} from './openvidu-components-angular.directive';
|
} from './openvidu-components-angular.directive';
|
||||||
import { ParticipantPanelAfterLocalParticipantDirective, PreJoinDirective } from './internals.directive';
|
import { LayoutAdditionalElementsDirective, ParticipantPanelAfterLocalParticipantDirective, PreJoinDirective } from './internals.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -31,7 +31,8 @@ import { ParticipantPanelAfterLocalParticipantDirective, PreJoinDirective } from
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
PreJoinDirective,
|
PreJoinDirective,
|
||||||
ParticipantPanelAfterLocalParticipantDirective
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
|
LayoutAdditionalElementsDirective
|
||||||
// BackgroundEffectsPanelDirective
|
// BackgroundEffectsPanelDirective
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -48,7 +49,8 @@ import { ParticipantPanelAfterLocalParticipantDirective, PreJoinDirective } from
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
PreJoinDirective,
|
PreJoinDirective,
|
||||||
ParticipantPanelAfterLocalParticipantDirective
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
|
LayoutAdditionalElementsDirective
|
||||||
// BackgroundEffectsPanelDirective
|
// BackgroundEffectsPanelDirective
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,7 +15,11 @@ import {
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
ToolbarDirective
|
ToolbarDirective
|
||||||
} from '../../directives/template/openvidu-components-angular.directive';
|
} from '../../directives/template/openvidu-components-angular.directive';
|
||||||
import { PreJoinDirective, ParticipantPanelAfterLocalParticipantDirective } from '../../directives/template/internals.directive';
|
import {
|
||||||
|
PreJoinDirective,
|
||||||
|
ParticipantPanelAfterLocalParticipantDirective,
|
||||||
|
LayoutAdditionalElementsDirective
|
||||||
|
} from '../../directives/template/internals.directive';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration object for all templates in the videoconference component
|
* Configuration object for all templates in the videoconference component
|
||||||
|
@ -41,6 +45,7 @@ export interface TemplateConfiguration {
|
||||||
// Layout templates
|
// Layout templates
|
||||||
layoutTemplate: TemplateRef<any>;
|
layoutTemplate: TemplateRef<any>;
|
||||||
streamTemplate: TemplateRef<any>;
|
streamTemplate: TemplateRef<any>;
|
||||||
|
layoutAdditionalElementsTemplate?: TemplateRef<any>;
|
||||||
|
|
||||||
// PreJoin template
|
// PreJoin template
|
||||||
preJoinTemplate?: TemplateRef<any>;
|
preJoinTemplate?: TemplateRef<any>;
|
||||||
|
@ -66,6 +71,14 @@ export interface ToolbarTemplateConfiguration {
|
||||||
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
|
toolbarAdditionalPanelButtonsTemplate?: TemplateRef<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object for layout component templates
|
||||||
|
*/
|
||||||
|
export interface LayoutTemplateConfiguration {
|
||||||
|
layoutStreamTemplate?: TemplateRef<any>;
|
||||||
|
layoutAdditionalElementsTemplate?: TemplateRef<any>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration object for participants panel component templates
|
* Configuration object for participants panel component templates
|
||||||
*/
|
*/
|
||||||
|
@ -108,6 +121,7 @@ export interface ExternalDirectives {
|
||||||
layout?: LayoutDirective;
|
layout?: LayoutDirective;
|
||||||
stream?: StreamDirective;
|
stream?: StreamDirective;
|
||||||
preJoin?: PreJoinDirective;
|
preJoin?: PreJoinDirective;
|
||||||
|
layoutAdditionalElements?: LayoutAdditionalElementsDirective;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,6 +194,11 @@ export class TemplateManagerService {
|
||||||
this.log.d('Setting EXTERNAL PARTICIPANT PANEL ITEM ELEMENTS');
|
this.log.d('Setting EXTERNAL PARTICIPANT PANEL ITEM ELEMENTS');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (externalDirectives.layoutAdditionalElements) {
|
||||||
|
this.log.d('Setting EXTERNAL ADDITIONAL LAYOUT ELEMENTS');
|
||||||
|
config.layoutAdditionalElementsTemplate = externalDirectives.layoutAdditionalElements.template;
|
||||||
|
}
|
||||||
|
|
||||||
this.log.d('Template setup completed', config);
|
this.log.d('Template setup completed', config);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
@ -349,6 +368,21 @@ export class TemplateManagerService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up templates for the LayoutComponent
|
||||||
|
*/
|
||||||
|
setupLayoutTemplates(
|
||||||
|
externalStream?: StreamDirective,
|
||||||
|
externalLayoutAdditionalElements?: LayoutAdditionalElementsDirective
|
||||||
|
): LayoutTemplateConfiguration {
|
||||||
|
this.log.d('Setting up layout templates...');
|
||||||
|
|
||||||
|
return {
|
||||||
|
layoutStreamTemplate: externalStream?.template,
|
||||||
|
layoutAdditionalElementsTemplate: externalLayoutAdditionalElements?.template
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up templates for the ParticipantsPanelComponent
|
* Sets up templates for the ParticipantsPanelComponent
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue