diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts index 538e9a14..447ceb89 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts @@ -2,9 +2,54 @@ import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; /** * The ***ovToolbar** directive allows to replace the default toolbar component injecting your custom template. + * In the example below we've replaced the default toolbar and added the **toggleAudio** and **toggleVide** features. * - * @example - * + * *You can run the sample [here]()*. + * + * + *```html + * + *
+ * + * + *
+ *
+ * ``` + * + * We have used the {@link OpenViduService} for publishing/unpublishing the audio and video. + * + * ```javascript + * export class ToolbarDirectiveComponent { + * tokens: TokenModel; + * sessionId = 'toolbar-directive-example'; + * OPENVIDU_URL = 'https://localhost:4443'; + * OPENVIDU_SECRET = 'MY_SECRET'; + * publishVideo = true; + * publishAudio = true; + * constructor(private restService: RestService, private openviduService: OpenViduService) {} + * + * async onJoinButtonClicked() { + * this.tokens = { + * webcam: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET), + * screen: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET) + * }; + * } + * + * toggleVideo() { + * this.publishVideo = !this.publishVideo; + * this.openviduService.publishVideo(this.publishVideo); + * } + * + * toggleAudio() { + * this.publishAudio = !this.publishAudio; + * this.openviduService.publishAudio(this.publishAudio); + * } + * } + * ``` + * + *
+ * + *
* */ @Directive({ @@ -18,30 +63,56 @@ export class ToolbarDirective { } /** - * The ***ovToolbarAdditionalButtons** directive allows to add additional buttons to the toolbar. - * There are two ways to use this directive: + * The ***ovToolbarAdditionalButtons** directive allows to add additional buttons to the toolbar. We've added the same buttons as the {@link ToolbarDirective}. + * Here we are using the {@link ParticipantService} fror checking the audio or video status. * - * 1. Adding it to an element as a child of the parent element **_ov-videoconference_** {@link VideoconferenceComponent} - * @example - * - *
- * - * + * _You can check the sample [here]()_. + * + * + *```html + * + *
+ * + * *
*
+ * ``` * - *
- * 2. Adding it to an element as child of the element tagged with the {@link ToolbarDirective} - * @example - * - * - *
- * - * - *
- *
- *
+ * ```javascript + * export class ToolbarAdditionalButtonsDirectiveComponent { + * tokens: TokenModel; + * sessionId = 'toolbar-additionalbtn-directive-example'; * + * OPENVIDU_URL = 'https://localhost:4443'; + * OPENVIDU_SECRET = 'MY_SECRET'; + * + * constructor( + * private restService: RestService, + * private openviduService: OpenViduService, + * private participantService: ParticipantService + * ) {} + * + * async onJoinButtonClicked() { + * this.tokens = { + * webcam: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET), + * screen: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET) + * }; + * } + * + * toggleVideo() { + * const publishVideo = !this.participantService.isMyVideoActive(); + * this.openviduService.publishVideo(publishVideo); + * } + * + * toggleAudio() { + * const publishAudio = !this.participantService.isMyAudioActive(); + * this.openviduService.publishAudio(publishAudio); + * } + * } + * ``` + *
+ * + *
*/ @Directive({ selector: '[ovToolbarAdditionalButtons]' @@ -53,18 +124,6 @@ export class ToolbarAdditionalButtonsDirective { constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} } -/** - * The ***ovPanel** directive allows to replace the default panel component injecting your custom template. - * - * This directive is closely related to {@link ChatPanelDirective} and {@link ParticipantsPanelDirective}. - * - * - * @example - * - * ... - * - * - */ @Directive({ selector: '[ovPanel]' @@ -75,29 +134,77 @@ export class PanelDirective { /** - * The ***ovChatPanel** directive allows to replace the defaultchat panel template injecting your own component. - * There are two ways to use this directive: + * The ***ovChatPanel** directive allows to replace the default chat panel template injecting your own component. + * Here we're going to redefine the chat template in a few code lines. * - * 1. Adding it to an element as a child of the parent element **_ov-videoconference_** {@link VideoconferenceComponent} - * @example - * - * + * _You can check the sample [here]()_. + * + * ```html + * + *
+ *

Chat

+ *
+ *
    + *
  • {{ msg }}
  • + *
+ *
+ * + * + *
*
+ *``` * - *
- * 2. Adding it to an element as child of the element tagged with the {@link ToolbarDirective} - * @example - * - * - * - * - * * - *
- * INFO: - * You also can use the default components adsada dasda d asd + * As we need to get the OpenVidu Browser [Session](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Session.html) + * for sending messages to others, we can get it from the `onSessionCreated` event fired by the {@link VideoconferenceComponent} when the session has been created. + * + * Once we have the session created, we can use the `signal` method for sending our messages. + * + * + * + * ```javascript + * export class ChatPanelDirectiveComponent { + * tokens: TokenModel; + * sessionId = 'chat-panel-directive-example'; + * OPENVIDU_URL = 'https://localhost:4443'; + * OPENVIDU_SECRET = 'MY_SECRET'; + * session: Session; + * messages: string[] = []; + * constructor(private restService: RestService) {} + * + * async onJoinButtonClicked() { + * this.tokens = { + * webcam: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET), + * screen: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET) + * }; + * } + * + * onSessionCreated(session: Session) { + * this.session = session; + * this.session.on(`signal:${Signal.CHAT}`, (event: any) => { + * const msg = JSON.parse(event.data).message; + * this.messages.push(msg); + * }); + * } + * + * send(message: string): void { + * const signalOptions: SignalOptions = { + * data: JSON.stringify({ message }), + * type: Signal.CHAT, + * to: undefined + * }; + * this.session.signal(signalOptions); + * } + *} + * ``` + * + *
+ * *
- * */ @Directive({ selector: '[ovChatPanel]' @@ -106,6 +213,79 @@ export class ChatPanelDirective { constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} } +/** + * The ***ovParticipantsPanel** directive allows to replace the default participants panel template injecting your own component. + * Here we're going to redefine the participants template in a few code lines. + * + * _You can check the sample [here]()_. + * + * ```html + * + *
+ *
    + *
  • {{localParticipant.nickname}}
  • + *
+ * + *
    + *
  • {{p.nickname}}
  • + *
+ *
+ *
+ *``` + * + * + * As we need to get the participants in our session, we are subscribing to them using the {@link ParticipantService}. We'll get the local participant + * and the remote participants and we will be able to update the participants panel on every update. + * + * + * ```javascript + * export class ParticipantsPanelDirectiveComponent implements OnInit, OnDestroy { + * tokens: TokenModel; + * sessionId = 'participants-panel-directive-example'; + * OPENVIDU_URL = 'https://localhost:4443'; + * OPENVIDU_SECRET = 'MY_SECRET'; + * localParticipant: ParticipantAbstractModel; + * remoteParticipants: ParticipantAbstractModel[]; + * localParticipantSubs: Subscription; + * remoteParticipantsSubs: Subscription; + * + * constructor( + * private restService: RestService, + * private participantService: ParticipantService + * ) {} + * + * ngOnInit(): void { + * this.subscribeToParticipants(); + * } + * + * ngOnDestroy() { + * this.localParticipantSubs.unsubscribe(); + * this.remoteParticipantsSubs.unsubscribe(); + * } + * + * async onJoinButtonClicked() { + * this.tokens = { + * webcam: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET), + * screen: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET) + * }; + * } + * subscribeToParticipants() { + * this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => { + * this.localParticipant = p; + * }); + * + * this.remoteParticipantsSubs = this.participantService.remoteParticipantsObs.subscribe((participants) => { + * this.remoteParticipants = participants; + * }); + * } + * } + * + * ``` + * + *
+ * + *
+ */ @Directive({ selector: '[ovParticipantsPanel]' }) @@ -127,6 +307,80 @@ export class ParticipantPanelItemElementsDirective { constructor(public template: TemplateRef, public viewContainer: ViewContainerRef) {} } + +/** + * + * The ***ovLayout** directive allows us replacing the default layout with ours. As we have to add a stream for each participant, + * we must get the local and remote participants. + * + * As the deafult {@link StreamComponent} needs the participant stream, and as the participants streams extraction is not trivial, + * openvidu-angular provides us a {@link ParticipantStreamsPipe}for extracting the streams of each participant with ease. + * + * _You can check the sample [here]()_. + * + * ```html + * + *
+ *
+ *
+ * + *
+ *
+ * + *
+ *
+ *
+ *
+ *``` + * + * ```javascript + * export class LayoutDirectiveComponent implements OnInit, OnDestroy { + * tokens: TokenModel; + * sessionId = 'participants-panel-directive-example'; + * OPENVIDU_URL = 'https://localhost:4443'; + * OPENVIDU_SECRET = 'MY_SECRET'; + * localParticipant: ParticipantAbstractModel; + * remoteParticipants: ParticipantAbstractModel[]; + * localParticipantSubs: Subscription; + * remoteParticipantsSubs: Subscription; + * + * constructor( + * private restService: RestService, + * private participantService: ParticipantService + * ) {} + * + * ngOnInit(): void { + * this.subscribeToParticipants(); + * } + * + * ngOnDestroy() { + * this.localParticipantSubs.unsubscribe(); + * this.remoteParticipantsSubs.unsubscribe(); + * } + * + * async onJoinButtonClicked() { + * this.tokens = { + * webcam: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET), + * screen: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET) + * }; + * } + * subscribeToParticipants() { + * this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => { + * this.localParticipant = p; + * }); + * + * this.remoteParticipantsSubs = this.participantService.remoteParticipantsObs.subscribe((participants) => { + * this.remoteParticipants = participants; + * }); + * } + * } + * + * ``` + * + *
+ * + *
+ */ @Directive({ selector: '[ovLayout]' }) @@ -135,29 +389,42 @@ export class LayoutDirective { } /** - * The ***ovStream** directive allows to replace the default stream component template injecting your own component. - * There are two ways to use this directive: + * The ***ovStream** directive allows to replace the default {@link StreamComponent} template injecting your own component. + * In the example below, we have to customize the nickname position and styles replacing the default stream. * - * 1. Adding it to an element as a child of the parent element **_ov-videoconference_** {@link VideoconferenceComponent} - * @example - * + * With ***ovStream** directive we can access to the stream object from its context using the `let` keyword and + * referencing to the `stream` variable: `*ovStream="let stream"`. Now we can access to the {@link StreamModel} object. * + * _You can check the sample [here]()_. * - * + * ```html + * + *
+ * + *

{{stream.participant.nickname}}

+ *
*
+ * ``` * - *
- * 2. Adding it to an element as child of the element tagged with the {@link ToolbarDirective} - * @example - * - * - * - * - * + * ```javascript + * export class StreamDirectiveComponent { + * tokens: TokenModel; + * sessionId = 'toolbar-directive-example'; + * OPENVIDU_URL = 'https://localhost:4443'; + * OPENVIDU_SECRET = 'MY_SECRET'; * - *
- * INFO: - * You also can use the default components adsada dasda d asd + * constructor(private restService: RestService) {} + * + * async onJoinButtonClicked() { + * this.tokens = { + * webcam: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET), + * screen: await this.restService.getToken(this.sessionId, this.OPENVIDU_URL, this.OPENVIDU_SECRET) + * }; + * } + * } + * ``` + *
+ * *
* */ diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts index a6f4817a..12ede04a 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/pipes/participant.pipe.ts @@ -1,9 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import { StreamModel, ParticipantAbstractModel } from '../models/participant.model'; -/** - * @internal - */ @Pipe({ name: 'streams' }) export class ParticipantStreamsPipe implements PipeTransform { constructor() {} diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts index 4f9ca494..3e68f17f 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts @@ -212,7 +212,7 @@ export class OpenViduService { private unpublish(publisher: Publisher): void { if (!!publisher) { if (publisher === this.participantService.getMyCameraPublisher()) { - this.publishAudioAux(this.participantService.getMyScreenPublisher(), this.participantService.hasCameraAudioActive()); + this.publishAudioAux(this.participantService.getMyScreenPublisher(), this.participantService.isMyAudioActive()); this.webcamSession.unpublish(publisher); } else if (publisher === this.participantService.getMyScreenPublisher()) { this.screenSession.unpublish(publisher); @@ -221,8 +221,7 @@ export class OpenViduService { } async publishVideo(publish: boolean): Promise { - const publishAudio = this.participantService.hasCameraAudioActive(); - // const publishVideo = !this.participantService.hasCameraVideoActive(); + const publishAudio = this.participantService.isMyAudioActive(); // Disabling webcam if (this.participantService.haveICameraAndScreenActive()) { @@ -279,8 +278,8 @@ export class OpenViduService { } else if (this.participantService.isOnlyMyCameraActive()) { // I only have the camera published const hasAudioDevicesAvailable = this.deviceService.hasAudioDeviceAvailable(); - const willWebcamBePresent = this.participantService.isMyCameraActive() && this.participantService.hasCameraVideoActive(); - const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && this.participantService.hasCameraAudioActive(); + const willWebcamBePresent = this.participantService.isMyCameraActive() && this.participantService.isMyVideoActive(); + const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && this.participantService.isMyAudioActive(); console.warn('will be audio active', hasAudio); const properties: PublisherProperties = { @@ -309,7 +308,7 @@ export class OpenViduService { await this.connectSession(this.getScreenSession(), this.tokenService.getScreenToken()); } await this.publish(this.participantService.getMyScreenPublisher()); - if (!this.participantService.hasCameraVideoActive()) { + if (!this.participantService.isMyVideoActive()) { // Disabling webcam this.participantService.disableWebcamStream(); this.unpublish(this.participantService.getMyCameraPublisher()); @@ -367,8 +366,8 @@ export class OpenViduService { const properties: PublisherProperties = { videoSource: this.videoSource, audioSource: this.audioSource, - publishVideo: this.participantService.hasCameraVideoActive(), - publishAudio: this.participantService.hasCameraAudioActive(), + publishVideo: this.participantService.isMyVideoActive(), + publishAudio: this.participantService.isMyAudioActive(), mirror }; @@ -392,7 +391,7 @@ export class OpenViduService { sendSignal(type: Signal, connections?: Connection[], data?: any): void { const signalOptions: SignalOptions = { data: JSON.stringify(data), - type: type, + type, to: connections && connections.length > 0 ? connections : undefined }; this.webcamSession.signal(signalOptions); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts index 64614bb2..76479430 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts @@ -11,17 +11,9 @@ import { LoggerService } from '../logger/logger.service'; providedIn: 'root' }) export class ParticipantService { - /** - * @internal - * Local participants observables - */ localParticipantObs: Observable; protected _localParticipant = >new BehaviorSubject(null); - /** - * @internal - * Remote participants observables - */ remoteParticipantsObs: Observable; protected _remoteParticipants = >new BehaviorSubject([]); @@ -189,6 +181,14 @@ export class ParticipantService { return this.localParticipant.isCameraActive(); } + isMyVideoActive(): boolean { + return this.localParticipant.isCameraVideoActive(); + } + + isMyAudioActive(): boolean { + return this.localParticipant?.isCameraAudioActive() || this.localParticipant?.isScreenAudioActive(); + } + /** * @internal */ @@ -217,19 +217,6 @@ export class ParticipantService { return this.isMyCameraActive() && this.isMyScreenActive(); } - /** - * @internal - */ - hasCameraVideoActive(): boolean { - return this.localParticipant.isCameraVideoActive(); - } - - /** - * @internal - */ - hasCameraAudioActive(): boolean { - return this.localParticipant?.isCameraAudioActive(); - } /** * @internal @@ -383,6 +370,9 @@ export class ParticipantService { } } + /** + * @internal + */ setRemoteMutedForcibly(id: string, value: boolean) { const participant = this.getRemoteParticipantById(id); if (participant) { diff --git a/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts b/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts index cf54962e..17f2ef45 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/public-api.ts @@ -42,6 +42,7 @@ export * from './lib/models/logger.model'; export * from './lib/models/video-type.model'; export * from './lib/models/notification-options.model'; export * from './lib/models/token.model'; +export * from './lib/models/signal.model'; // Pipes export * from './lib/pipes/participant.pipe'; diff --git a/openvidu-components-angular/src/assets/doc/chatPanelDirective-example.png b/openvidu-components-angular/src/assets/doc/chatPanelDirective-example.png new file mode 100644 index 00000000..74c589ce Binary files /dev/null and b/openvidu-components-angular/src/assets/doc/chatPanelDirective-example.png differ diff --git a/openvidu-components-angular/src/assets/doc/layoutDirective-example.png b/openvidu-components-angular/src/assets/doc/layoutDirective-example.png new file mode 100644 index 00000000..147f1ab0 Binary files /dev/null and b/openvidu-components-angular/src/assets/doc/layoutDirective-example.png differ diff --git a/openvidu-components-angular/src/assets/doc/participantsPanelDirective-example.png b/openvidu-components-angular/src/assets/doc/participantsPanelDirective-example.png new file mode 100644 index 00000000..9f1af8a3 Binary files /dev/null and b/openvidu-components-angular/src/assets/doc/participantsPanelDirective-example.png differ diff --git a/openvidu-components-angular/src/assets/doc/streamDirective-example.png b/openvidu-components-angular/src/assets/doc/streamDirective-example.png new file mode 100644 index 00000000..9bb2a21a Binary files /dev/null and b/openvidu-components-angular/src/assets/doc/streamDirective-example.png differ diff --git a/openvidu-components-angular/src/assets/doc/toolbarAdditionalButtonsDirective-example.png b/openvidu-components-angular/src/assets/doc/toolbarAdditionalButtonsDirective-example.png new file mode 100644 index 00000000..07b0abed Binary files /dev/null and b/openvidu-components-angular/src/assets/doc/toolbarAdditionalButtonsDirective-example.png differ diff --git a/openvidu-components-angular/src/assets/doc/toolbardirective-example.png b/openvidu-components-angular/src/assets/doc/toolbardirective-example.png new file mode 100644 index 00000000..c6c60ce3 Binary files /dev/null and b/openvidu-components-angular/src/assets/doc/toolbardirective-example.png differ diff --git a/openvidu-components-angular/src/doc/.compodocrc.json b/openvidu-components-angular/src/doc/.compodocrc.json index cb307775..37b631fa 100644 --- a/openvidu-components-angular/src/doc/.compodocrc.json +++ b/openvidu-components-angular/src/doc/.compodocrc.json @@ -16,5 +16,5 @@ "theme": "gitbook", "customFavicon": "src/favicon.ico", "extTheme": "src/doc/", - "assetsFolder": "src/assets/images/app.gif" + "assetsFolder": "src/assets/doc" } diff --git a/openvidu-components-angular/src/doc/tsconfig.doc.json b/openvidu-components-angular/src/doc/tsconfig.doc.json index 7dbbc46a..a95801dc 100644 --- a/openvidu-components-angular/src/doc/tsconfig.doc.json +++ b/openvidu-components-angular/src/doc/tsconfig.doc.json @@ -4,6 +4,7 @@ "../../projects/openvidu-angular/src/lib/directives/**/*.ts", "../../projects/openvidu-angular/src/lib/services/**/*.ts", "../../projects/openvidu-angular/src/lib/models/**/*.ts", + "../../projects/openvidu-angular/src/lib/pipes/**/*.ts", "../app/openvidu-webcomponent/**/*.ts" ], "exclude": [