From ce28db76c966cadf781cd08690dd2c0354f37804 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 2 Sep 2020 19:19:18 -0300 Subject: [PATCH] add a dialog with payment details for each payment. for outgoing payments this needs a preimage to be good, but we don't have it yet because we don't get it from backends. --- lnbits/core/static/js/wallet.js | 104 +++++++++--------- lnbits/core/templates/core/wallet.html | 81 ++++++++++++-- .../lnticket/templates/lnticket/display.html | 15 +-- lnbits/static/css/base.css | 9 ++ lnbits/static/js/base.js | 68 ++++++------ 5 files changed, 172 insertions(+), 105 deletions(-) diff --git a/lnbits/core/static/js/wallet.js b/lnbits/core/static/js/wallet.js index 67aa850..140ca47 100644 --- a/lnbits/core/static/js/wallet.js +++ b/lnbits/core/static/js/wallet.js @@ -14,10 +14,10 @@ function generateChart(canvas, payments) { } _.each( - payments.slice(0).sort(function(a, b) { + payments.slice(0).sort(function (a, b) { return a.time - b.time }), - function(tx) { + function (tx) { txs.push({ hour: Quasar.utils.date.formatDate(tx.date, 'YYYY-MM-DDTHH:00'), sat: tx.sat @@ -25,17 +25,17 @@ function generateChart(canvas, payments) { } ) - _.each(_.groupBy(txs, 'hour'), function(value, day) { + _.each(_.groupBy(txs, 'hour'), function (value, day) { var income = _.reduce( value, - function(memo, tx) { + function (memo, tx) { return tx.sat >= 0 ? memo + tx.sat : memo }, 0 ) var outcome = _.reduce( value, - function(memo, tx) { + function (memo, tx) { return tx.sat < 0 ? memo + Math.abs(tx.sat) : memo }, 0 @@ -67,20 +67,14 @@ function generateChart(canvas, payments) { type: 'bar', label: 'in', barPercentage: 0.75, - backgroundColor: window - .Color('rgb(76,175,80)') - .alpha(0.5) - .rgbString() // green + backgroundColor: window.Color('rgb(76,175,80)').alpha(0.5).rgbString() // green }, { data: data.outcome, type: 'bar', label: 'out', barPercentage: 0.75, - backgroundColor: window - .Color('rgb(233,30,99)') - .alpha(0.5) - .rgbString() // pink + backgroundColor: window.Color('rgb(233,30,99)').alpha(0.5).rgbString() // pink } ] }, @@ -121,7 +115,7 @@ function generateChart(canvas, payments) { new Vue({ el: '#vue', mixins: [windowMixin], - data: function() { + data: function () { return { user: LNbits.map.user(window.user), receive: { @@ -183,49 +177,49 @@ new Vue({ } }, computed: { - filteredPayments: function() { + filteredPayments: function () { var q = this.paymentsTable.filter if (!q || q === '') return this.payments return LNbits.utils.search(this.payments, q) }, - balance: function() { + balance: function () { if (this.payments.length) { return ( - _.pluck(this.payments, 'amount').reduce(function(a, b) { + _.pluck(this.payments, 'amount').reduce(function (a, b) { return a + b }, 0) / 1000 ) } return this.g.wallet.sat }, - fbalance: function() { + fbalance: function () { return LNbits.utils.formatSat(this.balance) }, - canPay: function() { + canPay: function () { if (!this.send.invoice) return false return this.send.invoice.sat <= this.balance }, - pendingPaymentsExist: function() { + pendingPaymentsExist: function () { return this.payments ? _.where(this.payments, {pending: 1}).length > 0 : false } }, methods: { - closeCamera: function() { + closeCamera: function () { this.sendCamera.show = false }, - showCamera: function() { + showCamera: function () { this.sendCamera.show = true }, - showChart: function() { + showChart: function () { this.paymentsChart.show = true - this.$nextTick(function() { + this.$nextTick(function () { generateChart(this.$refs.canvas, this.payments) }) }, - showReceiveDialog: function() { + showReceiveDialog: function () { this.receive = { show: true, status: 'pending', @@ -237,7 +231,7 @@ new Vue({ paymentChecker: null } }, - showSendDialog: function() { + showSendDialog: function () { this.send = { show: true, invoice: null, @@ -247,20 +241,20 @@ new Vue({ paymentChecker: null } }, - closeReceiveDialog: function() { + closeReceiveDialog: function () { var checker = this.receive.paymentChecker - setTimeout(function() { + setTimeout(function () { clearInterval(checker) }, 10000) }, - closeSendDialog: function() { + closeSendDialog: function () { this.sendCamera.show = false var checker = this.send.paymentChecker - setTimeout(function() { + setTimeout(function () { clearInterval(checker) }, 1000) }, - createInvoice: function() { + createInvoice: function () { var self = this this.receive.status = 'loading' LNbits.api @@ -269,14 +263,14 @@ new Vue({ this.receive.data.amount, this.receive.data.memo ) - .then(function(response) { + .then(function (response) { self.receive.status = 'success' self.receive.paymentReq = response.data.payment_request - self.receive.paymentChecker = setInterval(function() { + self.receive.paymentChecker = setInterval(function () { LNbits.api .getPayment(self.g.wallet, response.data.payment_hash) - .then(function(response) { + .then(function (response) { if (response.data.paid) { self.fetchPayments() self.receive.show = false @@ -285,17 +279,17 @@ new Vue({ }) }, 2000) }) - .catch(function(error) { + .catch(function (error) { LNbits.utils.notifyApiError(error) self.receive.status = 'pending' }) }, - decodeQR: function(res) { + decodeQR: function (res) { this.send.data.bolt11 = res this.decodeInvoice() this.sendCamera.show = false }, - decodeInvoice: function() { + decodeInvoice: function () { if (this.send.data.bolt11.startsWith('lightning:')) { this.send.data.bolt11 = this.send.data.bolt11.slice(10) } @@ -320,7 +314,7 @@ new Vue({ fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000) } - _.each(invoice.data.tags, function(tag) { + _.each(invoice.data.tags, function (tag) { if (_.isObject(tag) && _.has(tag, 'description')) { if (tag.description === 'payment_hash') { cleanInvoice.hash = tag.value @@ -341,7 +335,7 @@ new Vue({ this.send.invoice = Object.freeze(cleanInvoice) }, - payInvoice: function() { + payInvoice: function () { var self = this let dismissPaymentMsg = this.$q.notify({ @@ -352,11 +346,11 @@ new Vue({ LNbits.api .payInvoice(this.g.wallet, this.send.data.bolt11) - .then(function(response) { - self.send.paymentChecker = setInterval(function() { + .then(function (response) { + self.send.paymentChecker = setInterval(function () { LNbits.api .getPayment(self.g.wallet, response.data.payment_hash) - .then(function(res) { + .then(function (res) { if (res.data.paid) { self.send.show = false clearInterval(self.send.paymentChecker) @@ -366,58 +360,58 @@ new Vue({ }) }, 2000) }) - .catch(function(error) { + .catch(function (error) { dismissPaymentMsg() LNbits.utils.notifyApiError(error) }) }, - deleteWallet: function(walletId, user) { + deleteWallet: function (walletId, user) { LNbits.utils .confirmDialog('Are you sure you want to delete this wallet?') - .onOk(function() { + .onOk(function () { LNbits.href.deleteWallet(walletId, user) }) }, - fetchPayments: function(checkPending) { + fetchPayments: function (checkPending) { var self = this return LNbits.api .getPayments(this.g.wallet, checkPending) - .then(function(response) { + .then(function (response) { self.payments = response.data - .map(function(obj) { + .map(function (obj) { return LNbits.map.payment(obj) }) - .sort(function(a, b) { + .sort(function (a, b) { return b.time - a.time }) }) }, - checkPendingPayments: function() { + checkPendingPayments: function () { var dismissMsg = this.$q.notify({ timeout: 0, message: 'Checking pending transactions...', icon: null }) - this.fetchPayments(true).then(function() { + this.fetchPayments(true).then(function () { dismissMsg() }) }, - exportCSV: function() { + exportCSV: function () { LNbits.utils.exportCSV(this.paymentsTable.columns, this.payments) } }, watch: { - payments: function() { + payments: function () { EventHub.$emit('update-wallet-balance', [this.g.wallet.id, this.balance]) } }, - created: function() { + created: function () { this.fetchPayments() setTimeout(this.checkPendingPayments(), 1200) }, - mounted: function() { + mounted: function () { if ( this.$refs.disclaimer && !this.$q.localStorage.getItem('lnbits.disclaimerShown') diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index 766f2ea..2d47674 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -103,19 +103,28 @@ - + Pending - - #{{ props.row.tag }} - - + + #{{ props.row.tag }} + + {{ props.row.memo }} @@ -125,6 +134,64 @@ {{ props.row.fsat }} + + + + +
+ Copy invoice + Close +
+
+ +
+
+ + Payment Received +
+
+ + Payment Sent +
+
+ + Outgoing payment pending +
+ Payment Hash +
+ {{ props.row.payment_hash }} +
+
+
+
{% endraw %} diff --git a/lnbits/extensions/lnticket/templates/lnticket/display.html b/lnbits/extensions/lnticket/templates/lnticket/display.html index e1d1c90..4ab829e 100644 --- a/lnbits/extensions/lnticket/templates/lnticket/display.html +++ b/lnbits/extensions/lnticket/templates/lnticket/display.html @@ -137,15 +137,12 @@ Invoice: function () { var self = this axios - .post( - '/lnticket/api/v1/tickets/{{ form_id }}', - { - form: '{{ form_id }}', - name: self.formDialog.data.name, - email: self.formDialog.data.email, - ltext: self.formDialog.data.text, - } - ) + .post('/lnticket/api/v1/tickets/{{ form_id }}', { + form: '{{ form_id }}', + name: self.formDialog.data.name, + email: self.formDialog.data.email, + ltext: self.formDialog.data.text + }) .then(function (response) { self.paymentReq = response.data.payment_request self.paymentCheck = response.data.payment_hash diff --git a/lnbits/static/css/base.css b/lnbits/static/css/base.css index f5a9268..ffd5309 100644 --- a/lnbits/static/css/base.css +++ b/lnbits/static/css/base.css @@ -66,3 +66,12 @@ a.inherit { direction: ltr; -moz-font-feature-settings: 'liga'; -moz-osx-font-smoothing: grayscale; } + +.text-wrap { + word-wrap: break-word; + word-break: break-all; +} + +.mono { + font-family: monospace; +} diff --git a/lnbits/static/js/base.js b/lnbits/static/js/base.js index 9449181..c32141b 100644 --- a/lnbits/static/js/base.js +++ b/lnbits/static/js/base.js @@ -6,7 +6,7 @@ var EventHub = new Vue() var LNbits = { api: { - request: function(method, url, apiKey, data) { + request: function (method, url, apiKey, data) { return axios({ method: method, url: url, @@ -16,20 +16,20 @@ var LNbits = { data: data }) }, - createInvoice: function(wallet, amount, memo) { + createInvoice: function (wallet, amount, memo) { return this.request('post', '/api/v1/payments', wallet.inkey, { out: false, amount: amount, memo: memo }) }, - payInvoice: function(wallet, bolt11) { + payInvoice: function (wallet, bolt11) { return this.request('post', '/api/v1/payments', wallet.adminkey, { out: true, bolt11: bolt11 }) }, - getPayments: function(wallet, checkPending) { + getPayments: function (wallet, checkPending) { var query_param = checkPending ? '?check_pending' : '' return this.request( 'get', @@ -37,7 +37,7 @@ var LNbits = { wallet.inkey ) }, - getPayment: function(wallet, paymentHash) { + getPayment: function (wallet, paymentHash) { return this.request( 'get', '/api/v1/payments/' + paymentHash, @@ -46,16 +46,16 @@ var LNbits = { } }, href: { - createWallet: function(walletName, userId) { + createWallet: function (walletName, userId) { window.location.href = '/wallet?' + (userId ? 'usr=' + userId + '&' : '') + 'nme=' + walletName }, - deleteWallet: function(walletId, userId) { + deleteWallet: function (walletId, userId) { window.location.href = '/deletewallet?usr=' + userId + '&wal=' + walletId } }, map: { - extension: function(data) { + extension: function (data) { var obj = _.object( ['code', 'isValid', 'name', 'shortDescription', 'icon'], data @@ -63,17 +63,17 @@ var LNbits = { obj.url = ['/', obj.code, '/'].join('') return obj }, - user: function(data) { + user: function (data) { var obj = _.object(['id', 'email', 'extensions', 'wallets'], data) var mapWallet = this.wallet obj.wallets = obj.wallets - .map(function(obj) { + .map(function (obj) { return mapWallet(obj) }) - .sort(function(a, b) { + .sort(function (a, b) { return a.name.localeCompare(b.name) }) - obj.walletOptions = obj.wallets.map(function(obj) { + obj.walletOptions = obj.wallets.map(function (obj) { return { label: [obj.name, ' - ', obj.id].join(''), value: obj.id @@ -81,7 +81,7 @@ var LNbits = { }) return obj }, - wallet: function(data) { + wallet: function (data) { var obj = _.object( ['id', 'name', 'user', 'adminkey', 'inkey', 'balance'], data @@ -92,7 +92,7 @@ var LNbits = { obj.url = ['/wallet?usr=', obj.user, '&wal=', obj.id].join('') return obj }, - payment: function(data) { + payment: function (data) { var obj = _.object( [ 'checking_id', @@ -124,7 +124,7 @@ var LNbits = { } }, utils: { - confirmDialog: function(msg) { + confirmDialog: function (msg) { return Quasar.plugins.Dialog.create({ message: msg, ok: { @@ -137,16 +137,16 @@ var LNbits = { } }) }, - formatCurrency: function(value, currency) { + formatCurrency: function (value, currency) { return new Intl.NumberFormat(LOCALE, { style: 'currency', currency: currency }).format(value) }, - formatSat: function(value) { + formatSat: function (value) { return new Intl.NumberFormat(LOCALE).format(value) }, - notifyApiError: function(error) { + notifyApiError: function (error) { var types = { 400: 'warning', 401: 'warning', @@ -163,12 +163,12 @@ var LNbits = { icon: null }) }, - search: function(data, q, field, separator) { + search: function (data, q, field, separator) { try { var queries = q.toLowerCase().split(separator || ' ') - return data.filter(function(obj) { + return data.filter(function (obj) { var matches = 0 - _.each(queries, function(q) { + _.each(queries, function (q) { if (obj[field].indexOf(q) !== -1) matches++ }) return matches === queries.length @@ -177,8 +177,8 @@ var LNbits = { return data } }, - exportCSV: function(columns, data) { - var wrapCsvValue = function(val, formatFn) { + exportCSV: function (columns, data) { + var wrapCsvValue = function (val, formatFn) { var formatted = formatFn !== void 0 ? formatFn(val) : val formatted = @@ -190,14 +190,14 @@ var LNbits = { } var content = [ - columns.map(function(col) { + columns.map(function (col) { return wrapCsvValue(col.label) }) ] .concat( - data.map(function(row) { + data.map(function (row) { return columns - .map(function(col) { + .map(function (col) { return wrapCsvValue( typeof col.field === 'function' ? col.field(row) @@ -228,7 +228,7 @@ var LNbits = { } var windowMixin = { - data: function() { + data: function () { return { g: { visibleDrawer: false, @@ -240,13 +240,13 @@ var windowMixin = { } }, methods: { - toggleDarkMode: function() { + toggleDarkMode: function () { this.$q.dark.toggle() this.$q.localStorage.set('lnbits.darkMode', this.$q.dark.isActive) }, - copyText: function(text, message, position) { + copyText: function (text, message, position) { var notify = this.$q.notify - Quasar.utils.copyToClipboard(text).then(function() { + Quasar.utils.copyToClipboard(text).then(function () { notify({ message: message || 'Copied to clipboard!', position: position || 'bottom' @@ -254,7 +254,7 @@ var windowMixin = { }) } }, - created: function() { + created: function () { this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode')) if (window.user) { this.g.user = Object.freeze(LNbits.map.user(window.user)) @@ -266,10 +266,10 @@ var windowMixin = { var user = this.g.user this.g.extensions = Object.freeze( window.extensions - .map(function(data) { + .map(function (data) { return LNbits.map.extension(data) }) - .map(function(obj) { + .map(function (obj) { if (user) { obj.isEnabled = user.extensions.indexOf(obj.code) !== -1 } else { @@ -277,7 +277,7 @@ var windowMixin = { } return obj }) - .sort(function(a, b) { + .sort(function (a, b) { return a.name > b.name }) )