import Head from 'next/head'
import Link from 'next/link'
import dynamic from 'next/dynamic'
import React, { useEffect, useRef, useState } from 'react'
import { QRCodeSVG } from 'qrcode.react'
import styles from '../../styles/Post.module.scss'
import localStyles from './ee-qrcode.module.scss'
import HeaderNav from '../../lib/HeaderNav'
import HeroImage from '../../lib/HeroImage'
let scannerWorker = null
async function startCapture(video, canvas, captureType) {
if (typeof window !== 'undefined') {
let captureStream = null
try {
if (captureType === 'screen')
captureStream = await navigator.mediaDevices.getDisplayMedia({ audio: false, video: true })
else
captureStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { facingMode: 'environment' } })
} catch (e) {
alert(`Failed to start screen capture: ${e}`)
return
}
video.srcObject = captureStream
video.play()
await new Promise(resolve => {
video.addEventListener('loadedmetadata', resolve)
})
canvas.width = video.videoWidth
canvas.height = video.videoHeight
return (() => {
captureStream.getTracks().forEach(track => track.stop())
video.srcObject = null
})
}
}
function scanImage(imageData) {
if (scannerWorker === null) {
scannerWorker = new Worker(new URL('../../lib/ee-qrcode.worker.js', import.meta.url))
}
return new Promise(resolve => {
scannerWorker.onmessage = e => resolve(e.data.response)
scannerWorker.postMessage({imageData})
})
}
async function processImage(video, hiddenCanvas, previewCanvas, filterSymbols, onResult) {
const hiddenCtx = hiddenCanvas.getContext('2d')
hiddenCtx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
const allSymbols = await scanImage(hiddenCtx.getImageData(0, 0, video.videoWidth, video.videoHeight))
previewCanvas.width = video.videoWidth
previewCanvas.height = video.videoHeight
const previewCtx = previewCanvas.getContext('2d')
previewCtx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
const symbols = allSymbols.filter(filterSymbols || (() => true))
previewCtx.strokeStyle = 'rgba(255, 0, 0, 0.7)'
previewCtx.lineWidth = 6
symbols.map((sym) => {
if (sym.points < 2) return
const [firstPoint, ...points] = sym.points
previewCtx.beginPath()
previewCtx.moveTo(firstPoint.x, firstPoint.y)
points.forEach(point => previewCtx.lineTo(point.x, point.y))
previewCtx.closePath()
previewCtx.stroke()
})
if (symbols.length > 0) {
onResult(symbols[0].value)
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function runScanner({ video, canvas, previewCanvas, filterSymbols, onResult, running, captureType }) {
const stopCapture = await startCapture(video, canvas, captureType)
while (running()) {
await processImage(video, canvas, previewCanvas, filterSymbols, onResult)
await sleep(500)
}
stopCapture()
}
function CodeScanner({ filterSymbols, onResult, captureType }) {
const videoRef = useRef(null)
const canvasRef = useRef(null)
const previewCanvasRef = useRef(null)
useEffect(() => {
let running = true;
if (videoRef.current && canvasRef.current && previewCanvasRef.current) {
runScanner({ video: videoRef.current, canvas: canvasRef.current, previewCanvas: previewCanvasRef.current, running: (() => running), filterSymbols, onResult, captureType })
}
return (() => {
running = false;
});
}, [videoRef, canvasRef, previewCanvasRef, captureType])
return (
)
}
function esimQrcodeFilter(symbol) {
if (symbol.typeName !== 'ZBAR_QRCODE') return false
if (!symbol.value.match(/^LPA:/)) return false
return true
}
function fixData(srcData) {
return srcData.replace(/[$][$]$/, '')
}
function StartCaptureButtons({ setRunning }) {
const isBrowser = typeof window !== 'undefined'
const hasScreenCapture = isBrowser && typeof navigator.mediaDevices.getDisplayMedia === 'function';
const hasWebcamCapture = isBrowser && typeof navigator.mediaDevices.getUserMedia === 'function';
return (
{hasScreenCapture ? setRunning('screen')}>Start Screen Capture : null}
{hasWebcamCapture ? setRunning('camera')}>Start Webcam Capture : null}
)
}
export default function EsimMangler() {
const [running, setRunning] = useState(false)
const [scannedData, setScannedData] = useState(null)
return (
EE QR Code Mangler
EE QR Code Mangler | Luke Granger-Brown
EE eSIM QR Code Fixer
EE, as of 2022-05-01, still generates not-quite-spec-compliant eSIM QR Codes, which get rejected by some Android devices - including the Pixel series of devices.
This page is intended to generate a fixed version of the QR code (which happens entirely on your computer!).
{running ? setRunning(false)}>Stop Capture : (
)}
{(scannedData !== null) ?
: null}
{(scannedData !== null) ?
{fixData(scannedData)}
: null}
{(running != false) ?
{
setScannedData(result)
setRunning(false)
}} captureType={running} /> : null}
)
}