### File

`src/app/_services/user.service.ts`

### Index

###### Properties
accounts Private accountsList accountsSubject actions Private actionsList actionsSubject categories Private categoriesList categoriesSubject headers history Private historyList historySubject keystore registry signer
###### Methods
addAccount approveAction Async changeAccountInfo Async getAccountByAddress Async getAccountByPhone getAccountDetailsFromMeta getAccountStatus getAccountTypes getActionById getActions getCategories getCategoryByProduct getGenders getLockedAccounts getTransactionTypes Async init Async loadAccounts Async loadChangesToAccountStructure resetAccountsList resetPin revokeAction Async updateMeta Async updatePhonePointers wrap

### Constructor

`constructor(httpClient: HttpClient, loggingService: LoggingService, tokenService: TokenService)`
Defined in src/app/_services/user.service.ts:44
Parameters :
Name | Type | Optional
httpClient | `HttpClient` | No
loggingService | `LoggingService` | No
tokenService | `TokenService` | No

### Methods

addAccount
`addAccount(account: AccountDetails, cacheSize: number, top: boolean)`
Defined in src/app/_services/user.service.ts:325
Parameters :
Name | Type | Optional | Default value
account | `AccountDetails` | No |
cacheSize | `number` | No |
top | `boolean` | No | `true`
Returns : `void`

approveAction
`approveAction(id: string)`
Defined in src/app/_services/user.service.ts:170
Parameters :
Name | Type | Optional
id | `string` | No
Returns : `Observable<any>`

Async changeAccountInfo
`changeAccountInfo(address: string, name: string, phoneNumber: string, age: string, type: string, bio: string, gender: string, businessCategory: string, userLocation: string, location: string, locationType: string, oldPhoneNumber: string)`
Defined in src/app/_services/user.service.ts:72
Parameters :
Name | Type | Optional
address | `string` | No
name | `string` | No
phoneNumber | `string` | No
age | `string` | No
type | `string` | No
bio | `string` | No
gender | `string` | No
businessCategory | `string` | No
userLocation | `string` | No
location | `string` | No
locationType | `string` | No
oldPhoneNumber | `string` | No
Returns : `Promise<boolean>`

Async getAccountByAddress
`getAccountByAddress(accountAddress: string, limit: number, history: boolean, top: boolean)`
Defined in src/app/_services/user.service.ts:234
Parameters :
Name | Type | Optional | Default value
accountAddress | `string` | No |
limit | `number` | No | `100`
history | `boolean` | No | `false`
top | `boolean` | No | `true`
Returns : `Promise<Observable<AccountDetails>>`

Async getAccountByPhone
`getAccountByPhone(phoneNumber: string, limit: number)`
Defined in src/app/_services/user.service.ts:270
Parameters :
Name | Type | Optional | Default value
phoneNumber | `string` | No |
limit | `number` | No | `100`
Returns : `Promise<Observable<AccountDetails>>`

getAccountDetailsFromMeta
`getAccountDetailsFromMeta(userKey: string)`
Defined in src/app/_services/user.service.ts:178
Parameters :
Name | Type | Optional
userKey | `string` | No
Returns : `Observable<any>`

getAccountStatus
`getAccountStatus(phone: string)`
Defined in src/app/_services/user.service.ts:63
Parameters :
Name | Type | Optional
phone | `string` | No
Returns : `Observable<any>`

getAccountTypes
`getAccountTypes()`
Defined in src/app/_services/user.service.ts:313
Returns : `Observable<any>`

getActionById
`getActionById(id: string)`
Defined in src/app/_services/user.service.ts:166
Parameters :
Name | Type | Optional
id | `string` | No
Returns : `Observable<any>`

getActions
`getActions()`
Defined in src/app/_services/user.service.ts:159
Returns : `void`

getCategories
`getCategories()`
Defined in src/app/_services/user.service.ts:293
Returns : `void`

