Remove redundant/backup element_qr_scanner and old_e_app_codemirror files.
This commit is contained in:
@@ -1,543 +0,0 @@
|
||||
<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 = 15;
|
||||
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 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 mounted = false;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
// 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 successCallback = function(error: any) {
|
||||
console.log('Camera access allowed');
|
||||
user_media_status = 'allowed';
|
||||
|
||||
// let subject = 'Camera Access Allowed';
|
||||
// let message = error;
|
||||
// send_init_confirm_email(subject, message);
|
||||
|
||||
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 errorCallback = function(error: any) {
|
||||
if (error.name == 'NotAllowedError') {
|
||||
console.log('Camera access not allowed!');
|
||||
user_media_status = 'denied';
|
||||
|
||||
// let subject = 'Camera Access Denied';
|
||||
// let message = error;
|
||||
// send_init_confirm_email(subject, message);
|
||||
|
||||
dispatch('qr_camera', {
|
||||
status: 'denied',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// $: 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;
|
||||
}
|
||||
|
||||
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 ] }
|
||||
);
|
||||
}
|
||||
|
||||
if (html5_qr_code.getState() == 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';
|
||||
|
||||
// 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';
|
||||
|
||||
let subject = 'QR Scanning Start Error';
|
||||
let message = err;
|
||||
send_init_confirm_email(subject, message);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
console.log('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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-primary border border-primary-500 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 preset-tonal-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}
|
||||
<!-- </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>
|
||||
@@ -1,683 +0,0 @@
|
||||
<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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-primary border border-primary-500 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 preset-tonal-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>
|
||||
@@ -1,641 +0,0 @@
|
||||
<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 mounted = false;
|
||||
|
||||
|
||||
onMount(() => {
|
||||
console.log('** Element Mounted: ** QR Scanner');
|
||||
|
||||
// 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();
|
||||
// }
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
onDestroy(async () => {
|
||||
console.log('** Element Destroyed: ** QR Scanner');
|
||||
|
||||
qr_scan_result = null;
|
||||
qr_found_text = null;
|
||||
|
||||
await handle_stop_qr_scanning();
|
||||
});
|
||||
|
||||
|
||||
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();
|
||||
// }
|
||||
};
|
||||
|
||||
var errorCallback = 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');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-primary border border-primary-500 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 preset-tonal-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>
|
||||
@@ -1,670 +0,0 @@
|
||||
<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 preset-tonal-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 preset-tonal-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 preset-tonal-primary">
|
||||
<span class="fas fa-play"></span>
|
||||
Resume
|
||||
</button>
|
||||
|
||||
<button
|
||||
on:click={handle_stop_qr_scanning}
|
||||
class="ae_btn__stop btn btn-sm preset-tonal-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 preset-tonal-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 preset-tonal-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 preset-tonal-primary border border-primary-500 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 preset-tonal-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>
|
||||
@@ -1,140 +0,0 @@
|
||||
<script lang="ts">
|
||||
// *** Import Svelte version 5 specific
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// BEGIN: CodeMirror
|
||||
|
||||
import {basicSetup} from "codemirror";
|
||||
import { EditorView, keymap, placeholder as placeholderExt } from "@codemirror/view";
|
||||
|
||||
import { EditorState, StateEffect, type Extension } from "@codemirror/state";
|
||||
import { indentWithTab } from "@codemirror/commands";
|
||||
|
||||
import {
|
||||
defaultHighlightStyle, syntaxHighlighting, indentOnInput,
|
||||
bracketMatching, foldGutter, foldKeymap
|
||||
} from "@codemirror/language";
|
||||
import { indentUnit, type LanguageSupport } from "@codemirror/language";
|
||||
import {languages} from "@codemirror/language-data"
|
||||
import { css } from "@codemirror/lang-css";
|
||||
import { html } from "@codemirror/lang-html";
|
||||
import {javascript} from "@codemirror/lang-javascript";
|
||||
import {markdown} from "@codemirror/lang-markdown";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
|
||||
// END: CodeMirror
|
||||
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
content: string; // Actual text content to be displayed in the editor
|
||||
classes: string;
|
||||
basic: boolean;
|
||||
useTab: boolean;
|
||||
tabSize: number;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
content = 'TEST content for CodeMirror',
|
||||
classes = '',
|
||||
basic = true,
|
||||
useTab = true,
|
||||
tabSize = 4,
|
||||
}: Props = $props();
|
||||
|
||||
let state_extensions = [
|
||||
// ...get_base_extensions(basic, useTab, tabSize, lineWrapping, placeholder, editable, readonly, lang),
|
||||
// ...get_theme(theme, styles),
|
||||
basicSetup,
|
||||
keymap.of([indentWithTab]),
|
||||
javascript(),
|
||||
markdown({codeLanguages: languages})
|
||||
// ...extensions,
|
||||
];
|
||||
|
||||
let editor_view: EditorView;
|
||||
let element: HTMLElement|undefined = $state();
|
||||
|
||||
if (browser) {
|
||||
// editor_view = create_editor_view();
|
||||
|
||||
|
||||
let test_view = new EditorView({
|
||||
doc: "Hello\n\n```javascript\nlet x = 'y'\n```",
|
||||
extensions: [
|
||||
basicSetup,
|
||||
markdown({codeLanguages: languages})
|
||||
],
|
||||
parent: document.getElementById('codemirror-view') ?? element,
|
||||
state: EditorState.create({
|
||||
doc: "Hello\n\n```javascript\nlet x = 'y'\n```",
|
||||
extensions: [
|
||||
basicSetup,
|
||||
markdown({codeLanguages: languages})
|
||||
],
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
function create_editor_view(): EditorView {
|
||||
return new EditorView({
|
||||
doc: content,
|
||||
extensions: [basicSetup, javascript()],
|
||||
// parent: element,
|
||||
parent: document.getElementById('codemirror-view') ?? element,
|
||||
state: create_editor_state(content),
|
||||
});
|
||||
}
|
||||
|
||||
function create_editor_state(value: string | null | undefined): EditorState {
|
||||
return EditorState.create({
|
||||
doc: value ?? undefined,
|
||||
extensions: state_extensions,
|
||||
});
|
||||
}
|
||||
|
||||
function update(value: string | null | undefined): void {
|
||||
if (first_update) {
|
||||
first_update = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_from_state) {
|
||||
update_from_state = false;
|
||||
return;
|
||||
}
|
||||
|
||||
update_from_prop = true;
|
||||
|
||||
editor_view.setState(create_editor_state(value));
|
||||
|
||||
update_from_prop = false;
|
||||
}
|
||||
|
||||
|
||||
function handle_change(): void {
|
||||
const new_value = editor_view.state.doc.toString();
|
||||
if (new_value === content) return;
|
||||
|
||||
update_from_state = true;
|
||||
|
||||
content = new_value;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{#if browser}
|
||||
<!-- <div class="codemirror-wrapper {classes}" bind:this={element} ></div> -->
|
||||
<div id="codemirror-view" class="codemirror-wrapper {classes}" ></div>
|
||||
{:else}
|
||||
<div class="scm-waiting {classes}">
|
||||
<div class="scm-waiting__loading scm-loading">
|
||||
<!-- <div class="scm-loading__spinner" /> -->
|
||||
<p class="scm-loading__text">Loading editor...</p>
|
||||
</div>
|
||||
|
||||
<pre class="scm-pre cm-editor">{content}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,226 +0,0 @@
|
||||
<script lang="ts" context="module">
|
||||
export type ThemeSpec = Record<string, StyleSpec>;
|
||||
export type StyleSpec = {
|
||||
[propOrSelector: string]: string | number | StyleSpec | null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onDestroy, onMount } from "svelte";
|
||||
import { basicSetup } from "codemirror";
|
||||
import { EditorView, keymap, placeholder as placeholderExt } from "@codemirror/view";
|
||||
import { EditorState, StateEffect, type Extension } from "@codemirror/state";
|
||||
import { indentWithTab } from "@codemirror/commands";
|
||||
import { indentUnit, type LanguageSupport } from "@codemirror/language";
|
||||
import { debounce } from "./util";
|
||||
|
||||
let classes = "";
|
||||
export { classes as class };
|
||||
export let value: string | null | undefined = "";
|
||||
|
||||
export let basic = true;
|
||||
export let lang: LanguageSupport | null | undefined = undefined;
|
||||
export let theme: Extension | null | undefined = undefined;
|
||||
export let extensions: Extension[] = [];
|
||||
|
||||
export let useTab = true;
|
||||
export let tabSize = 2;
|
||||
|
||||
export let styles: ThemeSpec | null | undefined = undefined;
|
||||
export let lineWrapping = false;
|
||||
export let editable = true;
|
||||
export let readonly = false;
|
||||
export let placeholder: string | HTMLElement | null | undefined = undefined;
|
||||
|
||||
export let nodebounce = false;
|
||||
|
||||
const is_browser = typeof window !== "undefined";
|
||||
const dispatch = createEventDispatcher<{ change: string, ready: EditorView, reconfigure: EditorView }>();
|
||||
|
||||
let element: HTMLDivElement;
|
||||
let view: EditorView;
|
||||
|
||||
let update_from_prop = false;
|
||||
let update_from_state = false;
|
||||
let first_config = true;
|
||||
let first_update = true;
|
||||
|
||||
$: state_extensions = [
|
||||
...get_base_extensions(basic, useTab, tabSize, lineWrapping, placeholder, editable, readonly, lang),
|
||||
...get_theme(theme, styles),
|
||||
...extensions,
|
||||
];
|
||||
|
||||
$: view && update(value);
|
||||
$: view && state_extensions && reconfigure();
|
||||
|
||||
$: on_change = nodebounce ? handle_change : debounce(handle_change, 300);
|
||||
|
||||
onMount(() => {
|
||||
view = create_editor_view();
|
||||
dispatch('ready', view);
|
||||
});
|
||||
onDestroy(() => view?.destroy());
|
||||
|
||||
function create_editor_view(): EditorView {
|
||||
return new EditorView({
|
||||
parent: element,
|
||||
state: create_editor_state(value),
|
||||
dispatch(transaction) {
|
||||
view.update([transaction]);
|
||||
|
||||
if (!update_from_prop && transaction.docChanged) {
|
||||
on_change();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function reconfigure(): void {
|
||||
if (first_config) {
|
||||
first_config = false;
|
||||
return;
|
||||
}
|
||||
|
||||
view.dispatch({
|
||||
effects: StateEffect.reconfigure.of(state_extensions),
|
||||
});
|
||||
|
||||
dispatch('reconfigure', view);
|
||||
}
|
||||
|
||||
function update(value: string | null | undefined): void {
|
||||
if (first_update) {
|
||||
first_update = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_from_state) {
|
||||
update_from_state = false;
|
||||
return;
|
||||
}
|
||||
|
||||
update_from_prop = true;
|
||||
|
||||
view.setState(create_editor_state(value));
|
||||
|
||||
update_from_prop = false;
|
||||
}
|
||||
|
||||
function handle_change(): void {
|
||||
const new_value = view.state.doc.toString();
|
||||
if (new_value === value) return;
|
||||
|
||||
update_from_state = true;
|
||||
|
||||
value = new_value;
|
||||
dispatch("change", value);
|
||||
}
|
||||
|
||||
function create_editor_state(value: string | null | undefined): EditorState {
|
||||
return EditorState.create({
|
||||
doc: value ?? undefined,
|
||||
extensions: state_extensions,
|
||||
});
|
||||
}
|
||||
|
||||
function get_base_extensions(
|
||||
basic: boolean,
|
||||
useTab: boolean,
|
||||
tabSize: number,
|
||||
lineWrapping: boolean,
|
||||
placeholder: string | HTMLElement | null | undefined,
|
||||
editable: boolean,
|
||||
readonly: boolean,
|
||||
lang: LanguageSupport | null | undefined
|
||||
): Extension[] {
|
||||
const extensions: Extension[] = [
|
||||
indentUnit.of(" ".repeat(tabSize)),
|
||||
EditorView.editable.of(editable),
|
||||
EditorState.readOnly.of(readonly),
|
||||
];
|
||||
|
||||
if (basic) extensions.push(basicSetup);
|
||||
if (useTab) extensions.push(keymap.of([indentWithTab]));
|
||||
if (placeholder) extensions.push(placeholderExt(placeholder));
|
||||
if (lang) extensions.push(lang);
|
||||
if (lineWrapping) extensions.push(EditorView.lineWrapping);
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
function get_theme(theme: Extension | null | undefined, styles: ThemeSpec | null | undefined): Extension[] {
|
||||
const extensions: Extension[] = [];
|
||||
if (styles) extensions.push(EditorView.theme(styles));
|
||||
if (theme) extensions.push(theme);
|
||||
return extensions;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if is_browser}
|
||||
<div class="codemirror-wrapper {classes}" bind:this={element}></div>
|
||||
{:else}
|
||||
<div class="scm-waiting {classes}">
|
||||
<div class="scm-waiting__loading scm-loading">
|
||||
<div class="scm-loading__spinner"></div>
|
||||
<p class="scm-loading__text">Loading editor...</p>
|
||||
</div>
|
||||
|
||||
<pre class="scm-pre cm-editor">{value}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.codemirror-wrapper :global(.cm-focused) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.scm-waiting {
|
||||
position: relative;
|
||||
}
|
||||
.scm-waiting__loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.scm-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.scm-loading__spinner {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 100%;
|
||||
border: solid 2px #000;
|
||||
border-top-color: transparent;
|
||||
margin-right: 0.75rem;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.scm-loading__text {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.scm-pre {
|
||||
font-size: 0.85rem;
|
||||
font-family: monospace;
|
||||
tab-size: 2;
|
||||
-moz-tab-size: 2;
|
||||
resize: none;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user