Compare commits

...
42 Commits
Author SHA1 Message Date
arcbtc 86a1313258 make cards highlight with neon 2024-12-20 02:02:57 +00:00
arcbtc 0fa8fca70e Merge remote-tracking branch 'origin/backgroundimage' into all_ui_elements 2024-12-20 01:56:18 +00:00
arcbtc b33518f075 Merge remote-tracking branch 'origin/calls_to_action' into all_ui_elements 2024-12-20 01:55:43 +00:00
arcbtc 3dce95968e fix 2024-12-20 01:55:00 +00:00
arcbtc 7c92788cd8 fix 2024-12-20 01:54:06 +00:00
arcbtc 2e31d1fd91 Merge branch 'better_fiat_tracking' into all_ui_elements 2024-12-20 01:50:11 +00:00
arcbtc 6c353f366d Merge branch 'card_flip' into all_ui_elements 2024-12-20 01:43:08 +00:00
Arc 902f3c7c6f format 2024-12-20 01:26:47 +00:00
arcbtc 7fbffa20d7 added to card, bg idea was terrible 2024-12-20 01:25:53 +00:00
Arc 0185f1e465 bundle/format 2024-12-19 23:52:27 +00:00
arcbtc 42b3616b03 Cards working nicely on mobile 2024-12-19 23:43:39 +00:00
arcbtc a446cd6c5b different way to do wallet cards 2024-12-19 22:47:37 +00:00
arcbtc 3c0c1ca930 small bug 2024-12-19 20:25:59 +00:00
arcbtc 1a4c706ed4 fixed merge bug 2024-12-19 20:10:15 +00:00
arcbtc 8969c0235d Merge remote-tracking branch 'origin/dev' into better_fiat_tracking 2024-12-19 20:08:37 +00:00
Arc 34ee5b273a format 2024-12-19 19:30:30 +00:00
arcbtc c7c1af9465 working 2024-12-19 19:29:32 +00:00
ArcandGitHub f7ed194e02 Merge branch 'dev' into backgroundimage 2024-12-19 16:24:44 +00:00
ArcandGitHub fd4b6af1de Merge branch 'dev' into calls_to_action 2024-12-19 16:24:29 +00:00
Arc 342bab5714 format 2024-12-19 16:06:28 +00:00
arcbtc 3d9a882066 More calls to actions 2024-12-19 16:05:37 +00:00
Arc 85b2f30a1a format 2024-12-19 13:38:25 +00:00
arcbtc 07eb0ecdde bug 2024-12-19 13:37:11 +00:00
arcbtc ea0e1e032e Merge branch 'better_fiat_tracking' of https://github.com/lnbits/lnbits into better_fiat_tracking 2024-12-19 13:36:30 +00:00
arcbtc b23e8ea781 bug 2024-12-19 13:35:53 +00:00
Arc fa61016ed1 bundle 2024-12-19 13:31:06 +00:00
arcbtc 6f44b755e4 default in create_wallet 2024-12-19 13:22:06 +00:00
Arc 5997fd74fa format and bundle 2024-12-18 23:28:42 +00:00
arcbtc fb5659de2a working, also fixed the button 2024-12-18 23:19:03 +00:00
arcbtc e13fd39e75 layout bug 2024-12-18 22:15:17 +00:00
arcbtc acd3b598b7 bug fix 2024-12-18 21:55:54 +00:00
arcbtc 1a6671b4b3 bug 2024-12-18 21:38:48 +00:00
arcbtc 7fc723c2dd added js 2024-12-18 21:33:02 +00:00
arcbtc 468a786f78 step3 2024-12-18 21:00:41 +00:00
arcbtc f74a7cbe1f step2 2024-12-18 20:56:28 +00:00
arcbtc ce9b63c54e step1 wallet card adjust 2024-12-18 20:53:55 +00:00
arcbtc 3dd1c6992b Trigger to default fiat conversion on
Like all the other wallets
2024-12-18 20:40:32 +00:00
Arc 1c49430ece fundle 2024-12-18 14:55:47 +00:00
arcbtc b14b3bbaad trans 2024-12-18 14:45:20 +00:00
arcbtc 020f6b6b1b account.html 2024-12-18 14:37:35 +00:00
arcbtc 0314453100 account.js 2024-12-18 14:35:40 +00:00
arcbtc 37dca56ccc base.js 2024-12-18 14:34:14 +00:00
15 changed files with 682 additions and 180 deletions
+1
View File
@@ -23,6 +23,7 @@ async def create_wallet(
user=user_id,
adminkey=uuid4().hex,
inkey=uuid4().hex,
currency="USD",
)
await (conn or db).insert("wallets", wallet)
return wallet
+6
View File
@@ -70,6 +70,7 @@
/>
</template>
</q-input>
<q-select
v-else-if="['component', 'response_code','request_method'].includes(col.name)"
v-model="searchData[col.name]"
@@ -82,6 +83,11 @@
<span v-else v-text="col.label"></span>
</q-th>
<q-th>
<q-btn flat round icon="settings" tag="a" href="/admin#audit"
><q-tooltip v-text="$t('admin_settings')"></q-tooltip
></q-btn>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
+26
View File
@@ -286,6 +286,16 @@
</div>
</q-tab-panel>
<q-tab-panel name="theme">
<q-btn
v-if="g.user.admin"
class="absolute-top-right"
flat
round
icon="settings"
tag="a"
href="/admin#site_customisation"
><q-tooltip v-text="$t('admin_settings')"></q-tooltip
></q-btn>
<div class="row q-mb-md">
<div class="col-4"><span v-text="$t('language')"></span></div>
<div class="col-8">
@@ -412,6 +422,22 @@
</q-btn>
</div>
</div>
<div class="row q-mb-md">
<div class="col-4">
<span v-text="$t('background_image')"></span>
</div>
<div class="col-8">
<q-input
v-model="backgroundImage"
:label="$t('background_image')"
@update:model-value="backgroundImageFunc"
>
<q-tooltip
><span v-text="$t('background_image')"></span
></q-tooltip>
</q-input>
</div>
</div>
<div class="row q-mb-md">
<div class="col-4">
<span v-text="$t('gradient_background')"></span>
@@ -49,6 +49,15 @@
v-text="$t('new_version') + ` (${updatableExtensions?.length})`"
></span>
</q-badge>
<q-btn
v-if="g.user.admin"
flat
round
icon="settings"
tag="a"
href="/admin#extensions"
><q-tooltip v-text="$t('admin_settings')"></q-tooltip
></q-btn>
</q-tabs>
</div>
</div>
+137 -101
View File
@@ -25,102 +25,124 @@
} : ''"
>
{% endif %}
<q-scroll-area
v-if="!mobileSimple"
style="
height: 115px;
width: 100%;
overflow-x: auto;
overflow-y: hidden;
"
>
<div class="row no-wrap q-gutter-md q-pr-md">
<q-card
v-for="wallet in g.user.wallets"
:key="wallet.id"
class="wallet-list-card"
bordered
tag="a"
:href="wallet.url"
:style="
g.wallet && g.wallet.id === wallet.id
? `border: 1px solid ${primaryColor}; width: 250px; text-decoration: none;`
: 'width: 250px; text-decoration: none;'
"
:class="{
'active-wallet-card': g.wallet && g.wallet.id === wallet.id
}"
>
<q-card-section>
<div class="row items-center">
<q-avatar
size="lg"
:color="
g.wallet && g.wallet.id === wallet.id
? $q.dark.isActive
? 'primary'
: 'primary'
: 'grey-5'
"
>
<q-icon
name="flash_on"
:size="$q.dark.isActive ? '21px' : '20px'"
:color="$q.dark.isActive ? 'black' : 'grey-3'"
></q-icon>
</q-avatar>
<div
class="text-h6 q-pl-md"
:class="{
'text-bold': g.wallet && g.wallet.id === wallet.id
}"
v-text="wallet.name"
></div>
</div>
<div class="row items-center q-pt-sm">
<h6 class="q-my-none text-no-wrap">
<strong v-text="wallet.fsat"></strong>
<small> {{LNBITS_DENOMINATION}}</small>
</h6>
</div>
</q-card-section>
</q-card>
</div>
</q-scroll-area>
<div class="row">
<div class="col-lg-9 col-12">
<q-card
:style="$q.screen.lt.md ? {
background: $q.screen.lt.md ? 'none !important': ''
, boxShadow: $q.screen.lt.md ? 'none !important': ''
, margin: $q.screen.lt.md && mobileSimple ? 'auto !important': ''
, width: $q.screen.lt.md && mobileSimple ? '90% !important': ''
} : ''"
>
<q-card-section>
<h3 class="q-my-none text-no-wrap">
<div class="row">
<div class="col-1" style="max-width: 30px">
<q-btn
v-if="update.currency"
@click="swapBalancePriority"
style="height: 50px"
class="q-mt-lg"
flat
dense
icon="swap_vert"
></q-btn>
</div>
<div class="col-11">
<div
class="column"
:class="{
'q-pt-sm': update.currency,
'q-pt-lg': !update.currency
}"
v-if="!isPrioritySwapped"
style="height: 90px"
>
<div class="col-7">
<div class="row">
<div class="col-auto">
<div class="text-h3 q-my-none text-no-wrap">
<strong v-text="formattedBalance"></strong>
<small> {{LNBITS_DENOMINATION}}</small>
</div>
</div>
<div class="col-auto">
<lnbits-update-balance
v-if="$q.screen.lt.lg"
:wallet_id="this.g.wallet.id"
@credit-value="handleBalanceUpdate"
class="q-ml-md"
:callback="updateBalanceCallback"
:small_btn="true"
></lnbits-update-balance>
</h3>
<div class="row">
<div class="col">
<div v-if="g.wallet.currency">
</div>
</div>
</div>
<div class="col-2">
<div v-if="update.currency">
<span
class="text-h5 text-italic"
v-text="formattedFiatBalance"
v-text="formattedFiatAmount"
style="opacity: 0.75"
></span>
</div>
</div>
<div class="col">
</div>
<div
class="column"
v-if="isPrioritySwapped"
:class="{
'q-pt-sm': update.currency,
'q-pt-lg': !update.currency
}"
style="height: 100px"
>
<div class="col-7">
<div class="row">
<div class="col-auto">
<div
v-if="update.currency"
class="text-h3 q-my-none text-no-wrap"
>
<strong v-text="formattedFiatAmount"></strong>
</div>
</div>
<div class="col-auto">
<lnbits-update-balance
v-if="$q.screen.lt.lg"
:wallet_id="this.g.wallet.id"
:callback="updateBalanceCallback"
:small_btn="true"
></lnbits-update-balance>
</div>
</div>
</div>
<div class="col-2">
<span
class="text-h5 text-italic"
style="opacity: 0.75"
v-text="formattedBalance + ' {{LNBITS_DENOMINATION}}'"
>
</span>
</div>
</div>
<div
class="absolute-right q-pa-md"
v-if="$q.screen.gt.md && update.currency"
>
<div class="text-bold text-italic">BTC Price</div>
<span
class="text-bold text-italic"
v-text="formattedExchange"
></span>
</div>
<q-btn
v-if="$q.screen.lt.md"
@click="mobileSimple = !mobileSimple"
color="primary"
class="float-right lt-md"
class="q-ml-xl absolute-right"
dense
size="sm"
style="height: 20px; margin-top: 75px"
flat
:icon="mobileSimple ? 'unfold_more' : 'unfold_less'"
:label="mobileSimple ? $t('more') : $t('less')"
@@ -133,35 +155,49 @@
<q-btn
unelevated
color="primary"
class="full-width"
class="q-mr-md"
@click="showParseDialog"
:label="$t('paste_request')"
></q-btn>
</div>
<div class="col">
<q-btn
unelevated
color="primary"
class="full-width"
class="q-mr-md"
@click="showReceiveDialog"
:label="$t('create_invoice')"
></q-btn>
</div>
<div class="col">
<q-btn
unelevated
color="secondary"
icon="photo_camera"
@click="showCamera"
:label="$t('scan')"
>
<q-btn unelevated color="secondary" icon="qr_code_scanner">
<q-tooltip
><span v-text="$t('camera_tooltip')"></span
></q-tooltip>
</q-btn>
<lnbits-update-balance
v-if="$q.screen.gt.md"
:wallet_id="this.g.wallet.id"
:callback="updateBalanceCallback"
:small_btn="false"
></lnbits-update-balance>
</div>
</div>
</q-card>
</div>
<div class="col-3" v-show="$q.screen.gt.md">
<q-card class="q-ml-md q-pa-none" style="height: 172px">
<q-card-section style="padding: 0">
<canvas
ref="transactionChart"
style="
height: 172px;
width: 100%;
margin: 0;
padding: 0;
transform: translate(-2%, 4%);
"
></canvas>
</q-card-section>
</q-card>
</div>
</div>
<q-card
:style="
$q.screen.lt.md
@@ -178,17 +214,14 @@
:update="updatePayments"
:wallet="this.g.wallet"
:mobile-simple="mobileSimple"
/>
></payment-list>
</q-card-section>
</q-card>
</div>
{% if HIDE_API %}
<div class="col-12 col-md-4 q-gutter-y-md">
{% else %}
<div
v-if="!mobileSimple || $q.screen.gt.sm"
class="col-12 col-md-5 q-gutter-y-md"
>
<div v-if="!mobileSimple || $q.screen.gt.sm" class="col-12 col-md-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-mt-none q-mb-sm">
@@ -277,24 +310,27 @@
<q-card>
<q-card-section>
<div style="max-width: 360px">
<div class="row">
<div class="col-auto">
<q-toggle
v-model="fiatTracking"
color="primary"
@update:model-value="handleFiatTracking"
></q-toggle>
</div>
<div class="col">
<q-select
filled
dense
clearable
v-model="update.currency"
type="text"
:label="$t('currency')"
:label="!fiatTracking ? 'Disabled' : $t('currency')"
:options="receive.units.filter((u) => u !== 'sat')"
:disable="!fiatTracking"
></q-select>
</div>
<q-btn
:disable="!update.name.length"
unelevated
class="q-mt-sm"
color="primary"
:label="$t('update_currency')"
@click="updateWallet({ currency: update.currency || '' })"
></q-btn>
</div>
</div>
</q-card-section>
</q-card>
</q-expansion-item>
File diff suppressed because one or more lines are too long
+18 -8
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -26,6 +26,7 @@ window.localisation.en = {
close_channel: 'Close Channel',
close: 'Close',
restart: 'Restart server',
background_image: 'Background Image',
save: 'Save',
save_tooltip: 'Save your changes',
credit_debit: 'Credit / Debit',
@@ -254,6 +255,7 @@ window.localisation.en = {
gradient_background: 'Gradient Background',
language: 'Language',
color_scheme: 'Color Scheme',
admin_settings: 'Admin Settings',
extension_cost: 'This release requires a payment of minimum {cost} sats.',
extension_paid_sats: 'You have already paid {paid_sats} sats.',
release_details_error: 'Cannot get the release details.',
+78 -19
View File
@@ -12,7 +12,12 @@ window.app = Vue.createApp({
'confettiFireworks',
'confettiStars'
],
borderOptions: ['retro-border', 'hard-border', 'no-border'],
borderOptions: [
'retro-border',
'hard-border',
'neon-border',
'no-border'
],
tab: 'user',
credentialsData: {
show: false,
@@ -40,28 +45,72 @@ window.app = Vue.createApp({
}
},
applyGradient() {
darkBgColor = this.$q.localStorage.getItem('lnbits.darkBgColor')
primaryColor = this.$q.localStorage.getItem('lnbits.primaryColor')
if (this.gradientChoice) {
this.$q.localStorage.set('lnbits.gradientBg', this.gradientChoice)
if (this.$q.localStorage.getItem('lnbits.gradientBg')) {
if (!this.$q.dark.isActive) {
this.toggleDarkMode()
}
this.setColors()
darkBgColor = this.$q.localStorage.getItem('lnbits.darkBgColor')
primaryColor = this.$q.localStorage.getItem('lnbits.primaryColor')
const gradientStyle = `linear-gradient(to bottom right, ${LNbits.utils.hexDarken(String(primaryColor), -70)}, #0a0a0a)`
document.body.style.setProperty(
'background-image',
gradientStyle,
'important'
)
const gradientStyleCards = `background-color: ${LNbits.utils.hexAlpha(String(darkBgColor), 0.4)} !important`
const gradientStyleCards = `background-color: ${LNbits.utils.hexAlpha(String(darkBgColor), 0.55)} !important`
const style = document.createElement('style')
style.innerHTML =
`body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark), body.body${this.$q.dark.isActive ? '--dark' : ''} .q-header, body.body${this.$q.dark.isActive ? '--dark' : ''} .q-drawer { ${gradientStyleCards} }` +
`body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"].body--dark{background: ${LNbits.utils.hexDarken(String(primaryColor), -88)} !important; }` +
`[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card--dark{background: ${String(darkBgColor)} !important;} }`
style.innerHTML = `
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark),
body.body${this.$q.dark.isActive ? '--dark' : ''} .q-header,
body.body${this.$q.dark.isActive ? '--dark' : ''} .q-drawer,
body.body${this.$q.dark.isActive ? '--dark' : ''} .q-tab-panels {
${gradientStyleCards}
}
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"].body--dark {
background: ${LNbits.utils.hexDarken(String(primaryColor), -88)} !important;
}
[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card--dark {
background: ${String(darkBgColor)} !important;
}
`
document.head.appendChild(style)
}
},
applyBackgroundImage() {
if (this.backgroundImage) {
this.$q.localStorage.set('lnbits.backgroundImage', this.backgroundImage)
this.gradientChoice = true
this.applyGradient()
}
let bgImage = this.$q.localStorage.getItem('lnbits.backgroundImage')
if (bgImage) {
this.backgroundImage = bgImage
const style = document.createElement('style')
style.innerHTML = `
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"]::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url(${bgImage});
background-size: cover;
filter: blur(8px);
z-index: -1;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-page-container {
backdrop-filter: none; /* Ensure the page content is not affected */
}`
document.head.appendChild(style)
this.$q.localStorage.set('lnbits.gradientBg', true)
} else {
this.$q.localStorage.set('lnbits.gradientBg', false)
}
},
applyBorder() {
@@ -74,6 +123,9 @@ window.app = Vue.createApp({
if (borderStyle == 'hard-border') {
borderStyleCSS = `box-shadow: 0 0 0 1px rgba(0,0,0,.12), 0 0 0 1px #ffffff47; border: none;`
}
if (borderStyle == 'neon-border') {
borderStyleCSS = `border: 2px solid ${this.$q.localStorage.getItem('lnbits.primaryColor')}; box-shadow: none;`
}
if (borderStyle == 'no-border') {
borderStyleCSS = `box-shadow: none; border: none;`
}
@@ -87,6 +139,8 @@ window.app = Vue.createApp({
toggleGradient() {
this.gradientChoice = !this.gradientChoice
this.applyGradient()
this.$q.localStorage.set('lnbits.backgroundImage', '')
this.applyBorder()
if (!this.gradientChoice) {
window.location.reload()
}
@@ -95,13 +149,17 @@ window.app = Vue.createApp({
reactionChoiceFunc() {
this.$q.localStorage.set('lnbits.reactions', this.reactionChoice)
},
changeColor(newValue) {
backgroundImageFunc() {
this.$q.localStorage.set('lnbits.backgroundImage', this.backgroundImage)
this.applyBackgroundImage()
},
changeColor: function (newValue) {
document.body.setAttribute('data-theme', newValue)
this.$q.localStorage.set('lnbits.theme', newValue)
this.setColors()
if (this.$q.localStorage.getItem('lnbits.gradientBg')) {
this.applyGradient()
}
this.applyBackgroundImage()
this.applyBorder()
},
async updateAccount() {
try {
@@ -208,11 +266,12 @@ window.app = Vue.createApp({
} catch (e) {
LNbits.utils.notifyApiError(e)
}
if (this.$q.localStorage.getItem('lnbits.gradientBg')) {
this.applyGradient()
const hash = window.location.hash.replace('#', '')
if (hash) {
this.tab = hash
}
if (this.$q.localStorage.getItem('lnbits.border')) {
this.applyGradient()
this.applyBackgroundImage()
this.applyBorder()
}
}
})
+4
View File
@@ -122,6 +122,10 @@ window.app = Vue.createApp({
this.getSettings()
this.getAudit()
this.balance = +'{{ balance|safe }}'
const hash = window.location.hash.replace('#', '')
if (hash) {
this.tab = hash
}
},
computed: {
lnbitsVersion() {
+66 -5
View File
@@ -453,8 +453,10 @@ window.windowMixin = {
data() {
return {
toggleSubs: true,
walletFlip: true,
reactionChoice: 'confettiBothSides',
borderChoice: '',
backgroundImage: '',
gradientChoice:
this.$q.localStorage.getItem('lnbits.gradientBg') || false,
isUserAuthorized: false,
@@ -472,6 +474,17 @@ window.windowMixin = {
},
methods: {
flipWallets(smallScreen) {
this.walletFlip = !this.walletFlip
if (this.walletFlip && smallScreen) {
this.g.visibleDrawer = false
}
this.$q.localStorage.set('lnbits.walletFlip', this.walletFlip)
},
simpleMobile() {
this.mobileSimple = !this.mobileSimple
this.$q.localStorage.set('lnbits.mobileSimple', this.mobileSimple)
},
changeColor(newValue) {
document.body.setAttribute('data-theme', newValue)
this.$q.localStorage.set('lnbits.theme', newValue)
@@ -487,12 +500,54 @@ window.windowMixin = {
gradientStyle,
'important'
)
const gradientStyleCards = `background-color: ${LNbits.utils.hexAlpha(String(darkBgColor), 0.4)} !important`
const gradientStyleCards = `background-color: ${LNbits.utils.hexAlpha(String(darkBgColor), 0.55)} !important`
const style = document.createElement('style')
style.innerHTML =
`body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark), body.body${this.$q.dark.isActive ? '--dark' : ''} .q-header, body.body${this.$q.dark.isActive ? '--dark' : ''} .q-drawer { ${gradientStyleCards} }` +
`body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"].body--dark{background: ${LNbits.utils.hexDarken(String(primaryColor), -88)} !important; }` +
`[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card--dark{background: ${String(darkBgColor)} !important;} }`
style.innerHTML = `
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card:not(.q-dialog .q-card, .lnbits__dialog-card, .q-dialog-plugin--dark),
body.body${this.$q.dark.isActive ? '--dark' : ''} .q-header,
body.body${this.$q.dark.isActive ? '--dark' : ''} .q-drawer,
body.body${this.$q.dark.isActive ? '--dark' : ''} .q-tab-panels {
${gradientStyleCards}
}
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"].body--dark {
background: ${LNbits.utils.hexDarken(String(primaryColor), -88)} !important;
}
[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-card--dark {
background: ${String(darkBgColor)} !important;
}
`
document.head.appendChild(style)
}
},
applyBackgroundImage() {
if (this.backgroundImage) {
this.$q.localStorage.set('lnbits.backgroundImage', this.backgroundImage)
}
let bgImage = this.$q.localStorage.getItem('lnbits.backgroundImage')
if (bgImage) {
const style = document.createElement('style')
style.innerHTML = `
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"]::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url(${bgImage});
background-size: cover;
filter: blur(8px);
z-index: -1;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
body[data-theme="${this.$q.localStorage.getItem('lnbits.theme')}"] .q-page-container {
backdrop-filter: none; /* Ensure the page content is not affected */
}`
document.head.appendChild(style)
}
},
@@ -510,6 +565,9 @@ window.windowMixin = {
if (borderStyle == 'hard-border') {
borderStyleCSS = `box-shadow: 0 0 0 1px rgba(0,0,0,.12), 0 0 0 1px #ffffff47; border: none;`
}
if (borderStyle == 'neon-border') {
borderStyleCSS = `border: 2px solid ${this.$q.localStorage.getItem('lnbits.primaryColor')}; box-shadow: none;`
}
if (borderStyle == 'no-border') {
borderStyleCSS = `box-shadow: none; border: none;`
}
@@ -686,6 +744,7 @@ window.windowMixin = {
this.applyGradient()
this.applyBorder()
this.applyBackgroundImage()
if (window.user) {
this.g.user = Object.freeze(window.LNbits.map.user(window.user))
@@ -700,6 +759,8 @@ window.windowMixin = {
}
await this.checkUsrInUrl()
this.themeParams()
this.walletFlip = this.$q.localStorage.getItem('lnbits.walletFlip')
this.mobileSimple = this.$q.localStorage.getItem('lnbits.mobileSimple')
}
}
+1 -1
View File
@@ -484,7 +484,7 @@ window.app.component('lnbits-dynamic-chips', {
window.app.component('lnbits-update-balance', {
template: '#lnbits-update-balance',
mixins: [window.windowMixin],
props: ['wallet_id', 'credit-value'],
props: ['wallet_id', 'credit-value', 'small_btn'],
computed: {
denomination() {
return LNBITS_DENOMINATION
+189 -22
View File
@@ -7,6 +7,7 @@ window.app = Vue.createApp({
origin: window.location.origin,
wallet: LNbits.map.wallet(window.wallet),
user: LNbits.map.user(window.user),
mobileSimple: false,
exportUrl: `${window.location.origin}/wallet?usr=${window.user.id}&wal=${window.wallet.id}`,
baseUrl: `${window.location.protocol}//${window.location.host}/`,
receive: {
@@ -50,7 +51,6 @@ window.app = Vue.createApp({
},
balance: parseInt(wallet.balance_msat / 1000),
fiatBalance: 0,
mobileSimple: false,
update: {
name: null,
currency: null
@@ -59,7 +59,16 @@ window.app = Vue.createApp({
adminkeyHidden: true,
hasNfc: false,
nfcReaderAbortController: null,
primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor')
isPrioritySwapped: false,
fiatTracking: false,
formattedFiatAmount: 0,
exchangeRate: 0,
formattedExchange: null,
ignoreWatcher: true,
primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor'),
transactions: [],
transactionChart: null,
paymentsRef: null
}
},
computed: {
@@ -70,14 +79,6 @@ window.app = Vue.createApp({
return LNbits.utils.formatSat(this.balance || this.g.wallet.sat)
}
},
formattedFiatBalance() {
if (this.fiatBalance) {
return LNbits.utils.formatCurrency(
this.fiatBalance.toFixed(2),
this.g.wallet.currency
)
}
},
canPay() {
if (!this.parse.invoice) return false
return this.parse.invoice.sat <= this.balance
@@ -97,6 +98,95 @@ window.app = Vue.createApp({
}
},
methods: {
formatFiatAmount(amount, currency) {
this.update.currency = currency
this.formattedFiatAmount = LNbits.utils.formatCurrency(
amount.toFixed(2),
currency
)
this.formattedExchange = LNbits.utils.formatCurrency(
this.exchangeRate,
currency
)
},
computeCumulativeBalance(transactions) {
let balance = 0
return transactions.map(transaction => {
balance += transaction.amount // Use the amount field for the balance
return balance
})
},
initCharts() {
this.transactionChart = new Chart(
this.$refs.transactionChart.getContext('2d'),
{
type: 'line',
options: {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: 0,
margin: 0
},
plugins: {
legend: {
display: false
},
title: {
display: false
},
tooltip: {
enabled: true, // Enable tooltips to show data on hover
callbacks: {
// Custom tooltip callback to show amount and time
label: tooltipItem => {
const index = tooltipItem.dataIndex
const transaction = this.transactions[index] // Match tooltip point with transaction
const amount = transaction.amount
return [
`Balance: ${tooltipItem.raw / 1000}sats`, // Display cumulative balance
`Amount: ${amount / 1000}sats`
]
}
}
}
},
elements: {
point: {
radius: 4, // Points will now be visible
hoverRadius: 6 // Increase point size on hover
}
},
scales: {
x: {
display: false
},
y: {
display: false
}
}
},
data: {
labels: this.transactions.map(
tx => new Date(tx.time).toLocaleString() // Use time for labels
),
datasets: [
{
label: 'Cumulative Balance',
data: this.computeCumulativeBalance(this.transactions),
backgroundColor: LNbits.utils.hexAlpha(this.primaryColor, 0.3),
borderColor: this.primaryColor,
borderWidth: 2,
fill: true,
tension: 0.2
}
]
}
}
)
},
msatoshiFormat(value) {
return LNbits.utils.formatSat(value / 1000)
},
@@ -515,7 +605,6 @@ window.app = Vue.createApp({
type: 'positive',
timeout: 3500
})
window.location.reload()
})
.catch(err => {
LNbits.utils.notifyApiError(err)
@@ -539,15 +628,23 @@ window.app = Vue.createApp({
})
})
},
updateFiatBalance() {
if (!this.g.wallet.currency) return 0
updateFiatBalance(currency) {
// set rate from local storage to avoid clunky api calls
this.exchangeRate = this.$q.localStorage.getItem('lnbits.exchangeRate')
this.fiatBalance =
(this.exchangeRate / 100000000) * (this.balance || this.g.wallet.sat)
this.formatFiatAmount(this.fiatBalance, currency)
// make api call
LNbits.api
.request('POST', `/api/v1/conversion`, null, {
amount: this.balance || this.g.wallet.sat,
to: this.g.wallet.currency
})
.request('GET', `/api/v1/rate/` + currency, null)
.then(response => {
this.fiatBalance = response.data[this.g.wallet.currency]
this.fiatBalance =
(response.data.price / 100000000) *
(this.balance || this.g.wallet.sat)
this.exchangeRate = response.data.price.toFixed(2)
this.fiatTracking = true
this.formatFiatAmount(this.fiatBalance, currency)
this.$q.localStorage.set('lnbits.exchangeRate', this.exchangeRate)
})
.catch(e => console.error(e))
},
@@ -652,6 +749,48 @@ window.app = Vue.createApp({
dismissPaymentMsg()
LNbits.utils.notifyApiError(err)
})
},
handleWallet(wallet) {
console.log('Wallet from child:', wallet)
},
fetchPayments() {
return LNbits.api
.getPayments(
this.g.wallet,
'sortby=time&direction=asc&status[ne]=failed'
)
.then(response => {
console.log(response.data.data)
for (let payment of response.data.data) {
record = {
payment_hash: payment.payment_hash,
amount: payment.amount,
description: payment.description,
time: payment.time
}
this.transactions.push(record)
}
console.log(this.transactions)
})
.catch(err => {
this.paymentsTable.loading = false
LNbits.utils.notifyApiError(err)
})
},
swapBalancePriority() {
this.isPrioritySwapped = !this.isPrioritySwapped
this.$q.localStorage.setItem(
'lnbits.isPrioritySwapped',
this.isPrioritySwapped
)
},
handleFiatTracking() {
if (this.fiatTracking === false) {
this.update.currency = ''
this.$q.localStorage.setItem('lnbits.isPrioritySwapped', false)
this.isPrioritySwapped = false
this.$q.localStorage.remove(`lnbits.exchangeRate`)
}
}
},
created() {
@@ -665,20 +804,39 @@ window.app = Vue.createApp({
if (this.$q.screen.lt.md) {
this.mobileSimple = true
}
setTimeout(() => {
this.ignoreWatcher = false
}, 3000)
if (this.g.wallet.currency) {
this.fiatTracking = true
this.updateFiatBalance(this.g.wallet.currency)
}
this.update.name = this.g.wallet.name
this.update.currency = this.g.wallet.currency
this.receive.units = ['sat', ...window.currencies]
this.updateFiatBalance()
this.fetchPayments().then(() => {
this.initCharts()
})
},
watch: {
'$q.screen.gt.sm'(value) {
if (value == true) {
this.mobileSimple = false
}
},
updatePayments() {
this.updateFiatBalance()
this.fetchBalance()
},
'update.currency'(newValue) {
if (this.ignoreWatcher || this.update.currency == '') return
this.updateWallet({currency: newValue})
this.updateFiatBalance(newValue)
},
'$q.screen.gt.sm'(value) {
if (value == true) {
this.mobileSimple = false
}
}
},
},
mounted() {
// show disclaimer
@@ -686,6 +844,15 @@ window.app = Vue.createApp({
this.disclaimerDialog.show = true
this.$q.localStorage.set('lnbits.disclaimerShown', true)
}
// check blanace priority
if (this.$q.localStorage.getItem('lnbits.isPrioritySwapped')) {
this.isPrioritySwapped = this.$q.localStorage.getItem(
'lnbits.isPrioritySwapped'
)
} else {
this.isPrioritySwapped = false
this.$q.localStorage.setItem('lnbits.isPrioritySwapped', false)
}
// listen to incoming payments
LNbits.events.onInvoicePaid(this.g.wallet, data => {
console.log('Payment received:', data.payment.payment_hash)
+93 -1
View File
@@ -131,6 +131,21 @@
<q-item-label> </q-item-label>
</q-item-section>
</q-item>
<q-item tag="a" href="/account#theme" clickable v-close-popup
><q-item-section>
<q-icon
:name="$q.dark.isActive ? 'dark_mode' : 'light_mode'"
/>
</q-item-section>
<q-item-section>
<q-item-label>
<span v-text="$t('theme')"></span>
</q-item-label>
</q-item-section>
<q-item-section>
<q-item-label> </q-item-label>
</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup @click="logout"
><q-item-section>
@@ -158,7 +173,22 @@
show-if-above
:elevated="$q.screen.lt.md"
>
<lnbits-wallet-list :balance="balance"></lnbits-wallet-list>
<q-item-label
:style="$q.dark.isActive ? 'color:rgba(255, 255, 255, 0.64)' : ''"
class="q-item__label q-item__label--header"
header
v-text="$t('wallets')"
></q-item-label>
<q-btn
flat
:icon=" walletFlip ? 'keyboard_arrow_right' : 'keyboard_arrow_down'"
class="absolute-top-right"
@click="flipWallets($q.screen.lt.md)"
></q-btn>
<lnbits-wallet-list
v-if="!walletFlip"
:balance="balance"
></lnbits-wallet-list>
<lnbits-manage
:show-admin="'{{LNBITS_ADMIN_UI}}' == 'True'"
@@ -172,6 +202,68 @@
{% endblock %} {% block page_container %}
<q-page-container>
<q-page class="q-px-md q-py-lg" :class="{'q-px-lg': $q.screen.gt.xs}">
<q-scroll-area
v-if="walletFlip"
style="
height: 130px;
width: 100%;
overflow-x: auto;
overflow-y: hidden;
"
>
<div class="row no-wrap q-gutter-md q-pr-md">
<q-card
v-for="wallet in g.user.wallets"
:key="wallet.id"
class="wallet-list-card"
tag="a"
:href="wallet.url"
:style="
g.wallet && g.wallet.id === wallet.id
? `border: 1px solid ${primaryColor}; width: 250px; text-decoration: none;`
: 'width: 250px; text-decoration: none; border: 0px'
"
:class="{
'active-wallet-card': g.wallet && g.wallet.id === wallet.id
}"
>
<q-card-section>
<div class="row items-center">
<q-avatar
size="lg"
:color="
g.wallet && g.wallet.id === wallet.id
? $q.dark.isActive
? 'primary'
: 'primary'
: 'grey-5'
"
>
<q-icon
name="flash_on"
:size="$q.dark.isActive ? '21px' : '20px'"
:color="$q.dark.isActive ? 'black' : 'grey-3'"
></q-icon>
</q-avatar>
<div
class="text-h6 q-pl-md"
:class="{
'text-bold': g.wallet && g.wallet.id === wallet.id
}"
v-text="wallet.name"
></div>
</div>
<div class="row items-center q-pt-sm">
<h6 class="q-my-none text-no-wrap">
<strong v-text="wallet.fsat"></strong>
<small> {{LNBITS_DENOMINATION}}</small>
</h6>
</div>
</q-card-section>
</q-card>
</div>
</q-scroll-area>
{% block page %}{% endblock %}
</q-page>
</q-page-container>
+32 -3
View File
@@ -4,7 +4,6 @@
dense
class="lnbits-drawer__q-list"
>
<q-item-label header v-text="$t('wallets')"></q-item-label>
<q-item
v-for="wallet in wallets"
:key="wallet.id"
@@ -53,7 +52,6 @@
side
v-show="activeWallet && activeWallet.id === wallet.id"
>
<q-icon name="chevron_right" color="grey-5" size="md"></q-icon>
</q-item-section>
</q-item>
<q-item clickable @click="showForm = !showForm">
@@ -506,7 +504,38 @@
</template>
<template id="lnbits-update-balance">
<q-btn v-if="admin" :label="$t('credit_debit')" color="secondary" size="sm">
<q-btn
v-if="admin && small_btn"
flat
round
color="primary"
size="sm"
icon="add"
>
<q-popup-edit class="bg-accent text-white" v-slot="scope" v-model="credit">
<q-input
filled
:label="$t('credit_label', {denomination: denomination})"
v-model="scope.value"
dense
autofocus
@keyup.enter="scope.set"
>
<template v-slot:append>
<q-icon name="edit" />
</template>
</q-input>
</q-popup-edit>
<q-tooltip v-text="$t('credit_hint')"></q-tooltip>
</q-btn>
<q-btn
v-if="admin && !small_btn"
color="primary"
:label="$t('credit_debit')"
class="float-right q-mt-sm"
size="sm"
>
<q-popup-edit class="bg-accent text-white" v-slot="scope" v-model="credit">
<q-input
filled