From 1e559f48ab23fa87a48edbd7b8b6b0121cd8edff Mon Sep 17 00:00:00 2001 From: Dominik Halfkann Date: Sat, 16 Dec 2023 16:32:15 +0100 Subject: [PATCH 1/3] add "about me" in profile, add jwt authentication --- src/app/app.config.ts | 4 +- src/app/chat/data-access/chat-http.service.ts | 6 +- .../chat/data-access/chat-state.adapter.ts | 40 +++-- .../ui/chat-conversation-header.component.ts | 4 +- .../chat-messages/chat-messages.component.ts | 4 +- .../chat-conversation-list-entry.component.ts | 6 +- src/app/chat/ui/old/chat.service.ts | 4 +- src/app/chat/ui/old/chat.store.ts | 4 +- .../profile/data-access/profile.service.ts | 12 ++ .../data-access/types/profile.types.ts | 2 +- .../edit-profile/edit-profile.component.html | 1 + .../edit-profile/edit-profile.component.ts | 12 +- .../init-dance-experience.component.ts | 7 +- ...init-partner-dance-experience.component.ts | 7 +- .../init-personal-data.component.ts | 7 +- .../init-profile-image.component.ts | 4 +- .../init-user-name.component.ts | 4 +- .../profile/feature/profile-new.component.ts | 141 ++++++++---------- .../profile-page/profile-page.component.ts | 4 +- src/app/profile/profile.routes.ts | 6 + .../dance-experience-form.component.ts | 4 +- .../personal-data-form.component.html | 8 + .../personal-data-form.component.ts | 18 ++- .../ui/profile-data-entry.component.ts | 37 +++++ src/app/profile/util/city-lookup.validator.ts | 4 +- .../util/dancer-profile-not-created.guard.ts | 7 +- .../util/dancer-profile-sufficient.guard.ts | 7 +- .../data-access/auth/auth-storage.service.ts | 5 +- .../auth/authentication.service.ts | 16 +- .../data-access/auth/authentication.types.ts | 5 + .../shared/data-access/environment.service.ts | 6 +- ...file.service.ts => profile-old.service.ts} | 2 +- src/app/shared/ui/img.component.ts | 52 +++++++ .../ui/profile-menu-button.component.ts | 4 +- src/app/shared/util/age.pipe.ts | 6 +- .../auth/auth-with-credentials.interceptor.ts | 23 --- src/app/shared/util/auth/with-auth.pipe.ts | 25 ++++ .../with-credentials-interceptor.service.ts | 49 ++++++ ...s => with-credentials.interceptor.spec.ts} | 8 +- 39 files changed, 384 insertions(+), 181 deletions(-) create mode 100644 src/app/profile/data-access/profile.service.ts create mode 100644 src/app/profile/ui/profile-data-entry.component.ts rename src/app/shared/data-access/profile/{profile.service.ts => profile-old.service.ts} (99%) create mode 100644 src/app/shared/ui/img.component.ts delete mode 100644 src/app/shared/util/auth/auth-with-credentials.interceptor.ts create mode 100644 src/app/shared/util/auth/with-auth.pipe.ts create mode 100644 src/app/shared/util/auth/with-credentials-interceptor.service.ts rename src/app/shared/util/auth/{auth-with-credentials.interceptor.spec.ts => with-credentials.interceptor.spec.ts} (51%) diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 2dbc51a4..952780f5 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -22,7 +22,7 @@ import { AppInstanceIdInterceptor } from '@shared/util/logging/app-instance-id.i import { defaultStoreProvider } from '@state-adapt/angular'; import { DancierBackendMockedService } from '@shared/data-access/dancier-backend-mocked.service'; import { UnauthorizedInterceptor } from '@shared/util/auth/unauthorized.interceptor'; -import { AuthWithCredentialsInterceptor } from '@shared/util/auth/auth-with-credentials.interceptor'; +import { WithCredentialsInterceptor } from '@shared/util/auth/with-credentials-interceptor.service'; const httpInterceptorProviders = [ { @@ -32,7 +32,7 @@ const httpInterceptorProviders = [ }, { provide: HTTP_INTERCEPTORS, - useClass: AuthWithCredentialsInterceptor, + useClass: WithCredentialsInterceptor, multi: true, }, { diff --git a/src/app/chat/data-access/chat-http.service.ts b/src/app/chat/data-access/chat-http.service.ts index 14221c2b..2d193be3 100644 --- a/src/app/chat/data-access/chat-http.service.ts +++ b/src/app/chat/data-access/chat-http.service.ts @@ -18,7 +18,7 @@ import { MessageResponse, MessageResponseWithChatId, } from './chat.types'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; @Injectable({ providedIn: 'root', @@ -30,7 +30,7 @@ export class ChatHttpService { private readonly chatApiUrl: string; private readonly dancerApiUrl: string; - private profileService = inject(ProfileService); + private profileService = inject(ProfileOldService); constructor( private http: HttpClient, @@ -66,7 +66,7 @@ export class ChatHttpService { this.http // .get('/chats', this.defaultOptions) .get(this.chatApiUrl, this.defaultOptions) - .pipe(map((chatList) => chatList.chats)) + .pipe(map((chatList) => chatList.chats || [])) ); } diff --git a/src/app/chat/data-access/chat-state.adapter.ts b/src/app/chat/data-access/chat-state.adapter.ts index 523990a0..f9d6d870 100644 --- a/src/app/chat/data-access/chat-state.adapter.ts +++ b/src/app/chat/data-access/chat-state.adapter.ts @@ -9,27 +9,25 @@ import { import { HttpErrorResponse } from '@angular/common/http'; export const chatStateAdapter = createAdapter()({ - chatsFetched: (state, chatsDto: ChatDto[]) => ({ - ...state, - chatsFetchState: 'loaded', - chatCreated: false, - chats: [ - // only add new chats (chat id not in state yet) - ...state.chats, - ...chatsDto - .filter( - (chatDto) => - !state.chats.find((stateChat) => stateChat.id === chatDto.chatId) - ) - .map((chatDto) => ({ - id: chatDto.chatId, - participants: chatDto.dancerIds.map((dancerId) => ({ - id: dancerId, - })), - messages: [], - })), - ], - }), + chatsFetched: (state, chatsDto: ChatDto[]) => { + const newChats = chatsDto + .filter( + (chatDto) => + !state.chats.find((stateChat) => stateChat.id === chatDto.chatId) + ) + .map((chatDto) => ({ + id: chatDto.chatId, + participants: chatDto.dancerIds.map((dancerId) => ({ id: dancerId })), + messages: [], + })); + + return { + ...state, + chatsFetchState: 'loaded', + chatCreated: false, + chats: [...state.chats, ...newChats], + }; + }, chatsFetchedError: (state, chatsFetchError: HttpErrorResponse) => ({ ...state, diff --git a/src/app/chat/ui/chat-conversation-header.component.ts b/src/app/chat/ui/chat-conversation-header.component.ts index 9ef212de..e9e5a472 100644 --- a/src/app/chat/ui/chat-conversation-header.component.ts +++ b/src/app/chat/ui/chat-conversation-header.component.ts @@ -9,7 +9,7 @@ import { CommonModule } from '@angular/common'; import { ChatStateService } from '../data-access/chat-state.service'; import { ChatParticipant } from '../data-access/chat.types'; import { toSignal } from '@angular/core/rxjs-interop'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { startWith } from 'rxjs/operators'; import { Profile } from '../../profile/data-access/types/profile.types'; import { map } from 'rxjs'; @@ -37,7 +37,7 @@ export class ChatConversationHeaderComponent { private chatState = inject(ChatStateService); ownProfileId: Signal = toSignal( - inject(ProfileService).profile$.pipe( + inject(ProfileOldService).profile$.pipe( startWith({ id: 'dancerId1', } as Profile), diff --git a/src/app/chat/ui/chat-messages/chat-messages.component.ts b/src/app/chat/ui/chat-messages/chat-messages.component.ts index c4728800..d397b739 100644 --- a/src/app/chat/ui/chat-messages/chat-messages.component.ts +++ b/src/app/chat/ui/chat-messages/chat-messages.component.ts @@ -9,7 +9,7 @@ import { AsyncPipe, JsonPipe, NgClass, NgFor, NgIf } from '@angular/common'; import { ChatMessage } from '../../data-access/chat.types'; import { ChatStateService } from '../../data-access/chat-state.service'; import { toSignal } from '@angular/core/rxjs-interop'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { startWith } from 'rxjs/operators'; import { Profile } from '../../../profile/data-access/types/profile.types'; import { map } from 'rxjs'; @@ -50,7 +50,7 @@ import { map } from 'rxjs'; export class ChatMessagesComponent { chatState = inject(ChatStateService); ownUserId: Signal = toSignal( - inject(ProfileService).profile$.pipe( + inject(ProfileOldService).profile$.pipe( startWith({ id: 'dancerId1', } as Profile), diff --git a/src/app/chat/ui/conversation-list/chat-conversation-list-entry.component.ts b/src/app/chat/ui/conversation-list/chat-conversation-list-entry.component.ts index 9b41aea7..05906d3a 100644 --- a/src/app/chat/ui/conversation-list/chat-conversation-list-entry.component.ts +++ b/src/app/chat/ui/conversation-list/chat-conversation-list-entry.component.ts @@ -13,7 +13,7 @@ import { } from '../../data-access/chat-state.service'; import { ImageService } from '@shared/data-access/image.service'; import { ChatParticipant } from '../../data-access/chat.types'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { map } from 'rxjs'; import { toSignal } from '@angular/core/rxjs-interop'; import { startWith } from 'rxjs/operators'; @@ -62,7 +62,7 @@ import { Profile } from '../../../profile/data-access/types/profile.types'; export class ChatConversationListEntryComponent { chatState = inject(ChatStateService); ownProfileId: Signal = toSignal( - inject(ProfileService).profile$.pipe( + inject(ProfileOldService).profile$.pipe( startWith({ id: 'dancerId1', } as Profile), @@ -115,7 +115,7 @@ export class ChatConversationListEntryComponent { // constructor( // public imageService: ImageService, // public chatStore: ChatStore, - // public profileService: ProfileService, + // public profileService: ProfileOldService, // public router: Router // ) { // this.profileService.profile$.subscribe((profile) => { diff --git a/src/app/chat/ui/old/chat.service.ts b/src/app/chat/ui/old/chat.service.ts index 8dcf9501..193e039d 100644 --- a/src/app/chat/ui/old/chat.service.ts +++ b/src/app/chat/ui/old/chat.service.ts @@ -11,7 +11,7 @@ // } from '../../common/types/chat.types'; // import { combineLatest, map, Observable, shareReplay, switchMap } from 'rxjs'; // import { Router } from '@angular/router'; -// import { ProfileService } from '@shared/profile/profile.service'; +// import { ProfileOldService } from '@shared/profile/profile.service'; // // type FetchChatsDto = { // chats: ChatDto[]; @@ -47,7 +47,7 @@ // private http: HttpClient, // private environment: EnvironmentService, // private authStorageService: AuthStorageService, -// private profileService: ProfileService, +// private profileService: ProfileOldService, // private router: Router // ) { // this.chatApiUrl = `${this.environment.getApiUrl()}/chats`; diff --git a/src/app/chat/ui/old/chat.store.ts b/src/app/chat/ui/old/chat.store.ts index c166a252..5545184d 100644 --- a/src/app/chat/ui/old/chat.store.ts +++ b/src/app/chat/ui/old/chat.store.ts @@ -16,7 +16,7 @@ // withLatestFrom, // } from 'rxjs'; // import { ChatService } from './chat.service'; -// import { ProfileService } from '@shared/profile/profile.service'; +// import { ProfileOldService } from '@shared/profile/profile.service'; // import { EnvironmentService } from '@shared/common/environment.service'; // // export type ChatState = { @@ -47,7 +47,7 @@ // { // constructor( // private chatService: ChatService, -// private profileService: ProfileService, +// private profileService: ProfileOldService, // private environmentService: EnvironmentService // @Inject(IntervalSchedulerToken) private scheduler: SchedulerLike // ) { // super(defaultState); diff --git a/src/app/profile/data-access/profile.service.ts b/src/app/profile/data-access/profile.service.ts new file mode 100644 index 00000000..a37bd987 --- /dev/null +++ b/src/app/profile/data-access/profile.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class ProfileService { + constructor() {} + + // public getOwnProfile(): void {} + // + // public getProfile(dancerId: string): void {} +} diff --git a/src/app/profile/data-access/types/profile.types.ts b/src/app/profile/data-access/types/profile.types.ts index 454e3746..c54f6207 100644 --- a/src/app/profile/data-access/types/profile.types.ts +++ b/src/app/profile/data-access/types/profile.types.ts @@ -50,6 +50,7 @@ export type Dance = { }; export type PersonalData = { + aboutMe: string; size: number; gender: Gender; birthDate: string; @@ -60,7 +61,6 @@ export type PersonalData = { export type Profile = PersonalData & { id?: string; - aboutMe: string; dancerName: string; ableTo: Dance[]; wantsTo: Dance[]; diff --git a/src/app/profile/feature/edit-profile/edit-profile.component.html b/src/app/profile/feature/edit-profile/edit-profile.component.html index f5d45265..e7dcc532 100644 --- a/src/app/profile/feature/edit-profile/edit-profile.component.html +++ b/src/app/profile/feature/edit-profile/edit-profile.component.html @@ -13,6 +13,7 @@

Profilbild

? croppedImage : (profileService.getProfileImageSrc() | async) " + (error)="handleMissingImage($event)" /> diff --git a/src/app/profile/feature/edit-profile/edit-profile.component.ts b/src/app/profile/feature/edit-profile/edit-profile.component.ts index 2ad275f1..b014e646 100644 --- a/src/app/profile/feature/edit-profile/edit-profile.component.ts +++ b/src/app/profile/feature/edit-profile/edit-profile.component.ts @@ -5,7 +5,7 @@ import { ReactiveFormsModule, } from '@angular/forms'; import { APIError, APIResponse } from '@shared/util/http/response.types'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { Profile, UploadedImageDao, @@ -23,6 +23,7 @@ import { DanceExperienceFormComponent } from '../../ui/dance-experience-form/dan import { PersonalDataFormComponent } from '../../ui/personal-data-form/personal-data-form.component'; import { AsyncPipe, NgIf } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; +import { ImageService } from '@shared/data-access/image.service'; type EditProfileForm = { personalData: FormGroup>; @@ -60,8 +61,9 @@ export class EditProfileComponent { constructor( private fb: NonNullableFormBuilder, - public profileService: ProfileService, + public profileService: ProfileOldService, private imageUploadService: ImageUploadService, + private imageService: ImageService, private router: Router ) {} @@ -88,6 +90,7 @@ export class EditProfileComponent { if (this.profileForm.valid) { const formValues = this.profileForm.getRawValue(); const profile: Partial = { + aboutMe: formValues.personalData!.aboutMe!, size: formValues.personalData!.size!, birthDate: format(formValues.personalData!.birthDate!, 'yyyy-MM-dd', { locale: de, @@ -123,4 +126,9 @@ export class EditProfileComponent { this.profileForm.markAllAsTouched(); } } + + handleMissingImage($event: ErrorEvent): void { + ($event.target as HTMLImageElement).src = + this.imageService.getDefaultDancerImage(); + } } diff --git a/src/app/profile/feature/initial-setup/init-dance-experience/init-dance-experience.component.ts b/src/app/profile/feature/initial-setup/init-dance-experience/init-dance-experience.component.ts index 68d94665..4db9abd0 100644 --- a/src/app/profile/feature/initial-setup/init-dance-experience/init-dance-experience.component.ts +++ b/src/app/profile/feature/initial-setup/init-dance-experience/init-dance-experience.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { DanceExperienceForm } from '../../../ui/dance-experience-form/dance-form.type'; import { Dance } from '../../../data-access/types/profile.types'; @@ -30,7 +30,10 @@ export class InitDanceExperienceComponent { apiError?: APIError; - constructor(public profileService: ProfileService, private router: Router) {} + constructor( + public profileService: ProfileOldService, + private router: Router + ) {} submitForm(): void { if (this.form.valid && this.form.value.dances) { diff --git a/src/app/profile/feature/initial-setup/init-partner-dance-experience/init-partner-dance-experience.component.ts b/src/app/profile/feature/initial-setup/init-partner-dance-experience/init-partner-dance-experience.component.ts index 0331353e..63f582fa 100644 --- a/src/app/profile/feature/initial-setup/init-partner-dance-experience/init-partner-dance-experience.component.ts +++ b/src/app/profile/feature/initial-setup/init-partner-dance-experience/init-partner-dance-experience.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { DanceExperienceForm } from '../../../ui/dance-experience-form/dance-form.type'; import { Dance } from '../../../data-access/types/profile.types'; @@ -30,7 +30,10 @@ export class InitPartnerDanceExperienceComponent { apiError?: APIError; - constructor(public profileService: ProfileService, private router: Router) {} + constructor( + public profileService: ProfileOldService, + private router: Router + ) {} submitForm(): void { if (this.form.valid && this.form.value.dances) { diff --git a/src/app/profile/feature/initial-setup/init-personal-data/init-personal-data.component.ts b/src/app/profile/feature/initial-setup/init-personal-data/init-personal-data.component.ts index 9401d8ea..0f8c8692 100644 --- a/src/app/profile/feature/initial-setup/init-personal-data/init-personal-data.component.ts +++ b/src/app/profile/feature/initial-setup/init-personal-data/init-personal-data.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { Router } from '@angular/router'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { PersonalData } from '../../../data-access/types/profile.types'; import { UntilDestroy } from '@ngneat/until-destroy'; import { APIError } from '@shared/util/http/response.types'; @@ -34,7 +34,10 @@ export class InitPersonalDataComponent { error?: APIError; - constructor(public profileService: ProfileService, private router: Router) {} + constructor( + public profileService: ProfileOldService, + private router: Router + ) {} submitForm(): void { if (this.personalDataForm.valid) { diff --git a/src/app/profile/feature/initial-setup/init-profile-image/init-profile-image.component.ts b/src/app/profile/feature/initial-setup/init-profile-image/init-profile-image.component.ts index 7e396258..2c72d6ec 100644 --- a/src/app/profile/feature/initial-setup/init-profile-image/init-profile-image.component.ts +++ b/src/app/profile/feature/initial-setup/init-profile-image/init-profile-image.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { ImageUploadService } from '../../../data-access/image-upload.service'; import { ImageCroppedEvent, ImageCropperModule } from 'ngx-image-cropper'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { APIResponse } from '@shared/util/http/response.types'; import { UploadedImageDao } from '../../../data-access/types/profile.types'; import { Router } from '@angular/router'; @@ -22,7 +22,7 @@ export class InitProfileImageComponent { constructor( private imageUploadService: ImageUploadService, - private profileService: ProfileService, + private profileService: ProfileOldService, private router: Router ) {} diff --git a/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.ts b/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.ts index 70e9d039..ad46b465 100644 --- a/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.ts +++ b/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.ts @@ -6,7 +6,7 @@ import { } from '@angular/forms'; import { Router } from '@angular/router'; import { ProfileHttpService } from '@shared/data-access/profile/profile-http.service'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { APIError, APIResponse, @@ -44,7 +44,7 @@ export class InitUserNameComponent { error?: APIError; constructor( - public profileService: ProfileService, + public profileService: ProfileOldService, private profileHttpService: ProfileHttpService, private fb: NonNullableFormBuilder, private router: Router diff --git a/src/app/profile/feature/profile-new.component.ts b/src/app/profile/feature/profile-new.component.ts index 19a95022..c8862f93 100644 --- a/src/app/profile/feature/profile-new.component.ts +++ b/src/app/profile/feature/profile-new.component.ts @@ -1,11 +1,13 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { DisplayDanceLevelPipe } from '../util/pipes/display-dance-level.pipe'; import { DisplayDanceRolePipe } from '../util/pipes/display-dance-role.pipe'; import { AgePipe } from '@shared/util/age.pipe'; import { DisplayGenderPipe } from '../util/pipes/display-gender.pipe'; import { Router } from '@angular/router'; +import { ProfileDataEntryComponent } from '../ui/profile-data-entry.component'; +import { ImageService } from '@shared/data-access/image.service'; @Component({ selector: 'app-profile-new', @@ -16,15 +18,19 @@ import { Router } from '@angular/router'; DisplayDanceRolePipe, AgePipe, DisplayGenderPipe, + ProfileDataEntryComponent, ], template: ` -
-
+
+
Profile Image
@@ -45,88 +51,55 @@ import { Router } from '@angular/router';
Profil bearbeiten
- - - - - - - - - - - + -
-
- - - -
-
-
Wohnort
-
{{ profile.city }}
-
-
+ -
-
- - - -
-
-
Alter
-
{{ profile.birthDate | age }}
-
-
+ -
-
- - - -
-
-
Körpergröße
-
{{ profile.size }} cm
-
-
+ -
-
- - - -
-
-
Geschlecht
-
{{ profile.gender | displayGender }}
-
-
+ -
-
- - - -
-
-
Tanzerfahrung
-
- {{ danceExperience.dance }} - ({{ danceExperience.level | displayDanceLevel }}, - {{ danceExperience.leading | displayDanceRole }}) -
-
-
+ icon="music-note-beamed" + label="Tanzerfahrung" + [value]=" + danceExperience.dance + + ' (' + + (danceExperience.level | displayDanceLevel) + + ', ' + + (danceExperience.leading | displayDanceRole) + + ')' + " + >
@@ -134,10 +107,16 @@ import { Router } from '@angular/router'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ProfileNewComponent { - profileService = inject(ProfileService); + imageService = inject(ImageService); + profileService = inject(ProfileOldService); router = inject(Router); editProfile(): void { this.router.navigate(['profile', 'edit']); } + + handleMissingImage($event: ErrorEvent): void { + ($event.target as HTMLImageElement).src = + this.imageService.getDefaultDancerImage(); + } } diff --git a/src/app/profile/feature/profile-page/profile-page.component.ts b/src/app/profile/feature/profile-page/profile-page.component.ts index 91074164..d9365ffa 100644 --- a/src/app/profile/feature/profile-page/profile-page.component.ts +++ b/src/app/profile/feature/profile-page/profile-page.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { EnvironmentService } from '@shared/data-access/environment.service'; import { Router } from '@angular/router'; import { DisplayDanceRolePipe } from '../../util/pipes/display-dance-role.pipe'; @@ -27,7 +27,7 @@ import { AsyncPipe, NgFor, NgIf } from '@angular/common'; }) export class ProfilePageComponent { constructor( - public profileService: ProfileService, + public profileService: ProfileOldService, public environmentService: EnvironmentService, private router: Router ) {} diff --git a/src/app/profile/profile.routes.ts b/src/app/profile/profile.routes.ts index 22c64eaa..baa75212 100644 --- a/src/app/profile/profile.routes.ts +++ b/src/app/profile/profile.routes.ts @@ -16,8 +16,14 @@ import { loggedInGuard } from '@shared/util/auth/logged-in.guard'; import { ProfileNewComponent } from './feature/profile-new.component'; export const PROFILE_ROUTES: Routes = [ + { + path: 'view/:participantId', + pathMatch: 'full', + component: ProfileNewComponent, + }, { path: '', + pathMatch: 'full', component: ProfileNewComponent, canActivate: [ loggedInGuard, diff --git a/src/app/profile/ui/dance-experience-form/dance-experience-form.component.ts b/src/app/profile/ui/dance-experience-form/dance-experience-form.component.ts index 7bdea3cc..c628e2db 100644 --- a/src/app/profile/ui/dance-experience-form/dance-experience-form.component.ts +++ b/src/app/profile/ui/dance-experience-form/dance-experience-form.component.ts @@ -13,7 +13,7 @@ import { DanceType, Profile, } from '../../data-access/types/profile.types'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { Router } from '@angular/router'; import { DanceExperienceEntryForm } from './dance-form.type'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -42,7 +42,7 @@ export class DanceExperienceFormComponent implements OnInit { @Input() danceFormType: 'own' | 'partner' = 'own'; constructor( - public profileService: ProfileService, + public profileService: ProfileOldService, private router: Router, private formGroupDirective: FormGroupDirective ) {} diff --git a/src/app/profile/ui/personal-data-form/personal-data-form.component.html b/src/app/profile/ui/personal-data-form/personal-data-form.component.html index be0cf3e4..5c190aaf 100644 --- a/src/app/profile/ui/personal-data-form/personal-data-form.component.html +++ b/src/app/profile/ui/personal-data-form/personal-data-form.component.html @@ -1,4 +1,12 @@ + + Über mich + + {{ personalDataForm.get('aboutMe')?.value?.length }} / 300 + + Geschlecht diff --git a/src/app/profile/ui/personal-data-form/personal-data-form.component.ts b/src/app/profile/ui/personal-data-form/personal-data-form.component.ts index d880046d..fc623269 100644 --- a/src/app/profile/ui/personal-data-form/personal-data-form.component.ts +++ b/src/app/profile/ui/personal-data-form/personal-data-form.component.ts @@ -9,7 +9,7 @@ import { } from '@angular/forms'; import { Gender, genderList } from '../../data-access/types/profile.types'; import { CityLookupValidator } from '../../util/city-lookup.validator'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { map } from 'rxjs/operators'; import { distinctUntilChanged, of, switchMap } from 'rxjs'; @@ -55,7 +55,7 @@ export class PersonalDataFormComponent implements OnInit { constructor( private formGroupDirective: FormGroupDirective, private fb: NonNullableFormBuilder, - private profileService: ProfileService + private profileService: ProfileOldService ) { // set min and max selectable date of birth relative to the current year const currentYear = new Date().getFullYear(); @@ -65,6 +65,13 @@ export class PersonalDataFormComponent implements OnInit { ngOnInit(): void { this.personalDataForm = this.formGroupDirective.form; + this.personalDataForm.addControl( + 'aboutMe', + new FormControl('', { + validators: [Validators.maxLength(500)], + nonNullable: true, + }) + ); this.personalDataForm.addControl( 'birthDate', new FormControl(null, [Validators.required]) @@ -103,6 +110,7 @@ export class PersonalDataFormComponent implements OnInit { .pipe(untilDestroyed(this)) .subscribe((profile) => { this.personalDataForm.patchValue({ + aboutMe: profile?.aboutMe, birthDate: parse(profile?.birthDate, 'yyyy-MM-dd', new Date()), zipCode: profile?.zipCode, city: profile?.city, @@ -136,3 +144,9 @@ export class PersonalDataFormComponent implements OnInit { return this.personalDataForm.get(field)!.hasError(error); } } + +/* +Hallo, ich bin der Dominik. Ich spiele gerne Karten und mache auch sonst allerlei Sachen. Manchmal trinke ich Kaffee, aber nur am Wochenende, meistens trinke ich Tee, damit das klar ist. Jojo, das ist töfte, oder? + +Bla blub, es ist cool ich zu sein. Nein wirklich, es ist super. Jemand anderes zu sein wäre auch langweilig. + */ diff --git a/src/app/profile/ui/profile-data-entry.component.ts b/src/app/profile/ui/profile-data-entry.component.ts new file mode 100644 index 00000000..41bd6a17 --- /dev/null +++ b/src/app/profile/ui/profile-data-entry.component.ts @@ -0,0 +1,37 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DisplayGenderPipe } from '../util/pipes/display-gender.pipe'; + +@Component({ + selector: 'app-profile-data-entry', + standalone: true, + imports: [CommonModule, DisplayGenderPipe], + template: ` +
+
+ + + +
+
+
+ {{ label }} +
+
+ {{ value }} +
+
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProfileDataEntryComponent { + @Input({ required: true }) + icon!: string; + + @Input({ required: true }) + label!: string; + + @Input({ required: true }) + value!: string; +} diff --git a/src/app/profile/util/city-lookup.validator.ts b/src/app/profile/util/city-lookup.validator.ts index 935c7801..36c5bd6b 100644 --- a/src/app/profile/util/city-lookup.validator.ts +++ b/src/app/profile/util/city-lookup.validator.ts @@ -4,12 +4,12 @@ import { AsyncValidatorFn, ValidationErrors, } from '@angular/forms'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { distinctUntilChanged, filter, Observable, of, switchMap } from 'rxjs'; import { map } from 'rxjs/operators'; export class CityLookupValidator { - static createValidator(profileService: ProfileService): AsyncValidatorFn { + static createValidator(profileService: ProfileOldService): AsyncValidatorFn { return (control: AbstractControl): Observable => { return of(control.value).pipe( filter(isNonNull), diff --git a/src/app/profile/util/dancer-profile-not-created.guard.ts b/src/app/profile/util/dancer-profile-not-created.guard.ts index bfaf1167..0a1b4b69 100644 --- a/src/app/profile/util/dancer-profile-not-created.guard.ts +++ b/src/app/profile/util/dancer-profile-not-created.guard.ts @@ -6,14 +6,17 @@ import { UrlTree, } from '@angular/router'; import { Observable, take } from 'rxjs'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root', }) export class DancerProfileNotCreatedGuard { - constructor(private profileService: ProfileService, private router: Router) {} + constructor( + private profileService: ProfileOldService, + private router: Router + ) {} canActivateChild( childRoute: ActivatedRouteSnapshot, diff --git a/src/app/profile/util/dancer-profile-sufficient.guard.ts b/src/app/profile/util/dancer-profile-sufficient.guard.ts index 6d4903b7..2d3bde3b 100644 --- a/src/app/profile/util/dancer-profile-sufficient.guard.ts +++ b/src/app/profile/util/dancer-profile-sufficient.guard.ts @@ -6,14 +6,17 @@ import { UrlTree, } from '@angular/router'; import { Observable, take } from 'rxjs'; -import { ProfileService } from '@shared/data-access/profile/profile.service'; +import { ProfileOldService } from '@shared/data-access/profile/profile-old.service'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root', }) export class DancerProfileSufficientGuard { - constructor(private profileService: ProfileService, private router: Router) {} + constructor( + private profileService: ProfileOldService, + private router: Router + ) {} canActivate( _route: ActivatedRouteSnapshot, diff --git a/src/app/shared/data-access/auth/auth-storage.service.ts b/src/app/shared/data-access/auth/auth-storage.service.ts index 8b2b917a..fd64f90b 100644 --- a/src/app/shared/data-access/auth/auth-storage.service.ts +++ b/src/app/shared/data-access/auth/auth-storage.service.ts @@ -4,6 +4,7 @@ import { BehaviorSubject } from 'rxjs'; export type AuthData = { isLoggedIn: boolean; isHuman: boolean; + jwt: string; }; const AUTH_DATA_KEY = 'authData'; @@ -26,16 +27,18 @@ export class AuthStorageService { return JSON.parse(authItem); } else { return { + jwt: '', isLoggedIn: false, isHuman: false, }; } } - public setLoginState(loginState: boolean): void { + public setLoginState(loginState: boolean, jwt?: string): void { const newAuthData = { ...this._authData$.getValue(), isLoggedIn: loginState, + jwt: jwt || '', }; localStorage.setItem(AUTH_DATA_KEY, JSON.stringify(newAuthData)); this._authData$.next(newAuthData); diff --git a/src/app/shared/data-access/auth/authentication.service.ts b/src/app/shared/data-access/auth/authentication.service.ts index f6296d4f..e9d318d2 100644 --- a/src/app/shared/data-access/auth/authentication.service.ts +++ b/src/app/shared/data-access/auth/authentication.service.ts @@ -7,6 +7,7 @@ import { import { EmailValidationCodeRequest, LoginRequest, + LoginResponse, PasswordChangeRequest, UserRegistration, } from './authentication.types'; @@ -58,12 +59,21 @@ export class AuthenticationService { ); } - login(loginRequest: LoginRequest): Observable> { + login(loginRequest: LoginRequest): Observable> { return this.http - .post(`${this.baseUrl}/login`, loginRequest, this.defaultOptions) + .post( + `${this.baseUrl}/login`, + loginRequest, + this.defaultOptions + ) .pipe( map(asSuccess), - tap((_) => this.authStorageService.setLoginState(true)), + tap((response) => + this.authStorageService.setLoginState( + true, + response.payload.accessToken + ) + ), catchError((error: HttpErrorResponse) => { switch (error.status) { case 401: diff --git a/src/app/shared/data-access/auth/authentication.types.ts b/src/app/shared/data-access/auth/authentication.types.ts index 24bb44ad..1503ffee 100644 --- a/src/app/shared/data-access/auth/authentication.types.ts +++ b/src/app/shared/data-access/auth/authentication.types.ts @@ -16,3 +16,8 @@ export type PasswordChangeRequest = { export type EmailValidationCodeRequest = { email: string; }; + +export type LoginResponse = { + accessToken: string; + tokenType: string; +}; diff --git a/src/app/shared/data-access/environment.service.ts b/src/app/shared/data-access/environment.service.ts index 85146889..15184ed8 100644 --- a/src/app/shared/data-access/environment.service.ts +++ b/src/app/shared/data-access/environment.service.ts @@ -17,10 +17,14 @@ export class EnvironmentService { return false; } - public getProduction(): boolean { + public isProduction(): boolean { return environment.production; } + public isLocalDevelopment(): boolean { + return !environment.production; + } + public getApiUrl(): string { return environment.apiUrl; } diff --git a/src/app/shared/data-access/profile/profile.service.ts b/src/app/shared/data-access/profile/profile-old.service.ts similarity index 99% rename from src/app/shared/data-access/profile/profile.service.ts rename to src/app/shared/data-access/profile/profile-old.service.ts index de665551..f6417415 100644 --- a/src/app/shared/data-access/profile/profile.service.ts +++ b/src/app/shared/data-access/profile/profile-old.service.ts @@ -16,7 +16,7 @@ import { ImageService } from '../image.service'; @Injectable({ providedIn: 'root', }) -export class ProfileService { +export class ProfileOldService { private _profile = new BehaviorSubject(null); public readonly profile$: Observable = this._profile .asObservable() diff --git a/src/app/shared/ui/img.component.ts b/src/app/shared/ui/img.component.ts new file mode 100644 index 00000000..c6d912f2 --- /dev/null +++ b/src/app/shared/ui/img.component.ts @@ -0,0 +1,52 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + inject, + Input, + Output, +} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { EnvironmentService } from '../data-access/environment.service'; +import { WithAuthPipe } from '../util/auth/with-auth.pipe'; + +@Component({ + selector: 'app-img', + standalone: true, + imports: [CommonModule, WithAuthPipe], + template: ` + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ImgComponent { + @Input({ required: true }) src: string | undefined; + + @Input() + imgClass: string = ''; + + @Input() + altText: string = ''; + + @Output() + imgError = new EventEmitter(); + + public readonly environment = inject(EnvironmentService); + + handleError($event: ErrorEvent): void { + this.imgError.emit($event); + } +} diff --git a/src/app/shared/ui/layout/navigation/ui/profile-menu-button.component.ts b/src/app/shared/ui/layout/navigation/ui/profile-menu-button.component.ts index a6db48e5..b27e356e 100644 --- a/src/app/shared/ui/layout/navigation/ui/profile-menu-button.component.ts +++ b/src/app/shared/ui/layout/navigation/ui/profile-menu-button.component.ts @@ -5,7 +5,7 @@ import { inject, Output, } from '@angular/core'; -import { ProfileService } from '../../../../data-access/profile/profile.service'; +import { ProfileOldService } from '../../../../data-access/profile/profile-old.service'; import { AsyncPipe } from '@angular/common'; import { handleAutoChangeDetectionStatus } from '@angular/cdk/testing'; @@ -37,7 +37,7 @@ export class ProfileMenuButtonComponent { @Output() profileButtonClicked = new EventEmitter(); - public profileService = inject(ProfileService); + public profileService = inject(ProfileOldService); protected readonly handleAutoChangeDetectionStatus = handleAutoChangeDetectionStatus; diff --git a/src/app/shared/util/age.pipe.ts b/src/app/shared/util/age.pipe.ts index e1953f71..ba2ea358 100644 --- a/src/app/shared/util/age.pipe.ts +++ b/src/app/shared/util/age.pipe.ts @@ -5,12 +5,12 @@ import { Pipe, PipeTransform } from '@angular/core'; standalone: true, }) export class AgePipe implements PipeTransform { - transform(birthdate?: string): number | string { + transform(birthdate?: string): string { return birthdate ? calculateAge(birthdate) : 'unknown'; } } -function calculateAge(birthdate: string): number | string { +function calculateAge(birthdate: string): string { const birthdateAsDate = new Date(birthdate); if (birthdateAsDate.toString() === 'Invalid Date') { console.error('Error on parsing birthdate', birthdate); @@ -26,5 +26,5 @@ function calculateAge(birthdate: string): number | string { ) { age--; } - return age; + return age.toString(); } diff --git a/src/app/shared/util/auth/auth-with-credentials.interceptor.ts b/src/app/shared/util/auth/auth-with-credentials.interceptor.ts deleted file mode 100644 index d08ac350..00000000 --- a/src/app/shared/util/auth/auth-with-credentials.interceptor.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@angular/core'; -import { - HttpEvent, - HttpHandler, - HttpInterceptor, - HttpRequest, -} from '@angular/common/http'; -import { Observable } from 'rxjs'; - -@Injectable() -export class AuthWithCredentialsInterceptor implements HttpInterceptor { - constructor() {} - - intercept( - request: HttpRequest, - next: HttpHandler - ): Observable> { - const modifiedRequest = request.clone({ - withCredentials: true, - }); - return next.handle(modifiedRequest); - } -} diff --git a/src/app/shared/util/auth/with-auth.pipe.ts b/src/app/shared/util/auth/with-auth.pipe.ts new file mode 100644 index 00000000..e55cb922 --- /dev/null +++ b/src/app/shared/util/auth/with-auth.pipe.ts @@ -0,0 +1,25 @@ +import { inject, Pipe, PipeTransform } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { map, Observable } from 'rxjs'; + +@Pipe({ + name: 'withAuth', + standalone: true, +}) +export class WithAuthPipe implements PipeTransform { + private readonly http = inject(HttpClient); + private readonly sanitizer = inject(DomSanitizer); + + constructor() {} + + transform(url: string): Observable { + return this.http + .get(url, { responseType: 'blob' }) + .pipe( + map((blob) => + this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob)) + ) + ); + } +} diff --git a/src/app/shared/util/auth/with-credentials-interceptor.service.ts b/src/app/shared/util/auth/with-credentials-interceptor.service.ts new file mode 100644 index 00000000..8972ee92 --- /dev/null +++ b/src/app/shared/util/auth/with-credentials-interceptor.service.ts @@ -0,0 +1,49 @@ +import { inject, Injectable } from '@angular/core'; +import { + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpRequest, +} from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { AuthStorageService } from '../../data-access/auth/auth-storage.service'; +import { EnvironmentService } from '../../data-access/environment.service'; + +@Injectable() +export class WithCredentialsInterceptor implements HttpInterceptor { + private authStorage = inject(AuthStorageService); + private environment = inject(EnvironmentService); + + constructor() {} + + intercept( + request: HttpRequest, + next: HttpHandler + ): Observable> { + let modifiedRequest; + if (this.environment.isLocalDevelopment()) { + modifiedRequest = this.addJwtAuthorization(request); + } else { + modifiedRequest = this.addCredentialsCookie(request); + } + + return next.handle(modifiedRequest); + } + + private addJwtAuthorization(request: HttpRequest): HttpRequest { + if (!this.authStorage.getSnapshot().isLoggedIn) { + return request; + } + + const jwt = this.authStorage.getSnapshot().jwt; + return request.clone({ + headers: request.headers.append('Authorization', `Bearer ${jwt}`), + }); + } + + private addCredentialsCookie(request: HttpRequest): HttpRequest { + return request.clone({ + withCredentials: true, + }); + } +} diff --git a/src/app/shared/util/auth/auth-with-credentials.interceptor.spec.ts b/src/app/shared/util/auth/with-credentials.interceptor.spec.ts similarity index 51% rename from src/app/shared/util/auth/auth-with-credentials.interceptor.spec.ts rename to src/app/shared/util/auth/with-credentials.interceptor.spec.ts index 177a6070..ca0b9498 100644 --- a/src/app/shared/util/auth/auth-with-credentials.interceptor.spec.ts +++ b/src/app/shared/util/auth/with-credentials.interceptor.spec.ts @@ -1,17 +1,17 @@ import { TestBed } from '@angular/core/testing'; -import { AuthWithCredentialsInterceptor } from './auth-with-credentials.interceptor'; +import { WithCredentialsInterceptor } from './with-credentials-interceptor.service'; describe('AuthWithCredentialsInterceptor', () => { beforeEach(() => TestBed.configureTestingModule({ - providers: [AuthWithCredentialsInterceptor], + providers: [WithCredentialsInterceptor], }) ); it('should be created', () => { - const interceptor: AuthWithCredentialsInterceptor = TestBed.inject( - AuthWithCredentialsInterceptor + const interceptor: WithCredentialsInterceptor = TestBed.inject( + WithCredentialsInterceptor ); expect(interceptor).toBeTruthy(); }); From e881cd02becaa117c33970cdb32ade01e5e488dd Mon Sep 17 00:00:00 2001 From: Dominik Halfkann Date: Sat, 16 Dec 2023 16:33:10 +0100 Subject: [PATCH 2/3] edit lint config --- .eslintrc.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index ff08d87a..2abdf216 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -102,7 +102,6 @@ "@angular-eslint/template/attributes-order": "warn", "@angular-eslint/template/banana-in-box": "error", "@angular-eslint/template/conditional-complexity": "error", - "@angular-eslint/template/cyclomatic-complexity": "error", "@angular-eslint/template/no-interpolation-in-attributes": "error", "@angular-eslint/template/no-negated-async": "error", "@angular-eslint/template/no-positive-tabindex": "error", From f43f5ae0776ff67a601a811f4659557474652020 Mon Sep 17 00:00:00 2001 From: Dominik Halfkann Date: Sat, 16 Dec 2023 17:03:17 +0100 Subject: [PATCH 3/3] fix tests --- .../init-user-name/init-user-name.component.spec.ts | 6 +++++- .../shared/data-access/auth/auth-storage.service.spec.ts | 6 ++++++ .../shared/data-access/auth/authentication.service.spec.ts | 5 ++++- src/app/shared/data-access/auth/authentication.service.ts | 2 +- src/app/shared/util/age.pipe.spec.ts | 6 +++--- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.spec.ts b/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.spec.ts index 67c3ad0a..e5a190c9 100644 --- a/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.spec.ts +++ b/src/app/profile/feature/initial-setup/init-user-name/init-user-name.component.spec.ts @@ -62,7 +62,11 @@ describe('Setting up username', () => { }), }, MockProvider(AuthStorageService, { - authData$: of({ isLoggedIn: true, isHuman: true }), + authData$: of({ + isLoggedIn: true, + isHuman: true, + jwt: 'token', + }), }), ], stubsEnabled: false, diff --git a/src/app/shared/data-access/auth/auth-storage.service.spec.ts b/src/app/shared/data-access/auth/auth-storage.service.spec.ts index 3995e6a8..fa893a08 100644 --- a/src/app/shared/data-access/auth/auth-storage.service.spec.ts +++ b/src/app/shared/data-access/auth/auth-storage.service.spec.ts @@ -39,6 +39,7 @@ describe('AuthStorageService', () => { const expectedAuthData: AuthData = { isLoggedIn: true, isHuman: true, + jwt: '123', }; getItemSpy.mockImplementation((key: string) => { if (key === 'authData') { @@ -63,6 +64,7 @@ describe('AuthStorageService', () => { const expectedAuthData: AuthData = { isLoggedIn: false, isHuman: false, + jwt: '', }; const actualAuthData = service.getSnapshot(); expect(actualAuthData).toEqual(expectedAuthData); @@ -84,6 +86,7 @@ describe('AuthStorageService', () => { expect(setItemSpy).toHaveBeenCalledWith( 'authData', JSON.stringify({ + jwt: '', isLoggedIn: true, isHuman: false, }) @@ -94,6 +97,7 @@ describe('AuthStorageService', () => { expect(listener).toHaveBeenCalledWith({ isLoggedIn: true, isHuman: false, + jwt: '', }); }); }); @@ -113,6 +117,7 @@ describe('AuthStorageService', () => { expect(setItemSpy).toHaveBeenCalledWith( 'authData', JSON.stringify({ + jwt: '', isLoggedIn: false, isHuman: true, }) @@ -121,6 +126,7 @@ describe('AuthStorageService', () => { it('informs subscribers about the change', () => { expect(listener).toHaveBeenCalledWith({ + jwt: '', isLoggedIn: false, isHuman: true, }); diff --git a/src/app/shared/data-access/auth/authentication.service.spec.ts b/src/app/shared/data-access/auth/authentication.service.spec.ts index 6761b93f..41d72bd0 100644 --- a/src/app/shared/data-access/auth/authentication.service.spec.ts +++ b/src/app/shared/data-access/auth/authentication.service.spec.ts @@ -119,7 +119,10 @@ describe('AuthenticationService', () => { status: 200, statusText: 'OK', }); - expect(authStorageService.setLoginState).toHaveBeenCalledWith(true); + expect(authStorageService.setLoginState).toHaveBeenCalledWith( + true, + undefined + ); }); it("doesn't set the login state in the auth storage service when login fails", () => { diff --git a/src/app/shared/data-access/auth/authentication.service.ts b/src/app/shared/data-access/auth/authentication.service.ts index e9d318d2..a1e71c06 100644 --- a/src/app/shared/data-access/auth/authentication.service.ts +++ b/src/app/shared/data-access/auth/authentication.service.ts @@ -71,7 +71,7 @@ export class AuthenticationService { tap((response) => this.authStorageService.setLoginState( true, - response.payload.accessToken + response.payload?.accessToken ) ), catchError((error: HttpErrorResponse) => { diff --git a/src/app/shared/util/age.pipe.spec.ts b/src/app/shared/util/age.pipe.spec.ts index 837967ff..2159d19c 100644 --- a/src/app/shared/util/age.pipe.spec.ts +++ b/src/app/shared/util/age.pipe.spec.ts @@ -17,9 +17,9 @@ describe('AgePipe', () => { }); it('returns the age for a given birthdate considering leap years', () => { - expect(agePipe.transform('1999-12-31')).toBe(1); - expect(agePipe.transform('2000-01-01')).toBe(1); - expect(agePipe.transform('2000-01-02')).toBe(0); + expect(agePipe.transform('1999-12-31')).toBe('1'); + expect(agePipe.transform('2000-01-01')).toBe('1'); + expect(agePipe.transform('2000-01-02')).toBe('0'); }); it('returns "unknown" when the birthdate is not parsable', () => {