From 9c1697dfeeb2b00866ae967cc19d8faa81484f77 Mon Sep 17 00:00:00 2001 From: Pablo Fraile Alonso Date: Tue, 24 Oct 2023 16:00:04 +0200 Subject: [PATCH] feat: add async get refuges list --- .../refuge-list/refuges-list.page.html | 10 +-- .../refuges/refuge-list/refuges-list.page.ts | 90 +++++++++---------- .../schemas/refuge/get-all-refuges-schema.ts | 29 +++--- app/src/app/services/refuge/refuge.service.ts | 88 +++++++++--------- 4 files changed, 111 insertions(+), 106 deletions(-) diff --git a/app/src/app/pages/refuges/refuge-list/refuges-list.page.html b/app/src/app/pages/refuges/refuge-list/refuges-list.page.html index 8f6644f..c3b5f42 100644 --- a/app/src/app/pages/refuges/refuge-list/refuges-list.page.html +++ b/app/src/app/pages/refuges/refuge-list/refuges-list.page.html @@ -20,7 +20,7 @@
{{refuge.name}}

- RegiĆ³:
+ RegiĆ³:
{{refuge.region}}

- Altitud:
+ Altitud:
{{refuge.altitude}}m

Capacitat: -
- - Hivern: {{refuge.capacity.winter}}
+
+ - Hivern: {{refuge.capacity.winter}}
- Estiu: {{refuge.capacity.summer}}

