openvidu-components: Added preliminary subtitles

pull/743/head
csantosm 2022-07-05 12:03:35 +02:00
parent fc7b961010
commit 0b74590252
39 changed files with 905 additions and 353 deletions

View File

@ -20,6 +20,7 @@ var PARTICIPANTS_PANEL_BUTTON;
var ACTIVITIES_RECORDING_ACTIVITY;
var RECORDING_ERROR;
var TOOLBAR_SETTINGS_BUTTON;
var SUBTITLES_BUTTON;
var SESSION_NAME;
@ -37,6 +38,7 @@ $(document).ready(() => {
RECORDING_BUTTON = url.searchParams.get("recordingBtn") === null ? true : url.searchParams.get("recordingBtn") === 'true';
FULLSCREEN_BUTTON = url.searchParams.get("fullscreenBtn") === null ? true : url.searchParams.get("fullscreenBtn") === 'true';
TOOLBAR_SETTINGS_BUTTON = url.searchParams.get("toolbarSettingsBtn") === null ? true : url.searchParams.get("toolbarSettingsBtn") === 'true';
SUBTITLES_BUTTON = url.searchParams.get("toolbarSubtitlesBtn") === null ? true : url.searchParams.get("toolbarSubtitlesBtn") === 'true';
LEAVE_BUTTON = url.searchParams.get("leaveBtn") === null ? true : url.searchParams.get("leaveBtn") === 'true';
ACTIVITIES_PANEL_BUTTON = url.searchParams.get("activitiesPanelBtn") === null ? true : url.searchParams.get("activitiesPanelBtn") === 'true';
@ -147,6 +149,7 @@ async function joinSession(sessionName, participantName) {
webComponent.toolbarFullscreenButton = FULLSCREEN_BUTTON;
webComponent.toolbarSettingsButton = TOOLBAR_SETTINGS_BUTTON;
webComponent.toolbarSubtitlesButton = SUBTITLES_BUTTON;
webComponent.toolbarLeaveButton = LEAVE_BUTTON;
webComponent.toolbarRecordingButton = RECORDING_BUTTON;
webComponent.toolbarActivitiesPanelButton = ACTIVITIES_PANEL_BUTTON;

View File

@ -252,6 +252,41 @@ describe('Testing API Directives', () => {
expect(element.length).equals(0);
});
it('should HIDE the SUBTITLES button', async () => {
let element;
await browser.get(`${url}?prejoin=false&toolbarSubtitlesBtn=false`);
element = await browser.wait(until.elementLocated(By.id('session-container')), TIMEOUT);
expect(await element.isDisplayed()).to.be.true;
// Checking if toolbar is present
element = await browser.wait(until.elementLocated(By.id('media-buttons-container')), TIMEOUT);
expect(await element.isDisplayed()).to.be.true;
// Open more options menu
element = await browser.wait(until.elementLocated(By.id('more-options-btn')), TIMEOUT);
await element.click();
await browser.sleep(500);
// Checking if subtitles button is not present
element = await browser.wait(until.elementLocated(By.className('mat-menu-content')), TIMEOUT);
expect(await element.isDisplayed()).to.be.true;
element = await browser.findElements(By.id('subtitles-btn'));
expect(element.length).equals(0);
element = await browser.findElements(By.id('toolbar-settings-btn'));
expect(element.length).equals(1);
await element[0].click();
await browser.sleep(500);
element = await browser.wait(until.elementLocated(By.className('settings-container')), TIMEOUT);
expect(await element.isDisplayed()).to.be.true;
element = await browser.findElements(By.id('subtitles-opt'));
expect(element.length).equals(0);
});
it('should HIDE the TOOLBAR SETTINGS button', async () => {
let element;
await browser.get(`${url}?prejoin=false&toolbarSettingsBtn=false`);

View File

@ -30,7 +30,7 @@ export class AudioWaveComponent implements OnInit, OnDestroy {
if (this.streamManager) {
this.streamManager.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => {
if (event.reason === 'trackReplaced' && event.changedProperty === 'audioActive') {
// FIXUP: When the audio track is replaced, the startSpeakingEvents is not fired by openvidu-browser
// TODO: When the audio track is replaced, the startSpeakingEvents is not fired by openvidu-browser
this.unsubscribeSpeakingEvents();
this.subscribeSpeakingEvents();
}

View File

@ -1,18 +1,18 @@
<div id="layout" class="layout">
<div
class="OT_root OT_publisher"
*ngFor="let stream of localParticipant | streams"
[ngClass]="{ OV_big: stream.videoEnlarged }"
>
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: stream }"></ng-container>
<div class="container" [class.withSubtitles]="subtitlesEnabled">
<div id="layout" class="layout">
<div class="OT_root OT_publisher" *ngFor="let stream of localParticipant | streams" [ngClass]="{ OV_big: stream.videoEnlarged }">
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: stream }"></ng-container>
</div>
<div
*ngFor="let stream of remoteParticipants | streams"
class="OT_root OT_publisher"
id="remote-participant"
[ngClass]="{ OV_big: stream.videoEnlarged }"
>
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: stream }"></ng-container>
</div>
</div>
<div
*ngFor="let stream of remoteParticipants | streams"
class="OT_root OT_publisher"
id="remote-participant"
[ngClass]="{ OV_big: stream.videoEnlarged }"
>
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: stream }"></ng-container>
</div>
<ov-subtitles *ngIf="subtitlesEnabled" class="OV_ignored"></ov-subtitles>
</div>

View File

@ -73,8 +73,14 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
localParticipant: ParticipantAbstractModel;
remoteParticipants: ParticipantAbstractModel[] = [];
protected localParticipantSubs: Subscription;
protected remoteParticipantsSubs: Subscription;
/**
* @ignore
*/
subtitlesEnabled = true;
private localParticipantSubs: Subscription;
private remoteParticipantsSubs: Subscription;
private subtitlesSubs: Subscription;
/**
* @ignore
@ -83,10 +89,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
ngOnInit(): void {
this.subscribeToParticipants();
this.subscribeToSubtitles();
}
ngAfterViewInit() {
let timeout: number = 0;
let timeout: number = 100;
this.layoutService.initialize(timeout);
this.layoutService.update(timeout);
}
@ -96,9 +103,18 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
this.remoteParticipants = [];
if (this.localParticipantSubs) this.localParticipantSubs.unsubscribe();
if (this.remoteParticipantsSubs) this.remoteParticipantsSubs.unsubscribe();
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
this.layoutService.clear();
}
private subscribeToSubtitles() {
this.subtitlesSubs = this.layoutService.subtitlesTogglingObs.subscribe((value: boolean) => {
this.subtitlesEnabled = value;
this.cd.markForCheck();
this.layoutService.update();
});
}
private subscribeToParticipants() {
this.localParticipantSubs = this.participantService.localParticipantObs.subscribe((p) => {
this.localParticipant = p;

View File

@ -1,4 +1,4 @@
<div class="panel-container" id="background-effects-container" fxLayout="column" fxLayoutAlign="space-evenly none">
<div class="panel-container" id="settings-container" fxLayout="column" fxLayoutAlign="space-evenly none">
<div class="panel-header-container" fxFlex="55px" fxLayoutAlign="start center">
<h3 class="panel-title">{{ 'PANEL.SETTINGS.TITLE' | translate }}</h3>
<button class="panel-close-button" mat-icon-button matTooltip="{{ 'PANEL.CLOSE' | translate }}" (click)="close()">
@ -8,26 +8,35 @@
<div class="settings-container" fxFlex="100%" fxLayoutAlign="space-evenly none">
<div class="item-menu">
<mat-selection-list #settings [multiple]="false">
<mat-list-option class="option" [selected]="true" value="general">
<mat-selection-list (selectionChange)="onSelectionChanged($event)" [multiple]="false">
<mat-list-option class="option" id="general-opt" [selected]="selectedOption === settingsOptions.GENERAL" [value]="settingsOptions.GENERAL">
<mat-icon mat-list-icon>manage_accounts</mat-icon>
<div mat-line>{{ 'PANEL.SETTINGS.GENERAL' | translate }}</div>
</mat-list-option>
<mat-list-option class="option" value="video">
<mat-list-option class="option" id="video-opt" [selected]="selectedOption === settingsOptions.VIDEO" [value]="settingsOptions.VIDEO">
<mat-icon mat-list-icon>videocam</mat-icon>
<div mat-line>{{ 'PANEL.SETTINGS.VIDEO' | translate }}</div>
</mat-list-option>
<mat-list-option class="option" value="audio">
<mat-list-option class="option" id="audio-opt" [selected]="selectedOption === settingsOptions.AUDIO" [value]="settingsOptions.AUDIO">
<mat-icon mat-list-icon>mic</mat-icon>
<div mat-line>{{ 'PANEL.SETTINGS.AUDIO' | translate }}</div>
</mat-list-option>
<mat-list-option
*ngIf="showSubtitles"
class="option"
[selected]="selectedOption === settingsOptions.SUBTITLES"
[value]="settingsOptions.SUBTITLES"
id="subtitles-opt"
>
<mat-icon mat-list-icon>closed_caption</mat-icon>
<div mat-line>{{ 'PANEL.SETTINGS.SUBTITLE' | translate }}</div>
</mat-list-option>
</mat-selection-list>
</div>
<div class="item-content">
<div *ngIf="settings.selectedOptions.selected[0]?.value === 'general'">
<div *ngIf="selectedOption === settingsOptions.GENERAL">
<ov-nickname-input></ov-nickname-input>
<mat-list>
<mat-list-item>
<mat-icon mat-list-icon>language</mat-icon>
@ -36,8 +45,9 @@
</mat-list-item>
</mat-list>
</div>
<ov-video-devices-select *ngIf="settings.selectedOptions.selected[0]?.value === 'video'"></ov-video-devices-select>
<ov-audio-devices-select *ngIf="settings.selectedOptions.selected[0]?.value === 'audio'"></ov-audio-devices-select>
<ov-video-devices-select *ngIf="selectedOption === settingsOptions.VIDEO"></ov-video-devices-select>
<ov-audio-devices-select *ngIf="selectedOption === settingsOptions.AUDIO"></ov-audio-devices-select>
<ov-subtitles-settings *ngIf="selectedOption === settingsOptions.SUBTITLES && showSubtitles"></ov-subtitles-settings>
</div>
</div>
</div>

View File

@ -1,7 +1,8 @@
import { Component, HostListener, OnInit } from '@angular/core';
import { MatOptionSelectionChange } from '@angular/material/core';
import { PanelType } from '../../../models/panel.model';
import { PanelService } from '../../../services/panel/panel.service';
import { Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { PanelSettingsOptions, PanelType } from '../../../models/panel.model';
import { OpenViduAngularConfigService } from '../../../services/config/openvidu-angular.config.service';
import { PanelEvent, PanelService } from '../../../services/panel/panel.service';
@Component({
selector: 'ov-settings-panel',
@ -9,12 +10,41 @@ import { PanelService } from '../../../services/panel/panel.service';
styleUrls: ['../panel.component.css', './settings-panel.component.css']
})
export class SettingsPanelComponent implements OnInit {
settingsOptions: typeof PanelSettingsOptions = PanelSettingsOptions;
selectedOption: PanelSettingsOptions = PanelSettingsOptions.GENERAL;
showSubtitles: boolean = true;
private subtitlesSubs: Subscription;
panelSubscription: Subscription;
constructor(private panelService: PanelService, private libService: OpenViduAngularConfigService) {}
ngOnInit() {
this.subscribeToPanelToggling();
this.subscribeToDirectives();
}
selectedOption: string;
constructor(private panelService: PanelService) {}
ngOnInit() {}
ngOnDestroy() {
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
}
close() {
this.panelService.togglePanel(PanelType.SETTINGS);
}
onSelectionChanged(e: any){
this.selectedOption = e.option.value;
}
private subscribeToDirectives() {
this.subtitlesSubs = this.libService.subtitlesButtonObs.subscribe((value: boolean) => {
this.showSubtitles = value;
});
}
private subscribeToPanelToggling() {
this.panelSubscription = this.panelService.panelOpenedObs.subscribe(
(ev: PanelEvent) => {
if (ev.type === PanelType.SETTINGS && !!ev.expand) {
this.selectedOption = ev.expand as PanelSettingsOptions;
}
}
);
}
}

View File

@ -8,7 +8,7 @@
position: relative;
height: calc(100% - 70px);
min-height: calc(100% - 70px);
padding-top: 10px;
padding-top: 0px;
width: 100%;
overflow: hidden;
}

View File

@ -0,0 +1,8 @@
::ng-deep .mat-slide-toggle.mat-checked .mat-slide-toggle-bar {
background-color: var(--ov-tertiary-color);
}
::ng-deep .mat-slide-toggle.mat-checked .mat-slide-toggle-thumb{
background-color: var(--ov-light-color);
}

View File

@ -0,0 +1,21 @@
<mat-list>
<mat-list-item>
<div mat-line>{{ 'PANEL.SETTINGS.SUBTITLE' | translate }}</div>
<mat-slide-toggle (change)="toggleSubtitles()" [checked]="subtitlesEnabled" [disableRipple]="true"></mat-slide-toggle>
</mat-list-item>
<mat-list-item>
<div mat-line>{{ 'PANEL.SETTINGS.LANGUAGE' | translate }}:</div>
<button mat-flat-button [matMenuTriggerFor]="menu" class="lang-button" [disabled]="true">
<span>{{langSelected}}</span>
<mat-icon>expand_more</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item *ngFor="let lang of languagesAvailable" (click)="onLangSelected(lang)">
<span>{{lang}}</span>
</button>
</mat-menu>
</mat-list-item>
</mat-list>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SubtitlesComponent } from './subtitles.component';
describe('SubtitlesComponent', () => {
let component: SubtitlesComponent;
let fixture: ComponentFixture<SubtitlesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SubtitlesComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SubtitlesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,40 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { LayoutService } from '../../../services/layout/layout.service';
@Component({
selector: 'ov-subtitles-settings',
templateUrl: './subtitles.component.html',
styleUrls: ['./subtitles.component.css']
})
export class SubtitlesSettingComponent implements OnInit, OnDestroy {
subtitlesEnabled: boolean;
languagesAvailable = [];
subtitlesSubs: Subscription;
langSelected: string = 'English';
constructor(private layoutService: LayoutService) {}
ngOnInit(): void {
this.subscribeToSubtitles();
}
ngOnDestroy() {
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
}
onLangSelected(lang: string){
this.langSelected = lang;
}
toggleSubtitles() {
this.layoutService.toggleSubtitles();
}
private subscribeToSubtitles() {
this.subtitlesSubs = this.layoutService.subtitlesTogglingObs.subscribe((value: boolean) => {
this.subtitlesEnabled = value;
// this.cd.markForCheck();
});
}
}

View File

@ -0,0 +1,44 @@
.subtitles-container {
/* padding: 5px; */
display: flex;
height: 75px;
}
.subtitles-offset {
height: 75px;
width: 115px;
text-align: center;
}
.subtitles-main-container {
flex-grow: 1;
text-align: center;
align-self: center;
}
.subtitles-offset + .subtitles-offset {
margin-left: 2%;
}
#subtitle-settings-btn {
color: var(--ov-text-color);
background-color: var(--ov-secondary-color);
}
#subtitle-settings-icon {
font-size: 15px;
height: 15px;
width: 15px;
padding-right: 5px;
}
#subtitle-text {
color: var(--ov-text-color);
background-color: var(--ov-logo-background-color);
font-size: 18px;
font-family: Arial, Helvetica, sans-serif;
padding: 4.5px;
line-height: 1.6;
word-break: break-word;
}
.small-text {
font-size: 14px !important;
}

