new auth, hot entries

This commit is contained in:
_Bastler 2021-10-05 14:39:31 +02:00
parent 8cc7978954
commit 1da5fc638c
14 changed files with 149 additions and 128 deletions

View File

@ -5,6 +5,7 @@ import { AuthGuard, AuthUpdateGuard, AuthenticatedGuard, AnonymousGuard } from '
import { PageBookmarks } from './pages/bookmarks/bookmarks.page';
import { PageComment } from './pages/comment/comment.page';
import { PageEntry } from './pages/entry/entry.page';
import { PageHot } from './pages/hot/hot.page';
import { PageLogin } from './pages/login/login.page';
import { PageNew } from './pages/new/new.page';
import { PageNotFound } from './pages/notfound/notfound.page';
@ -27,11 +28,12 @@ const routes: Routes = [
{ path: 'u/:username', component: PageUser, canActivate: [ AuthenticatedGuard ] },
{ path: 'u/c/:username', component: PageUserComments, canActivate: [ AuthenticatedGuard ] },
{ path: 'u/e/:username', component: PageUserEntries, canActivate: [ AuthenticatedGuard ] },
{ path: 'hot', component: PageHot, canActivate: [ AuthenticatedGuard ] },
{ path: 'new', component: PageNew, canActivate: [ AuthenticatedGuard ] },
{ path: 'bookmarks', component: PageBookmarks, canActivate: [ AuthenticatedGuard ] },
{ path: 'submit', component: PageSubmission, canActivate: [ AuthenticatedGuard ] },
{ path: 'login', component: PageLogin, canActivate: [ AnonymousGuard ] },
{ path: 'settings', component: PageSettings, canActivate: [ AuthenticatedGuard ] },
{ path: 'settings', component: PageSettings, canActivate: [ AuthenticatedGuard ] },
{ path: 'unavailable', component: PageUnavailable },
{ path: '**', component: PageNotFound, pathMatch: 'full', canActivate: [ AuthUpdateGuard ] },
]

View File

