Skip to content

Commit bc370db

Browse files
committed
feat(core): add loading state for my pokemon page
1 parent 5c3b305 commit bc370db

7 files changed

Lines changed: 109 additions & 38 deletions

File tree

src/app/features/authentication/types/user.type.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ export type User = {
88
name: string;
99
language: Language;
1010
favouritePokemonId: number;
11-
caughtPokemonIds?: number[];
11+
caughtPokemonIds: number[];
1212
};

src/app/features/my-pokemon/my-pokemon.component.html

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@ <h1 class="first-heading__title" i18n>My Pokemon</h1>
55
[title]="userPokemon?.length ? translations.findMore : translations.findPokemon"
66
/>
77
</div>
8-
<ul i18n class="my-pokemon__grid">
9-
@for (pokemon of userPokemon; track pokemon.id) {
8+
@if (userPokemon === undefined) {
9+
<ul class="my-pokemon__grid fade-out">
1010
<li>
11-
<app-pokemon-card [pokemon]="pokemon" />
11+
<app-pokemon-card [loading]="true" />
1212
</li>
13-
}
14-
</ul>
15-
@if (!userPokemon?.length) {
13+
<li>
14+
<app-pokemon-card [loading]="true" />
15+
</li>
16+
<li>
17+
<app-pokemon-card [loading]="true" />
18+
</li>
19+
</ul>
20+
} @else if (!userPokemon.length) {
1621
<div class="pokemons-empty__container">
1722
<div>
1823
<p i18n>
@@ -31,5 +36,13 @@ <h1 class="first-heading__title" i18n>My Pokemon</h1>
3136
/>
3237
</div>
3338
</div>
39+
} @else {
40+
<ul i18n class="my-pokemon__grid fade-in">
41+
@for (pokemon of userPokemon; track pokemon.id) {
42+
<li>
43+
<app-pokemon-card [pokemon]="pokemon" />
44+
</li>
45+
}
46+
</ul>
3447
}
3548
</main>

src/app/features/my-pokemon/my-pokemon.component.scss

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,27 @@ $empty-image-width: 200px;
1212
.my-pokemon__search-container {
1313
display: flex;
1414
justify-content: center;
15+
margin-block-end: var(--spacing-r-5xl);
1516
}
1617

1718
.my-pokemon__grid {
1819
display: grid;
1920
grid-template-columns: 1fr;
2021
gap: var(--spacing-r-xl);
22+
visibility: hidden;
23+
opacity: 0;
2124

2225
@include mq.for-tablet-up {
2326
grid-template-columns: repeat($grid-columns, 1fr);
2427
}
28+
29+
&.fade-in {
30+
animation: fade-in 0.1s forwards;
31+
}
32+
33+
&.fade-out {
34+
animation: fade-out 0.4s forwards;
35+
}
2536
}
2637

2738
.pokemons-empty__container {
@@ -40,3 +51,27 @@ $empty-image-width: 200px;
4051
}
4152
}
4253
}
54+
55+
@keyframes fade-in {
56+
from {
57+
visibility: hidden;
58+
opacity: 0;
59+
}
60+
61+
to {
62+
visibility: visible;
63+
opacity: 1;
64+
}
65+
}
66+
67+
@keyframes fade-out {
68+
from {
69+
visibility: visible;
70+
opacity: 1;
71+
}
72+
73+
to {
74+
visibility: hidden;
75+
opacity: 0;
76+
}
77+
}

src/app/features/my-pokemon/my-pokemon.component.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import { AlertService } from '~core/services/alert.service';
1919
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
2020

