This commit is contained in:
2020-03-02 18:47:04 +01:00
committed by smuddy
parent 5b746e0db5
commit ccd91aa81c
93 changed files with 444 additions and 89 deletions

View File

@@ -1,6 +1,6 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router'; import {RouterModule, Routes} from '@angular/router';
import {AngularFireAuthGuard, redirectUnauthorizedTo} from '@angular/fire/auth-guard';
const routes: Routes = [ const routes: Routes = [
{ {
@@ -10,7 +10,13 @@ const routes: Routes = [
}, },
{ {
path: 'songs', path: 'songs',
loadChildren: () => import('./songs/songs.module').then(m => m.SongsModule) loadChildren: () => import('./modules/songs/songs.module').then(m => m.SongsModule),
canActivate: [AngularFireAuthGuard],
data: {authGuardPipe: () => redirectUnauthorizedTo(['user', 'login'])}
},
{
path: 'user',
loadChildren: () => import('./modules/user/user.module').then(m => m.UserModule)
} }
]; ];

View File

@@ -6,11 +6,13 @@ import {AppComponent} from './app.component';
import {ServiceWorkerModule} from '@angular/service-worker'; import {ServiceWorkerModule} from '@angular/service-worker';
import {environment} from '../environments/environment'; import {environment} from '../environments/environment';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {ApplicationFrameModule} from './application-frame/application-frame.module'; import {ApplicationFrameModule} from './widget-modules/application-frame/application-frame.module';
import {AngularFireModule} from '@angular/fire'; import {AngularFireModule} from '@angular/fire';
import {AngularFirestoreModule} from '@angular/fire/firestore'; import {AngularFirestoreModule} from '@angular/fire/firestore';
import {AngularFireStorageModule} from '@angular/fire/storage'; import {AngularFireStorageModule} from '@angular/fire/storage';
import {AngularFireDatabaseModule} from '@angular/fire/database'; import {AngularFireDatabaseModule} from '@angular/fire/database';
import {AngularFireAuthModule} from '@angular/fire/auth';
import {AngularFireAuthGuardModule} from '@angular/fire/auth-guard';
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -29,7 +31,9 @@ import {AngularFireDatabaseModule} from '@angular/fire/database';
AngularFireModule.initializeApp(environment.firebase), AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule.enablePersistence({synchronizeTabs: true}), AngularFirestoreModule.enablePersistence({synchronizeTabs: true}),
AngularFireStorageModule, AngularFireStorageModule,
AngularFireDatabaseModule AngularFireDatabaseModule,
AngularFireAuthModule,
AngularFireAuthGuardModule,
], ],
providers: [], providers: [],

View File

@@ -1,9 +0,0 @@
<nav class="head">
<div class="links">
<a href="#" routerLink="/songs" routerLinkActive="active">Inhalt</a></div>
<div class="actions">
<app-filter></app-filter>
</div>
</nav>

View File

