refactor: extract send-to component
This commit is contained in:
parent
3e7ce185f6
commit
3765900be0
8 changed files with 203 additions and 155 deletions
|
|
@ -158,7 +158,6 @@ async function addressList(path) {
|
|||
|
||||
created: async function () {
|
||||
await this.refreshAddresses()
|
||||
// this.$emit('update:addresses', this.addresses)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
:rules="[val => !!val || 'Field is required']"
|
||||
type="number"
|
||||
label="sats/vbyte"
|
||||
@input="feeRateChanged"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
|
|
@ -23,7 +22,6 @@
|
|||
:label-value="getFeeRateLabel(feeRate)"
|
||||
:min="1"
|
||||
:max="recommededFees.fastestFee"
|
||||
@input="feeRateChanged"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
async function feeRate(path) {
|
||||
const template = await loadTemplateAsync(path)
|
||||
Vue.component('fees', {
|
||||
name: 'fees',
|
||||
Vue.component('fee-rate', {
|
||||
name: 'fee-rate',
|
||||
template,
|
||||
|
||||
props: ['totalfee', 'sats_denominated'],
|
||||
props: ['rate', 'totalfee', 'sats_denominated'],
|
||||
watch: {
|
||||
immediate: true,
|
||||
totalfee: function (newVal, oldVal) {
|
||||
console.log('### ', newVal, oldVal)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
feeRate: {
|
||||
get: function () {
|
||||
return this['rate']
|
||||
},
|
||||
set: function (value) {
|
||||
console.log('### computed update rate')
|
||||
this.$emit('update:rate', +value)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
feeRate: 1,
|
||||
recommededFees: {
|
||||
fastestFee: 1,
|
||||
halfHourFee: 1,
|
||||
|
|
@ -29,10 +39,7 @@ async function feeRate(path) {
|
|||
satBtc(val, showUnit = true) {
|
||||
return satOrBtc(val, showUnit, this['sats_denominated'])
|
||||
},
|
||||
feeRateChanged: function (newFeeRate) {
|
||||
console.log('### value', newFeeRate)
|
||||
this.$emit('update:fee-rate', +newFeeRate)
|
||||
},
|
||||
|
||||
refreshRecommendedFees: async function () {
|
||||
const {
|
||||
bitcoin: {fees: feesAPI}
|
||||
|
|
@ -56,7 +63,6 @@ async function feeRate(path) {
|
|||
console.log('### created fees ')
|
||||
await this.refreshRecommendedFees()
|
||||
this.feeRate = this.recommededFees.halfHourFee
|
||||
this.feeRateChanged(this.recommededFees.halfHourFee)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
<div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col-12">
|
||||
<q-table
|
||||
flat
|
||||
dense
|
||||
hide-header
|
||||
:data="data"
|
||||
:columns="paymentTable.columns"
|
||||
:pagination.sync="paymentTable.pagination"
|
||||
>
|
||||
<template v-slot:body="props">
|
||||
<q-tr :props="props">
|
||||
<div class="row no-wrap">
|
||||
<div class="col-1">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="l"
|
||||
@click="deletePaymentAddress(props.row)"
|
||||
icon="cancel"
|
||||
color="pink"
|
||||
class="q-mt-sm"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="col-7 q-pr-lg">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="props.row.address"
|
||||
type="text"
|
||||
label="Address"
|
||||
:rules="[val => !!val || 'Field is required']"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-3 q-pr-lg">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.number="props.row.amount"
|
||||
type="number"
|
||||
step="1"
|
||||
label="Amount (sats)"
|
||||
:rules="[val => !!val || 'Field is required', val => +val > DUST_LIMIT || 'Amount to small (below dust limit)'] "
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<q-btn outline color="grey" @click="sendMaxToAddress(props.row)"
|
||||
>Max</q-btn
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</q-tr>
|
||||
</template>
|
||||
<template v-slot:bottom-row>
|
||||
<q-tr>
|
||||
<q-td colspan="100%">
|
||||
<div class="row items-center no-wrap">
|
||||
<div class="col-3 q-pr-lg">
|
||||
<q-btn outline color="grey" @click="addPaymentAddress"
|
||||
>Add Send Address</q-btn
|
||||
>
|
||||
</div>
|
||||
<div class="col-4 q-pr-lg"></div>
|
||||
<div class="col-5">
|
||||
<div class="float-right">
|
||||
<span class="text-weight-bold">Payed Amount: </span>
|
||||
<q-badge class="text-subtitle2 q-ml-lg" color="blue">
|
||||
{{satBtc(getTotalPaymentAmount())}}
|
||||
</q-badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
async function sendTo(path) {
|
||||
const template = await loadTemplateAsync(path)
|
||||
Vue.component('send-to', {
|
||||
name: 'send-to',
|
||||
template,
|
||||
|
||||
props: ['data', 'tx-size', 'total-amount', 'fee-rate', 'sats_denominated'],
|
||||
|
||||
computed: {
|
||||
dataLocal: {
|
||||
get: function () {
|
||||
return this.data
|
||||
},
|
||||
set: function (value) {
|
||||
console.log('### computed update data')
|
||||
this.$emit('update:data', value)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
amount: 0,
|
||||
paymentTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'data',
|
||||
align: 'left'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 10
|
||||
},
|
||||
filter: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
satBtc(val, showUnit = true) {
|
||||
return satOrBtc(val, showUnit, this['sats_denominated'])
|
||||
},
|
||||
addPaymentAddress: function () {
|
||||
this.dataLocal.push({address: '', amount: undefined})
|
||||
},
|
||||
deletePaymentAddress: function (v) {
|
||||
const index = this.dataLocal.indexOf(v)
|
||||
if (index !== -1) {
|
||||
this.dataLocal.splice(index, 1)
|
||||
}
|
||||
},
|
||||
getTotalPaymentAmount: function () {
|
||||
return this.dataLocal.reduce((t, a) => t + (a.amount || 0), 0)
|
||||
},
|
||||
sendMaxToAddress: function (paymentAddress = {}) {
|
||||
this.amount = 0
|
||||
// const tx = this.createTx(true)
|
||||
// this.payment.txSize = Math.round(txSize(tx))
|
||||
const fee = this['fee-rate'] * this['tx-size']
|
||||
const inputAmount = this['total-amount']
|
||||
const payedAmount = this.getTotalPaymentAmount()
|
||||
paymentAddress.amount = Math.max(0, inputAmount - payedAmount - fee)
|
||||
}
|
||||
},
|
||||
|
||||
created: async function () {
|
||||
this.dataLocal = [{address: '', amount: undefined}]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ const watchOnly = async () => {
|
|||
await history('static/components/history/history.html')
|
||||
await utxoList('static/components/utxo-list/utxo-list.html')
|
||||
await feeRate('static/components/fee-rate/fee-rate.html')
|
||||
await sendTo('static/components/send-to/send-to.html')
|
||||
await payment('static/components/payment/payment.html')
|
||||
|
||||
Vue.filter('reverse', function (value) {
|
||||
|
|
@ -85,7 +86,22 @@ const watchOnly = async () => {
|
|||
addressNote: '',
|
||||
showPayment: false,
|
||||
showCustomFee: false,
|
||||
feeValue: 0
|
||||
feeRate: 1,
|
||||
sendToList: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
txSize: function() {
|
||||
const tx = this.createTx()
|
||||
return Math.round(txSize(tx))
|
||||
},
|
||||
txSizeNoChange: function() {
|
||||
const tx = this.createTx(true)
|
||||
return Math.round(txSize(tx))
|
||||
},
|
||||
feeValue: function(){
|
||||
return this.feeRate * this.txSize
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -191,7 +207,7 @@ const watchOnly = async () => {
|
|||
//################### PAYMENT ###################
|
||||
createTx: function (excludeChange = false) {
|
||||
const tx = {
|
||||
fee_rate: this.payment.feeRate,
|
||||
fee_rate: this.feeRate,
|
||||
tx_size: this.payment.txSize,
|
||||
masterpubs: this.walletAccounts.map(w => ({
|
||||
public_key: w.masterpub,
|
||||
|
|
@ -205,7 +221,7 @@ const watchOnly = async () => {
|
|||
a.tx_id < b.tx_id ? -1 : a.tx_id > b.tx_id ? 1 : a.vout - b.vout
|
||||
)
|
||||
|
||||
tx.outputs = this.payment.data.map(out => ({
|
||||
tx.outputs = this.sendToList.map(out => ({
|
||||
address: out.address,
|
||||
amount: out.amount
|
||||
}))
|
||||
|
|
@ -221,37 +237,27 @@ const watchOnly = async () => {
|
|||
}
|
||||
// Only sort by amount on UI level (no lib for address decode)
|
||||
// Should sort by scriptPubKey (as byte array) on the backend
|
||||
// todo: just shuffle
|
||||
tx.outputs.sort((a, b) => a.amount - b.amount)
|
||||
|
||||
return tx
|
||||
},
|
||||
createChangeOutput: function () {
|
||||
const change = this.payment.changeAddress
|
||||
const fee = this.payment.feeRate * this.payment.txSize
|
||||
const inputAmount = this.getTotalSelectedUtxoAmount()
|
||||
const payedAmount = this.getTotalPaymentAmount()
|
||||
// const inputAmount = this.getTotalSelectedUtxoAmount() // todo: set amount separately
|
||||
// const payedAmount = this.getTotalPaymentAmount()
|
||||
const walletAcount =
|
||||
this.walletAccounts.find(w => w.id === change.wallet) || {}
|
||||
|
||||
return {
|
||||
address: change.address,
|
||||
amount: inputAmount - payedAmount - fee,
|
||||
// amount: inputAmount - payedAmount - this.feeValue,
|
||||
addressIndex: change.addressIndex,
|
||||
addressIndex: change.addressIndex,
|
||||
masterpub_fingerprint: walletAcount.fingerprint
|
||||
}
|
||||
},
|
||||
computeFee: function (feeRate) {
|
||||
const tx = this.createTx()
|
||||
this.payment.txSize = Math.round(txSize(tx))
|
||||
return feeRate * this.payment.txSize
|
||||
},
|
||||
deletePaymentAddress: function (v) {
|
||||
const index = this.payment.data.indexOf(v)
|
||||
if (index !== -1) {
|
||||
this.payment.data.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
initPaymentData: async function () {
|
||||
if (!this.payment.show) return
|
||||
await this.$refs.addressList.refreshAddresses()
|
||||
|
|
@ -263,9 +269,6 @@ const watchOnly = async () => {
|
|||
this.payment.feeRate = this.payment.recommededFees.halfHourFee
|
||||
},
|
||||
|
||||
addPaymentAddress: function () {
|
||||
this.payment.data.push({address: '', amount: undefined})
|
||||
},
|
||||
getTotalPaymentAmount: function () {
|
||||
return this.payment.data.reduce((t, a) => t + (a.amount || 0), 0)
|
||||
},
|
||||
|
|
@ -281,22 +284,14 @@ const watchOnly = async () => {
|
|||
// this.tab = 'utxos'
|
||||
await this.initPaymentData()
|
||||
},
|
||||
sendMaxToAddress: function (paymentAddress = {}) {
|
||||
paymentAddress.amount = 0
|
||||
const tx = this.createTx(true)
|
||||
this.payment.txSize = Math.round(txSize(tx))
|
||||
const fee = this.payment.feeRate * this.payment.txSize
|
||||
const inputAmount = this.getTotalSelectedUtxoAmount()
|
||||
const payedAmount = this.getTotalPaymentAmount()
|
||||
paymentAddress.amount = Math.max(0, inputAmount - payedAmount - fee)
|
||||
},
|
||||
|
||||
//################### PSBT ###################
|
||||
createPsbt: async function () {
|
||||
const wallet = this.g.user.wallets[0]
|
||||
try {
|
||||
this.computeFee(this.payment.feeRate)
|
||||
// this.computeFee(this.feeRate)
|
||||
const tx = this.createTx()
|
||||
txSize(tx)
|
||||
// txSize(tx)
|
||||
for (const input of tx.inputs) {
|
||||
input.tx_hex = await this.fetchTxHex(input.tx_id)
|
||||
}
|
||||
|
|
@ -960,10 +955,6 @@ const watchOnly = async () => {
|
|||
this.addresses = addresses
|
||||
await this.scanAddressWithAmount()
|
||||
},
|
||||
handleFeeRateChanged: function (newFeeRate) {
|
||||
console.log('### newFeeRate', newFeeRate)
|
||||
this.feeValue = this.computeFee(newFeeRate)
|
||||
}
|
||||
},
|
||||
created: async function () {
|
||||
if (this.g.user.wallets.length) {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,4 @@
|
|||
const tables = {
|
||||
paymentTable: {
|
||||
columns: [
|
||||
{
|
||||
name: 'data',
|
||||
align: 'left'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 10
|
||||
},
|
||||
filter: ''
|
||||
},
|
||||
summaryTable: {
|
||||
columns: [
|
||||
{
|
||||
|
|
@ -43,7 +31,7 @@ const tableData = {
|
|||
total: 0
|
||||
},
|
||||
payment: {
|
||||
data: [{address: '', amount: undefined}],
|
||||
data: [{address: '', amount: undefined}], // todo: remove
|
||||
changeWallet: null,
|
||||
changeAddress: {},
|
||||
changeAmount: 0,
|
||||
|
|
|
|||
|
|
@ -506,96 +506,12 @@
|
|||
<q-tab-panel name="destination">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col-12">
|
||||
<q-table
|
||||
flat
|
||||
dense
|
||||
hide-header
|
||||
:data="payment.data"
|
||||
:columns="paymentTable.columns"
|
||||
:pagination.sync="paymentTable.pagination"
|
||||
>
|
||||
<template v-slot:body="props">
|
||||
<q-tr :props="props">
|
||||
<div class="row no-wrap">
|
||||
<div class="col-1">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
size="l"
|
||||
@click="deletePaymentAddress(props.row)"
|
||||
icon="cancel"
|
||||
color="pink"
|
||||
class="q-mt-sm"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="col-7 q-pr-lg">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="props.row.address"
|
||||
type="text"
|
||||
label="Address"
|
||||
:rules="[val => !!val || 'Field is required']"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-3 q-pr-lg">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.number="props.row.amount"
|
||||
type="number"
|
||||
step="1"
|
||||
label="Amount (sats)"
|
||||
:rules="[val => !!val || 'Field is required', val => +val > DUST_LIMIT || 'Amount to small (below dust limit)'] "
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
@click="sendMaxToAddress(props.row)"
|
||||
>Max</q-btn
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</q-tr>
|
||||
</template>
|
||||
<template v-slot:bottom-row>
|
||||
<q-tr>
|
||||
<q-td colspan="100%">
|
||||
<div class="row items-center no-wrap">
|
||||
<div class="col-3 q-pr-lg">
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
@click="addPaymentAddress"
|
||||
>Add Send Address</q-btn
|
||||
>
|
||||
</div>
|
||||
<div class="col-4 q-pr-lg"></div>
|
||||
<div class="col-5">
|
||||
<div class="float-right">
|
||||
<span class="text-weight-bold"
|
||||
>Payed Amount:
|
||||
</span>
|
||||
<q-badge
|
||||
class="text-subtitle2 q-ml-lg"
|
||||
color="blue"
|
||||
>
|
||||
{{satBtc(getTotalPaymentAmount())}}
|
||||
</q-badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{sendToList}}
|
||||
<send-to
|
||||
:data.sync="sendToList"
|
||||
:tx:size="txSizeNoChange"
|
||||
:sats-denominated="config.data.sats_denominated"
|
||||
></send-to>
|
||||
<!-- <div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col-12">
|
||||
<q-table
|
||||
|
|
@ -617,7 +533,7 @@
|
|||
</q-td>
|
||||
<q-td key="fees" :props="props">
|
||||
<q-badge class="text-subtitle2" color="orange">
|
||||
{{satBtc(computeFee())}}
|
||||
{{satBtc(feeValue)}}
|
||||
</q-badge>
|
||||
</q-td>
|
||||
<q-td key="change" :props="props">
|
||||
|
|
@ -673,10 +589,11 @@
|
|||
<q-card-section>
|
||||
<div class="row items-center no-wrap q-mb-md q-pt-md">
|
||||
<div class="col-12">
|
||||
<fees
|
||||
{{feeRate}}
|
||||
<fee-rate
|
||||
:totalfee="feeValue"
|
||||
@update:fee-rate="handleFeeRateChanged"
|
||||
></fees>
|
||||
:rate.sync="feeRate"
|
||||
></fee-rate>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
|
@ -950,6 +867,7 @@
|
|||
<script src="{{ url_for('watchonly_static', path='components/history/history.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/utxo-list/utxo-list.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/fee-rate/fee-rate.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/send-to/send-to.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='components/payment/payment.js') }}"></script>
|
||||
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue