update tslint -> eslint

This commit is contained in:
2021-05-21 20:17:26 +02:00
parent 80260df71f
commit a195fafa6b
252 changed files with 3080 additions and 2420 deletions

View File

@@ -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();
});
});

View File

@@ -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'});
}
}

View File

@@ -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();
});
});

View File

@@ -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);

View File

@@ -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}`;
}

View File

@@ -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];

View File

@@ -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);
service.list$().subscribe(s => {
expect(s).toEqual([
{title: 'title1'}
] as any);
}
);
}));
it(
'should list songs',
waitForAsync(() => {
const service: SongDataService = TestBed.inject(SongDataService);
service.list$().subscribe(s => {
void expect(s[0].title).toEqual('title1');
});
})
);
});

View File

@@ -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();
}

View File

@@ -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);
service.list$().subscribe(s => {
expect(s).toEqual([
{title: 'title1'}
] as any);
});
}));
it(
'should list songs',
waitForAsync(() => {
const service: SongService = TestBed.inject(SongService);
service.list$().subscribe(s => {
void expect(s[0].title).toEqual('title1');
});
})
);
});

View File

@@ -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);
// }
}

View File

@@ -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');
});
});

View File

@@ -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, {
type,
number: indices[type]++,
lines: []
}];
if (this.regexSection.exec(line)) {
return [
...array,
{
type,
number: indices[type]++,
lines: [],
},
];
}
array[array.length - 1].lines.push(this.getLineOfLineText(line, transpose));
return array;
@@ -49,36 +51,35 @@ 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;
}
const typeString = match[1];
switch (typeString) {
case 'Strophe':
case 'Strophe':
return SectionType.Verse;
case 'Refrain':
case 'Refrain':
return SectionType.Chorus;
case 'Bridge':
case 'Bridge':
return SectionType.Bridge;
}
}
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 : [];
}
}

View File

@@ -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();
});
});

View File

@@ -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] : '');
}
}

View File

@@ -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();
});
});

View File

@@ -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);
}

View 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;
}
}