View File

@ -0,0 +1,12 @@
<div class="subtitles-container">
<div *ngIf="screenSize !== 'sm' && screenSize !== 'xs' && !settingsPanelOpened" class="subtitles-offset">
<button (click)="onSettingsCliked()" id="subtitle-settings-btn" mat-flat-button>
<mat-icon id="subtitle-settings-icon">settings</mat-icon>
<span>English</span>
</button>
</div>
<div class="subtitles-main-container">
<span id="subtitle-text" [class.small-text]="screenSize === 'xs' ">(Demo preview): {{ subtitleText }}</span>
</div>
<div class="subtitles-offset" *ngIf="screenSize !== 'sm' && screenSize !== 'xs' && !settingsPanelOpened"></div>
</div>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SubtitlesComponent } from './subtitles.component';
describe('SubtitlesComponent', () => {
let component: SubtitlesComponent;
let fixture: ComponentFixture<SubtitlesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SubtitlesComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SubtitlesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,78 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { PanelSettingsOptions, PanelType } from '../../models/panel.model';
import { PanelEvent, PanelService } from '../../services/panel/panel.service';
import { DocumentService } from '../../services/document/document.service';
import { MediaChange } from '@angular/flex-layout';
//TODO: BORRAR
import { LoremIpsum } from 'lorem-ipsum';
/**
* @internal
*/
@Component({
selector: 'ov-subtitles',
templateUrl: './subtitles.component.html',
styleUrls: ['./subtitles.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SubtitlesComponent implements OnInit {
/**
* @ignore
*/
subtitleText: string;
/**
* @ignore
*/
screenSize: string;
/**
* @ignore
*/
settingsPanelOpened: boolean;
private screenSizeSub: Subscription;
private panelTogglingSubscription: Subscription;
interval: NodeJS.Timer;
constructor(private documentService: DocumentService, private panelService: PanelService, private cd: ChangeDetectorRef) {}
ngOnInit(): void {
this.subscribeToScreenSize();
this.subscribeToPanelToggling();
//TODO: Subscribe to openvidu-browser subtitle event
// TODO: REMOVE ------------------
const lorem = new LoremIpsum();
this.interval = setInterval(() => {
this.subtitleText = lorem.generateSentences(1);
this.cd.markForCheck();
}, 3000);
// TODO: REMOVE ------------------
}
ngOnDestroy() {
if (this.screenSizeSub) this.screenSizeSub.unsubscribe();
if(this.panelTogglingSubscription) this.panelTogglingSubscription.unsubscribe();
//TODO: Unsubscribe to openvidu-browser subtitle event
clearInterval(this.interval);
}
onSettingsCliked() {
this.panelService.togglePanel(PanelType.SETTINGS, PanelSettingsOptions.SUBTITLES);
}
private subscribeToPanelToggling() {
this.panelTogglingSubscription = this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => {
this.settingsPanelOpened = ev.opened;
this.cd.markForCheck();
});
}
private subscribeToScreenSize() {
this.screenSizeSub = this.documentService.screenSizeObs.subscribe((change: MediaChange[]) => {
this.screenSize = change[0].mqAlias;
console.log(this.screenSize);
this.cd.markForCheck();
});
}
}

View File

@ -103,6 +103,19 @@
<span>{{ 'TOOLBAR.BACKGROUND' | translate }}</span>
</button>
<!-- Subtitles button -->
<button
*ngIf="!isMinimal && showSubtitlesButton"
[disabled]="isConnectionLost"
mat-menu-item
id="subtitles-btn"
(click)="toggleSubtitles()"
>
<mat-icon>closed_caption</mat-icon>
<span *ngIf="subtitlesEnabled">{{ 'TOOLBAR.DISABLE_SUBTITLES' | translate }}</span>
<span *ngIf="!subtitlesEnabled">{{ 'TOOLBAR.ENABLE_SUBTITLES' | translate }}</span>
</button>
<mat-divider class="divider" *ngIf="!isMinimal && showSettingsButton"></mat-divider>
<!-- Settings button -->

View File

@ -39,6 +39,7 @@ import { RecordingService } from '../../services/recording/recording.service';
import { RecordingInfo, RecordingStatus } from '../../models/recording.model';
import { TranslateService } from '../../services/translate/translate.service';
import { MediaChange } from '@angular/flex-layout';
import { LayoutService } from '../../services/layout/layout.service';
/**
*
@ -303,6 +304,17 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
* @ignore
*/
showSessionName: boolean = true;
/**
* @ignore
*/
showSubtitlesButton: boolean = true;
/**
* @ignore
*/
subtitlesEnabled: boolean;
/**
* @ignore
*/
@ -350,6 +362,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
private displaySessionNameSub: Subscription;
private screenSizeSub: Subscription;
private settingsButtonSub: Subscription;
private subtitlesSubs: Subscription;
private currentWindowHeight = window.innerHeight;
/**
@ -365,6 +378,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
protected oVDevicesService: DeviceService,
protected actionService: ActionService,
protected loggerSrv: LoggerService,
private layoutService: LayoutService,
private cd: ChangeDetectorRef,
private libService: OpenViduAngularConfigService,
private platformService: PlatformService,
@ -411,6 +425,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
this.subscribeToChatMessages();
this.subscribeToRecordingStatus();
this.subscribeToScreenSize();
this.subscribeToSubtitlesToggling();
}
ngAfterViewInit() {
@ -438,6 +453,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
if (this.recordingSubscription) this.recordingSubscription.unsubscribe();
if (this.screenSizeSub) this.screenSizeSub.unsubscribe();
if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe();
if (this.subtitlesSubs) this.subtitlesSubs.unsubscribe();
}
/**
@ -528,6 +544,13 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS);
}
/**
* @ignore
*/
toggleSubtitles() {
this.layoutService.toggleSubtitles();
}
/**
* @ignore
*/
@ -674,6 +697,10 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
this.showSessionName = value;
this.cd.markForCheck();
});
this.subtitlesSubs = this.libService.subtitlesButtonObs.subscribe((value: boolean) => {
this.showSubtitlesButton = value;
this.cd.markForCheck();
});
}
private subscribeToScreenSize() {
@ -682,6 +709,13 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
});
}
private subscribeToSubtitlesToggling() {
this.subtitlesSubs = this.layoutService.subtitlesTogglingObs.subscribe((value: boolean) => {
this.subtitlesEnabled = value;
this.cd.markForCheck();
});
}
private checkDisplayMoreOptions() {
this.showMoreOptionsButton =
this.showFullscreenButton || this.showBackgroundEffectsButton || this.showRecordingButton || this.showSettingsButton;

View File

@ -20,7 +20,8 @@ import {
ToolbarActivitiesPanelButtonDirective,
ToolbarBackgroundEffectsButtonDirective,
ToolbarRecordingButtonDirective,
ToolbarSettingsButtonDirective
ToolbarSettingsButtonDirective,
ToolbarSubtitleButtonDirective
} from './toolbar.directive';
import {
AudioMutedDirective,
@ -41,6 +42,7 @@ import {
ToolbarScreenshareButtonDirective,
ToolbarFullscreenButtonDirective,
ToolbarBackgroundEffectsButtonDirective,
ToolbarSubtitleButtonDirective,
ToolbarLeaveButtonDirective,
ToolbarRecordingButtonDirective,
ToolbarParticipantsPanelButtonDirective,
@ -70,6 +72,7 @@ import {
ToolbarScreenshareButtonDirective,
ToolbarFullscreenButtonDirective,
ToolbarBackgroundEffectsButtonDirective,
ToolbarSubtitleButtonDirective,
ToolbarLeaveButtonDirective,
ToolbarRecordingButtonDirective,
ToolbarParticipantsPanelButtonDirective,

View File

@ -242,6 +242,65 @@ export class ToolbarBackgroundEffectsButtonDirective implements AfterViewInit, O
}
}
/**
* The **subtitleButton** directive allows show/hide the subtitle toolbar button.
*
* Default: `true`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `toolbar` component:
*
* @example
* <ov-videoconference [toolbarSubtitleButton]="false"></ov-videoconference>
*
* \
* And it also can be used in the {@link ToolbarComponent}.
* @example
* <ov-toolbar [subtitleButton]="false"></ov-toolbar>
*/
@Directive({
selector: 'ov-videoconference[toolbarSubtitlesButton], ov-toolbar[subtitlesButton]'
})
export class ToolbarSubtitleButtonDirective implements AfterViewInit, OnDestroy {
/**
* @ignore
*/
@Input() set toolbarSubtitlesButton(value: boolean) {
this.subtitlesButtonValue = value;
this.update(this.subtitlesButtonValue);
}
/**
* @ignore
*/
@Input() set subtitlesButton(value: boolean) {
this.subtitlesButtonValue = value;
this.update(this.subtitlesButtonValue);
}
private subtitlesButtonValue: boolean = true;
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngAfterViewInit() {
this.update(this.subtitlesButtonValue);
}
ngOnDestroy(): void {
this.clear();
}
private clear() {
this.subtitlesButtonValue = true;
this.update(true);
}
private update(value: boolean) {
if (this.libService.subtitlesButton.getValue() !== value) {
this.libService.subtitlesButton.next(value);
}
}
}
/**
* The **settingsButton** directive allows show/hide the settings toolbar button.
*

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS":"更多选项",
"FULLSCREEN":"全屏",
"EXIT_FULLSCREEN": "退出全屏",
"ENABLE_SUBTITLES": "启用字幕",
"DISABLE_SUBTITLES": "禁用字幕",
"BACKGROUND":"背景效果",
"START_RECORDING": "开始录音",
"STOP_RECORDING": "停止录制",
@ -70,7 +72,8 @@
"GENERAL": "一般的",
"VIDEO": "视频",
"AUDIO": "声音的",
"LANGUAGE": "语"
"LANGUAGE": "语",
"SUBTITLE": "字幕"
},
"BACKGROUND": {
"TITLE": "背景效果",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "Weitere Optionen",
"FULLSCREEN": "Vollbild",
"EXIT_FULLSCREEN": "Vollbildmodus beenden",
"ENABLE_SUBTITLES": "Untertitel aktivieren",
"DISABLE_SUBTITLES": "Untertitel deaktivieren",
"BACKGROUND": "Hintergrund-Effekte",
"START_RECORDING": "Aufzeichnung starten",
"STOP_RECORDING": "Aufzeichnung stoppen",
@ -70,7 +72,8 @@
"GENERAL": "Allgemein",
"VIDEO": "Video",
"AUDIO": "Audio",
"LANGUAGE": "Sprache"
"LANGUAGE": "Sprache",
"SUBTITLE": "Untertitel"
},
"BACKGROUND": {
"TITLE": "Hintergrund-Effekte",

View File

@ -35,6 +35,8 @@
"MORE_OPTIONS": "More options",
"FULLSCREEN": "Fullscreen",
"EXIT_FULLSCREEN": "Exit fullscreen",
"ENABLE_SUBTITLES": "Enable subtitles",
"DISABLE_SUBTITLES": "Disable subtitles",
"BACKGROUND": "Background effects",
"START_RECORDING": "Start recording",
"STOP_RECORDING": "Stop recording",
@ -71,7 +73,8 @@
"GENERAL": "General",
"VIDEO": "Video",
"AUDIO": "Audio",
"LANGUAGE": "Language"
"LANGUAGE": "Language",
"SUBTITLE": "Subtitles"
},
"BACKGROUND": {
"TITLE": "Background effects",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "Más opciones",
"EXIT_FULLSCREEN": "Quitar pantalla completa",
"FULLSCREEN": "Pantalla completa",
"ENABLE_SUBTITLES": "Activar subtítulos",
"DISABLE_SUBTITLES": "Desactivar subtítulos",
"BACKGROUND": "Efectos de fondo",
"START_RECORDING": "Iniciar grabación",
"STOP_RECORDING": "Detener grabación",
@ -70,7 +72,8 @@
"GENERAL": "General",
"VIDEO": "Video",
"AUDIO": "Audio",
"LANGUAGE": "Idioma"
"LANGUAGE": "Idioma",
"SUBTITLE": "Subtítulos"
},
"BACKGROUND": {
"TITLE": "Efectos de fondo",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "Plus d'options",
"FULLSCREEN": "Plein écran",
"EXIT_FULLSCREEN": "Quitter le plein écran",
"ENABLE_SUBTITLES": "Activer les sous-titres",
"DISABLE_SUBTITLES": "Désactiver les sous-titres",
"BACKGROUND": "Effets de fond",
"START_RECORDING": "démarrer l'enregistrement",
"STOP_RECORDING": "Arrêter l'enregistrement",
@ -70,7 +72,8 @@
"GENERAL": "Général",
"VIDEO": "Vidéo",
"AUDIO": "l'audio",
"LANGUAGE": "Langue"
"LANGUAGE": "Langue",
"SUBTITLE": "Les sous-titres"
},
"BACKGROUND": {
"TITLE": "Effets de fond",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "अधिक विकल्प",
"FULLSCREEN": "पूर्ण स्क्रीन",
"EXIT_FULLSCREEN": "पूर्ण स्क्रीन से बाहर निकलें",
"ENABLE_SUBTITLES": "उपशीर्षक सक्षम करें",
"DISABLE_SUBTITLES": "उपशीर्षक अक्षम करें",
"BACKGROUND": "पृष्ठभूमि प्रभाव",
"START_RECORDING": "रिकॉर्डिंग प्रारंभ करें",
"STOP_RECORDING": "रिकॉर्डिंग रोकें",
@ -70,7 +72,8 @@
"GENERAL": "सामान्य",
"VIDEO": "वीडियो",
"AUDIO": "ऑडियो",
"LANGUAGE": "भाषा"
"LANGUAGE": "भाषा",
"SUBTITLE": "उपशीर्षक"
},
"BACKGROUND": {
"TITLE": "पृष्ठभूमि प्रभाव",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "Altre opzioni",
"FULLSCREEN": "Schermo intero",
"EXIT_FULLSCREEN": "Esci dallo schermo intero",
"ENABLE_SUBTITLES": "Abilita i sottotitoli",
"DISABLE_SUBTITLES": "Disabilita i sottotitoli",
"BACKGROUND": "Effetti di sfondo",
"START_RECORDING": "Avvia registrazione",
"STOP_RECORDING": "Interrompi registrazione",
@ -70,7 +72,8 @@
"GENERAL": "Generale",
"VIDEO": "video",
"AUDIO": "Audio",
"LANGUAGE":"Lingua"
"LANGUAGE":"Lingua",
"SUBTITLE": "Sottotitoli"
},
"BACKGROUND": {
"TITLE": "Effetti di sfondo",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "その他のオプション",
"FULLSCREEN": "フルスクリーン",
"EXIT_FULLSCREEN": "フルスクリーンを終了する",
"ENABLE_SUBTITLES": "字幕を有効にする",
"DISABLE_SUBTITLES": "字幕を無効にする",
"BACKGROUND": "背景効果",
"START_RECORDING": "録画開始",
"STOP_RECORDING": "録画の停止",
@ -70,7 +72,8 @@
"GENERAL": "全般的",
"VIDEO": "ビデオ",
"AUDIO": "オーディオ",
"LANGUAGE":"言語"
"LANGUAGE":"言語",
"SUBTITLE": "字幕"
},
"BACKGROUND": {
"TITLE": "背景効果",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "Meer opties",
"FULLSCREEN": "Volledig scherm",
"EXIT_FULLSCREEN": "Volledig scherm verlaten",
"ENABLE_SUBTITLES": "Ondertiteling inschakelen",
"DISABLE_SUBTITLES": "Ondertiteling uitschakelen",
"BACKGROUND": "Achtergrondeffecten",
"START_RECORDING": "Start opname",
"STOP_RECORDING": "Stop opname",
@ -70,7 +72,8 @@
"GENERAL": "Algemeen",
"VIDEO": "Video",
"AUDIO": "Audio",
"LANGUAGE":"Taal"
"LANGUAGE":"Taal",
"SUBTITLE": "Ondertitels"
},
"BACKGROUND": {
"TITLE": "Achtergrondeffecten",

View File

@ -34,6 +34,8 @@
"MORE_OPTIONS": "Mais opções",
"FULLSCREEN": "Tela cheia",
"EXIT_FULLSCREEN": "Sair da tela cheia",
"ENABLE_SUBTITLES": "Ativar legendas",
"DISABLE_SUBTITLES": "Desativar legendas",
"BACKGROUND": "Efeitos de fundo",
"START_RECORDING": "Iniciar_gravação",
"STOP_RECORDING": "Parar de gravar",
@ -70,7 +72,8 @@
"GENERAL": "Em geral",
"VIDEO": "Vídeo",
"AUDIO": "Áudio",
"LANGUAGE":"Linguagem"
"LANGUAGE":"Linguagem",
"SUBTITLE": "Legendas"
},
"BACKGROUND": {
"TITLE": "Efeitos de fundo",

View File

@ -5,4 +5,11 @@ export enum PanelType {
ACTIVITIES = 'activities',
SETTINGS = 'settings'
}
}
export enum PanelSettingsOptions {
GENERAL = 'general',
AUDIO = 'audio',
VIDEO = 'video',
SUBTITLES = 'subtitles'
}

View File

@ -19,6 +19,7 @@ import { MatMenuModule } from '@angular/material/menu';
import { MatListModule } from '@angular/material/list';
import { MatDividerModule } from '@angular/material/divider';
import { MatExpansionModule } from '@angular/material/expansion';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
@ -47,6 +48,7 @@ import { NgModule } from '@angular/core';
MatDividerModule,
MatListModule,
MatExpansionModule,
MatSlideToggleModule
]
})
export class AppMaterialModule { }

View File

@ -58,6 +58,8 @@ import { VideoDevicesComponent } from './components/settings/video-devices/video
import { AudioDevicesComponent } from './components/settings/audio-devices/audio-devices.component';
import { NicknameInputComponent } from './components/settings/nickname-input/nickname-input.component';
import { LangSelectorComponent } from './components/settings/lang-selector/lang-selector.component';
import { SubtitlesSettingComponent } from './components/settings/subtitles/subtitles.component';
import { SubtitlesComponent } from './components/subtitles/subtitles.component';
@NgModule({
declarations: [
@ -66,6 +68,7 @@ import { LangSelectorComponent } from './components/settings/lang-selector/lang-
ChatPanelComponent,
SessionComponent,
LayoutComponent,
SubtitlesComponent,
StreamComponent,
DialogTemplateComponent,
RecordingDialogComponent,
@ -87,6 +90,7 @@ import { LangSelectorComponent } from './components/settings/lang-selector/lang-
AudioDevicesComponent,
NicknameInputComponent,
LangSelectorComponent,
SubtitlesSettingComponent,
BackgroundEffectsPanelComponent,
SettingsPanelComponent,
ActivitiesPanelComponent,

View File

@ -28,6 +28,9 @@ export class OpenViduAngularConfigService {
fullscreenButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
fullscreenButtonObs: Observable<boolean>;
subtitlesButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
subtitlesButtonObs: Observable<boolean>;
toolbarSettingsButton = <BehaviorSubject<boolean>>new BehaviorSubject(true);
toolbarSettingsButtonObs: Observable<boolean>;
@ -92,6 +95,7 @@ export class OpenViduAngularConfigService {
this.displayLogoObs = this.displayLogo.asObservable();
this.recordingButtonObs = this.recordingButton.asObservable();
this.toolbarSettingsButtonObs = this.toolbarSettingsButton.asObservable();
this.subtitlesButtonObs = this.subtitlesButton.asObservable();
//Stream observables
this.displayParticipantNameObs = this.displayParticipantName.asObservable();
this.displayAudioDetectionObs = this.displayAudioDetection.asObservable();

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { LayoutClass, OpenViduLayout, OpenViduLayoutOptions } from '../../models/layout.model';
import { LayoutAlignment, LayoutClass, OpenViduLayout, OpenViduLayoutOptions } from '../../models/layout.model';
import { DocumentService } from '../document/document.service';
/**
@ -10,13 +10,19 @@ import { DocumentService } from '../document/document.service';
providedIn: 'root'
})
export class LayoutService {
layoutContainer: HTMLElement | null = null;
layoutWidthObs: Observable<number>;
private _layoutWidthObs: BehaviorSubject<number> = new BehaviorSubject(0);
subtitlesTogglingObs: Observable<boolean>;
private layoutWidth: BehaviorSubject<number> = new BehaviorSubject(0);
private openviduLayout: OpenViduLayout;
private openviduLayoutOptions: OpenViduLayoutOptions;
private subtitlesToggling: BehaviorSubject<boolean> = new BehaviorSubject(false);
constructor(private documentService: DocumentService) {
this.layoutWidthObs = this._layoutWidthObs.asObservable();
this.layoutWidthObs = this.layoutWidth.asObservable();
this.subtitlesTogglingObs = this.subtitlesToggling.asObservable();
}
initialize(timeout: number = null) {
@ -34,8 +40,10 @@ export class LayoutService {
private _initialize() {
this.openviduLayout = new OpenViduLayout();
this.openviduLayoutOptions = this.getOptions();
const element = document.getElementById('layout');
this.openviduLayout.initLayoutContainer(element, this.openviduLayoutOptions);
this.layoutContainer = document.getElementById('layout');
if(this.layoutContainer){
this.openviduLayout.initLayoutContainer(this.layoutContainer, this.openviduLayoutOptions);
}
}
private getOptions(): OpenViduLayoutOptions {
@ -46,20 +54,37 @@ export class LayoutService {
and minRatio and maxRatio are ignored (default false) */,
bigClass: LayoutClass.BIG_ELEMENT, // The class to add to elements that should be sized bigger
smallClass: LayoutClass.SMALL_ELEMENT,
ignoredClass: LayoutClass.IGNORED_ELEMENT,
bigPercentage: 0.8, // The maximum percentage of space the big ones should take up
minBigPercentage: 0, // If this is set then it will scale down the big space if there is left over whitespace down to this minimum size
bigFixedRatio: false, // fixedRatio for the big ones
bigMaxRatio: 9 / 16, // The narrowest ratio to use for the big elements (default 2x3)
bigMinRatio: 9 / 16, // The widest ratio to use for the big elements (default 16x9)
bigFirst: true, // Whether to place the big one in the top left (true) or bottom right
animate: true // Whether you want to animate the transitions. Deprecated property, to disable it remove the transaction property on OT_publisher css class
animate: true, // Whether you want to animate the transitions. Deprecated property, to disable it remove the transaction property on OT_publisher css class
alignItems: LayoutAlignment.CENTER,
bigAlignItems: LayoutAlignment.CENTER,
smallAlignItems: LayoutAlignment.CENTER,
maxWidth: Infinity, // The maximum width of the elements
maxHeight: Infinity, // The maximum height of the elements
smallMaxWidth: Infinity,
smallMaxHeight: Infinity,
bigMaxWidth: Infinity,
bigMaxHeight: Infinity,
scaleLastRow: true,
bigScaleLastRow: true
};
return options;
}
toggleSubtitles() {
this.subtitlesToggling.next(!this.subtitlesToggling.getValue());
}
update(timeout: number = null) {
const updateAux = () => {
if (!!this.openviduLayout) {
this.openviduLayout.updateLayout();
this.openviduLayout.updateLayout(this.layoutContainer, this.openviduLayoutOptions);
this.sendLayoutWidthEvent();
}
};
@ -84,7 +109,7 @@ export class LayoutService {
LayoutClass.SIDENAV_CONTAINER
);
if (sidenavLayoutElement && sidenavLayoutElement.clientWidth) {
this._layoutWidthObs.next(sidenavLayoutElement.clientWidth);
this.layoutWidth.next(sidenavLayoutElement.clientWidth);
}
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { ILogger } from '../../models/logger.model';
import { PanelType } from '../../models/panel.model';
import { PanelSettingsOptions, PanelType } from '../../models/panel.model';
import { LoggerService } from '../logger/logger.service';
export interface PanelEvent {
@ -38,7 +38,7 @@ export class PanelService {
* Open or close the panel type received. Calling this method with the panel opened and the same type panel, will close the panel.
* If the type is differente, it will switch to the properly panel.
*/
togglePanel(type: PanelType | string, expand?: string) {
togglePanel(type: PanelType | string, expand?: PanelSettingsOptions | string) {
let nextOpenedValue: boolean = false;
if (this.panelMap.has(type)) {
this.log.d(`Toggling ${type} menu`);

View File

@ -17,6 +17,7 @@
[toolbarParticipantsPanelButton]="_toolbarParticipantsPanelButton"
[toolbarDisplayLogo]="_toolbarDisplayLogo"
[toolbarDisplaySessionName]="_toolbarDisplaySessionName"
[toolbarSubtitlesButton]="_toolbarSubtitlesButton"
[streamDisplayParticipantName]="_streamDisplayParticipantName"
[streamDisplayAudioDetection]="_streamDisplayAudioDetection"
[streamSettingsButton]="_streamSettingsButton"

View File

@ -79,6 +79,10 @@ export class OpenviduWebComponentComponent implements OnInit {
* @internal
*/
_toolbarDisplaySessionName: boolean = true;
/**
* @internal
*/
_toolbarSubtitlesButton: boolean = true;
/**
* @internal
*/
@ -330,6 +334,20 @@ export class OpenviduWebComponentComponent implements OnInit {
@Input() set toolbarDisplaySessionName(value: string | boolean) {
this._toolbarDisplaySessionName = this.castToBoolean(value);
}
/**
* The **toolbarDisplaySessionName** attribute allows show/hide the session name.
*
* Default: `true`
*
* <div class="warn-container">
* <span>WARNING</span>: If you want to use this parameter to OpenVidu Web Component statically, you have to replace the <strong>camelCase</strong> with a <strong>hyphen between words</strong>.</div>
*
* @example
* <openvidu-webcomponent toolbar-display-session-name="false"></openvidu-webcomponent>
*/
@Input() set toolbarSubtitlesButton(value: string | boolean) {
this._toolbarSubtitlesButton = this.castToBoolean(value);
}
/**
* The **streamDisplayParticipantName** attribute allows show/hide the participants name in stream component.
*