@ -1,114 +1,19 @@
import { Component, HostListener } from '@angular/core';
import { Component } from '@angular/core';
import { AuthService } from './services/auth.service';
import { I18nService } from './services/i18n.service';
import { Router } from '@angular/router';
import { DomSanitizer } from '@angular/platform-browser';
import { MatIconRegistry } from '@angular/material/icon';
import { DateAdapter } from '@angular/material/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.scss' ]
templateUrl: './app.component.html'
})
export class AppComponent {
opened = true;
darkTheme = "false";
title = 'bstlboard';
currentLocale: String;
datetimeformat: String;
locales;
auth;
constructor(
private i18n: I18nService,
private authService: AuthService,
private router: Router,
private iconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer,
private _adapter: DateAdapter<any>) {
iconRegistry.addSvgIcon('logo', sanitizer.bypassSecurityTrustResourceUrl('assets/icons/logo.svg'));
constructor(private i18n: I18nService) {
}
ngOnInit() {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.currentLocale = this.i18n.getLocale();
this.locales = this.i18n.getLocales();
this.authService.auth.subscribe(data => {
this.auth = data;
})
this._adapter.setLocale(this.currentLocale);
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
if (width < 768) {
this.opened = false;
} else {
this.opened = true;
}
if (localStorage.getItem("bstlboard.darkTheme") == "true") {
this.darkTheme = "true";
window.document.body.classList.add("dark-theme");
}
window.document.title = this.i18n.get('bstlboard', []);
}
setLocale(locale) {
localStorage.setItem("bstlboard.locale", locale);
if (this.auth && this.auth.authenticated) {
window.location.reload();
} else {
window.location.reload();
}
}
darkThemeChange($event) {
if ($event.checked) {
this.darkTheme = "true";
} else {
this.darkTheme = "false";
}
localStorage.setItem("bstlboard.darkTheme", this.darkTheme);
if (this.auth && this.auth.authenticated) {
window.location.reload();
} else {
window.location.reload();
}
}
logout() {
this.authService.logout().subscribe(data => {
this.router.navigate([ "" ]).then(() => {
window.location.reload();
});
})
}
isBiggerScreen() {
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
if (width < 768) {
return false;
} else {
return true;
}
}
openExternal(url, target = '_self') {
window.open(url, target);
}
@HostListener('window:resize', [ '$event' ])
onResize(event) {
if (event.target.innerWidth < 768) {
this.opened = false;
} else {
this.opened = true;
}
}
}

View File

@ -19,6 +19,7 @@ import { AppComponent } from './app.component';
import { PageBookmarks } from './pages/bookmarks/bookmarks.page';
import { PageComment } from './pages/comment/comment.page';
import { PageEntry } from './pages/entry/entry.page';
import { PageHot } from './pages/hot/hot.page';
import { PageLogin } from './pages/login/login.page';
import { PageNew } from './pages/new/new.page';
import { PageNotFound } from './pages/notfound/notfound.page'
@ -80,6 +81,7 @@ export class XhrInterceptor implements HttpInterceptor {
PageBookmarks,
PageComment,
PageEntry,
PageHot,
PageLogin,
PageNew,
PageNotFound,

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { AuthService, RequestError } from '../services/auth.service';
import { UserService } from '../services/user.service';
import { I18nService } from '../services/i18n.service';
@ -29,6 +29,10 @@ export class AuthGuard implements CanActivate {
return this.authService.getAuth().then(response => {
return true;
}).catch(function (error) {
if (error instanceof RequestError && (error as RequestError).getResponse().status == 401) {
return true;
}
return that.router.navigateByUrl(that.router.parseUrl('/unavailable?target=' + next.url), { skipLocationChange: true });
});
}
@ -43,10 +47,6 @@ export class AuthenticatedGuard implements CanActivate {
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const that = this;
return this.authService.getAuth().then((data: any) => {
if (!data || !data.authenticated) {
return that.router.navigateByUrl(that.router.parseUrl('/login?target=' + encodeURIComponent(state.url)), { skipLocationChange: true, replaceUrl: true });
}
this.userService.get().subscribe((user: any) => {
let updateLocale = false;
let updateTheme = false;
@ -72,9 +72,12 @@ export class AuthenticatedGuard implements CanActivate {
}
});
return true;
}).catch(function (error) {
if (error instanceof RequestError && (error as RequestError).getResponse().status == 401) {
return that.router.navigateByUrl(that.router.parseUrl('/login?target=' + encodeURIComponent(state.url)), { skipLocationChange: true, replaceUrl: true });
}
return that.router.navigateByUrl(that.router.parseUrl('/unavailable?target=' + next.url), { skipLocationChange: true });
});
}
@ -89,12 +92,12 @@ export class AnonymousGuard implements CanActivate {
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const that = this;
return this.authService.getAuth().then((data: any) => {
if (data && data.authenticated) {
this.router.navigateByUrl('/');
return false;
}
return true;
this.router.navigateByUrl('/');
return false;
}).catch(function (error) {
if (error instanceof RequestError && (error as RequestError).getResponse().status == 401) {
return true;
}
return that.router.navigateByUrl(that.router.parseUrl('/unavailable?target=' + next.url), { replaceUrl: true });
});
}

View File

@ -0,0 +1 @@
<ui-entries [entries]="entries" [refresh]="boundRefresh" [update]="boundUpdate"></ui-entries>

View File

@ -0,0 +1,81 @@
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { EntriesService } from '../../services/entries.service';
import { PageEvent } from '@angular/material/paginator';
@Component({
selector: 'page-hot',
templateUrl: './hot.page.html'
})
export class PageHot implements OnInit {
entries: any;
boundRefresh: Function;
boundUpdate: Function;
init: boolean = true;
constructor(private entriesService: EntriesService, private router: Router, private route: ActivatedRoute) { }
ngOnInit(): void {
this.boundRefresh = this.refresh.bind(this);
this.boundUpdate = this.update.bind(this);
this.route.queryParams.subscribe(params => {
if (this.init) {
this.entries = {};
if (params[ 'p' ]) {
this.entries.number = +params[ 'p' ] - 1;
if (this.entries.number < 0) {
this.entries.number = 0;
}
}
if (params[ 's' ]) {
this.entries.size = +params[ 's' ];
}
this.refresh();
this.init = false;
}
});
}
refresh(): void {
if (!this.entries) {
this.entries = {};
}
this.entries.content = null;
this.entriesService.getComments(this.entries.number || 0, this.entries.size || 30).subscribe((data: any) => {
this.entries = data;
}, (error) => { })
}
update(event: PageEvent) {
this.entries.content = null;
const params: any = { p: null, s: null };
if (event.pageIndex != 0) {
params.p = event.pageIndex + 1;
}
if (event.pageSize != 30) {
params.s = event.pageSize;
}
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams: params,
queryParamsHandling: 'merge'
});
this.entriesService.getComments(event.pageIndex, event.pageSize).subscribe((data: any) => {
this.entries = data;
}, (error) => { })
}
}

View File

@ -64,7 +64,6 @@ export class PageLogin implements OnInit {
}
externalLogin(client: any): void {
console.log(this.autologin);
if (this.autologin) {
localStorage.setItem("bstlboard.autologin", client.id);
} else {

View File

@ -19,7 +19,7 @@ export class AuthService {
this.auth.next(data);
return data;
}, error => {
throw new Error(error);
throw new RequestError(error);
});
}
@ -35,4 +35,20 @@ export class AuthService {
return this.http.post(environment.apiUrl + "/logout", {});
}
}
export class RequestError extends Error {
response : any;
constructor(response : any) {
super(response.message);
this.response = response;
// Set the prototype explicitly.
Object.setPrototypeOf(this, RequestError.prototype);
}
getResponse() : any {
return this.response;
}
}

