I am done for the night/morning...

This commit is contained in:
2024-04-10 20:31:32 -04:00
parent 5411df5893
commit 920dd176fe
8 changed files with 2092 additions and 40 deletions

View File

@@ -69,6 +69,8 @@ export let ae_app_local_data_struct: key_val = {
'qry__limit': 20,
'qry__offset': 0,
qr_scanner_version: 'one',
'ds': {},
'hub': {
'show_element__cfg': true,

View File

@@ -0,0 +1,683 @@
<script lang="ts">
// *** Import Svelte core
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
// import 'html5-qrcode';
import {Html5Qrcode, Html5QrcodeScannerState, Html5QrcodeSupportedFormats} from 'html5-qrcode';
// *** Import Aether core variables and functions
import { api } from '$lib/api';
import { ae_api } from '$lib/ae_stores';
// *** Import Aether core components
// import Element_input from './element_input.svelte';
// import ae from '/element_input.svelte';
// import Input_element from '/element_input.svelte';
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Export/Exposed variables and functions for component
export let start_qr_scanner: boolean = true;
export let show_pause_btn: boolean = false; // pause and resume buttons
export let show_qr_manual_text_entry_option: boolean = false;
export let show_qr_manual_badge_id_entry_option: boolean = false;
export let show_qr_scan_result: boolean = true;
export let qr_fps = 10;
export let qr_viewfinder_width = 275; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
export let qr_facing_mode = 'environment'; // environment, user, { exact: 'environment'}, { exact: 'user'}
// *** Set initial variables
let scanning_status: string = 'not_started';
let qr_scan_result: null|string = null;
let qr_found_text: null|string = null;
let qr_entered_text: null|string = null;
let qr_entered_badge_id: null|string = null;
let show_qr_manual_entry: null|boolean = null;
let disable_submit_badge_id_btn: boolean = true;
let user_media_status = 'not_requested';
let debug_comment: string = 'Debugging QR Scanner';
let debug_info: any;
// let max_results: number = 50;
const dispatch = createEventDispatcher();
let html5_qr_code: any|null|string = null;
// let html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// let qr_scan_cfg = { fps: 10, qrbox: 400 }; // default was 250 and using 300 when 600px
// let qr_scan_cfg = { fps: qr_fps, qrbox: qr_viewfinder_width }; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
let qr_scan_cfg = {};
// let mounted = false;
onMount(() => {
console.log('** Element Mounted: ** QR Scanner');
console.log('** Element Mounted: ** QR Scanner - getUserMedia');
var constraints = {
video: false,
audio: false
}
navigator.mediaDevices.getUserMedia(constraints)
.then(get_user_media_success, get_user_media_error);
// navigator.mediaDevices.getUserMedia(constraints)
// .then(get_user_media_success, get_user_media_error)
// .catch(function(err) {
// //log to console first
// console.log(err); /* handle the error */
// if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") {
// //required track is missing
// } else if (err.name == "NotReadableError" || err.name == "TrackStartError") {
// //webcam or mic are already in use
// } else if (err.name == "OverconstrainedError" || err.name == "ConstraintNotSatisfiedError") {
// //constraints can not be satisfied by avb. devices
// } else if (err.name == "NotAllowedError" || err.name == "PermissionDeniedError") {
// //permission denied in browser
// } else if (err.name == "TypeError" || err.name == "TypeError") {
// //empty constraints object
// } else {
// //other errors
// }
// });
// navigator.mediaDevices.getUserMedia({video: true})
// .then(get_user_media_success, get_user_media_error);
// console.log('** Element Mounted: ** QR Scanner - setTimeout 750ms then getUserMedia');
// Wait 250ms for everything to fully load
// setTimeout(() => {
// console.log('** Element Mounted: ** QR Scanner - inside setTimeout 750ms');
// navigator.mediaDevices.getUserMedia({video: true})
// .then(get_user_media_success, get_user_media_error);
// }, 750);
// console.log('** Element Mounted: ** QR Scanner - setTimeout end 750ms');
// NOTE: This can only be done after the page has fully loaded.
// if (start_qr_scanner) {
// handle_start_qr_scanning();
// }
});
onDestroy(async () => {
console.log('** Element Destroyed: ** QR Scanner');
qr_scan_result = null;
qr_found_text = null;
await handle_stop_qr_scanning();
});
var get_user_media_success = function(error: any) {
console.log('Camera access allowed');
user_media_status = 'allowed';
debug_comment = 'Camera Access Allowed';
debug_info = JSON.stringify(error);
// if (html5_qr_code) {
// console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
// debug_info = 'html5_qr_code object found. Clearing and creating new Html5Qrcode...';
// html5_qr_code.clear();
// // document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
// } else {
// console.log('html5_qr_code not found. Creating new Html5Qrcode...');
// debug_info = 'html5_qr_code not found. Creating new Html5Qrcode...';
// }
html5_qr_code = new Html5Qrcode(
'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
);
debug_comment = 'Html5Qrcode created';
debug_info = 'new Html5Qrcode for element id=qr_scanner_viewfinder QR_CODE';
if (start_qr_scanner) {
// console.log('** Element Mounted: ** QR Scanner - getUserMedia in setTimeout');
setTimeout(() => {
console.log('** Element Mounted: ** QR Scanner - setTimeout');
console.log('Ready to start QR scanning!');
debug_info = 'Ready to start QR scanning!';
handle_start_qr_scanning();
}, 2500);
// console.log('** Element Mounted: ** QR Scanner - setTimeout end');
}
// let subject = 'Camera Access Allowed';
// let message = error;
// send_init_confirm_email(subject, message);
console.log('Dispatching qr_camera');
debug_info = 'Dispatching qr_camera';
dispatch('qr_camera', {
status: 'allowed',
});
// NOTE: This can only be done after the page has fully loaded.
// if (start_qr_scanner) {
// handle_start_qr_scanning();
// }
};
var get_user_media_error = function(error: any) {
if (error.name == 'NotAllowedError') {
console.log('Camera access not allowed!');
user_media_status = 'denied';
debug_comment = 'Camera Access Denied';
debug_info = JSON.stringify(error);
// alert('Error trying to start camera');
// alert(error);
let subject = 'Camera Access Denied';
let message = error;
send_init_confirm_email(subject, message);
dispatch('qr_camera', {
status: 'denied',
});
}
};
// $: if (start_qr_scanner && user_media_status == 'allowed' && (scanning_status == 'not_started' || scanning_status == 'paused')) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else {
// // console.log('STOP QR SCANNING');
// // handle_stop_qr_scanning();
// }
// $: if (mounted && start_qr_scanner) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else if (mounted && !start_qr_scanner) {
// console.log('STOP QR SCANNING');
// handle_stop_qr_scanning();
// }
async function handle_start_qr_scanning() {
console.log('*** handle_start_qr_scanning() ***');
if (user_media_status == 'denied') {
console.log('Camera access not allowed!');
return;
} else if (user_media_status == 'not_requested') {
console.log('Camera access not requested yet!');
return;
} else if (user_media_status == 'allowed') {
console.log('Camera access allowed!');
debug_info = 'Camera access allowed!';
} else {
console.log('Camera access status unknown! This should not happen?');
debug_info = 'Camera access status unknown! This should not happen?';
return;
}
qr_scan_result = null;
qr_found_text = null;
debug_comment = 'Starting QR Scanning...';
// if (html5_qr_code) {
// console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
// debug_info = 'html5_qr_code object found. Clearing and creating new Html5Qrcode...';
// html5_qr_code.clear();
// // document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// } else {
// console.log('html5_qr_code not found. Creating new Html5Qrcode...');
// debug_info = 'html5_qr_code not found. Creating new Html5Qrcode...';
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// }
debug_info = 'Check the state and start if not started...';
let current_state = await html5_qr_code.getState()
if (current_state == Html5QrcodeScannerState.NOT_STARTED) {
// console.log('Scanner is not started');
debug_info = 'Waiting... Scanner is not started';
setTimeout(() => {
debug_info = 'Waited long enough! Starting for real!';
html5_qr_code.start({ facingMode: qr_facing_mode }, qr_scan_cfg, handle_qr_scan_success, handle_qr_scan_error)
.then((ignore: any) => {
console.log('Scanning has started');
scanning_status = 'scanning';
debug_info = 'Scanning has started';
// let subject = 'QR Scanning Started';
// let message = ignore;
// send_init_confirm_email(subject, message);
return true;
}).catch((err) => {
console.log('There was an error while trying to start the QR scanner');
scanning_status = 'start_error';
debug_comment = 'Error starting scanner after getState() NOT_STARTED';
debug_info = 'Error starting scanner: ' + JSON.stringify(err);
// let subject = 'QR Scanning Start Error';
// let message = err;
// send_init_confirm_email(subject, message);
// Error getting userMedia, error = NotReadableError: Could not start video source
return false;
});
}, 750);
debug_info = 'Waiting...?';
} else {
console.log('Scanner is already started');
debug_info = 'Scanner is already started';
return;
}
}
function handle_pause_qr_scanning() {
if (html5_qr_code && html5_qr_code.getState() != Html5QrcodeScannerState.SCANNING) {
console.log('Scanner is not scanning!');
return;
}
html5_qr_code.pause();
scanning_status = 'paused';
}
function handle_resume_qr_scanning() {
if (html5_qr_code && html5_qr_code.getState() != Html5QrcodeScannerState.PAUSED) {
console.log('Scanner is not paused!');
return;
}
document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
html5_qr_code.resume();
scanning_status = 'scanning';
}
async function handle_stop_qr_scanning() {
start_qr_scanner = false;
if (!html5_qr_code) {
console.log('html5_qr_code object found. Nothing to stop?');
return false;
}
let state = html5_qr_code.getState();
console.log('html5_qr_code state:', state);
if (state == Html5QrcodeScannerState.NOT_STARTED) {
console.log('Scanner is not started');
return;
}
if (state == Html5QrcodeScannerState.PAUSED || state == Html5QrcodeScannerState.SCANNING) {
console.log('Scanner is not started');
await html5_qr_code.stop()
scanning_status = 'not_started';
return;
}
await html5_qr_code.clear();
return true;
// html5_qr_code.pause();
// return html5_qr_code.stop()
// .then((ignore) => {
// console.log('Scanning has stopped');
// // document.getElementById('qr_scanner_viewfinder').classList.add('d_none');
// scanning_status = 'not_started';
// }).then((ignore) => {
// // html5_qr_code = null;
// // html5_qr_code.clear();
// }).catch((err) => {
// console.log('There was an error while trying to stop the scanning');
// return false;
// });
// html5_qr_code = null;
}
// Callback function for QrcodeSuccessCallback (decodedText: string, result: Html5QrcodeResult)
function handle_qr_scan_success(decoded_text, decoded_result) {
console.log('*** handle_qr_scan_success() ***');
console.log(`QR scanned = ${decoded_text}`, decoded_result);
qr_scan_result = decoded_text; // NOTE: decoded_result is not currently used by html5-qrcode
qr_found_text = decoded_text;
dispatch('qr_scan_result', {
result: qr_scan_result, // This text will need to be parsed to get more info.
text: qr_found_text, // This text will need to be parsed to get more info.
entry_method: 'QR',
});
// handle_pause_qr_scanning();
handle_stop_qr_scanning();
}
// Callback function for QrcodeErrorCallback (errorMessage: string, error: Html5QrcodeError)
// NOTE: Most of the time this is normal and not an actual error. It just did not find something to scan.
function handle_qr_scan_error(qr_error_message, qr_code_error) {
// console.log('*** handle_qr_scan_error() ***');
if (qr_code_error.type) {
console.log(`Error scanning code = ${qr_error_message}`, qr_code_error);
return;
}
}
$: if ( qr_entered_badge_id && qr_entered_badge_id.length >= 11 && qr_entered_badge_id && qr_entered_badge_id.length <= 14) {
disable_submit_badge_id_btn = false;
} else {
disable_submit_badge_id_btn = true;
}
function handle_qr_manual_entry() {
console.log('*** handle_qr_manual_entry() ***');
if (qr_entered_text) {
console.log(`QR entered text = ${qr_entered_text}`);
} else if (qr_entered_badge_id) {
console.log(`QR entered badge ID = ${qr_entered_badge_id}`);
qr_entered_text = `OBJ:ot:event_badge,oi:${qr_entered_badge_id}`;
console.log(`Parse to proper QR badge ID = ${qr_entered_text}`);
}
// html5_qr_code.stop().then((ignore) => {
// console.log('Scanning has stopped');
// document.getElementById('qr_scanner_viewfinder').classList.add('d_none');
// }).catch((err) => {
// console.log('There was an error while trying to stop the scanning');
// });
qr_scan_result = qr_entered_text;
dispatch('qr_scan_result', {
result: qr_scan_result,
entry_method: 'manual',
});
qr_scan_result = null;
qr_entered_text = null;
}
function send_init_confirm_email(subject, message) {
console.log(`*** send_init_confirm_email() *** ${subject}`);
let to_email = 'scott.idem+skdev@oneskyit.com';
// let origin_url = encodeURI(`${data.url.origin}`);
let full_subject = `${subject} Aether QR Scanner Debugging`;
let body_html = `
<div>Scott,
<p>This is an automatic debug email from the Aether QR scanner.</p>
</div>
<br>
<div>
Message:<br>
<pre>
${JSON.stringify(message)}
</pre>
</div>
`;
api.send_email({
api_cfg: $ae_api,
from_email: 'noreply+ae_qr_debug@oneskyit.com',
from_name: 'AE QR Debug',
to_email: to_email,
subject: full_subject,
body_html: body_html,
});
}
</script>
<section
class="ae_element qr_scanner border-2 border-slate-500/10 space-y-2 flex flex-col gap-1 justify-center items-center min-w-full max-w-full"
class:not_started={scanning_status == 'not_started'}
class:paused={scanning_status == 'paused'}
class:scanning={scanning_status == 'scanning'}
>
<!-- <header>
<h2>QR Scanner</h2>
</header> -->
<!-- <fieldset class=""> -->
<!-- <legend class="d_none">QR Scanner:</legend> -->
<div
class="ae_container qr_scanning_container"
>
<div
class="ae_options m-1"
>
{#if scanning_status == 'not_started' }
<button on:click={handle_start_qr_scanning} class="btn btn-lg variant-soft-primary btn_start"><span class="fas fa-qrcode mx-1"></span> Start Scanning</button>
<span class="loading-text">
Scanning stopped
</span>
{:else if scanning_status == 'paused' && show_pause_btn}
<button on:click={handle_resume_qr_scanning} class="btn btn-md variant-soft-primary btn_resume"><span class="fas fa-play"></span> Resume</button>
<span>Scanning paused</span>
{:else if scanning_status == 'scanning'}
<button on:click={handle_stop_qr_scanning} class="btn btn-md variant-soft-secondary btn_stop">
<span class="fas fa-crosshairs fa-spin opacity-50 m-1"></span>
<!-- <span class="fas fa-stop-circle m-1"></span> -->
Stop
</button>
{#if show_pause_btn}
<button on:click={handle_pause_qr_scanning} class="btn btn-lg variant-soft-secondary btn_pause"><span class="fas fa-pause-circle"></span> Pause</button>
{/if}
<!-- <span>Scanning for QR code...</span> -->
<!-- <div class="modal-loading"> -->
<!-- <span class="fas fa-crosshairs fa-spin opacity-50"></span> -->
<span class="loading-text">
Scanning for QR code...
</span>
<!-- </div> -->
{/if}
</div>
<div id="qr_scanner_viewfinder" class="qr_scanner_viewfinder grow flex flex-col justify-center items-center" style=""></div> <!-- width: 600px -->
</div>
{#if show_qr_manual_text_entry_option}
<div class="ae_container qr_manual_entry text_entry">
{#if show_qr_manual_entry}
<label for="entered_text" class="">Enter text</label>
<input type="text" name="entered_text" id="entered_text" bind:value="{qr_entered_text}">
<button on:click={handle_qr_manual_entry} class="btn btn-md variant-soft-warning"><span class="fas fa-paper-plane"></span> Submit Text</button>
<div class="search_by_text">
<input type='text' placeholder="Name or Email" label="Name or Email" value={search_query_str} focus={true} on:oninput={handle_oninput_search_query_str} />
</div>
{:else}
<button on:click={() => show_qr_manual_entry=true} class="btn btn-md variant-soft-warning"><span class="fas fa-keyboard"></span> Enter Text</button>
{/if}
</div>
{/if}
{#if show_qr_manual_badge_id_entry_option}
<div class="ae_container qr_manual_entry badge_id_entry">
{#if show_qr_manual_entry}
<form on:submit|preventDefault={() => handle_qr_manual_entry} class="flex">
<!-- <label for="entered_badge_id" class="">Enter badge ID</label>
<input type="text" name="entered_badge_id" id="entered_badge_id" bind:value="{qr_entered_badge_id}"> -->
<input
bind:value="{qr_entered_badge_id}"
type="text"
name="entered_badge_id"
id="entered_badge_id"
required
placeholder="Enter Badge ID"
class="input max-w-52"
/>
<button
type="submit"
on:click={handle_qr_manual_entry}
disabled={disable_submit_badge_id_btn}
class="btn btn-md variant-ghost-primary m-1"
class:btn_default={disable_submit_badge_id_btn}
class:btn_primary={!disable_submit_badge_id_btn}
>
<span class="fas fa-paper-plane mx-1"></span> Submit Badge ID
</button>
</form>
{:else}
<button on:click={() => show_qr_manual_entry=true} class="btn btn-md variant-soft-secondary m-1"><span class="fas fa-keyboard mx-1"></span> Enter Badge ID</button>
{/if}
</div>
{/if}
{#if show_qr_scan_result && qr_scan_result}
<div class="ae_container qr_scan_result">
<span class="label">Raw Result:</span>
<span id="qr_scan_result_value" class="value">{qr_scan_result}</span>
</div>
{/if}
{debug_comment ?? 'Debugging QR Scanner'}
{#if debug_info}
<div class="ae_container debug_info">
<span class="label">Debug Info:</span>
<span class="value">{debug_info}</span>
</div>
{/if}
<!-- </fieldset> -->
</section>
<style>
.not_started {
background-color: hsla(0, 100%, 75%, 0.3);
border-color: hsla(0, 100%, 75%, 0.6);
}
.paused {
background-color: hsla(60, 100%, 75%, 0.3);
border-color: hsla(60, 100%, 75%, 0.6);
}
.scanning {
background-color: hsla(120, 100%, 75%, 0.3);
border-color: hsla(120, 100%, 75%, 0.6);
}
.qr_scanner {
/* outline: solid thin pink; */
max-width: 100vw;
/* overflow-x: scroll; */
display: flex;
flex-direction: column;
/* flex-wrap: wrap; */
justify-content: flex-start;
align-items: center; /* center */
align-content: stretch;
}
.ae_element.qr_scanner div.qr_scanner_viewfinder {
/* max-width: 100vw; */
/* contain: content; */
/* contain: contain; */
}
.qr_scanner .qr_scanner_viewfinder {
/* outline: dashed medium blue; */
min-width: 400px;
width: 100%;
/* max-width: 100%; */
max-width: 500px;
/* max-width: 100vw; */
/* outline: solid thin red; */
contain: contain;
overflow-x: scroll;
}
@media (max-width: 767px) {
.qr_scanner .qr_scanner_viewfinder {
/* outline: dashed medium red; */
min-width: 80vw;
/* width: 100%; */
/* max-width: 100%; */
/* max-width: 450px; */
max-width: 100vw;
margin: 0;
padding: 0;
}
}
</style>

View File

@@ -8,6 +8,7 @@ import {Html5Qrcode, Html5QrcodeScannerState, Html5QrcodeSupportedFormats} from
import { api } from '$lib/api';
import { ae_api } from '$lib/ae_stores';
// *** Import Aether core components
// import Element_input from './element_input.svelte';
// import ae from '/element_input.svelte';
@@ -39,6 +40,9 @@ let disable_submit_badge_id_btn: boolean = true;
let user_media_status = 'not_requested';
let debug_comment: string = 'Debugging QR Scanner';
let debug_info: any;
// let max_results: number = 50;
@@ -58,14 +62,24 @@ let qr_scan_cfg = { fps: qr_fps, qrbox: qr_viewfinder_width }; // 275 seems good
onMount(() => {
console.log('** Element Mounted: ** QR Scanner');
// NOTE: We only want to trigger the scanning to start after the page has fully loaded.
navigator.mediaDevices.getUserMedia({video: true})
.then(successCallback, errorCallback);
// Wait 250ms for everything to fully load
// setTimeout(() => {
// console.log('** Element Mounted: ** QR Scanner - setTimeout');
// NOTE: We only want to trigger the scanning to start after the page has fully loaded.
navigator.mediaDevices.getUserMedia({video: true})
.then(successCallback, errorCallback);
console.log('** Element Mounted: ** QR Scanner - getUserMedia in setTimeout');
// mounted = true;
// }, 750);
// console.log('** Element Mounted: ** QR Scanner - setTimeout end');
// NOTE: This can only be done after the page has fully loaded.
// if (start_qr_scanner) {
// handle_start_qr_scanning();
// }
});
@@ -83,25 +97,58 @@ var successCallback = function(error: any) {
console.log('Camera access allowed');
user_media_status = 'allowed';
debug_comment = 'Camera Access Allowed';
debug_info = JSON.stringify(error);
if (html5_qr_code) {
console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
debug_info = 'html5_qr_code object found. Clearing and creating new Html5Qrcode...';
html5_qr_code.clear();
// document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
} else {
console.log('html5_qr_code not found. Creating new Html5Qrcode...');
debug_info = 'html5_qr_code not found. Creating new Html5Qrcode...';
}
html5_qr_code = new Html5Qrcode(
'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
);
debug_info = 'new Html5Qrcode for element id=qr_scanner_viewfinder';
if (start_qr_scanner) {
console.log('Ready to start QR scanning!');
debug_info = 'Ready to start QR scanning!';
handle_start_qr_scanning();
}
// let subject = 'Camera Access Allowed';
// let message = error;
// send_init_confirm_email(subject, message);
console.log('Dispatching qr_camera');
debug_info = 'Dispatching qr_camera';
dispatch('qr_camera', {
status: 'allowed',
});
// NOTE: This can only be done after the page has fully loaded.
if (start_qr_scanner) {
handle_start_qr_scanning();
}
// if (start_qr_scanner) {
// handle_start_qr_scanning();
// }
};
var errorCallback = function(error: any) {
if (error.name == 'NotAllowedError') {
console.log('Camera access not allowed!');
user_media_status = 'denied';
alert('Error trying to start camera');
alert(error);
debug_comment = 'Camera Access Denied';
debug_info = JSON.stringify(error);
// alert('Error trying to start camera');
// alert(error);
let subject = 'Camera Access Denied';
let message = error;
@@ -113,6 +160,14 @@ var errorCallback = function(error: any) {
}
};
// $: if (start_qr_scanner && user_media_status == 'allowed' && (scanning_status == 'not_started' || scanning_status == 'paused')) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else {
// // console.log('STOP QR SCANNING');
// // handle_stop_qr_scanning();
// }
// $: if (mounted && start_qr_scanner) {
// console.log('START QR SCANNING');
@@ -132,27 +187,44 @@ async function handle_start_qr_scanning() {
} else if (user_media_status == 'not_requested') {
console.log('Camera access not requested yet!');
return;
} else if (user_media_status == 'allowed') {
console.log('Camera access allowed!');
debug_info = 'Camera access allowed!';
} else {
console.log('Camera access status unknown! This should not happen?');
debug_info = 'Camera access status unknown! This should not happen?';
return;
}
qr_scan_result = null;
qr_found_text = null;
if (html5_qr_code) {
console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
// html5_qr_code.clear();
// document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
html5_qr_code = new Html5Qrcode(
'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
);
} else {
console.log('html5_qr_code not found. Creating new Html5Qrcode...');
html5_qr_code = new Html5Qrcode(
'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
);
}
debug_comment = 'Starting QR Scanning...';
if (html5_qr_code.getState() == Html5QrcodeScannerState.NOT_STARTED) {
// if (html5_qr_code) {
// console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
// debug_info = 'html5_qr_code object found. Clearing and creating new Html5Qrcode...';
// html5_qr_code.clear();
// // document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// } else {
// console.log('html5_qr_code not found. Creating new Html5Qrcode...');
// debug_info = 'html5_qr_code not found. Creating new Html5Qrcode...';
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// }
debug_info = 'Check the state and start if not started...';
let current_state = await html5_qr_code.getState()
if (current_state == Html5QrcodeScannerState.NOT_STARTED) {
// console.log('Scanner is not started');
return await html5_qr_code.start({ facingMode: qr_facing_mode }, qr_scan_cfg, handle_qr_scan_success, handle_qr_scan_error)
@@ -160,6 +232,8 @@ async function handle_start_qr_scanning() {
console.log('Scanning has started');
scanning_status = 'scanning';
debug_info = 'Scanning has started';
// let subject = 'QR Scanning Started';
// let message = ignore;
// send_init_confirm_email(subject, message);
@@ -169,6 +243,9 @@ async function handle_start_qr_scanning() {
console.log('There was an error while trying to start the QR scanner');
scanning_status = 'start_error';
debug_info = 'Error starting scanner: ' + JSON.stringify(err);
// let subject = 'QR Scanning Start Error';
// let message = err;
// send_init_confirm_email(subject, message);
@@ -181,6 +258,7 @@ async function handle_start_qr_scanning() {
} else {
console.log('Scanner is already started');
debug_info = 'Scanner is already started';
return;
}
@@ -383,10 +461,10 @@ function send_init_confirm_email(subject, message) {
class="ae_container qr_scanning_container"
>
<div
class="ae_options m-1"
class="ae_options flex flex-row justify-center items-center gap-1 m-1"
>
{#if scanning_status == 'not_started' }
<button on:click={handle_start_qr_scanning} class="btn btn-lg variant-soft-primary btn_start"><span class="fas fa-qrcode mx-1"></span> Start Scanning X</button>
<button on:click={handle_start_qr_scanning} class="btn btn-lg variant-soft-primary btn_start"><span class="fas fa-qrcode mx-1"></span> Start Scanning</button>
<span class="loading-text">
Scanning stopped
</span>
@@ -464,7 +542,15 @@ function send_init_confirm_email(subject, message) {
</form>
{:else}
<button on:click={() => show_qr_manual_entry=true} class="btn btn-md variant-soft-secondary m-1"><span class="fas fa-keyboard mx-1"></span> Enter Badge ID</button>
<button on:click={() =>
{
handle_stop_qr_scanning();
show_qr_manual_entry=true;
}}
class="btn btn-md variant-soft-secondary m-1"
>
<span class="fas fa-keyboard mx-1"></span> Enter Badge ID
</button>
{/if}
</div>
{/if}
@@ -475,6 +561,14 @@ function send_init_confirm_email(subject, message) {
<span id="qr_scan_result_value" class="value">{qr_scan_result}</span>
</div>
{/if}
<!-- {debug_comment ?? 'Debugging QR Scanner'}
{#if debug_info}
<div class="ae_container debug_info">
<span class="label">Debug Info:</span>
<span class="value">{debug_info}</span>
</div>
{/if} -->
<!-- </fieldset> -->
</section>

View File

@@ -0,0 +1,670 @@
<script lang="ts">
// *** Import Svelte core
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
// import 'html5-qrcode';
import {Html5Qrcode, Html5QrcodeScannerState, Html5QrcodeSupportedFormats} from 'html5-qrcode';
// *** Import Aether core variables and functions
import { api } from '$lib/api';
import { ae_api } from '$lib/ae_stores';
// *** Import Aether core components
// import Element_input from './element_input.svelte';
// import ae from '/element_input.svelte';
// import Input_element from '/element_input.svelte';
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Export/Exposed variables and functions for component
export let start_qr_scanner: boolean = true;
export let show_pause_btn: boolean = false; // pause and resume buttons
export let show_qr_manual_text_entry_option: boolean = false;
export let show_qr_manual_badge_id_entry_option: boolean = false;
export let show_qr_scan_result: boolean = true;
export let qr_fps = 10;
export let qr_viewfinder_width = 275; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
export let qr_facing_mode = 'environment'; // environment, user, { exact: 'environment'}, { exact: 'user'}
// *** Set initial variables
let scanning_status: string = 'not_started';
let qr_scan_result: null|string = null;
let qr_found_text: null|string = null;
let qr_entered_text: null|string = null;
let qr_entered_badge_id: null|string = null;
let show_qr_manual_entry: null|boolean = null;
let disable_submit_badge_id_btn: boolean = true;
let user_media_status = 'not_requested';
let debug_comment: string = 'Debugging QR Scanner';
let debug_info: any;
let html5_qr_code: any|null|string = null;
// let qr_scan_cfg = { fps: 10, qrbox: 400 }; // default was 250 and using 300 when 600px
let qr_scan_cfg = { fps: qr_fps, qrbox: qr_viewfinder_width }; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
// const html5QrCode = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
onMount(() => {
console.log('** Element Mounted: ** QR Scanner');
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// navigator.mediaDevices.getUserMedia({video: true})
// .then(get_user_media_success, get_user_media_error);
// console.log('** Element Mounted: ** QR Scanner - getUserMedia in setTimeout');
});
onDestroy(async () => {
console.log('** Element Destroyed: ** QR Scanner');
qr_scan_result = null;
qr_found_text = null;
await handle_stop_qr_scanning();
});
var get_user_media_success = function(error: any) {
console.log('Camera access allowed');
user_media_status = 'allowed';
debug_comment = 'Camera Access Allowed';
debug_info = JSON.stringify(error);
if (html5_qr_code) {
console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
debug_info = 'html5_qr_code object found. Clearing and creating new Html5Qrcode...';
html5_qr_code.clear();
// document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
} else {
console.log('html5_qr_code not found. Creating new Html5Qrcode...');
debug_info = 'html5_qr_code not found. Creating new Html5Qrcode...';
html5_qr_code = new Html5Qrcode(
'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
);
}
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
debug_info = 'new Html5Qrcode for element id=qr_scanner_viewfinder';
if (start_qr_scanner && 1==2) {
console.log('Ready to start QR scanning! (after x500ms)');
debug_comment = 'Starting QR after 500ms...';
debug_info = 'Ready to start QR scanning! (after 2500ms)';
setTimeout(() => {
console.log('Inside QR scanning timeout after x500ms...');
// handle_start_qr_scanning();
handle_start_qr_scanning_trust();
}, 2500);
console.log('Started QR scanning after x500ms...');
}
// let subject = 'Camera Access Allowed';
// let message = error;
// send_init_confirm_email(subject, message);
// console.log('Dispatching qr_camera');
// debug_info = 'Dispatching qr_camera';
// dispatch('qr_camera', {
// status: 'allowed',
// });
// NOTE: This can only be done after the page has fully loaded.
// if (start_qr_scanner) {
// handle_start_qr_scanning();
// }
};
var get_user_media_error = function(error: any) {
if (error.name == 'NotAllowedError') {
console.log('Camera access not allowed!');
user_media_status = 'denied';
debug_comment = 'Camera Access Denied';
debug_info = JSON.stringify(error);
// alert('Error trying to start camera');
// alert(error);
let subject = 'Camera Access Denied';
let message = error;
send_init_confirm_email(subject, message);
// dispatch('qr_camera', {
// status: 'denied',
// });
}
};
// $: if (start_qr_scanner && user_media_status == 'allowed' && (scanning_status == 'not_started' || scanning_status == 'paused')) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else {
// // console.log('STOP QR SCANNING');
// // handle_stop_qr_scanning();
// }
// $: if (mounted && start_qr_scanner) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else if (mounted && !start_qr_scanner) {
// console.log('STOP QR SCANNING');
// handle_stop_qr_scanning();
// }
async function handle_start_qr_scanning_trust() {
console.log('*** handle_start_qr_scanning_trust() ***');
qr_scan_result = null;
qr_found_text = null;
debug_comment = 'Starting trusting QR scanning...';
debug_info = 'Just start!';
await html5_qr_code.clear();
html5_qr_code.start({ facingMode: qr_facing_mode }, qr_scan_cfg, handle_qr_scan_success, handle_qr_scan_error)
.then((ignore: any) => {
console.log('Scanning has started');
scanning_status = 'scanning';
debug_info = 'Scanning has started';
// let subject = 'QR Scanning Started';
// let message = ignore;
// send_init_confirm_email(subject, message);
return true;
}).catch((err) => {
console.log('There was an error while trying to start the QR scanner');
scanning_status = 'start_error';
debug_info = 'Error starting scanner: ' + JSON.stringify(err);
// let subject = 'QR Scanning Start Error';
// let message = err;
// send_init_confirm_email(subject, message);
// Error getting userMedia, error = NotReadableError: Could not start video source
return false;
});
return true;
}
async function handle_start_qr_scanning() {
console.log('*** handle_start_qr_scanning() ***');
if (user_media_status == 'denied') {
console.log('Camera access not allowed!');
return;
} else if (user_media_status == 'not_requested') {
console.log('Camera access not requested yet!');
return;
} else if (user_media_status == 'allowed') {
console.log('Camera access allowed!');
debug_info = 'Camera access allowed!';
} else {
console.log('Camera access status unknown! This should not happen?');
debug_info = 'Camera access status unknown! This should not happen?';
return;
}
qr_scan_result = null;
qr_found_text = null;
debug_comment = 'Starting QR Scanning...';
debug_info = 'Check the state and start if not started...';
let current_state = await html5_qr_code.getState()
if (current_state == Html5QrcodeScannerState.NOT_STARTED) {
// console.log('Scanner is not started');
return await html5_qr_code.start({ facingMode: qr_facing_mode }, qr_scan_cfg, handle_qr_scan_success, handle_qr_scan_error)
.then((ignore: any) => {
console.log('Scanning has started');
scanning_status = 'scanning';
debug_info = 'Scanning has started';
// let subject = 'QR Scanning Started';
// let message = ignore;
// send_init_confirm_email(subject, message);
return true;
}).catch((err) => {
console.log('There was an error while trying to start the QR scanner');
scanning_status = 'start_error';
debug_info = 'Error starting scanner: ' + JSON.stringify(err);
// let subject = 'QR Scanning Start Error';
// let message = err;
// send_init_confirm_email(subject, message);
// Error getting userMedia, error = NotReadableError: Could not start video source
return false;
});
} else {
console.log('Scanner is already started');
debug_info = 'Scanner is already started';
return;
}
}
function handle_pause_qr_scanning() {
if (html5_qr_code && html5_qr_code.getState() != Html5QrcodeScannerState.SCANNING) {
console.log('Scanner is not scanning!');
return;
}
html5_qr_code.pause();
scanning_status = 'paused';
}
function handle_resume_qr_scanning() {
if (html5_qr_code && html5_qr_code.getState() != Html5QrcodeScannerState.PAUSED) {
console.log('Scanner is not paused!');
return;
}
html5_qr_code.resume();
scanning_status = 'scanning';
}
async function handle_stop_qr_scanning() {
start_qr_scanner = false;
if (!html5_qr_code) {
console.log('html5_qr_code object found. Nothing to stop?');
return false;
}
await html5_qr_code.stop();
// let state = html5_qr_code.getState();
// console.log('html5_qr_code state:', state);
// if (state == Html5QrcodeScannerState.NOT_STARTED) {
// console.log('Scanner is not started');
// return;
// }
// if (state == Html5QrcodeScannerState.PAUSED || state == Html5QrcodeScannerState.SCANNING) {
// console.log('Scanner is not started');
// await html5_qr_code.stop();
// scanning_status = 'not_started';
// return;
// }
await html5_qr_code.clear();
return true;
// html5_qr_code.pause();
// return html5_qr_code.stop()
// .then((ignore) => {
// console.log('Scanning has stopped');
// // document.getElementById('qr_scanner_viewfinder').classList.add('d_none');
// scanning_status = 'not_started';
// }).then((ignore) => {
// // html5_qr_code = null;
// // html5_qr_code.clear();
// }).catch((err) => {
// console.log('There was an error while trying to stop the scanning');
// return false;
// });
// html5_qr_code = null;
}
// Callback function for QrcodeSuccessCallback (decodedText: string, result: Html5QrcodeResult)
function handle_qr_scan_success(decoded_text, decoded_result) {
console.log('*** handle_qr_scan_success() ***');
console.log(`QR scanned = ${decoded_text}`, decoded_result);
qr_scan_result = decoded_text; // NOTE: decoded_result is not currently used by html5-qrcode
qr_found_text = decoded_text;
dispatch('qr_scan_result', {
result: qr_scan_result, // This text will need to be parsed to get more info.
text: qr_found_text, // This text will need to be parsed to get more info.
entry_method: 'QR',
});
// handle_pause_qr_scanning();
handle_stop_qr_scanning();
}
// Callback function for QrcodeErrorCallback (errorMessage: string, error: Html5QrcodeError)
// NOTE: Most of the time this is normal and not an actual error. It just did not find something to scan.
function handle_qr_scan_error(qr_error_message, qr_code_error) {
// console.log('*** handle_qr_scan_error() ***');
if (qr_code_error.type) {
console.log(`Error scanning code = ${qr_error_message}`, qr_code_error);
return;
}
}
$: if ( qr_entered_badge_id && qr_entered_badge_id.length >= 11 && qr_entered_badge_id && qr_entered_badge_id.length <= 14) {
disable_submit_badge_id_btn = false;
} else {
disable_submit_badge_id_btn = true;
}
function handle_qr_manual_entry() {
console.log('*** handle_qr_manual_entry() ***');
if (qr_entered_text) {
console.log(`QR entered text = ${qr_entered_text}`);
} else if (qr_entered_badge_id) {
console.log(`QR entered badge ID = ${qr_entered_badge_id}`);
qr_entered_text = `OBJ:ot:event_badge,oi:${qr_entered_badge_id}`;
console.log(`Parse to proper QR badge ID = ${qr_entered_text}`);
}
// html5_qr_code.stop().then((ignore) => {
// console.log('Scanning has stopped');
// document.getElementById('qr_scanner_viewfinder').classList.add('d_none');
// }).catch((err) => {
// console.log('There was an error while trying to stop the scanning');
// });
qr_scan_result = qr_entered_text;
dispatch('qr_scan_result', {
result: qr_scan_result,
entry_method: 'manual',
});
qr_scan_result = null;
qr_entered_text = null;
}
function send_init_confirm_email(subject, message) {
console.log(`*** send_init_confirm_email() *** ${subject}`);
let to_email = 'scott.idem+skdev@oneskyit.com';
// let origin_url = encodeURI(`${data.url.origin}`);
let full_subject = `${subject} Aether QR Scanner Debugging`;
let body_html = `
<div>Scott,
<p>This is an automatic debug email from the Aether QR scanner.</p>
</div>
<br>
<div>
Message:<br>
<pre>
${JSON.stringify(message)}
</pre>
</div>
`;
api.send_email({
api_cfg: $ae_api,
from_email: 'noreply+ae_qr_debug@oneskyit.com',
from_name: 'AE QR Debug',
to_email: to_email,
subject: full_subject,
body_html: body_html,
});
}
</script>
<section
class="ae_element qr_scanner border-2 border-slate-500/10 space-y-2 flex flex-col gap-1 justify-center items-center min-w-full max-w-full"
class:not_started={scanning_status == 'not_started'}
class:paused={scanning_status == 'paused'}
class:scanning={scanning_status == 'scanning'}
>
<!-- <header>
<h2>QR Scanner</h2>
</header> -->
<!-- <fieldset class=""> -->
<!-- <legend class="d_none">QR Scanner:</legend> -->
<div
class="ae_container qr_scanning_container"
>
<div
class="ae_options flex flex-row gap-1 m-1"
>
<button
type="button"
on:click={ () => {
navigator.mediaDevices.getUserMedia({video: true})
.then(get_user_media_success, get_user_media_error);
}}
class="ae_btn__allow_camera btn btn-sm variant-soft-primary"
>
<span class="fas fa-camera mx-1"></span>
Allow Camera Access
</button>
<button
type="button"
on:click={ () => {
handle_start_qr_scanning_trust();
// Select back camera or fail with `OverconstrainedError`.
// html5_qr_code.start({ facingMode: { exact: "environment"} }, config, qrCodeSuccessCallback);
}}
class="ae_btn__start btn btn-sm variant-soft-primary"
>
<span class="fas fa-qrcode mx-1"></span>
Start Scanning
</button>
<button
on:click={ () => {
// Select back camera or fail with `OverconstrainedError`.
html5_qr_code.start({ facingMode: { exact: "environment"} }, config, qrCodeSuccessCallback);
}}
class="ae_btn__resume btn btn-sm variant-soft-primary">
<span class="fas fa-play"></span>
Resume
</button>
<button
on:click={handle_stop_qr_scanning}
class="ae_btn__stop btn btn-sm variant-soft-secondary"
>
<span class="fas fa-crosshairs fa-spin opacity-50 m-1"></span>
<!-- <span class="fas fa-stop-circle m-1"></span> -->
Stop
</button>
</div>
<div id="qr_scanner_viewfinder" class="qr_scanner_viewfinder grow flex flex-col justify-center items-center" style=""></div> <!-- width: 600px -->
</div>
{#if show_qr_manual_text_entry_option}
<div class="ae_container qr_manual_entry text_entry">
{#if show_qr_manual_entry}
<label for="entered_text" class="">Enter text</label>
<input type="text" name="entered_text" id="entered_text" bind:value="{qr_entered_text}">
<button on:click={handle_qr_manual_entry} class="btn btn-md variant-soft-warning"><span class="fas fa-paper-plane"></span> Submit Text</button>
<div class="search_by_text">
<input type='text' placeholder="Name or Email" label="Name or Email" value={search_query_str} focus={true} on:oninput={handle_oninput_search_query_str} />
</div>
{:else}
<button on:click={() => show_qr_manual_entry=true} class="btn btn-md variant-soft-warning"><span class="fas fa-keyboard"></span> Enter Text</button>
{/if}
</div>
{/if}
{#if show_qr_manual_badge_id_entry_option}
<div class="ae_container qr_manual_entry badge_id_entry">
{#if show_qr_manual_entry}
<form on:submit|preventDefault={() => handle_qr_manual_entry} class="flex">
<!-- <label for="entered_badge_id" class="">Enter badge ID</label>
<input type="text" name="entered_badge_id" id="entered_badge_id" bind:value="{qr_entered_badge_id}"> -->
<input
bind:value="{qr_entered_badge_id}"
type="text"
name="entered_badge_id"
id="entered_badge_id"
required
placeholder="Enter Badge ID"
class="input max-w-52"
/>
<button
type="submit"
on:click={handle_qr_manual_entry}
disabled={disable_submit_badge_id_btn}
class="btn btn-md variant-ghost-primary m-1"
class:btn_default={disable_submit_badge_id_btn}
class:btn_primary={!disable_submit_badge_id_btn}
>
<span class="fas fa-paper-plane mx-1"></span> Submit Badge ID
</button>
</form>
{:else}
<button on:click={() => show_qr_manual_entry=true} class="btn btn-md variant-soft-secondary m-1"><span class="fas fa-keyboard mx-1"></span> Enter Badge ID</button>
{/if}
</div>
{/if}
{#if show_qr_scan_result && qr_scan_result}
<div class="ae_container qr_scan_result">
<span class="label">Raw Result:</span>
<span id="qr_scan_result_value" class="value">{qr_scan_result}</span>
</div>
{/if}
{debug_comment ?? 'Debugging QR Scanner'}
{#if debug_info}
<div class="ae_container debug_info">
<span class="label">Debug Info:</span>
<span class="value">{debug_info}</span>
</div>
{/if}
<!-- </fieldset> -->
</section>
<style>
.not_started {
background-color: hsla(0, 100%, 75%, 0.3);
border-color: hsla(0, 100%, 75%, 0.6);
}
.paused {
background-color: hsla(60, 100%, 75%, 0.3);
border-color: hsla(60, 100%, 75%, 0.6);
}
.scanning {
background-color: hsla(120, 100%, 75%, 0.3);
border-color: hsla(120, 100%, 75%, 0.6);
}
.qr_scanner {
/* outline: solid thin pink; */
max-width: 100vw;
/* overflow-x: scroll; */
display: flex;
flex-direction: column;
/* flex-wrap: wrap; */
justify-content: flex-start;
align-items: center; /* center */
align-content: stretch;
}
.ae_element.qr_scanner div.qr_scanner_viewfinder {
/* max-width: 100vw; */
/* contain: content; */
/* contain: contain; */
}
.qr_scanner .qr_scanner_viewfinder {
/* outline: dashed medium blue; */
min-width: 400px;
width: 100%;
/* max-width: 100%; */
max-width: 500px;
/* max-width: 100vw; */
/* outline: solid thin red; */
contain: contain;
overflow-x: scroll;
}
@media (max-width: 767px) {
.qr_scanner .qr_scanner_viewfinder {
/* outline: dashed medium red; */
min-width: 80vw;
/* width: 100%; */
/* max-width: 100%; */
/* max-width: 450px; */
max-width: 100vw;
margin: 0;
padding: 0;
}
}
</style>

View File

@@ -0,0 +1,560 @@
<script lang="ts">
// *** Import Svelte core
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
// import 'html5-qrcode';
import {Html5Qrcode, Html5QrcodeScannerState, Html5QrcodeSupportedFormats} from 'html5-qrcode';
// *** Import Aether core variables and functions
import { api } from '$lib/api';
import { ae_api } from '$lib/ae_stores';
// *** Import Aether core components
// import Element_input from './element_input.svelte';
// import ae from '/element_input.svelte';
// import Input_element from '/element_input.svelte';
// *** Import Aether module variables and functions
// *** Import Aether module components
// *** Export/Exposed variables and functions for component
export let start_qr_scanner: boolean = true;
export let show_pause_btn: boolean = false; // pause and resume buttons
export let show_qr_manual_text_entry_option: boolean = false;
export let show_qr_manual_badge_id_entry_option: boolean = false;
export let show_qr_scan_result: boolean = true;
export let qr_fps = 10;
export let qr_viewfinder_width = 275; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
export let qr_facing_mode = 'environment'; // environment, user, { exact: 'environment'}, { exact: 'user'}
const dispatch = createEventDispatcher();
// *** Set initial variables
let scanning_status: string = 'not_started';
let qr_scan_result: null|string = null;
let qr_found_text: null|string = null;
let qr_entered_text: null|string = null;
let qr_entered_badge_id: null|string = null;
let show_qr_manual_entry: null|boolean = null;
let disable_submit_badge_id_btn: boolean = true;
let user_media_status = 'not_requested';
let debug_comment: string = 'Debugging QR Scanner';
let debug_info: any;
let html5_qr_code: any|null|string = null;
// let qr_scan_cfg = { fps: 10, qrbox: 400 }; // default was 250 and using 300 when 600px
let qr_scan_cfg = { fps: qr_fps, qrbox: qr_viewfinder_width }; // 275 seems good... Need to not let the this be larger than the container which changes based on the width of the screen/window.
// const html5QrCode = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
onMount(() => {
console.log('** Element Mounted: ** QR Scanner');
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
// navigator.mediaDevices.getUserMedia({video: true})
// .then(get_user_media_success, get_user_media_error);
// console.log('** Element Mounted: ** QR Scanner - getUserMedia in setTimeout');
});
onDestroy(async () => {
console.log('** Element Destroyed: ** QR Scanner');
qr_scan_result = null;
qr_found_text = null;
await handle_stop_qr_scanning();
});
var get_user_media_success = function(error: any) {
console.log('Camera access allowed');
user_media_status = 'allowed';
debug_comment = 'Camera Access Allowed';
debug_info = JSON.stringify(error);
if (html5_qr_code) {
console.log('html5_qr_code object found. Clearing and creating new Html5Qrcode...');
debug_info = 'html5_qr_code object found. Clearing and creating new Html5Qrcode...';
html5_qr_code.clear();
// document.getElementById('qr_scanner_viewfinder').classList.remove('d_none');
} else {
console.log('html5_qr_code not found. Creating new Html5Qrcode...');
debug_info = 'html5_qr_code not found. Creating new Html5Qrcode...';
html5_qr_code = new Html5Qrcode(
'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
);
}
// html5_qr_code = new Html5Qrcode(
// 'qr_scanner_viewfinder', { formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ] }
// );
debug_info = 'new Html5Qrcode for element id=qr_scanner_viewfinder';
if (start_qr_scanner && 1==2) {
console.log('Ready to start QR scanning! (after x500ms)');
debug_comment = 'Starting QR after 500ms...';
debug_info = 'Ready to start QR scanning! (after 2500ms)';
setTimeout(() => {
console.log('Inside QR scanning timeout after x500ms...');
handle_start_qr_scanning_trust();
}, 2500);
console.log('Started QR scanning after x500ms...');
}
// let subject = 'Camera Access Allowed';
// let message = error;
// send_init_confirm_email(subject, message);
console.log('Dispatching qr_camera');
debug_info = 'Dispatching qr_camera';
dispatch('qr_camera', {
status: 'allowed',
});
};
var get_user_media_error = function(error: any) {
if (error.name == 'NotAllowedError') {
console.log('Camera access not allowed!');
user_media_status = 'denied';
debug_comment = 'Camera Access Denied';
debug_info = JSON.stringify(error);
// alert('Error trying to start camera');
// alert(error);
let subject = 'Camera Access Denied';
let message = error;
send_init_confirm_email(subject, message);
// dispatch('qr_camera', {
// status: 'denied',
// });
}
};
// $: if (start_qr_scanner && user_media_status == 'allowed' && (scanning_status == 'not_started' || scanning_status == 'paused')) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else {
// // console.log('STOP QR SCANNING');
// // handle_stop_qr_scanning();
// }
// $: if (mounted && start_qr_scanner) {
// console.log('START QR SCANNING');
// handle_start_qr_scanning();
// } else if (mounted && !start_qr_scanner) {
// console.log('STOP QR SCANNING');
// handle_stop_qr_scanning();
// }
async function handle_start_qr_scanning_trust() {
console.log('*** handle_start_qr_scanning_trust() ***');
qr_scan_result = null;
qr_found_text = null;
debug_comment = 'Starting trusting QR scanning...';
debug_info = 'Just start!';
await html5_qr_code.clear();
html5_qr_code.start({ facingMode: qr_facing_mode }, qr_scan_cfg, handle_qr_scan_success, handle_qr_scan_error)
.then((ignore: any) => {
console.log('Scanning has started');
scanning_status = 'scanning';
debug_info = 'Scanning has started';
// let subject = 'QR Scanning Started';
// let message = ignore;
// send_init_confirm_email(subject, message);
return true;
}).catch((err) => {
console.log('There was an error while trying to start the QR scanner');
scanning_status = 'start_error';
debug_info = 'Error starting scanner: ' + JSON.stringify(err);
// let subject = 'QR Scanning Start Error';
// let message = err;
// send_init_confirm_email(subject, message);
// Error getting userMedia, error = NotReadableError: Could not start video source
return false;
});
return true;
}
async function handle_stop_qr_scanning() {
start_qr_scanner = false;
if (!html5_qr_code) {
console.log('html5_qr_code object found. Nothing to stop?');
scanning_status = 'not_started';
return false;
}
await html5_qr_code.stop();
scanning_status = 'not_started';
await html5_qr_code.clear();
return true;
}
// Callback function for QrcodeSuccessCallback (decodedText: string, result: Html5QrcodeResult)
function handle_qr_scan_success(decoded_text, decoded_result) {
console.log(`*** handle_qr_scan_success() *** QR scanned = ${decoded_text}`, decoded_result);
qr_scan_result = decoded_text; // NOTE: decoded_result is not currently used by html5-qrcode
qr_found_text = decoded_text;
dispatch('qr_scan_result', {
result: qr_scan_result, // This text will need to be parsed to get more info.
text: qr_found_text, // This text will need to be parsed to get more info.
entry_method: 'QR',
});
// handle_pause_qr_scanning();
handle_stop_qr_scanning();
}
// Callback function for QrcodeErrorCallback (errorMessage: string, error: Html5QrcodeError)
// NOTE: Most of the time this is normal and not an actual error. It just did not find something to scan.
function handle_qr_scan_error(qr_error_message, qr_code_error) {
// console.log('*** handle_qr_scan_error() ***');
if (qr_code_error.type) {
console.log(`Error scanning code = ${qr_error_message}`, qr_code_error);
return;
}
}
$: if ( qr_entered_badge_id && qr_entered_badge_id.length >= 11 && qr_entered_badge_id && qr_entered_badge_id.length <= 14) {
disable_submit_badge_id_btn = false;
} else {
disable_submit_badge_id_btn = true;
}
function handle_qr_manual_entry() {
console.log('*** handle_qr_manual_entry() ***');
if (qr_entered_text) {
console.log(`QR entered text = ${qr_entered_text}`);
} else if (qr_entered_badge_id) {
console.log(`QR entered badge ID = ${qr_entered_badge_id}`);
qr_entered_text = `OBJ:ot:event_badge,oi:${qr_entered_badge_id}`;
console.log(`Parse to proper QR badge ID = ${qr_entered_text}`);
}
// html5_qr_code.stop().then((ignore) => {
// console.log('Scanning has stopped');
// document.getElementById('qr_scanner_viewfinder').classList.add('d_none');
// }).catch((err) => {
// console.log('There was an error while trying to stop the scanning');
// });
qr_scan_result = qr_entered_text;
dispatch('qr_scan_result', {
result: qr_scan_result,
entry_method: 'manual',
});
qr_scan_result = null;
qr_entered_text = null;
}
function send_init_confirm_email(subject, message) {
console.log(`*** send_init_confirm_email() *** ${subject}`);
let to_email = 'scott.idem+skdev@oneskyit.com';
// let origin_url = encodeURI(`${data.url.origin}`);
let full_subject = `${subject} Aether QR Scanner Debugging`;
let body_html = `
<div>Scott,
<p>This is an automatic debug email from the Aether QR scanner.</p>
</div>
<br>
<div>
Message:<br>
<pre>
${JSON.stringify(message)}
</pre>
</div>
`;
api.send_email({
api_cfg: $ae_api,
from_email: 'noreply+ae_qr_debug@oneskyit.com',
from_name: 'AE QR Debug',
to_email: to_email,
subject: full_subject,
body_html: body_html,
});
}
</script>
<section
class="ae_element qr_scanner border-2 border-slate-500/10 space-y-2 flex flex-col gap-1 justify-center items-center min-w-full max-w-full"
class:not_started={scanning_status == 'not_started'}
class:paused={scanning_status == 'paused'}
class:scanning={scanning_status == 'scanning'}
>
<!-- <header>
<h2>QR Scanner</h2>
</header> -->
<!-- <fieldset class=""> -->
<!-- <legend class="d_none">QR Scanner:</legend> -->
<div
class="ae_container qr_scanning_container"
>
<div
class="ae_options flex flex-row justify-center items-center gap-1 m-1"
>
<button
type="button"
on:click={ () => {
navigator.mediaDevices.getUserMedia({video: true})
.then(get_user_media_success, get_user_media_error);
}}
class="ae_btn__allow_camera btn btn-sm variant-soft-primary"
>
<span class="fas fa-camera mx-1"></span>
Allow Camera Access
</button>
<button
type="button"
on:click={ () => {
handle_start_qr_scanning_trust();
// Select back camera or fail with `OverconstrainedError`.
// html5_qr_code.start({ facingMode: { exact: "environment"} }, config, qrCodeSuccessCallback);
}}
class="ae_btn__start btn btn-sm variant-soft-primary"
>
<span class="fas fa-qrcode mx-1"></span>
Start Scanning
</button>
<!-- <button
on:click={ () => {
html5_qr_code.start({ facingMode: { exact: "environment"} }, config, qrCodeSuccessCallback);
}}
class="ae_btn__resume btn btn-sm variant-soft-primary">
<span class="fas fa-play"></span>
Resume
</button> -->
{#if (scanning_status == 'scanning')}
<button
on:click={handle_stop_qr_scanning}
class="ae_btn__stop btn btn-sm variant-soft-secondary"
>
<span class="fas fa-crosshairs fa-spin opacity-50 m-1"></span>
<!-- <span class="fas fa-stop-circle m-1"></span> -->
Stop
</button>
{/if}
</div>
<div id="qr_scanner_viewfinder" class="qr_scanner_viewfinder grow flex flex-col justify-center items-center" style=""></div> <!-- width: 600px -->
</div>
{#if show_qr_manual_text_entry_option}
<div class="ae_container qr_manual_entry text_entry">
{#if show_qr_manual_entry}
<label for="entered_text" class="">Enter text</label>
<input type="text" name="entered_text" id="entered_text" bind:value="{qr_entered_text}">
<button on:click={handle_qr_manual_entry} class="btn btn-md variant-soft-warning"><span class="fas fa-paper-plane"></span> Submit Text</button>
<div class="search_by_text">
<input type='text' placeholder="Name or Email" label="Name or Email" value={search_query_str} focus={true} on:oninput={handle_oninput_search_query_str} />
</div>
{:else}
<button on:click={() => {
handle_stop_qr_scanning();
show_qr_manual_entry=true;
}}
class="btn btn-md variant-soft-warning m-1"
>
<span class="fas fa-keyboard mx-1"></span> Enter Text
</button>
{/if}
</div>
{/if}
{#if show_qr_manual_badge_id_entry_option}
<div class="ae_container qr_manual_entry badge_id_entry">
{#if show_qr_manual_entry}
<form on:submit|preventDefault={() => handle_qr_manual_entry} class="flex">
<!-- <label for="entered_badge_id" class="">Enter badge ID</label>
<input type="text" name="entered_badge_id" id="entered_badge_id" bind:value="{qr_entered_badge_id}"> -->
<input
bind:value="{qr_entered_badge_id}"
type="text"
name="entered_badge_id"
id="entered_badge_id"
required
placeholder="Enter Badge ID"
class="input max-w-52"
/>
<button
type="submit"
on:click={handle_qr_manual_entry}
disabled={disable_submit_badge_id_btn}
class="btn btn-md variant-ghost-primary m-1"
class:btn_default={disable_submit_badge_id_btn}
class:btn_primary={!disable_submit_badge_id_btn}
>
<span class="fas fa-paper-plane mx-1"></span> Submit Badge ID
</button>
</form>
{:else}
<button on:click={() => {
handle_stop_qr_scanning();
show_qr_manual_entry=true;
}}
class="btn btn-md variant-soft-secondary m-1"
>
<span class="fas fa-keyboard mx-1"></span> Enter Badge ID
</button>
{/if}
</div>
{/if}
{#if show_qr_scan_result && qr_scan_result}
<div class="ae_container qr_scan_result">
<span class="label">Raw Result:</span>
<span id="qr_scan_result_value" class="value">{qr_scan_result}</span>
</div>
{/if}
<!-- {debug_comment ?? 'Debugging QR Scanner'}
{#if debug_info}
<div class="ae_container debug_info">
<span class="label">Debug Info:</span>
<span class="value">{debug_info}</span>
</div>
{/if} -->
<!-- </fieldset> -->
<p>v2 - Try pressing the "Allow Camera Access" button and then the "Start Scanning" button if it does not start on its own. This fix is not perfect. A permanent solution is actively being worked on in the development version.</p>
</section>
<style>
.not_started {
background-color: hsla(0, 100%, 75%, 0.3);
border-color: hsla(0, 100%, 75%, 0.6);
}
.paused {
background-color: hsla(60, 100%, 75%, 0.3);
border-color: hsla(60, 100%, 75%, 0.6);
}
.scanning {
background-color: hsla(120, 100%, 75%, 0.3);
border-color: hsla(120, 100%, 75%, 0.6);
}
.qr_scanner {
/* outline: solid thin pink; */
max-width: 100vw;
/* overflow-x: scroll; */
display: flex;
flex-direction: column;
/* flex-wrap: wrap; */
justify-content: flex-start;
align-items: center; /* center */
align-content: stretch;
}
.ae_element.qr_scanner div.qr_scanner_viewfinder {
/* max-width: 100vw; */
/* contain: content; */
/* contain: contain; */
}
.qr_scanner .qr_scanner_viewfinder {
/* outline: dashed medium blue; */
min-width: 400px;
width: 100%;
/* max-width: 100%; */
max-width: 500px;
/* max-width: 100vw; */
/* outline: solid thin red; */
contain: contain;
overflow-x: scroll;
}
@media (max-width: 767px) {
.qr_scanner .qr_scanner_viewfinder {
/* outline: dashed medium red; */
min-width: 80vw;
/* width: 100%; */
/* max-width: 100%; */
/* max-width: 450px; */
max-width: 100vw;
margin: 0;
padding: 0;
}
}
</style>

View File

@@ -643,11 +643,6 @@ function handle_qr_camera(event) {
<header class="popover__header flex gap-1 justify-between items-center p-1 border-b">
<h2 class="h3">Scan</h2>
<div class="variant-ghost-error">
<span class="fas fa-exclamation-triangle mx-1"></span>
<span>Bug fix in progress for scanning with some devices -2024-04-10 10:15 AM</span>
</div>
<div class="popover__actions">
<button
type="button"
@@ -658,7 +653,7 @@ function handle_qr_camera(event) {
}}
>
<span class="fas fa-times mx-1"></span>
Close X
Close
</button>
</div>
</header>

View File

@@ -10,6 +10,8 @@ import { events_loc, events_sess, events_slct, events_trigger } from '$lib/ae_ev
import { events_func } from '$lib/ae_events_functions';
import Element_qr_scanner from '$lib/element_qr_scanner.svelte';
import Element_qr_scanner_v2 from '$lib/element_qr_scanner_v2.svelte';
import Element_qr_scanner_dev from '$lib/element_qr_scanner_dev.svelte';
// TEMPORARY: For testing and development
@@ -202,7 +204,14 @@ function handle_qr_camera(event) {
Please check your browser's permissions.
{/if}
</p>
<!--
<Element_qr_scanner
start_qr_scanner={false}
show_qr_scan_result={true}
show_qr_manual_badge_id_entry_option={true}
on:qr_scan_result={handle_qr_scan_result}
on:qr_camera={handle_qr_camera}
/> -->
<span class="flex flex-col md:flex-row wrap justify-center items-center">
<button
@@ -218,7 +227,7 @@ function handle_qr_camera(event) {
}}
title="Scan a QR code to add a person to the leads list."
>
<span class="fas fa-qrcode mx-1"></span>
<span class="fas fa-qrcode mx-2"></span>
Scan to Add Person
</button>
@@ -232,10 +241,9 @@ function handle_qr_camera(event) {
// event_exhibit_tracking_obj_create_promise = null;
$events_sess.leads.show_form__search = true;
}}
disabled={!$ae_loc.trusted_access && 2==4}
title="Search for a person to add to the leads list."
>
<span class="fas fa-search mx-1"></span>
<span class="fas fa-search mx-2"></span>
Search to Add Person
</button>
</span>
@@ -249,6 +257,26 @@ function handle_qr_camera(event) {
</span>
{#if $events_loc.leads.show_content__scan_requirements}
<div class="border border-slate-500/10 p-2 variant-ghost-warning p-1">
<span class="fas fa-exclamation-triangle mx-1"></span>
<span>
<strong>Alert</strong>
<span>Use Scanner Version:</span>
<select
bind:value={$ae_loc.qr_scanner_version}
class="select w-52"
>
<option value={'one'}>v1</option>
<option value={'two'}>v2</option>
<option value={"dev"}>Development</option>
</select>
<p>A bug was found late Monday that affects some mobile devices (cell phones and tablets). Incremental fixes have been added, but this issue has been difficult to address accoss all devices. We recommend most users stick with v1 (default). If you are not having any trouble and have no idea what this message is about, then this is what you want.</p>
<p>We recommend you try v2 if you are having trouble with the camera not starting or turning on. This version allows you to manually attempt to Allow Camera Access and to Start Scanning if it does not start on its own. This fix is not perfect. A permanent solution is actively being worked on in the development version.</p>
<p>A fix is in progress to address all devices. -2024-04-10 2:25 AM (Prague time)</p>
</span>
</div>
<div class="border border-slate-500/10 p-2 variant-soft-secondary">
<p>You will need a device with a camera to scan the QR codes. You will also of course need one or more valid QR codes to scan.
<!-- <button class="ae_btn btn_sm" on:click={() => show='qr_codes'}><span class="fas fa-qrcode"></span> Example QR Codes</button> -->
@@ -305,7 +333,7 @@ function handle_qr_camera(event) {
placeholder="Search for attendee"
bind:value={$events_sess.leads.entered_search_str}
required
class="input max-w-56"
class="input text-lg w-56"
autofocus
/>
@@ -660,8 +688,7 @@ function handle_qr_camera(event) {
<section class="popover__content grow flex flex-col gap-4 items-center ae_modal_scrollfix">
<!-- <div class=""> -->
{#if !$ae_loc.qr_scanner_version || $ae_loc.qr_scanner_version == 'one'}
<Element_qr_scanner
start_qr_scanner={$events_sess.leads.qr_scan_start}
show_qr_scan_result={true}
@@ -669,7 +696,28 @@ function handle_qr_camera(event) {
on:qr_scan_result={handle_qr_scan_result}
on:qr_camera={handle_qr_camera}
/>
<!-- </div> -->
{:else if $ae_loc.qr_scanner_version == 'two'}
<Element_qr_scanner_v2
start_qr_scanner={$events_sess.leads.qr_scan_start}
show_qr_scan_result={true}
show_qr_manual_badge_id_entry_option={true}
on:qr_scan_result={handle_qr_scan_result}
on:qr_camera={handle_qr_camera}
/>
<!-- v2 -->
{:else if $ae_loc.qr_scanner_version == 'dev'}
<Element_qr_scanner_dev
start_qr_scanner={$events_sess.leads.qr_scan_start}
show_qr_scan_result={true}
show_qr_manual_badge_id_entry_option={true}
on:qr_scan_result={handle_qr_scan_result}
on:qr_camera={handle_qr_camera}
/>
vDEV
{:else}
???
{/if}
<div class="qr_quick_results variant-soft-secondary font-bold p-4">
{@html $events_sess.leads.qr_scan_result ?? 'No results yet'}

View File

@@ -423,7 +423,7 @@ $: if ($events_slct.exhibit_id) {
}}
disabled={!$events_slct.exhibit_obj.priority}
class="btn btn-sm variant-ghost-primary w-48 mb-1 export_data_btn"
title={`TEMPORARILY DISABLED: Download leads data for ${$events_slct.exhibit_obj.name}`}
title={`Download leads data for ${$events_slct.exhibit_obj.name}`}
>
{#await ae_promises.download__exhibit_tracking_export}
<span class="fas fa-spinner fa-spin"></span>