Added service node details.

This commit is contained in:
Mikunj 2019-07-16 09:32:05 +10:00
parent d5a6ebd11e
commit f7fbb6d7c8
4 changed files with 251 additions and 10 deletions

View File

@ -0,0 +1,211 @@
<template>
<q-modal class="serviceNodeDetails" v-model="isVisible" maximized>
<q-modal-layout>
<q-toolbar slot="header" color="dark" inverted>
<q-btn
flat
round
dense
@click="isVisible = false"
icon="reply"
/>
<q-toolbar-title>
{{ $t("titles.serviceNodeDetails") }}
</q-toolbar-title>
<q-btn
v-if="node.requested_unlock_height === 0"
class="q-mr-sm"
color="primary"
@click="unlock(node.service_node_pubkey, $event)"
:disabled="!is_ready || unlock_status.sending"
:label="$t('buttons.unlock')"
/>
<q-btn v-if="can_open" color="primary" @click="openExplorer" :label="$t('buttons.viewOnExplorer')" />
</q-toolbar>
<div class="layout-padding">
<h6 class="q-mt-xs q-mb-none text-weight-light">{{ $t("strings.serviceNodeDetails.serviceNodeKey") }}</h6>
<p class="break-all">{{ node.service_node_pubkey }}</p>
<div class="info row justify-between">
<div class="infoBox">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.stakingRequirement") }}</span></div>
<div class="value"><span><FormatLoki :amount="node.staking_requirement" raw-value /></span></div>
</div>
</div>
<div class="infoBox">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.totalContributed") }}</span></div>
<div class="value"><span><FormatLoki :amount="node.total_contributed" raw-value /></span></div>
</div>
</div>
<div class="infoBox">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.registrationHeight") }}</span></div>
<div class="value"><span>{{ node.registration_height }}</span></div>
</div>
</div>
<div class="infoBox">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.operatorFee") }}</span></div>
<div class="value"><span>{{ operatorFee }}</span></div>
</div>
</div>
<div class="infoBox" v-if="node.requested_unlock_height > 0">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.unlockHeight") }}</span></div>
<div class="value"><span>{{ node.requested_unlock_height }}</span></div>
</div>
</div>
<div class="infoBox">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.lastUptimeProof") }}</span></div>
<div class="value"><span>{{ formatDate(node.last_uptime_proof) }}</span></div>
</div>
</div>
<div class="infoBox">
<div class="infoBoxContent">
<div class="text"><span>{{ $t("strings.serviceNodeDetails.lastRewardBlockHeight") }}</span></div>
<div class="value"><span>{{ node.last_reward_block_height }}</span></div>
</div>
</div>
</div>
<q-list no-border :dark="theme=='dark'" class="loki-list">
<q-list-header class="q-px-none">{{ $t("strings.serviceNodeDetails.contributors") }}:</q-list-header>
<q-item class="loki-list-item" v-for="contributor in contributors" :key="contributor.address">
<q-item-main>
<q-item-tile v-if="isMe(contributor)" class="name non-selectable">{{ $t('strings.me') }}</q-item-tile>
<q-item-tile v-else class="name non-selectable">{{ contributor.name }}</q-item-tile>
<q-item-tile class="address ellipsis non-selectable">{{ contributor.address }}</q-item-tile>
<q-item-tile class="non-selectable" sublabel>
<span v-if="isOperator(contributor)">{{ $t('strings.operator') }} </span>
{{ $t('strings.contribution') }}: <FormatLoki :amount="contributor.amount" raw-value />
</q-item-tile>
</q-item-main>
<q-context-menu>
<q-list link separator style="min-width: 150px; max-height: 300px;">
<q-item v-close-overlay
@click.native="copyAddress(contributor.address, $event)">
<q-item-main :label="$t('menuItems.copyAddress')" />
</q-item>
</q-list>
</q-context-menu>
</q-item>
</q-list>
</div>
<q-inner-loading :visible="unlock_status.sending" :dark="theme=='dark'">
<q-spinner color="primary" :size="30" />
</q-inner-loading>
</q-modal-layout>
</q-modal>
</template>
<script>
const { clipboard } = require("electron")
import { mapState } from "vuex"
import { date } from "quasar"
const { formatDate } = date
import FormatLoki from "components/format_loki"
export default {
name: "ServiceNodeDetails",
props: {
unlock: {
type: Function,
required: true
},
},
computed: mapState({
theme: state => state.gateway.app.config.appearance.theme,
unlock_status: state => state.gateway.service_node_status.unlock,
is_ready (state) {
return this.$store.getters["gateway/isReady"]
},
our_address: state => {
const primary = state.gateway.wallet.address_list.primary[0]
return (primary && primary.address) || null
},
can_open (state) {
const { net_type } = state.gateway.app.config.app
return net_type !== "stagenet"
},
contributors (state) {
if (!this.node.contributors) return []
const { address_book } = state.gateway.wallet.address_list
let contributors = []
for (const contributor of this.node.contributors) {
let values = { ...contributor };
const address = address_book.find(a => a.address === contributor.address);
if (address) {
const { name, description} = address
const separator = description === "" ? "" : " - "
values.name = `${name}${separator}${description}`
}
contributors.push(values)
}
return contributors
},
operatorFee () {
const operatorPortion = this.node.portions_for_operator
const percentageFee = operatorPortion / 18446744073709551612 * 100
return `${percentageFee}%`
},
}),
data () {
return {
isVisible: false,
node: {},
}
},
methods: {
openExplorer () {
this.$gateway.send("core", "open_explorer", {type: "service_node", id: this.node.service_node_pubkey})
},
formatDate (timestamp) {
return date.formatDate(timestamp, "YYYY-MM-DD hh:mm a")
},
copyAddress (address, event) {
event.stopPropagation()
for(let i = 0; i < event.path.length; i++) {
if(event.path[i].tagName == "BUTTON") {
event.path[i].blur()
break
}
}
clipboard.writeText(address)
this.$q.notify({
type: "positive",
timeout: 1000,
message: this.$t("notification.positive.addressCopied")
})
},
isMe (contributor) {
return contributor.address === this.our_address
},
isOperator (contributor) {
return this.node.operator_address === contributor.address
},
},
components: {
FormatLoki
}
}
</script>
<style lang="scss">
.serviceNodeDetails {
.name {
font-size: 0.92rem;
}
.infoBox {
margin-left: 30px;
}
.info {
margin-right: 30px;
}
}
</style>

