Skip to content

Commit e0a8f88

Browse files
Bug fixes
1 parent 3bb6822 commit e0a8f88

7 files changed

Lines changed: 155 additions & 79 deletions

File tree

frontend/package-lock.json

Lines changed: 33 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
"private": true,
77
"dependencies": {
88
"@babbage/go": "^0.1.16",
9+
"@bsv/amountinator-react": "^1.0.1",
910
"@bsv/identity-react": "^1.1.14",
10-
"@bsv/message-box-client": "^2.0.2",
11+
"@bsv/message-box-client": "^2.0.4",
1112
"@bsv/sdk": "^2.0.4",
1213
"@bsv/uhrp-react": "^1.0.6",
1314
"@emotion/react": "^11.11.1",
@@ -16,7 +17,6 @@
1617
"@mui/material": "^5.13.6",
1718
"@types/jest": "^29.5.11",
1819
"@types/node": "^20.11.0",
19-
"amountinator-react": "^0.1.40",
2020
"qr-scanner": "^1.4.2",
2121
"qrcode": "^1.5.4",
2222
"react": "^18.2.0",

frontend/src/App.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import RecentlySentList from './components/RecentlySentList'
66

77
import './App.scss'
88
import { toast } from 'react-toastify'
9-
import { AmountDisplay } from 'amountinator-react'
9+
import { AmountDisplay } from '@bsv/amountinator-react'
1010

1111
// Import PeerPayClient
1212
import { IncomingPayment } from '@bsv/message-box-client'
@@ -27,6 +27,11 @@ const App: React.FC = () => {
2727
const [loading, setLoading] = useState(false)
2828
const [bulkAccepting, setBulkAccepting] = useState(false)
2929

30+
const totalIncomingAmount = payments.reduce((sum, payment) => {
31+
const amount = Number(payment.token.amount)
32+
return sum + (Number.isFinite(amount) ? amount : 0)
33+
}, 0)
34+
3035
const fetchPayments = useCallback(async () => {
3136
try {
3237
setLoading(true)
@@ -178,8 +183,8 @@ const App: React.FC = () => {
178183
</Typography>
179184
<Box className='amount-inline amount-inline-total'>
180185
<AmountDisplay
181-
paymentAmount={payments.reduce((s, p) => s + (p.token.amount || 0), 0)}
182-
formatOptions={{ useCommas: true, decimalPlaces: 0 }}
186+
paymentAmount={totalIncomingAmount}
187+
formatOptions={{ useCommas: true, decimalPlaces: 2 }}
183188
/>
184189
</Box>
185190
</Box>

frontend/src/components/PaymentForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from '@mui/icons-material'
1010
import { toast } from 'react-toastify'
1111
import { DisplayableIdentity } from '@bsv/sdk'
12-
import { AmountInputField, AmountDisplay } from 'amountinator-react'
12+
import { AmountInputField, AmountDisplay } from '@bsv/amountinator-react'
1313
import ContactModal from './ContactModal'
1414
import { peerPayClient } from '../utils/peerPayClient'
1515

frontend/src/components/PaymentList.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React, { useState } from 'react'
2-
import { List, ListItem, Button, Box, Typography, CircularProgress, Avatar } from '@mui/material'
2+
import { List, ListItem, Button, Box, Typography, CircularProgress } from '@mui/material'
33
import { IncomingPayment } from '@bsv/message-box-client'
44
import { toast } from 'react-toastify'
5-
import { AmountDisplay } from 'amountinator-react'
5+
import { AmountDisplay } from '@bsv/amountinator-react'
6+
import { IdentityCard } from '@bsv/identity-react'
67
import { peerPayClient } from '../utils/peerPayClient'
78

89
export interface Payment {
@@ -25,8 +26,6 @@ interface PaymentListProps {
2526
isBulkAccepting?: boolean
2627
}
2728

28-
const abbreviateKey = (key: string) => `${key.slice(0, 10)}...${key.slice(-8)}`
29-
3029
const PaymentList: React.FC<PaymentListProps> = ({ payments = [], onUpdatePayments, isBulkAccepting = false }) => {
3130
const [processingMap, setProcessingMap] = useState<Record<string, 'accept' | 'reject'>>({})
3231

@@ -117,17 +116,20 @@ const PaymentList: React.FC<PaymentListProps> = ({ payments = [], onUpdatePaymen
117116
alignItems: 'center'
118117
}}
119118
>
120-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, minWidth: 0 }}>
121-
<Avatar sx={{ width: 34, height: 34, bgcolor: 'rgba(95, 226, 196, 0.22)', color: 'primary.main' }}>
122-
{payment.sender.slice(0, 2).toUpperCase()}
123-
</Avatar>
119+
<Box sx={{ display: 'flex', alignItems: 'center', minWidth: 0 }}>
124120
<Box sx={{ minWidth: 0 }}>
125-
<Typography variant='subtitle2' sx={{ fontWeight: 600 }} noWrap>
126-
{abbreviateKey(payment.sender)}
127-
</Typography>
128-
<Typography variant='caption' color='text.secondary' noWrap>
129-
Incoming transfer
130-
</Typography>
121+
<Box
122+
sx={{
123+
'& .MuiTypography-h6': {
124+
color: 'text.primary !important'
125+
},
126+
'& .MuiTypography-body2': {
127+
color: 'text.secondary !important'
128+
}
129+
}}
130+
>
131+
<IdentityCard identityKey={payment.sender} themeMode='dark' />
132+
</Box>
131133
</Box>
132134
</Box>
133135

@@ -138,7 +140,7 @@ const PaymentList: React.FC<PaymentListProps> = ({ payments = [], onUpdatePaymen
138140
</Typography>
139141
<AmountDisplay
140142
paymentAmount={payment.token.amount}
141-
formatOptions={{ useCommas: true, decimalPlaces: 0 }}
143+
formatOptions={{ useCommas: true, decimalPlaces: 2 }}
142144
/>
143145
</Box>
144146
</Box>

frontend/src/components/QRScanner.tsx

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,74 @@ const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen }) => {
3737
setIsLoading(true)
3838
setError('')
3939

40-
// Get camera stream directly for better compatibility
41-
const stream = await navigator.mediaDevices.getUserMedia({
42-
video: {
43-
facingMode: 'environment',
44-
width: { ideal: 1280, max: 1920 },
45-
height: { ideal: 720, max: 1080 },
46-
},
47-
})
40+
if (!window.isSecureContext && window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
41+
throw new Error('Camera access requires HTTPS or localhost. Please use a secure connection.')
42+
}
43+
44+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
45+
throw new Error('Camera API is not available in this browser.')
46+
}
4847

49-
// Set up video element with the stream so the scanner can reuse it
48+
if (scannerRef.current) {
49+
scannerRef.current.destroy()
50+
scannerRef.current = null
51+
}
52+
53+
if (videoRef.current.srcObject instanceof MediaStream) {
54+
const existingStream = videoRef.current.srcObject as MediaStream
55+
existingStream.getTracks().forEach(track => track.stop())
56+
videoRef.current.srcObject = null
57+
}
58+
59+
let stream: MediaStream
60+
61+
try {
62+
stream = await navigator.mediaDevices.getUserMedia({
63+
video: {
64+
facingMode: { ideal: 'environment' },
65+
width: { ideal: 1280, max: 1920 },
66+
height: { ideal: 720, max: 1080 }
67+
}
68+
})
69+
} catch {
70+
stream = await navigator.mediaDevices.getUserMedia({ video: true })
71+
}
72+
73+
// Set up video element with the stream and ensure playback starts
5074
videoRef.current.srcObject = stream
75+
videoRef.current.setAttribute('playsinline', 'true')
76+
videoRef.current.muted = true
77+
78+
await new Promise<void>((resolve, reject) => {
79+
const video = videoRef.current
80+
if (!video) {
81+
reject(new Error('Video element not available'))
82+
return
83+
}
84+
85+
if (video.readyState >= 2) {
86+
resolve()
87+
return
88+
}
89+
90+
const timeoutId = window.setTimeout(() => {
91+
video.onloadedmetadata = null
92+
reject(new Error('Timed out while waiting for camera stream'))
93+
}, 7000)
94+
95+
video.onloadedmetadata = () => {
96+
window.clearTimeout(timeoutId)
97+
video.onloadedmetadata = null
98+
resolve()
99+
}
100+
})
101+
102+
await videoRef.current.play()
103+
104+
// Persist the stream to ensure it can be stopped on cleanup
105+
setVideoStream(stream)
106+
setHasPermission(true)
107+
setIsLoading(false)
51108

52109
// Initialize QR scanner on the video element
53110
const scanner = new QrScanner(
@@ -61,21 +118,20 @@ const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen }) => {
61118
preferredCamera: 'environment',
62119
highlightScanRegion: true,
63120
highlightCodeOutline: true,
64-
returnDetailedScanResult: true,
121+
returnDetailedScanResult: true
65122
}
66123
)
67124

68125
// Configure scanner options
69126
scanner.setInversionMode('both')
70127
scannerRef.current = scanner
71128

72-
await scanner.start()
73-
74-
// Persist the stream to ensure it can be stopped on cleanup
75-
setVideoStream(stream)
76-
77-
setHasPermission(true)
78-
setIsLoading(false)
129+
await Promise.race([
130+
scanner.start(),
131+
new Promise((_, reject) => {
132+
window.setTimeout(() => reject(new Error('Camera initialization timed out. Please try again.')), 7000)
133+
})
134+
])
79135

80136
} catch (err: any) {
81137
// Ensure any partially opened resources are released
@@ -99,7 +155,7 @@ const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen }) => {
99155
setError('Camera not supported in this browser. Try using Chrome, Firefox, or Safari.')
100156
} else if (err.name === 'NotReadableError') {
101157
setError('Camera is already in use by another application')
102-
} else if (err.message?.includes('secure context')) {
158+
} else if (err.message?.includes('secure context') || err.message?.includes('HTTPS or localhost')) {
103159
setError('Camera access requires HTTPS or localhost. Please use a secure connection.')
104160
} else {
105161
setError(`Failed to initialize camera: ${err.message || 'Unknown error'}`)
@@ -211,11 +267,16 @@ const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen }) => {
211267
{isLoading && (
212268
<Box
213269
sx={{
270+
position: 'absolute',
271+
inset: 0,
214272
display: 'flex',
215273
flexDirection: 'column',
216274
alignItems: 'center',
217275
gap: 2,
218276
color: 'white',
277+
justifyContent: 'center',
278+
backgroundColor: 'rgba(0, 0, 0, 0.55)',
279+
zIndex: 2
219280
}}
220281
>
221282
<CircularProgress color="inherit" />
@@ -293,7 +354,7 @@ const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen }) => {
293354
width: '100%',
294355
height: '100%',
295356
objectFit: 'cover',
296-
display: isLoading || error ? 'none' : 'block',
357+
display: error ? 'none' : 'block'
297358
}}
298359
playsInline
299360
muted

0 commit comments

Comments
 (0)