diff --git a/app/src/app/pages/refuges/refuge-list/refuges-list.page.ts b/app/src/app/pages/refuges/refuge-list/refuges-list.page.ts index 6255891..fb8429e 100644 --- a/app/src/app/pages/refuges/refuge-list/refuges-list.page.ts +++ b/app/src/app/pages/refuges/refuge-list/refuges-list.page.ts @@ -1,13 +1,17 @@ -import { Component, OnInit } from '@angular/core'; -import { RefugeService } from '../../../services/refuge/refuge.service'; -import { AlertController } from '@ionic/angular'; -import { match } from 'ts-pattern'; -import { Refuge } from '../../../schemas/refuge/refuge'; +import {Component, OnInit} from '@angular/core'; +import {RefugeService} from '../../../services/refuge/refuge.service'; +import {AlertController} from '@ionic/angular'; +import {isMatching, match} from 'ts-pattern'; +import {Refuge} from '../../../schemas/refuge/refuge'; import { + CorrectGetRefuges, + CorrectGetRefugesPattern, + ErrorGetRefuges, + ErrorGetRefugesPattern, GetAllRefugesErrors, - GetAllRefugesResponse, } from '../../../schemas/refuge/get-all-refuges-schema'; -import { Router } from '@angular/router'; +import {Router} from '@angular/router'; +import {BehaviorSubject, combineLatest, filter, map, Observable, Subject} from "rxjs"; @Component({ selector: 'app-refuges', @@ -15,37 +19,52 @@ import { Router } from '@angular/router'; styleUrls: ['./refuges-list.page.scss'], }) export class RefugesListPage implements OnInit { - refuges: Refuge[] = []; searchTerm: string = ''; - allRefuges: Refuge[] = []; + refuges: Observable + errors: Observable + private search: Subject constructor( private router: Router, private refugeService: RefugeService, private alertController: AlertController, ) { - this.getRefuges(); + this.errors = this.refugeService.getRefuges().pipe( + filter((response): response is ErrorGetRefuges => !isMatching(ErrorGetRefugesPattern, response)), + map((response: ErrorGetRefuges) => response.error) + ); + this.search = new BehaviorSubject(""); + const searchInput = this.search.asObservable(); + const allRefuges = this.refugeService.getRefuges().pipe( + filter((response): response is CorrectGetRefuges => isMatching(CorrectGetRefugesPattern, response)), + map((response: CorrectGetRefuges) => response.data), + ); + this.refuges = combineLatest([searchInput, allRefuges]).pipe( + map(([searchTerm, refuges]) => { + if (searchTerm === "") return refuges; + return refuges.filter((refuge: Refuge) => refuge.name.toLowerCase().includes(searchTerm.toLowerCase())) + }), + ) } - ngOnInit() {} - - getRefuges() { - return this.refugeService.getRefuges().subscribe({ - next: (response: any) => this.handleGetAllRefugesResponse(response), + ngOnInit() { + this.errors.subscribe({ + next: (error: GetAllRefugesErrors) => this.handleError(error), error: () => this.handleClientError().then(), - }); + }) } - private handleGetAllRefugesResponse(response: GetAllRefugesResponse) { - match(response) - .with({ status: 'correct' }, (response) => { - this.allRefuges = response.data; - this.refuges = this.allRefuges; - }) - .with({ status: 'error' }, (response) => { - this.handleError(response.error); - }) - .exhaustive(); + + getImageUrlFor(refuge: Refuge): string { + return this.refugeService.getImageUrlFor(refuge); + } + + searchByName() { + this.search.next(this.searchTerm); + } + + createRefuge() { + console.log('create refuge'); } private handleError(error: GetAllRefugesErrors) { @@ -68,7 +87,6 @@ export class RefugesListPage implements OnInit { text: 'OK', handler: () => { this.alertController.dismiss().then(); - this.getRefuges(); }, }, ], @@ -91,22 +109,4 @@ export class RefugesListPage implements OnInit { }) .then(); } - - getImageUrlFor(refuge: Refuge): string { - return this.refugeService.getImageUrlFor(refuge); - } - - searchByName() { - if (this.searchTerm === '') { - this.refuges = this.allRefuges; - return; - } - this.refuges = this.allRefuges.filter((refuge: Refuge) => - refuge.name.toLowerCase().includes(this.searchTerm.toLowerCase()), - ); - } - - createRefuge() { - console.log('create refuge'); - } } diff --git a/app/src/app/schemas/refuge/get-all-refuges-schema.ts b/app/src/app/schemas/refuge/get-all-refuges-schema.ts index d32d4b0..51ad7a5 100644 --- a/app/src/app/schemas/refuge/get-all-refuges-schema.ts +++ b/app/src/app/schemas/refuge/get-all-refuges-schema.ts @@ -1,21 +1,28 @@ -import { Refuge } from './refuge'; -import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; -import { match } from 'ts-pattern'; +import {Refuge} from './refuge'; +import {HttpErrorResponse} from '@angular/common/http'; +import {match, P} from 'ts-pattern'; export enum GetAllRefugesErrors { UNKNOWN_ERROR = 'UNKNOWN_ERROR', SERVER_INCORRECT_DATA_FORMAT_ERROR = 'SERVER_INCORRECT_DATA_FORMAT_ERROR', } +export type CorrectGetRefuges = { + status: 'correct'; + data: Refuge[]; +} + +export type ErrorGetRefuges = { + status: 'error'; + error: GetAllRefugesErrors; +}; + export type GetAllRefugesResponse = - | { - status: 'correct'; - data: Refuge[]; - } - | { - status: 'error'; - error: GetAllRefugesErrors; - }; + | CorrectGetRefuges + | ErrorGetRefuges; + +export const CorrectGetRefugesPattern: P.Pattern = {}; +export const ErrorGetRefugesPattern: P.Pattern = {}; export namespace GetAllRefugesErrors { export function from(err: HttpErrorResponse): GetAllRefugesErrors | never { diff --git a/app/src/app/services/refuge/refuge.service.ts b/app/src/app/services/refuge/refuge.service.ts index f2ddccf..70a4fa9 100644 --- a/app/src/app/services/refuge/refuge.service.ts +++ b/app/src/app/services/refuge/refuge.service.ts @@ -1,30 +1,50 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpErrorResponse } from '@angular/common/http'; -import { catchError, map, Observable, ObservableInput, of, retry } from 'rxjs'; -import { - GetAllRefugesErrors, - GetAllRefugesResponse, -} from '../../schemas/refuge/get-all-refuges-schema'; -import { environment } from '../../../environments/environment'; -import { isValidId, Refuge, RefugePattern } from '../../schemas/refuge/refuge'; -import { isMatching } from 'ts-pattern'; -import { - GetRefugeFromIdErrors, - GetRefugeResponse, -} from '../../schemas/refuge/get-refuge-schema'; -import { - DeleteRefugeFromIdErrors, - DeleteRefugeResponse, -} from '../../schemas/refuge/delete-refuge-schema'; +import {Injectable} from '@angular/core'; +import {HttpClient, HttpErrorResponse} from '@angular/common/http'; +import {catchError, distinctUntilChanged, map, mergeMap, Observable, ObservableInput, of, retry, timer} from 'rxjs'; +import {GetAllRefugesErrors, GetAllRefugesResponse,} from '../../schemas/refuge/get-all-refuges-schema'; +import {environment} from '../../../environments/environment'; +import {isValidId, Refuge, RefugePattern} from '../../schemas/refuge/refuge'; +import {isMatching} from 'ts-pattern'; +import {GetRefugeFromIdErrors, GetRefugeResponse,} from '../../schemas/refuge/get-refuge-schema'; +import {DeleteRefugeFromIdErrors, DeleteRefugeResponse,} from '../../schemas/refuge/delete-refuge-schema'; @Injectable({ providedIn: 'root', }) export class RefugeService { - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) { + } + /** + * @description get refuges from the API every 3 seconds + */ getRefuges(): Observable { - return this.getAllRefugesFromApi(); + return timer(0, 3000).pipe( + mergeMap(() => this.getAllRefugesFromApi()), + distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)) + ); + } + + getRefugeFrom(id: string): Observable { + if (!isValidId(id)) + return of({ + status: 'error', + error: GetRefugeFromIdErrors.CLIENT_SEND_DATA_ERROR, + }); + return this.getRefugeFromApi(id); + } + + getImageUrlFor(refuge: Refuge): string { + return `${environment.API}/static/images/refuges/${refuge.image}`; + } + + deleteRefuge(id: string): Observable { + if (!isValidId(id)) + return of({ + status: 'error', + error: DeleteRefugeFromIdErrors.CLIENT_SEND_DATA_ERROR, + }); + return this.deleteRefugeFromApi(id); } private getAllRefugesFromApi(): Observable { @@ -32,7 +52,7 @@ export class RefugeService { return this.http.get(endpoint).pipe( map((refuges: Refuge[]) => { if (isMatching(RefugePattern, refuges.values())) - return { status: 'correct', data: refuges }; + return {status: 'correct', data: refuges}; return { status: 'error', error: GetAllRefugesErrors.SERVER_INCORRECT_DATA_FORMAT_ERROR, @@ -53,21 +73,12 @@ export class RefugeService { return `${environment.API}/refuges/`; } - getRefugeFrom(id: string): Observable { - if (!isValidId(id)) - return of({ - status: 'error', - error: GetRefugeFromIdErrors.CLIENT_SEND_DATA_ERROR, - }); - return this.getRefugeFromApi(id); - } - private getRefugeFromApi(id: string): Observable { const endpoint = this.getRefugeFromIdEndpoint(id); return this.http.get(endpoint).pipe( map((refuge: Refuge) => { if (isMatching(RefugePattern, refuge)) - return { status: 'correct', data: refuge }; + return {status: 'correct', data: refuge}; return { status: 'error', error: GetRefugeFromIdErrors.SERVER_INCORRECT_DATA_FORMAT_ERROR, @@ -88,25 +99,12 @@ export class RefugeService { return `${environment.API}/refuges/${id}/`; } - getImageUrlFor(refuge: Refuge): string { - return `${environment.API}/static/images/refuges/${refuge.image}`; - } - - deleteRefuge(id: string): Observable { - if (!isValidId(id)) - return of({ - status: 'error', - error: DeleteRefugeFromIdErrors.CLIENT_SEND_DATA_ERROR, - }); - return this.deleteRefugeFromApi(id); - } - private deleteRefugeFromApi(id: string): Observable { const endpoint = this.deleteRefugeFromIdEndpoint(id); return this.http.delete(endpoint).pipe( map((refuge: Refuge) => { if (isMatching(RefugePattern, refuge)) - return { status: 'correct', data: refuge }; + return {status: 'correct', data: refuge}; return { status: 'error', error: DeleteRefugeFromIdErrors.SERVER_INCORRECT_DATA_FORMAT_ERROR,