@@ -124,6 +124,7 @@ window.app = Vue.createApp({
124124 totalfsat : 0 ,
125125 addedAmount : 0 ,
126126 enablePrint : false ,
127+ enableRemote : false ,
127128 receiptData : null ,
128129 orderReceipt : false ,
129130 printDialog : {
@@ -139,6 +140,9 @@ window.app = Vue.createApp({
139140 this . $q . localStorage . getItem ( 'lnbits.tpos.header' ) !== 'shown' ,
140141 categoryColors : { } ,
141142 categoryColorIndex : 0 ,
143+ remoteInvoiceWs : null ,
144+ remoteInvoiceReconnectTimer : null ,
145+ paymentWsByHash : { } ,
142146 pastelColors : [
143147 'blue-5' ,
144148 'green-5' ,
@@ -261,6 +265,73 @@ window.app = Vue.createApp({
261265 }
262266 } ,
263267 methods : {
268+ connectRemoteInvoiceWS ( ) {
269+ if ( ! this . enableRemote || ! this . tposId ) return
270+ if ( this . remoteInvoiceWs ) return
271+
272+ const url = new URL ( window . location )
273+ url . protocol = url . protocol === 'https:' ? 'wss' : 'ws'
274+ url . pathname = `/api/v1/ws/${ this . tposId } `
275+ const ws = new WebSocket ( url )
276+ this . remoteInvoiceWs = ws
277+
278+ ws . onmessage = ( { data} ) => this . handleRemoteInvoiceMessage ( data )
279+ ws . onclose = ( ) => {
280+ this . remoteInvoiceWs = null
281+ if ( this . enableRemote ) {
282+ this . remoteInvoiceReconnectTimer = setTimeout ( ( ) => {
283+ this . connectRemoteInvoiceWS ( )
284+ } , 2000 )
285+ }
286+ }
287+ ws . onerror = err => {
288+ console . warn ( 'Remote websocket error:' , err )
289+ }
290+ } ,
291+ disconnectRemoteInvoiceWS ( ) {
292+ if ( this . remoteInvoiceReconnectTimer ) {
293+ clearTimeout ( this . remoteInvoiceReconnectTimer )
294+ this . remoteInvoiceReconnectTimer = null
295+ }
296+ if ( this . remoteInvoiceWs ) {
297+ this . remoteInvoiceWs . close ( )
298+ this . remoteInvoiceWs = null
299+ }
300+ } ,
301+ handleRemoteInvoiceMessage ( rawData ) {
302+ let payload = null
303+ try {
304+ payload = JSON . parse ( rawData )
305+ } catch {
306+ // Ignore non-JSON events from other TPoS websocket usages.
307+ return
308+ }
309+
310+ if ( payload . type !== 'invoice_created' ) return
311+ if ( ! payload . payment_hash || ! payload . payment_request ) return
312+ this . amount = payload . amount_fiat || this . amount
313+ this . tipAmount = payload . tip_amount || this . tipAmount
314+ this . exchangeRate = payload . exchange_rate || this . exchangeRate
315+
316+ this . openInvoiceDialog ( payload . payment_hash , payload . payment_request )
317+ this . subscribeToPaymentWS ( payload . payment_hash )
318+ } ,
319+ openInvoiceDialog ( paymentHash , paymentRequest ) {
320+ if (
321+ this . invoiceDialog . show &&
322+ this . invoiceDialog . data . payment_hash === paymentHash
323+ ) {
324+ return
325+ }
326+ this . invoiceDialog . data . payment_hash = paymentHash
327+ this . invoiceDialog . data . payment_request = paymentRequest
328+ this . invoiceDialog . show = true
329+ this . readNfcTag ( )
330+ this . invoiceDialog . dismissMsg = Quasar . Notify . create ( {
331+ timeout : 0 ,
332+ message : 'Waiting for payment...'
333+ } )
334+ } ,
264335 setColor ( category ) {
265336 if ( ! category || category . toLowerCase ( ) === 'all' ) {
266337 return 'primary'
@@ -338,6 +409,7 @@ window.app = Vue.createApp({
338409 if ( ! cartItem ) return
339410 this . $q
340411 . dialog ( {
412+ position : 'top' ,
341413 title : 'Set price' ,
342414 message : 'Update item price for this cart line' ,
343415 prompt : {
@@ -366,6 +438,7 @@ window.app = Vue.createApp({
366438 if ( ! cartItem ) return
367439 this . $q
368440 . dialog ( {
441+ position : 'top' ,
369442 title : 'Set note' ,
370443 message : 'Add a note for this item' ,
371444 prompt : {
@@ -838,40 +911,33 @@ window.app = Vue.createApp({
838911 null ,
839912 params
840913 )
914+ let paymentRequest = 'lightning:' + data . bolt11 . toUpperCase ( )
841915 if (
842916 data . extra . fiat_payment_request &&
843917 ! data . extra . fiat_payment_request . startsWith ( 'pi_' )
844918 ) {
845- this . invoiceDialog . data . payment_request =
846- data . extra . fiat_payment_request
919+ paymentRequest = data . extra . fiat_payment_request
847920 } else if (
848921 data . extra . fiat_payment_request &&
849922 data . extra . fiat_payment_request . startsWith ( 'pi_' )
850923 ) {
851- this . invoiceDialog . data . payment_request = 'tap_to_pay'
852- } else {
853- this . invoiceDialog . data . payment_request =
854- 'lightning:' + data . bolt11 . toUpperCase ( )
924+ paymentRequest = 'tap_to_pay'
855925 }
856- this . invoiceDialog . data . payment_hash = data . payment_hash
857- this . invoiceDialog . show = true
858- this . readNfcTag ( )
859- this . invoiceDialog . dismissMsg = Quasar . Notify . create ( {
860- timeout : 0 ,
861- message : 'Waiting for payment...'
862- } )
926+ this . openInvoiceDialog ( data . payment_hash , paymentRequest )
863927 this . subscribeToPaymentWS ( data . payment_hash )
864928 } catch ( error ) {
865929 console . error ( error )
866930 LNbits . utils . notifyApiError ( error )
867931 }
868932 } ,
869933 subscribeToPaymentWS ( paymentHash ) {
934+ if ( this . paymentWsByHash [ paymentHash ] ) return
870935 try {
871936 const url = new URL ( window . location )
872937 url . protocol = url . protocol === 'https:' ? 'wss' : 'ws'
873938 url . pathname = `/api/v1/ws/${ paymentHash } `
874939 const ws = new WebSocket ( url )
940+ this . paymentWsByHash [ paymentHash ] = ws
875941 ws . onmessage = async ( { data} ) => {
876942 const payment = JSON . parse ( data )
877943 if ( payment . pending === false ) {
@@ -889,6 +955,9 @@ window.app = Vue.createApp({
889955 ws . close ( )
890956 }
891957 }
958+ ws . onclose = ( ) => {
959+ delete this . paymentWsByHash [ paymentHash ]
960+ }
892961 } catch ( err ) {
893962 console . warn ( err )
894963 LNbits . utils . notifyApiError ( err )
@@ -1302,6 +1371,7 @@ window.app = Vue.createApp({
13021371 this . tposLNaddress = tpos . lnaddress
13031372 this . tposLNaddressCut = tpos . lnaddress_cut
13041373 this . enablePrint = tpos . enable_receipt_print
1374+ this . enableRemote = Boolean ( tpos . enable_remote )
13051375 this . fiatProvider = tpos . fiat_provider
13061376
13071377 this . tip_options = tpos . tip_options == 'null' ? null : tpos . tip_options
@@ -1334,6 +1404,11 @@ window.app = Vue.createApp({
13341404 if ( this . headerElement ) {
13351405 this . headerElement . style . display = this . headerHidden ? 'none' : ''
13361406 }
1407+ this . connectRemoteInvoiceWS ( )
1408+ } ,
1409+ beforeUnmount ( ) {
1410+ this . disconnectRemoteInvoiceWS ( )
1411+ Object . values ( this . paymentWsByHash ) . forEach ( ws => ws . close ( ) )
13371412 } ,
13381413 onMounted ( ) {
13391414 if ( ! this . headerElement ) {
0 commit comments