From 8c883ea7000749728d290841bc5d333781245791 Mon Sep 17 00:00:00 2001 From: Alexander Bai Date: Thu, 13 Aug 2020 16:11:59 -0400 Subject: [PATCH] web-ui: updated the frontend ui web-ui: modularize the pages (calculator, governance missing) web-ui: added block-list page, changed latest-blocks to block-table. web-ui: block details/raw page (WIP) web-ui: updated blocks tab web-ui: fixed the colspan for labels in block-details page. web-ui: fixed the unit-tests. web-ui: paginated the block list table web-ui: updated the blocks api url web-ui: updated the block-table component and blocks api endpoint. web-ui: added i18n to the block-list component. web-ui: moved truncate to utils and added test. web-ui: updated the style for better responsiveness web-ui: updated the dashboard page layout web-ui: Fixed type definition of block.transactions field. web-ui: Made amAgo utility function. web-ui: Added transaction list page. web-ui: Fixed unit test for transactions table. web-ui: Updated the transaction details/raw pages web-ui: Updated the dashboard page ui web-ui: Updated the nodes page ui web-ui: Updated the node stats on the UI. web-ui: Fixed unit test for node-ticker. web-ui: Updated the addresses page ui web-ui: Updated the addresses page ui web-ui: Added calculator page ui web-ui: Updated the calculator page ui web-ui: Updated statistics display values web-ui: Updated coins in masternodes value in the ui web-ui: Updated the ui to for coinsStaking --- web-ui/src/app/app-routing.module.ts | 17 +- web-ui/src/app/app.component.html | 8 +- web-ui/src/app/app.component.ts | 64 +++- web-ui/src/app/app.module.ts | 75 +--- .../address-details.component.html | 89 ----- .../address-details.component.css | 5 + .../address-details.component.html | 108 ++++++ .../address-details.component.spec.ts | 14 +- .../address-details.component.ts | 56 +-- .../address-list/address-list.component.css | 15 + .../address-list/address-list.component.html | 43 +++ .../address-list.component.spec.ts} | 20 +- .../address-list/address-list.component.ts} | 33 +- .../addresses/addresses-routing.module.ts | 28 ++ .../addresses/addresses.component.html | 3 + .../addresses/addresses.component.ts | 7 + .../components/addresses/addresses.module.ts | 37 ++ .../block-details.component.html | 339 ------------------ .../block-raw/block-raw.component.css | 0 .../app/components/block/block.component.css | 0 .../app/components/block/block.component.html | 14 - .../components/block/block.component.spec.ts | 35 -- .../app/components/block/block.component.ts | 24 -- .../block-details/block-details.component.css | 37 ++ .../block-details.component.html | 306 ++++++++++++++++ .../block-details.component.spec.ts | 97 +++++ .../block-details/block-details.component.ts | 60 +--- .../block-list/block-list.component.css | 20 ++ .../block-list/block-list.component.html | 21 ++ .../block-list/block-list.component.spec.ts | 44 +++ .../blocks/block-list/block-list.component.ts | 32 ++ .../blocks/block-raw/block-raw.component.css | 5 + .../block-raw/block-raw.component.html | 4 +- .../block-raw/block-raw.component.spec.ts | 10 +- .../block-raw/block-raw.component.ts | 10 +- .../blocks/block/block.component.css | 43 +++ .../blocks/block/block.component.html | 37 ++ .../block/block.component.spec.ts} | 40 +-- .../blocks/block/block.component.ts | 57 +++ .../blocks/blocks-routing.module.ts | 29 ++ .../components/blocks/blocks.component.html | 3 + .../app/components/blocks/blocks.component.ts | 7 + .../app/components/blocks/blocks.module.ts | 41 +++ .../calculator/calculator.component.css | 142 ++++++++ .../calculator/calculator.component.html | 203 +++++++++++ .../calculator/calculator.component.spec.ts | 45 +++ .../calculator/calculator.component.ts | 56 +++ .../components/finder/finder.component.css | 33 -- .../components/finder/finder.component.html | 33 -- .../components/home/home-routing.module.ts | 17 + .../app/components/home/home.component.css | 15 - .../app/components/home/home.component.html | 20 -- web-ui/src/app/components/home/home.module.ts | 35 ++ .../components/home/home/home.component.css | 10 + .../components/home/home/home.component.html | 31 ++ .../home/{ => home}/home.component.spec.ts | 2 +- .../home/{ => home}/home.component.ts | 0 .../home/ticker/ticker.component.css | 48 +++ .../home/ticker/ticker.component.html | 184 ++++++++++ .../ticker/ticker.component.spec.ts | 10 +- .../home/ticker/ticker.component.ts | 48 +++ .../latest-blocks/latest-blocks.component.css | 9 - .../latest-blocks.component.html | 41 --- .../latest-blocks/latest-blocks.component.ts | 92 ----- .../masternodes/masternodes.component.html | 46 --- .../components/navbar/navbar.component.css | 18 - .../components/navbar/navbar.component.html | 17 - .../app/components/navbar/navbar.component.ts | 36 -- .../masternode-details.component.css | 0 .../masternode-details.component.html | 0 .../masternode-details.component.spec.ts | 10 +- .../masternode-details.component.ts | 10 +- .../masternodes/masternodes.component.css | 0 .../masternodes/masternodes.component.html | 42 +++ .../masternodes/masternodes.component.spec.ts | 8 +- .../masternodes/masternodes.component.ts | 19 +- .../node-list/node-list.component.css} | 0 .../nodes/node-list/node-list.component.html | 21 ++ .../node-list/node-list.component.spec.ts | 36 ++ .../nodes/node-list/node-list.component.ts | 14 + .../node-ticker/node-ticker.component.css | 25 ++ .../node-ticker/node-ticker.component.html | 212 +++++++++++ .../node-ticker/node-ticker.component.spec.ts | 47 +++ .../node-ticker/node-ticker.component.ts | 39 ++ .../components/nodes/nodes-routing.module.ts | 28 ++ .../app/components/nodes/nodes.component.html | 3 + .../app/components/nodes/nodes.component.ts | 7 + .../src/app/components/nodes/nodes.module.ts | 45 +++ .../tposnodes/tposnodes.component.css} | 0 .../nodes/tposnodes/tposnodes.component.html | 42 +++ .../tposnodes/tposnodes.component.spec.ts | 50 +++ .../nodes/tposnodes/tposnodes.component.ts | 49 +++ .../richest-addresses.component.css | 9 - .../richest-addresses.component.html | 46 --- .../block-table/block-table.component.css | 18 + .../block-table/block-table.component.html | 44 +++ .../block-table.component.spec.ts} | 18 +- .../block-table/block-table.component.ts | 89 +++++ .../shared/finder/finder.component.css | 20 ++ .../shared/finder/finder.component.html | 27 ++ .../finder/finder.component.spec.ts | 14 +- .../{ => shared}/finder/finder.component.ts | 18 +- .../{ => shared}/footer/footer.component.css | 0 .../{ => shared}/footer/footer.component.html | 0 .../footer/footer.component.spec.ts | 0 .../{ => shared}/footer/footer.component.ts | 0 .../shared/navbar/navbar.component.css | 53 +++ .../shared/navbar/navbar.component.html | 25 ++ .../navbar/navbar.component.spec.ts | 0 .../shared/navbar/navbar.component.ts | 74 ++++ .../app/components/shared/shared.module.ts | 42 +++ .../transaction-table.component.css | 11 + .../transaction-table.component.html | 52 +++ .../transaction-table.component.spec.ts | 58 +++ .../transaction-table.component.ts | 93 +++++ .../components/ticker/ticker.component.css | 61 ---- .../components/ticker/ticker.component.html | 79 ---- .../app/components/ticker/ticker.component.ts | 36 -- .../transaction-raw.component.css | 0 .../transaction/transaction.component.css | 0 .../transaction/transaction.component.html | 17 - .../transaction-details.component.css | 0 .../transaction-details.component.html | 46 +-- .../transaction-details.component.spec.ts | 12 +- .../transaction-details.component.ts | 28 +- .../transaction-list.component.css | 20 ++ .../transaction-list.component.html | 21 ++ .../transaction-list.component.spec.ts | 36 ++ .../transaction-list.component.ts | 14 + .../transaction-raw.component.css | 5 + .../transaction-raw.component.html | 2 +- .../transaction-raw.component.spec.ts | 10 +- .../transaction-raw.component.ts | 12 +- .../transaction/transaction.component.css | 43 +++ .../transaction/transaction.component.html | 27 ++ .../transaction/transaction.component.spec.ts | 0 .../transaction/transaction.component.ts | 0 .../transactions-routing.module.ts | 28 ++ .../transactions/transactions.component.html | 3 + .../transactions/transactions.component.ts | 7 + .../transactions/transactions.module.ts | 41 +++ web-ui/src/app/models/block.ts | 2 +- web-ui/src/app/models/ticker.ts | 18 + web-ui/src/app/models/tposcontract.ts | 11 + web-ui/src/app/models/tposnode.ts | 11 + web-ui/src/app/models/transaction.ts | 1 + web-ui/src/app/pipes/pipes.module.ts | 32 ++ web-ui/src/app/services/addresses.service.ts | 17 +- web-ui/src/app/services/blocks.service.ts | 8 +- web-ui/src/app/services/ticker.service.ts | 16 +- web-ui/src/app/services/tposnodes.service.ts | 30 ++ .../src/app/services/transactions.service.ts | 10 + web-ui/src/app/utils.spec.ts | 13 + web-ui/src/app/utils.ts | 35 ++ web-ui/src/assets/xsn-logo.png | Bin 0 -> 947 bytes web-ui/src/assets/xsn-logo@2x.png | Bin 0 -> 2729 bytes web-ui/src/styles.css | 239 +++++++++++- 157 files changed, 4077 insertions(+), 1460 deletions(-) delete mode 100644 web-ui/src/app/components/address-details/address-details.component.html create mode 100644 web-ui/src/app/components/addresses/address-details/address-details.component.css create mode 100644 web-ui/src/app/components/addresses/address-details/address-details.component.html rename web-ui/src/app/components/{ => addresses}/address-details/address-details.component.spec.ts (71%) rename web-ui/src/app/components/{ => addresses}/address-details/address-details.component.ts (50%) create mode 100644 web-ui/src/app/components/addresses/address-list/address-list.component.css create mode 100644 web-ui/src/app/components/addresses/address-list/address-list.component.html rename web-ui/src/app/components/{richest-addresses/richest-addresses.component.spec.ts => addresses/address-list/address-list.component.spec.ts} (70%) rename web-ui/src/app/components/{richest-addresses/richest-addresses.component.ts => addresses/address-list/address-list.component.ts} (56%) create mode 100644 web-ui/src/app/components/addresses/addresses-routing.module.ts create mode 100644 web-ui/src/app/components/addresses/addresses.component.html create mode 100644 web-ui/src/app/components/addresses/addresses.component.ts create mode 100644 web-ui/src/app/components/addresses/addresses.module.ts delete mode 100644 web-ui/src/app/components/block-details/block-details.component.html delete mode 100644 web-ui/src/app/components/block-raw/block-raw.component.css delete mode 100644 web-ui/src/app/components/block/block.component.css delete mode 100644 web-ui/src/app/components/block/block.component.html delete mode 100644 web-ui/src/app/components/block/block.component.spec.ts delete mode 100644 web-ui/src/app/components/block/block.component.ts create mode 100644 web-ui/src/app/components/blocks/block-details/block-details.component.css create mode 100644 web-ui/src/app/components/blocks/block-details/block-details.component.html create mode 100644 web-ui/src/app/components/blocks/block-details/block-details.component.spec.ts rename web-ui/src/app/components/{ => blocks}/block-details/block-details.component.ts (58%) create mode 100644 web-ui/src/app/components/blocks/block-list/block-list.component.css create mode 100644 web-ui/src/app/components/blocks/block-list/block-list.component.html create mode 100644 web-ui/src/app/components/blocks/block-list/block-list.component.spec.ts create mode 100644 web-ui/src/app/components/blocks/block-list/block-list.component.ts create mode 100644 web-ui/src/app/components/blocks/block-raw/block-raw.component.css rename web-ui/src/app/components/{ => blocks}/block-raw/block-raw.component.html (70%) rename web-ui/src/app/components/{ => blocks}/block-raw/block-raw.component.spec.ts (85%) rename web-ui/src/app/components/{ => blocks}/block-raw/block-raw.component.ts (79%) create mode 100644 web-ui/src/app/components/blocks/block/block.component.css create mode 100644 web-ui/src/app/components/blocks/block/block.component.html rename web-ui/src/app/components/{block-details/block-details.component.spec.ts => blocks/block/block.component.spec.ts} (52%) create mode 100644 web-ui/src/app/components/blocks/block/block.component.ts create mode 100644 web-ui/src/app/components/blocks/blocks-routing.module.ts create mode 100644 web-ui/src/app/components/blocks/blocks.component.html create mode 100644 web-ui/src/app/components/blocks/blocks.component.ts create mode 100644 web-ui/src/app/components/blocks/blocks.module.ts create mode 100644 web-ui/src/app/components/calculator/calculator.component.css create mode 100644 web-ui/src/app/components/calculator/calculator.component.html create mode 100644 web-ui/src/app/components/calculator/calculator.component.spec.ts create mode 100644 web-ui/src/app/components/calculator/calculator.component.ts delete mode 100644 web-ui/src/app/components/finder/finder.component.css delete mode 100644 web-ui/src/app/components/finder/finder.component.html create mode 100644 web-ui/src/app/components/home/home-routing.module.ts delete mode 100644 web-ui/src/app/components/home/home.component.css delete mode 100644 web-ui/src/app/components/home/home.component.html create mode 100644 web-ui/src/app/components/home/home.module.ts create mode 100644 web-ui/src/app/components/home/home/home.component.css create mode 100644 web-ui/src/app/components/home/home/home.component.html rename web-ui/src/app/components/home/{ => home}/home.component.spec.ts (93%) rename web-ui/src/app/components/home/{ => home}/home.component.ts (100%) create mode 100644 web-ui/src/app/components/home/ticker/ticker.component.css create mode 100644 web-ui/src/app/components/home/ticker/ticker.component.html rename web-ui/src/app/components/{ => home}/ticker/ticker.component.spec.ts (71%) create mode 100644 web-ui/src/app/components/home/ticker/ticker.component.ts delete mode 100644 web-ui/src/app/components/latest-blocks/latest-blocks.component.css delete mode 100644 web-ui/src/app/components/latest-blocks/latest-blocks.component.html delete mode 100644 web-ui/src/app/components/latest-blocks/latest-blocks.component.ts delete mode 100644 web-ui/src/app/components/masternodes/masternodes.component.html delete mode 100644 web-ui/src/app/components/navbar/navbar.component.css delete mode 100644 web-ui/src/app/components/navbar/navbar.component.html delete mode 100644 web-ui/src/app/components/navbar/navbar.component.ts rename web-ui/src/app/components/{ => nodes}/masternode-details/masternode-details.component.css (100%) rename web-ui/src/app/components/{ => nodes}/masternode-details/masternode-details.component.html (100%) rename web-ui/src/app/components/{ => nodes}/masternode-details/masternode-details.component.spec.ts (84%) rename web-ui/src/app/components/{ => nodes}/masternode-details/masternode-details.component.ts (77%) rename web-ui/src/app/components/{ => nodes}/masternodes/masternodes.component.css (100%) create mode 100644 web-ui/src/app/components/nodes/masternodes/masternodes.component.html rename web-ui/src/app/components/{ => nodes}/masternodes/masternodes.component.spec.ts (87%) rename web-ui/src/app/components/{ => nodes}/masternodes/masternodes.component.ts (62%) rename web-ui/src/app/components/{address-details/address-details.component.css => nodes/node-list/node-list.component.css} (100%) create mode 100644 web-ui/src/app/components/nodes/node-list/node-list.component.html create mode 100644 web-ui/src/app/components/nodes/node-list/node-list.component.spec.ts create mode 100644 web-ui/src/app/components/nodes/node-list/node-list.component.ts create mode 100644 web-ui/src/app/components/nodes/node-ticker/node-ticker.component.css create mode 100644 web-ui/src/app/components/nodes/node-ticker/node-ticker.component.html create mode 100644 web-ui/src/app/components/nodes/node-ticker/node-ticker.component.spec.ts create mode 100644 web-ui/src/app/components/nodes/node-ticker/node-ticker.component.ts create mode 100644 web-ui/src/app/components/nodes/nodes-routing.module.ts create mode 100644 web-ui/src/app/components/nodes/nodes.component.html create mode 100644 web-ui/src/app/components/nodes/nodes.component.ts create mode 100644 web-ui/src/app/components/nodes/nodes.module.ts rename web-ui/src/app/components/{block-details/block-details.component.css => nodes/tposnodes/tposnodes.component.css} (100%) create mode 100644 web-ui/src/app/components/nodes/tposnodes/tposnodes.component.html create mode 100644 web-ui/src/app/components/nodes/tposnodes/tposnodes.component.spec.ts create mode 100644 web-ui/src/app/components/nodes/tposnodes/tposnodes.component.ts delete mode 100644 web-ui/src/app/components/richest-addresses/richest-addresses.component.css delete mode 100644 web-ui/src/app/components/richest-addresses/richest-addresses.component.html create mode 100644 web-ui/src/app/components/shared/block-table/block-table.component.css create mode 100644 web-ui/src/app/components/shared/block-table/block-table.component.html rename web-ui/src/app/components/{latest-blocks/latest-blocks.component.spec.ts => shared/block-table/block-table.component.spec.ts} (73%) create mode 100644 web-ui/src/app/components/shared/block-table/block-table.component.ts create mode 100644 web-ui/src/app/components/shared/finder/finder.component.css create mode 100644 web-ui/src/app/components/shared/finder/finder.component.html rename web-ui/src/app/components/{ => shared}/finder/finder.component.spec.ts (85%) rename web-ui/src/app/components/{ => shared}/finder/finder.component.ts (83%) rename web-ui/src/app/components/{ => shared}/footer/footer.component.css (100%) rename web-ui/src/app/components/{ => shared}/footer/footer.component.html (100%) rename web-ui/src/app/components/{ => shared}/footer/footer.component.spec.ts (100%) rename web-ui/src/app/components/{ => shared}/footer/footer.component.ts (100%) create mode 100644 web-ui/src/app/components/shared/navbar/navbar.component.css create mode 100644 web-ui/src/app/components/shared/navbar/navbar.component.html rename web-ui/src/app/components/{ => shared}/navbar/navbar.component.spec.ts (100%) create mode 100644 web-ui/src/app/components/shared/navbar/navbar.component.ts create mode 100644 web-ui/src/app/components/shared/shared.module.ts create mode 100644 web-ui/src/app/components/shared/transaction-table/transaction-table.component.css create mode 100644 web-ui/src/app/components/shared/transaction-table/transaction-table.component.html create mode 100644 web-ui/src/app/components/shared/transaction-table/transaction-table.component.spec.ts create mode 100644 web-ui/src/app/components/shared/transaction-table/transaction-table.component.ts delete mode 100644 web-ui/src/app/components/ticker/ticker.component.css delete mode 100644 web-ui/src/app/components/ticker/ticker.component.html delete mode 100644 web-ui/src/app/components/ticker/ticker.component.ts delete mode 100644 web-ui/src/app/components/transaction-raw/transaction-raw.component.css delete mode 100644 web-ui/src/app/components/transaction/transaction.component.css delete mode 100644 web-ui/src/app/components/transaction/transaction.component.html rename web-ui/src/app/components/{ => transactions}/transaction-details/transaction-details.component.css (100%) rename web-ui/src/app/components/{ => transactions}/transaction-details/transaction-details.component.html (66%) rename web-ui/src/app/components/{ => transactions}/transaction-details/transaction-details.component.spec.ts (81%) rename web-ui/src/app/components/{ => transactions}/transaction-details/transaction-details.component.ts (77%) create mode 100644 web-ui/src/app/components/transactions/transaction-list/transaction-list.component.css create mode 100644 web-ui/src/app/components/transactions/transaction-list/transaction-list.component.html create mode 100644 web-ui/src/app/components/transactions/transaction-list/transaction-list.component.spec.ts create mode 100644 web-ui/src/app/components/transactions/transaction-list/transaction-list.component.ts create mode 100644 web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.css rename web-ui/src/app/components/{ => transactions}/transaction-raw/transaction-raw.component.html (73%) rename web-ui/src/app/components/{ => transactions}/transaction-raw/transaction-raw.component.spec.ts (84%) rename web-ui/src/app/components/{ => transactions}/transaction-raw/transaction-raw.component.ts (75%) create mode 100644 web-ui/src/app/components/transactions/transaction/transaction.component.css create mode 100644 web-ui/src/app/components/transactions/transaction/transaction.component.html rename web-ui/src/app/components/{ => transactions}/transaction/transaction.component.spec.ts (100%) rename web-ui/src/app/components/{ => transactions}/transaction/transaction.component.ts (100%) create mode 100644 web-ui/src/app/components/transactions/transactions-routing.module.ts create mode 100644 web-ui/src/app/components/transactions/transactions.component.html create mode 100644 web-ui/src/app/components/transactions/transactions.component.ts create mode 100644 web-ui/src/app/components/transactions/transactions.module.ts create mode 100644 web-ui/src/app/models/tposcontract.ts create mode 100644 web-ui/src/app/models/tposnode.ts create mode 100644 web-ui/src/app/pipes/pipes.module.ts create mode 100644 web-ui/src/app/services/tposnodes.service.ts create mode 100644 web-ui/src/app/utils.spec.ts create mode 100644 web-ui/src/assets/xsn-logo.png create mode 100644 web-ui/src/assets/xsn-logo@2x.png diff --git a/web-ui/src/app/app-routing.module.ts b/web-ui/src/app/app-routing.module.ts index f6b3628f..79352934 100644 --- a/web-ui/src/app/app-routing.module.ts +++ b/web-ui/src/app/app-routing.module.ts @@ -1,20 +1,17 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { HomeComponent } from './components/home/home.component'; -import { TransactionComponent } from './components/transaction/transaction.component'; -import { AddressDetailsComponent } from './components/address-details/address-details.component'; -import { BlockComponent } from './components/block/block.component'; -import { MasternodeDetailsComponent } from './components/masternode-details/masternode-details.component'; import { TrezorConnectComponent } from './components/trezor-connect/trezor-connect.component'; +import { CalculatorComponent } from './components/calculator/calculator.component'; const routes: Routes = [ - { path: '', component: HomeComponent }, - { path: 'addresses/:address', component: AddressDetailsComponent }, - { path: 'blocks/:query', component: BlockComponent }, - { path: 'transactions/:txid', component: TransactionComponent }, - { path: 'masternodes/:ip', component: MasternodeDetailsComponent }, + { path: '', loadChildren: './components/home/home.module#HomeModule' }, + { path: 'blocks', loadChildren: './components/blocks/blocks.module#BlocksModule' }, + { path: 'transactions', loadChildren: './components/transactions/transactions.module#TransactionsModule' }, + { path: 'nodes', loadChildren: './components/nodes/nodes.module#NodesModule' }, + { path: 'addresses', loadChildren: './components/addresses/addresses.module#AddressesModule' }, { path: 'trezor', component: TrezorConnectComponent }, + { path: 'calculator', component: CalculatorComponent }, { path: '**', redirectTo: '' } ]; diff --git a/web-ui/src/app/app.component.html b/web-ui/src/app/app.component.html index 754cb953..359bb35c 100644 --- a/web-ui/src/app/app.component.html +++ b/web-ui/src/app/app.component.html @@ -1,6 +1,8 @@ -
- +
+
+ +
- + \ No newline at end of file diff --git a/web-ui/src/app/app.component.ts b/web-ui/src/app/app.component.ts index 603025e1..e099f2d7 100644 --- a/web-ui/src/app/app.component.ts +++ b/web-ui/src/app/app.component.ts @@ -73,7 +73,7 @@ export class AppComponent implements OnInit { 'TPoS': 'Trustless Proof of Stake', // messages - 'message.unavailable': 'Unavailable', + 'message.unavailable': '-', 'message.serverUnavailable': 'The server unavailable, please try again in a minute', 'message.unknownErrors': 'Unknown error, please try again in a minute', 'message.transactionNotFound': 'Transaction not found', @@ -81,6 +81,7 @@ export class AppComponent implements OnInit { 'message.blockNotFound': 'Block not found', 'message.masternodeNotFound': 'Masternode not found', 'message.loadingLatestBlocks': 'Loading latest blocks...', + 'message.loadingTransactions': 'Loading transactions...', 'message.loadingRichestAddresses': 'Loading richest addresses...', 'message.transactionsNotAvailable': 'The transactions are not available, please try again in some minutes', 'message.totalSupply': 'The total number of coins generated (excluding the burned coins)', @@ -98,11 +99,11 @@ export class AppComponent implements OnInit { 'action.verifyAddress': 'Show full address', // labels - 'label.searchField': 'Transaction id, Blockhash, Block number, Address, IP address', + 'label.searchField': 'Search by transaction ID, address, blockhash, block height, IP address', 'label.transactionId': 'Transaction Id', 'label.confirmations': 'Confirmations', 'label.blockhash': 'Block Hash', - 'label.blocktime': 'Block Time', + 'label.blocktime': 'Time', 'label.medianTime': 'Median Time', 'label.noInput': 'No input', 'label.coinbase': 'Coinbase', @@ -120,13 +121,24 @@ export class AppComponent implements OnInit { 'label.normalFee': 'Normal', 'label.lowFee': 'Low', 'label.satoshis': 'Satoshis', + 'label.showMore': 'Show More', + 'label.currentInflation': 'Current Inflation', + 'label.masternodeROI': 'Masternode ROI', + 'label.stakingROI': 'Staking ROI', + 'label.coinsInMasternodes': 'Coins in Masternodes', + 'label.tposNodes': 'TPoS Nodes', + 'label.coinsStaking': 'Coins Staking', + 'label.coinsTrustlesslyStaking': 'Coins Trustlessly Staking', 'label.address': 'Address', + 'label.addresses': 'Addresses', + 'label.ip': 'IP', 'label.balance': 'Balance', 'label.available': 'Available', 'label.received': 'Received', 'label.spent': 'Spent', 'label.transactionCount': 'Transactions', + 'label.transactinoLabel': 'Transactions', 'label.blockType': 'Block type', 'label.next': 'Next', @@ -138,13 +150,18 @@ export class AppComponent implements OnInit { 'label.bits': 'Bits', 'label.chainwork': 'Chainwork', 'label.difficulty': 'Difficulty', - 'label.transactions': 'Transactions', + 'label.transactions': 'TXS', 'label.rewards': 'Rewards', 'label.coinstake': 'Coinstake', 'label.masternode': 'Masternode', 'label.amount': 'Amount', 'label.blockReward': 'Block reward', + 'label.txHash': 'TX Hash', + 'label.type': 'Type', + 'label.result': 'Result', + 'label.time': 'Time', + 'label.tposContract': 'Contract', 'label.owner': 'Owner', 'label.merchant': 'Merchant', @@ -152,7 +169,9 @@ export class AppComponent implements OnInit { 'label.summary': 'Summary', 'label.block': 'Block', 'label.transaction': 'Transaction', - 'label.height': 'Block height', + 'label.height': 'Height', + 'label.inflation': 'Inflation', + 'label.price': 'Price', 'label.extractedBy': 'Extracted by', 'label.latestBlocks': 'Latest 10 blocks', 'label.totalSupply': 'Total supply', @@ -165,12 +184,41 @@ export class AppComponent implements OnInit { 'label.protocol': 'Protocol', 'label.status': 'Status', 'label.lastSeen': 'Last seen', - 'label.payee': 'Payee', - 'label.active': 'Active', + 'label.payee': 'Payee Address', + 'label.active': 'Active Since', 'label.details': 'Details', 'label.raw': 'Raw', 'label.date': 'Date', - 'label.more': 'More' + 'label.more': 'More', + 'label.enabled': 'Enabled', + 'label.distributedAcross': 'Distributed Across', + 'label.protocols': 'Protocols', + + 'label.isStakingCoinsTrustlessly': 'Is this address staking coins trustlessly?', + 'label.contractState': 'Contract state', + 'label.merchantAddress': 'Merchant address', + 'label.merchantCommission': 'Merchant commission', + 'label.contractTxid': 'Contract txid', + + 'label.yes': 'Yes', + 'label.no': 'No', + 'label.tposDetails': 'TPoS Details', + + 'label.calculator': 'Calculator', + 'label.xsnAmountHold': 'Amount of XSN you hold', + 'label.optimalSetup': 'Optimal setup', + 'label.masternodeAnd': 'Masternode(s) and', + 'label.xsnStaking': 'XSN staking', + 'label.masternodeStakingRemaining': 'Masternode(s) + staking remaining', + 'label.moreProfitable': 'is 33% more profitable', + 'label.stakingAllCoins': 'Staking all coins', + 'label.daily': 'Daily', + 'label.monthly': 'Monthly', + 'label.yearly': 'Yearly', + 'label.days': 'days', + 'label.waitingTimePerReward': 'Waiting time per reward (est.)', + 'label.requiredForMasternode': 'Required for Masternode', + 'label.rewardPerBlock': 'Reward per block' }; } } diff --git a/web-ui/src/app/app.module.ts b/web-ui/src/app/app.module.ts index 4577c524..40d9931d 100644 --- a/web-ui/src/app/app.module.ts +++ b/web-ui/src/app/app.module.ts @@ -1,22 +1,15 @@ -import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; -import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppRoutingModule } from './app-routing.module'; -import { AlertModule, BsDropdownModule, CollapseModule, TooltipModule, ModalModule } from 'ngx-bootstrap'; +import { AlertModule } from 'ngx-bootstrap'; import { TabsModule } from 'ngx-bootstrap/tabs'; import { TranslateModule } from '@ngx-translate/core'; import { ToastrModule } from 'ngx-toastr'; +import { FormsModule } from '@angular/forms'; import { NgHttpLoaderModule } from 'ng-http-loader'; -import { NgxPaginationModule } from 'ngx-pagination'; - -import { MomentModule } from 'ngx-moment'; - -import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { AddressesService } from './services/addresses.service'; import { BalancesService } from './services/balances.service'; @@ -31,71 +24,30 @@ import { TransactionsService } from './services/transactions.service'; import { TrezorRepositoryService } from './services/trezor-repository.service'; import { AppComponent } from './app.component'; -import { HomeComponent } from './components/home/home.component'; -import { FooterComponent } from './components/footer/footer.component'; -import { NavbarComponent } from './components/navbar/navbar.component'; -import { TransactionDetailsComponent } from './components/transaction-details/transaction-details.component'; -import { FinderComponent } from './components/finder/finder.component'; -import { AddressDetailsComponent } from './components/address-details/address-details.component'; -import { BlockDetailsComponent } from './components/block-details/block-details.component'; -import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component'; -import { TickerComponent } from './components/ticker/ticker.component'; -import { RichestAddressesComponent } from './components/richest-addresses/richest-addresses.component'; -import { MasternodesComponent } from './components/masternodes/masternodes.component'; -import { MasternodeDetailsComponent } from './components/masternode-details/masternode-details.component'; -import { TransactionRawComponent } from './components/transaction-raw/transaction-raw.component'; -import { TransactionComponent } from './components/transaction/transaction.component'; -import { BlockComponent } from './components/block/block.component'; -import { BlockRawComponent } from './components/block-raw/block-raw.component'; -import { ExplorerDatetimePipe } from './pipes/explorer-datetime.pipe'; -import { ExplorerCurrencyPipe } from './pipes/explorer-currency.pipe'; -import { ExplorerAmountPipe } from './pipes/explorer-amount.pipe'; import { TrezorConnectComponent } from './components/trezor-connect/trezor-connect.component'; +import { SharedModule } from './components/shared/shared.module'; +import { PipesModule } from './pipes/pipes.module'; +import { TposnodesService } from './services/tposnodes.service'; +import { CalculatorComponent } from './components/calculator/calculator.component'; @NgModule({ declarations: [ AppComponent, - HomeComponent, - FooterComponent, - NavbarComponent, - TransactionDetailsComponent, - FinderComponent, - AddressDetailsComponent, - BlockDetailsComponent, - LatestBlocksComponent, - TickerComponent, - RichestAddressesComponent, - MasternodesComponent, - MasternodeDetailsComponent, - TransactionRawComponent, - TransactionComponent, - BlockComponent, - BlockRawComponent, - ExplorerDatetimePipe, - ExplorerCurrencyPipe, - ExplorerAmountPipe, - TrezorConnectComponent + TrezorConnectComponent, + CalculatorComponent ], imports: [ AppRoutingModule, - AlertModule.forRoot(), - BsDropdownModule.forRoot(), - CollapseModule.forRoot(), - TooltipModule.forRoot(), - ModalModule.forRoot(), - CommonModule, + FormsModule, + SharedModule, + PipesModule, BrowserAnimationsModule, ToastrModule.forRoot(), - BrowserModule, - FormsModule, - ReactiveFormsModule, HttpClientModule, NgHttpLoaderModule, TranslateModule.forRoot(), - NgxPaginationModule, TabsModule.forRoot(), - MomentModule, - InfiniteScrollModule + AlertModule.forRoot() ], providers: [ AddressesService, @@ -104,6 +56,7 @@ import { TrezorConnectComponent } from './components/trezor-connect/trezor-conne ErrorService, LanguageService, MasternodesService, + TposnodesService, NavigatorService, NotificationService, TickerService, diff --git a/web-ui/src/app/components/address-details/address-details.component.html b/web-ui/src/app/components/address-details/address-details.component.html deleted file mode 100644 index 043c8464..00000000 --- a/web-ui/src/app/components/address-details/address-details.component.html +++ /dev/null @@ -1,89 +0,0 @@ -
-
-
-
- {{'message.addressNotFound' | translate}} -
-
-
-

{{'label.address' | translate}}

-

{{ addressLabel[addressString]}}

- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
{{'label.summary' | translate}}
{{'label.address' | translate}}{{addressString}}
{{'label.balance' | translate}}{{address.available | explorerCurrency}}
{{'label.received' | translate}}{{address.received | explorerCurrency}}
{{'label.spent' | translate}}{{address.spent | explorerCurrency}}
-
-
-
- -
- -
-

{{'label.transactions' | translate}}

-
-
- - - - - - - - - - - - - - - - - - - - - - -
#{{'label.transaction' | translate}}{{'label.blockhash' | translate}}{{'label.date' | translate}}{{'label.value' | translate}}{{'label.size' | translate}}
{{index + 1}} - {{item.id | slice:0:15}}... - - {{item.blockhash | slice:0:15}}... - {{item.time * 1000 | explorerDatetime}}{{renderValue(item) | explorerCurrency}}{{item.size}} bytes
-
-
-
-
-
diff --git a/web-ui/src/app/components/addresses/address-details/address-details.component.css b/web-ui/src/app/components/addresses/address-details/address-details.component.css new file mode 100644 index 00000000..75b2ac61 --- /dev/null +++ b/web-ui/src/app/components/addresses/address-details/address-details.component.css @@ -0,0 +1,5 @@ +.transaction-table-wrapper { + width: 100%; + padding: 0; + margin-top: 15px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/addresses/address-details/address-details.component.html b/web-ui/src/app/components/addresses/address-details/address-details.component.html new file mode 100644 index 00000000..bdaf45d4 --- /dev/null +++ b/web-ui/src/app/components/addresses/address-details/address-details.component.html @@ -0,0 +1,108 @@ +
+
+ + + +   {{addressLabel[addressString] ? addressLabel[addressString] : 'label.address' | translate}} +
+ +
+
+
+ {{'message.addressNotFound' | translate}} +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ {{'label.summary' | translate}} +

+
{{'label.address' | translate}}{{addressString}}
{{'label.balance' | translate}}{{address.available | explorerCurrency}}
{{'label.received' | translate}}{{address.received | explorerCurrency}}
{{'label.spent' | translate}}{{address.spent | explorerCurrency}}
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ {{'label.tposDetails' | translate}} ({{'label.enabled' | translate | uppercase}}) +

+
{{'label.isStakingCoinsTrustlessly' | translate}}{{'label.yes' | translate}}
{{'label.contractState' | translate}}{{tposContract ? tposContract.state : ''}}
{{'label.merchantAddress' | translate}} + {{tposContract.merchant}} +
{{'label.merchantCommission' | translate}}{{tposContract ? tposContract.merchantCommission : ''}}
{{'label.contractTxid' | translate}} + {{tposContract.txid}} +
+
+
+
+ +
+ +

{{'label.transactinoLabel' | translate}}

+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/address-details/address-details.component.spec.ts b/web-ui/src/app/components/addresses/address-details/address-details.component.spec.ts similarity index 71% rename from web-ui/src/app/components/address-details/address-details.component.spec.ts rename to web-ui/src/app/components/addresses/address-details/address-details.component.spec.ts index 80af318f..cb24884a 100644 --- a/web-ui/src/app/components/address-details/address-details.component.spec.ts +++ b/web-ui/src/app/components/addresses/address-details/address-details.component.spec.ts @@ -1,14 +1,14 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { AddressDetailsComponent } from './address-details.component'; -import { ExplorerCurrencyPipe } from '../../pipes/explorer-currency.pipe'; -import { ExplorerDatetimePipe } from '../../pipes/explorer-datetime.pipe'; +import { ExplorerCurrencyPipe } from '../../../pipes/explorer-currency.pipe'; +import { ExplorerDatetimePipe } from '../../../pipes/explorer-datetime.pipe'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { AddressesService } from '../../services/addresses.service'; -import { ErrorService } from '../../services/error.service'; +import { AddressesService } from '../../../services/addresses.service'; +import { ErrorService } from '../../../services/error.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -17,11 +17,13 @@ describe('AddressDetailsComponent', () => { let component: AddressDetailsComponent; let fixture: ComponentFixture; - const addressesServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('AddressesService', ['get', 'getAddressesV2']); + const addressesServiceSpy: jasmine.SpyObj = + jasmine.createSpyObj('AddressesService', ['get', 'getAddressesV2', 'getTposContracts']); const errorServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('ErrorService', ['renderServerErrors']); beforeEach(async(() => { addressesServiceSpy.get.and.returnValue(Observable.create()); + addressesServiceSpy.getTposContracts.and.returnValue(Observable.create()); TestBed.configureTestingModule({ declarations: [ @@ -39,7 +41,7 @@ describe('AddressDetailsComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/address-details/address-details.component.ts b/web-ui/src/app/components/addresses/address-details/address-details.component.ts similarity index 50% rename from web-ui/src/app/components/address-details/address-details.component.ts rename to web-ui/src/app/components/addresses/address-details/address-details.component.ts index 67e0a01f..6813972e 100644 --- a/web-ui/src/app/components/address-details/address-details.component.ts +++ b/web-ui/src/app/components/addresses/address-details/address-details.component.ts @@ -1,15 +1,17 @@ -import {tap} from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; import { Component, OnInit, HostListener } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Balance } from '../../models/balance'; -import { AddressesService } from '../../services/addresses.service'; -import { ErrorService } from '../../services/error.service'; -import { LightWalletTransaction } from '../..//models/light-wallet-transaction'; +import { Balance } from '../../../models/balance'; +import { AddressesService } from '../../../services/addresses.service'; +import { ErrorService } from '../../../services/error.service'; +import { LightWalletTransaction } from '../../../models/light-wallet-transaction'; -import { getNumberOfRowsForScreen } from '../../utils'; -import { addressLabels } from '../../config'; +import { getNumberOfRowsForScreen } from '../../../utils'; +import { addressLabels } from '../../../config'; +import { TposContract } from '../../../models/tposcontract'; +import { WrappedResult } from '../../../models/wrapped-result'; @Component({ selector: 'app-address-details', @@ -21,6 +23,7 @@ export class AddressDetailsComponent implements OnInit { address: Balance; addressString: string; addressLabel = addressLabels; + tposContract: TposContract = null; // pagination limit = 30; @@ -34,29 +37,23 @@ export class AddressDetailsComponent implements OnInit { ngOnInit() { const height = this.getScreenSize(); this.limit = getNumberOfRowsForScreen(height); - this.addressString = this.route.snapshot.paramMap.get('address'); + this.addressString = this.route.snapshot.paramMap.get('id'); this.addressesService.get(this.addressString).subscribe( response => this.onAddressRetrieved(response), response => this.onError(response) ); + this.addressesService.getTposContracts(this.addressString).subscribe( + response => this.onTposContractsReceived(response), + response => this.onError(response) + ); } private onAddressRetrieved(response: Balance) { this.address = response; - this.load(); } - load() { - const order = 'desc'; - let lastSeenTxid = ''; - if (this.items.length > 0) { - lastSeenTxid = this.items[this.items.length - 1].id; - } - - this.addressesService - .getTransactionsV2(this.addressString, this.limit, lastSeenTxid, order).pipe( - tap(response => this.items.push(...response.data))) - .subscribe(); + private onTposContractsReceived(response: WrappedResult) { + this.tposContract = response.data[0]; } @HostListener('window:resize', ['$event']) @@ -67,23 +64,4 @@ export class AddressDetailsComponent implements OnInit { private onError(response: any) { this.errorService.renderServerErrors(null, response); } - - renderValue(tx: LightWalletTransaction): string { - const spent = tx - .inputs - .map(input => input.value) - .reduce((a, b) => a + b, 0); - - const received = tx - .outputs - .map(output => output.value) - .reduce((a, b) => a + b, 0); - - const diff = Math.abs(received - spent); - if (received >= spent) { - return '+' + diff; - } else { - return '-' + diff; - } - } } diff --git a/web-ui/src/app/components/addresses/address-list/address-list.component.css b/web-ui/src/app/components/addresses/address-list/address-list.component.css new file mode 100644 index 00000000..0ea14c57 --- /dev/null +++ b/web-ui/src/app/components/addresses/address-list/address-list.component.css @@ -0,0 +1,15 @@ +.new-address { + animation: blinker 1s linear; +} + +@keyframes blinker { + 50% { + background-color: #FFFFFF08; + } +} + +.address-table-wrapper { + width: 100%; + padding: 0; + margin-top: 15px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/addresses/address-list/address-list.component.html b/web-ui/src/app/components/addresses/address-list/address-list.component.html new file mode 100644 index 00000000..94a5293f --- /dev/null +++ b/web-ui/src/app/components/addresses/address-list/address-list.component.html @@ -0,0 +1,43 @@ +
+
+ {{'label.addresses' | translate}} +
+
+ +
+

{{'label.richestAddresses' | translate}}

+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
#{{'label.address' | translate | uppercase}}{{'label.amount' | translate | uppercase}}{{'label.percentOfCoins' | translate | uppercase}}{{'label.addressLabel' | translate | uppercase}}
{{index + 1}} + {{item.address}} + {{item.available | explorerCurrency}}{{'message.unavailable' | translate}} + {{getPercent(item) | number:'1.2-2'}} % + + {{addressLabel[item.address]}} +
+
+
+
diff --git a/web-ui/src/app/components/richest-addresses/richest-addresses.component.spec.ts b/web-ui/src/app/components/addresses/address-list/address-list.component.spec.ts similarity index 70% rename from web-ui/src/app/components/richest-addresses/richest-addresses.component.spec.ts rename to web-ui/src/app/components/addresses/address-list/address-list.component.spec.ts index 54f2809e..bada6c47 100644 --- a/web-ui/src/app/components/richest-addresses/richest-addresses.component.spec.ts +++ b/web-ui/src/app/components/addresses/address-list/address-list.component.spec.ts @@ -1,21 +1,21 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { RichestAddressesComponent } from './richest-addresses.component'; -import { ExplorerCurrencyPipe } from '../../pipes/explorer-currency.pipe'; +import { AddressListComponent } from './address-list.component'; +import { ExplorerCurrencyPipe } from '../../../pipes/explorer-currency.pipe'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { TranslateModule } from '@ngx-translate/core'; -import { BalancesService } from '../../services/balances.service'; -import { TickerService } from '../../services/ticker.service'; -import { NotificationService } from '../../services/notification.service'; +import { BalancesService } from '../../../services/balances.service'; +import { TickerService } from '../../../services/ticker.service'; +import { NotificationService } from '../../../services/notification.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; -describe('RichestAddressesComponent', () => { - let component: RichestAddressesComponent; - let fixture: ComponentFixture; +describe('AddressListComponent', () => { + let component: AddressListComponent; + let fixture: ComponentFixture; const balancesServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('BalancesService', ['getHighest']); const tickerServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TickerService', ['get']); @@ -27,7 +27,7 @@ describe('RichestAddressesComponent', () => { TestBed.configureTestingModule({ declarations: [ - RichestAddressesComponent, + AddressListComponent, ExplorerCurrencyPipe ], imports: [ @@ -45,7 +45,7 @@ describe('RichestAddressesComponent', () => { })); beforeEach(() => { - fixture = TestBed.createComponent(RichestAddressesComponent); + fixture = TestBed.createComponent(AddressListComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/web-ui/src/app/components/richest-addresses/richest-addresses.component.ts b/web-ui/src/app/components/addresses/address-list/address-list.component.ts similarity index 56% rename from web-ui/src/app/components/richest-addresses/richest-addresses.component.ts rename to web-ui/src/app/components/addresses/address-list/address-list.component.ts index 2b61edec..42d612b5 100644 --- a/web-ui/src/app/components/richest-addresses/richest-addresses.component.ts +++ b/web-ui/src/app/components/addresses/address-list/address-list.component.ts @@ -2,22 +2,21 @@ import { tap } from 'rxjs/operators'; import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; -import { Balance } from '../../models/balance'; +import { Balance } from '../../../models/balance'; -import { BalancesService } from '../../services/balances.service'; -import { TickerService } from '../../services/ticker.service'; -import { NotificationService } from '../../services/notification.service'; -import { ServerStats } from '../../models/ticker'; +import { BalancesService } from '../../../services/balances.service'; +import { TickerService } from '../../../services/ticker.service'; +import { ServerStats } from '../../../models/ticker'; -import { getNumberOfRowsForScreen } from '../../utils'; -import { addressLabels } from '../../config'; +import { getNumberOfRowsForScreen } from '../../../utils'; +import { addressLabels } from '../../../config'; @Component({ - selector: 'app-richest-addresses', - templateUrl: './richest-addresses.component.html', - styleUrls: ['./richest-addresses.component.css'] + selector: 'app-address-list', + templateUrl: './address-list.component.html', + styleUrls: ['./address-list.component.css'] }) -export class RichestAddressesComponent implements OnInit { +export class AddressListComponent implements OnInit { // ticker ticker: ServerStats; @@ -30,15 +29,13 @@ export class RichestAddressesComponent implements OnInit { constructor( private balancesService: BalancesService, - private tickerService: TickerService, - private notificationService: NotificationService) { } + private tickerService: TickerService) { } ngOnInit() { const height = this.getScreenSize(); - // this.limit = getNumberOfRowsForScreen(height); + this.limit = getNumberOfRowsForScreen(height); this.load(); this.tickerService.get().subscribe(response => this.ticker = response); - this.notificationService.warning("Mount our explorer (https://github.com/X9Developers/block-explorer/) on your computer and query the DB directly to view the full address database"); } load() { @@ -47,9 +44,9 @@ export class RichestAddressesComponent implements OnInit { // here leads to inaccurate results. // // for now, let's limit this to 100 results only. - if (this.items.length >= 100) { - return; - } + // if (this.items.length >= 100) { + // return; + // } let lastSeenAddress = ''; if (this.items.length > 0) { diff --git a/web-ui/src/app/components/addresses/addresses-routing.module.ts b/web-ui/src/app/components/addresses/addresses-routing.module.ts new file mode 100644 index 00000000..daa35a26 --- /dev/null +++ b/web-ui/src/app/components/addresses/addresses-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AddressesComponent } from './addresses.component'; +import { AddressDetailsComponent } from './address-details/address-details.component'; +import { AddressListComponent } from './address-list/address-list.component'; + +const routes: Routes = [ + { + path: '', + component: AddressesComponent, + children: [ + { + path: ':id', + component: AddressDetailsComponent + }, + { + path: '', + component: AddressListComponent + } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AddressesRoutingModule { } diff --git a/web-ui/src/app/components/addresses/addresses.component.html b/web-ui/src/app/components/addresses/addresses.component.html new file mode 100644 index 00000000..c5a194e2 --- /dev/null +++ b/web-ui/src/app/components/addresses/addresses.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/web-ui/src/app/components/addresses/addresses.component.ts b/web-ui/src/app/components/addresses/addresses.component.ts new file mode 100644 index 00000000..cbfab52f --- /dev/null +++ b/web-ui/src/app/components/addresses/addresses.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-addresses', + templateUrl: './addresses.component.html' +}) +export class AddressesComponent { } diff --git a/web-ui/src/app/components/addresses/addresses.module.ts b/web-ui/src/app/components/addresses/addresses.module.ts new file mode 100644 index 00000000..640693d2 --- /dev/null +++ b/web-ui/src/app/components/addresses/addresses.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { SharedModule } from '../shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { MomentModule } from 'ngx-moment'; +import { TabsModule, AlertModule } from 'ngx-bootstrap'; +import { PipesModule } from '../../pipes/pipes.module'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { AddressesRoutingModule } from './addresses-routing.module'; +import { AddressesComponent } from './addresses.component'; +import { AddressDetailsComponent } from './address-details/address-details.component'; +import { AddressListComponent } from './address-list/address-list.component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + AddressesRoutingModule, + SharedModule, + PipesModule, + TranslateModule, + MomentModule, + TabsModule, + AlertModule, + InfiniteScrollModule + ], + declarations: [ + AddressesComponent, + AddressDetailsComponent, + AddressListComponent + ] +}) + +export class AddressesModule { } diff --git a/web-ui/src/app/components/block-details/block-details.component.html b/web-ui/src/app/components/block-details/block-details.component.html deleted file mode 100644 index 56df6105..00000000 --- a/web-ui/src/app/components/block-details/block-details.component.html +++ /dev/null @@ -1,339 +0,0 @@ -
- -
- {{'message.blockNotFound' | translate}} -
- -
-
- -

{{'label.block' | translate}} #{{blockDetails.block.height}}

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{'label.summary' | translate}}
{{'label.blockType' | translate}}{{getBlockType(blockDetails)}}
{{'label.tposContract' | translate}} - {{blockDetails.block.tposContract}} -
{{'label.blockhash' | translate}}{{blockDetails.block.hash}}
{{'label.merkleRoot' | translate}}{{blockDetails.block.merkleRoot}}
{{'label.confirmations' | translate}}{{blockDetails.block.confirmations}}
{{'label.size' | translate}}{{blockDetails.block.size}} bytes
{{'label.version' | translate}}{{blockDetails.block.version}}
{{'label.nonce' | translate}}{{blockDetails.block.nonce}}
{{'label.bits' | translate}}{{blockDetails.block.bits}}
{{'label.chainwork' | translate}}{{blockDetails.block.chainwork}}
{{'label.difficulty' | translate}}{{blockDetails.block.difficulty}}
{{'label.blocktime' | translate}}{{blockDetails.block.time * 1000 | explorerDatetime}}
{{'label.medianTime' | translate}}{{blockDetails.block.medianTime * 1000 | explorerDatetime}}
- - {{'label.previous' | translate}} ({{blockDetails.block.height - 1}}) - - - - {{'label.next' | translate}} ({{blockDetails.block.height + 1}}) - -
-
-
- -
- - -
- - -
-
-
- - - - - - - - - - - - - - -
{{'label.blockReward' | translate}}{{blockDetails.rewards.reward.value | explorerCurrency}}
{{'label.address' | translate}} - {{blockDetails.rewards.reward.address}} -
-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{'label.rewards' | translate}}{{getPoSTotalReward(blockDetails) | explorerCurrency}}
- {{'label.coinstake' | translate}} -
{{'label.amount' | translate}}{{blockDetails.rewards.coinstake.value | explorerCurrency}}
{{'label.address' | translate}} - {{blockDetails.rewards.coinstake.address}} -
- {{'label.masternode' | translate}} -
{{'label.amount' | translate}}{{blockDetails.rewards.masternode.value | explorerCurrency}}
{{'label.address' | translate}} - {{blockDetails.rewards.masternode.address}} -
-
-
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{'label.rewards' | translate}}{{getTPoSTotalReward(blockDetails) | explorerCurrency}}
- {{'label.owner' | translate}} -
{{'label.amount' | translate}}{{blockDetails.rewards.owner.value | explorerCurrency}}
{{'label.address' | translate}} - {{blockDetails.rewards.owner.address}} -
- {{'label.merchant' | translate}} -
{{'label.amount' | translate}}{{blockDetails.rewards.merchant.value | explorerCurrency}}
{{'label.address' | translate}} - {{blockDetails.rewards.merchant.address}} -
- {{'label.masternode' | translate}} -
{{'label.amount' | translate}}{{blockDetails.rewards.masternode.value | explorerCurrency}}
{{'label.address' | translate}} - {{blockDetails.rewards.masternode.address}} -
-
-
- -
- - - -
- {{'message.transactionsNotAvailable' | translate}} -
- - -
- -
- -
- - - - - - - - - - - - - - - - - - - - -
#{{'label.transaction' | translate}}{{'label.date' | translate}}{{'label.value' | translate}}{{'label.size' | translate}}
{{index + 1}} - {{item.id | slice:0:35}}... - {{item.time * 1000 | explorerDatetime}}{{item.sent | explorerCurrency}}{{item.size}} bytes
-
- - -
- -
- -
- - - - - - - - - - - - - - -
#{{'label.transaction' | translate}}
{{index + 1}} - {{item | slice:0:35}}... -
-
- -
- -
- -
-
diff --git a/web-ui/src/app/components/block-raw/block-raw.component.css b/web-ui/src/app/components/block-raw/block-raw.component.css deleted file mode 100644 index e69de29b..00000000 diff --git a/web-ui/src/app/components/block/block.component.css b/web-ui/src/app/components/block/block.component.css deleted file mode 100644 index e69de29b..00000000 diff --git a/web-ui/src/app/components/block/block.component.html b/web-ui/src/app/components/block/block.component.html deleted file mode 100644 index 283915f8..00000000 --- a/web-ui/src/app/components/block/block.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
-
- - - - - - - - -
-
-
diff --git a/web-ui/src/app/components/block/block.component.spec.ts b/web-ui/src/app/components/block/block.component.spec.ts deleted file mode 100644 index 5bac74d8..00000000 --- a/web-ui/src/app/components/block/block.component.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BlockComponent } from './block.component'; - -import { TranslateModule } from '@ngx-translate/core'; - -import { NO_ERRORS_SCHEMA, } from '@angular/core'; - -describe('BlockComponent', () => { - let component: BlockComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ - BlockComponent - ], - imports: [ - TranslateModule.forRoot() - ], - schemas: [NO_ERRORS_SCHEMA] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(BlockComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/web-ui/src/app/components/block/block.component.ts b/web-ui/src/app/components/block/block.component.ts deleted file mode 100644 index 95347d80..00000000 --- a/web-ui/src/app/components/block/block.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-block', - templateUrl: './block.component.html', - styleUrls: ['./block.component.css'] -}) -export class BlockComponent implements OnInit { - - currentView = 'details'; - - constructor() { } - - ngOnInit() { - } - - selectView(view: string) { - this.currentView = view; - } - - isSelected(view: string): boolean { - return this.currentView === view; - } -} diff --git a/web-ui/src/app/components/blocks/block-details/block-details.component.css b/web-ui/src/app/components/blocks/block-details/block-details.component.css new file mode 100644 index 00000000..5d8ed592 --- /dev/null +++ b/web-ui/src/app/components/blocks/block-details/block-details.component.css @@ -0,0 +1,37 @@ +.block-details-content { + font-size: 12px; +} + +.block-details-left-side { + border-right: 1px solid #282D3C; + margin-right: -1px; +} + +.block-details-right-side { + border-left: 1px solid #282D3C; +} + +@media screen and (max-width: 1199px) { + + .block-details-left-side, + .block-details-right-side { + border: none; + margin-right: 0; + } + + .block-details-right-side>div:first-child { + border-top: 1px solid #282D3C; + } +} + +.block-details-label-row { + font-size: 14px; +} + +.block-details-transactions-container { + padding-top: 10px; +} + +.block-details-transactions-container>div:first-child { + border-top: 1px solid #282D3C; +} \ No newline at end of file diff --git a/web-ui/src/app/components/blocks/block-details/block-details.component.html b/web-ui/src/app/components/blocks/block-details/block-details.component.html new file mode 100644 index 00000000..607deb48 --- /dev/null +++ b/web-ui/src/app/components/blocks/block-details/block-details.component.html @@ -0,0 +1,306 @@ +
+ +
+ {{'message.blockNotFound' | translate}} +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{'label.summary' | translate | uppercase}} +
{{'label.blockType' | translate}}{{getBlockType(blockDetails)}}
{{'label.tposContract' | translate}} + {{blockDetails.block.tposContract}} +
{{'label.blockhash' | translate}}{{blockDetails.block.hash}}
{{'label.merkleRoot' | translate}}{{blockDetails.block.merkleRoot}}
{{'label.confirmations' | translate}}{{blockDetails.block.confirmations}}
{{'label.size' | translate}}{{blockDetails.block.size}} bytes
{{'label.version' | translate}}{{blockDetails.block.version}}
{{'label.nonce' | translate}}{{blockDetails.block.nonce}}
{{'label.bits' | translate}}{{blockDetails.block.bits}}
{{'label.chainwork' | translate}}{{blockDetails.block.chainwork}}
{{'label.difficulty' | translate}}{{blockDetails.block.difficulty}}
{{'label.blocktime' | translate}}{{blockDetails.block.time * 1000 | explorerDatetime}}
{{'label.medianTime' | translate}}{{blockDetails.block.medianTime * 1000 | explorerDatetime}}
+
+
+ + +
+ + +
+
+ + + + + + + + + + + +
+ {{'label.blockReward' | translate | uppercase}}
+ {{blockDetails.rewards.reward.value | explorerCurrency}} +
{{'label.address' | translate}} + {{blockDetails.rewards.reward.address}} +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{'label.rewards' | translate | uppercase}}
+ {{getPoSTotalReward(blockDetails) | explorerCurrency}} +
+ {{'label.coinstake' | translate | uppercase}} +
{{'label.amount' | translate}}{{blockDetails.rewards.coinstake.value | explorerCurrency}}
{{'label.address' | translate}} + {{blockDetails.rewards.coinstake.address}} +
+ {{'label.masternode' | translate | uppercase}} +
{{'label.amount' | translate}}{{blockDetails.rewards.masternode.value | explorerCurrency}}
{{'label.address' | translate}} + {{blockDetails.rewards.masternode.address}} +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{'label.rewards' | translate | uppercase}}
+ {{getTPoSTotalReward(blockDetails) | explorerCurrency}} +
+ {{'label.owner' | translate | uppercase}} +
{{'label.amount' | translate}}{{blockDetails.rewards.owner.value | explorerCurrency}}
{{'label.address' | translate}} + {{blockDetails.rewards.owner.address}} +
+ {{'label.merchant' | translate | uppercase}} +
{{'label.amount' | translate}}{{blockDetails.rewards.merchant.value | explorerCurrency}}
{{'label.address' | translate}} + {{blockDetails.rewards.merchant.address}} +
+ {{'label.masternode' | translate | uppercase}} +
{{'label.amount' | translate}}{{blockDetails.rewards.masternode.value | explorerCurrency}}
{{'label.address' | translate}} + {{blockDetails.rewards.masternode.address}} +
+
+
+ +
+ + + +
+ {{'message.transactionsNotAvailable' | translate}} +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
#{{'label.transaction' | translate}}{{'label.date' | translate}}{{'label.value' | translate}}{{'label.size' | translate}}
{{index + 1}} + {{item.id | slice:0:35}}... + {{item.time * 1000 | explorerDatetime}}{{item.sent | explorerCurrency}}{{item.size}} bytes
+
+ + +
+ +
+ + + + + + + + + + + + + + +
#{{'label.transaction' | translate}}
{{index + 1}} + {{item | slice:0:35}}... +
+
+
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/blocks/block-details/block-details.component.spec.ts b/web-ui/src/app/components/blocks/block-details/block-details.component.spec.ts new file mode 100644 index 00000000..a084b3a8 --- /dev/null +++ b/web-ui/src/app/components/blocks/block-details/block-details.component.spec.ts @@ -0,0 +1,97 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BlockDetailsComponent } from './block-details.component'; +import { ExplorerCurrencyPipe } from '../../../pipes/explorer-currency.pipe'; +import { ExplorerDatetimePipe } from '../../../pipes/explorer-datetime.pipe'; + +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { BlocksService } from '../../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; +import { Observable } from 'rxjs'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; + +const sampleBlockDetails = { + 'block': { + 'hash': '225f7e1388f4d4199033ff077d0ece9aaa04d82fda8cc3c5789fa18f2a84b597', + 'previousBlockhash': '83b8ebe36a88e6a2df01abcd97aae6c908be9088e94379f9cc4f533a171db43f', + 'nextBlockhash': 'dc4cfb8f386e5cedbbe002da267a19daeb2598ce500bd1bb04c1fa59cffbbf99', + 'merkleRoot': '29d826f65e46a9cdb09740a576cb066ba10e4acdb27977340a6a9b571042bf76', + 'transactions': [ + 'b2b3029d694ce3ad1cbbc3f49f5d1c94b63a313ab4bc26a13a5094826b305ef8', + 'fc9d478982c89b1e309bb42bd1ce64c5a99f7a818d5e9e4920a6a70a31bd168e' + ], + 'confirmations': 3, + 'size': 490, + 'height': 1277693, + 'version': 536870912, + 'time': 1597940753, + 'medianTime': 1597940526, + 'nonce': 0, + 'bits': '1c0143a1', + 'chainwork': '0000000000000000000000000000000000000000000000000c4daa33831c9b22', + 'difficulty': 202.5004526306896, + 'tposContract': '6170c1916b53f7c18353dc6271efa53bf17f43635de571976a9a7719fad76be0' + }, + 'rewards': { + 'owner': { + 'address': 'XvfB23af9aFfqZ2NFGqqwsUcitUkKH5RwM', + 'value': 8.73 + }, + 'merchant': { + 'address': 'XczvAzAPnqAa2hQL3ip1rJHmiBoZ6E5RGM', + 'value': 0.27 + }, + 'masternode': { + 'address': 'XwyktBrhrGct4BuJCt3GAPvV6jNyo4Stfw', + 'value': 9 + }, + 'coinstake': null, + 'reward': null, + 'stakedAmount': 2008.82863013, + 'stakedDuration': 358545 + } +}; + +describe('BlockDetailsComponent', () => { + let component: BlockDetailsComponent; + let fixture: ComponentFixture; + + const blocksServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('BlocksService', ['getTransactionsV2', 'pipe', 'tape']); + const errorServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('ErrorService', ['renderServerErrors']); + + beforeEach(async(() => { + blocksServiceSpy.getTransactionsV2.and.returnValue(Observable.create()); + + TestBed.configureTestingModule({ + declarations: [ + BlockDetailsComponent, + ExplorerCurrencyPipe, + ExplorerDatetimePipe + ], + imports: [ + RouterTestingModule, + TranslateModule.forRoot() + ], + providers: [ + { provide: BlocksService, useValue: blocksServiceSpy }, + { provide: ErrorService, useValue: errorServiceSpy } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BlockDetailsComponent); + component = fixture.componentInstance; + component.blockDetails = sampleBlockDetails; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/block-details/block-details.component.ts b/web-ui/src/app/components/blocks/block-details/block-details.component.ts similarity index 58% rename from web-ui/src/app/components/block-details/block-details.component.ts rename to web-ui/src/app/components/blocks/block-details/block-details.component.ts index 16bcb31a..c9f64ad2 100644 --- a/web-ui/src/app/components/block-details/block-details.component.ts +++ b/web-ui/src/app/components/blocks/block-details/block-details.component.ts @@ -1,16 +1,10 @@ +import { Component, OnInit, Input, HostListener } from '@angular/core'; +import { tap } from 'rxjs/operators'; -import {tap} from 'rxjs/operators'; -import { Component, OnInit, HostListener } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { BlockDetails } from '../../models/block'; -import { Transaction } from '../../models/transaction'; - -import { BlocksService } from '../../services/blocks.service'; -import { ErrorService } from '../../services/error.service'; -import { PaginatedResult } from '../../models/paginated-result'; - -import { getNumberOfRowsForScreen } from '../../utils'; +import { BlockDetails } from '../../../models/block'; +import { Transaction } from '../../../models/transaction'; +import { BlocksService } from '../../../services/blocks.service'; +import { getNumberOfRowsForScreen } from '../../../utils'; @Component({ selector: 'app-block-details', @@ -19,42 +13,25 @@ import { getNumberOfRowsForScreen } from '../../utils'; }) export class BlockDetailsComponent implements OnInit { - blockhash: string; + @Input() blockDetails: BlockDetails; // pagination limit = 30; transactions: Transaction[] = []; - constructor( - private route: ActivatedRoute, - private blocksService: BlocksService, - private errorService: ErrorService) { } + constructor(private blocksService: BlocksService) { } ngOnInit() { const height = this.getScreenSize(); this.limit = getNumberOfRowsForScreen(height); - this.route.params.forEach(params => this.onQuery(params['query'])); - } - private onQuery(query: string) { - this.clearCurrentValues(); - this.blocksService.get(query).subscribe( - response => this.onBlockRetrieved(response), - response => this.onError(response) - ); - } - - private clearCurrentValues() { - this.blockhash = null; - this.blockDetails = null; - this.transactions = []; + this.load(); } - private onBlockRetrieved(response: BlockDetails) { - this.blockDetails = response; - this.blockhash = response.block.hash; - this.load(); + @HostListener('window:resize', ['$event']) + private getScreenSize(_?): number { + return window.innerHeight; } load() { @@ -64,20 +41,11 @@ export class BlockDetailsComponent implements OnInit { } this.blocksService - .getTransactionsV2(this.blockhash, this.limit, lastSeenTxid).pipe( - tap(response => this.transactions.push(...response.data))) + .getTransactionsV2(this.blockDetails.block.hash, this.limit, lastSeenTxid).pipe( + tap(response => this.transactions.push(...response.data))) .subscribe(); } - @HostListener('window:resize', ['$event']) - private getScreenSize(_?): number { - return window.innerHeight; - } - - private onError(response: any) { - this.errorService.renderServerErrors(null, response); - } - getBlockType(details: BlockDetails): string { if (details.block.tposContract != null) { return 'Trustless Proof of Stake'; diff --git a/web-ui/src/app/components/blocks/block-list/block-list.component.css b/web-ui/src/app/components/blocks/block-list/block-list.component.css new file mode 100644 index 00000000..f519295b --- /dev/null +++ b/web-ui/src/app/components/blocks/block-list/block-list.component.css @@ -0,0 +1,20 @@ +.block-table-wrapper { + width: 100%; + padding: 0; + margin-top: 15px; +} + +.blocks-info { + float: right; + background-color: #1C202E; + border-radius: 20px; + display: flex; + flex-direction: row; + align-items: center; + padding: 2px 6px; + font-size: 12px; +} + +.blocks-info>div { + margin: 10px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/blocks/block-list/block-list.component.html b/web-ui/src/app/components/blocks/block-list/block-list.component.html new file mode 100644 index 00000000..a9881094 --- /dev/null +++ b/web-ui/src/app/components/blocks/block-list/block-list.component.html @@ -0,0 +1,21 @@ +
+
+ {{'label.blocks' | translate}} +
+
+
+
+ {{'label.height' | translate | uppercase}}: {{stats.blocks}} +
+
+ {{'label.inflation' | translate | uppercase}}: 7.58% +
+
+ {{'label.price' | translate | uppercase}}: ${{prices.usd}} +
+
+
+
+ +
+
\ No newline at end of file diff --git a/web-ui/src/app/components/blocks/block-list/block-list.component.spec.ts b/web-ui/src/app/components/blocks/block-list/block-list.component.spec.ts new file mode 100644 index 00000000..fc0c920a --- /dev/null +++ b/web-ui/src/app/components/blocks/block-list/block-list.component.spec.ts @@ -0,0 +1,44 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { BlockListComponent } from './block-list.component'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; +import { TickerService } from '../../../services/ticker.service'; +import { Observable } from 'rxjs'; + +describe('BlockListComponent', () => { + let component: BlockListComponent; + let fixture: ComponentFixture; + + const tickerServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TickerService', ['get', 'getPrices']); + + beforeEach(async(() => { + tickerServiceSpy.get.and.returnValue(Observable.create()); + tickerServiceSpy.getPrices.and.returnValue(Observable.create()); + + TestBed.configureTestingModule({ + declarations: [ + BlockListComponent + ], + imports: [ + TranslateModule.forRoot() + ], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + { provide: TickerService, useValue: tickerServiceSpy } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BlockListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/blocks/block-list/block-list.component.ts b/web-ui/src/app/components/blocks/block-list/block-list.component.ts new file mode 100644 index 00000000..31a9d861 --- /dev/null +++ b/web-ui/src/app/components/blocks/block-list/block-list.component.ts @@ -0,0 +1,32 @@ +import { Component, OnInit } from '@angular/core'; +import { Prices, ServerStats } from '../../../models/ticker'; +import { TickerService } from '../../../services/ticker.service'; + +@Component({ + selector: 'app-block-list', + templateUrl: './block-list.component.html', + styleUrls: ['./block-list.component.css'] +}) +export class BlockListComponent implements OnInit { + + prices: Prices = new Prices(); + stats: ServerStats = new ServerStats(); + + constructor(private tickerService: TickerService) { } + + ngOnInit() { + this.tickerService + .getPrices() + .subscribe( + response => this.prices = response, + response => console.log(response) + ); + + this.tickerService + .get() + .subscribe( + response => this.stats = response, + response => console.log(response) + ); + } +} diff --git a/web-ui/src/app/components/blocks/block-raw/block-raw.component.css b/web-ui/src/app/components/blocks/block-raw/block-raw.component.css new file mode 100644 index 00000000..c5a0d11b --- /dev/null +++ b/web-ui/src/app/components/blocks/block-raw/block-raw.component.css @@ -0,0 +1,5 @@ +pre { + background-color: transparent; + border: none; + padding: 10px 20px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/block-raw/block-raw.component.html b/web-ui/src/app/components/blocks/block-raw/block-raw.component.html similarity index 70% rename from web-ui/src/app/components/block-raw/block-raw.component.html rename to web-ui/src/app/components/blocks/block-raw/block-raw.component.html index a484b7e7..c7b78029 100644 --- a/web-ui/src/app/components/block-raw/block-raw.component.html +++ b/web-ui/src/app/components/blocks/block-raw/block-raw.component.html @@ -3,6 +3,6 @@ {{'message.blockNotFound' | translate}}
-
{{block | json}}
+
{{block | json}}
-
+ \ No newline at end of file diff --git a/web-ui/src/app/components/block-raw/block-raw.component.spec.ts b/web-ui/src/app/components/blocks/block-raw/block-raw.component.spec.ts similarity index 85% rename from web-ui/src/app/components/block-raw/block-raw.component.spec.ts rename to web-ui/src/app/components/blocks/block-raw/block-raw.component.spec.ts index 5c8f7011..92043d53 100644 --- a/web-ui/src/app/components/block-raw/block-raw.component.spec.ts +++ b/web-ui/src/app/components/blocks/block-raw/block-raw.component.spec.ts @@ -5,9 +5,9 @@ import { BlockRawComponent } from './block-raw.component'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { NavigatorService } from './../../services/navigator.service'; -import { BlocksService } from '../../services/blocks.service'; -import { ErrorService } from '../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { BlocksService } from '../../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; import { TranslateService } from '@ngx-translate/core'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -26,7 +26,7 @@ describe('BlockRawComponent', () => { blocksServiceSpy.getRaw.and.returnValue(Observable.create()); TestBed.configureTestingModule({ - declarations: [ BlockRawComponent ], + declarations: [BlockRawComponent], imports: [ RouterTestingModule, TranslateModule.forRoot() @@ -39,7 +39,7 @@ describe('BlockRawComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/block-raw/block-raw.component.ts b/web-ui/src/app/components/blocks/block-raw/block-raw.component.ts similarity index 79% rename from web-ui/src/app/components/block-raw/block-raw.component.ts rename to web-ui/src/app/components/blocks/block-raw/block-raw.component.ts index 7f97862e..9e6cb264 100644 --- a/web-ui/src/app/components/block-raw/block-raw.component.ts +++ b/web-ui/src/app/components/blocks/block-raw/block-raw.component.ts @@ -3,11 +3,11 @@ import { ActivatedRoute, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { Transaction } from '../../models/transaction'; +import { Transaction } from '../../../models/transaction'; -import { ErrorService } from '../../services/error.service'; -import { NavigatorService } from '../../services/navigator.service'; -import { BlocksService } from '../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { BlocksService } from '../../../services/blocks.service'; @Component({ selector: 'app-block-raw', @@ -26,7 +26,7 @@ export class BlockRawComponent implements OnInit { private errorService: ErrorService) { } ngOnInit() { - this.route.params.forEach(params => this.onBlockQuery(params['query'])); + this.route.params.forEach(params => this.onBlockQuery(params['id'])); } private onBlockQuery(query: string) { diff --git a/web-ui/src/app/components/blocks/block/block.component.css b/web-ui/src/app/components/blocks/block/block.component.css new file mode 100644 index 00000000..3f1620ae --- /dev/null +++ b/web-ui/src/app/components/blocks/block/block.component.css @@ -0,0 +1,43 @@ +.block-tab-bar { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; +} + +.block-tab-bar>li>a { + color: #01D9DE; + background-color: transparent; + font-size: 14px; + padding: 0; +} + +.block-tab-bar>li { + margin: 0 5px; + padding: 13px 13px 9px; + cursor: pointer; +} + +.block-tab-bar>li.active>a { + color: #FFFFFF; +} + +.block-tab-bar>li.active, +.block-tab-bar>li:hover { + background-color: #222534; + border-radius: 20px; +} + +.block-tab-content { + padding: 10px 0; + border-top: 1px solid #282D3C; +} + +.block-tab-footer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 20px; + border-top: 1px solid #282D3C; +} \ No newline at end of file diff --git a/web-ui/src/app/components/blocks/block/block.component.html b/web-ui/src/app/components/blocks/block/block.component.html new file mode 100644 index 00000000..d36fe840 --- /dev/null +++ b/web-ui/src/app/components/blocks/block/block.component.html @@ -0,0 +1,37 @@ +
+
+ + + +   {{'label.block' | translate}} #{{blockDetails && blockDetails.block.height}} +
+ +
+ + + + +
+ +
\ No newline at end of file diff --git a/web-ui/src/app/components/block-details/block-details.component.spec.ts b/web-ui/src/app/components/blocks/block/block.component.spec.ts similarity index 52% rename from web-ui/src/app/components/block-details/block-details.component.spec.ts rename to web-ui/src/app/components/blocks/block/block.component.spec.ts index 0f419665..53ede9db 100644 --- a/web-ui/src/app/components/block-details/block-details.component.spec.ts +++ b/web-ui/src/app/components/blocks/block/block.component.spec.ts @@ -1,23 +1,23 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { BlockDetailsComponent } from './block-details.component'; -import { ExplorerCurrencyPipe } from '../../pipes/explorer-currency.pipe'; -import { ExplorerDatetimePipe } from '../../pipes/explorer-datetime.pipe'; +import { BlockComponent } from './block.component'; -import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { BlocksService } from './../../services/blocks.service'; -import { ErrorService } from '../../services/error.service'; +import { NO_ERRORS_SCHEMA, } from '@angular/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { HttpClientModule } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { NO_ERRORS_SCHEMA, } from '@angular/core'; +import { BlocksService } from '../../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; +import { NotificationService } from '../../../services/notification.service'; -describe('BlockDetailsComponent', () => { - let component: BlockDetailsComponent; - let fixture: ComponentFixture; +describe('BlockComponent', () => { + let component: BlockComponent; + let fixture: ComponentFixture; - const blocksServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('BlocksService', ['get', 'getTransactionsV2']); + const blocksServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('BlocksService', ['get']); const errorServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('ErrorService', ['renderServerErrors']); beforeEach(async(() => { @@ -25,25 +25,25 @@ describe('BlockDetailsComponent', () => { TestBed.configureTestingModule({ declarations: [ - BlockDetailsComponent, - ExplorerCurrencyPipe, - ExplorerDatetimePipe + BlockComponent ], imports: [ + TranslateModule.forRoot(), RouterTestingModule, - TranslateModule.forRoot() + HttpClientModule ], + schemas: [NO_ERRORS_SCHEMA], providers: [ { provide: BlocksService, useValue: blocksServiceSpy }, - { provide: ErrorService, useValue: errorServiceSpy } - ], - schemas: [NO_ERRORS_SCHEMA] + { provide: ErrorService, useValue: errorServiceSpy }, + NotificationService + ] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(BlockDetailsComponent); + fixture = TestBed.createComponent(BlockComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/web-ui/src/app/components/blocks/block/block.component.ts b/web-ui/src/app/components/blocks/block/block.component.ts new file mode 100644 index 00000000..6bf3f738 --- /dev/null +++ b/web-ui/src/app/components/blocks/block/block.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit, HostListener } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { BlockDetails } from '../../../models/block'; +import { BlocksService } from '../../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; + +@Component({ + selector: 'app-block', + templateUrl: './block.component.html', + styleUrls: ['./block.component.css'] +}) +export class BlockComponent implements OnInit { + + currentView = 'details'; + + blockhash: string; + blockDetails: BlockDetails; + + constructor(private route: ActivatedRoute, + private blocksService: BlocksService, + private errorService: ErrorService) { } + + ngOnInit() { + this.route.params.forEach(params => this.onQuery(params['id'])); + } + + private onQuery(query: string) { + this.clearCurrentValues(); + this.blocksService.get(query).subscribe( + response => this.onBlockRetrieved(response), + response => this.onError(response) + ); + } + + private clearCurrentValues() { + this.blockhash = null; + this.blockDetails = null; + } + + private onBlockRetrieved(response: BlockDetails) { + this.blockDetails = response; + this.blockhash = response.block.hash; + } + + private onError(response: any) { + this.errorService.renderServerErrors(null, response); + } + + selectView(view: string) { + this.currentView = view; + } + + isSelected(view: string): boolean { + return this.currentView === view; + } +} diff --git a/web-ui/src/app/components/blocks/blocks-routing.module.ts b/web-ui/src/app/components/blocks/blocks-routing.module.ts new file mode 100644 index 00000000..b543857b --- /dev/null +++ b/web-ui/src/app/components/blocks/blocks-routing.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { BlocksComponent } from './blocks.component'; +import { BlockComponent } from './block/block.component'; +import { BlockListComponent } from './block-list/block-list.component'; + +const routes: Routes = [ + { + path: '', + component: BlocksComponent, + children: [ + { + path: ':id', + component: BlockComponent + }, + { + path: '', + component: BlockListComponent + } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class BlocksRoutingModule { } diff --git a/web-ui/src/app/components/blocks/blocks.component.html b/web-ui/src/app/components/blocks/blocks.component.html new file mode 100644 index 00000000..4564712d --- /dev/null +++ b/web-ui/src/app/components/blocks/blocks.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/web-ui/src/app/components/blocks/blocks.component.ts b/web-ui/src/app/components/blocks/blocks.component.ts new file mode 100644 index 00000000..d4015451 --- /dev/null +++ b/web-ui/src/app/components/blocks/blocks.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-blocks', + templateUrl: './blocks.component.html' +}) +export class BlocksComponent { } diff --git a/web-ui/src/app/components/blocks/blocks.module.ts b/web-ui/src/app/components/blocks/blocks.module.ts new file mode 100644 index 00000000..d5af5dde --- /dev/null +++ b/web-ui/src/app/components/blocks/blocks.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BlocksRoutingModule } from './blocks-routing.module'; +import { BlockComponent } from './block/block.component'; +import { BlocksComponent } from './blocks.component'; +import { BlockDetailsComponent } from './block-details/block-details.component'; +import { BlockRawComponent } from './block-raw/block-raw.component'; +import { SharedModule } from '../shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { MomentModule } from 'ngx-moment'; +import { TabsModule, AlertModule } from 'ngx-bootstrap'; +import { PipesModule } from '../../pipes/pipes.module'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { BlockListComponent } from './block-list/block-list.component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + BlocksRoutingModule, + SharedModule, + PipesModule, + TranslateModule, + MomentModule, + TabsModule, + AlertModule, + InfiniteScrollModule + ], + declarations: [ + BlocksComponent, + BlockListComponent, + BlockComponent, + BlockDetailsComponent, + BlockRawComponent + ] +}) + +export class BlocksModule { } diff --git a/web-ui/src/app/components/calculator/calculator.component.css b/web-ui/src/app/components/calculator/calculator.component.css new file mode 100644 index 00000000..2f039f09 --- /dev/null +++ b/web-ui/src/app/components/calculator/calculator.component.css @@ -0,0 +1,142 @@ +@media (min-width:992px) { + .calculator-panel { + padding-left: 0; + padding-right: 10px; + } + + .calculator-stats { + padding-left: 10px; + padding-right: 0; + } +} + +.calculator-panel .page-title { + margin: 25px 0; +} + +.calculator-label { + font-size: 12px; + color: #989DCC; + font-weight: 300; + letter-spacing: 0.9px; + margin-bottom: 5px; +} + +.calculator-hold-amount { + padding: 20px 20px 16px; + margin-top: 5px; + border-radius: 8px; + font-size: 24px; + font-weight: 300; + background: #1C202E 0% 0% no-repeat padding-box; + + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; +} + +.calculator-hold-amount-text { + margin-left: 10px; +} + +.calculator-optimal-setup { + font-size: 14px; + text-align: center; + margin-top: 18px; +} + +.calculator-more-profitable { + font-size: 16px; + font-weight: 300; + text-align: center; + margin-top: 46px; +} + +.calculator-comparison-bar { + height: 40px; + border-radius: 20px; + + background: repeating-linear-gradient( + -45deg, + #FFFFFF, + #FFFFFF 15px, + #000000 15px, + #000000 30px + ); + font-size: 16px; + + margin-top: 35px; + margin-bottom: 25px; +} + +.calculator-staking-bar { + height: 100%; + background-color: #6D56EEF0; + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + + color: #FFFFFF; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; +} + +.calculator-masternode-staking-bar { + height: 100%; + background-color: #3CE0E0F0; + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + + color: #2D5759; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; +} + +.calculator-comparison-table { + margin-top: 25px; +} + +.calculator-comparison-table>tbody>tr>td { + padding: 5px 15px 0; + font-size: 16px; + font-weight: 300; +} + +.calculator-stats-container { + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: flex-start; + flex-wrap: wrap; + + padding: 20px; +} + +.calculator-stat-item { + margin-bottom: 30px; +} + +.calculator-stat-item:last-child { + display: flex; + flex-direction: column; + align-items: center; +} + +.calculator-stat-data { + font-size: 18px; + font-weight: 300; +} + +.calculator-balance-input { + background-color: transparent !important; + color: #01D9DE; + outline: none; + box-shadow: none; + font-size: 24px; + font-weight: 300; + border: none; +} \ No newline at end of file diff --git a/web-ui/src/app/components/calculator/calculator.component.html b/web-ui/src/app/components/calculator/calculator.component.html new file mode 100644 index 00000000..15154fc6 --- /dev/null +++ b/web-ui/src/app/components/calculator/calculator.component.html @@ -0,0 +1,203 @@ +
+
+
+
+
+ {{'label.masternodes' | translate}}: +
+
+ {{stats.masternodes}} +
+
+ +
+
+ {{'label.tposNodes' | translate}}: +
+
+ {{stats.tposnodes}} +
+
+ +
+
+ {{'label.coinsInMasternodes' | translate}}: +
+
+ {{stats.masternodes * 15000}} XSN
+ ({{stats.masternodes * 15000 / serverStats.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+ +
+
+ {{'label.coinsStaking' | translate}}: +
+
+ {{stats.coinsStaking}} XSN
+ ({{stats.coinsStaking / serverStats.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+ +
+
+ {{'label.masternodeROI' | translate}}: +
+
+ 7.58% Year +
+
+ +
+
+ {{'label.stakingROI' | translate}}: +
+
+ 7.58% Year +
+
+ +
+
+ {{'label.requiredForMasternode' | translate}}: +
+
+ {{requiredForMasternode}} XSN
+ $ {{prices.usd * requiredForMasternode}}
+ {{prices.btc * requiredForMasternode}} BTC +
+
+ +
+
+ {{'label.rewardPerBlock' | translate}}: +
+
+ 16.5 XSN +
+
+
+
+ +
+
+
+ {{'label.calculator' | translate}} +
+ +
+ {{'label.xsnAmountHold' | translate}} +
+ +
+
+ + +
+
+ +
+ {{'label.optimalSetup' | translate}} : {{masternodeCount}} {{'label.masternodeAnd' | translate}} {{xsnStaking}} {{'label.xsnStaking' | translate}} +
+ +
+ {{'label.masternodeStakingRemaining' | translate}} {{'label.moreProfitable' | translate}} +
+ +
+
+
+ {{'label.stakingAllCoins' | translate}} +
+
+ {{'label.masternodeStakingRemaining' | translate}} +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
{{'label.daily' | translate}}{{'label.daily' | translate}}
14.5347 XSN14.5347 XSN
$ 0.0000$ 0.0000
0.00000000 BTC0.00000000 BTC
+
+ +
+ + + + + + + + + + + + + + + + + + + +
{{'label.monthly' | translate}}{{'label.monthly' | translate}}
473.3261 XSN473.3261 XSN
$ 0.0000$ 0.0000
0.00000000 BTC0.00000000 BTC
+
+ +
+ + + + + + + + + + + + + + + + + + + +
{{'label.yearly' | translate}}{{'label.yearly' | translate}}
5 755.3261 XSN (ROI: 64.42%)5 755.3261 XSN (ROI: 64.42%)
$ 0.0000$ 0.0000
0.00000000 BTC0.00000000 BTC
+
+ +
+ + + + + + + + + + + +
{{'label.waitingTimePerReward' | translate}}{{'label.waitingTimePerReward' | translate}}
1.74 {{'label.days' | translate}}1.74 {{'label.days' | translate}}
+
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/calculator/calculator.component.spec.ts b/web-ui/src/app/components/calculator/calculator.component.spec.ts new file mode 100644 index 00000000..978e8faa --- /dev/null +++ b/web-ui/src/app/components/calculator/calculator.component.spec.ts @@ -0,0 +1,45 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CalculatorComponent } from './calculator.component'; + +import { TranslateModule } from '@ngx-translate/core'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; +import { TickerService } from '../../services/ticker.service'; +import { Observable } from 'rxjs'; + +describe('CalculatorComponent', () => { + let component: CalculatorComponent; + let fixture: ComponentFixture; + + const tickerServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TickerService', ['getNodeStats', 'get', 'getPrices']); + + beforeEach(async(() => { + tickerServiceSpy.getNodeStats.and.returnValue(Observable.create()); + tickerServiceSpy.get.and.returnValue(Observable.create()); + tickerServiceSpy.getPrices.and.returnValue(Observable.create()); + + TestBed.configureTestingModule({ + declarations: [CalculatorComponent], + imports: [ + TranslateModule.forRoot(), + RouterTestingModule + ], + providers: [ + { provide: TickerService, useValue: tickerServiceSpy }], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CalculatorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/calculator/calculator.component.ts b/web-ui/src/app/components/calculator/calculator.component.ts new file mode 100644 index 00000000..3cfd43df --- /dev/null +++ b/web-ui/src/app/components/calculator/calculator.component.ts @@ -0,0 +1,56 @@ +import { Component, OnInit } from '@angular/core'; +import { NodeStats, Prices, ServerStats } from '../../models/ticker'; +import { TickerService } from '../../services/ticker.service'; + +@Component({ + selector: 'app-calculator', + templateUrl: './calculator.component.html', + styleUrls: ['./calculator.component.css'] +}) +export class CalculatorComponent implements OnInit { + + transaction: any; + prices: Prices = new Prices(); + stats: NodeStats = new NodeStats(); + serverStats: ServerStats = new ServerStats(); + holdAmount = 17531; + + requiredForMasternode = 15000; + + masternodeCount = 1; + xsnStaking = 2531; + + constructor(private tickerService: TickerService) { } + + ngOnInit() { + this.tickerService + .getNodeStats() + .subscribe( + response => this.stats = response, + response => this.onError(response) + ); + + this.tickerService + .get() + .subscribe( + response => this.serverStats = response, + response => this.onError(response) + ); + + this.tickerService + .getPrices() + .subscribe( + response => this.prices = response, + response => this.onError(response) + ); + } + + private onError(response: any) { + console.log(response); + } + + onChangeAmount() { + this.masternodeCount = Math.floor(this.holdAmount / this.requiredForMasternode); + this.xsnStaking = this.holdAmount % this.requiredForMasternode; + } +} diff --git a/web-ui/src/app/components/finder/finder.component.css b/web-ui/src/app/components/finder/finder.component.css deleted file mode 100644 index 9c06eacf..00000000 --- a/web-ui/src/app/components/finder/finder.component.css +++ /dev/null @@ -1,33 +0,0 @@ -.btn-search { - background: #2C80FF; - border-radius: 0 8px 8px 0; - border-radius: 0px 8px 8px 0px; -} - -::-webkit-input-placeholder { - font-size: 14px; -} - -:-ms-input-placeholder { - /* Internet Explorer */ - font-size: 14px; -} - -::placeholder { - font-size: 14px; -} - -.input-group-addon { - background-color: #ffffff; - border-left: #ffffff; - border-right: #ffffff; -} - -.input-lg { - border-left: #ffffff; -} - -.padding-finder { - padding-left: 2px; - padding-right: 2px; -} diff --git a/web-ui/src/app/components/finder/finder.component.html b/web-ui/src/app/components/finder/finder.component.html deleted file mode 100644 index 43b35b74..00000000 --- a/web-ui/src/app/components/finder/finder.component.html +++ /dev/null @@ -1,33 +0,0 @@ -
-
-
- -
-
-
- -
- - - - - - -
-
- -
- {{ errorService.getFieldError(form, 'searchField') | translate }} -
- -
-
-
-
diff --git a/web-ui/src/app/components/home/home-routing.module.ts b/web-ui/src/app/components/home/home-routing.module.ts new file mode 100644 index 00000000..988f9244 --- /dev/null +++ b/web-ui/src/app/components/home/home-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { HomeComponent } from './home/home.component'; + +const routes: Routes = [ + { + path: '', + component: HomeComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class HomeRoutingModule { } diff --git a/web-ui/src/app/components/home/home.component.css b/web-ui/src/app/components/home/home.component.css deleted file mode 100644 index fbae1615..00000000 --- a/web-ui/src/app/components/home/home.component.css +++ /dev/null @@ -1,15 +0,0 @@ -.table-container { - background-color: white; - border-radius: 4px; - -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); - -moz-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); - box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); -} - -tabset > ul > li > a > span { - font-family: InterUI-Medium; - font-size: 22px; - color: #6672A6; - letter-spacing: -0.17px; - text-align: center; -} \ No newline at end of file diff --git a/web-ui/src/app/components/home/home.component.html b/web-ui/src/app/components/home/home.component.html deleted file mode 100644 index fe084091..00000000 --- a/web-ui/src/app/components/home/home.component.html +++ /dev/null @@ -1,20 +0,0 @@ -
- - - -
-
- - - - - - - - - - - -
-
-
diff --git a/web-ui/src/app/components/home/home.module.ts b/web-ui/src/app/components/home/home.module.ts new file mode 100644 index 00000000..565c541c --- /dev/null +++ b/web-ui/src/app/components/home/home.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { HomeComponent } from './home/home.component'; +import { HomeRoutingModule } from './home-routing.module'; +import { TickerComponent } from './ticker/ticker.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../shared/shared.module'; +import { PipesModule } from '../../pipes/pipes.module'; +import { MomentModule } from 'ngx-moment'; +import { TabsModule, AlertModule } from 'ngx-bootstrap'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; + +@NgModule({ + imports: [ + HomeRoutingModule, + CommonModule, + FormsModule, + TranslateModule, + ReactiveFormsModule, + SharedModule, + PipesModule, + TranslateModule, + MomentModule, + TabsModule, + AlertModule, + InfiniteScrollModule + ], + declarations: [ + HomeComponent, + TickerComponent + ] +}) +export class HomeModule { } diff --git a/web-ui/src/app/components/home/home/home.component.css b/web-ui/src/app/components/home/home/home.component.css new file mode 100644 index 00000000..45d4b524 --- /dev/null +++ b/web-ui/src/app/components/home/home/home.component.css @@ -0,0 +1,10 @@ +.home-table-container { + padding: 15px; +} + +.show-more { + float: right; + padding: 9px 18px 7px; + border-radius: 20px; + border: 2px solid #01D9DE; +} \ No newline at end of file diff --git a/web-ui/src/app/components/home/home/home.component.html b/web-ui/src/app/components/home/home/home.component.html new file mode 100644 index 00000000..be3a22b3 --- /dev/null +++ b/web-ui/src/app/components/home/home/home.component.html @@ -0,0 +1,31 @@ +
+ + +
+
+
+ {{'label.blocks' | translate}} +
+ + +
+
+ +
+
+
+ {{'label.transactions' | translate}} +
+ + +
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/home/home.component.spec.ts b/web-ui/src/app/components/home/home/home.component.spec.ts similarity index 93% rename from web-ui/src/app/components/home/home.component.spec.ts rename to web-ui/src/app/components/home/home/home.component.spec.ts index a98520ae..2708bb08 100644 --- a/web-ui/src/app/components/home/home.component.spec.ts +++ b/web-ui/src/app/components/home/home/home.component.spec.ts @@ -6,7 +6,7 @@ import { APP_BASE_HREF } from '@angular/common'; import { TabsModule } from 'ngx-bootstrap/tabs'; import { TranslateModule } from '@ngx-translate/core'; -import { HomeComponent } from '../../components/home/home.component'; +import { HomeComponent } from './home.component'; describe('HomeComponent', () => { let component: HomeComponent; diff --git a/web-ui/src/app/components/home/home.component.ts b/web-ui/src/app/components/home/home/home.component.ts similarity index 100% rename from web-ui/src/app/components/home/home.component.ts rename to web-ui/src/app/components/home/home/home.component.ts diff --git a/web-ui/src/app/components/home/ticker/ticker.component.css b/web-ui/src/app/components/home/ticker/ticker.component.css new file mode 100644 index 00000000..aaf7b584 --- /dev/null +++ b/web-ui/src/app/components/home/ticker/ticker.component.css @@ -0,0 +1,48 @@ +.ticker-container +{ + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: space-between; + flex-wrap: wrap; + margin-left: -30px; + margin-right: -30px; +} + +span.help { + color: #01D9DE; + cursor: help; + font-size: 16px; + float: right; +} + +div.data { + font-size: 18px; + color: #FFFFFF; + letter-spacing: 0; + margin-bottom: 10px; + font-weight: 300; +} + +div.lbl-bottom { + font-size: 12px; + color: #989DCC; + letter-spacing: 0.9px; + margin-bottom: 5px; +} + +.height-data { + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.height-data>span { + font-size: 36px; +} + +.height-data>span.sub-label { + font-size: 12px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/home/ticker/ticker.component.html b/web-ui/src/app/components/home/ticker/ticker.component.html new file mode 100644 index 00000000..76902a69 --- /dev/null +++ b/web-ui/src/app/components/home/ticker/ticker.component.html @@ -0,0 +1,184 @@ +
+ +
+
+
+ {{'label.totalSupply' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + {{ticker.totalSupply | explorerAmount}} {{config.currentCurrency}} + + +
+ +
+ {{'label.circulatingSupply' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{ticker.circulatingSupply | explorerAmount}} {{config.currentCurrency}} + + +
+
+
+ +
+
+
+ {{'label.height' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{ticker.blocks}} + + + + Last 3 minutes ago + +
+
+
+ +
+
+
+
+ {{'label.masternodes' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.masternodes}} + +
+
+ +
+
+ {{'label.coinsInMasternodes' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.masternodes * 15000}} XSN
+ ({{nodeStats.masternodes * 15000 / ticker.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+
+ +
+
+ {{'label.tposNodes' | translate}} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.tposnodes}} + +
+
+ +
+
+ {{'label.coinsStaking' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.coinsStaking}} XSN
+ ({{nodeStats.coinsStaking / ticker.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+
+
+
+ +
+
+
+
+ {{'label.currentInflation' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + 11.38% Year + +
+
+ +
+
+ {{'label.price' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{prices.usd}}$ + +
+
+ +
+
+ {{'label.masternodeROI' | translate}} +
+
+ + {{'message.unavailable' | translate}} + + + + 16.62% Year + +
+
+ +
+
+ {{'label.stakingROI' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + 12.08% Year + +
+
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/ticker/ticker.component.spec.ts b/web-ui/src/app/components/home/ticker/ticker.component.spec.ts similarity index 71% rename from web-ui/src/app/components/ticker/ticker.component.spec.ts rename to web-ui/src/app/components/home/ticker/ticker.component.spec.ts index 7c991534..6c67b589 100644 --- a/web-ui/src/app/components/ticker/ticker.component.spec.ts +++ b/web-ui/src/app/components/home/ticker/ticker.component.spec.ts @@ -1,22 +1,24 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TickerComponent } from './ticker.component'; -import { ExplorerCurrencyPipe } from '../../pipes/explorer-currency.pipe'; -import { ExplorerAmountPipe } from '../../pipes/explorer-amount.pipe'; +import { ExplorerCurrencyPipe } from '../../../pipes/explorer-currency.pipe'; +import { ExplorerAmountPipe } from '../../../pipes/explorer-amount.pipe'; import { TranslateModule } from '@ngx-translate/core'; -import { TickerService } from '../../services/ticker.service'; +import { TickerService } from '../../../services/ticker.service'; import { Observable } from 'rxjs'; describe('TickerComponent', () => { let component: TickerComponent; let fixture: ComponentFixture; - const tickerServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TickerService', ['get']); + const tickerServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TickerService', ['get', 'getPrices', 'getNodeStats']); beforeEach(async(() => { tickerServiceSpy.get.and.returnValue(Observable.create()); + tickerServiceSpy.getNodeStats.and.returnValue(Observable.create()); + tickerServiceSpy.getPrices.and.returnValue(Observable.create()); TestBed.configureTestingModule({ declarations: [ diff --git a/web-ui/src/app/components/home/ticker/ticker.component.ts b/web-ui/src/app/components/home/ticker/ticker.component.ts new file mode 100644 index 00000000..fd7d5e6b --- /dev/null +++ b/web-ui/src/app/components/home/ticker/ticker.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; + +import { TickerService } from '../../../services/ticker.service'; +import { NodeStats, Prices, ServerStats } from '../../../models/ticker'; +import { Config } from '../../../config'; + + +@Component({ + selector: 'app-ticker', + templateUrl: './ticker.component.html', + styleUrls: ['./ticker.component.css'] +}) +export class TickerComponent implements OnInit { + + ticker: ServerStats = new ServerStats(); + nodeStats: NodeStats = new NodeStats(); + prices: Prices = new Prices(); + config = Config; + + constructor(private tickerService: TickerService) { } + + ngOnInit() { + this.tickerService + .get() + .subscribe( + response => this.ticker = response, + response => this.onError(response) + ); + + this.tickerService + .getNodeStats() + .subscribe( + response => this.nodeStats = response, + response => this.onError(response) + ); + + this.tickerService + .getPrices() + .subscribe( + response => this.prices = response, + response => this.onError(response) + ); + } + + private onError(response: any) { + console.log(response); + } +} diff --git a/web-ui/src/app/components/latest-blocks/latest-blocks.component.css b/web-ui/src/app/components/latest-blocks/latest-blocks.component.css deleted file mode 100644 index 22885d6a..00000000 --- a/web-ui/src/app/components/latest-blocks/latest-blocks.component.css +++ /dev/null @@ -1,9 +0,0 @@ -.latest-block { - animation: blinker 1s linear; -} - -@keyframes blinker { - 50% { - background-color: #D4FFD4; - } -} \ No newline at end of file diff --git a/web-ui/src/app/components/latest-blocks/latest-blocks.component.html b/web-ui/src/app/components/latest-blocks/latest-blocks.component.html deleted file mode 100644 index 5f2fdc00..00000000 --- a/web-ui/src/app/components/latest-blocks/latest-blocks.component.html +++ /dev/null @@ -1,41 +0,0 @@ -
-
-
-

{{'label.latestBlocks' | translate}}

-
-
- -
-
-

{{'message.loadingLatestBlocks' | translate}}

-
-
- -
-
- - - - - - - - - - - - - - - - - - - - -
{{'label.height' | translate}}{{'label.blocktime' | translate}}{{'label.transactions' | translate}}{{'label.difficulty' | translate}}{{'label.extractedBy' | translate}}
- {{item.height}} - {{item.time * 1000 | amTimeAgo}}{{item.transactions.length}}{{item.difficulty | number:'1.2-2'}}{{extractedBy(item) | translate}}
-
-
-
diff --git a/web-ui/src/app/components/latest-blocks/latest-blocks.component.ts b/web-ui/src/app/components/latest-blocks/latest-blocks.component.ts deleted file mode 100644 index 72dfbe22..00000000 --- a/web-ui/src/app/components/latest-blocks/latest-blocks.component.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import {tap, merge, switchMap} from 'rxjs/operators'; -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { Subscription , Subject , of } from 'rxjs'; - -import { Block } from '../../models/block'; - -import { BlocksService } from '../../services/blocks.service'; -import { ErrorService } from '../../services/error.service'; - -@Component({ - selector: 'app-latest-blocks', - templateUrl: './latest-blocks.component.html', - styleUrls: ['./latest-blocks.component.css'] -}) -export class LatestBlocksComponent implements OnInit, OnDestroy { - - blocks: Block[] = []; - private latestBlockHeight = 0; - private subscription$: Subscription; - - constructor( - private route: ActivatedRoute, - private router: Router, - private blocksService: BlocksService, - private errorService: ErrorService) { } - - ngOnInit() { - this.updateBlocks(); - } - - ngOnDestroy() { - if (this.subscription$ != null) { - this.subscription$.unsubscribe(); - } - } - - private updateBlocks() { - const polling$ = new Subject(); - - /** - * There is around 1 new block per minute, it is a waste of resources - * to be checking for new blocks quite frequently. - */ - const interval = 50000; - - // polling based on https://stackoverflow.com/a/42659054/3211175 - this.subscription$ = of(null).pipe( - merge(polling$), - switchMap(_ => - this.blocksService.getLatest().pipe( - tap(_ => { - setTimeout(_ => polling$.next(null), interval); - })) - ),) - .subscribe( - response => this.onBlockRetrieved(response), - response => this.onError(response) - ); - } - - private onBlockRetrieved(response: Block[]) { - this.latestBlockHeight = this.blocks.reduce((max, block) => Math.max(block.height, max), 0); - this.blocks = response; - } - - private onError(response: any) { - this.errorService.renderServerErrors(null, response); - } - - extractedBy(block: Block): string { - if (block.height <= 75) { - return 'PoW'; - } - - if (block.tposContract == null) { - return 'PoS'; - } else { - return 'TPoS'; - } - } - - age(block: Block): string { - return ''; - } - - isBlockRecent(item: Block): boolean { - return item.height > this.latestBlockHeight; - } -} diff --git a/web-ui/src/app/components/masternodes/masternodes.component.html b/web-ui/src/app/components/masternodes/masternodes.component.html deleted file mode 100644 index 03127e1f..00000000 --- a/web-ui/src/app/components/masternodes/masternodes.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
-
-

{{'label.masternodes' | translate}}

-
- -
- - - - - - - - - - - - - - - - - - - - - - - - -
#{{'label.address' | translate}}{{'label.protocol' | translate}}{{'label.active' | translate}}{{'label.lastSeen' | translate}}{{'label.payee' | translate}}
{{(currentPage - 1) * pageSize + index + 1}}{{item.ip}}{{item.protocol}}{{Math.max(item.activeSeconds, 0) | amDuration:'seconds'}}{{item.lastSeen * 1000 | amTimeAgo}} - {{item.payee}} -
-
- -
-
- - -
-
-
diff --git a/web-ui/src/app/components/navbar/navbar.component.css b/web-ui/src/app/components/navbar/navbar.component.css deleted file mode 100644 index ff418989..00000000 --- a/web-ui/src/app/components/navbar/navbar.component.css +++ /dev/null @@ -1,18 +0,0 @@ -.navbar-logo { - width: 35px; - height: 35px; - margin-top: -7.5px; -} - -.navbar-brand { - font-size: 24px; - color: #141829; - font-weight: bold; -} - -.navbar-tab { - margin-top: 1.8px; - font-size: 16px; - color: #141829; - font-weight: bold; -} diff --git a/web-ui/src/app/components/navbar/navbar.component.html b/web-ui/src/app/components/navbar/navbar.component.html deleted file mode 100644 index a19e9f29..00000000 --- a/web-ui/src/app/components/navbar/navbar.component.html +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/web-ui/src/app/components/navbar/navbar.component.ts b/web-ui/src/app/components/navbar/navbar.component.ts deleted file mode 100644 index 22eefbea..00000000 --- a/web-ui/src/app/components/navbar/navbar.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Location } from '@angular/common'; - -class Tab { - label: string; - path: string; -} - -@Component({ - selector: 'app-navbar', - templateUrl: './navbar.component.html', - styleUrls: ['./navbar.component.css'] -}) -export class NavbarComponent implements OnInit { - - public tabs: Tab[] = [ - { - label: 'Trezor Wallet', - path: '/trezor' - } - ]; - - constructor(private location: Location) { } - - ngOnInit() { - } - - /* tabs */ - isSelected(path: string): boolean { - if (!path.startsWith('/')) { - path = '/' + path; - } - - return this.location.isCurrentPathEqualTo(path); - } -} diff --git a/web-ui/src/app/components/masternode-details/masternode-details.component.css b/web-ui/src/app/components/nodes/masternode-details/masternode-details.component.css similarity index 100% rename from web-ui/src/app/components/masternode-details/masternode-details.component.css rename to web-ui/src/app/components/nodes/masternode-details/masternode-details.component.css diff --git a/web-ui/src/app/components/masternode-details/masternode-details.component.html b/web-ui/src/app/components/nodes/masternode-details/masternode-details.component.html similarity index 100% rename from web-ui/src/app/components/masternode-details/masternode-details.component.html rename to web-ui/src/app/components/nodes/masternode-details/masternode-details.component.html diff --git a/web-ui/src/app/components/masternode-details/masternode-details.component.spec.ts b/web-ui/src/app/components/nodes/masternode-details/masternode-details.component.spec.ts similarity index 84% rename from web-ui/src/app/components/masternode-details/masternode-details.component.spec.ts rename to web-ui/src/app/components/nodes/masternode-details/masternode-details.component.spec.ts index a015a25e..6468ebbe 100644 --- a/web-ui/src/app/components/masternode-details/masternode-details.component.spec.ts +++ b/web-ui/src/app/components/nodes/masternode-details/masternode-details.component.spec.ts @@ -1,15 +1,15 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MasternodeDetailsComponent } from './masternode-details.component'; -import { ExplorerDatetimePipe } from '../../pipes/explorer-datetime.pipe'; +import { ExplorerDatetimePipe } from '../../../pipes/explorer-datetime.pipe'; import { MomentModule } from 'ngx-moment'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { NavigatorService } from '../../services/navigator.service'; -import { MasternodesService } from '../../services/masternodes.service'; -import { ErrorService } from '../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { MasternodesService } from '../../../services/masternodes.service'; +import { ErrorService } from '../../../services/error.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -42,7 +42,7 @@ describe('MasternodeDetailsComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/masternode-details/masternode-details.component.ts b/web-ui/src/app/components/nodes/masternode-details/masternode-details.component.ts similarity index 77% rename from web-ui/src/app/components/masternode-details/masternode-details.component.ts rename to web-ui/src/app/components/nodes/masternode-details/masternode-details.component.ts index 9137d96d..e1f27e89 100644 --- a/web-ui/src/app/components/masternode-details/masternode-details.component.ts +++ b/web-ui/src/app/components/nodes/masternode-details/masternode-details.component.ts @@ -1,13 +1,11 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; +import { Masternode } from '../../../models/masternode'; -import { Masternode } from '../../models/masternode'; - -import { MasternodesService } from '../../services/masternodes.service'; -import { ErrorService } from '../../services/error.service'; -import { NavigatorService } from '../../services/navigator.service'; +import { MasternodesService } from '../../../services/masternodes.service'; +import { ErrorService } from '../../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; @Component({ selector: 'app-masternode-details', diff --git a/web-ui/src/app/components/masternodes/masternodes.component.css b/web-ui/src/app/components/nodes/masternodes/masternodes.component.css similarity index 100% rename from web-ui/src/app/components/masternodes/masternodes.component.css rename to web-ui/src/app/components/nodes/masternodes/masternodes.component.css diff --git a/web-ui/src/app/components/nodes/masternodes/masternodes.component.html b/web-ui/src/app/components/nodes/masternodes/masternodes.component.html new file mode 100644 index 00000000..915c0e77 --- /dev/null +++ b/web-ui/src/app/components/nodes/masternodes/masternodes.component.html @@ -0,0 +1,42 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
#{{'label.ip' | translate | uppercase}}{{'label.protocol' | translate | uppercase}}{{'label.active' | translate | uppercase}}{{'label.lastSeen' | translate | uppercase}}{{'label.payee' | translate | uppercase}}
{{(currentPage - 1) * pageSize + index + 1}}{{item.ip}}{{item.protocol}}{{Math.max(item.activeSeconds, 0) | amDuration:'seconds'}}{{amAgo(item.lastSeen)}} + {{item.payee}} +
+
+ +
+
+ + +
+
+
diff --git a/web-ui/src/app/components/masternodes/masternodes.component.spec.ts b/web-ui/src/app/components/nodes/masternodes/masternodes.component.spec.ts similarity index 87% rename from web-ui/src/app/components/masternodes/masternodes.component.spec.ts rename to web-ui/src/app/components/nodes/masternodes/masternodes.component.spec.ts index d64f2ac4..b63c2ebf 100644 --- a/web-ui/src/app/components/masternodes/masternodes.component.spec.ts +++ b/web-ui/src/app/components/nodes/masternodes/masternodes.component.spec.ts @@ -6,8 +6,8 @@ import { MomentModule } from 'ngx-moment'; import { NgxPaginationModule } from 'ngx-pagination'; import { TranslateModule } from '@ngx-translate/core'; -import { MasternodesService } from '../../services/masternodes.service'; -import { ErrorService } from '../../services/error.service'; +import { MasternodesService } from '../../../services/masternodes.service'; +import { ErrorService } from '../../../services/error.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -23,7 +23,7 @@ describe('MasternodesComponent', () => { masternodesServiceSpy.get.and.returnValue(Observable.create()); TestBed.configureTestingModule({ - declarations: [ MasternodesComponent ], + declarations: [MasternodesComponent], imports: [ MomentModule, NgxPaginationModule, @@ -35,7 +35,7 @@ describe('MasternodesComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/masternodes/masternodes.component.ts b/web-ui/src/app/components/nodes/masternodes/masternodes.component.ts similarity index 62% rename from web-ui/src/app/components/masternodes/masternodes.component.ts rename to web-ui/src/app/components/nodes/masternodes/masternodes.component.ts index fb0e1d32..5fa602f1 100644 --- a/web-ui/src/app/components/masternodes/masternodes.component.ts +++ b/web-ui/src/app/components/nodes/masternodes/masternodes.component.ts @@ -1,16 +1,15 @@ -import {map, tap} from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; +import { Masternode } from '../../../models/masternode'; +import { MasternodesService } from '../../../services/masternodes.service'; +import { ErrorService } from '../../../services/error.service'; - -import { Masternode } from '../../models/masternode'; - -import { MasternodesService } from '../../services/masternodes.service'; -import { ErrorService } from '../../services/error.service'; +import { amAgo } from '../../../utils'; @Component({ selector: 'app-masternodes', @@ -27,6 +26,8 @@ export class MasternodesComponent implements OnInit { pageSize = 10; asyncItems: Observable; + amAgo = amAgo; + constructor( private masternodesService: MasternodesService, private errorService: ErrorService) { } @@ -41,8 +42,8 @@ export class MasternodesComponent implements OnInit { this.asyncItems = this.masternodesService .get(offset, limit, 'activeSeconds:desc').pipe( - tap(response => this.total = response.total), - tap(response => this.currentPage = 1 + (response.offset / this.pageSize)), - map(response => response.data),); + tap(response => this.total = response.total), + tap(response => this.currentPage = 1 + (response.offset / this.pageSize)), + map(response => response.data)); } } diff --git a/web-ui/src/app/components/address-details/address-details.component.css b/web-ui/src/app/components/nodes/node-list/node-list.component.css similarity index 100% rename from web-ui/src/app/components/address-details/address-details.component.css rename to web-ui/src/app/components/nodes/node-list/node-list.component.css diff --git a/web-ui/src/app/components/nodes/node-list/node-list.component.html b/web-ui/src/app/components/nodes/node-list/node-list.component.html new file mode 100644 index 00000000..1aa51163 --- /dev/null +++ b/web-ui/src/app/components/nodes/node-list/node-list.component.html @@ -0,0 +1,21 @@ +
+ + +
+
+
+ {{'label.masternodes' | translate}} +
+ +
+
+ +
+
+
+ {{'label.tposNodes' | translate}} +
+ +
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/nodes/node-list/node-list.component.spec.ts b/web-ui/src/app/components/nodes/node-list/node-list.component.spec.ts new file mode 100644 index 00000000..0e8a2641 --- /dev/null +++ b/web-ui/src/app/components/nodes/node-list/node-list.component.spec.ts @@ -0,0 +1,36 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { NodeListComponent } from './node-list.component'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; + +describe('NodeListComponent', () => { + let component: NodeListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + NodeListComponent + ], + imports: [ + TranslateModule.forRoot() + ], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NodeListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/nodes/node-list/node-list.component.ts b/web-ui/src/app/components/nodes/node-list/node-list.component.ts new file mode 100644 index 00000000..7ff5f42a --- /dev/null +++ b/web-ui/src/app/components/nodes/node-list/node-list.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-node-list', + templateUrl: './node-list.component.html', + styleUrls: ['./node-list.component.css'] +}) +export class NodeListComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } +} diff --git a/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.css b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.css new file mode 100644 index 00000000..525cbadc --- /dev/null +++ b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.css @@ -0,0 +1,25 @@ +.ticker-container +{ + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: space-between; + flex-wrap: wrap; + margin-left: -30px; + margin-right: -30px; +} + +div.data { + font-size: 18px; + color: #FFFFFF; + letter-spacing: 0; + margin-bottom: 10px; + font-weight: 300; +} + +div.lbl-bottom { + font-size: 12px; + color: #989DCC; + letter-spacing: 0.9px; + margin-bottom: 5px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.html b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.html new file mode 100644 index 00000000..f99795a6 --- /dev/null +++ b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.html @@ -0,0 +1,212 @@ +
+ +
+
+
+
+ {{'label.masternodes' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.masternodes}} + +
+
+ +
+
+ {{'label.enabled' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.enabledMasternodes}} ({{nodeStats.enabledMasternodes / nodeStats.masternodes | percent}}) + +
+
+ +
+
+ {{'label.coinsInMasternodes' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.masternodes * 15000}} XSN
+ ({{nodeStats.masternodes * 15000 / stats.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+
+ +
+
+ {{'label.masternodeROI' | translate}} +
+
+ + {{'message.unavailable' | translate}} + + + + 16.62% Year + +
+
+ +
+
+ {{'label.distributedAcross' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + 25 countries + +
+
+ +
+
+ {{'label.protocols' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + +
+ {{item.key}}: {{item.value}} ({{item.value / nodeStats.masternodes | percent}}) +
+
+
+
+
+
+ +
+
+
+
+ {{'label.tposNodes' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.tposnodes}} + +
+
+ +
+
+ {{'label.enabled' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.enabledTposnodes}} ({{nodeStats.enabledTposnodes / nodeStats.tposnodes | percent}}) + +
+
+ +
+
+ {{'label.coinsStaking' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.coinsStaking}} XSN
+ ({{nodeStats.coinsStaking / stats.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+
+ +
+
+ {{'label.stakingROI' | translate}} +
+
+ + {{'message.unavailable' | translate}} + + + + 16.62% Year + +
+
+ +
+
+ {{'label.distributedAcross' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + 25 countries + +
+
+ +
+
+ {{'label.protocols' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + +
+ {{item.key}}: {{item.value}} ({{item.value / nodeStats.tposnodes | percent}}) +
+
+
+
+ +
+
+ {{'label.coinsTrustlesslyStaking' | translate }} +
+
+ + {{'message.unavailable' | translate}} + + + + {{nodeStats.coinsStaking}} XSN
+ ({{nodeStats.coinsStaking / stats.circulatingSupply | number: '1.2-2' | percent}} of circ. supply) +
+
+
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.spec.ts b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.spec.ts new file mode 100644 index 00000000..642d844b --- /dev/null +++ b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.spec.ts @@ -0,0 +1,47 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NodeTickerComponent } from './node-ticker.component'; +import { ExplorerCurrencyPipe } from '../../../pipes/explorer-currency.pipe'; +import { ExplorerAmountPipe } from '../../../pipes/explorer-amount.pipe'; + +import { TranslateModule } from '@ngx-translate/core'; + +import { TickerService } from '../../../services/ticker.service'; +import { Observable } from 'rxjs'; + +describe('NodeTickerComponent', () => { + let component: NodeTickerComponent; + let fixture: ComponentFixture; + + const tickerServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TickerService', ['get', 'getNodeStats']); + + beforeEach(async(() => { + tickerServiceSpy.get.and.returnValue(Observable.create()); + tickerServiceSpy.getNodeStats.and.returnValue(Observable.create()); + + TestBed.configureTestingModule({ + declarations: [ + NodeTickerComponent, + ExplorerCurrencyPipe, + ExplorerAmountPipe + ], + imports: [ + TranslateModule.forRoot() + ], + providers: [ + { provide: TickerService, useValue: tickerServiceSpy } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NodeTickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.ts b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.ts new file mode 100644 index 00000000..a172751c --- /dev/null +++ b/web-ui/src/app/components/nodes/node-ticker/node-ticker.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; + +import { TickerService } from '../../../services/ticker.service'; +import { NodeStats, ServerStats } from '../../../models/ticker'; +import { Config } from '../../../config'; + +@Component({ + selector: 'app-node-ticker', + templateUrl: './node-ticker.component.html', + styleUrls: ['./node-ticker.component.css'] +}) +export class NodeTickerComponent implements OnInit { + + stats: ServerStats = new ServerStats(); + nodeStats: NodeStats = new NodeStats(); + config = Config; + + constructor(private tickerService: TickerService) { } + + ngOnInit() { + this.tickerService + .getNodeStats() + .subscribe( + response => this.nodeStats = response, + response => this.onError(response) + ); + + this.tickerService + .get() + .subscribe( + response => this.stats = response, + response => this.onError(response) + ); + } + + private onError(response: any) { + console.log(response); + } +} diff --git a/web-ui/src/app/components/nodes/nodes-routing.module.ts b/web-ui/src/app/components/nodes/nodes-routing.module.ts new file mode 100644 index 00000000..92fbcd9b --- /dev/null +++ b/web-ui/src/app/components/nodes/nodes-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { NodesComponent } from './nodes.component'; +import { MasternodeDetailsComponent } from './masternode-details/masternode-details.component'; +import { NodeListComponent } from './node-list/node-list.component'; + +const routes: Routes = [ + { + path: '', + component: NodesComponent, + children: [ + { + path: ':id', + component: MasternodeDetailsComponent + }, + { + path: '', + component: NodeListComponent + } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class NodesRoutingModule { } diff --git a/web-ui/src/app/components/nodes/nodes.component.html b/web-ui/src/app/components/nodes/nodes.component.html new file mode 100644 index 00000000..f5ba6fee --- /dev/null +++ b/web-ui/src/app/components/nodes/nodes.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/web-ui/src/app/components/nodes/nodes.component.ts b/web-ui/src/app/components/nodes/nodes.component.ts new file mode 100644 index 00000000..03503e8e --- /dev/null +++ b/web-ui/src/app/components/nodes/nodes.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-nodes', + templateUrl: './nodes.component.html' +}) +export class NodesComponent { } diff --git a/web-ui/src/app/components/nodes/nodes.module.ts b/web-ui/src/app/components/nodes/nodes.module.ts new file mode 100644 index 00000000..e42baac3 --- /dev/null +++ b/web-ui/src/app/components/nodes/nodes.module.ts @@ -0,0 +1,45 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { SharedModule } from '../shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { MomentModule } from 'ngx-moment'; +import { TabsModule, AlertModule } from 'ngx-bootstrap'; +import { PipesModule } from '../../pipes/pipes.module'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { NodesRoutingModule } from './nodes-routing.module'; +import { NodesComponent } from './nodes.component'; +import { MasternodesComponent } from './masternodes/masternodes.component'; +import { MasternodeDetailsComponent } from './masternode-details/masternode-details.component'; +import { NgxPaginationModule } from 'ngx-pagination'; +import { TposnodesComponent } from './tposnodes/tposnodes.component'; +import { NodeListComponent } from './node-list/node-list.component'; +import { NodeTickerComponent } from './node-ticker/node-ticker.component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + NodesRoutingModule, + SharedModule, + PipesModule, + TranslateModule, + MomentModule, + TabsModule, + AlertModule, + InfiniteScrollModule, + NgxPaginationModule + ], + declarations: [ + NodesComponent, + MasternodesComponent, + TposnodesComponent, + NodeListComponent, + NodeTickerComponent, + MasternodeDetailsComponent + ] +}) + +export class NodesModule { } diff --git a/web-ui/src/app/components/block-details/block-details.component.css b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.css similarity index 100% rename from web-ui/src/app/components/block-details/block-details.component.css rename to web-ui/src/app/components/nodes/tposnodes/tposnodes.component.css diff --git a/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.html b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.html new file mode 100644 index 00000000..286259e5 --- /dev/null +++ b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.html @@ -0,0 +1,42 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
#{{'label.ip' | translate | uppercase}}{{'label.protocol' | translate | uppercase}}{{'label.active' | translate | uppercase}}{{'label.lastSeen' | translate | uppercase}}{{'label.payee' | translate | uppercase}}
{{(currentPage - 1) * pageSize + index + 1}}{{item.ip}}{{item.protocol}}{{Math.max(item.activeSeconds, 0) | amDuration:'seconds'}}{{amAgo(item.lastSeen)}} + {{item.payee}} +
+
+ +
+
+ + +
+
+
diff --git a/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.spec.ts b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.spec.ts new file mode 100644 index 00000000..5740c78e --- /dev/null +++ b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.spec.ts @@ -0,0 +1,50 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TposnodesComponent } from './tposnodes.component'; + +import { MomentModule } from 'ngx-moment'; +import { NgxPaginationModule } from 'ngx-pagination'; +import { TranslateModule } from '@ngx-translate/core'; + +import { TposnodesService } from '../../../services/tposnodes.service'; +import { ErrorService } from '../../../services/error.service'; +import { Observable } from 'rxjs'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; + +describe('TposnodesComponent', () => { + let component: TposnodesComponent; + let fixture: ComponentFixture; + + const tposnodesServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TposnodesService', ['get']); + const errorServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('ErrorService', ['renderServerErrors']); + + beforeEach(async(() => { + tposnodesServiceSpy.get.and.returnValue(Observable.create()); + + TestBed.configureTestingModule({ + declarations: [TposnodesComponent], + imports: [ + MomentModule, + NgxPaginationModule, + TranslateModule.forRoot() + ], + providers: [ + { provide: TposnodesService, useValue: tposnodesServiceSpy }, + { provide: ErrorService, useValue: errorServiceSpy } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TposnodesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.ts b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.ts new file mode 100644 index 00000000..0ea942a0 --- /dev/null +++ b/web-ui/src/app/components/nodes/tposnodes/tposnodes.component.ts @@ -0,0 +1,49 @@ + +import { map, tap } from 'rxjs/operators'; +import { Component, OnInit } from '@angular/core'; + +import { Observable } from 'rxjs'; + +import { Tposnode } from '../../../models/tposnode'; + +import { TposnodesService } from '../../../services/tposnodes.service'; +import { ErrorService } from '../../../services/error.service'; + +import { amAgo } from '../../../utils'; + +@Component({ + selector: 'app-tposnodes', + templateUrl: './tposnodes.component.html', + styleUrls: ['./tposnodes.component.css'] +}) +export class TposnodesComponent implements OnInit { + + Math: Math = Math; + + // pagination + total = 0; + currentPage = 1; + pageSize = 10; + asyncItems: Observable; + + amAgo = amAgo; + + constructor( + private tposnodesService: TposnodesService, + private errorService: ErrorService) { } + + ngOnInit() { + this.getPage(this.currentPage); + } + + getPage(page: number) { + const offset = (page - 1) * this.pageSize; + const limit = this.pageSize; + + this.asyncItems = this.tposnodesService + .get(offset, limit, 'activeSeconds:desc').pipe( + tap(response => this.total = response.total), + tap(response => this.currentPage = 1 + (response.offset / this.pageSize)), + map(response => response.data)); + } +} diff --git a/web-ui/src/app/components/richest-addresses/richest-addresses.component.css b/web-ui/src/app/components/richest-addresses/richest-addresses.component.css deleted file mode 100644 index 1f6a9c52..00000000 --- a/web-ui/src/app/components/richest-addresses/richest-addresses.component.css +++ /dev/null @@ -1,9 +0,0 @@ -.new-address { - animation: blinker 1s linear; -} - -@keyframes blinker { - 50% { - background-color: #D4FFD4; - } -} diff --git a/web-ui/src/app/components/richest-addresses/richest-addresses.component.html b/web-ui/src/app/components/richest-addresses/richest-addresses.component.html deleted file mode 100644 index a776bca7..00000000 --- a/web-ui/src/app/components/richest-addresses/richest-addresses.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
-
-

{{'label.richestAddresses' | translate}}

-
- -
- - - - - - - - - - - - - - - - - - - - - -
#{{'label.address' | translate}}{{'label.amount' | translate}}{{'label.percentOfCoins' | translate}}{{'label.addressLabel' | translate}}
{{index + 1}} - {{item.address}} - {{item.available | explorerCurrency}}{{'message.unavailable' | translate}} -
- {{getPercent(item) | number:'1.2-2'}} % -
-
- {{addressLabel[item.address]}} -
-
-
diff --git a/web-ui/src/app/components/shared/block-table/block-table.component.css b/web-ui/src/app/components/shared/block-table/block-table.component.css new file mode 100644 index 00000000..2805a99a --- /dev/null +++ b/web-ui/src/app/components/shared/block-table/block-table.component.css @@ -0,0 +1,18 @@ +:host { + width: 100%; +} + +.block-table { + animation: blinker 1s linear; +} + +@keyframes blinker { + 50% { + background-color: #FFFFFF08; + } +} + +th:last-child, +td:last-child { + text-align: right; +} \ No newline at end of file diff --git a/web-ui/src/app/components/shared/block-table/block-table.component.html b/web-ui/src/app/components/shared/block-table/block-table.component.html new file mode 100644 index 00000000..e615773e --- /dev/null +++ b/web-ui/src/app/components/shared/block-table/block-table.component.html @@ -0,0 +1,44 @@ +
+ +
+
+

{{'message.loadingLatestBlocks' | translate}}

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
{{'label.height' | translate | uppercase}}{{'label.blockhash' | translate | uppercase}}{{'label.extractedBy' | translate | uppercase}}{{'label.difficulty' | translate | uppercase}}{{'label.transactions' | translate | uppercase}}{{'label.blocktime' | translate | uppercase}}
+ {{item.height}} + + {{truncate(item.hash, 12, 8) | uppercase}} + {{extractedBy(item) | translate}}{{item.difficulty | number:'1.2-2'}}{{item.transactions}} + {{item.time * 1000 | amLocal | amDateFormat: 'YYYY-MM-DD HH:mm:ss'}}
+ ({{amAgo(item.time)}}) +
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/latest-blocks/latest-blocks.component.spec.ts b/web-ui/src/app/components/shared/block-table/block-table.component.spec.ts similarity index 73% rename from web-ui/src/app/components/latest-blocks/latest-blocks.component.spec.ts rename to web-ui/src/app/components/shared/block-table/block-table.component.spec.ts index 6a635ca6..55b92985 100644 --- a/web-ui/src/app/components/latest-blocks/latest-blocks.component.spec.ts +++ b/web-ui/src/app/components/shared/block-table/block-table.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { LatestBlocksComponent } from './latest-blocks.component'; +import { BlockTableComponent } from './block-table.component'; import { MomentModule } from 'ngx-moment'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { BlocksService } from '../../services/blocks.service'; -import { ErrorService } from '../../services/error.service'; +import { BlocksService } from '../../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; -describe('LatestBlocksComponent', () => { - let component: LatestBlocksComponent; - let fixture: ComponentFixture; +describe('BlockTableComponent', () => { + let component: BlockTableComponent; + let fixture: ComponentFixture; const blocksServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('BlocksService', ['getLatest']); const errorServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('ErrorService', ['renderServerErrors']); @@ -24,7 +24,7 @@ describe('LatestBlocksComponent', () => { TestBed.configureTestingModule({ declarations: [ - LatestBlocksComponent + BlockTableComponent ], imports: [ TranslateModule.forRoot(), @@ -37,11 +37,11 @@ describe('LatestBlocksComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(LatestBlocksComponent); + fixture = TestBed.createComponent(BlockTableComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/web-ui/src/app/components/shared/block-table/block-table.component.ts b/web-ui/src/app/components/shared/block-table/block-table.component.ts new file mode 100644 index 00000000..362eb439 --- /dev/null +++ b/web-ui/src/app/components/shared/block-table/block-table.component.ts @@ -0,0 +1,89 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; + +import { Subscription } from 'rxjs'; + +import { Block } from '../../../models/block'; + +import { BlocksService } from '../../../services/blocks.service'; +import { ErrorService } from '../../../services/error.service'; +import { truncate, amAgo } from '../../../utils'; + +@Component({ + selector: 'app-block-table', + templateUrl: './block-table.component.html', + styleUrls: ['./block-table.component.css'] +}) +export class BlockTableComponent implements OnInit, OnDestroy { + + @Input() + hideBlockHash: boolean; + + blocks: Block[] = []; + private latestBlockHeight = 0; + private subscription$: Subscription; + + limit = 20; + + truncate = truncate; + amAgo = amAgo; + + constructor( + private route: ActivatedRoute, + private router: Router, + private blocksService: BlocksService, + private errorService: ErrorService) { } + + ngOnInit() { + this.updateBlocks(); + } + + ngOnDestroy() { + if (this.subscription$ != null) { + this.subscription$.unsubscribe(); + } + } + + private updateBlocks() { + let lastSeenHash = ''; + if (this.blocks.length > 0) { + lastSeenHash = this.blocks[this.blocks.length - 1].hash; + } + + this.blocksService + .getLatest(this.limit, lastSeenHash) + .subscribe( + response => this.onBlockRetrieved(response), + response => this.onError(response) + ); + } + + private onBlockRetrieved(response: Block[]) { + // this.latestBlockHeight = this.blocks.reduce((max, block) => Math.max(block.height, max), 0); + this.blocks = this.blocks.concat(response); + } + + private onError(response: any) { + this.errorService.renderServerErrors(null, response); + } + + extractedBy(block: Block): string { + if (block.height <= 75) { + return 'PoW'; + } + + if (block.tposContract == null) { + return 'PoS'; + } else { + return 'TPoS'; + } + } + + age(block: Block): string { + return ''; + } + + isBlockRecent(item: Block): boolean { + return item.height > this.latestBlockHeight; + } +} diff --git a/web-ui/src/app/components/shared/finder/finder.component.css b/web-ui/src/app/components/shared/finder/finder.component.css new file mode 100644 index 00000000..ecdc2128 --- /dev/null +++ b/web-ui/src/app/components/shared/finder/finder.component.css @@ -0,0 +1,20 @@ +.btn-search { + background: #FFFFFF08 0% 0% no-repeat padding-box; + height: 40px; + border: none; + border-radius: 8px; + margin-left: 0 !important; +} + +.btn-search:disabled { + background: #FFFFFF08 0% 0% no-repeat padding-box; +} + +.input-group-btn { + background: transparent; +} + +.finder-wrapper { + padding: 0; + margin-top: 5px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/shared/finder/finder.component.html b/web-ui/src/app/components/shared/finder/finder.component.html new file mode 100644 index 00000000..a38be6c7 --- /dev/null +++ b/web-ui/src/app/components/shared/finder/finder.component.html @@ -0,0 +1,27 @@ +
+
+ +
+
+ + + + + + +
+
+ +
+ {{ errorService.getFieldError(form, 'searchField') | translate }} +
+ +
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/finder/finder.component.spec.ts b/web-ui/src/app/components/shared/finder/finder.component.spec.ts similarity index 85% rename from web-ui/src/app/components/finder/finder.component.spec.ts rename to web-ui/src/app/components/shared/finder/finder.component.spec.ts index 0247312f..28177a95 100644 --- a/web-ui/src/app/components/finder/finder.component.spec.ts +++ b/web-ui/src/app/components/shared/finder/finder.component.spec.ts @@ -6,12 +6,12 @@ import { TranslateModule } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NavigatorService } from '../../services/navigator.service'; -import { AddressesService } from '../../services/addresses.service'; -import { BlocksService } from '../../services/blocks.service'; -import { MasternodesService } from '../../services/masternodes.service'; -import { TransactionsService } from '../../services/transactions.service'; -import { ErrorService } from '../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { AddressesService } from '../../../services/addresses.service'; +import { BlocksService } from '../../../services/blocks.service'; +import { MasternodesService } from '../../../services/masternodes.service'; +import { TransactionsService } from '../../../services/transactions.service'; +import { ErrorService } from '../../../services/error.service'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -62,7 +62,7 @@ describe('TransactionFinderComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/finder/finder.component.ts b/web-ui/src/app/components/shared/finder/finder.component.ts similarity index 83% rename from web-ui/src/app/components/finder/finder.component.ts rename to web-ui/src/app/components/shared/finder/finder.component.ts index ac90bdcd..357a9491 100644 --- a/web-ui/src/app/components/finder/finder.component.ts +++ b/web-ui/src/app/components/shared/finder/finder.component.ts @@ -3,12 +3,12 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; -import { ErrorService } from '../../services/error.service'; -import { NavigatorService } from '../../services/navigator.service'; -import { AddressesService } from '../../services/addresses.service'; -import { BlocksService } from '../../services/blocks.service'; -import { TransactionsService } from '../../services/transactions.service'; -import { MasternodesService } from '../../services/masternodes.service'; +import { ErrorService } from '../../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { AddressesService } from '../../../services/addresses.service'; +import { BlocksService } from '../../../services/blocks.service'; +import { TransactionsService } from '../../../services/transactions.service'; +import { MasternodesService } from '../../../services/masternodes.service'; const BLOCK_REGEX = '^[A-Fa-f0-9]{64}$'; const BLOCK_NUMBER_REGEX = '^(\\d{1,10})$'; @@ -48,6 +48,10 @@ export class FinderComponent implements OnInit { } onSubmit() { + if (!this.form.valid) { + return; + } + const searchField = this.form.get('searchField').value; if (new RegExp(ADDRESS_REGEX).test(searchField)) { @@ -79,7 +83,7 @@ export class FinderComponent implements OnInit { private lookForBlock(query: string) { this.blocksService.get(query) .subscribe( - response => this.navigatorService.blockDetails(query), + response => this.navigatorService.blockDetails(query), response => this.onNothingFound() ); } diff --git a/web-ui/src/app/components/footer/footer.component.css b/web-ui/src/app/components/shared/footer/footer.component.css similarity index 100% rename from web-ui/src/app/components/footer/footer.component.css rename to web-ui/src/app/components/shared/footer/footer.component.css diff --git a/web-ui/src/app/components/footer/footer.component.html b/web-ui/src/app/components/shared/footer/footer.component.html similarity index 100% rename from web-ui/src/app/components/footer/footer.component.html rename to web-ui/src/app/components/shared/footer/footer.component.html diff --git a/web-ui/src/app/components/footer/footer.component.spec.ts b/web-ui/src/app/components/shared/footer/footer.component.spec.ts similarity index 100% rename from web-ui/src/app/components/footer/footer.component.spec.ts rename to web-ui/src/app/components/shared/footer/footer.component.spec.ts diff --git a/web-ui/src/app/components/footer/footer.component.ts b/web-ui/src/app/components/shared/footer/footer.component.ts similarity index 100% rename from web-ui/src/app/components/footer/footer.component.ts rename to web-ui/src/app/components/shared/footer/footer.component.ts diff --git a/web-ui/src/app/components/shared/navbar/navbar.component.css b/web-ui/src/app/components/shared/navbar/navbar.component.css new file mode 100644 index 00000000..69469b8a --- /dev/null +++ b/web-ui/src/app/components/shared/navbar/navbar.component.css @@ -0,0 +1,53 @@ +.navbar-default { + background-color: #090D1C; + border-color: #090D1C; + padding: 48px 0 0; +} + +.navbar-logo { + width: 35px; + height: 35px; + margin-top: -7.5px; +} + +.navbar-brand { + font-size: 30px; + text-align: left; + letter-spacing: -1.5px; + color: #FFFFFF; + font-weight: 300; +} + +.navbar-brand:hover { + color: #FFFFFF; +} + +.navbar-brand-bold { + font-weight: 500; +} + +.navbar-tabbar { + margin-top: 30px; + padding: 0 15px; +} + +.navbar-nav>li.active { + background: transparent linear-gradient(0deg, #01D9DE18 0%, #2558EB00 100%) 0% 0% no-repeat padding-box; +} + +.navbar-nav>li { + background: transparent; +} + +.navbar-nav>li>.navbar-tab { + background: transparent; + color: #5C5F7D; +} + +.navbar-nav>li>.navbar-tab.main-tab { + color: #989DCC; +} + +.navbar-nav>li.active>.navbar-tab { + color: #FFFFFF; +} \ No newline at end of file diff --git a/web-ui/src/app/components/shared/navbar/navbar.component.html b/web-ui/src/app/components/shared/navbar/navbar.component.html new file mode 100644 index 00000000..9b4b0ff3 --- /dev/null +++ b/web-ui/src/app/components/shared/navbar/navbar.component.html @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/web-ui/src/app/components/navbar/navbar.component.spec.ts b/web-ui/src/app/components/shared/navbar/navbar.component.spec.ts similarity index 100% rename from web-ui/src/app/components/navbar/navbar.component.spec.ts rename to web-ui/src/app/components/shared/navbar/navbar.component.spec.ts diff --git a/web-ui/src/app/components/shared/navbar/navbar.component.ts b/web-ui/src/app/components/shared/navbar/navbar.component.ts new file mode 100644 index 00000000..e9b0f6f6 --- /dev/null +++ b/web-ui/src/app/components/shared/navbar/navbar.component.ts @@ -0,0 +1,74 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +class Tab { + label: string; + path: string; + mainTab: boolean; +} + +@Component({ + selector: 'app-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.css'] +}) +export class NavbarComponent implements OnInit { + + public tabs: Tab[] = [ + { + label: 'Dashboard', + path: '/', + mainTab: true + }, + { + label: 'Nodes', + path: '/nodes', + mainTab: true + }, + { + label: 'Blocks', + path: '/blocks', + mainTab: true + }, + { + label: 'Transactions', + path: '/transactions', + mainTab: true + }, + { + label: 'Addresses', + path: '/addresses', + mainTab: true + }, + { + label: 'Calculator', + path: '/calculator', + mainTab: false + }, + { + label: 'Governance', + path: '/governance', + mainTab: false + } + ]; + + public currentUrl = null; + + constructor(private router: Router) { + } + + ngOnInit() { + } + + /* tabs */ + isSelected(path: string): boolean { + path = path.replace(/\//g, ''); + + const segments = this.router.url.split('/'); + if (path === '/') { + return segments.length === 1; + } + + return segments[1] === path; + } +} diff --git a/web-ui/src/app/components/shared/shared.module.ts b/web-ui/src/app/components/shared/shared.module.ts new file mode 100644 index 00000000..6ee32dd7 --- /dev/null +++ b/web-ui/src/app/components/shared/shared.module.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { MomentModule } from 'ngx-moment'; +import { NavbarComponent } from './navbar/navbar.component'; +import { FinderComponent } from './finder/finder.component'; +import { FooterComponent } from './footer/footer.component'; +import { BlockTableComponent } from './block-table/block-table.component'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { TransactionTableComponent } from './transaction-table/transaction-table.component'; +import { PipesModule } from '../../pipes/pipes.module'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + CommonModule, + PipesModule, + ReactiveFormsModule, + RouterModule, + TranslateModule, + MomentModule, + InfiniteScrollModule + ], + declarations: [ + NavbarComponent, + FinderComponent, + FooterComponent, + BlockTableComponent, + TransactionTableComponent + ], + exports: [ + NavbarComponent, + FinderComponent, + FooterComponent, + BlockTableComponent, + TransactionTableComponent + ] +}) +export class SharedModule { } diff --git a/web-ui/src/app/components/shared/transaction-table/transaction-table.component.css b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.css new file mode 100644 index 00000000..4ff334b3 --- /dev/null +++ b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.css @@ -0,0 +1,11 @@ +:host { + width: 100%; +} + +span.glyphicon-ok { + color: #3FE061; +} + +span.glyphicon-remove { + color: #F15F5F; +} \ No newline at end of file diff --git a/web-ui/src/app/components/shared/transaction-table/transaction-table.component.html b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.html new file mode 100644 index 00000000..0f0a2fe0 --- /dev/null +++ b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.html @@ -0,0 +1,52 @@ +
+ +
+
+

{{'message.loadingTransactions' | translate}}

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{'label.height' | translate | uppercase}}{{'label.txHash' | translate | uppercase}}{{'label.type' | translate | uppercase}}{{'label.result' | translate | uppercase}}{{'label.amount' | translate | uppercase}}{{'label.fee' | translate | uppercase}}{{'label.height' | translate | uppercase}}{{'label.time' | translate | uppercase}}
+ {{item.height}} + + {{truncate(item.id, 8, 5) | uppercase}} + Reward: Masternode + {{getResult(item) ? 'Success' : 'Fail'}} + {{getAmount(item) | explorerCurrency}}{{getFee(item) | explorerCurrency}} + {{item.height}} + + {{item.time * 1000 | amLocal | amDateFormat: 'YYYY-MM-DD HH:mm:ss'}}
+ ({{amAgo(item.time)}}) +
+
+
+
\ No newline at end of file diff --git a/web-ui/src/app/components/shared/transaction-table/transaction-table.component.spec.ts b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.spec.ts new file mode 100644 index 00000000..a687d467 --- /dev/null +++ b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.spec.ts @@ -0,0 +1,58 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TransactionTableComponent } from './transaction-table.component'; + +import { MomentModule } from 'ngx-moment'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { PipesModule } from '../../../pipes/pipes.module'; + +import { TransactionsService } from '../../../services/transactions.service'; +import { AddressesService } from '../../../services/addresses.service'; +import { ErrorService } from '../../../services/error.service'; +import { Observable } from 'rxjs'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; + +describe('TransactionTableComponent', () => { + let component: TransactionTableComponent; + let fixture: ComponentFixture; + + const transactionsServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('TransactionsService', ['getList']); + const addressesServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('AddressesService', ['getTransactions']); + const errorServiceSpy: jasmine.SpyObj = jasmine.createSpyObj('ErrorService', ['renderServerErrors']); + + beforeEach(async(() => { + transactionsServiceSpy.getList.and.returnValue(Observable.create()); + addressesServiceSpy.getTransactions.and.returnValue(Observable.create()); + + TestBed.configureTestingModule({ + declarations: [ + TransactionTableComponent + ], + imports: [ + TranslateModule.forRoot(), + MomentModule, + PipesModule, + RouterTestingModule + ], + providers: [ + { provide: TransactionsService, useValue: transactionsServiceSpy }, + { provide: AddressesService, useValue: addressesServiceSpy }, + { provide: ErrorService, useValue: errorServiceSpy } + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TransactionTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/shared/transaction-table/transaction-table.component.ts b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.ts new file mode 100644 index 00000000..6746062f --- /dev/null +++ b/web-ui/src/app/components/shared/transaction-table/transaction-table.component.ts @@ -0,0 +1,93 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; + +import { Subscription } from 'rxjs'; + +import { TransactionsService } from '../../../services/transactions.service'; +import { ErrorService } from '../../../services/error.service'; +import { AddressesService } from '../../../services/addresses.service'; +import { truncate, amAgo } from '../../../utils'; +import { Transaction } from '../../../models/transaction'; + +@Component({ + selector: 'app-transaction-table', + templateUrl: './transaction-table.component.html', + styleUrls: ['./transaction-table.component.css'] +}) +export class TransactionTableComponent implements OnInit, OnDestroy { + + @Input() + hideBlockHash: boolean; + + @Input() + address: string; + + transactions: Transaction[] = []; + private subscription$: Subscription; + + limit = 20; + + truncate = truncate; + amAgo = amAgo; + + constructor( + private transactionsService: TransactionsService, + private addressesService: AddressesService, + private errorService: ErrorService) { } + + ngOnInit() { + this.updateTransactions(); + } + + ngOnDestroy() { + if (this.subscription$ != null) { + this.subscription$.unsubscribe(); + } + } + + private updateTransactions() { + let lastSeenTxId = ''; + if (this.transactions.length > 0) { + lastSeenTxId = this.transactions[this.transactions.length - 1].id; + } + + if (this.address) { + this.addressesService + .getTransactions(this.address, this.limit, lastSeenTxId) + .subscribe( + response => this.onTransactionRetrieved(response.data), + response => this.onError(response) + ); + } else { + this.transactionsService + .getList(lastSeenTxId, this.limit) + .subscribe( + response => this.onTransactionRetrieved(response.data), + response => this.onError(response) + ); + } + } + + private onTransactionRetrieved(response: Transaction[]) { + // this.lastSeenTxId = this.transactions.reduce((max, block) => Math.max(block.height, max), 0); + this.transactions = this.transactions.concat(response); + } + + private onError(response: any) { + this.errorService.renderServerErrors(null, response); + } + + getResult(item: Transaction) { + if (item.height) { + return true; + } + return false; + } + + getAmount(item: Transaction) { + return item.received; + } + + getFee(item: Transaction) { + return Math.max(item.sent - item.received, 0); + } +} diff --git a/web-ui/src/app/components/ticker/ticker.component.css b/web-ui/src/app/components/ticker/ticker.component.css deleted file mode 100644 index 94ae34e9..00000000 --- a/web-ui/src/app/components/ticker/ticker.component.css +++ /dev/null @@ -1,61 +0,0 @@ -span.help { - color: blue; - cursor: help; - font-size: 16px; - float: right; -} - -div.panel-default { - border: 1px solid #6672A6; - border-radius: 8px; - border-radius: 8px; - background-color: #141829; - padding-left: 2px !important; - padding-right: 2px !important; -} - -div.data { - opacity: 0.9; - font-family: sans-serif; - font-size: 20px; - color: #FFFFFF; - letter-spacing: 0; - padding: 15px; - padding-bottom: 0px; - font-weight: bold; -} - -div.lbl-bottom { - font-family: sans-serif; - font-size: 11px; - color: #8192DB; - letter-spacing: 0.9px; - padding: 15px; - padding-top: 0px; - font-weight: bold; -} - -small.currency { - font-size: 10px; - vertical-align: text-top; - padding-top: 10px; -} - -@media (max-width:767px) { - - .no-left, - .no-right { - padding-left: 0; - padding-right: 0; - } -} - -@media (min-width:768px) { - .no-left { - padding-left: 0; - } - - .no-right { - padding-right: 0; - } -} diff --git a/web-ui/src/app/components/ticker/ticker.component.html b/web-ui/src/app/components/ticker/ticker.component.html deleted file mode 100644 index e787f8b2..00000000 --- a/web-ui/src/app/components/ticker/ticker.component.html +++ /dev/null @@ -1,79 +0,0 @@ -
-
- -
-
- -
- - {{'message.unavailable' | translate}} - - - {{ticker.totalSupply | explorerAmount}} - {{config.currentCurrency}} - -
-
- {{'label.totalSupply' | translate | uppercase}} - -
-
-
- -
-
- -
- - {{'message.unavailable' | translate}} - - - - {{ticker.circulatingSupply | explorerAmount}} - {{config.currentCurrency}} - -
- -
- {{'label.circulatingSupply' | translate | uppercase}} - -
-
-
- -
-
- -
- - {{'message.unavailable' | translate}} - - - - {{ticker.blocks}} - -
-
- {{'label.blocks' | translate | uppercase}} -
-
-
- -
-
-
- - {{'message.unavailable' | translate}} - - - - {{ticker.masternodes}} - -
-
- {{'label.masternodes' | translate | uppercase}} -
-
-
-
-
diff --git a/web-ui/src/app/components/ticker/ticker.component.ts b/web-ui/src/app/components/ticker/ticker.component.ts deleted file mode 100644 index 3c3ea490..00000000 --- a/web-ui/src/app/components/ticker/ticker.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { TickerService } from '../../services/ticker.service'; -import { ServerStats } from '../../models/ticker'; -import { Config } from '../../config'; - - -@Component({ - selector: 'app-ticker', - templateUrl: './ticker.component.html', - styleUrls: ['./ticker.component.css'] -}) -export class TickerComponent implements OnInit { - - ticker: ServerStats; - config = Config; - - constructor(private tickerService: TickerService) { } - - ngOnInit() { - this.tickerService - .get() - .subscribe( - response => this.onTickerRetrieved(response), - response => this.onError(response) - ); - } - - private onTickerRetrieved(ticker: ServerStats) { - this.ticker = ticker; - } - - private onError(response: any) { - console.log(response); - } -} diff --git a/web-ui/src/app/components/transaction-raw/transaction-raw.component.css b/web-ui/src/app/components/transaction-raw/transaction-raw.component.css deleted file mode 100644 index e69de29b..00000000 diff --git a/web-ui/src/app/components/transaction/transaction.component.css b/web-ui/src/app/components/transaction/transaction.component.css deleted file mode 100644 index e69de29b..00000000 diff --git a/web-ui/src/app/components/transaction/transaction.component.html b/web-ui/src/app/components/transaction/transaction.component.html deleted file mode 100644 index bd645bc3..00000000 --- a/web-ui/src/app/components/transaction/transaction.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
-
-
-

{{'label.transaction' | translate}}

- - - - - - - - -
-
-
-
diff --git a/web-ui/src/app/components/transaction-details/transaction-details.component.css b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.css similarity index 100% rename from web-ui/src/app/components/transaction-details/transaction-details.component.css rename to web-ui/src/app/components/transactions/transaction-details/transaction-details.component.css diff --git a/web-ui/src/app/components/transaction-details/transaction-details.component.html b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.html similarity index 66% rename from web-ui/src/app/components/transaction-details/transaction-details.component.html rename to web-ui/src/app/components/transactions/transaction-details/transaction-details.component.html index 1360a72a..fb062ffe 100644 --- a/web-ui/src/app/components/transaction-details/transaction-details.component.html +++ b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.html @@ -1,4 +1,4 @@ -
+
{{'message.transactionNotFound' | translate}}
@@ -6,35 +6,35 @@
- +
- + - + - + - + - + - + @@ -43,21 +43,25 @@ -
-
-
{{'label.summary' | translate}}{{'label.summary' | translate}}
{{'label.transactionId' | translate}}{{'label.transactionId' | translate}} {{transaction.id}}
{{'label.confirmations' | translate}}{{'label.confirmations' | translate}} {{transaction.confirmations}}
{{'label.blockhash' | translate}}{{'label.blockhash' | translate}} - {{transaction.blockhash}} + {{transaction.blockhash}}
{{'label.blocktime' | translate}}{{'label.blocktime' | translate}} {{transaction.blocktime * 1000 | explorerDatetime}}
{{'label.fee' | translate}}{{'label.fee' | translate}} {{getFee(transaction) | explorerCurrency}}
+
- + - - + + @@ -68,10 +72,10 @@ @@ -84,16 +88,16 @@ - diff --git a/web-ui/src/app/components/transaction-details/transaction-details.component.spec.ts b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.spec.ts similarity index 81% rename from web-ui/src/app/components/transaction-details/transaction-details.component.spec.ts rename to web-ui/src/app/components/transactions/transaction-details/transaction-details.component.spec.ts index 0e4b96fc..95bf20e3 100644 --- a/web-ui/src/app/components/transaction-details/transaction-details.component.spec.ts +++ b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.spec.ts @@ -1,15 +1,15 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TransactionDetailsComponent } from './transaction-details.component'; -import { ExplorerDatetimePipe } from '../../pipes/explorer-datetime.pipe'; -import { ExplorerCurrencyPipe } from '../../pipes/explorer-currency.pipe'; +import { ExplorerDatetimePipe } from '../../../pipes/explorer-datetime.pipe'; +import { ExplorerCurrencyPipe } from '../../../pipes/explorer-currency.pipe'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { NavigatorService } from '../../services/navigator.service'; -import { TransactionsService } from '../../services/transactions.service'; -import { ErrorService } from '../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { TransactionsService } from '../../../services/transactions.service'; +import { ErrorService } from '../../../services/error.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -42,7 +42,7 @@ describe('TransactionDetailsComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/transaction-details/transaction-details.component.ts b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.ts similarity index 77% rename from web-ui/src/app/components/transaction-details/transaction-details.component.ts rename to web-ui/src/app/components/transactions/transaction-details/transaction-details.component.ts index 5eac8fd2..e214f0a5 100644 --- a/web-ui/src/app/components/transaction-details/transaction-details.component.ts +++ b/web-ui/src/app/components/transactions/transaction-details/transaction-details.component.ts @@ -1,13 +1,11 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; +import { Transaction, TransactionValue } from '../../../models/transaction'; -import { Transaction, TransactionValue } from '../../models/transaction'; - -import { ErrorService } from '../../services/error.service'; -import { NavigatorService } from '../../services/navigator.service'; -import { TransactionsService } from '../../services/transactions.service'; +import { ErrorService } from '../../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { TransactionsService } from '../../../services/transactions.service'; @Component({ selector: 'app-transaction-details', @@ -28,7 +26,7 @@ export class TransactionDetailsComponent implements OnInit { private errorService: ErrorService) { } ngOnInit() { - this.route.params.forEach(params => this.onTransactionId(params['txid'])); + this.route.params.forEach(params => this.onTransactionId(params['id'])); } private onTransactionId(txid: string) { @@ -52,16 +50,16 @@ export class TransactionDetailsComponent implements OnInit { const addresses = new Set(rows.map(r => r.address)); const collapsedRows = Array.from(addresses) .map(address => { - const sum = rows - .filter(r => r.address === address) - .map(r => r.value) - .reduce((a, b) => a + b); + const sum = rows + .filter(r => r.address === address) + .map(r => r.value) + .reduce((a, b) => a + b); - const newValue = new TransactionValue(); - newValue.address = address; - newValue.value = sum; + const newValue = new TransactionValue(); + newValue.address = address; + newValue.value = sum; - return newValue; + return newValue; }); return collapsedRows; diff --git a/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.css b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.css new file mode 100644 index 00000000..85929d49 --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.css @@ -0,0 +1,20 @@ +.transaction-table-wrapper { + width: 100%; + padding: 0; + margin-top: 15px; +} + +.blocks-info { + float: right; + background-color: #1C202E; + border-radius: 20px; + display: flex; + flex-direction: row; + align-items: center; + padding: 2px 6px; + font-size: 12px; +} + +.blocks-info>div { + margin: 10px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.html b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.html new file mode 100644 index 00000000..db920d60 --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.html @@ -0,0 +1,21 @@ +
+
+ {{'label.transactinoLabel' | translate}} +
+
+
+
+ {{'label.height' | translate | uppercase}}: 1551778 +
+
+ {{'label.inflation' | translate | uppercase}}: 7.58% +
+
+ {{'label.price' | translate | uppercase}}: $2.63 +
+
+
+
+ +
+
\ No newline at end of file diff --git a/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.spec.ts b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.spec.ts new file mode 100644 index 00000000..4cd8dda1 --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.spec.ts @@ -0,0 +1,36 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { TransactionListComponent } from './transaction-list.component'; + +import { NO_ERRORS_SCHEMA, } from '@angular/core'; + +describe('TransactionListComponent', () => { + let component: TransactionListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + TransactionListComponent + ], + imports: [ + TranslateModule.forRoot() + ], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TransactionListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.ts b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.ts new file mode 100644 index 00000000..d1c41dfd --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction-list/transaction-list.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-transaction-list', + templateUrl: './transaction-list.component.html', + styleUrls: ['./transaction-list.component.css'] +}) +export class TransactionListComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } +} diff --git a/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.css b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.css new file mode 100644 index 00000000..c5a0d11b --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.css @@ -0,0 +1,5 @@ +pre { + background-color: transparent; + border: none; + padding: 10px 20px; +} \ No newline at end of file diff --git a/web-ui/src/app/components/transaction-raw/transaction-raw.component.html b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.html similarity index 73% rename from web-ui/src/app/components/transaction-raw/transaction-raw.component.html rename to web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.html index 63334a43..115cbb24 100644 --- a/web-ui/src/app/components/transaction-raw/transaction-raw.component.html +++ b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.html @@ -3,6 +3,6 @@ {{'message.transactionNotFound' | translate}}
-
{{transaction | json}}
+
{{transaction | json}}
diff --git a/web-ui/src/app/components/transaction-raw/transaction-raw.component.spec.ts b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.spec.ts similarity index 84% rename from web-ui/src/app/components/transaction-raw/transaction-raw.component.spec.ts rename to web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.spec.ts index aecf86bc..543b450a 100644 --- a/web-ui/src/app/components/transaction-raw/transaction-raw.component.spec.ts +++ b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.spec.ts @@ -5,9 +5,9 @@ import { TransactionRawComponent } from './transaction-raw.component'; import { TranslateModule } from '@ngx-translate/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { NavigatorService } from '../../services/navigator.service'; -import { TransactionsService } from '../../services/transactions.service'; -import { ErrorService } from '../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { TransactionsService } from '../../../services/transactions.service'; +import { ErrorService } from '../../../services/error.service'; import { Observable } from 'rxjs'; import { NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -24,7 +24,7 @@ describe('TransactionRawComponent', () => { transactionsServiceSpy.getRaw.and.returnValue(Observable.create()); TestBed.configureTestingModule({ - declarations: [ TransactionRawComponent ], + declarations: [TransactionRawComponent], imports: [ TranslateModule.forRoot(), RouterTestingModule @@ -36,7 +36,7 @@ describe('TransactionRawComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/web-ui/src/app/components/transaction-raw/transaction-raw.component.ts b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.ts similarity index 75% rename from web-ui/src/app/components/transaction-raw/transaction-raw.component.ts rename to web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.ts index 04051eae..1f28a33c 100644 --- a/web-ui/src/app/components/transaction-raw/transaction-raw.component.ts +++ b/web-ui/src/app/components/transactions/transaction-raw/transaction-raw.component.ts @@ -1,13 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; - -import { Transaction } from '../../models/transaction'; - -import { ErrorService } from '../../services/error.service'; -import { NavigatorService } from '../../services/navigator.service'; -import { TransactionsService } from '../../services/transactions.service'; +import { ErrorService } from '../../../services/error.service'; +import { NavigatorService } from '../../../services/navigator.service'; +import { TransactionsService } from '../../../services/transactions.service'; @Component({ selector: 'app-transaction-raw', @@ -26,7 +22,7 @@ export class TransactionRawComponent implements OnInit { private errorService: ErrorService) { } ngOnInit() { - this.route.params.forEach(params => this.onTransactionId(params['txid'])); + this.route.params.forEach(params => this.onTransactionId(params['id'])); } private onTransactionId(txid: string) { diff --git a/web-ui/src/app/components/transactions/transaction/transaction.component.css b/web-ui/src/app/components/transactions/transaction/transaction.component.css new file mode 100644 index 00000000..68ed9e2b --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction/transaction.component.css @@ -0,0 +1,43 @@ +.transaction-tab-bar { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; +} + +.transaction-tab-bar>li>a { + color: #01D9DE; + background-color: transparent; + font-size: 14px; + padding: 0; +} + +.transaction-tab-bar>li { + margin: 0 5px; + padding: 13px 13px 9px; + cursor: pointer; +} + +.transaction-tab-bar>li.active>a { + color: #FFFFFF; +} + +.transaction-tab-bar>li.active, +.transaction-tab-bar>li:hover { + background-color: #222534; + border-radius: 20px; +} + +.transaction-tab-content { + padding: 10px 0; + border-top: 1px solid #282D3C; +} + +.transaction-tab-footer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 20px; + border-top: 1px solid #282D3C; +} \ No newline at end of file diff --git a/web-ui/src/app/components/transactions/transaction/transaction.component.html b/web-ui/src/app/components/transactions/transaction/transaction.component.html new file mode 100644 index 00000000..a77d1959 --- /dev/null +++ b/web-ui/src/app/components/transactions/transaction/transaction.component.html @@ -0,0 +1,27 @@ +
+
+ + + +   {{'label.transactinoLabel' | translate}} +
+ +
+ + + + +
+ + +
\ No newline at end of file diff --git a/web-ui/src/app/components/transaction/transaction.component.spec.ts b/web-ui/src/app/components/transactions/transaction/transaction.component.spec.ts similarity index 100% rename from web-ui/src/app/components/transaction/transaction.component.spec.ts rename to web-ui/src/app/components/transactions/transaction/transaction.component.spec.ts diff --git a/web-ui/src/app/components/transaction/transaction.component.ts b/web-ui/src/app/components/transactions/transaction/transaction.component.ts similarity index 100% rename from web-ui/src/app/components/transaction/transaction.component.ts rename to web-ui/src/app/components/transactions/transaction/transaction.component.ts diff --git a/web-ui/src/app/components/transactions/transactions-routing.module.ts b/web-ui/src/app/components/transactions/transactions-routing.module.ts new file mode 100644 index 00000000..19c714a2 --- /dev/null +++ b/web-ui/src/app/components/transactions/transactions-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { TransactionsComponent } from './transactions.component'; +import { TransactionComponent } from './transaction/transaction.component'; +import { TransactionListComponent } from './transaction-list/transaction-list.component'; + +const routes: Routes = [ + { + path: '', + component: TransactionsComponent, + children: [ + { + path: ':id', + component: TransactionComponent + }, + { + path: '', + component: TransactionListComponent + } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class TransactionsRoutingModule { } diff --git a/web-ui/src/app/components/transactions/transactions.component.html b/web-ui/src/app/components/transactions/transactions.component.html new file mode 100644 index 00000000..f68959dc --- /dev/null +++ b/web-ui/src/app/components/transactions/transactions.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/web-ui/src/app/components/transactions/transactions.component.ts b/web-ui/src/app/components/transactions/transactions.component.ts new file mode 100644 index 00000000..8d90e8de --- /dev/null +++ b/web-ui/src/app/components/transactions/transactions.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-transactions', + templateUrl: './transactions.component.html' +}) +export class TransactionsComponent { } diff --git a/web-ui/src/app/components/transactions/transactions.module.ts b/web-ui/src/app/components/transactions/transactions.module.ts new file mode 100644 index 00000000..74518098 --- /dev/null +++ b/web-ui/src/app/components/transactions/transactions.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { SharedModule } from '../shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { MomentModule } from 'ngx-moment'; +import { TabsModule, AlertModule } from 'ngx-bootstrap'; +import { PipesModule } from '../../pipes/pipes.module'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { TransactionsRoutingModule } from './transactions-routing.module'; +import { TransactionsComponent } from './transactions.component'; +import { TransactionComponent } from './transaction/transaction.component'; +import { TransactionDetailsComponent } from './transaction-details/transaction-details.component'; +import { TransactionRawComponent } from './transaction-raw/transaction-raw.component'; +import { TransactionListComponent } from './transaction-list/transaction-list.component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + TransactionsRoutingModule, + SharedModule, + PipesModule, + TranslateModule, + MomentModule, + TabsModule, + AlertModule, + InfiniteScrollModule + ], + declarations: [ + TransactionsComponent, + TransactionListComponent, + TransactionComponent, + TransactionDetailsComponent, + TransactionRawComponent + ] +}) + +export class TransactionsModule { } diff --git a/web-ui/src/app/models/block.ts b/web-ui/src/app/models/block.ts index 0745eb73..1582eae8 100644 --- a/web-ui/src/app/models/block.ts +++ b/web-ui/src/app/models/block.ts @@ -10,7 +10,7 @@ export class Block { previousBlockhash: string; nextBlockhash: string; merkleRoot: string; - transactions: string[]; + transactions: string[] | number; confirmations: number; size: number; height: number; diff --git a/web-ui/src/app/models/ticker.ts b/web-ui/src/app/models/ticker.ts index a7602d42..770af23e 100644 --- a/web-ui/src/app/models/ticker.ts +++ b/web-ui/src/app/models/ticker.ts @@ -5,4 +5,22 @@ export class ServerStats { transactions: number; blocks: number; masternodes: number; + tposnodes: number; +} + +export class NodeStats { + masternodes: number; + enabledMasternodes: number; + masternodesProtocols: Record; + tposnodes: number; + enabledTposnodes: number; + tposnodesProtocols: Record; + coinsStaking: number; +} + +export class Prices { + usd: number; + btc: number; + volume: number; + marketcap: number; } diff --git a/web-ui/src/app/models/tposcontract.ts b/web-ui/src/app/models/tposcontract.ts new file mode 100644 index 00000000..98992f35 --- /dev/null +++ b/web-ui/src/app/models/tposcontract.ts @@ -0,0 +1,11 @@ + +export class TposContract { + txid: string; + index: number; + owner: string; + merchant: string; + merchantCommission: number; + time: number; + state: string; +} + diff --git a/web-ui/src/app/models/tposnode.ts b/web-ui/src/app/models/tposnode.ts new file mode 100644 index 00000000..7c22e53b --- /dev/null +++ b/web-ui/src/app/models/tposnode.ts @@ -0,0 +1,11 @@ + +export class Tposnode { + pubkey: string; + payee: string; + activeSeconds: number; + lastSeen: number; + status: string; + protocol: string; + ip: string; + txid: string; +} diff --git a/web-ui/src/app/models/transaction.ts b/web-ui/src/app/models/transaction.ts index 20494573..c833242e 100644 --- a/web-ui/src/app/models/transaction.ts +++ b/web-ui/src/app/models/transaction.ts @@ -10,6 +10,7 @@ export class Transaction { output: TransactionValue[]; sent: number; received: number; + height: string; } export class TransactionValue { diff --git a/web-ui/src/app/pipes/pipes.module.ts b/web-ui/src/app/pipes/pipes.module.ts new file mode 100644 index 00000000..97733762 --- /dev/null +++ b/web-ui/src/app/pipes/pipes.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { MomentModule } from 'ngx-moment'; +import { ExplorerAmountPipe } from './explorer-amount.pipe'; +import { ExplorerCurrencyPipe } from './explorer-currency.pipe'; +import { ExplorerDatetimePipe } from './explorer-datetime.pipe'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + CommonModule, + ReactiveFormsModule, + RouterModule, + TranslateModule, + MomentModule + ], + declarations: [ + ExplorerAmountPipe, + ExplorerCurrencyPipe, + ExplorerDatetimePipe + ], + exports: [ + ExplorerAmountPipe, + ExplorerCurrencyPipe, + ExplorerDatetimePipe + ] +}) +export class PipesModule { } diff --git a/web-ui/src/app/services/addresses.service.ts b/web-ui/src/app/services/addresses.service.ts index 8d399786..bfcdbe06 100644 --- a/web-ui/src/app/services/addresses.service.ts +++ b/web-ui/src/app/services/addresses.service.ts @@ -10,6 +10,7 @@ import { LightWalletTransaction } from '../models/light-wallet-transaction'; import { Transaction } from '../models/transaction'; import { UTXO } from '../models/utxo'; import { WrappedResult } from '../models/wrapped-result'; +import { TposContract } from '../models/tposcontract'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) @@ -28,8 +29,15 @@ export class AddressesService { return this.http.get(url); } - getTransactions(address: string, offset: number = 0, limit: number = 10, orderBy: string = ''): Observable> { - const url = `${this.baseUrl}/${address}/transactions?offset=${offset}&limit=${limit}&orderBy=${orderBy}`; + getTransactions( + address: string, + limit: number = 10, + lastSeenTxid: string = '', + order: string = 'desc'): Observable> { + let url = `${this.baseUrl}/${address}/transactions?limit=${limit}&orderBy=${order}`; + if (lastSeenTxid !== '') { + url += `&lastSeenTxid=${lastSeenTxid}`; + } return this.http.get>(url); } @@ -51,4 +59,9 @@ export class AddressesService { const url = `${this.baseUrl}/${address}/utxos`; return this.http.get(url); } + + getTposContracts(address): Observable> { + const url = `${this.baseUrl}/${address}/tposcontracts`; + return this.http.get>(url); + } } diff --git a/web-ui/src/app/services/blocks.service.ts b/web-ui/src/app/services/blocks.service.ts index 48a394a9..cfbf912d 100644 --- a/web-ui/src/app/services/blocks.service.ts +++ b/web-ui/src/app/services/blocks.service.ts @@ -45,7 +45,11 @@ export class BlocksService { return this.http.get>(url); } - getLatest(): Observable { - return this.http.get(this.baseUrl); + getLatest(limit: number = 10, lastSeenHash: string = ''): Observable { + let url = `${this.baseUrlV2}?limit=${limit}`; + if (lastSeenHash !== '') { + url += `&lastSeenHash=${lastSeenHash}`; + } + return this.http.get(url); } } diff --git a/web-ui/src/app/services/ticker.service.ts b/web-ui/src/app/services/ticker.service.ts index 67e393a9..911e3d91 100644 --- a/web-ui/src/app/services/ticker.service.ts +++ b/web-ui/src/app/services/ticker.service.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs'; import { environment } from '../../environments/environment'; -import { ServerStats } from '../models/ticker'; +import { NodeStats, Prices, ServerStats } from '../models/ticker'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) @@ -13,12 +13,22 @@ const httpOptions = { @Injectable() export class TickerService { - private baseUrl = environment.api.url + '/stats'; + private baseUrl = environment.api.url; constructor(private http: HttpClient) { } get(): Observable { - const url = this.baseUrl; + const url = this.baseUrl + '/stats'; return this.http.get(url); } + + getNodeStats(): Observable { + const url = this.baseUrl + '/node-stats'; + return this.http.get(url); + } + + getPrices(): Observable { + const url = this.baseUrl + '/prices'; + return this.http.get(url); + } } diff --git a/web-ui/src/app/services/tposnodes.service.ts b/web-ui/src/app/services/tposnodes.service.ts new file mode 100644 index 00000000..ed29c1b5 --- /dev/null +++ b/web-ui/src/app/services/tposnodes.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { environment } from '../../environments/environment'; + +import { Tposnode } from '../models/tposnode'; +import { PaginatedResult } from '../models/paginated-result'; + +const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }) +}; + +@Injectable() +export class TposnodesService { + + private baseUrl = environment.api.url + '/merchantnodes'; + + constructor(private http: HttpClient) { } + + get(offset: number = 0, limit: number = 10, orderBy: string = ''): Observable> { + const url = `${this.baseUrl}?offset=${offset}&limit=${limit}&orderBy=${orderBy}`; + return this.http.get>(url); + } + + getByIP(ip: string): Observable { + const url = `${this.baseUrl}/${ip}`; + return this.http.get(url); + } +} diff --git a/web-ui/src/app/services/transactions.service.ts b/web-ui/src/app/services/transactions.service.ts index 83fc8e22..d40a0372 100644 --- a/web-ui/src/app/services/transactions.service.ts +++ b/web-ui/src/app/services/transactions.service.ts @@ -5,6 +5,7 @@ import { Observable } from 'rxjs'; import { environment } from '../../environments/environment'; import { Transaction } from '../models/transaction'; +import { PaginatedResult } from '../models/paginated-result'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) @@ -17,6 +18,15 @@ export class TransactionsService { constructor(private http: HttpClient) { } + getList(lastSeenTxId: string, limit: number = 10, orderBy: string = 'asc'): Observable> { + let url = `${this.baseUrl}?limit=${limit}&orderBy=${orderBy}`; + if (lastSeenTxId) { + url += `&lastSeenTxid=${lastSeenTxId}`; + } + + return this.http.get>(url); + } + get(txid: string): Observable { const url = `${this.baseUrl}/${txid}`; return this.http.get(url); diff --git a/web-ui/src/app/utils.spec.ts b/web-ui/src/app/utils.spec.ts new file mode 100644 index 00000000..4b01089f --- /dev/null +++ b/web-ui/src/app/utils.spec.ts @@ -0,0 +1,13 @@ +import {truncate} from './utils'; + +describe('Utils testing', () => { + it('should truncate the string', (() => { + expect(truncate('1234567890abcde', 5, 5, '-')).toEqual('12345-abcde'); + expect(truncate('1234567890abcde', 9, 5)).toEqual('123456789 ... abcde'); + })); + + it('shouldn\'t truncate string with short length', (() => { + expect(truncate('1234567890abcde', 10, 5, '-')).toEqual('1234567890abcde'); + expect(truncate('1234567890abcde', 11, 10)).toEqual('1234567890abcde'); + })); +}); diff --git a/web-ui/src/app/utils.ts b/web-ui/src/app/utils.ts index d917bbbb..ff1745a4 100644 --- a/web-ui/src/app/utils.ts +++ b/web-ui/src/app/utils.ts @@ -4,3 +4,38 @@ export function getNumberOfRowsForScreen(height: number): number { } return Math.min(10 + Math.ceil((height - 550) / 20), 100); } + +export function truncate(fullStr, frontChars, backChars, separator = null) { + if (fullStr.length <= frontChars + backChars) { + return fullStr; + } + + separator = separator || ' ... '; + return fullStr.substr(0, frontChars) + + separator + + fullStr.substr(fullStr.length - backChars); +} + +export function amAgo(timestamp: number): string { + const current = new Date().getTime() / 1000; + let diff = current - timestamp; + + const units = [ + {unit: 's', interval: 60}, + {unit: 'm', interval: 60}, + {unit: 'h', interval: 24}, + {unit: 'd', interval: -1} + ]; + + let i = 0; + while (diff > 0 && i < units.length) { + if (units[i].interval > 0 && diff / units[i].interval >= 1) { + diff /= units[i].interval; + i++; + } else { + break; + } + } + + return diff.toFixed(0) + units[i].unit + ' ago'; +} \ No newline at end of file diff --git a/web-ui/src/assets/xsn-logo.png b/web-ui/src/assets/xsn-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e1541e5bd89fb4b62e019fdb52927d8cefdebd GIT binary patch literal 947 zcmV;k15EshP)Kd@=p~;D1Pf83hX@r1@g=Mj@uRd%K@%|{9S3x3M%}V5`&`bwo;wV~^}Jj- zoO9QIuf0BwB4P+|N75bcy$h=B$JPXMEV^FWuRXcu5aJO>+10v(bLR)IytgTN$H16(NRj)<3Y z?*}AZ^}CforM)f9=}rMVC0#E~?!&wSbOS$0>dN=V0}Fv4CH+x6-vE?SRM}Gz@h0#F z@DK2~ZxB(Maqh;|k9M#L!K9pD7uX>Rrf^e`}7l9yNrWft(R1q$p1CIXKGUrIWb zlRN;d2JB1U_kQYEl^86?7l7-Mek*}J2Yi^c90xW_I*=evmqj`GI!X3p!_`U*c-u=gU6TH9p}fh!T7v_hN&2tqxxQ3q zf4mN?v7b^1R#^Q?QGAp;TUFj`zo;uw;R*lV`HwnFaxqAnub zPb+{|fiEQek|0k59|OI>=aL*fPJcgd&M~{WzS~v{+AgQyZVRM;N(|CTkRtMRV?j(0jtyfy4gIjqqU}d^8{=P?bSq-l6 z3BX^HE+mL)wVHbS^C=b)Zdp&~`ac4kE2yv8At^IG-I8p~0blt=<&5HkZtI|gAxeKd? Vf=Z+7Mg;%>002ovPDHLkV1gGAqGJF6 literal 0 HcmV?d00001 diff --git a/web-ui/src/assets/xsn-logo@2x.png b/web-ui/src/assets/xsn-logo@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5c29f0579db12f72c808f1f86d7dd0f6faf756 GIT binary patch literal 2729 zcmV;a3Rd-rP)5nc|5eD!&A})a73Mhi&jyoucfrJ?Oz&HL{zF{JS5Mu%^ zsDL{vvM3(Bcj*cTJ!m$ zy;|@6Lxc6eyxm$mxY!W&-W!_%y28(h_y8~waVjECUreA94Etw5ez`P(osNkA#`L<*FD%BccIBwmk*S0duY)*684{Td=8I&5}v`soQ!f)W*o zf%#ZOd?Le%Za}@XL&NhX@4a_G*f>V#wTVTeyN5^tbyY-MnK!{86hQqee;%9fgX!)JQ|6kD>Wp~PhW5(;fb1b_! z8iUvhh?H=jIs#~*U**)tBjS@00hqy1(;!bo#DCDSwI+IJbq&(XSeLOB_ajc~y`$N6 z3COb|^L&DNv$gha4lP-e9zj2#uFfz6Ca<23h=1pR02*+!yeAM&tFl3XS0qXi3m>~xGXGA;|5zn;N{Jwe(U@BKdM~4E; zE3Gw(HDA4V%rG}+0ws#(d@iZ~Rz>tgKm)KUU?L^tpUa=X%BN=%nD7H-f+PLlkSp?$ zk(?wSTm)yzg#t9xNFZ*^oUUg%(%j~nnXwg6^?D^1*G9xwBf?LL!@I=d$%yz{(%{rC zsG)vP(9b3qV{-zhpwJs~A#>S$Kzr7Fen)lBnol=#NJKUP+6g~VpfF#Ih%Y3k3lx#~ zOVWXH3#3-{rE$IW@JT?pE!`B zeLVqro>=@g9mH8|SS;pifB>OLZqi-NDg)=F@4fRVI#Hr;ry(NZdAU@SGoNVj@^2E* z3)S=vJsRhHFCD-!p8tJC`=ole6%dN>Fo4<}+@HJeLA}VjBo-;a02Dp%d52+-Wpq$Y z1SiL&M0=mmh%%~?W55XkdWzJEJ>(i7G?SbFG0acKDS(U-esy4!S$4k*tS z1CXukJ8fwLJ~im#q?NJ>b(;g1(Q?mNXl`R z_wR{_M~`c5)_Au!pafM*VTt_XG)yk=>d&&OKh;`8zav-g-Lu*y(YZ6hSq}U&XJbo^>=kjhd>+%3{YsR)dEBhr&FJs*DaCS3@AN~DiYe{w`b>oRzUnRyJvMt zAq0fM>K0Wazn--K)HMKCdw`y))h}@VhrVi(+%l#vWcA!?6QE&pgEuYY6hcj>_eG`Z zkJd&m@xGj3oFk}k!fCLpbo*L z@`$%pbl!Zfwf1^~uZ<0L1~n}yP>r|MDfI@es2~T_9)kBQ=^dy(3-4^Dks)i zmFTN$^KxY=r`DDa)eSQU7)>OuTjViFWXS7OK!;}2vm#@m$2f^zf?(~$0lI;|rem~9 z`vpg66nGZdY8m7JoBM792@?P7~RBEV?0lp`pKF7kw$ z0ZQtP%}C=IPqZb!<-!z!VcgoFna+Rr-cuO%#Uv;ox@)SXzWgp}GOtteB;7M2$zYxZ zLIkEfNxs+06kxPekX^>*C^=~XI6Xg{OmKXCmtwy?g$y@?bksZ!5JODLKpuZSBV!`b z;dxO6ijn1c!ZE2|8dwzOD1eMF`Hf?I-3AJhckhVQUI%N3C^syLC30=Snvj8jF!1GJ z+lidlX`1LzCAq;kxl@}cQ+F_Q8g-fh7~$Xu4m1xHtW8DZoL%da;jLwXC?06%u=JZxKWgt^d=Lx_%fP>@rmv!|^mg;RtE5vVO#g^l;{ zwhp#RT5B7)dNJU6T*8`|0gPJRrO7?=+Q1hjH^Fe#DMOt~%wlm;Pgj6y%U9S@OzUAH zJ2@0cgvOoH+7sYWQ3TGjfE+rn+0sz<-fKAlj9g@g{NWrCCot1y{#s90fa=@_5Vgeb zjFSV?DcRT7g6;M^I@A#k&kDyO^Wj>7ErOwj1(H45GJu`IQ zb$kz;&jd@3ic}q@P5tAzGXxMXRDD1pF9Bk=a`Oh?nxXstf9IS?DqxTl5goZDVsc7u z&j()s;c9Kl`$wtNrUli$hVJuxrbB%}36n(1EhX6L25>&421vE8OGMO(k z9e~w$kkD`1Rvx~8q)faGkeUNAFw+Z_T1ZN0u9fNG9~?>W>L|JZ(}M&+VxT6}HUgm^yzoDi*Q1Zqb~5P&)W$RE{_)g6`d j4Q3%yY!@Y$qTb&Dv89!$(qcvp00000NkvXXu0mjfMo<5F literal 0 HcmV?d00001 diff --git a/web-ui/src/styles.css b/web-ui/src/styles.css index 45dd83a1..dea0475d 100644 --- a/web-ui/src/styles.css +++ b/web-ui/src/styles.css @@ -2,6 +2,8 @@ @import '~bootstrap/dist/css/bootstrap.min.css'; @import "~ngx-toastr/toastr.css"; +@import url("https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,700;0,900;1,300;1,400;1,500;1,700;1,900&display=swap"); + html { margin: 0px; height: 100%; @@ -12,8 +14,11 @@ body { margin: 0px; min-height: 100%; width: 100%; - background: #141829; + background: #090D1C; background-attachment: fixed; + font-family: 'Rubik', sans-serif; + overflow-x: hidden; + padding: 15px; } .table-borderless>tbody>tr>td, @@ -26,7 +31,7 @@ body { } .table-container { - background-color: white; + background-color: #090D1C; border-radius: 4px; -webkit-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75); -moz-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75); @@ -35,3 +40,233 @@ body { padding-left: 0px; padding-right: 0px; } + +.tab-content { + background-color: #0F1223; + border-radius: 8px; + margin-bottom: 86px; + + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + flex-wrap: wrap; + + overflow-wrap: anywhere; +} + +/* Global Formats */ + +.text-highlight { + color: #01D9DE; +} + +.text-primary { + color: #FFFFFF +} + +.text-label { + color: #989DCC; +} + +/* Components */ + +a { + text-decoration: none !important; +} + +/* TextInput */ + +input[type="text"], +textarea { + background: #FFFFFF08 0% 0% no-repeat padding-box !important; + border-radius: 8px; + border: none; + height: 40px; + padding: 14px 16px 12px; +} + +.form-control::placeholder { + text-align: left; + color: #989DCC; + font-size: 12px; +} + +.form-control::-webkit-input-placeholder { + text-align: left; + color: #989DCC; + font-size: 12px; +} + +.form-control::-ms-input-placeholder { + text-align: left; + color: #989DCC; + font-size: 12px; +} + +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus, +input:-webkit-autofill:active { + -webkit-text-fill-color: #FFFFFF; + transition: background-color 5000s ease-in-out 0s; +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* Firefox */ +input[type=number] { + -moz-appearance: textfield; +} + +input:focus, +input:active { + outline: none; + outline-width: 0; + box-shadow: none; +} + +/* Page Title */ + +.page-title { + color: #FFFFFF; + text-align: left; + letter-spacing: 0px; + font-size: 20px; + font-weight: 500; + margin: 25px -5px 25px 5px; +} + +/* Tables */ + +.table>thead>tr>th { + color: #989DCC; + font-size: 14px; + font-weight: 300; + padding: 15px 20px; +} + +.table>tbody>tr>td { + padding: 15px 20px; +} + +tr.row:before, +tr.row:after { + content: none; +} + +.table-striped>tbody>tr:nth-of-type(odd) { + background-color: #FFFFFF08; +} + +.table-hover>tbody>tr:hover { + background-color: #FFFFFF08; +} + +th.right-align, +td.right-align { + text-align: right; +} + +/* Panel */ + +.panel { + background-color: transparent; +} + +.panel-body { + background-color: #0F1223; + border-radius: 8px; + border: none; + + height: 100%; + + overflow-wrap: anywhere; +} + +.ticker-container .panel-body { + display: flex; + flex-direction: row; + align-items: stretch; + justify-content: flex-start; + flex-wrap: wrap; + margin: 0 0 15px; +} + +.ticker-container .panel-body>div { + padding-left: 0; + padding-right: 0; +} + +@media (max-width:991px) { + + .no-left, + .no-right { + padding-left: unset; + padding-right: unset; + } +} + +@media (min-width:992px) { + .no-left { + padding-left: 0 !important; + } + + .no-right { + padding-right: 0 !important; + } +} + +/* ngx-pagination customization */ + +pagination-controls /deep/ .ngx-pagination { + padding: 0; + margin-left: -20px; + margin-right: -20px; +} + +pagination-controls /deep/ .ngx-pagination .current, +pagination-controls /deep/ .ngx-pagination li{ + min-width: 20px; + margin: 0 2px; +} + +pagination-controls /deep/ .ngx-pagination a { + color: #989DCC; +} + +pagination-controls /deep/ .ngx-pagination a:hover { + background: transparent; + color: #5C5F7D; +} + +pagination-controls /deep/ .ngx-pagination .current { + background: #222534; + color: #FFFFFF; + + border-radius: 10px; +} + +pagination-controls /deep/ .ngx-pagination .pagination-previous a::before, +pagination-controls /deep/ .ngx-pagination .pagination-previous.disabled::before { + content: '\276E'; +} + +pagination-controls /deep/ .ngx-pagination .pagination-next a::after, +pagination-controls /deep/ .ngx-pagination .pagination-next.disabled::after { + content: '\276F'; +} + +pagination-controls /deep/ .ngx-pagination .pagination-previous a::before, +pagination-controls /deep/ .ngx-pagination .pagination-next a::after { + color: #01D9DE; +} + +pagination-controls /deep/ .ngx-pagination .pagination-previous.disabled::before, +pagination-controls /deep/ .ngx-pagination .pagination-next.disabled::after { + color: #989DCC; +} \ No newline at end of file
{{'label.noInput' | translate}} + {{'label.noInput' | translate}} +
{{'label.from' | translate}}{{ getTotal(collapsedInput) | explorerCurrency }} + {{'label.from' | translate}} + + {{getTotal(collapsedInput) | explorerCurrency }} +
- {{item.address}} ({{count(item.address, transaction.input)}}) + {{item.address}} ({{count(item.address, transaction.input)}}) - {{item.address}} + {{item.address}} {{item.value | explorerCurrency}}
{{'label.output' | translate}} + {{ getTotal(collapsedOutput) | explorerCurrency }} - +
- {{item.address}} ({{count(item.address, transaction.output)}}) + {{item.address}} ({{count(item.address, transaction.output)}}) - {{item.address}} + {{item.address}} {{item.value | explorerCurrency}}