diff --git a/lnbits/extensions/livestream/lnurl.py b/lnbits/extensions/livestream/lnurl.py index 49f3f4a6a..d36c694f5 100644 --- a/lnbits/extensions/livestream/lnurl.py +++ b/lnbits/extensions/livestream/lnurl.py @@ -26,7 +26,10 @@ async def lnurl_response(ls_id): metadata=await track.lnurlpay_metadata, ) - return jsonify(resp.dict()) + params = resp.dict() + params["commentAllowed"] = 300 + + return jsonify(params) @livestream_ext.route("/lnurl/cb/", methods=["GET"]) @@ -54,6 +57,12 @@ async def lnurl_callback(track_id): ), ) + comment = request.args.get("comment") + if len(comment or "") > 300: + return jsonify( + LnurlErrorResponse(reason=f"Got a comment with {len(comment)} characters, but can only accept 300").dict() + ) + ls = await get_livestream_by_track(track_id) payment_hash, payment_request = await create_invoice( @@ -61,9 +70,13 @@ async def lnurl_callback(track_id): amount=int(amount_received / 1000), memo=await track.description(), description_hash=hashlib.sha256((await track.lnurlpay_metadata).encode("utf-8")).digest(), - extra={"tag": "livestream", "track": track.id}, + extra={"tag": "livestream", "track": track.id, "comment": comment}, ) - resp = LnurlPayActionResponse(pr=payment_request, success_action=track.success_action(payment_hash), routes=[],) + resp = LnurlPayActionResponse( + pr=payment_request, + success_action=track.success_action(payment_hash), + routes=[], + ) return jsonify(resp.dict()) diff --git a/lnbits/extensions/livestream/static/js/index.js b/lnbits/extensions/livestream/static/js/index.js index bd8779478..81620d3a3 100644 --- a/lnbits/extensions/livestream/static/js/index.js +++ b/lnbits/extensions/livestream/static/js/index.js @@ -7,6 +7,7 @@ new Vue({ mixins: [windowMixin], data() { return { + cancelListener: () => {}, selectedWallet: null, nextCurrentTrack: null, livestream: { @@ -51,6 +52,7 @@ new Vue({ changedWallet(wallet) { this.selectedWallet = wallet this.loadLivestream() + this.startPaymentNotifier() }, loadLivestream() { LNbits.api @@ -67,6 +69,30 @@ new Vue({ LNbits.utils.notifyApiError(err) }) }, + startPaymentNotifier() { + this.cancelListener() + + this.cancelListener = LNbits.events.onInvoicePaid( + this.selectedWallet, + payment => { + let satoshiAmount = Math.round(payment.amount / 1000) + let trackName = ( + this.tracksMap[payment.extra.track] || {name: '[unknown]'} + ).name + + this.$q.notify({ + message: `Someone paid ${satoshiAmount} sat for the track ${trackName}.`, + caption: payment.extra.comment + ? `"${payment.extra.comment}"` + : undefined, + color: 'secondary', + html: true, + timeout: 0, + actions: [{label: 'Dismiss', color: 'white', handler: () => {}}] + }) + } + ) + }, addTrack() { let {name, producer, price_sat, download_url} = this.trackDialog.data @@ -171,5 +197,6 @@ new Vue({ created() { this.selectedWallet = this.g.user.wallets[0] this.loadLivestream() + this.startPaymentNotifier() } }) diff --git a/lnbits/static/js/base.js b/lnbits/static/js/base.js index 122d676d3..b13ded462 100644 --- a/lnbits/static/js/base.js +++ b/lnbits/static/js/base.js @@ -65,15 +65,37 @@ window.LNbits = { }, events: { onInvoicePaid: function (wallet, cb) { - if (!this.pis) { - this.pis = new EventSource( + let listener = ev => { + cb(JSON.parse(ev.data)) + } + + this.listenersCount = this.listenersCount || {[wallet.inkey]: 0} + this.listenersCount[wallet.inkey]++ + + this.listeners = this.listeners || {} + if (!(wallet.inkey in this.listeners)) { + this.listeners[wallet.inkey] = new EventSource( '/api/v1/payments/sse?api-key=' + wallet.inkey ) } - this.pis.addEventListener('payment-received', ev => - cb(JSON.parse(ev.data)) + this.listeners[wallet.inkey].addEventListener( + 'payment-received', + listener ) + + return () => { + this.listeners[wallet.inkey].removeEventListener( + 'payment-received', + listener + ) + this.listenersCount[wallet.inkey]-- + + if (this.listenersCount[wallet.inkey] <= 0) { + this.listeners[wallet.inkey].close() + delete this.listeners[wallet.inkey] + } + } } }, href: {