getCategoryByProduct
`getCategoryByProduct(product: string, categories: object)`
Defined in src/app/_services/user.service.ts:300
Parameters :
Name | Type | Optional
product | `string` | No
categories | `object` | No
Returns : `string`

getGenders
`getGenders()`
Defined in src/app/_services/user.service.ts:321
Returns : `Observable<any>`

getLockedAccounts
`getLockedAccounts(offset: number, limit: number)`
Defined in src/app/_services/user.service.ts:68
Parameters :
Name | Type | Optional
offset | `number` | No
limit | `number` | No
Returns : `Observable<any>`

getTransactionTypes
`getTransactionTypes()`
Defined in src/app/_services/user.service.ts:317
Returns : `Observable<any>`

Async init
`init()`
Defined in src/app/_services/user.service.ts:52
Returns : `Promise<void>`

Async loadAccounts
`loadAccounts(limit: number, offset: number)`
Defined in src/app/_services/user.service.ts:196
Parameters :
Name | Type | Optional | Default value
limit | `number` | No | `100`
offset | `number` | No | `0`
Returns : `Promise<void>`

Async loadChangesToAccountStructure
`loadChangesToAccountStructure(name: string, phoneNumber: string, age: string, type: string, bio: string, gender: string, businessCategory: string, userLocation: string, location: string, locationType: string)`
Defined in src/app/_services/user.service.ts:357
Parameters :
Name | Type | Optional
name | `string` | No
phoneNumber | `string` | No
age | `string` | No
type | `string` | No
bio | `string` | No
gender | `string` | No
businessCategory | `string` | No
userLocation | `string` | No
location | `string` | No
locationType | `string` | No
Returns : `Promise<AccountDetails>`

resetAccountsList
`resetAccountsList()`
Defined in src/app/_services/user.service.ts:288
Returns : `void`

resetPin
`resetPin(phone: string)`
Defined in src/app/_services/user.service.ts:58
Parameters :
Name | Type | Optional
phone | `string` | No
Returns : `Observable<any>`

revokeAction
`revokeAction(id: string)`
Defined in src/app/_services/user.service.ts:174
Parameters :
Name | Type | Optional
id | `string` | No
Returns : `Observable<any>`

Async updateMeta
`updateMeta(syncableAccount: Syncable, accountKey: string, headers: HttpHeaders)`
Defined in src/app/_services/user.service.ts:135
Parameters :
Name | Type | Optional
syncableAccount | `Syncable` | No
accountKey | `string` | No
headers | `HttpHeaders` | No
Returns : `Promise<boolean>`

Async updatePhonePointers
`updatePhonePointers(address: string, oldPhoneNumber: string, newPhoneNumber: string)`
Defined in src/app/_services/user.service.ts:413
Parameters :
Name | Type | Optional
address | `string` | No
oldPhoneNumber | `string` | No
newPhoneNumber | `string` | No
Returns : `Promise<void>`

wrap
`wrap(syncable: Syncable, signer: Signer)`
Defined in src/app/_services/user.service.ts:182
Parameters :
Name | Type | Optional
syncable | `Syncable` | No
signer | `Signer` | No
Returns : `Promise<Envelope>`

### Properties

accounts
Type : `Array<AccountDetails>`
Default value : `[]`
Defined in src/app/_services/user.service.ts:28

Private accountsList
Type : `BehaviorSubject<Array<AccountDetails>>`
Default value : `new BehaviorSubject< Array<AccountDetails> >(this.accounts)`
Defined in src/app/_services/user.service.ts:29

accountsSubject
Type : `Observable<Array<AccountDetails>>`
Default value : `this.accountsList.asObservable()`
Defined in src/app/_services/user.service.ts:32

actions
Type : `Array<any>`
Default value : `[]`
Defined in src/app/_services/user.service.ts:34

Private actionsList
Type : `BehaviorSubject<any>`
Default value : `new BehaviorSubject<any>(this.actions)`
Defined in src/app/_services/user.service.ts:35

