{% extends "public.html" %} {% block toolbar_title %} {{ tpos.name }}
<q-btn
  flat
  dense
  size="md"
  @click.prevent="urlDialog.show = true"
  icon="share"
  color="white"
></q-btn>
{% endblock %} {% block footer %}{% endblock %} {% block page_container %}
<q-page-container>
  <q-page>
    <q-page-sticky v-if="exchangeRate" expand position="top">
      <div class="row justify-center full-width">
        <div class="col-12 col-sm-8 col-md-6 col-lg-4 text-center">
          <h3 class="q-mb-md">{% raw %}{{ famount }}{% endraw %}</h3>
          <h5 class="q-mt-none q-mb-sm">
            {% raw %}{{ fsat }}{% endraw %} <small>sat</small>
          </h5>
        </div>
      </div>
    </q-page-sticky>
    <q-page-sticky expand position="bottom">
      <div class="row justify-center full-width">
        <div class="col-12 col-sm-8 col-md-6 col-lg-4">
          <div class="keypad q-pa-sm">
            <q-btn
              unelevated
              @click="stack.push(1)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >1</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(2)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >2</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(3)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >3</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(4)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >4</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(5)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >5</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(6)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >6</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(7)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >7</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(8)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >8</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(9)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >9</q-btn
            >
            <q-btn
              unelevated
              @click="stack.splice(-1, 1)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >DEL</q-btn
            >
            <q-btn
              unelevated
              @click="stack.push(0)"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              >0</q-btn
            >
            <q-btn
              unelevated
              @click="stack = []"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              class="btn-cancel"
              >C</q-btn
            >
            <q-btn
              unelevated
              :disabled="amount == 0"
              @click="submitForm()"
              size="xl"
              :outline="!($q.dark.isActive)"
              rounded
              color="primary"
              class="btn-confirm"
              >OK</q-btn
            >
          </div>
        </div>
      </div>
    </q-page-sticky>
    <q-dialog
      v-model="invoiceDialog.show"
      position="top"
      @hide="closeInvoiceDialog"
    >
      <q-card
        v-if="invoiceDialog.data"
        class="q-pa-lg q-pt-xl lnbits__dialog-card"
      >
        <q-responsive :ratio="1" class="q-mx-xl q-mb-md">
          <qrcode
            :value="invoiceDialog.data.payment_request"
            :options="{width: 800}"
            class="rounded-borders"
          ></qrcode>
        </q-responsive>
        <div class="text-center">
          <h3 class="q-my-md">{% raw %}{{ famount }}{% endraw %}</h3>
          <h5 class="q-mt-none">
            {% raw %}{{ fsat }}
            <small>sat</small>
            <span v-show="tip_options" style="font-size: 0.75rem"
              >( + {{ tipAmountSat }} tip)</span
            >
            {% endraw %}
          </h5>
          <q-btn
            outline
            color="grey"
            icon="nfc"
            @click="readNfcTag()"
            :disable="nfcTagReading"
          ></q-btn>
        </div>
        <div class="row q-mt-lg">
          <q-btn
            outline
            color="grey"
            @click="copyText(invoiceDialog.data.payment_request)"
            >Copy invoice</q-btn
          >
          <q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
        </div>
      </q-card>
    </q-dialog>

    <q-dialog v-model="tipDialog.show" position="top">
      <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
        <div class="text-center q-mb-xl">
          <b style="font-size: 24px">Would you like to leave a tip?</b>
        </div>
        <div class="text-center q-mb-xl">
          <q-btn
            style="padding: 10px; margin: 3px"
            unelevated
            @click="processTipSelection(tip)"
            size="xl"
            :outline="!($q.dark.isActive)"
            rounded
            color="primary"
            v-for="tip in this.tip_options"
            :key="tip"
            >{% raw %}{{ tip }}{% endraw %}%</q-btn
          >
        </div>
        <div class="text-center q-mb-xl">
          <p><a @click="processTipSelection(0)"> No, thanks</a></p>
        </div>
        <div class="row q-mt-lg">
          <q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
        </div>
      </q-card>
    </q-dialog>

    <q-dialog v-model="urlDialog.show" position="top">
      <q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
        <q-responsive :ratio="1" class="q-mx-xl q-mb-md">
          <qrcode
            value="{{ request.url }}"
            :options="{width: 800}"
            class="rounded-borders"
          ></qrcode>
        </q-responsive>
        <div class="text-center q-mb-xl">
          <p style="word-break: break-all">
            <strong>{{ tpos.name }}</strong><br />{{ request.url }}
          </p>
        </div>
        <div class="row q-mt-lg">
          <q-btn
            outline
            color="grey"
            @click="copyText('{{ request.url }}', 'TPoS URL copied to clipboard!')"
            >Copy URL</q-btn
          >
          <q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
        </div>
      </q-card>
    </q-dialog>

    <q-dialog v-model="complete.show" position="top">
      <q-icon
        name="check"
        transition-show="fade"
        class="text-light-green"
        style="font-size: min(90vw, 40em)"
      ></q-icon>
    </q-dialog>
  </q-page>
</q-page-container>
{% endblock %} {% block styles %}
<style>
  * {
    touch-action: manipulation;
  }

  .keypad {
    display: grid;
    grid-gap: 8px;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(4, 1fr);
  }

  .keypad .btn {
    height: 100%;
  }

  .keypad .btn-confirm {
    grid-area: 1 / 4 / 5 / 4;
  }
</style>
{% endblock %} {% block scripts %}
<script>
  Vue.component(VueQrcode.name, VueQrcode)

  new Vue({
    el: '#vue',
    mixins: [windowMixin],
    data: function () {
      return {
        tposId: '{{ tpos.id }}',
        currency: '{{ tpos.currency }}',
        tip_options: null,
        exchangeRate: null,
        stack: [],
        tipAmount: 0.0,
        hasNFC: false,
        nfcTagReading: false,
        invoiceDialog: {
          show: false,
          data: null,
          dismissMsg: null,
          paymentChecker: null
        },
        tipDialog: {
          show: false
        },
        urlDialog: {
          show: false
        },
        complete: {
          show: false
        }
      }
    },
    computed: {
      amount: function () {
        if (!this.stack.length) return 0.0
        return (Number(this.stack.join('')) / 100).toFixed(2)
      },
      famount: function () {
        return LNbits.utils.formatCurrency(this.amount, this.currency)
      },
      sat: function () {
        if (!this.exchangeRate) return 0
        return Math.ceil(
          ((this.amount - this.tipAmount) / this.exchangeRate) * 100000000
        )
      },
      tipAmountSat: function () {
        if (!this.exchangeRate) return 0
        return Math.ceil((this.tipAmount / this.exchangeRate) * 100000000)
      },
      fsat: function () {
        return LNbits.utils.formatSat(this.sat)
      }
    },
    methods: {
      closeInvoiceDialog: function () {
        this.stack = []
        this.tipAmount = 0.0
        var dialog = this.invoiceDialog
        setTimeout(function () {
          clearInterval(dialog.paymentChecker)
          dialog.dismissMsg()
        }, 3000)
      },
      processTipSelection: function (selectedTipOption) {
        this.tipDialog.show = false

        if (selectedTipOption) {
          const tipAmount = parseFloat(
            parseFloat((selectedTipOption / 100) * this.amount)
          )
          const subtotal = parseFloat(this.amount)
          const grandTotal = parseFloat((tipAmount + subtotal).toFixed(2))
          const totalString = grandTotal.toFixed(2).toString()

          this.stack = []
          for (var i = 0; i < totalString.length; i++) {
            const char = totalString[i]

            if (char !== '.') {
              this.stack.push(char)
            }
          }

          this.tipAmount = tipAmount
        }

        this.showInvoice()
      },
      submitForm: function () {
        if (this.tip_options && this.tip_options.length) {
          this.showTipModal()
        } else {
          this.showInvoice()
        }
      },
      showTipModal: function () {
        this.tipDialog.show = true
      },
      showInvoice: function () {
        var self = this
        var dialog = this.invoiceDialog
        axios
          .post('/tpos/api/v1/tposs/' + this.tposId + '/invoices', null, {
            params: {
              amount: this.sat,
              tipAmount: this.tipAmountSat
            }
          })
          .then(function (response) {
            dialog.data = response.data
            dialog.show = true

            dialog.dismissMsg = self.$q.notify({
              timeout: 0,
              message: 'Waiting for payment...'
            })

            dialog.paymentChecker = setInterval(function () {
              axios
                .get(
                  '/tpos/api/v1/tposs/' +
                    self.tposId +
                    '/invoices/' +
                    response.data.payment_hash
                )
                .then(function (res) {
                  if (res.data.paid) {
                    clearInterval(dialog.paymentChecker)
                    dialog.dismissMsg()
                    dialog.show = false

                    self.complete.show = true
                  }
                })
            }, 3000)
          })
          .catch(function (error) {
            LNbits.utils.notifyApiError(error)
          })
      },
      readNfcTag: function () {
        try {
          const self = this

          if (typeof NDEFReader == 'undefined') {
            throw {
              toString: function () {
                return 'NFC not supported on this device or browser.'
              }
            }
          }

          const ndef = new NDEFReader()

          const readerAbortController = new AbortController()
          readerAbortController.signal.onabort = event => {
            console.log('All NFC Read operations have been aborted.')
          }

          this.nfcTagReading = true
          this.$q.notify({
            message: 'Tap your NFC tag to pay this invoice with LNURLw.'
          })

          return ndef.scan({signal: readerAbortController.signal}).then(() => {
            ndef.onreadingerror = () => {
              self.nfcTagReading = false

              this.$q.notify({
                type: 'negative',
                message: 'There was an error reading this NFC tag.'
              })

              readerAbortController.abort()
            }

            ndef.onreading = ({message}) => {
              //Decode NDEF data from tag
              const textDecoder = new TextDecoder('utf-8')

              const record = message.records.find(el => {
                const payload = textDecoder.decode(el.data)
                return payload.toUpperCase().indexOf('LNURL') !== -1
              })

              const lnurl = textDecoder.decode(record.data)

              //User feedback, show loader icon
              self.nfcTagReading = false
              self.payInvoice(lnurl, readerAbortController)

              this.$q.notify({
                type: 'positive',
                message: 'NFC tag read successfully.'
              })
            }
          })
        } catch (error) {
          this.nfcTagReading = false
          this.$q.notify({
            type: 'negative',
            message: error
              ? error.toString()
              : 'An unexpected error has occurred.'
          })
        }
      },
      payInvoice: function (lnurl, readerAbortController) {
        const self = this

        return axios
          .post(
            '/tpos/api/v1/tposs/' +
              self.tposId +
              '/invoices/' +
              self.invoiceDialog.data.payment_request +
              '/pay',
            {
              lnurl: lnurl
            }
          )
          .then(response => {
            if (!response.data.success) {
              this.$q.notify({
                type: 'negative',
                message: response.data.detail
              })
            }

            readerAbortController.abort()
          })
      },
      getRates: function () {
        var self = this
        axios.get('https://api.opennode.co/v1/rates').then(function (response) {
          self.exchangeRate =
            response.data.data['BTC' + self.currency][self.currency]
        })
      }
    },
    created: function () {
      var getRates = this.getRates
      getRates()
      this.tip_options =
        '{{ tpos.tip_options | tojson }}' == 'null'
          ? null
          : JSON.parse('{{ tpos.tip_options }}')
      setInterval(function () {
        getRates()
      }, 120000)
    }
  })
</script>
{% endblock %}
