UNPKG

copay-crown

Version:

A Secure Crown Wallet

321 lines (282 loc) 10.6 kB
import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Events, ModalController, NavParams, ViewController } from 'ionic-angular'; import { Logger } from '../../providers/logger/logger'; // providers import { BwcErrorProvider } from '../../providers/bwc-error/bwc-error'; import { ConfigProvider } from '../../providers/config/config'; import { FeeProvider } from '../../providers/fee/fee'; import { OnGoingProcessProvider } from '../../providers/on-going-process/on-going-process'; import { PlatformProvider } from '../../providers/platform/platform'; import { PopupProvider } from '../../providers/popup/popup'; import { ProfileProvider } from '../../providers/profile/profile'; import { TxFormatProvider } from '../../providers/tx-format/tx-format'; import { WalletProvider } from '../../providers/wallet/wallet'; // pages import { FinishModalPage } from '../finish/finish'; import * as _ from 'lodash'; @Component({ selector: 'page-txp-details', templateUrl: 'txp-details.html' }) export class TxpDetailsPage { public wallet: any; public tx: any; public copayers: any; public copayerId: string; public isShared: boolean; public canSign: boolean; public color: string; public buttonText: string; public successText: string; public actionList: any[]; public paymentExpired: boolean; public expires: string; public currentSpendUnconfirmed: boolean; public loading: boolean; private isGlidera: boolean; private GLIDERA_LOCK_TIME: number; private countDown: any; private isCordova: boolean; constructor( private navParams: NavParams, private platformProvider: PlatformProvider, private feeProvider: FeeProvider, private events: Events, private logger: Logger, private popupProvider: PopupProvider, private bwcError: BwcErrorProvider, private walletProvider: WalletProvider, private onGoingProcessProvider: OnGoingProcessProvider, private viewCtrl: ViewController, private configProvider: ConfigProvider, private profileProvider: ProfileProvider, private txFormatProvider: TxFormatProvider, private translate: TranslateService, private modalCtrl: ModalController ) { let config = this.configProvider.get().wallet; this.tx = this.navParams.data.tx; this.wallet = this.tx.wallet ? this.tx.wallet : this.profileProvider.getWallet(this.tx.walletId); this.tx = this.txFormatProvider.processTx(this.wallet.coin, this.tx); if (!this.tx.toAddress) this.tx.toAddress = this.tx.outputs[0].toAddress; this.isGlidera = this.navParams.data.isGlidera; this.GLIDERA_LOCK_TIME = 6 * 60 * 60; this.currentSpendUnconfirmed = config.spendUnconfirmed; this.loading = false; this.isCordova = this.platformProvider.isCordova; this.copayers = this.wallet.status.wallet.copayers; this.copayerId = this.wallet.credentials.copayerId; this.isShared = this.wallet.credentials.n > 1; this.canSign = this.wallet.canSign() || this.wallet.isPrivKeyExternal(); this.color = this.wallet.color; } ionViewWillEnter() { this.displayFeeValues(); this.initActionList(); this.checkPaypro(); this.applyButtonText(); // ToDo: use tx.customData instead of tx.message if (this.tx.message === 'Glidera transaction' && this.isGlidera) { this.tx.isGlidera = true; if (this.tx.canBeRemoved) { this.tx.canBeRemoved = (Date.now() / 1000 - (this.tx.ts || this.tx.createdOn)) > this.GLIDERA_LOCK_TIME; } } this.events.subscribe('bwsEvent', (walletId: string, type: string, n: number) => { _.each([ 'TxProposalRejectedBy', 'TxProposalAcceptedBy', 'transactionProposalRemoved', 'TxProposalRemoved', 'NewOutgoingTx', 'UpdateTx' ], (eventName: string) => { if (walletId == this.wallet.id && type == eventName) { this.updateTxInfo(eventName); } }); }); } private displayFeeValues(): void { this.tx.feeFiatStr = this.txFormatProvider.formatAlternativeStr(this.wallet.coin, this.tx.fee); this.tx.feeRateStr = (this.tx.fee / (this.tx.amount + this.tx.fee) * 100).toFixed(2) + '%'; this.tx.feeLevelStr = this.feeProvider.feeOpts[this.tx.feeLevel]; } private applyButtonText(): void { var lastSigner = _.filter(this.tx.actions, { type: 'accept' }).length == this.tx.requiredSignatures - 1; if (lastSigner) { this.buttonText = this.translate.instant('Click to send'); this.successText = this.translate.instant('Payment Sent'); } else { this.buttonText = this.translate.instant('Click to accept'); this.successText = this.translate.instant('Payment Accepted'); } } private initActionList(): void { this.actionList = []; if (!this.isShared) return; var actionDescriptions = { created: this.translate.instant('Proposal Created'), accept: this.translate.instant('Accepted'), reject: this.translate.instant('Rejected'), broadcasted: this.translate.instant('Broadcasted'), }; this.actionList.push({ type: 'created', time: this.tx.createdOn, description: actionDescriptions['created'], by: this.tx.creatorName }); _.each(this.tx.actions, (action: any) => { this.actionList.push({ type: action.type, time: action.createdOn, description: actionDescriptions[action.type], by: action.copayerName }); }); setTimeout(() => { this.actionList.reverse(); }, 10); } private checkPaypro() { if (this.tx.payProUrl) { this.wallet.fetchPayPro({ payProUrl: this.tx.payProUrl, }, (err, paypro) => { if (err) { this.logger.error(err); this.paymentExpired = true; this.popupProvider.ionicAlert(null, this.translate.instant('Could not fetch the invoice')); return; } this.tx.paypro = paypro; this.paymentTimeControl(this.tx.paypro.expires); }); } } private paymentTimeControl(expirationTime) { let setExpirationTime = (): void => { let now = Math.floor(Date.now() / 1000); if (now > expirationTime) { this.paymentExpired = true; if (this.countDown) clearInterval(this.countDown); return; } let totalSecs = expirationTime - now; let m = Math.floor(totalSecs / 60); let s = totalSecs % 60; this.expires = ('0' + m).slice(-2) + ":" + ('0' + s).slice(-2); }; this.paymentExpired = false; setExpirationTime(); this.countDown = setInterval(() => { setExpirationTime(); }, 1000); } private setError(err: any, prefix: string): void { this.loading = false; this.popupProvider.ionicAlert(this.translate.instant('Error'), this.bwcError.msg(err, prefix)); } public sign(): void { this.loading = true; this.walletProvider.publishAndSign(this.wallet, this.tx).then((txp: any) => { this.onGoingProcessProvider.clear(); this.openFinishModal(); }).catch((err: any) => { this.onGoingProcessProvider.clear(); this.setError(err, ('Could not send payment')); }); } public reject(txp: any): void { let title = this.translate.instant('Warning!'); let msg = this.translate.instant('Are you sure you want to reject this transaction?'); this.popupProvider.ionicConfirm(title, msg, null, null).then((res: boolean) => { if (!res) return this.loading = true; this.onGoingProcessProvider.set('rejectTx'); this.walletProvider.reject(this.wallet, this.tx).then((txpr) => { this.onGoingProcessProvider.clear(); this.close(); }).catch((err: any) => { this.onGoingProcessProvider.clear(); this.setError(err, this.translate.instant('Could not reject payment')); }); }); } public remove(): void { let title = this.translate.instant('Warning!'); let msg = this.translate.instant('Are you sure you want to remove this transaction?'); this.popupProvider.ionicConfirm(title, msg, null, null).then((res: boolean) => { if (!res) return; this.onGoingProcessProvider.set('removeTx'); this.walletProvider.removeTx(this.wallet, this.tx).then(() => { this.onGoingProcessProvider.clear(); this.close(); }).catch((err: any) => { this.onGoingProcessProvider.clear(); if (err && !(err.message && err.message.match(/Unexpected/))) { this.setError(err, this.translate.instant('Could not delete payment proposal')); } }); }); } public broadcast(txp: any): void { this.loading = true; this.onGoingProcessProvider.set('broadcastingTx'); this.walletProvider.broadcastTx(this.wallet, this.tx).then((txpb: any) => { this.onGoingProcessProvider.clear(); this.openFinishModal(); }).catch((err: any) => { this.onGoingProcessProvider.clear(); this.setError(err, 'Could not broadcast payment'); }); } public getShortNetworkName(): string { return this.wallet.credentials.networkName.substring(0, 4); } private updateTxInfo(eventName: string): void { this.walletProvider.getTxp(this.wallet, this.tx.id).then((tx: any) => { let action = _.find(tx.actions, { copayerId: this.wallet.credentials.copayerId }); this.tx = this.txFormatProvider.processTx(this.wallet.coin, tx); if (!action && tx.status == 'pending') this.tx.pendingForUs = true; this.updateCopayerList(); this.initActionList(); }).catch((err) => { if (err.message && err.message == 'Transaction proposal not found' && (eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved')) { this.tx.removed = true; this.tx.canBeRemoved = false; this.tx.pendingForUs = false; } }); } public updateCopayerList(): void { _.map(this.copayers, (cp: any) => { _.each(this.tx.actions, (ac: any) => { if (cp.id == ac.copayerId) { cp.action = ac.type; } }); }); } public onConfirm(): void { this.sign(); }; public close(): void { this.loading = false; this.viewCtrl.dismiss(); } private openFinishModal() { let modal = this.modalCtrl.create(FinishModalPage, { finishText: this.successText }, { showBackdrop: true, enableBackdropDismiss: false }); modal.present(); modal.onDidDismiss(() => { this.close(); }) } }