import { Injectable, OnDestroy } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { of, Subject } from 'rxjs'; import { map, mergeMap, catchError, withLatestFrom, takeUntil } from 'rxjs/operators'; import { Location } from '@angular/common'; import { MatDialog } from '@angular/material/dialog'; import { environment, API_URL } from '../../../environments/environment'; import { LoggerService } from '../../shared/services/logger.service'; import { CommonService } from '../../shared/services/common.service'; import { SessionService } from '../../shared/services/session.service'; import { GetInfo, GetInfoChain, Fees, Balance, NetworkInfo, Payment, GraphNode, Transaction, SwitchReq, ListInvoices, PendingChannelsGroup, UTXO } from '../../shared/models/lndModels'; import { InvoiceInformationComponent } from '../transactions/invoice-information-modal/invoice-information.component'; import { ErrorMessageComponent } from '../../shared/components/data-modal/error-message/error-message.component'; import { CurrencyUnitEnum, FEE_LIMIT_TYPES, PAGE_SIZE } from '../../shared/services/consts-enums-functions'; import * as RTLActions from '../../store/rtl.actions'; import * as fromRTLReducer from '../../store/rtl.reducers'; import * as LNDActions from './lnd.actions'; import * as fromLNDReducers from '../store/lnd.reducers'; @Injectable() export class LNDEffects implements OnDestroy { dialogRef: any; CHILD_API_URL = API_URL + '/lnd'; private unSubs: Array> = [new Subject(), new Subject()]; constructor( private actions$: Actions, private httpClient: HttpClient, private store: Store, private logger: LoggerService, private commonService: CommonService, private sessionService: SessionService, public dialog: MatDialog, private router: Router, private location: Location) { this.store.select('lnd') .pipe(takeUntil(this.unSubs[0])) .subscribe((rtlStore) => { if(rtlStore.initialAPIResponseStatus[0] === 'INCOMPLETE' && rtlStore.initialAPIResponseStatus.length > 8) { // Num of Initial APIs + 1 rtlStore.initialAPIResponseStatus[0] = 'COMPLETE'; this.store.dispatch(new RTLActions.CloseSpinner()); } }); } @Effect() infoFetch = this.actions$.pipe( ofType(LNDActions.FETCH_INFO_LND), withLatestFrom(this.store.select('root')), mergeMap(([action, store]: [LNDActions.FetchInfo, fromRTLReducer.RootState]) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchInfo')); return this.httpClient.get(this.CHILD_API_URL + environment.GETINFO_API) .pipe( takeUntil(this.actions$.pipe(ofType(RTLActions.SET_SELECTED_NODE))), map((info) => { this.logger.info(info); if (!info.identity_pubkey) { this.sessionService.removeItem('lndUnlocked'); this.logger.info('Redirecting to Unlock'); this.router.navigate(['/lnd/wallet']); return { type: LNDActions.SET_INFO_LND, payload: {} }; } else { info.lnImplementation = 'LND'; this.initializeRemainingData(info, action.payload.loadPage); return { type: LNDActions.SET_INFO_LND, payload: info ? info : {} }; } }), catchError((err) => { if ((typeof err.error.error === 'string' && err.error.error.includes('Not Found')) || err.status === 502) { this.sessionService.removeItem('lndUnlocked'); this.logger.info('Redirecting to Unlock'); this.router.navigate(['/lnd/wallet']); this.handleErrorWithoutAlert('FetchInfo', 'Fetching Node Info Failed.', err); } else { const code = (err.error && err.error.error && err.error.error.message && err.error.error.message.code) ? err.error.error.message.code : (err.error && err.error.error && err.error.error.code) ? err.error.error.code : err.status ? err.status : ''; const message = (err.error.message ? err.error.message + ' ' : '') + ((err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : (err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.error : (err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : (err.error.error && typeof err.error.error === 'string') ? err.error.error : typeof err.error === 'string' ? err.error : 'Unknown Error'); this.router.navigate(['/error'], { state: { errorCode: code, errorMessage: message }}); this.handleErrorWithoutAlert('FetchInfo', 'Fetching Node Info Failed.', err); } return of({type: RTLActions.VOID}); }) ); } )); @Effect() peersFetch = this.actions$.pipe( ofType(LNDActions.FETCH_PEERS_LND), mergeMap((action: LNDActions.FetchPeers) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchPeers')); return this.httpClient.get(this.CHILD_API_URL + environment.PEERS_API) .pipe( map((peers: any) => { this.logger.info(peers); return { type: LNDActions.SET_PEERS_LND, payload: peers ? peers : [] }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchPeers', 'Fetching Peers Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() saveNewPeer = this.actions$.pipe( ofType(LNDActions.SAVE_NEW_PEER_LND), withLatestFrom(this.store.select('lnd')), mergeMap(([action, lndData]: [LNDActions.SaveNewPeer, fromLNDReducers.LNDState]) => { this.store.dispatch(new LNDActions.ClearEffectError('SaveNewPeer')); return this.httpClient.post(this.CHILD_API_URL + environment.PEERS_API, { pubkey: action.payload.pubkey, host: action.payload.host, perm: action.payload.perm }) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new LNDActions.SetPeers((postRes && postRes.length > 0) ? postRes : [])); return { type: LNDActions.NEWLY_ADDED_PEER_LND, payload: {peer: postRes[0]} }; }), catchError((err: any) => { this.handleErrorWithoutAlert('SaveNewPeer', 'Peer Connection Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() detachPeer = this.actions$.pipe( ofType(LNDActions.DETACH_PEER_LND), mergeMap((action: LNDActions.DetachPeer) => { return this.httpClient.delete(this.CHILD_API_URL + environment.PEERS_API + '/' + action.payload.pubkey) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.OpenSnackBar('Peer Disconnected Successfully.')); return { type: LNDActions.REMOVE_PEER_LND, payload: { pubkey: action.payload.pubkey } }; }), catchError((err: any) => { this.handleErrorWithAlert('ERROR', 'Unable to Detach Peer. Try again later.', this.CHILD_API_URL + environment.PEERS_API + '/' + action.payload.pubkey, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() saveNewInvoice = this.actions$.pipe( ofType(LNDActions.SAVE_NEW_INVOICE_LND), mergeMap((action: LNDActions.SaveNewInvoice) => { this.store.dispatch(new LNDActions.ClearEffectError('SaveNewInvoice')); return this.httpClient.post(this.CHILD_API_URL + environment.INVOICES_API, { memo: action.payload.memo, amount: action.payload.invoiceValue, private: action.payload.private, expiry: action.payload.expiry }) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new LNDActions.FetchInvoices({ num_max_invoices: action.payload.pageSize, reversed: true })); if (action.payload.openModal) { postRes.memo = action.payload.memo; postRes.value = action.payload.invoiceValue; postRes.expiry = action.payload.expiry; postRes.cltv_expiry = '144'; postRes.private = action.payload.private; postRes.creation_date = Math.round(new Date().getTime() / 1000).toString(); postRes.creation_date_str = this.commonService.convertTimestampToDate(+postRes.creation_date); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: RTLActions.OPEN_ALERT, payload: { data: { invoice: postRes, newlyAdded: true, component: InvoiceInformationComponent }} } } else { return { type: LNDActions.NEWLY_SAVED_INVOICE_LND, payload: { paymentRequest: postRes.payment_request } } } }), catchError((err: any) => { this.handleErrorWithoutAlert('SaveNewInvoice', 'Add Invoice Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() openNewChannel = this.actions$.pipe( ofType(LNDActions.SAVE_NEW_CHANNEL_LND), mergeMap((action: LNDActions.SaveNewChannel) => { this.store.dispatch(new LNDActions.ClearEffectError('SaveNewChannel')); return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API, { node_pubkey: action.payload.selectedPeerPubkey, local_funding_amount: action.payload.fundingAmount, private: action.payload.private, trans_type: action.payload.transType, trans_type_value: action.payload.transTypeValue, spend_unconfirmed: action.payload.spendUnconfirmed }) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new LNDActions.FetchBalance('blockchain')); this.store.dispatch(new LNDActions.FetchAllChannels()); this.store.dispatch(new LNDActions.BackupChannels({ channelPoint: 'ALL', showMessage: 'Channel Added Successfully!' })); return { type: LNDActions.FETCH_PENDING_CHANNELS_LND }; }), catchError((err: any) => { this.handleErrorWithoutAlert('SaveNewChannel', 'Opening Channel Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() updateChannel = this.actions$.pipe( ofType(LNDActions.UPDATE_CHANNELS_LND), mergeMap((action: LNDActions.UpdateChannels) => { return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API + '/chanPolicy', { baseFeeMsat: action.payload.baseFeeMsat, feeRate: action.payload.feeRate, timeLockDelta: action.payload.timeLockDelta, chanPoint: action.payload.chanPoint }) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); if(action.payload.chanPoint === 'all') { this.store.dispatch(new RTLActions.OpenSnackBar('All Channels Updated Successfully.')); } else { this.store.dispatch(new RTLActions.OpenSnackBar('Channel Updated Successfully!')); } return { type: LNDActions.FETCH_ALL_CHANNELS_LND }; }), catchError((err: any) => { this.handleErrorWithAlert('ERROR', 'Update Channel Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/chanPolicy', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() closeChannel = this.actions$.pipe( ofType(LNDActions.CLOSE_CHANNEL_LND), mergeMap((action: LNDActions.CloseChannel) => { let reqUrl = this.CHILD_API_URL + environment.CHANNELS_API + '/' + action.payload.channelPoint + '?force=' + action.payload.forcibly; if(action.payload.targetConf) { reqUrl = reqUrl + '&target_conf=' + action.payload.targetConf; } if(action.payload.satPerByte) { reqUrl = reqUrl + '&sat_per_byte=' + action.payload.satPerByte; } return this.httpClient.delete(reqUrl) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new LNDActions.FetchBalance('channels')); this.store.dispatch(new LNDActions.FetchBalance('blockchain')); this.store.dispatch(new LNDActions.FetchAllChannels()); if (action.payload.forcibly) { this.store.dispatch(new LNDActions.FetchPendingChannels()); } else { this.store.dispatch(new LNDActions.FetchClosedChannels()); } this.store.dispatch(new LNDActions.BackupChannels({ channelPoint: 'ALL', showMessage: 'Channel Closed Successfully!' })); return { type: LNDActions.REMOVE_CHANNEL_LND, payload: { channelPoint: action.payload.channelPoint } }; }), catchError((err: any) => { this.handleErrorWithAlert('ERROR', 'Unable to Close Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_API + '/' + action.payload.channelPoint + '?force=' + action.payload.forcibly, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() backupChannels = this.actions$.pipe( ofType(LNDActions.BACKUP_CHANNELS_LND), mergeMap((action: LNDActions.BackupChannels) => { this.store.dispatch(new LNDActions.ClearEffectError('BackupChannels')); return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/' + action.payload.channelPoint) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.OpenSnackBar(action.payload.showMessage + ' ' + postRes.message)); return { type: LNDActions.BACKUP_CHANNELS_RES_LND, payload: postRes.message }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'BackupChannels', code: err.status, message: err.error.error })); this.handleErrorWithAlert('ERROR', action.payload.showMessage + ' ' + 'Unable to Backup Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/' + action.payload.channelPoint, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() verifyChannels = this.actions$.pipe( ofType(LNDActions.VERIFY_CHANNELS_LND), mergeMap((action: LNDActions.VerifyChannels) => { this.store.dispatch(new LNDActions.ClearEffectError('VerifyChannels')); return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/verify/' + action.payload.channelPoint, {}) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.OpenSnackBar(postRes.message)); return { type: LNDActions.VERIFY_CHANNELS_RES_LND, payload: postRes.message }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'VerifyChannels', code: err.status, message: err.error.error })); this.handleErrorWithAlert('ERROR', 'Unable to Verify Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/verify/' + action.payload.channelPoint, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() restoreChannels = this.actions$.pipe( ofType(LNDActions.RESTORE_CHANNELS_LND), mergeMap((action: LNDActions.RestoreChannels) => { this.store.dispatch(new LNDActions.ClearEffectError('RestoreChannels')); return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/restore/' + action.payload.channelPoint, {}) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.OpenSnackBar(postRes.message)); this.store.dispatch(new LNDActions.SetRestoreChannelsList(postRes.list)); return { type: LNDActions.RESTORE_CHANNELS_RES_LND, payload: postRes.message }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'RestoreChannels', code: err.status, message: err.error.error })); this.handleErrorWithAlert('ERROR', 'Unable to Restore Channel. Try again later.', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/restore/' + action.payload.channelPoint, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() fetchFees = this.actions$.pipe( ofType(LNDActions.FETCH_FEES_LND), mergeMap((action: LNDActions.FetchFees) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchFees')); return this.httpClient.get(this.CHILD_API_URL + environment.FEES_API); }), map((fees) => { this.logger.info(fees); if(fees.forwarding_events_history) { this.store.dispatch(new LNDActions.SetForwardingHistory(fees.forwarding_events_history)); delete fees.forwarding_events_history; } return { type: LNDActions.SET_FEES_LND, payload: fees ? fees : {} }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchFees', 'Fetching Fees Failed.', err); return of({type: RTLActions.VOID}); }) ); @Effect() balanceFetch = this.actions$.pipe( ofType(LNDActions.FETCH_BALANCE_LND), mergeMap((action: LNDActions.FetchBalance) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchBalance/' + action.payload)); return this.httpClient.get(this.CHILD_API_URL + environment.BALANCE_API + '/' + action.payload) .pipe( map((res: any) => { if (action.payload === 'channels') { this.store.dispatch(new LNDActions.FetchBalance('blockchain')); } this.logger.info(res); const emptyRes = (action.payload === 'channels') ? { balance: '', btc_balance: '' } : { total_balance: '', btc_total_balance: '' }; return { type: LNDActions.SET_BALANCE_LND, payload: res ? { target: action.payload, balance: res } : { target: action.payload, balance: emptyRes } }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchBalance/' + action.payload, 'Fetching' + this.commonService.titleCase(action.payload) + ' Balance Failed.', err); return of({type: RTLActions.VOID}); } )); } )); @Effect() networkInfoFetch = this.actions$.pipe( ofType(LNDActions.FETCH_NETWORK_LND), mergeMap((action: LNDActions.FetchNetwork) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchNetwork')); return this.httpClient.get(this.CHILD_API_URL + environment.NETWORK_API + '/info'); }), map((networkInfo) => { this.logger.info(networkInfo); return { type: LNDActions.SET_NETWORK_LND, payload: networkInfo ? networkInfo : {} }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchNetwork', 'Fetching Network Failed.', err); return of({type: RTLActions.VOID}); } )); @Effect() channelsAllFetch = this.actions$.pipe( ofType(LNDActions.FETCH_ALL_CHANNELS_LND), mergeMap((action: LNDActions.FetchAllChannels) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchChannels/all')); return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API) .pipe( map((channels: any) => { this.logger.info(channels); return { type: LNDActions.SET_ALL_CHANNELS_LND, payload: (channels && channels.channels && channels.channels.length > 0) ? channels.channels : [] }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchChannels/all', 'Fetching All Channels Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() channelsPendingFetch = this.actions$.pipe( ofType(LNDActions.FETCH_PENDING_CHANNELS_LND), mergeMap((action: LNDActions.FetchPendingChannels) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchChannels/pending')); return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API + '/pending') .pipe( map((channels: any) => { this.logger.info(channels); let pendingChannels: PendingChannelsGroup = { open: {num_channels: 0, limbo_balance: 0}, closing: {num_channels: 0, limbo_balance: 0}, force_closing: {num_channels: 0, limbo_balance: 0}, waiting_close: {num_channels: 0, limbo_balance: 0}, total_channels: 0, total_limbo_balance: 0}; if (channels) { pendingChannels.total_limbo_balance = channels.total_limbo_balance; if (channels.pending_closing_channels) { pendingChannels.closing.num_channels = channels.pending_closing_channels.length; pendingChannels.total_channels = pendingChannels.total_channels + channels.pending_closing_channels.length; channels.pending_closing_channels.forEach(closingChannel => { pendingChannels.closing.limbo_balance = +pendingChannels.closing.limbo_balance + (closingChannel.channel.local_balance ? +closingChannel.channel.local_balance : 0); }); } if (channels.pending_force_closing_channels) { pendingChannels.force_closing.num_channels = channels.pending_force_closing_channels.length; pendingChannels.total_channels = pendingChannels.total_channels + channels.pending_force_closing_channels.length; channels.pending_force_closing_channels.forEach(closingChannel => { pendingChannels.force_closing.limbo_balance = +pendingChannels.force_closing.limbo_balance + (closingChannel.channel.local_balance ? +closingChannel.channel.local_balance : 0); }); } if (channels.pending_open_channels) { pendingChannels.open.num_channels = channels.pending_open_channels.length; pendingChannels.total_channels = pendingChannels.total_channels + channels.pending_open_channels.length; channels.pending_open_channels.forEach(openingChannel => { pendingChannels.open.limbo_balance = +pendingChannels.open.limbo_balance + (openingChannel.channel.local_balance ? +openingChannel.channel.local_balance : 0); }); } if (channels.waiting_close_channels) { pendingChannels.waiting_close.num_channels = channels.waiting_close_channels.length; pendingChannels.total_channels = pendingChannels.total_channels + channels.waiting_close_channels.length; channels.waiting_close_channels.forEach(closingChannel => { pendingChannels.waiting_close.limbo_balance = +pendingChannels.waiting_close.limbo_balance + (closingChannel.channel.local_balance ? +closingChannel.channel.local_balance : 0); }); } } return { type: LNDActions.SET_PENDING_CHANNELS_LND, payload: channels ? { channels: channels, pendingChannels: pendingChannels } : {channels: {}, pendingChannels: pendingChannels} }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchChannels/pending', 'Fetching Pending Channels Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() channelsClosedFetch = this.actions$.pipe( ofType(LNDActions.FETCH_CLOSED_CHANNELS_LND), mergeMap((action: LNDActions.FetchClosedChannels) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchChannels/closed')); return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API + '/closed') .pipe( map((channels: any) => { this.logger.info(channels); return { type: LNDActions.SET_CLOSED_CHANNELS_LND, payload: (channels && channels.channels && channels.channels.length > 0) ? channels.channels : [] }; }, catchError((err: any) => { this.handleErrorWithoutAlert('FetchChannels/closed', 'Fetching Closed Channels Failed.', err); return of({type: RTLActions.VOID}); }) )); } )); @Effect() invoicesFetch = this.actions$.pipe( ofType(LNDActions.FETCH_INVOICES_LND), mergeMap((action: LNDActions.FetchInvoices) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchInvoices')); const num_max_invoices = (action.payload.num_max_invoices) ? action.payload.num_max_invoices : 100; const index_offset = (action.payload.index_offset) ? action.payload.index_offset : 0; const reversed = (action.payload.reversed) ? action.payload.reversed : false; return this.httpClient.get(this.CHILD_API_URL + environment.INVOICES_API + '?num_max_invoices=' + num_max_invoices + '&index_offset=' + index_offset + '&reversed=' + reversed) .pipe(map((res: ListInvoices) => { this.logger.info(res); if (action.payload.reversed && !action.payload.index_offset) { this.store.dispatch(new LNDActions.SetTotalInvoices(+res.last_index_offset)); } return { type: LNDActions.SET_INVOICES_LND, payload: res }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchInvoices', 'Fetching Invoices Failed.', err); return of({type: RTLActions.VOID}); } )); })); @Effect() transactionsFetch = this.actions$.pipe( ofType(LNDActions.FETCH_TRANSACTIONS_LND), mergeMap((action: LNDActions.FetchTransactions) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchTransactions')); return this.httpClient.get(this.CHILD_API_URL + environment.TRANSACTIONS_API); }), map((transactions) => { this.logger.info(transactions); return { type: LNDActions.SET_TRANSACTIONS_LND, payload: (transactions && transactions.length > 0) ? transactions : [] }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchTransactions', 'Fetching Transactions Failed.', err); return of({type: RTLActions.VOID}); } )); @Effect() utxosFetch = this.actions$.pipe( ofType(LNDActions.FETCH_UTXOS_LND), withLatestFrom(this.store.select('lnd')), mergeMap(([action, lndData]: [LNDActions.FetchUTXOs, fromLNDReducers.LNDState]) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchUTXOs')); return this.httpClient.get(this.CHILD_API_URL + environment.WALLET_API + '/getUTXOs?max_confs=' + (lndData.information && lndData.information.block_height ? lndData.information.block_height : 1000000000)); }), map((utxos) => { this.logger.info(utxos); return { type: LNDActions.SET_UTXOS_LND, payload: (utxos && utxos.length > 0) ? utxos : [] }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchUTXOs', 'Fetching UTXOs Failed.', err); return of({type: RTLActions.VOID}); } )); @Effect() paymentsFetch = this.actions$.pipe( ofType(LNDActions.FETCH_PAYMENTS_LND), mergeMap((action: LNDActions.FetchPayments) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchPayments')); return this.httpClient.get(this.CHILD_API_URL + environment.PAYMENTS_API); }), map((payments) => { this.logger.info(payments); return { type: LNDActions.SET_PAYMENTS_LND, payload: payments ? payments : [] }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchPayments', 'Fetching Payments Failed.', err); return of({type: RTLActions.VOID}); } )); @Effect() sendPayment = this.actions$.pipe( ofType(LNDActions.SEND_PAYMENT_LND), withLatestFrom(this.store.select('root')), mergeMap(([action, store]: [LNDActions.SendPayment, any]) => { this.store.dispatch(new LNDActions.ClearEffectError('SendPayment')); let queryHeaders = {}; if (action.payload.outgoingChannel) { queryHeaders['outgoingChannel'] = action.payload.outgoingChannel.chan_id; } if (action.payload.allowSelfPayment) { queryHeaders['allowSelfPayment'] = action.payload.allowSelfPayment; } // Channel Rebalancing if (action.payload.lastHopPubkey) { queryHeaders['lastHopPubkey'] = action.payload.lastHopPubkey; } if(action.payload.feeLimitType && action.payload.feeLimitType !== FEE_LIMIT_TYPES[0]) { queryHeaders['feeLimit'] = {}; queryHeaders['feeLimit'][action.payload.feeLimitType.id] = action.payload.feeLimit; } if (action.payload.zeroAmtInvoice) { queryHeaders['paymentDecoded'] = action.payload.paymentDecoded; } else { queryHeaders['paymentReq'] = action.payload.paymentReq; } return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', queryHeaders) .pipe( map((sendRes: any) => { this.logger.info(sendRes); if (sendRes.payment_error) { if (action.payload.allowSelfPayment) { this.store.dispatch(new LNDActions.FetchInvoices({ num_max_invoices: PAGE_SIZE, reversed: true })); return { type: LNDActions.SEND_PAYMENT_STATUS_LND, payload: sendRes }; } else { this.logger.error('Error: ' + sendRes.payment_error); const myErr = {status: sendRes.payment_error.status, error: sendRes.payment_error.error && sendRes.payment_error.error.error && typeof(sendRes.payment_error.error.error) === 'object' ? sendRes.payment_error.error.error : {error: sendRes.payment_error.error && sendRes.payment_error.error.error ? sendRes.payment_error.error.error : 'Unknown Error'}}; if (action.payload.fromDialog) { this.handleErrorWithoutAlert('SendPayment', 'Send Payment Failed.', myErr); } else { this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', myErr); } return of({type: RTLActions.VOID}); } } else { this.store.dispatch(new LNDActions.FetchAllChannels()); this.store.dispatch(new LNDActions.FetchBalance('channels')); this.store.dispatch(new LNDActions.FetchPayments()); if (action.payload.allowSelfPayment) { this.store.dispatch(new LNDActions.FetchInvoices({ num_max_invoices: PAGE_SIZE, reversed: true })); } else { this.store.dispatch(new RTLActions.CloseSpinner()); let msg = 'Payment Sent Successfully.'; if(sendRes.payment_route && sendRes.payment_route.total_fees_msat) { msg = 'Payment sent successfully with the total fee ' + sendRes.payment_route.total_fees_msat + ' (mSats).'; } this.store.dispatch(new RTLActions.OpenSnackBar(msg)); } return { type: LNDActions.SEND_PAYMENT_STATUS_LND, payload: sendRes }; } }), catchError((err: any) => { if (action.payload.allowSelfPayment) { this.store.dispatch(new LNDActions.FetchInvoices({ num_max_invoices: PAGE_SIZE, reversed: true })); return of({ type: LNDActions.SEND_PAYMENT_STATUS_LND, payload: err }); } else { this.logger.error('Error: ' + JSON.stringify(err)); const myErr = {status: err.status, error: err.error && err.error.error && typeof(err.error.error) === 'object' ? err.error.error : {error: err.error && err.error.error ? err.error.error : 'Unknown Error'}}; if (action.payload.fromDialog) { this.handleErrorWithoutAlert('SendPayment', 'Send Payment Failed.', myErr); } else { this.handleErrorWithAlert('ERROR', 'Send Payment Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/transactions', myErr); } return of({type: RTLActions.VOID}); } }) ); }) ); @Effect() graphNodeFetch = this.actions$.pipe( ofType(LNDActions.FETCH_GRAPH_NODE_LND), mergeMap((action: LNDActions.FetchGraphNode) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchGraphNode')); return this.httpClient.get(this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload.pubkey) .pipe(map((graphNode: any) => { this.logger.info(graphNode); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.SET_GRAPH_NODE_LND, payload: graphNode && graphNode.node ? {node: graphNode.node} : {node: null} }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchGraphNode', 'Fetching Graph Node Failed.', err); return of({type: RTLActions.VOID}); })); } )); @Effect({ dispatch: false }) setGraphNode = this.actions$.pipe( ofType(LNDActions.SET_GRAPH_NODE_LND), map((action: LNDActions.SetGraphNode) => { this.logger.info(action.payload); return action.payload; }) ); @Effect() getNewAddress = this.actions$.pipe( ofType(LNDActions.GET_NEW_ADDRESS_LND), mergeMap((action: LNDActions.GetNewAddress) => { return this.httpClient.get(this.CHILD_API_URL + environment.NEW_ADDRESS_API + '?type=' + action.payload.addressId) .pipe(map((newAddress: any) => { this.logger.info(newAddress); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.SET_NEW_ADDRESS_LND, payload: (newAddress && newAddress.address) ? newAddress.address : {} }; }), catchError((err: any) => { this.handleErrorWithAlert('ERROR', 'Generate New Address Failed', this.CHILD_API_URL + environment.NEW_ADDRESS_API + '?type=' + action.payload.addressId, err); return of({type: RTLActions.VOID}); })); }) ); @Effect({ dispatch: false }) setNewAddress = this.actions$.pipe( ofType(LNDActions.SET_NEW_ADDRESS_LND), map((action: LNDActions.SetNewAddress) => { this.logger.info(action.payload); return action.payload; }) ); @Effect() SetChannelTransaction = this.actions$.pipe( ofType(LNDActions.SET_CHANNEL_TRANSACTION_LND), mergeMap((action: LNDActions.SetChannelTransaction) => { this.store.dispatch(new LNDActions.ClearEffectError('SetChannelTransaction')); return this.httpClient.post(this.CHILD_API_URL + environment.TRANSACTIONS_API, { amount: action.payload.amount, address: action.payload.address, sendAll: action.payload.sendAll, fees: action.payload.fees, blocks: action.payload.blocks } ) .pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new LNDActions.FetchTransactions()); this.store.dispatch(new LNDActions.FetchBalance('blockchain')); return { type: LNDActions.SET_CHANNEL_TRANSACTION_RES_LND, payload: postRes }; }), catchError((err: any) => { this.handleErrorWithoutAlert('SetChannelTransaction', 'Sending Fund Failed.', err); return of({type: RTLActions.VOID}); })); }) ); @Effect() fetchForwardingHistory = this.actions$.pipe( ofType(LNDActions.GET_FORWARDING_HISTORY_LND), mergeMap((action: LNDActions.GetForwardingHistory) => { this.store.dispatch(new LNDActions.ClearEffectError('GetForwardingHistory')); const queryHeaders: SwitchReq = { num_max_events: action.payload.num_max_events, index_offset: action.payload.index_offset, end_time: action.payload.end_time, start_time: action.payload.start_time }; return this.httpClient.post(this.CHILD_API_URL + environment.SWITCH_API, queryHeaders) .pipe( map((fhRes: any) => { this.logger.info(fhRes); return { type: LNDActions.SET_FORWARDING_HISTORY_LND, payload: fhRes }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'GetForwardingHistory', code: err.status, message: err.error.error })); this.handleErrorWithAlert('ERROR', 'Get Forwarding History Failed', this.CHILD_API_URL + environment.SWITCH_API, err); return of({type: RTLActions.VOID}); }) ); }) ); @Effect() queryRoutesFetch = this.actions$.pipe( ofType(LNDActions.GET_QUERY_ROUTES_LND), mergeMap((action: LNDActions.GetQueryRoutes) => { let url = this.CHILD_API_URL + environment.NETWORK_API + '/routes/' + action.payload.destPubkey + '/' + action.payload.amount; if (action.payload.outgoingChanId) { url = url + '?outgoing_chan_id=' + action.payload.outgoingChanId; } return this.httpClient.get(url) .pipe( map((qrRes: any) => { this.logger.info(qrRes); return { type: LNDActions.SET_QUERY_ROUTES_LND, payload: qrRes }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.SetQueryRoutes({})); this.handleErrorWithAlert('ERROR', 'Get Query Routes Failed', this.CHILD_API_URL + environment.NETWORK_API, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect({ dispatch: false }) setQueryRoutes = this.actions$.pipe( ofType(LNDActions.SET_QUERY_ROUTES_LND), map((action: LNDActions.SetQueryRoutes) => { return action.payload; }) ); @Effect() genSeed = this.actions$.pipe( ofType(LNDActions.GEN_SEED_LND), mergeMap((action: LNDActions.GenSeed) => { return this.httpClient.get(this.CHILD_API_URL + environment.WALLET_API + '/genseed/' + action.payload) .pipe( map((postRes: any) => { this.logger.info('Generated GenSeed!'); this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.GEN_SEED_RESPONSE_LND, payload: postRes.cipher_seed_mnemonic }; }), catchError((err) => { this.handleErrorWithAlert('ERROR', 'Genseed Generation Failed', this.CHILD_API_URL + environment.WALLET_API + '/genseed/' + action.payload, err); return of({type: RTLActions.VOID}); }) ); } )); @Effect() updateSelNodeOptions = this.actions$.pipe( ofType(RTLActions.UPDATE_SELECTED_NODE_OPTIONS), mergeMap((action: RTLActions.UpdateSelectedNodeOptions) => { return this.httpClient.get(this.CHILD_API_URL + environment.WALLET_API + '/updateSelNodeOptions') .pipe( map((postRes: any) => { this.logger.info('Update Sel Node Successfull'); this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: RTLActions.VOID }; }), catchError((err) => { this.handleErrorWithAlert('ERROR', 'Update macaroon for newly initialized node failed! Please check the macaroon path and restart the server!', 'Update Macaroon', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect({ dispatch: false }) genSeedResponse = this.actions$.pipe( ofType(LNDActions.GEN_SEED_RESPONSE_LND), map((action: LNDActions.GenSeedResponse) => { return action.payload; }) ); @Effect({ dispatch: false }) initWalletRes = this.actions$.pipe( ofType(LNDActions.INIT_WALLET_RESPONSE_LND), map((action: LNDActions.InitWalletResponse) => { return action.payload; }) ); @Effect() initWallet = this.actions$.pipe( ofType(LNDActions.INIT_WALLET_LND), mergeMap((action: LNDActions.InitWallet) => { return this.httpClient.post(this.CHILD_API_URL + environment.WALLET_API + '/wallet/initwallet', { wallet_password: action.payload.pwd, cipher_seed_mnemonic: action.payload.cipher ? action.payload.cipher : '', aezeed_passphrase: action.payload.passphrase ? action.payload.passphrase : '' }) .pipe( map((postRes) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.INIT_WALLET_RESPONSE_LND, payload: postRes }; }), catchError((err) => { this.handleErrorWithAlert('ERROR', 'Wallet Initialization Failed', this.CHILD_API_URL + environment.WALLET_API + '/initwallet', err); return of({type: RTLActions.VOID}); }) ); } )); @Effect({ dispatch: false }) unlockWallet = this.actions$.pipe( ofType(LNDActions.UNLOCK_WALLET_LND), mergeMap((action: LNDActions.UnlockWallet) => { return this.httpClient.post(this.CHILD_API_URL + environment.WALLET_API + '/wallet/unlockwallet', { wallet_password: action.payload.pwd }) .pipe( map((postRes) => { this.logger.info(postRes); this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.OpenSpinner('Initializing Node...')); this.logger.info('Successfully Unlocked!'); this.sessionService.setItem('lndUnlocked', 'true'); setTimeout(() => { this.store.dispatch(new RTLActions.CloseSpinner()); this.logger.info('Successfully Initialized!'); this.store.dispatch(new LNDActions.FetchInfo({loadPage: 'HOME'})); }, 1000 * 5); return { type: RTLActions.VOID }; }), catchError((err) => { this.handleErrorWithAlert('ERROR', 'Unlock Wallet Failed', this.CHILD_API_URL + environment.WALLET_API + '/unlockwallet', err); return of({ type: RTLActions.VOID }); }) ); } )); @Effect() peerLookup = this.actions$.pipe( ofType(LNDActions.PEER_LOOKUP_LND), mergeMap((action: LNDActions.PeerLookup) => { this.store.dispatch(new LNDActions.ClearEffectError('Lookup')); return this.httpClient.get(this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload) .pipe( map((resPeer) => { this.logger.info(resPeer); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.SET_LOOKUP_LND, payload: resPeer }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'Lookup', code: err.status, message: err.error.message })); this.handleErrorWithAlert('ERROR', 'Peer Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/node/' + action.payload, err); return of({type: RTLActions.VOID}); }) ); }) ); @Effect() channelLookup = this.actions$.pipe( ofType(LNDActions.CHANNEL_LOOKUP_LND), mergeMap((action: LNDActions.ChannelLookup) => { this.store.dispatch(new LNDActions.ClearEffectError('Lookup')); return this.httpClient.get(this.CHILD_API_URL + environment.NETWORK_API + '/edge/' + action.payload) .pipe( map((resChannel) => { this.logger.info(resChannel); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.SET_LOOKUP_LND, payload: resChannel }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'Lookup', code: err.status, message: err.error.message })); this.handleErrorWithAlert('ERROR', 'Channel Lookup Failed', this.CHILD_API_URL + environment.NETWORK_API + '/edge/' + action.payload, err); this.store.dispatch(new LNDActions.SetLookup({})); return of({type: RTLActions.VOID}); }) ); }) ); @Effect() invoiceLookup = this.actions$.pipe( ofType(LNDActions.INVOICE_LOOKUP_LND), mergeMap((action: LNDActions.InvoiceLookup) => { this.store.dispatch(new LNDActions.ClearEffectError('Lookup')); return this.httpClient.get(this.CHILD_API_URL + environment.INVOICES_API + '/' + action.payload) .pipe( map((resInvoice) => { this.logger.info(resInvoice); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.SET_LOOKUP_LND, payload: resInvoice }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'Lookup', code: err.status, message: err.error.message })); this.handleErrorWithAlert('ERROR', 'Invoice Lookup Failed', this.CHILD_API_URL + environment.INVOICES_API + '/' + action.payload, err); return of({type: RTLActions.VOID}); }) ); }) ); @Effect({ dispatch: false }) setLookup = this.actions$.pipe( ofType(LNDActions.SET_LOOKUP_LND), map((action: LNDActions.SetLookup) => { this.logger.info(action.payload); return action.payload; }) ); @Effect() getRestoreChannelList = this.actions$.pipe( ofType(LNDActions.RESTORE_CHANNELS_LIST_LND), mergeMap((action: LNDActions.RestoreChannelsList) => { this.store.dispatch(new LNDActions.ClearEffectError('RestoreChannelsList')); return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_BACKUP_API + '/restore/list') .pipe( map((resRestoreList) => { this.logger.info(resRestoreList); this.store.dispatch(new RTLActions.CloseSpinner()); return { type: LNDActions.SET_RESTORE_CHANNELS_LIST_LND, payload: (resRestoreList) ? resRestoreList : {all_restore_exists: false, files: []} }; }), catchError((err: any) => { this.store.dispatch(new LNDActions.EffectError({ action: 'RestoreChannelsList', code: err.status, message: err.error.message })); this.handleErrorWithAlert('ERROR', 'Restore Channels List Failed', this.CHILD_API_URL + environment.CHANNELS_BACKUP_API, err); return of({type: RTLActions.VOID}); }) ); }) ); @Effect({ dispatch: false }) setRestoreChannelList = this.actions$.pipe( ofType(LNDActions.SET_RESTORE_CHANNELS_LIST_LND), map((action: LNDActions.SetRestoreChannelsList) => { this.logger.info(action.payload); return action.payload; }) ); @Effect() getLoopSwaps = this.actions$.pipe( ofType(LNDActions.FETCH_LOOP_SWAPS_LND), mergeMap((action: LNDActions.FetchLoopSwaps) => { this.store.dispatch(new LNDActions.ClearEffectError('FetchSwaps')); return this.httpClient.get(this.CHILD_API_URL + environment.LOOP_API + '/swaps') .pipe( map((swaps: any) => { this.logger.info(swaps); return { type: LNDActions.SET_LOOP_SWAPS_LND, payload: swaps }; }), catchError((err: any) => { this.handleErrorWithoutAlert('FetchSwaps', 'Fetching Swaps Failed.', err); return of({type: RTLActions.VOID}); }) ); } )); initializeRemainingData(info: any, landingPage: string) { this.sessionService.setItem('lndUnlocked', 'true'); if (info.chains) { if (typeof info.chains[0] === 'string') { info.smaller_currency_unit = (info.chains[0].toString().toLowerCase().indexOf('bitcoin') < 0) ? CurrencyUnitEnum.LITOSHIS : CurrencyUnitEnum.SATS; info.currency_unit = (info.chains[0].toString().toLowerCase().indexOf('bitcoin') < 0) ? CurrencyUnitEnum.LTC : CurrencyUnitEnum.BTC; } else if (typeof info.chains[0] === 'object' && info.chains[0].hasOwnProperty('chain')) { const getInfoChain = info.chains[0]; info.smaller_currency_unit = (getInfoChain.chain.toLowerCase().indexOf('bitcoin') < 0) ? CurrencyUnitEnum.LITOSHIS : CurrencyUnitEnum.SATS; info.currency_unit = (getInfoChain.chain.toLowerCase().indexOf('bitcoin') < 0) ? CurrencyUnitEnum.LTC : CurrencyUnitEnum.BTC; } info.version = (!info.version) ? '' : info.version.split(' ')[0]; } else { info.smaller_currency_unit = CurrencyUnitEnum.SATS; info.currency_unit = CurrencyUnitEnum.BTC; info.version = (!info.version) ? '' : info.version.split(' ')[0]; } const node_data = { identity_pubkey: info.identity_pubkey, alias: info.alias, testnet: info.testnet, chains: info.chains, uris: info.uris, version: info.version, currency_unit: info.currency_unit, smaller_currency_unit: info.smaller_currency_unit }; this.store.dispatch(new RTLActions.OpenSpinner('Initializing Node Data...')); this.store.dispatch(new RTLActions.SetNodeData(node_data)); this.store.dispatch(new LNDActions.FetchPeers()); this.store.dispatch(new LNDActions.FetchBalance('channels')); this.store.dispatch(new LNDActions.FetchNetwork()); this.store.dispatch(new LNDActions.FetchAllChannels()); this.store.dispatch(new LNDActions.FetchPendingChannels()); this.store.dispatch(new LNDActions.FetchClosedChannels()); this.store.dispatch(new LNDActions.FetchInvoices({num_max_invoices: 10, reversed: true})); this.store.dispatch(new LNDActions.FetchPayments()); this.store.dispatch(new LNDActions.FetchFees()); //Fetches monthly forwarding history as well, to count total number of events let newRoute = this.location.path(); if(newRoute.includes('/cl/')) { newRoute = newRoute.replace('/cl/', '/lnd/'); } else if (newRoute.includes('/ecl/')) { newRoute = newRoute.replace('/ecl/', '/lnd/'); } if(newRoute.includes('/unlock') || newRoute.includes('/login') || newRoute.includes('/error') || newRoute === '' || landingPage === 'HOME' || newRoute.includes('?access-key=')) { newRoute = '/lnd/home'; } this.router.navigate([newRoute]); } handleErrorWithoutAlert(actionName: string, genericErrorMessage: string, err: { status: number, error: any }) { this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err)); if (err.status === 401) { this.logger.info('Redirecting to Login'); this.store.dispatch(new RTLActions.CloseAllDialogs()); this.store.dispatch(new RTLActions.Logout()); this.store.dispatch(new RTLActions.OpenSnackBar('Authentication Failed. Redirecting to Login.')); } else { this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new LNDActions.EffectError({ action: actionName, code: err.status.toString(), message: (err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : (err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.error : (err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : (err.error.error && typeof err.error.error === 'string') ? err.error.error : typeof err.error === 'string' ? err.error : genericErrorMessage})); } } handleErrorWithAlert(alertType: string, alertTitle: string, errURL: string, err: { status: number, error: any }) { this.logger.error(err); if (err.status === 401) { this.logger.info('Redirecting to Login'); this.store.dispatch(new RTLActions.CloseAllDialogs()); this.store.dispatch(new RTLActions.Logout()); this.store.dispatch(new RTLActions.OpenSnackBar('Authentication Failed. Redirecting to Login.')); } else { this.store.dispatch(new RTLActions.CloseSpinner()); this.store.dispatch(new RTLActions.OpenAlert({ data: { type: alertType, alertTitle: alertTitle, message: { code: err.status, message: (err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error : (err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.error : (err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error : (err.error.error && typeof err.error.error === 'string') ? err.error.error : typeof err.error === 'string' ? err.error : 'Unknown Error', URL: errURL }, component: ErrorMessageComponent } })); } } ngOnDestroy() { this.unSubs.forEach(completeSub => { completeSub.next(); completeSub.complete(); }); } }