View File

@ -14,6 +14,10 @@ export class EntriesService {
return this.http.get(environment.apiUrl + "/entries?page=" + page + "&size=" + size);
}
getComments(page: number, size: number) {
return this.http.get(environment.apiUrl + "/entries/comments?page=" + page + "&size=" + size);
}
getNew(page: number, size: number) {
return this.http.get(environment.apiUrl + "/entries/new?page=" + page + "&size=" + size);
}

View File

@ -7,7 +7,7 @@
{{'bstlboard' | i18n}}
</span>
<span class="spacer"></span>
<ng-container *ngIf="auth && auth.authenticated">
<ng-container *ngIf="authenticated">
<a routerLink="/submit" mat-raised-button color="accent">{{'submission' |
i18n}}</a>
</ng-container>
@ -19,24 +19,27 @@
(click)="!isBiggerScreen() && opened=false">
<mat-nav-list>
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-list-item>
<a *ngIf="!authenticated" routerLink="/login" routerLinkActive="active" mat-list-item>
<mat-icon>login</mat-icon> {{'login' | i18n}}
</a>
<a *ngIf="auth && auth.authenticated" routerLink="/" routerLinkActive="active" mat-list-item>
<mat-icon>trending_up</mat-icon> {{'top' | i18n}}
<a *ngIf="authenticated" routerLink="/" routerLinkActive="active" mat-list-item>
<mat-icon>trending_up</mat-icon> {{'page.top' | i18n}}
</a>
<a *ngIf="auth && auth.authenticated" routerLink="/new" routerLinkActive="active" mat-list-item>
<mat-icon>history</mat-icon> {{'new' | i18n}}
<a *ngIf="authenticated" routerLink="/new" routerLinkActive="active" mat-list-item>
<mat-icon>history</mat-icon> {{'page.new' | i18n}}
</a>
<a *ngIf="auth && auth.authenticated" routerLink="/bookmarks" routerLinkActive="active" mat-list-item>
<a *ngIf="authenticated" routerLink="/hot" routerLinkActive="active" mat-list-item>
<mat-icon>whatshot</mat-icon> {{'page.hot' | i18n}}
</a>
<a *ngIf="authenticated" routerLink="/bookmarks" routerLinkActive="active" mat-list-item>
<mat-icon>bookmarks</mat-icon> {{'bookmarks' | i18n}}
</a>
<mat-divider></mat-divider>
<a *ngIf="auth && auth.authenticated" routerLink="/settings" routerLinkActive="active" mat-list-item>
<a *ngIf="authenticated" routerLink="/settings" routerLinkActive="active" mat-list-item>
<mat-icon>tune</mat-icon> {{'settings' | i18n}}
</a>
<mat-divider></mat-divider>
<a *ngIf="auth && auth.authenticated" (click)="logout()" mat-list-item>
<a *ngIf="authenticated" (click)="logout()" mat-list-item>
<mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}}
</a>
</mat-nav-list>

View File

@ -0,0 +1,3 @@
mat-sidenav {
min-width: 200px;
}

View File

@ -10,7 +10,8 @@ import { DateAdapter } from '@angular/material/core';
@Component({
selector: 'ui-main',
templateUrl: './main.ui.html'
templateUrl: './main.ui.html',
styleUrls : ['./main.ui.scss']
})
export class UiMain {
@ -20,7 +21,7 @@ export class UiMain {
currentLocale: String;
datetimeformat: String;
locales;
auth;
authenticated : boolean = false;
constructor(
private i18n: I18nService,
@ -38,7 +39,9 @@ export class UiMain {
this.currentLocale = this.i18n.getLocale();
this.locales = this.i18n.getLocales();
this.authService.auth.subscribe(data => {
this.auth = data;
this.authenticated = true;
}, (error) => {
this.authenticated = false;
})
this._adapter.setLocale(this.currentLocale);
@ -59,8 +62,7 @@ export class UiMain {
setLocale(locale) {
localStorage.setItem("bstlboard.locale", locale);
if (this.auth && this.auth.authenticated) {
if (this.authenticated) {
this.userService.get().subscribe((user: any) => {
user.locale = locale;
this.userService.update(user).subscribe(() => {
@ -77,7 +79,7 @@ export class UiMain {
localStorage.setItem("bstlboard.darkTheme", this.darkTheme ? "true" : "false");
if (this.auth && this.auth.authenticated) {
if (this.authenticated) {
this.userService.get().subscribe((user: any) => {
user.darkTheme = this.darkTheme;
this.userService.update(user).subscribe(() => {