refactor: extract address-list component

This commit is contained in:
Vlad Stan 2022-07-25 14:57:54 +03:00
parent 8376f08e8b
commit ff29aacace
6 changed files with 431 additions and 368 deletions

View file

@ -48,7 +48,7 @@ You can now use this wallet on the LNBits [SatsPayServer](https://github.com/lnb
- shows the UTXOs for all wallets
- there can be multiple UTXOs for the same address
### Make Payment
### New Payment
- create a new `Partially Signed Bitcoin Transaction`
- multiple `Send Addresses` can be added
- the `Max` button next to an address is for sending the remaining funds to this address (no change)
@ -57,7 +57,7 @@ You can now use this wallet on the LNBits [SatsPayServer](https://github.com/lnb
- `Show Advanced` allows to (see `screenshot 2`):
- select from which account the change address will be selected (defaults to the first one)
- select the `Fee Rate`
- it defaults to the `Medium` value at the moment the `Make Payment` button was clicked
- it defaults to the `Medium` value at the moment the `New Payment` button was clicked
- it can be refreshed
- warnings are shown if the fee is too Low or to High

View file

@ -0,0 +1,204 @@
<div>
<div class="row items-center no-wrap q-mb-md">
<div class="col q-pr-lg">
<q-select
filled
clearable
dense
emit-value
v-model="selectedWallet"
:options="accounts"
label="Wallet Account"
></q-select>
</div>
<div class="col q-pr-lg">
<q-select
filled
clearable
dense
emit-value
multiple
:options="filterOptions"
v-model="filterValues"
label="Filter"
></q-select>
</div>
<div class="col-auto">
<q-input
borderless
dense
debounce="300"
v-model="addressesTable.filter"
placeholder="Search"
>
<template v-slot:append>
<q-icon name="search"></q-icon>
</template>
</q-input>
</div>
</div>
<q-table
style="height: 400px"
flat
dense
:data="getFilteredAddresses()"
row-key="id"
virtual-scroll
:columns="addressesTable.columns"
:pagination.sync="addressesTable.pagination"
:filter="addressesTable.filter"
>
<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="address" :props="props">
<div>
<a
style="color: unset"
:href="mempool_endpoint + '/address/' + props.row.address"
target="_blank"
>
{{props.row.address}}</a
>
<q-badge
v-if="props.row.branch_index === 1"
color="orange"
class="q-mr-md"
outline
>
change
</q-badge>
<q-btn
v-if="props.row.gapLimitExceeded"
color="yellow"
icon="warning"
title="Gap Limit Exceeded"
@click="props.row.expanded= !props.row.expanded"
outline
class="q-ml-md"
size="xs"
>
</q-btn>
</div>
</q-td>
<q-td
key="amount"
:props="props"
:class="props.row.amount > 0 ? 'text-green-13 text-weight-bold' : ''"
>
<div>{{satBtc(props.row.amount)}}</div>
</q-td>
<q-td key="note" :props="props" :class="">
<div>{{props.row.note}}</div>
</q-td>
<q-td key="wallet" :props="props" :class="">
<div>{{getWalletName(props.row.wallet)}}</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
dense
size="md"
icon="qr_code"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="showAddressDetails(props.row)"
>
QR Code</q-btn
>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
dense
size="md"
icon="refresh"
color="grey"
@click="scanAddress(props.row)"
>
Rescan</q-btn
>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
dense
size="md"
icon="history"
color="grey"
@click="searchInTab('history', props.row.address)"
>History</q-btn
>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
dense
size="md"
color="grey"
@click="searchInTab('utxos', props.row.address)"
>View Coins</q-btn
>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-2 q-pr-lg">Note:</div>
<div class="col-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="props.row.note"
type="text"
label="Note"
></q-input>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
color="grey"
@click="updateNoteForAddress(props.row, props.row.note)"
>Update
</q-btn>
</div>
</div>
<div v-if="props.row.error" class="row items-center no-wrap q-mb-md">
<div class="col-2 q-pr-lg"></div>
<div class="col-10 q-pr-lg">
<q-badge color="red">{{props.row.error}}</q-badge>
</div>
</div>
<div
v-if="props.row.gapLimitExceeded"
class="row items-center no-wrap q-mb-md"
>
<div class="col-2 q-pr-lg"></div>
<div class="col-10 q-pr-lg">
<q-badge color="yellow" text-color="black"
>Gap limit of 20 addresses exceeded. Other wallets might not
detect funds at this address.</q-badge
>
</div>
</div>
</q-td>
</q-tr>
</template>
</q-table>
</div>

View file

@ -0,0 +1,169 @@
async function addressList(path) {
const template = await loadTemplateAsync(path)
Vue.component('address-list', {
name: 'address-list',
template,
props: ['accounts', 'mempool_endpoint', 'inkey'],
data: function () {
return {
addresses: [],
show: false,
data: [],
history: [],
selectedWallet: null,
note: '',
filterOptions: [
'Show Change Addresses',
'Show Gap Addresses',
'Only With Amount'
],
filterValues: [],
addressesTable: {
columns: [
{
name: 'expand',
align: 'left',
label: ''
},
{
name: 'address',
align: 'left',
label: 'Address',
field: 'address',
sortable: true
},
{
name: 'amount',
align: 'left',
label: 'Amount',
field: 'amount',
sortable: true
},
{
name: 'note',
align: 'left',
label: 'Note',
field: 'note',
sortable: true
},
{
name: 'wallet',
align: 'left',
label: 'Account',
field: 'wallet',
sortable: true
}
],
pagination: {
rowsPerPage: 0,
sortBy: 'amount',
descending: true
},
filter: ''
}
}
},
watch: {
immediate: true,
accounts(newVal, oldVal) {
if ((newVal || []).length !== (oldVal || []).length) {
console.log('### refreshAddresses')
this.refreshAddresses() // todo await
}
}
},
methods: {
satBtc(val, showUnit = true) {
return satOrBtc(val, showUnit, this['sats_denominated'])
},
getWalletName: function (walletId) {
const wallet = (this.accounts || []).find(wl => wl.id === walletId)
return wallet ? wallet.title : 'unknown'
},
getFilteredAddresses: function () {
const selectedWalletId = this.selectedWallet?.id
const filter = this.filterValues || []
const includeChangeAddrs = filter.includes('Show Change Addresses')
const includeGapAddrs = filter.includes('Show Gap Addresses')
const excludeNoAmount = filter.includes('Only With Amount')
const walletsLimit = (this.accounts || []).reduce((r, w) => {
r[`_${w.id}`] = w.address_no
return r
}, {})
console.log('### walletsLimit', walletsLimit)
console.log('### this.addresses', this.addresses)
const fAddresses = this.addresses.filter(
a =>
(includeChangeAddrs || !a.isChange) &&
(includeGapAddrs ||
a.isChange ||
a.addressIndex <= walletsLimit[`_${a.wallet}`]) &&
!(excludeNoAmount && a.amount === 0) &&
(!selectedWalletId || a.wallet === selectedWalletId)
)
console.log('### fAddresses', fAddresses)
return fAddresses
},
getAddressesForWallet: async function (walletId) {
try {
const {data} = await LNbits.api.request(
'GET',
'/watchonly/api/v1/addresses/' + walletId,
this.inkey
)
return data.map(mapAddressesData)
} catch (err) {
this.$q.notify({
type: 'warning',
message: `Failed to fetch addresses for wallet with id ${walletId}.`,
timeout: 10000
})
LNbits.utils.notifyApiError(err)
}
return []
},
refreshAddresses: async function () {
console.log('### refreshAddresses, this.accounts', this.accounts)
if (!this.accounts) return
this.addresses = []
for (const {id, type} of this.accounts) {
const newAddresses = await this.getAddressesForWallet(id)
const uniqueAddresses = newAddresses.filter(
newAddr => !this.addresses.find(a => a.address === newAddr.address)
)
const lastAcctiveAddress =
uniqueAddresses.filter(a => !a.isChange && a.hasActivity).pop() ||
{}
uniqueAddresses.forEach(a => {
a.expanded = false
a.accountType = type
a.gapLimitExceeded =
!a.isChange &&
a.addressIndex >
lastAcctiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
})
this.addresses.push(...uniqueAddresses)
}
console.log('### refreshAddresses, this.addresse', this.addresses)
this.$emit('update:addresses', this.addresses)
},
scanAddress: async function (addressData) {
this.$emit('scan:address', addressData)
},
showAddressDetails: function (addressData) {
this.$emit('show-address-details', addressData)
}
},
created: async function () {
await this.refreshAddresses()
}
})
}

View file

@ -3,6 +3,7 @@ const watchOnly = async () => {
await walletConfig('static/components/wallet-config/wallet-config.html')
await walletList('static/components/wallet-list/wallet-list.html')
await addressList('static/components/address-list/address-list.html')
Vue.filter('reverse', function (value) {
// slice to make a copy of array, then reverse the copy
@ -71,7 +72,12 @@ const watchOnly = async () => {
...tables,
...tableData,
walletAccounts: []
walletAccounts: [],
addresses: [],
history: [],
showAddress: false,
addressNote: ''
}
},
@ -79,56 +85,13 @@ const watchOnly = async () => {
//################### CONFIG ###################
//################### WALLETS ###################
getAddressesForWallet: async function (walletId) {
try {
const {data} = await LNbits.api.request(
'GET',
'/watchonly/api/v1/addresses/' + walletId,
this.g.user.wallets[0].inkey
)
return data.map(mapAddressesData)
} catch (err) {
this.$q.notify({
type: 'warning',
message: `Failed to fetch addresses for wallet with id ${walletId}.`,
timeout: 10000
})
LNbits.utils.notifyApiError(err)
}
return []
},
getWalletName: function (walletId) {
const wallet = this.walletAccounts.find(wl => wl.id === walletId)
return wallet ? wallet.title : 'unknown'
},
//################### ADDRESSES ###################
refreshAddresses: async function () {
// const wallets = await this.getWatchOnlyWallets() todo: revisit
// const wallets =
this.addresses.data = []
for (const {id, type} of this.walletAccounts) {
const newAddresses = await this.getAddressesForWallet(id)
const uniqueAddresses = newAddresses.filter(
newAddr =>
!this.addresses.data.find(a => a.address === newAddr.address)
)
const lastAcctiveAddress =
uniqueAddresses.filter(a => !a.isChange && a.hasActivity).pop() ||
{}
uniqueAddresses.forEach(a => {
a.expanded = false
a.accountType = type
a.gapLimitExceeded =
!a.isChange &&
a.addressIndex >
lastAcctiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
})
this.addresses.data.push(...uniqueAddresses)
}
},
updateAmountForAddress: async function (addressData, amount = 0) {
try {
const wallet = this.g.user.wallets[0]
@ -171,35 +134,12 @@ const watchOnly = async () => {
{note: addressData.note}
)
const updatedAddress =
this.addresses.data.find(a => a.id === addressData.id) || {}
this.addresses.find(a => a.id === addressData.id) || {}
updatedAddress.note = note
} catch (err) {
LNbits.utils.notifyApiError(err)
}
},
getFilteredAddresses: function () {
const selectedWalletId = this.addresses.selectedWallet?.id
const filter = this.addresses.filterValues || []
const includeChangeAddrs = filter.includes('Show Change Addresses')
const includeGapAddrs = filter.includes('Show Gap Addresses')
const excludeNoAmount = filter.includes('Only With Amount')
const walletsLimit = this.walletAccounts.reduce((r, w) => {
r[`_${w.id}`] = w.address_no
return r
}, {})
const addresses = this.addresses.data.filter(
a =>
(includeChangeAddrs || !a.isChange) &&
(includeGapAddrs ||
a.isChange ||
a.addressIndex <= walletsLimit[`_${a.wallet}`]) &&
!(excludeNoAmount && a.amount === 0) &&
(!selectedWalletId || a.wallet === selectedWalletId)
)
return addresses
},
//################### ADDRESS HISTORY ###################
addressHistoryFromTxs: function (addressData, txs) {
@ -219,9 +159,7 @@ const watchOnly = async () => {
return addressHistory
},
getFilteredAddressesHistory: function () {
return this.addresses.history.filter(
a => (!a.isChange || a.sent) && !a.isSubItem
)
return this.history.filter(a => (!a.isChange || a.sent) && !a.isSubItem)
},
exportHistoryToCSV: function () {
const history = this.getFilteredAddressesHistory().map(a => ({
@ -235,7 +173,7 @@ const watchOnly = async () => {
)
},
markSameTxAddressHistory: function () {
this.addresses.history
this.history
.filter(s => s.sent)
.forEach((el, i, arr) => {
if (el.isSubItem) return
@ -321,7 +259,7 @@ const watchOnly = async () => {
},
initPaymentData: async function () {
if (!this.payment.show) return
await this.refreshAddresses()
await this.$refs.addressList.refreshAddresses()
this.payment.showAdvanced = false
this.payment.changeWallet = this.walletAccounts[0]
@ -347,7 +285,7 @@ const watchOnly = async () => {
},
selectChangeAddress: function (wallet = {}) {
this.payment.changeAddress =
this.addresses.data.find(
this.addresses.find(
a => a.wallet === wallet.id && a.isChange && !a.hasActivity
) || {}
},
@ -840,18 +778,18 @@ const watchOnly = async () => {
},
//################### UTXOs ###################
scanAllAddresses: async function () {
await this.refreshAddresses()
this.addresses.history = []
let addresses = this.addresses.data
await this.$refs.addressList.refreshAddresses()
this.history = []
let addresses = this.addresses
this.utxos.data = []
this.utxos.total = 0
// Loop while new funds are found on the gap adresses.
// Use 1000 limit as a safety check (scan 20 000 addresses max)
for (let i = 0; i < 1000 && addresses.length; i++) {
await this.updateUtxosForAddresses(addresses)
const oldAddresses = this.addresses.data.slice()
await this.refreshAddresses()
const newAddresses = this.addresses.data.slice()
const oldAddresses = this.addresses.slice()
await this.$refs.addressList.refreshAddresses()
const newAddresses = this.addresses.slice()
// check if gap addresses have been extended
addresses = newAddresses.filter(
newAddr => !oldAddresses.find(oldAddr => oldAddr.id === newAddr.id)
@ -868,11 +806,12 @@ const watchOnly = async () => {
scanAddressWithAmount: async function () {
this.utxos.data = []
this.utxos.total = 0
this.addresses.history = []
const addresses = this.addresses.data.filter(a => a.hasActivity)
this.history = []
const addresses = this.addresses.filter(a => a.hasActivity)
await this.updateUtxosForAddresses(addresses)
},
scanAddress: async function (addressData) {
console.log('### scanAddress', addressData)
this.updateUtxosForAddresses([addressData])
this.$q.notify({
type: 'positive',
@ -887,15 +826,13 @@ const watchOnly = async () => {
for (addrData of addresses) {
const addressHistory = await this.getAddressTxsDelayed(addrData)
// remove old entries
this.addresses.history = this.addresses.history.filter(
this.history = this.history.filter(
h => h.address !== addrData.address
)
// add new entrie
this.addresses.history.push(...addressHistory)
this.addresses.history.sort((a, b) =>
!a.height ? -1 : b.height - a.height
)
this.history.push(...addressHistory)
this.history.sort((a, b) => (!a.height ? -1 : b.height - a.height))
this.markSameTxAddressHistory()
if (addressHistory.length) {
@ -1038,9 +975,10 @@ const watchOnly = async () => {
//################### OTHER ###################
openQrCodeDialog: function (addressData) {
console.log('### addressData', addressData)
this.currentAddress = addressData
this.addresses.note = addressData.note || ''
this.addresses.show = true
this.addressNote = addressData.note || ''
this.showAddress = true
},
searchInTab: function (tab, value) {
this.tab = tab
@ -1052,7 +990,7 @@ const watchOnly = async () => {
},
updateAccounts: async function (accounts) {
this.walletAccounts = accounts
await this.refreshAddresses()
// await this.refreshAddressesxx() // todo: automatic now?
await this.scanAddressWithAmount()
if (this.payment.changeWallet) {
@ -1066,13 +1004,17 @@ const watchOnly = async () => {
}
}
},
handleNewReceiveAddress: function (addressData) {
showAddressDetails: function (addressData) {
console.log('### showAddressDetails addressData', addressData)
this.openQrCodeDialog(addressData)
},
handleAddressesUpdated: function (addresses) {
this.addresses = addresses
}
},
created: async function () {
if (this.g.user.wallets.length) {
await this.refreshAddresses()
// await this.refreshAddressesxxx() todo: done when <address-list is created
await this.scanAddressWithAmount()
}
}

View file

@ -87,49 +87,7 @@ const tables = {
}
]
},
addressesTable: {
columns: [
{
name: 'expand',
align: 'left',
label: ''
},
{
name: 'address',
align: 'left',
label: 'Address',
field: 'address',
sortable: true
},
{
name: 'amount',
align: 'left',
label: 'Amount',
field: 'amount',
sortable: true
},
{
name: 'note',
align: 'left',
label: 'Note',
field: 'note',
sortable: true
},
{
name: 'wallet',
align: 'left',
label: 'Account',
field: 'wallet',
sortable: true
}
],
pagination: {
rowsPerPage: 0,
sortBy: 'amount',
descending: true
},
filter: ''
},
historyTable: {
columns: [
{
@ -194,20 +152,6 @@ const tables = {
}
const tableData = {
// walletAccounts: [], // todo: remove?
addresses: {
show: false,
data: [],
history: [],
selectedWallet: null,
note: '',
filterOptions: [
'Show Change Addresses',
'Show Gap Addresses',
'Only With Amount'
],
filterValues: []
},
utxos: {
data: [],
total: 0

View file

@ -12,14 +12,13 @@
:adminkey="g.user.wallets[0].adminkey"
:inkey="g.user.wallets[0].inkey"
:sats-denominated="config.data.sats_denominated"
:addresses="addresses.data"
:addresses="addresses"
@accounts-update="updateAccounts"
@new-receive-address="handleNewReceiveAddress"
@new-receive-address="showAddressDetails"
>
</wallet-list>
{% raw %}
<!-- :walletAccounts.sync="walletAccounts" -->
<!-- :walletAccounts="walletAccounts" -->
<q-card>
<div class="row q-pt-sm q-pb-sm items-center no-wrap q-mb-md">
<div class="col-3 q-pl-md">
@ -46,7 +45,7 @@
class="btn-full"
@click="goToPaymentView"
:disabled="scan.scanning == true"
>Make Payment</q-btn
>New Payment</q-btn
>
</div>
</div>
@ -70,210 +69,16 @@
</q-tabs>
<q-tab-panels v-model="tab">
<q-tab-panel name="addresses">
<div class="row items-center no-wrap q-mb-md">
<div class="col q-pr-lg">
<q-select
filled
clearable
dense
emit-value
v-model="addresses.selectedWallet"
:options="walletAccounts"
label="Wallet Account"
></q-select>
</div>
<div class="col q-pr-lg">
<q-select
filled
clearable
dense
emit-value
multiple
:options="addresses.filterOptions"
v-model="addresses.filterValues"
:options="walletAccounts"
label="Filter"
></q-select>
</div>
<div class="col-auto">
<q-input
borderless
dense
debounce="300"
v-model="addressesTable.filter"
placeholder="Search"
<address-list
ref="addressList"
:accounts="walletAccounts"
:mempool_endpoint="config.data.mempool_endpoint"
@update:addresses="handleAddressesUpdated"
@scan:address="scanAddress"
@show-address-details="showAddressDetails"
:inkey="g.user.wallets[0].inkey"
>
<template v-slot:append>
<q-icon name="search"></q-icon>
</template>
</q-input>
</div>
</div>
<q-table
style="height: 400px"
flat
dense
:data="getFilteredAddresses()"
row-key="id"
virtual-scroll
:columns="addressesTable.columns"
:pagination.sync="addressesTable.pagination"
:filter="addressesTable.filter"
>
<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="address" :props="props">
<div>
<a
style="color: unset"
:href="config.data.mempool_endpoint + '/address/' + props.row.address"
target="_blank"
>
{{props.row.address}}</a
>
<q-badge
v-if="props.row.branch_index === 1"
color="orange"
class="q-mr-md"
outline
>
change
</q-badge>
<q-btn
v-if="props.row.gapLimitExceeded"
color="yellow"
icon="warning"
title="Gap Limit Exceeded"
@click="props.row.expanded= !props.row.expanded"
outline
class="q-ml-md"
size="xs"
>
</q-btn>
</div>
</q-td>
<q-td
key="amount"
:props="props"
:class="props.row.amount > 0 ? 'text-green-13 text-weight-bold' : ''"
>
<div>{{satBtc(props.row.amount)}}</div>
</q-td>
<q-td key="note" :props="props" :class="">
<div>{{props.row.note}}</div>
</q-td>
<q-td key="wallet" :props="props" :class="">
<div>{{getWalletName(props.row.wallet)}}</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
dense
size="md"
icon="qr_code"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="openQrCodeDialog(props.row)"
>QR Code</q-btn
>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
dense
size="md"
icon="refresh"
color="grey"
@click="scanAddress(props.row)"
>Rescan</q-btn
>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
dense
size="md"
icon="history"
color="grey"
@click="searchInTab('history', props.row.address)"
>History</q-btn
>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
dense
size="md"
color="grey"
@click="searchInTab('utxos', props.row.address)"
>View Coins</q-btn
>
</div>
</div>
<div class="row items-center no-wrap q-mb-md">
<div class="col-2 q-pr-lg">Note:</div>
<div class="col-8 q-pr-lg">
<q-input
filled
dense
v-model.trim="props.row.note"
type="text"
label="Note"
></q-input>
</div>
<div class="col-2 q-pr-lg">
<q-btn
outline
color="grey"
@click="updateNoteForAddress(props.row, props.row.note)"
>Update</q-btn
>
</div>
</div>
<div
v-if="props.row.error"
class="row items-center no-wrap q-mb-md"
>
<div class="col-2 q-pr-lg"></div>
<div class="col-10 q-pr-lg">
<q-badge color="red">{{props.row.error}}</q-badge>
</div>
</div>
<div
v-if="props.row.gapLimitExceeded"
class="row items-center no-wrap q-mb-md"
>
<div class="col-2 q-pr-lg"></div>
<div class="col-10 q-pr-lg">
<q-badge color="yellow" text-color="black"
>Gap limit of 20 addresses exceeded. Other wallets
might not detect funds at this address.</q-badge
>
</div>
</div>
</q-td>
</q-tr>
</template>
</q-table>
</address-list>
</q-tab-panel>
<q-tab-panel name="history">
<div class="row items-center no-wrap q-mb-md">
@ -427,7 +232,7 @@
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<q-toggle
label="Make Payment"
label="New Payment"
color="secodary"
v-model="payment.show"
@input="initPaymentData()"
@ -1264,11 +1069,9 @@
</q-card-section>
</q-card>
</div>
<q-dialog v-model="addresses.show" position="top">
<q-card v-if="addresses.data" class="q-pa-lg lnbits__dialog-card">
{% raw %}
<q-dialog v-model="showAddress" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<h5 class="text-subtitle1 q-my-none">Address Details</h5>
<q-separator></q-separator><br />
@ -1296,7 +1099,7 @@
<q-input
filled
dense
v-model.trim="addresses.note"
v-model.trim="addressNote"
type="text"
label="Note"
></q-input>
@ -1313,7 +1116,7 @@
outline
v-close-popup
color="grey"
@click="updateNoteForAddress(currentAddress, addresses.note)"
@click="updateNoteForAddress(currentAddress, addressNote)"
class="q-ml-sm"
>Save Note</q-btn
>
@ -1473,5 +1276,6 @@
<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-list/wallet-list.js') }}"></script>
<script src="{{ url_for('watchonly_static', path='components/address-list/address-list.js') }}"></script>
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
{% endblock %}