Skip to content

Commit b0a6d4f

Browse files
committed
Merge branch 'main' into testing
2 parents 37e1935 + 0c24387 commit b0a6d4f

20 files changed

+436
-147
lines changed

compose.dev.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ services:
4848
- /app/node_modules
4949
- ./services/collaboration:/app
5050

51+
collaboration-db:
52+
ports:
53+
- 27020:27017
54+
5155
history:
5256
command: npm run dev
5357
ports:

frontend/src/_services/authentication.service.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
11
// Modified from https://jasonwatmore.com/post/2022/11/15/angular-14-jwt-authentication-example-tutorial#login-component-ts
2-
import { Injectable } from '@angular/core';
2+
import { DestroyRef, inject, Injectable } from '@angular/core';
33
import { Router } from '@angular/router';
44
import { HttpClient } from '@angular/common/http';
5-
import { BehaviorSubject, Observable } from 'rxjs';
5+
import { BehaviorSubject, Observable, timer } from 'rxjs';
6+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
67
import { map, switchMap } from 'rxjs/operators';
78
import { UServRes } from '../_models/user.service.model';
89
import { User } from '../_models/user.model';
910
import { ApiService } from './api.service';
11+
import { ToastService } from './toast.service';
1012

1113
@Injectable({ providedIn: 'root' })
1214
export class AuthenticationService extends ApiService {
1315
protected apiPath = 'user';
1416

17+
private destroyRef = inject(DestroyRef);
18+
1519
private userSubject: BehaviorSubject<User | null>;
1620
public user$: Observable<User | null>;
1721

1822
constructor(
1923
private router: Router,
2024
private http: HttpClient,
25+
private toastService: ToastService,
2126
) {
2227
super();
2328
const userData = localStorage.getItem('user');
@@ -53,6 +58,8 @@ export class AuthenticationService extends ApiService {
5358
}
5459
localStorage.setItem('user', JSON.stringify(user));
5560
this.userSubject.next(user);
61+
this.startTokenExpiryCheck();
62+
5663
return user;
5764
}),
5865
);
@@ -119,4 +126,41 @@ export class AuthenticationService extends ApiService {
119126
}),
120127
);
121128
}
129+
130+
displaySessionExpiryWarning(): void {
131+
this.toastService.showToast('Your session will expire in less than 5 minutes. Please log in again.');
132+
}
133+
134+
public startTokenExpiryCheck(): void {
135+
const tokenExpirationTime = this.getTokenExpiration();
136+
if (!tokenExpirationTime) {
137+
this.logout();
138+
return;
139+
}
140+
141+
const oneMinute = 60 * 1000;
142+
const timeLeft = tokenExpirationTime - Date.now();
143+
if (timeLeft > 5 * oneMinute) {
144+
timer(timeLeft - 5 * oneMinute)
145+
.pipe(takeUntilDestroyed(this.destroyRef))
146+
.subscribe(() => this.displaySessionExpiryWarning());
147+
} else {
148+
this.displaySessionExpiryWarning();
149+
}
150+
151+
timer(timeLeft)
152+
.pipe(takeUntilDestroyed(this.destroyRef))
153+
.subscribe(() => {
154+
alert('Your session has expired. Please log in again.');
155+
this.logout();
156+
});
157+
}
158+
159+
private getTokenExpiration() {
160+
const user = this.userValue;
161+
if (!user || !user.accessToken) return null;
162+
163+
const tokenPayload = JSON.parse(atob(user.accessToken.split('.')[1]));
164+
return tokenPayload.exp ? tokenPayload.exp * 1000 : null;
165+
}
122166
}

frontend/src/_services/history.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
22
import { HttpClient } from '@angular/common/http';
33
import { Observable } from 'rxjs';
44
import { map } from 'rxjs/operators';
5-
import { historyResponse, MatchingHistory } from '../app/account/history/history.model';
5+
import { historyResponse, MatchingHistory } from '../app/history/history.model';
66
import { ApiService } from './api.service';
77

88
@Injectable({
@@ -22,7 +22,8 @@ export class HistoryService extends ApiService {
2222
id: item._id,
2323
roomId: item.roomId,
2424
collaborator: item.collaborator.username,
25-
question: item.question,
25+
title: item.question.title,
26+
description: item.question.description,
2627
topics: item.question.topics,
2728
difficulty: item.question.difficulty,
2829
status: item.status,

frontend/src/app/account/account.component.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { LoginComponent } from './login/login.component';
55
import { RegisterComponent } from './register/register.component';
66
import { LayoutComponent } from './layout.component';
77
import { ProfileComponent } from './profile/profile.component';
8-
import { HistoryComponent } from './history/history.component';
98

109
const routes: Routes = [
1110
{
@@ -16,7 +15,6 @@ const routes: Routes = [
1615
{ path: 'login', component: LoginComponent },
1716
{ path: 'register', component: RegisterComponent },
1817
{ path: 'profile', component: ProfileComponent },
19-
{ path: 'history', component: HistoryComponent },
2018
],
2119
},
2220
];

frontend/src/app/account/account.module.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { RegisterComponent } from './register/register.component';
77
import { LayoutComponent } from './layout.component';
88
import { AccountRoutingModule } from './account.component';
99
import { ProfileComponent } from './profile/profile.component';
10-
import { HistoryComponent } from './history/history.component';
1110

1211
@NgModule({
1312
imports: [
@@ -18,7 +17,6 @@ import { HistoryComponent } from './history/history.component';
1817
LoginComponent,
1918
RegisterComponent,
2019
ProfileComponent,
21-
HistoryComponent,
2220
],
2321
})
2422
export class AccountModule {}

frontend/src/app/account/history/history.component.html

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<main>
22
<app-navigation-bar />
3+
<p-toast></p-toast>
34
<router-outlet />
45
</main>

frontend/src/app/app.component.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
import { Component } from '@angular/core';
1+
import { Component, OnInit } from '@angular/core';
22
import { RouterOutlet } from '@angular/router';
33
import { ButtonModule } from 'primeng/button';
44
import { PasswordModule } from 'primeng/password';
5+
import { ToastModule } from 'primeng/toast';
56
import { NavigationBarComponent } from './navigation-bar/navigation-bar.component';
6-
7+
import { AuthenticationService } from '../_services/authentication.service';
8+
import { MessageService } from 'primeng/api';
79
@Component({
810
selector: 'app-root',
911
standalone: true,
10-
imports: [NavigationBarComponent, RouterOutlet, ButtonModule, PasswordModule],
12+
imports: [NavigationBarComponent, RouterOutlet, ButtonModule, PasswordModule, ToastModule],
13+
providers: [MessageService],
1114
templateUrl: './app.component.html',
1215
styleUrl: './app.component.css',
1316
})
14-
export class AppComponent {
17+
export class AppComponent implements OnInit {
1518
title = 'frontend';
19+
20+
constructor(private authService: AuthenticationService) {}
21+
ngOnInit() {
22+
this.authService.startTokenExpiryCheck();
23+
}
1624
}

frontend/src/app/app.routes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { MatchingComponent } from './matching/matching.component';
55
import { HomeComponent } from './home/home.component';
66
import { AuthGuardService } from '../_services/auth.guard.service';
77
import { CollabGuardService } from '../_services/collab.guard.service';
8+
import { HistoryComponent } from './history/history.component';
89

910
const accountModule = () => import('./account/account.module').then(x => x.AccountModule);
1011

@@ -33,6 +34,11 @@ export const routes: Routes = [
3334
component: HomeComponent,
3435
canActivate: [AuthGuardService],
3536
},
37+
{
38+
path: 'history',
39+
component: HistoryComponent,
40+
canActivate: [AuthGuardService],
41+
},
3642
{
3743
path: '**',
3844
redirectTo: '/home',

frontend/src/app/account/history/history.component.css renamed to frontend/src/app/history/history.component.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
.container {
2+
min-height: calc(100dvh - 160px);
3+
width: 100%;
4+
justify-content: center;
5+
align-items: center;
6+
padding: 1rem;
7+
margin-top: auto;
8+
}
9+
110
.sliding-panel {
211
position: fixed;
312
top: 0;
@@ -9,6 +18,7 @@
918
box-shadow: -2px 0 5px rgba(0,0,0,0.5);
1019
transition: right 0.3s ease;
1120
z-index: 1000;
21+
max-width: 90%;
1222
}
1323

1424
.sliding-panel.open {

0 commit comments

Comments
 (0)