feat: update Auth Guards and create main layout to simplify app.component
This commit is contained in:
parent
876aadcd05
commit
4b795ae977
13 changed files with 319 additions and 274 deletions
|
|
@ -1,40 +1 @@
|
||||||
@if (this.isLoggedIn === null) {
|
<router-outlet></router-outlet> <app-overlay></app-overlay>
|
||||||
<router-outlet></router-outlet>
|
|
||||||
<app-overlay></app-overlay>
|
|
||||||
} @else {
|
|
||||||
<div
|
|
||||||
[ngClass]="{
|
|
||||||
'blur-background': sharedService.isAnyDialogOpen
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="container">
|
|
||||||
<app-sidebar></app-sidebar>
|
|
||||||
<app-header></app-header>
|
|
||||||
<div class="main-content">
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<app-sidebar-mobile></app-sidebar-mobile>
|
|
||||||
</div>
|
|
||||||
@if (sharedService.isNewContactDialogOpen) {
|
|
||||||
<app-contact-edit
|
|
||||||
[ngClass]="{
|
|
||||||
'blur-background': sharedService.isDeleteContactDialogOpen,
|
|
||||||
'd-none': sharedService.isDeleteContactDialogOpen
|
|
||||||
}"
|
|
||||||
></app-contact-edit>
|
|
||||||
} @if (sharedService.isEditContactDialogOpen) {
|
|
||||||
<app-contact-edit
|
|
||||||
[currentUserId]="sharedService.currentUserId"
|
|
||||||
[ngClass]="{
|
|
||||||
'blur-background': sharedService.isDeleteContactDialogOpen,
|
|
||||||
'd-none': sharedService.isDeleteContactDialogOpen
|
|
||||||
}"
|
|
||||||
></app-contact-edit>
|
|
||||||
} @if (sharedService.isDeleteContactDialogOpen) {
|
|
||||||
<app-contact-delete
|
|
||||||
[currentUserId]="sharedService.currentUserId"
|
|
||||||
></app-contact-delete>
|
|
||||||
}
|
|
||||||
<app-overlay></app-overlay>
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-header {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 232px;
|
|
||||||
right: 0;
|
|
||||||
height: 96px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--white);
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-sidebar {
|
|
||||||
width: 232px;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-sidebar-mobile {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
position: fixed;
|
|
||||||
top: 96px;
|
|
||||||
left: 232px;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 64px;
|
|
||||||
background-color: var(--bgContent);
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blur-background {
|
|
||||||
filter: blur(5px);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-none {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 910px) {
|
|
||||||
.main-content {
|
|
||||||
padding: 32px;
|
|
||||||
left: 0;
|
|
||||||
bottom: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-header {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-sidebar-mobile {
|
|
||||||
display: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
|
||||||
.main-content {
|
|
||||||
top: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-header {
|
|
||||||
height: 80px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 450px) {
|
|
||||||
.main-content {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +1,14 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { HeaderComponent } from './shared/components/header/header.component';
|
|
||||||
import { SidebarComponent } from './shared/components/sidebar/sidebar.component';
|
|
||||||
import { LanguageService } from './services/language.service';
|
|
||||||
import { SidebarMobileComponent } from './shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
|
|
||||||
import { ContactEditNewComponent } from './components/contacts/contact-edit-new/contact-edit-new.component';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { SharedService } from './services/shared.service';
|
|
||||||
import { ContactDeleteComponent } from './components/contacts/contact-delete/contact-delete.component';
|
|
||||||
import { OverlayComponent } from './shared/components/overlay/overlay.component';
|
import { OverlayComponent } from './shared/components/overlay/overlay.component';
|
||||||
import { FirebaseService } from './services/firebase.service';
|
|
||||||
import { filter } from 'rxjs';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [RouterOutlet, OverlayComponent],
|
||||||
RouterOutlet,
|
|
||||||
HeaderComponent,
|
|
||||||
SidebarComponent,
|
|
||||||
SidebarMobileComponent,
|
|
||||||
ContactEditNewComponent,
|
|
||||||
ContactDeleteComponent,
|
|
||||||
CommonModule,
|
|
||||||
OverlayComponent,
|
|
||||||
],
|
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss',
|
styleUrl: './app.component.scss',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'join';
|
title = 'join';
|
||||||
isLoggedIn: string = '';
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public langService: LanguageService,
|
|
||||||
public sharedService: SharedService,
|
|
||||||
private firebaseService: FirebaseService,
|
|
||||||
private router: Router
|
|
||||||
) {
|
|
||||||
this.checkAndClearLocalStorage();
|
|
||||||
this.isLoggedIn = this.firebaseService.getCurrentUserId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lifecycle hook that is called after the component has been initialized.
|
|
||||||
*
|
|
||||||
* Checks if the user is logged in by verifying the `isLoggedIn` property.
|
|
||||||
*/
|
|
||||||
ngOnInit() {
|
|
||||||
if (this.isLoggedIn === null) {
|
|
||||||
this.checkPwResetRoute();
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/summary']);
|
|
||||||
this.sharedService.isBtnDisabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the local storage after a certain time period (12h) has
|
|
||||||
* passed since the last login.
|
|
||||||
*/
|
|
||||||
checkAndClearLocalStorage() {
|
|
||||||
const startTime = localStorage.getItem('sessionTimeJOIN');
|
|
||||||
|
|
||||||
if (startTime) {
|
|
||||||
const startTimeMills = parseInt(startTime);
|
|
||||||
const currentTime = new Date().getTime();
|
|
||||||
const timeDifference = 12 * 60 * 60 * 1000; // 12h
|
|
||||||
if (currentTime - startTimeMills > timeDifference) {
|
|
||||||
localStorage.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens to router events and checks if the current route is one of the
|
|
||||||
* allowed routes for password reset, registration, forgotten password,
|
|
||||||
* or login. If it is not, it navigates to the login page.
|
|
||||||
*/
|
|
||||||
checkPwResetRoute() {
|
|
||||||
this.router.events
|
|
||||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
|
||||||
.subscribe(() => {
|
|
||||||
const urlTree = this.router.parseUrl(this.router.url);
|
|
||||||
const firstSegment = this.getFirstSegment(urlTree);
|
|
||||||
const allowedRoutes = ['pw-reset', 'register', 'forgot-pw', 'login'];
|
|
||||||
|
|
||||||
if (!allowedRoutes.includes(firstSegment)) {
|
|
||||||
this.router.navigate(['/login']);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first segment of the current URL or an empty string if it does not exist.
|
|
||||||
*
|
|
||||||
* @param urlTree The current URL tree.
|
|
||||||
* @returns The first segment of the current URL or an empty string if it does not exist.
|
|
||||||
*/
|
|
||||||
private getFirstSegment(urlTree: any): string {
|
|
||||||
return urlTree.root.children['primary']?.segments[0]?.path || '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,41 +12,42 @@ import { LoginComponent } from './components/login/login.component';
|
||||||
import { RegisterComponent } from './components/login/register/register.component';
|
import { RegisterComponent } from './components/login/register/register.component';
|
||||||
import { ForgotPwComponent } from './components/login/forgot-pw/forgot-pw.component';
|
import { ForgotPwComponent } from './components/login/forgot-pw/forgot-pw.component';
|
||||||
import { PwResetComponent } from './components/login/forgot-pw/pw-reset/pw-reset.component';
|
import { PwResetComponent } from './components/login/forgot-pw/pw-reset/pw-reset.component';
|
||||||
import { AuthGuard } from './auth.guard';
|
import { AuthenticatedGuard } from './guards/authenticated.guard';
|
||||||
|
import { RedirectIfAuthenticatedGuard } from './guards/redirect-if-authenticated.guard';
|
||||||
|
import { MainLayoutComponent } from './components/main-layout/main-layout.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: LoginComponent },
|
// Parent route for all pages that do not require authenticated access
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
canActivate: [RedirectIfAuthenticatedGuard],
|
||||||
|
children: [
|
||||||
|
{ path: '', component: LoginComponent, pathMatch: 'full' },
|
||||||
{ path: 'login', component: LoginComponent },
|
{ path: 'login', component: LoginComponent },
|
||||||
{ path: 'login/notice/:id', component: LoginComponent },
|
{ path: 'login/notice/:id', component: LoginComponent },
|
||||||
{ path: 'login/imprint', component: ImprintComponent },
|
|
||||||
{ path: 'login/privacy-policy', component: PrivacyPolicyComponent },
|
|
||||||
{ path: 'register', component: RegisterComponent },
|
{ path: 'register', component: RegisterComponent },
|
||||||
{ path: 'forgot-pw', component: ForgotPwComponent },
|
{ path: 'forgot-pw', component: ForgotPwComponent },
|
||||||
{ path: 'pw-reset', component: PwResetComponent },
|
{ path: 'pw-reset', component: PwResetComponent },
|
||||||
{ path: 'summary', component: SummaryComponent, canActivate: [AuthGuard] },
|
{ path: 'login/imprint', component: ImprintComponent },
|
||||||
{ path: 'add-task', component: AddTaskComponent, canActivate: [AuthGuard] },
|
{ path: 'login/privacy-policy', component: PrivacyPolicyComponent },
|
||||||
{
|
],
|
||||||
path: 'add-task/:id',
|
|
||||||
component: AddTaskComponent,
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
},
|
},
|
||||||
{ path: 'board', component: BoardComponent, canActivate: [AuthGuard] },
|
// Protected routes with MainLayout
|
||||||
{
|
{
|
||||||
path: 'task/:id',
|
path: '',
|
||||||
component: TaskOverlayComponent,
|
component: MainLayoutComponent,
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthenticatedGuard],
|
||||||
},
|
children: [
|
||||||
{
|
{ path: 'summary', component: SummaryComponent },
|
||||||
path: 'task-edit/:id',
|
{ path: 'add-task', component: AddTaskComponent },
|
||||||
component: TaskEditOverlayComponent,
|
{ path: 'add-task/:id', component: AddTaskComponent },
|
||||||
canActivate: [AuthGuard],
|
{ path: 'board', component: BoardComponent },
|
||||||
},
|
{ path: 'task/:id', component: TaskOverlayComponent },
|
||||||
{ path: 'contacts', component: ContactsComponent, canActivate: [AuthGuard] },
|
{ path: 'task-edit/:id', component: TaskEditOverlayComponent },
|
||||||
{ path: 'help', component: HelpComponent, canActivate: [AuthGuard] },
|
{ path: 'contacts', component: ContactsComponent },
|
||||||
{ path: 'imprint', component: ImprintComponent, canActivate: [AuthGuard] },
|
{ path: 'help', component: HelpComponent },
|
||||||
{
|
{ path: 'imprint', component: ImprintComponent },
|
||||||
path: 'privacy-policy',
|
{ path: 'privacy-policy', component: PrivacyPolicyComponent },
|
||||||
component: PrivacyPolicyComponent,
|
],
|
||||||
canActivate: [AuthGuard],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
import { CanActivateFn } from '@angular/router';
|
|
||||||
|
|
||||||
import { authGuard } from './auth.guard';
|
|
||||||
|
|
||||||
describe('authGuard', () => {
|
|
||||||
const executeGuard: CanActivateFn = (...guardParameters) =>
|
|
||||||
TestBed.runInInjectionContext(() => authGuard(...guardParameters));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(executeGuard).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -46,6 +46,9 @@ export class LoginComponent {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
if (this.loginService.checkAuthUser()) {
|
||||||
|
this.router.navigate(['/summary']);
|
||||||
|
}
|
||||||
this.routeParams();
|
this.routeParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
34
src/app/components/main-layout/main-layout.component.html
Normal file
34
src/app/components/main-layout/main-layout.component.html
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<div
|
||||||
|
[ngClass]="{
|
||||||
|
'blur-background': sharedService.isAnyDialogOpen
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="container">
|
||||||
|
<app-sidebar></app-sidebar>
|
||||||
|
<app-header></app-header>
|
||||||
|
<div class="main-content">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<app-sidebar-mobile></app-sidebar-mobile>
|
||||||
|
</div>
|
||||||
|
@if (sharedService.isNewContactDialogOpen) {
|
||||||
|
<app-contact-edit
|
||||||
|
[ngClass]="{
|
||||||
|
'blur-background': sharedService.isDeleteContactDialogOpen,
|
||||||
|
'd-none': sharedService.isDeleteContactDialogOpen
|
||||||
|
}"
|
||||||
|
></app-contact-edit>
|
||||||
|
} @if (sharedService.isEditContactDialogOpen) {
|
||||||
|
<app-contact-edit
|
||||||
|
[currentUserId]="sharedService.currentUserId"
|
||||||
|
[ngClass]="{
|
||||||
|
'blur-background': sharedService.isDeleteContactDialogOpen,
|
||||||
|
'd-none': sharedService.isDeleteContactDialogOpen
|
||||||
|
}"
|
||||||
|
></app-contact-edit>
|
||||||
|
} @if (sharedService.isDeleteContactDialogOpen) {
|
||||||
|
<app-contact-delete
|
||||||
|
[currentUserId]="sharedService.currentUserId"
|
||||||
|
></app-contact-delete>
|
||||||
|
}
|
||||||
80
src/app/components/main-layout/main-layout.component.scss
Normal file
80
src/app/components/main-layout/main-layout.component.scss
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 232px;
|
||||||
|
right: 0;
|
||||||
|
height: 96px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--white);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-sidebar {
|
||||||
|
width: 232px;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-sidebar-mobile {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
position: fixed;
|
||||||
|
top: 96px;
|
||||||
|
left: 232px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 64px;
|
||||||
|
background-color: var(--bgContent);
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blur-background {
|
||||||
|
filter: blur(5px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 910px) {
|
||||||
|
.main-content {
|
||||||
|
padding: 32px;
|
||||||
|
left: 0;
|
||||||
|
bottom: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-header {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-sidebar-mobile {
|
||||||
|
display: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 700px) {
|
||||||
|
.main-content {
|
||||||
|
top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
app-header {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 450px) {
|
||||||
|
.main-content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/app/components/main-layout/main-layout.component.spec.ts
Normal file
23
src/app/components/main-layout/main-layout.component.spec.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MainLayoutComponent } from './main-layout.component';
|
||||||
|
|
||||||
|
describe('MainLayoutComponent', () => {
|
||||||
|
let component: MainLayoutComponent;
|
||||||
|
let fixture: ComponentFixture<MainLayoutComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MainLayoutComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MainLayoutComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
101
src/app/components/main-layout/main-layout.component.ts
Normal file
101
src/app/components/main-layout/main-layout.component.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { filter } from 'rxjs';
|
||||||
|
import { FirebaseService } from '../../services/firebase.service';
|
||||||
|
import { LanguageService } from '../../services/language.service';
|
||||||
|
import { SharedService } from '../../services/shared.service';
|
||||||
|
import { SidebarMobileComponent } from '../../shared/components/sidebar/sidebar-mobile/sidebar-mobile.component';
|
||||||
|
import { SidebarComponent } from '../../shared/components/sidebar/sidebar.component';
|
||||||
|
import { ContactDeleteComponent } from '../contacts/contact-delete/contact-delete.component';
|
||||||
|
import { ContactEditNewComponent } from '../contacts/contact-edit-new/contact-edit-new.component';
|
||||||
|
import { HeaderComponent } from '../../shared/components/header/header.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-main-layout',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
RouterOutlet,
|
||||||
|
HeaderComponent,
|
||||||
|
SidebarComponent,
|
||||||
|
SidebarMobileComponent,
|
||||||
|
ContactEditNewComponent,
|
||||||
|
ContactDeleteComponent,
|
||||||
|
CommonModule,
|
||||||
|
],
|
||||||
|
templateUrl: './main-layout.component.html',
|
||||||
|
styleUrl: './main-layout.component.scss',
|
||||||
|
})
|
||||||
|
export class MainLayoutComponent {
|
||||||
|
isLoggedIn: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public langService: LanguageService,
|
||||||
|
public sharedService: SharedService,
|
||||||
|
private firebaseService: FirebaseService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
this.checkAndClearLocalStorage();
|
||||||
|
this.isLoggedIn = this.firebaseService.getCurrentUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifecycle hook that is called after the component has been initialized.
|
||||||
|
*
|
||||||
|
* Checks if the user is logged in by verifying the `isLoggedIn` property.
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
if (this.isLoggedIn === null) {
|
||||||
|
this.checkPwResetRoute();
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/summary']);
|
||||||
|
this.sharedService.isBtnDisabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the local storage after a certain time period (12h) has
|
||||||
|
* passed since the last login.
|
||||||
|
*/
|
||||||
|
checkAndClearLocalStorage() {
|
||||||
|
const startTime = localStorage.getItem('sessionTimeJOIN');
|
||||||
|
|
||||||
|
if (startTime) {
|
||||||
|
const startTimeMills = parseInt(startTime);
|
||||||
|
const currentTime = new Date().getTime();
|
||||||
|
const timeDifference = 12 * 60 * 60 * 1000; // 12h
|
||||||
|
if (currentTime - startTimeMills > timeDifference) {
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to router events and checks if the current route is one of the
|
||||||
|
* allowed routes for password reset, registration, forgotten password,
|
||||||
|
* or login. If it is not, it navigates to the login page.
|
||||||
|
*/
|
||||||
|
checkPwResetRoute() {
|
||||||
|
this.router.events
|
||||||
|
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||||
|
.subscribe(() => {
|
||||||
|
const urlTree = this.router.parseUrl(this.router.url);
|
||||||
|
const firstSegment = this.getFirstSegment(urlTree);
|
||||||
|
const allowedRoutes = ['pw-reset', 'register', 'forgot-pw', 'login'];
|
||||||
|
|
||||||
|
if (!allowedRoutes.includes(firstSegment)) {
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first segment of the current URL or an empty string if it does not exist.
|
||||||
|
*
|
||||||
|
* @param urlTree The current URL tree.
|
||||||
|
* @returns The first segment of the current URL or an empty string if it does not exist.
|
||||||
|
*/
|
||||||
|
private getFirstSegment(urlTree: any): string {
|
||||||
|
return urlTree.root.children['primary']?.segments[0]?.path || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/app/guards/authenticated.guard.ts
Normal file
28
src/app/guards/authenticated.guard.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { catchError, map, Observable, of } from 'rxjs';
|
||||||
|
import { LoginService } from '../services/auth.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthenticatedGuard {
|
||||||
|
constructor(private loginService: LoginService, private router: Router) {}
|
||||||
|
|
||||||
|
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return this.loginService.checkAuthUser().pipe(
|
||||||
|
map((isAuthenticated) => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError(() => {
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
return of(false);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,32 +2,25 @@ import { Injectable } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { map, catchError } from 'rxjs/operators';
|
import { map, catchError } from 'rxjs/operators';
|
||||||
import { LoginService } from './services/auth.service';
|
import { LoginService } from '../services/auth.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthGuard {
|
export class RedirectIfAuthenticatedGuard {
|
||||||
constructor(private loginService: LoginService, private router: Router) {}
|
constructor(private loginService: LoginService, private router: Router) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* The canActivate guard checks if the user is authenticated.
|
|
||||||
* If the user is authenticated, the guard returns true.
|
|
||||||
* If the user is not authenticated, the guard navigates to the root route and returns false.
|
|
||||||
* If an error occurs during the authentication check, the guard navigates to the root route and returns false.
|
|
||||||
*/
|
|
||||||
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
return this.loginService.checkAuthUser().pipe(
|
return this.loginService.checkAuthUser().pipe(
|
||||||
map((isAuthenticated) => {
|
map((isAuthenticated) => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
return true;
|
this.router.navigate(['/summary']);
|
||||||
} else {
|
|
||||||
this.router.navigate(['/']);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}),
|
}),
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/login']);
|
||||||
return of(false);
|
return of(false);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -50,8 +50,16 @@ export class LoginService {
|
||||||
*/
|
*/
|
||||||
checkAuthUser(): Observable<boolean> {
|
checkAuthUser(): Observable<boolean> {
|
||||||
return this.firebaseService.getAuthUser().pipe(
|
return this.firebaseService.getAuthUser().pipe(
|
||||||
map((user) => !!user),
|
map((user) => {
|
||||||
catchError(() => of(false))
|
if (user) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError(() => {
|
||||||
|
return of(false);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue