mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-24 22:58:46 +01:00
refactor: extract wallet-list
component
This commit is contained in:
parent
b18a4d37e3
commit
c5755ac587
7 changed files with 422 additions and 365 deletions
|
@ -41,7 +41,7 @@ async function walletConfig(path) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: async function () {
|
created: async function () {
|
||||||
await this.getConfig()
|
await this.getConfig()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
<div>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<q-btn unelevated color="primary" @click="formDialog.show = true"
|
||||||
|
>Add Wallet Account
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto q-pr-lg"></div>
|
||||||
|
<div class="col-auto q-pl-lg">
|
||||||
|
<q-input
|
||||||
|
borderless
|
||||||
|
dense
|
||||||
|
debounce="300"
|
||||||
|
v-model="filter"
|
||||||
|
placeholder="Search"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="search"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-table
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:data="walletAccounts"
|
||||||
|
row-key="id"
|
||||||
|
:columns="walletsTable.columns"
|
||||||
|
:pagination.sync="walletsTable.pagination"
|
||||||
|
:filter="filter"
|
||||||
|
>
|
||||||
|
<template v-slot:header="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<q-th auto-width></q-th>
|
||||||
|
<q-th
|
||||||
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
|
:props="props"
|
||||||
|
auto-width
|
||||||
|
>
|
||||||
|
{{ col.label }}
|
||||||
|
</q-th>
|
||||||
|
<q-th auto-width></q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<q-td auto-width>
|
||||||
|
<q-btn
|
||||||
|
size="sm"
|
||||||
|
color="accent"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="props.row.expanded= !props.row.expanded"
|
||||||
|
:icon="props.row.expanded? 'remove' : 'add'"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
<q-td key="new">
|
||||||
|
<q-badge
|
||||||
|
size="lg"
|
||||||
|
color="secondary"
|
||||||
|
class="q-mr-md cursor-pointer"
|
||||||
|
@click="openGetFreshAddressDialog(props.row.id)"
|
||||||
|
>
|
||||||
|
New Receive Address
|
||||||
|
</q-badge>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<q-td key="title" :props="props" :class="">
|
||||||
|
<div>{{props.row.title}}</div>
|
||||||
|
</q-td>
|
||||||
|
<q-td key="amount" :props="props" :class="">
|
||||||
|
<div>{{getAmmountForWallet(props.row.id)}}</div>
|
||||||
|
</q-td>
|
||||||
|
<q-td key="type" :props="props" :class="">
|
||||||
|
<div>{{props.row.type}}</div>
|
||||||
|
</q-td>
|
||||||
|
<q-td key="id" :props="props" :class="">
|
||||||
|
<div>{{props.row.id}}</div>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
<q-tr v-show="props.row.expanded" :props="props">
|
||||||
|
<q-td colspan="100%">
|
||||||
|
<div class="row items-center q-mt-md q-mb-lg">
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
<div class="col-4 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="secondary"
|
||||||
|
@click="openGetFreshAddressDialog(props.row.id)"
|
||||||
|
>New Receive Address</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
{{getAccountDescription(props.row.type)}}
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-2 q-pr-lg">Master Pubkey:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<q-input
|
||||||
|
v-model="props.row.masterpub"
|
||||||
|
filled
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-2 q-pr-lg">Last Address Index:</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<span v-if="props.row.address_no >= 0"
|
||||||
|
>{{props.row.address_no}}</span
|
||||||
|
>
|
||||||
|
<span v-if="props.row.address_no < 0">none</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-2 q-pr-lg">Fingerprint:</div>
|
||||||
|
<div class="col-8">{{props.row.fingerprint}}</div>
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row items-center q-mt-md q-mb-lg">
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
<div class="col-4 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="pink"
|
||||||
|
icon="cancel"
|
||||||
|
@click="deleteWalletAccount(props.row.id)"
|
||||||
|
>Delete</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-4"></div>
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-dialog v-model="formDialog.show" position="top" @hide="closeFormDialog">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<q-form @submit="addWalletAccount" class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="formDialog.data.title"
|
||||||
|
type="text"
|
||||||
|
label="Title"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="textarea"
|
||||||
|
v-model="formDialog.data.masterpub"
|
||||||
|
height="50px"
|
||||||
|
autogrow
|
||||||
|
label="Account Extended Public Key; xpub, ypub, zpub; Bitcoin Descriptor"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="
|
||||||
|
formDialog.data.masterpub == null ||
|
||||||
|
formDialog.data.title == null"
|
||||||
|
type="submit"
|
||||||
|
>Add Watch-Only Account</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||||
|
>Cancel</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,184 @@
|
||||||
|
async function walletList(path) {
|
||||||
|
const template = await loadTemplateAsync(path)
|
||||||
|
Vue.component('wallet-list', {
|
||||||
|
name: 'wallet-list',
|
||||||
|
template,
|
||||||
|
|
||||||
|
props: ['adminkey', 'inkey', 'sats-denominated', 'addresses'],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
walletAccounts: [],
|
||||||
|
address: {},
|
||||||
|
formDialog: {
|
||||||
|
show: false,
|
||||||
|
data: {}
|
||||||
|
},
|
||||||
|
filter: '',
|
||||||
|
walletsTable: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'new',
|
||||||
|
align: 'left',
|
||||||
|
label: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Title',
|
||||||
|
field: 'title'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'amount',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Amount'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Type',
|
||||||
|
field: 'type'
|
||||||
|
},
|
||||||
|
{name: 'id', align: 'left', label: 'ID', field: 'id'}
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
rowsPerPage: 10
|
||||||
|
},
|
||||||
|
filter: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
satBtc(val, showUnit = true) {
|
||||||
|
return satOrBtc(val, showUnit, this['sats_denominated'])
|
||||||
|
},
|
||||||
|
|
||||||
|
addWalletAccount: async function () {
|
||||||
|
const data = _.omit(this.formDialog.data, 'wallet')
|
||||||
|
await this.createWalletAccount(data)
|
||||||
|
},
|
||||||
|
createWalletAccount: async function (data) {
|
||||||
|
try {
|
||||||
|
const response = await LNbits.api.request(
|
||||||
|
'POST',
|
||||||
|
'/watchonly/api/v1/wallet',
|
||||||
|
this.adminkey,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
this.walletAccounts.push(mapWalletAccount(response.data))
|
||||||
|
this.formDialog.show = false
|
||||||
|
|
||||||
|
await this.refreshWalletAccounts()
|
||||||
|
} catch (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteWalletAccount: function (walletAccountId) {
|
||||||
|
LNbits.utils
|
||||||
|
.confirmDialog(
|
||||||
|
'Are you sure you want to delete this watch only wallet?'
|
||||||
|
)
|
||||||
|
.onOk(async () => {
|
||||||
|
try {
|
||||||
|
await LNbits.api.request(
|
||||||
|
'DELETE',
|
||||||
|
'/watchonly/api/v1/wallet/' + walletAccountId,
|
||||||
|
this.adminkey
|
||||||
|
)
|
||||||
|
this.walletAccounts = _.reject(this.walletAccounts, function (
|
||||||
|
obj
|
||||||
|
) {
|
||||||
|
return obj.id === walletAccountId
|
||||||
|
})
|
||||||
|
await this.refreshWalletAccounts()
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.payment.changeWallet &&
|
||||||
|
this.payment.changeWallet.id === walletAccountId
|
||||||
|
) {
|
||||||
|
this.payment.changeWallet = this.walletAccounts[0]
|
||||||
|
this.selectChangeAddress(this.payment.changeWallet)
|
||||||
|
}
|
||||||
|
await this.scanAddressWithAmount()
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message:
|
||||||
|
'Error while deleting wallet account. Please try again.',
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getWatchOnlyWallets: async function () {
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'GET',
|
||||||
|
'/watchonly/api/v1/wallet',
|
||||||
|
this.inkey
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Failed to fetch wallets.',
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
refreshWalletAccounts: async function () {
|
||||||
|
const wallets = await this.getWatchOnlyWallets()
|
||||||
|
this.walletAccounts = wallets.map(w => mapWalletAccount(w))
|
||||||
|
this.$emit('accounts-update', this.walletAccounts)
|
||||||
|
},
|
||||||
|
getAmmountForWallet: function (walletId) {
|
||||||
|
const amount = this.addresses
|
||||||
|
.filter(a => a.wallet === walletId)
|
||||||
|
.reduce((t, a) => t + a.amount || 0, 0)
|
||||||
|
return this.satBtc(amount)
|
||||||
|
},
|
||||||
|
closeFormDialog: function () {
|
||||||
|
this.formDialog.data = {
|
||||||
|
is_unique: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAccountDescription: function (accountType) {
|
||||||
|
return getAccountDescription(accountType)
|
||||||
|
},
|
||||||
|
openGetFreshAddressDialog: async function (walletId) {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'GET',
|
||||||
|
`/watchonly/api/v1/address/${walletId}`,
|
||||||
|
this.inkey
|
||||||
|
)
|
||||||
|
const addressData = mapAddressesData(data)
|
||||||
|
|
||||||
|
addressData.note = `Shared on ${currentDateTime()}`
|
||||||
|
const lastAcctiveAddress =
|
||||||
|
this.addresses
|
||||||
|
.filter(
|
||||||
|
a =>
|
||||||
|
a.wallet === addressData.wallet && !a.isChange && a.hasActivity
|
||||||
|
)
|
||||||
|
.pop() || {}
|
||||||
|
addressData.gapLimitExceeded =
|
||||||
|
!addressData.isChange &&
|
||||||
|
addressData.addressIndex >
|
||||||
|
lastAcctiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
|
||||||
|
|
||||||
|
const wallet = this.walletAccounts.find(w => w.id === walletId) || {}
|
||||||
|
wallet.address_no = addressData.addressIndex
|
||||||
|
this.$emit('new-receive-address', addressData)
|
||||||
|
this.$emit('accounts-update', this.walletAccounts)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: async function () {
|
||||||
|
if (this.inkey) {
|
||||||
|
await this.refreshWalletAccounts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ const watchOnly = async () => {
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
|
|
||||||
await walletConfig('static/components/wallet-config/wallet-config.html')
|
await walletConfig('static/components/wallet-config/wallet-config.html')
|
||||||
|
await walletList('static/components/wallet-list/wallet-list.html')
|
||||||
|
|
||||||
Vue.filter('reverse', function (value) {
|
Vue.filter('reverse', function (value) {
|
||||||
// slice to make a copy of array, then reverse the copy
|
// slice to make a copy of array, then reverse the copy
|
||||||
|
@ -14,7 +15,7 @@ const watchOnly = async () => {
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
DUST_LIMIT: 546,
|
DUST_LIMIT: 546,
|
||||||
filter: '',
|
filter: '', // todo: remove?
|
||||||
|
|
||||||
scan: {
|
scan: {
|
||||||
scanning: false,
|
scanning: false,
|
||||||
|
@ -32,7 +33,7 @@ const watchOnly = async () => {
|
||||||
receive_gap_limit: 20,
|
receive_gap_limit: 20,
|
||||||
change_gap_limit: 5
|
change_gap_limit: 5
|
||||||
},
|
},
|
||||||
DEFAULT_RECEIVE_GAP_LIMIT: 20,
|
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -63,17 +64,14 @@ const watchOnly = async () => {
|
||||||
psbtSent: false
|
psbtSent: false
|
||||||
},
|
},
|
||||||
|
|
||||||
formDialog: {
|
|
||||||
show: false,
|
|
||||||
data: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
qrCodeDialog: {
|
qrCodeDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
data: null
|
data: null
|
||||||
},
|
},
|
||||||
...tables,
|
...tables,
|
||||||
...tableData
|
...tableData,
|
||||||
|
|
||||||
|
walletAccounts: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -81,74 +79,6 @@ const watchOnly = async () => {
|
||||||
//################### CONFIG ###################
|
//################### CONFIG ###################
|
||||||
|
|
||||||
//################### WALLETS ###################
|
//################### WALLETS ###################
|
||||||
getWalletName: function (walletId) {
|
|
||||||
const wallet = this.walletAccounts.find(wl => wl.id === walletId)
|
|
||||||
return wallet ? wallet.title : 'unknown'
|
|
||||||
},
|
|
||||||
addWalletAccount: async function () {
|
|
||||||
const wallet = this.g.user.wallets[0]
|
|
||||||
const data = _.omit(this.formDialog.data, 'wallet')
|
|
||||||
await this.createWalletAccount(wallet, data)
|
|
||||||
},
|
|
||||||
createWalletAccount: async function (wallet, data) {
|
|
||||||
try {
|
|
||||||
const response = await LNbits.api.request(
|
|
||||||
'POST',
|
|
||||||
'/watchonly/api/v1/wallet',
|
|
||||||
wallet.adminkey,
|
|
||||||
data
|
|
||||||
)
|
|
||||||
this.walletAccounts.push(mapWalletAccount(response.data))
|
|
||||||
this.formDialog.show = false
|
|
||||||
|
|
||||||
await this.refreshWalletAccounts()
|
|
||||||
await this.refreshAddresses()
|
|
||||||
|
|
||||||
if (!this.payment.changeWallett) {
|
|
||||||
this.payment.changeWallet = this.walletAccounts[0]
|
|
||||||
this.selectChangeAddress(this.payment.changeWallet)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
LNbits.utils.notifyApiError(error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deleteWalletAccount: function (walletAccountId) {
|
|
||||||
LNbits.utils
|
|
||||||
.confirmDialog(
|
|
||||||
'Are you sure you want to delete this watch only wallet?'
|
|
||||||
)
|
|
||||||
.onOk(async () => {
|
|
||||||
try {
|
|
||||||
await LNbits.api.request(
|
|
||||||
'DELETE',
|
|
||||||
'/watchonly/api/v1/wallet/' + walletAccountId,
|
|
||||||
this.g.user.wallets[0].adminkey
|
|
||||||
)
|
|
||||||
this.walletAccounts = _.reject(this.walletAccounts, function (
|
|
||||||
obj
|
|
||||||
) {
|
|
||||||
return obj.id === walletAccountId
|
|
||||||
})
|
|
||||||
await this.refreshWalletAccounts()
|
|
||||||
await this.refreshAddresses()
|
|
||||||
if (
|
|
||||||
this.payment.changeWallet &&
|
|
||||||
this.payment.changeWallet.id === walletAccountId
|
|
||||||
) {
|
|
||||||
this.payment.changeWallet = this.walletAccounts[0]
|
|
||||||
this.selectChangeAddress(this.payment.changeWallet)
|
|
||||||
}
|
|
||||||
await this.scanAddressWithAmount()
|
|
||||||
} catch (error) {
|
|
||||||
this.$q.notify({
|
|
||||||
type: 'warning',
|
|
||||||
message:
|
|
||||||
'Error while deleting wallet account. Please try again.',
|
|
||||||
timeout: 10000
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getAddressesForWallet: async function (walletId) {
|
getAddressesForWallet: async function (walletId) {
|
||||||
try {
|
try {
|
||||||
const {data} = await LNbits.api.request(
|
const {data} = await LNbits.api.request(
|
||||||
|
@ -167,41 +97,17 @@ const watchOnly = async () => {
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
getWatchOnlyWallets: async function () {
|
getWalletName: function (walletId) {
|
||||||
try {
|
const wallet = this.walletAccounts.find(wl => wl.id === walletId)
|
||||||
const {data} = await LNbits.api.request(
|
return wallet ? wallet.title : 'unknown'
|
||||||
'GET',
|
|
||||||
'/watchonly/api/v1/wallet',
|
|
||||||
this.g.user.wallets[0].inkey
|
|
||||||
)
|
|
||||||
return data
|
|
||||||
} catch (error) {
|
|
||||||
this.$q.notify({
|
|
||||||
type: 'warning',
|
|
||||||
message: 'Failed to fetch wallets.',
|
|
||||||
timeout: 10000
|
|
||||||
})
|
|
||||||
LNbits.utils.notifyApiError(error)
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
},
|
},
|
||||||
refreshWalletAccounts: async function () {
|
|
||||||
const wallets = await this.getWatchOnlyWallets()
|
|
||||||
this.walletAccounts = wallets.map(w => mapWalletAccount(w))
|
|
||||||
},
|
|
||||||
getAmmountForWallet: function (walletId) {
|
|
||||||
const amount = this.addresses.data
|
|
||||||
.filter(a => a.wallet === walletId)
|
|
||||||
.reduce((t, a) => t + a.amount || 0, 0)
|
|
||||||
return this.satBtc(amount)
|
|
||||||
},
|
|
||||||
|
|
||||||
//################### ADDRESSES ###################
|
//################### ADDRESSES ###################
|
||||||
|
|
||||||
refreshAddresses: async function () {
|
refreshAddresses: async function () {
|
||||||
const wallets = await this.getWatchOnlyWallets()
|
// const wallets = await this.getWatchOnlyWallets() todo: revisit
|
||||||
|
// const wallets =
|
||||||
this.addresses.data = []
|
this.addresses.data = []
|
||||||
for (const {id, type} of wallets) {
|
for (const {id, type} of this.walletAccounts) {
|
||||||
const newAddresses = await this.getAddressesForWallet(id)
|
const newAddresses = await this.getAddressesForWallet(id)
|
||||||
const uniqueAddresses = newAddresses.filter(
|
const uniqueAddresses = newAddresses.filter(
|
||||||
newAddr =>
|
newAddr =>
|
||||||
|
@ -218,8 +124,7 @@ const watchOnly = async () => {
|
||||||
a.gapLimitExceeded =
|
a.gapLimitExceeded =
|
||||||
!a.isChange &&
|
!a.isChange &&
|
||||||
a.addressIndex >
|
a.addressIndex >
|
||||||
lastAcctiveAddress.addressIndex +
|
lastAcctiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
|
||||||
this.config.DEFAULT_RECEIVE_GAP_LIMIT
|
|
||||||
})
|
})
|
||||||
this.addresses.data.push(...uniqueAddresses)
|
this.addresses.data.push(...uniqueAddresses)
|
||||||
}
|
}
|
||||||
|
@ -295,33 +200,6 @@ const watchOnly = async () => {
|
||||||
)
|
)
|
||||||
return addresses
|
return addresses
|
||||||
},
|
},
|
||||||
openGetFreshAddressDialog: async function (walletId) {
|
|
||||||
const {data} = await LNbits.api.request(
|
|
||||||
'GET',
|
|
||||||
`/watchonly/api/v1/address/${walletId}`,
|
|
||||||
this.g.user.wallets[0].inkey
|
|
||||||
)
|
|
||||||
const addressData = mapAddressesData(data)
|
|
||||||
|
|
||||||
addressData.note = `Shared on ${currentDateTime()}`
|
|
||||||
const lastAcctiveAddress =
|
|
||||||
this.addresses.data
|
|
||||||
.filter(
|
|
||||||
a =>
|
|
||||||
a.wallet === addressData.wallet && !a.isChange && a.hasActivity
|
|
||||||
)
|
|
||||||
.pop() || {}
|
|
||||||
addressData.gapLimitExceeded =
|
|
||||||
!addressData.isChange &&
|
|
||||||
addressData.addressIndex >
|
|
||||||
lastAcctiveAddress.addressIndex +
|
|
||||||
this.config.DEFAULT_RECEIVE_GAP_LIMIT
|
|
||||||
|
|
||||||
this.openQrCodeDialog(addressData)
|
|
||||||
const wallet = this.walletAccounts.find(w => w.id === walletId) || {}
|
|
||||||
wallet.address_no = addressData.addressIndex
|
|
||||||
await this.refreshAddresses()
|
|
||||||
},
|
|
||||||
|
|
||||||
//################### ADDRESS HISTORY ###################
|
//################### ADDRESS HISTORY ###################
|
||||||
addressHistoryFromTxs: function (addressData, txs) {
|
addressHistoryFromTxs: function (addressData, txs) {
|
||||||
|
@ -1158,11 +1036,7 @@ const watchOnly = async () => {
|
||||||
},
|
},
|
||||||
|
|
||||||
//################### OTHER ###################
|
//################### OTHER ###################
|
||||||
closeFormDialog: function () {
|
|
||||||
this.formDialog.data = {
|
|
||||||
is_unique: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openQrCodeDialog: function (addressData) {
|
openQrCodeDialog: function (addressData) {
|
||||||
this.currentAddress = addressData
|
this.currentAddress = addressData
|
||||||
this.addresses.note = addressData.note || ''
|
this.addresses.note = addressData.note || ''
|
||||||
|
@ -1176,13 +1050,27 @@ const watchOnly = async () => {
|
||||||
satBtc(val, showUnit = true) {
|
satBtc(val, showUnit = true) {
|
||||||
return satOrBtc(val, showUnit, this.config.data.sats_denominated)
|
return satOrBtc(val, showUnit, this.config.data.sats_denominated)
|
||||||
},
|
},
|
||||||
getAccountDescription: function (accountType) {
|
updateAccounts: async function (accounts) {
|
||||||
return getAccountDescription(accountType)
|
this.walletAccounts = accounts
|
||||||
|
await this.refreshAddresses()
|
||||||
|
|
||||||
|
if (this.payment.changeWallet) {
|
||||||
|
const changeAccount = this.walletAccounts.find(
|
||||||
|
w => w.id === this.payment.changeWallet.id
|
||||||
|
)
|
||||||
|
// change account deleted
|
||||||
|
if (!changeAccount) {
|
||||||
|
this.payment.changeWallet = this.walletAccounts[0]
|
||||||
|
this.selectChangeAddress(this.payment.changeWallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleNewReceiveAddress: function (addressData) {
|
||||||
|
this.openQrCodeDialog(addressData)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: async function () {
|
created: async function () {
|
||||||
if (this.g.user.wallets.length) {
|
if (this.g.user.wallets.length) {
|
||||||
await this.refreshWalletAccounts()
|
|
||||||
await this.refreshAddresses()
|
await this.refreshAddresses()
|
||||||
await this.scanAddressWithAmount()
|
await this.scanAddressWithAmount()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,4 @@
|
||||||
const tables = {
|
const tables = {
|
||||||
walletsTable: {
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
name: 'new',
|
|
||||||
align: 'left',
|
|
||||||
label: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Title',
|
|
||||||
field: 'title'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'amount',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Amount'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'type',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Type',
|
|
||||||
field: 'type'
|
|
||||||
},
|
|
||||||
{name: 'id', align: 'left', label: 'ID', field: 'id'}
|
|
||||||
],
|
|
||||||
pagination: {
|
|
||||||
rowsPerPage: 10
|
|
||||||
},
|
|
||||||
filter: ''
|
|
||||||
},
|
|
||||||
utxosTable: {
|
utxosTable: {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
|
@ -225,7 +194,7 @@ const tables = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableData = {
|
const tableData = {
|
||||||
walletAccounts: [],
|
// walletAccounts: [], // todo: remove?
|
||||||
addresses: {
|
addresses: {
|
||||||
show: false,
|
show: false,
|
||||||
data: [],
|
data: [],
|
||||||
|
|
|
@ -8,6 +8,8 @@ const COMMAND_WIPE = '/wipe'
|
||||||
const COMMAND_SEED = '/seed'
|
const COMMAND_SEED = '/seed'
|
||||||
const COMMAND_RESTORE = '/restore'
|
const COMMAND_RESTORE = '/restore'
|
||||||
|
|
||||||
|
const DEFAULT_RECEIVE_GAP_LIMIT = 20
|
||||||
|
|
||||||
const blockTimeToDate = blockTime =>
|
const blockTimeToDate = blockTime =>
|
||||||
blockTime ? moment(blockTime * 1000).format('LLL') : ''
|
blockTime ? moment(blockTime * 1000).format('LLL') : ''
|
||||||
|
|
||||||
|
@ -158,7 +160,7 @@ function loadTemplateAsync(path) {
|
||||||
if (this.readyState == 4) {
|
if (this.readyState == 4) {
|
||||||
if (this.status == 200) resolve(this.responseText)
|
if (this.status == 200) resolve(this.responseText)
|
||||||
|
|
||||||
if (this.status == 404) resolve('Page not found.')
|
if (this.status == 404) resolve(`<div>Page not found: ${path}</div>`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,157 +7,19 @@
|
||||||
:config="config"
|
:config="config"
|
||||||
:adminkey="g.user.wallets[0].adminkey"
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
></wallet-config>
|
></wallet-config>
|
||||||
|
|
||||||
|
<wallet-list
|
||||||
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
|
:inkey="g.user.wallets[0].inkey"
|
||||||
|
:sats-denominated="config.data.sats_denominated"
|
||||||
|
:addresses="addresses.data"
|
||||||
|
@accounts-update="updateAccounts"
|
||||||
|
@new-receive-address="handleNewReceiveAddress"
|
||||||
|
>
|
||||||
|
</wallet-list>
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
<!-- :walletAccounts.sync="walletAccounts" -->
|
||||||
<q-card>
|
<!-- :walletAccounts="walletAccounts" -->
|
||||||
<q-card-section>
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
|
||||||
<div class="col">
|
|
||||||
<q-btn unelevated color="primary" @click="formDialog.show = true"
|
|
||||||
>Add Wallet Account
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-auto q-pr-lg"></div>
|
|
||||||
<div class="col-auto q-pl-lg">
|
|
||||||
<q-input
|
|
||||||
borderless
|
|
||||||
dense
|
|
||||||
debounce="300"
|
|
||||||
v-model="filter"
|
|
||||||
placeholder="Search"
|
|
||||||
>
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search"></q-icon>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-table
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
:data="walletAccounts"
|
|
||||||
row-key="id"
|
|
||||||
:columns="walletsTable.columns"
|
|
||||||
:pagination.sync="walletsTable.pagination"
|
|
||||||
:filter="filter"
|
|
||||||
>
|
|
||||||
<template v-slot:header="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-th auto-width></q-th>
|
|
||||||
<q-th
|
|
||||||
v-for="col in props.cols"
|
|
||||||
:key="col.name"
|
|
||||||
:props="props"
|
|
||||||
auto-width
|
|
||||||
>
|
|
||||||
{{ col.label }}
|
|
||||||
</q-th>
|
|
||||||
<q-th auto-width></q-th>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-td auto-width>
|
|
||||||
<q-btn
|
|
||||||
size="sm"
|
|
||||||
color="accent"
|
|
||||||
round
|
|
||||||
dense
|
|
||||||
@click="props.row.expanded= !props.row.expanded"
|
|
||||||
:icon="props.row.expanded? 'remove' : 'add'"
|
|
||||||
/>
|
|
||||||
</q-td>
|
|
||||||
<q-td key="new">
|
|
||||||
<q-badge
|
|
||||||
size="lg"
|
|
||||||
color="secondary"
|
|
||||||
class="q-mr-md cursor-pointer"
|
|
||||||
@click="openGetFreshAddressDialog(props.row.id)"
|
|
||||||
>
|
|
||||||
New Receive Address
|
|
||||||
</q-badge>
|
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td key="title" :props="props" :class="">
|
|
||||||
<div>{{props.row.title}}</div>
|
|
||||||
</q-td>
|
|
||||||
<q-td key="amount" :props="props" :class="">
|
|
||||||
<div>{{getAmmountForWallet(props.row.id)}}</div>
|
|
||||||
</q-td>
|
|
||||||
<q-td key="type" :props="props" :class="">
|
|
||||||
<div>{{props.row.type}}</div>
|
|
||||||
</q-td>
|
|
||||||
<q-td key="id" :props="props" :class="">
|
|
||||||
<div>{{props.row.id}}</div>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
<q-tr v-show="props.row.expanded" :props="props">
|
|
||||||
<q-td colspan="100%">
|
|
||||||
<div class="row items-center q-mt-md q-mb-lg">
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
<div class="col-4 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="secondary"
|
|
||||||
@click="openGetFreshAddressDialog(props.row.id)"
|
|
||||||
>New Receive Address</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
{{getAccountDescription(props.row.type)}}
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
|
||||||
<div class="col-2 q-pr-lg">Master Pubkey:</div>
|
|
||||||
<div class="col-8">
|
|
||||||
<q-input
|
|
||||||
v-model="props.row.masterpub"
|
|
||||||
filled
|
|
||||||
readonly
|
|
||||||
type="textarea"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
|
||||||
<div class="col-2 q-pr-lg">Last Address Index:</div>
|
|
||||||
<div class="col-8">
|
|
||||||
<span v-if="props.row.address_no >= 0"
|
|
||||||
>{{props.row.address_no}}</span
|
|
||||||
>
|
|
||||||
<span v-if="props.row.address_no < 0">none</span>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
|
||||||
<div class="col-2 q-pr-lg">Fingerprint:</div>
|
|
||||||
<div class="col-8">{{props.row.fingerprint}}</div>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row items-center q-mt-md q-mb-lg">
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
<div class="col-4 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="pink"
|
|
||||||
icon="cancel"
|
|
||||||
@click="deleteWalletAccount(props.row.id)"
|
|
||||||
>Delete</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col-4"></div>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
</div>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
|
|
||||||
<q-card>
|
<q-card>
|
||||||
<div class="row q-pt-sm q-pb-sm items-center no-wrap q-mb-md">
|
<div class="row q-pt-sm q-pb-sm items-center no-wrap q-mb-md">
|
||||||
<div class="col-3 q-pl-md">
|
<div class="col-3 q-pl-md">
|
||||||
|
@ -1403,44 +1265,6 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-dialog v-model="formDialog.show" position="top" @hide="closeFormDialog">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<q-form @submit="addWalletAccount" class="q-gutter-md">
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="formDialog.data.title"
|
|
||||||
type="text"
|
|
||||||
label="Title"
|
|
||||||
></q-input>
|
|
||||||
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
type="textarea"
|
|
||||||
v-model="formDialog.data.masterpub"
|
|
||||||
height="50px"
|
|
||||||
autogrow
|
|
||||||
label="Account Extended Public Key; xpub, ypub, zpub; Bitcoin Descriptor"
|
|
||||||
></q-input>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
:disable="
|
|
||||||
formDialog.data.masterpub == null ||
|
|
||||||
formDialog.data.title == null"
|
|
||||||
type="submit"
|
|
||||||
>Add Watch-Only Account</q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
|
||||||
>Cancel</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="addresses.show" position="top">
|
<q-dialog v-model="addresses.show" position="top">
|
||||||
<q-card v-if="addresses.data" class="q-pa-lg lnbits__dialog-card">
|
<q-card v-if="addresses.data" class="q-pa-lg lnbits__dialog-card">
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
@ -1648,5 +1472,6 @@
|
||||||
<script src="{{ url_for('watchonly_static', path='js/utils.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='js/utils.js') }}"></script>
|
||||||
<script src="{{ url_for('watchonly_static', path='components/my-checkbox/my-checkbox.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='components/my-checkbox/my-checkbox.js') }}"></script>
|
||||||
<script src="{{ url_for('watchonly_static', path='components/wallet-config/wallet-config.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='components/wallet-config/wallet-config.js') }}"></script>
|
||||||
|
<script src="{{ url_for('watchonly_static', path='components/wallet-list/wallet-list.js') }}"></script>
|
||||||
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue