lnbits-legend/lnbits/static/bundle-components.min.js
dni ⚡ 053ea20508
feat: update to Vue3 (#2677)
* update packages for vue3
* fix make bundle and make checkbundle to include bundle-components
* add lnbits/static/bundle-components.js

---------

Co-authored-by: Pavol Rusnak <pavol@rusnak.io>
2024-09-24 11:06:27 +02:00

1 line
43 KiB
JavaScript

function generateChart(e,n){const t=n.reduce(((e,n)=>(e.labels.push(n.date),e.income.push(n.income),e.spending.push(n.spending),e.cumulative.push(n.balance),e)),{labels:[],income:[],spending:[],cumulative:[]});return new Chart(e.getContext("2d"),{type:"bar",data:{labels:t.labels,datasets:[{data:t.cumulative,type:"line",label:"balance",backgroundColor:"#673ab7",borderColor:"#673ab7",borderWidth:4,pointRadius:3,fill:!1},{data:t.income,type:"bar",label:"in",barPercentage:.75,backgroundColor:window.Color("rgb(76,175,80)").alpha(.5).rgbString()},{data:t.spending,type:"bar",label:"out",barPercentage:.75,backgroundColor:window.Color("rgb(233,30,99)").alpha(.5).rgbString()}]},options:{title:{text:"Chart.js Combo Time Scale"},tooltips:{mode:"index",intersect:!1},scales:{xAxes:[{type:"time",display:!0,time:{minUnit:"hour",stepSize:3}}]},animation:{duration:0},elements:{line:{tension:0}}}})}window.app.component("lnbits-funding-sources",{mixins:[window.windowMixin],props:["form-data","allowed-funding-sources"],methods:{getFundingSourceLabel(e){const n=this.rawFundingSources.find((n=>n[0]===e));return n?n[1]:e}},computed:{fundingSources(){let e=[];for(const[n,t,i]of this.rawFundingSources){const t={};if(null!==i)for(let[e,n]of Object.entries(i))t[e]={label:n,value:null};e.push([n,t])}return new Map(e)},sortedAllowedFundingSources(){return this.allowedFundingSources.sort()}},data:()=>({rawFundingSources:[["VoidWallet","Void Wallet",null],["FakeWallet","Fake Wallet",{fake_wallet_secret:"Secret"}],["CoreLightningWallet","Core Lightning",{corelightning_rpc:"Endpoint",corelightning_pay_command:"Custom Pay Command"}],["CoreLightningRestWallet","Core Lightning Rest",{corelightning_rest_url:"Endpoint",corelightning_rest_cert:"Certificate",corelightning_rest_macaroon:"Macaroon"}],["LndRestWallet","Lightning Network Daemon (LND Rest)",{lnd_rest_endpoint:"Endpoint",lnd_rest_cert:"Certificate",lnd_rest_macaroon:"Macaroon",lnd_rest_macaroon_encrypted:"Encrypted Macaroon",lnd_rest_route_hints:"Enable Route Hints"}],["LndWallet","Lightning Network Daemon (LND)",{lnd_grpc_endpoint:"Endpoint",lnd_grpc_cert:"Certificate",lnd_grpc_port:"Port",lnd_grpc_admin_macaroon:"Admin Macaroon",lnd_grpc_macaroon_encrypted:"Encrypted Macaroon"}],["LnTipsWallet","LN.Tips",{lntips_api_endpoint:"Endpoint",lntips_api_key:"API Key"}],["LNPayWallet","LN Pay",{lnpay_api_endpoint:"Endpoint",lnpay_api_key:"API Key",lnpay_wallet_key:"Wallet Key"}],["EclairWallet","Eclair (ACINQ)",{eclair_url:"URL",eclair_pass:"Password"}],["LNbitsWallet","LNbits",{lnbits_endpoint:"Endpoint",lnbits_key:"Admin Key"}],["BlinkWallet","Blink",{blink_api_endpoint:"Endpoint",blink_ws_endpoint:"WebSocket",blink_token:"Key"}],["AlbyWallet","Alby",{alby_api_endpoint:"Endpoint",alby_access_token:"Key"}],["BoltzWallet","Boltz",{boltz_client_endpoint:"Endpoint",boltz_client_macaroon:"Admin Macaroon path or hex",boltz_client_cert:"Certificate path or hex",boltz_client_wallet:"Wallet Name"}],["ZBDWallet","ZBD",{zbd_api_endpoint:"Endpoint",zbd_api_key:"Key"}],["PhoenixdWallet","Phoenixd",{phoenixd_api_endpoint:"Endpoint",phoenixd_api_password:"Key"}],["OpenNodeWallet","OpenNode",{opennode_api_endpoint:"Endpoint",opennode_key:"Key"}],["ClicheWallet","Cliche (NBD)",{cliche_endpoint:"Endpoint"}],["SparkWallet","Spark",{spark_url:"Endpoint",spark_token:"Token"}],["NWCWallet","Nostr Wallet Connect",{nwc_pairing_url:"Pairing URL"}],["BreezSdkWallet","Breez SDK",{breez_api_key:"Breez API Key",breez_greenlight_seed:"Greenlight Seed",breez_greenlight_device_key:"Greenlight Device Key",breez_greenlight_device_cert:"Greenlight Device Cert",breez_greenlight_invite_code:"Greenlight Invite Code"}]]}),template:'\n <div class="funding-sources">\n <h6 class="q-mt-xl q-mb-md">Funding Sources</h6>\n <div class="row">\n <div class="col-12">\n <p>Active Funding<small> (Requires server restart)</small></p>\n <q-select\n filled\n v-model="formData.lnbits_backend_wallet_class"\n hint="Select the active funding wallet"\n :options="sortedAllowedFundingSources"\n :option-label="(item) => getFundingSourceLabel(item)"\n ></q-select>\n </div>\n </div>\n <q-list\n class="q-mt-md"\n v-for="(fund, idx) in allowedFundingSources"\n :key="idx"\n >\n <div v-if="fundingSources.get(fund) && fund === formData.lnbits_backend_wallet_class">\n <div class="row"\n v-for="([key, prop], i) in Object.entries(fundingSources.get(fund))"\n :key="i"\n >\n <div class="col-12">\n <q-input\n filled\n type="text"\n class="q-mt-sm"\n v-model="formData[key]"\n :label="prop.label"\n :hint="prop.hint"\n ></q-input>\n </div>\n </div>\n </div>\n </q-list>\n </div>\n '}),window.app.component("lnbits-extension-settings-form",{name:"lnbits-extension-settings-form",props:["options","adminkey","endpoint"],methods:{async updateSettings(){if(!this.settings)return this.$q.notify({message:"No settings to update",type:"negative"});try{const{data:e}=await LNbits.api.request("PUT",this.endpoint,this.adminkey,this.settings);this.settings=e}catch(e){LNbits.utils.notifyApiError(e)}},getSettings:async function(){try{const{data:e}=await LNbits.api.request("GET",this.endpoint,this.adminkey);this.settings=e}catch(e){LNbits.utils.notifyApiError(e)}},resetSettings:async function(){LNbits.utils.confirmDialog("Are you sure you want to reset the settings?").onOk((async()=>{try{await LNbits.api.request("DELETE",this.endpoint,this.adminkey),await this.getSettings()}catch(e){LNbits.utils.notifyApiError(e)}}))}},created:async function(){await this.getSettings()},template:'\n <q-form v-if="settings" @submit="updateSettings" class="q-gutter-md">\n <lnbits-dynamic-fields :options="options" v-model="settings"></lnbits-dynamic-fields>\n <div class="row q-mt-lg">\n <q-btn v-close-popup unelevated color="primary" type="submit">Update</q-btn>\n <q-btn v-close-popup unelevated color="danger" @click="resetSettings" >Reset</q-btn>\n <slot name="actions"></slot>\n </div>\n </q-form>\n ',data:function(){return{settings:void 0}}}),window.app.component("lnbits-extension-settings-btn-dialog",{name:"lnbits-extension-settings-btn-dialog",props:["options","adminkey","endpoint"],template:'\n <q-btn v-if="options" unelevated @click="show = true" color="primary" icon="settings" class="float-right">\n <q-dialog v-model="show" position="top">\n <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">\n <lnbits-extension-settings-form :options="options" :adminkey="adminkey" :endpoint="endpoint">\n <template v-slot:actions>\n <q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>\n </template>\n </lnbits-extension-settings-form>\n </q-card>\n </q-dialog>\n </q-btn>\n ',data:function(){return{show:!1}}}),window.app.component("lnbits-extension-rating",{name:"lnbits-extension-rating",props:["rating"],template:'\n <div style="margin-bottom: 3px">\n <q-rating\n v-model="rating"\n size="1.5em"\n :max="5"\n color="primary"\n ><q-tooltip>\n <span v-text="$t(\'extension_rating_soon\')"></span> </q-tooltip\n ></q-rating>\n </div>\n '}),window.app.component("payment-list",{name:"payment-list",props:["update","wallet","mobileSimple","lazy"],mixins:[window.windowMixin],data:function(){return{denomination:LNBITS_DENOMINATION,payments:[],paymentsTable:{columns:[{name:"time",align:"left",label:this.$t("memo")+"/"+this.$t("date"),field:"date",sortable:!0},{name:"amount",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:"sat",sortable:!0}],pagination:{rowsPerPage:10,page:1,sortBy:"time",descending:!0,rowsNumber:10},search:null,loading:!1},exportTagName:"",exportPaymentTagList:[],paymentsCSV:{columns:[{name:"pending",align:"left",label:"Pending",field:"pending"},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"},{name:"time",align:"left",label:this.$t("date"),field:"date",sortable:!0},{name:"amount",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:"sat",sortable:!0},{name:"fee",align:"right",label:this.$t("fee")+" (m"+LNBITS_DENOMINATION+")",field:"fee"},{name:"tag",align:"right",label:this.$t("tag"),field:"tag"},{name:"payment_hash",align:"right",label:this.$t("payment_hash"),field:"payment_hash"},{name:"payment_proof",align:"right",label:this.$t("payment_proof"),field:"payment_proof"},{name:"webhook",align:"right",label:this.$t("webhook"),field:"webhook"},{name:"fiat_currency",align:"right",label:"Fiat Currency",field:e=>e.extra.wallet_fiat_currency},{name:"fiat_amount",align:"right",label:"Fiat Amount",field:e=>e.extra.wallet_fiat_amount}],filter:null,loading:!1}}},computed:{filteredPayments:function(){var e=this.paymentsTable.search;return e&&""!==e?LNbits.utils.search(this.payments,e):this.payments},paymentsOmitter(){return this.$q.screen.lt.md&&this.mobileSimple?this.payments.length>0?[this.payments[0]]:[]:this.payments},pendingPaymentsExist:function(){return-1!==this.payments.findIndex((e=>e.pending))}},methods:{fetchPayments:function(e){const n=LNbits.utils.prepareFilterQuery(this.paymentsTable,e);return LNbits.api.getPayments(this.wallet,n).then((e=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=e.data.total,this.payments=e.data.data.map((e=>LNbits.map.payment(e)))})).catch((e=>{this.paymentsTable.loading=!1,LNbits.utils.notifyApiError(e)}))},paymentTableRowKey:function(e){return e.payment_hash+e.amount},exportCSV(e=!1){const n=this.paymentsTable.pagination,t={sortby:n.sortBy??"time",direction:n.descending?"desc":"asc"},i=new URLSearchParams(t);LNbits.api.getPayments(this.wallet,i).then((n=>{let t=n.data.data.map(LNbits.map.payment),i=this.paymentsCSV.columns;if(e){this.exportPaymentTagList.length&&(t=t.filter((e=>this.exportPaymentTagList.includes(e.tag))));const e=Object.keys(t.reduce(((e,n)=>({...e,...n.details})),{})).map((e=>({name:e,align:"right",label:e.charAt(0).toUpperCase()+e.slice(1).replace(/([A-Z])/g," $1"),field:n=>n.details[e],format:e=>"object"==typeof e?JSON.stringify(e):e})));i=this.paymentsCSV.columns.concat(e)}LNbits.utils.exportCSV(i,t,this.wallet.name+"-payments")}))},addFilterTag:function(){if(!this.exportTagName)return;const e=this.exportTagName.trim();this.exportPaymentTagList=this.exportPaymentTagList.filter((n=>n!==e)),this.exportPaymentTagList.push(e),this.exportTagName=""},removeExportTag:function(e){this.exportPaymentTagList=this.exportPaymentTagList.filter((n=>n!==e))},formatCurrency:function(e,n){try{return LNbits.utils.formatCurrency(e,n)}catch(n){return console.error(n),`${e} ???`}}},watch:{lazy:function(e){!0===e&&this.fetchPayments()},update:function(){this.fetchPayments()}},created:function(){void 0===this.lazy&&this.fetchPayments()},template:'\n <q-card\n :style="$q.screen.lt.md ? {\n background: $q.screen.lt.md ? \'none !important\': \'\'\n , boxShadow: $q.screen.lt.md ? \'none !important\': \'\'\n , marginTop: $q.screen.lt.md ? \'0px !important\': \'\'\n } : \'\'"\n >\n <q-card-section>\n <div class="row items-center no-wrap q-mb-sm">\n <div class="col">\n <h5\n class="text-subtitle1 q-my-none"\n :v-text="$t(\'transactions\')"\n ></h5>\n </div>\n <div class="gt-sm col-auto">\n <q-btn-dropdown\n outline\n persistent\n class="q-mr-sm"\n color="grey"\n :label="$t(\'export_csv\')"\n split\n @click="exportCSV(false)"\n >\n <q-list>\n <q-item>\n <q-item-section>\n <q-input\n @keydown.enter="addFilterTag"\n filled\n dense\n v-model="exportTagName"\n type="text"\n label="Payment Tags"\n class="q-pa-sm"\n >\n <q-btn\n @click="addFilterTag"\n dense\n flat\n icon="add"\n ></q-btn>\n </q-input>\n </q-item-section>\n </q-item>\n <q-item v-if="exportPaymentTagList.length">\n <q-item-section>\n <div>\n <q-chip\n v-for="tag in exportPaymentTagList"\n :key="tag"\n removable\n @remove="removeExportTag(tag)"\n color="primary"\n text-color="white"\n :label="tag"\n ></q-chip>\n </div>\n </q-item-section>\n </q-item>\n\n <q-item>\n <q-item-section>\n <q-btn v-close-popup outline color="grey" @click="exportCSV(true)" label="Export to CSV with details" ></q-btn>\n </q-item-section>\n </q-item>\n </q-list>\n </q-btn-dropdown>\n <payment-chart :wallet="wallet" />\n </div>\n </div>\n <q-input\n :style="$q.screen.lt.md ? {\n display: mobileSimple ? \'none !important\': \'\'\n } : \'\'"\n filled\n dense\n clearable\n v-model="paymentsTable.search"\n debounce="300"\n :placeholder="$t(\'search_by_tag_memo_amount\')"\n class="q-mb-md"\n >\n </q-input>\n <q-table\n dense\n flat\n :rows="paymentsOmitter"\n :row-key="paymentTableRowKey"\n :columns="paymentsTable.columns"\n :pagination.sync="paymentsTable.pagination"\n :no-data-label="$t(\'no_transactions\')"\n :filter="paymentsTable.search"\n :loading="paymentsTable.loading"\n :hide-header="mobileSimple"\n :hide-bottom="mobileSimple"\n @request="fetchPayments"\n >\n <template v-slot:header="props">\n <q-tr :props="props">\n <q-th auto-width></q-th>\n <q-th\n v-for="col in props.cols"\n :key="col.name"\n :props="props"\n v-text="col.label"\n ></q-th>\n </q-tr>\n </template>\n <template v-slot:body="props">\n <q-tr :props="props">\n <q-td auto-width class="text-center">\n <q-icon\n v-if="props.row.isPaid"\n size="14px"\n :name="props.row.isOut ? \'call_made\' : \'call_received\'"\n :color="props.row.isOut ? \'pink\' : \'green\'"\n @click="props.expand = !props.expand"\n ></q-icon>\n <q-icon\n v-else-if="props.row.isFailed"\n name="warning"\n color="yellow"\n @click="props.expand = !props.expand"\n >\n <q-tooltip\n ><span>failed</span\n ></q-tooltip>\n </q-icon>\n <q-icon\n v-else\n name="settings_ethernet"\n color="grey"\n @click="props.expand = !props.expand"\n >\n <q-tooltip\n ><span v-text="$t(\'pending\')"></span\n ></q-tooltip>\n </q-icon>\n </q-td>\n <q-td\n key="time"\n :props="props"\n style="white-space: normal; word-break: break-all"\n >\n <q-badge\n v-if="props.row.tag"\n color="yellow"\n text-color="black"\n >\n <a\n v-text="\'#\'+props.row.tag"\n class="inherit"\n :href="[\'/\', props.row.tag].join(\'\')"\n ></a>\n </q-badge>\n <span v-text="props.row.memo"></span>\n <br />\n\n <i>\n <span v-text="props.row.dateFrom"></span>\n <q-tooltip\n ><span v-text="props.row.date"></span\n ></q-tooltip>\n </i>\n </q-td>\n <q-td\n auto-width\n key="amount"\n v-if="denomination != \'sats\'"\n :props="props"\n class="col1"\n v-text="parseFloat(String(props.row.fsat).replaceAll(\',\', \'\')) / 100"\n >\n </q-td>\n <q-td class="col2" auto-width key="amount" v-else :props="props">\n <span v-text="props.row.fsat"></span>\n <br />\n <i v-if="props.row.extra.wallet_fiat_currency">\n <span\n v-text="formatCurrency(props.row.extra.wallet_fiat_amount, props.row.extra.wallet_fiat_currency)"\n ></span>\n <br />\n </i>\n <i v-if="props.row.extra.fiat_currency">\n <span\n v-text="formatCurrency(props.row.extra.fiat_amount, props.row.extra.fiat_currency)"\n ></span>\n </i>\n </q-td>\n\n <q-dialog v-model="props.expand" :props="props" position="top">\n <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">\n <div class="text-center q-mb-lg">\n <div v-if="props.row.isIn && props.row.isPending">\n <q-icon name="settings_ethernet" color="grey"></q-icon>\n <span v-text="$t(\'invoice_waiting\')"></span>\n <lnbits-payment-details\n :payment="props.row"\n ></lnbits-payment-details>\n <div\n v-if="props.row.bolt11"\n class="text-center q-mb-lg"\n >\n <a :href="\'lightning:\' + props.row.bolt11">\n <q-responsive :ratio="1" class="q-mx-xl">\n <lnbits-qrcode\n :value="\'lightning:\' + props.row.bolt11.toUpperCase()"\n ></lnbits-qrcode>\n </q-responsive>\n </a>\n </div>\n <div class="row q-mt-lg">\n <q-btn\n outline\n color="grey"\n @click="copyText(props.row.bolt11)"\n :label="$t(\'copy_invoice\')"\n ></q-btn>\n <q-btn\n v-close-popup\n flat\n color="grey"\n class="q-ml-auto"\n :label="$t(\'close\')"\n ></q-btn>\n </div>\n </div>\n <div v-else-if="props.row.isOut && props.row.isPending">\n <q-icon name="settings_ethernet" color="grey"></q-icon>\n <span v-text="$t(\'outgoing_payment_pending\')"></span>\n <lnbits-payment-details\n :payment="props.row"\n ></lnbits-payment-details>\n </div>\n <div v-else-if="props.row.isPaid && props.row.isIn">\n <q-icon\n size="18px"\n :name="\'call_received\'"\n :color="\'green\'"\n ></q-icon>\n <span v-text="$t(\'payment_received\')"></span>\n <lnbits-payment-details\n :payment="props.row"\n ></lnbits-payment-details>\n </div>\n <div v-else-if="props.row.isPaid && props.row.isOut">\n <q-icon\n size="18px"\n :name="\'call_made\'"\n :color="\'pink\'"\n ></q-icon>\n <span v-text="$t(\'payment_sent\')"></span>\n <lnbits-payment-details\n :payment="props.row"\n ></lnbits-payment-details>\n </div>\n <div v-else-if="props.row.isFailed">\n <q-icon name="warning" color="yellow"></q-icon>\n <span>Payment failed</span>\n <lnbits-payment-details\n :payment="props.row"\n ></lnbits-payment-details>\n </div>\n </div>\n </q-card>\n </q-dialog>\n </q-tr>\n </template>\n </q-table>\n </q-card-section>\n </q-card>\n '}),window.app.component("payment-chart",{name:"payment-chart",props:["wallet"],mixins:[window.windowMixin],data:function(){return{paymentsChart:{show:!1,group:{value:"hour",label:"Hour"},groupOptions:[{value:"hour",label:"Hour"},{value:"day",label:"Day"},{value:"week",label:"Week"},{value:"month",label:"Month"},{value:"year",label:"Year"}],instance:null}}},methods:{showChart:function(){this.paymentsChart.show=!0,LNbits.api.request("GET","/api/v1/payments/history?group="+this.paymentsChart.group.value,this.wallet.adminkey).then((e=>{this.$nextTick((()=>{this.paymentsChart.instance&&this.paymentsChart.instance.destroy(),this.paymentsChart.instance=generateChart(this.$refs.canvas,e.data)}))})).catch((e=>{LNbits.utils.notifyApiError(e),this.paymentsChart.show=!1}))}},template:'\n <span id="payment-chart">\n <q-btn dense flat round icon="show_chart" color="grey" @click="showChart" >\n <q-tooltip>\n <span v-text="$t(\'chart_tooltip\')"></span>\n </q-tooltip>\n </q-btn>\n\n <q-dialog v-model="paymentsChart.show" position="top">\n <q-card class="q-pa-sm" style="width: 800px; max-width: unset">\n <q-card-section>\n <div class="row q-gutter-sm justify-between">\n <div class="text-h6">Payments Chart</div>\n <q-select label="Group" filled dense v-model="paymentsChart.group"\n style="min-width: 120px"\n :options="paymentsChart.groupOptions"\n >\n </q-select>\n </div>\n <canvas ref="canvas" width="600" height="400"></canvas>\n </q-card-section>\n </q-card>\n </q-dialog>\n </span>\n '}),window.app.component(QrcodeVue),window.app.component("lnbits-fsat",{props:{amount:{type:Number,default:0}},template:"<span>{{ fsat }}</span>",computed:{fsat:function(){return LNbits.utils.formatSat(this.amount)}}}),window.app.component("lnbits-wallet-list",{props:["balance"],data:function(){return{user:null,activeWallet:null,balance:0,showForm:!1,walletName:"",LNBITS_DENOMINATION:LNBITS_DENOMINATION}},template:'\n <q-list v-if="user && user.wallets.length" dense class="lnbits-drawer__q-list">\n <q-item-label header v-text="$t(\'wallets\')"></q-item-label>\n <q-item v-for="wallet in wallets" :key="wallet.id"\n clickable\n :active="activeWallet && activeWallet.id === wallet.id"\n tag="a" :href="wallet.url">\n <q-item-section side>\n <q-avatar size="md"\n :color="(activeWallet && activeWallet.id === wallet.id)\n ? (($q.dark.isActive) ? \'primary\' : \'primary\')\n : \'grey-5\'">\n <q-icon name="flash_on" :size="($q.dark.isActive) ? \'21px\' : \'20px\'"\n :color="($q.dark.isActive) ? \'blue-grey-10\' : \'grey-3\'"></q-icon>\n </q-avatar>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1">{{ wallet.name }}</q-item-label>\n <q-item-label v-if="LNBITS_DENOMINATION != \'sats\'" caption>{{ parseFloat(String(wallet.live_fsat).replaceAll(",", "")) / 100 }} {{ LNBITS_DENOMINATION }}</q-item-label>\n <q-item-label v-else caption>{{ wallet.live_fsat }} {{ LNBITS_DENOMINATION }}</q-item-label>\n </q-item-section>\n <q-item-section side v-show="activeWallet && activeWallet.id === wallet.id">\n <q-icon name="chevron_right" color="grey-5" size="md"></q-icon>\n </q-item-section>\n </q-item>\n <q-item clickable @click="showForm = !showForm">\n <q-item-section side>\n <q-icon :name="(showForm) ? \'remove\' : \'add\'" color="grey-5" size="md"></q-icon>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1" class="text-caption" v-text="$t(\'add_wallet\')"></q-item-label>\n </q-item-section>\n </q-item>\n <q-item v-if="showForm">\n <q-item-section>\n <q-form @submit="createWallet">\n <q-input filled dense v-model="walletName" label="Name wallet *">\n <template v-slot:append>\n <q-btn round dense flat icon="send" size="sm" @click="createWallet" :disable="walletName === \'\'"></q-btn>\n </template>\n </q-input>\n </q-form>\n </q-item-section>\n </q-item>\n </q-list>\n ',computed:{wallets:function(){var e=this.balance;return this.user.wallets.map((function(n){return n.live_fsat=e.length&&e[0]===n.id?LNbits.utils.formatSat(e[1]):n.fsat,n}))}},methods:{createWallet:function(){LNbits.api.createWallet(this.user.wallets[0],this.walletName)}},created:function(){window.user&&(this.user=LNbits.map.user(window.user)),window.wallet&&(this.activeWallet=LNbits.map.wallet(window.wallet)),document.addEventListener("updateWalletBalance",this.updateWalletBalance)}}),window.app.component("lnbits-extension-list",{data:function(){return{extensions:[],user:null}},template:'\n <q-list v-if="user && userExtensions.length > 0" dense class="lnbits-drawer__q-list">\n <q-item-label header v-text="$t(\'extensions\')"></q-item-label>\n <q-item v-for="extension in userExtensions" :key="extension.code"\n clickable\n :active="extension.isActive"\n tag="a" :href="extension.url">\n <q-item-section side>\n <q-avatar size="md">\n <q-img\n :src="extension.tile"\n style="max-width:20px"\n ></q-img>\n </q-avatar>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1">{{ extension.name }} </q-item-label>\n </q-item-section>\n <q-item-section side v-show="extension.isActive">\n <q-icon name="chevron_right" color="grey-5" size="md"></q-icon>\n </q-item-section>\n </q-item>\n <div class="lt-md q-mt-xl q-mb-xl"></div>\n </q-list>\n ',computed:{userExtensions:function(){if(!this.user)return[];var e=window.location.pathname,n=this.user.extensions;return this.extensions.filter((function(e){return-1!==n.indexOf(e.code)})).map((function(n){return n.isActive=e.startsWith(n.url),n}))}},created:function(){window.extensions&&(this.extensions=window.extensions.map((function(e){return LNbits.map.extension(e)})).sort((function(e,n){return e.name.localeCompare(n.name)}))),window.user&&(this.user=LNbits.map.user(window.user))}}),window.app.component("lnbits-manage",{props:["showAdmin","showNode","showExtensions","showUsers"],methods:{isActive:function(e){return window.location.pathname===e}},data:function(){return{extensions:[],user:null}},template:'\n <q-list v-if="user" dense class="lnbits-drawer__q-list">\n <q-item-label header v-text="$t(\'manage\')"></q-item-label>\n <div v-if="user.admin">\n <q-item v-if=\'showAdmin\' clickable tag="a" href="/admin" :active="isActive(\'/admin\')">\n <q-item-section side>\n <q-icon name="admin_panel_settings" :color="isActive(\'/admin\') ? \'primary\' : \'grey-5\'" size="md"></q-icon>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1" v-text="$t(\'server\')"></q-item-label>\n </q-item-section>\n </q-item>\n <q-item v-if=\'showNode\' clickable tag="a" href="/node" :active="isActive(\'/node\')">\n <q-item-section side>\n <q-icon name="developer_board" :color="isActive(\'/node\') ? \'primary\' : \'grey-5\'" size="md"></q-icon>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1" v-text="$t(\'node\')"></q-item-label>\n </q-item-section>\n </q-item>\n <q-item v-if="showUsers" clickable tag="a" href="/users" :active="isActive(\'/users\')">\n <q-item-section side>\n <q-icon name="groups" :color="isActive(\'/users\') ? \'primary\' : \'grey-5\'" size="md"></q-icon>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1" v-text="$t(\'users\')"></q-item-label>\n </q-item-section>\n </q-item>\n </div>\n <q-item v-if="showExtensions" clickable tag="a" href="/extensions" :active="isActive(\'/extensions\')">\n <q-item-section side>\n <q-icon name="extension" :color="isActive(\'/extensions\') ? \'primary\' : \'grey-5\'" size="md"></q-icon>\n </q-item-section>\n <q-item-section>\n <q-item-label lines="1" v-text="$t(\'extensions\')"></q-item-label>\n </q-item-section>\n </q-item>\n </q-list>\n ',created:function(){window.user&&(this.user=LNbits.map.user(window.user))}}),window.app.component("lnbits-payment-details",{props:["payment"],mixins:[window.windowMixin],data:function(){return{LNBITS_DENOMINATION:LNBITS_DENOMINATION}},template:'\n <div class="q-py-md" style="text-align: left">\n\n <div v-if="payment.tag" class="row justify-center q-mb-md">\n <q-badge v-if="hasTag" color="yellow" text-color="black">\n #{{ payment.tag }}\n </q-badge>\n </div>\n\n <div class="row">\n <b v-text="$t(\'created\')"></b>:\n {{ payment.date }} ({{ payment.dateFrom }})\n </div>\n\n <div class="row" v-if="hasExpiry">\n <b v-text="$t(\'expiry\')"></b>:\n {{ payment.expirydate }} ({{ payment.expirydateFrom }})\n </div>\n\n <div class="row">\n <b v-text="$t(\'amount\')"></b>:\n {{ (payment.amount / 1000).toFixed(3) }} {{LNBITS_DENOMINATION}}\n </div>\n\n <div class="row">\n <b v-text="$t(\'fee\')"></b>:\n {{ (payment.fee / 1000).toFixed(3) }} {{LNBITS_DENOMINATION}}\n </div>\n\n <div class="text-wrap">\n <b style="white-space: nowrap;" v-text="$t(\'payment_hash\')"></b>:&nbsp;{{ payment.payment_hash }}\n <q-icon name="content_copy" @click="copyText(payment.payment_hash)" size="1em" color="grey" class="q-mb-xs cursor-pointer" />\n </div>\n\n <div class="text-wrap">\n <b style="white-space: nowrap;" v-text="$t(\'memo\')"></b>:&nbsp;{{ payment.memo }}\n </div>\n\n <div class="text-wrap" v-if="payment.webhook">\n <b style="white-space: nowrap;" v-text="$t(\'webhook\')"></b>:&nbsp;{{ payment.webhook }}:&nbsp;<q-badge :color="webhookStatusColor" text-color="white">\n {{ webhookStatusText }}\n </q-badge>\n </div>\n\n <div class="text-wrap" v-if="hasPreimage">\n <b style="white-space: nowrap;" v-text="$t(\'payment_proof\')"></b>:&nbsp;{{ payment.preimage }}\n </div>\n\n <div class="row" v-for="entry in extras">\n <q-badge v-if="hasTag" color="secondary" text-color="white">\n extra\n </q-badge>\n <b>{{ entry.key }}</b>:\n {{ entry.value }}\n </div>\n\n <div class="row" v-if="hasSuccessAction">\n <b>Success action</b>:\n <lnbits-lnurlpay-success-action\n :payment="payment"\n :success_action="payment.extra.success_action"\n ></lnbits-lnurlpay-success-action>\n </div>\n\n</div>\n ',computed:{hasPreimage(){return this.payment.preimage&&"0000000000000000000000000000000000000000000000000000000000000000"!==this.payment.preimage},hasExpiry(){return!!this.payment.expiry},hasSuccessAction(){return this.hasPreimage&&this.payment.extra&&this.payment.extra.success_action},webhookStatusColor(){return this.payment.webhook_status>=300||this.payment.webhook_status<0?"red-10":this.payment.webhook_status?"green-10":"cyan-7"},webhookStatusText(){return this.payment.webhook_status?this.payment.webhook_status:"not sent yet"},hasTag(){return this.payment.extra&&!!this.payment.extra.tag},extras(){if(!this.payment.extra)return[];let e=_.omit(this.payment.extra,["tag","success_action"]);return Object.keys(e).map((n=>({key:n,value:e[n]})))}}}),window.app.component("lnbits-lnurlpay-success-action",{props:["payment","success_action"],data(){return{decryptedValue:this.success_action.ciphertext}},template:'\n <div>\n <p class="q-mb-sm">{{ success_action.message || success_action.description }}</p>\n <code v-if="decryptedValue" class="text-h6 q-mt-sm q-mb-none">\n {{ decryptedValue }}\n </code>\n <p v-else-if="success_action.url" class="text-h6 q-mt-sm q-mb-none">\n <a target="_blank" style="color: inherit;" :href="success_action.url">{{ success_action.url }}</a>\n </p>\n </div>\n ',mounted:function(){if("aes"!==this.success_action.tag)return null;decryptLnurlPayAES(this.success_action,this.payment.preimage).then((e=>{this.decryptedValue=e}))}}),window.app.component("lnbits-qrcode",{mixins:[window.windowMixin],components:{QrcodeVue:QrcodeVue},props:["value"],data:()=>({logo:LNBITS_QR_LOGO}),template:'\n <div class="qrcode__wrapper">\n <qrcode-vue :value="value" size="350" class="rounded-borders"></qrcode-vue>\n <img class="qrcode__image" :src="logo" alt="..." />\n </div>\n '}),window.app.component("lnbits-notifications-btn",{mixins:[window.windowMixin],props:["pubkey"],data:()=>({isSupported:!1,isSubscribed:!1,isPermissionGranted:!1,isPermissionDenied:!1}),template:'\n <q-btn\n v-if="g.user.wallets"\n :disabled="!this.isSupported"\n dense\n flat\n round\n @click="toggleNotifications()"\n :icon="this.isSubscribed ? \'notifications_active\' : \'notifications_off\'"\n size="sm"\n type="a"\n >\n <q-tooltip v-if="this.isSupported && !this.isSubscribed">Subscribe to notifications</q-tooltip>\n <q-tooltip v-if="this.isSupported && this.isSubscribed">Unsubscribe from notifications</q-tooltip>\n <q-tooltip v-if="this.isSupported && this.isPermissionDenied">\n Notifications are disabled,<br/>please enable or reset permissions\n </q-tooltip>\n <q-tooltip v-if="!this.isSupported">Notifications are not supported</q-tooltip>\n </q-btn>\n ',methods:{urlB64ToUint8Array(e){const n=(e+"=".repeat((4-e.length%4)%4)).replace(/\-/g,"+").replace(/_/g,"/"),t=atob(n),i=new Uint8Array(t.length);for(let e=0;e<t.length;++e)i[e]=t.charCodeAt(e);return i},toggleNotifications(){this.isSubscribed?this.unsubscribe():this.subscribe()},saveUserSubscribed(e){let n=JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[];n.includes(e)||n.push(e),this.$q.localStorage.set("lnbits.webpush.subscribedUsers",JSON.stringify(n))},removeUserSubscribed(e){let n=JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[];n=n.filter((n=>n!==e)),this.$q.localStorage.set("lnbits.webpush.subscribedUsers",JSON.stringify(n))},isUserSubscribed(e){return(JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[]).includes(e)},subscribe(){var e=this;this.isSupported&&!this.isPermissionDenied&&(Notification.requestPermission().then((e=>{this.isPermissionGranted="granted"===e,this.isPermissionDenied="denied"===e})).catch((function(e){console.log(e)})),navigator.serviceWorker.ready.then((n=>{navigator.serviceWorker.getRegistration().then((n=>{n.pushManager.getSubscription().then((function(t){if(null===t||!e.isUserSubscribed(e.g.user.id)){const t={applicationServerKey:e.urlB64ToUint8Array(e.pubkey),userVisibleOnly:!0};n.pushManager.subscribe(t).then((function(n){LNbits.api.request("POST","/api/v1/webpush",e.g.user.wallets[0].adminkey,{subscription:JSON.stringify(n)}).then((function(n){e.saveUserSubscribed(n.data.user),e.isSubscribed=!0})).catch((function(e){LNbits.utils.notifyApiError(e)}))}))}})).catch((function(e){console.log(e)}))}))})))},unsubscribe(){var e=this;navigator.serviceWorker.ready.then((n=>{n.pushManager.getSubscription().then((n=>{n&&LNbits.api.request("DELETE","/api/v1/webpush?endpoint="+btoa(n.endpoint),e.g.user.wallets[0].adminkey).then((function(){e.removeUserSubscribed(e.g.user.id),e.isSubscribed=!1})).catch((function(e){LNbits.utils.notifyApiError(e)}))}))})).catch((function(e){console.log(e)}))},checkSupported:function(){let e="https:"===window.location.protocol,n="serviceWorker"in navigator,t="Notification"in window,i="PushManager"in window;return this.isSupported=e&&n&&t&&i,this.isSupported||console.log("Notifications disabled because requirements are not met:",{HTTPS:e,"Service Worker API":n,"Notification API":t,"Push API":i}),this.isSupported},updateSubscriptionStatus:async function(){var e=this;await navigator.serviceWorker.ready.then((n=>{n.pushManager.getSubscription().then((n=>{e.isSubscribed=!!n&&e.isUserSubscribed(e.g.user.id)}))})).catch((function(e){console.log(e)}))}},created:function(){this.isPermissionDenied="denied"===Notification.permission,this.checkSupported()&&this.updateSubscriptionStatus()}}),window.app.component("lnbits-dynamic-fields",{mixins:[window.windowMixin],props:["options","value"],data:()=>({formData:null,rules:[e=>!!e||"Field is required"]}),template:'\n <div v-if="formData">\n <div class="row q-mb-lg" v-for="o in options">\n <div class="col auto-width">\n <p v-if=o.options?.length class="q-ml-xl">\n <span v-text="o.label || o.name"></span> <small v-if="o.description"> (<span v-text="o.description"></span>)</small>\n </p>\n <lnbits-dynamic-fields v-if="o.options?.length" :options="o.options" v-model="formData[o.name]"\n @input="handleValueChanged" class="q-ml-xl">\n </lnbits-dynamic-fields>\n <div v-else>\n <q-input\n v-if="o.type === \'number\'"\n type="number"\n v-model="formData[o.name]"\n @input="handleValueChanged"\n :label="o.label || o.name"\n :hint="o.description"\n :rules="applyRules(o.required)"\n filled\n dense\n ></q-input>\n <q-input\n v-else-if="o.type === \'text\'"\n type="textarea"\n rows="5"\n v-model="formData[o.name]"\n @input="handleValueChanged"\n :label="o.label || o.name"\n :hint="o.description"\n :rules="applyRules(o.required)"\n filled\n dense\n ></q-input>\n <q-input\n v-else-if="o.type === \'password\'"\n v-model="formData[o.name]"\n @input="handleValueChanged"\n type="password"\n :label="o.label || o.name"\n :hint="o.description"\n :rules="applyRules(o.required)"\n filled\n dense\n ></q-input>\n <q-select\n v-else-if="o.type === \'select\'"\n v-model="formData[o.name]"\n @input="handleValueChanged"\n :label="o.label || o.name"\n :hint="o.description"\n :options="o.values"\n :rules="applyRules(o.required)"\n ></q-select>\n <q-select\n v-else-if="o.isList"\n v-model.trim="formData[o.name]"\n @input="handleValueChanged"\n input-debounce="0"\n new-value-mode="add-unique"\n :label="o.label || o.name"\n :hint="o.description"\n :rules="applyRules(o.required)"\n filled\n multiple\n dense\n use-input\n use-chips\n multiple\n hide-dropdown-icon\n ></q-select>\n <div v-else-if="o.type === \'bool\'">\n <q-item tag="label" v-ripple>\n <q-item-section avatar top>\n <q-checkbox v-model="formData[o.name]" @input="handleValueChanged" />\n </q-item-section>\n <q-item-section>\n <q-item-label><span v-text="o.label || o.name"></span></q-item-label>\n <q-item-label caption> <span v-text="o.description"></span> </q-item-label>\n </q-item-section>\n </q-item>\n </div>\n <q-input\n v-else-if="o.type === \'hidden\'"\n v-model="formData[o.name]"\n type="text"\n style="display: none"\n :rules="applyRules(o.required)"\n ></q-input>\n <q-input\n v-else\n v-model="formData[o.name]"\n @input="handleValueChanged"\n :hint="o.description"\n :label="o.label || o.name"\n :rules="applyRules(o.required)"\n filled\n dense\n ></q-input>\n </div>\n </div>\n </div>\n </div>\n ',methods:{applyRules(e){return e?this.rules:[]},buildData(e,n={}){return e.reduce(((e,t)=>(t.options?.length?e[t.name]=this.buildData(t.options,n[t.name]):e[t.name]=n[t.name]??t.default,e)),{})},handleValueChanged(){this.$emit("input",this.formData)}},created:function(){this.formData=this.buildData(this.options,this.value)}}),window.app.component("lnbits-update-balance",{mixins:[window.windowMixin],props:["wallet_id","callback"],computed:{denomination:()=>LNBITS_DENOMINATION,admin(){return this.g.user.admin}},data:function(){return{credit:0}},methods:{updateBalance:function(e){LNbits.api.updateBalance(e,this.wallet_id).then((n=>{if("Success"!==n.data.status)throw new Error(n.data);this.callback({success:!0,credit:parseInt(e),wallet_id:this.wallet_id})})).then((n=>(e=parseInt(e),this.$q.notify({type:"positive",message:this.$t("wallet_topup_ok",{amount:e}),icon:null}),e))).catch((function(e){LNbits.utils.notifyApiError(e)}))}},template:'\n <q-btn\n v-if="admin"\n round\n color="primary"\n icon="add"\n size="sm"\n >\n <q-popup-edit\n class="bg-accent text-white"\n v-slot="scope"\n v-model="credit"\n >\n <q-input\n filled\n :label=\'$t("credit_label", { denomination: denomination })\'\n :hint="$t(\'credit_hint\')"\n v-model="scope.value"\n dense\n autofocus\n @keyup.enter="updateBalance(scope.value)"\n >\n <template v-slot:append>\n <q-icon name="edit" />\n </template>\n </q-input>\n </q-popup-edit>\n <q-tooltip>Topup Wallet</q-tooltip>\n </q-btn>\n '}),window.app.use(VueQrcodeReader),window.app.use(Quasar),window.app.use(window.i18n),window.app.mount("#vue");