diff --git a/package-lock.json b/package-lock.json index dffd11a27..7ee716777 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "kukai", - "version": "0.1.0", + "version": "1.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0ea557f75..081376263 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kukai", - "version": "1.0.3", + "version": "1.1.0", "license": "MIT", "scripts": { "ng": "ng", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1f32d87f8..e7513fa7c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,7 +13,6 @@ import { ModalModule, AlertModule, ProgressbarModule, ButtonsModule, BsDropdownM import { AppComponent } from './app.component'; // Services -import { MessagesComponent } from './components/messages/messages.component'; import { MessageService } from './services/message.service'; import { WalletService } from './services/wallet.service'; import { ActivityService } from './services/activity.service'; @@ -45,18 +44,21 @@ import { CoordinatorService } from './services/coordinator.service'; import { OperationService } from './services/operation.service'; import { BakeryComponent } from './components/bakery/bakery.component'; import { ActivateComponent } from './components/activate/activate.component'; +import { MessagesComponent } from './components/messages/messages.component'; // Empty // Pipes import { ErrorHandlingPipe } from './pipes/error-handling.pipe'; import { DelegatorNamePipe } from './pipes/delegator-name.pipe'; - +import { TruncatePipe } from './pipes/truncate.pipe'; +import { TimeAgoPipe } from './pipes/time-ago.pipe'; @NgModule({ declarations: [ + AppComponent, + + // View components HomePageComponent, NewWalletComponent, - MessagesComponent, - AppComponent, OfflineSigningComponent, ImportComponent, StartComponent, @@ -71,8 +73,13 @@ import { DelegatorNamePipe } from './pipes/delegator-name.pipe'; MnemonicImportComponent, BakeryComponent, ActivateComponent, + MessagesComponent, // Empty + + // Pipes ErrorHandlingPipe, - DelegatorNamePipe + DelegatorNamePipe, + TruncatePipe, + TimeAgoPipe ], imports: [ BrowserModule, @@ -88,6 +95,7 @@ import { DelegatorNamePipe } from './pipes/delegator-name.pipe'; TabsModule.forRoot() ], providers: [ + // Services MessageService, WalletService, ActivityService, @@ -103,8 +111,12 @@ import { DelegatorNamePipe } from './pipes/delegator-name.pipe'; ExportService, DelegateService, TzscanService, + + // Pipes ErrorHandlingPipe, - DelegatorNamePipe + DelegatorNamePipe, + TruncatePipe, + TimeAgoPipe ], bootstrap: [AppComponent] }) diff --git a/src/app/components/account/account.component.html b/src/app/components/account/account.component.html index ec8448a08..7733d80fe 100644 --- a/src/app/components/account/account.component.html +++ b/src/app/components/account/account.component.html @@ -17,10 +17,12 @@

Account

(${{ balanceUSD | number:'1.00'}} USD) +
+
diff --git a/src/app/components/account/account.component.scss b/src/app/components/account/account.component.scss index a6d0a4dc9..75f887a29 100644 --- a/src/app/components/account/account.component.scss +++ b/src/app/components/account/account.component.scss @@ -23,7 +23,7 @@ } .min-width { - min-width: 394px !important; + min-width: 825px !important; } /* Responsive breakpoints */ diff --git a/src/app/components/account/account.component.ts b/src/app/components/account/account.component.ts index db722f736..e4c6b1e4a 100644 --- a/src/app/components/account/account.component.ts +++ b/src/app/components/account/account.component.ts @@ -1,7 +1,6 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; -import { BalanceService } from '../../services/balance.service'; + import { CoordinatorService } from '../../services/coordinator.service'; @Component({ @@ -16,7 +15,6 @@ export class AccountComponent implements OnInit { activePkh: string; constructor( public walletService: WalletService, - private messageService: MessageService, private coordinatorService: CoordinatorService ) { } diff --git a/src/app/components/activate/activate.component.ts b/src/app/components/activate/activate.component.ts index 610412922..00e958993 100644 --- a/src/app/components/activate/activate.component.ts +++ b/src/app/components/activate/activate.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; + import { MessageService } from '../../services/message.service'; import { OperationService } from '../../services/operation.service'; diff --git a/src/app/components/activity/activity.component.html b/src/app/components/activity/activity.component.html index d5058511d..ccd2d0c59 100644 --- a/src/app/components/activity/activity.component.html +++ b/src/app/components/activity/activity.component.html @@ -1,36 +1,81 @@
- - - - - - - - - - - - + + +
TimestampTypeAmount (ꜩ)Status
+ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - +
{{ transaction.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}{{ transaction.type }} - {{ transaction.amount / 1000000 | number:'1.0' }} ꜩ + Txn HashBlockDateTypeCounterparty Amount (ꜩ)Status
+ + {{ transaction.hash | truncate: '4':'true'}} + + + + {{ transaction.block | truncate: '4':'true'}} + + + + + {{ transaction.timestamp | timeAgo }} + + + {{ getType(transaction) }} + + + + From + from + to + +
+ + {{ getCounterparty(transaction) }} + +
+ + {{ transaction.amount / 1000000 | number:'1.0' }} ꜩ - {{ getStatus(transaction) }} + + + + {{ getStatus(transaction) }}
- +

Wallet not configured! -

\ No newline at end of file +

+ + \ No newline at end of file diff --git a/src/app/components/activity/activity.component.scss b/src/app/components/activity/activity.component.scss index d3d52363b..022214e48 100644 --- a/src/app/components/activity/activity.component.scss +++ b/src/app/components/activity/activity.component.scss @@ -12,6 +12,40 @@ .mono { font-family: "Courier New", Courier, monospace; } + +.table-row { + line-height: 1.2; //original at 1.5 +} +.table-cell { + vertical-align: middle; + padding: 10px; +} + +.row-hash { + font-weight: normal; +} +.transaction { + //font-weight: bold; +} + +.counterparty { + //word-wrap: break-word; +} + +.received { + background-color: #5cb85c; // rgb(0, 110, 9); +} + +.sent { + background-color: rgb(221, 21, 21); +} + +.amount { + text-align: right; +} +.smallText { + font-size: 80%; +} @media(min-width:768px) { #page-wrapper { position: inherit; diff --git a/src/app/components/activity/activity.component.ts b/src/app/components/activity/activity.component.ts index 22b1b7800..c016de234 100644 --- a/src/app/components/activity/activity.component.ts +++ b/src/app/components/activity/activity.component.ts @@ -1,31 +1,64 @@ import { Component, Input, OnInit, AfterViewInit, SimpleChange } from '@angular/core'; import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; +import { Constants } from '../../constants'; @Component({ - selector: 'app-activity', - templateUrl: './activity.component.html', - styleUrls: ['./activity.component.scss'] + selector: 'app-activity', + templateUrl: './activity.component.html', + styleUrls: ['./activity.component.scss'] }) export class ActivityComponent implements OnInit { - accounts = null; - @Input() activePkh: string; - constructor( - private walletService: WalletService, - private messageService: MessageService - ) { } + accounts = null; + CONSTANTS = new Constants(); + @Input() activePkh: string; + constructor( + private walletService: WalletService + ) {} - ngOnInit() { if (this.walletService.wallet) { this.init(); } } - init() { - this.accounts = this.walletService.wallet.accounts; - } - getStatus(transaction: any): string { - if (transaction.failed) { - return 'Failed'; - } else if (transaction.block === 'prevalidation') { - return 'Unconfirmed'; - } else { - return 'Confirmed'; + ngOnInit() { if (this.walletService.wallet) { this.init(); } } + init() { + this.accounts = this.walletService.wallet.accounts; + console.log('transaction', this.accounts[0].activities); + } + getStatus(transaction: any): string { + if (transaction.failed) { + return 'Failed'; + } else if (transaction.block === 'prevalidation') { + return 'Unconfirmed'; + } else { + return 'Confirmed'; + } + } + + getType(transaction: any): string { + if (transaction.type !== 'transaction') { + return transaction.type; + } else { + let operationType = ''; + if (transaction.amount > 0) { + operationType = 'received'; + } else { + operationType = 'sent'; + } + return operationType; + } + } + + getCounterparty(transaction: any): string { + console.log('transaction - getCounterparty', transaction); + let counterparty = ''; + + // Checks for delegation as destination is stored in transaction.destination.tz + if (transaction.type === 'delegation') { + return transaction.destination.tz; + } + + if (this.activePkh === transaction.source) { + counterparty = transaction.destination; // to + } else { + counterparty = transaction.source; // from + } + + return counterparty; } - } } diff --git a/src/app/components/backup/backup.component.html b/src/app/components/backup/backup.component.html index 956a63b9b..e83aeb7ba 100644 --- a/src/app/components/backup/backup.component.html +++ b/src/app/components/backup/backup.component.html @@ -44,7 +44,7 @@

2. Export View-only Wallet to file

- + {{ pk }} diff --git a/src/app/components/backup/backup.component.ts b/src/app/components/backup/backup.component.ts index 7ec91a51f..f594d0ced 100644 --- a/src/app/components/backup/backup.component.ts +++ b/src/app/components/backup/backup.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, OnDestroy, Input } from '@angular/core'; + import { WalletService } from '../../services/wallet.service'; import { ExportService } from '../../services/export.service'; import { MessageService } from '../../services/message.service'; diff --git a/src/app/components/bakery/bakery.component.ts b/src/app/components/bakery/bakery.component.ts index 0aa2bce49..2439cfc64 100644 --- a/src/app/components/bakery/bakery.component.ts +++ b/src/app/components/bakery/bakery.component.ts @@ -1,8 +1,9 @@ import { Component, OnInit } from '@angular/core'; + import { WalletService } from '../../services/wallet.service'; import { MessageService } from '../../services/message.service'; import { ActivityService } from '../../services/activity.service'; -import { Account, Balance, Activity } from '../../interfaces'; + import { DelegatorNamePipe } from '../../pipes/delegator-name.pipe'; diff --git a/src/app/components/delegate/delegate.component.html b/src/app/components/delegate/delegate.component.html index 1f2cb7a36..b21128dc9 100644 --- a/src/app/components/delegate/delegate.component.html +++ b/src/app/components/delegate/delegate.component.html @@ -33,7 +33,7 @@ {{ formInvalid }}
@@ -57,8 +57,10 @@
- {{ pwdValid }} + @@ -80,10 +82,10 @@

-

Your funding of a new account have successfully been broadcasted to the network

+

Your delegation operation has been broadcasted successfully to the network

-

Your unsigned transaction have successfully been created

+

Your unsigned transaction have been created successfully

@@ -92,7 +94,9 @@
- + diff --git a/src/app/components/delegate/delegate.component.scss b/src/app/components/delegate/delegate.component.scss index e69de29bb..5c5e779a0 100644 --- a/src/app/components/delegate/delegate.component.scss +++ b/src/app/components/delegate/delegate.component.scss @@ -0,0 +1,8 @@ +.modal-footer { + padding-bottom: 0rem; +} + +.fa-check-circle { + color: #28a745; //#5cb85c; + font-size: 96px; +} \ No newline at end of file diff --git a/src/app/components/delegate/delegate.component.ts b/src/app/components/delegate/delegate.component.ts index b56faba50..d96a53721 100644 --- a/src/app/components/delegate/delegate.component.ts +++ b/src/app/components/delegate/delegate.component.ts @@ -1,12 +1,11 @@ -import { Component, TemplateRef, OnInit, ViewEncapsulation, Input, ViewChild, ElementRef } from '@angular/core'; +import { Component, TemplateRef, OnInit, Input, ViewChild } from '@angular/core'; +import { BsModalService } from 'ngx-bootstrap/modal'; +import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; +import { KeyPair } from '../../interfaces'; import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; import { CoordinatorService } from '../../services/coordinator.service'; import { OperationService } from '../../services/operation.service'; -import { BsModalService } from 'ngx-bootstrap/modal'; -import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; -import { KeyPair } from '../../interfaces'; import { ExportService } from '../../services/export.service'; @Component({ @@ -35,7 +34,6 @@ export class DelegateComponent implements OnInit { constructor( private modalService: BsModalService, private walletService: WalletService, - private messageService: MessageService, private operationService: OperationService, private coordinatorService: CoordinatorService, private exportService: ExportService diff --git a/src/app/components/home-page/home-page.component.html b/src/app/components/home-page/home-page.component.html index 279e88394..836c544df 100644 --- a/src/app/components/home-page/home-page.component.html +++ b/src/app/components/home-page/home-page.component.html @@ -7,8 +7,8 @@

on a path of self-discovery -
{{ this.walletService.walletTypePrint() }} - [ Betanet ] +
{{ this.walletService.walletTypePrint() }} +
[ {{ CONSTANTS.NET.NAME }} ]


diff --git a/src/app/components/home-page/home-page.component.scss b/src/app/components/home-page/home-page.component.scss index 93ecc0758..3560e516a 100644 --- a/src/app/components/home-page/home-page.component.scss +++ b/src/app/components/home-page/home-page.component.scss @@ -8,11 +8,20 @@ body { font-family: 'Poppins', sans-serif; background: #fafafa; } -.red { +.zeronet { + color: rgb(230, 0, 0); + font-weight: bold; +} + +.betanet { color: rgb(0, 110, 9); font-weight: bold; } +.white { + color: #fafafa; +} + p { font-family: 'Poppins', sans-serif; font-size: 1.1em; @@ -90,6 +99,7 @@ a, a:hover, a:focus { #sidebar .sub-heading { font-size: 0.9em; color: #000 !important; + margin-bottom: 0px; } #sidebar ul.components { @@ -99,7 +109,7 @@ a, a:hover, a:focus { #sidebar ul p { color: #fff; - padding: 10px; + padding: 7x; } #sidebar ul li a { diff --git a/src/app/components/home-page/home-page.component.ts b/src/app/components/home-page/home-page.component.ts index 650816106..e6fde67e9 100644 --- a/src/app/components/home-page/home-page.component.ts +++ b/src/app/components/home-page/home-page.component.ts @@ -1,9 +1,10 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; -import { ActivityService } from '../../services/activity.service'; import { CoordinatorService } from '../../services/coordinator.service'; -import { Router } from '@angular/router'; + +import { Constants } from '../../constants'; @Component({ selector: 'app-home-page', @@ -11,11 +12,15 @@ import { Router } from '@angular/router'; styleUrls: ['./home-page.component.scss'] }) export class HomePageComponent implements OnInit { - constructor(public walletService: WalletService, - private messageService: MessageService, - private activityService: ActivityService, - private coordinatorService: CoordinatorService, - private router: Router) { } + isCollapsed = false; + + CONSTANTS = new Constants(); + + constructor( + private router: Router, + public walletService: WalletService, + private coordinatorService: CoordinatorService + ) { } ngOnInit() { } diff --git a/src/app/components/import/import.component.html b/src/app/components/import/import.component.html index 7c7aa1d1b..8ad99e6ff 100644 --- a/src/app/components/import/import.component.html +++ b/src/app/components/import/import.component.html @@ -53,9 +53,7 @@

2. Import wallet from Seed or ICO credentials

3. Observer wallet

-

- Track a wallet from its public key hash -

+

Track a wallet from its public key hash

diff --git a/src/app/components/import/import.component.ts b/src/app/components/import/import.component.ts index 2e418b4e2..412b21ea2 100644 --- a/src/app/components/import/import.component.ts +++ b/src/app/components/import/import.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, Input } from '@angular/core'; -import { WalletService } from '../../services/wallet.service'; import { Router } from '@angular/router'; + +import { WalletService } from '../../services/wallet.service'; import { MessageService } from '../../services/message.service'; import { ImportService } from '../../services/import.service'; @@ -36,7 +37,7 @@ export class ImportComponent implements OnInit { this.importService.importWalletFromPkh(this.pkh); this.router.navigate(['/overview']); } else { - this.messageService.addError('Invalid public key hash'); + this.messageService.addError('Invalid public key hash!'); } } importFromPk() { diff --git a/src/app/components/messages/messages.component.ts b/src/app/components/messages/messages.component.ts index fd712efd7..725ad5c91 100644 --- a/src/app/components/messages/messages.component.ts +++ b/src/app/components/messages/messages.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; + import { MessageService } from '../../services/message.service'; @Component({ diff --git a/src/app/components/mnemonic-import/mnemonic-import.component.html b/src/app/components/mnemonic-import/mnemonic-import.component.html index c3841221e..963d48fb4 100644 --- a/src/app/components/mnemonic-import/mnemonic-import.component.html +++ b/src/app/components/mnemonic-import/mnemonic-import.component.html @@ -30,7 +30,7 @@

Retrieve your Wallet

- If your mnemonic phrase is protected with a passphrase, it need to be entered here + If your mnemonic phrase is protected with a passphrase, it needs to be entered here
@@ -109,7 +109,7 @@

Wallet created


Your wallet is now set up and ready to use.

-

Please download your Encrypted Keystore File..

+

Please download your Encrypted Keystore File.

diff --git a/src/app/components/mnemonic-import/mnemonic-import.component.ts b/src/app/components/mnemonic-import/mnemonic-import.component.ts index fd9d20245..80675f394 100644 --- a/src/app/components/mnemonic-import/mnemonic-import.component.ts +++ b/src/app/components/mnemonic-import/mnemonic-import.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { ImportService } from '../../services/import.service'; import { Router } from '@angular/router'; + +import { ImportService } from '../../services/import.service'; import { MessageService } from '../../services/message.service'; import { OperationService } from '../../services/operation.service'; import { WalletService } from '../../services/wallet.service'; diff --git a/src/app/components/new-account/new-account.component.html b/src/app/components/new-account/new-account.component.html index 00ead9e5d..74acdf214 100644 --- a/src/app/components/new-account/new-account.component.html +++ b/src/app/components/new-account/new-account.component.html @@ -9,22 +9,25 @@
@@ -56,9 +59,12 @@
- + + @@ -79,19 +85,22 @@

-

Your funding of a new account have successfully been broadcasted to the network

+

Your funding of a new account has been broadcasted successfully to the network

-

Your unsigned transaction have successfully been created

+

Your unsigned transaction has been created successfully

Operation failed!

{{ sendResponse.payload.msg }}

- - + + \ No newline at end of file diff --git a/src/app/components/new-account/new-account.component.ts b/src/app/components/new-account/new-account.component.ts index b79b53126..af6fcef31 100644 --- a/src/app/components/new-account/new-account.component.ts +++ b/src/app/components/new-account/new-account.component.ts @@ -1,6 +1,6 @@ -import { Component, OnInit, ElementRef, ViewChild, TemplateRef, Input } from '@angular/core'; +import { Component, OnInit, ViewChild, TemplateRef, Input } from '@angular/core'; + import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; import { BsModalService } from 'ngx-bootstrap/modal'; import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; import { KeyPair } from '../../interfaces'; @@ -34,7 +34,6 @@ export class NewAccountComponent implements OnInit { constructor( private walletService: WalletService, - private messageService: MessageService, private modalService: BsModalService, private operationService: OperationService, private coordinatorService: CoordinatorService, @@ -53,7 +52,7 @@ export class NewAccountComponent implements OnInit { open1(template1: TemplateRef) { this.clearForm(); - this.modalRef1 = this.modalService.show(template1, { class: 'modal-sm' }); + this.modalRef1 = this.modalService.show(template1, { class: 'first' }); } open2(template: TemplateRef) { @@ -131,9 +130,9 @@ export class NewAccountComponent implements OnInit { invalidInput(): string { if (!Number(this.amount) && this.amount && this.amount !== '0') { - return 'invalid amount'; + return 'Invalid amount'; } else if (!Number(this.fee) && this.fee && this.fee !== '0') { - return 'invalid fee'; + return 'Invalid fee'; } else { return ''; } diff --git a/src/app/components/new-wallet/new-wallet.component.html b/src/app/components/new-wallet/new-wallet.component.html index b5aa66ae6..e5ee62f81 100644 --- a/src/app/components/new-wallet/new-wallet.component.html +++ b/src/app/components/new-wallet/new-wallet.component.html @@ -31,9 +31,9 @@

Save your Seed

When you create a new wallet, a new Tezos seed is generated. Your Tezos seed is the master key of all your wallet and any value inside it.

Make sure to back it up, write it down, and keep it incredibly safe!

-

**Do not lose it!** It cannot be recovered if you lose it.

-

**Do not share it!** Your funds will be stolen if you use the seed on a malicious/phishing site.

-

**Make a backup!** Secure it like the millions it may be worth one day.

+

**Do not lose it!** It cannot be recovered if you lose it.

+

**Do not share it!** Your funds will be stolen if you use the seed on a malicious/phishing site.

+

**Make a backup!** Secure it like the millions it may be worth one day.

@@ -99,7 +99,7 @@

Wallet created

Your public key hash: {{ getPkh() }}


Your wallet is now set up and ready to use.

-

Please download your Encrypted Keystore File..

+

Please download your Encrypted Keystore File.

diff --git a/src/app/components/new-wallet/new-wallet.component.ts b/src/app/components/new-wallet/new-wallet.component.ts index fd688a2d8..f1e541eae 100644 --- a/src/app/components/new-wallet/new-wallet.component.ts +++ b/src/app/components/new-wallet/new-wallet.component.ts @@ -1,13 +1,14 @@ -import { Component, OnInit, Input, Query, HostListener } from '@angular/core'; -import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; -import { ChangeDetectorRef } from '@angular/core'; +import { Component, OnInit, Input, HostListener } from '@angular/core'; + import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/fromEvent'; -import * as bip39 from 'bip39'; +import * as rnd from 'randomatic'; + +import { WalletService } from '../../services/wallet.service'; +import { MessageService } from '../../services/message.service'; import { ExportService } from '../../services/export.service'; import { ImportService } from '../../services/import.service'; -import * as rnd from 'randomatic'; + @Component({ selector: 'app-new-wallet', @@ -63,11 +64,12 @@ export class NewWalletComponent implements OnInit { console.log('Out of range'); } } - constructor(private walletService: WalletService, + constructor( + private walletService: WalletService, private messageService: MessageService, private exportService: ExportService, - private importService: ImportService, - private cdRef: ChangeDetectorRef) { } + private importService: ImportService + ) { } ngOnInit() { // rnd() only to create an offset for better visualisation. diff --git a/src/app/components/offline-signing/offline-signing.component.html b/src/app/components/offline-signing/offline-signing.component.html index 4f62ceec5..53bb37fdb 100644 --- a/src/app/components/offline-signing/offline-signing.component.html +++ b/src/app/components/offline-signing/offline-signing.component.html @@ -10,13 +10,24 @@

Offline signing

-

In 3 easy steps, you can broadcast an operation to the tezos network without ever needing to use your secret - key in an online environment. Wallet client that can be used offline is found on our GitHub. - Make sure you have exported a view-only wallet from the backup view in - your full wallet. The full wallet should be stored on an offline system and the view-only wallet on an online - system. In the view-only wallet proceed with the operation (step 1). This will create an unsigned operation - that will be exported to a file. Move the unsigned operation to your full wallet and sign it (step 2). The - signed operation can now be exported and transfered back to an online system and broadcasted (step 3).

+ +

In 3 easy steps, you can broadcast an operation to the tezos network without ever exposing your seed + to an online environment. Desktop client for the offline signature can be found on our GitHub. +

+
+
    +
  1. + The full wallet should be created in an offline system. Use that wallet to export a View-only wallet, which does not contain your private key. + This wallet can be safely used on an online system to create unsigned operations. +
  2. +
  3. + From your View-only wallet, you can create and download an unsigned operation file ('.tzop'). Import this unsigned operation to your full wallet, + verify its content and then enter your wallet password to sign the operation. You can download the new signed operation in another '.tzop' file. +
  4. +
  5. The signed operation can now be transferred back to an online system. Verify its content and then broadcast it to the Tezos network.
  6. +
+
+

@@ -30,7 +41,7 @@

1. Unsigned operation (online)

You can create an unsigned operation with your view-only wallet in the - Overview.

+ Overview section.


@@ -54,7 +65,7 @@

2. Sign operation (offline)

Operation
- +
@@ -64,15 +75,20 @@

2. Sign operation (offline)

-
-
-
{{ decodeOutput }}
+ + Wallet appear to be online. Are you really sure you want to proceed with the signature process? + + +
+
{{ decodeUnsignedOutput }}
+
3. Broadcast (online)

Import or paste your signed operation and transmit your operation to the Tezos network

+
-
Operation @@ -111,6 +127,14 @@

3. Broadcast (online)

+ + Would you like to decode the operation? + + +
+
{{ decodeSignedOutput }}
+
+
diff --git a/src/app/components/offline-signing/offline-signing.component.scss b/src/app/components/offline-signing/offline-signing.component.scss index 38825b1a7..5300a8fa0 100644 --- a/src/app/components/offline-signing/offline-signing.component.scss +++ b/src/app/components/offline-signing/offline-signing.component.scss @@ -13,6 +13,18 @@ h4 { font-weight: 400; } +.help-steps { + font-weight: 400; + padding-top: 0.4rem; + padding-bottom: 0.4rem; + padding-right: 1.5rem; + padding-left: 1.5rem; +} + +.help-steps > ol > li { + padding-top: 0.4rem; +} + .min-size-prepend { min-width: 98px; } @@ -47,4 +59,5 @@ textarea:disabled { border-color: red; width: 625px; padding-left: 20px; + background-color: #eeeeee; } \ No newline at end of file diff --git a/src/app/components/offline-signing/offline-signing.component.ts b/src/app/components/offline-signing/offline-signing.component.ts index 7e597d084..17a24b0fd 100644 --- a/src/app/components/offline-signing/offline-signing.component.ts +++ b/src/app/components/offline-signing/offline-signing.component.ts @@ -1,4 +1,5 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; + import { WalletService } from '../../services/wallet.service'; import { OperationService } from '../../services/operation.service'; import { MessageService } from '../../services/message.service'; @@ -15,17 +16,19 @@ export class OfflineSigningComponent implements OnInit { InputImportOperationFileStep2 = 'Choose file'; InputImportOperationFileStep3 = 'Choose file'; - unsigned = ''; + unsigned = ''; // Will contain the unsigned hash signed1 = ''; signed2 = ''; signedOp = ''; pwd = ''; pwdPlaceholder = ''; - decodeOutput = ''; + decodeUnsignedOutput = ''; + decodeSignedOutput = ''; errorMessage = ''; showInstructions = false; instructionBtn = 'Show help'; isFullWallet = false; + notAllowOnlineSigning = true; constructor( public walletService: WalletService, @@ -56,19 +59,41 @@ export class OfflineSigningComponent implements OnInit { } decodeUnsignedOp() { if (!this.unsigned) { - this.decodeOutput = ''; + this.decodeUnsignedOutput = ''; console.log('don\'t decode'); } else { console.log('decode...'); try { const op = this.operationService.decodeOpBytes(this.unsigned); - this.decodeOutput = '\n### PLEASE VERIFY THIS DATA ARE CORRECT BEFORE SIGNING ###\n'; + this.decodeUnsignedOutput = '\n### PLEASE VERIFY THIS DATA IS CORRECT BEFORE SIGNING IT ###\n'; const output = this.operationService.fop2strings(op); + for (let i = 0; i < output.length; i++) { - this.decodeOutput = this.decodeOutput + '\n' + output[i]; + this.decodeUnsignedOutput = this.decodeUnsignedOutput + '\n' + output[i]; } + this.decodeUnsignedOutput = this.decodeUnsignedOutput + '\n' + '\n'; } catch (e) { - this.decodeOutput = '\n### FAILED TO DECODE OPERATION BYTES! YOU ARE ADVICED TO NOT PROCEED ###\n'; + this.decodeUnsignedOutput = '\n### FAILED TO DECODE OPERATION BYTES! YOU ARE ADVISED TO NOT PROCEED ###\n'; + } + } + } + decodeSignedOp() { + if (!this.signed2) { + this.decodeSignedOutput = ''; + console.log('don\'t decode'); + } else { + console.log('decode...'); + try { + const op = this.operationService.decodeOpBytes(this.signed2.slice(0, this.signed2.length - 128)); + this.decodeSignedOutput = '\n### PLEASE VERIFY THIS DATA IS CORRECT BEFORE BROADCASTING IT ###\n'; + const output = this.operationService.fop2strings(op); + + for (let i = 0; i < output.length; i++) { + this.decodeSignedOutput = this.decodeSignedOutput + '\n' + output[i]; + } + this.decodeSignedOutput = this.decodeSignedOutput + '\n' + '\n'; + } catch (e) { + this.decodeSignedOutput = '\n### FAILED TO DECODE OPERATION BYTES! YOU ARE ADVICED TO NOT PROCEED ###\n'; } } } @@ -78,8 +103,11 @@ export class OfflineSigningComponent implements OnInit { } return (this.walletService.wallet.XTZrate !== null || this.walletService.wallet.balance.balanceXTZ !== null); } + allowToSignInOnlineWallet() { + this.notAllowOnlineSigning = false; + } notAllowedToSign(): boolean { - return (!this.isFullWallet || this.isOnline()); + return (!this.isFullWallet || (this.isOnline() && this.notAllowOnlineSigning)); } broadcast() { if (this.signed2) { @@ -127,7 +155,7 @@ export class OfflineSigningComponent implements OnInit { if (data.signed === true && data.hex) { this.signed2 = data.hex; } else { - this.messageService.addWarning('Not an unsigned operation!'); + this.messageService.addWarning('Not a signed operation!'); } } else { this.messageService.addError('Failed to read file!'); diff --git a/src/app/components/overview/overview.component.scss b/src/app/components/overview/overview.component.scss index 29096d5b3..5f3c4c860 100644 --- a/src/app/components/overview/overview.component.scss +++ b/src/app/components/overview/overview.component.scss @@ -52,7 +52,7 @@ } .min-size-address-cell { - min-width: 388px; + min-width: 392px; } .min-size-other-cell { diff --git a/src/app/components/overview/overview.component.ts b/src/app/components/overview/overview.component.ts index e0485ff61..eb2559127 100644 --- a/src/app/components/overview/overview.component.ts +++ b/src/app/components/overview/overview.component.ts @@ -1,13 +1,11 @@ -import { Component, OnInit, Input, Inject, Injectable } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; // import { DOCUMENT } from '@angular/platform-browser'; -import { SlicePipe } from '@angular/common'; -import { BsDropdownDirective } from 'ngx-bootstrap/dropdown/bs-dropdown.directive'; -import { Router } from '@angular/router'; + +import * as copy from 'copy-to-clipboard'; + import { WalletService } from '../../services/wallet.service'; import { MessageService } from '../../services/message.service'; -import { BalanceService } from '../../services/balance.service'; import { CoordinatorService } from '../../services/coordinator.service'; -import * as copy from 'copy-to-clipboard'; @Component({ selector: 'app-overview', @@ -22,10 +20,8 @@ export class OverviewComponent implements OnInit { dom: Document; constructor( - private router: Router, public walletService: WalletService, private messageService: MessageService, - private balanceService: BalanceService, private coordinatorService: CoordinatorService ) { } diff --git a/src/app/components/receive/receive.component.html b/src/app/components/receive/receive.component.html index 01d70d1d2..8570fd76f 100644 --- a/src/app/components/receive/receive.component.html +++ b/src/app/components/receive/receive.component.html @@ -1,7 +1,7 @@ - +
+ diff --git a/src/app/components/receive/receive.component.scss b/src/app/components/receive/receive.component.scss index 7e72bd99e..667c7a01a 100644 --- a/src/app/components/receive/receive.component.scss +++ b/src/app/components/receive/receive.component.scss @@ -1,9 +1,24 @@ #canvas { margin: auto; } + +#toAddress { + // background-color: black; + // min-width: 321px; // not working +} + +.modal-content { + // width: 70% !important; // not working +} + .modal-header { text-align: center; } + +.modal-footer { + padding-bottom: 1rem; +} + .btn-op { width: 120px; border-radius: 0rem; diff --git a/src/app/components/receive/receive.component.ts b/src/app/components/receive/receive.component.ts index f4178563d..1b91ed0f3 100644 --- a/src/app/components/receive/receive.component.ts +++ b/src/app/components/receive/receive.component.ts @@ -1,11 +1,14 @@ -import { NgModel } from '@angular/forms'; -import { Component, OnInit, AfterViewInit, ViewChild, ElementRef, TemplateRef, Input } from '@angular/core'; -import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; -import * as QRCode from 'qrcode'; +import { Component, OnInit, ViewChild, TemplateRef, Input } from '@angular/core'; + import { BsModalService } from 'ngx-bootstrap/modal'; import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; +import * as QRCode from 'qrcode'; + +import { WalletService } from '../../services/wallet.service'; + + + @Component({ selector: 'app-receive', templateUrl: './receive.component.html', @@ -27,7 +30,6 @@ export class ReceiveComponent implements OnInit { constructor( private walletService: WalletService, - private messageService: MessageService, private modalService: BsModalService ) { } @@ -71,7 +73,7 @@ export class ReceiveComponent implements OnInit { open1(template1: TemplateRef) { if (this.activePkh) { - this.modalRef1 = this.modalService.show(template1, { class: 'modal-sm' }); + this.modalRef1 = this.modalService.show(template1, { class: 'first' }); // modal-sm / modal-lg setTimeout(() => { this.getQR(); }, 100); diff --git a/src/app/components/send/send.component.html b/src/app/components/send/send.component.html index 54ea62505..945253826 100644 --- a/src/app/components/send/send.component.html +++ b/src/app/components/send/send.component.html @@ -14,7 +14,7 @@
-
@@ -55,7 +55,7 @@ {{ formInvalid }}
@@ -84,8 +84,9 @@
- - + {{ pwdValid }} @@ -112,11 +113,11 @@

-

Your transaction has been successfully broadcasted to the network

+

Your transaction has been broadcasted successfully to the network

{{ sendResponse.payload.opHash }}

-

Your unsigned transaction have successfully been created

+

Your unsigned transaction has been created successfully

@@ -125,11 +126,12 @@

{{ sendResponse.payload.msg }}

- - - + diff --git a/src/app/components/send/send.component.scss b/src/app/components/send/send.component.scss index c56f6b857..d1a11c291 100644 --- a/src/app/components/send/send.component.scss +++ b/src/app/components/send/send.component.scss @@ -18,6 +18,10 @@ text-align: center; } +.modal-footer { + padding-bottom: 0rem; +} + .text-color { color: #6c757d; text-align: right; diff --git a/src/app/components/send/send.component.ts b/src/app/components/send/send.component.ts index 52069f237..99b44ef3a 100644 --- a/src/app/components/send/send.component.ts +++ b/src/app/components/send/send.component.ts @@ -1,13 +1,14 @@ -import { Component, TemplateRef, OnInit, ViewEncapsulation, Input, ViewChild, ElementRef } from '@angular/core'; -import { DOCUMENT } from '@angular/platform-browser'; +import { Component, TemplateRef, OnInit, ViewEncapsulation, Input, ViewChild } from '@angular/core'; + +import { BsModalService } from 'ngx-bootstrap/modal'; +import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; + +import { KeyPair } from '../../interfaces'; + import { WalletService } from '../../services/wallet.service'; -import { MessageService } from '../../services/message.service'; import { CoordinatorService } from '../../services/coordinator.service'; import { OperationService } from '../../services/operation.service'; import { ExportService } from '../../services/export.service'; -import { BsModalService } from 'ngx-bootstrap/modal'; -import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; -import { KeyPair } from '../../interfaces'; @Component({ selector: 'app-send', @@ -47,7 +48,6 @@ export class SendComponent implements OnInit { constructor( private modalService: BsModalService, private walletService: WalletService, - private messageService: MessageService, private operationService: OperationService, private coordinatorService: CoordinatorService, private exportService: ExportService @@ -135,7 +135,7 @@ export class SendComponent implements OnInit { open1(template1: TemplateRef) { if (this.walletService.wallet) { this.clearForm(); - this.modalRef1 = this.modalService.show(template1, { class: 'first' }); + this.modalRef1 = this.modalService.show(template1, { class: 'first' }); // modal-sm / modal-lg } } @@ -222,13 +222,13 @@ export class SendComponent implements OnInit { invalidInput(): string { if (!this.activePkh || this.activePkh.length !== 36) { - return 'invalid sender address'; + return 'Invalid sender address'; } else if (!this.toPkh || this.toPkh.length !== 36) { - return 'invalid receiver address'; + return 'Invalid receiver address'; } else if (!Number(this.amount) && this.amount && this.amount !== '0') { - return 'invalid amount'; + return 'Invalid amount'; } else if (!Number(this.fee) && this.fee && this.fee !== '0') { - return 'invalid fee'; + return 'Invalid fee'; } else { return ''; } diff --git a/src/app/components/start/start.component.ts b/src/app/components/start/start.component.ts index 8591e7dc6..0b7759ef6 100644 --- a/src/app/components/start/start.component.ts +++ b/src/app/components/start/start.component.ts @@ -1,7 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { WalletService } from '../../services/wallet.service'; + import { Router } from '@angular/router'; +import { WalletService } from '../../services/wallet.service'; + @Component({ selector: 'app-start', templateUrl: './start.component.html', diff --git a/src/app/constants.ts b/src/app/constants.ts new file mode 100644 index 000000000..9357d4f26 --- /dev/null +++ b/src/app/constants.ts @@ -0,0 +1,33 @@ +interface Net { + NAME: string; + API_URL: string; + NODE_URL: string; + BLOCK_EXPLORER_URL: string; + CHAIN_ID: string; + } +export class Constants { + // Select Betanet or Zeronet + readonly NET: Net = this.betanet(); + // readonly NET: Net = this.zeronet(); + + zeronet(): Net { + const ZERONET: Net = { + NAME: 'Zeronet', + API_URL: 'https://zeronet-api.tzscan.io/', + NODE_URL: 'https://zeronet-node.tzscan.io/', + BLOCK_EXPLORER_URL: 'https://zeronet.tzscan.io/', + CHAIN_ID: 'ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK' + }; + return ZERONET; + } + betanet(): Net { + const BETANET: Net = { + NAME: 'Betanet', + API_URL: 'https://api.tzscan.io/', + NODE_URL: 'https://rpc.tezrpc.me/', + BLOCK_EXPLORER_URL: 'https://tzscan.io/', + CHAIN_ID: 'PsYLVpVvgbLhAhoqAkMFUo6gudkJ9weNXhUYCiLDzcUpFpkk8Wt' + }; + return BETANET; + } +} diff --git a/src/app/pipes/delegator-name.pipe.ts b/src/app/pipes/delegator-name.pipe.ts index a6e734663..a0f8df552 100644 --- a/src/app/pipes/delegator-name.pipe.ts +++ b/src/app/pipes/delegator-name.pipe.ts @@ -1,30 +1,50 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'delegatorName' + name: 'delegatorName' }) export class DelegatorNamePipe implements PipeTransform { - map: Map = new Map([ - ['tz1TDSmoZXwVevLTEvKCTHWpomG76oC9S2fJ', 'Tezos.Community'], - ['tz1WCd2jm4uSt4vntk4vSuUWoZQGhLcDuR9q', 'Happy Tezos'], - ['tz1XQ7SRj4QQWjaeebNd8dFwuTrCot3GGDRF', 'Tz Baker'], - ['tz1YKh8T79LAtWxX29N5VedCSmaZGw9LNVxQ', 'Tezos Brazil'], - ['tz3bEQoFCZEEfZMskefZ8q8e4eiHH1pssRax', 'Ceibo XTZ'], - ['tz1hThMBD8jQjFt78heuCnKxJnJtQo9Ao25X', 'Tezos Chef'], - ['tz1L3vFD8mFzBaS8yLHFsd7qDJY1t276Dh8i', 'Zednode'], - ['tz1Tnjaxk6tbAeC2TmMApPh8UsrEVQvhHvx5', 'My Crypto Delegate'], - ['tz1LesY3S4wfe15SNm1W3qJmQzWxLqVjTruH', 'Xtez.io'], - ['tz1L5GqtsKbasq9yD4hvtGC7VprPXDPmeb9V', 'Tezos Bakes'] - ]); - transform(pkh: string): any { - if (!pkh) { - return ''; - } - const name = this.map.get(pkh); - if (name) { - return name; - } else { - return pkh; + map: Map = new Map([ + ['tz1XQ7SRj4QQWjaeebNd8dFwuTrCot3GGDRF', 'Tz Baker'], // no longer listed + // ['tz1L5GqtsKbasq9yD4hvtGC7VprPXDPmeb9V', 'Tezos Bakes'], no longer active + ['tz1TDSmoZXwVevLTEvKCTHWpomG76oC9S2fJ', 'Tezos Community'], + ['tz1eEnQhbwf6trb8Q8mPb2RaPkNk2rN7BKi8', 'Cryptium Labs'], + ['tz1NortRftucvAkD1J58L32EhSVrQEWJCEnB', 'Bake’n’Rolls'], + ['tz1LLNkQK4UQV6QcFShiXJ2vT2ELw449MzAA', 'TezoSteam'], + ['tz1bHzftcTKZMTZgLLtnrXydCm6UEqf4ivca', 'Tezos Vote'], + ['tz1YTyvABUyhE7JHpxMVBVqjZnZM4ofMrWKE', 'Tezos Delegate EU'], + ['tz1WCd2jm4uSt4vntk4vSuUWoZQGhLcDuR9q', 'Happy Tezos'], + ['tz1TcH4Nb3aHNDJ7CGZhU7jgAK1BkSP4Lxds', 'XTZ Antipodes'], + ['tz1iLbZZ9uoRuVJCrZ9ZwiJMpfzhy3c67mav', 'AirBie'], + ['tz1WpeqFaBG9Jm73Dmgqamy8eF8NWLz9JCoY', 'Staking Facilities'], + ['tz1L3vFD8mFzBaS8yLHFsd7qDJY1t276Dh8i', 'Zednode'], + ['tz1iZEKy4LaAjnTmn2RuGDf2iqdAQKnRi8kY', 'Tezzigator'], + ['tz1YKh8T79LAtWxX29N5VedCSmaZGw9LNVxQ', 'TezosBr'], + ['tz1hThMBD8jQjFt78heuCnKxJnJtQo9Ao25X', 'Tezos Chef'], + ['tz3bEQoFCZEEfZMskefZ8q8e4eiHH1pssRax', 'Ceibo XTZ'], // Listing Error + ['tz1Tnjaxk6tbAeC2TmMApPh8UsrEVQvhHvx5', 'Crypto Delegate'], + ['tz1ZTG13gkvouxSANka3HG3uys8C5gu3DPXZ', 'Just a Baker'], + ['tz1PYLN9TsKZHfn2GtrXnxkeGvahmYdBTG5v', 'Tezos Bakes'], + ['tz1Zhv3RkfU2pHrmaiDyxp7kFZpZrUCu1CiF', 'TZBake'], + ['tz1PriNQyDC7d5ccPAD96ugujYy5YbdGLdQ5', 'Tezos Baker JP'], + ['tz1Z1WwoqgRFbLE3YNdYRpCx44NSfiMJzeAG', 'Bakemon'], + ['tz1bkg7rynMXVcjomoe3diB4URfv8GU2GAcw', 'tz Bank'], + ['tz1Lhf4J9Qxoe3DZ2nfe8FGDnvVj7oKjnMY6', 'Tez Baker'], // Listing Error + ['tz1YdCPrYbksK7HCoYKDyzgfXwY16Fy9rrGa', 'Norn Delegate'], + ['tz1LesY3S4wfe15SNm1W3qJmQzWxLqVjTruH', 'Xtez.io'], + ['tz1Yc6ATtfUJyDjHwJ8WoVL22sJueDenueke', 'TezDele Baker A'], + ['tz1SdwBHocSrcuMFNLPUg4LPRfx9eaqjVUEL', 'TezDele Baker B'] + ]); + + transform(pkh: string): string { + if (!pkh) { + return ''; + } + const name = this.map.get(pkh); + if (name) { + return name; + } else { + return pkh; + } } - } } diff --git a/src/app/pipes/time-ago.pipe.spec.ts b/src/app/pipes/time-ago.pipe.spec.ts new file mode 100644 index 000000000..860586f65 --- /dev/null +++ b/src/app/pipes/time-ago.pipe.spec.ts @@ -0,0 +1,8 @@ +import { TimeAgoPipe } from './time-ago.pipe'; + +describe('TimeAgoPipe', () => { + it('create an instance', () => { + const pipe = new TimeAgoPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/pipes/time-ago.pipe.ts b/src/app/pipes/time-ago.pipe.ts new file mode 100644 index 000000000..1d9f8538e --- /dev/null +++ b/src/app/pipes/time-ago.pipe.ts @@ -0,0 +1,91 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'timeAgo', + pure: true +}) +export class TimeAgoPipe implements PipeTransform { + transform(dateString: string | null): string { + + let result: string; + + // Tranforming from String to Date format + const dateValue: Date = new Date(dateString); + + // current time + const now = new Date().getTime(); + + // time since transaction was made in seconds + const delta = (now - dateValue.getTime()) / 1000; + + // If transaction has just been broadcasted then dateString will be null in the first few seconds (prevalidation stage) + if (delta < 20 || (dateString === null)) { + return 'just now'; + } + + // Return interval format in seconds, hours or days + if (delta < 60) { // Sent in last minute + result = Math.floor(delta) + ' sec '; + } else if (delta < 3600) { // Sent in last hour: 1h = 3600 sec -> displays seconds + result = Math.floor(delta / 60) + ' mn '; + + // Adds seconds details if there's a remainder in 'delta % 60' + if (Math.floor(delta % 60) !== 0) { + result = result + Math.floor(delta % 60) + ' sec '; + } + } else if (delta < 86400) { // Sent on last day: 1d = 86400 sec -> displays hours and seconds + result = String(Math.floor(delta / 3600)); + + // Adds suffix hr or hrs depending on 'delta / 3600' value + result = Math.floor(delta / 3600) > 1 ? result + ' hrs ' : result + ' hr '; + + // Adds minutes details if there's a remainder in 'delta % 3600' + if (Math.floor(delta % 3600) !== 0) { + result = result + Math.floor((delta % 3600) / 60) + ' mn '; + } + } else if (delta < 2592000) { // Sent on last month: 1m = 2592000 sec (30 days) -> displays days and hours + result = String(Math.floor(delta / 86400)); + + // Adds suffix day or days depending on 'delta / 86400' value + result = Math.floor(delta / 86400) > 1 ? result + ' days ' : result + ' day '; + + // Adds hours details if there's a remainder in 'delta % 86400' + if (Math.floor(delta % 86400) !== 0) { + result = result + String(Math.floor((delta % 86400) / 3600)); + + // Adds suffix hr or hrs depending on '(delta % 86400) / 3600' value + result = Math.floor((delta % 86400) / 3600) > 1 ? result + ' hrs ' : result + ' hr '; + } + } else if (delta < 31536000) { // Sent on last year: 1y = 31536000 sec (365 days) -> displays months and days + result = String(Math.floor(delta / 2592000)); + + // Adds suffix month or months depending on 'delta / 2592000' value + result = Math.floor(delta / 2592000) > 1 ? result + ' months ' : result + ' month '; + + // Adds days details if there's a remainder in 'delta / 2592000' + if (Math.floor(delta % 2592000) !== 0) { + result = result + String(Math.floor((delta % 2592000) / 86400)); + + // Adds suffix day or days depending on '(delta % 2592000) / 86400' value + result = Math.floor((delta % 2592000) / 86400) > 1 ? result + ' days ' : result + ' day '; + } + + } else { // Sent more than one year ago -> displays years and months + result = String(Math.floor(delta / 31536000)); + + // Adds suffix year or years depending on 'delta / 31536000' value + result = Math.floor(delta / 31536000) > 1 ? result + ' years ' : result + ' year '; + + // Adds months details if there's a remainder in 'delta / 31536000' + if (Math.floor(delta % 31536000) !== 0) { + result = result + String(Math.floor((delta % 31536000) / 2592000)); + + // Adds suffix month or months depending on '(delta % 31536000) / 2592000' value + result = Math.floor((delta % 31536000) / 2592000) > 1 ? result + ' months ' : result + ' month '; + } + + } + + return result + 'ago'; + } +} diff --git a/src/app/pipes/truncate.pipe.spec.ts b/src/app/pipes/truncate.pipe.spec.ts new file mode 100644 index 000000000..d56eae6eb --- /dev/null +++ b/src/app/pipes/truncate.pipe.spec.ts @@ -0,0 +1,8 @@ +import { TruncatePipe } from './truncate.pipe'; + +describe('TruncatePipe', () => { + it('create an instance', () => { + const pipe = new TruncatePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/pipes/truncate.pipe.ts b/src/app/pipes/truncate.pipe.ts new file mode 100644 index 000000000..d8b413602 --- /dev/null +++ b/src/app/pipes/truncate.pipe.ts @@ -0,0 +1,22 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'truncate' +}) +export class TruncatePipe implements PipeTransform { + transform(value?: string, args?: number, endTrail?: boolean): string { + const limit = args ? args : 3; + const trail = '...'; + + let returnString; + + if (endTrail && value.length > limit) { + const endTrailstring = value.slice(-limit); + returnString = value.substring(0, limit) + trail + endTrailstring; + + return returnString; + } else { + return value.length > limit ? value.substring(0, limit) + trail : value; + } + } +} diff --git a/src/app/services/encryption.service.ts b/src/app/services/encryption.service.ts index ce8ea1265..1cb19bc43 100644 --- a/src/app/services/encryption.service.ts +++ b/src/app/services/encryption.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; -import { WalletService } from './wallet.service'; + import * as pbkdf2 from 'pbkdf2'; -import * as CryptoJS from 'crypto-js'; import * as AES from 'aes-js'; @Injectable() diff --git a/src/app/services/export.service.ts b/src/app/services/export.service.ts index 2e872cb53..cb28a9bc3 100644 --- a/src/app/services/export.service.ts +++ b/src/app/services/export.service.ts @@ -1,8 +1,11 @@ import { Injectable } from '@angular/core'; import { saveAs } from 'file-saver/FileSaver'; -import { WalletService } from './wallet.service'; + import { WalletType } from './../interfaces'; +import { WalletService } from './wallet.service'; + + @Injectable() export class ExportService { diff --git a/src/app/services/import.service.ts b/src/app/services/import.service.ts index 20baea66f..7680fca35 100644 --- a/src/app/services/import.service.ts +++ b/src/app/services/import.service.ts @@ -1,10 +1,12 @@ import { Injectable } from '@angular/core'; -import { WalletService } from './wallet.service'; -import { WalletType } from './../interfaces'; import { Observable } from 'rxjs/Observable'; +import * as bip39 from 'bip39'; + +import { WalletType } from './../interfaces'; + +import { WalletService } from './wallet.service'; import { MessageService } from './message.service'; import { BalanceService } from './balance.service'; -import * as bip39 from 'bip39'; import { CoordinatorService } from './coordinator.service'; import { OperationService } from './operation.service'; import { TzscanService } from './tzscan.service'; @@ -95,7 +97,7 @@ export class ImportService { index = 1; } const KT = data[i].type.operations[index].tz1.tz; - if (KT !== pkh) { + if (this.walletService.wallet.accounts.findIndex(a => a.pkh === KT) === -1) { this.walletService.addAccount(KT); console.log('Added: ' + KT); this.coordinatorService.start(KT); diff --git a/src/app/services/operation.service.ts b/src/app/services/operation.service.ts index 414029335..f77cce4eb 100644 --- a/src/app/services/operation.service.ts +++ b/src/app/services/operation.service.ts @@ -7,6 +7,8 @@ import { Buffer } from 'buffer'; import * as libs from 'libsodium-wrappers'; import * as Bs58check from 'bs58check'; import * as bip39 from 'bip39'; +import { Constants } from '../constants'; + import { ErrorHandlingPipe } from '../pipes/error-handling.pipe'; const httpOptions = { @@ -20,8 +22,9 @@ export interface KeyPair { } @Injectable() export class OperationService { - nodeURL = 'https://rpc.tezrpc.me'; - CHAIN_ID = 'PsYLVpVvgbLhAhoqAkMFUo6gudkJ9weNXhUYCiLDzcUpFpkk8Wt'; + CONSTANTS = new Constants(); + nodeURL = this.CONSTANTS.NET.NODE_URL; + CHAIN_ID = this.CONSTANTS.NET.CHAIN_ID; prefix = { tz1: new Uint8Array([6, 161, 159]), tz2: new Uint8Array([6, 161, 161]), @@ -553,10 +556,11 @@ export class OperationService { let index = 0; const op = this.decodeCommon({ kind: 'delegation' }, content); console.log('hex: ' + op.rest + ' ' + op.rest.length); - if (op.rest.slice(index, index += 2) !== 'ff') { + if (op.rest.slice(index, index += 2) === 'ff') { + op.data.delegate = this.decodePkh(op.rest.slice(index, index += 42)); + } else if (op.rest.slice(index - 2, index) !== '00') { throw new Error('TagErrorD1'); } - op.data.delegate = this.decodePkh(op.rest.slice(index, index += 42)); console.log('INDEX' + index); if (op.rest.length === index) { return [op.data]; diff --git a/src/app/services/tzrate.service.ts b/src/app/services/tzrate.service.ts index 6ff8e7e36..2386b87b7 100644 --- a/src/app/services/tzrate.service.ts +++ b/src/app/services/tzrate.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Response } from '@angular/http'; -import { WalletService } from './wallet.service'; + import 'rxjs/add/operator/map'; +import { WalletService } from './wallet.service'; + + @Injectable() export class TzrateService { diff --git a/src/app/services/tzscan.service.ts b/src/app/services/tzscan.service.ts index 9c99f538f..d34d614a1 100644 --- a/src/app/services/tzscan.service.ts +++ b/src/app/services/tzscan.service.ts @@ -1,13 +1,16 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Constants } from '../constants'; + const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; @Injectable() export class TzscanService { - apiUrl = 'https://api.tzscan.io/'; + CONSTANTS = new Constants(); + apiUrl = this.CONSTANTS.NET.API_URL; constructor(private http: HttpClient) { } numberOperations(pkh: string) { diff --git a/src/app/services/wallet.service.ts b/src/app/services/wallet.service.ts index 34fe45d5a..abcc9b6a8 100644 --- a/src/app/services/wallet.service.ts +++ b/src/app/services/wallet.service.ts @@ -1,16 +1,18 @@ import { Injectable } from '@angular/core'; -import { MessageService } from './message.service'; -import { Wallet, Account, Balance, KeyPair, WalletType } from './../interfaces'; + +import * as bip39 from 'bip39'; + +import { Wallet, Balance, KeyPair, WalletType } from './../interfaces'; + import { EncryptionService } from './encryption.service'; import { OperationService } from './operation.service'; -import * as bip39 from 'bip39'; + @Injectable() export class WalletService { storeKey = `kukai-wallet`; wallet: Wallet; constructor( - private messageService: MessageService, private encryptionService: EncryptionService, private operationService: OperationService ) { } diff --git a/src/electron/contextMenu.js b/src/electron/contextMenu.js new file mode 100644 index 000000000..0c65e7a35 --- /dev/null +++ b/src/electron/contextMenu.js @@ -0,0 +1,66 @@ + +module.export = [ + {role:'copy'}, + {role:'paste'}, + { type: 'separator' }, + {role:'undo'}, + {role:'redo'} +] + +/* +module.export = [ + { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'CmdOrCtrl+Z', + selector: 'undo:' + }, + { + label: 'Redo', + accelerator: 'Shift+CmdOrCtrl+Z', + selector: 'redo:' + }, + { type: 'separator' }, + { + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + selector: 'cut:' + }, + { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + selector: 'copy:' + }, + { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + selector: 'paste:' + }, + { type: 'separator' }, + { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + selector: 'selectAll:' + } + ] + } +]; +*/ + +/* +export const editMenuTemplate: any = { + label: 'Edit', + submenu: [ + { label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' }, + { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' }, + { type: 'separator' }, + { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' }, + { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' }, + { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' }, + { type: 'separator' }, + { label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' } + ] +}; +*/ diff --git a/src/electron/main.js b/src/electron/main.js index 4d4980d35..be38d1d46 100644 --- a/src/electron/main.js +++ b/src/electron/main.js @@ -1,15 +1,34 @@ -const {app, BrowserWindow} = require('electron') +const {app, BrowserWindow, Menu} = require('electron') // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let win + // Context menu + //let contextMenu = Menu.buildFromTemplate(require('./contextMenu.js')) //TypeError: Invalid template for Menu + + // TestContext menu + let testContextMenu = Menu.buildFromTemplate([ + {role:'copy'}, + {role:'paste'}, + { type: 'separator' }, + {role:'undo'}, + {role:'redo'} + ]) + function createWindow () { // Create the browser window. win = new BrowserWindow({width: 1400, height: 1000, autoHideMenuBar: true, icon: 'assets/icons/png/64x64.png'}) // and load the index.html of the app. win.loadFile('index.html') - + + // Listening to right-mouse-click + win.webContents.on('context-menu', (e) => { + e.preventDefault() + testContextMenu.popup(win) + }) + + // Open the DevTools. // win.webContents.openDevTools() @@ -25,7 +44,10 @@ const {app, BrowserWindow} = require('electron') // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. - app.on('ready', createWindow) + app.on('ready', () => { + createWindow() + //Menu.setApplicationMenu(contextMenu) + }) // Quit when all windows are closed. app.on('window-all-closed', () => { diff --git a/src/electron/package.json b/src/electron/package.json index 1ef89bffd..9ebe75cc4 100644 --- a/src/electron/package.json +++ b/src/electron/package.json @@ -1,6 +1,6 @@ { "name": "kukai", - "version": "1.0.3", + "version": "1.1.0", "main": "main.js", "scripts": { "start": "electron ."