mirror of https://github.com/OpenVidu/openvidu.git
ov-components: add PreJoin directive to support custom pre-join templates in VideoconferenceComponent
parent
4dd007395f
commit
c304c9c761
|
@ -5,15 +5,20 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [@inOutAnimation] id="pre-join-container" *ngIf="showPrejoin && !loading">
|
<div [@inOutAnimation] id="pre-join-container" *ngIf="showPrejoin && !loading">
|
||||||
<ov-pre-join
|
<ng-container *ngIf="openviduAngularPreJoinTemplate; else defaultPreJoin">
|
||||||
[error]="_tokenError"
|
<ng-container *ngTemplateOutlet="openviduAngularPreJoinTemplate"></ng-container>
|
||||||
(onReadyToJoin)="_onReadyToJoin()"
|
</ng-container>
|
||||||
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
|
<ng-template #defaultPreJoin>
|
||||||
(onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)"
|
<ov-pre-join
|
||||||
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
|
[error]="_tokenError"
|
||||||
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
|
(onReadyToJoin)="_onReadyToJoin()"
|
||||||
(onLangChanged)="onLangChanged.emit($event)"
|
(onVideoDeviceChanged)="onVideoDeviceChanged.emit($event)"
|
||||||
></ov-pre-join>
|
(onVideoEnabledChanged)="onVideoEnabledChanged.emit($event)"
|
||||||
|
(onAudioDeviceChanged)="onAudioDeviceChanged.emit($event)"
|
||||||
|
(onAudioEnabledChanged)="onAudioEnabledChanged.emit($event)"
|
||||||
|
(onLangChanged)="onLangChanged.emit($event)"
|
||||||
|
></ov-pre-join>
|
||||||
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="spinner" *ngIf="!loading && error">
|
<div id="spinner" *ngIf="!loading && error">
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
ParticipantPanelItemDirective,
|
ParticipantPanelItemDirective,
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ParticipantsPanelDirective,
|
ParticipantsPanelDirective,
|
||||||
|
PreJoinDirective,
|
||||||
StreamDirective,
|
StreamDirective,
|
||||||
ToolbarAdditionalButtonsDirective,
|
ToolbarAdditionalButtonsDirective,
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
|
@ -114,6 +115,12 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
*/
|
*/
|
||||||
@ContentChild(StreamDirective) externalStream: StreamDirective;
|
@ContentChild(StreamDirective) externalStream: StreamDirective;
|
||||||
|
|
||||||
|
// *** PreJoin ***
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
@ContentChild(PreJoinDirective) externalPreJoin: PreJoinDirective;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
@ -198,6 +205,10 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
openviduAngularStreamTemplate: TemplateRef<any>;
|
openviduAngularStreamTemplate: TemplateRef<any>;
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
openviduAngularPreJoinTemplate: TemplateRef<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides event notifications that fire when the local participant is ready to join to the room.
|
* Provides event notifications that fire when the local participant is ready to join to the room.
|
||||||
|
@ -435,6 +446,7 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
this.setupToolbarTemplate();
|
this.setupToolbarTemplate();
|
||||||
this.setupPanelTemplate();
|
this.setupPanelTemplate();
|
||||||
this.setupLayoutTemplate();
|
this.setupLayoutTemplate();
|
||||||
|
this.setupPreJoinTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -576,6 +588,19 @@ export class VideoconferenceComponent implements OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private setupPreJoinTemplate(): void {
|
||||||
|
if (this.externalPreJoin) {
|
||||||
|
this.log.d('Setting EXTERNAL PREJOIN');
|
||||||
|
this.openviduAngularPreJoinTemplate = this.externalPreJoin.template;
|
||||||
|
} else {
|
||||||
|
this.log.d('Setting DEFAULT PREJOIN');
|
||||||
|
// Keep the default behavior - no template is set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,7 +12,8 @@ import {
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
AdditionalPanelsDirective,
|
AdditionalPanelsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
BackgroundEffectsPanelDirective
|
BackgroundEffectsPanelDirective,
|
||||||
|
PreJoinDirective
|
||||||
} from './openvidu-components-angular.directive';
|
} from './openvidu-components-angular.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -29,6 +30,7 @@ import {
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
|
PreJoinDirective,
|
||||||
// BackgroundEffectsPanelDirective
|
// BackgroundEffectsPanelDirective
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -44,6 +46,7 @@ import {
|
||||||
ToolbarAdditionalPanelButtonsDirective,
|
ToolbarAdditionalPanelButtonsDirective,
|
||||||
ParticipantPanelItemElementsDirective,
|
ParticipantPanelItemElementsDirective,
|
||||||
ActivitiesPanelDirective,
|
ActivitiesPanelDirective,
|
||||||
|
PreJoinDirective,
|
||||||
// BackgroundEffectsPanelDirective
|
// BackgroundEffectsPanelDirective
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -1814,3 +1814,197 @@ export class StreamDirective {
|
||||||
public container: ViewContainerRef
|
public container: ViewContainerRef
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ***ovPreJoin** directive empowers you to substitute the default pre-join component template with a custom one.
|
||||||
|
* This directive allows you to create a completely custom pre-join experience while maintaining the core functionality.
|
||||||
|
*
|
||||||
|
* In the example below, we demonstrate how to replace the pre-join template with a custom one that includes
|
||||||
|
* device selection and a custom join button.
|
||||||
|
*
|
||||||
|
* <!--ovPreJoin-start-tutorial-->
|
||||||
|
* ```typescript
|
||||||
|
* import { HttpClient } from '@angular/common/http';
|
||||||
|
* import { Component } from '@angular/core';
|
||||||
|
* import { lastValueFrom } from 'rxjs';
|
||||||
|
* import { FormsModule } from '@angular/forms';
|
||||||
|
*
|
||||||
|
* import {
|
||||||
|
* DeviceService,
|
||||||
|
* ParticipantService,
|
||||||
|
* OpenViduComponentsModule,
|
||||||
|
* } from 'openvidu-components-angular';
|
||||||
|
*
|
||||||
|
* @Component({
|
||||||
|
* selector: 'app-root',
|
||||||
|
* template: `
|
||||||
|
* <ov-videoconference
|
||||||
|
* [token]="token"
|
||||||
|
* [livekitUrl]="LIVEKIT_URL"
|
||||||
|
* (onTokenRequested)="onTokenRequested($event)"
|
||||||
|
* (onReadyToJoin)="onReadyToJoin()"
|
||||||
|
* >
|
||||||
|
* <!-- Custom Pre-Join Component -->
|
||||||
|
* <div *ovPreJoin class="custom-prejoin">
|
||||||
|
* <h2>Join Meeting</h2>
|
||||||
|
* <div class="prejoin-form">
|
||||||
|
* <input
|
||||||
|
* type="text"
|
||||||
|
* placeholder="Enter your name"
|
||||||
|
* [(ngModel)]="participantName"
|
||||||
|
* class="name-input"
|
||||||
|
* />
|
||||||
|
* <button
|
||||||
|
* (click)="joinMeeting()"
|
||||||
|
* [disabled]="!participantName"
|
||||||
|
* class="join-button"
|
||||||
|
* >
|
||||||
|
* Join Meeting
|
||||||
|
* </button>
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
* </ov-videoconference>
|
||||||
|
* `,
|
||||||
|
* styles: `
|
||||||
|
* .custom-prejoin {
|
||||||
|
* display: flex;
|
||||||
|
* flex-direction: column;
|
||||||
|
* align-items: center;
|
||||||
|
* justify-content: center;
|
||||||
|
* height: 100vh;
|
||||||
|
* background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
* color: white;
|
||||||
|
* }
|
||||||
|
* .prejoin-form {
|
||||||
|
* display: flex;
|
||||||
|
* flex-direction: column;
|
||||||
|
* gap: 20px;
|
||||||
|
* align-items: center;
|
||||||
|
* }
|
||||||
|
* .name-input {
|
||||||
|
* padding: 12px;
|
||||||
|
* border: none;
|
||||||
|
* border-radius: 8px;
|
||||||
|
* font-size: 16px;
|
||||||
|
* min-width: 250px;
|
||||||
|
* }
|
||||||
|
* .join-button {
|
||||||
|
* padding: 12px 24px;
|
||||||
|
* background: #4CAF50;
|
||||||
|
* color: white;
|
||||||
|
* border: none;
|
||||||
|
* border-radius: 8px;
|
||||||
|
* font-size: 16px;
|
||||||
|
* cursor: pointer;
|
||||||
|
* transition: background 0.3s;
|
||||||
|
* }
|
||||||
|
* .join-button:hover:not(:disabled) {
|
||||||
|
* background: #45a049;
|
||||||
|
* }
|
||||||
|
* .join-button:disabled {
|
||||||
|
* background: #cccccc;
|
||||||
|
* cursor: not-allowed;
|
||||||
|
* }
|
||||||
|
* `,
|
||||||
|
* standalone: true,
|
||||||
|
* imports: [OpenViduComponentsModule, FormsModule],
|
||||||
|
* })
|
||||||
|
* export class AppComponent {
|
||||||
|
* // For local development, leave these variables empty
|
||||||
|
* // For production, configure them with correct URLs depending on your deployment
|
||||||
|
* APPLICATION_SERVER_URL = '';
|
||||||
|
* LIVEKIT_URL = '';
|
||||||
|
*
|
||||||
|
* // Define the name of the room and initialize the token variable
|
||||||
|
* roomName = 'custom-prejoin';
|
||||||
|
* token!: string;
|
||||||
|
* participantName: string = '';
|
||||||
|
*
|
||||||
|
* constructor(
|
||||||
|
* private httpClient: HttpClient,
|
||||||
|
* private deviceService: DeviceService,
|
||||||
|
* private participantService: ParticipantService
|
||||||
|
* ) {
|
||||||
|
* this.configureUrls();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* private configureUrls() {
|
||||||
|
* // If APPLICATION_SERVER_URL is not configured, use default value from local development
|
||||||
|
* if (!this.APPLICATION_SERVER_URL) {
|
||||||
|
* if (window.location.hostname === 'localhost') {
|
||||||
|
* this.APPLICATION_SERVER_URL = 'http://localhost:6080/';
|
||||||
|
* } else {
|
||||||
|
* this.APPLICATION_SERVER_URL =
|
||||||
|
* 'https://' + window.location.hostname + ':6443/';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // If LIVEKIT_URL is not configured, use default value from local development
|
||||||
|
* if (!this.LIVEKIT_URL) {
|
||||||
|
* if (window.location.hostname === 'localhost') {
|
||||||
|
* this.LIVEKIT_URL = 'ws://localhost:7880/';
|
||||||
|
* } else {
|
||||||
|
* this.LIVEKIT_URL = 'wss://' + window.location.hostname + ':7443/';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Function to request a token when a participant joins the room
|
||||||
|
* async onTokenRequested(participantName: string) {
|
||||||
|
* const { token } = await this.getToken(this.roomName, participantName);
|
||||||
|
* this.token = token;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Function called when ready to join
|
||||||
|
* onReadyToJoin() {
|
||||||
|
* console.log('Ready to join the meeting');
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Function to join the meeting
|
||||||
|
* async joinMeeting() {
|
||||||
|
* if (this.participantName.trim()) {
|
||||||
|
* // Request token with the participant name
|
||||||
|
* await this.onTokenRequested(this.participantName);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Function to get a token from the server
|
||||||
|
* getToken(roomName: string, participantName: string): Promise<any> {
|
||||||
|
* try {
|
||||||
|
* // Send a POST request to the server to obtain a token
|
||||||
|
* return lastValueFrom(
|
||||||
|
* this.httpClient.post<any>(this.APPLICATION_SERVER_URL + 'token', {
|
||||||
|
* roomName,
|
||||||
|
* participantName,
|
||||||
|
* })
|
||||||
|
* );
|
||||||
|
* } catch (error: any) {
|
||||||
|
* // Handle errors, e.g., if the server is not reachable
|
||||||
|
* if (error.status === 404) {
|
||||||
|
* throw {
|
||||||
|
* status: error.status,
|
||||||
|
* message:
|
||||||
|
* 'Cannot connect with the backend. ' + error.url + ' not found',
|
||||||
|
* };
|
||||||
|
* }
|
||||||
|
* throw error;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* <!--ovPreJoin-end-tutorial-->
|
||||||
|
*
|
||||||
|
* For a detailed tutorial on customizing the pre-join component, please visit [this link](https://openvidu.io/latest/docs/tutorials/angular-components/openvidu-custom-prejoin/).
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ovPreJoin]',
|
||||||
|
standalone: false
|
||||||
|
})
|
||||||
|
export class PreJoinDirective {
|
||||||
|
constructor(
|
||||||
|
public template: TemplateRef<any>,
|
||||||
|
public container: ViewContainerRef
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue