Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
10 kB
1
Indexable
Never
/**
 * TODO this service is to be deprecated.
 * This will be replaced by app.service
 */

import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Auth } from 'aws-amplify';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { map, catchError, filter, switchMap, pluck } from 'rxjs/operators';
import { Router } from '@angular/router';
import { IUser, IUserCognito, ISellerListingWithUser } from '@models';
import { APP_CONFIG, IAppConfig } from '@config';
import { AirbrakeErrorHandler } from '@core';
import { ChatKittyService } from './chatKitty.service';
import { SellerListingService } from './seller-listing.service';
import { HelperService } from './helper.service';
import { IAppUser } from '@app/app.model';

@Injectable({ providedIn: 'root' })
export class UsersService {
  private readonly userSubject = new BehaviorSubject<IUser>(null);
  private readonly userFavSubject = new BehaviorSubject<ISellerListingWithUser[]>([]);
  private readonly cognitoDataSubject = new BehaviorSubject<any>({
    accessToken: '',
    idToken: '',
    exp: null,
  });

  private readonly baseUrl: string;
  private favIds: string[] = [];
  private favs: ISellerListingWithUser[] = [];

  constructor(
    private readonly http: HttpClient,
    private readonly chatKittyService: ChatKittyService,
    private readonly sellerListingService: SellerListingService,
    private readonly helperService: HelperService,
    private readonly router: Router,
    @Inject(APP_CONFIG) private readonly config: IAppConfig,
    private readonly errorHandler: AirbrakeErrorHandler
  ) {
    this.baseUrl = `${this.config.apiUrl}/manage/users`;
  }

  get cognitoData(): Observable<any> {
    return this.cognitoDataSubject.asObservable();
  }

  get user() {
    return this.userSubject.asObservable().pipe(filter(Boolean)) as Observable<IUser>;
  }

  get favorites() {
    return this.userFavSubject.asObservable();
  }

  get userStateWithNull() {
    return this.userSubject.asObservable();
  }

  me() {
    return this.http.get<IAppUser>(`${this.baseUrl}/me`);
  }

  // TODO is now done in the backend
  getVerificationDetails(user): IUser {
    if (!user) {
      return null;
    }
    const { userDetails, verification } = user;

    const isEmailVerified = user.verification.email;
    const isPhoneVerified = verification ? !!verification.phone : false;
    const isLicenseVerified = verification ? !!verification.vouched : false;
    const isBankConnected = !!(userDetails.agreementNumber || verification.bank);
    const isVerified = !!(isEmailVerified && isPhoneVerified && isLicenseVerified && isBankConnected);

    return {
      ...user,
      verification: {
        ...verification,
        isEmailVerified,
        isPhoneVerified,
        isLicenseVerified,
        isBankConnected,
        isVerified,
      },
    };
  }

  /**
   *  TODO below are not used yet
   *  backup purposes for now
   * */

  getUserDealContacts(id) {
    return this.http.get(`${this.baseUrl}/${id}/deals/contacts`);
  }

  editUsers(data) {
    return this.http.put(`${this.baseUrl}`, data);
  }

  // updateEmail(data) {
  //   return this.http.put(`${this.baseUrl}/email`, data);
  // }

  updateDriverLicense(data) {
    return this.http.put(`${this.baseUrl}/license`, data);
  }

  updateNotifications(data) {
    return this.http.put(`${this.baseUrl}/notifications`, data);
  }

  updateVerification(data) {
    return this.http.put(`${this.baseUrl}/verification`, data);
  }

  // updateProfile(data) {
  //   return this.http.put(`${this.baseUrl}/profile`, data);
  // }

  updatePhone(data) {
    return this.http.post<any>(`${this.baseUrl}/phone`, data);
  }

  verifyPhone(data) {
    return this.http.put<any>(`${this.baseUrl}/phone`, data);
  }

  forgotPassword(email: string) {
    return this.http.post<any>(`${this.baseUrl}/forgot-password`, { email });
  }

  resetPassword(params: { code: string; email: string; password: string }) {
    return this.http.put<any>(`${this.baseUrl}/reset-password`, params);
  }

  resendPhoneCode() {
    return this.http.get<any>(`${this.baseUrl}/phone`);
  }

  confirmEmail(data: { code: string }) {
    return this.http.post<any>(`${this.baseUrl}/confirm-email`, data);
  }

  sendVerification() {
    return this.http.post<any>(`${this.baseUrl}/email/send-verification`, {});
  }

  getCurrentProfile() {
    return from(Auth.currentSession()).pipe(
      switchMap(() => this.me()),
      switchMap((user) => this.getAwsProfile().pipe(map((cognito) => ({ ...user, cognito })))),
      switchMap((user) => this.configureUser(user)),
      map((user: IUser) => {
        if (user.verification?.bank !== user.verification.isBankConnected) {
          this.updateVerification({
            bank: user.verification.isBankConnected,
          }).subscribe();
        }

        this.userSubject.next(user);
        // TODO admin has no favs
        // this.getFavs()
        //   .pipe(
        //     pluck('data'),
        //     map((favs: { _id: string; favorites: IFavItem[] }) => {
        //       this.favIds = favs.favorites.map((fav) => fav.listingId?._id);
        //       this.favs = favs.favorites.map((fav) => fav.listingId);
        //       return this.userFavSubject.next(this.favs);
        //     })
        //   )
        //   .subscribe();
        return user;
      }),
      catchError((err) => {
        this.userSubject.next(null);
        this.cognitoDataSubject.next({ accessToken: '', idToken: '', exp: null });
        this.errorHandler.handleError(err);
        return of(null);
      })
    );
  }

  getAwsProfile() {
    return this.cognitoData.pipe(
      switchMap((cognitoData) => {
        const hostedUiData = this.getHostedUIData();
        if (hostedUiData.accessToken) {
          this.cognitoDataSubject.next(hostedUiData);
        }
        if (cognitoData?.accessToken) {
          return this.cognitoData;
        }
        return new Observable((observer) => {
          const abortController = new AbortController();
          const subscription = from(Auth.currentAuthenticatedUser({ bypassCache: true })).subscribe(observer);

          return () => {
            abortController.abort();
            subscription.unsubscribe();
          };
        });
      })
    ) as Observable<IUserCognito>;
  }

  signOut() {
    Auth.signOut({ global: true }).then(() => {
      localStorage.clear();
      this.helperService.deleteCookies();
      this.userSubject.next(null);
      this.chatKittyService.closeUserSession();
      this.sellerListingService.clearListingCache();
      this.router.navigate(['/auth/login']);
    });
  }

  getUserApplicationData() {
    this.user
      .pipe
      // TODO refine
      // tap((user: IUser) => {
      //   this.oneSignalService.setExternalUserId(user?._id);
      // }),
      // tap((user: IUser) => {
      //   this.chatKittyService.startUserSession(user);
      // }),
      // switchMap((user) => {
      //   if (user.userDetails.personNumber && user.userDetails.agreementNumber) {
      //     return this.usallianceService.getAccount();
      //   }
      //   return of(null);
      // })
      ()
      .subscribe();
  }

  processCognitoCallback(url) {
    const parameter = url.split('&');
    const idToken = parameter[0].split('=')[1];
    const accessToken = parameter[1].split('=')[1];
    const exp = parameter[2].split('=')[1];
    this.setHostedUIData({ accessToken, idToken, exp });
    this.cognitoDataSubject.next({ accessToken, idToken, exp: Date.now() + exp * 100 });
  }

  setHostedUIData({ accessToken, idToken, exp }) {
    sessionStorage.setItem('hostedUI_access_token', accessToken);
    sessionStorage.setItem('hostedUI_id_token', idToken);
    sessionStorage.setItem('hostedUI_exp', String(Date.now() + exp * 100));
  }

  getHostedUIData(): { idToken: string; accessToken: string; exp: number } {
    const data = {
      accessToken: sessionStorage.getItem('hostedUI_access_token') || '',
      idToken: sessionStorage.getItem('hostedUI_id_token') || '',
      exp: Number(sessionStorage.getItem('hostedUI_exp')) || null,
    };
    if (Number(data.exp) - Date.now() > 0) {
      return data;
    }
    sessionStorage.setItem('hostedUI_access_token', '');
    sessionStorage.setItem('hostedUI_id_token', '');
    sessionStorage.setItem('hostedUI_exp', null);
    return {
      accessToken: '',
      idToken: '',
      exp: null,
    };
  }

  // Set user's data
  configureUser(rawUser): Observable<IUser> {
    return of(rawUser).pipe(
      map((user) => this.getVerificationDetails(user)),
      map((user) => this.setShortName(user))
    );
  }

  setShortName(user: IUser): IUser {
    const { firstName, lastName, nickname } = user.userDetails;
    const shortName =
      (nickname || firstName) && lastName ? `${nickname || firstName} ${lastName.charAt(0)}.` : 'User name not set';
    const userDetails = { ...user.userDetails, shortName };
    return { ...user, userDetails };
  }

  public getFavs(): Observable<ISellerListingWithUser[]> {
    return this.http.get(`${this.baseUrl}/fav`) as Observable<ISellerListingWithUser[]>;
  }

  public processFav(listingId) {
    if (this.favIds.indexOf(listingId) >= 0) {
      this.removeFav(listingId);
    } else {
      this.addFav(listingId);
    }
  }

  public addFav(listingId) {
    this.http
      .post(`${this.baseUrl}/fav`, { listingId })
      .pipe(pluck('data'))
      .subscribe((res: any) => {
        this.favIds = res.favorites.map((fav) => fav.listingId?._id);
        this.favs = res.favorites.map((fav) => fav.listingId);
        this.userFavSubject.next(this.favs);
      });
  }

  public removeFav(listingId) {
    return this.http
      .delete(`${this.baseUrl}/fav`, { params: { listingId } })
      .pipe(pluck('data'))
      .subscribe((res: any) => {
        this.favIds = res.favorites.map((fav) => fav.listingId?._id);
        this.favs = res.favorites.map((fav) => fav.listingId);

        this.userFavSubject.next(this.favs);
      });
  }
}