diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c8bd0d1..e29e3c6 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -20,6 +20,12 @@ const routes: Routes = [ canActivate: [AngularFireAuthGuard], data: {authGuardPipe: () => redirectUnauthorizedTo(['user', 'login'])} }, + { + path: 'presentation', + loadChildren: () => import('./modules/presentation/presentation.module').then(m => m.PresentationModule), + canActivate: [AngularFireAuthGuard], + data: {authGuardPipe: () => redirectUnauthorizedTo(['user', 'login'])} + }, { path: 'user', loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule) diff --git a/src/app/modules/presentation/monitor/monitor.component.html b/src/app/modules/presentation/monitor/monitor.component.html new file mode 100644 index 0000000..43cc45b --- /dev/null +++ b/src/app/modules/presentation/monitor/monitor.component.html @@ -0,0 +1,5 @@ +
+ + + +
diff --git a/src/app/modules/presentation/monitor/monitor.component.less b/src/app/modules/presentation/monitor/monitor.component.less new file mode 100644 index 0000000..a6eca77 --- /dev/null +++ b/src/app/modules/presentation/monitor/monitor.component.less @@ -0,0 +1,14 @@ +.fullscreen { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: black; + z-index: 1; + + padding: 50px; + + color: white; + font-size: 30px; +} diff --git a/src/app/modules/presentation/monitor/monitor.component.spec.ts b/src/app/modules/presentation/monitor/monitor.component.spec.ts new file mode 100644 index 0000000..9c23c0f --- /dev/null +++ b/src/app/modules/presentation/monitor/monitor.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {MonitorComponent} from './monitor.component'; + +describe('MonitorComponent', () => { + let component: MonitorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MonitorComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MonitorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/presentation/monitor/monitor.component.ts b/src/app/modules/presentation/monitor/monitor.component.ts new file mode 100644 index 0000000..063340c --- /dev/null +++ b/src/app/modules/presentation/monitor/monitor.component.ts @@ -0,0 +1,39 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {map, switchMap, tap} from 'rxjs/operators'; +import {ShowService} from '../../shows/services/show.service'; +import {SongService} from '../../songs/services/song.service'; +import {Section, TextRenderingService} from '../../songs/services/text-rendering.service'; +import {Song} from '../../songs/services/song'; + +@Component({ + selector: 'app-monitor', + templateUrl: './monitor.component.html', + styleUrls: ['./monitor.component.less'] +}) +export class MonitorComponent implements OnInit { + public song: Song; + private index: number; + private sections: Section[]; + + constructor( + private activatedRoute: ActivatedRoute, + private showService: ShowService, + private songService: SongService, + private textRenderingService: TextRenderingService, + ) { + } + + ngOnInit(): void { + this.activatedRoute.params.pipe( + map(_ => _.showId), + switchMap(_ => this.showService.read$(_)), + tap(_ => this.index = _.presentationSection), + switchMap(_ => this.songService.read(_.presentationSongId)) + ).subscribe(_ => { + this.song = _; + this.sections = this.textRenderingService.parse(_.text); + }); + } + +} diff --git a/src/app/modules/presentation/presentation-routing.module.ts b/src/app/modules/presentation/presentation-routing.module.ts new file mode 100644 index 0000000..6dd8627 --- /dev/null +++ b/src/app/modules/presentation/presentation-routing.module.ts @@ -0,0 +1,28 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {RemoteComponent} from './remote/remote.component'; +import {MonitorComponent} from './monitor/monitor.component'; + + +const routes: Routes = [ + { + path: '', + pathMatch: 'full', + redirectTo: 'remote' + }, + { + path: 'remote', + component: RemoteComponent + }, + { + path: 'monitor/:showId', + component: MonitorComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class PresentationRoutingModule { +} diff --git a/src/app/modules/presentation/presentation.module.ts b/src/app/modules/presentation/presentation.module.ts new file mode 100644 index 0000000..0efce3c --- /dev/null +++ b/src/app/modules/presentation/presentation.module.ts @@ -0,0 +1,29 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; + +import {PresentationRoutingModule} from './presentation-routing.module'; +import {MonitorComponent} from './monitor/monitor.component'; +import {RemoteComponent} from './remote/remote.component'; +import {CardModule} from '../../widget-modules/components/card/card.module'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatSelectModule} from '@angular/material/select'; +import {ShowTypeTranslaterModule} from '../../widget-modules/pipes/show-type-translater/show-type-translater.module'; +import {SectionTypeTranslatorModule} from '../../widget-modules/pipes/section-type-translator/section-type-translator.module'; +import {SongTextModule} from '../../widget-modules/components/song-text/song-text.module'; + + +@NgModule({ + declarations: [MonitorComponent, RemoteComponent], + imports: [ + CommonModule, + PresentationRoutingModule, + CardModule, + MatFormFieldModule, + MatSelectModule, + ShowTypeTranslaterModule, + SectionTypeTranslatorModule, + SongTextModule + ] +}) +export class PresentationModule { +} diff --git a/src/app/modules/presentation/remote/remote.component.html b/src/app/modules/presentation/remote/remote.component.html new file mode 100644 index 0000000..f65668d --- /dev/null +++ b/src/app/modules/presentation/remote/remote.component.html @@ -0,0 +1,29 @@ +
+ + + Veranstaltung + + + {{show.showType|showType}}, {{show.date.toDate()|date:'dd.MM.yyyy'}} + + + + +
+
{{song.title}}
+
+
+
{{section.type|sectionType}} {{section.number + 1}}
+
{{getFirstLine(section)}}
+
+
+
+ + Presenter öffnen + +
+ + +
diff --git a/src/app/modules/presentation/remote/remote.component.less b/src/app/modules/presentation/remote/remote.component.less new file mode 100644 index 0000000..8184f62 --- /dev/null +++ b/src/app/modules/presentation/remote/remote.component.less @@ -0,0 +1,67 @@ +@import "../../../../styles/shadow"; + +.song { + background: #fffa; + width: 100%; + padding: 10px; + border-radius: 8px; + margin-bottom: 10px; + box-sizing: border-box; +} + +.title { + font-weight: bold; + padding: 0 10px 10px 10px; + +} + +.song-parts { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + @media screen and (max-width: 860px) { + grid-template-columns: 1fr 1fr 1fr 1fr; + } + @media screen and (max-width: 660px) { + grid-template-columns: 1fr 1fr 1fr; + } + @media screen and (max-width: 460px) { + grid-template-columns: 1fr 1fr; + } + grid-gap: 10px; +} + +.song-part { + background: #fff; + border-radius: 8px; + overflow: hidden; + transition: 300ms all ease-in-out; + cursor: pointer; + .card-1; + + &:hover { + .card-2; + } + + &.active { + .card-5; + + .head { + background: #4286f4; + color: white; + border-bottom: 0.5px solid #4286f4; + } + } +} + +.head { + transition: 300ms all ease-in-out; + background: #f4f4f4; + border-bottom: 0.5px solid #ddd; + padding: 10px; + font-weight: bold; + +} + +.fragment { + padding: 10px; +} diff --git a/src/app/modules/presentation/remote/remote.component.spec.ts b/src/app/modules/presentation/remote/remote.component.spec.ts new file mode 100644 index 0000000..0ccbf3f --- /dev/null +++ b/src/app/modules/presentation/remote/remote.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {RemoteComponent} from './remote.component'; + +describe('RemoteComponent', () => { + let component: RemoteComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [RemoteComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RemoteComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/presentation/remote/remote.component.ts b/src/app/modules/presentation/remote/remote.component.ts new file mode 100644 index 0000000..091df09 --- /dev/null +++ b/src/app/modules/presentation/remote/remote.component.ts @@ -0,0 +1,63 @@ +import {Component} from '@angular/core'; +import {ShowDataService} from '../../shows/services/show-data.service'; +import {Observable} from 'rxjs'; +import {Show} from '../../shows/services/show'; +import {MatSelectChange} from '@angular/material/select'; +import {ShowSongService} from '../../shows/services/show-song.service'; +import {SongService} from '../../songs/services/song.service'; +import {Song} from '../../songs/services/song'; +import {Section, TextRenderingService} from '../../songs/services/text-rendering.service'; + +export interface PresentationSong { + id: string; + title: string; + sections: Section[]; +} + +@Component({ + selector: 'app-remote', + templateUrl: './remote.component.html', + styleUrls: ['./remote.component.less'] +}) +export class RemoteComponent { + public shows$: Observable; + public show$: Observable; + public songs: Song[]; + public presentationSongs: PresentationSong[]; + public currentShowId: string; + + constructor( + private showDataService: ShowDataService, + private showSongService: ShowSongService, + private songService: SongService, + private textRenderingService: TextRenderingService + ) { + this.shows$ = showDataService.list$(); + songService.list$().subscribe(_ => this.songs = _); + } + + public onShowChanged(change: MatSelectChange): void { + this.currentShowId = change.value; + this.show$ = this.showDataService.read$(change.value); + this.showSongService.list$(change.value).subscribe(_ => { + this.presentationSongs = _ + .map(song => this.songs.filter(f => f.id == song.songId)[0]) + .map(song => ({ + id: song.id, + title: song.title, + sections: this.textRenderingService.parse(song.text) + })) + }); + } + + public getFirstLine(section: Section): string { + return section.lines.filter(_ => _.type === 1)[0].text; + } + + public async onSectionClick(id: string, index: number): Promise { + await this.showDataService.update(this.currentShowId, { + presentationSongId: id, + presentationSection: index + }) + } +} diff --git a/src/app/modules/presentation/services/presentation.service.spec.ts b/src/app/modules/presentation/services/presentation.service.spec.ts new file mode 100644 index 0000000..3f91b19 --- /dev/null +++ b/src/app/modules/presentation/services/presentation.service.spec.ts @@ -0,0 +1,16 @@ +import {TestBed} from '@angular/core/testing'; + +import {PresentationService} from './presentation.service'; + +describe('PresentationService', () => { + let service: PresentationService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PresentationService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/modules/presentation/services/presentation.service.ts b/src/app/modules/presentation/services/presentation.service.ts new file mode 100644 index 0000000..a650b4e --- /dev/null +++ b/src/app/modules/presentation/services/presentation.service.ts @@ -0,0 +1,10 @@ +import {Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class PresentationService { + + constructor() { + } +} diff --git a/src/app/modules/shows/services/show.ts b/src/app/modules/shows/services/show.ts index 95c50fe..a7636d0 100644 --- a/src/app/modules/shows/services/show.ts +++ b/src/app/modules/shows/services/show.ts @@ -1,7 +1,6 @@ import * as firebase from 'firebase'; import Timestamp = firebase.firestore.Timestamp; - export interface Show { id: string; showType: string; @@ -9,5 +8,9 @@ export interface Show { owner: string; public: boolean; reported: boolean; + + presentationSongId: string; + presentationSection: number; + } diff --git a/src/app/widget-modules/components/application-frame/navigation/navigation.component.html b/src/app/widget-modules/components/application-frame/navigation/navigation.component.html index 4371a6e..f73adc7 100644 --- a/src/app/widget-modules/components/application-frame/navigation/navigation.component.html +++ b/src/app/widget-modules/components/application-frame/navigation/navigation.component.html @@ -2,6 +2,7 @@
diff --git a/src/app/widget-modules/components/application-frame/navigation/navigation.component.ts b/src/app/widget-modules/components/application-frame/navigation/navigation.component.ts index af245bc..70b8ac7 100644 --- a/src/app/widget-modules/components/application-frame/navigation/navigation.component.ts +++ b/src/app/widget-modules/components/application-frame/navigation/navigation.component.ts @@ -4,6 +4,7 @@ import {faPersonBooth} from '@fortawesome/free-solid-svg-icons/faPersonBooth'; import {faUserCog} from '@fortawesome/free-solid-svg-icons/faUserCog'; import {fromEvent} from 'rxjs'; import {distinctUntilChanged, map, shareReplay, startWith} from 'rxjs/operators'; +import {faChalkboard} from '@fortawesome/free-solid-svg-icons/faChalkboard'; @Component({ selector: 'app-navigation', @@ -15,6 +16,7 @@ export class NavigationComponent { public faSongs = faMusic; public faShows = faPersonBooth; public faUser = faUserCog; + public faPresentation = faChalkboard; public readonly windowScroll$ = fromEvent(window, 'scroll').pipe(map(x => window.scrollY), startWith(0), distinctUntilChanged(), shareReplay(1)); diff --git a/src/app/widget-modules/components/card/card.component.less b/src/app/widget-modules/components/card/card.component.less index 7d37287..a04459a 100644 --- a/src/app/widget-modules/components/card/card.component.less +++ b/src/app/widget-modules/components/card/card.component.less @@ -4,7 +4,7 @@ .card-3; margin: 20px; border-radius: 8px; - background: #fffe; + background: #fffb; overflow: hidden; width: 800px; transition: 300ms all ease-in-out; diff --git a/src/app/widget-modules/components/song-text/song-text.component.html b/src/app/widget-modules/components/song-text/song-text.component.html index 377e8f0..d3bf0a1 100644 --- a/src/app/widget-modules/components/song-text/song-text.component.html +++ b/src/app/widget-modules/components/song-text/song-text.component.html @@ -1,6 +1,6 @@
-