Add ability to login

This commit is contained in:
rubikscraft 2022-03-03 15:44:22 +01:00
parent e0230b26ae
commit 9f8cf14807
No known key found for this signature in database
GPG key ID: 1463EBE9200A5CD4
10 changed files with 138 additions and 23 deletions

View file

@ -27,6 +27,7 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"jwt-decode": "^3.1.2",
"ngx-auto-unsubscribe-decorator": "^1.0.0",
"ngx-dropzone": "^3.1.0",
"picsur-shared": "*",
"rxjs": "~7.5.4",

View file

@ -23,10 +23,18 @@ import { KeyService } from './key.service';
providedIn: 'root',
})
export class UserService {
public get user() {
public get liveUser() {
return this.userSubject;
}
public get user() {
return this.userSubject.getValue();
}
public get isLoggedIn() {
return this.userSubject.getValue() !== null;
}
private userSubject = new BehaviorSubject<EUser | null>(null);
constructor(private api: ApiService, private key: KeyService) {
@ -74,6 +82,7 @@ export class UserService {
const user = await this.extractUser(apikey);
if (HasFailed(user)) {
console.warn(user.getReason());
await this.logout();
return;
}
@ -82,6 +91,7 @@ export class UserService {
const fetchedUser = await this.fetchUser();
if (HasFailed(fetchedUser)) {
console.warn(fetchedUser.getReason());
await this.logout();
return;
}

View file

@ -2,9 +2,34 @@
<a [routerLink]="['/']" class="svg-logo">
<img src="/assets/image/logo/picsur.svg" alt="Picsur" />
</a>
<a [routerLink]="['/']" class="text-link">
<a [routerLink]="['/']" class="text-link d-none d-sm-block">
<span>Picsur</span>
</a>
<span class="spacer"></span>
<button mat-stroked-button (click)="doLogin()">Login</button>
<button *ngIf="!isLoggedIn" mat-stroked-button (click)="doLogin()">
Login
</button>
<span *ngIf="isLoggedIn" class="username d-none d-sm-block">
{{ user?.username }}
</span>
<button *ngIf="isLoggedIn" mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>account_circle</mat-icon>
</button>
<mat-menu #menu="matMenu" xPosition="before">
<ng-template matMenuContent>
<span mat-menu-item disabled>
<div class="centered">
<h2>{{ user?.username }}</h2>
</div>
</span>
<button mat-menu-item (click)="doLogout()">
<mat-icon>logout</mat-icon>
<span>Logout</span>
</button>
</ng-template>
</mat-menu>
</mat-toolbar>

View file

@ -6,12 +6,24 @@
align-items: center;
}
.username {
margin-right: 1rem;
}
.mat-menu-item[disabled] {
color: inherit;
}
img {
height: 48px;
width: 48px;
border-radius: 20%;
}
h2 {
margin: 0;
}
.text-link {
color: inherit;
text-decoration: inherit;

View file

@ -1,15 +1,57 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { HasFailed } from 'picsur-shared/dist/types';
import { UserService } from 'src/app/api/user.service';
import { SnackBarType } from 'src/app/models/snack-bar-type';
import { UtilService } from 'src/app/util/util.service';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
constructor(private router: Router) {}
export class HeaderComponent implements OnInit {
private currentUser: EUser | null = null;
public get user() {
return this.currentUser;
}
public get isLoggedIn() {
return this.currentUser !== null;
}
constructor(
private router: Router,
private userService: UserService,
private utilService: UtilService
) {}
ngOnInit(): void {
this.subscribeUser();
}
@AutoUnsubscribe()
subscribeUser() {
return this.userService.liveUser.subscribe((user) => {
console.log('user', user);
this.currentUser = user;
});
}
doLogin() {
this.router.navigate(['/login']);
}
doLogout() {
const user = this.userService.logout();
if (HasFailed(user)) {
this.utilService.showSnackBar(user.getReason(), SnackBarType.Error);
return;
}
this.utilService.showSnackBar('Logout successful', SnackBarType.Success);
}
}

View file

@ -4,12 +4,23 @@ import { HeaderComponent } from './header.component';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
import { ApiModule } from 'src/app/api/api.module';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { UtilModule } from 'src/app/util/util.module';
@NgModule({
imports: [CommonModule, MatToolbarModule, MatButtonModule, RouterModule],
imports: [
CommonModule,
MatToolbarModule,
MatButtonModule,
RouterModule,
ApiModule,
MatIconModule,
MatMenuModule,
UtilModule,
],
declarations: [HeaderComponent],
exports: [HeaderComponent],
})
export class HeaderModule {
}
export class HeaderModule {}

View file

@ -1,8 +1,10 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { HasFailed } from 'picsur-shared/dist/types';
import { Subscription } from 'rxjs';
import { UserService } from 'src/app/api/user.service';
import { SnackBarType } from 'src/app/models/snack-bar-type';
import { UtilService } from 'src/app/util/util.service';
import { LoginControl } from './login.model';
@Component({
@ -10,23 +12,20 @@ import { LoginControl } from './login.model';
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit, OnDestroy {
private userSubscription: Subscription;
export class LoginComponent implements OnInit {
model = new LoginControl();
loginFail = false;
constructor(private userService: UserService, private router: Router) {}
constructor(
private userService: UserService,
private router: Router,
private utilService: UtilService
) {}
ngOnInit(): void {
console.log('init');
this.userSubscription = this.userService.user.subscribe((user) => {
console.log('sub', user);
});
if (this.userService.isLoggedIn) {
this.router.navigate(['/'], { replaceUrl: true });
}
ngOnDestroy(): void {
this.userSubscription.unsubscribe();
}
async onSubmit() {
@ -42,6 +41,7 @@ export class LoginComponent implements OnInit, OnDestroy {
return;
}
this.utilService.showSnackBar('Login successful', SnackBarType.Success);
this.router.navigate(['/']);
}
}

View file

@ -0,0 +1,6 @@
.mat-icon {
// Yes yes, !important is here, deal with it
// If someone wants to properly figure out why icons are escaping their 24px box, feel free to
height: initial !important;
width: initial !important;
}

View file

@ -1,2 +1,3 @@
@import "./personal.scss";
@import "./snackbar.scss";
@import "./fixes.scss";

View file

@ -5582,6 +5582,13 @@ neo-async@^2.6.2:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
ngx-auto-unsubscribe-decorator@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ngx-auto-unsubscribe-decorator/-/ngx-auto-unsubscribe-decorator-1.0.0.tgz#780830df3d5f43543b1bb9b6ed3fc9a4db5f60b4"
integrity sha512-bCXxWBE/AcmbxKSdYqNmjwDJXcUHbQvfCHGZvHiqqbGR7fwZRYMWPNxe4oiw5C+QIEYb+sOWWDOu2hQ2QhD0/w==
dependencies:
tslib "^2.1.0"
ngx-dropzone@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ngx-dropzone/-/ngx-dropzone-3.1.0.tgz#f2045a9ca90903fa96304f1b2aa33a922f14b15a"