Browse Source

refactor: `/wallet` tweaks

fee_issues
Eneko Illarramendi 5 years ago
parent
commit
649cc888ab
  1. 14
      lnbits/core/static/js/wallet.js
  2. 4
      lnbits/core/templates/core/index.html
  3. 54
      lnbits/core/templates/core/wallet.html
  4. 2
      lnbits/core/views/api.py
  5. 44
      lnbits/static/js/base.js
  6. 25
      lnbits/static/js/components.js
  7. 2
      lnbits/static/vendor/quasar@1.9.12/quasar.ie.polyfills.umd.min.js
  8. 1
      lnbits/static/vendor/quasar@1.9.12/quasar.min.css
  9. 806
      lnbits/static/vendor/quasar@1.9.12/quasar.umd.js
  10. 6
      lnbits/static/vendor/quasar@1.9.12/quasar.umd.min.js
  11. 1
      lnbits/static/vendor/quasar@1.9.7/quasar.min.css
  12. 6
      lnbits/static/vendor/quasar@1.9.7/quasar.umd.min.js
  13. 16
      lnbits/static/vendor/vuex@3.1.3/vuex.js
  14. 6
      lnbits/static/vendor/vuex@3.1.3/vuex.min.js
  15. 12
      lnbits/templates/base.html

14
lnbits/core/static/js/wallet.js

