feat: move wallet.html to vue component. FINAL DynamicComponent pr (#3559)

This commit is contained in:
dni ⚡
2025-11-25 11:45:53 +01:00
committed by GitHub
parent 0c6e8394c8
commit 33e2fc2ea8
19 changed files with 928 additions and 1128 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
@@ -1,13 +1,5 @@
window.app.component('lnbits-admin-server', {
props: ['form-data'],
template: '#lnbits-admin-server',
mixins: [window.windowMixin],
data() {
return {
currencies: []
}
},
async created() {
this.currencies = await LNbits.api.getCurrencies()
}
mixins: [window.windowMixin]
})
+3
View File
@@ -25,11 +25,14 @@ window.g = Vue.reactive({
newWalletType: 'lightning',
updatePayments: false,
updatePaymentsHash: '',
currencies: WINDOW_SETTINGS.LNBITS_CURRENCIES ?? [],
allowedCurrencies: WINDOW_SETTINGS.LNBITS_ALLOWED_CURRENCIES ?? [],
locale: localStore('lnbits.lang', navigator.languages[1] ?? 'en'),
disclaimerShown: localStore('lnbits.disclaimerShown', false),
isFiatPriority: localStore('lnbits.isFiatPriority', false),
mobileSimple: localStore('lnbits.mobileSimple', true),
walletFlip: localStore('lnbits.walletFlip', false),
lastActiveWallet: localStore('lnbits.lastActiveWallet', null),
darkChoice: localStore('lnbits.darkMode', true),
themeChoice: localStore('lnbits.theme', WINDOW_SETTINGS.LNBITS_DEFAULT_THEME),
borderChoice: localStore(
+5 -141
View File
@@ -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 = [
{
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',
name: 'Node',
@@ -159,6 +19,11 @@ const routes = [
name: 'Audit',
component: PageAudit
},
{
path: '/wallet',
name: 'Wallet',
component: PageWallet
},
{
path: '/wallets',
name: 'Wallets',
@@ -236,5 +101,4 @@ window.app.use(window.i18n)
window.app.provide('g', g)
window.app.use(window.router)
window.app.component('DynamicComponent', DynamicComponent)
window.app.mount('#vue')
@@ -1,4 +1,5 @@
window.WalletPageLogic = {
window.PageWallet = {
template: '#page-wallet',
mixins: [window.windowMixin],
data() {
return {
@@ -104,9 +105,6 @@ window.WalletPageLogic = {
showCamera() {
this.parse.camera.show = true
},
focusInput(el) {
this.$nextTick(() => this.$refs[el].focus())
},
showReceiveDialog() {
this.receive.show = true
this.receive.status = 'pending'
@@ -121,7 +119,6 @@ window.WalletPageLogic = {
: 'sat'
this.receive.minMax = [0, 2100000000000000]
this.receive.lnurl = null
this.focusInput('setAmount')
},
onReceiveDialogHide() {
if (this.hasNfc) {
@@ -140,7 +137,6 @@ window.WalletPageLogic = {
this.parse.data.internalMemo = null
this.parse.data.paymentChecker = null
this.parse.camera.show = false
this.focusInput('textArea')
},
closeParseDialog() {
setTimeout(() => {
@@ -645,8 +641,24 @@ window.WalletPageLogic = {
this.decodeRequest()
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: {
'g.lastActiveWallet'(val) {
this.$q.localStorage.setItem('lnbits.lastActiveWallet', val)
},
'g.updatePayments'() {
this.parse.show = false
if (this.receive.paymentHash === this.g.updatePaymentsHash) {
@@ -667,9 +679,13 @@ window.WalletPageLogic = {
}
},
'g.wallet'() {
if (this.g.wallet.currency && this.g.fiatTracking) {
if (this.g.wallet.currency) {
this.g.fiatTracking = true
this.g.fiatBalance =
(this.g.exchangeRate / 100000000) * this.g.wallet.sat
} else {
this.g.fiatBalance = 0
this.g.fiatTracking = false
}
},
'g.isFiatPriority'() {
+1 -1
View File
@@ -74,7 +74,7 @@ window.PageWallets = {
}
},
goToWallet(walletId) {
window.location = `/wallet?wal=${walletId}`
this.$router.push({path: '/wallet', query: {wal: walletId}})
},
formattedFiatAmount(amount, currency) {
return LNbits.utils.formatCurrency(Number(amount).toFixed(2), currency)
+1
View File
@@ -68,6 +68,7 @@ window.windowMixin = {
},
selectWallet(wallet) {
this.g.wallet = wallet
this.g.lastActiveWallet = wallet.id
this.g.updatePayments = !this.g.updatePayments
this.balance = parseInt(wallet.balance_msat / 1000)
const currentPath = this.$route.path
+1
View File
@@ -51,6 +51,7 @@
"js/pages/node.js",
"js/pages/node-public.js",
"js/pages/audit.js",
"js/pages/wallet.js",
"js/pages/wallets.js",
"js/pages/users.js",
"js/pages/account.js",