diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 1dadeaa..b319f6d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -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 ] }, ] diff --git a/src/app/app.component.scss b/src/app/app.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e330ffb..d8b7ac3 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -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) { - 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; - } - } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index fb10c6f..6ac9910 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -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, diff --git a/src/app/auth/auth.guard.ts b/src/app/auth/auth.guard.ts index 75a5071..816f1d0 100644 --- a/src/app/auth/auth.guard.ts +++ b/src/app/auth/auth.guard.ts @@ -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 }); }); } diff --git a/src/app/pages/hot/hot.page.html b/src/app/pages/hot/hot.page.html new file mode 100644 index 0000000..24cc006 --- /dev/null +++ b/src/app/pages/hot/hot.page.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/pages/hot/hot.page.ts b/src/app/pages/hot/hot.page.ts new file mode 100644 index 0000000..5c5e7a9 --- /dev/null +++ b/src/app/pages/hot/hot.page.ts @@ -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) => { }) + } + +} diff --git a/src/app/pages/login/login.page.ts b/src/app/pages/login/login.page.ts index 5cf3edf..d0174b6 100644 --- a/src/app/pages/login/login.page.ts +++ b/src/app/pages/login/login.page.ts @@ -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 { diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index b2b5106..23ac7c3 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -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; + } } \ No newline at end of file diff --git a/src/app/services/entries.service.ts b/src/app/services/entries.service.ts index ec82b8e..57ce041 100644 --- a/src/app/services/entries.service.ts +++ b/src/app/services/entries.service.ts @@ -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); } diff --git a/src/app/ui/main/main.component.scss b/src/app/ui/main/main.component.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/ui/main/main.ui.html b/src/app/ui/main/main.ui.html index 30b027f..c96050f 100644 --- a/src/app/ui/main/main.ui.html +++ b/src/app/ui/main/main.ui.html @@ -7,7 +7,7 @@ {{'bstlboard' | i18n}} - + {{'submission' | i18n}} @@ -19,24 +19,27 @@ (click)="!isBiggerScreen() && opened=false"> - + login {{'login' | i18n}} - - trending_up {{'top' | i18n}} + + trending_up {{'page.top' | i18n}} - - history {{'new' | i18n}} + + history {{'page.new' | i18n}} - + + whatshot {{'page.hot' | i18n}} + + bookmarks {{'bookmarks' | i18n}} - + tune {{'settings' | i18n}} - + exit_to_app {{'logout' | i18n}} diff --git a/src/app/ui/main/main.ui.scss b/src/app/ui/main/main.ui.scss new file mode 100644 index 0000000..df3bb3c --- /dev/null +++ b/src/app/ui/main/main.ui.scss @@ -0,0 +1,3 @@ +mat-sidenav { + min-width: 200px; +} \ No newline at end of file diff --git a/src/app/ui/main/main.ui.ts b/src/app/ui/main/main.ui.ts index f870aa4..d2f20f8 100644 --- a/src/app/ui/main/main.ui.ts +++ b/src/app/ui/main/main.ui.ts @@ -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(() => {