1
1
<script lang =" ts" >
2
- import AppNav from " $lib/fragments/AppNav/AppNav.svelte" ;
3
- import { Drawer } from " $lib/ui" ;
4
- import * as Button from " $lib/ui/Button" ;
5
- import {
6
- FlashlightIcon ,
7
- Image02Icon ,
8
- QrCodeIcon ,
9
- } from " @hugeicons/core-free-icons" ;
10
- import { HugeiconsIcon } from " @hugeicons/svelte" ;
11
- import {
12
- Format ,
13
- type PermissionState ,
14
- type Scanned ,
15
- cancel ,
16
- checkPermissions ,
17
- requestPermissions ,
18
- scan ,
19
- } from " @tauri-apps/plugin-barcode-scanner" ;
20
- import { onDestroy , onMount } from " svelte" ;
21
- import type { SVGAttributes } from " svelte/elements" ;
22
-
23
- const pathProps: SVGAttributes <SVGPathElement > = {
24
- stroke: " white" ,
25
- " stroke-width" : 7 ,
26
- " stroke-linecap" : " round" ,
27
- " stroke-linejoin" : " round" ,
28
- };
29
-
30
- let codeScannedDrawerOpen = $state (false );
31
- let loggedInDrawerOpen = $state (false );
32
- let flashlightOn = $state (false );
33
-
34
- let scannedData: Scanned | undefined = $state (undefined );
35
-
36
- let scanning = false ;
37
- let loading = false ;
38
-
39
- let permissions_nullable: PermissionState | null ;
40
-
41
- async function startScan() {
42
- let permissions = await checkPermissions ()
43
- .then ((permissions ) => {
44
- return permissions ;
45
- })
46
- .catch (() => {
47
- return null ; // possibly return "denied"? or does that imply that the check has been successful, but was actively denied?
48
- });
49
-
50
- // TODO: handle receiving "prompt-with-rationale" (issue: https://github.com/tauri-apps/plugins-workspace/issues/979)
51
- if (permissions === " prompt" ) {
52
- permissions = await requestPermissions (); // handle in more detail?
53
- }
54
-
55
- permissions_nullable = permissions ;
56
-
57
- if (permissions === " granted" ) {
58
- // Scanning parameters
59
- const formats = [Format .QRCode ];
60
- const windowed = true ;
61
-
62
- if (scanning ) return ;
63
- scanning = true ;
64
- scan ({ formats , windowed })
65
- .then ((res ) => {
66
- console .log (" Scan result:" , res );
67
- scannedData = res ;
68
- codeScannedDrawerOpen = true ;
69
- })
70
- .catch ((error ) => {
71
- // TODO: display error to user
72
- console .error (" Scan error:" , error );
2
+ import AppNav from " $lib/fragments/AppNav/AppNav.svelte" ;
3
+ import { Drawer } from " $lib/ui" ;
4
+ import * as Button from " $lib/ui/Button" ;
5
+ import {
6
+ FlashlightIcon ,
7
+ Image02Icon ,
8
+ QrCodeIcon ,
9
+ } from " @hugeicons/core-free-icons" ;
10
+ import { HugeiconsIcon } from " @hugeicons/svelte" ;
11
+ import {
12
+ Format ,
13
+ type PermissionState ,
14
+ type Scanned ,
15
+ cancel ,
16
+ checkPermissions ,
17
+ requestPermissions ,
18
+ scan ,
19
+ } from " @tauri-apps/plugin-barcode-scanner" ;
20
+ import { onDestroy , onMount } from " svelte" ;
21
+ import type { SVGAttributes } from " svelte/elements" ;
22
+
23
+ const pathProps: SVGAttributes <SVGPathElement > = {
24
+ stroke: " white" ,
25
+ " stroke-width" : 7 ,
26
+ " stroke-linecap" : " round" ,
27
+ " stroke-linejoin" : " round" ,
28
+ };
29
+
30
+ let codeScannedDrawerOpen = $state (false );
31
+ let loggedInDrawerOpen = $state (false );
32
+ let flashlightOn = $state (false );
33
+
34
+ let scannedData: Scanned | undefined = $state (undefined );
35
+
36
+ let scanning = false ;
37
+ let loading = false ;
38
+
39
+ let permissions_nullable: PermissionState | null ;
40
+
41
+ async function startScan() {
42
+ let permissions = await checkPermissions ()
43
+ .then ((permissions ) => {
44
+ return permissions ;
73
45
})
74
- .finally (() => {
75
- scanning = false ;
46
+ .catch (() => {
47
+ return null ; // possibly return "denied"? or does that imply that the check has been successful, but was actively denied?
76
48
});
77
- }
78
49
79
- console .error (" Permission denied or not granted" );
80
- // TODO: consider handling GUI for permission denied
81
- }
50
+ // TODO: handle receiving "prompt-with-rationale" (issue: https://github.com/tauri-apps/plugins-workspace/issues/979)
51
+ if (permissions === " prompt" ) {
52
+ permissions = await requestPermissions (); // handle in more detail?
53
+ }
54
+
55
+ permissions_nullable = permissions ;
56
+
57
+ if (permissions === " granted" ) {
58
+ // Scanning parameters
59
+ const formats = [Format .QRCode ];
60
+ const windowed = true ;
61
+
62
+ if (scanning ) return ;
63
+ scanning = true ;
64
+ scan ({ formats , windowed })
65
+ .then ((res ) => {
66
+ console .log (" Scan result:" , res );
67
+ scannedData = res ;
68
+ codeScannedDrawerOpen = true ;
69
+ })
70
+ .catch ((error ) => {
71
+ // TODO: display error to user
72
+ console .error (" Scan error:" , error );
73
+ })
74
+ .finally (() => {
75
+ scanning = false ;
76
+ });
77
+ }
78
+
79
+ console .error (" Permission denied or not granted" );
80
+ // TODO: consider handling GUI for permission denied
81
+ }
82
82
83
- async function cancelScan() {
84
- await cancel ();
85
- scanning = false ;
86
- }
83
+ async function cancelScan() {
84
+ await cancel ();
85
+ scanning = false ;
86
+ }
87
87
88
- onMount (async () => {
89
- startScan ();
90
- });
88
+ onMount (async () => {
89
+ startScan ();
90
+ });
91
91
92
- onDestroy (async () => {
93
- await cancelScan ();
94
- });
92
+ onDestroy (async () => {
93
+ await cancelScan ();
94
+ });
95
95
</script >
96
96
97
97
<AppNav title =" Scan QR Code" titleClasses =" text-white" iconColor =" white" />
98
98
99
- <svg class =" mx-auto mt-48" width =" 204" height =" 215" viewBox =" 0 0 204 215" fill =" none" xmlns =" http://www.w3.org/2000/svg" >
100
- <path d ="M46 4H15C8.92487 4 4 8.92487 4 15V46" {...pathProps }/>
101
- <path d ="M158 4H189C195.075 4 200 8.92487 200 15V46" {...pathProps }/>
102
- <path d ="M46 211H15C8.92487 211 4 206.075 4 200V169" {...pathProps }/>
103
- <path d ="M158 211H189C195.075 211 200 206.075 200 200V169" {...pathProps }/>
99
+ <svg
100
+ class =" mx-auto mt-48"
101
+ width =" 204"
102
+ height =" 215"
103
+ viewBox =" 0 0 204 215"
104
+ fill =" none"
105
+ xmlns =" http://www.w3.org/2000/svg"
106
+ >
107
+ <path d ="M46 4H15C8.92487 4 4 8.92487 4 15V46" {...pathProps } />
108
+ <path d ="M158 4H189C195.075 4 200 8.92487 200 15V46" {...pathProps } />
109
+ <path d ="M46 211H15C8.92487 211 4 206.075 4 200V169" {...pathProps } />
110
+ <path d ="M158 211H189C195.075 211 200 206.075 200 200V169" {...pathProps } />
104
111
</svg >
105
112
106
- <h4 class =" text-white font-semibold text-center mt-20" >Point the camera at the code</h4 >
113
+ <h4 class =" text-white font-semibold text-center mt-20" >
114
+ Point the camera at the code
115
+ </h4 >
107
116
108
- <div class =" fixed bottom-2 left-1/2 -translate-x-1/2 z-10 flex gap-8 justify-center items-center" >
109
- <Button .Icon
110
- icon ={Image02Icon }
111
- bgColor =" white"
112
- bgSize =" md"
113
- />
117
+ <div
118
+ class =" fixed bottom-2 left-1/2 -translate-x-1/2 z-10 flex gap-8 justify-center items-center"
119
+ >
120
+ <Button .Icon icon ={Image02Icon } bgColor =" white" bgSize =" md" />
114
121
<Button .Icon
115
122
icon ={QrCodeIcon }
116
123
bgColor =" white"
117
124
bgSize =" lg"
118
125
iconSize =" lg"
119
- callback ={() => { codeScannedDrawerOpen = true ; }}
126
+ callback ={() => {
127
+ codeScannedDrawerOpen = true ;
128
+ }}
120
129
/>
121
130
<Button .Icon
122
- icon ={FlashlightIcon }
131
+ icon ={FlashlightIcon }
123
132
aria-label =" Toggle flashlight"
124
133
bgSize =" md"
125
134
iconSize ={32 }
@@ -135,9 +144,15 @@ onDestroy(async () => {
135
144
bind:isPaneOpen ={codeScannedDrawerOpen }
136
145
class =" flex flex-col gap-4 items-center justify-center"
137
146
>
138
- <div class =" flex justify-center mb-4 relative items-center overflow-hidden bg-gray rounded-xl p-4 h-[72px] w-[72px]" >
139
- <div class =" bg-white h-[16px] w-[200px] -rotate-45 absolute top-1" ></div >
140
- <div class =" bg-white h-[16px] w-[200px] -rotate-45 absolute bottom-1" ></div >
147
+ <div
148
+ class =" flex justify-center mb-4 relative items-center overflow-hidden bg-gray rounded-xl p-4 h-[72px] w-[72px]"
149
+ >
150
+ <div
151
+ class =" bg-white h-[16px] w-[200px] -rotate-45 absolute top-1"
152
+ ></div >
153
+ <div
154
+ class =" bg-white h-[16px] w-[200px] -rotate-45 absolute bottom-1"
155
+ ></div >
141
156
<HugeiconsIcon
142
157
size ={40 }
143
158
className =" z-10"
@@ -152,22 +167,27 @@ onDestroy(async () => {
152
167
153
168
<div class =" bg-gray rounded-2xl w-full p-4 mt-4" >
154
169
<h4 class =" text-base text-black-700" >Website URL</h4 >
155
- <p class ="text-black-700 font-normal underline" >{scannedData ?.content }</p >
170
+ <p class =" text-black-700 font-normal underline" >
171
+ {scannedData ?.content }
172
+ </p >
156
173
</div >
157
174
<div class =" flex justify-center gap-3 items-center mt-4" >
158
175
<Button .Action
159
176
variant =" danger-soft"
160
177
class =" w-full"
161
- callback ={() => { codeScannedDrawerOpen = false ; startScan (); }}
178
+ callback ={() => {
179
+ codeScannedDrawerOpen = false ;
180
+ startScan ();
181
+ }}
162
182
>
163
183
Decline
164
184
</Button .Action >
165
185
<Button .Action
166
186
variant =" solid"
167
187
class =" w-full"
168
188
callback ={() => {
169
- codeScannedDrawerOpen = false
170
- loggedInDrawerOpen = true
189
+ codeScannedDrawerOpen = false ;
190
+ loggedInDrawerOpen = true ;
171
191
startScan ();
172
192
}}
173
193
>
@@ -182,9 +202,15 @@ onDestroy(async () => {
182
202
bind:isPaneOpen ={loggedInDrawerOpen }
183
203
class =" flex flex-col gap-4 items-center justify-center"
184
204
>
185
- <div class =" flex justify-center mb-4 relative items-center overflow-hidden bg-gray rounded-xl p-4 h-[72px] w-[72px]" >
186
- <div class =" bg-white h-[16px] w-[200px] -rotate-45 absolute top-1" ></div >
187
- <div class =" bg-white h-[16px] w-[200px] -rotate-45 absolute bottom-1" ></div >
205
+ <div
206
+ class =" flex justify-center mb-4 relative items-center overflow-hidden bg-gray rounded-xl p-4 h-[72px] w-[72px]"
207
+ >
208
+ <div
209
+ class =" bg-white h-[16px] w-[200px] -rotate-45 absolute top-1"
210
+ ></div >
211
+ <div
212
+ class =" bg-white h-[16px] w-[200px] -rotate-45 absolute bottom-1"
213
+ ></div >
188
214
<HugeiconsIcon
189
215
size ={40 }
190
216
className =" z-10"
@@ -201,10 +227,19 @@ onDestroy(async () => {
201
227
<Button .Action
202
228
variant =" solid"
203
229
class =" w-full"
204
- callback ={() => { loggedInDrawerOpen = false ; startScan (); }}
230
+ callback ={() => {
231
+ loggedInDrawerOpen = false ;
232
+ startScan ();
233
+ }}
205
234
>
206
235
Close
207
236
</Button .Action >
208
237
</div >
209
238
</Drawer >
210
239
240
+ <style >
241
+ :global(body , * :not (button )) {
242
+ background-color : #00000000 ;
243
+ overflow-y : hidden ;
244
+ }
245
+ </style >
0 commit comments