Join and leave travels
This commit is contained in:
17
src/app/entities/travel-dto.ts
Normal file
17
src/app/entities/travel-dto.ts
Normal file
@@ -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
|
||||
) { }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,14 @@
|
||||
<p>ID: {{ travel.id }}</p>
|
||||
<p>Conductor: {{ travel.driverInfo.name }}</p><br>
|
||||
<p>{{ travel.description }}</p>
|
||||
<p>Travelers</p>
|
||||
<ul>
|
||||
<li *ngFor="let traveler of travel.travelersInfo">{{ traveler.name }}</li>
|
||||
</ul>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<button mat-raised-button class="btn btn-default" (click)="goBack()">Back</button>
|
||||
<button *ngIf="canEdit" mat-raised-button class="btn btn-default" (click)="edit()">Edit</button>
|
||||
<button *ngIf="canJoin" mat-raised-button class="btn btn-default" (click)="join()">Join</button>
|
||||
<button *ngIf="canLeave" mat-raised-button class="btn btn-default" (click)="leave()">Leave</button>
|
||||
<button *ngIf="canEdit" mat-raised-button class="btn btn-default" (click)="edit()">Edit</button>
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
16
src/app/services/ability.service.spec.ts
Normal file
16
src/app/services/ability.service.spec.ts
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
49
src/app/services/ability.service.ts
Normal file
49
src/app/services/ability.service.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<T> = (params?: { [param: string]: any }) => (Observable<ApiResponse<ListDto<T>>>);
|
||||
|
||||
@@ -28,7 +29,7 @@ export class ApiService {
|
||||
}
|
||||
|
||||
getTravel = (travelId: TravelId) => {
|
||||
return this.callApi<Travel>(PUBLIC_API_URL + '/travel', { travelId });
|
||||
return this.callApi<TravelDto>(PUBLIC_API_URL + '/travel', { travelId });
|
||||
}
|
||||
|
||||
getTravels = (params?: { [param: string]: any }) => {
|
||||
@@ -47,6 +48,14 @@ export class ApiService {
|
||||
return this.callApi<void>(TRAVEL_API_URL + '/edit', null, travel);
|
||||
}
|
||||
|
||||
joinTravel = (travelId: TravelId) => {
|
||||
return this.callApi<void>(TRAVEL_API_URL + '/join', { travelId });
|
||||
}
|
||||
|
||||
leaveTravel = (travelId: TravelId) => {
|
||||
return this.callApi<void>(TRAVEL_API_URL + '/leave', { travelId });
|
||||
}
|
||||
|
||||
private callApi<T>(
|
||||
url: string,
|
||||
params?: { [param: string]: any },
|
||||
|
||||
@@ -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<boolean> {
|
||||
this.oauthService.configure(authConfig);
|
||||
this.oauthService.setupAutomaticSilentRefresh();
|
||||
|
||||
Reference in New Issue
Block a user