@@ -29,7 +29,8 @@ export interface Section {
}) })
export class TextRenderingService { export class TextRenderingService {
constructor() { } constructor() {
}
public render(text: string): Section[] { public render(text: string): Section[] {
const lines = text.match(/[^\r\n]+/g); const lines = text.match(/[^\r\n]+/g);

View File

@@ -3,7 +3,6 @@ import {Upload} from './upload';
import {FileDataService} from './file-data.service'; import {FileDataService} from './file-data.service';
import {AngularFireStorage} from '@angular/fire/storage'; import {AngularFireStorage} from '@angular/fire/storage';
import {finalize} from 'rxjs/operators'; import {finalize} from 'rxjs/operators';
import {File} from './file';
import {FileBase} from './fileBase'; import {FileBase} from './fileBase';
import {FileServer} from './fileServer'; import {FileServer} from './fileServer';

View File

@@ -1,4 +1,4 @@
@import "../../../../styles/styles"; @import "../../../../../styles/styles";
.list-item { .list-item {
padding: 5px 20px; padding: 5px 20px;

View File

@@ -3,7 +3,7 @@ import {SongService} from '../services/song.service';
import {Song} from '../models/song'; import {Song} from '../models/song';
import {debounceTime, map} from 'rxjs/operators'; import {debounceTime, map} from 'rxjs/operators';
import {combineLatest, Observable} from 'rxjs'; import {combineLatest, Observable} from 'rxjs';
import {fade} from '../../animations'; import {fade} from '../../../animations';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
@Component({ @Component({

View File

@@ -2,9 +2,9 @@ import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {SongListComponent} from './song-list.component'; import {SongListComponent} from './song-list.component';
import {ListItemComponent} from './list-item/list-item.component'; import {ListItemComponent} from './list-item/list-item.component';
import {CardModule} from '../../widget-modules/components/card/card.module'; import {CardModule} from '../../../widget-modules/components/card/card.module';
import {RouterModule} from '@angular/router'; import {RouterModule} from '@angular/router';
import {LegalTypeTranslatorModule} from '../../widget-modules/pipes/legal-type-translator/legal-type-translator.module'; import {LegalTypeTranslatorModule} from '../../../widget-modules/pipes/legal-type-translator/legal-type-translator.module';
@NgModule({ @NgModule({

View File

@@ -36,7 +36,6 @@
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Rechtlicher Status</mat-label> <mat-label>Rechtlicher Status</mat-label>
<mat-select formControlName="legalType"> <mat-select formControlName="legalType">

View File

@@ -1,21 +1,21 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {EditComponent} from './edit.component'; import {EditComponent} from './edit.component';
import {CardModule} from '../../../widget-modules/components/card/card.module'; import {CardModule} from '../../../../widget-modules/components/card/card.module';
import {SongTypeTranslaterModule} from '../../../widget-modules/pipes/song-type-translater/song-type-translater.module'; import {SongTypeTranslaterModule} from '../../../../widget-modules/pipes/song-type-translater/song-type-translater.module';
import {ReactiveFormsModule} from '@angular/forms'; import {ReactiveFormsModule} from '@angular/forms';
import {MatInputModule} from '@angular/material/input'; import {MatInputModule} from '@angular/material/input';
import {MatCheckboxModule} from '@angular/material/checkbox'; import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatSelectModule} from '@angular/material/select'; import {MatSelectModule} from '@angular/material/select';
import {MatButtonModule} from '@angular/material/button'; import {MatButtonModule} from '@angular/material/button';
import {ButtonRowModule} from '../../../widget-modules/components/button-row/button-row.module'; import {ButtonRowModule} from '../../../../widget-modules/components/button-row/button-row.module';
import {RouterModule} from '@angular/router'; import {RouterModule} from '@angular/router';
import {EditSongComponent} from './edit-song/edit-song.component'; import {EditSongComponent} from './edit-song/edit-song.component';
import {EditFileComponent} from './edit-file/edit-file.component'; import {EditFileComponent} from './edit-file/edit-file.component';
import {MatIconModule} from '@angular/material/icon'; import {MatIconModule} from '@angular/material/icon';
import {FileComponent} from './edit-file/file/file.component'; import {FileComponent} from './edit-file/file/file.component';
import {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module'; 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'; import {LegalTypeTranslatorModule} from '../../../../widget-modules/pipes/legal-type-translator/legal-type-translator.module';
@NgModule({ @NgModule({

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

View File

@@ -0,0 +1,7 @@
<app-card *ngIf="user$|async as user">
<h2>Hallo {{user.name}}</h2>
<p>{{user.role|role}}</p>
<app-button-row>
<button mat-button routerLink="../logout">Abmelden</button>
</app-button-row>
</app-card>

View File

@@ -0,0 +1,25 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {InfoComponent} from './info.component';
describe('InfoComponent', () => {
let component: InfoComponent;
let fixture: ComponentFixture<InfoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [InfoComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InfoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,21 @@
import {Component, OnInit} from '@angular/core';
import {UserService} from '../../../services/user.service';
import {Observable} from 'rxjs';
import {User} from '../../../services/user';
@Component({
selector: 'app-info',
templateUrl: './info.component.html',
styleUrls: ['./info.component.less']
})
export class InfoComponent implements OnInit {
public user$: Observable<User>;
constructor(private userService: UserService) {
}
ngOnInit() {
this.user$ = this.userService.user$;
}
}

View File

@@ -0,0 +1,8 @@
import {RolePipe} from './role.pipe';
describe('RolePipe', () => {
it('create an instance', () => {
const pipe = new RolePipe();
expect(pipe).toBeTruthy();
});
});

View File

@@ -0,0 +1,15 @@
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'role'
})
export class RolePipe implements PipeTransform {
transform(role: 'admin'): string {
switch (role) {
case 'admin':
return 'Administrator';
}
}
}

View File

@@ -0,0 +1,8 @@
import {AuthMessagePipe} from './auth-message.pipe';
describe('AuthMessagePipe', () => {
it('create an instance', () => {
const pipe = new AuthMessagePipe();
expect(pipe).toBeTruthy();
});
});

View File

@@ -0,0 +1,19 @@
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'authMessage'
})
export class AuthMessagePipe implements PipeTransform {
transform(code: string): string {
switch (code) {
case 'auth/user-not-found':
return 'Benutzer wurde nicht gefunden';
case 'auth/wrong-password':
return 'Passwort ist falsch';
default :
return code;
}
}
}

View File

@@ -0,0 +1,19 @@
<app-card>
<div [formGroup]="form" class="form">
<mat-form-field appearance="outline">
<mat-label>E-Mail Addresse</mat-label>
<input (keyup.enter)="onLogin()" formControlName="user" matInput>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Passwort</mat-label>
<input (keyup.enter)="onLogin()" formControlName="pass" matInput type="password">
</mat-form-field>
<app-button-row>
<button (click)="onLogin()" mat-button>Anmelden</button>
<p *ngIf="errorMessage" class="error">{{errorMessage|authMessage}}</p>
</app-button-row>
</div>
</app-card>

View File

@@ -0,0 +1,8 @@
.form {
display: grid;
}
p.error {
margin: 8px 10px;
color: darkred;
}

View File

@@ -0,0 +1,25 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {LoginComponent} from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LoginComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,36 @@
import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {AngularFireAuth} from '@angular/fire/auth';
import {Router} from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less']
})
export class LoginComponent implements OnInit {
public form: FormGroup;
public errorMessage: string;
constructor(public afAuth: AngularFireAuth, private router: Router) {
}
ngOnInit() {
this.form = new FormGroup({
user: new FormControl(null, [Validators.required, Validators.email]),
pass: new FormControl(null, [Validators.required]),
});
}
public async onLogin() {
this.form.updateValueAndValidity();
if (this.form.valid) {
try {
await this.afAuth.auth.signInWithEmailAndPassword(this.form.value.user, this.form.value.pass);
await this.router.navigateByUrl('/');
} catch (ex) {
this.errorMessage = ex.code;
}
}
}
}

View File

@@ -0,0 +1 @@
<p>logout works!</p>

View File

@@ -0,0 +1,25 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {LogoutComponent} from './logout.component';
describe('LogoutComponent', () => {
let component: LogoutComponent;
let fixture: ComponentFixture<LogoutComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LogoutComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LogoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,18 @@
import {AfterViewInit, Component} from '@angular/core';
import {AngularFireAuth} from '@angular/fire/auth';
import {Router} from '@angular/router';
@Component({
selector: 'app-logout',
templateUrl: './logout.component.html',
styleUrls: ['./logout.component.less']
})
export class LogoutComponent implements AfterViewInit {
constructor(public afAuth: AngularFireAuth, private router: Router) {
}
public async ngAfterViewInit() {
await this.afAuth.auth.signOut();
await this.router.navigateByUrl('/');
}
}

View File

@@ -0,0 +1,36 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {LoginComponent} from './login/login.component';
import {InfoComponent} from './info/info.component';
import {LogoutComponent} from './logout/logout.component';
import {AngularFireAuthGuard, redirectUnauthorizedTo} from '@angular/fire/auth-guard';
const routes: Routes = [
{
path: '',
redirectTo: 'info',
pathMatch: 'full'
},
{
path: 'login',
component: LoginComponent
},
{
path: 'logout',
component: LogoutComponent
},
{
path: 'info',
component: InfoComponent,
canActivate: [AngularFireAuthGuard],
data: {authGuardPipe: () => redirectUnauthorizedTo(['user', 'login'])}
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UserRoutingModule {
}

View File

@@ -0,0 +1,33 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {LoginComponent} from './login/login.component';
import {UserRoutingModule} from './user-routing.module';
import {CardModule} from '../../widget-modules/components/card/card.module';
import {MatFormFieldModule} from '@angular/material/form-field';
import {ReactiveFormsModule} from '@angular/forms';
import {MatInputModule} from '@angular/material/input';
import {ButtonRowModule} from '../../widget-modules/components/button-row/button-row.module';
import {MatButtonModule} from '@angular/material/button';
import {AuthMessagePipe} from './login/auth-message.pipe';
import {InfoComponent} from './info/info.component';
import {LogoutComponent} from './logout/logout.component';
import {RolePipe} from './info/role.pipe';
@NgModule({
declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe],
imports: [
CommonModule,
UserRoutingModule,
CardModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
ButtonRowModule,
MatButtonModule,
]
})
export class UserModule {
}

View File

@@ -0,0 +1,12 @@
import {TestBed} from '@angular/core/testing';
import {UserService} from './user.service';
describe('UserService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: UserService = TestBed.get(UserService);
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,21 @@
import {Injectable} from '@angular/core';
import {AngularFireAuth} from '@angular/fire/auth';
import {Observable} from 'rxjs';
import {filter, switchMap} from 'rxjs/operators';
import {User} from './user';
import {AngularFirestore} from '@angular/fire/firestore';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private afAuth: AngularFireAuth, private afs: AngularFirestore) {
}
public get user$(): Observable<User> {
return this.afAuth.authState.pipe(
filter(_ => !!_),
switchMap(auth => this.afs.doc<User>('user/' + auth.uid).valueChanges())
);
}
}

4
src/app/services/user.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface User {
name: string;
role: 'admin';
}

View File

@@ -1,27 +0,0 @@
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 {
}

View File

@@ -1,2 +1 @@
<input (input)="onInputChange($event.target.value)" placeholder="Suche"> <input (input)="onInputChange($event.target.value)" placeholder="Suche">

View File

@@ -1,4 +1,4 @@
@import "../../../../styles/styles"; @import "../../../../../styles/styles";
input { input {
font-size: 16px; font-size: 16px;

View File

@@ -0,0 +1,11 @@
<nav class="head">
<div class="links">
<a href="#" routerLink="/songs" routerLinkActive="active">Lieder</a>
<a href="#" routerLink="/user" routerLinkActive="active">Benutzer</a>
</div>
<div class="actions">
<app-filter></app-filter>
</div>
</nav>

View File

@@ -1,5 +1,5 @@
@import "../../../styles/styles"; @import "../../../../styles/styles";
@import "../../../styles/shadow"; @import "../../../../styles/shadow";
nav { nav {
&.head { &.head {
@@ -45,3 +45,8 @@ nav {
height: 100%; height: 100%;
align-items: center; align-items: center;
} }
.links {
display: flex;
}

View File

@@ -6,7 +6,7 @@
border-radius: 8px; border-radius: 8px;
background: #fffe; background: #fffe;
overflow: hidden; overflow: hidden;
width: 800px; width: 50vw;
&.padding { &.padding {
padding: 20px; padding: 20px;

View File

@@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common';
import {LegalOwnerPipe} from './legal-owner.pipe'; import {LegalOwnerPipe} from './legal-owner.pipe';
@NgModule({ @NgModule({
declarations: [LegalOwnerPipe], declarations: [LegalOwnerPipe],
exports: [ exports: [
@@ -13,4 +12,5 @@ import {LegalOwnerPipe} from './legal-owner.pipe';
CommonModule CommonModule
] ]
}) })
export class LegalOwnerTranslatorModule { } export class LegalOwnerTranslatorModule {
}

View File

@@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common';
import {LegalTypePipe} from './legal-type.pipe'; import {LegalTypePipe} from './legal-type.pipe';
@NgModule({ @NgModule({
declarations: [LegalTypePipe], declarations: [LegalTypePipe],
exports: [ exports: [
@@ -13,4 +12,5 @@ import { LegalTypePipe } from './legal-type.pipe';
CommonModule CommonModule
] ]
}) })
export class LegalTypeTranslatorModule { } export class LegalTypeTranslatorModule {
}

View File

@@ -1,4 +1,3 @@
import {enableProdMode} from '@angular/core'; import {enableProdMode} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';