actionsSubject
Type : `Observable<Array<any>>`
Default value : `this.actionsList.asObservable()`
Defined in src/app/_services/user.service.ts:36

categories
Type : `object`
Default value : `{}`
Defined in src/app/_services/user.service.ts:38

Private categoriesList
Type : `BehaviorSubject<object>`
Default value : `new BehaviorSubject<object>(this.categories)`
Defined in src/app/_services/user.service.ts:39

categoriesSubject
Type : `Observable<object>`
Default value : `this.categoriesList.asObservable()`
Defined in src/app/_services/user.service.ts:40

headers
Type : `HttpHeaders`
Default value : `new HttpHeaders({ 'x-cic-automerge': 'client' })`
Defined in src/app/_services/user.service.ts:23

history
Type : `Array<any>`
Default value : `[]`
Defined in src/app/_services/user.service.ts:42

Private historyList
Type : `BehaviorSubject<any>`
Default value : `new BehaviorSubject<any>(this.history)`
Defined in src/app/_services/user.service.ts:43

historySubject
Type : `Observable<Array<any>>`
Default value : `this.historyList.asObservable()`
Defined in src/app/_services/user.service.ts:44

keystore
Type : `MutableKeyStore`
Defined in src/app/_services/user.service.ts:24

registry
Type : `CICRegistry`
Defined in src/app/_services/user.service.ts:26

