Copy and start to modify templates

This commit is contained in:
Fitti
2021-06-28 09:05:49 +02:00
parent 22a68f3e9f
commit f70eac2a48
3 changed files with 653 additions and 41 deletions

View File

@@ -0,0 +1,22 @@
<q-expansion-item
group="extras"
icon="swap_vertical_circle"
label="Info"
:content-inset-level="0.5"
>
<q-card>
<q-card-section>
<h5 class="text-subtitle1 q-my-none">
Twitch Alerts: Integrate Bitcoin into your stream alerts!
</h5>
<p>
Accept Bitcoin donations on Twitch, and integrate them into your alerts.
Present your viewers with a simple donation page, and add those donations
to Streamlabs to play alerts on your stream!<br />
<small>
Created by, <a href="https://github.com/Fittiboy">Fitti</a></small
>
</p>
</q-card-section>
</q-card>
</q-expansion-item>

View File

@@ -0,0 +1,197 @@
{% extends "public.html" %} {% block page %}
<div class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
<q-card class="q-pa-lg">
<q-card-section class="q-pa-none">
<h3 class="q-my-none">{{ form_name }}</h3>
<br />
<h5 class="q-my-none">{{ form_desc }}</h5>
<br />
<q-form @submit="Invoice()" class="q-gutter-md">
<q-input
filled
dense
v-model.trim="formDialog.data.name"
type="name"
label="Your Name"
></q-input>
<q-input
filled
dense
v-model.trim="formDialog.data.amount"
type="number"
label="Amount of sats"
></q-input>
<q-input
filled
dense
v-model.number="formDialog.data.text"
type="textarea"
label="Donation Message"
></q-input>
<p>{% raw %}{{amountWords}}{% endraw %}</p>
<div class="row q-mt-lg">
<q-btn
unelevated
color="deep-purple"
:disable="formDialog.data.name == '' || formDialog.data.text == ''"
type="submit"
>Submit</q-btn
>
<q-btn @click="resetForm" flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card-section>
</q-card>
</div>
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
<q-card
v-if="!receive.paymentReq"
class="q-pa-lg q-pt-xl lnbits__dialog-card"
>
</q-card>
<q-card v-else class="q-pa-lg q-pt-xl lnbits__dialog-card">
<div class="text-center q-mb-lg">
<a :href="'lightning:' + receive.paymentReq">
<q-responsive :ratio="1" class="q-mx-xl">
<qrcode
:value="paymentReq"
:options="{width: 340}"
class="rounded-borders"
></qrcode>
</q-responsive>
</a>
</div>
<div class="row q-mt-lg">
<q-btn outline color="grey" @click="copyText(receive.paymentReq)"
>Copy invoice</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %}
<script>
console.log('{{ form_costpword }}')
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {
paymentReq: null,
redirectUrl: null,
formDialog: {
show: false,
data: {
name: '',
email: '',
text: ''
}
},
receive: {
show: false,
status: 'pending',
paymentReq: null
}
}
},
computed: {
amountWords() {
var regex = /\s+/gi
var nwords = this.formDialog.data.text
.trim()
.replace(regex, ' ')
.split(' ').length
var sats = nwords * parseInt('{{ form_costpword }}')
if (sats === parseInt('{{ form_costpword }}')) {
return '0 Sats to pay'
} else {
this.formDialog.data.sats = sats
return sats + ' Sats to pay'
}
}
},
methods: {
resetForm: function (e) {
e.preventDefault()
this.formDialog.data.name = ''
this.formDialog.data.email = ''
this.formDialog.data.text = ''
},
closeReceiveDialog: function () {
var checker = this.receive.paymentChecker
dismissMsg()
clearInterval(paymentChecker)
setTimeout(function () {}, 10000)
},
Invoice: function () {
var self = this
axios
.post('/lnticket/api/v1/tickets/{{ form_id }}', {
form: '{{ form_id }}',
name: self.formDialog.data.name,
email: self.formDialog.data.email,
ltext: self.formDialog.data.text,
sats: self.formDialog.data.sats
})
.then(function (response) {
self.paymentReq = response.data.payment_request
self.paymentCheck = response.data.payment_hash
dismissMsg = self.$q.notify({
timeout: 0,
message: 'Waiting for payment...'
})
self.receive = {
show: true,
status: 'pending',
paymentReq: self.paymentReq
}
paymentChecker = setInterval(function () {
axios
.get('/lnticket/api/v1/tickets/' + self.paymentCheck)
.then(function (res) {
if (res.data.paid) {
clearInterval(paymentChecker)
self.receive = {
show: false,
status: 'complete',
paymentReq: null
}
dismissMsg()
self.formDialog.data.name = ''
self.formDialog.data.email = ''
self.formDialog.data.text = ''
self.$q.notify({
type: 'positive',
message: 'Sent, thank you!',
icon: 'thumb_up'
})
}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}, 2000)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}
}
})
</script>
{% endblock %}

View File

@@ -1,56 +1,449 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
<q-card>
<q-card-section>
<h5 class="text-subtitle1 q-mt-none q-mb-md">Frameworks used by LNbits</h5>
<q-list>
<q-item
v-for="tool in tools"
:key="tool.name"
tag="a"
:href="tool.url"
target="_blank"
>
{% raw %}
<!-- with raw Flask won't try to interpret the Vue moustaches -->
<q-item-section>
<q-item-label>{{ tool.name }}</q-item-label>
<q-item-label caption>{{ tool.language }}</q-item-label>
</q-item-section>
{% endraw %}
</q-item>
</q-list>
<q-separator class="q-my-lg"></q-separator>
<p>
A magical "g" is always available, with info about the user, wallets and
extensions:
</p>
<code class="text-caption">{% raw %}{{ g }}{% endraw %}</code>
</q-card-section>
</q-card>
<div class="row q-col-gutter-md">
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
<q-card>
<q-card-section>
<q-btn unelevated color="deep-purple" @click="formDialog.show = true"
>New Service</q-btn
>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Services</h5>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportservicesCSV"
>Export to CSV</q-btn
>
</div>
</div>
<q-table
dense
flat
:data="services"
row-key="id"
:columns="servicesTable.columns"
:pagination.sync="servicesTable.pagination"
>
{% raw %}
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
<q-th auto-width></q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
unelevated
dense
size="xs"
icon="link"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
:href="props.row.displayUrl"
target="_blank"
></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="updateformDialog(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="deleteService(props.row.id)"
icon="cancel"
color="pink"
></q-btn>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Donations</h5>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportdonationsCSV"
>Export to CSV</q-btn
>
</div>
</div>
<q-table
dense
flat
:data="donations"
row-key="id"
:columns="donationsTable.columns"
:pagination.sync="donationsTable.pagination"
>
{% raw %}
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" v-if="props.row.paid">
<q-td auto-width>
<q-btn
unelevated
dense
size="xs"
icon="email"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
:href="'mailto:' + props.row.email"
></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="deleteDonation(props.row.id)"
icon="cancel"
color="pink"
></q-btn>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-my-none">
LNbits Twitch Alerts extension
</h6>
</q-card-section>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list> {% include "twitchalerts/_api_docs.html" %} </q-list>
</q-card-section>
</q-card>
</div>
<q-dialog v-model="formDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="sendServiceData" class="q-gutter-md">
<q-select
filled
dense
emit-value
v-model="formDialog.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"
>
</q-select>
<q-input
filled
dense
v-model.trim="formDialog.data.username"
type="name"
label="Twitch Username *"
></q-input>
<q-input
filled
dense
v-model.trim="formDialog.data.service"
type="text"
label="Streamlabs"
hint="The service you use for alerts. (Currently only Streamlabs)"
readonly
placeholder="Streamlabs"
></q-input>
<q-input
filled
dense
v-model.trim="formDialog.data.clientid"
type="name"
label="Client ID *"
></q-input>
<q-input
filled
dense
v-model.trim="formDialog.data.clientsecret"
type="name"
label="Client Secret *"
></q-input>
<div class="row q-mt-lg">
<q-btn
v-if="formDialog.data.id"
unelevated
color="deep-purple"
type="submit"
>Update Service</q-btn
>
<q-btn
v-else
unelevated
color="deep-purple"
:disable="formDialog.data.clientid == null || formDialog.data.clientsecret == 0 || formDialog.data.username == null"
type="submit"
>Create Service</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
var mapTwitchAlerts = function(obj) {
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.amount)
obj.displayUrl = ['/twitchalerts/', obj.id].join('')
return obj
}
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
data: function() {
return {
tools: []
services: [],
donations: [],
servicesTable: {
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
{name: 'username', align: 'left', label: 'Twitch Username', field: 'username'},
{name: 'wallet', align: 'left', label: 'Wallet', field: 'wallet'},
{
name: 'service',
align: 'left',
label: 'Service',
field: 'service'
},
{
name: 'clientid',
align: 'left',
label: 'Client ID',
field: 'clientid'
},
{
name: 'clientsecret',
align: 'left',
label: 'Client Secret',
field: 'clientsecret'
}
],
pagination: {
rowsPerPage: 10
}
},
donationsTable: {
columns: [
{name: 'service', align: 'left', label: 'Service', field: 'service'},
{name: 'donor', align: 'left', label: 'Donor', field: 'donor'},
{name: 'ltext', align: 'left', label: 'Message', field: 'ltext'},
{name: 'sats', align: 'left', label: 'Sats', field: 'sats'}
],
pagination: {
rowsPerPage: 10
}
},
formDialog: {
show: false,
data: {}
}
}
},
created: function () {
var self = this
methods: {
getDonations: function() {
var self = this
// axios is available for making requests
axios({
method: 'GET',
url: '/twitchalerts/api/v1/tools',
headers: {
'X-TwitchAlerts-header': 'not-used'
LNbits.api
.request(
'GET',
'/twitchalerts/api/v1/donations',
this.g.user.wallets[0].inkey
)
.then(function(response) {
self.donations = response.data.map(function(obj) {
return mapTwitchAlerts(obj)
})
})
},
deleteDonation: function(ticketId) {
var self = this
var donations = _.findWhere(this.donations, {id: ticketId})
LNbits.utils
.confirmDialog('Are you sure you want to delete this ticket')
.onOk(function() {
LNbits.api
.request(
'DELETE',
'/twitchalerts/api/v1/donations/' + ticketId,
_.findWhere(self.g.user.wallets, {id: donations.wallet}).inkey
)
.then(function(response) {
self.donations = _.reject(self.donations, function(obj) {
return obj.id == ticketId
})
})
.catch(function(error) {
LNbits.utils.notifyApiError(error)
})
})
},
exportdonationsCSV: function() {
LNbits.utils.exportCSV(this.donationsTable.columns, this.donations)
},
getServices: function() {
var self = this
LNbits.api
.request(
'GET',
'/twitchalerts/api/v1/services?all_wallets',
this.g.user.wallets[0].inkey
)
.then(function(response) {
self.services = response.data.map(function(obj) {
return mapTwitchAlerts(obj)
})
})
},
sendServiceData: function() {
var wallet = _.findWhere(this.g.user.wallets, {
id: this.formDialog.data.wallet
})
var data = this.formDialog.data
if (data.id) {
this.updateService(wallet, data)
} else {
this.createService(wallet, data)
}
}).then(function (response) {
self.tools = response.data
})
},
createService: function(wallet, data) {
var self = this
LNbits.api
.request('POST', '/twitchalerts/api/v1/services', wallet.inkey, data)
.then(function(response) {
self.services.push(mapTwitchAlerts(response.data))
self.formDialog.show = false
self.formDialog.data = {}
})
.catch(function(error) {
LNbits.utils.notifyApiError(error)
})
},
updateformDialog: function(formId) {
var link = _.findWhere(this.services, {id: formId})
console.log(link.id)
this.formDialog.data.id = link.id
this.formDialog.data.wallet = link.wallet
this.formDialog.data.name = link.name
this.formDialog.data.description = link.description
this.formDialog.data.costpword = link.costpword
this.formDialog.show = true
},
updateService: function(wallet, data) {
var self = this
console.log(data)
LNbits.api
.request(
'PUT',
'/twitchalerts/api/v1/services/' + data.id,
wallet.inkey,
data
)
.then(function(response) {
self.services = _.reject(self.services, function(obj) {
return obj.id == data.id
})
self.services.push(mapTwitchAlerts(response.data))
self.formDialog.show = false
self.formDialog.data = {}
})
.catch(function(error) {
LNbits.utils.notifyApiError(error)
})
},
deleteService: function(servicesId) {
var self = this
var services = _.findWhere(this.services, {id: servicesId})
LNbits.utils
.confirmDialog('Are you sure you want to delete this service link?')
.onOk(function() {
LNbits.api
.request(
'DELETE',
'/twitchalerts/api/v1/services/' + servicesId,
_.findWhere(self.g.user.wallets, {id: services.wallet}).inkey
)
.then(function(response) {
self.services = _.reject(self.services, function(obj) {
return obj.id == servicesId
})
})
.catch(function(error) {
LNbits.utils.notifyApiError(error)
})
})
},
exportservicesCSV: function() {
LNbits.utils.exportCSV(this.servicesTable.columns, this.services)
}
},
created: function() {
if (this.g.user.wallets.length) {
this.getDonations()
this.getServices()
}
}
})
</script>