Merge branch 'better_fiat_tracking' into all_ui_elements

This commit is contained in:
arcbtc 2024-12-20 01:50:11 +00:00
commit 2e31d1fd91
6 changed files with 287 additions and 45 deletions

View File

@ -23,6 +23,7 @@ async def create_wallet(
user=user_id,
adminkey=uuid4().hex,
inkey=uuid4().hex,
currency="USD",
)
await (conn or db).insert("wallets", wallet)
return wallet

View File

@ -122,6 +122,160 @@
</div>
</div>
<q-card
:style="$q.screen.lt.md ? {
background: $q.screen.lt.md ? 'none !important': ''
, boxShadow: $q.screen.lt.md ? 'none !important': ''
, width: $q.screen.lt.md && mobileSimple ? '90% !important': ''
} : ''"
>
<q-card-section>
<div class="row">
<div class="col-1" style="max-width: 30px">
<q-btn
v-if="update.currency"
@click="swapBalancePriority"
style="height: 50px"
class="q-mt-lg"
flat
dense
icon="swap_vert"
></q-btn>
</div>
<div class="col-11">
<div
class="column"
:class="{
'q-pt-sm': update.currency,
'q-pt-lg': !update.currency
}"
v-if="!isPrioritySwapped"
style="height: 90px"
>
<div class="col-7">
<div class="row">
<div class="col-auto">
<div class="text-h3 q-my-none text-no-wrap">
<strong v-text="formattedBalance"></strong>
<small> {{LNBITS_DENOMINATION}}</small>
</div>
</div>
<div class="col-auto">
<lnbits-update-balance
v-if="$q.screen.lt.lg"
:wallet_id="this.g.wallet.id"
:callback="updateBalanceCallback"
:small_btn="true"
></lnbits-update-balance>
</div>
</div>
</div>
<div class="col-2">
<div v-if="update.currency">
<span
class="text-h5 text-italic"
v-text="formattedFiatAmount"
style="opacity: 0.75"
></span>
</div>
</div>
</div>
<div
class="column"
v-if="isPrioritySwapped"
:class="{
'q-pt-sm': update.currency,
'q-pt-lg': !update.currency
}"
style="height: 100px"
>
<div class="col-7">
<div class="row">
<div class="col-auto">
<div
v-if="update.currency"
class="text-h3 q-my-none text-no-wrap"
>
<strong v-text="formattedFiatAmount"></strong>
</div>
</div>
<div class="col-auto">
<lnbits-update-balance
v-if="$q.screen.lt.lg"
:wallet_id="this.g.wallet.id"
:callback="updateBalanceCallback"
:small_btn="true"
></lnbits-update-balance>
</div>
</div>
</div>
<div class="col-2">
<span
class="text-h5 text-italic"
style="opacity: 0.75"
v-text="formattedBalance + ' {{LNBITS_DENOMINATION}}'"
>
</span>
</div>
</div>
<div
class="absolute-right q-pa-md"
v-if="$q.screen.gt.md && update.currency"
>
<div class="text-bold text-italic">BTC Price</div>
<span
class="text-bold text-italic"
v-text="formattedExchange"
></span>
</div>
<q-btn
v-if="$q.screen.lt.md"
@click="mobileSimple = !mobileSimple"
color="primary"
class="q-ml-xl absolute-right"
dense
size="sm"
style="height: 20px; margin-top: 75px"
flat
:icon="mobileSimple ? 'unfold_more' : 'unfold_less'"
:label="mobileSimple ? $t('more') : $t('less')"
></q-btn>
</div>
</div>
</q-card-section>
<div class="row q-pb-md q-px-md q-col-gutter-md gt-sm">
<div class="col">
<q-btn
unelevated
color="primary"
class="q-mr-md"
@click="showParseDialog"
:label="$t('paste_request')"
></q-btn>
<q-btn
unelevated
color="primary"
class="q-mr-md"
@click="showReceiveDialog"
:label="$t('create_invoice')"
></q-btn>
<q-btn unelevated color="secondary" icon="qr_code_scanner">
<q-tooltip
><span v-text="$t('camera_tooltip')"></span
></q-tooltip>
</q-btn>
<lnbits-update-balance
v-if="$q.screen.gt.md"
:wallet_id="this.g.wallet.id"
:callback="updateBalanceCallback"
:small_btn="false"
></lnbits-update-balance>
</div>
</div>
</q-card>
<q-card
:style="
$q.screen.lt.md
@ -138,17 +292,14 @@
:update="updatePayments"
:wallet="this.g.wallet"
:mobile-simple="mobileSimple"
/>
></payment-list>
</q-card-section>
</q-card>
</div>
{% if HIDE_API %}
<div class="col-12 col-md-4 q-gutter-y-md">
{% else %}
<div
v-if="!mobileSimple || $q.screen.gt.sm"
class="col-12 col-md-5 q-gutter-y-md"
>
<div v-if="!mobileSimple" class="col-12 col-md-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-mt-none q-mb-sm">
@ -237,24 +388,27 @@
<q-card>
<q-card-section>
<div style="max-width: 360px">
<q-select
filled
dense
clearable
v-model="update.currency"
type="text"
:label="$t('currency')"
:options="receive.units.filter((u) => u !== 'sat')"
></q-select>
<div class="row">
<div class="col-auto">
<q-toggle
v-model="fiatTracking"
color="primary"
@update:model-value="handleFiatTracking"
></q-toggle>
</div>
<div class="col">
<q-select
filled
dense
v-model="update.currency"
type="text"
:label="!fiatTracking ? 'Disabled' : $t('currency')"
:options="receive.units.filter((u) => u !== 'sat')"
:disable="!fiatTracking"
></q-select>
</div>
</div>
</div>
<q-btn
:disable="!update.name.length"
unelevated
class="q-mt-sm"
color="primary"
:label="$t('update_currency')"
@click="updateWallet({ currency: update.currency || '' })"
></q-btn>
</q-card-section>
</q-card>
</q-expansion-item>

File diff suppressed because one or more lines are too long

View File

@ -484,7 +484,7 @@ window.app.component('lnbits-dynamic-chips', {
window.app.component('lnbits-update-balance', {
template: '#lnbits-update-balance',
mixins: [window.windowMixin],
props: ['wallet_id', 'credit-value'],
props: ['wallet_id', 'credit-value', 'small_btn'],
computed: {
denomination() {
return LNBITS_DENOMINATION

View File

@ -59,6 +59,12 @@ window.app = Vue.createApp({
adminkeyHidden: true,
hasNfc: false,
nfcReaderAbortController: null,
isPrioritySwapped: false,
fiatTracking: false,
formattedFiatAmount: 0,
exchangeRate: 0,
formattedExchange: null,
ignoreWatcher: true,
primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor'),
transactions: [],
transactionChart: null,
@ -73,14 +79,6 @@ window.app = Vue.createApp({
return LNbits.utils.formatSat(this.balance || this.g.wallet.sat)
}
},
formattedFiatBalance() {
if (this.fiatBalance) {
return LNbits.utils.formatCurrency(
this.fiatBalance.toFixed(2),
this.g.wallet.currency
)
}
},
canPay() {
if (!this.parse.invoice) return false
return this.parse.invoice.sat <= this.balance
@ -100,6 +98,17 @@ window.app = Vue.createApp({
}
},
methods: {
formatFiatAmount(amount, currency) {
this.update.currency = currency
this.formattedFiatAmount = LNbits.utils.formatCurrency(
amount.toFixed(2),
currency
)
this.formattedExchange = LNbits.utils.formatCurrency(
this.exchangeRate,
currency
)
},
computeCumulativeBalance(transactions) {
let balance = 0
return transactions.map(transaction => {
@ -596,7 +605,6 @@ window.app = Vue.createApp({
type: 'positive',
timeout: 3500
})
window.location.reload()
})
.catch(err => {
LNbits.utils.notifyApiError(err)
@ -620,15 +628,23 @@ window.app = Vue.createApp({
})
})
},
updateFiatBalance() {
if (!this.g.wallet.currency) return 0
updateFiatBalance(currency) {
// set rate from local storage to avoid clunky api calls
this.exchangeRate = this.$q.localStorage.getItem('lnbits.exchangeRate')
this.fiatBalance =
(this.exchangeRate / 100000000) * (this.balance || this.g.wallet.sat)
this.formatFiatAmount(this.fiatBalance, currency)
// make api call
LNbits.api
.request('POST', `/api/v1/conversion`, null, {
amount: this.balance || this.g.wallet.sat,
to: this.g.wallet.currency
})
.request('GET', `/api/v1/rate/` + currency, null)
.then(response => {
this.fiatBalance = response.data[this.g.wallet.currency]
this.fiatBalance =
(response.data.price / 100000000) *
(this.balance || this.g.wallet.sat)
this.exchangeRate = response.data.price.toFixed(2)
this.fiatTracking = true
this.formatFiatAmount(this.fiatBalance, currency)
this.$q.localStorage.set('lnbits.exchangeRate', this.exchangeRate)
})
.catch(e => console.error(e))
},
@ -760,6 +776,21 @@ window.app = Vue.createApp({
this.paymentsTable.loading = false
LNbits.utils.notifyApiError(err)
})
},
swapBalancePriority() {
this.isPrioritySwapped = !this.isPrioritySwapped
this.$q.localStorage.setItem(
'lnbits.isPrioritySwapped',
this.isPrioritySwapped
)
},
handleFiatTracking() {
if (this.fiatTracking === false) {
this.update.currency = ''
this.$q.localStorage.setItem('lnbits.isPrioritySwapped', false)
this.isPrioritySwapped = false
this.$q.localStorage.remove(`lnbits.exchangeRate`)
}
}
},
created() {
@ -773,23 +804,39 @@ window.app = Vue.createApp({
if (this.$q.screen.lt.md) {
this.mobileSimple = true
}
setTimeout(() => {
this.ignoreWatcher = false
}, 3000)
if (this.g.wallet.currency) {
this.fiatTracking = true
this.updateFiatBalance(this.g.wallet.currency)
}
this.update.name = this.g.wallet.name
this.update.currency = this.g.wallet.currency
this.receive.units = ['sat', ...window.currencies]
this.updateFiatBalance()
this.fetchPayments().then(() => {
this.initCharts()
})
},
watch: {
'$q.screen.gt.sm'(value) {
if (value == true) {
this.mobileSimple = false
}
},
updatePayments() {
this.updateFiatBalance()
this.fetchBalance()
},
'update.currency'(newValue) {
if (this.ignoreWatcher || this.update.currency == '') return
this.updateWallet({currency: newValue})
this.updateFiatBalance(newValue)
},
'$q.screen.gt.sm'(value) {
if (value == true) {
this.mobileSimple = false
}
}
},
},
mounted() {
// show disclaimer
@ -797,6 +844,15 @@ window.app = Vue.createApp({
this.disclaimerDialog.show = true
this.$q.localStorage.set('lnbits.disclaimerShown', true)
}
// check blanace priority
if (this.$q.localStorage.getItem('lnbits.isPrioritySwapped')) {
this.isPrioritySwapped = this.$q.localStorage.getItem(
'lnbits.isPrioritySwapped'
)
} else {
this.isPrioritySwapped = false
this.$q.localStorage.setItem('lnbits.isPrioritySwapped', false)
}
// listen to incoming payments
LNbits.events.onInvoicePaid(this.g.wallet, data => {
console.log('Payment received:', data.payment.payment_hash)

View File

@ -504,7 +504,38 @@
</template>
<template id="lnbits-update-balance">
<q-btn v-if="admin" :label="$t('credit_debit')" color="secondary" size="sm">
<q-btn
v-if="admin && small_btn"
flat
round
color="primary"
size="sm"
icon="add"
>
<q-popup-edit class="bg-accent text-white" v-slot="scope" v-model="credit">
<q-input
filled
:label="$t('credit_label', {denomination: denomination})"
v-model="scope.value"
dense
autofocus
@keyup.enter="scope.set"
>
<template v-slot:append>
<q-icon name="edit" />
</template>
</q-input>
</q-popup-edit>
<q-tooltip v-text="$t('credit_hint')"></q-tooltip>
</q-btn>
<q-btn
v-if="admin && !small_btn"
color="primary"
:label="$t('credit_debit')"
class="float-right q-mt-sm"
size="sm"
>
<q-popup-edit class="bg-accent text-white" v-slot="scope" v-model="credit">
<q-input
filled