signer
Type : `Signer`
Defined in src/app/_services/user.service.ts:25

    import { Injectable } from '@angular/core';
    import { BehaviorSubject, Observable, Subject } from 'rxjs';
    import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
    import { environment } from '@src/environments/environment';
    import { first } from 'rxjs/operators';
    import { ArgPair, Envelope, Phone, Syncable, User } from 'cic-client-meta';
    import { AccountDetails } from '@app/_models';
    import { LoggingService } from '@app/_services/logging.service';
    import { TokenService } from '@app/_services/token.service';
    import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp';
    import { RegistryService } from '@app/_services/registry.service';
    import { CICRegistry } from '@cicnet/cic-client';
    import { personValidation, updateSyncable, vcardValidation } from '@app/_helpers';
    import { add0x, strip0x } from '@src/assets/js/ethtx/dist/hex';
    import { KeystoreService } from '@app/_services/keystore.service';
    import * as Automerge from 'automerge';
    const vCard = require('vcard-parser');

    @Injectable({
      providedIn: 'root',
    })
    export class UserService {
      headers: HttpHeaders = new HttpHeaders({ 'x-cic-automerge': 'client' });
      keystore: MutableKeyStore;
      signer: Signer;
      registry: CICRegistry;

      accounts: Array<AccountDetails> = [];
      private accountsList: BehaviorSubject<Array<AccountDetails>> = new BehaviorSubject<
        Array<AccountDetails>
      >(this.accounts);
      accountsSubject: Observable<Array<AccountDetails>> = this.accountsList.asObservable();

      actions: Array<any> = [];
      private actionsList: BehaviorSubject<any> = new BehaviorSubject<any>(this.actions);
      actionsSubject: Observable<Array<any>> = this.actionsList.asObservable();

      categories: object = {};
      private categoriesList: BehaviorSubject<object> = new BehaviorSubject<object>(this.categories);
      categoriesSubject: Observable<object> = this.categoriesList.asObservable();

      history: Array<any> = [];
      private historyList: BehaviorSubject<any> = new BehaviorSubject<any>(this.history);
      historySubject: Observable<Array<any>> = this.historyList.asObservable();

      constructor(
        private httpClient: HttpClient,
        private loggingService: LoggingService,
        private tokenService: TokenService
      ) {}

      async init(): Promise<void> {
        this.keystore = await KeystoreService.getKeystore();
        this.signer = new PGPSigner(this.keystore);
        this.registry = await RegistryService.getRegistry();
      }

      resetPin(phone: string): Observable<any> {
        const params: HttpParams = new HttpParams().set('phoneNumber', phone);
        return this.httpClient.put(`${environment.cicUssdUrl}/pin`, { params });
      }

      getAccountStatus(phone: string): Observable<any> {
        const params: HttpParams = new HttpParams().set('phoneNumber', phone);
        return this.httpClient.get(`${environment.cicUssdUrl}/pin`, { params });
      }

      getLockedAccounts(offset: number, limit: number): Observable<any> {
        return this.httpClient.get(`${environment.cicUssdUrl}/accounts/locked/${offset}/${limit}`);
      }

      async changeAccountInfo(
        address: string,
        name: string,
        phoneNumber: string,
        age: string,
        type: string,
        bio: string,
        gender: string,
        businessCategory: string,
        userLocation: string,
        location: string,
        locationType: string,
        oldPhoneNumber: string
      ): Promise<boolean> {
        const accountInfo = await this.loadChangesToAccountStructure(
          name,
          phoneNumber,
          age,
          type,
          bio,
          gender,
          businessCategory,
          userLocation,
          location,
          locationType
        );
        const accountKey: string = await User.toKey(address);
        return new Promise((resolve) => {
          let status: boolean = false;
          this.getAccountDetailsFromMeta(accountKey)
            .pipe(first())
            .subscribe(
              async (res) => {
                const syncableAccount: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
                const update: Array<ArgPair> = [];
                for (const prop of Object.keys(accountInfo)) {
                  update.push(new ArgPair(prop, accountInfo[prop]));
                }
                updateSyncable(update, 'client-branch', syncableAccount);
                await personValidation(syncableAccount.m.data);
                status = await this.updateMeta(syncableAccount, accountKey, this.headers);
                if (status && phoneNumber !== oldPhoneNumber) {
                  await this.updatePhonePointers(address, oldPhoneNumber, phoneNumber);
                }
                resolve(status);
              },
              async (error) => {
                this.loggingService.sendErrorLevelMessage(
                  'Cannot find account info in meta service',
                  this,
                  { error }
                );
                const syncableAccount: Syncable = new Syncable(accountKey, accountInfo);
                status = await this.updateMeta(syncableAccount, accountKey, this.headers);
                if (status && phoneNumber !== oldPhoneNumber) {
                  await this.updatePhonePointers(address, oldPhoneNumber, phoneNumber);
                }
                resolve(status);
              }
            );
        });
      }

      async updateMeta(
        syncableAccount: Syncable,
        accountKey: string,
        headers: HttpHeaders
      ): Promise<boolean> {
        const envelope: Envelope = await this.wrap(syncableAccount, this.signer);
        const reqBody: string = envelope.toJSON();
        return new Promise((resolve) => {
          this.httpClient
            .put(`${environment.cicMetaUrl}/${accountKey}`, reqBody, { headers })
            .pipe(first())
            .subscribe(
              (res) => {
                this.loggingService.sendInfoLevelMessage(`Response: ${res}`);
                resolve(true);
              },
              (error) => {
                this.loggingService.sendErrorLevelMessage('Metadata update failed!', this, { error });
                resolve(false);
              }
            );
        });
      }

      getActions(): void {
        this.httpClient
          .get(`${environment.cicCacheUrl}/actions`)
          .pipe(first())
          .subscribe((res) => this.actionsList.next(res));
      }

      getActionById(id: string): Observable<any> {
        return this.httpClient.get(`${environment.cicCacheUrl}/actions/${id}`);
      }

      approveAction(id: string): Observable<any> {
        return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: true });
      }

      revokeAction(id: string): Observable<any> {
        return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: false });
      }

      getAccountDetailsFromMeta(userKey: string): Observable<any> {
        return this.httpClient.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers });
      }

      wrap(syncable: Syncable, signer: Signer): Promise<Envelope> {
        return new Promise<Envelope>(async (resolve, reject) => {
          syncable.setSigner(signer);
          syncable.onwrap = async (env) => {
            if (env === undefined) {
              reject();
              return;
            }
            resolve(env);
          };
          await syncable.sign();
        });
      }

      async loadAccounts(limit: number = 100, offset: number = 0): Promise<void> {
        try {
          const accountRegistry = await RegistryService.getAccountRegistry();
          const accountAddresses: Array<string> = await accountRegistry.last(offset + limit);
          this.loggingService.sendInfoLevelMessage(accountAddresses);
          if (typeof Worker !== 'undefined') {
            const worker = new Worker('@app/_workers/fetch-accounts.worker', { type: 'module' });
            worker.onmessage = ({ data }) => {
              if (data) {
                this.tokenService.load.subscribe(async (status: boolean) => {
                  if (status) {
                    data.balance = await this.tokenService.getTokenBalance(
                      data.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
                    );
                  }
                });
                this.addAccount(data, limit, false);
              }
            };
            worker.postMessage({
              addresses: accountAddresses.slice(offset, offset + limit),
              url: environment.cicMetaUrl,
              token: sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN')),
            });
          } else {
            this.loggingService.sendInfoLevelMessage(
              'Web workers are not supported in this environment'
            );
            for (const accountAddress of accountAddresses.slice(offset, offset + limit)) {
              await this.getAccountByAddress(accountAddress, limit, false, false);
            }
          }
        } catch (error) {
          this.loggingService.sendErrorLevelMessage('Unable to load accounts.', 'user.service', error);
          throw error;
        }
      }

      async getAccountByAddress(
        accountAddress: string,
        limit: number = 100,
        history: boolean = false,
        top: boolean = true
      ): Promise<Observable<AccountDetails>> {
        const accountSubject: Subject<any> = new Subject<any>();
        this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress)))
          .pipe(first())
          .subscribe(async (res) => {
            const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
            if (history) {
              try {
                // @ts-ignore
                this.historyList.next(Automerge.getHistory(account.m).reverse());
              } catch (error) {
                this.loggingService.sendErrorLevelMessage('No history found', this, { error });
              }
            }
            const accountInfo = account.m.data;
            await personValidation(accountInfo);
            this.tokenService.load.subscribe(async (status: boolean) => {
              if (status) {
                accountInfo.balance = await this.tokenService.getTokenBalance(
                  accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
                );
              }
            });
            accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
            await vcardValidation(accountInfo.vcard);
            this.addAccount(accountInfo, limit, top);
            accountSubject.next(accountInfo);
          });
        return accountSubject.asObservable();
      }

      async getAccountByPhone(
        phoneNumber: string,
        limit: number = 100
      ): Promise<Observable<AccountDetails>> {
        const accountSubject: Subject<any> = new Subject<any>();
        this.getAccountDetailsFromMeta(await Phone.toKey(phoneNumber))
          .pipe(first())
          .subscribe(async (res) => {
            const response: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
            const address: string = response.m.data;
            const account: Observable<AccountDetails> = await this.getAccountByAddress(address, limit);
            account.subscribe((result) => {
              accountSubject.next(result);
            });
          });
        return accountSubject.asObservable();
      }

      resetAccountsList(): void {
        this.accounts = [];
        this.accountsList.next(this.accounts);
      }

      getCategories(): void {
        this.httpClient
          .get(`${environment.cicMetaUrl}/categories`)
          .pipe(first())
          .subscribe((res: object) => this.categoriesList.next(res));
      }

      getCategoryByProduct(product: string, categories: object): string {
        const keywords = product.toLowerCase().split(' ');
        for (const keyword of keywords) {
          const queriedCategory: string = Object.keys(categories).find((key) =>
            categories[key].includes(keyword)
          );
          if (queriedCategory) {
            return queriedCategory;
          }
        }
        return 'other';
      }

      getAccountTypes(): Observable<any> {
        return this.httpClient.get(`${environment.cicMetaUrl}/accounttypes`);
      }

      getTransactionTypes(): Observable<any> {
        return this.httpClient.get(`${environment.cicMetaUrl}/transactiontypes`);
      }

      getGenders(): Observable<any> {
        return this.httpClient.get(`${environment.cicMetaUrl}/genders`);
      }

      addAccount(account: AccountDetails, cacheSize: number, top: boolean = true): void {
        const savedIndex = this.accounts.findIndex(
          (acc) =>
            acc.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0] ===
            account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
        );
        if (top) {
          if (savedIndex === 0) {
            return;
          }
          if (savedIndex > 0) {
            this.accounts.splice(savedIndex, 1);
          }
          this.accounts.unshift(account);
          if (this.accounts.length > cacheSize) {
            this.accounts.length = Math.min(this.accounts.length, cacheSize);
          }
        } else {
          if (
            this.accounts.length >= cacheSize ||
            (savedIndex !== -1 && savedIndex === this.accounts.length - 1)
          ) {
            return;
          }
          if (savedIndex !== -1 && savedIndex < this.accounts.length - 1) {
            this.accounts.splice(savedIndex, 1);
          }
          this.accounts.push(account);
        }
        this.accountsList.next(this.accounts);
      }

      async loadChangesToAccountStructure(
        name: string,
        phoneNumber: string,
        age: string,
        type: string,
        bio: string,
        gender: string,
        businessCategory: string,
        userLocation: string,
        location: string,
        locationType: string
      ): Promise<AccountDetails> {
        const accountInfo: any = {
          vcard: {
            fn: [{}],
            n: [{}],
            tel: [{}],
          },
          location: {},
        };
        if (name) {
          accountInfo.vcard.fn[0].value = name;
          accountInfo.vcard.n[0].value = name.split(' ');
        }
        if (phoneNumber) {
          accountInfo.vcard.tel[0].value = phoneNumber;
        }
        if (bio) {
          accountInfo.products = [bio];
        }
        if (gender) {
          accountInfo.gender = gender;
        }
        if (age) {
          accountInfo.age = age;
        }
        if (type) {
          accountInfo.type = type;
        }
        if (businessCategory) {
          accountInfo.category = businessCategory;
        }
        if (location) {
          accountInfo.location.area = location;
        }
        if (userLocation) {
          accountInfo.location.area_name = userLocation;
        }
        if (locationType) {
          accountInfo.location.area_type = locationType;
        }
        await vcardValidation(accountInfo.vcard);
        accountInfo.vcard = btoa(vCard.generate(accountInfo.vcard));
        return accountInfo;
      }

      async updatePhonePointers(
        address: string,
        oldPhoneNumber: string,
        newPhoneNumber: string
      ): Promise<void> {
        const oldPhoneKey: string = await Phone.toKey(oldPhoneNumber);
        const newPhoneKey: string = await Phone.toKey(newPhoneNumber);
        const newPhoneData: Syncable = new Syncable(newPhoneKey, strip0x(address));
        const newPhoneSetStatus = await this.updateMeta(newPhoneData, newPhoneKey, this.headers);
        if (!newPhoneSetStatus) {
          const error = `Failed to update new phone number pointer: ${newPhoneNumber}`;
          this.loggingService.sendErrorLevelMessage(error, this, { error });
        } else {
          const oldPhoneData: Syncable = new Syncable(oldPhoneKey, '');
          const oldPhoneSetStatus = await this.updateMeta(oldPhoneData, oldPhoneKey, this.headers);
          if (!oldPhoneSetStatus) {
            const error = `Failed to update old phone number pointer: ${oldPhoneNumber}`;
            this.loggingService.sendErrorLevelMessage(error, this, { error });
          }
        }
      }
    }

# result-matching ""

# No results matching ""