Advertisement
attilan

NgRx effect

Oct 15th, 2020
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { Injectable } from '@angular/core';
  2. import { Actions, createEffect, ofType } from '@ngrx/effects';
  3. import {
  4.   catchError,
  5.   concatMap,
  6.   map,
  7.   switchMap,
  8.   tap,
  9.   toArray,
  10.   withLatestFrom,
  11. } from 'rxjs/operators';
  12. import { Action, select, Store } from '@ngrx/store';
  13. import { UsersService } from '../../services/users.service';
  14. import { concat, forkJoin, merge, Observable, of } from 'rxjs';
  15. import { HttpErrorResponse } from '@angular/common/http';
  16. import { UserSkillState } from '../user-skill.state';
  17. import {
  18.   loadUserSkills,
  19.   setUserSkillSaveLoading,
  20.   setUserSkillsLoadError,
  21.   updateUserSkillById,
  22.   userSkillActionTypes,
  23. } from '../actions/user-skill.action';
  24. import { selectUserSkills } from '../selectors/user-skill.selector';
  25. import { IUserSkill } from '../../classes/IUserSkill';
  26. import { SkillService } from '../../../skills/services/skill.service';
  27. import { Skill } from '../../../skills/classes/Skill';
  28. import { loadSkills } from '../../../skills/ngrx/actions/skill.action';
  29. import { selectProfile } from '../../../../ngrx/selectors/app.selector';
  30. import { IProfile } from '../../../profile/classes/IProfile';
  31. import { MainConstants } from '../../../../constant';
  32. import { showGenericError, showToast } from '../../../../ngrx/actions/app.action';
  33. import { ToastType } from '../../../../classes/ToastType';
  34.  
  35. interface RequestUserSkillsAction extends Action {
  36.   userId: number;
  37. }
  38.  
  39. interface RequestSaveUserSkillManyAction extends Action {
  40.   deletedSkillIds: number[];
  41.   userId: number;
  42. }
  43.  
  44. @Injectable()
  45. export class UserSkillEffect {
  46.   constructor(
  47.     private actions$: Actions,
  48.     private usersService: UsersService,
  49.     private skillService: SkillService,
  50.     private store: Store<UserSkillState>
  51.   ) {}
  52.  
  53.   requestUserSkills$ = createEffect(() =>
  54.     this.actions$.pipe(
  55.       ofType(userSkillActionTypes.requestUserSkills),
  56.       withLatestFrom(this.store.pipe(select(selectProfile))),
  57.       switchMap(([{ userId }, { id: profileId }]: [RequestUserSkillsAction, IProfile]) => {
  58.         userId = userId || profileId;
  59.         return this.usersService.getUserSkills(userId);
  60.       }),
  61.       map((userSkills: IUserSkill[]) =>
  62.         loadUserSkills({
  63.           userSkills,
  64.         })
  65.       ),
  66.       catchError((err: HttpErrorResponse) => of(setUserSkillsLoadError({ error: err.message })))
  67.     )
  68.   );
  69.  
  70.   addUserSkillMany$ = createEffect(() =>
  71.     this.actions$
  72.       .pipe(
  73.         ofType(userSkillActionTypes.requestSaveUserSkillMany),
  74.         withLatestFrom(this.store.pipe(select(selectUserSkills))),
  75.         concatMap((action: [RequestSaveUserSkillManyAction, IUserSkill[]]) => {
  76.           this.store.dispatch(setUserSkillSaveLoading({ isSaveLoading: true }));
  77.           const deletedSkillIds = action[0].deletedSkillIds;
  78.           const userId = action[0].userId;
  79.           if (Array.isArray(deletedSkillIds) && deletedSkillIds.length > 0) {
  80.             return this.deleteUserSkills(deletedSkillIds, userId).pipe(map(() => action));
  81.           }
  82.           return of(null).pipe(map(() => action));
  83.         }),
  84.         concatMap((action: [RequestSaveUserSkillManyAction, IUserSkill[]]) =>
  85.           this.handleNewSkillSave(action[1], action[0].userId)
  86.         ),
  87.         concatMap(
  88.           ({ oldIds, newIds, userId }: { oldIds: number[]; newIds: number[]; userId: number }) => {
  89.             oldIds.forEach((oldId: number, index: number) =>
  90.               this.store.dispatch(
  91.                 updateUserSkillById({ userSkill: { id: oldId, changes: { id: newIds[index] } } })
  92.               )
  93.             );
  94.             return of(userId);
  95.           }
  96.         ),
  97.         withLatestFrom(this.store.pipe(select(selectUserSkills))),
  98.         concatMap(([userId, userSkills]: [number, IUserSkill[]]) =>
  99.           this.postUserSkills(userSkills, userId).pipe(map(() => userId))
  100.         ),
  101.         concatMap((userId: number) => {
  102.           const userSkills$ = this.usersService.getUserSkills(userId);
  103.           const skills$ = this.skillService.getAllSkills();
  104.           return forkJoin(userSkills$, skills$);
  105.         }),
  106.         switchMap(([userSkills, skills]: [IUserSkill[], Skill[]]) => {
  107.           return merge([
  108.             loadSkills({ skills }),
  109.             loadUserSkills({ userSkills }),
  110.             setUserSkillSaveLoading({ isSaveLoading: false }),
  111.             showToast({
  112.               translationKey: 'COMMON.SUCCESS',
  113.               toastType: ToastType.Success,
  114.               params: null,
  115.             }),
  116.           ]);
  117.         })
  118.       )
  119.       .pipe(
  120.         catchError(() =>
  121.           merge([showGenericError(), setUserSkillSaveLoading({ isSaveLoading: false })])
  122.         )
  123.       )
  124.   );
  125.  
  126.   private deleteUserSkills(skillIds: number[], userId: number): Observable<unknown> {
  127.     return this.usersService.deleteUserSkills({
  128.       userId: userId,
  129.       skillIds,
  130.     });
  131.   }
  132.  
  133.   private postUserSkills(skills: IUserSkill[], userId: number): Observable<IUserSkill[]> {
  134.     return this.usersService.postUserSkills({
  135.       userId: userId,
  136.       skills: skills.map((skill) => ({
  137.         skillId: skill.id,
  138.         level: skill.level || MainConstants.NEW_SKILL_TO_USER_DEF_LEVEL,
  139.       })),
  140.     });
  141.   }
  142.  
  143.   private handleNewSkillSave(
  144.     userSkills: IUserSkill[],
  145.     userId: number
  146.   ): Observable<{ oldIds: number[]; newIds: number[]; userId: number }> {
  147.     const newSkills = userSkills.filter((userSkill: IUserSkill) => userSkill.id <= 0);
  148.     const postNewSkills$: Observable<Skill>[] = newSkills.map((newSkill) =>
  149.       this.skillService.createSkill({
  150.         name: newSkill.name,
  151.         description: newSkill.description,
  152.         isApproved: 0,
  153.       })
  154.     );
  155.     let index = 0;
  156.     const oldIds = [];
  157.     const newIds = [];
  158.     return concat(...postNewSkills$).pipe(
  159.       tap((skill: Skill) => {
  160.         oldIds.push(newSkills[index].id);
  161.         newIds.push(skill.id);
  162.         index++;
  163.       }),
  164.       toArray(),
  165.       map(() => ({ oldIds, newIds, userId })) // return new skills with created ids
  166.     );
  167.   }
  168. }
  169.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement