update tslint -> eslint
This commit is contained in:
59
.eslintrc.json
Normal file
59
.eslintrc.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"projects/**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"tsconfig.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates",
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-member-accessibility": "error",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "app",
|
||||
"style": "kebab-case",
|
||||
"type": "element"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/unbound-method": [
|
||||
"off"
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "app",
|
||||
"style": "camelCase",
|
||||
"type": "attribute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
26
.prettierrc
Normal file
26
.prettierrc
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "as-needed",
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid",
|
||||
"jsxBracketSameLine": false,
|
||||
"printWidth": 200,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.component.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "html"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
15
angular.json
15
angular.json
@@ -116,15 +116,11 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -145,6 +141,7 @@
|
||||
},
|
||||
"defaultProject": "wgenerator",
|
||||
"cli": {
|
||||
"analytics": "4047dcd7-89f4-402f-958e-e365a5505c55"
|
||||
"analytics": "4047dcd7-89f4-402f-958e-e365a5505c55",
|
||||
"defaultCollection": "@angular-eslint/schematics"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
import {AppPage} from './app.po';
|
||||
import {browser, logging} from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
@@ -9,15 +9,17 @@ describe('workspace-project App', () => {
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('wgenerator app is running!');
|
||||
void page.navigateTo();
|
||||
void expect(page.getTitleText()).toEqual('wgenerator app is running!');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
void expect(logs).not.toContain(
|
||||
jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
} as logging.Entry)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
1143
package-lock.json
generated
1143
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -4,7 +4,7 @@
|
||||
"scripts": {
|
||||
"start": "ng serve --aot",
|
||||
"build": "ng build",
|
||||
"deploy": "ng build --prod && firebase deploy",
|
||||
"deploy": "npm i && ng build --prod && firebase deploy",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint --fix",
|
||||
"postinstall": "ngcc"
|
||||
@@ -39,13 +39,22 @@
|
||||
"devDependencies": {
|
||||
"@angular-devkit/architect": "0.1200.1",
|
||||
"@angular-devkit/build-angular": "~12.0.1",
|
||||
"@angular-eslint/builder": "12.0.0",
|
||||
"@angular-eslint/eslint-plugin": "12.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "12.0.0",
|
||||
"@angular-eslint/schematics": "12.0.0",
|
||||
"@angular-eslint/template-parser": "12.0.0",
|
||||
"@angular/cli": "~12.0.1",
|
||||
"@angular/compiler-cli": "~12.0.1",
|
||||
"@angular/language-service": "~12.0.1",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.12.36",
|
||||
"codelyzer": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.23.0",
|
||||
"@typescript-eslint/parser": "4.23.0",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"firebase-tools": "^7.12.0",
|
||||
"fuzzy": "^0.1.3",
|
||||
"inquirer": "^6.2.2",
|
||||
@@ -57,10 +66,10 @@
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"prettier": "^2.3.0",
|
||||
"protractor": "~7.0.0",
|
||||
"swiper": "^6.6.2",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,20 @@ import {animate, query, state, style, transition, trigger} from '@angular/animat
|
||||
export const fade = [
|
||||
// the fade-in/fade-out animation.
|
||||
trigger('fade', [
|
||||
|
||||
// the "in" style determines the "resting" state of the element when it is visible.
|
||||
state('in', style({opacity: 1, transform: 'translateY(0px)'})),
|
||||
|
||||
// fade in when created. this could also be written as transition('void => *')
|
||||
transition(':enter', [
|
||||
style({opacity: 0, transform: 'translateY(-10px)'}),
|
||||
animate(200)
|
||||
transition(':enter', [style({opacity: 0, transform: 'translateY(-10px)'}), animate(200)]),
|
||||
]),
|
||||
])
|
||||
];
|
||||
|
||||
export const fader =
|
||||
trigger('fader', [
|
||||
export const fader = trigger('fader', [
|
||||
transition('* <=> *', [
|
||||
// Set a default style for enter and leave
|
||||
query(':enter, :leave', [
|
||||
query(
|
||||
':enter, :leave',
|
||||
[
|
||||
style({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
@@ -27,10 +24,22 @@ export const fader =
|
||||
opacity: 0,
|
||||
transform: 'scale(1) translateY(-10px)',
|
||||
}),
|
||||
], {optional: true}),
|
||||
],
|
||||
{optional: true}
|
||||
),
|
||||
// Animate the new page in
|
||||
query(':enter', [
|
||||
animate('200ms ease', style({opacity: 1, transform: 'scale(1) translateY(0)'})),
|
||||
], {optional: true}),
|
||||
query(
|
||||
':enter',
|
||||
[
|
||||
animate(
|
||||
'200ms ease',
|
||||
style({
|
||||
opacity: 1,
|
||||
transform: 'scale(1) translateY(0)',
|
||||
})
|
||||
),
|
||||
],
|
||||
{optional: true}
|
||||
),
|
||||
]),
|
||||
]);
|
||||
]);
|
||||
|
||||
@@ -7,7 +7,7 @@ const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'songs',
|
||||
pathMatch: 'full'
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'songs',
|
||||
@@ -15,8 +15,8 @@ const routes: Routes = [
|
||||
canActivate: [AngularFireAuthGuard, RoleGuard],
|
||||
data: {
|
||||
authGuardPipe: () => redirectUnauthorizedTo(['user', 'login']),
|
||||
requiredRoles: ['user']
|
||||
}
|
||||
requiredRoles: ['user'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'shows',
|
||||
@@ -24,8 +24,8 @@ const routes: Routes = [
|
||||
canActivate: [AngularFireAuthGuard, RoleGuard],
|
||||
data: {
|
||||
authGuardPipe: () => redirectUnauthorizedTo(['user', 'login']),
|
||||
requiredRoles: ['leader']
|
||||
}
|
||||
requiredRoles: ['leader'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'presentation',
|
||||
@@ -33,12 +33,12 @@ const routes: Routes = [
|
||||
canActivate: [AngularFireAuthGuard, RoleGuard],
|
||||
data: {
|
||||
authGuardPipe: () => redirectUnauthorizedTo(['user', 'login']),
|
||||
requiredRoles: ['presenter']
|
||||
}
|
||||
requiredRoles: ['presenter'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule)
|
||||
loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule),
|
||||
},
|
||||
{
|
||||
path: 'brand',
|
||||
@@ -47,12 +47,16 @@ const routes: Routes = [
|
||||
{
|
||||
path: 'guest',
|
||||
loadChildren: () => import('./modules/guest/guest.module').then(m => m.GuestModule),
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules, relativeLinkResolution: 'legacy' })],
|
||||
exports: [RouterModule]
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
preloadingStrategy: PreloadAllModules,
|
||||
relativeLinkResolution: 'legacy',
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule {
|
||||
}
|
||||
export class AppRoutingModule {}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<app-navigation></app-navigation>
|
||||
<perfect-scrollbar #scrollbar (psScrollY)="onScoll($event)" [perfectScrollbar]
|
||||
class="scroll" style="max-height: calc(100vh); width: 100%; overflow: hidden;"
|
||||
<perfect-scrollbar
|
||||
#scrollbar
|
||||
(psScrollY)="onScoll($event)"
|
||||
[perfectScrollbar]
|
||||
class="scroll"
|
||||
style="max-height: calc(100vh); width: 100%; overflow: hidden"
|
||||
>
|
||||
<div [@fader]="o.isActivated ? o.activatedRoute : ''" class="content">
|
||||
<router-outlet #o="outlet"></router-outlet>
|
||||
|
||||
@@ -3,20 +3,17 @@ import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [AppComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
void expect(fixture.debugElement.componentInstance).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,30 +7,24 @@ import {PerfectScrollbarComponent} from 'ngx-perfect-scrollbar';
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.less'],
|
||||
animations: [fader]
|
||||
animations: [fader],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
@ViewChild('scrollbar', {static: false}) public scrollbar: PerfectScrollbarComponent;
|
||||
|
||||
@ViewChild('scrollbar', {static: false}) scrollbar: PerfectScrollbarComponent;
|
||||
|
||||
constructor(private scrollService: ScrollService) {
|
||||
public constructor(private scrollService: ScrollService) {
|
||||
scrollService.restoreScrollPosition$.subscribe(pos => {
|
||||
if (this.scrollbar && pos) {
|
||||
// this.scrollbar.scrollTo(pos, 0);
|
||||
this.scrollbar.directiveRef.scrollTo(0, pos, 300);
|
||||
// debugger;
|
||||
}
|
||||
if (this.scrollbar && pos) this.scrollbar.directiveRef.scrollTo(0, pos, 300);
|
||||
});
|
||||
}
|
||||
|
||||
public static hideLoader = () => document.querySelector('#load-bg').classList.add('hidden');
|
||||
public static hideLoader: () => void = () => document.querySelector('#load-bg').classList.add('hidden');
|
||||
|
||||
public ngOnInit(): void {
|
||||
setTimeout(() => AppComponent.hideLoader(), 800);
|
||||
|
||||
}
|
||||
|
||||
onScoll($event: { srcElement: { scrollTop: number } }) {
|
||||
public onScoll($event: {srcElement: {scrollTop: number}}): void {
|
||||
this.scrollService.saveScrollPosition($event.srcElement.scrollTop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
import {PerfectScrollbarModule} from 'ngx-perfect-scrollbar';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
AppRoutingModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}),
|
||||
ServiceWorkerModule.register('ngsw-worker.js', {
|
||||
enabled: environment.production,
|
||||
}),
|
||||
BrowserAnimationsModule,
|
||||
|
||||
ApplicationFrameModule,
|
||||
@@ -38,13 +38,8 @@ import {PerfectScrollbarModule} from 'ngx-perfect-scrollbar';
|
||||
AngularFireAuthGuardModule,
|
||||
FontAwesomeModule,
|
||||
PerfectScrollbarModule,
|
||||
|
||||
],
|
||||
providers: [
|
||||
{provide: MAT_DATE_LOCALE, useValue: 'de-DE'},
|
||||
],
|
||||
providers: [{provide: MAT_DATE_LOCALE, useValue: 'de-DE'}],
|
||||
bootstrap: [AppComponent],
|
||||
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
export class AppModule {}
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('BrandComponent', () => {
|
||||
let component: BrandComponent;
|
||||
let fixture: ComponentFixture<BrandComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [BrandComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [BrandComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BrandComponent);
|
||||
@@ -20,6 +21,6 @@ describe('BrandComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-brand',
|
||||
templateUrl: './brand.component.html',
|
||||
styleUrls: ['./brand.component.less']
|
||||
styleUrls: ['./brand.component.less'],
|
||||
})
|
||||
export class BrandComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
export class BrandComponent {}
|
||||
|
||||
@@ -5,26 +5,20 @@ import {RouterModule, Routes} from '@angular/router';
|
||||
import {LogoModule} from '../../widget-modules/components/logo/logo.module';
|
||||
import {NewUserComponent} from './new-user/new-user.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
component: BrandComponent
|
||||
component: BrandComponent,
|
||||
},
|
||||
{
|
||||
path: 'new-user',
|
||||
component: NewUserComponent
|
||||
component: NewUserComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [BrandComponent, NewUserComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild(routes),
|
||||
LogoModule
|
||||
]
|
||||
imports: [CommonModule, RouterModule.forChild(routes), LogoModule],
|
||||
})
|
||||
export class BrandModule {
|
||||
}
|
||||
export class BrandModule {}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<div class="frame">
|
||||
<app-brand class="brand"></app-brand>
|
||||
<div *ngIf="user$|async as user" class="text">
|
||||
<div *ngIf="user$ | async as user" class="text">
|
||||
<div class="welcome">WILLKOMMEN</div>
|
||||
<div class="name">{{user.name}}</div>
|
||||
<div class="roles">Es wurden noch keine Berechtigungen zugeteilt, bitte wende Dich an den Administrator!</div>
|
||||
<div class="name">{{ user.name }}</div>
|
||||
<div class="roles">
|
||||
Es wurden noch keine Berechtigungen zugeteilt, bitte wende Dich an den
|
||||
Administrator!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('NewUserComponent', () => {
|
||||
let component: NewUserComponent;
|
||||
let fixture: ComponentFixture<NewUserComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [NewUserComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [NewUserComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NewUserComponent);
|
||||
@@ -20,6 +21,6 @@ describe('NewUserComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,12 +6,12 @@ import {User} from '../../../services/user/user';
|
||||
@Component({
|
||||
selector: 'app-new-user',
|
||||
templateUrl: './new-user.component.html',
|
||||
styleUrls: ['./new-user.component.less']
|
||||
styleUrls: ['./new-user.component.less'],
|
||||
})
|
||||
export class NewUserComponent {
|
||||
public user$: Observable<User>;
|
||||
|
||||
constructor(private userService: UserService) {
|
||||
public constructor(private userService: UserService) {
|
||||
this.user$ = userService.user$;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<div *ngIf="songs$|async as songs" class="view">
|
||||
<div *ngIf="songs$ | async as songs" class="view">
|
||||
<swiper>
|
||||
<div *ngFor="let song of songs" class="song">
|
||||
<app-song-text [showSwitch]="false" [text]="song|async"
|
||||
chordMode="hide"></app-song-text>
|
||||
<app-song-text
|
||||
[showSwitch]="false"
|
||||
[text]="song | async"
|
||||
chordMode="hide"
|
||||
></app-song-text>
|
||||
</div>
|
||||
</swiper>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('GuestComponent', () => {
|
||||
let component: GuestComponent;
|
||||
let fixture: ComponentFixture<GuestComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [GuestComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [GuestComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GuestComponent);
|
||||
@@ -20,6 +21,6 @@ describe('GuestComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,28 +8,18 @@ import {ShowSongService} from '../shows/services/show-song.service';
|
||||
@Component({
|
||||
selector: 'app-guest',
|
||||
templateUrl: './guest.component.html',
|
||||
styleUrls: ['./guest.component.less']
|
||||
styleUrls: ['./guest.component.less'],
|
||||
})
|
||||
export class GuestComponent implements OnInit {
|
||||
public songs$: Observable<Observable<string>[]>;
|
||||
|
||||
|
||||
constructor(
|
||||
private songService: SongService,
|
||||
private globalSettingsService: GlobalSettingsService,
|
||||
private showSongService: ShowSongService,
|
||||
) {
|
||||
}
|
||||
public constructor(private songService: SongService, private globalSettingsService: GlobalSettingsService, private showSongService: ShowSongService) {}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.songs$ = this.globalSettingsService.get$.pipe(
|
||||
map(_ => _.currentShow),
|
||||
switchMap(_ => this.showSongService.list$(_)),
|
||||
map(_ => _
|
||||
.sort((x, y) => x.order - y.order)
|
||||
.map(showSong => this.songService.read$(showSong.songId).pipe(map(song => song.text)))
|
||||
)
|
||||
map(_ => _.sort((x, y) => x.order - y.order).map(showSong => this.songService.read$(showSong.songId).pipe(map(song => song.text))))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,23 +13,16 @@ const DEFAULT_SWIPER_CONFIG: SwiperConfigInterface = {
|
||||
scrollbar: false,
|
||||
navigation: true,
|
||||
pagination: false,
|
||||
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
declarations: [GuestComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([{path: '', component: GuestComponent}]),
|
||||
SwiperModule,
|
||||
SongTextModule
|
||||
],
|
||||
imports: [CommonModule, RouterModule.forChild([{path: '', component: GuestComponent}]), SwiperModule, SongTextModule],
|
||||
providers: [
|
||||
{
|
||||
provide: SWIPER_CONFIG,
|
||||
useValue: DEFAULT_SWIPER_CONFIG
|
||||
}
|
||||
]
|
||||
useValue: DEFAULT_SWIPER_CONFIG,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class GuestModule {
|
||||
}
|
||||
export class GuestModule {}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<p *ngIf="song.artist">{{song.artist}}</p>
|
||||
<p *ngIf="song.label">{{song.label}}</p>
|
||||
<p *ngIf="song.termsOfUse" class="terms-of-use">{{song.termsOfUse}}</p>
|
||||
<p *ngIf="song.origin">{{song.origin}}</p>
|
||||
<p *ngIf="song.artist">{{ song.artist }}</p>
|
||||
<p *ngIf="song.label">{{ song.label }}</p>
|
||||
<p *ngIf="song.termsOfUse" class="terms-of-use">{{ song.termsOfUse }}</p>
|
||||
<p *ngIf="song.origin">{{ song.origin }}</p>
|
||||
|
||||
<div *ngIf="song.legalOwnerId">
|
||||
<p *ngIf="song.legalOwner==='CCLI' && config">CCLI-Liednummer {{song.legalOwnerId}},
|
||||
CCLI-Lizenznummer {{config.ccliLicenseId}}</p>
|
||||
<p *ngIf="song.legalOwner!=='CCLI'">Liednummer {{song.legalOwnerId}}</p>
|
||||
<p *ngIf="song.legalOwner === 'CCLI' && config">
|
||||
CCLI-Liednummer {{ song.legalOwnerId }}, CCLI-Lizenznummer
|
||||
{{ config.ccliLicenseId }}
|
||||
</p>
|
||||
<p *ngIf="song.legalOwner !== 'CCLI'">Liednummer {{ song.legalOwnerId }}</p>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('LegalComponent', () => {
|
||||
let component: LegalComponent;
|
||||
let fixture: ComponentFixture<LegalComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LegalComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [LegalComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LegalComponent);
|
||||
@@ -20,6 +21,6 @@ describe('LegalComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import {Config} from '../../../../services/config';
|
||||
@Component({
|
||||
selector: 'app-legal',
|
||||
templateUrl: './legal.component.html',
|
||||
styleUrls: ['./legal.component.less']
|
||||
styleUrls: ['./legal.component.less'],
|
||||
})
|
||||
export class LegalComponent {
|
||||
@Input() public song: Song;
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -6,12 +6,13 @@ describe('LogoComponent', () => {
|
||||
let component: LogoComponent;
|
||||
let fixture: ComponentFixture<LogoComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LogoComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [LogoComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LogoComponent);
|
||||
@@ -20,6 +21,6 @@ describe('LogoComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-logo',
|
||||
templateUrl: './logo.component.html',
|
||||
styleUrls: ['./logo.component.less']
|
||||
styleUrls: ['./logo.component.less'],
|
||||
})
|
||||
export class LogoComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
export class LogoComponent {}
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
<div *ngIf="song" [style.font-size.px]="zoom" class="fullscreen background">
|
||||
|
||||
|
||||
<div @songSwitch [class.blur]="songId==='title'" [class.hide]="songId!=='title' && songId!=='empty'"
|
||||
class="start fullscreen logo">
|
||||
<div
|
||||
@songSwitch
|
||||
[class.blur]="songId === 'title'"
|
||||
[class.hide]="songId !== 'title' && songId !== 'empty'"
|
||||
class="start fullscreen logo"
|
||||
>
|
||||
<app-logo></app-logo>
|
||||
</div>
|
||||
|
||||
<div *ngIf="songId==='title'" @songSwitch class="start fullscreen">
|
||||
<div>{{showType|showType}}</div>
|
||||
<div class="date">{{date|date:'dd.MM.yyyy'}}</div>
|
||||
<div *ngIf="songId === 'title'" @songSwitch class="start fullscreen">
|
||||
<div>{{ showType | showType }}</div>
|
||||
<div class="date">{{ date | date: "dd.MM.yyyy" }}</div>
|
||||
</div>
|
||||
|
||||
<app-song-text *ngIf="songId!=='title' && songId!=='empty'" @songSwitch [fullscreen]="true" [index]="index"
|
||||
[showSwitch]="false" [text]="song.text"
|
||||
chordMode="hide"></app-song-text>
|
||||
<app-legal *ngIf="songId!=='title' && songId!=='empty'" @songSwitch [config]="config$|async"
|
||||
[song]="song"></app-legal>
|
||||
|
||||
<app-song-text
|
||||
*ngIf="songId !== 'title' && songId !== 'empty'"
|
||||
@songSwitch
|
||||
[fullscreen]="true"
|
||||
[index]="index"
|
||||
[showSwitch]="false"
|
||||
[text]="song.text"
|
||||
chordMode="hide"
|
||||
></app-song-text>
|
||||
<app-legal
|
||||
*ngIf="songId !== 'title' && songId !== 'empty'"
|
||||
@songSwitch
|
||||
[config]="config$ | async"
|
||||
[song]="song"
|
||||
></app-legal>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('MonitorComponent', () => {
|
||||
let component: MonitorComponent;
|
||||
let fixture: ComponentFixture<MonitorComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MonitorComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [MonitorComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MonitorComponent);
|
||||
@@ -20,6 +21,6 @@ describe('MonitorComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,14 +8,14 @@ import {Config} from '../../../services/config';
|
||||
import {Observable} from 'rxjs';
|
||||
import {ConfigService} from '../../../services/config.service';
|
||||
import {songSwitch} from '../../../widget-modules/components/song-text/animation';
|
||||
import {Section} from '../../songs/services/section';
|
||||
import {TextRenderingService} from '../../songs/services/text-rendering.service';
|
||||
import {Show} from '../../shows/services/show';
|
||||
|
||||
@Component({
|
||||
selector: 'app-monitor',
|
||||
templateUrl: './monitor.component.html',
|
||||
styleUrls: ['./monitor.component.less'],
|
||||
animations: [songSwitch]
|
||||
animations: [songSwitch],
|
||||
})
|
||||
export class MonitorComponent implements OnInit {
|
||||
public song: Song;
|
||||
@@ -25,35 +25,37 @@ export class MonitorComponent implements OnInit {
|
||||
public index: number;
|
||||
public showType: string;
|
||||
public date: Date;
|
||||
private sections: Section[];
|
||||
public config$: Observable<Config>;
|
||||
|
||||
constructor(
|
||||
// private sections: Section[];
|
||||
|
||||
public constructor(
|
||||
private showService: ShowService,
|
||||
private songService: SongService,
|
||||
private textRenderingService: TextRenderingService,
|
||||
private globalSettingsService: GlobalSettingsService,
|
||||
private configService: ConfigService,
|
||||
private configService: ConfigService
|
||||
) {
|
||||
this.config$ = configService.get$;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.globalSettingsService.get$.pipe(
|
||||
public ngOnInit(): void {
|
||||
this.globalSettingsService.get$
|
||||
.pipe(
|
||||
map(_ => _.currentShow),
|
||||
distinctUntilChanged(),
|
||||
tap(_ => this.currentShowId = _),
|
||||
tap(_ => (this.currentShowId = _)),
|
||||
switchMap(_ => this.showService.read$(_)),
|
||||
tap(_ => this.showType = _.showType),
|
||||
tap(_ => this.date = _.date.toDate()),
|
||||
tap(_ => this.songId = _.presentationSongId),
|
||||
tap(_ => this.index = _.presentationSection),
|
||||
tap(_ => this.zoom = _.presentationZoom ?? 30),
|
||||
switchMap(_ => this.songService.read$(_.presentationSongId))
|
||||
).subscribe((_: Song) => {
|
||||
tap(_ => (this.showType = _.showType)),
|
||||
tap(_ => (this.date = _.date.toDate())),
|
||||
tap(_ => (this.songId = _.presentationSongId)),
|
||||
tap(_ => (this.index = _.presentationSection)),
|
||||
tap(_ => (this.zoom = _.presentationZoom ?? 30)),
|
||||
switchMap((_: Show) => this.songService.read$(_.presentationSongId))
|
||||
)
|
||||
.subscribe((_: Song) => {
|
||||
this.song = _;
|
||||
this.sections = this.textRenderingService.parse(_.text, null);
|
||||
// this.sections = this.textRenderingService.parse(_.text, null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,26 +3,24 @@ 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'
|
||||
redirectTo: 'remote',
|
||||
},
|
||||
{
|
||||
path: 'remote',
|
||||
component: RemoteComponent
|
||||
component: RemoteComponent,
|
||||
},
|
||||
{
|
||||
path: 'monitor',
|
||||
component: MonitorComponent
|
||||
}
|
||||
component: MonitorComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PresentationRoutingModule {
|
||||
}
|
||||
export class PresentationRoutingModule {}
|
||||
|
||||
@@ -18,12 +18,9 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {AddSongModule} from '../../widget-modules/components/add-song/add-song.module';
|
||||
import {LogoComponent} from './monitor/logo/logo.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [MonitorComponent, RemoteComponent, LegalComponent, LogoComponent],
|
||||
exports: [
|
||||
RemoteComponent
|
||||
],
|
||||
exports: [RemoteComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PresentationRoutingModule,
|
||||
@@ -38,8 +35,7 @@ import {LogoComponent} from './monitor/logo/logo.component';
|
||||
MatSliderModule,
|
||||
FormsModule,
|
||||
AddSongModule,
|
||||
ReactiveFormsModule
|
||||
]
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
})
|
||||
export class PresentationModule {
|
||||
}
|
||||
export class PresentationModule {}
|
||||
|
||||
@@ -1,41 +1,60 @@
|
||||
<div *ngIf="shows$|async as shows">
|
||||
<div *ngIf="shows$ | async as shows">
|
||||
<app-card>
|
||||
<p *ngIf="!shows.length" @fade>
|
||||
Es ist derzeit keine Veranstaltung vorhanden
|
||||
</p>
|
||||
|
||||
<p *ngIf="!shows.length" @fade>Es ist derzeit keine Veranstaltung vorhanden</p>
|
||||
|
||||
<mat-form-field *ngIf="shows.length>0" @fade appearance="outline">
|
||||
<mat-form-field *ngIf="shows.length > 0" @fade appearance="outline">
|
||||
<mat-label>Veranstaltung</mat-label>
|
||||
<mat-select [formControl]="showControl">
|
||||
<mat-option *ngFor="let show of shows" [value]="show.id">
|
||||
{{show.showType|showType}}, {{show.date.toDate()|date:'dd.MM.yyyy'}}
|
||||
{{ show.showType | showType }},
|
||||
{{ show.date.toDate() | date: "dd.MM.yyyy" }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<ng-container *ngIf="!progress">
|
||||
<div *ngIf="show" class="song-parts padding-bottom">
|
||||
<div (click)="onSectionClick('title', -1)"
|
||||
[class.active]="show.presentationSongId==='title'"
|
||||
class="song-part">
|
||||
<div
|
||||
(click)="onSectionClick('title', -1)"
|
||||
[class.active]="show.presentationSongId === 'title'"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">Veranstaltung</div>
|
||||
</div>
|
||||
<div (click)="onSectionClick('empty', -1)"
|
||||
[class.active]="show.presentationSongId==='empty'"
|
||||
class="song-part">
|
||||
<div
|
||||
(click)="onSectionClick('empty', -1)"
|
||||
[class.active]="show.presentationSongId === 'empty'"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">Leer</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let song of presentationSongs" @fade class="song">
|
||||
<div [class.active]="show.presentationSongId===song.id" class="title song-part">
|
||||
<div (click)="onSectionClick(song.id, -1)" class="head">{{song.title}}</div>
|
||||
<div
|
||||
[class.active]="show.presentationSongId === song.id"
|
||||
class="title song-part"
|
||||
>
|
||||
<div (click)="onSectionClick(song.id, -1)" class="head">
|
||||
{{ song.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="show" class="song-parts">
|
||||
<div (click)="onSectionClick(song.id, i)" *ngFor="let section of song.sections; index as i"
|
||||
[class.active]="show.presentationSongId===song.id && show.presentationSection===i"
|
||||
class="song-part">
|
||||
<div class="head">{{section.type|sectionType}} {{section.number + 1}}</div>
|
||||
<div class="fragment">{{getFirstLine(section)}}</div>
|
||||
<div
|
||||
(click)="onSectionClick(song.id, i)"
|
||||
*ngFor="let section of song.sections; index as i"
|
||||
[class.active]="
|
||||
show.presentationSongId === song.id &&
|
||||
show.presentationSection === i
|
||||
"
|
||||
class="song-part"
|
||||
>
|
||||
<div class="head">
|
||||
{{ section.type | sectionType }} {{ section.number + 1 }}
|
||||
</div>
|
||||
<div class="fragment">{{ getFirstLine(section) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,11 +75,13 @@
|
||||
</mat-slider>
|
||||
</div>
|
||||
|
||||
|
||||
<app-add-song *ngIf="show" [addedLive]="true" [showId]="currentShowId" [showSongs]="showSongs"
|
||||
[songs]="songs"></app-add-song>
|
||||
<app-add-song
|
||||
*ngIf="show"
|
||||
[addedLive]="true"
|
||||
[showId]="currentShowId"
|
||||
[showSongs]="showSongs"
|
||||
[songs]="songs"
|
||||
></app-add-song>
|
||||
</ng-container>
|
||||
</app-card>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('RemoteComponent', () => {
|
||||
let component: RemoteComponent;
|
||||
let fixture: ComponentFixture<RemoteComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RemoteComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [RemoteComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RemoteComponent);
|
||||
@@ -20,6 +21,6 @@ describe('RemoteComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface PresentationSong {
|
||||
selector: 'app-remote',
|
||||
templateUrl: './remote.component.html',
|
||||
styleUrls: ['./remote.component.less'],
|
||||
animations: [fade]
|
||||
animations: [fade],
|
||||
})
|
||||
export class RemoteComponent {
|
||||
public shows$: Observable<Show[]>;
|
||||
@@ -39,24 +39,26 @@ export class RemoteComponent {
|
||||
public faDesktop = faDesktop;
|
||||
public showControl = new FormControl();
|
||||
|
||||
constructor(
|
||||
public constructor(
|
||||
private showService: ShowService,
|
||||
private showSongService: ShowSongService,
|
||||
private songService: SongService,
|
||||
private textRenderingService: TextRenderingService,
|
||||
private globalSettingsService: GlobalSettingsService,
|
||||
private globalSettingsService: GlobalSettingsService
|
||||
) {
|
||||
this.shows$ = showService.list$(true);
|
||||
songService.list$().subscribe(_ => this.songs = _);
|
||||
songService.list$().subscribe(_ => (this.songs = _));
|
||||
|
||||
globalSettingsService.get$.pipe(
|
||||
globalSettingsService.get$
|
||||
.pipe(
|
||||
map(_ => _.currentShow),
|
||||
distinctUntilChanged()
|
||||
).subscribe(_ => {
|
||||
)
|
||||
.subscribe(_ => {
|
||||
this.showControl.setValue(_, {emitEvent: false});
|
||||
this.onShowChanged(_, false);
|
||||
void this.onShowChanged(_, false);
|
||||
});
|
||||
this.showControl.valueChanges.subscribe(value => this.onShowChanged(value));
|
||||
this.showControl.valueChanges.subscribe((value: string) => void this.onShowChanged(value));
|
||||
}
|
||||
|
||||
public async onShowChanged(change: string, updateShow = true): Promise<void> {
|
||||
@@ -68,15 +70,13 @@ export class RemoteComponent {
|
||||
await this.showService.update$(change, {presentationSongId: 'title'});
|
||||
}
|
||||
this.currentShowId = change;
|
||||
this.showService.read$(change).subscribe(_ => this.show = _);
|
||||
this.showService.read$(change).subscribe(_ => (this.show = _));
|
||||
this.showSongService.list$(change).subscribe(_ => {
|
||||
this.showSongs = _;
|
||||
this.presentationSongs = _
|
||||
.map(song => this.songs.filter(f => f.id == song.songId)[0])
|
||||
.map(song => ({
|
||||
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, null)
|
||||
sections: this.textRenderingService.parse(song.text, null),
|
||||
}));
|
||||
});
|
||||
await delay(500);
|
||||
@@ -90,13 +90,11 @@ export class RemoteComponent {
|
||||
public async onSectionClick(id: string, index: number): Promise<void> {
|
||||
await this.showService.update$(this.currentShowId, {
|
||||
presentationSongId: id,
|
||||
presentationSection: index
|
||||
presentationSection: index,
|
||||
});
|
||||
}
|
||||
|
||||
public async onZoom(zoom: number) {
|
||||
await this.showService.update$(this.currentShowId, {
|
||||
presentationZoom: zoom,
|
||||
});
|
||||
public async onZoom(zoom: number): Promise<void> {
|
||||
await this.showService.update$(this.currentShowId, {presentationZoom: zoom});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('PresentationService', () => {
|
||||
let service: PresentationService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(PresentationService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PresentationService {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
export class PresentationService {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="list-item">
|
||||
<div>{{show.date.toDate()|date:'dd.MM.yyyy'}}</div>
|
||||
<div>{{show.showType|showType}}</div>
|
||||
<div>{{ show.date.toDate() | date: "dd.MM.yyyy" }}</div>
|
||||
<div>{{ show.showType | showType }}</div>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('ListItemComponent', () => {
|
||||
let component: ListItemComponent;
|
||||
let fixture: ComponentFixture<ListItemComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ListItemComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [ListItemComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListItemComponent);
|
||||
@@ -20,6 +21,6 @@ describe('ListItemComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import {Show} from '../../services/show';
|
||||
@Component({
|
||||
selector: 'app-list-item',
|
||||
templateUrl: './list-item.component.html',
|
||||
styleUrls: ['./list-item.component.less']
|
||||
styleUrls: ['./list-item.component.less'],
|
||||
})
|
||||
export class ListItemComponent {
|
||||
@Input() public show: Show;
|
||||
|
||||
@@ -2,15 +2,32 @@
|
||||
<app-list-header></app-list-header>
|
||||
|
||||
<ng-container *ngIf="shows$ | async as shows">
|
||||
<app-card *ngIf="getPrivateSongs(shows).length>0" [@fade] [padding]="false" heading="meine Veranstaltungen">
|
||||
<app-list-item *ngFor="let show of getPrivateSongs(shows)" [routerLink]="show.id" [show]="show"></app-list-item>
|
||||
<app-card
|
||||
*ngIf="getPrivateSongs(shows).length > 0"
|
||||
[@fade]
|
||||
[padding]="false"
|
||||
heading="meine Veranstaltungen"
|
||||
>
|
||||
<app-list-item
|
||||
*ngFor="let show of getPrivateSongs(shows)"
|
||||
[routerLink]="show.id"
|
||||
[show]="show"
|
||||
></app-list-item>
|
||||
</app-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="shows$ | async as shows">
|
||||
<app-card *ngIf="getPublicShows(shows).length>0" [@fade] [padding]="false"
|
||||
heading="veröffentlichte Veranstaltungen">
|
||||
<app-list-item *ngFor="let show of getPublicShows(shows)" [routerLink]="show.id" [show]="show"></app-list-item>
|
||||
<app-card
|
||||
*ngIf="getPublicShows(shows).length > 0"
|
||||
[@fade]
|
||||
[padding]="false"
|
||||
heading="veröffentlichte Veranstaltungen"
|
||||
>
|
||||
<app-list-item
|
||||
*ngFor="let show of getPublicShows(shows)"
|
||||
[routerLink]="show.id"
|
||||
[show]="show"
|
||||
></app-list-item>
|
||||
</app-card>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('ListComponent', () => {
|
||||
let component: ListComponent;
|
||||
let fixture: ComponentFixture<ListComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ListComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [ListComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListComponent);
|
||||
@@ -20,6 +21,6 @@ describe('ListComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,12 +8,12 @@ import {ShowService} from '../services/show.service';
|
||||
selector: 'app-list',
|
||||
templateUrl: './list.component.html',
|
||||
styleUrls: ['./list.component.less'],
|
||||
animations: [fade]
|
||||
animations: [fade],
|
||||
})
|
||||
export class ListComponent {
|
||||
public shows$: Observable<Show[]>;
|
||||
|
||||
constructor(showService: ShowService) {
|
||||
public constructor(showService: ShowService) {
|
||||
this.shows$ = showService.list$();
|
||||
}
|
||||
|
||||
@@ -24,5 +24,4 @@ export class ListComponent {
|
||||
public getPrivateSongs(songs: Show[]): Show[] {
|
||||
return songs.filter(_ => !_.published);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<div>
|
||||
<app-card closeLink="/shows" heading="Neue Veranstaltung">
|
||||
|
||||
<div [formGroup]="form" class="split">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Art der Veranstaltung</mat-label>
|
||||
<mat-select formControlName="showType">
|
||||
<mat-optgroup label="öffentlich">
|
||||
<mat-option *ngFor="let key of showTypePublic" [value]="key">{{key|showType}}</mat-option>
|
||||
<mat-option *ngFor="let key of showTypePublic" [value]="key">{{
|
||||
key | showType
|
||||
}}</mat-option>
|
||||
</mat-optgroup>
|
||||
<mat-optgroup label="privat">
|
||||
<mat-option *ngFor="let key of showTypePrivate" [value]="key">{{key|showType}}</mat-option>
|
||||
<mat-option *ngFor="let key of showTypePrivate" [value]="key">{{
|
||||
key | showType
|
||||
}}</mat-option>
|
||||
</mat-optgroup>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Datum</mat-label>
|
||||
<input [matDatepicker]="picker" formControlName="date" matInput>
|
||||
<input [matDatepicker]="picker" formControlName="date" matInput/>
|
||||
<mat-datepicker-toggle [for]="picker" matSuffix></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker touchUi></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('NewComponent', () => {
|
||||
let component: NewComponent;
|
||||
let fixture: ComponentFixture<NewComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [NewComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [NewComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NewComponent);
|
||||
@@ -20,6 +21,6 @@ describe('NewComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import {faSave} from '@fortawesome/free-solid-svg-icons/faSave';
|
||||
@Component({
|
||||
selector: 'app-new',
|
||||
templateUrl: './new.component.html',
|
||||
styleUrls: ['./new.component.less']
|
||||
styleUrls: ['./new.component.less'],
|
||||
})
|
||||
export class NewComponent implements OnInit {
|
||||
public shows$: Observable<Show[]>;
|
||||
@@ -19,7 +19,7 @@ export class NewComponent implements OnInit {
|
||||
public form: FormGroup;
|
||||
public faSave = faSave;
|
||||
|
||||
constructor(private showService: ShowService, showDataService: ShowDataService, private router: Router) {
|
||||
public constructor(private showService: ShowService, showDataService: ShowDataService, private router: Router) {
|
||||
this.shows$ = showDataService.list$();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class NewComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public async onSave() {
|
||||
public async onSave(): Promise<void> {
|
||||
this.form.markAllAsTouched();
|
||||
if (!this.form.valid) {
|
||||
return;
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('DocxService', () => {
|
||||
let service: DocxService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(DocxService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,29 +24,24 @@ export interface DownloadOptions {
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DocxService {
|
||||
|
||||
constructor(
|
||||
public constructor(
|
||||
private showService: ShowService,
|
||||
private showSongService: ShowSongService,
|
||||
private songService: SongService,
|
||||
private textRenderingService: TextRenderingService,
|
||||
private userService: UserService,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
}
|
||||
private configService: ConfigService
|
||||
) {}
|
||||
|
||||
public async create(showId: string, options: DownloadOptions = {}): Promise<any> {
|
||||
public async create(showId: string, options: DownloadOptions = {}): Promise<void> {
|
||||
const {show, songs, user, config} = await this.prepareData(showId);
|
||||
const type = new ShowTypePipe().transform(show.showType);
|
||||
const title = `${type} ${show.date.toDate().toLocaleDateString()}`;
|
||||
|
||||
const paragraphs = [
|
||||
...this.renderTitle(title),
|
||||
...this.renderSongs(songs, options, config),
|
||||
];
|
||||
const paragraphs = [...this.renderTitle(title), ...this.renderSongs(songs, options, config)];
|
||||
|
||||
const sections: ISectionOptions[] = [
|
||||
{
|
||||
@@ -56,11 +51,10 @@ export class DocxService {
|
||||
},
|
||||
},
|
||||
children: paragraphs,
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
const document = this.prepareNewDocument(type, user.name, options, sections);
|
||||
|
||||
|
||||
const blob = await Packer.toBlob(document);
|
||||
|
||||
// saveAs from FileSaver will download the file
|
||||
@@ -72,7 +66,7 @@ export class DocxService {
|
||||
creator: name,
|
||||
title: type,
|
||||
description: '... mit Beschreibung',
|
||||
sections: sections,
|
||||
sections,
|
||||
styles: {
|
||||
paragraphStyles: [
|
||||
{
|
||||
@@ -83,7 +77,8 @@ export class DocxService {
|
||||
quickFormat: true,
|
||||
run: options?.chordMode === 'hide' ? {} : {font: 'courier new'},
|
||||
paragraph: {indent: {left: 0}},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
id: 'licence',
|
||||
name: 'Lizenz',
|
||||
basedOn: 'Normal',
|
||||
@@ -92,13 +87,13 @@ export class DocxService {
|
||||
run: {size: 15, color: 'grey'},
|
||||
paragraph: {indent: {left: 0}},
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private renderSongs(songs: { showSong: ShowSong; song: Song; sections: Section[] }[], options: DownloadOptions, config: Config): Paragraph[] {
|
||||
return songs.reduce((p, song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections, options, config)], []);
|
||||
private renderSongs(songs: {showSong: ShowSong; song: Song; sections: Section[]}[], options: DownloadOptions, config: Config): Paragraph[] {
|
||||
return songs.reduce((p: Paragraph[], song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections, options, config)], []);
|
||||
}
|
||||
|
||||
private renderSong(showSong: ShowSong, song: Song, sections: Section[], options: DownloadOptions, config: Config): Paragraph[] {
|
||||
@@ -106,15 +101,11 @@ export class DocxService {
|
||||
const copyright = this.renderCopyright(song, options, config);
|
||||
const songText = this.renderSongText(sections, options?.chordMode ?? showSong.chordMode);
|
||||
|
||||
return [
|
||||
songTitle,
|
||||
copyright,
|
||||
...songText
|
||||
].filter(_ => _);
|
||||
return [songTitle, copyright, ...songText].filter(_ => _);
|
||||
}
|
||||
|
||||
private renderSongText(sections: Section[], chordMode: ChordMode) {
|
||||
return sections.reduce((p, section) => [...p, ...this.renderSection(section, chordMode)], []);
|
||||
private renderSongText(sections: Section[], chordMode: ChordMode): Paragraph[] {
|
||||
return sections.reduce((p: Paragraph[], section) => [...p, ...this.renderSection(section, chordMode)], []);
|
||||
}
|
||||
|
||||
private renderSongTitle(song: Song): Paragraph {
|
||||
@@ -137,9 +128,7 @@ export class DocxService {
|
||||
const artist = song.artist ? song.artist + ', ' : '';
|
||||
const termsOfUse = song.termsOfUse ? song.termsOfUse + ', ' : '';
|
||||
const origin = song.origin ? song.origin + ', ' : '';
|
||||
const licence = song.legalOwner === 'CCLI'
|
||||
? 'CCLI-Liednummer: ' + song.legalOwnerId + ', CCLI-Lizenz: ' + config.ccliLicenseId
|
||||
: 'CCLI-Liednummer: ' + song.legalOwnerId;
|
||||
const licence = song.legalOwner === 'CCLI' ? 'CCLI-Liednummer: ' + song.legalOwnerId + ', CCLI-Lizenz: ' + config.ccliLicenseId : 'CCLI-Liednummer: ' + song.legalOwnerId;
|
||||
|
||||
return new Paragraph({
|
||||
text: artist + label + termsOfUse + origin + licence,
|
||||
@@ -147,7 +136,6 @@ export class DocxService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private renderSection(section: Section, chordMode: ChordMode): Paragraph[] {
|
||||
return section.lines
|
||||
.filter(line => {
|
||||
@@ -171,13 +159,11 @@ export class DocxService {
|
||||
return new Paragraph({
|
||||
text: line.text,
|
||||
style: 'songtext',
|
||||
spacing
|
||||
spacing,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private renderTitle(type: string): Paragraph[] {
|
||||
|
||||
const songTitle = new Paragraph({
|
||||
text: type,
|
||||
heading: HeadingLevel.HEADING_1,
|
||||
@@ -187,34 +173,39 @@ export class DocxService {
|
||||
return [songTitle];
|
||||
}
|
||||
|
||||
private async prepareData(showId: string): Promise<{ songs: ({ showSong: ShowSong, song: Song, sections: Section[] })[]; show: Show, user: User, config: Config }> {
|
||||
private async prepareData(showId: string): Promise<{
|
||||
songs: {showSong: ShowSong; song: Song; sections: Section[]}[];
|
||||
show: Show;
|
||||
user: User;
|
||||
config: Config;
|
||||
}> {
|
||||
const show = await this.showService.read$(showId).pipe(first()).toPromise();
|
||||
const user = await this.userService.getUserbyId(show.owner);
|
||||
const config = await this.configService.get();
|
||||
|
||||
const showSongs = await this.showSongService.list(showId);
|
||||
const songsAsync = await showSongs.map(async showSong => {
|
||||
const songsAsync = showSongs.map(async showSong => {
|
||||
const song = await this.songService.read(showSong.songId);
|
||||
const sections = this.textRenderingService.parse(song.text, {
|
||||
baseKey: showSong.keyOriginal,
|
||||
targetKey: showSong.key
|
||||
targetKey: showSong.key,
|
||||
});
|
||||
return {
|
||||
showSong,
|
||||
song,
|
||||
sections
|
||||
sections,
|
||||
};
|
||||
});
|
||||
const songs = await Promise.all(songsAsync);
|
||||
return {songs, show, user, config};
|
||||
}
|
||||
|
||||
private saveAs(blob, fileName) {
|
||||
const a = document.createElement('a') as any;
|
||||
private saveAs(blob: Blob, fileName: string) {
|
||||
const a = document.createElement('a');
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.setAttribute('target', '_self');
|
||||
a.style = 'display: none';
|
||||
a.style.display = 'none';
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
|
||||
@@ -3,10 +3,10 @@ import {TestBed} from '@angular/core/testing';
|
||||
import {ShowDataService} from './show-data.service';
|
||||
|
||||
describe('ShowDataService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
beforeEach(() => void TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ShowDataService = TestBed.get(ShowDataService);
|
||||
expect(service).toBeTruthy();
|
||||
const service: ShowDataService = TestBed.inject(ShowDataService);
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,17 +2,17 @@ import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {DbService} from '../../../services/db.service';
|
||||
import {Show} from './show';
|
||||
import {QueryFn} from '@angular/fire/firestore/interfaces';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ShowDataService {
|
||||
private collection = 'shows';
|
||||
|
||||
constructor(private dbService: DbService) {
|
||||
}
|
||||
public constructor(private dbService: DbService) {}
|
||||
|
||||
public list$ = (queryFn?): Observable<Show[]> => this.dbService.col$(this.collection, queryFn);
|
||||
public list$ = (queryFn?: QueryFn): Observable<Show[]> => this.dbService.col$(this.collection, queryFn);
|
||||
public read$ = (showId: string): Observable<Show | undefined> => this.dbService.doc$(`${this.collection}/${showId}`);
|
||||
public update = async (showId: string, data: Partial<Show>): Promise<void> => await this.dbService.doc(`${this.collection}/${showId}`).update(data);
|
||||
public add = async (data: Partial<Show>): Promise<string> => (await this.dbService.col(this.collection).add(data)).id;
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('ShowSongDataService', () => {
|
||||
let service: ShowSongDataService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ShowSongDataService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,20 +2,21 @@ import {Injectable} from '@angular/core';
|
||||
import {DbService} from '../../../services/db.service';
|
||||
import {Observable} from 'rxjs';
|
||||
import {ShowSong} from './show-song';
|
||||
import {QueryFn} from '@angular/fire/firestore/interfaces';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ShowSongDataService {
|
||||
private collection = 'shows';
|
||||
private subCollection = 'songs';
|
||||
|
||||
constructor(private dbService: DbService) {
|
||||
}
|
||||
public constructor(private dbService: DbService) {}
|
||||
|
||||
public list$ = (showId: string, queryFn?): Observable<ShowSong[]> => this.dbService.col$(`${this.collection}/${showId}/${this.subCollection}`, queryFn);
|
||||
public list$ = (showId: string, queryFn?: QueryFn): Observable<ShowSong[]> => this.dbService.col$(`${this.collection}/${showId}/${this.subCollection}`, queryFn);
|
||||
public read$ = (showId: string, songId: string): Observable<ShowSong | undefined> => this.dbService.doc$(`${this.collection}/${showId}/${this.subCollection}/${songId}`);
|
||||
public update$ = async (showId: string, songId: string, data: Partial<ShowSong>): Promise<void> => await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).update(data);
|
||||
public update$ = async (showId: string, songId: string, data: Partial<ShowSong>): Promise<void> =>
|
||||
await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).update(data);
|
||||
public delete = async (showId: string, songId: string): Promise<void> => await this.dbService.doc(`${this.collection}/${showId}/${this.subCollection}/${songId}`).delete();
|
||||
public add = async (showId: string, data: Partial<ShowSong>): Promise<string> => (await this.dbService.col(`${this.collection}/${showId}/${this.subCollection}`).add(data)).id;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('ShowSongService', () => {
|
||||
let service: ShowSongService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ShowSongService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,16 +7,10 @@ import {first, take} from 'rxjs/operators';
|
||||
import {UserService} from '../../../services/user/user.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ShowSongService {
|
||||
|
||||
constructor(
|
||||
private showSongDataService: ShowSongDataService,
|
||||
private songDataService: SongDataService,
|
||||
private userService: UserService,
|
||||
) {
|
||||
}
|
||||
public constructor(private showSongDataService: ShowSongDataService, private songDataService: SongDataService, private userService: UserService) {}
|
||||
|
||||
public async new$(showId: string, songId: string, order: number, addedLive = false): Promise<string> {
|
||||
const song = await this.songDataService.read$(songId).pipe(take(1)).toPromise();
|
||||
@@ -27,7 +21,7 @@ export class ShowSongService {
|
||||
key: song.key,
|
||||
keyOriginal: song.key,
|
||||
chordMode: user.chordMode,
|
||||
addedLive
|
||||
addedLive,
|
||||
};
|
||||
return await this.showSongDataService.add(showId, data);
|
||||
}
|
||||
|
||||
@@ -5,33 +5,40 @@ import {ShowDataService} from './show-data.service';
|
||||
|
||||
describe('ShowService', () => {
|
||||
const mockShowDataService = {add: Promise.resolve(null)};
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: ShowDataService, useValue: mockShowDataService}
|
||||
]
|
||||
}));
|
||||
beforeEach(
|
||||
() =>
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [{provide: ShowDataService, useValue: mockShowDataService}],
|
||||
})
|
||||
);
|
||||
|
||||
ShowService.SHOW_TYPE_PUBLIC.forEach(type => {
|
||||
it('should calc public flag for ' + type, async () => {
|
||||
const service: ShowService = TestBed.get(ShowService);
|
||||
const service: ShowService = TestBed.inject(ShowService);
|
||||
const addSpy = spyOn(TestBed.inject(ShowDataService), 'add').and.returnValue(Promise.resolve('id'));
|
||||
|
||||
const id = await service.new$({showType: type});
|
||||
|
||||
expect(id).toBe('id');
|
||||
expect(addSpy).toHaveBeenCalledWith({showType: type, public: true});
|
||||
void expect(id).toBe('id');
|
||||
void expect(addSpy).toHaveBeenCalledWith({
|
||||
showType: type,
|
||||
public: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ShowService.SHOW_TYPE_PRIVATE.forEach(type => {
|
||||
it('should calc private flag for ' + type, async () => {
|
||||
const service: ShowService = TestBed.get(ShowService);
|
||||
const service: ShowService = TestBed.inject(ShowService);
|
||||
const addSpy = spyOn(TestBed.inject(ShowDataService), 'add').and.returnValue(Promise.resolve('id'));
|
||||
|
||||
const id = await service.new$({showType: type});
|
||||
|
||||
expect(id).toBe('id');
|
||||
expect(addSpy).toHaveBeenCalledWith({showType: type, public: false});
|
||||
void expect(id).toBe('id');
|
||||
void expect(addSpy).toHaveBeenCalledWith({
|
||||
showType: type,
|
||||
public: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,31 +7,28 @@ import {map, switchMap} from 'rxjs/operators';
|
||||
import {User} from '../../../services/user/user';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ShowService {
|
||||
|
||||
public static SHOW_TYPE = ['service-worship', 'service-praise', 'home-group-big', 'home-group', 'prayer-group', 'teens-group', 'kids-group', 'misc-public', 'misc-private'];
|
||||
public static SHOW_TYPE_PUBLIC = ['service-worship', 'service-praise', 'home-group-big', 'teens-group', 'kids-group', 'misc-public'];
|
||||
public static SHOW_TYPE_PRIVATE = ['home-group', 'prayer-group', 'misc-private',];
|
||||
public static SHOW_TYPE_PRIVATE = ['home-group', 'prayer-group', 'misc-private'];
|
||||
private user: User;
|
||||
|
||||
constructor(private showDataService: ShowDataService, private userService: UserService) {
|
||||
userService.user$.subscribe(_ => this.user = _);
|
||||
public constructor(private showDataService: ShowDataService, private userService: UserService) {
|
||||
userService.user$.subscribe(_ => (this.user = _));
|
||||
}
|
||||
|
||||
public read$ = (showId: string): Observable<Show> => this.showDataService.read$(showId);
|
||||
|
||||
public list$(publishedOnly: boolean = false): Observable<Show[]> {
|
||||
|
||||
public list$(publishedOnly = false): Observable<Show[]> {
|
||||
return this.userService.user$.pipe(
|
||||
switchMap(_ => this.showDataService.list$(), (user: User, shows: Show[]) => ({user, shows})),
|
||||
map(_ => _.shows
|
||||
.filter(_ => !_.archived)
|
||||
.filter(show => show.published || (show.owner === _.user.id && !publishedOnly))
|
||||
)
|
||||
switchMap(
|
||||
() => this.showDataService.list$(),
|
||||
(user: User, shows: Show[]) => ({user, shows})
|
||||
),
|
||||
map(s => s.shows.filter(_ => !_.archived).filter(show => show.published || (show.owner === s.user.id && !publishedOnly)))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public update$ = async (showId: string, data: Partial<Show>): Promise<void> => this.showDataService.update(showId, data);
|
||||
|
||||
@@ -14,6 +14,4 @@ export interface Show {
|
||||
presentationSongId: string;
|
||||
presentationSection: number;
|
||||
presentationZoom: number;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,82 @@
|
||||
<div *ngIf="(show$|async) as show">
|
||||
<div *ngIf="show$ | async as show">
|
||||
<app-card
|
||||
closeLink="../"
|
||||
heading="{{show.showType|showType}}, {{show.date.toDate()|date:'dd.MM.yyyy'}} - {{getStatus(show)}}">
|
||||
<i *ngIf="show.public">öffentliche Veranstaltung von
|
||||
heading="{{ show.showType | showType }}, {{
|
||||
show.date.toDate() | date: 'dd.MM.yyyy'
|
||||
}} - {{ getStatus(show) }}"
|
||||
>
|
||||
<i *ngIf="show.public"
|
||||
>öffentliche Veranstaltung von
|
||||
<app-user-name [userId]="show.owner"></app-user-name>
|
||||
</i>
|
||||
<i *ngIf="!show.public">geschlossene Veranstaltung von
|
||||
<i *ngIf="!show.public"
|
||||
>geschlossene Veranstaltung von
|
||||
<app-user-name [userId]="show.owner"></app-user-name>
|
||||
</i>
|
||||
<p *ngIf="!show.published">
|
||||
<mat-checkbox [(ngModel)]="showText">Text anzeigen</mat-checkbox>
|
||||
</p>
|
||||
<div *ngIf="showSongs && songs" class="song-list">
|
||||
<app-song *ngFor="let song of showSongs" [showId]="showId"
|
||||
<app-song
|
||||
*ngFor="let song of showSongs"
|
||||
[Song]="getSong(song.songId)"
|
||||
[showId]="showId"
|
||||
[showSong]="song"
|
||||
[showSongs]="showSongs"
|
||||
[showText]="showText"
|
||||
[show]="show"
|
||||
[song]="getSong(song.songId)"
|
||||
class="song-row"
|
||||
></app-song>
|
||||
</div>
|
||||
|
||||
<app-add-song *ngIf="songs && !show.published" [showId]="showId" [showSongs]="showSongs"
|
||||
[songs]="songs"></app-add-song>
|
||||
<app-add-song
|
||||
*ngIf="songs && !show.published"
|
||||
[showId]="showId"
|
||||
[showSongs]="showSongs"
|
||||
[songs]="songs"
|
||||
></app-add-song>
|
||||
|
||||
<app-button-row>
|
||||
<ng-container *appOwner="show.owner">
|
||||
<app-button (click)="onArchive(true)" *ngIf="!show.archived" [icon]="faBox">
|
||||
<app-button
|
||||
(click)="onArchive(true)"
|
||||
*ngIf="!show.archived"
|
||||
[icon]="faBox"
|
||||
>
|
||||
Archivieren
|
||||
</app-button>
|
||||
<app-button (click)="onArchive(false)" *ngIf="show.archived" [icon]="faBoxOpen">
|
||||
<app-button
|
||||
(click)="onArchive(false)"
|
||||
*ngIf="show.archived"
|
||||
[icon]="faBoxOpen"
|
||||
>
|
||||
Wiederherstellen
|
||||
</app-button>
|
||||
<app-button (click)="onPublish(true)" *ngIf="!show.published" [icon]="faPublish">
|
||||
<app-button
|
||||
(click)="onPublish(true)"
|
||||
*ngIf="!show.published"
|
||||
[icon]="faPublish"
|
||||
>
|
||||
Veröffentlichen
|
||||
</app-button>
|
||||
<app-button (click)="onPublish(false)" *ngIf="show.published" [icon]="faUnpublish">
|
||||
<app-button
|
||||
(click)="onPublish(false)"
|
||||
*ngIf="show.published"
|
||||
[icon]="faUnpublish"
|
||||
>
|
||||
Veröffentlichung zurückziehen
|
||||
</app-button>
|
||||
</ng-container>
|
||||
<app-button [icon]="faDownload" [matMenuTriggerFor]="menu">Herunterladen</app-button>
|
||||
<app-button [icon]="faDownload" [matMenuTriggerFor]="menu"
|
||||
>Herunterladen
|
||||
</app-button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<app-button (click)="onDownload()" [icon]="faUser">Ablauf für Lobpreisleiter</app-button>
|
||||
<app-button (click)="onDownloadHandout()" [icon]="faUsers">Handout mit Copyright Infos</app-button>
|
||||
<app-button (click)="onDownload()" [icon]="faUser"
|
||||
>Ablauf für Lobpreisleiter
|
||||
</app-button>
|
||||
<app-button (click)="onDownloadHandout()" [icon]="faUsers"
|
||||
>Handout mit Copyright Infos
|
||||
</app-button>
|
||||
</mat-menu>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('ShowComponent', () => {
|
||||
let component: ShowComponent;
|
||||
let fixture: ComponentFixture<ShowComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ShowComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [ShowComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShowComponent);
|
||||
@@ -20,6 +21,6 @@ describe('ShowComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers';
|
||||
@Component({
|
||||
selector: 'app-show',
|
||||
templateUrl: './show.component.html',
|
||||
styleUrls: ['./show.component.less']
|
||||
styleUrls: ['./show.component.less'],
|
||||
})
|
||||
export class ShowComponent implements OnInit {
|
||||
public show$: Observable<Show>;
|
||||
@@ -37,29 +37,31 @@ export class ShowComponent implements OnInit {
|
||||
public faUser = faUser;
|
||||
public faUsers = faUsers;
|
||||
|
||||
constructor(
|
||||
public constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private showService: ShowService,
|
||||
private songService: SongService,
|
||||
private showSongService: ShowSongService,
|
||||
private docxService: DocxService,
|
||||
) {
|
||||
}
|
||||
private docxService: DocxService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
public ngOnInit(): void {
|
||||
this.show$ = this.activatedRoute.params.pipe(
|
||||
map(param => param.showId),
|
||||
tap(_ => this.showId = _),
|
||||
switchMap(showId => this.showService.read$(showId))
|
||||
map((param: {showId: string}) => param.showId),
|
||||
tap((_: string) => (this.showId = _)),
|
||||
switchMap((showId: string) => this.showService.read$(showId))
|
||||
);
|
||||
this.activatedRoute.params.pipe(
|
||||
map(param => param.showId),
|
||||
this.activatedRoute.params
|
||||
.pipe(
|
||||
map((param: {showId: string}) => param.showId),
|
||||
switchMap(showId => this.showSongService.list$(showId)),
|
||||
filter(_ => !!_)
|
||||
).subscribe(_ => this.showSongs = _);
|
||||
this.songService.list$().pipe(
|
||||
filter(_ => !!_)
|
||||
).subscribe(_ => this.songs = _);
|
||||
)
|
||||
.subscribe(_ => (this.showSongs = _));
|
||||
this.songService
|
||||
.list$()
|
||||
.pipe(filter(_ => !!_))
|
||||
.subscribe(_ => (this.songs = _));
|
||||
}
|
||||
|
||||
public getSong(songId: string): Song {
|
||||
@@ -90,6 +92,9 @@ export class ShowComponent implements OnInit {
|
||||
}
|
||||
|
||||
public async onDownloadHandout(): Promise<void> {
|
||||
await this.docxService.create(this.showId, {chordMode: 'hide', copyright: true});
|
||||
await this.docxService.create(this.showId, {
|
||||
chordMode: 'hide',
|
||||
copyright: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
<div *ngIf="_song">
|
||||
<div *ngIf="show.published" class="title published">{{_song.title}}</div>
|
||||
<div *ngIf="iSong">
|
||||
<div *ngIf="show.published" class="title published">{{ iSong.title }}</div>
|
||||
|
||||
<div *ngIf="!show.published" class="song">
|
||||
<app-menu-button (click)="reorder(true)" [icon]="faUp" class="btn-up btn-icon"></app-menu-button>
|
||||
<app-menu-button (click)="reorder(false)" [icon]="faDown" class="btn-down btn-icon"></app-menu-button>
|
||||
<span class="title">{{_song.title}}</span>
|
||||
<app-menu-button
|
||||
(click)="reorder(true)"
|
||||
[icon]="faUp"
|
||||
class="btn-up btn-icon"
|
||||
></app-menu-button>
|
||||
<app-menu-button
|
||||
(click)="reorder(false)"
|
||||
[icon]="faDown"
|
||||
class="btn-down btn-icon"
|
||||
></app-menu-button>
|
||||
<span class="title">{{ iSong.title }}</span>
|
||||
<span class="keys">
|
||||
<span *ngIf="showSong.keyOriginal!==showSong.key">{{showSong.keyOriginal}} → </span>
|
||||
<span *ngIf="showSong.keyOriginal !== showSong.key"
|
||||
>{{ showSong.keyOriginal }} → </span
|
||||
>
|
||||
<mat-form-field *ngIf="keys" appearance="standard">
|
||||
<mat-select [formControl]="keyFormControl">
|
||||
<mat-option *ngFor="let key of keys" [value]="key">{{key}}</mat-option>
|
||||
<mat-option *ngFor="let key of keys" [value]="key">{{
|
||||
key
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</span>
|
||||
<app-menu-button (click)="onDelete()" [icon]="faDelete" class="btn-delete btn-icon"></app-menu-button>
|
||||
<app-menu-button
|
||||
(click)="onDelete()"
|
||||
[icon]="faDelete"
|
||||
class="btn-delete btn-icon"
|
||||
></app-menu-button>
|
||||
</div>
|
||||
<app-song-text (chordModeChanged)="onChordModeChanged($event)" *ngIf="showText || show.published"
|
||||
[chordMode]="showSong.chordMode" [transpose]="{baseKey: showSong.keyOriginal, targetKey: showSong.key}"
|
||||
[showSwitch]="!show.published" [text]="_song.text"></app-song-text>
|
||||
<app-song-text
|
||||
(chordModeChanged)="onChordModeChanged($event)"
|
||||
*ngIf="showText || show.published"
|
||||
[chordMode]="showSong.chordMode"
|
||||
[showSwitch]="!show.published"
|
||||
[text]="iSong.text"
|
||||
[transpose]="{ baseKey: showSong.keyOriginal, targetKey: showSong.key }"
|
||||
></app-song-text>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('SongComponent', () => {
|
||||
let component: SongComponent;
|
||||
let fixture: ComponentFixture<SongComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SongComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [SongComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SongComponent);
|
||||
@@ -20,6 +21,6 @@ describe('SongComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import {Show} from '../../services/show';
|
||||
@Component({
|
||||
selector: 'app-song',
|
||||
templateUrl: './song.component.html',
|
||||
styleUrls: ['./song.component.less']
|
||||
styleUrls: ['./song.component.less'],
|
||||
})
|
||||
export class SongComponent implements OnInit {
|
||||
@Input() public show: Show;
|
||||
@@ -22,30 +22,25 @@ export class SongComponent implements OnInit {
|
||||
@Input() public showId: string;
|
||||
@Input() public showText: boolean;
|
||||
|
||||
|
||||
public keys: string[];
|
||||
public faDelete = faTrash;
|
||||
public faUp = faCaretUp;
|
||||
public faDown = faCaretDown;
|
||||
public keyFormControl: FormControl;
|
||||
public iSong: Song;
|
||||
|
||||
constructor(
|
||||
private showSongService: ShowSongService,
|
||||
) {
|
||||
}
|
||||
|
||||
public _song: Song;
|
||||
public constructor(private showSongService: ShowSongService) {}
|
||||
|
||||
@Input()
|
||||
public set song(song: Song) {
|
||||
this._song = song;
|
||||
this.keys = !!song ? getScale(song.key) : [];
|
||||
public set Song(song: Song) {
|
||||
this.iSong = song;
|
||||
this.keys = song ? getScale(song.key) : [];
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.keyFormControl = new FormControl(this.showSong.key);
|
||||
this.keyFormControl.valueChanges.subscribe(async value => {
|
||||
await this.showSongService.update$(this.showId, this.showSong.id, {key: value});
|
||||
this.keyFormControl.valueChanges.subscribe((value: string) => {
|
||||
void this.showSongService.update$(this.showId, this.showSong.id, {key: value});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,7 +48,6 @@ export class SongComponent implements OnInit {
|
||||
await this.showSongService.delete$(this.showId, this.showSong.id);
|
||||
}
|
||||
|
||||
|
||||
public async reorder(up: boolean): Promise<void> {
|
||||
if (up) {
|
||||
await this.reorderUp();
|
||||
@@ -63,7 +57,7 @@ export class SongComponent implements OnInit {
|
||||
}
|
||||
|
||||
public async reorderUp(): Promise<void> {
|
||||
const index = this.showSongs.findIndex(_ => _.songId === this._song.id);
|
||||
const index = this.showSongs.findIndex(_ => _.songId === this.iSong.id);
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -71,12 +65,16 @@ export class SongComponent implements OnInit {
|
||||
const song = this.showSongs[index];
|
||||
const toggleSong = this.showSongs[index - 1];
|
||||
|
||||
await this.showSongService.update$(this.showId, song.id, {order: toggleSong.order});
|
||||
await this.showSongService.update$(this.showId, toggleSong.id, {order: song.order});
|
||||
await this.showSongService.update$(this.showId, song.id, {
|
||||
order: toggleSong.order,
|
||||
});
|
||||
await this.showSongService.update$(this.showId, toggleSong.id, {
|
||||
order: song.order,
|
||||
});
|
||||
}
|
||||
|
||||
public async reorderDown(): Promise<void> {
|
||||
const index = this.showSongs.findIndex(_ => _.songId === this._song.id);
|
||||
const index = this.showSongs.findIndex(_ => _.songId === this.iSong.id);
|
||||
if (index === this.showSongs.length - 1) {
|
||||
return;
|
||||
}
|
||||
@@ -84,11 +82,17 @@ export class SongComponent implements OnInit {
|
||||
const song = this.showSongs[index];
|
||||
const toggleSong = this.showSongs[index + 1];
|
||||
|
||||
await this.showSongService.update$(this.showId, song.id, {order: toggleSong.order});
|
||||
await this.showSongService.update$(this.showId, toggleSong.id, {order: song.order});
|
||||
await this.showSongService.update$(this.showId, song.id, {
|
||||
order: toggleSong.order,
|
||||
});
|
||||
await this.showSongService.update$(this.showId, toggleSong.id, {
|
||||
order: song.order,
|
||||
});
|
||||
}
|
||||
|
||||
public async onChordModeChanged(value: ChordMode): Promise<void> {
|
||||
await this.showSongService.update$(this.showId, this.showSong.id, {chordMode: value});
|
||||
await this.showSongService.update$(this.showId, this.showSong.id, {
|
||||
chordMode: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,26 +4,24 @@ import {NewComponent} from './new/new.component';
|
||||
import {ListComponent} from './list/list.component';
|
||||
import {ShowComponent} from './show/show.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
component: ListComponent
|
||||
component: ListComponent,
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
component: NewComponent
|
||||
component: NewComponent,
|
||||
},
|
||||
{
|
||||
path: ':showId',
|
||||
component: ShowComponent
|
||||
}
|
||||
component: ShowComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class ShowsRoutingModule {
|
||||
}
|
||||
export class ShowsRoutingModule {}
|
||||
|
||||
@@ -29,7 +29,6 @@ import {OwnerModule} from '../../services/user/owner.module';
|
||||
import {UserNameModule} from '../../services/user/user-name/user-name.module';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [NewComponent, ListComponent, ListItemComponent, ShowComponent, SongComponent],
|
||||
imports: [
|
||||
@@ -57,7 +56,6 @@ import {MatMenuModule} from '@angular/material/menu';
|
||||
OwnerModule,
|
||||
UserNameModule,
|
||||
MatMenuModule,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class ShowsModule {
|
||||
}
|
||||
export class ShowsModule {}
|
||||
|
||||
@@ -3,10 +3,10 @@ import {TestBed} from '@angular/core/testing';
|
||||
import {FileDataService} from './file-data.service';
|
||||
|
||||
describe('FileDataService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
beforeEach(() => void TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: FileDataService = TestBed.get(FileDataService);
|
||||
expect(service).toBeTruthy();
|
||||
const service: FileDataService = TestBed.inject(FileDataService);
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,12 +5,10 @@ import {FileServer} from './fileServer';
|
||||
import {DbService} from '../../../services/db.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class FileDataService {
|
||||
|
||||
constructor(private db: DbService) {
|
||||
}
|
||||
public constructor(private db: DbService) {}
|
||||
|
||||
public async set(songId: string, file: FileServer): Promise<string> {
|
||||
const songRef = this.db.doc('songs/' + songId);
|
||||
@@ -27,8 +25,5 @@ export class FileDataService {
|
||||
public read$(songId: string): Observable<File[]> {
|
||||
const songRef = this.db.doc('songs/' + songId);
|
||||
return songRef.collection<File>('files').valueChanges({idField: 'id'});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('FileService', () => {
|
||||
let service: FileService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(FileService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,22 +4,17 @@ import {Observable} from 'rxjs';
|
||||
import {FileDataService} from './file-data.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class FileService {
|
||||
|
||||
constructor(
|
||||
private storage: AngularFireStorage,
|
||||
private fileDataService: FileDataService
|
||||
) {
|
||||
}
|
||||
public constructor(private storage: AngularFireStorage, private fileDataService: FileDataService) {}
|
||||
|
||||
public getDownloadUrl(path: string): Observable<string> {
|
||||
const ref = this.storage.ref(path);
|
||||
return ref.getDownloadURL();
|
||||
return ref.getDownloadURL() as Observable<string>;
|
||||
}
|
||||
|
||||
public async delete(path: string, songId: string, fileId: string) {
|
||||
public async delete(path: string, songId: string, fileId: string): Promise<void> {
|
||||
const ref = this.storage.ref(path);
|
||||
await ref.delete().toPromise();
|
||||
await this.fileDataService.delete(songId, fileId);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export class FileBase {
|
||||
|
||||
protected basePath = '/attachments';
|
||||
protected directory = (songId: string) => `${this.basePath}/${songId}`;
|
||||
protected directory: (songId: string) => string = (songId: string) => `${this.basePath}/${songId}`;
|
||||
}
|
||||
|
||||
@@ -1,133 +1,156 @@
|
||||
export const KEYS = [
|
||||
'C#', 'C', 'Db', 'D#', 'D', 'Eb', 'E', 'F#', 'F', 'Gb', 'G#', 'G', 'Ab', 'A#', 'A', 'B', 'H',
|
||||
'c#', 'c', 'db', 'd#', 'd', 'eb', 'e', 'f#', 'f', 'gb', 'g#', 'g', 'ab', 'a#', 'a', 'b', 'h'
|
||||
];
|
||||
export const KEYS_MAJOR_FLAT = [
|
||||
'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H',
|
||||
];
|
||||
export const KEYS_MAJOR_B = [
|
||||
'C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'B', 'H',
|
||||
];
|
||||
export const KEYS_MINOR_FLAT = [
|
||||
'c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'h',
|
||||
];
|
||||
export const KEYS_MINOR_B = [
|
||||
'c', 'db', 'd', 'eb', 'e', 'f', 'gb', 'g', 'ab', 'a', 'b', 'h',
|
||||
export const KEYS: string[] = [
|
||||
'C#',
|
||||
'C',
|
||||
'Db',
|
||||
'D#',
|
||||
'D',
|
||||
'Eb',
|
||||
'E',
|
||||
'F#',
|
||||
'F',
|
||||
'Gb',
|
||||
'G#',
|
||||
'G',
|
||||
'Ab',
|
||||
'A#',
|
||||
'A',
|
||||
'B',
|
||||
'H',
|
||||
'c#',
|
||||
'c',
|
||||
'db',
|
||||
'd#',
|
||||
'd',
|
||||
'eb',
|
||||
'e',
|
||||
'f#',
|
||||
'f',
|
||||
'gb',
|
||||
'g#',
|
||||
'g',
|
||||
'ab',
|
||||
'a#',
|
||||
'a',
|
||||
'b',
|
||||
'h',
|
||||
];
|
||||
export const KEYS_MAJOR_FLAT: string[] = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H'];
|
||||
export const KEYS_MAJOR_B: string[] = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'B', 'H'];
|
||||
export const KEYS_MINOR_FLAT: string[] = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'h'];
|
||||
export const KEYS_MINOR_B: string[] = ['c', 'db', 'd', 'eb', 'e', 'f', 'gb', 'g', 'ab', 'a', 'b', 'h'];
|
||||
|
||||
export type scale = 'b' | 'flat'
|
||||
export type scale = 'b' | 'flat';
|
||||
|
||||
const scaleTypeAssignment: { [key: string]: string[][] } = {
|
||||
'C': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
const scaleTypeAssignment: {[key: string]: string[][]} = {
|
||||
C: [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'C#': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'Db': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'D': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
Db: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
D: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'D#': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'Eb': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'E': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'F': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
Eb: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
E: [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
F: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'F#': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'Gb': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'G': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
Gb: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
G: [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'G#': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'Ab': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'A': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
Ab: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
A: [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'A#': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'B': [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
'H': [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
'c': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
B: [KEYS_MAJOR_B, KEYS_MINOR_B],
|
||||
H: [KEYS_MAJOR_FLAT, KEYS_MINOR_FLAT],
|
||||
c: [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'c#': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'db': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'd': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
db: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
d: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'd#': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'eb': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'e': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'f': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
eb: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
e: [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
f: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'f#': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'gb': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'g': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
gb: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
g: [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'g#': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'ab': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'a': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
ab: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
a: [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'a#': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
'b': [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
'h': [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
}
|
||||
|
||||
const scaleAssignment = {
|
||||
'C': KEYS_MAJOR_FLAT,
|
||||
'C#': KEYS_MAJOR_FLAT,
|
||||
'Db': KEYS_MAJOR_B,
|
||||
'D': KEYS_MAJOR_B,
|
||||
'D#': KEYS_MAJOR_FLAT,
|
||||
'Eb': KEYS_MAJOR_B,
|
||||
'E': KEYS_MAJOR_FLAT,
|
||||
'F': KEYS_MAJOR_B,
|
||||
'F#': KEYS_MAJOR_FLAT,
|
||||
'Gb': KEYS_MAJOR_B,
|
||||
'G': KEYS_MAJOR_FLAT,
|
||||
'G#': KEYS_MAJOR_FLAT,
|
||||
'Ab': KEYS_MAJOR_B,
|
||||
'A': KEYS_MAJOR_FLAT,
|
||||
'A#': KEYS_MAJOR_FLAT,
|
||||
'B': KEYS_MAJOR_B,
|
||||
'H': KEYS_MAJOR_FLAT,
|
||||
'c': KEYS_MINOR_FLAT,
|
||||
'c#': KEYS_MINOR_FLAT,
|
||||
'db': KEYS_MINOR_B,
|
||||
'd': KEYS_MINOR_B,
|
||||
'd#': KEYS_MINOR_FLAT,
|
||||
'eb': KEYS_MINOR_B,
|
||||
'e': KEYS_MINOR_FLAT,
|
||||
'f': KEYS_MINOR_B,
|
||||
'f#': KEYS_MINOR_FLAT,
|
||||
'gb': KEYS_MINOR_B,
|
||||
'g': KEYS_MINOR_FLAT,
|
||||
'g#': KEYS_MINOR_FLAT,
|
||||
'ab': KEYS_MINOR_B,
|
||||
'a': KEYS_MINOR_FLAT,
|
||||
'a#': KEYS_MINOR_FLAT,
|
||||
'b': KEYS_MINOR_B,
|
||||
'h': KEYS_MINOR_FLAT,
|
||||
b: [KEYS_MINOR_B, KEYS_MAJOR_B],
|
||||
h: [KEYS_MINOR_FLAT, KEYS_MAJOR_FLAT],
|
||||
};
|
||||
|
||||
export const scaleMapping = {
|
||||
'C': 'C',
|
||||
const scaleAssignment: {[key: string]: string[]} = {
|
||||
C: KEYS_MAJOR_FLAT,
|
||||
'C#': KEYS_MAJOR_FLAT,
|
||||
Db: KEYS_MAJOR_B,
|
||||
D: KEYS_MAJOR_B,
|
||||
'D#': KEYS_MAJOR_FLAT,
|
||||
Eb: KEYS_MAJOR_B,
|
||||
E: KEYS_MAJOR_FLAT,
|
||||
F: KEYS_MAJOR_B,
|
||||
'F#': KEYS_MAJOR_FLAT,
|
||||
Gb: KEYS_MAJOR_B,
|
||||
G: KEYS_MAJOR_FLAT,
|
||||
'G#': KEYS_MAJOR_FLAT,
|
||||
Ab: KEYS_MAJOR_B,
|
||||
A: KEYS_MAJOR_FLAT,
|
||||
'A#': KEYS_MAJOR_FLAT,
|
||||
B: KEYS_MAJOR_B,
|
||||
H: KEYS_MAJOR_FLAT,
|
||||
c: KEYS_MINOR_FLAT,
|
||||
'c#': KEYS_MINOR_FLAT,
|
||||
db: KEYS_MINOR_B,
|
||||
d: KEYS_MINOR_B,
|
||||
'd#': KEYS_MINOR_FLAT,
|
||||
eb: KEYS_MINOR_B,
|
||||
e: KEYS_MINOR_FLAT,
|
||||
f: KEYS_MINOR_B,
|
||||
'f#': KEYS_MINOR_FLAT,
|
||||
gb: KEYS_MINOR_B,
|
||||
g: KEYS_MINOR_FLAT,
|
||||
'g#': KEYS_MINOR_FLAT,
|
||||
ab: KEYS_MINOR_B,
|
||||
a: KEYS_MINOR_FLAT,
|
||||
'a#': KEYS_MINOR_FLAT,
|
||||
b: KEYS_MINOR_B,
|
||||
h: KEYS_MINOR_FLAT,
|
||||
};
|
||||
|
||||
export const scaleMapping: {[key: string]: string} = {
|
||||
C: 'C',
|
||||
'C#': 'C♯',
|
||||
'Db': 'D♭',
|
||||
'D': 'D',
|
||||
Db: 'D♭',
|
||||
D: 'D',
|
||||
'D#': 'D♯',
|
||||
'Eb': 'E♭',
|
||||
'E': 'E',
|
||||
'F': 'F',
|
||||
Eb: 'E♭',
|
||||
E: 'E',
|
||||
F: 'F',
|
||||
'F#': 'F♯',
|
||||
'Gb': 'D♭',
|
||||
'G': 'G',
|
||||
Gb: 'D♭',
|
||||
G: 'G',
|
||||
'G#': 'G♯',
|
||||
'Ab': 'A♭',
|
||||
'A': 'A',
|
||||
Ab: 'A♭',
|
||||
A: 'A',
|
||||
'A#': 'A♯',
|
||||
'B': 'B',
|
||||
'H': 'H',
|
||||
'c': 'c',
|
||||
B: 'B',
|
||||
H: 'H',
|
||||
c: 'c',
|
||||
'c#': 'c♯',
|
||||
'db': 'd♭',
|
||||
'd': 'd',
|
||||
db: 'd♭',
|
||||
d: 'd',
|
||||
'd#': 'd♯',
|
||||
'eb': 'e♭',
|
||||
'e': 'e',
|
||||
'f': 'f',
|
||||
eb: 'e♭',
|
||||
e: 'e',
|
||||
f: 'f',
|
||||
'f#': 'f♯',
|
||||
'gb': 'g♭',
|
||||
'g': 'g',
|
||||
gb: 'g♭',
|
||||
g: 'g',
|
||||
'g#': 'g♯',
|
||||
'ab': 'a♭',
|
||||
'a': 'a',
|
||||
ab: 'a♭',
|
||||
a: 'a',
|
||||
'a#': 'a♯',
|
||||
'b': 'b',
|
||||
'h': 'h',
|
||||
b: 'b',
|
||||
h: 'h',
|
||||
};
|
||||
|
||||
export const getScale = (key: string): string[] => scaleAssignment[key];
|
||||
export const getScaleType = (key: string): string[][] => scaleTypeAssignment[key];
|
||||
|
||||
|
||||
@@ -5,37 +5,35 @@ import {AngularFirestore} from '@angular/fire/firestore';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('SongDataService', () => {
|
||||
|
||||
const songs = [
|
||||
{title: 'title1'}
|
||||
];
|
||||
const songs = [{title: 'title1'}];
|
||||
|
||||
const angularFirestoreCollection = {
|
||||
valueChanges: () => of(songs)
|
||||
valueChanges: () => of(songs),
|
||||
};
|
||||
|
||||
const mockAngularFirestore = {
|
||||
collection: () => angularFirestoreCollection
|
||||
collection: () => angularFirestoreCollection,
|
||||
};
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: AngularFirestore, useValue: mockAngularFirestore}
|
||||
]
|
||||
}));
|
||||
beforeEach(
|
||||
() =>
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [{provide: AngularFirestore, useValue: mockAngularFirestore}],
|
||||
})
|
||||
);
|
||||
|
||||
it('should be created', () => {
|
||||
const service: SongDataService = TestBed.get(SongDataService);
|
||||
expect(service).toBeTruthy();
|
||||
const service: SongDataService = TestBed.inject(SongDataService);
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should list songs', waitForAsync(() => {
|
||||
const service: SongDataService = TestBed.get(SongDataService);
|
||||
it(
|
||||
'should list songs',
|
||||
waitForAsync(() => {
|
||||
const service: SongDataService = TestBed.inject(SongDataService);
|
||||
service.list$().subscribe(s => {
|
||||
expect(s).toEqual([
|
||||
{title: 'title1'}
|
||||
] as any);
|
||||
}
|
||||
void expect(s[0].title).toEqual('title1');
|
||||
});
|
||||
})
|
||||
);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -4,19 +4,16 @@ import {Observable} from 'rxjs';
|
||||
import {DbService} from '../../../services/db.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SongDataService {
|
||||
private collection = 'songs';
|
||||
|
||||
constructor(private dbService: DbService) {
|
||||
}
|
||||
public constructor(private dbService: DbService) {}
|
||||
|
||||
public list$ = (): Observable<Song[]> => this.dbService.col$(this.collection);
|
||||
public read$ = (songId: string): Observable<Song | undefined> => this.dbService.doc$(this.collection + '/' + songId);
|
||||
public update$ = async (songId: string, data: Partial<Song>): Promise<void> => await this.dbService.doc(this.collection + '/' + songId).update(data);
|
||||
public add = async (data: Partial<Song>): Promise<string> => (await this.dbService.col(this.collection).add(data)).id;
|
||||
public delete = async (songId: string): Promise<void> => await this.dbService.doc(this.collection + '/' + songId).delete();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,32 +5,31 @@ import {SongDataService} from './song-data.service';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('SongService', () => {
|
||||
|
||||
const songs = [
|
||||
{title: 'title1'}
|
||||
];
|
||||
const songs = [{title: 'title1'}];
|
||||
|
||||
const mockSongDataService = {
|
||||
list: () => of(songs)
|
||||
list: () => of(songs),
|
||||
};
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: SongDataService, useValue: mockSongDataService}
|
||||
]
|
||||
}));
|
||||
beforeEach(
|
||||
() =>
|
||||
void TestBed.configureTestingModule({
|
||||
providers: [{provide: SongDataService, useValue: mockSongDataService}],
|
||||
})
|
||||
);
|
||||
|
||||
it('should be created', () => {
|
||||
const service: SongService = TestBed.get(SongService);
|
||||
expect(service).toBeTruthy();
|
||||
const service: SongService = TestBed.inject(SongService);
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should list songs', waitForAsync(() => {
|
||||
const service: SongService = TestBed.get(SongService);
|
||||
it(
|
||||
'should list songs',
|
||||
waitForAsync(() => {
|
||||
const service: SongService = TestBed.inject(SongService);
|
||||
service.list$().subscribe(s => {
|
||||
expect(s).toEqual([
|
||||
{title: 'title1'}
|
||||
] as any);
|
||||
void expect(s[0].title).toEqual('title1');
|
||||
});
|
||||
}));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,31 +2,30 @@ import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Song} from './song';
|
||||
import {SongDataService} from './song-data.service';
|
||||
import {first, tap} from 'rxjs/operators';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {UserService} from '../../../services/user/user.service';
|
||||
import * as firebase from 'firebase';
|
||||
import Timestamp = firebase.firestore.Timestamp;
|
||||
|
||||
declare var importCCLI: any;
|
||||
// declare let importCCLI: any;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SongService {
|
||||
|
||||
public static TYPES = ['Praise', 'Worship'];
|
||||
public static STATUS = ['draft', 'set', 'final'];
|
||||
|
||||
public static LEGAL_OWNER = ['CCLI', 'other'];
|
||||
public static LEGAL_TYPE = ['open', 'allowed'];
|
||||
|
||||
private list: Song[];
|
||||
// private list: Song[];
|
||||
|
||||
constructor(private songDataService: SongDataService, private userService: UserService) {
|
||||
importCCLI = (songs: Song[]) => this.updateFromCLI(songs);
|
||||
public constructor(private songDataService: SongDataService, private userService: UserService) {
|
||||
// importCCLI = (songs: Song[]) => this.updateFromCLI(songs);
|
||||
}
|
||||
|
||||
public list$ = (): Observable<Song[]> => this.songDataService.list$().pipe(tap(_ => this.list = _));
|
||||
public list$ = (): Observable<Song[]> => this.songDataService.list$(); //.pipe(tap(_ => (this.list = _)));
|
||||
public read$ = (songId: string): Observable<Song | undefined> => this.songDataService.read$(songId);
|
||||
public read = (songId: string): Promise<Song | undefined> => this.read$(songId).pipe(first()).toPromise();
|
||||
|
||||
@@ -38,38 +37,41 @@ export class SongService {
|
||||
await this.songDataService.update$(songId, {...data, edits});
|
||||
}
|
||||
|
||||
public async new(number: number, title: string): Promise<string> {
|
||||
return await this.songDataService.add({number, title, status: 'draft', legalType: 'open'});
|
||||
public async new(songNumber: number, title: string): Promise<string> {
|
||||
return await this.songDataService.add({
|
||||
number: songNumber,
|
||||
title,
|
||||
status: 'draft',
|
||||
legalType: 'open',
|
||||
});
|
||||
}
|
||||
|
||||
public async delete(songId: string): Promise<void> {
|
||||
await this.songDataService.delete(songId);
|
||||
}
|
||||
|
||||
// https://www.csvjson.com/csv2json
|
||||
private async updateFromCLI(songs: Song[]) {
|
||||
const mapped = songs.map(_ => ({
|
||||
number: _.number,
|
||||
legalType: _.legalType === 'ja' ? 'allowed' : 'open',
|
||||
legalOwner: _.legalOwner === 'ja' ? 'CCLI' : 'other',
|
||||
title: _.title,
|
||||
legalOwnerId: _.legalOwnerId,
|
||||
origin: _.origin,
|
||||
artist: _.artist,
|
||||
comment: _.comment
|
||||
}));
|
||||
const promises = this.list.map(async _ => {
|
||||
// tslint:disable-next-line:triple-equals
|
||||
const mappedSongs = mapped.filter(f => f.number == _.number);
|
||||
if (mappedSongs.length === 1) {
|
||||
const mappedSong = mappedSongs[0];
|
||||
const id = _.id;
|
||||
return await this.update$(id, mappedSong);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
|
||||
// https://www.csvjson.com/csv2json
|
||||
// private async updateFromCLI(songs: Song[]) {
|
||||
// const mapped = songs.map(_ => ({
|
||||
// number: _.number,
|
||||
// legalType: _.legalType === 'ja' ? 'allowed' : 'open',
|
||||
// legalOwner: _.legalOwner === 'ja' ? 'CCLI' : 'other',
|
||||
// title: _.title,
|
||||
// legalOwnerId: _.legalOwnerId,
|
||||
// origin: _.origin,
|
||||
// artist: _.artist,
|
||||
// comment: _.comment,
|
||||
// }));
|
||||
// const promises = this.list.map(async _ => {
|
||||
// // eslint-disable-next-line eqeqeq
|
||||
// const mappedSongs = mapped.filter(f => f.number == _.number);
|
||||
// if (mappedSongs.length === 1) {
|
||||
// const mappedSong = mappedSongs[0];
|
||||
// const id = _.id;
|
||||
// return await this.update$(id, mappedSong);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// await Promise.all(promises);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {LineType, SectionType, TextRenderingService} from './text-rendering.service';
|
||||
import {TextRenderingService} from './text-rendering.service';
|
||||
import {LineType} from './line-type';
|
||||
import {SectionType} from './section-type';
|
||||
|
||||
describe('TextRenderingService', () => {
|
||||
const testText = `Strophe
|
||||
@@ -23,60 +24,59 @@ Bridge
|
||||
Cool bridge without any chords
|
||||
`;
|
||||
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
beforeEach(() => void TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: TextRenderingService = TestBed.get(TextRenderingService);
|
||||
expect(service).toBeTruthy();
|
||||
const service: TextRenderingService = TestBed.inject(TextRenderingService);
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should parse section types', () => {
|
||||
const service: TextRenderingService = TestBed.get(TextRenderingService);
|
||||
const service: TextRenderingService = TestBed.inject(TextRenderingService);
|
||||
const sections = service.parse(testText, null);
|
||||
expect(sections[0].type).toBe(SectionType.Verse);
|
||||
expect(sections[0].number).toBe(0);
|
||||
expect(sections[1].type).toBe(SectionType.Verse);
|
||||
expect(sections[1].number).toBe(1);
|
||||
expect(sections[2].type).toBe(SectionType.Chorus);
|
||||
expect(sections[2].number).toBe(0);
|
||||
expect(sections[3].type).toBe(SectionType.Bridge);
|
||||
expect(sections[3].number).toBe(0);
|
||||
void expect(sections[0].type).toBe(SectionType.Verse);
|
||||
void expect(sections[0].number).toBe(0);
|
||||
void expect(sections[1].type).toBe(SectionType.Verse);
|
||||
void expect(sections[1].number).toBe(1);
|
||||
void expect(sections[2].type).toBe(SectionType.Chorus);
|
||||
void expect(sections[2].number).toBe(0);
|
||||
void expect(sections[3].type).toBe(SectionType.Bridge);
|
||||
void expect(sections[3].number).toBe(0);
|
||||
});
|
||||
|
||||
it('should parse text lines', () => {
|
||||
const service: TextRenderingService = TestBed.get(TextRenderingService);
|
||||
const service: TextRenderingService = TestBed.inject(TextRenderingService);
|
||||
const sections = service.parse(testText, null);
|
||||
expect(sections[0].lines[1].type).toBe(LineType.text);
|
||||
expect(sections[0].lines[1].text).toBe('Text Line 1-1');
|
||||
expect(sections[0].lines[3].type).toBe(LineType.text);
|
||||
expect(sections[0].lines[3].text).toBe('Text Line 2-1');
|
||||
expect(sections[1].lines[1].type).toBe(LineType.text);
|
||||
expect(sections[1].lines[1].text).toBe('Text Line 1-2');
|
||||
expect(sections[1].lines[3].type).toBe(LineType.text);
|
||||
expect(sections[1].lines[3].text).toBe('Text Line 2-2');
|
||||
expect(sections[2].lines[1].type).toBe(LineType.text);
|
||||
expect(sections[2].lines[1].text).toBe('and the chorus');
|
||||
expect(sections[3].lines[0].type).toBe(LineType.text);
|
||||
expect(sections[3].lines[0].text).toBe('Cool bridge without any chords');
|
||||
void expect(sections[0].lines[1].type).toBe(LineType.text);
|
||||
void expect(sections[0].lines[1].text).toBe('Text Line 1-1');
|
||||
void expect(sections[0].lines[3].type).toBe(LineType.text);
|
||||
void expect(sections[0].lines[3].text).toBe('Text Line 2-1');
|
||||
void expect(sections[1].lines[1].type).toBe(LineType.text);
|
||||
void expect(sections[1].lines[1].text).toBe('Text Line 1-2');
|
||||
void expect(sections[1].lines[3].type).toBe(LineType.text);
|
||||
void expect(sections[1].lines[3].text).toBe('Text Line 2-2');
|
||||
void expect(sections[2].lines[1].type).toBe(LineType.text);
|
||||
void expect(sections[2].lines[1].text).toBe('and the chorus');
|
||||
void expect(sections[3].lines[0].type).toBe(LineType.text);
|
||||
void expect(sections[3].lines[0].text).toBe('Cool bridge without any chords');
|
||||
});
|
||||
|
||||
it('should parse chord lines', () => {
|
||||
const service: TextRenderingService = TestBed.inject(TextRenderingService);
|
||||
const sections = service.parse(testText, null);
|
||||
expect(sections[0].lines[0].type).toBe(LineType.chord);
|
||||
expect(sections[0].lines[0].text).toBe('C D E F G A H');
|
||||
expect(sections[0].lines[2].type).toBe(LineType.chord);
|
||||
expect(sections[0].lines[2].text).toBe(' a d e f g a h c b');
|
||||
expect(sections[1].lines[0].type).toBe(LineType.chord);
|
||||
expect(sections[1].lines[0].text).toBe('C D E F G A H');
|
||||
expect(sections[1].lines[2].type).toBe(LineType.chord);
|
||||
expect(sections[1].lines[2].text).toBe(' a d e f g a h c b');
|
||||
expect(sections[2].lines[0].type).toBe(LineType.chord);
|
||||
expect(sections[2].lines[0].text).toBe('c c# db c7 cmaj7 c/e');
|
||||
void expect(sections[0].lines[0].type).toBe(LineType.chord);
|
||||
void expect(sections[0].lines[0].text).toBe('C D E F G A H');
|
||||
void expect(sections[0].lines[2].type).toBe(LineType.chord);
|
||||
void expect(sections[0].lines[2].text).toBe(' a d e f g a h c b');
|
||||
void expect(sections[1].lines[0].type).toBe(LineType.chord);
|
||||
void expect(sections[1].lines[0].text).toBe('C D E F G A H');
|
||||
void expect(sections[1].lines[2].type).toBe(LineType.chord);
|
||||
void expect(sections[1].lines[2].text).toBe(' a d e f g a h c b');
|
||||
void expect(sections[2].lines[0].type).toBe(LineType.chord);
|
||||
void expect(sections[2].lines[0].text).toBe('c c# db c7 cmaj7 c/e');
|
||||
|
||||
// c c# db c7 cmaj7 c/e
|
||||
expect(sections[2].lines[0].chords).toEqual([
|
||||
void expect(sections[2].lines[0].chords).toEqual([
|
||||
{chord: 'c', length: 1, position: 0},
|
||||
{chord: 'c#', length: 2, position: 2},
|
||||
{chord: 'db', length: 2, position: 5},
|
||||
@@ -92,10 +92,9 @@ Cool bridge without any chords
|
||||
g# F# E g# F# E
|
||||
text`;
|
||||
const sections = service.parse(text, null);
|
||||
expect(sections[0].lines[0].type).toBe(LineType.chord);
|
||||
expect(sections[0].lines[0].text).toBe('g# F# E g# F# E');
|
||||
expect(sections[0].lines[1].type).toBe(LineType.text);
|
||||
expect(sections[0].lines[1].text).toBe('text');
|
||||
|
||||
void expect(sections[0].lines[0].type).toBe(LineType.chord);
|
||||
void expect(sections[0].lines[0].text).toBe('g# F# E g# F# E');
|
||||
void expect(sections[0].lines[1].type).toBe(LineType.text);
|
||||
void expect(sections[0].lines[1].text).toBe('text');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,13 +8,12 @@ import {Chord} from './chord';
|
||||
import {Line} from './line';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TextRenderingService {
|
||||
private regexSection = /(Strophe|Refrain|Bridge)/;
|
||||
|
||||
constructor(private transposeService: TransposeService) {
|
||||
}
|
||||
public constructor(private transposeService: TransposeService) {}
|
||||
|
||||
public parse(text: string, transpose: TransposeMode): Section[] {
|
||||
if (!text) {
|
||||
@@ -28,12 +27,15 @@ export class TextRenderingService {
|
||||
};
|
||||
return arrayOfLines.reduce((array, line) => {
|
||||
const type = this.getSectionTypeOfLine(line);
|
||||
if (line.match(this.regexSection)) {
|
||||
return [...array, {
|
||||
if (this.regexSection.exec(line)) {
|
||||
return [
|
||||
...array,
|
||||
{
|
||||
type,
|
||||
number: indices[type]++,
|
||||
lines: []
|
||||
}];
|
||||
lines: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
array[array.length - 1].lines.push(this.getLineOfLineText(line, transpose));
|
||||
return array;
|
||||
@@ -49,16 +51,14 @@ export class TextRenderingService {
|
||||
const type = hasMatches ? LineType.chord : LineType.text;
|
||||
|
||||
const line = {type, text, chords: hasMatches ? cords : undefined};
|
||||
return transpose
|
||||
? this.transposeService.transpose(line, transpose.baseKey, transpose.targetKey)
|
||||
: this.transposeService.renderChords(line);
|
||||
return transpose ? this.transposeService.transpose(line, transpose.baseKey, transpose.targetKey) : this.transposeService.renderChords(line);
|
||||
}
|
||||
|
||||
private getSectionTypeOfLine(line: string): SectionType {
|
||||
if (!line) {
|
||||
return null;
|
||||
}
|
||||
const match = line.match(this.regexSection);
|
||||
const match = this.regexSection.exec(line);
|
||||
if (!match || match.length < 2) {
|
||||
return null;
|
||||
}
|
||||
@@ -74,11 +74,12 @@ export class TextRenderingService {
|
||||
}
|
||||
|
||||
private readChords(chordLine: string): Chord[] {
|
||||
let match;
|
||||
let match: string[];
|
||||
const chords: Chord[] = [];
|
||||
|
||||
// https://regex101.com/r/68jMB8/5
|
||||
const regex = /(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h)(\/(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h))?(\d+|maj7)?/mg;
|
||||
const regex =
|
||||
/(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h)(\/(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h))?(\d+|maj7)?/gm;
|
||||
|
||||
while ((match = regex.exec(chordLine)) !== null) {
|
||||
const chord: Chord = {
|
||||
@@ -101,5 +102,4 @@ export class TextRenderingService {
|
||||
const isChrod = chordCount * 1.2 > lineCount;
|
||||
return isChrod ? chords : [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('TransposeService', () => {
|
||||
let service: TransposeService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(TransposeService);
|
||||
});
|
||||
|
||||
@@ -15,6 +15,6 @@ describe('TransposeService', () => {
|
||||
const map = service.getMap('D', distance);
|
||||
|
||||
console.log(map);
|
||||
expect(service).toBeTruthy();
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,11 +4,12 @@ import {LineType} from './line-type';
|
||||
import {Chord} from './chord';
|
||||
import {Line} from './line';
|
||||
|
||||
type TransposeMap = {[key: string]: string};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TransposeService {
|
||||
|
||||
public transpose(line: Line, baseKey: string, targetKey: string): Line {
|
||||
if (line.type !== LineType.chord) {
|
||||
return line;
|
||||
@@ -33,13 +34,10 @@ export class TransposeService {
|
||||
|
||||
public getDistance(baseKey: string, targetKey: string): number {
|
||||
const scale = getScaleType(baseKey);
|
||||
return scale ? (
|
||||
(scale[0].indexOf(targetKey) - scale[0].indexOf(baseKey)) ??
|
||||
(scale[1].indexOf(targetKey) - scale[1].indexOf(baseKey))
|
||||
) % 12 : 0;
|
||||
return scale ? (scale[0].indexOf(targetKey) - scale[0].indexOf(baseKey) ?? scale[1].indexOf(targetKey) - scale[1].indexOf(baseKey)) % 12 : 0;
|
||||
}
|
||||
|
||||
public getMap(baseKey: string, difference: number) {
|
||||
public getMap(baseKey: string, difference: number): TransposeMap | null {
|
||||
const scale = getScaleType(baseKey);
|
||||
if (!scale) {
|
||||
return null;
|
||||
@@ -59,10 +57,14 @@ export class TransposeService {
|
||||
return map;
|
||||
}
|
||||
|
||||
private transposeChord(chord: Chord, map: {}): Chord {
|
||||
private transposeChord(chord: Chord, map: TransposeMap): Chord {
|
||||
const translatedChord = map[chord.chord];
|
||||
const translatedSlashChord = chord.slashChord ? map[chord.slashChord] : null;
|
||||
return {...chord, chord: translatedChord, slashChord: translatedSlashChord};
|
||||
return {
|
||||
...chord,
|
||||
chord: translatedChord,
|
||||
slashChord: translatedSlashChord,
|
||||
};
|
||||
}
|
||||
|
||||
private renderLine(chords: Chord[]): string {
|
||||
@@ -83,9 +85,6 @@ export class TransposeService {
|
||||
}
|
||||
|
||||
private renderChord(chord: Chord) {
|
||||
return (
|
||||
scaleMapping[chord.chord] +
|
||||
(chord.add ? chord.add : '') +
|
||||
(chord.slashChord ? '/' + scaleMapping[chord.slashChord] : ''));
|
||||
return scaleMapping[chord.chord] + (chord.add ? chord.add : '') + (chord.slashChord ? '/' + scaleMapping[chord.slashChord] : '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ import {TestBed} from '@angular/core/testing';
|
||||
import {UploadService} from './upload.service';
|
||||
|
||||
describe('UploadServiceService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
beforeEach(() => void TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: UploadService = TestBed.get(UploadService);
|
||||
expect(service).toBeTruthy();
|
||||
const service: UploadService = TestBed.inject(UploadService);
|
||||
void expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,17 +6,15 @@ import {finalize} from 'rxjs/operators';
|
||||
import {FileBase} from './fileBase';
|
||||
import {FileServer} from './fileServer';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UploadService extends FileBase {
|
||||
|
||||
constructor(private fileDataService: FileDataService, private angularFireStorage: AngularFireStorage) {
|
||||
public constructor(private fileDataService: FileDataService, private angularFireStorage: AngularFireStorage) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async pushUpload(songId: string, upload: Upload) {
|
||||
public pushUpload(songId: string, upload: Upload): void {
|
||||
const directory = this.directory(songId);
|
||||
const filePath = `${directory}/${upload.file.name}`;
|
||||
upload.path = directory;
|
||||
@@ -24,20 +22,18 @@ export class UploadService extends FileBase {
|
||||
const ref = this.angularFireStorage.ref(filePath);
|
||||
const task = ref.put(upload.file);
|
||||
|
||||
task.percentageChanges().subscribe(percent => upload.progress = percent);
|
||||
task.snapshotChanges().pipe(
|
||||
finalize(() => {
|
||||
this.saveFileData(songId, upload);
|
||||
})
|
||||
).subscribe();
|
||||
|
||||
task.percentageChanges().subscribe(percent => (upload.progress = percent));
|
||||
task
|
||||
.snapshotChanges()
|
||||
.pipe(finalize(() => void this.saveFileData(songId, upload)))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private async saveFileData(songId: string, upload: Upload) {
|
||||
const file: FileServer = {
|
||||
name: upload.file.name,
|
||||
path: upload.path,
|
||||
createdAt: new Date()
|
||||
createdAt: new Date(),
|
||||
};
|
||||
await this.fileDataService.set(songId, file);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
export class Upload {
|
||||
public $key: string;
|
||||
public file: File;
|
||||
public name: string;
|
||||
public path: string;
|
||||
public progress: number;
|
||||
public createdAt: Date = new Date();
|
||||
|
||||
$key: string;
|
||||
file: Upload;
|
||||
name: string;
|
||||
path: string;
|
||||
progress: number;
|
||||
createdAt: Date = new Date();
|
||||
|
||||
constructor(file: Upload) {
|
||||
public constructor(file: File) {
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<div [formGroup]="filterFormGroup">
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Titel oder Text</mat-label>
|
||||
<input formControlName="q" matInput>
|
||||
<input formControlName="q" matInput/>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="third">
|
||||
@@ -10,16 +9,19 @@
|
||||
<mat-label>Typ</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let type of types" [value]="type">{{type | songType}}</mat-option>
|
||||
<mat-option *ngFor="let type of types" [value]="type">{{
|
||||
type | songType
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Tonart</mat-label>
|
||||
<mat-select formControlName="key">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let key of keys" [value]="key">{{key|key}}</mat-option>
|
||||
<mat-option *ngFor="let key of keys" [value]="key">{{
|
||||
key | key
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -27,7 +29,9 @@
|
||||
<mat-label>Rechtlicher Status</mat-label>
|
||||
<mat-select formControlName="legalType">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
|
||||
<mat-option *ngFor="let key of legalType" [value]="key">{{
|
||||
key | legalType
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@@ -35,10 +39,12 @@
|
||||
<mat-label>Attribute</mat-label>
|
||||
<mat-select formControlName="flag">
|
||||
<mat-option [value]="null">- kein Filter -</mat-option>
|
||||
<mat-option *ngFor="let flag of getFlags()" [value]="flag">{{flag}}</mat-option>
|
||||
<mat-option *ngFor="let flag of getFlags()" [value]="flag">{{
|
||||
flag
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<i>Anzahl der Suchergebnisse: {{songs.length}}</i>
|
||||
<i>Anzahl der Suchergebnisse: {{ songs.length }}</i>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('FilterComponent', () => {
|
||||
let component: FilterComponent;
|
||||
let fixture: ComponentFixture<FilterComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FilterComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [FilterComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FilterComponent);
|
||||
@@ -20,6 +21,6 @@ describe('FilterComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {FormBuilder, FormGroup} from '@angular/forms';
|
||||
import {SongService} from '../../services/song.service';
|
||||
@@ -9,18 +9,17 @@ import {KEYS} from '../../services/key.helper';
|
||||
@Component({
|
||||
selector: 'app-filter',
|
||||
templateUrl: './filter.component.html',
|
||||
styleUrls: ['./filter.component.less']
|
||||
styleUrls: ['./filter.component.less'],
|
||||
})
|
||||
export class FilterComponent implements OnInit {
|
||||
|
||||
export class FilterComponent {
|
||||
public filterFormGroup: FormGroup;
|
||||
@Input() route: string;
|
||||
@Input() songs: Song[];
|
||||
@Input() public route: string;
|
||||
@Input() public songs: Song[];
|
||||
public types = SongService.TYPES;
|
||||
public legalType = SongService.LEGAL_TYPE;
|
||||
public keys = KEYS;
|
||||
|
||||
constructor(private router: Router, activatedRoute: ActivatedRoute, fb: FormBuilder) {
|
||||
public constructor(private router: Router, activatedRoute: ActivatedRoute, fb: FormBuilder) {
|
||||
this.filterFormGroup = fb.group({
|
||||
q: '',
|
||||
type: '',
|
||||
@@ -30,32 +29,18 @@ export class FilterComponent implements OnInit {
|
||||
});
|
||||
|
||||
activatedRoute.queryParams.subscribe((filterValues: FilterValues) => {
|
||||
if (filterValues.q) {
|
||||
this.filterFormGroup.controls.q.setValue(filterValues.q);
|
||||
}
|
||||
if (filterValues.type) {
|
||||
this.filterFormGroup.controls.type.setValue(filterValues.type);
|
||||
}
|
||||
if (filterValues.key) {
|
||||
this.filterFormGroup.controls.key.setValue(filterValues.key);
|
||||
}
|
||||
if (filterValues.legalType) {
|
||||
this.filterFormGroup.controls.legalType.setValue(filterValues.legalType);
|
||||
}
|
||||
if (filterValues.flag) {
|
||||
this.filterFormGroup.controls.flag.setValue(filterValues.flag);
|
||||
}
|
||||
if (filterValues.q) this.filterFormGroup.controls.q.setValue(filterValues.q);
|
||||
if (filterValues.type) this.filterFormGroup.controls.type.setValue(filterValues.type);
|
||||
if (filterValues.key) this.filterFormGroup.controls.key.setValue(filterValues.key);
|
||||
if (filterValues.legalType) this.filterFormGroup.controls.legalType.setValue(filterValues.legalType);
|
||||
if (filterValues.flag) this.filterFormGroup.controls.flag.setValue(filterValues.flag);
|
||||
});
|
||||
|
||||
this.filterFormGroup.controls.q.valueChanges.subscribe(_ => this.filerValueChanged('q', _));
|
||||
this.filterFormGroup.controls.key.valueChanges.subscribe(_ => this.filerValueChanged('key', _));
|
||||
this.filterFormGroup.controls.type.valueChanges.subscribe(_ => this.filerValueChanged('type', _));
|
||||
this.filterFormGroup.controls.legalType.valueChanges.subscribe(_ => this.filerValueChanged('legalType', _));
|
||||
this.filterFormGroup.controls.flag.valueChanges.subscribe(_ => this.filerValueChanged('flag', _));
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.filterFormGroup.controls.q.valueChanges.subscribe(_ => void this.filerValueChanged('q', _));
|
||||
this.filterFormGroup.controls.key.valueChanges.subscribe(_ => void this.filerValueChanged('key', _));
|
||||
this.filterFormGroup.controls.type.valueChanges.subscribe(_ => void this.filerValueChanged('type', _));
|
||||
this.filterFormGroup.controls.legalType.valueChanges.subscribe(_ => void this.filerValueChanged('legalType', _));
|
||||
this.filterFormGroup.controls.flag.valueChanges.subscribe(_ => void this.filerValueChanged('flag', _));
|
||||
}
|
||||
|
||||
public getFlags(): string[] {
|
||||
@@ -66,13 +51,14 @@ export class FilterComponent implements OnInit {
|
||||
.reduce((pn, u) => [...pn, ...u], [])
|
||||
.filter(_ => !!_);
|
||||
|
||||
const uqFlags = flags.filter((n, i) => flags.indexOf(n) === i);
|
||||
|
||||
return uqFlags;
|
||||
return flags.filter((n, i) => flags.indexOf(n) === i);
|
||||
}
|
||||
|
||||
private async filerValueChanged(key: string, value: string): Promise<void> {
|
||||
const route = this.router.createUrlTree([this.route], {queryParams: {[key]: value}, queryParamsHandling: 'merge'});
|
||||
const route = this.router.createUrlTree([this.route], {
|
||||
queryParams: {[key]: value},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
await this.router.navigateByUrl(route);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
<div class="list-item">
|
||||
<div class="number">{{song.number}}</div>
|
||||
<div>{{song.title}}</div>
|
||||
<div class="number">{{ song.number }}</div>
|
||||
<div>{{ song.title }}</div>
|
||||
<div>
|
||||
<ng-container *appRole="['contributor']">
|
||||
<span *ngIf="song.status==='draft' || !song.status" class="warning" matTooltip="Entwurf"
|
||||
matTooltipPosition="before">
|
||||
<span
|
||||
*ngIf="song.status === 'draft' || !song.status"
|
||||
class="warning"
|
||||
matTooltip="Entwurf"
|
||||
matTooltipPosition="before"
|
||||
>
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</span>
|
||||
<span *ngIf="song.status==='set'" class="neutral" matTooltip="Entwurf" matTooltipPosition="before">
|
||||
<span
|
||||
*ngIf="song.status === 'set'"
|
||||
class="neutral"
|
||||
matTooltip="Entwurf"
|
||||
matTooltipPosition="before"
|
||||
>
|
||||
<fa-icon [icon]="faDraft"></fa-icon>
|
||||
</span>
|
||||
<span *ngIf="song.status==='final'" class="success" matTooltip="Final" matTooltipPosition="before">
|
||||
<span
|
||||
*ngIf="song.status === 'final'"
|
||||
class="success"
|
||||
matTooltip="Final"
|
||||
matTooltipPosition="before"
|
||||
>
|
||||
<fa-icon [icon]="faFinal"></fa-icon>
|
||||
</span>
|
||||
</ng-container>
|
||||
<span *ngIf="song.legalType==='open'" class="warning" matTooltip="rechtlicher Status ist ungeklärt"
|
||||
matTooltipPosition="before"><fa-icon [icon]="faLegal"></fa-icon> </span>
|
||||
<span
|
||||
*ngIf="song.legalType === 'open'"
|
||||
class="warning"
|
||||
matTooltip="rechtlicher Status ist ungeklärt"
|
||||
matTooltipPosition="before"
|
||||
><fa-icon [icon]="faLegal"></fa-icon> </span
|
||||
>
|
||||
</div>
|
||||
<div>{{song.key}}</div>
|
||||
|
||||
<div>{{ song.key }}</div>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('ListItemComponent', () => {
|
||||
let component: ListItemComponent;
|
||||
let fixture: ComponentFixture<ListItemComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ListItemComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [ListItemComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListItemComponent);
|
||||
@@ -20,6 +21,6 @@ describe('ListItemComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {Song} from '../../services/song';
|
||||
import {faBalanceScaleRight} from '@fortawesome/free-solid-svg-icons/faBalanceScaleRight';
|
||||
import {faPencilRuler} from '@fortawesome/free-solid-svg-icons/faPencilRuler';
|
||||
@@ -7,18 +7,11 @@ import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
|
||||
@Component({
|
||||
selector: 'app-list-item',
|
||||
templateUrl: './list-item.component.html',
|
||||
styleUrls: ['./list-item.component.less']
|
||||
styleUrls: ['./list-item.component.less'],
|
||||
})
|
||||
export class ListItemComponent implements OnInit {
|
||||
export class ListItemComponent {
|
||||
@Input() public song: Song;
|
||||
public faLegal = faBalanceScaleRight;
|
||||
public faDraft = faPencilRuler;
|
||||
public faFinal = faCheck;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
</app-list-header>
|
||||
|
||||
<app-card [padding]="false">
|
||||
<app-list-item *ngFor="let song of songs" [routerLink]="song.id" [song]="song"></app-list-item>
|
||||
<app-list-item
|
||||
*ngFor="let song of songs"
|
||||
[routerLink]="song.id"
|
||||
[song]="song"
|
||||
></app-list-item>
|
||||
</app-card>
|
||||
</div>
|
||||
|
||||
@@ -9,24 +9,21 @@ describe('SongListComponent', () => {
|
||||
let component: SongListComponent;
|
||||
let fixture: ComponentFixture<SongListComponent>;
|
||||
|
||||
const songs = [
|
||||
{title: 'title1'}
|
||||
];
|
||||
const songs = [{title: 'title1'}];
|
||||
|
||||
const mockSongService = {
|
||||
list: () => of(songs)
|
||||
list: () => of(songs),
|
||||
};
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [SongListComponent],
|
||||
providers: [
|
||||
{provide: SongService, useValue: mockSongService}
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
providers: [{provide: SongService, useValue: mockSongService}],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SongListComponent);
|
||||
@@ -35,13 +32,11 @@ describe('SongListComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should read songs from SongService', fakeAsync(() => {
|
||||
tick();
|
||||
expect(component.songs$).toEqual([
|
||||
{title: 'title1'}
|
||||
] as any);
|
||||
void expect(component.songs$).toEqual([{title: 'title1'}]);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -13,28 +13,21 @@ import {ScrollService} from '../../../services/scroll.service';
|
||||
selector: 'app-songs',
|
||||
templateUrl: './song-list.component.html',
|
||||
styleUrls: ['./song-list.component.less'],
|
||||
animations: [fade]
|
||||
animations: [fade],
|
||||
})
|
||||
export class SongListComponent implements OnInit, OnDestroy {
|
||||
|
||||
public songs$: Observable<Song[]>;
|
||||
public anyFilterActive = false;
|
||||
|
||||
constructor(
|
||||
private songService: SongService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private scrollService: ScrollService) {
|
||||
}
|
||||
public constructor(private songService: SongService, private activatedRoute: ActivatedRoute, private scrollService: ScrollService) {}
|
||||
|
||||
ngOnInit() {
|
||||
public ngOnInit(): void {
|
||||
const filter$ = this.activatedRoute.queryParams.pipe(
|
||||
debounceTime(300),
|
||||
map(_ => _ as FilterValues)
|
||||
);
|
||||
|
||||
const songs$ = this.songService.list$().pipe(
|
||||
map(songs => songs.sort((a, b) => a.number - b.number)),
|
||||
);
|
||||
const songs$ = this.songService.list$().pipe(map(songs => songs.sort((a, b) => a.number - b.number)));
|
||||
|
||||
this.songs$ = combineLatest([filter$, songs$]).pipe(
|
||||
map(_ => {
|
||||
|
||||
@@ -17,7 +17,6 @@ import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {RoleModule} from '../../../services/user/role.module';
|
||||
import {KeyTranslatorModule} from '../../../widget-modules/pipes/key-translator/key-translator.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [SongListComponent, ListItemComponent, FilterComponent],
|
||||
exports: [SongListComponent],
|
||||
@@ -37,7 +36,6 @@ import {KeyTranslatorModule} from '../../../widget-modules/pipes/key-translator/
|
||||
MatTooltipModule,
|
||||
RoleModule,
|
||||
KeyTranslatorModule,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class SongListModule {
|
||||
}
|
||||
export class SongListModule {}
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
<app-card heading="Angehängte Dateien">
|
||||
|
||||
|
||||
<div *ngIf="currentUpload">
|
||||
<div class="progress">
|
||||
<div [ngStyle]="{ 'width': currentUpload?.progress + '%' }" class="progress-bar progress-bar-animated"></div>
|
||||
<div
|
||||
[ngStyle]="{ width: currentUpload?.progress + '%' }"
|
||||
class="progress-bar progress-bar-animated"
|
||||
></div>
|
||||
</div>
|
||||
Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
|
||||
Progress: {{ currentUpload?.name }} | {{ currentUpload?.progress }}%
|
||||
Complete
|
||||
</div>
|
||||
<div class="upload">
|
||||
<label>
|
||||
<input (change)="detectFiles($event)" type="file">
|
||||
<input (change)="detectFiles($event)" type="file"/>
|
||||
</label>
|
||||
|
||||
<button (click)="uploadSingle()"
|
||||
<button
|
||||
(click)="uploadSingle()"
|
||||
[disabled]="!selectedFiles"
|
||||
mat-icon-button>
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p *ngFor="let file of (files$|async)">
|
||||
<p *ngFor="let file of files$ | async">
|
||||
<app-file [file]="file" [songId]="songId"></app-file>
|
||||
</p>
|
||||
</app-card>
|
||||
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('EditFileComponent', () => {
|
||||
let component: EditFileComponent;
|
||||
let fixture: ComponentFixture<EditFileComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EditFileComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [EditFileComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditFileComponent);
|
||||
@@ -20,6 +21,6 @@ describe('EditFileComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,41 +10,33 @@ import {File} from '../../../services/file';
|
||||
@Component({
|
||||
selector: 'app-edit-file',
|
||||
templateUrl: './edit-file.component.html',
|
||||
styleUrls: ['./edit-file.component.less']
|
||||
styleUrls: ['./edit-file.component.less'],
|
||||
})
|
||||
export class EditFileComponent {
|
||||
|
||||
public selectedFiles: FileList;
|
||||
public currentUpload: Upload;
|
||||
public songId: string;
|
||||
public files$: Observable<File[]>;
|
||||
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private uploadService: UploadService,
|
||||
private fileService: FileDataService,
|
||||
) {
|
||||
this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
).subscribe(songId => {
|
||||
public constructor(private activatedRoute: ActivatedRoute, private uploadService: UploadService, private fileService: FileDataService) {
|
||||
this.activatedRoute.params.pipe(map((param: {songId: string}) => param.songId)).subscribe(songId => {
|
||||
this.songId = songId;
|
||||
});
|
||||
|
||||
this.files$ = this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
map((param: {songId: string}) => param.songId),
|
||||
switchMap(songId => this.fileService.read$(songId))
|
||||
);
|
||||
}
|
||||
|
||||
detectFiles(event) {
|
||||
this.selectedFiles = event.target.files;
|
||||
public detectFiles(event: Event): void {
|
||||
const target = event.target as HTMLInputElement;
|
||||
this.selectedFiles = target.files;
|
||||
}
|
||||
|
||||
public async uploadSingle() {
|
||||
public uploadSingle(): void {
|
||||
const file = this.selectedFiles.item(0);
|
||||
this.currentUpload = new Upload(file as any);
|
||||
await this.uploadService.pushUpload(this.songId, this.currentUpload);
|
||||
this.currentUpload = new Upload(file);
|
||||
this.uploadService.pushUpload(this.songId, this.currentUpload);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
<fa-icon [icon]="faTrash"></fa-icon>
|
||||
</button>
|
||||
|
||||
<a [href]="url$|async" target="_blank">{{name}}</a>
|
||||
<a [href]="url$ | async" target="_blank">{{ name }}</a>
|
||||
|
||||
@@ -6,12 +6,13 @@ describe('FileComponent', () => {
|
||||
let component: FileComponent;
|
||||
let fixture: ComponentFixture<FileComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FileComponent]
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
void TestBed.configureTestingModule({
|
||||
declarations: [FileComponent],
|
||||
}).compileComponents();
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FileComponent);
|
||||
@@ -20,6 +21,6 @@ describe('FileComponent', () => {
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
void expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,20 +7,20 @@ import {FileService} from '../../../../services/file.service';
|
||||
@Component({
|
||||
selector: 'app-file',
|
||||
templateUrl: './file.component.html',
|
||||
styleUrls: ['./file.component.less']
|
||||
styleUrls: ['./file.component.less'],
|
||||
})
|
||||
export class FileComponent {
|
||||
public url$: Observable<string>;
|
||||
public name: string;
|
||||
public faTrash = faTrashAlt;
|
||||
@Input() songId: string;
|
||||
@Input() public songId: string;
|
||||
private fileId: string;
|
||||
private path: string;
|
||||
|
||||
constructor(private fileService: FileService) {
|
||||
}
|
||||
public constructor(private fileService: FileService) {}
|
||||
|
||||
@Input() set file(file: File) {
|
||||
@Input()
|
||||
public set file(file: File) {
|
||||
this.url$ = this.fileService.getDownloadUrl(file.path + '/' + file.name);
|
||||
this.name = file.name;
|
||||
this.fileId = file.id;
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('EditSongGuard', () => {
|
||||
let guard: EditSongGuard;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
void TestBed.configureTestingModule({});
|
||||
guard = TestBed.inject(EditSongGuard);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(guard).toBeTruthy();
|
||||
void expect(guard).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user