Improve basic Angular tutorial

This commit is contained in:
juancarmore 2024-05-07 17:31:56 +02:00
parent 186d6d5806
commit 031acf3db1
10 changed files with 96 additions and 68 deletions

View File

@ -1,27 +1,30 @@
# Angular
# Basic Angular
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.6.
Basic client application built with Angular. It internally uses [livekit-client-sdk-js](https://docs.livekit.io/client-sdk-js/).
## Development server
For further information, check the [tutorial documentation](https://livekit-tutorials.openvidu.io/basic/client/angular).
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Prerequisites
## Code scaffolding
- [Node](https://nodejs.org/en/download)
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Run
## Build
1. Download repository
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
```bash
git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git
cd openvidu-livekit-tutorials/client/angular
```
## Running unit tests
2. Install dependencies
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
```bash
npm install
```
## Running end-to-end tests
3. Run the application
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
```bash
npm start
```

View File

@ -124,6 +124,11 @@
#join-dialog h2 {
font-size: 40px;
}
#layout-container {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
}
@media screen and (max-width: 480px) {
@ -135,4 +140,8 @@
#join-dialog h2 {
font-size: 30px;
}
#layout-container {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
}

View File

@ -31,11 +31,17 @@
[participantIdentity]="roomForm.value.participantName!"
[isLocal]="true"
></video-component>
} @for (publication of remoteTrackPublications.keys(); track publication.trackSid) { @if (publication.kind === "video") {
<video-component [track]="publication.videoTrack!" [participantIdentity]="getParticipantIdentity(publication)"></video-component>
} @else {
<audio-component [track]="castToRemoteAudioTrack(publication.audioTrack!)" hidden></audio-component>
} }
}
@for (remoteTrack of remoteTracksMap.values(); track remoteTrack.trackPublication.trackSid) {
@if (remoteTrack.trackPublication.kind === 'video') {
<video-component
[track]="remoteTrack.trackPublication.videoTrack!"
[participantIdentity]="remoteTrack.participantIdentity"
></video-component>
} @else {
<audio-component [track]="remoteTrack.trackPublication.audioTrack!" hidden></audio-component>
}
}
</div>
</div>
}

View File

