Files
lnbits/lnbits/templates/pages/wallet.vue
T
2026-06-26 13:24:22 +01:00

1059 lines
36 KiB
Vue

<template id="page-wallet">
<div v-if="g.wallet" 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 cursor-pointer"
role="button"
tabindex="0"
@click="showWalletTotalBreakdown"
@keyup.enter="showWalletTotalBreakdown"
>
<strong
v-text="
utils.formatBalance(g.wallet.sat, g.denomination)
"
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 cursor-pointer"
role="button"
tabindex="0"
@click="showWalletTotalBreakdown"
@keyup.enter="showWalletTotalBreakdown"
>
<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="
utils.formatBalance(
g.wallet.sat,
g.denomination,
g.denomination
)
"
>
</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="
utils.formatCurrency(g.exchangeRate, g.wallet.currency)
"
></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="!$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="showReceiveDialog"
:label="$t('receive')"
:disable="!this.g.wallet.canReceivePayments"
icon="file_download"
></q-btn>
<q-btn
unelevated
color="primary"
class="q-mr-md"
@click="showParseDialog"
:disable="!this.g.wallet.canSendPayments"
:label="$t('send')"
icon="file_upload"
></q-btn>
<q-btn
v-if="g.hasCamera"
unelevated
icon="qr_code_scanner"
color="secondary"
@click="g.scanner = decodeQR"
:disable="
!g.wallet.canReceivePayments && !g.wallet.canSendPayments
"
>
<q-tooltip
><span v-text="$t('camera_tooltip')"></span
></q-tooltip>
</q-btn>
<div
v-if="g.settings.walletFeaturedButtonUrl"
class="float-right q-mt-sm q-ml-sm"
>
<q-btn
color="primary"
:label="g.settings.walletFeaturedButtonLabel"
:icon="g.settings.walletFeaturedButtonIcon || undefined"
size="sm"
:to="g.settings.walletFeaturedButtonUrl"
>
</q-btn>
</div>
<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
:wallet="g.wallet"
:payment-filter="paymentFilter"
></lnbits-payment-list>
</q-card-section>
</q-card>
</div>
<div
v-show="!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="g.settings.showAdSpace">
<q-card-section class="text-subtitle1">
<span v-text="g.settings.adSpaceTitle"></span>
<a :href="ad[0]" class="lnbits-ad" v-for="ad in g.settings.adSpace">
<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="totalBreakdown.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-card-section>
<div class="row items-start q-mb-md">
<div class="col">
<div
class="text-h4 text-bold"
v-text="primaryTotalBreakdownValue"
></div>
<div
v-if="secondaryTotalBreakdownValue"
class="text-h6 text-italic"
style="opacity: 0.75"
v-text="secondaryTotalBreakdownValue"
></div>
<div
class="text-caption text-grey-5"
v-text="selectedTotalBreakdownCount + ' payments'"
></div>
</div>
<q-btn
flat
dense
round
color="grey"
icon="refresh"
:loading="totalBreakdown.loading"
@click="fetchTotalBreakdown"
>
<q-tooltip>Refresh</q-tooltip>
</q-btn>
</div>
<q-inner-loading :showing="totalBreakdown.loading"></q-inner-loading>
<div v-if="hasFiatTotalBreakdown" class="q-mb-md">
<q-checkbox
v-model="totalBreakdown.selectedTypes"
val="bitcoin"
label="Bitcoin"
></q-checkbox>
<q-checkbox
v-model="totalBreakdown.selectedTypes"
val="fiat"
label="Fiat"
></q-checkbox>
</div>
<q-separator v-if="hasFiatTotalBreakdown" class="q-mb-md"></q-separator>
<div style="max-height: min(52vh, 420px); overflow-y: auto">
<q-list dense>
<q-item
v-for="tag in totalBreakdownTags"
:key="totalBreakdownTagKey(tag)"
tag="label"
v-ripple
>
<q-item-section side>
<q-checkbox
v-model="totalBreakdown.selectedTags"
:val="totalBreakdownTagKey(tag)"
></q-checkbox>
</q-item-section>
<q-item-section>
<q-item-label>
<q-badge
v-if="tag"
color="yellow"
text-color="black"
v-text="'#' + tag"
></q-badge>
<span v-else v-text="totalBreakdownTagLabel(tag)"></span>
</q-item-label>
<q-item-label
caption
v-text="totalBreakdownTagCount(tag) + ' payments'"
></q-item-label>
</q-item-section>
<q-item-section side>
<span
v-text="formatTotalBreakdownMsat(totalBreakdownTagMsat(tag))"
></span>
</q-item-section>
</q-item>
</q-list>
</div>
<div v-if="!totalBreakdown.loading && !totalBreakdown.rows.length">
<q-banner class="bg-transparent text-grey-5">
No completed payments.
</q-banner>
</div>
<div class="row q-mt-md">
<q-btn
v-close-popup
flat
color="grey"
class="q-ml-auto"
:label="$t('close')"
></q-btn>
</div>
</q-card-section>
</q-card>
</q-dialog>
<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="g.settings.hasHoldinvoice"
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>
<q-img src="/static/images/logos/lnbits.png"></q-img>
</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
v-if="g.user.fiat_providers?.includes('stripe')"
: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>
<q-img src="/static/images/stripe_logo.ico"></q-img>
</q-avatar>
</q-item-section>
<q-item-section>
<span v-text="$t('pay_with', {provider: 'Stripe'})"></span>
</q-item-section>
</q-item>
<q-separator
v-if="g.user.fiat_providers?.includes('paypal')"
></q-separator>
<q-item
v-if="g.user.fiat_providers?.includes('paypal')"
:active="receive.fiatProvider === 'paypal'"
@click="receive.fiatProvider = 'paypal'"
active-class="bg-teal-1 text-grey-8 text-weight-bold"
clickable
v-ripple
>
<q-item-section avatar>
<q-avatar color="blue-8" text-color="white">PP</q-avatar>
</q-item-section>
<q-item-section>
<span v-text="$t('pay_with', {provider: 'PayPal'})"></span>
</q-item-section>
</q-item>
<q-separator
v-if="g.user.fiat_providers?.includes('square')"
></q-separator>
<q-item
v-if="g.user.fiat_providers?.includes('square')"
:active="receive.fiatProvider === 'square'"
@click="receive.fiatProvider = 'square'"
active-class="bg-teal-1 text-grey-8 text-weight-bold"
clickable
v-ripple
>
<q-item-section avatar>
<q-avatar>
<q-img src="/static/images/square_logo.png"></q-img>
</q-avatar>
</q-item-section>
<q-item-section>
<span v-text="$t('pay_with', {provider: 'Square'})"></span>
</q-item-section>
</q-item>
<q-separator
v-if="g.user.fiat_providers?.includes('revolut')"
></q-separator>
<q-item
v-if="g.user.fiat_providers?.includes('revolut')"
:active="receive.fiatProvider === 'revolut'"
@click="receive.fiatProvider = 'revolut'"
active-class="bg-teal-1 text-grey-8 text-weight-bold"
clickable
v-ripple
>
<q-item-section avatar>
<q-avatar color="deep-orange-7" text-color="white">R</q-avatar>
</q-item-section>
<q-item-section>
<span v-text="$t('pay_with', {provider: 'Revolut'})"></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"
:href="receive.fiatPaymentReq"
:value="receive.fiatPaymentReq"
>
</lnbits-qrcode>
<lnbits-qrcode
v-else
:href="'lightning:' + receive.paymentReq"
:value="'LIGHTNING:' + receive.paymentReq.toUpperCase()"
>
</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="utils.formatBalance(parse.invoice.sat, g.denomination)"
></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="
utils.formatBalance(parse.invoice.sat, g.denomination)
"
></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="utils.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="utils.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"></q-img>
</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>
<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
@click="g.scanner = decodeQR"
round
unelevated
size="35px"
icon="qr_code_scanner"
class="text-white bg-primary z-top vertical-bottom absolute-center absolute"
>
</q-btn>
</div>
</template>