Skip to content

Commit a534316

Browse files
committed
fix: various lint/type errors
1 parent a577504 commit a534316

File tree

6 files changed

+66
-37
lines changed

6 files changed

+66
-37
lines changed

src/components/QrcodeCapture.vue

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,13 @@ const props = defineProps({
2424
const emit = defineEmits(['detect'])
2525
2626
// methods
27-
const onDetect = async promise => {
28-
// FIXME: why await twice here???
29-
const detectedCodes = await (await promise)
30-
emit('detect', detectedCodes)
31-
}
32-
3327
const onChangeInput = (event: Event) => {
3428
if (!(event.target instanceof HTMLInputElement) || !event.target.files) return
3529
36-
const files = [...Array.from(event.target.files)]
37-
38-
for (const promise of files.map(file => processFile(file, props.formats))) {
39-
onDetect(promise)
30+
for (const file of Array.from(event.target.files)) {
31+
processFile(file, props.formats).then(detectedCodes => {
32+
emit('detect', detectedCodes)
33+
})
4034
}
4135
}
4236
</script>

src/components/QrcodeDropZone.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const props = defineProps({
2424
const emit = defineEmits(['detect', 'dragover', 'error'])
2525
2626
// methods
27-
const onDetect = async promise => {
27+
const onDetect = async (promise : Promise<any>) => {
2828
try {
2929
const detectedCodes = await promise
3030
emit('detect', detectedCodes)

src/components/QrcodeStream.vue

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@
2828
import type { DetectedBarcode, BarcodeFormat } from '@sec-ant/barcode-detector'
2929
import { nextTick, onUnmounted, computed, onMounted, ref, toRefs, watch, type PropType } from 'vue'
3030
31-
import { adaptOldFormat, keepScanning, setScanningFormats } from '../misc/scanner'
31+
import { keepScanning, setScanningFormats } from '../misc/scanner'
3232
import * as cameraController from '../misc/camera'
3333
import type { Point } from '../types/types'
34+
import { assert } from '../misc/util'
3435
3536
const props = defineProps({
3637
constraints: {
37-
type: Object,
38+
type: Object as PropType<MediaTrackConstraints>,
3839
default() {
3940
return { facingMode: 'environment' }
4041
}
@@ -72,7 +73,6 @@ const isMounted = ref(false)
7273
7374
onMounted(() => {
7475
isMounted.value = true
75-
setScanningFormats(props.formats)
7676
})
7777
7878
onUnmounted(() => {
@@ -97,9 +97,18 @@ const cameraSettings = computed(() => {
9797
})
9898
9999
watch(cameraSettings, async cameraSettings => {
100+
const videoEl = videoRef.value
101+
assert(videoEl !== undefined, 'cameraSettings watcher should never be triggered when component is not mounted. Thus video element should always be defined.')
102+
103+
const canvas = pauseFrameRef.value
104+
assert(canvas !== undefined, 'cameraSettings watcher should never be triggered when component is not mounted. Thus canvas should always be defined.')
105+
106+
const ctx = canvas.getContext('2d')
107+
assert(ctx !== null, 'if cavnas is defined, canvas 2d context should also be non-null')
108+
100109
if (cameraSettings.shouldStream) { // start camera
101110
try {
102-
const capabilities = await cameraController.start(videoRef.value, cameraSettings)
111+
const capabilities = await cameraController.start(videoEl, cameraSettings)
103112
104113
// if the component is destroyed before `camera.start` resolves the
105114
// `onBeforeUnmount` hook has no chance to clear the remaining camera
@@ -116,14 +125,10 @@ watch(cameraSettings, async cameraSettings => {
116125
}
117126
} else { // stop camera
118127
// paint pause frame
119-
const canvas = pauseFrameRef.value
120-
const ctx = canvas.getContext('2d')
121-
const video = videoRef.value
122-
123-
canvas.width = video.videoWidth
124-
canvas.height = video.videoHeight
128+
canvas.width = videoEl.videoWidth
129+
canvas.height = videoEl.videoHeight
125130
126-
ctx?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
131+
ctx.drawImage(videoEl, 0, 0, videoEl.videoWidth, videoEl.videoHeight)
127132
128133
cameraController.stop()
129134
cameraActive.value = false
@@ -146,7 +151,10 @@ const shouldScan = computed(() => cameraSettings.value.shouldStream && cameraAct
146151
147152
watch(shouldScan, shouldScan => {
148153
if (shouldScan) {
154+
assert(pauseFrameRef.value !== undefined, 'shouldScan watcher should only be triggered when component is mounted. Thus pause frame canvas is defined')
149155
clearCanvas(pauseFrameRef.value)
156+
157+
assert(trackingLayerRef.value !== undefined, 'shouldScan watcher should only be triggered when component is mounted. Thus tracking canvas is defined')
150158
clearCanvas(trackingLayerRef.value)
151159
152160
// Minimum delay in milliseconds between frames to be scanned. Don't scan
@@ -159,8 +167,9 @@ watch(shouldScan, shouldScan => {
159167
}
160168
}
161169
170+
assert(videoRef.value !== undefined, 'shouldScan watcher should only be triggered when component is mounted. Thus video element is defined')
162171
keepScanning(videoRef.value, {
163-
detectHandler: detectedCodes => emit('detect', detectedCodes),
172+
detectHandler: (detectedCodes : DetectedBarcode[]) => emit('detect', detectedCodes),
164173
formats: props.formats,
165174
locateHandler: onLocate,
166175
minDelay: scanInterval()
@@ -171,23 +180,16 @@ watch(shouldScan, shouldScan => {
171180
// methods
172181
const clearCanvas = (canvas: HTMLCanvasElement) => {
173182
const ctx = canvas.getContext('2d')
174-
175-
console.assert(
176-
ctx !== undefined,
177-
'tried to clear canvas with undefined 2D context'
178-
)
179-
183+
assert(ctx !== null, 'canvas 2d context should always be non-null')
180184
ctx.clearRect(0, 0, canvas.width, canvas.height)
181185
}
182186
183187
const onLocate = (detectedCodes: DetectedBarcode[]) => {
184188
const canvas = trackingLayerRef.value
185-
const video = videoRef.value
189+
assert(canvas !== undefined, 'onLocate handler should only be called when component is mounted. Thus tracking canvas is always defined.')
186190
187-
console.assert(
188-
canvas !== undefined && video !== undefined,
189-
'onLocate handler called although component is not mounted'
190-
)
191+
const video = videoRef.value
192+
assert(video !== undefined, 'onLocate handler should only be called when component is mounted. Thus video element is always defined.')
191193
192194
if (detectedCodes.length === 0 || props.track === undefined) {
193195
clearCanvas(canvas)

src/misc/camera.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function getCapabilities() : MediaTrackCapabilities {
4040
export async function start(
4141
videoEl: HTMLVideoElement, { constraints, torch }: {
4242
constraints: MediaTrackConstraints
43-
torch: string
43+
torch: boolean
4444
}
4545
) : Promise<MediaTrackCapabilities> {
4646
// At least in Chrome `navigator.mediaDevices` is undefined when the page is

src/misc/scanner.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export const setScanningFormats = (formats: BarcodeFormat[]) => {
88
barcodeDetector = new BarcodeDetector({ formats })
99
}
1010

11+
type ScanHandler = (_ : DetectedBarcode[]) => void
12+
1113
/**
1214
* Continuously extracts frames from camera stream and tries to read
1315
* potentially pictured QR codes.
@@ -17,9 +19,12 @@ export const keepScanning = async (
1719
{
1820
detectHandler,
1921
locateHandler,
20-
minDelay
21-
}: { detectHandler: any; locateHandler: any; minDelay: any }
22+
minDelay,
23+
formats
24+
}: { detectHandler: ScanHandler; locateHandler: ScanHandler; minDelay: number, formats : BarcodeFormat[] }
2225
) => {
26+
barcodeDetector = new BarcodeDetector({ formats })
27+
2328
const processFrame =
2429
(state: {
2530
lastScanned: number

src/misc/util.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,31 @@ export const indempotent = <T>(action: (x: any) => T) => {
1818
}
1919
}
2020
}
21+
22+
/**
23+
* Throws an error if the `condition` in the first argument is `false`.
24+
* This function is useful to make assumptions explicit. For example,
25+
* let's say we have a variable
26+
*
27+
* const value : string | undefined = ...
28+
*
29+
* but from the context we know that it can actually never be `undefined`.
30+
* We can access attributes of `value` with
31+
*
32+
* value?.length
33+
*
34+
* but if the assumption is actually broken, we can a silent error.
35+
* In contrast, with
36+
*
37+
* assert(value !== undefined, 'reason why we assume value always defined')
38+
* value.length // no type error
39+
*
40+
* We make the assumption explicit and force a laud error. Also the type
41+
* check can narrow the type of `value` to `string` after the `assert` and we
42+
* can access properties without type error.
43+
*/
44+
export function assert(condition : boolean, failureMessage? : string): asserts condition {
45+
if (condition === false) {
46+
throw new Error(failureMessage ?? 'assertion failure')
47+
}
48+
}

0 commit comments

Comments
 (0)