View File

@ -3,7 +3,7 @@
<div class="q-pa-md">
<div class="q-pb-sm header">{{ $t('titles.currentlyStakedNodes') }}</div>
<q-list class="service-node-list" no-border>
<q-item v-for="node in service_nodes" :key="node.service_node_pubkey">
<q-item v-for="node in service_nodes" :key="node.service_node_pubkey" @click.native="details(node)">
<q-item-main>
<q-item-tile class="ellipsis" label>{{ node.service_node_pubkey }}</q-item-tile>
<q-item-tile sublabel class="non-selectable">{{ getRole(node) }} {{ getFee(node) }} {{ $t('strings.contribution') }}: <FormatLoki :amount="node.ourContributionAmount" /></q-item-tile>
@ -15,13 +15,11 @@
size="md"
:label="$t('buttons.unlock')"
:disabled="!is_ready || unlock_status.sending"
@click="unlockWarning(node.service_node_pubkey)"
@click.native="unlockWarning(node.service_node_pubkey, $event)"
/>
<q-item-label
v-if="node.requested_unlock_height > 0"
>
<q-item-tile label v-if="node.requested_unlock_height > 0">
{{ $t('strings.unlockingAtHeight', { number: node.requested_unlock_height }) }}
</q-item-label>
</q-item-tile>
</q-item-side>
<q-context-menu>
<q-list link separator style="min-width: 150px; max-height: 300px;">
@ -37,6 +35,8 @@
</q-list>
</div>
<ServiceNodeDetails ref="serviceNodeDetails" :unlock="unlockWarning" />
<q-inner-loading :visible="unlock_status.sending" :dark="theme=='dark'">
<q-spinner color="primary" :size="30" />
</q-inner-loading>
@ -45,12 +45,14 @@
<script>
const objectAssignDeep = require("object-assign-deep");
import { clipboard } from "electron"
import { mapState } from "vuex"
import { required } from "vuelidate/lib/validators"
import { service_node_key } from "src/validators/common"
import LokiField from "components/loki_field"
import WalletPassword from "src/mixins/wallet_password"
import FormatLoki from "components/format_loki"
import ServiceNodeDetails from "components/service_node_details"
export default {
name: "ServiceNodeUnlock",
@ -131,7 +133,12 @@ export default {
},
},
methods: {
unlockWarning (key) {
details (node) {
this.$refs.serviceNodeDetails.isVisible = true
this.$refs.serviceNodeDetails.node = node
},
unlockWarning (key, event) {
event.stopPropagation()
this.$q.dialog({
title: this.$t("dialog.unlockServiceNodeWarning.title"),
message: this.$t("dialog.unlockServiceNodeWarning.message"),
@ -202,13 +209,14 @@ export default {
getFee (node) {
const operatorPortion = node.portions_for_operator
const percentageFee = operatorPortion / 18446744073709551612 * 100
return `${percentageFee}% ${this.$t('strings.fee')}`
return `${percentageFee}% ${this.$t('strings.transactions.fee')}`
},
},
mixins: [WalletPassword],
components: {
LokiField,
FormatLoki
FormatLoki,
ServiceNodeDetails
}
}
</script>

View File

@ -511,6 +511,7 @@ footer,
.service-node-list {
.q-item {
cursor: pointer;
background: #313131;
-webkit-transition: background-color 0.2s ease-in;
transition: background-color 0.2s ease-in;
@ -623,3 +624,12 @@ footer,
font-size: 14px;
}
}
.serviceNodeDetails {
.address {
color: white;
}
.name {
color: #979797;
}
}

View File

@ -388,11 +388,11 @@ export default {
},
destinationUnknown: "Destination Unknown",
editAddressBookEntry: "Edit address book entry",
fee: "Fee",
loadingSettings: "Loading settings",
lokiBalance: "Balance",
lokiUnlockedBalance: "Unlocked balance",
lokiUnlockedShort: "Unlocked",
me: "Me",
noTransactionsFound: "No transactions found",
notes: "Notes",
numberOfUnspentOutputs: "Number of unspent outputs",
@ -416,6 +416,17 @@ export default {
seedWords: "Seed words",
selectLanguage: "Select language",
serviceNodeRegistrationDescription: "Enter the {registerCommand} command produced by the daemon that is registering to become a Service Node using the \"{prepareCommand}\" command",
serviceNodeDetails: {
contributors: "Contributors",
lastRewardBlockHeight: "Last reward block height",
lastUptimeProof: "Last uptime proof",
operatorFee: "Operator Fee",
registrationHeight: "Registration height",
unlockHeight: "Unlock height",
serviceNodeKey: "Service Node Key",
stakingRequirement: "Staking requirement",
totalContributed: "Total contributed"
},
spendKey: "Spend key",
startingDaemon: "Starting daemon",
startingWallet: "Starting wallet",
@ -470,6 +481,7 @@ export default {
registration: "REGISTRATION",
staking: "STAKING"
},
serviceNodeDetails: "Service node details",
settings: {
title: "Settings",
tabs: {