2121
@Component({
22-
selector: 'app-my-pokemon',
23-
templateUrl: './my-pokemon.component.html',
24-
styleUrl: './my-pokemon.component.scss',
25-
imports: [PokemonCardComponent, NgOptimizedImage, PokemonSearchComponent],
26-
changeDetection: ChangeDetectionStrategy.OnPush,
27-
schemas: [CUSTOM_ELEMENTS_SCHEMA]
22+
selector: 'app-my-pokemon',
23+
templateUrl: './my-pokemon.component.html',
24+
styleUrl: './my-pokemon.component.scss',
25+
imports: [PokemonCardComponent, NgOptimizedImage, PokemonSearchComponent],
26+
changeDetection: ChangeDetectionStrategy.OnPush,
27+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
2828
})
2929
export class MyPokemonComponent implements OnInit {
3030
private readonly userService = inject(UserService);
@@ -44,8 +44,7 @@ export class MyPokemonComponent implements OnInit {
4444
.subscribe({
4545
next: (user) => {
4646
this.user = user;
47-
48-
if (this.user.caughtPokemonIds) {
47+
if (this.user.caughtPokemonIds.length > 0) {
4948
this.pokemonService
5049
.getPokemons(this.user.caughtPokemonIds)
5150
.pipe(takeUntilDestroyed(this.destroyRef))
@@ -58,6 +57,9 @@ export class MyPokemonComponent implements OnInit {
5857
this.alertService.createErrorAlert(translations.genericErrorAlert);
5958
},
6059
});
60+
} else {
61+
this.userPokemon = [];
62+
this.changeDetectorRef.markForCheck();
6163
}
6264
},
6365
});

src/app/features/pokemon/components/pokedex/pokedex.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class PokedexComponent implements OnInit {
6262
next: (user: User) => {
6363
this.user = user;
6464
this.pokemonImage = pokemonValue.sprites.front_default;
65-
this.userHasPokemon = user.caughtPokemonIds?.includes(pokemonValue.id) ?? false;
65+
this.userHasPokemon = user.caughtPokemonIds.includes(pokemonValue.id);
6666
setTimeout(() => {
6767
this.isPokedexClosed = false;
6868
this.changeDetectorRef.markForCheck();
@@ -106,7 +106,7 @@ export class PokedexComponent implements OnInit {
106106
const pokemonValue = this.pokemon();
107107
if (pokemonValue) {
108108
this.pokemonImage = pokemonValue.sprites.front_default;
109-
this.userHasPokemon = this.user?.caughtPokemonIds?.includes(pokemonValue.id) ?? false;
109+
this.userHasPokemon = this.user?.caughtPokemonIds.includes(pokemonValue.id) ?? false;
110110
this.changeDetectorRef.markForCheck();
111111
}
112112
}
Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,37 @@
1-
<app-card>
2-
<ng-container i18n ngProjectAs="heading">
3-
<h2 class="card__heading">{{ pokemon()?.name | firstTitleCase }}</h2>
4-
</ng-container>
5-
<ng-container ngProjectAs="body">
6-
<div class="pokemon__image-container">
7-
<img
8-
alt="angular logo"
9-
i18n-alt
10-
width="100"
11-
height="100"
12-
priority
13-
[ngSrc]="pokemonImage || ''"
14-
/>
15-
</div>
16-
<p i18n>N.º: {{pokemon()?.order}}</p>
17-
<p i18n>Height: {{pokemon()?.height}} dm</p>
18-
<p i18n>Weight: {{pokemon()?.weight}} hg</p>
19-
</ng-container>
20-
</app-card>
1+
<!-- eslint-disable-next-line @angular-eslint/template/no-call-expression -->
2+
@if (!loading()) {
3+
<app-card>
4+
<ng-container i18n ngProjectAs="heading">
5+
<h2 class="card__heading">{{ pokemon()?.name | firstTitleCase }}</h2>
6+
</ng-container>
7+
<ng-container ngProjectAs="body">
8+
<div class="pokemon__image-container">
9+
<img
10+
alt="angular logo"
11+
i18n-alt
12+
width="100"
13+
height="100"
14+
priority
15+
[ngSrc]="pokemonImage || ''"
16+
/>
17+
</div>
18+
<p i18n>N.º: {{pokemon()?.order}}</p>
19+
<p i18n>Height: {{pokemon()?.height}} dm</p>
20+
<p i18n>Weight: {{pokemon()?.weight}} hg</p>
21+
</ng-container>
22+
</app-card>
23+
} @else {
24+
<app-card>
25+
<ng-container ngProjectAs="heading">
26+
<sl-skeleton effect="pulse" />
27+
</ng-container>
28+
<ng-container ngProjectAs="body">
29+
<div class="pokemon__image-container">
30+
<sl-skeleton effect="pulse" />
31+
</div>
32+
<p><sl-skeleton effect="pulse" /></p>
33+
<p><sl-skeleton effect="pulse" /></p>
34+
<p><sl-skeleton effect="pulse" /></p>
35+
</ng-container>
36+
</app-card>
37+
}

src/app/features/pokemon/components/pokemon-card/pokemon-card.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import type { OnInit } from '@angular/core';
2-
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2+
import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, input } from '@angular/core';
33
import type { Pokemon } from '~features/pokemon/types/pokemon.type';
44
import { CardComponent } from '~core/components/card/card.component';
55
import { FirstTitleCasePipe } from '~core/pipes/first-title-case.pipe';
66
import { NgOptimizedImage } from '@angular/common';
77

8+
import '@shoelace-style/shoelace/dist/components/skeleton/skeleton.js';
9+
810
@Component({
911
selector: 'app-pokemon-card',
1012
templateUrl: './pokemon-card.component.html',
1113
styleUrl: './pokemon-card.component.scss',
1214
changeDetection: ChangeDetectionStrategy.OnPush,
1315
imports: [CardComponent, FirstTitleCasePipe, NgOptimizedImage],
16+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
1417
})
1518
export class PokemonCardComponent implements OnInit {
1619
readonly pokemon = input<Pokemon>();
20+
readonly loading = input<boolean>();
1721

1822
pokemonImage: string | undefined;
1923

0 commit comments

Comments
 (0)