-
Notifications
You must be signed in to change notification settings - Fork 89
Expand file tree
/
Copy pathindex.tsx
More file actions
126 lines (113 loc) · 3.88 KB
/
index.tsx
File metadata and controls
126 lines (113 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import React, { useEffect, useRef, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { askForCameraAccess } from 'services/remote'
import Dialog from 'widgets/Dialog'
import LoadingDialog from 'widgets/LoadingDialog'
import AlertDialog from 'widgets/AlertDialog'
import { isSuccessResponse } from 'utils'
import jsQR from 'jsqr'
import styles from './cameraScanDialog.module.scss'
const IMAGE_SIZE = 400
export interface Point {
x: number
y: number
}
const CameraScanDialog = ({ close, onConfirm }: { close: () => void; onConfirm: (result: string) => void }) => {
const [t] = useTranslation()
const videoRef = useRef<HTMLVideoElement>()
const canvasRef = useRef<HTMLCanvasElement>(null)
const canvas2dRef = useRef<CanvasRenderingContext2D>()
const [dialogType, setDialogType] = useState<'' | 'opening' | 'access-fail' | 'scan'>('')
const drawLine = (begin: Point, end: Point) => {
if (!canvas2dRef.current) return
canvas2dRef.current.beginPath()
canvas2dRef.current.moveTo(begin.x, begin.y)
canvas2dRef.current.lineTo(end.x, end.y)
canvas2dRef.current.lineWidth = 4
canvas2dRef.current.strokeStyle = '#00c891'
canvas2dRef.current.stroke()
}
const scan = useCallback(() => {
if (videoRef.current?.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA) {
setDialogType('scan')
const canvas2d = canvasRef.current?.getContext('2d')
if (canvas2d) {
canvas2d.drawImage(videoRef.current, 0, 0, IMAGE_SIZE, IMAGE_SIZE)
canvas2dRef.current = canvas2d
const imageData = canvas2d.getImageData(0, 0, IMAGE_SIZE, IMAGE_SIZE)
const code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: 'dontInvert',
})
if (code) {
drawLine(code.location.topLeftCorner, code.location.topRightCorner)
drawLine(code.location.topRightCorner, code.location.bottomRightCorner)
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner)
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner)
onConfirm(code.data)
}
}
}
requestAnimationFrame(scan)
}, [setDialogType])
useEffect(() => {
let mediaStream: MediaStream
askForCameraAccess().then(accessRes => {
if (isSuccessResponse(accessRes)) {
setDialogType('opening')
navigator.mediaDevices
.getUserMedia({
audio: false,
video: { width: IMAGE_SIZE, height: IMAGE_SIZE },
})
.then(res => {
if (res) {
videoRef.current = document.createElement('video')
videoRef.current.srcObject = res
videoRef.current.play()
mediaStream = res
requestAnimationFrame(scan)
}
})
} else {
setDialogType('access-fail')
}
})
return () => {
if (mediaStream) {
mediaStream.getTracks().forEach(track => {
track.stop()
})
}
}
}, [])
return (
<>
<Dialog
show={dialogType === 'scan'}
title={t('wallet-connect.scan-with-camera')}
onCancel={close}
showCancel={false}
showConfirm={false}
showFooter={false}
>
<div className={styles.container}>
<div className={styles.scanBox}>
<canvas ref={canvasRef} width="400px" height="400px" />
</div>
</div>
</Dialog>
<LoadingDialog show={dialogType === 'opening'} message={t('wallet-connect.camera-connecting')} />
<AlertDialog
show={dialogType === 'access-fail'}
title={t('wallet-connect.camera-fail')}
message={t('wallet-connect.camera-msg')}
type="failed"
onCancel={() => {
close()
}}
/>
</>
)
}
CameraScanDialog.displayName = 'CameraScanDialog'
export default CameraScanDialog