diff --git a/lnbits/extensions/watchonly/README.md b/lnbits/extensions/watchonly/README.md
index be7bf351..7cdd7bf0 100644
--- a/lnbits/extensions/watchonly/README.md
+++ b/lnbits/extensions/watchonly/README.md
@@ -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
diff --git a/lnbits/extensions/watchonly/static/components/address-list/address-list.html b/lnbits/extensions/watchonly/static/components/address-list/address-list.html
new file mode 100644
index 00000000..9dcedf4d
--- /dev/null
+++ b/lnbits/extensions/watchonly/static/components/address-list/address-list.html
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{satBtc(props.row.amount)}}
+
+
+
+ {{props.row.note}}
+
+
+ {{getWalletName(props.row.wallet)}}
+
+
+
+
+
+
+
+
+ QR Code
+
+
+
+ Rescan
+
+
+ History
+
+
+ View Coins
+
+
+
+
+
Note:
+
+
+
+
+ Update
+
+
+
+
+
+
+
+ {{props.row.error}}
+
+
+
+
+
+ Gap limit of 20 addresses exceeded. Other wallets might not
+ detect funds at this address.
+
+
+
+
+
+
+
diff --git a/lnbits/extensions/watchonly/static/components/address-list/address-list.js b/lnbits/extensions/watchonly/static/components/address-list/address-list.js
new file mode 100644
index 00000000..6614c6e2
--- /dev/null
+++ b/lnbits/extensions/watchonly/static/components/address-list/address-list.js
@@ -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()
+ }
+ })
+}
diff --git a/lnbits/extensions/watchonly/static/js/index.js b/lnbits/extensions/watchonly/static/js/index.js
index be0dff4c..66ecd3c1 100644
--- a/lnbits/extensions/watchonly/static/js/index.js
+++ b/lnbits/extensions/watchonly/static/js/index.js
@@ -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
+
{% raw %}
-
-
@@ -46,7 +45,7 @@
class="btn-full"
@click="goToPaymentView"
:disabled="scan.scanning == true"
- >Make PaymentNew Payment
@@ -70,210 +69,16 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{satBtc(props.row.amount)}}
-
-
-
- {{props.row.note}}
-
-
- {{getWalletName(props.row.wallet)}}
-
-
-
-
-
-
-
- QR Code
-
-
- Rescan
-
-
- History
-
-
- View Coins
-
-
-
-
-
Note:
-
-
-
-
- Update
-
-
-
-
-
-
- {{props.row.error}}
-
-
-
-
-
- Gap limit of 20 addresses exceeded. Other wallets
- might not detect funds at this address.
-
-
-
-
-
-
+
@@ -427,7 +232,7 @@
-
-
-
- {% raw %}
-
+ {% raw %}
+
+
Address Details
@@ -1296,7 +1099,7 @@
@@ -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
@@ -1473,5 +1276,6 @@
+
{% endblock %}