login
This commit is contained in:
22
src/app/modules/songs/models/song.ts
Normal file
22
src/app/modules/songs/models/song.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export interface Song {
|
||||
id: string;
|
||||
comment: string;
|
||||
final: boolean;
|
||||
key: string;
|
||||
number: number;
|
||||
tempo: number;
|
||||
text: string;
|
||||
title: string;
|
||||
type: string;
|
||||
|
||||
legalType: string;
|
||||
legalLink: string;
|
||||
legalOwner: string;
|
||||
legalOwnerId: string;
|
||||
legalLicenseId: string;
|
||||
|
||||
artist: string;
|
||||
label: string;
|
||||
termsOfUse: string;
|
||||
origin: string;
|
||||
}
|
||||
12
src/app/modules/songs/services/file-data.service.spec.ts
Normal file
12
src/app/modules/songs/services/file-data.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {FileDataService} from './file-data.service';
|
||||
|
||||
describe('FileDataService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: FileDataService = TestBed.get(FileDataService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
34
src/app/modules/songs/services/file-data.service.ts
Normal file
34
src/app/modules/songs/services/file-data.service.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {SongDataService} from './song-data.service';
|
||||
import {File} from './file';
|
||||
import {Observable} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {FileServer} from './fileServer';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FileDataService {
|
||||
|
||||
constructor(private songDataService: SongDataService) {
|
||||
}
|
||||
|
||||
public async put(songId: string, file: FileServer): Promise<string> {
|
||||
const songRef = this.songDataService.getSongRef(songId);
|
||||
const fileCollection = songRef.collection('files');
|
||||
const id = await fileCollection.add(file);
|
||||
return id.id;
|
||||
}
|
||||
|
||||
public get$(songId: string): Observable<File[]> {
|
||||
const songRef = this.songDataService.getSongRef(songId);
|
||||
return songRef.collection<File>('files').snapshotChanges().pipe(map(actions => {
|
||||
return actions.map(a => ({
|
||||
...a.payload.doc.data(),
|
||||
id: a.payload.doc.id
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
5
src/app/modules/songs/services/file.ts
Normal file
5
src/app/modules/songs/services/file.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import {FileServer} from './fileServer';
|
||||
|
||||
export interface File extends FileServer {
|
||||
id: string;
|
||||
}
|
||||
5
src/app/modules/songs/services/fileBase.ts
Normal file
5
src/app/modules/songs/services/fileBase.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export class FileBase {
|
||||
|
||||
protected basePath = '/attachments';
|
||||
protected directory = (songId: string) => `${this.basePath}/${songId}`;
|
||||
}
|
||||
5
src/app/modules/songs/services/fileServer.ts
Normal file
5
src/app/modules/songs/services/fileServer.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface FileServer {
|
||||
name: string;
|
||||
path: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
41
src/app/modules/songs/services/song-data.service.spec.ts
Normal file
41
src/app/modules/songs/services/song-data.service.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {async, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {SongDataService} from './song-data.service';
|
||||
import {AngularFirestore} from '@angular/fire/firestore';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('SongDataService', () => {
|
||||
|
||||
const songs = [
|
||||
{title: 'title1'}
|
||||
];
|
||||
|
||||
const angularFirestoreCollection = {
|
||||
valueChanges: () => of(songs)
|
||||
};
|
||||
|
||||
const mockAngularFirestore = {
|
||||
collection: () => angularFirestoreCollection
|
||||
};
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: AngularFirestore, useValue: mockAngularFirestore}
|
||||
]
|
||||
}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: SongDataService = TestBed.get(SongDataService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should list songs', async(() => {
|
||||
const service: SongDataService = TestBed.get(SongDataService);
|
||||
service.list().subscribe(s => {
|
||||
expect(s).toEqual([
|
||||
{title: 'title1'}
|
||||
] as any);
|
||||
}
|
||||
);
|
||||
}));
|
||||
});
|
||||
40
src/app/modules/songs/services/song-data.service.ts
Normal file
40
src/app/modules/songs/services/song-data.service.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {AngularFirestore, AngularFirestoreCollection} from '@angular/fire/firestore';
|
||||
import {Song} from '../models/song';
|
||||
import {Observable} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {AngularFirestoreDocument} from '@angular/fire/firestore/document/document';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SongDataService {
|
||||
private songCollection: AngularFirestoreCollection<Song>;
|
||||
private readonly songs: Observable<Song[]>;
|
||||
|
||||
constructor(private afs: AngularFirestore) {
|
||||
this.songCollection = afs.collection<Song>('songs');
|
||||
this.songs = this.songCollection.snapshotChanges().pipe(map(actions => {
|
||||
return actions.map(a => ({
|
||||
...a.payload.doc.data(),
|
||||
id: a.payload.doc.id
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
public list = (): Observable<Song[]> => this.songs;
|
||||
|
||||
public getSongRef = (songId: string): AngularFirestoreDocument<Song> => this.afs.doc<Song>('songs/' + songId);
|
||||
|
||||
public read(songId: string): Observable<Song | undefined> {
|
||||
return this.getSongRef(songId).valueChanges().pipe(map(song => ({
|
||||
...song,
|
||||
id: songId
|
||||
} as Song)));
|
||||
}
|
||||
|
||||
public async update(songId: string, data: any): Promise<void> {
|
||||
await this.getSongRef(songId).update(data);
|
||||
}
|
||||
}
|
||||
|
||||
36
src/app/modules/songs/services/song.service.spec.ts
Normal file
36
src/app/modules/songs/services/song.service.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {async, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {SongService} from './song.service';
|
||||
import {SongDataService} from './song-data.service';
|
||||
import {of} from 'rxjs';
|
||||
|
||||
describe('SongService', () => {
|
||||
|
||||
const songs = [
|
||||
{title: 'title1'}
|
||||
];
|
||||
|
||||
const mockSongDataService = {
|
||||
list: () => of(songs)
|
||||
};
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: SongDataService, useValue: mockSongDataService}
|
||||
]
|
||||
}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: SongService = TestBed.get(SongService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should list songs', async(() => {
|
||||
const service: SongService = TestBed.get(SongService);
|
||||
service.list$().subscribe(s => {
|
||||
expect(s).toEqual([
|
||||
{title: 'title1'}
|
||||
] as any);
|
||||
});
|
||||
}));
|
||||
});
|
||||
61
src/app/modules/songs/services/song.service.ts
Normal file
61
src/app/modules/songs/services/song.service.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Song} from '../models/song';
|
||||
import {SongDataService} from './song-data.service';
|
||||
import {tap} from 'rxjs/operators';
|
||||
|
||||
declare var importCCLI: any;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SongService {
|
||||
|
||||
public TYPES = ['Praise', 'Worship'];
|
||||
|
||||
public LEGAL_OWNER = ['CCLI', 'other'];
|
||||
public LEGAL_TYPE = ['open', 'allowed'];
|
||||
|
||||
public 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'
|
||||
];
|
||||
private list: Song[];
|
||||
|
||||
constructor(private songDataService: SongDataService) {
|
||||
importCCLI = (songs: Song[]) => this.updateFromCLI(songs);
|
||||
}
|
||||
|
||||
public list$ = (): Observable<Song[]> => this.songDataService.list().pipe(tap(_ => this.list = _));
|
||||
public read = (songId: string): Observable<Song | undefined> => this.songDataService.read(songId);
|
||||
|
||||
public async update(songId: string, data: any): Promise<void> {
|
||||
await this.songDataService.update(songId, data);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {TextRenderingService} from './text-rendering.service';
|
||||
|
||||
describe('TextRenderingService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: TextRenderingService = TestBed.get(TextRenderingService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
45
src/app/modules/songs/services/text-rendering.service.ts
Normal file
45
src/app/modules/songs/services/text-rendering.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
export enum SectionType {
|
||||
Verse,
|
||||
Chorus,
|
||||
Bridge,
|
||||
}
|
||||
|
||||
export enum LineType {
|
||||
title,
|
||||
chrod,
|
||||
text,
|
||||
}
|
||||
|
||||
export interface Line {
|
||||
type: LineType;
|
||||
text: string;
|
||||
}
|
||||
|
||||
|
||||
export interface Section {
|
||||
type: SectionType;
|
||||
number: number;
|
||||
lines: Line[];
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TextRenderingService {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public render(text: string): Section[] {
|
||||
const lines = text.match(/[^\r\n]+/g);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private findSection(line: string) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
12
src/app/modules/songs/services/upload.service.spec.ts
Normal file
12
src/app/modules/songs/services/upload.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {UploadService} from './upload.service';
|
||||
|
||||
describe('UploadServiceService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: UploadService = TestBed.get(UploadService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
60
src/app/modules/songs/services/upload.service.ts
Normal file
60
src/app/modules/songs/services/upload.service.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Upload} from './upload';
|
||||
import {FileDataService} from './file-data.service';
|
||||
import {AngularFireStorage} from '@angular/fire/storage';
|
||||
import {finalize} from 'rxjs/operators';
|
||||
import {FileBase} from './fileBase';
|
||||
import {FileServer} from './fileServer';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UploadService extends FileBase {
|
||||
|
||||
constructor(private fileDataService: FileDataService, private angularFireStorage: AngularFireStorage) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async pushUpload(songId: string, upload: Upload) {
|
||||
const directory = this.directory(songId);
|
||||
const filePath = `${directory}/${upload.file.name}`;
|
||||
upload.path = directory;
|
||||
|
||||
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();
|
||||
|
||||
// const storageRef = storage().ref();
|
||||
// const uploadTask = storageRef.child(`${this.basePath}/${songId}/${file.file.name}`).put(file.file as any);
|
||||
//
|
||||
// uploadTask.on(storage.TaskEvent.STATE_CHANGED,
|
||||
// (snapshot) => {
|
||||
// file.progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
|
||||
// },
|
||||
// (error) => {
|
||||
// console.log(error);
|
||||
// },
|
||||
// () => {
|
||||
// file.url = uploadTask.snapshot.downloadURL;
|
||||
// file.name = file.file.name;
|
||||
// this.saveFileData(songId, file);
|
||||
// }
|
||||
// );
|
||||
}
|
||||
|
||||
private async saveFileData(songId: string, upload: Upload) {
|
||||
const file: FileServer = {
|
||||
name: upload.file.name,
|
||||
path: upload.path,
|
||||
createdAt: new Date()
|
||||
};
|
||||
await this.fileDataService.put(songId, file);
|
||||
}
|
||||
}
|
||||
13
src/app/modules/songs/services/upload.ts
Normal file
13
src/app/modules/songs/services/upload.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export class Upload {
|
||||
|
||||
$key: string;
|
||||
file: Upload;
|
||||
name: string;
|
||||
path: string;
|
||||
progress: number;
|
||||
createdAt: Date = new Date();
|
||||
|
||||
constructor(file: Upload) {
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="list-item">
|
||||
<div class="number">{{song.number}}</div>
|
||||
<div>{{song.title}}</div>
|
||||
<div>{{song.key}}</div>
|
||||
<div>{{song.legalType | legalType}}</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
@import "../../../../../styles/styles";
|
||||
|
||||
.list-item {
|
||||
padding: 5px 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 50px auto 30px 100px;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: @primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {ListItemComponent} from './list-item.component';
|
||||
|
||||
describe('ListItemComponent', () => {
|
||||
let component: ListItemComponent;
|
||||
let fixture: ComponentFixture<ListItemComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ListItemComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ListItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Song} from '../../models/song';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-item',
|
||||
templateUrl: './list-item.component.html',
|
||||
styleUrls: ['./list-item.component.less']
|
||||
})
|
||||
export class ListItemComponent implements OnInit {
|
||||
@Input() public song: Song;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
3
src/app/modules/songs/song-list/song-list.component.html
Normal file
3
src/app/modules/songs/song-list/song-list.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<app-card *ngIf="songs$ | async as songs" [@fade] [padding]="false">
|
||||
<app-list-item *ngFor="let song of songs" [routerLink]="song.id" [song]="song"></app-list-item>
|
||||
</app-card>
|
||||
2
src/app/modules/songs/song-list/song-list.component.less
Normal file
2
src/app/modules/songs/song-list/song-list.component.less
Normal file
@@ -0,0 +1,2 @@
|
||||
.song-list {
|
||||
}
|
||||
47
src/app/modules/songs/song-list/song-list.component.spec.ts
Normal file
47
src/app/modules/songs/song-list/song-list.component.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
|
||||
import {SongListComponent} from './song-list.component';
|
||||
import {of} from 'rxjs';
|
||||
import {SongService} from '../services/song.service';
|
||||
import {NO_ERRORS_SCHEMA} from '@angular/core';
|
||||
|
||||
describe('SongListComponent', () => {
|
||||
let component: SongListComponent;
|
||||
let fixture: ComponentFixture<SongListComponent>;
|
||||
|
||||
const songs = [
|
||||
{title: 'title1'}
|
||||
];
|
||||
|
||||
const mockSongService = {
|
||||
list: () => of(songs)
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SongListComponent],
|
||||
providers: [
|
||||
{provide: SongService, useValue: mockSongService}
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SongListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should read songs from SongService', fakeAsync(() => {
|
||||
tick();
|
||||
expect(component.songs$).toEqual([
|
||||
{title: 'title1'}
|
||||
] as any);
|
||||
}));
|
||||
});
|
||||
51
src/app/modules/songs/song-list/song-list.component.ts
Normal file
51
src/app/modules/songs/song-list/song-list.component.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {SongService} from '../services/song.service';
|
||||
import {Song} from '../models/song';
|
||||
import {debounceTime, map} from 'rxjs/operators';
|
||||
import {combineLatest, Observable} from 'rxjs';
|
||||
import {fade} from '../../../animations';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-songs',
|
||||
templateUrl: './song-list.component.html',
|
||||
styleUrls: ['./song-list.component.less'],
|
||||
animations: [fade]
|
||||
})
|
||||
export class SongListComponent implements OnInit {
|
||||
|
||||
public songs$: Observable<Song[]>;
|
||||
|
||||
constructor(private songService: SongService, private activatedRoute: ActivatedRoute) {
|
||||
}
|
||||
|
||||
private static filter(song: Song, filterValue: string): boolean {
|
||||
if (!filterValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const textMatch = song.text && SongListComponent.normalize(song.text).indexOf(SongListComponent.normalize(filterValue)) !== -1;
|
||||
const titleMatch = song.title && SongListComponent.normalize(song.title).indexOf(SongListComponent.normalize(filterValue)) !== -1;
|
||||
|
||||
return textMatch || titleMatch;
|
||||
}
|
||||
|
||||
private static normalize(input: string): string {
|
||||
return input.toLowerCase().replace(/\s/g, '');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const filter$ = this.activatedRoute.queryParams.pipe(
|
||||
debounceTime(300),
|
||||
map(_ => _.q)
|
||||
);
|
||||
|
||||
const songs$ = this.songService.list$().pipe(
|
||||
map(songs => songs.sort((a, b) => a.number - b.number)),
|
||||
);
|
||||
|
||||
this.songs$ = combineLatest([filter$, songs$]).pipe(
|
||||
map(_ => _[1].filter(song => SongListComponent.filter(song, _[0])))
|
||||
);
|
||||
}
|
||||
}
|
||||
22
src/app/modules/songs/song-list/song-list.module.ts
Normal file
22
src/app/modules/songs/song-list/song-list.module.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SongListComponent} from './song-list.component';
|
||||
import {ListItemComponent} from './list-item/list-item.component';
|
||||
import {CardModule} from '../../../widget-modules/components/card/card.module';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {LegalTypeTranslatorModule} from '../../../widget-modules/pipes/legal-type-translator/legal-type-translator.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [SongListComponent, ListItemComponent],
|
||||
exports: [SongListComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
|
||||
CardModule,
|
||||
LegalTypeTranslatorModule
|
||||
]
|
||||
})
|
||||
export class SongListModule {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<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>
|
||||
Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
|
||||
</div>
|
||||
<div class="upload">
|
||||
<label>
|
||||
<input (change)="detectFiles($event)" type="file">
|
||||
</label>
|
||||
|
||||
<button (click)="uploadSingle()"
|
||||
[disabled]="!selectedFiles"
|
||||
mat-icon-button>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</app-card>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.upload {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {EditFileComponent} from './edit-file.component';
|
||||
|
||||
describe('EditFileComponent', () => {
|
||||
let component: EditFileComponent;
|
||||
let fixture: ComponentFixture<EditFileComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EditFileComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditFileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Upload} from '../../../services/upload';
|
||||
import {UploadService} from '../../../services/upload.service';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-file',
|
||||
templateUrl: './edit-file.component.html',
|
||||
styleUrls: ['./edit-file.component.less']
|
||||
})
|
||||
export class EditFileComponent {
|
||||
|
||||
public selectedFiles: FileList;
|
||||
public currentUpload: Upload;
|
||||
public songId: string;
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute, private uploadService: UploadService) {
|
||||
this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
).subscribe(songId => {
|
||||
this.songId = songId;
|
||||
});
|
||||
}
|
||||
|
||||
detectFiles(event) {
|
||||
this.selectedFiles = event.target.files;
|
||||
}
|
||||
|
||||
public async uploadSingle() {
|
||||
const file = this.selectedFiles.item(0);
|
||||
this.currentUpload = new Upload(file as any);
|
||||
await this.uploadService.pushUpload(this.songId, this.currentUpload);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>file works!</p>
|
||||
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {FileComponent} from './file.component';
|
||||
|
||||
describe('FileComponent', () => {
|
||||
let component: FileComponent;
|
||||
let fixture: ComponentFixture<FileComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FileComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-file',
|
||||
templateUrl: './file.component.html',
|
||||
styleUrls: ['./file.component.less']
|
||||
})
|
||||
export class FileComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<app-card *ngIf="song" [heading]="song.number + ' bearbeiten'">
|
||||
|
||||
<form [formGroup]="form" class="form">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Titel</mat-label>
|
||||
<input formControlName="title" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="third">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Typ</mat-label>
|
||||
<mat-select formControlName="type">
|
||||
<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 *ngFor="let key of keys" [value]="key">{{key}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Tempo</mat-label>
|
||||
<input formControlName="tempo" matInput>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Songtext</mat-label>
|
||||
<textarea [mat-autosize]="true" formControlName="text" matInput></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Kommentar</mat-label>
|
||||
<textarea [mat-autosize]="true" formControlName="comment" matInput></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechtlicher Status</mat-label>
|
||||
<mat-select formControlName="legalType">
|
||||
<mat-option *ngFor="let key of legalType" [value]="key">{{key|legalType}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber</mat-label>
|
||||
<mat-select formControlName="legalOwner">
|
||||
<mat-option *ngFor="let key of legalOwner" [value]="key">{{key|legalOwner}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber Link</mat-label>
|
||||
<input formControlName="legalLink" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Rechteinhaber ID (z.B. CCLI Liednummer)</mat-label>
|
||||
<input formControlName="legalOwnerId" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Lizenznummer</mat-label>
|
||||
<input formControlName="legalLicenseId" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Künstler</mat-label>
|
||||
<input formControlName="artist" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Verlag</mat-label>
|
||||
<input formControlName="label" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Nutzungsbedingungen</mat-label>
|
||||
<input formControlName="termsOfUse" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>abweichende Quelle</mat-label>
|
||||
<input formControlName="origin" matInput>
|
||||
</mat-form-field>
|
||||
|
||||
</form>
|
||||
|
||||
<app-button-row>
|
||||
<button (click)="onSave()" color="primary" mat-flat-button>Speichern</button>
|
||||
<button mat-stroked-button routerLink="../">Abbrechen</button>
|
||||
</app-button-row>
|
||||
</app-card>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.form {
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.third {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {EditSongComponent} from './edit-song.component';
|
||||
|
||||
describe('EditSongComponent', () => {
|
||||
let component: EditSongComponent;
|
||||
let fixture: ComponentFixture<EditSongComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EditSongComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditSongComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Song} from '../../../models/song';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {SongService} from '../../../services/song.service';
|
||||
import {EditService} from '../edit.service';
|
||||
import {first, map, switchMap} from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-song',
|
||||
templateUrl: './edit-song.component.html',
|
||||
styleUrls: ['./edit-song.component.less']
|
||||
})
|
||||
export class EditSongComponent implements OnInit {
|
||||
public song: Song;
|
||||
public form: FormGroup;
|
||||
public keys = this.songService.KEYS;
|
||||
public types = this.songService.TYPES;
|
||||
public legalOwner = this.songService.LEGAL_OWNER;
|
||||
public legalType = this.songService.LEGAL_TYPE;
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private songService: SongService,
|
||||
private editService: EditService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
switchMap(songId => this.songService.read(songId)),
|
||||
first()
|
||||
).subscribe(song => {
|
||||
this.song = song;
|
||||
this.form = this.editService.createSongForm(song);
|
||||
});
|
||||
}
|
||||
|
||||
public async onSave(): Promise<void> {
|
||||
const data = this.form.value;
|
||||
await this.songService.update(this.song.id, data);
|
||||
await this.router.navigateByUrl('songs/' + this.song.id);
|
||||
}
|
||||
|
||||
}
|
||||
2
src/app/modules/songs/song/edit/edit.component.html
Normal file
2
src/app/modules/songs/song/edit/edit.component.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<app-edit-song></app-edit-song>
|
||||
<app-edit-file></app-edit-file>
|
||||
0
src/app/modules/songs/song/edit/edit.component.less
Normal file
0
src/app/modules/songs/song/edit/edit.component.less
Normal file
25
src/app/modules/songs/song/edit/edit.component.spec.ts
Normal file
25
src/app/modules/songs/song/edit/edit.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {EditComponent} from './edit.component';
|
||||
|
||||
describe('EditComponent', () => {
|
||||
let component: EditComponent;
|
||||
let fixture: ComponentFixture<EditComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EditComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
10
src/app/modules/songs/song/edit/edit.component.ts
Normal file
10
src/app/modules/songs/song/edit/edit.component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit',
|
||||
templateUrl: './edit.component.html',
|
||||
styleUrls: ['./edit.component.less']
|
||||
})
|
||||
export class EditComponent {
|
||||
|
||||
}
|
||||
44
src/app/modules/songs/song/edit/edit.module.ts
Normal file
44
src/app/modules/songs/song/edit/edit.module.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {EditComponent} from './edit.component';
|
||||
import {CardModule} from '../../../../widget-modules/components/card/card.module';
|
||||
import {SongTypeTranslaterModule} from '../../../../widget-modules/pipes/song-type-translater/song-type-translater.module';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {ButtonRowModule} from '../../../../widget-modules/components/button-row/button-row.module';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {EditSongComponent} from './edit-song/edit-song.component';
|
||||
import {EditFileComponent} from './edit-file/edit-file.component';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {FileComponent} from './edit-file/file/file.component';
|
||||
import {LegalOwnerTranslatorModule} from '../../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module';
|
||||
import {LegalTypeTranslatorModule} from '../../../../widget-modules/pipes/legal-type-translator/legal-type-translator.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [EditComponent, EditSongComponent, EditFileComponent, FileComponent],
|
||||
exports: [EditComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
SongTypeTranslaterModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule,
|
||||
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
ButtonRowModule,
|
||||
|
||||
MatIconModule,
|
||||
LegalOwnerTranslatorModule,
|
||||
LegalTypeTranslatorModule,
|
||||
|
||||
]
|
||||
})
|
||||
export class EditModule {
|
||||
}
|
||||
12
src/app/modules/songs/song/edit/edit.service.spec.ts
Normal file
12
src/app/modules/songs/song/edit/edit.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {EditService} from './edit.service';
|
||||
|
||||
describe('EditService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: EditService = TestBed.get(EditService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
34
src/app/modules/songs/song/edit/edit.service.ts
Normal file
34
src/app/modules/songs/song/edit/edit.service.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Song} from '../../models/song';
|
||||
import {FormControl, FormGroup} from '@angular/forms';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class EditService {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public createSongForm(song: Song): FormGroup {
|
||||
return new FormGroup({
|
||||
text: new FormControl(song.text),
|
||||
title: new FormControl(song.title),
|
||||
comment: new FormControl(song.comment),
|
||||
key: new FormControl(song.key),
|
||||
tempo: new FormControl(song.tempo),
|
||||
type: new FormControl(song.type),
|
||||
|
||||
legalType: new FormControl(song.legalType),
|
||||
legalLink: new FormControl(song.legalLink),
|
||||
legalOwner: new FormControl(song.legalOwner),
|
||||
legalOwnerId: new FormControl(song.legalOwnerId),
|
||||
legalLicenseId: new FormControl(song.legalLicenseId),
|
||||
|
||||
artist: new FormControl(song.artist),
|
||||
label: new FormControl(song.label),
|
||||
termsOfUse: new FormControl(song.termsOfUse),
|
||||
origin: new FormControl(song.origin),
|
||||
});
|
||||
}
|
||||
}
|
||||
29
src/app/modules/songs/song/song.component.html
Normal file
29
src/app/modules/songs/song/song.component.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<app-card *ngIf="song$ | async as song" [heading]="song.number + ' - ' + song.title">
|
||||
<div class="song">
|
||||
<div>
|
||||
<div class="detail">
|
||||
<div>Typ: {{song.type | songType}}</div>
|
||||
<div>Tonart: {{song.key}}</div>
|
||||
<div>Tempo: {{song.tempo}}</div>
|
||||
<div *ngIf="song.legalOwner">Rechteinhaber: {{song.legalOwner|legalOwner}}</div>
|
||||
<div *ngIf="song.legalOwnerId">Rechteinhaber ID: {{song.legalOwnerId}}</div>
|
||||
<div *ngIf="song.legalLicenseId">Lizenznummer: {{song.legalLicenseId}}</div>
|
||||
<div *ngIf="song.artist">Künstler: {{song.artist}}</div>
|
||||
<div *ngIf="song.label">Verlag: {{song.label}}</div>
|
||||
<div *ngIf="song.origin">Quelle: {{song.origin}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text">{{song.text}}</div>
|
||||
<div class="text">{{song.comment}}</div>
|
||||
<div>
|
||||
<h3>Anhänge</h3>
|
||||
<div *ngFor="let file of (files$|async)">{{file.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-button-row>
|
||||
<button color="primary" mat-flat-button routerLink="edit">Bearbeiten</button>
|
||||
</app-button-row>
|
||||
|
||||
</app-card>
|
||||
17
src/app/modules/songs/song/song.component.less
Normal file
17
src/app/modules/songs/song/song.component.less
Normal file
@@ -0,0 +1,17 @@
|
||||
.song {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 20px;
|
||||
grid-gap: 20px;
|
||||
}
|
||||
|
||||
.text {
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Ubuntu Mono', monospace;
|
||||
|
||||
}
|
||||
|
||||
.detail {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
39
src/app/modules/songs/song/song.component.spec.ts
Normal file
39
src/app/modules/songs/song/song.component.spec.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
|
||||
import {SongComponent} from './song.component';
|
||||
import {of} from 'rxjs';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
|
||||
describe('SongComponent', () => {
|
||||
let component: SongComponent;
|
||||
let fixture: ComponentFixture<SongComponent>;
|
||||
|
||||
const mockActivatedRoute = {
|
||||
params: of({songId: '4711'})
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SongComponent],
|
||||
providers: [
|
||||
{provide: ActivatedRoute, useValue: mockActivatedRoute}
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SongComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should provide songId', fakeAsync(() => {
|
||||
tick();
|
||||
expect(component.songId).toBe('4711');
|
||||
}));
|
||||
});
|
||||
39
src/app/modules/songs/song/song.component.ts
Normal file
39
src/app/modules/songs/song/song.component.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {SongService} from '../services/song.service';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
import {Song} from '../models/song';
|
||||
import {Observable} from 'rxjs';
|
||||
import {FileDataService} from '../services/file-data.service';
|
||||
import {File} from '../services/file';
|
||||
|
||||
@Component({
|
||||
selector: 'app-song',
|
||||
templateUrl: './song.component.html',
|
||||
styleUrls: ['./song.component.less']
|
||||
})
|
||||
export class SongComponent implements OnInit {
|
||||
public song$: Observable<Song>;
|
||||
public files$: Observable<File[]>;
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private songService: SongService,
|
||||
private fileService: FileDataService,
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.song$ = this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
switchMap(songId => this.songService.read(songId))
|
||||
);
|
||||
|
||||
this.files$ = this.activatedRoute.params.pipe(
|
||||
map(param => param.songId),
|
||||
switchMap(songId => this.fileService.get$(songId))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
27
src/app/modules/songs/song/song.module.ts
Normal file
27
src/app/modules/songs/song/song.module.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SongComponent} from './song.component';
|
||||
import {CardModule} from '../../../widget-modules/components/card/card.module';
|
||||
import {SongTypeTranslaterModule} from '../../../widget-modules/pipes/song-type-translater/song-type-translater.module';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {ButtonRowModule} from '../../../widget-modules/components/button-row/button-row.module';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [SongComponent],
|
||||
exports: [SongComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
RouterModule,
|
||||
|
||||
SongTypeTranslaterModule,
|
||||
MatButtonModule,
|
||||
ButtonRowModule,
|
||||
LegalOwnerTranslatorModule,
|
||||
]
|
||||
})
|
||||
export class SongModule {
|
||||
}
|
||||
29
src/app/modules/songs/songs-routing.module.ts
Normal file
29
src/app/modules/songs/songs-routing.module.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {SongComponent} from './song/song.component';
|
||||
import {SongListComponent} from './song-list/song-list.component';
|
||||
import {EditComponent} from './song/edit/edit.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: SongListComponent,
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: ':songId/edit',
|
||||
component: EditComponent
|
||||
},
|
||||
{
|
||||
path: ':songId',
|
||||
component: SongComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class SongsRoutingModule {
|
||||
}
|
||||
20
src/app/modules/songs/songs.module.ts
Normal file
20
src/app/modules/songs/songs.module.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {SongsRoutingModule} from './songs-routing.module';
|
||||
import {SongListModule} from './song-list/song-list.module';
|
||||
import {SongModule} from './song/song.module';
|
||||
import {EditModule} from './song/edit/edit.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SongsRoutingModule,
|
||||
SongListModule,
|
||||
SongModule,
|
||||
EditModule
|
||||
]
|
||||
})
|
||||
export class SongsModule {
|
||||
}
|
||||
Reference in New Issue
Block a user