diff --git a/src/app/annuaire/annuaire-header/annuaire-header.component.ts b/src/app/annuaire/annuaire-header/annuaire-header.component.ts index f8178e77c34990b29db762356885a85a3e7d31b0..1c60be68d4ce40f7de590bd98b5d507f453f6c36 100644 --- a/src/app/annuaire/annuaire-header/annuaire-header.component.ts +++ b/src/app/annuaire/annuaire-header/annuaire-header.component.ts @@ -12,9 +12,8 @@ import { SearchQuery } from '../models/searchQuery.model'; }) export class AnnuaireHeaderComponent implements OnInit, OnChanges { @Input() shouldResetFilters = 0; - @Input() shouldShowMore = 0; + @Input() shouldShowNextPage = 0; @Output() searchEvent = new EventEmitter<SearchQuery>(); - @Output() shouldLoad = new EventEmitter<boolean>(); public addStructureFormModal = false; public modalTypeOpened: TypeModal; @@ -62,12 +61,15 @@ export class AnnuaireHeaderComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { if (changes.shouldResetFilters && changes.shouldResetFilters.currentValue !== 0) { this.resetFilters(); - this.shouldLoad.emit(true); } - if (changes.shouldShowMore && !changes.shouldShowMore.firstChange) { + if ( + changes.shouldShowNextPage && + !changes.shouldShowNextPage.firstChange && + changes.shouldShowNextPage.currentValue > 1 + ) { this.searchEvent.emit({ search: this.searchInput, - page: changes.shouldShowMore.currentValue, + page: changes.shouldShowNextPage.currentValue, jobFilters: this.jobFilterChecked, employerFilters: this.employerFilterChecked, }); @@ -89,7 +91,6 @@ export class AnnuaireHeaderComponent implements OnInit, OnChanges { /** Sends an array containing all filters */ public applyFilter(): void { - this.shouldLoad.emit(true); this.shouldResetFilters = 0; // Add search input filter if (this.searchInput) { @@ -106,6 +107,7 @@ export class AnnuaireHeaderComponent implements OnInit, OnChanges { this.splitFilters(this.searchService.checkedFilterList); this.searchEvent.emit({ search: this.searchInput, + page: 1, jobFilters: this.jobFilterChecked, employerFilters: this.employerFilterChecked, }); @@ -157,25 +159,25 @@ export class AnnuaireHeaderComponent implements OnInit, OnChanges { } public resetFilters(): void { - this.shouldLoad.emit(true); this.searchInput = ''; this.searchService.checkedFilterList = []; this.employersFiltersActive = false; this.jobsFiltersActive = false; this.employerFilterChecked = []; this.jobFilterChecked = []; - this.searchEvent.emit({ search: '', jobFilters: [], employerFilters: [] }); - this.router.navigate(['/annuaire']); + this.searchEvent.emit({ search: '', page: 1, jobFilters: [], employerFilters: [] }); + // Delay to avoid error in console: "NG0100: ExpressionChangedAfterItHasBeenCheckedError" + setTimeout(() => this.router.navigate(['/annuaire']), 1); } public removeFilter(filter: string): void { - this.shouldLoad.emit(true); const index = this.searchService.checkedFilterList.findIndex((checkedFilter: string) => checkedFilter === filter); this.searchService.checkedFilterList.splice(index, 1); this.splitFilters(this.searchService.checkedFilterList); this.countCheckedFilters(); this.searchEvent.emit({ search: this.searchInput, + page: 1, jobFilters: this.jobFilterChecked, employerFilters: this.employerFilterChecked, }); diff --git a/src/app/annuaire/annuaire.component.html b/src/app/annuaire/annuaire.component.html index a4df678ae677f06f2ce99d0567ccc84b0814c6b2..99b9180941e7d5fe5308ac32723f172c7744093a 100644 --- a/src/app/annuaire/annuaire.component.html +++ b/src/app/annuaire/annuaire.component.html @@ -4,9 +4,8 @@ <app-annuaire-header class="hide-on-print" [shouldResetFilters]="resetFilters" - [shouldShowMore]="nextPage" + [shouldShowNextPage]="searchService.annuaireSearchQuery?.page" (searchEvent)="searchUsers($event)" - (shouldLoad)="shouldLoad($event)" /> <app-result-list [isLoading]="isLoading" @@ -18,5 +17,11 @@ /> </div> - <app-result-list *ngIf="!userIsLoggedIn()" [userList]="[]" [isLogged]="false" [totalUserResult]="totalUserResult" /> + <app-result-list + *ngIf="!userIsLoggedIn()" + [isLoading]="isLoading" + [userList]="[]" + [isLogged]="false" + [totalUserResult]="totalUserResult" + /> </div> diff --git a/src/app/annuaire/annuaire.component.ts b/src/app/annuaire/annuaire.component.ts index f46d4f0327ae177eb281ba1c6a482bd037d4ce09..799bfee957c8560d052ca34ce78617f3522bd656 100644 --- a/src/app/annuaire/annuaire.component.ts +++ b/src/app/annuaire/annuaire.component.ts @@ -11,27 +11,27 @@ import { SearchQuery } from './models/searchQuery.model'; }) export class AnnuaireComponent implements OnInit { constructor( - private searchService: SearchService, + public searchService: SearchService, private authService: AuthService, ) {} public userList: UserAnnuary[] = []; public totalUserResult: number; public resetFilters = 0; - public nextPage = 1; public isLoading = true; + private isAlreadySearching = false; ngOnInit(): void { - if (!this.userIsLoggedIn()) { + if (this.userIsLoggedIn()) { + this.userList = this.searchService.previousResult$.getValue().docs; + this.totalUserResult = this.searchService.previousResult$.getValue().count; + if (this.userList.length !== 0) { + this.isLoading = false; + } + } else { this.searchService.getUserRegistryCount().subscribe((count) => { this.totalUserResult = count; + this.isLoading = false; }); - this.isLoading = false; - } - - this.userList = this.searchService.previousResult$.getValue().docs; - this.totalUserResult = this.searchService.previousResult$.getValue().count; - if (this.userList.length !== 0) { - this.isLoading = false; } } @@ -44,29 +44,31 @@ export class AnnuaireComponent implements OnInit { } public searchUsers(params: SearchQuery): void { - this.loadParams(params); - this.searchService - .searchUserRegistry(params.search, params.page, params.jobFilters, params.employerFilters) - .then((res) => { - this.searchService.previousResult$.next({ count: res.count, docs: res.docs }); - this.userList = res.docs; - this.totalUserResult = res.count; - this.isLoading = false; - }); + if (!this.isAlreadySearching) { + this.isAlreadySearching = true; + params.page = params.page || 1; + if (params.page === 1) { + this.userList = []; + } + this.loadParams(params); + this.isLoading = true; + this.searchService + .searchUserRegistry(params.search, params.page, params.jobFilters, params.employerFilters) + .then((res) => { + // We only push new users instead of reassigning userList to avoid an unwanted scroll + this.userList.push(...res.docs); + this.searchService.previousResult$.next({ count: res.count, docs: this.userList }); + this.totalUserResult = res.count; + this.isLoading = false; + this.isAlreadySearching = false; + }); + } } public shouldResetFilters(): void { this.resetFilters++; } public showMore(): void { - this.nextPage++; - } - - /** - * Should load in specific conditions such as changes in filters and search input. - * Should NOT load when showing more result because it would result in losing scroll position because of the rerender of the list. - */ - public shouldLoad(value: boolean): void { - this.isLoading = value; + this.searchService.annuaireSearchQuery.page++; } } diff --git a/src/app/annuaire/result-list/result-list.component.html b/src/app/annuaire/result-list/result-list.component.html index 9bb0da4397d357293219fbe8eabe083955300080..0b198da379a9c7bf7f41324b307ef359b620d67e 100644 --- a/src/app/annuaire/result-list/result-list.component.html +++ b/src/app/annuaire/result-list/result-list.component.html @@ -1,68 +1,66 @@ -<div class="results" id="resultList" (scroll)="onScroll($event)"> - <div *ngIf="isLoading" class="loader" aria-busy="true"> - <img class="loader-gif" src="/assets/gif/loader_circle.gif" alt /> +<div class="results" id="resultList"> + <div *ngIf="!isLogged && !isLoading" class="notConnected"> + <img src="../../assets/ico/annuaire-unlogged.svg" alt="Illustration annuaire" /> + <div class="count" [ngPlural]="totalUserResult"> + <ng-template ngPluralCase="0">Aucun membre n'est présent dans l'annuaire Rés'in</ng-template> + <ng-template ngPluralCase="1">1 membre est présent dans l'annuaire Rés'in</ng-template> + <ng-template ngPluralCase="other"> + {{ totalUserResult }} membres sont présents dans l'annuaire Rés'in + </ng-template> + </div> + <div class="access"> + Pour accéder à l’annuaire de Rés'in et contacter les membres,<br /> + veuillez vous connecter ou vous créer un compte. + </div> + <div class="buttons"> + <app-button [label]="'Créer un compte'" [variant]="'secondary'" (action)="goRegister()" /> + <app-button [label]="'Se connecter'" [variant]="'primary'" (action)="goLogin()" /> + </div> </div> - <ng-container *ngIf="!isLoading"> - <ng-container *ngIf="isLogged"> - <div class="membersCount"> - <h2 class="uppercase">membres du réseau</h2> - <div class="userNumber"> - {{ userList.length }} membre{{ userList.length > 1 ? 's' : '' }} - <span *ngIf="showPagination"> sur {{ totalUserResult }} </span> - </div> + <ng-container *ngIf="isLogged"> + <div *ngIf="!isLoading" class="membersCount"> + <h2 class="uppercase">membres du réseau</h2> + <div class="userNumber"> + {{ userList.length }} membre{{ userList.length > 1 ? 's' : '' }} + <span *ngIf="showPagination"> sur {{ totalUserResult }} </span> </div> - <div class="resultsInfo"> - <div *ngIf="userList.length" class="users"> - <app-member-card - *ngFor="let user of userList" - [member]="user" - [showAppointment]="true" - [showContactInfo]="false" - [showEmployer]="true" - /> - <div *ngIf="showPagination" class="pagination"> - <p>{{ userList.length }} membres affichés sur {{ totalUserResult }}</p> - <app-button - [label]="'Voir plus'" - [variant]="'primaryBlack'" - [size]="'medium'" - [wide]="true" - (action)="showMore()" - /> - </div> - </div> - <ng-container *ngIf="userList.length === 0"> - <p class="noResult">Aucun résultat ne correspond à vos filtres</p> - <p class="filtersInfo">Merci de réinitialiser ou modifier les filtres afin d'élargir votre recherche</p> + </div> + <div class="resultsInfo"> + <ng-container *ngIf="userList.length === 0 && !isLoading"> + <p class="noResult">Aucun résultat ne correspond à vos filtres</p> + <p class="filtersInfo">Merci de réinitialiser ou modifier les filtres afin d'élargir votre recherche</p> + <app-button + tabindex="0" + [variant]="'primary'" + [wide]="true" + [label]="'Réinitialiser les filtres'" + [iconPosition]="'right'" + [iconName]="'refresh'" + (click)="resetFilters()" + /> + </ng-container> + <div *ngIf="userList.length" class="users"> + <app-member-card + *ngFor="let user of userList" + [member]="user" + [showAppointment]="true" + [showContactInfo]="false" + [showEmployer]="true" + /> + <div *ngIf="showPagination && !isLoading" class="pagination"> + <p>{{ userList.length }} membres affichés sur {{ totalUserResult }}</p> <app-button - tabindex="0" - [variant]="'primary'" + [label]="'Voir plus'" + [variant]="'primaryBlack'" + [size]="'medium'" [wide]="true" - [label]="'Réinitialiser les filtres'" - [iconPosition]="'right'" - [iconName]="'refresh'" - (click)="resetFilters()" + (action)="showMore()" /> - </ng-container> - </div> - </ng-container> - <div *ngIf="!isLogged" class="notConnected"> - <img src="../../assets/ico/annuaire-unlogged.svg" alt="Illustration annuaire" /> - <div class="count" [ngPlural]="totalUserResult"> - <ng-template ngPluralCase="0">Aucun membre n'est présent dans l'annuaire Rés'in</ng-template> - <ng-template ngPluralCase="1">1 membre est présent dans l'annuaire Rés'in</ng-template> - <ng-template ngPluralCase="other"> - {{ totalUserResult }} membres sont présents dans l'annuaire Rés'in - </ng-template> - </div> - <div class="access"> - Pour accéder à l’annuaire de Rés'in et contacter les membres,<br /> - veuillez vous connecter ou vous créer un compte. - </div> - <div class="buttons"> - <app-button [label]="'Créer un compte'" [variant]="'secondary'" (action)="goRegister()" /> - <app-button [label]="'Se connecter'" [variant]="'primary'" (action)="goLogin()" /> + </div> </div> </div> </ng-container> + <div *ngIf="isLoading" class="loader" aria-busy="true"> + <img class="loader-gif" src="/assets/gif/loader_circle.gif" alt /> + </div> </div> diff --git a/src/app/annuaire/result-list/result-list.component.ts b/src/app/annuaire/result-list/result-list.component.ts index 3a4fb2944f894056812f315bded4746fe04a3f2b..032a7672dd146bec945c5a7c96d162ee186ea249 100644 --- a/src/app/annuaire/result-list/result-list.component.ts +++ b/src/app/annuaire/result-list/result-list.component.ts @@ -23,11 +23,11 @@ export class ResultListComponent implements OnChanges, AfterViewInit { public showPagination = false; ngAfterViewInit(): void { - document.getElementById('resultList')?.scrollTo({ top: this.windowScrollService.scrollY.value }); - } - - public onScroll(event): void { - this.windowScrollService.scrollY.next(event.target.scrollTop); + // Delay required before all member-card components are displayed on the page + setTimeout( + () => document.getElementById('app-body')?.scrollTo({ top: this.windowScrollService.scrollYToPreserve.value }), + 10, + ); } ngOnChanges(changes: SimpleChanges): void { diff --git a/src/app/app.component.html b/src/app/app.component.html index 577c071fda09197a01df06f5bf1f56081bfa93e1..9c6e35adc207da6636b821a60bbdc541f1c0d7af 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,6 @@ <app-header /> <div class="app-container"> - <div class="app-body"> + <div class="app-body" id="app-body" (scroll)="onScroll($event)"> <div *ngIf="loading" class="loader" aria-busy="true"> <img class="loader-gif" src="/assets/gif/loader_circle.gif" alt /> </div> diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e20c5bb41d3e6ab23a4dc1631fa1a2e2341f5f20..a5e208793f3f1cf67f2a8014afe8c34e397b8657 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -14,6 +14,7 @@ import { AuthService } from './services/auth.service'; import { RouterListenerService } from './services/routerListener.service'; import { UpdateService } from './services/update.service'; import { PrintService } from './shared/service/print.service'; +import { WindowScrollService } from './shared/service/windowScroll.service'; @Component({ selector: 'app-root', @@ -32,6 +33,7 @@ export class AppComponent implements OnInit { private router: Router, private runtimeConfigLoaderService: RuntimeConfigLoaderService, private matomoInitializer: MatomoInitializerService, + private windowScrollService: WindowScrollService, ) { if (this.authService.isLoggedIn()) { this.profilService.getProfile(); @@ -86,4 +88,8 @@ export class AppComponent implements OnInit { }); this.runtimeConfigLoaderService.loadConfig().subscribe(); } + + public onScroll(event): void { + this.windowScrollService.scrollY.next(event.target.scrollTop); + } } diff --git a/src/app/shared/components/member-card/member-card.component.ts b/src/app/shared/components/member-card/member-card.component.ts index 4e8926204d41360d41d5e19ba1c6c8df18959dc2..07a0849ab91d6475ab4d11dbb144873eabd1ec31 100644 --- a/src/app/shared/components/member-card/member-card.component.ts +++ b/src/app/shared/components/member-card/member-card.component.ts @@ -3,6 +3,7 @@ import { Router } from '@angular/router'; import { get } from 'lodash'; import { Owner } from '../../../models/owner.model'; import { User, UserAnnuary } from '../../../models/user.model'; +import { WindowScrollService } from '../../service/windowScroll.service'; @Component({ selector: 'app-member-card', @@ -33,7 +34,10 @@ export class MemberCardComponent implements OnInit { @Output() public selectedCard = new EventEmitter<string>(); - constructor(private router: Router) {} + constructor( + private router: Router, + private windowScrollService: WindowScrollService, + ) {} ngOnInit(): void { if (this.showRadioButton && this.redirectToProfile) { @@ -62,6 +66,7 @@ export class MemberCardComponent implements OnInit { cardClicked(): void { if (this.redirectToProfile) { + this.windowScrollService.scrollYToPreserve.next(this.windowScrollService.scrollY.value); this.router.navigateByUrl(`/profile/${this.member._id}`); } else if (this.showRadioButton) { this.selectedCard.emit(this.member._id); diff --git a/src/app/shared/service/windowScroll.service.ts b/src/app/shared/service/windowScroll.service.ts index cdd0342c777988c9bf311e94a8e67238eb7b0ee5..59ec1ac14c67fc2af9ef5b083c4e038e4447234c 100644 --- a/src/app/shared/service/windowScroll.service.ts +++ b/src/app/shared/service/windowScroll.service.ts @@ -8,6 +8,8 @@ export class WindowScrollService { scrollY = new BehaviorSubject(null); scrollY$ = this.scrollY.asObservable(); + scrollYToPreserve = new BehaviorSubject(null); + public updateScrollY(value: Event): void { this.scrollY.next(value); }