From d36457e0ea1bf2aa2ddcac9c4919341e4b88c84b Mon Sep 17 00:00:00 2001 From: Eneko Nieto Date: Sat, 20 Feb 2021 01:53:08 +0100 Subject: [PATCH] Join and leave travels --- src/app/entities/travel-dto.ts | 17 ++++++ src/app/entities/travel.ts | 37 ++++++++----- src/app/pages/travel/travel.component.html | 8 ++- src/app/pages/travel/travel.component.ts | 64 +++++++++++++++++----- src/app/services/ability.service.spec.ts | 16 ++++++ src/app/services/ability.service.ts | 49 +++++++++++++++++ src/app/services/api.service.ts | 11 +++- src/app/services/auth.service.ts | 12 ---- 8 files changed, 174 insertions(+), 40 deletions(-) create mode 100644 src/app/entities/travel-dto.ts create mode 100644 src/app/services/ability.service.spec.ts create mode 100644 src/app/services/ability.service.ts diff --git a/src/app/entities/travel-dto.ts b/src/app/entities/travel-dto.ts new file mode 100644 index 0000000..e392298 --- /dev/null +++ b/src/app/entities/travel-dto.ts @@ -0,0 +1,17 @@ +import { User, UserId } from './user'; + +export type TravelId = number; + +export class TravelDto { + constructor( + public id: TravelId = -1, + public driverInfo: User = null, + public travelersInfo: User[] = [], + public departureDate: string = '', + public origin: string = '', + public destination: string = '', + public places: number = 0, + public matrixRoomId: string = '', + public description?: string + ) { } +} diff --git a/src/app/entities/travel.ts b/src/app/entities/travel.ts index 1b92123..40d9eec 100644 --- a/src/app/entities/travel.ts +++ b/src/app/entities/travel.ts @@ -1,21 +1,32 @@ -import { User } from './user'; +import { TravelDto } from './travel-dto'; +import { User, UserId } from './user'; export type TravelId = number; -export class Travel { - constructor( - public id: TravelId = -1, - public driverInfo: User = null, - public travelersInfo: User[] = [], - public departureDate: string = '', - public origin: string = '', - public destination: string = '', - public places: number = 0, - public matrixRoomId: string = '', - public description?: string - ) { } +export class Travel extends TravelDto { + constructor(travelDto: TravelDto) { + super(travelDto.id, + travelDto.driverInfo, + travelDto.travelersInfo, + travelDto.departureDate, + travelDto.origin, + travelDto.destination, + travelDto.places, + travelDto.matrixRoomId, + travelDto.description); + } availablePlaces(): number { return this.places - this.travelersInfo.length; } + + hasTraveler(id: UserId): boolean { + let hasTraveler = false; + this.travelersInfo.forEach(traveler => { + if (traveler.id === id) { + hasTraveler = true; + } + }); + return hasTraveler; + } } diff --git a/src/app/pages/travel/travel.component.html b/src/app/pages/travel/travel.component.html index 1f7349b..01c4ced 100644 --- a/src/app/pages/travel/travel.component.html +++ b/src/app/pages/travel/travel.component.html @@ -9,8 +9,14 @@

ID: {{ travel.id }}

Conductor: {{ travel.driverInfo.name }}


{{ travel.description }}

+

Travelers

+ - + + + \ No newline at end of file diff --git a/src/app/pages/travel/travel.component.ts b/src/app/pages/travel/travel.component.ts index 3161dd5..d02a8d5 100644 --- a/src/app/pages/travel/travel.component.ts +++ b/src/app/pages/travel/travel.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { AuthService } from 'src/app/services/auth.service'; +import { AbilityService } from 'src/app/services/ability.service'; import { Travel, TravelId } from '../../entities/travel'; import { ApiService } from '../../services/api.service'; @@ -16,7 +16,7 @@ export class TravelComponent implements OnInit { constructor( private router: Router, - private authService: AuthService, + private abilityService: AbilityService, private apiService: ApiService, private route: ActivatedRoute ) { } @@ -25,15 +25,7 @@ export class TravelComponent implements OnInit { this.route.queryParams.subscribe(params => { this.travelId = this.route.snapshot.paramMap.get('id') as unknown as TravelId; - this.apiService.getTravel(this.travelId) - .subscribe(res => { - if (res.success) { - this.travel = res.data; - } - else { - console.error('Error getting travel ' + this.travelId + ': ' + res.error); - } - }); + this.loadTravel(); }); } @@ -41,11 +33,57 @@ export class TravelComponent implements OnInit { this.router.navigateByUrl('/'); } + get canJoin(): boolean { + return this.abilityService.canJoinTravel(this.travel); + } + + join(): void { + this.apiService.joinTravel(this.travel.id) + .subscribe(res => { + if (res.success) { + console.log('Joined travel'); + this.loadTravel(); + } + else { + console.error('Error joining travel id=' + this.travel.id + ': ' + res.error.code + ' ' + res.error.msg); + } + }); + } + + get canLeave(): boolean { + return this.abilityService.canLeaveTravel(this.travel); + } + + leave(): void { + this.apiService.leaveTravel(this.travel.id) + .subscribe(res => { + if (res.success) { + console.log('Left travel'); + this.loadTravel(); + } + else { + console.error('Error leaving travel id=' + this.travel.id + ': ' + res.error.code + ' ' + res.error.msg); + } + }); + } + + get canEdit(): boolean { + return this.abilityService.canEditTravel(this.travel); + } + edit(): void { this.router.navigateByUrl(`/travel/${this.travelId}/edit`); } - get canEdit(): boolean { - return this.authService.canEditTravel(this.travel); + private loadTravel(): void { + this.apiService.getTravel(this.travelId) + .subscribe(res => { + if (res.success) { + this.travel = new Travel(res.data); + } + else { + console.error('Error getting travel ' + this.travelId + ': ' + res.error); + } + }); } } diff --git a/src/app/services/ability.service.spec.ts b/src/app/services/ability.service.spec.ts new file mode 100644 index 0000000..ec39538 --- /dev/null +++ b/src/app/services/ability.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AbilityService } from './ability.service'; + +describe('AbilityService', () => { + let service: AbilityService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AbilityService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/ability.service.ts b/src/app/services/ability.service.ts new file mode 100644 index 0000000..3eb6b16 --- /dev/null +++ b/src/app/services/ability.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { Travel } from '../entities/travel'; +import { AuthService } from './auth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AbilityService { + + constructor( + private authService: AuthService + ) { } + + canEditTravel(travel: Travel): boolean { + if (travel == null) { + return false; + } + const user = this.authService.currentUser(); + if (user == null) { + return false; + } + + return user.admin || (user.id === travel.driverInfo.id); + } + + canJoinTravel(travel: Travel): boolean { + if (travel == null) { + return false; + } + const user = this.authService.currentUser(); + if (user == null) { + return false; + } + + return (travel.driverInfo.id !== user.id) && (! travel.hasTraveler(user.id)); + } + + canLeaveTravel(travel: Travel): boolean { + if (travel == null) { + return false; + } + const user = this.authService.currentUser(); + if (user == null) { + return false; + } + + return travel.hasTraveler(user.id); + } +} diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index e61d89e..b1404bb 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -8,6 +8,7 @@ import { PUBLIC_API_URL, TRAVEL_API_URL, USER_API_URL } from '../app.config'; import { ListDto } from '../entities/list-dto'; import { User } from '../entities/user'; import { AuthService } from './auth.service'; +import { TravelDto } from '../entities/travel-dto'; export type ApiCall = (params?: { [param: string]: any }) => (Observable>>); @@ -28,7 +29,7 @@ export class ApiService { } getTravel = (travelId: TravelId) => { - return this.callApi(PUBLIC_API_URL + '/travel', { travelId }); + return this.callApi(PUBLIC_API_URL + '/travel', { travelId }); } getTravels = (params?: { [param: string]: any }) => { @@ -47,6 +48,14 @@ export class ApiService { return this.callApi(TRAVEL_API_URL + '/edit', null, travel); } + joinTravel = (travelId: TravelId) => { + return this.callApi(TRAVEL_API_URL + '/join', { travelId }); + } + + leaveTravel = (travelId: TravelId) => { + return this.callApi(TRAVEL_API_URL + '/leave', { travelId }); + } + private callApi( url: string, params?: { [param: string]: any }, diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index dae3189..432e743 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -34,18 +34,6 @@ export class AuthService { return User.fromClaims(claims); } - canEditTravel(travel: Travel): boolean { - if (travel == null) { - return false; - } - const user = this.currentUser(); - if (user == null) { - return false; - } - - return user.admin || (user.id === travel.driverInfo.id); - } - private async configureAndTryLogin(): Promise { this.oauthService.configure(authConfig); this.oauthService.setupAutomaticSilentRefresh();