feat: move wallet.html to vue component. FINAL DynamicComponent pr (#3559)
This commit is contained in:
parent
0c6e8394c8
commit
33e2fc2ea8
19 changed files with 928 additions and 1128 deletions
|
|
@ -1,900 +0,0 @@
|
||||||
{% if not ajax %} {% extends "base.html" %} {% endif %}
|
|
||||||
<!---->
|
|
||||||
{% from "macros.jinja" import window_vars with context %}
|
|
||||||
<!---->
|
|
||||||
{% block scripts %} {{ window_vars(user, wallet) }}{% endblock %} {% block page
|
|
||||||
%}
|
|
||||||
<div class="row q-col-gutter-md" style="margin-bottom: 6rem">
|
|
||||||
{% if HIDE_API and AD_SPACE_ENABLED and AD_SPACE %}
|
|
||||||
<div class="col-12 col-md-8 q-gutter-y-md">
|
|
||||||
{% elif HIDE_API %}
|
|
||||||
<div class="col-12 q-gutter-y-md">
|
|
||||||
{% else %}
|
|
||||||
<div class="col-12 col-md-7 q-gutter-y-md wallet-wrapper">
|
|
||||||
{% endif %}
|
|
||||||
<q-card class="wallet-card">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row q-gutter-sm">
|
|
||||||
<div v-if="g.fiatTracking" class="col-auto">
|
|
||||||
<q-btn
|
|
||||||
@click="g.isFiatPriority = !g.isFiatPriority"
|
|
||||||
style="height: 50px"
|
|
||||||
class="q-mt-lg"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="swap_vert"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div
|
|
||||||
v-if="!g.isFiatPriority || !g.fiatTracking"
|
|
||||||
class="column"
|
|
||||||
:class="{
|
|
||||||
'q-pt-sm': g.fiatTracking,
|
|
||||||
'q-pt-lg': !g.fiatTracking
|
|
||||||
}"
|
|
||||||
style="height: 100px"
|
|
||||||
>
|
|
||||||
<div class="col-7">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<div class="text-h3 q-my-none full-width">
|
|
||||||
<strong
|
|
||||||
v-text="formatBalance(g.wallet.sat)"
|
|
||||||
class="text-no-wrap"
|
|
||||||
:style="{fontSize: 'clamp(0.75rem, 10vw, 3rem)', display: 'inline-block', maxWidth: '100%'}"
|
|
||||||
></strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<lnbits-update-balance
|
|
||||||
v-if="$q.screen.lt.lg"
|
|
||||||
:wallet_id="g.wallet.id"
|
|
||||||
:callback="updateBalanceCallback"
|
|
||||||
:small_btn="true"
|
|
||||||
></lnbits-update-balance>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<div v-if="g.fiatTracking">
|
|
||||||
<span
|
|
||||||
class="text-h5 text-italic"
|
|
||||||
v-text="formattedFiatAmount"
|
|
||||||
style="opacity: 0.75"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="column"
|
|
||||||
v-if="g.isFiatPriority && g.fiatTracking"
|
|
||||||
:class="{
|
|
||||||
'q-pt-sm': g.fiatTracking,
|
|
||||||
'q-pt-lg': !g.fiatTracking
|
|
||||||
}"
|
|
||||||
style="height: 100px"
|
|
||||||
>
|
|
||||||
<div class="col-7">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<div
|
|
||||||
v-if="g.fiatTracking"
|
|
||||||
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="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="formatBalance(g.wallet.sat)"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="absolute-right q-pa-md"
|
|
||||||
v-if="$q.screen.gt.md && g.fiatTracking && g.isSatsDenomination"
|
|
||||||
>
|
|
||||||
<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="g.mobileSimple = !g.mobileSimple"
|
|
||||||
color="primary"
|
|
||||||
class="q-ml-xl absolute-right"
|
|
||||||
dense
|
|
||||||
size="sm"
|
|
||||||
style="height: 20px; margin-top: 75px"
|
|
||||||
flat
|
|
||||||
:icon="g.mobileSimple ? 'unfold_more' : 'unfold_less'"
|
|
||||||
:label="g.mobileSimple ? $t('more') : $t('less')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!g.mobileSimple || !$q.screen.lt.md"
|
|
||||||
class="lnbits-wallet-buttons row q-gutter-md"
|
|
||||||
>
|
|
||||||
<div class="col">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
class="q-mr-md"
|
|
||||||
@click="showParseDialog"
|
|
||||||
:disable="!this.g.wallet.canSendPayments"
|
|
||||||
:label="$t('paste_request')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
class="q-mr-md"
|
|
||||||
@click="showReceiveDialog"
|
|
||||||
:disable="!this.g.wallet.canReceivePayments"
|
|
||||||
:label="$t('create_invoice')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="secondary"
|
|
||||||
icon="qr_code_scanner"
|
|
||||||
:disable="!this.g.wallet.canReceivePayments && !this.g.wallet.canSendPayments"
|
|
||||||
@click="showCamera"
|
|
||||||
>
|
|
||||||
<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-section>
|
|
||||||
</q-card>
|
|
||||||
|
|
||||||
<q-card class="wallet-card">
|
|
||||||
<q-card-section>
|
|
||||||
<lnbits-payment-list
|
|
||||||
:update="updatePayments"
|
|
||||||
:expand-details="expandDetails"
|
|
||||||
:payment-filter="paymentFilter"
|
|
||||||
></lnbits-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="!g.mobileSimple || !$q.screen.lt.md"
|
|
||||||
class="col-12 col-md-5 q-gutter-y-md"
|
|
||||||
>
|
|
||||||
<lnbits-wallet-extra
|
|
||||||
@update-wallet="updateWallet"
|
|
||||||
@send-lnurl="handleSendLnurl"
|
|
||||||
:chart-config="chartConfig"
|
|
||||||
></lnbits-wallet-extra>
|
|
||||||
{% endif %}
|
|
||||||
<q-card class="lnbits-wallet-ads" v-if="AD_SPACE_ENABLED">
|
|
||||||
<q-card-section class="text-subtitle1">
|
|
||||||
<span v-text="AD_SPACE_TITLE"></span>
|
|
||||||
<a :href="ad[0]" class="lnbits-ad" v-for="ad in g.ads">
|
|
||||||
<q-img
|
|
||||||
class="q-mb-xs"
|
|
||||||
v-if="$q.dark.isActive"
|
|
||||||
:src="ad[1]"
|
|
||||||
></q-img>
|
|
||||||
<q-img class="q-mb-xs" v-else :src="ad[2]"></q-img>
|
|
||||||
</a>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<lnbits-wallet-charts
|
|
||||||
:payment-filter="paymentFilter"
|
|
||||||
:chart-config="chartConfig"
|
|
||||||
></lnbits-wallet-charts>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-dialog
|
|
||||||
v-model="receive.show"
|
|
||||||
position="top"
|
|
||||||
@hide="onReceiveDialogHide"
|
|
||||||
>
|
|
||||||
<q-card
|
|
||||||
v-if="!receive.paymentReq"
|
|
||||||
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
|
||||||
>
|
|
||||||
<q-form @submit="createInvoice" class="q-gutter-md">
|
|
||||||
<p v-if="receive.lnurl" class="text-h6 text-center q-my-none">
|
|
||||||
<b v-text="receive.lnurl.domain"></b> is requesting an invoice:
|
|
||||||
</p>
|
|
||||||
{% if LNBITS_DENOMINATION != 'sats' %}
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="receive.data.amount"
|
|
||||||
:label="$t('amount') + '(' + denomination + ') *'"
|
|
||||||
mask="#.##"
|
|
||||||
fill-mask="0"
|
|
||||||
reverse-fill-mask
|
|
||||||
:min="receive.minMax[0]"
|
|
||||||
:max="receive.minMax[1]"
|
|
||||||
:readonly="receive.lnurl && receive.lnurl.fixed"
|
|
||||||
></q-input>
|
|
||||||
{% else %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-10">
|
|
||||||
<q-select
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="receive.unit"
|
|
||||||
type="text"
|
|
||||||
:label="$t('unit')"
|
|
||||||
:options="receive.units"
|
|
||||||
></q-select>
|
|
||||||
</div>
|
|
||||||
<div class="col-2">
|
|
||||||
<q-btn
|
|
||||||
v-if="g.fiatTracking"
|
|
||||||
@click="g.isFiatPriority = !g.isFiatPriority"
|
|
||||||
class="float-right"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="swap_vert"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-input
|
|
||||||
ref="setAmount"
|
|
||||||
filled
|
|
||||||
:pattern="receive.unit === 'sat' ? '\\d*' : '\\d*\\.?\\d*'"
|
|
||||||
inputmode="numeric"
|
|
||||||
dense
|
|
||||||
v-model.number="receive.data.amount"
|
|
||||||
:label="$t('amount') + ' (' + receive.unit + ') *'"
|
|
||||||
:min="receive.minMax[0]"
|
|
||||||
:max="receive.minMax[1]"
|
|
||||||
:readonly="receive.lnurl && receive.lnurl.fixed"
|
|
||||||
></q-input>
|
|
||||||
{% endif %}
|
|
||||||
<q-input
|
|
||||||
v-if="has_holdinvoice"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="receive.data.payment_hash"
|
|
||||||
:label="$t('hold_invoice_payment_hash')"
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
type="textarea"
|
|
||||||
rows="2"
|
|
||||||
v-model="receive.data.memo"
|
|
||||||
:label="$t('memo')"
|
|
||||||
>
|
|
||||||
<template v-if="receive.data.internalMemo === null" v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
name="add_comment"
|
|
||||||
@click.stop.prevent="receive.data.internalMemo = ''"
|
|
||||||
class="cursor-pointer"
|
|
||||||
></q-icon>
|
|
||||||
<q-tooltip>
|
|
||||||
<span v-text="$t('internal_memo')"></span>
|
|
||||||
</q-tooltip>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<q-input
|
|
||||||
v-if="receive.data.internalMemo !== null"
|
|
||||||
autogrow
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="receive.data.internalMemo"
|
|
||||||
class="q-mb-lg"
|
|
||||||
:label="$t('internal_memo')"
|
|
||||||
:hint="$t('internal_memo_hint_receive')"
|
|
||||||
:rules="[ val => !val || val.length <= 512 || 'Please use maximum 512 characters' ]"
|
|
||||||
><template v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
name="cancel"
|
|
||||||
@click.stop.prevent="receive.data.internalMemo = null"
|
|
||||||
class="cursor-pointer"
|
|
||||||
/> </template
|
|
||||||
></q-input>
|
|
||||||
<div v-if="g.user.fiat_providers?.length" class="q-mt-md">
|
|
||||||
<q-list bordered dense class="rounded-borders">
|
|
||||||
<q-item-label dense header>
|
|
||||||
<span v-text="$t('select_payment_provider')"></span>
|
|
||||||
</q-item-label>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-item
|
|
||||||
:active="!receive.fiatProvider"
|
|
||||||
@click="receive.fiatProvider = ''"
|
|
||||||
active-class="bg-teal-1 text-grey-8 text-weight-bold"
|
|
||||||
clickable
|
|
||||||
v-ripple
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-avatar square>
|
|
||||||
<img src="/static/images/logos/lnbits.png" />
|
|
||||||
</q-avatar>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<span
|
|
||||||
v-text="$t('pay_with', {provider: 'Lightning Network'})"
|
|
||||||
></span>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-item
|
|
||||||
:active="receive.fiatProvider === 'stripe'"
|
|
||||||
@click="receive.fiatProvider = 'stripe'"
|
|
||||||
active-class="bg-teal-1 text-grey-8 text-weight-bold"
|
|
||||||
clickable
|
|
||||||
v-ripple
|
|
||||||
>
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-avatar>
|
|
||||||
<img src="/static/images/stripe_logo.ico" />
|
|
||||||
</q-avatar>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<span v-text="$t('pay_with', {provider: 'Stripe'})"></span>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="receive.status == 'pending'" class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
:disable="receive.data.amount == null || receive.data.amount <= 0"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
v-if="receive.lnurl"
|
|
||||||
v-text="`${$t('withdraw_from')} ${receive.lnurl.domain}`"
|
|
||||||
></span>
|
|
||||||
<span v-else v-text="$t('create_invoice')"></span>
|
|
||||||
</q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<q-spinner-bars
|
|
||||||
v-if="receive.status == 'loading'"
|
|
||||||
color="primary"
|
|
||||||
size="2.55em"
|
|
||||||
></q-spinner-bars>
|
|
||||||
</q-form>
|
|
||||||
</q-card>
|
|
||||||
<q-card
|
|
||||||
v-else-if="receive.paymentReq && receive.lnurl == null"
|
|
||||||
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
|
||||||
>
|
|
||||||
<lnbits-qrcode
|
|
||||||
v-if="receive.fiatPaymentReq"
|
|
||||||
:show-buttons="false"
|
|
||||||
:href="receive.fiatPaymentReq"
|
|
||||||
:value="receive.fiatPaymentReq"
|
|
||||||
>
|
|
||||||
</lnbits-qrcode>
|
|
||||||
<lnbits-qrcode
|
|
||||||
v-else
|
|
||||||
:href="'lightning:' + receive.paymentReq"
|
|
||||||
:value="'lightning:' + receive.paymentReq"
|
|
||||||
>
|
|
||||||
</lnbits-qrcode>
|
|
||||||
<div class="text-center">
|
|
||||||
<h3 class="q-my-md">
|
|
||||||
<span v-text="formattedAmount"></span>
|
|
||||||
</h3>
|
|
||||||
<h5 v-if="receive.unit != 'sat'" class="q-mt-none q-mb-sm">
|
|
||||||
<span v-text="formattedSatAmount"></span>
|
|
||||||
</h5>
|
|
||||||
<div v-if="!receive.fiatPaymentReq">
|
|
||||||
<q-chip v-if="hasNfc" outline square color="positive">
|
|
||||||
<q-avatar
|
|
||||||
icon="nfc"
|
|
||||||
color="positive"
|
|
||||||
text-color="white"
|
|
||||||
></q-avatar>
|
|
||||||
<span v-text="$t('nfc_supported')"></span>
|
|
||||||
</q-chip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('close')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="parse.show" @hide="closeParseDialog" position="top">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<div v-if="parse.invoice">
|
|
||||||
<div class="column content-center text-center q-mb-md">
|
|
||||||
<div v-if="!g.isFiatPriority">
|
|
||||||
<h4 class="q-my-none text-bold">
|
|
||||||
<span v-text="formatBalance(parse.invoice.sat)"></span>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<h4
|
|
||||||
class="q-my-none text-bold"
|
|
||||||
v-text="parse.invoice.fiatAmount"
|
|
||||||
></h4>
|
|
||||||
</div>
|
|
||||||
<div class="q-my-md absolute">
|
|
||||||
<q-btn
|
|
||||||
v-if="g.fiatTracking"
|
|
||||||
@click="g.isFiatPriority = !g.isFiatPriority"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
icon="swap_vert"
|
|
||||||
color="primary"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<div v-if="g.fiatTracking">
|
|
||||||
<div v-if="g.isFiatPriority">
|
|
||||||
<h5 class="q-my-none text-bold">
|
|
||||||
<span v-text="formatBalance(parse.invoice.sat)"></span>
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div v-else style="opacity: 0.75">
|
|
||||||
<div class="text-h5 text-italic">
|
|
||||||
<span
|
|
||||||
v-text="parse.invoice.fiatAmount"
|
|
||||||
style="opacity: 0.75"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<h6 class="text-center" v-text="parse.invoice.description"></h6>
|
|
||||||
<q-input
|
|
||||||
autogrow
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="parse.data.internalMemo"
|
|
||||||
:label="$t('internal_memo')"
|
|
||||||
:hint="$t('internal_memo_hint_pay')"
|
|
||||||
class="q-mb-lg"
|
|
||||||
:rules="[ val => !val || val.length <= 512 || 'Please use maximum 512 characters' ]"
|
|
||||||
><template v-if="parse.data.internalMemo" v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
name="cancel"
|
|
||||||
@click.stop.prevent="parse.data.internalMemo = null"
|
|
||||||
class="cursor-pointer" /></template
|
|
||||||
></q-input>
|
|
||||||
<q-list separator bordered dense class="q-mb-md">
|
|
||||||
<q-expansion-item expand-separator icon="info" label="Details">
|
|
||||||
<q-list separator>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label v-text="$t('created')"></q-item-label>
|
|
||||||
<q-item-label
|
|
||||||
caption
|
|
||||||
v-text="parse.invoice.createdDate"
|
|
||||||
></q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
|
|
||||||
<q-item-section side top>
|
|
||||||
<q-item-label
|
|
||||||
caption
|
|
||||||
v-text="parse.invoice.createdDateFrom"
|
|
||||||
></q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label v-text="$t('expire_date')"></q-item-label>
|
|
||||||
<q-item-label
|
|
||||||
caption
|
|
||||||
v-text="parse.invoice.expireDate"
|
|
||||||
></q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section side top>
|
|
||||||
<q-item-label
|
|
||||||
caption
|
|
||||||
v-text="parse.invoice.expireDateFrom"
|
|
||||||
></q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label v-text="$t('payment_hash')"></q-item-label>
|
|
||||||
<q-item-label
|
|
||||||
caption
|
|
||||||
v-text="`${parse.invoice.hash.slice(0, 12)}...${parse.invoice.hash.slice(-12)}`"
|
|
||||||
></q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section side>
|
|
||||||
<q-item-label>
|
|
||||||
<q-icon
|
|
||||||
name="content_copy"
|
|
||||||
@click="copyText(parse.invoice.hash)"
|
|
||||||
size="1em"
|
|
||||||
color="grey"
|
|
||||||
class="cursor-pointer"
|
|
||||||
/>
|
|
||||||
</q-item-label>
|
|
||||||
<q-tooltip>
|
|
||||||
<span v-text="parse.invoice.hash"></span>
|
|
||||||
</q-tooltip>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label v-text="$t('Invoice')"></q-item-label>
|
|
||||||
<q-item-label
|
|
||||||
caption
|
|
||||||
v-text="`${parse.invoice.bolt11.slice(0, 12)}...${parse.invoice.bolt11.slice(-12)}`"
|
|
||||||
></q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section side>
|
|
||||||
<q-item-label>
|
|
||||||
<q-icon
|
|
||||||
name="content_copy"
|
|
||||||
@click="copyText(parse.invoice.bolt11)"
|
|
||||||
size="1em"
|
|
||||||
color="grey"
|
|
||||||
class="cursor-pointer"
|
|
||||||
/>
|
|
||||||
</q-item-label>
|
|
||||||
<q-tooltip>
|
|
||||||
<span v-text="parse.invoice.bolt11"></span>
|
|
||||||
</q-tooltip>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-expansion-item>
|
|
||||||
</q-list>
|
|
||||||
<div v-if="canPay" class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
@click="payInvoice"
|
|
||||||
:label="$t('pay')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
<div v-else class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
:label="$t('not_enough_funds')"
|
|
||||||
unelevated
|
|
||||||
disabled
|
|
||||||
color="yellow"
|
|
||||||
text-color="black"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="parse.lnurlauth">
|
|
||||||
<q-form @submit="authLnurl" class="q-gutter-md">
|
|
||||||
<p class="q-my-none text-h6">
|
|
||||||
Authenticate with <b v-text="parse.lnurlauth.domain"></b>?
|
|
||||||
</p>
|
|
||||||
<q-separator class="q-my-sm"></q-separator>
|
|
||||||
<p>
|
|
||||||
For every website and for every LNbits wallet, a new keypair
|
|
||||||
will be deterministically generated so your identity can't be
|
|
||||||
tied to your LNbits wallet or linked across websites. No other
|
|
||||||
data will be shared with
|
|
||||||
<span v-text="parse.lnurlauth.domain"></span>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Your public key for <b v-text="parse.lnurlauth.domain"></b> is:
|
|
||||||
</p>
|
|
||||||
<p class="q-mx-xl">
|
|
||||||
<code class="text-wrap" v-text="parse.lnurlauth.pubkey"></code>
|
|
||||||
</p>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
type="submit"
|
|
||||||
:label="$t('login')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
:label="$t('cancel')"
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="parse.lnurlpay">
|
|
||||||
<q-form @submit="payLnurl" class="q-gutter-md">
|
|
||||||
<p v-if="parse.lnurlpay.fixed" class="q-my-none text-h6">
|
|
||||||
<b v-text="parse.lnurlpay.domain"></b> is requesting
|
|
||||||
<span
|
|
||||||
v-text="msatoshiFormat(parse.lnurlpay.maxSendable)"
|
|
||||||
></span>
|
|
||||||
<span v-text="denomination"></span>
|
|
||||||
<span v-if="parse.lnurlpay.commentAllowed > 0">
|
|
||||||
<br />
|
|
||||||
and a
|
|
||||||
<span v-text="parse.lnurlpay.commentAllowed"></span>-char
|
|
||||||
comment
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p v-else class="q-my-none text-h6 text-center">
|
|
||||||
<b
|
|
||||||
v-text="parse.lnurlpay.targetUser || parse.lnurlpay.domain"
|
|
||||||
></b>
|
|
||||||
is requesting <br />
|
|
||||||
between
|
|
||||||
<b v-text="msatoshiFormat(parse.lnurlpay.minSendable)"></b> and
|
|
||||||
<b v-text="msatoshiFormat(parse.lnurlpay.maxSendable)"></b>
|
|
||||||
<span v-text="denomination"></span>
|
|
||||||
<span v-if="parse.lnurlpay.commentAllowed > 0">
|
|
||||||
<br />
|
|
||||||
and a
|
|
||||||
<span v-text="parse.lnurlpay.commentAllowed"></span>-char
|
|
||||||
comment
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<q-separator class="q-my-sm"></q-separator>
|
|
||||||
<div class="row">
|
|
||||||
<p
|
|
||||||
class="col text-justify text-italic"
|
|
||||||
v-text="parse.lnurlpay.description"
|
|
||||||
></p>
|
|
||||||
<p class="col-4 q-pl-md" v-if="parse.lnurlpay.image">
|
|
||||||
<q-img :src="parse.lnurlpay.image" />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col q-mb-lg">
|
|
||||||
<q-select
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-if="!parse.lnurlpay.fixed"
|
|
||||||
v-model="parse.data.unit"
|
|
||||||
type="text"
|
|
||||||
:label="$t('unit')"
|
|
||||||
:options="receive.units"
|
|
||||||
></q-select>
|
|
||||||
<br />
|
|
||||||
<q-input
|
|
||||||
ref="setAmount"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.number="parse.data.amount"
|
|
||||||
:label="$t('amount') + ' (' + parse.data.unit + ') *'"
|
|
||||||
:mask="parse.data.unit == 'sat' ? '#' : ''"
|
|
||||||
:step="parse.data.unit == 'sat' ? '1': '0.01'"
|
|
||||||
fill-mask="0"
|
|
||||||
reverse-fill-mask
|
|
||||||
:min="parse.lnurlpay.minSendable / 1000"
|
|
||||||
:max="parse.lnurlpay.maxSendable / 1000"
|
|
||||||
:readonly="parse.lnurlpay && parse.lnurlpay.fixed"
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="col-8 q-pl-md"
|
|
||||||
v-if="parse.lnurlpay.commentAllowed > 0"
|
|
||||||
>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="parse.data.comment"
|
|
||||||
:type="parse.lnurlpay.commentAllowed > 512 ? 'textarea' : 'text'"
|
|
||||||
label="Comment (optional)"
|
|
||||||
:maxlength="parse.lnurlpay.commentAllowed"
|
|
||||||
><template
|
|
||||||
v-if="parse.data.internalMemo === null"
|
|
||||||
v-slot:append
|
|
||||||
>
|
|
||||||
<q-icon
|
|
||||||
name="add_comment"
|
|
||||||
@click.stop.prevent="parse.data.internalMemo = ''"
|
|
||||||
class="cursor-pointer"
|
|
||||||
></q-icon>
|
|
||||||
<q-tooltip>
|
|
||||||
<span v-text="$t('internal_memo')"></span>
|
|
||||||
</q-tooltip> </template
|
|
||||||
></q-input>
|
|
||||||
<br />
|
|
||||||
<q-input
|
|
||||||
v-if="parse.data.internalMemo !== null"
|
|
||||||
autogrow
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="parse.data.internalMemo"
|
|
||||||
:label="$t('internal_memo')"
|
|
||||||
:hint="$t('internal_memo_hint_pay')"
|
|
||||||
class=""
|
|
||||||
:rules="[ val => !val || val.length <= 512 || 'Please use maximum 512 characters' ]"
|
|
||||||
><template v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
name="cancel"
|
|
||||||
@click.stop.prevent="parse.data.internalMemo = null"
|
|
||||||
class="cursor-pointer"
|
|
||||||
/> </template
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn unelevated color="primary" type="submit">Send</q-btn>
|
|
||||||
<q-btn
|
|
||||||
:label="$t('cancel')"
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<q-form
|
|
||||||
v-if="!parse.camera.show"
|
|
||||||
@submit="decodeRequest"
|
|
||||||
class="q-gutter-md"
|
|
||||||
>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="parse.data.request"
|
|
||||||
type="textarea"
|
|
||||||
:label="$t('paste_invoice_label')"
|
|
||||||
ref="textArea"
|
|
||||||
>
|
|
||||||
</q-input>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
:disable="parse.data.request == ''"
|
|
||||||
type="submit"
|
|
||||||
:label="$t('read')"
|
|
||||||
></q-btn>
|
|
||||||
<q-icon
|
|
||||||
name="content_paste"
|
|
||||||
color="grey"
|
|
||||||
class="q-mt-xs q-ml-sm q-mr-auto"
|
|
||||||
v-if="parse.copy.show"
|
|
||||||
@click="pasteToTextArea"
|
|
||||||
>
|
|
||||||
<q-tooltip>
|
|
||||||
<span v-text="$t('paste_from_clipboard')"></span>
|
|
||||||
</q-tooltip>
|
|
||||||
</q-icon>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-form>
|
|
||||||
<div v-else>
|
|
||||||
<q-responsive :ratio="1">
|
|
||||||
<qrcode-stream
|
|
||||||
@detect="decodeQR"
|
|
||||||
@camera-on="onInitQR"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode-stream>
|
|
||||||
</q-responsive>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
:label="$t('cancel')"
|
|
||||||
@click="closeCamera"
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
>
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-dialog v-model="parse.camera.show" position="top">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl">
|
|
||||||
<div class="text-center q-mb-lg">
|
|
||||||
<qrcode-stream
|
|
||||||
@detect="decodeQR"
|
|
||||||
@camera-on="onInitQR"
|
|
||||||
class="rounded-borders"
|
|
||||||
></qrcode-stream>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
@click="closeCamera"
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('cancel')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
<div
|
|
||||||
class="lt-md fixed-bottom left-0 right-0 bg-primary text-white shadow-2 z-top"
|
|
||||||
>
|
|
||||||
<q-tabs
|
|
||||||
active-class="px-0"
|
|
||||||
indicator-color="transparent"
|
|
||||||
align="justify"
|
|
||||||
>
|
|
||||||
<q-tab
|
|
||||||
icon="file_download"
|
|
||||||
@click="showReceiveDialog"
|
|
||||||
:label="$t('receive')"
|
|
||||||
>
|
|
||||||
</q-tab>
|
|
||||||
|
|
||||||
<q-tab
|
|
||||||
@click="showParseDialog"
|
|
||||||
icon="file_upload"
|
|
||||||
:label="$t('send')"
|
|
||||||
>
|
|
||||||
</q-tab>
|
|
||||||
</q-tabs>
|
|
||||||
<q-btn
|
|
||||||
round
|
|
||||||
size="35px"
|
|
||||||
unelevated
|
|
||||||
icon="qr_code_scanner"
|
|
||||||
@click="showCamera"
|
|
||||||
class="text-white bg-primary z-top vertical-bottom absolute-center absolute"
|
|
||||||
>
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Annotated
|
|
||||||
from urllib.parse import urlencode, urlparse
|
from urllib.parse import urlencode, urlparse
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi import Cookie, Depends, Query, Request
|
from fastapi import Depends, Request
|
||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
from lnurl import url_decode
|
from lnurl import url_decode
|
||||||
from pydantic.types import UUID4
|
|
||||||
|
|
||||||
from lnbits.core.helpers import to_valid_user_id
|
from lnbits.core.helpers import to_valid_user_id
|
||||||
from lnbits.core.models import User
|
from lnbits.core.models import User
|
||||||
|
|
@ -25,12 +23,7 @@ from lnbits.decorators import (
|
||||||
from lnbits.helpers import check_callback_url, template_renderer
|
from lnbits.helpers import check_callback_url, template_renderer
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
|
|
||||||
from ...utils.exchange_rates import allowed_currencies
|
from ..crud import get_user
|
||||||
from ..crud import (
|
|
||||||
create_wallet,
|
|
||||||
get_user,
|
|
||||||
get_wallet,
|
|
||||||
)
|
|
||||||
|
|
||||||
generic_router = APIRouter(
|
generic_router = APIRouter(
|
||||||
tags=["Core NON-API Website Routes"], include_in_schema=False
|
tags=["Core NON-API Website Routes"], include_in_schema=False
|
||||||
|
|
@ -42,54 +35,6 @@ async def favicon():
|
||||||
return RedirectResponse(settings.lnbits_qr_logo)
|
return RedirectResponse(settings.lnbits_qr_logo)
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get(
|
|
||||||
"/wallet",
|
|
||||||
response_class=HTMLResponse,
|
|
||||||
description="show wallet page",
|
|
||||||
)
|
|
||||||
async def get_user_wallet(
|
|
||||||
request: Request,
|
|
||||||
lnbits_last_active_wallet: Annotated[str | None, Cookie()] = None,
|
|
||||||
user: User = Depends(check_user_exists),
|
|
||||||
wal: UUID4 | None = Query(None),
|
|
||||||
):
|
|
||||||
if wal:
|
|
||||||
wallet = await get_wallet(wal.hex)
|
|
||||||
elif len(user.wallets) == 0:
|
|
||||||
wallet = await create_wallet(user_id=user.id)
|
|
||||||
user.wallets.append(wallet)
|
|
||||||
elif lnbits_last_active_wallet and user.get_wallet(lnbits_last_active_wallet):
|
|
||||||
wallet = await get_wallet(lnbits_last_active_wallet)
|
|
||||||
else:
|
|
||||||
wallet = user.wallets[0]
|
|
||||||
|
|
||||||
if not wallet or wallet.deleted:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.NOT_FOUND,
|
|
||||||
detail="Wallet not found",
|
|
||||||
)
|
|
||||||
if wallet.user != user.id:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.FORBIDDEN,
|
|
||||||
detail="Not your wallet.",
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
"user": user.json(),
|
|
||||||
"wallet": wallet.json(),
|
|
||||||
"wallet_name": wallet.name,
|
|
||||||
"currencies": allowed_currencies(),
|
|
||||||
"service_fee": settings.lnbits_service_fee,
|
|
||||||
"service_fee_max": settings.lnbits_service_fee_max,
|
|
||||||
"web_manifest": f"/manifest/{user.id}.webmanifest",
|
|
||||||
}
|
|
||||||
|
|
||||||
return template_renderer().TemplateResponse(
|
|
||||||
request,
|
|
||||||
"core/wallet.html",
|
|
||||||
{**context, "ajax": _is_ajax_request(request)},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/robots.txt", response_class=HTMLResponse)
|
@generic_router.get("/robots.txt", response_class=HTMLResponse)
|
||||||
async def robots():
|
async def robots():
|
||||||
data = "User-agent: *\nDisallow: /"
|
data = "User-agent: *\nDisallow: /"
|
||||||
|
|
@ -251,6 +196,7 @@ admin_ui_checks = [Depends(check_admin), Depends(check_admin_ui)]
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/payments")
|
@generic_router.get("/payments")
|
||||||
|
@generic_router.get("/wallet")
|
||||||
@generic_router.get("/wallets")
|
@generic_router.get("/wallets")
|
||||||
@generic_router.get("/account")
|
@generic_router.get("/account")
|
||||||
@generic_router.get("/extensions")
|
@generic_router.get("/extensions")
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from pydantic.schema import field_schema
|
||||||
from lnbits.jinja2_templating import Jinja2Templates
|
from lnbits.jinja2_templating import Jinja2Templates
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
from lnbits.utils.crypto import AESCipher
|
from lnbits.utils.crypto import AESCipher
|
||||||
|
from lnbits.utils.exchange_rates import currencies
|
||||||
|
|
||||||
from .db import FilterModel
|
from .db import FilterModel
|
||||||
|
|
||||||
|
|
@ -109,6 +110,8 @@ def template_renderer(additional_folders: list | None = None) -> Jinja2Templates
|
||||||
"LNBITS_NOSTR_CONFIGURED": settings.is_nostr_notifications_configured(),
|
"LNBITS_NOSTR_CONFIGURED": settings.is_nostr_notifications_configured(),
|
||||||
"LNBITS_TELEGRAM_CONFIGURED": settings.is_telegram_notifications_configured(),
|
"LNBITS_TELEGRAM_CONFIGURED": settings.is_telegram_notifications_configured(),
|
||||||
"LNBITS_EXT_BUILDER": settings.lnbits_extensions_builder_activate_non_admins,
|
"LNBITS_EXT_BUILDER": settings.lnbits_extensions_builder_activate_non_admins,
|
||||||
|
"LNBITS_CURRENCIES": list(currencies.keys()),
|
||||||
|
"LNBITS_ALLOWED_CURRENCIES": settings.lnbits_allowed_currencies,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.env.globals["WINDOW_SETTINGS"] = window_settings
|
t.env.globals["WINDOW_SETTINGS"] = window_settings
|
||||||
|
|
|
||||||
2
lnbits/static/bundle-components.min.js
vendored
2
lnbits/static/bundle-components.min.js
vendored
File diff suppressed because one or more lines are too long
2
lnbits/static/bundle.min.js
vendored
2
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +1,5 @@
|
||||||
window.app.component('lnbits-admin-server', {
|
window.app.component('lnbits-admin-server', {
|
||||||
props: ['form-data'],
|
props: ['form-data'],
|
||||||
template: '#lnbits-admin-server',
|
template: '#lnbits-admin-server',
|
||||||
mixins: [window.windowMixin],
|
mixins: [window.windowMixin]
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
currencies: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
this.currencies = await LNbits.api.getCurrencies()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,14 @@ window.g = Vue.reactive({
|
||||||
newWalletType: 'lightning',
|
newWalletType: 'lightning',
|
||||||
updatePayments: false,
|
updatePayments: false,
|
||||||
updatePaymentsHash: '',
|
updatePaymentsHash: '',
|
||||||
|
currencies: WINDOW_SETTINGS.LNBITS_CURRENCIES ?? [],
|
||||||
|
allowedCurrencies: WINDOW_SETTINGS.LNBITS_ALLOWED_CURRENCIES ?? [],
|
||||||
locale: localStore('lnbits.lang', navigator.languages[1] ?? 'en'),
|
locale: localStore('lnbits.lang', navigator.languages[1] ?? 'en'),
|
||||||
disclaimerShown: localStore('lnbits.disclaimerShown', false),
|
disclaimerShown: localStore('lnbits.disclaimerShown', false),
|
||||||
isFiatPriority: localStore('lnbits.isFiatPriority', false),
|
isFiatPriority: localStore('lnbits.isFiatPriority', false),
|
||||||
mobileSimple: localStore('lnbits.mobileSimple', true),
|
mobileSimple: localStore('lnbits.mobileSimple', true),
|
||||||
walletFlip: localStore('lnbits.walletFlip', false),
|
walletFlip: localStore('lnbits.walletFlip', false),
|
||||||
|
lastActiveWallet: localStore('lnbits.lastActiveWallet', null),
|
||||||
darkChoice: localStore('lnbits.darkMode', true),
|
darkChoice: localStore('lnbits.darkMode', true),
|
||||||
themeChoice: localStore('lnbits.theme', WINDOW_SETTINGS.LNBITS_DEFAULT_THEME),
|
themeChoice: localStore('lnbits.theme', WINDOW_SETTINGS.LNBITS_DEFAULT_THEME),
|
||||||
borderChoice: localStore(
|
borderChoice: localStore(
|
||||||
|
|
|
||||||
|
|
@ -1,144 +1,4 @@
|
||||||
const DynamicComponent = {
|
|
||||||
props: {
|
|
||||||
fetchUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
scripts: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
keys: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
await this.loadDynamicContent()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async loadScript(src) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const existingScript = document.querySelector(`script[src="${src}"]`)
|
|
||||||
if (existingScript) {
|
|
||||||
existingScript.remove()
|
|
||||||
}
|
|
||||||
const script = document.createElement('script')
|
|
||||||
script.src = src
|
|
||||||
script.async = true
|
|
||||||
script.onload = resolve
|
|
||||||
script.onerror = () =>
|
|
||||||
reject(new Error(`Failed to load script: ${src}`))
|
|
||||||
document.head.appendChild(script)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async loadDynamicContent() {
|
|
||||||
this.$q.loading.show()
|
|
||||||
try {
|
|
||||||
const cleanUrl = this.fetchUrl.split('#')[0]
|
|
||||||
//grab page content, need to be before loading scripts
|
|
||||||
const response = await fetch(cleanUrl, {
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
Accept: 'text/html',
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const html = await response.text()
|
|
||||||
|
|
||||||
// load window variables
|
|
||||||
const parser = new DOMParser()
|
|
||||||
const htmlDocument = parser.parseFromString(html, 'text/html')
|
|
||||||
const inlineScript = htmlDocument.querySelector('#window-vars-script')
|
|
||||||
if (inlineScript) {
|
|
||||||
new Function(inlineScript.innerHTML)() // Execute the script
|
|
||||||
}
|
|
||||||
|
|
||||||
//load scripts defined in the route
|
|
||||||
await this.loadScript('/static/js/base.js')
|
|
||||||
for (const script of this.scripts) {
|
|
||||||
await this.loadScript(script)
|
|
||||||
}
|
|
||||||
|
|
||||||
//housecleaning, remove old component
|
|
||||||
const previousRouteName =
|
|
||||||
this.$router.currentRoute.value.meta.previousRouteName
|
|
||||||
if (
|
|
||||||
previousRouteName &&
|
|
||||||
window.app._context.components[previousRouteName]
|
|
||||||
) {
|
|
||||||
delete window.app._context.components[previousRouteName]
|
|
||||||
}
|
|
||||||
//load component logic
|
|
||||||
const logicKey = `${this.$route.name}PageLogic`
|
|
||||||
const componentLogic = window[logicKey]
|
|
||||||
|
|
||||||
if (!componentLogic) {
|
|
||||||
throw new Error(
|
|
||||||
`Component logic '${logicKey}' not found. Ensure it is defined in the script.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add mixins
|
|
||||||
componentLogic.mixins = componentLogic.mixins || []
|
|
||||||
if (window.windowMixin) {
|
|
||||||
componentLogic.mixins.push(window.windowMixin)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Build component
|
|
||||||
window.app.component(this.$route.name, {
|
|
||||||
...componentLogic,
|
|
||||||
template: html // Use the fetched HTML as the template
|
|
||||||
})
|
|
||||||
delete window[logicKey] //dont need this anymore
|
|
||||||
this.$forceUpdate()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading dynamic content:', error)
|
|
||||||
} finally {
|
|
||||||
this.$q.loading.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
$route(to, from) {
|
|
||||||
const validRouteNames = routes.map(route => route.name)
|
|
||||||
if (validRouteNames.includes(to.name)) {
|
|
||||||
this.$router.currentRoute.value.meta.previousRouteName = from.name
|
|
||||||
this.loadDynamicContent()
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
`Route '${to.name}' is not valid. Leave this one to Fastapi.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
|
||||||
<component :is="$route.name"></component>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
|
||||||
path: '/wallet',
|
|
||||||
name: 'Wallet',
|
|
||||||
component: DynamicComponent,
|
|
||||||
props: route => {
|
|
||||||
let fetchUrl = '/wallet'
|
|
||||||
if (Object.keys(route.query).length > 0) {
|
|
||||||
fetchUrl += '?'
|
|
||||||
for (const [key, value] of Object.entries(route.query)) {
|
|
||||||
fetchUrl += `${key}=${value}&`
|
|
||||||
}
|
|
||||||
fetchUrl = fetchUrl.slice(0, -1) // remove last &
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
fetchUrl,
|
|
||||||
scripts: ['/static/js/wallet.js']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/node',
|
path: '/node',
|
||||||
name: 'Node',
|
name: 'Node',
|
||||||
|
|
@ -159,6 +19,11 @@ const routes = [
|
||||||
name: 'Audit',
|
name: 'Audit',
|
||||||
component: PageAudit
|
component: PageAudit
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/wallet',
|
||||||
|
name: 'Wallet',
|
||||||
|
component: PageWallet
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/wallets',
|
path: '/wallets',
|
||||||
name: 'Wallets',
|
name: 'Wallets',
|
||||||
|
|
@ -236,5 +101,4 @@ window.app.use(window.i18n)
|
||||||
|
|
||||||
window.app.provide('g', g)
|
window.app.provide('g', g)
|
||||||
window.app.use(window.router)
|
window.app.use(window.router)
|
||||||
window.app.component('DynamicComponent', DynamicComponent)
|
|
||||||
window.app.mount('#vue')
|
window.app.mount('#vue')
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
window.WalletPageLogic = {
|
window.PageWallet = {
|
||||||
|
template: '#page-wallet',
|
||||||
mixins: [window.windowMixin],
|
mixins: [window.windowMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -104,9 +105,6 @@ window.WalletPageLogic = {
|
||||||
showCamera() {
|
showCamera() {
|
||||||
this.parse.camera.show = true
|
this.parse.camera.show = true
|
||||||
},
|
},
|
||||||
focusInput(el) {
|
|
||||||
this.$nextTick(() => this.$refs[el].focus())
|
|
||||||
},
|
|
||||||
showReceiveDialog() {
|
showReceiveDialog() {
|
||||||
this.receive.show = true
|
this.receive.show = true
|
||||||
this.receive.status = 'pending'
|
this.receive.status = 'pending'
|
||||||
|
|
@ -121,7 +119,6 @@ window.WalletPageLogic = {
|
||||||
: 'sat'
|
: 'sat'
|
||||||
this.receive.minMax = [0, 2100000000000000]
|
this.receive.minMax = [0, 2100000000000000]
|
||||||
this.receive.lnurl = null
|
this.receive.lnurl = null
|
||||||
this.focusInput('setAmount')
|
|
||||||
},
|
},
|
||||||
onReceiveDialogHide() {
|
onReceiveDialogHide() {
|
||||||
if (this.hasNfc) {
|
if (this.hasNfc) {
|
||||||
|
|
@ -140,7 +137,6 @@ window.WalletPageLogic = {
|
||||||
this.parse.data.internalMemo = null
|
this.parse.data.internalMemo = null
|
||||||
this.parse.data.paymentChecker = null
|
this.parse.data.paymentChecker = null
|
||||||
this.parse.camera.show = false
|
this.parse.camera.show = false
|
||||||
this.focusInput('textArea')
|
|
||||||
},
|
},
|
||||||
closeParseDialog() {
|
closeParseDialog() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -645,8 +641,24 @@ window.WalletPageLogic = {
|
||||||
this.decodeRequest()
|
this.decodeRequest()
|
||||||
this.parse.show = true
|
this.parse.show = true
|
||||||
}
|
}
|
||||||
|
if (urlParams.has('wal')) {
|
||||||
|
const wallet = g.user.wallets.find(w => w.id === urlParams.get('wal'))
|
||||||
|
if (wallet) {
|
||||||
|
this.selectWallet(wallet)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const wallet = g.user.wallets.find(w => w.id === this.g.lastActiveWallet)
|
||||||
|
if (wallet) {
|
||||||
|
this.selectWallet(wallet)
|
||||||
|
} else if (g.user.wallets.length > 0) {
|
||||||
|
this.selectWallet(g.user.wallets[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
'g.lastActiveWallet'(val) {
|
||||||
|
this.$q.localStorage.setItem('lnbits.lastActiveWallet', val)
|
||||||
|
},
|
||||||
'g.updatePayments'() {
|
'g.updatePayments'() {
|
||||||
this.parse.show = false
|
this.parse.show = false
|
||||||
if (this.receive.paymentHash === this.g.updatePaymentsHash) {
|
if (this.receive.paymentHash === this.g.updatePaymentsHash) {
|
||||||
|
|
@ -667,9 +679,13 @@ window.WalletPageLogic = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'g.wallet'() {
|
'g.wallet'() {
|
||||||
if (this.g.wallet.currency && this.g.fiatTracking) {
|
if (this.g.wallet.currency) {
|
||||||
|
this.g.fiatTracking = true
|
||||||
this.g.fiatBalance =
|
this.g.fiatBalance =
|
||||||
(this.g.exchangeRate / 100000000) * this.g.wallet.sat
|
(this.g.exchangeRate / 100000000) * this.g.wallet.sat
|
||||||
|
} else {
|
||||||
|
this.g.fiatBalance = 0
|
||||||
|
this.g.fiatTracking = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'g.isFiatPriority'() {
|
'g.isFiatPriority'() {
|
||||||
|
|
@ -74,7 +74,7 @@ window.PageWallets = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
goToWallet(walletId) {
|
goToWallet(walletId) {
|
||||||
window.location = `/wallet?wal=${walletId}`
|
this.$router.push({path: '/wallet', query: {wal: walletId}})
|
||||||
},
|
},
|
||||||
formattedFiatAmount(amount, currency) {
|
formattedFiatAmount(amount, currency) {
|
||||||
return LNbits.utils.formatCurrency(Number(amount).toFixed(2), currency)
|
return LNbits.utils.formatCurrency(Number(amount).toFixed(2), currency)
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ window.windowMixin = {
|
||||||
},
|
},
|
||||||
selectWallet(wallet) {
|
selectWallet(wallet) {
|
||||||
this.g.wallet = wallet
|
this.g.wallet = wallet
|
||||||
|
this.g.lastActiveWallet = wallet.id
|
||||||
this.g.updatePayments = !this.g.updatePayments
|
this.g.updatePayments = !this.g.updatePayments
|
||||||
this.balance = parseInt(wallet.balance_msat / 1000)
|
this.balance = parseInt(wallet.balance_msat / 1000)
|
||||||
const currentPath = this.$route.path
|
const currentPath = this.$route.path
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
"js/pages/node.js",
|
"js/pages/node.js",
|
||||||
"js/pages/node-public.js",
|
"js/pages/node-public.js",
|
||||||
"js/pages/audit.js",
|
"js/pages/audit.js",
|
||||||
|
"js/pages/wallet.js",
|
||||||
"js/pages/wallets.js",
|
"js/pages/wallets.js",
|
||||||
"js/pages/users.js",
|
"js/pages/users.js",
|
||||||
"js/pages/account.js",
|
"js/pages/account.js",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
multiple
|
multiple
|
||||||
:hint="$t('allowed_currencies_hint')"
|
:hint="$t('allowed_currencies_hint')"
|
||||||
:label="$t('allowed_currencies')"
|
:label="$t('allowed_currencies')"
|
||||||
:options="currencies"
|
:options="g.currencies"
|
||||||
></q-select>
|
></q-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
:options="
|
:options="
|
||||||
formData.lnbits_allowed_currencies?.length
|
formData.lnbits_allowed_currencies?.length
|
||||||
? formData.lnbits_allowed_currencies
|
? formData.lnbits_allowed_currencies
|
||||||
: currencies
|
: g.allowedCurrencies
|
||||||
"
|
"
|
||||||
></q-select>
|
></q-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
v-if="!HIDE_API"
|
||||||
group="api"
|
group="api"
|
||||||
dense
|
dense
|
||||||
expand-separator
|
expand-separator
|
||||||
|
|
@ -132,6 +133,7 @@
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
v-if="!HIDE_API"
|
||||||
group="api"
|
group="api"
|
||||||
dense
|
dense
|
||||||
expand-separator
|
expand-separator
|
||||||
|
|
@ -176,6 +178,7 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
v-if="!HIDE_API"
|
||||||
group="api"
|
group="api"
|
||||||
dense
|
dense
|
||||||
expand-separator
|
expand-separator
|
||||||
|
|
@ -222,6 +225,7 @@
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
v-if="!HIDE_API"
|
||||||
group="api"
|
group="api"
|
||||||
dense
|
dense
|
||||||
expand-separator
|
expand-separator
|
||||||
|
|
@ -250,6 +254,7 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
v-if="!HIDE_API"
|
||||||
group="api"
|
group="api"
|
||||||
dense
|
dense
|
||||||
expand-separator
|
expand-separator
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@
|
||||||
@change="updateWallet({currency: g.wallet.currency})"
|
@change="updateWallet({currency: g.wallet.currency})"
|
||||||
type="text"
|
type="text"
|
||||||
:disable="g.fiatTracking"
|
:disable="g.fiatTracking"
|
||||||
:options="currencies"
|
:options="g.allowedCurrencies"
|
||||||
:label="$t('currency_settings')"
|
:label="$t('currency_settings')"
|
||||||
></q-select>
|
></q-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,4 @@ include('pages/audit.vue') %} {% include('pages/wallets.vue') %} {%
|
||||||
include('pages/users.vue') %} {% include('pages/admin.vue') %} {%
|
include('pages/users.vue') %} {% include('pages/admin.vue') %} {%
|
||||||
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %} {%
|
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %} {%
|
||||||
include('pages/extensions.vue') %} {% include('pages/first-install.vue') %} {%
|
include('pages/extensions.vue') %} {% include('pages/first-install.vue') %} {%
|
||||||
include('pages/home.vue') %}
|
include('pages/home.vue') %} {% include('pages/wallet.vue') %}
|
||||||
|
|
|
||||||
875
lnbits/templates/pages/wallet.vue
Normal file
875
lnbits/templates/pages/wallet.vue
Normal file
|
|
@ -0,0 +1,875 @@
|
||||||
|
<template id="page-wallet">
|
||||||
|
<div class="row q-col-gutter-md" style="margin-bottom: 6rem">
|
||||||
|
<div class="col-12 col-md-7 q-gutter-y-md wallet-wrapper">
|
||||||
|
<q-card class="wallet-card">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row q-gutter-sm">
|
||||||
|
<div v-if="g.fiatTracking" class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
@click="g.isFiatPriority = !g.isFiatPriority"
|
||||||
|
style="height: 50px"
|
||||||
|
class="q-mt-lg"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="swap_vert"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div
|
||||||
|
v-if="!g.isFiatPriority || !g.fiatTracking"
|
||||||
|
class="column"
|
||||||
|
:class="{
|
||||||
|
'q-pt-sm': g.fiatTracking,
|
||||||
|
'q-pt-lg': !g.fiatTracking
|
||||||
|
}"
|
||||||
|
style="height: 100px"
|
||||||
|
>
|
||||||
|
<div class="col-7">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="text-h3 q-my-none full-width">
|
||||||
|
<strong
|
||||||
|
v-text="formatBalance(g.wallet.sat)"
|
||||||
|
class="text-no-wrap"
|
||||||
|
:style="{
|
||||||
|
fontSize: 'clamp(0.75rem, 10vw, 3rem)',
|
||||||
|
display: 'inline-block',
|
||||||
|
maxWidth: '100%'
|
||||||
|
}"
|
||||||
|
></strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<lnbits-update-balance
|
||||||
|
v-if="$q.screen.lt.lg"
|
||||||
|
:wallet_id="g.wallet.id"
|
||||||
|
:callback="updateBalanceCallback"
|
||||||
|
:small_btn="true"
|
||||||
|
></lnbits-update-balance>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div v-if="g.fiatTracking">
|
||||||
|
<span
|
||||||
|
class="text-h5 text-italic"
|
||||||
|
v-text="formattedFiatAmount"
|
||||||
|
style="opacity: 0.75"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="column"
|
||||||
|
v-if="g.isFiatPriority && g.fiatTracking"
|
||||||
|
:class="{
|
||||||
|
'q-pt-sm': g.fiatTracking,
|
||||||
|
'q-pt-lg': !g.fiatTracking
|
||||||
|
}"
|
||||||
|
style="height: 100px"
|
||||||
|
>
|
||||||
|
<div class="col-7">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div
|
||||||
|
v-if="g.fiatTracking"
|
||||||
|
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="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="formatBalance(g.wallet.sat)"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="absolute-right q-pa-md"
|
||||||
|
v-if="$q.screen.gt.md && g.fiatTracking && g.isSatsDenomination"
|
||||||
|
>
|
||||||
|
<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="g.mobileSimple = !g.mobileSimple"
|
||||||
|
color="primary"
|
||||||
|
class="q-ml-xl absolute-right"
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
style="height: 20px; margin-top: 75px"
|
||||||
|
flat
|
||||||
|
:icon="g.mobileSimple ? 'unfold_more' : 'unfold_less'"
|
||||||
|
:label="g.mobileSimple ? $t('more') : $t('less')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!g.mobileSimple || !$q.screen.lt.md"
|
||||||
|
class="lnbits-wallet-buttons row q-gutter-md"
|
||||||
|
>
|
||||||
|
<div class="col">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-md"
|
||||||
|
@click="showParseDialog"
|
||||||
|
:disable="!this.g.wallet.canSendPayments"
|
||||||
|
:label="$t('paste_request')"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-md"
|
||||||
|
@click="showReceiveDialog"
|
||||||
|
:disable="!this.g.wallet.canReceivePayments"
|
||||||
|
:label="$t('create_invoice')"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="secondary"
|
||||||
|
icon="qr_code_scanner"
|
||||||
|
:disable="
|
||||||
|
!this.g.wallet.canReceivePayments &&
|
||||||
|
!this.g.wallet.canSendPayments
|
||||||
|
"
|
||||||
|
@click="showCamera"
|
||||||
|
>
|
||||||
|
<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-section>
|
||||||
|
</q-card>
|
||||||
|
<q-card class="wallet-card">
|
||||||
|
<q-card-section>
|
||||||
|
<lnbits-payment-list
|
||||||
|
:update="updatePayments"
|
||||||
|
:expand-details="expandDetails"
|
||||||
|
:payment-filter="paymentFilter"
|
||||||
|
></lnbits-payment-list>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!g.mobileSimple || !$q.screen.lt.md"
|
||||||
|
class="col-12 col-md-5 q-gutter-y-md"
|
||||||
|
>
|
||||||
|
<lnbits-wallet-extra
|
||||||
|
@update-wallet="updateWallet"
|
||||||
|
@send-lnurl="handleSendLnurl"
|
||||||
|
:chart-config="chartConfig"
|
||||||
|
></lnbits-wallet-extra>
|
||||||
|
<q-card class="lnbits-wallet-ads" v-if="AD_SPACE_ENABLED">
|
||||||
|
<q-card-section class="text-subtitle1">
|
||||||
|
<span v-text="AD_SPACE_TITLE"></span>
|
||||||
|
<a :href="ad[0]" class="lnbits-ad" v-for="ad in g.ads">
|
||||||
|
<q-img class="q-mb-xs" v-if="$q.dark.isActive" :src="ad[1]"></q-img>
|
||||||
|
<q-img class="q-mb-xs" v-else :src="ad[2]"></q-img>
|
||||||
|
</a>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
<lnbits-wallet-charts
|
||||||
|
:payment-filter="paymentFilter"
|
||||||
|
:chart-config="chartConfig"
|
||||||
|
></lnbits-wallet-charts>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-dialog v-model="receive.show" position="top" @hide="onReceiveDialogHide">
|
||||||
|
<q-card
|
||||||
|
v-if="!receive.paymentReq"
|
||||||
|
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||||
|
>
|
||||||
|
<q-form @submit="createInvoice" class="q-gutter-md">
|
||||||
|
<p v-if="receive.lnurl" class="text-h6 text-center q-my-none">
|
||||||
|
<b v-text="receive.lnurl.domain"></b> is requesting an invoice:
|
||||||
|
</p>
|
||||||
|
<q-input
|
||||||
|
v-if="!g.isSatsDenomination"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="receive.data.amount"
|
||||||
|
:label="$t('amount') + '(' + denomination + ') *'"
|
||||||
|
mask="#.##"
|
||||||
|
fill-mask="0"
|
||||||
|
reverse-fill-mask
|
||||||
|
:min="receive.minMax[0]"
|
||||||
|
:max="receive.minMax[1]"
|
||||||
|
:readonly="receive.lnurl && receive.lnurl.fixed"
|
||||||
|
></q-input>
|
||||||
|
<div v-else>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-10">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="receive.unit"
|
||||||
|
type="text"
|
||||||
|
:label="$t('unit')"
|
||||||
|
:options="receive.units"
|
||||||
|
></q-select>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<q-btn
|
||||||
|
v-if="g.fiatTracking"
|
||||||
|
@click="g.isFiatPriority = !g.isFiatPriority"
|
||||||
|
class="float-right"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="swap_vert"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-input
|
||||||
|
class="q-mt-md"
|
||||||
|
ref="setAmount"
|
||||||
|
filled
|
||||||
|
:pattern="receive.unit === 'sat' ? '\\d*' : '\\d*\\.?\\d*'"
|
||||||
|
inputmode="numeric"
|
||||||
|
dense
|
||||||
|
v-model.number="receive.data.amount"
|
||||||
|
:label="$t('amount') + ' (' + receive.unit + ') *'"
|
||||||
|
:min="receive.minMax[0]"
|
||||||
|
:max="receive.minMax[1]"
|
||||||
|
:readonly="receive.lnurl && receive.lnurl.fixed"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
<q-input
|
||||||
|
v-if="has_holdinvoice"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="receive.data.payment_hash"
|
||||||
|
:label="$t('hold_invoice_payment_hash')"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
type="textarea"
|
||||||
|
rows="2"
|
||||||
|
v-model="receive.data.memo"
|
||||||
|
:label="$t('memo')"
|
||||||
|
>
|
||||||
|
<template v-if="receive.data.internalMemo === null" v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="add_comment"
|
||||||
|
@click.stop.prevent="receive.data.internalMemo = ''"
|
||||||
|
class="cursor-pointer"
|
||||||
|
></q-icon>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="$t('internal_memo')"></span>
|
||||||
|
</q-tooltip>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
<q-input
|
||||||
|
v-if="receive.data.internalMemo !== null"
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="receive.data.internalMemo"
|
||||||
|
class="q-mb-lg"
|
||||||
|
:label="$t('internal_memo')"
|
||||||
|
:hint="$t('internal_memo_hint_receive')"
|
||||||
|
:rules="[
|
||||||
|
val =>
|
||||||
|
!val || val.length <= 512 || 'Please use maximum 512 characters'
|
||||||
|
]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="receive.data.internalMemo = null"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/> </template
|
||||||
|
></q-input>
|
||||||
|
<div v-if="g.user.fiat_providers?.length" class="q-mt-md">
|
||||||
|
<q-list bordered dense class="rounded-borders">
|
||||||
|
<q-item-label dense header>
|
||||||
|
<span v-text="$t('select_payment_provider')"></span>
|
||||||
|
</q-item-label>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-item
|
||||||
|
:active="!receive.fiatProvider"
|
||||||
|
@click="receive.fiatProvider = ''"
|
||||||
|
active-class="bg-teal-1 text-grey-8 text-weight-bold"
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar square>
|
||||||
|
<img src="/static/images/logos/lnbits.png" />
|
||||||
|
</q-avatar>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<span
|
||||||
|
v-text="$t('pay_with', {provider: 'Lightning Network'})"
|
||||||
|
></span>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-item
|
||||||
|
:active="receive.fiatProvider === 'stripe'"
|
||||||
|
@click="receive.fiatProvider = 'stripe'"
|
||||||
|
active-class="bg-teal-1 text-grey-8 text-weight-bold"
|
||||||
|
clickable
|
||||||
|
v-ripple
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar>
|
||||||
|
<img src="/static/images/stripe_logo.ico" />
|
||||||
|
</q-avatar>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<span v-text="$t('pay_with', {provider: 'Stripe'})"></span>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="receive.status == 'pending'" class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="receive.data.amount == null || receive.data.amount <= 0"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="receive.lnurl"
|
||||||
|
v-text="`${$t('withdraw_from')} ${receive.lnurl.domain}`"
|
||||||
|
></span>
|
||||||
|
<span v-else v-text="$t('create_invoice')"></span>
|
||||||
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<q-spinner-bars
|
||||||
|
v-if="receive.status == 'loading'"
|
||||||
|
color="primary"
|
||||||
|
size="2.55em"
|
||||||
|
></q-spinner-bars>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
<q-card
|
||||||
|
v-else-if="receive.paymentReq && receive.lnurl == null"
|
||||||
|
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||||
|
>
|
||||||
|
<lnbits-qrcode
|
||||||
|
v-if="receive.fiatPaymentReq"
|
||||||
|
:show-buttons="false"
|
||||||
|
:href="receive.fiatPaymentReq"
|
||||||
|
:value="receive.fiatPaymentReq"
|
||||||
|
>
|
||||||
|
</lnbits-qrcode>
|
||||||
|
<lnbits-qrcode
|
||||||
|
v-else
|
||||||
|
:href="'lightning:' + receive.paymentReq"
|
||||||
|
:value="'lightning:' + receive.paymentReq"
|
||||||
|
>
|
||||||
|
</lnbits-qrcode>
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="q-my-md">
|
||||||
|
<span v-text="formattedAmount"></span>
|
||||||
|
</h3>
|
||||||
|
<h5 v-if="receive.unit != 'sat'" class="q-mt-none q-mb-sm">
|
||||||
|
<span v-text="formattedSatAmount"></span>
|
||||||
|
</h5>
|
||||||
|
<div v-if="!receive.fiatPaymentReq">
|
||||||
|
<q-chip v-if="hasNfc" outline square color="positive">
|
||||||
|
<q-avatar icon="nfc" color="positive" text-color="white"></q-avatar>
|
||||||
|
<span v-text="$t('nfc_supported')"></span>
|
||||||
|
</q-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('close')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="parse.show" @hide="closeParseDialog" position="top">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<div v-if="parse.invoice">
|
||||||
|
<div class="column content-center text-center q-mb-md">
|
||||||
|
<div v-if="!g.isFiatPriority">
|
||||||
|
<h4 class="q-my-none text-bold">
|
||||||
|
<span v-text="formatBalance(parse.invoice.sat)"></span>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<h4
|
||||||
|
class="q-my-none text-bold"
|
||||||
|
v-text="parse.invoice.fiatAmount"
|
||||||
|
></h4>
|
||||||
|
</div>
|
||||||
|
<div class="q-my-md absolute">
|
||||||
|
<q-btn
|
||||||
|
v-if="g.fiatTracking"
|
||||||
|
@click="g.isFiatPriority = !g.isFiatPriority"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
icon="swap_vert"
|
||||||
|
color="primary"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div v-if="g.fiatTracking">
|
||||||
|
<div v-if="g.isFiatPriority">
|
||||||
|
<h5 class="q-my-none text-bold">
|
||||||
|
<span v-text="formatBalance(parse.invoice.sat)"></span>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div v-else style="opacity: 0.75">
|
||||||
|
<div class="text-h5 text-italic">
|
||||||
|
<span
|
||||||
|
v-text="parse.invoice.fiatAmount"
|
||||||
|
style="opacity: 0.75"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<h6 class="text-center" v-text="parse.invoice.description"></h6>
|
||||||
|
<q-input
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="parse.data.internalMemo"
|
||||||
|
:label="$t('internal_memo')"
|
||||||
|
:hint="$t('internal_memo_hint_pay')"
|
||||||
|
class="q-mb-lg"
|
||||||
|
:rules="[
|
||||||
|
val =>
|
||||||
|
!val || val.length <= 512 || 'Please use maximum 512 characters'
|
||||||
|
]"
|
||||||
|
><template v-if="parse.data.internalMemo" v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="parse.data.internalMemo = null"
|
||||||
|
class="cursor-pointer" /></template
|
||||||
|
></q-input>
|
||||||
|
<q-list separator bordered dense class="q-mb-md">
|
||||||
|
<q-expansion-item expand-separator icon="info" label="Details">
|
||||||
|
<q-list separator>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label v-text="$t('created')"></q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="parse.invoice.createdDate"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section side top>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="parse.invoice.createdDateFrom"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label v-text="$t('expire_date')"></q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="parse.invoice.expireDate"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side top>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="parse.invoice.expireDateFrom"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label v-text="$t('payment_hash')"></q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="
|
||||||
|
`${parse.invoice.hash.slice(0, 12)}...${parse.invoice.hash.slice(-12)}`
|
||||||
|
"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-item-label>
|
||||||
|
<q-icon
|
||||||
|
name="content_copy"
|
||||||
|
@click="copyText(parse.invoice.hash)"
|
||||||
|
size="1em"
|
||||||
|
color="grey"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/>
|
||||||
|
</q-item-label>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="parse.invoice.hash"></span>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label v-text="$t('Invoice')"></q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="
|
||||||
|
`${parse.invoice.bolt11.slice(0, 12)}...${parse.invoice.bolt11.slice(-12)}`
|
||||||
|
"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-item-label>
|
||||||
|
<q-icon
|
||||||
|
name="content_copy"
|
||||||
|
@click="copyText(parse.invoice.bolt11)"
|
||||||
|
size="1em"
|
||||||
|
color="grey"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/>
|
||||||
|
</q-item-label>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="parse.invoice.bolt11"></span>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-list>
|
||||||
|
<div v-if="canPay" class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
@click="payInvoice"
|
||||||
|
:label="$t('pay')"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div v-else class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
:label="$t('not_enough_funds')"
|
||||||
|
unelevated
|
||||||
|
disabled
|
||||||
|
color="yellow"
|
||||||
|
text-color="black"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="parse.lnurlauth">
|
||||||
|
<q-form @submit="authLnurl" class="q-gutter-md">
|
||||||
|
<p class="q-my-none text-h6">
|
||||||
|
Authenticate with <b v-text="parse.lnurlauth.domain"></b>?
|
||||||
|
</p>
|
||||||
|
<q-separator class="q-my-sm"></q-separator>
|
||||||
|
<p>
|
||||||
|
For every website and for every LNbits wallet, a new keypair will be
|
||||||
|
deterministically generated so your identity can't be tied to your
|
||||||
|
LNbits wallet or linked across websites. No other data will be
|
||||||
|
shared with
|
||||||
|
<span v-text="parse.lnurlauth.domain"></span>.
|
||||||
|
</p>
|
||||||
|
<p>Your public key for <b v-text="parse.lnurlauth.domain"></b> is:</p>
|
||||||
|
<p class="q-mx-xl">
|
||||||
|
<code class="text-wrap" v-text="parse.lnurlauth.pubkey"></code>
|
||||||
|
</p>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
:label="$t('login')"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
:label="$t('cancel')"
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="parse.lnurlpay">
|
||||||
|
<q-form @submit="payLnurl" class="q-gutter-md">
|
||||||
|
<p v-if="parse.lnurlpay.fixed" class="q-my-none text-h6">
|
||||||
|
<b v-text="parse.lnurlpay.domain"></b> is requesting
|
||||||
|
<span v-text="msatoshiFormat(parse.lnurlpay.maxSendable)"></span>
|
||||||
|
<span v-text="denomination"></span>
|
||||||
|
<span v-if="parse.lnurlpay.commentAllowed > 0">
|
||||||
|
<br />
|
||||||
|
and a
|
||||||
|
<span v-text="parse.lnurlpay.commentAllowed"></span>-char comment
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p v-else class="q-my-none text-h6 text-center">
|
||||||
|
<b v-text="parse.lnurlpay.targetUser || parse.lnurlpay.domain"></b>
|
||||||
|
is requesting <br />
|
||||||
|
between
|
||||||
|
<b v-text="msatoshiFormat(parse.lnurlpay.minSendable)"></b> and
|
||||||
|
<b v-text="msatoshiFormat(parse.lnurlpay.maxSendable)"></b>
|
||||||
|
<span v-text="denomination"></span>
|
||||||
|
<span v-if="parse.lnurlpay.commentAllowed > 0">
|
||||||
|
<br />
|
||||||
|
and a
|
||||||
|
<span v-text="parse.lnurlpay.commentAllowed"></span>-char comment
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<q-separator class="q-my-sm"></q-separator>
|
||||||
|
<div class="row">
|
||||||
|
<p
|
||||||
|
class="col text-justify text-italic"
|
||||||
|
v-text="parse.lnurlpay.description"
|
||||||
|
></p>
|
||||||
|
<p class="col-4 q-pl-md" v-if="parse.lnurlpay.image">
|
||||||
|
<q-img :src="parse.lnurlpay.image" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-lg">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-if="!parse.lnurlpay.fixed"
|
||||||
|
v-model="parse.data.unit"
|
||||||
|
type="text"
|
||||||
|
:label="$t('unit')"
|
||||||
|
:options="receive.units"
|
||||||
|
></q-select>
|
||||||
|
<br />
|
||||||
|
<q-input
|
||||||
|
ref="setAmount"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="parse.data.amount"
|
||||||
|
:label="$t('amount') + ' (' + parse.data.unit + ') *'"
|
||||||
|
:mask="parse.data.unit == 'sat' ? '#' : ''"
|
||||||
|
:step="parse.data.unit == 'sat' ? '1' : '0.01'"
|
||||||
|
fill-mask="0"
|
||||||
|
reverse-fill-mask
|
||||||
|
:min="parse.lnurlpay.minSendable / 1000"
|
||||||
|
:max="parse.lnurlpay.maxSendable / 1000"
|
||||||
|
:readonly="parse.lnurlpay && parse.lnurlpay.fixed"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 q-pl-md" v-if="parse.lnurlpay.commentAllowed > 0">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="parse.data.comment"
|
||||||
|
:type="
|
||||||
|
parse.lnurlpay.commentAllowed > 512 ? 'textarea' : 'text'
|
||||||
|
"
|
||||||
|
label="Comment (optional)"
|
||||||
|
:maxlength="parse.lnurlpay.commentAllowed"
|
||||||
|
><template
|
||||||
|
v-if="parse.data.internalMemo === null"
|
||||||
|
v-slot:append
|
||||||
|
>
|
||||||
|
<q-icon
|
||||||
|
name="add_comment"
|
||||||
|
@click.stop.prevent="parse.data.internalMemo = ''"
|
||||||
|
class="cursor-pointer"
|
||||||
|
></q-icon>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="$t('internal_memo')"></span>
|
||||||
|
</q-tooltip> </template
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
<q-input
|
||||||
|
v-if="parse.data.internalMemo !== null"
|
||||||
|
autogrow
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="parse.data.internalMemo"
|
||||||
|
:label="$t('internal_memo')"
|
||||||
|
:hint="$t('internal_memo_hint_pay')"
|
||||||
|
class=""
|
||||||
|
:rules="[
|
||||||
|
val =>
|
||||||
|
!val ||
|
||||||
|
val.length <= 512 ||
|
||||||
|
'Please use maximum 512 characters'
|
||||||
|
]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="parse.data.internalMemo = null"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/> </template
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="primary" type="submit">Send</q-btn>
|
||||||
|
<q-btn
|
||||||
|
:label="$t('cancel')"
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<q-form
|
||||||
|
v-if="!parse.camera.show"
|
||||||
|
@submit="decodeRequest"
|
||||||
|
class="q-gutter-md"
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="parse.data.request"
|
||||||
|
type="textarea"
|
||||||
|
:label="$t('paste_invoice_label')"
|
||||||
|
ref="textArea"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="parse.data.request == ''"
|
||||||
|
type="submit"
|
||||||
|
:label="$t('read')"
|
||||||
|
></q-btn>
|
||||||
|
<q-icon
|
||||||
|
name="content_paste"
|
||||||
|
color="grey"
|
||||||
|
class="q-mt-xs q-ml-sm q-mr-auto"
|
||||||
|
v-if="parse.copy.show"
|
||||||
|
@click="pasteToTextArea"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="$t('paste_from_clipboard')"></span>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
<div v-else>
|
||||||
|
<q-responsive :ratio="1">
|
||||||
|
<qrcode-stream
|
||||||
|
@detect="decodeQR"
|
||||||
|
@camera-on="onInitQR"
|
||||||
|
class="rounded-borders"
|
||||||
|
></qrcode-stream>
|
||||||
|
</q-responsive>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
:label="$t('cancel')"
|
||||||
|
@click="closeCamera"
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="parse.camera.show" position="top">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl">
|
||||||
|
<div class="text-center q-mb-lg">
|
||||||
|
<qrcode-stream
|
||||||
|
@detect="decodeQR"
|
||||||
|
@camera-on="onInitQR"
|
||||||
|
class="rounded-borders"
|
||||||
|
></qrcode-stream>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
@click="closeCamera"
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('cancel')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="lt-md fixed-bottom left-0 right-0 bg-primary text-white shadow-2 z-top"
|
||||||
|
>
|
||||||
|
<q-tabs active-class="px-0" indicator-color="transparent" align="justify">
|
||||||
|
<q-tab
|
||||||
|
icon="file_download"
|
||||||
|
@click="showReceiveDialog"
|
||||||
|
:label="$t('receive')"
|
||||||
|
>
|
||||||
|
</q-tab>
|
||||||
|
|
||||||
|
<q-tab @click="showParseDialog" icon="file_upload" :label="$t('send')">
|
||||||
|
</q-tab>
|
||||||
|
</q-tabs>
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
size="35px"
|
||||||
|
unelevated
|
||||||
|
icon="qr_code_scanner"
|
||||||
|
@click="showCamera"
|
||||||
|
class="text-white bg-primary z-top vertical-bottom absolute-center absolute"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -103,6 +103,7 @@
|
||||||
"js/pages/node.js",
|
"js/pages/node.js",
|
||||||
"js/pages/node-public.js",
|
"js/pages/node-public.js",
|
||||||
"js/pages/audit.js",
|
"js/pages/audit.js",
|
||||||
|
"js/pages/wallet.js",
|
||||||
"js/pages/wallets.js",
|
"js/pages/wallets.js",
|
||||||
"js/pages/users.js",
|
"js/pages/users.js",
|
||||||
"js/pages/account.js",
|
"js/pages/account.js",
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,6 @@ async def test_get_wallet_with_user_and_wallet(client, to_user, to_wallet):
|
||||||
assert response.status_code == 200, f"{response.url} {response.status_code}"
|
assert response.status_code == 200, f"{response.url} {response.status_code}"
|
||||||
|
|
||||||
|
|
||||||
# check GET /wallet: wrong wallet and user, expect 400
|
|
||||||
@pytest.mark.anyio
|
|
||||||
async def test_get_wallet_with_user_and_wrong_wallet(client, to_user):
|
|
||||||
response = await client.get("wallet", params={"usr": to_user.id, "wal": "1"})
|
|
||||||
assert response.status_code == 400, f"{response.url} {response.status_code}"
|
|
||||||
|
|
||||||
|
|
||||||
# check GET /extensions: extensions list
|
# check GET /extensions: extensions list
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_get_extensions(client, to_user):
|
async def test_get_extensions(client, to_user):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue