@@ -85,28 +85,125 @@ fn QRDisplay(qr_svg: String, user_id: i32) -> Element {
8585
8686/// Fullscreen QR code modal
8787#[ component]
88- pub fn QRModal ( qr_svg : String , user_id : i32 , on_close : EventHandler < ( ) > ) -> Element {
88+ pub fn QRModal (
89+ qr_svg : String ,
90+ user_id : i32 ,
91+ on_close : EventHandler < ( ) > ,
92+ on_scan : Option < EventHandler < String > > ,
93+ ) -> Element {
94+ let show_scanner = on_scan. is_some ( ) ;
95+
8996 rsx ! {
9097 // Backdrop - covers entire screen with semi-transparent grey
9198 div {
9299 class: "fixed inset-0 flex items-center justify-center z-50" ,
93100 style: "background-color: rgba(0, 0, 0, 0.7);" ,
94- onclick: move |_| on_close. call( ( ) ) ,
101+ onclick: move |_| {
102+ // Attempt to stop scanner JS
103+ if show_scanner {
104+ let mut eval = document:: eval( "if (window.stopQrScanner) window.stopQrScanner();" ) ;
105+ // We don't need to wait for it
106+ }
107+ on_close. call( ( ) ) ;
108+ } ,
95109
96- // Modal content - centered QR code
110+ // Modal content - centered QR code or scanner
97111 div {
98112 class: "relative flex flex-col items-center justify-center" ,
99113 onclick: move |e| e. stop_propagation( ) ,
100114
101- // Large QR code display
102- div { class: "w-[95vmin] h-[95vmin] max-w-[500px] max-h-[500px] flex flex-col items-center justify-center gap-4" ,
103- div {
104- class: "w-full h-full bg-background-neutral-primary rounded-2xl" ,
105- dangerous_inner_html: "{qr_svg}" ,
115+ // Display Area
116+ div { class: "w-[95vmin] h-[95vmin] max-w-[500px] max-h-[500px] flex flex-col items-center justify-center gap-4 bg-background-neutral-primary rounded-2xl p-4" ,
117+ if let Some ( handler) = on_scan {
118+ Scanner { on_scan: handler }
119+ } else {
120+ div {
121+ class: "w-full h-full bg-background-neutral-primary rounded-2xl" ,
122+ dangerous_inner_html: "{qr_svg}" ,
123+ }
124+ div { class: "text-white font-semibold text-lg" , "User ID: {user_id}" }
125+ }
126+ }
127+ }
128+ }
129+ }
130+ }
131+
132+ #[ component]
133+ fn Scanner ( on_scan : EventHandler < String > ) -> Element {
134+ // Use eval to initialize the scanner
135+ // We use use_future to run this once on mount
136+ let mut eval = document:: eval (
137+ r#"
138+ const scanHandler = await dioxus.recv();
139+
140+ function onScanSuccess(decodedText, decodedResult) {
141+
142+ // Check if URL matches our expected format
143+ if (decodedText.includes("/scan/")) {
144+ const parts = decodedText.split("/scan/");
145+ if (parts.length === 2) {
146+ const userId = parts[1];
147+
148+ // Stop scanning immediately
149+ if (window.html5QrcodeScanner) {
150+ window.html5QrcodeScanner.clear().then(() => {
151+ // Send back to Rust after clearing
152+ dioxus.send(userId);
153+ }).catch(err => {
154+ console.error("Failed to clear scanner", err);
155+ dioxus.send(userId);
156+ });
157+ } else {
158+ dioxus.send(userId);
106159 }
107- div { class: "text-white font-semibold text-lg" , "User ID: {user_id}" }
108160 }
109161 }
110162 }
163+
164+ function onScanFailure(error) {
165+ // handle scan failure
166+ }
167+
168+ window.stopQrScanner = function() {
169+ if (window.html5QrcodeScanner) {
170+ window.html5QrcodeScanner.clear();
171+ }
172+ };
173+
174+ setTimeout(() => {
175+ if (document.getElementById('reader')) {
176+ window.html5QrcodeScanner = new Html5QrcodeScanner(
177+ "reader",
178+ { fps: 10, qrbox: {width: 250, height: 250} },
179+ false);
180+ window.html5QrcodeScanner.render(onScanSuccess, onScanFailure);
181+ }
182+ }, 100);
183+ "# ,
184+ ) ;
185+
186+ // Handle scan result
187+ use_future ( move || {
188+ let mut eval = eval. clone ( ) ;
189+ let scan_handler = on_scan. clone ( ) ;
190+ async move {
191+ eval. send ( true ) . unwrap ( ) ; // Start the script
192+ if let Ok ( scanned_user_id) = eval. recv :: < String > ( ) . await {
193+ scan_handler. call ( scanned_user_id) ;
194+ }
195+ }
196+ } ) ;
197+
198+ rsx ! {
199+ div { class: "w-full text-center text-lg font-semibold mb-2" , "Scan Participant QR" }
200+ div {
201+ id: "reader" ,
202+ class: "w-full bg-black rounded-xl overflow-hidden shadow-lg border border-gray-800" ,
203+ style: "min-height: 300px;"
204+ }
205+ div { class: "text-sm text-foreground-neutral-secondary text-center mt-2" ,
206+ "Point camera at a check-in QR Code"
207+ }
111208 }
112209}
0 commit comments