mirror of https://github.com/BTCPrivate/copay.git
Refactor: tx-details
This commit is contained in:
parent
f09be76687
commit
cc47c1fe8a
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 40.1 (33804) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Wallet" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="1.4---Transaction-(Detail)" transform="translate(-39.000000, -914.000000)">
|
||||
<g id="Group-2-Copy-6" transform="translate(41.000000, 909.000000)">
|
||||
<g id="Group" transform="translate(0.000000, 7.000000)">
|
||||
<path d="M10.5,21 C16.2989899,21 21,16.2989899 21,10.5 C21,4.70101013 16.2989899,0 10.5,0 C4.70101013,0 0,4.70101013 0,10.5 C0,16.2989899 4.70101013,21 10.5,21 Z" id="Oval-2-Copy-3" stroke="#13E5B6" stroke-width="3" fill="#13E5B6"></path>
|
||||
<path d="M4.8565172,10.7739543 L17.6308211,10.7739543 M9.55650668,16.6261508 L3.82082067,10.8572239 L9.35708555,5.17917933" id="Line" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" transform="translate(10.725821, 10.902665) scale(1, -1) rotate(-135.000000) translate(-10.725821, -10.902665) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Card" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#647CE8;}
|
||||
.st1{fill:none;stroke:#B2C1F7;}
|
||||
</style>
|
||||
<path id="card" class="st0" d="M11.6,28C10.2,28,9,26.8,9,25.5V14.5c0-1.4,1.2-2.5,2.6-2.5h16.5c1.5,0,2.6,1.1,2.6,2.5v10.9
|
||||
C30.6,26.8,29.5,28,28,28H11.6z"/>
|
||||
<path id="stripe-bottom" class="st0" d="M9,18.1h22"/>
|
||||
<path id="stripe-top" class="st0" d="M9,15.6h22"/>
|
||||
<path id="bottom-line" class="st1" d="M11.3,23.8h4.3"/>
|
||||
<path id="top-line" class="st1" d="M11.3,21.3h8.4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 818 B |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
|
@ -1,6 +1,7 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import * as _ from "lodash";
|
||||
|
||||
//providers
|
||||
import { ConfigProvider } from '../../../providers/config/config';
|
||||
|
@ -13,7 +14,6 @@ import { PopupProvider } from '../../../providers/popup/popup';
|
|||
import { FeedbackCompletePage } from '../feedback-complete/feedback-complete';
|
||||
import { HomePage } from '../../home/home';
|
||||
|
||||
import * as _ from "lodash";
|
||||
|
||||
@Component({
|
||||
selector: 'page-send-feedback',
|
||||
|
|
|
@ -5,68 +5,151 @@
|
|||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-list *ngIf="btx">
|
||||
<ion-item>
|
||||
<div *ngIf="tx.action == 'received'" class="action">
|
||||
<img src="assets/img/tx-action/icon-received.svg" width="40">
|
||||
<span translate>Received</span>
|
||||
<div class="sending-label" *ngIf="btx.confirmations > 0">
|
||||
<img class="sending-img" src="assets/img/tx-action/icon-sent.svg" width="40" *ngIf="btx.action === 'sent'">
|
||||
<img class="sending-img" src="assets/img/tx-action/icon-received.svg" width="40" *ngIf="btx.action === 'received'">
|
||||
<img class="sending-img" src="assets/img/tx-action/icon-moved.svg" width="40" *ngIf="btx.action === 'moved'">
|
||||
<span *ngIf="btx.action === 'sent'">{{'Sent' | translate}}</span>
|
||||
<span *ngIf="btx.action === 'received'">{{'Received' | translate}}</span>
|
||||
<span *ngIf="btx.action === 'moved'">{{'Moved' | translate}}</span>
|
||||
</div>
|
||||
<div *ngIf="tx.action == 'sent'" class="action">
|
||||
<img src="assets/img/tx-action/icon-sent.svg" width="40">
|
||||
<span translate>Sent</span>
|
||||
<div class="sending-label" *ngIf="btx.confirmations === 0">
|
||||
<img class="sending-img" src="assets/img/tx-action/icon-confirming.svg">
|
||||
<span *ngIf="btx.action == 'sent' || btx.action == 'moved'" translate>Sending</span>
|
||||
<span *ngIf="btx.action == 'received'" translate>Receiving</span>
|
||||
</div>
|
||||
<div *ngIf="tx.action == 'moved'" class="action">
|
||||
<img src="assets/img/tx-action/icon-moved.svg" width="40">
|
||||
<span translate>Moved</span>
|
||||
<div class="amount-label">
|
||||
<div class="amount">{{btx.amountValueStr}}
|
||||
<span class="unit">{{btx.amountUnitStr}}</span>
|
||||
</div>
|
||||
<div class="alternative" (click)="showRate = !showRate">
|
||||
<span *ngIf="!showRate">{{btx.alternativeAmountStr}}</span>
|
||||
<span *ngIf="showRate">
|
||||
<span *ngIf="!rate">...</span>
|
||||
<span *ngIf="rate">
|
||||
{{rate| currency:'':2}} {{alternativeIsoCode}} ({{rateDate | amDateFormat:'MM/DD/YYYY HH:mm a'}})
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ion-item>
|
||||
|
||||
<div class="amount-str">{{tx.amount | satToUnit: wallet.coin}}</div>
|
||||
<div class="amount-alt-str">{{tx.amount | satToFiat: wallet.coin}}</div>
|
||||
<ion-item *ngIf="btx.action === 'sent'">
|
||||
<span class="label" translate>To</span>
|
||||
<span class="payment-proposal-to">
|
||||
<img class="icon-bitcoin" *ngIf="!cardId" src="assets/img/icon-bitcoin-small.svg">
|
||||
<img class="icon-bitcoin" *ngIf="cardId" src="assets/img/icon-card.svg" width="34">
|
||||
<div copy-to-clipboard="{{ btx.addressTo }}" class="ellipsis pointer">
|
||||
<span class="contact" *ngIf="!toName">{{ btx.addressTo }}"</span>
|
||||
<span *ngIf="toName">{{ toName }}</span>
|
||||
</div>
|
||||
</span>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-avatar item-start>
|
||||
<img src="assets/img/icon-wallet.svg" class="icon-wallet">
|
||||
</ion-avatar>
|
||||
<h2 translate>To</h2>
|
||||
<p *ngIf="tx.action != 'sent'">{{wallet.name || '...'}}</p>
|
||||
<p *ngIf="tx.action == 'sent'">{{tx.addressTo || '...'}}</p>
|
||||
<span class="label" *ngIf="btx.action === 'sent'" translate>From</span>
|
||||
<span class="label" *ngIf="btx.action !== 'sent'" translate>To</span>
|
||||
<div class="wallet">
|
||||
<ion-icon item-start>
|
||||
<img *ngIf="wallet.network == 'testnet'" [ngStyle]="{'background-color': wallet.color}" src="assets/img/icon-wallet-testnet.svg"
|
||||
class="icon-wallet" />
|
||||
<img *ngIf="wallet.network != 'testnet'" [ngStyle]="{'background-color': wallet.color}" src="assets/img/icon-wallet.svg"
|
||||
class="icon-wallet" />
|
||||
</ion-icon>
|
||||
<div>{{wallet.name}}</div>
|
||||
</div>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngIf="tx.action == 'sent'">
|
||||
<ion-avatar item-start>
|
||||
<img src="assets/img/icon-wallet.svg" class="icon-wallet">
|
||||
</ion-avatar>
|
||||
<h2 translate>From</h2>
|
||||
<p>{{wallet.name || '...'}}</p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>{{'Date' | translate}}</ion-label>
|
||||
<ion-item *ngIf="btx.action != 'received' && isShared">
|
||||
<ion-label translate>Created by</ion-label>
|
||||
<ion-note item-end>
|
||||
{{tx.time * 1000 | amDateFormat:'MM/DD/YYYY HH:mm a'}}
|
||||
{{btx.creatorName}}
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<button ion-item (click)="addMemo()">
|
||||
<ion-item *ngIf="btx.ts || btx.createdOn || btx.time">
|
||||
<ion-label translate>Date</ion-label>
|
||||
<ion-note item-end>
|
||||
<time>{{ (btx.ts || btx.createdOn || btx.time) * 1000 | amDateFormat:'MM/DD/YYYY hh:mm a'}}</time>
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<button ion-item (click)="showCommentPopup()">
|
||||
<ion-label>{{'Memo' | translate}}</ion-label>
|
||||
<span>{{tx.message}}</span>
|
||||
</button>
|
||||
|
||||
<button ion-item (click)="addMemo()" *ngIf="tx.action != 'received'">
|
||||
<ion-label>{{'Fee' | translate}}</ion-label>
|
||||
<ion-note item-end>
|
||||
{{tx.fees || '...' | satToUnit: wallet.coin}} - {{tx.fees || '...' | satToFiat: wallet.coin}}
|
||||
<ion-note item-end *ngIf="btx.note">
|
||||
{{btx.note.body || btx.message}}
|
||||
</ion-note>
|
||||
</button>
|
||||
|
||||
<ion-item *ngIf="btx.action != 'received'">
|
||||
<ion-label>{{'Fee' | translate}}</ion-label>
|
||||
<span>{{btx.feeStr || '...'}}</span>
|
||||
<ion-note item-end>
|
||||
<span>
|
||||
{{btx.feeFiatStr || '...'}}
|
||||
<span class="fee-rate" *ngIf="btx.feeRateStr" translate>- {{btx.feeRateStr}} of the transaction</span>
|
||||
</span>
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="low-fees" *ngIf="btx.action == 'received' && btx.lowFees">
|
||||
<img src="assets/img/icon-warning.png" width="20">
|
||||
<span translate>This transaction could take a long time to confirm or could be dropped due to the low fees set by the sender</span>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="low-fees" *ngIf="btx.lowAmount">
|
||||
<img src="assets/img/icon-warning.png" width="20">
|
||||
<span translate>
|
||||
This transaction amount is too small compared to current Bitcoin network fees. Spending these funds will need a Bitcoin network
|
||||
fee cost comparable to the funds itself.
|
||||
</span>
|
||||
<a (click)="readMore()" translate>Learn more</a>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>{{'Confirmations' | translate}}</ion-label>
|
||||
<ion-note item-end>
|
||||
{{confirmations || 'Unconfirmed' | translate}}
|
||||
<span *ngIf="!btx.confirmations || btx.confirmations == 0" translate>
|
||||
Unconfirmed
|
||||
</span>
|
||||
<span *ngIf="btx.confirmations>0 && !btx.safeConfirmed">
|
||||
{{btx.confirmations}}
|
||||
</span>
|
||||
<span *ngIf="btx.safeConfirmed">
|
||||
{{btx.safeConfirmed}}
|
||||
</span>
|
||||
</ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngIf="txsUnsubscribedForNotifications">
|
||||
<ion-label>{{'Notify me if confirmed' | translate}}</ion-label>
|
||||
<ion-toggle checked="false" *ngIf="!btx.confirmations || btx.confirmations == 0" [(ngModel)]="txNotification.value" (ionChange)="txConfirmNotificationChange()"></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<div *ngIf="actionList && actionList[0]">
|
||||
<ion-item-divider color="light">{{'Timeline' | translate}}</ion-item-divider>
|
||||
<div class="timeline-item" [ngClass]="{'action-created' : a.type == 'created' || a.type == 'accept', 'action-rejected' : a.type == 'reject'}"
|
||||
*ngFor="let a of actionList; let i = index">
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-content-icon">
|
||||
<div class="rejected" *ngIf="a.type === 'reject'">!</div>
|
||||
<img src="assets/img/icon-broadcasted.svg" *ngIf="a.type === 'broadcasted'">
|
||||
<div *ngIf="a.type !== 'reject' && a.type !== 'broadcasted'">{{actionList.length - i}}</div>
|
||||
</div>
|
||||
<div class="timeline-content-label">
|
||||
<div class="action">{{a.description}}</div>
|
||||
<div class="name">{{a.by}}</div>
|
||||
</div>
|
||||
<ion-note>
|
||||
<time>{{ a.time * 1000 | amTimeAgo}}</time>
|
||||
</ion-note>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<button ion-button block clear (click)="viewOnBlockchain()" translate>View on blockchain</button>
|
||||
<button ion-button block clear *ngIf="btx" (click)="viewOnBlockchain()" translate>View on blockchain</button>
|
||||
</ion-content>
|
|
@ -1,21 +1,132 @@
|
|||
page-tx-details {
|
||||
.action {
|
||||
$item-border-color: #EFEFEF;
|
||||
$item-lateral-padding: 20px;
|
||||
$v-success-color: #1abb9b;
|
||||
$v-icon-border-radius: 3px;
|
||||
$item-label-color: #6C6C6E;
|
||||
|
||||
.sending-label {
|
||||
display: flex;
|
||||
height: 4rem;
|
||||
align-items: center;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
.sending-img {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
span {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
}
|
||||
img {
|
||||
margin-right: 15px;
|
||||
.amount-label {
|
||||
padding-top: 2rem;
|
||||
.amount {
|
||||
font-size: 3.5rem;
|
||||
.unit {
|
||||
font-weight: 100;
|
||||
}
|
||||
}
|
||||
.alternative {
|
||||
margin-top: 1rem;
|
||||
color: color($global-colors, light-grey);
|
||||
}
|
||||
}
|
||||
.amount-str {
|
||||
font-size: 30px;
|
||||
margin-bottom: .5rem;
|
||||
|
||||
.payment-proposal-to, .wallet {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
.icon-bitcoin {
|
||||
margin-right: 1rem;
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
}
|
||||
}
|
||||
.amount-alt-str {
|
||||
font-size: 16px;
|
||||
margin-left: 2px;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
.timeline-item {
|
||||
border: 0;
|
||||
&:nth-child(2) {
|
||||
border-top: 1px solid $item-border-color;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: 1px solid $item-border-color;
|
||||
}
|
||||
padding: 17px $item-lateral-padding;
|
||||
|
||||
> * {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 40px;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
margin-right: .5rem;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background: #e8e8e8;
|
||||
height: 100px;
|
||||
width: 1px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%) translateY(-100%);
|
||||
transform: translateX(-50%) translateY(-100%);
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background: #e8e8e8;
|
||||
height: 100px;
|
||||
width: 1px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%) translateY(100%);
|
||||
transform: translateX(-50%) translateY(100%);
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
> div {
|
||||
border: 3px solid $v-success-color;
|
||||
border-radius: $v-icon-border-radius;
|
||||
display: flex;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
vertical-align: middle;
|
||||
color: $v-success-color;
|
||||
z-index: 10;
|
||||
|
||||
&.rejected {
|
||||
background: #E15061;
|
||||
border-color: #E15061;
|
||||
color: #FFFFFF;
|
||||
font-family: "Roboto-Bold";
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-label {
|
||||
flex-grow: 1;
|
||||
|
||||
> .action {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
> .name {
|
||||
color: #aaa;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import { Component } from "@angular/core";
|
||||
import { NavParams } from 'ionic-angular';
|
||||
import { NavController, NavParams, Events } from 'ionic-angular';
|
||||
import { Logger } from '@nsalaun/ng-logger';
|
||||
import * as _ from "lodash";
|
||||
|
||||
// Providers
|
||||
import { AddressBookProvider } from '../../providers/address-book/address-book';
|
||||
import { ConfigProvider } from '../../providers/config/config';
|
||||
import { ExternalLinkProvider } from '../../providers/external-link/external-link';
|
||||
import { OnGoingProcessProvider } from "../../providers/on-going-process/on-going-process";
|
||||
import { PopupProvider } from '../../providers/popup/popup';
|
||||
import { ProfileProvider } from '../../providers/profile/profile';
|
||||
import { TxConfirmNotificationProvider } from '../../providers/tx-confirm-notification/tx-confirm-notification';
|
||||
import { TxFormatProvider } from '../../providers/tx-format/tx-format';
|
||||
import { WalletProvider } from '../../providers/wallet/wallet';
|
||||
|
||||
@Component({
|
||||
|
@ -13,49 +19,253 @@ import { WalletProvider } from '../../providers/wallet/wallet';
|
|||
templateUrl: 'tx-details.html'
|
||||
})
|
||||
export class TxDetailsPage {
|
||||
public title: string;
|
||||
private txId: string;
|
||||
private config: any;
|
||||
private blockexplorerUrl: string;
|
||||
|
||||
public wallet: any;
|
||||
public tx: any;
|
||||
public confirmations: string;
|
||||
public btx: any;
|
||||
public actionList: Array<any>;
|
||||
public isShared: boolean;
|
||||
public title: string;
|
||||
public alternativeIsoCode: string;
|
||||
public rateDate: any;
|
||||
public rate: any;
|
||||
public txNotification: any;
|
||||
public color: string;
|
||||
public copayerId: string;
|
||||
public txsUnsubscribedForNotifications: boolean;
|
||||
public toName: string;
|
||||
|
||||
constructor(
|
||||
private navParams: NavParams,
|
||||
private walletProvider: WalletProvider,
|
||||
private profileProvider: ProfileProvider,
|
||||
private addressBookProvider: AddressBookProvider,
|
||||
private configProvider: ConfigProvider,
|
||||
private events: Events,
|
||||
private externalLinkProvider: ExternalLinkProvider,
|
||||
private logger: Logger,
|
||||
private navCtrl: NavController,
|
||||
private navParams: NavParams,
|
||||
private onGoingProcess: OnGoingProcessProvider,
|
||||
private logger: Logger
|
||||
private popupProvider: PopupProvider,
|
||||
private profileProvider: ProfileProvider,
|
||||
private txConfirmNotificationProvider: TxConfirmNotificationProvider,
|
||||
private txFormatProvider: TxFormatProvider,
|
||||
private walletProvider: WalletProvider
|
||||
) {
|
||||
this.config = this.configProvider.get();
|
||||
|
||||
this.txId = this.navParams.data.txid;
|
||||
this.title = 'Transaction'; // Todo: gettextCatalog
|
||||
this.wallet = this.profileProvider.getWallet(this.navParams.data.walletId);
|
||||
this.tx = {};
|
||||
this.confirmations = null;
|
||||
this.color = this.wallet.color;
|
||||
this.copayerId = this.wallet.credentials.copayerId;
|
||||
this.isShared = this.wallet.credentials.n > 1;
|
||||
this.txsUnsubscribedForNotifications = this.config.confirmedTxsNotifications ? !this.config.confirmedTxsNotifications.enabled : true;
|
||||
|
||||
if (this.wallet.coin == 'bch') {
|
||||
this.blockexplorerUrl = 'bch-insight.bitpay.com';
|
||||
} else {
|
||||
this.blockexplorerUrl = 'insight.bitpay.com';
|
||||
}
|
||||
|
||||
this.txConfirmNotificationProvider.checkIfEnabled(this.txId).then((res: any) => {
|
||||
this.txNotification = {
|
||||
value: res
|
||||
};
|
||||
});
|
||||
|
||||
this.updateTx();
|
||||
}
|
||||
|
||||
ionViewDidLoad() {
|
||||
const txid = this.navParams.data.txid;
|
||||
ionViewWillEnter() {
|
||||
this.events.subscribe('bwsEvent', (walletId: string, type: string, n: any) => {
|
||||
if (type == 'NewBlock' && n && n.data && n.data.network == 'livenet') this.updateTxDebounced({ hideLoading: true });
|
||||
});
|
||||
}
|
||||
|
||||
this.onGoingProcess.set('loadingTxInfo', true);
|
||||
this.walletProvider.getTx(this.wallet, txid).then((tx) => {
|
||||
this.onGoingProcess.set('loadingTxInfo', false);
|
||||
this.tx = tx;
|
||||
if (this.tx.action == 'sent') this.title = 'Sent Funds';
|
||||
if (this.tx.action == 'received') this.title = 'Received Funds';
|
||||
if (this.tx.action == 'moved') this.title = 'Moved Funds';
|
||||
ionViewWillLeave() {
|
||||
this.events.unsubscribe('bwsEvent');
|
||||
}
|
||||
|
||||
if (this.tx.safeConfirmed) this.confirmations = this.tx.safeConfirmed;
|
||||
else if (this.tx.confirmations > 6) this.confirmations = '6+';
|
||||
}).catch((err) => {
|
||||
public readMore(): void {
|
||||
let url = 'https://github.com/bitpay/copay/wiki/COPAY---FAQ#amount-too-low-to-spend';
|
||||
let optIn = true;
|
||||
let title = null;
|
||||
let message = 'Read more in our Wiki'; // Todo: gettextCatalog
|
||||
let okText = 'Open'; // Todo: gettextCatalog
|
||||
let cancelText = 'Go Back'; // Todo: gettextCatalog
|
||||
this.externalLinkProvider.open(url, optIn, title, message, okText, cancelText);
|
||||
}
|
||||
|
||||
private updateMemo(): void {
|
||||
this.walletProvider.getTxNote(this.wallet, this.btx.txid).then((note: string) => {
|
||||
if (!note) return;
|
||||
this.btx.note = note;
|
||||
}).catch((err: any) => {
|
||||
this.logger.warn('Could not fetch transaction note: ' + err);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
private initActionList(): void {
|
||||
this.actionList = [];
|
||||
if (this.btx.action != 'sent' || !this.isShared) return;
|
||||
|
||||
var actionDescriptions = {
|
||||
created: 'Proposal Created', // Todo: gettextCatalog
|
||||
accept: 'Accepted', // Todo: gettextCatalog
|
||||
reject: 'Rejected', // Todo: gettextCatalog
|
||||
broadcasted: 'Broadcasted' // Todo: gettextCatalog
|
||||
};
|
||||
|
||||
this.actionList.push({
|
||||
type: 'created',
|
||||
time: this.btx.createdOn,
|
||||
description: actionDescriptions.created,
|
||||
by: this.btx.creatorName
|
||||
});
|
||||
|
||||
_.each(this.btx.actions, (action: any) => {
|
||||
this.actionList.push({
|
||||
type: action.type,
|
||||
time: action.createdOn,
|
||||
description: actionDescriptions[action.type],
|
||||
by: action.copayerName
|
||||
});
|
||||
});
|
||||
|
||||
this.actionList.push({
|
||||
type: 'broadcasted',
|
||||
time: this.btx.time,
|
||||
description: actionDescriptions.broadcasted,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.actionList.reverse();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
private updateTxDebounced = _.debounce(this.updateTx, 1000);
|
||||
|
||||
private updateTx(opts?: any): void {
|
||||
opts = opts ? opts : {};
|
||||
if (!opts.hideLoading) this.onGoingProcess.set('loadingTxInfo', true);
|
||||
this.walletProvider.getTx(this.wallet, this.txId).then((tx: any) => {
|
||||
if (!opts.hideLoading) this.onGoingProcess.set('loadingTxInfo', false);
|
||||
|
||||
this.btx = this.txFormatProvider.processTx(this.wallet.coin, tx);
|
||||
let v: string = this.txFormatProvider.formatAlternativeStr(this.wallet.coin, tx.fees);
|
||||
this.btx.feeFiatStr = v;
|
||||
this.btx.feeRateStr = (this.btx.fees / (this.btx.amount + this.btx.fees) * 100).toFixed(2) + '%';
|
||||
|
||||
if (this.btx.action != 'invalid') {
|
||||
if (this.btx.action == 'sent') this.title = 'Sent Funds'; // Todo: gettextCatalog
|
||||
if (this.btx.action == 'received') this.title = 'Received Funds'; // Todo: gettextCatalog
|
||||
if (this.btx.action == 'moved') this.title = 'Moved Funds'; // Todo: gettextCatalog
|
||||
}
|
||||
|
||||
this.updateMemo();
|
||||
this.initActionList();
|
||||
this.getFiatRate();
|
||||
this.contact();
|
||||
|
||||
this.walletProvider.getLowAmount(this.wallet).then((amount: number) => {
|
||||
this.btx.lowAmount = tx.amount < amount;
|
||||
}).catch((err: any) => {
|
||||
this.logger.warn('Error getting low amounts: ' + err);
|
||||
return;
|
||||
});
|
||||
}).catch((err: any) => {
|
||||
if (!opts.hideLoading) this.onGoingProcess.set('loadingTxInfo', false);
|
||||
this.logger.warn('Error getting transaction: ' + err);
|
||||
this.navCtrl.pop();
|
||||
return this.popupProvider.ionicAlert('Error', 'Transaction not available at this time'); // Todo: gettextCatalog
|
||||
});
|
||||
}
|
||||
|
||||
public showCommentPopup(): void {
|
||||
let opts: any = {};
|
||||
if (this.btx.message) {
|
||||
opts.defaultText = this.btx.message;
|
||||
}
|
||||
if (this.btx.note && this.btx.note.body) opts.defaultText = this.btx.note.body;
|
||||
|
||||
this.popupProvider.ionicPrompt(this.wallet.name, 'Memo', opts).then((text: string) => { // Todo: gettextCatalog
|
||||
if (typeof text == "undefined") return;
|
||||
|
||||
this.btx.note = {
|
||||
body: text
|
||||
};
|
||||
this.logger.debug('Saving memo');
|
||||
|
||||
let args = {
|
||||
txid: this.btx.txid,
|
||||
body: text
|
||||
};
|
||||
|
||||
this.walletProvider.editTxNote(this.wallet, args).then((res: any) => {
|
||||
this.logger.info('Tx Note edited: ', res);
|
||||
}).catch((err: any) => {
|
||||
this.logger.debug('Could not save tx comment ' + err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public viewOnBlockchain(): void {
|
||||
let btx = this.btx;
|
||||
let url = 'https://' + (this.getShortNetworkName() == 'test' ? 'test-' : '') + this.blockexplorerUrl + '/tx/' + btx.txid;
|
||||
let optIn = true;
|
||||
let title = null;
|
||||
let message = 'View Transaction on Insight'; // Todo: gettextCatalog
|
||||
let okText = 'Open Insight'; // Todo: gettextCatalog
|
||||
let cancelText = 'Go Back'; // Todo: gettextCatalog
|
||||
this.externalLinkProvider.open(url, optIn, title, message, okText, cancelText);
|
||||
}
|
||||
|
||||
public getShortNetworkName(): string {
|
||||
let n: string = this.wallet.credentials.network;
|
||||
return n.substring(0, 4);
|
||||
}
|
||||
|
||||
private getFiatRate(): void {
|
||||
this.alternativeIsoCode = this.wallet.status.alternativeIsoCode;
|
||||
this.wallet.getFiatRate({
|
||||
code: this.alternativeIsoCode,
|
||||
ts: this.btx.time * 1000
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
this.logger.debug('Could not get historic rate');
|
||||
return;
|
||||
}
|
||||
if (res && res.rate) {
|
||||
this.rateDate = res.fetchedOn;
|
||||
this.rate = res.rate;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public txConfirmNotificationChange(): void {
|
||||
if (this.txNotification.value) {
|
||||
this.txConfirmNotificationProvider.subscribe(this.wallet, {
|
||||
txid: this.txId
|
||||
});
|
||||
} else {
|
||||
this.txConfirmNotificationProvider.unsubscribe(this.wallet, this.txId);
|
||||
}
|
||||
}
|
||||
|
||||
private contact(): void {
|
||||
let addr = this.btx.addressTo;
|
||||
this.addressBookProvider.get(addr).then((ab: any) => {
|
||||
if (ab) {
|
||||
let name = _.isObject(ab) ? ab.name : ab;
|
||||
this.toName = name;
|
||||
} else {
|
||||
this.toName = addr;
|
||||
}
|
||||
}).catch((err: any) => {
|
||||
this.logger.warn(err);
|
||||
});
|
||||
}
|
||||
|
||||
addMemo() {
|
||||
return;
|
||||
}
|
||||
|
||||
viewOnBlockchain() {
|
||||
const prefix = this.wallet.coin === 'bch' ? 'bch-' : this.wallet.network === 'testnet' ? 'test-' : '';
|
||||
const url = 'https://' + prefix + 'insight.bitpay.com/tx/' + this.tx.txid;
|
||||
this.externalLinkProvider.open(url);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -677,10 +677,11 @@ export class WalletProvider {
|
|||
// Approx utxo amount, from which the uxto is economically redeemable
|
||||
public getMinFee(wallet: any, nbOutputs?: number): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.feeProvider.getFeeLevels(wallet.coin).then((data) => {
|
||||
let lowLevelRate = (lodash.find(data.levels[wallet.network], {
|
||||
level: 'normal',
|
||||
}).feePerKb / 1000).toFixed(0);
|
||||
this.feeProvider.getFeeLevels(wallet.coin).then((data: any) => {
|
||||
let normalLevelRate: any = lodash.find(data.levels[wallet.network], (level: any) => {
|
||||
return level.level === 'normal';
|
||||
});
|
||||
let lowLevelRate: string = (normalLevelRate.feePerKb / 1000).toFixed(0);
|
||||
let size = this.getEstimatedTxSize(wallet, nbOutputs);
|
||||
return resolve(size * parseInt(lowLevelRate));
|
||||
}).catch((err) => {
|
||||
|
|
|
@ -120,6 +120,14 @@ $global-colors: (
|
|||
}
|
||||
}
|
||||
|
||||
.gravatar {
|
||||
min-width: 3rem;
|
||||
min-height: 3rem;
|
||||
}
|
||||
|
||||
|
||||
// Utilities
|
||||
|
||||
.assertive, a.assertive {
|
||||
color: color($global-colors, assertive);
|
||||
}
|
||||
|
@ -132,31 +140,13 @@ $global-colors: (
|
|||
font-weight: 700;
|
||||
}
|
||||
|
||||
.gravatar {
|
||||
min-width: 3rem;
|
||||
min-height: 3rem;
|
||||
.ellipsis {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wallet selector
|
||||
*/
|
||||
|
||||
@mixin wallets-list {
|
||||
height: 3.5rem;
|
||||
width: 3.5rem;
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
border-radius: $icon-border-radius;
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wallet-livenet::before {
|
||||
@include wallets-list;
|
||||
background: color($colors, primary) url('../assets/img/icon-wallet.svg') no-repeat 0px 0px;
|
||||
}
|
||||
|
||||
.wallet-testnet::before {
|
||||
@include wallets-list;
|
||||
background: color($colors, primary) url('../assets/img/icon-wallet-testnet.svg') no-repeat 0px 0px;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue