Merge pull request #5811 from tsiegleauq/applause-client
Add applause in client
This commit is contained in:
commit
cc65b756c7
2431
client/package-lock.json
generated
2431
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -66,6 +66,7 @@
|
|||||||
"lz4js": "^0.2.0",
|
"lz4js": "^0.2.0",
|
||||||
"material-icon-font": "git+https://github.com/petergng/materialIconFont.git",
|
"material-icon-font": "git+https://github.com/petergng/materialIconFont.git",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.27.0",
|
||||||
|
"ng-particles": "^2.1.11",
|
||||||
"ng2-charts": "^2.4.0",
|
"ng2-charts": "^2.4.0",
|
||||||
"ng2-pdf-viewer": "6.3.2",
|
"ng2-pdf-viewer": "6.3.2",
|
||||||
"ngx-device-detector": "^2.0.0",
|
"ngx-device-detector": "^2.0.0",
|
||||||
@ -78,6 +79,7 @@
|
|||||||
"rxjs": "^6.6.2",
|
"rxjs": "^6.6.2",
|
||||||
"tinymce": "5.4.2",
|
"tinymce": "5.4.2",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
|
"tsparticles": "^1.18.11",
|
||||||
"video.js": "^7.8.4",
|
"video.js": "^7.8.4",
|
||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.10.2"
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { ApplicationRef, Component } from '@angular/core';
|
import { ApplicationRef, Component } from '@angular/core';
|
||||||
|
import { MatIconRegistry } from '@angular/material/icon';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -70,6 +72,8 @@ export class AppComponent {
|
|||||||
* @param dataStoreUpgradeService
|
* @param dataStoreUpgradeService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private matIconRegistry: MatIconRegistry,
|
||||||
|
private domSanitizer: DomSanitizer,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
appRef: ApplicationRef,
|
appRef: ApplicationRef,
|
||||||
servertimeService: ServertimeService,
|
servertimeService: ServertimeService,
|
||||||
@ -100,6 +104,7 @@ export class AppComponent {
|
|||||||
this.overloadArrayFunctions();
|
this.overloadArrayFunctions();
|
||||||
this.overloadSetFunctions();
|
this.overloadSetFunctions();
|
||||||
this.overloadModulo();
|
this.overloadModulo();
|
||||||
|
this.loadCustomIcons();
|
||||||
|
|
||||||
// Wait until the App reaches a stable state.
|
// Wait until the App reaches a stable state.
|
||||||
// Required for the Service Worker.
|
// Required for the Service Worker.
|
||||||
@ -204,4 +209,11 @@ export class AppComponent {
|
|||||||
enumerable: false
|
enumerable: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadCustomIcons(): void {
|
||||||
|
this.matIconRegistry.addSvgIcon(
|
||||||
|
`clapping_hands`,
|
||||||
|
this.domSanitizer.bypassSecurityTrustResourceUrl('../assets/svg/clapping_hands.svg')
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
20
client/src/app/core/ui-services/applause.service.spec.ts
Normal file
20
client/src/app/core/ui-services/applause.service.spec.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { ApplauseService } from './applause.service';
|
||||||
|
|
||||||
|
describe('ApplauseService', () => {
|
||||||
|
let service: ApplauseService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
});
|
||||||
|
service = TestBed.inject(ApplauseService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
84
client/src/app/core/ui-services/applause.service.ts
Normal file
84
client/src/app/core/ui-services/applause.service.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
|
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { ConfigService } from './config.service';
|
||||||
|
import { HttpService } from '../core-services/http.service';
|
||||||
|
import { NotifyService } from '../core-services/notify.service';
|
||||||
|
|
||||||
|
export interface Applause {
|
||||||
|
level: number;
|
||||||
|
presentUsers: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ApplauseType {
|
||||||
|
particles = 'applause-type-particles',
|
||||||
|
bar = 'applause-type-bar'
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ApplauseService {
|
||||||
|
private applausePath = '/system/applause';
|
||||||
|
private applauseNotifyPath = 'applause';
|
||||||
|
private minApplauseLevel: number;
|
||||||
|
private maxApplauseLevel: number;
|
||||||
|
private presentApplauseUsers: number;
|
||||||
|
|
||||||
|
public applauseType: ApplauseType;
|
||||||
|
|
||||||
|
private applauseLevelSubject: Subject<number> = new Subject<number>();
|
||||||
|
public applauseLevelObservable = this.applauseLevelSubject.asObservable();
|
||||||
|
|
||||||
|
private get maxApplause(): number {
|
||||||
|
return this.maxApplauseLevel || this.presentApplauseUsers || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
configService: ConfigService,
|
||||||
|
private httpService: HttpService,
|
||||||
|
private notifyService: NotifyService
|
||||||
|
) {
|
||||||
|
configService.get<number>('general_system_applause_min_amount').subscribe(minLevel => {
|
||||||
|
this.minApplauseLevel = minLevel;
|
||||||
|
});
|
||||||
|
configService.get<number>('general_system_applause_max_amount').subscribe(maxLevel => {
|
||||||
|
this.maxApplauseLevel = maxLevel;
|
||||||
|
});
|
||||||
|
configService.get<ApplauseType>('general_system_applause_type').subscribe((type: ApplauseType) => {
|
||||||
|
this.applauseType = type;
|
||||||
|
});
|
||||||
|
this.notifyService
|
||||||
|
.getMessageObservable<Applause>(this.applauseNotifyPath)
|
||||||
|
.pipe(
|
||||||
|
map(notify => notify.message as Applause),
|
||||||
|
/**
|
||||||
|
* only updates when the effective applause level changes
|
||||||
|
*/
|
||||||
|
distinctUntilChanged((prev, curr) => {
|
||||||
|
return prev.level === curr.level;
|
||||||
|
}),
|
||||||
|
filter(curr => {
|
||||||
|
return curr.level === 0 || curr.level >= this.minApplauseLevel;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(applause => {
|
||||||
|
this.presentApplauseUsers = applause.presentUsers;
|
||||||
|
this.applauseLevelSubject.next(applause.level);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendApplause(): Promise<void> {
|
||||||
|
await this.httpService.post(this.applausePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getApplauseQuote(applauseLevel: number): number {
|
||||||
|
if (!applauseLevel) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const quote = applauseLevel / this.maxApplause || 0;
|
||||||
|
return quote > 1 ? 1 : quote;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { animate, style, transition, trigger } from '@angular/animations';
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||||
|
|
||||||
const slideIn = [style({ transform: 'translateX(-85%)' }), animate('600ms ease')];
|
const slideIn = [style({ transform: 'translateX(-85%)' }), animate('600ms ease')];
|
||||||
const slideOut = [
|
const slideOut = [
|
||||||
@ -11,4 +11,9 @@ const slideOut = [
|
|||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const fadeAnimation = trigger('fade', [
|
||||||
|
state('in', style({ opacity: 1 })),
|
||||||
|
transition(':enter', [style({ opacity: 0 }), animate(600)]),
|
||||||
|
transition(':leave', animate(600, style({ opacity: 0 })))
|
||||||
|
]);
|
||||||
export const navItemAnim = trigger('navItemAnim', [transition(':enter', slideIn), transition(':leave', slideOut)]);
|
export const navItemAnim = trigger('navItemAnim', [transition(':enter', slideIn), transition(':leave', slideOut)]);
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<div class="bar-wrapper" *ngIf="isApplauseTypeBar">
|
||||||
|
<os-progress class="progress-bar" [value]="percent">
|
||||||
|
<div class="level-indicator">
|
||||||
|
<div class="level">
|
||||||
|
<b *ngIf="showLevel && hasLevel" [@fade]="'in'">{{ level }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</os-progress>
|
||||||
|
</div>
|
@ -0,0 +1,26 @@
|
|||||||
|
.bar-wrapper {
|
||||||
|
min-width: 30px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
display: block;
|
||||||
|
width: 30px;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-indicator {
|
||||||
|
height: 50px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.level {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.particle-display {
|
||||||
|
// height: 100%;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { ApplauseDisplayComponent } from './applause-display.component';
|
||||||
|
|
||||||
|
describe('ApplauseDisplayComponent', () => {
|
||||||
|
let component: ApplauseDisplayComponent;
|
||||||
|
let fixture: ComponentFixture<ApplauseDisplayComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ApplauseDisplayComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { Applause, ApplauseService, ApplauseType } from 'app/core/ui-services/applause.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { fadeAnimation } from 'app/shared/animations';
|
||||||
|
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-applause-display',
|
||||||
|
templateUrl: './applause-display.component.html',
|
||||||
|
styleUrls: ['./applause-display.component.scss'],
|
||||||
|
animations: [fadeAnimation],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ApplauseDisplayComponent extends BaseViewComponentDirective {
|
||||||
|
public level = 0;
|
||||||
|
public showLevel: boolean;
|
||||||
|
public percent = 0;
|
||||||
|
|
||||||
|
public get hasLevel(): boolean {
|
||||||
|
return !!this.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isApplauseTypeBar(): boolean {
|
||||||
|
return this.applauseService.applauseType === ApplauseType.bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
cd: ChangeDetectorRef,
|
||||||
|
private applauseService: ApplauseService,
|
||||||
|
configService: ConfigService
|
||||||
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
this.subscriptions.push(
|
||||||
|
applauseService.applauseLevelObservable.subscribe(applauseLevel => {
|
||||||
|
this.level = applauseLevel || 0;
|
||||||
|
this.percent = this.applauseService.getApplauseQuote(this.level) * 100;
|
||||||
|
cd.markForCheck();
|
||||||
|
}),
|
||||||
|
configService.get<ApplauseType>('general_system_applause_type').subscribe(() => {
|
||||||
|
cd.markForCheck();
|
||||||
|
}),
|
||||||
|
configService.get<boolean>('general_system_applause_show_level').subscribe(show => {
|
||||||
|
this.showLevel = show;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -61,9 +61,7 @@
|
|||||||
matTooltip="{{ 'Exit live conference and continue livestream' | translate }}"
|
matTooltip="{{ 'Exit live conference and continue livestream' | translate }}"
|
||||||
*ngIf="videoStreamUrl && canSeeLiveStream && !isJitsiDialogOpen"
|
*ngIf="videoStreamUrl && canSeeLiveStream && !isJitsiDialogOpen"
|
||||||
>
|
>
|
||||||
<mat-icon color="warn">
|
<mat-icon color="warn"> meeting_room </mat-icon>
|
||||||
meeting_room
|
|
||||||
</mat-icon>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- mute/unmute button -->
|
<!-- mute/unmute button -->
|
||||||
@ -90,7 +88,11 @@
|
|||||||
(click)="enterConversation()"
|
(click)="enterConversation()"
|
||||||
matTooltip="{{ 'Enter live conference' | translate }}"
|
matTooltip="{{ 'Enter live conference' | translate }}"
|
||||||
>
|
>
|
||||||
<mat-icon color="primary" [@fadeInOut]="isEnterMeetingRoomVisible" (@fadeInOut.done)="triggerMeetingRoomButtonAnimation()">
|
<mat-icon
|
||||||
|
color="primary"
|
||||||
|
[@fadeInOut]="isEnterMeetingRoomVisible"
|
||||||
|
(@fadeInOut.done)="triggerMeetingRoomButtonAnimation()"
|
||||||
|
>
|
||||||
meeting_room
|
meeting_room
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
@ -103,11 +105,23 @@
|
|||||||
*ngIf="enableJitsi && !isAccessPermitted"
|
*ngIf="enableJitsi && !isAccessPermitted"
|
||||||
[routerLink]="['/agenda/speakers']"
|
[routerLink]="['/agenda/speakers']"
|
||||||
>
|
>
|
||||||
<mat-icon>
|
<mat-icon> no_meeting_room </mat-icon>
|
||||||
no_meeting_room
|
|
||||||
</mat-icon>
|
|
||||||
</a>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- applause button -->
|
||||||
|
<button
|
||||||
|
class="quick-icon indicator"
|
||||||
|
[disabled]="applauseDisabled"
|
||||||
|
mat-mini-fab
|
||||||
|
(click)="sendApplause()"
|
||||||
|
matTooltip="{{ 'Send applause' | translate }}"
|
||||||
|
*ngIf="showApplause"
|
||||||
|
[matBadge]="showApplauseBadge ? applauseLevel : null"
|
||||||
|
matBadgeColor="accent"
|
||||||
|
>
|
||||||
|
<mat-icon svgIcon="clapping_hands"></mat-icon>
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
@ -118,6 +132,8 @@
|
|||||||
'cast-shadow': showJitsiWindow
|
'cast-shadow': showJitsiWindow
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
<os-applause-display *ngIf="showApplause && isApplauseTypeBar" class="applause"></os-applause-display>
|
||||||
|
|
||||||
<!-- open-window button -->
|
<!-- open-window button -->
|
||||||
<button class="toggle-list-button" mat-button (click)="toggleShowJitsi()">
|
<button class="toggle-list-button" mat-button (click)="toggleShowJitsi()">
|
||||||
<ng-container *ngIf="currentState == state.jitsi">
|
<ng-container *ngIf="currentState == state.jitsi">
|
||||||
@ -149,7 +165,7 @@
|
|||||||
<div
|
<div
|
||||||
class="jitsi-list"
|
class="jitsi-list"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'cdk-visually-hidden': !showJitsiWindow
|
'hide-height': !showJitsiWindow
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ng-container *ngIf="currentState == state.jitsi">
|
<ng-container *ngIf="currentState == state.jitsi">
|
||||||
@ -175,6 +191,10 @@
|
|||||||
|
|
||||||
<!-- user list -->
|
<!-- user list -->
|
||||||
<div class="room-members" *ngIf="isJitsiActive && isJoined">
|
<div class="room-members" *ngIf="isJitsiActive && isJoined">
|
||||||
|
<os-particle-display
|
||||||
|
*ngIf="isApplauseTypeParticles"
|
||||||
|
class="room-list-applause-particles"
|
||||||
|
></os-particle-display>
|
||||||
<div class="member-list">
|
<div class="member-list">
|
||||||
<ol>
|
<ol>
|
||||||
<li
|
<li
|
||||||
@ -196,6 +216,7 @@
|
|||||||
<ng-container *ngIf="currentState == state.stream">
|
<ng-container *ngIf="currentState == state.stream">
|
||||||
<os-vjs-player
|
<os-vjs-player
|
||||||
[videoUrl]="videoStreamUrl"
|
[videoUrl]="videoStreamUrl"
|
||||||
|
[showParticles]="isApplauseTypeParticles"
|
||||||
(started)="onSteamStarted()"
|
(started)="onSteamStarted()"
|
||||||
*ngIf="(canSeeLiveStream && !streamActiveInAnotherTab) || streamRunning"
|
*ngIf="(canSeeLiveStream && !streamActiveInAnotherTab) || streamRunning"
|
||||||
></os-vjs-player>
|
></os-vjs-player>
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
.jitsi-bar {
|
.jitsi-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: relative;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
$wrapper-padding: 5px;
|
$wrapper-padding: 5px;
|
||||||
$bar-height: 40px;
|
$bar-height: 40px;
|
||||||
@ -90,11 +91,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-wrapper {
|
.list-wrapper {
|
||||||
|
position: relative;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
min-height: $bar-height;
|
min-height: $bar-height;
|
||||||
padding-top: $wrapper-padding;
|
padding-top: $wrapper-padding;
|
||||||
border-top-right-radius: 4px;
|
border-top-right-radius: 4px;
|
||||||
|
|
||||||
|
.applause {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 90px;
|
||||||
|
left: -90px;
|
||||||
|
bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.toggle-list-button {
|
.toggle-list-button {
|
||||||
position: relative;
|
position: relative;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
@ -121,6 +131,7 @@
|
|||||||
.jitsi-list {
|
.jitsi-list {
|
||||||
.content {
|
.content {
|
||||||
height: 40vh;
|
height: 40vh;
|
||||||
|
max-height: 100%;
|
||||||
clear: both;
|
clear: both;
|
||||||
|
|
||||||
.disconnected {
|
.disconnected {
|
||||||
@ -136,7 +147,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.room-members {
|
.room-members {
|
||||||
height: inherit;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.room-list-applause-particles {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 70px;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.member-list {
|
.member-list {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
.indicator {
|
.indicator {
|
||||||
color: mat-color($primary, default-contrast);
|
color: mat-color($primary, default-contrast);
|
||||||
|
|
||||||
|
svg path {
|
||||||
|
fill: mat-color($primary) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-list-button {
|
.toggle-list-button {
|
||||||
|
@ -11,6 +11,7 @@ import { ConstantsService } from 'app/core/core-services/constants.service';
|
|||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { Deferred } from 'app/core/promises/deferred';
|
import { Deferred } from 'app/core/promises/deferred';
|
||||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||||
|
import { ApplauseService, ApplauseType } from 'app/core/ui-services/applause.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { UserMediaPermService } from 'app/core/ui-services/user-media-perm.service';
|
import { UserMediaPermService } from 'app/core/ui-services/user-media-perm.service';
|
||||||
import { UserListIndexType } from 'app/site/agenda/models/view-list-of-speakers';
|
import { UserListIndexType } from 'app/site/agenda/models/view-list-of-speakers';
|
||||||
@ -96,6 +97,10 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit
|
|||||||
public showJitsiWindow = true;
|
public showJitsiWindow = true;
|
||||||
public muted = true;
|
public muted = true;
|
||||||
|
|
||||||
|
public showApplause: boolean;
|
||||||
|
public applauseDisabled = false;
|
||||||
|
private applauseTimeout: number;
|
||||||
|
|
||||||
@ViewChild('jitsi')
|
@ViewChild('jitsi')
|
||||||
private jitsiNode: ElementRef;
|
private jitsiNode: ElementRef;
|
||||||
|
|
||||||
@ -180,6 +185,26 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit
|
|||||||
public currentState: ConferenceState;
|
public currentState: ConferenceState;
|
||||||
public isEnterMeetingRoomVisible = true;
|
public isEnterMeetingRoomVisible = true;
|
||||||
|
|
||||||
|
public applauseLevel = 0;
|
||||||
|
private showApplauseLevel: boolean;
|
||||||
|
private isApplausBarUsed: boolean;
|
||||||
|
|
||||||
|
public get showApplauseBadge(): boolean {
|
||||||
|
return this.showApplauseLevel && this.applauseLevel > 0 && (!this.showJitsiWindow || !this.isApplausBarUsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get applauseType(): ApplauseType {
|
||||||
|
return this.applauseService.applauseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isApplauseTypeBar(): boolean {
|
||||||
|
return this.applauseType === ApplauseType.bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isApplauseTypeParticles(): boolean {
|
||||||
|
return this.applauseType === ApplauseType.particles;
|
||||||
|
}
|
||||||
|
|
||||||
private configOverwrite = {
|
private configOverwrite = {
|
||||||
startAudioOnly: false,
|
startAudioOnly: false,
|
||||||
// allows jitsi on mobile devices
|
// allows jitsi on mobile devices
|
||||||
@ -237,7 +262,8 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit
|
|||||||
private constantsService: ConstantsService,
|
private constantsService: ConstantsService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private closService: CurrentListOfSpeakersService,
|
private closService: CurrentListOfSpeakersService,
|
||||||
private userMediaPermService: UserMediaPermService
|
private userMediaPermService: UserMediaPermService,
|
||||||
|
private applauseService: ApplauseService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, snackBar);
|
super(titleService, translate, snackBar);
|
||||||
}
|
}
|
||||||
@ -350,6 +376,22 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit
|
|||||||
}),
|
}),
|
||||||
this.configService.get<boolean>('general_system_conference_open_video').subscribe(open => {
|
this.configService.get<boolean>('general_system_conference_open_video').subscribe(open => {
|
||||||
this.configOverwrite.startWithVideoMuted = !open;
|
this.configOverwrite.startWithVideoMuted = !open;
|
||||||
|
}),
|
||||||
|
this.configService.get<boolean>('general_system_applause_enable').subscribe(enable => {
|
||||||
|
this.showApplause = enable;
|
||||||
|
}),
|
||||||
|
this.configService.get<number>('general_system_stream_applause_timeout').subscribe(timeout => {
|
||||||
|
this.applauseTimeout = (timeout || 1) * 1000;
|
||||||
|
}),
|
||||||
|
this.configService.get<boolean>('general_system_applause_show_level').subscribe(show => {
|
||||||
|
this.showApplauseLevel = show;
|
||||||
|
}),
|
||||||
|
this.configService.get<any>('general_system_applause_type').subscribe(type => {
|
||||||
|
if (type === 'applause-type-bar') {
|
||||||
|
this.isApplausBarUsed = true;
|
||||||
|
} else {
|
||||||
|
this.isApplausBarUsed = false;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -362,7 +404,10 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit
|
|||||||
map(los => los?.findUserIndexOnList(this.operator.user.id) ?? -1),
|
map(los => los?.findUserIndexOnList(this.operator.user.id) ?? -1),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
)
|
)
|
||||||
.subscribe(userLosIndex => this.autoJoinJitsiByLosIndex(userLosIndex))
|
.subscribe(userLosIndex => this.autoJoinJitsiByLosIndex(userLosIndex)),
|
||||||
|
this.applauseService.applauseLevelObservable.subscribe(applauseLevel => {
|
||||||
|
this.applauseLevel = applauseLevel || 0;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,4 +640,12 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit
|
|||||||
private setConferenceState(newState: ConferenceState): void {
|
private setConferenceState(newState: ConferenceState): void {
|
||||||
this.currentState = newState;
|
this.currentState = newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sendApplause(): void {
|
||||||
|
this.applauseDisabled = true;
|
||||||
|
this.applauseService.sendApplause();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.applauseDisabled = false;
|
||||||
|
}, this.applauseTimeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
<div [osResized]="resizeSubject" class="particle-wrapper">
|
||||||
|
<Particles id="particles" [options]="particlesOptions" (particlesLoaded)="particlesLoaded($event)"> </Particles>
|
||||||
|
</div>
|
@ -0,0 +1,7 @@
|
|||||||
|
.particle-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tsparticles-canvas-el {
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { ParticleDisplayComponent } from './particle-display.component';
|
||||||
|
|
||||||
|
describe('ParticleDisplayComponent', () => {
|
||||||
|
let component: ParticleDisplayComponent;
|
||||||
|
let fixture: ComponentFixture<ParticleDisplayComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ParticleDisplayComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,233 @@
|
|||||||
|
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { auditTime } from 'rxjs/operators';
|
||||||
|
import { Container, SizeMode } from 'tsparticles';
|
||||||
|
import { Shape } from 'tsparticles/dist/Options/Classes/Particles/Shape/Shape';
|
||||||
|
import { IImageShape } from 'tsparticles/dist/Options/Interfaces/Particles/Shape/IImageShape';
|
||||||
|
import { Emitters } from 'tsparticles/dist/Plugins/Emitters/Emitters';
|
||||||
|
|
||||||
|
import { ApplauseService } from 'app/core/ui-services/applause.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { ElementSize } from 'app/shared/directives/resized.directive';
|
||||||
|
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-particle-display',
|
||||||
|
templateUrl: './particle-display.component.html',
|
||||||
|
styleUrls: ['./particle-display.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ParticleDisplayComponent extends BaseViewComponentDirective {
|
||||||
|
public resizeSubject = new Subject<ElementSize>();
|
||||||
|
private resizeAuditTime = 200;
|
||||||
|
|
||||||
|
private particleContainer: Container;
|
||||||
|
|
||||||
|
public set particleImage(imageUrl: string) {
|
||||||
|
this.setParticleImage(imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set particleLevel(level: number) {
|
||||||
|
this.setParticleLevel(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
private noAutomaticParticles = {
|
||||||
|
value: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
private slowBlinkingOpacity = {
|
||||||
|
value: 0.8,
|
||||||
|
animation: {
|
||||||
|
enable: true,
|
||||||
|
speed: 1,
|
||||||
|
sync: false,
|
||||||
|
minimumValue: 0.3
|
||||||
|
},
|
||||||
|
random: {
|
||||||
|
enable: true,
|
||||||
|
minimumValue: 0.8
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private imageOptions: IImageShape = {
|
||||||
|
replace_color: false,
|
||||||
|
replaceColor: false,
|
||||||
|
src: '',
|
||||||
|
width: 24,
|
||||||
|
height: 24
|
||||||
|
};
|
||||||
|
|
||||||
|
private customImageShape = {
|
||||||
|
type: 'image',
|
||||||
|
image: this.imageOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
private charShapeHearth = {
|
||||||
|
type: 'char',
|
||||||
|
options: {
|
||||||
|
char: {
|
||||||
|
fill: true,
|
||||||
|
font: 'Verdana',
|
||||||
|
weight: '200',
|
||||||
|
style: '',
|
||||||
|
value: ['❤']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private slightlyRandomSize: any = {
|
||||||
|
value: 16,
|
||||||
|
random: {
|
||||||
|
enable: true,
|
||||||
|
minimumValue: 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private moveUpOptions = {
|
||||||
|
enable: true,
|
||||||
|
direction: 'top',
|
||||||
|
speed: 1.0,
|
||||||
|
angle: {
|
||||||
|
offset: 45,
|
||||||
|
value: 90
|
||||||
|
},
|
||||||
|
gravity: {
|
||||||
|
enable: true,
|
||||||
|
maxSpeed: 1.5,
|
||||||
|
acceleration: -3
|
||||||
|
},
|
||||||
|
outModes: {
|
||||||
|
left: 'bounce',
|
||||||
|
right: 'bounce',
|
||||||
|
top: 'destroy'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private slowRandomRotation = {
|
||||||
|
value: 0,
|
||||||
|
enable: true,
|
||||||
|
direction: 'random',
|
||||||
|
animation: {
|
||||||
|
enable: true,
|
||||||
|
speed: 9
|
||||||
|
},
|
||||||
|
random: {
|
||||||
|
enable: true,
|
||||||
|
minimumValue: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private randomColor = {
|
||||||
|
value: 'random'
|
||||||
|
};
|
||||||
|
|
||||||
|
private singleBottomEmitter = [
|
||||||
|
{
|
||||||
|
direction: 'top',
|
||||||
|
rate: {
|
||||||
|
quantity: 0,
|
||||||
|
delay: 0.33
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
x: 50,
|
||||||
|
y: 100
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
mode: SizeMode.percent,
|
||||||
|
width: 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
public particlesOptions = {
|
||||||
|
fpsLimit: 30,
|
||||||
|
particles: {
|
||||||
|
number: this.noAutomaticParticles,
|
||||||
|
opacity: this.slowBlinkingOpacity,
|
||||||
|
rotate: this.slowRandomRotation,
|
||||||
|
move: this.moveUpOptions,
|
||||||
|
color: this.randomColor,
|
||||||
|
shape: this.charShapeHearth,
|
||||||
|
size: this.slightlyRandomSize
|
||||||
|
},
|
||||||
|
emitters: this.singleBottomEmitter,
|
||||||
|
detectRetina: true
|
||||||
|
};
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
configService: ConfigService,
|
||||||
|
private applauseService: ApplauseService
|
||||||
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.resizeSubject.pipe(auditTime(this.resizeAuditTime)).subscribe(size => {
|
||||||
|
this.updateParticleContainer(size);
|
||||||
|
}),
|
||||||
|
applauseService.applauseLevelObservable.subscribe(applause => {
|
||||||
|
this.particleLevel = this.calcEmitterLevel(applause || 0);
|
||||||
|
}),
|
||||||
|
configService.get<string>('general_system_applause_particle_image').subscribe(particleImage => {
|
||||||
|
this.particleImage = particleImage || undefined;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setParticleImage(particleImage: string): void {
|
||||||
|
if (particleImage) {
|
||||||
|
this.imageOptions.src = particleImage;
|
||||||
|
(this.particlesOptions.particles.shape as any) = this.customImageShape;
|
||||||
|
} else {
|
||||||
|
(this.particlesOptions.particles.shape as any) = this.charShapeHearth;
|
||||||
|
}
|
||||||
|
if (this.particleContainer) {
|
||||||
|
this.particleContainer.options.particles.load(this.particlesOptions.particles as any);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private calcEmitterLevel(applauseLevel: number): number {
|
||||||
|
if (!applauseLevel) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let emitterLevel = this.applauseService.getApplauseQuote(applauseLevel);
|
||||||
|
emitterLevel = Math.ceil(emitterLevel * 10);
|
||||||
|
return emitterLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setParticleLevel(level: number): void {
|
||||||
|
if (this.particleContainer) {
|
||||||
|
const emitters = this.particleContainer.plugins.get('emitters') as Emitters;
|
||||||
|
if (emitters) {
|
||||||
|
emitters.array[0].emitterOptions.rate.quantity = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateParticleContainer(size: ElementSize): void {
|
||||||
|
if (!size.height || !size.width) {
|
||||||
|
this.stop();
|
||||||
|
} else {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private stop(): void {
|
||||||
|
this.particleContainer?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private refresh(): void {
|
||||||
|
this.particleContainer?.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public particlesLoaded(container: Container): void {
|
||||||
|
this.particleContainer = container;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<div class="progress-wrapper">
|
||||||
|
<mat-icon class="end-icon" *ngIf="endIcon"> {{ endIcon }} </mat-icon>
|
||||||
|
<div class="slot">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="buffer bar"></div>
|
||||||
|
<div class="progress bar" [style.height.%]="value"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,36 @@
|
|||||||
|
$bar-margin: 10px;
|
||||||
|
$bar-border-radius: 15px;
|
||||||
|
|
||||||
|
.progress-wrapper {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.end-icon {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin-left: $bar-margin;
|
||||||
|
margin-right: $bar-margin;
|
||||||
|
border-radius: $bar-border-radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buffer {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
transition: height 0.5s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
|
@mixin os-progress-theme($theme) {
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
$accent: map-get($theme, accent);
|
||||||
|
|
||||||
|
.progress-wrapper {
|
||||||
|
background-color: mat-color($primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.end-icon {
|
||||||
|
color: mat-color($primary, default-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot {
|
||||||
|
color: mat-color($primary, default-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.buffer {
|
||||||
|
background-color: mat-color($primary, darker);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
background-color: mat-color($accent);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProgressComponent } from './progress.component';
|
||||||
|
|
||||||
|
describe('ProgressComponent', () => {
|
||||||
|
let component: ProgressComponent;
|
||||||
|
let fixture: ComponentFixture<ProgressComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ProgressComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProgressComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,14 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-progress',
|
||||||
|
templateUrl: './progress.component.html',
|
||||||
|
styleUrls: ['./progress.component.scss']
|
||||||
|
})
|
||||||
|
export class ProgressComponent {
|
||||||
|
@Input()
|
||||||
|
public value = 0;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public endIcon: string;
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
<div class="video-wrapper">
|
<div class="video-wrapper">
|
||||||
|
<os-particle-display *ngIf="showParticles" class="applause-particles"></os-particle-display>
|
||||||
<div class="player-container" [ngClass]="{ hide: !isUrlOnline }">
|
<div class="player-container" [ngClass]="{ hide: !isUrlOnline }">
|
||||||
<video #videoPlayer class="video-js" controls preload="none"></video>
|
<video #videoPlayer class="video-js" controls preload="none"></video>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
.video-wrapper {
|
.video-wrapper {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 200px;
|
|
||||||
|
.applause-particles {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
pointer-events: none !important;
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.is-offline-wrapper {
|
.is-offline-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -48,6 +48,9 @@ export class VjsPlayerComponent implements OnInit, OnDestroy {
|
|||||||
this.checkVideoUrl();
|
this.checkVideoUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public showParticles: boolean;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
private started: EventEmitter<void> = new EventEmitter();
|
private started: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
|
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { ResizeSensor } from 'css-element-queries';
|
import { ResizeSensor } from 'css-element-queries';
|
||||||
|
import { Interface } from 'readline';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
export interface ElementSize {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This directive takes a Subject<void> as input and everytime the surrounding element
|
* This directive takes a Subject<ElementSize> as input and everytime the surrounding element
|
||||||
* was resized, the subject is fired.
|
* was resized, the subject is fired.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
@ -15,7 +20,7 @@ import { Subject } from 'rxjs';
|
|||||||
})
|
})
|
||||||
export class ResizedDirective implements OnInit {
|
export class ResizedDirective implements OnInit {
|
||||||
@Input()
|
@Input()
|
||||||
public osResized: Subject<void>;
|
public osResized: Subject<ElementSize>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Old width, to check, if the width has actually changed.
|
* Old width, to check, if the width has actually changed.
|
||||||
@ -54,7 +59,10 @@ export class ResizedDirective implements OnInit {
|
|||||||
this.oldHeight = newHeight;
|
this.oldHeight = newHeight;
|
||||||
|
|
||||||
if (this.osResized) {
|
if (this.osResized) {
|
||||||
this.osResized.next();
|
this.osResized.next({
|
||||||
|
width: newWidth,
|
||||||
|
height: newHeight
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,10 @@ import { JitsiComponent } from './components/jitsi/jitsi.component';
|
|||||||
import { VjsPlayerComponent } from './components/vjs-player/vjs-player.component';
|
import { VjsPlayerComponent } from './components/vjs-player/vjs-player.component';
|
||||||
import { LiveStreamComponent } from './components/live-stream/live-stream.component';
|
import { LiveStreamComponent } from './components/live-stream/live-stream.component';
|
||||||
import { ListOfSpeakersContentComponent } from './components/list-of-speakers-content/list-of-speakers-content.component';
|
import { ListOfSpeakersContentComponent } from './components/list-of-speakers-content/list-of-speakers-content.component';
|
||||||
|
import { ApplauseDisplayComponent } from './components/applause-display/applause-display.component';
|
||||||
|
import { ProgressComponent } from './components/progress/progress.component';
|
||||||
|
import { NgParticlesModule } from 'ng-particles';
|
||||||
|
import { ParticleDisplayComponent } from './components/particle-display/particle-display.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Share Module for all "dumb" components and pipes.
|
* Share Module for all "dumb" components and pipes.
|
||||||
@ -194,7 +198,8 @@ import { ListOfSpeakersContentComponent } from './components/list-of-speakers-co
|
|||||||
PblNgridTargetEventsModule,
|
PblNgridTargetEventsModule,
|
||||||
PdfViewerModule,
|
PdfViewerModule,
|
||||||
NgxMaterialTimepickerModule,
|
NgxMaterialTimepickerModule,
|
||||||
ChartsModule
|
ChartsModule,
|
||||||
|
NgParticlesModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
@ -364,7 +369,10 @@ import { ListOfSpeakersContentComponent } from './components/list-of-speakers-co
|
|||||||
JitsiComponent,
|
JitsiComponent,
|
||||||
VjsPlayerComponent,
|
VjsPlayerComponent,
|
||||||
LiveStreamComponent,
|
LiveStreamComponent,
|
||||||
ListOfSpeakersContentComponent
|
ListOfSpeakersContentComponent,
|
||||||
|
ApplauseDisplayComponent,
|
||||||
|
ProgressComponent,
|
||||||
|
ParticleDisplayComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-card class="spacer-bottom-60" [ngClass]="isEditing ? 'os-form-card' : 'os-card'">
|
<mat-card class="spacer-bottom-60" [ngClass]="isEditing ? 'os-form-card' : 'os-card'">
|
||||||
<mat-card [ngClass]="isEditing ? 'os-form-card' : 'os-card'">
|
|
||||||
<ng-container *ngIf="!isEditing">
|
<ng-container *ngIf="!isEditing">
|
||||||
<div class="app-content">
|
<div class="app-content">
|
||||||
<h1>{{ startContent.general_event_welcome_title | translate }}</h1>
|
<h1>{{ startContent.general_event_welcome_title | translate }}</h1>
|
||||||
|
@ -9,43 +9,44 @@ $narrow-spacing: (
|
|||||||
);
|
);
|
||||||
|
|
||||||
/** Import brand theme */
|
/** Import brand theme */
|
||||||
@import './assets/styles/themes/default-dark.scss';
|
@import '~assets/styles/themes/default-dark.scss';
|
||||||
@import './assets/styles/themes/default-light.scss';
|
@import '~assets/styles/themes/default-light.scss';
|
||||||
@import './assets/styles/themes/green-dark.scss';
|
@import '~assets/styles/themes/green-dark.scss';
|
||||||
@import './assets/styles/themes/green-light.scss';
|
@import '~assets/styles/themes/green-light.scss';
|
||||||
@import './assets/styles/themes/red-dark.scss';
|
@import '~assets/styles/themes/red-dark.scss';
|
||||||
@import './assets/styles/themes/red-light.scss';
|
@import '~assets/styles/themes/red-light.scss';
|
||||||
@import './assets/styles/themes/solarized-dark.scss';
|
@import '~assets/styles/themes/solarized-dark.scss';
|
||||||
|
|
||||||
/** Global component style definition */
|
/** Global component style definition */
|
||||||
@import './assets/styles/global-components-style.scss';
|
@import '~assets/styles/global-components-style.scss';
|
||||||
|
|
||||||
/** Import the component-related style sheets here */
|
/** Import the component-related style sheets here */
|
||||||
@import './app/site/site.component.scss-theme.scss';
|
@import '~app/site/site.component.scss-theme.scss';
|
||||||
@import './app/shared/components/projector-button/projector-button.component.scss';
|
@import '~app/shared/components/projector-button/projector-button.component.scss';
|
||||||
@import './app/site/agenda/components/list-of-speakers/list-of-speakers.component.scss-theme.scss';
|
@import '~app/site/agenda/components/list-of-speakers/list-of-speakers.component.scss-theme.scss';
|
||||||
@import './app/shared/components/sorting-tree/sorting-tree.component.scss';
|
@import '~app/shared/components/sorting-tree/sorting-tree.component.scss';
|
||||||
@import './app/shared/components/global-spinner/global-spinner.component.scss';
|
@import '~app/shared/components/global-spinner/global-spinner.component.scss';
|
||||||
@import './app/shared/components/tile/tile.component.scss';
|
@import '~app/shared/components/tile/tile.component.scss';
|
||||||
@import './app/shared/components/block-tile/block-tile.component.scss';
|
@import '~app/shared/components/block-tile/block-tile.component.scss';
|
||||||
@import './app/shared/components/icon-container/icon-container.component.scss';
|
@import '~app/shared/components/icon-container/icon-container.component.scss';
|
||||||
@import './app/site/common/components/start/start.component.scss';
|
@import '~app/site/common/components/start/start.component.scss';
|
||||||
@import './app/site/mediafiles/components/mediafile-list/mediafile-list.component.scss-theme.scss';
|
@import '~app/site/mediafiles/components/mediafile-list/mediafile-list.component.scss-theme.scss';
|
||||||
@import './app/site/common/components/super-search/super-search.component.scss';
|
@import '~app/site/common/components/super-search/super-search.component.scss';
|
||||||
@import './app/shared/components/rounded-input/rounded-input.component.scss';
|
@import '~app/shared/components/rounded-input/rounded-input.component.scss';
|
||||||
@import './app/shared/components/meta-text-block/meta-text-block.component.scss';
|
@import '~app/shared/components/meta-text-block/meta-text-block.component.scss';
|
||||||
@import './app/site/config/components/config-field/config-field.component.scss-theme.scss';
|
@import '~app/site/config/components/config-field/config-field.component.scss-theme.scss';
|
||||||
@import './app/site/motions/modules/motion-detail/components/amendment-create-wizard/amendment-create-wizard.components.scss-theme.scss';
|
@import '~app/site/motions/modules/motion-detail/components/amendment-create-wizard/amendment-create-wizard.components.scss-theme.scss';
|
||||||
@import './app/site/motions/modules/motion-detail/components/motion-detail-diff/motion-detail-diff.component.scss-theme.scss';
|
@import '~app/site/motions/modules/motion-detail/components/motion-detail-diff/motion-detail-diff.component.scss-theme.scss';
|
||||||
@import './app/shared/components/banner/banner.component.scss-theme.scss';
|
@import '~app/shared/components/banner/banner.component.scss-theme.scss';
|
||||||
@import './app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss-theme.scss';
|
@import '~app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss-theme.scss';
|
||||||
@import './app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.scss-theme.scss';
|
@import '~app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.scss-theme.scss';
|
||||||
@import './app/site/assignments/modules/assignment-poll/components/assignment-poll-detail/assignment-poll-detail-component.scss-theme.scss';
|
@import '~app/site/assignments/modules/assignment-poll/components/assignment-poll-detail/assignment-poll-detail-component.scss-theme.scss';
|
||||||
@import './app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss';
|
@import '~app/shared/components/progress-snack-bar/progress-snack-bar.component.scss-theme.scss';
|
||||||
@import './app/shared/components/jitsi/jitsi.component.scss-theme.scss';
|
@import '~app/shared/components/jitsi/jitsi.component.scss-theme.scss';
|
||||||
@import './app/shared/components/list-view-table/list-view-table.component.scss-theme.scss';
|
@import '~app/shared/components/list-view-table/list-view-table.component.scss-theme.scss';
|
||||||
@import './app/site/common/components/user-statistics/user-statistics.component.scss-theme.scss';
|
@import '~app/site/common/components/user-statistics/user-statistics.component.scss-theme.scss';
|
||||||
@import './app/site/login/components/login-wrapper/login-wrapper.component.scss-theme.scss';
|
@import '~app/site/login/components/login-wrapper/login-wrapper.component.scss-theme.scss';
|
||||||
|
@import '~app/shared/components/progress/progress.component.scss-theme.scss';
|
||||||
|
|
||||||
/** Mix the component-related style-rules. Every single custom style goes here */
|
/** Mix the component-related style-rules. Every single custom style goes here */
|
||||||
@mixin openslides-components-theme($theme) {
|
@mixin openslides-components-theme($theme) {
|
||||||
@ -72,6 +73,7 @@ $narrow-spacing: (
|
|||||||
@include os-list-view-table-theme($theme);
|
@include os-list-view-table-theme($theme);
|
||||||
@include os-user-statistics-style($theme);
|
@include os-user-statistics-style($theme);
|
||||||
@include os-login-wrapper-theme($theme);
|
@include os-login-wrapper-theme($theme);
|
||||||
|
@include os-progress-theme($theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
.openslides-default-light-theme {
|
.openslides-default-light-theme {
|
||||||
@ -126,7 +128,7 @@ $narrow-spacing: (
|
|||||||
/**
|
/**
|
||||||
* Custom configuration for light themes
|
* Custom configuration for light themes
|
||||||
*/
|
*/
|
||||||
[class^="openslides-"][class*="-light-theme"] {
|
[class^='openslides-'][class*='-light-theme'] {
|
||||||
.logo-container {
|
.logo-container {
|
||||||
img.dark {
|
img.dark {
|
||||||
display: none;
|
display: none;
|
||||||
@ -140,7 +142,7 @@ $narrow-spacing: (
|
|||||||
/**
|
/**
|
||||||
* Custom configuration for dark themes
|
* Custom configuration for dark themes
|
||||||
*/
|
*/
|
||||||
[class^="openslides-"][class*="-dark-theme"] {
|
[class^='openslides-'][class*='-dark-theme'] {
|
||||||
color: white;
|
color: white;
|
||||||
.logo-container {
|
.logo-container {
|
||||||
img.dark {
|
img.dark {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import './assets/styles/color-palettes/os-blue';
|
@import '.~assets/styles/color-palettes/os-blue';
|
||||||
|
|
||||||
$openslides-primary: mat-palette($openslides-blue);
|
$openslides-primary: mat-palette($openslides-blue);
|
||||||
$openslides-accent: mat-palette($mat-light-blue);
|
$openslides-accent: mat-palette($mat-light-blue);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import './assets/styles/color-palettes/os-blue';
|
@import '~assets/styles/color-palettes/os-blue';
|
||||||
|
|
||||||
// Generate paletes using: https://material.io/design/color/
|
// Generate paletes using: https://material.io/design/color/
|
||||||
// default values for mat-palette: $default: 500, $lighter: 100, $darker: 700.
|
// default values for mat-palette: $default: 500, $lighter: 100, $darker: 700.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import './assets/styles/color-palettes/os-green.scss';
|
@import '~assets/styles/color-palettes/os-green.scss';
|
||||||
|
|
||||||
$openslides-primary: mat-palette($openslides-green);
|
$openslides-primary: mat-palette($openslides-green);
|
||||||
$openslides-accent: mat-palette($mat-amber);
|
$openslides-accent: mat-palette($mat-amber);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import './assets/styles/color-palettes/os-green.scss';
|
@import '~assets/styles/color-palettes/os-green.scss';
|
||||||
|
|
||||||
$openslides-primary: mat-palette($openslides-green);
|
$openslides-primary: mat-palette($openslides-green);
|
||||||
$openslides-accent: mat-palette($mat-amber);
|
$openslides-accent: mat-palette($mat-amber);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import './assets/styles/color-palettes/os-red.scss';
|
@import '~assets/styles/color-palettes/os-red.scss';
|
||||||
|
|
||||||
$openslides-primary: mat-palette($openslides-primary-red, 500, 300, 900);
|
$openslides-primary: mat-palette($openslides-primary-red, 500, 300, 900);
|
||||||
$openslides-accent: mat-palette($mat-amber);
|
$openslides-accent: mat-palette($mat-amber);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import './assets/styles/color-palettes/os-red.scss';
|
@import '~assets/styles/color-palettes/os-red.scss';
|
||||||
|
|
||||||
$openslides-primary: mat-palette($openslides-primary-red, 500, 300, 900);
|
$openslides-primary: mat-palette($openslides-primary-red, 500, 300, 900);
|
||||||
$openslides-accent: mat-palette($mat-amber);
|
$openslides-accent: mat-palette($mat-amber);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
@import './assets/styles/color-palettes/os-cyan.scss';
|
@import '~assets/styles/color-palettes/os-cyan.scss';
|
||||||
@import './assets/styles/color-palettes/os-gray.scss';
|
@import '~assets/styles/color-palettes/os-gray.scss';
|
||||||
|
|
||||||
$openslides-primary: mat-palette($openslides-grey);
|
$openslides-primary: mat-palette($openslides-grey);
|
||||||
$openslides-accent: mat-palette($openslides-cyan);
|
$openslides-accent: mat-palette($openslides-cyan);
|
||||||
|
226
client/src/assets/svg/clapping_hands.svg
Normal file
226
client/src/assets/svg/clapping_hands.svg
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
sodipodi:docname="clapping_hands.svg"
|
||||||
|
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs6">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1782">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#0c2c40;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop1778" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#2ca2ec;stop-opacity:1"
|
||||||
|
offset="1"
|
||||||
|
id="stop1780" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1722">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#f56055;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop1718" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#f56055;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop1720" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1505">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop1501" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop1503" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1505"
|
||||||
|
id="linearGradient1507"
|
||||||
|
x1="-72.521545"
|
||||||
|
y1="-354.46579"
|
||||||
|
x2="-72.521545"
|
||||||
|
y2="-220.85248"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="translate(4.5351563e-5,0.25821471)" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1722"
|
||||||
|
id="linearGradient1724"
|
||||||
|
x1="1637.6523"
|
||||||
|
y1="687.29828"
|
||||||
|
x2="1637.6523"
|
||||||
|
y2="726.80103"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1,0,0,1.708266,4.6566406e-4,-480.80936)" />
|
||||||
|
<radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1782"
|
||||||
|
id="radialGradient1784"
|
||||||
|
cx="773.6098"
|
||||||
|
cy="-57.234745"
|
||||||
|
fx="773.6098"
|
||||||
|
fy="-57.234745"
|
||||||
|
r="507.56699"
|
||||||
|
gradientTransform="matrix(0.78017444,0,0,0.14366746,326.05843,630.13805)"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1080"
|
||||||
|
id="namedview4"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-bbox-edge-midpoints="true"
|
||||||
|
inkscape:snap-bbox-midpoints="true"
|
||||||
|
inkscape:object-paths="true"
|
||||||
|
inkscape:snap-intersection-paths="true"
|
||||||
|
inkscape:snap-smooth-nodes="true"
|
||||||
|
inkscape:snap-midpoints="true"
|
||||||
|
inkscape:snap-object-midpoints="true"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="19.730628"
|
||||||
|
inkscape:cy="14.778162"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g2182"
|
||||||
|
inkscape:snap-global="true"
|
||||||
|
inkscape:document-rotation="0">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-8.5967379,25.493463"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide1229" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-5.4316862,28.164944"
|
||||||
|
orientation="0,-1"
|
||||||
|
id="guide1231" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<g
|
||||||
|
id="g2441"
|
||||||
|
transform="matrix(0.93524686,0,0,0.93524686,-1220.0337,-836.48596)"
|
||||||
|
style="fill:#000000;fill-opacity:1">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.76088502,0,0,0.76030764,311.54674,218.13833)"
|
||||||
|
id="g2397"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311">
|
||||||
|
<g
|
||||||
|
id="g1964-4"
|
||||||
|
transform="rotate(90,218.24928,1114.2299)"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311">
|
||||||
|
<g
|
||||||
|
id="g1948-2"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311">
|
||||||
|
<g
|
||||||
|
id="g1946-2"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311">
|
||||||
|
<g
|
||||||
|
id="g1944-4"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311">
|
||||||
|
<path
|
||||||
|
id="path1942-5"
|
||||||
|
d="M 23,5.5 V 20 c 0,2.2 -1.8,4 -4,4 H 11.7 C 10.62,24 9.6,23.57 8.85,22.81 L 1,14.83 c 0,0 1.26,-1.23 1.3,-1.25 0.22,-0.19 0.49,-0.29 0.79,-0.29 0.22,0 0.42,0.06 0.6,0.16 C 3.73,13.46 8,15.91 8,15.91 V 4 C 8,3.17 8.67,2.5 9.5,2.5 10.33,2.5 11,3.17 11,4 v 7 h 1 V 1.5 C 12,0.67 12.67,0 13.5,0 14.33,0 15,0.67 15,1.5 V 11 h 1 V 2.5 C 16,1.67 16.67,1 17.5,1 18.33,1 19,1.67 19,2.5 V 11 h 1 V 5.5 C 20,4.67 20.67,4 21.5,4 22.33,4 23,4.67 23,5.5 Z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(5.4402324e-7,4.0928558e-5)"
|
||||||
|
id="g1944-4-0"
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13311" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.894784;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
transform="matrix(1.1175883,0,0,1.1175883,-155.61227,-104.47506)"
|
||||||
|
id="g2182">
|
||||||
|
<g
|
||||||
|
id="g860"
|
||||||
|
style="opacity:1;stroke-width:0.855545"
|
||||||
|
transform="matrix(1.0458649,0,0,1.0458649,-60.689016,-41.511804)">
|
||||||
|
<path
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.855541;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 1326.2009,898.5852 -2.6143,2.61248 c 0.054,0.0477 0.1074,0.097 0.1593,0.14738 0.05,0.0525 0.099,0.10627 0.1459,0.16112 l 2.6168,-2.61488 z m -4.6234,-2.21282 -0.434,-3e-5 v 3.69727 c 0.1446,-0.006 0.2894,-0.006 0.434,4e-5 z m 7.1449,6.80056 -3.7002,3e-5 c 0.013,0.14447 0.013,0.28922 2e-4,0.43368 l 3.7,-2e-5 z"
|
||||||
|
id="path862" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.855541;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 1321.1426,895.92578 a 0.44743473,0.44743473 0 0 0 -0.4473,0.44727 v 3.69726 a 0.44743473,0.44743473 0 0 0 0.4668,0.44727 c 0.1322,-0.005 0.2643,-0.006 0.3965,0 a 0.44743473,0.44743473 0 0 0 0.4668,-0.44727 v -3.69726 a 0.44743473,0.44743473 0 0 0 -0.4473,-0.44727 z m 5.0703,2.21289 a 0.44743473,0.44743473 0 0 0 -0.3281,0.13086 l -2.6153,2.61133 a 0.44743473,0.44743473 0 0 0 0.021,0.65234 c 0.049,0.0433 0.096,0.0877 0.1426,0.13282 0.041,0.0437 0.082,0.0873 0.1191,0.13086 a 0.44743473,0.44743473 0 0 0 0.6563,0.0254 l 2.6152,-2.61524 a 0.44743473,0.44743473 0 0 0 0,-0.63281 l -0.3086,-0.30664 a 0.44743473,0.44743473 0 0 0 -0.3027,-0.12891 z m -1.1914,4.58594 a 0.44743473,0.44743473 0 0 0 -0.4453,0.48828 c 0.011,0.1179 0.011,0.23523 0,0.35352 a 0.44743473,0.44743473 0 0 0 0.4453,0.48828 h 3.7012 a 0.44743473,0.44743473 0 0 0 0.4472,-0.44727 v -0.43359 a 0.44743473,0.44743473 0 0 0 -0.4472,-0.44922 z"
|
||||||
|
id="path864" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.86183474,0,0,0.86183474,180.09301,126.9712)"
|
||||||
|
id="g2397-4"
|
||||||
|
style="fill:#000000;fill-opacity:1">
|
||||||
|
<g
|
||||||
|
id="g1964-4-5"
|
||||||
|
transform="rotate(90,218.24928,1114.2299)"
|
||||||
|
style="fill:#000000;fill-opacity:1">
|
||||||
|
<g
|
||||||
|
id="g1948-2-0"
|
||||||
|
style="fill:#000000;fill-opacity:1">
|
||||||
|
<g
|
||||||
|
id="g1946-2-57"
|
||||||
|
style="fill:#000000;fill-opacity:1">
|
||||||
|
<g
|
||||||
|
id="g1944-4-32"
|
||||||
|
style="fill:#000000;fill-opacity:1" />
|
||||||
|
<g
|
||||||
|
transform="translate(5.4402324e-7,4.0928558e-5)"
|
||||||
|
id="g1944-4-0-2"
|
||||||
|
style="fill:#000000;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.806028"
|
||||||
|
d="M 6.5569914,2.9864148 0.87698077,8.56734 C 0.33613684,9.1006455 0.03031238,9.8273997 0.03031238,10.595361 v 5.189794 c 0,1.181491 0.73189623,2.199492 1.76396712,2.62764 -0.00888,-0.08924 -0.02737,-0.174463 -0.02737,-0.265356 v -5.191514 c 0,-0.955146 0.3829864,-1.864797 1.0569667,-2.529425 L 6.3743238,6.9356205 C 6.8910656,6.0338659 7.5323796,4.9178609 7.538295,4.8989906 7.6098007,4.7710011 7.6521007,4.6281034 7.6521007,4.4716724 7.6521007,4.2583484 7.5805947,4.066391 7.4451576,3.90996 7.4310282,3.8814651 6.5571713,2.9864148 6.5571713,2.9864148 Z m 3.8401146,4.9778558 c -0.02737,0.048433 -0.0459,0.080279 -0.07769,0.1361228 C 10.154314,8.3899921 9.9344832,8.7732978 9.7158851,9.1548918 9.5822184,9.3881797 9.5695934,9.4091143 9.4485527,9.6201187 h 5.6800103 c 0.116884,-0.1710937 0.201821,-0.3659799 0.201821,-0.5892814 0,-0.5901895 -0.476748,-1.0665667 -1.067387,-1.0665667 z"
|
||||||
|
id="path1942-5-3" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 12 KiB |
@ -1,18 +1,18 @@
|
|||||||
/** theming */
|
/** theming */
|
||||||
@import './assets/styles/component-themes.scss';
|
@import '~assets/styles/component-themes.scss';
|
||||||
|
|
||||||
/** fonts */
|
/** fonts */
|
||||||
@import './assets/styles/fonts.scss';
|
@import '~assets/styles/fonts.scss';
|
||||||
@import '~material-icon-font/dist/Material-Icons.css';
|
@import '~material-icon-font/dist/Material-Icons.css';
|
||||||
|
|
||||||
/** Videojs */
|
/** Videojs */
|
||||||
@import '~video.js/dist/video-js.css';
|
@import '~video.js/dist/video-js.css';
|
||||||
|
|
||||||
/** Load projector specific SCSS values */
|
/** Load projector specific SCSS values */
|
||||||
@import './assets/styles/projector.scss';
|
@import '~assets/styles/projector.scss';
|
||||||
|
|
||||||
/** Load global scss variables and device mixing */
|
/** Load global scss variables and device mixing */
|
||||||
@import './assets/styles/variables.scss';
|
@import '~assets/styles/variables.scss';
|
||||||
|
|
||||||
.pbl-ngrid-cell {
|
.pbl-ngrid-cell {
|
||||||
.fill {
|
.fill {
|
||||||
@ -788,3 +788,7 @@ button.mat-menu-item.selected {
|
|||||||
.hide {
|
.hide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide-height {
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
@ -15,7 +15,6 @@ RUN apt-get -y update && apt-get install --no-install-recommends -y \
|
|||||||
libxmlsec1-dev \
|
libxmlsec1-dev \
|
||||||
libxmlsec1-openssl \
|
libxmlsec1-openssl \
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
||||||
RUN rm -rf /var/lib/apt/lists/*
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY requirements /app/requirements
|
COPY requirements /app/requirements
|
||||||
|
@ -169,6 +169,85 @@ def get_config_variables():
|
|||||||
subgroup="Live conference",
|
subgroup="Live conference",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Applause
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_applause_enable",
|
||||||
|
default_value=False,
|
||||||
|
input_type="boolean",
|
||||||
|
label="Enable virtual applause",
|
||||||
|
help_text="Shows a 'Send applause' icon in the live stream bar",
|
||||||
|
weight=170,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_applause_type",
|
||||||
|
default_value="applause-type-bar",
|
||||||
|
input_type="choice",
|
||||||
|
choices=(
|
||||||
|
{
|
||||||
|
"value": "applause-type-bar",
|
||||||
|
"display_name": "Bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "applause-type-particles",
|
||||||
|
"display_name": "Particles",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
label="Applause Type",
|
||||||
|
weight=171,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_applause_show_level",
|
||||||
|
default_value=False,
|
||||||
|
input_type="boolean",
|
||||||
|
label="Show applause amount",
|
||||||
|
weight=172,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_applause_min_amount",
|
||||||
|
default_value=1,
|
||||||
|
input_type="integer",
|
||||||
|
label="Lowest applause amount",
|
||||||
|
help_text="Lowest amount required for OpenSlides to recognize applause",
|
||||||
|
weight=173,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_applause_max_amount",
|
||||||
|
default_value=0,
|
||||||
|
input_type="integer",
|
||||||
|
label="Highest applause amount",
|
||||||
|
help_text="Defines the maximum deflection of the amount. Entering zero will use the amount of present users instead.",
|
||||||
|
weight=174,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_stream_applause_timeout",
|
||||||
|
default_value=5,
|
||||||
|
input_type="integer",
|
||||||
|
label="Applause timeout in seconds",
|
||||||
|
help_text="Determines how long a user has to wait to applaud again. Also determines the time in which applause is collected",
|
||||||
|
weight=175,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="general_system_applause_particle_image",
|
||||||
|
default_value="",
|
||||||
|
label="Applause particle image url",
|
||||||
|
help_text="Shows the given image as applause particle. Recommended image format: 24x24px, PNG, JPG or SVG",
|
||||||
|
weight=176,
|
||||||
|
subgroup="Virtual applause",
|
||||||
|
)
|
||||||
|
|
||||||
# General System
|
# General System
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
|
Loading…
Reference in New Issue
Block a user