Browse Source

Merge pull request #50 from getumbrel/blockchain

New block animation + polling bug fix
readme
Mayank Chhabra 4 years ago
committed by GitHub
parent
commit
235e817a3e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 228
      src/components/Blockchain.vue
  2. 64
      src/store/modules/bitcoin.js
  3. 43
      src/views/Bitcoin.vue
  4. 2
      src/views/Dashboard.vue

228
src/components/Blockchain.vue

@ -3,7 +3,7 @@
<div class="blockchain-container">
<div v-if="blocks.length">
<!-- transitions for blocks -->
<transition-group name="blockchain" mode="out-in" tag="ul">
<transition-group name="blockchain" mode="out-in" tag="ul" :duration="5000">
<li
href="#"
class="flex-column align-items-start px-3 px-lg-4 blockchain-block"
@ -13,47 +13,47 @@
<div class="d-flex w-100 justify-content-between">
<div class="d-flex">
<div class="blockchain-block-icon">
<svg
width="28"
height="30"
viewBox="0 0 28 30"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M23.3472 8.84298C24.6806 8.07317 24.6806 6.14865 23.3472 5.37886L15.0304 0.577324C14.4116 0.220073 13.6493 0.220077 13.0305 0.577333L4.71387 5.37887C3.38053 6.14866 3.38052 8.07316 4.71384 8.84297L13.0304 13.6447C13.6493 14.0019 14.4117 14.0019 15.0305 13.6447L23.3472 8.84298Z"
fill="#5351FB"
/>
<path
d="M14.9243 26.5359C14.9243 28.0755 16.591 29.0378 17.9243 28.268L26.2443 23.4644C26.8631 23.1072 27.2443 22.4469 27.2443 21.7324V12.1218C27.2443 10.5822 25.5777 9.61992 24.2444 10.3897L15.9243 15.1931C15.3055 15.5504 14.9243 16.2106 14.9243 16.9252V26.5359Z"
fill="#5351FB"
/>
<path
d="M3.8164 10.3897C2.48306 9.61995 0.816406 10.5822 0.816406 12.1218V21.7324C0.816406 22.4469 1.1976 23.1072 1.81639 23.4644L10.1362 28.268C11.4695 29.0378 13.1362 28.0755 13.1362 26.5359V16.9252C13.1362 16.2106 12.755 15.5504 12.1362 15.1931L3.8164 10.3897Z"
fill="#5351FB"
/>
</svg>
<div class="blockchain-block-icon-cube">
<span class="edge top">
<span class="inside"></span>
</span>
<span class="edge right">
<span class="inside"></span>
</span>
<span class="edge bottom">
<span class="inside"></span>
</span>
<span class="edge left">
<span class="inside"></span>
</span>
<span class="edge front">
<span class="inside"></span>
</span>
<span class="edge back">
<span class="inside"></span>
</span>
</div>
<div class="blockchain-block-icon-chainlink"></div>
<div class="blockchain-block-icon-bg"></div>
</div>
<div class="align-self-center">
<h6 class="mb-1 font-weight-normal">
Block {{ block.height.toLocaleString() }}
</h6>
<small class="text-muted" v-if="block.txs">
{{ block.txs.toLocaleString() }} transactions
<!-- <span>&bull; {{ Math.round(block.size / 1000) }} KB</span> -->
</small>
<h6 class="mb-1 font-weight-normal">Block {{ block.height.toLocaleString() }}</h6>
<small
class="text-muted"
v-if="block.numTransactions"
>{{ block.numTransactions.toLocaleString() }} transactions</small>
<!-- <small class="text-muted" v-if="block.size">
<span>&bull; {{ Math.round(block.size / 1000) }} KB</span>
</small>-->
</div>
</div>
<status variant="success" v-if="false">Valid</status>
<small
class="text-muted align-self-center text-right"
v-if="block.timestamp"
:title="blockReadableTime(block.timestamp)"
>
{{ blockTime(block.timestamp) }}
</small>
class="text-muted align-self-center text-right blockchain-block-timestamp"
v-if="block.time"
:title="blockReadableTime(block.time)"
>{{ blockTime(block.time) }}</small>
</div>
</li>
</transition-group>
@ -68,9 +68,7 @@
>
<div class="d-flex w-100 justify-content-between">
<div class="d-flex">
<div
class="blockchain-block-icon blockchain-block-icon-loading"
>
<div class="blockchain-block-icon blockchain-block-icon-loading">
<svg
width="28"
height="30"
@ -96,10 +94,7 @@
<div class="blockchain-block-icon-bg"></div>
</div>
<div class="align-self-center">
<span
class="d-block loading-placeholder mb-1"
style="width: 140px;"
></span>
<span class="d-block loading-placeholder mb-1" style="width: 140px;"></span>
<span
class="d-block loading-placeholder loading-placeholder-sm"
style="width: 80px"
@ -122,6 +117,8 @@
import moment from "moment";
import { mapState } from "vuex";
import Status from "@/components/Utility/Status";
export default {
data() {
return {
@ -147,8 +144,8 @@ export default {
},
poller(syncPercent) {
window.clearInterval(this.polling);
//if syncing or first load, fetch blocks every second
if (Number(syncPercent) !== 100 || this.blocks.length === 0) {
//if syncing, fetch blocks every second
if (Number(syncPercent) !== 100) {
this.polling = window.setInterval(this.fetchBlocks, 1000);
} else {
//else, slow down and fetch blocks every minute
@ -191,7 +188,9 @@ export default {
}
},
components: {}
components: {
Status
}
};
</script>
@ -251,14 +250,6 @@ export default {
height: 4rem;
width: 4rem;
svg {
z-index: 1;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.blockchain-block-icon-chainlink {
position: absolute;
height: 4rem;
@ -293,34 +284,125 @@ export default {
transition: all 0.6s ease-in-out;
}
.blockchain-block-icon {
svg {
transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1);
transition-delay: 0.1s;
}
.blockchain-block-icon-bg {
transition: all 0.6s cubic-bezier(0.77, 0, 0.175, 1);
}
}
.blockchain-block-icon-cube {
$cube-size: 22px;
transform-style: preserve-3d;
position: absolute;
width: $cube-size;
height: $cube-size;
top: 50%;
left: 50%;
z-index: 1;
margin-left: -($cube-size * 0.5);
margin-top: -($cube-size * 0.5);
transform: rotateX(-40deg) rotateY(45deg) rotateZ(0deg);
.edge {
width: $cube-size;
height: $cube-size;
line-height: $cube-size;
text-align: center;
box-shadow: inset 0px 0px 0px 1px #eeeeff;
background: #eeeeff;
display: block;
position: absolute;
.inside {
position: absolute;
top: $cube-size * 0.1;
left: $cube-size * 0.1;
width: $cube-size * 0.8;
height: $cube-size * 0.8;
background: #5351fb;
border-radius: $cube-size * 0.2;
}
&.top {
transform: rotate3d(1, 0, 0, 90deg);
margin-top: -($cube-size * 0.5);
}
&.right {
transform: rotate3d(0, 1, 0, 90deg);
margin-left: $cube-size * 0.5;
}
&.bottom {
transform: rotate3d(1, 0, 0, -90deg);
margin-top: $cube-size * 0.5;
}
&.left {
transform: rotate3d(0, 1, 0, -90deg);
margin-left: -($cube-size * 0.5);
}
&.front {
transform: translateZ($cube-size * 0.5);
}
&.back {
transform: translateZ(-($cube-size * 0.5)) rotate3d(1, 0, 0, 180deg);
}
}
}
.blockchain-block-timestamp {
position: relative;
&:before,
&:after {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
background: #fff;
opacity: 0;
}
&:before {
content: "● Validated";
color: #00cd98;
}
&:after {
content: "Validating...";
color: #b1b5b9;
}
}
//animations
.blockchain-enter {
opacity: 0;
transform: translateY(2rem);
.blockchain-block-icon {
svg {
transform: translate(-50%, -50%) scale(0);
}
.blockchain-block-icon-bg {
transform: scale(0);
background: #5351fb;
}
}
}
.blockchain-enter-active {
.blockchain-block-icon-cube {
animation: spin-cube 5s forwards ease;
}
.blockchain-block-timestamp {
&:after {
opacity: 1;
animation: slide-up 0.4s forwards ease;
animation-delay: 3s;
}
&:before {
opacity: 1;
animation: slide-up 0.4s forwards ease;
animation-delay: 4.6s;
}
}
}
.blockchain-enter-to {
opacity: 1;
.blockchain-block-icon {
svg {
transform: translate(-50%, -50%) scale(1);
}
.blockchain-block-icon-bg {
transform: scale(1);
background: #eeeeff;
@ -338,4 +420,24 @@ export default {
.blockchain-leave-active {
// position: absolute;
}
@keyframes spin-cube {
0% {
transform: rotateX(-40deg) rotateY(45deg) rotateZ(0deg);
}
100% {
transform: rotateX(-40deg) rotateY(945deg) rotateZ(540deg);
}
}
@keyframes slide-up {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-10px) rotateX(30deg);
opacity: 0;
}
}
</style>

64
src/store/modules/bitcoin.js

@ -236,70 +236,22 @@ const actions = {
//cache block height array of latest 3 blocks for loading view
const currentBlock = state.currentBlock;
//dont fetch blocks if no new block
//dont fetch blocks if no new block has been found
if (state.blocks.length && currentBlock === state.blocks[0]["height"]) {
return;
}
if (currentBlock < 4) {
return;
}
const blocks = [
{
height: currentBlock, //block height
txs: null,
timestamp: null,
size: null
},
{
height: currentBlock - 1, //block height
txs: null,
timestamp: null,
size: null
},
{
height: currentBlock - 2, //block number
txs: null,
timestamp: null,
size: null
}
];
// commit("setBlocks", blocks);
//fetch info per block;
const blocksWithInfo = [];
for (let block of blocks) {
//get hash
const blockHash = await API.get(
`${process.env.VUE_APP_API_URL}/v1/bitcoind/info/block?height=${block.height}`
);
if (!blockHash || !blockHash.hash) {
return;
}
//gete block info
const blockInfo = await API.get(
`${process.env.VUE_APP_API_URL}/v1/bitcoind/info/block?hash=${blockHash.hash}`
);
if (!blockInfo || !blockInfo.block) {
return;
}
//TODO: Fetch only new blocks
const latestThreeBlocks = await API.get(
`${process.env.VUE_APP_API_URL}/v1/bitcoind/info/blocks?from=${currentBlock - 2}&to=${currentBlock}`
);
blocksWithInfo.push({
height: blockInfo.height,
txs: blockInfo.transactions.length,
timestamp: blockInfo.blocktime,
size: blockInfo.size
});
if (!latestThreeBlocks.blocks) {
return;
}
// update blocks
commit("setBlocks", blocksWithInfo);
commit("setBlocks", latestThreeBlocks.blocks);
}
},

43
src/views/Bitcoin.vue

@ -20,18 +20,15 @@
</svg>
<small class="ml-1 text-success">Running</small>
<h3 class="d-block font-weight-bold mb-1">Bitcoin Core</h3>
<span class="d-block text-muted">{{
<span class="d-block text-muted">
{{
version ? `v${version}` : "..."
}}</span>
}}
</span>
</div>
</div>
<div>
<b-dropdown
variant="link"
toggle-class="text-decoration-none p-0"
no-caret
right
>
<b-dropdown variant="link" toggle-class="text-decoration-none p-0" no-caret right>
<template v-slot:button-content>
<svg
width="18"
@ -60,16 +57,10 @@
/>
</svg>
</template>
<b-dropdown-item href="#" disabled
>Check for update</b-dropdown-item
>
<b-dropdown-item href="#" disabled
>View information</b-dropdown-item
>
<b-dropdown-item href="#" disabled>Check for update</b-dropdown-item>
<b-dropdown-item href="#" disabled>View information</b-dropdown-item>
<b-dropdown-divider />
<b-dropdown-item variant="danger" href="#" disabled
>Stop Bitcoin Core</b-dropdown-item
>
<b-dropdown-item variant="danger" href="#" disabled>Stop Bitcoin Core</b-dropdown-item>
</b-dropdown>
</div>
</div>
@ -85,9 +76,7 @@
:loading="syncPercent !== 100 || blocks.length === 0"
>
<template v-slot:menu>
<b-dropdown-item variant="danger" href="#" disabled
>Resync Blockchain</b-dropdown-item
>
<b-dropdown-item variant="danger" href="#" disabled>Resync Blockchain</b-dropdown-item>
</template>
<div class>
<div class="px-3 px-lg-4 mb-3">
@ -95,7 +84,7 @@
<span class="align-self-end">Synchronized</span>
<h3 class="font-weight-normal mb-0">
<span v-if="syncPercent !== -1">
{{ syncPercent }}
{{ syncPercent >= 99.99 ? 100 : syncPercent }}
<small class>%</small>
</span>
@ -114,10 +103,7 @@
animated
striped
></b-progress>
<small
class="text-muted d-block text-right"
v-if="currentBlock < blockHeight - 1"
>
<small class="text-muted d-block text-right" v-if="currentBlock < blockHeight - 1">
{{ currentBlock.toLocaleString() }} of
{{ blockHeight.toLocaleString() }} blocks
</small>
@ -153,12 +139,7 @@
></stat>
</b-col>-->
<b-col col cols="6" md="3" xl="6">
<stat
title="Connections"
:value="stats.peers"
suffix="Peers"
showNumericChange
></stat>
<stat title="Connections" :value="stats.peers" suffix="Peers" showNumericChange></stat>
</b-col>
<b-col col cols="6" md="3" xl="6">
<stat

2
src/views/Dashboard.vue

@ -25,7 +25,7 @@
>
<template v-slot:title>
<CountUp
:value="{endVal: syncPercent, decimalPlaces: syncPercent === 100 ? 0 : 2}"
:value="{endVal: syncPercent >= 99.99 ? 100 : syncPercent, decimalPlaces: syncPercent >= 99.99 ? 0 : 2}"
suffix="%"
v-if="syncPercent !== -1"
/>

Loading…
Cancel
Save