diff options
| author | Syndamia <kamen.d.mladenov@protonmail.com> | 2021-01-22 15:07:18 +0200 |
|---|---|---|
| committer | Syndamia <kamen.d.mladenov@protonmail.com> | 2021-01-22 15:07:53 +0200 |
| commit | e161f444c64517f644613e3d4e4507c06ce108e0 (patch) | |
| tree | 4c9de6f6992bb0177167f5b7e130750cc69751aa /src/DevHive.Angular | |
| parent | c8e5dac43004ca5f38b8224d69d76c8613c5eb43 (diff) | |
| download | DevHive-e161f444c64517f644613e3d4e4507c06ce108e0.tar DevHive-e161f444c64517f644613e3d4e4507c06ce108e0.tar.gz DevHive-e161f444c64517f644613e3d4e4507c06ce108e0.zip | |
Requests to user (in feed, login, register, profile, profile-settings) now properly wait for the data to get loaded (instead of having a hard coded timer); Substantial improvement over how these pages handle errors; Login page shows request error message
Diffstat (limited to 'src/DevHive.Angular')
11 files changed, 198 insertions, 121 deletions
diff --git a/src/DevHive.Angular/src/app/app-constants.module.ts b/src/DevHive.Angular/src/app/app-constants.module.ts index 9ce8896..2215abd 100644 --- a/src/DevHive.Angular/src/app/app-constants.module.ts +++ b/src/DevHive.Angular/src/app/app-constants.module.ts @@ -4,7 +4,5 @@ export class AppConstants { public static API_USER_LOGIN_URL = AppConstants.API_USER_URL + '/login'; public static API_USER_REGISTER_URL = AppConstants.API_USER_URL + '/register'; - public static FETCH_TIMEOUT = 500; - public static FALLBACK_PROFILE_ICON = 'assets/images/feed/profile-pic.png'; } diff --git a/src/DevHive.Angular/src/app/app-routing.module.ts b/src/DevHive.Angular/src/app/app-routing.module.ts index c511fe2..d67ad8f 100644 --- a/src/DevHive.Angular/src/app/app-routing.module.ts +++ b/src/DevHive.Angular/src/app/app-routing.module.ts @@ -13,6 +13,7 @@ const routes: Routes = [ { path: 'register', component: RegisterComponent }, { path: 'profile/:username', component: ProfileComponent }, { path: 'profile/:username/settings', component: ProfileSettingsComponent }, + { path: 'error', component: ErrorComponent }, { path: '**', component: ErrorComponent } ]; diff --git a/src/DevHive.Angular/src/app/app.module.ts b/src/DevHive.Angular/src/app/app.module.ts index 8591724..80a9583 100644 --- a/src/DevHive.Angular/src/app/app.module.ts +++ b/src/DevHive.Angular/src/app/app.module.ts @@ -5,6 +5,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatInputModule } from '@angular/material/input'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; +import { HttpClientModule } from '@angular/common/http'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @@ -36,7 +37,8 @@ import { LoadingComponent } from './components/loading/loading.component'; BrowserAnimationsModule, MatFormFieldModule, MatInputModule, - MatButtonModule + MatButtonModule, + HttpClientModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.ts b/src/DevHive.Angular/src/app/components/feed/feed.component.ts index aa8599d..b027e5b 100644 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.ts +++ b/src/DevHive.Angular/src/app/components/feed/feed.component.ts @@ -5,6 +5,7 @@ import { User } from 'src/models/identity/user'; import { PostComponent } from '../post/post.component'; import { UserService } from '../../services/user.service'; import { AppConstants } from 'src/app/app-constants.module'; +import {HttpErrorResponse} from '@angular/common/http'; @Component({ selector: 'app-feed', @@ -22,6 +23,7 @@ export class FeedComponent implements OnInit { } ngOnInit(): void { + this.user = this._userService.getDefaultUser(); this.posts = [ new PostComponent(), new PostComponent(), @@ -30,20 +32,28 @@ export class FeedComponent implements OnInit { ]; if (sessionStorage.getItem('UserCred')) { - // Workaround for waiting the fetch response - // TODO: properly wait for it, before loading the page contents - setTimeout(() => { this.user = this._userService.fetchUserFromSessionStorage(); }, AppConstants.FETCH_TIMEOUT); - setTimeout(() => { - this.dataArrived = true; - if (this.user.imageUrl === '') { - this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; - } - }, AppConstants.FETCH_TIMEOUT + 100); + this._userService.getUserFromSessionStorageRequest().subscribe( + (res: object) => this.finishUserLoading(res), + (err: HttpErrorResponse) => this.bailOnBadToken() + ); } else { this._router.navigate(['/login']); } } + private finishUserLoading(res: object): void { + Object.assign(this.user, res); + if (this.user.imageUrl === '') { + this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; + } + this.dataArrived = true; + } + + private bailOnBadToken(): void { + this._userService.logoutUserFromSessionStorage(); + this._router.navigate(['/login']); + } + goToProfile(): void { this._router.navigate(['/profile/' + this.user.userName]); } diff --git a/src/DevHive.Angular/src/app/components/login/login.component.html b/src/DevHive.Angular/src/app/components/login/login.component.html index 166799f..c38bc34 100644 --- a/src/DevHive.Angular/src/app/components/login/login.component.html +++ b/src/DevHive.Angular/src/app/components/login/login.component.html @@ -1,3 +1,5 @@ +<div id="error-bar">{{errorMsg}}</div> + <div id="content"> <div class="title">Login</div> diff --git a/src/DevHive.Angular/src/app/components/login/login.component.ts b/src/DevHive.Angular/src/app/components/login/login.component.ts index bde7a09..e83f4b1 100644 --- a/src/DevHive.Angular/src/app/components/login/login.component.ts +++ b/src/DevHive.Angular/src/app/components/login/login.component.ts @@ -1,9 +1,10 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, ErrorHandler, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, FormControl, AbstractControl } from '@angular/forms'; import { Router } from '@angular/router'; import { Title } from '@angular/platform-browser'; import { UserService } from 'src/app/services/user.service'; import {AppConstants} from 'src/app/app-constants.module'; +import {HttpErrorResponse, HttpResponse} from '@angular/common/http'; @Component({ selector: 'app-login', @@ -12,6 +13,7 @@ import {AppConstants} from 'src/app/app-constants.module'; }) export class LoginComponent implements OnInit { private _title = 'Login'; + errorMsg = '' loginUserFormGroup: FormGroup; constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService) { @@ -30,8 +32,19 @@ export class LoginComponent implements OnInit { } onSubmit(): void { - setTimeout(() => { this._userService.loginUser(this.loginUserFormGroup); }, AppConstants.FETCH_TIMEOUT); - setTimeout(() => { this._router.navigate(['/']); }, AppConstants.FETCH_TIMEOUT + 100); + this._userService.loginUserRequest(this.loginUserFormGroup).subscribe( + res => this.finishLogin(res), + (err: HttpErrorResponse) => this.showError(err) + ); + } + + private finishLogin(res: object): void { + this._userService.setUserTokenToSessionStorage(res); + this._router.navigate(['/']); + } + + private showError(error: HttpErrorResponse): void { + this.errorMsg = error.message; } onRedirectRegister(): void { diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts index 3d7305f..b278e42 100644 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts +++ b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts @@ -1,3 +1,4 @@ +import {HttpErrorResponse} from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; import {Router} from '@angular/router'; import {AppConstants} from 'src/app/app-constants.module'; @@ -10,6 +11,7 @@ import {User} from 'src/models/identity/user'; styleUrls: ['./profile-settings.component.css'] }) export class ProfileSettingsComponent implements OnInit { + private _urlUsername: string; public dataArrived = false; public user: User; @@ -17,34 +19,49 @@ export class ProfileSettingsComponent implements OnInit { { } ngOnInit(): void { - let username = this._router.url.substring(9); - username = username.substring(0, username.length - 9); + this._urlUsername = this._router.url.substring(9) + this._urlUsername = this._urlUsername.substring(0, this._urlUsername.length - 9); + this.user = this._userService.getDefaultUser(); + + this._userService.getUserByUsernameRequest(this._urlUsername).subscribe( + (res: object) => this.finishUserLoading(res), + (err: HttpErrorResponse) => { this._router.navigate(['/error']); } + ); + } + + private finishUserLoading(res: object): void { + Object.assign(this.user, res); + if (this.user.imageUrl === '') { + this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; + } if (sessionStorage.getItem('UserCred')) { - // Workaround for waiting the fetch response - // TODO: properly wait for it, before loading the page contents - setTimeout(() => { - this.user = this._userService.fetchUserFromSessionStorage(); - }, AppConstants.FETCH_TIMEOUT); + const userFromToken: User = this._userService.getDefaultUser(); - // After getting the user, check if we're on the profile page of the logged in user - setTimeout(() => { - if (this.user.userName !== username) { - this.goToProfile(); - } else if (this.user.imageUrl === '') { - this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; - } - }, AppConstants.FETCH_TIMEOUT + 50); + this._userService.getUserFromSessionStorageRequest().subscribe( + (tokenRes: object) => { + Object.assign(userFromToken, tokenRes); - setTimeout(() => { - this.dataArrived = true; - }, AppConstants.FETCH_TIMEOUT + 100); + if (userFromToken.userName === this._urlUsername) { + this.dataArrived = true; + } + else { + this.goToProfile(); + } + }, + (err: HttpErrorResponse) => this.bailOnBadToken() + ); } else { this.goToProfile(); } } + private bailOnBadToken(): void { + this._userService.logoutUserFromSessionStorage(); + this._router.navigate(['/login']); + } + goToProfile(): void { this._router.navigate([this._router.url.substring(0, this._router.url.length - 9)]); } @@ -59,10 +76,12 @@ export class ProfileSettingsComponent implements OnInit { } deleteAccount(): void { - setTimeout(() => { this._userService.deleteUserRequest(this._userService.getUserIdFromSessionStroageToken()); }, AppConstants.FETCH_TIMEOUT); - setTimeout(() => { - this._userService.logoutUserFromSessionStorage(); - this._router.navigate(['/login']); - }, AppConstants.FETCH_TIMEOUT + 100); + this._userService.deleteUserFromSessionStorageRequest().subscribe( + (res: object) => { + this._userService.logoutUserFromSessionStorage(); + this._router.navigate(['/login']); + }, + (err: HttpErrorResponse) => console.log(err) + ); } } diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.ts b/src/DevHive.Angular/src/app/components/profile/profile.component.ts index 9eb6e91..bfa3e3d 100644 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.ts +++ b/src/DevHive.Angular/src/app/components/profile/profile.component.ts @@ -3,6 +3,7 @@ import { Router } from '@angular/router'; import { UserService } from 'src/app/services/user.service'; import { User } from 'src/models/identity/user'; import { AppConstants } from 'src/app/app-constants.module'; +import {HttpErrorResponse} from '@angular/common/http'; @Component({ selector: 'app-profile', @@ -10,6 +11,7 @@ import { AppConstants } from 'src/app/app-constants.module'; styleUrls: ['./profile.component.css'] }) export class ProfileComponent implements OnInit { + private _urlUsername: string; public loggedInUser = false; public dataArrived = false; public user: User; @@ -22,34 +24,44 @@ export class ProfileComponent implements OnInit { } ngOnInit(): void { - const username = this._router.url.substring(9); + this._urlUsername = this._router.url.substring(9); + this.user = this._userService.getDefaultUser(); + + this._userService.getUserByUsernameRequest(this._urlUsername).subscribe( + (res: object) => this.finishUserLoading(res), + (err: HttpErrorResponse) => { this._router.navigate(['/error']); } + ); + } + + private finishUserLoading(res: object): void { + Object.assign(this.user, res); + if (this.user.imageUrl === '') { + this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; + } if (sessionStorage.getItem('UserCred')) { - // Workaround for waiting the fetch response - // TODO: properly wait for it, before loading the page contents - setTimeout(() => { - this.user = this._userService.fetchUserFromSessionStorage(); - }, AppConstants.FETCH_TIMEOUT); + const userFromToken: User = this._userService.getDefaultUser(); - // After getting the user, check if we're on the profile page of the logged in user - setTimeout(() => { - if (this.user.userName !== username) { - this.setDefaultUser(); - } else { - if (this.user.imageUrl === '') { - this.user.imageUrl = AppConstants.FALLBACK_PROFILE_ICON; - } - this.loggedInUser = true; - } - }, AppConstants.FETCH_TIMEOUT + 50); + this._userService.getUserFromSessionStorageRequest().subscribe( + (tokenRes: object) => { + Object.assign(userFromToken, tokenRes); + + if (userFromToken.userName === this._urlUsername) { + this.loggedInUser = true; + } + this.dataArrived = true; + }, + (err: HttpErrorResponse) => this.bailOnBadToken() + ); } else { - this.setDefaultUser(); + this.dataArrived = true; } + } - setTimeout(() => { - this.dataArrived = true; - }, AppConstants.FETCH_TIMEOUT + 100); + private bailOnBadToken(): void { + this._userService.logoutUserFromSessionStorage(); + this._router.navigate(['/login']); } goBack(): void { diff --git a/src/DevHive.Angular/src/app/components/register/register.component.ts b/src/DevHive.Angular/src/app/components/register/register.component.ts index 3fdfbcd..d1d6af0 100644 --- a/src/DevHive.Angular/src/app/components/register/register.component.ts +++ b/src/DevHive.Angular/src/app/components/register/register.component.ts @@ -1,3 +1,4 @@ +import {HttpErrorResponse} from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Title } from '@angular/platform-browser'; @@ -46,10 +47,21 @@ export class RegisterComponent implements OnInit { } onSubmit(): void { - this._userService.registerUser(this.registerUserFormGroup); + this._userService.registerUserRequest(this.registerUserFormGroup).subscribe( + res => this.finishRegister(res), + (err: HttpErrorResponse) => this.showError(err) + ); + } + + private finishRegister(res: object): void { + this._userService.setUserTokenToSessionStorage(res); this._router.navigate(['/']); } + private showError(error: HttpErrorResponse): void { + // TODO: implement, holding out until tab-bar component is implemented + } + onRedirectRegister(): void { this._router.navigate(['/login']); } diff --git a/src/DevHive.Angular/src/app/services/user.service.ts b/src/DevHive.Angular/src/app/services/user.service.ts index 7cf574b..5bd26e9 100644 --- a/src/DevHive.Angular/src/app/services/user.service.ts +++ b/src/DevHive.Angular/src/app/services/user.service.ts @@ -6,96 +6,90 @@ import { IJWTPayload } from '../../interfaces/jwt-payload'; import { IUserCredentials } from '../../interfaces/user-credentials'; import { FormGroup } from '@angular/forms'; import { AppConstants } from 'src/app/app-constants.module'; +import {HttpClient, HttpErrorResponse, HttpHeaders, HttpParams} from '@angular/common/http'; +import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserService { - constructor() { } + constructor(private http: HttpClient) { } getDefaultUser(): User { return new User(Guid.createEmpty(), 'gosho_trapov', 'Gosho', 'Trapov', AppConstants.FALLBACK_PROFILE_ICON); } - getUserIdFromSessionStroageToken(): Guid { - const jwt: IJWTPayload = JSON.parse(sessionStorage.getItem('UserCred') ?? ''); + getUserIdFromSessionStorageToken(): Guid { + const jwt: IJWTPayload = { token: sessionStorage.getItem('UserCred') ?? '' }; const userCred = jwt_decode<IUserCredentials>(jwt.token); return userCred.ID; } - fetchUserFromSessionStorage(): User { + setUserTokenToSessionStorage(response: object): void { + const token = JSON.stringify(response); + sessionStorage.setItem('UserCred', token.substr(10, token.length - 12)); + } + + getUserFromSessionStorageRequest(): Observable<object> { // Get the token and userid from session storage - const jwt: IJWTPayload = JSON.parse(sessionStorage.getItem('UserCred') ?? ''); + const jwt: IJWTPayload = { token: sessionStorage.getItem('UserCred') ?? '' }; const userCred = jwt_decode<IUserCredentials>(jwt.token); - return this.fetchUser(userCred.ID, jwt.token); + return this.getUserRequest(userCred.ID, jwt.token); } - fetchUser(userId: Guid, authToken: string): User { - const fetchedUser: User = new User(Guid.createEmpty(), '', '', '', ''); + deleteUserFromSessionStorageRequest(): Observable<object> { + // Get the token and userid from session storage + const jwt: IJWTPayload = { token: sessionStorage.getItem('UserCred') ?? '' }; + const userCred = jwt_decode<IUserCredentials>(jwt.token); - // Fetch the data and assign it to fetchedUser - fetch(AppConstants.API_USER_URL + '?Id=' + userId, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + authToken - } - }).then(response => response.json()) - .then(data => Object.assign(fetchedUser, data)); + return this.deleteUserRequest(userCred.ID, jwt.token); + } - return fetchedUser; + logoutUserFromSessionStorage(): void { + sessionStorage.removeItem('UserCred'); } - // TODO: make return bool when the response is an error - loginUser(loginUserFormGroup: FormGroup): void { - // Save the fetch reponse in the sessionStorage - fetch(AppConstants.API_USER_LOGIN_URL, { - method: 'POST', - body: JSON.stringify({ - UserName: loginUserFormGroup.get('username')?.value, - Password: loginUserFormGroup.get('password')?.value - }), - headers: { - 'Content-Type': 'application/json' - } - }).then(response => response.json()) - .then(data => sessionStorage.setItem('UserCred', JSON.stringify(data))); + loginUserRequest(loginUserFormGroup: FormGroup): Observable<object> { + const body = { + UserName: loginUserFormGroup.get('username')?.value, + Password: loginUserFormGroup.get('password')?.value + }; + return this.http.post(AppConstants.API_USER_LOGIN_URL, body); } - // TODO: make return bool when the response is an error - registerUser(registerUserFormGroup: FormGroup): void { - // TODO: add a check for form data validity + registerUserRequest(registerUserFormGroup: FormGroup): Observable<object> { + // TODO?: add a check for form data validity + const body = { + UserName: registerUserFormGroup.get('username')?.value, + Email: registerUserFormGroup.get('email')?.value, + FirstName: registerUserFormGroup.get('firstName')?.value, + LastName: registerUserFormGroup.get('lastName')?.value, + Password: registerUserFormGroup.get('password')?.value + }; + return this.http.post(AppConstants.API_USER_REGISTER_URL, body); + } - // Like in login, save the fetch reponse in the sessionStorage - fetch(AppConstants.API_USER_REGISTER_URL, { - method: 'POST', - body: JSON.stringify({ - UserName: registerUserFormGroup.get('username')?.value, - Email: registerUserFormGroup.get('email')?.value, - FirstName: registerUserFormGroup.get('firstName')?.value, - LastName: registerUserFormGroup.get('lastName')?.value, - Password: registerUserFormGroup.get('password')?.value - }), - headers: { - 'Content-Type': 'application/json' - } - }).then(response => response.json()) - .then(data => sessionStorage.setItem('UserCred', JSON.stringify(data))); + getUserRequest(userId: Guid, authToken: string): Observable<object> { + const options = { + params: new HttpParams().set('Id', userId.toString()), + headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) + }; + return this.http.get(AppConstants.API_USER_URL, options); } - logoutUserFromSessionStorage(): void { - sessionStorage.removeItem('UserCred'); + getUserByUsernameRequest(username: string): Observable<object> { + const options = { + params: new HttpParams().set('UserName', username), + }; + return this.http.get(AppConstants.API_USER_URL + '/GetUser', options); } - deleteUserRequest(id: Guid): void { - const jwt = JSON.parse(sessionStorage.getItem('UserCred') ?? ''); - fetch(AppConstants.API_USER_URL + '?Id=' + id, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer ' + jwt.token - } - }); + deleteUserRequest(userId: Guid, authToken: string): Observable<object> { + const options = { + params: new HttpParams().set('Id', userId.toString()), + headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) + }; + return this.http.delete(AppConstants.API_USER_URL, options); } } diff --git a/src/DevHive.Angular/src/styles.css b/src/DevHive.Angular/src/styles.css index 95592a1..1881272 100644 --- a/src/DevHive.Angular/src/styles.css +++ b/src/DevHive.Angular/src/styles.css @@ -35,6 +35,20 @@ input:focus, button:focus { flex-direction: column; } +#error-bar { + text-align: center; + width: 100%; + position: absolute; + top: 0; + background-color: indianred; + color: white; + padding: .2em; +} + +#error-bar:empty { + display: none; +} + .rounded-border { border: 2px solid black; border-radius: .6em; |