@ -158,12 +158,6 @@ new Vue({
return (this.payments) return (this.payments)
? _.where(this.payments, {pending: 1}).length > 0 ? _.where(this.payments, {pending: 1}).length > 0
: false; : false;
},
paymentsFiltered: function () {
return this.payments;
return this.payments.filter(function (obj) {
return obj.isPaid;
});
} }
}, },
methods: { methods: {
@ -325,6 +319,14 @@ new Vue({
this.fetchPayments(true).then(function () { this.fetchPayments(true).then(function () {
dismissMsg(); dismissMsg();
}); });
},
exportCSV: function () {
LNbits.utils.exportCSV(this.paymentsTable.columns, this.payments);
}
},
watch: {
'payments': function () {
EventHub.$emit('update-wallet-balance', [this.w.wallet.id, this.balance]);
} }
}, },
created: function () { created: function () {

4
lnbits/core/templates/core/index.html

@ -57,10 +57,10 @@
</q-card-section> </q-card-section>
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn flat <q-btn flat
color="deep-purple" color="grey"
type="a" href="https://github.com/arcbtc/lnbits" target="_blank" rel="noopener">View project in GitHub</q-btn> type="a" href="https://github.com/arcbtc/lnbits" target="_blank" rel="noopener">View project in GitHub</q-btn>
<q-btn flat <q-btn flat
color="deep-purple" color="grey"
type="a" href="https://paywall.link/to/f4e4e" target="_blank" rel="noopener">Donate</q-btn> type="a" href="https://paywall.link/to/f4e4e" target="_blank" rel="noopener">Donate</q-btn>
</q-card-actions> </q-card-actions>
</q-card> </q-card>

54
lnbits/core/templates/core/wallet.html

@ -54,7 +54,7 @@
<h5 class="text-subtitle1 q-my-none">Transactions</h5> <h5 class="text-subtitle1 q-my-none">Transactions</h5>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<q-btn flat color="grey" onclick="exportbut()">Export to CSV</q-btn> <q-btn flat color="grey" @click="exportCSV">Export to CSV</q-btn>
<!--<q-btn v-if="pendingPaymentsExist" dense flat round icon="update" color="grey" @click="checkPendingPayments"> <!--<q-btn v-if="pendingPaymentsExist" dense flat round icon="update" color="grey" @click="checkPendingPayments">
<q-tooltip>Check pending</q-tooltip> <q-tooltip>Check pending</q-tooltip>
</q-btn>--> </q-btn>-->
@ -64,7 +64,7 @@
</div> </div>
</div> </div>
<q-table dense flat <q-table dense flat
:data="paymentsFiltered" :data="payments"
row-key="payhash" row-key="payhash"
:columns="paymentsTable.columns" :columns="paymentsTable.columns"
:pagination.sync="paymentsTable.pagination"> :pagination.sync="paymentsTable.pagination">
@ -122,40 +122,44 @@
label="API info" label="API info"
:content-inset-level="0.5" :content-inset-level="0.5"
> >
<q-expansion-item group="api" expand-separator label="Create an invoice"> <q-expansion-item group="api" dense expand-separator label="Create an invoice (incoming)">
<q-card> <q-card>
<q-card-section> <q-card-section>
Generate an invoice:<br /><code>POST /api/v1/invoices</code <code><span class="text-light-green">POST</span> /api/v1/payments</code>
><br />Header <h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code <code
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i >{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i
>"}</code >"}</code
><br /> ><br />
Body <code>{"value": "200","memo": "beer"} </code><br /> <h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
Returns <code>{"out": false, "value": &lt;int&gt;, "memo": &lt;string&gt;}</code>
<code>{"pay_req": string,"pay_id": string} </code><br /> <h5 class="text-caption q-mt-sm q-mb-none">Returns 201 CREATED (application/json)</h5>
*payment will not register in the wallet until the "check <code>{"checking_id": &lt;string&gt;, "payment_request": &lt;string&gt;}</code>
invoice" endpoint is used<br /><br /> </q-card-section>
</q-card>
Check invoice:<br /> </q-expansion-item>
Check an invoice:<br /><code <q-expansion-item group="api" dense expand-separator label="Pay an invoice (outgoing)">
>GET /api/v1/invoice/*payment_hash*</code <q-card>
><br />Header <q-card-section>
<code <code><span class="text-light-green">POST</span> /api/v1/payments</code>
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i <h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
>"}</code <code>{"Grpc-Metadata-macaroon": "{{ wallet.adminkey }}"}</code>
><br /> <h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
<code>{"out": true, "bolt11": &lt;string&gt;}</code>
Returns <h5 class="text-caption q-mt-sm q-mb-none">Returns 201 CREATED (application/json)</h5>
<code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br /> <code>{"checking_id": &lt;string&gt;}</code>
*if using LNTXBOT return will hang until paid<br /><br />
</q-card-section> </q-card-section>
</q-card> </q-card>
</q-expansion-item> </q-expansion-item>
<q-expansion-item group="api" expand-separator label="Get an invoice"> <q-expansion-item group="api" dense expand-separator label="Check an invoice (incoming or outgoing)"
class="q-mb-md">
<q-card> <q-card>
<q-card-section> <q-card-section>
This whole wallet will be deleted, the funds will be <strong>UNRECOVERABLE</strong>. <code><span class="text-light-blue">GET</span> /api/v1/payments/&lt;checking_id&gt;</code>
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
<code>{"Grpc-Metadata-macaroon": "{{ wallet.inkey }}"}</code>
<h5 class="text-caption q-mt-sm q-mb-none">Returns 200 OK (application/json)</h5>
<code>{"paid": &lt;bool&gt;}</code>
</q-card-section> </q-card-section>
</q-card> </q-card>
</q-expansion-item> </q-expansion-item>

2
lnbits/core/views/api.py

@ -45,7 +45,7 @@ def api_payments_create_invoice():
return jsonify({"checking_id": checking_id, "payment_request": payment_request}), Status.CREATED return jsonify({"checking_id": checking_id, "payment_request": payment_request}), Status.CREATED
@api_check_wallet_macaroon(key_type="invoice") @api_check_wallet_macaroon(key_type="admin")
@api_validate_post_request(required_params=["bolt11"]) @api_validate_post_request(required_params=["bolt11"])
def api_payments_pay_invoice(): def api_payments_pay_invoice():
if not isinstance(g.data["bolt11"], str) or not g.data["bolt11"].strip(): if not isinstance(g.data["bolt11"], str) or not g.data["bolt11"].strip():

44
lnbits/static/js/base.js

@ -1,5 +1,7 @@
var LOCALE = 'en' var LOCALE = 'en'
var EventHub = new Vue();
var LNbits = { var LNbits = {
api: { api: {
request: function (method, url, macaroon, data) { request: function (method, url, macaroon, data) {
@ -20,7 +22,7 @@ var LNbits = {
}); });
}, },
payInvoice: function (wallet, bolt11) { payInvoice: function (wallet, bolt11) {
return this.request('post', '/api/v1/payments', wallet.inkey, { return this.request('post', '/api/v1/payments', wallet.adminkey, {
out: true, out: true,
bolt11: bolt11 bolt11: bolt11
}); });
@ -53,7 +55,7 @@ var LNbits = {
obj.wallets = obj.wallets.map(function (obj) { obj.wallets = obj.wallets.map(function (obj) {
return mapWallet(obj); return mapWallet(obj);
}).sort(function (a, b) { }).sort(function (a, b) {
return a.name > b.name; return a.name.localeCompare(b.name);
}); });
return obj; return obj;
}, },
@ -94,6 +96,44 @@ var LNbits = {
caption: [error.response.status, ' ', error.response.statusText].join('').toUpperCase() || null, caption: [error.response.status, ' ', error.response.statusText].join('').toUpperCase() || null,
icon: null icon: null
}); });
},
exportCSV: function (columns, data) {
var wrapCsvValue = function(val, formatFn) {
var formatted = formatFn !== void 0
? formatFn(val)
: val;
formatted = (formatted === void 0 || formatted === null)
? ''
: String(formatted);
formatted = formatted.split('"').join('""');
return `"${formatted}"`;
}
var content = [columns.map(function (col) {
return wrapCsvValue(col.label);
})].concat(data.map(function (row) {
return columns.map(function (col) {
return wrapCsvValue(
(typeof col.field === 'function')
? col.field(row)
: row[(col.field === void 0) ? col.name : col.field],
col.format
);
}).join(',');
})).join('\r\n');
var status = Quasar.utils.exportFile('table-export.csv', content, 'text/csv');
if (status !== true) {
Quasar.plugins.Notify.create({
message: 'Browser denied file download...',
color: 'negative',
icon: null
});
}
} }
} }
}; };

25
lnbits/static/js/components.js

@ -3,6 +3,7 @@ Vue.component('lnbits-wallet-list', {
return { return {
user: null, user: null,
activeWallet: null, activeWallet: null,
activeBalance: [],
showForm: false, showForm: false,
walletName: '' walletName: ''
} }
@ -10,7 +11,7 @@ Vue.component('lnbits-wallet-list', {
template: ` template: `
<q-list v-if="user && user.wallets.length" dense class="lnbits-drawer__q-list"> <q-list v-if="user && user.wallets.length" dense class="lnbits-drawer__q-list">
<q-item-label header>Wallets</q-item-label> <q-item-label header>Wallets</q-item-label>
<q-item v-for="wallet in user.wallets" :key="wallet.id" <q-item v-for="wallet in wallets" :key="wallet.id"
clickable clickable
:active="activeWallet && activeWallet.id == wallet.id" :active="activeWallet && activeWallet.id == wallet.id"
tag="a" :href="wallet.url"> tag="a" :href="wallet.url">
@ -25,7 +26,7 @@ Vue.component('lnbits-wallet-list', {
</q-item-section> </q-item-section>
<q-item-section> <q-item-section>
<q-item-label lines="1">{{ wallet.name }}</q-item-label> <q-item-label lines="1">{{ wallet.name }}</q-item-label>
<q-item-label caption>{{ wallet.fsat }} sat</q-item-label> <q-item-label caption>{{ wallet.live_fsat }} sat</q-item-label>
</q-item-section> </q-item-section>
<q-item-section side v-show="activeWallet && activeWallet.id == wallet.id"> <q-item-section side v-show="activeWallet && activeWallet.id == wallet.id">
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon> <q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
@ -41,7 +42,7 @@ Vue.component('lnbits-wallet-list', {
</q-item> </q-item>
<q-item v-if="showForm"> <q-item v-if="showForm">
<q-item-section> <q-item-section>
<q-form> <q-form @submit="createWallet">
<q-input filled dense v-model="walletName" label="Name wallet *"> <q-input filled dense v-model="walletName" label="Name wallet *">
<template v-slot:append> <template v-slot:append>
<q-btn round dense flat icon="send" size="sm" @click="createWallet" :disable="walletName == ''"></q-btn> <q-btn round dense flat icon="send" size="sm" @click="createWallet" :disable="walletName == ''"></q-btn>
@ -52,9 +53,23 @@ Vue.component('lnbits-wallet-list', {
</q-item> </q-item>
</q-list> </q-list>
`, `,
computed: {
wallets: function () {
var bal = this.activeBalance;
return this.user.wallets.map(function (obj) {
obj.live_fsat = (bal.length && bal[0] == obj.id)
? LNbits.utils.formatSat(bal[1])
: obj.fsat;
return obj;
});
}
},
methods: { methods: {
createWallet: function () { createWallet: function () {
LNbits.href.createWallet(this.walletName, this.user.id); LNbits.href.createWallet(this.walletName, this.user.id);
},
updateWalletBalance: function (payload) {
this.activeBalance = payload;
} }
}, },
created: function () { created: function () {
@ -64,6 +79,7 @@ Vue.component('lnbits-wallet-list', {
if (window.wallet) { if (window.wallet) {
this.activeWallet = LNbits.map.wallet(window.wallet); this.activeWallet = LNbits.map.wallet(window.wallet);
} }
EventHub.$on('update-wallet-balance', this.updateWalletBalance);
} }
}); });
@ -113,8 +129,9 @@ Vue.component('lnbits-extension-list', {
this.extensions = window.extensions.map(function (data) { this.extensions = window.extensions.map(function (data) {
return LNbits.map.extension(data); return LNbits.map.extension(data);
}).sort(function (a, b) { }).sort(function (a, b) {
return a.name > b.name; return a.name.localeCompare(b.name);
}); });
if (window.user) { if (window.user) {
this.user = LNbits.map.user(window.user); this.user = LNbits.map.user(window.user);
} }

2
lnbits/static/vendor/quasar@1.9.7/quasar.ie.polyfills.umd.min.js → lnbits/static/vendor/quasar@1.9.12/quasar.ie.polyfills.umd.min.js

@ -1,5 +1,5 @@
/*! /*!
* Quasar Framework v1.9.7 * Quasar Framework v1.9.12
* (c) 2015-present Razvan Stoenescu * (c) 2015-present Razvan Stoenescu
* Released under the MIT License. * Released under the MIT License.
*/ */

1
lnbits/static/vendor/quasar@1.9.12/quasar.min.css

File diff suppressed because one or more lines are too long

806
lnbits/static/vendor/quasar@1.9.7/quasar.umd.js → lnbits/static/vendor/quasar@1.9.12/quasar.umd.js

File diff suppressed because it is too large

6
lnbits/static/vendor/quasar@1.9.12/quasar.umd.min.js

File diff suppressed because one or more lines are too long

1
lnbits/static/vendor/quasar@1.9.7/quasar.min.css

File diff suppressed because one or more lines are too long

6
lnbits/static/vendor/quasar@1.9.7/quasar.umd.min.js

File diff suppressed because one or more lines are too long

16
lnbits/static/vendor/vuex@3.1.2/vuex.js → lnbits/static/vendor/vuex@3.1.3/vuex.js

@ -1,6 +1,6 @@
/** /**
* vuex v3.1.2 * vuex v3.1.3
* (c) 2019 Evan You * (c) 2020 Evan You
* @license MIT * @license MIT
*/ */
(function (global, factory) { (function (global, factory) {
@ -398,7 +398,10 @@
handler(payload); handler(payload);
}); });
}); });
this._subscribers.forEach(function (sub) { return sub(mutation, this$1.state); });
this._subscribers
.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
.forEach(function (sub) { return sub(mutation, this$1.state); });
if ( if (
options && options.silent options && options.silent
@ -429,6 +432,7 @@
try { try {
this._actionSubscribers this._actionSubscribers
.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
.filter(function (sub) { return sub.before; }) .filter(function (sub) { return sub.before; })
.forEach(function (sub) { return sub.before(action, this$1.state); }); .forEach(function (sub) { return sub.before(action, this$1.state); });
} catch (e) { } catch (e) {
@ -797,9 +801,7 @@
} }
function getNestedState (state, path) { function getNestedState (state, path) {
return path.length return path.reduce(function (state, key) { return state[key]; }, state)
? path.reduce(function (state, key) { return state[key]; }, state)
: state
} }
function unifyObjectStyle (type, payload, options) { function unifyObjectStyle (type, payload, options) {
@ -1042,7 +1044,7 @@
var index = { var index = {
Store: Store, Store: Store,
install: install, install: install,
version: '3.1.2', version: '3.1.3',
mapState: mapState, mapState: mapState,
mapMutations: mapMutations, mapMutations: mapMutations,
mapGetters: mapGetters, mapGetters: mapGetters,

6
lnbits/static/vendor/vuex@3.1.2/vuex.min.js → lnbits/static/vendor/vuex@3.1.3/vuex.min.js

File diff suppressed because one or more lines are too long

12
lnbits/templates/base.html

@ -3,7 +3,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Material+Icons" type="text/css"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Material+Icons" type="text/css">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='vendor/quasar@1.9.7/quasar.min.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='vendor/quasar@1.9.12/quasar.min.css') }}">
{% assets 'base_css' %} {% assets 'base_css' %}
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}"> <link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
{% endassets %} {% endassets %}
@ -62,15 +62,15 @@
{% if DEBUG %} {% if DEBUG %}
<script src="{{ url_for('static', filename='vendor/vue@2.6.11/vue.js') }}"></script> <script src="{{ url_for('static', filename='vendor/vue@2.6.11/vue.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/vue-router@3.1.6/vue-router.js') }}"></script> <script src="{{ url_for('static', filename='vendor/vue-router@3.1.6/vue-router.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/vuex@3.1.2/vuex.js') }}"></script> <script src="{{ url_for('static', filename='vendor/vuex@3.1.3/vuex.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/quasar@1.9.7/quasar.umd.js') }}"></script> <script src="{{ url_for('static', filename='vendor/quasar@1.9.12/quasar.umd.js') }}"></script>
{% else %} {% else %}
{% assets output='__bundle__/vue.js', {% assets output='__bundle__/vue.js',
'vendor/quasar@1.9.7/quasar.ie.polyfills.umd.min.js', 'vendor/quasar@1.9.12/quasar.ie.polyfills.umd.min.js',
'vendor/vue@2.6.11/vue.min.js', 'vendor/vue@2.6.11/vue.min.js',
'vendor/vue-router@3.1.6/vue-router.min.js', 'vendor/vue-router@3.1.6/vue-router.min.js',
'vendor/vuex@3.1.2/vuex.min.js', 'vendor/vuex@3.1.3/vuex.min.js',
'vendor/quasar@1.9.7/quasar.umd.min.js' %} 'vendor/quasar@1.9.12/quasar.umd.min.js' %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script> <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %} {% endassets %}
{% endif %} {% endif %}

Loading…
Cancel
Save