@ -1,9 +1,7 @@
import { Component } from '@angular/core';
import { Component, HostListener, OnDestroy } from '@angular/core';
import { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';
import {
LocalAudioTrack,
LocalVideoTrack,
RemoteAudioTrack,
RemoteParticipant,
RemoteTrack,
RemoteTrackPublication,
@ -15,6 +13,11 @@ import { AudioComponent } from './audio/audio.component';
import { HttpClient } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';
type TrackInfo = {
trackPublication: RemoteTrackPublication;
participantIdentity: string;
};
// For local development, leave these variables empty
// For production, configure them with correct URLs depending on your deployment
var APPLICATION_SERVER_URL = '';
@ -27,7 +30,7 @@ var LIVEKIT_URL = '';
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent {
export class AppComponent implements OnDestroy {
roomForm = new FormGroup({
roomName: new FormControl('Test Room', Validators.required),
participantName: new FormControl('Participant' + Math.floor(Math.random() * 100), Validators.required),
@ -35,7 +38,7 @@ export class AppComponent {
room?: Room;
localTrack?: LocalVideoTrack;
remoteTrackPublications: Map<RemoteTrackPublication, string> = new Map();
remoteTracksMap: Map<string, TrackInfo> = new Map();
constructor(private httpClient: HttpClient) {
this.configureUrls();
@ -70,7 +73,10 @@ export class AppComponent {
this.room.on(
RoomEvent.TrackSubscribed,
(_track: RemoteTrack, publication: RemoteTrackPublication, participant: RemoteParticipant) => {
this.remoteTrackPublications.set(publication, participant.identity);
this.remoteTracksMap.set(publication.trackSid, {
trackPublication: publication,
participantIdentity: participant.identity,
});
}
);
@ -78,7 +84,7 @@ export class AppComponent {
this.room.on(
RoomEvent.TrackUnsubscribed,
(_track: RemoteTrack, publication: RemoteTrackPublication, _participant: RemoteParticipant) => {
this.remoteTrackPublications.delete(publication);
this.remoteTracksMap.delete(publication.trackSid);
}
);
@ -106,15 +112,13 @@ export class AppComponent {
// Empty all properties...
delete this.room;
delete this.localTrack;
this.remoteTrackPublications.clear();
this.remoteTracksMap.clear();
}
getParticipantIdentity(publication: RemoteTrackPublication): string {
return this.remoteTrackPublications.get(publication) || '';
}
castToRemoteAudioTrack(audioTrack: LocalAudioTrack | RemoteAudioTrack): RemoteAudioTrack {
return audioTrack as RemoteAudioTrack;
@HostListener('window:beforeunload')
async ngOnDestroy() {
// On window closed or component destroyed, leave the room
await this.leaveRoom();
}
/**

View File

@ -1 +1 @@
<audio #audioElement></audio>
<audio #audioElement [id]="track?.sid"></audio>

View File

@ -1,5 +1,5 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
import { RemoteAudioTrack } from 'livekit-client';
import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { LocalAudioTrack, RemoteAudioTrack } from 'livekit-client';
@Component({
selector: 'audio-component',
@ -8,10 +8,23 @@ import { RemoteAudioTrack } from 'livekit-client';
templateUrl: './audio.component.html',
styleUrl: './audio.component.css',
})
export class AudioComponent {
export class AudioComponent implements AfterViewInit, OnDestroy {
@ViewChild('audioElement') audioElement?: ElementRef<HTMLAudioElement>;
_track?: RemoteAudioTrack;
private _track?: RemoteAudioTrack | LocalAudioTrack;
@Input()
set track(track: RemoteAudioTrack | LocalAudioTrack) {
this._track = track;
if (this.audioElement) {
this._track.attach(this.audioElement.nativeElement);
}
}
get track(): RemoteAudioTrack | LocalAudioTrack | undefined {
return this._track;
}
ngAfterViewInit() {
if (this._track && this.audioElement) {
@ -19,12 +32,7 @@ export class AudioComponent {
}
}
@Input()
set track(track: RemoteAudioTrack) {
this._track = track;
if (this.audioElement) {
this._track.attach(this.audioElement.nativeElement);
}
ngOnDestroy() {
this._track?.detach();
}
}

View File

@ -27,17 +27,7 @@
}
/* Media Queries */
@media screen and (max-width: 768px) {
#layout-container {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
}
@media screen and (max-width: 480px) {
#layout-container {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.video-container {
aspect-ratio: 9/16;
}

View File

@ -1,6 +1,6 @@
<div id="camera-{{ participantIdentity }}" class="video-container">
<div class="participant-data">
<p>{{ participantIdentity }} @if (isLocal) { (You)}</p>
<p>{{ participantIdentity + (isLocal ? " (You)" : "") }}</p>
</div>
<video #videoElement></video>
<video #videoElement [id]="track?.sid"></video>
</div>

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { LocalVideoTrack, RemoteVideoTrack } from 'livekit-client';
@Component({
@ -8,19 +8,13 @@ import { LocalVideoTrack, RemoteVideoTrack } from 'livekit-client';
templateUrl: './video.component.html',
styleUrl: './video.component.css',
})
export class VideoComponent {
export class VideoComponent implements AfterViewInit, OnDestroy {
@ViewChild('videoElement') videoElement?: ElementRef<HTMLVideoElement>;
private _track?: LocalVideoTrack | RemoteVideoTrack;
@Input() participantIdentity?: string;
@Input() isLocal = false;
ngAfterViewInit() {
if (this._track && this.videoElement) {
this._track.attach(this.videoElement.nativeElement);
}
}
@Input()
set track(track: LocalVideoTrack | RemoteVideoTrack) {
this._track = track;
@ -29,4 +23,18 @@ export class VideoComponent {
this._track.attach(this.videoElement.nativeElement);
}
}
get track(): LocalVideoTrack | RemoteVideoTrack | undefined {
return this._track;
}
ngAfterViewInit() {
if (this._track && this.videoElement) {
this._track.attach(this.videoElement.nativeElement);
}
}
ngOnDestroy() {
this._track?.detach();
}
}

View File

@ -31,7 +31,7 @@
</head>
<body>
<header>
<a href="/" title="Home"><h1>Basic JavaScript</h1></a>
<a href="/" title="Home"><h1>Basic Angular</h1></a>
<div id="links">
<a
href="https://github.com/OpenVidu/openvidu-livekit-tutorials/tree/master/basic/frontend/javascript"