parent
6f818448b3
commit
e68bc0c4d6
@ -0,0 +1,310 @@ |
||||
<script lang="ts"> |
||||
import { t } from "svelte-i18n"; |
||||
import { |
||||
type Html5QrcodeResult, |
||||
type Html5QrcodeScanner, |
||||
} from "html5-qrcode"; |
||||
import { |
||||
Alert, |
||||
Modal, |
||||
Sidebar, |
||||
SidebarGroup, |
||||
SidebarWrapper, |
||||
Spinner, |
||||
} from "flowbite-svelte"; |
||||
import { |
||||
ArrowLeft, |
||||
ArrowRightCircle, |
||||
Camera, |
||||
CheckBadge, |
||||
QrCode, |
||||
} from "svelte-heros-v2"; |
||||
import CenteredLayout from "../lib/CenteredLayout.svelte"; |
||||
import { onMount } from "svelte"; |
||||
import { push } from "svelte-spa-router"; |
||||
|
||||
let WebQRScannerClassPromise: Promise<typeof Html5QrcodeScanner>; |
||||
let html5QrcodeScanner: Html5QrcodeScanner; |
||||
async function load_qr_scanner_lib() { |
||||
// Load in browser only |
||||
if (!tauri_platform && !WebQRScannerClassPromise) { |
||||
WebQRScannerClassPromise = new Promise((resolve) => { |
||||
import("html5-qrcode").then((lib) => resolve(lib.Html5QrcodeScanner)); |
||||
}); |
||||
} |
||||
// TODO: Load alternative for native apps? |
||||
} |
||||
|
||||
let top; |
||||
const tauri_platform: string | undefined = import.meta.env.TAURI_PLATFORM; |
||||
let has_camera: boolean | "checking" = "checking"; |
||||
let login_method: "scan" | "gen" | undefined = undefined; |
||||
|
||||
let scan_state: |
||||
| "before_start" |
||||
| "scanning" |
||||
| "has_scanned" |
||||
| "success" |
||||
| Error = "before_start"; |
||||
let scanned_qr: string | undefined = undefined; |
||||
|
||||
let gen_state: |
||||
| "before_start" |
||||
| "generating" |
||||
| "generated" |
||||
| "success" |
||||
| Error = "before_start"; |
||||
let generated_qr: string | undefined = undefined; |
||||
|
||||
const check_has_camera = async () => { |
||||
if (!tauri_platform) { |
||||
// If there is a camera, go to scan mode, else gen mode. |
||||
try { |
||||
const devices = await navigator.mediaDevices.enumerateDevices(); |
||||
has_camera = |
||||
devices.filter((device) => device.kind === "videoinput").length > 0; |
||||
} catch { |
||||
has_camera = false; |
||||
} |
||||
login_method = has_camera ? "scan" : "gen"; |
||||
// Load Scanner lib, if necessary. |
||||
if (has_camera) load_qr_scanner_lib(); |
||||
} else { |
||||
// TODO: rust API @niko |
||||
} |
||||
}; |
||||
check_has_camera(); |
||||
|
||||
function on_qr_scanned(text: string) { |
||||
scan_state = "has_scanned"; |
||||
scanned_qr = text; |
||||
// TODO: API calls for synchronization @niko |
||||
// ToRemove: |
||||
setTimeout(() => { |
||||
scan_state = "success"; |
||||
}, 2_000); |
||||
} |
||||
|
||||
function clear_scanner() { |
||||
if (html5QrcodeScanner) html5QrcodeScanner.clear(); |
||||
html5QrcodeScanner = null; |
||||
} |
||||
|
||||
async function open_scanner() { |
||||
scan_state = "scanning"; |
||||
|
||||
const WebQRScanner = await WebQRScannerClassPromise; |
||||
html5QrcodeScanner = new WebQRScanner( |
||||
"scanner-div", |
||||
{ fps: 10, qrbox: { width: 300, height: 300 }, formatsToSupport: [0] }, |
||||
false |
||||
); |
||||
|
||||
html5QrcodeScanner.render((decoded_text, decoded_result) => { |
||||
// Handle scan result |
||||
on_qr_scanned(decoded_text); |
||||
clear_scanner(); |
||||
}, undefined); |
||||
|
||||
// Auto-Request camera permissions (there's no native way, unfortunately...) |
||||
setTimeout(() => { |
||||
// Auto-start by clicking button |
||||
document.getElementById("html5-qrcode-button-camera-permission")?.click(); |
||||
}, 100); |
||||
} |
||||
|
||||
function close_scanner_modal() { |
||||
clear_scanner(); |
||||
if (scanned_qr) { |
||||
scan_state = "has_scanned"; |
||||
} else { |
||||
scan_state = "before_start"; |
||||
} |
||||
} |
||||
|
||||
function generate_qr() { |
||||
gen_state = "generating"; |
||||
// TODO: @niko generated_qr = await ng.generate_export_qr(); |
||||
// ToRemove: |
||||
setTimeout(() => { |
||||
gen_state = "generated"; |
||||
generated_qr = "dummy"; |
||||
}, 1500); |
||||
setTimeout(() => { |
||||
gen_state = "success"; |
||||
}, 3500); |
||||
} |
||||
|
||||
function continue_to_login(wallet) {} |
||||
|
||||
function scrollToTop() { |
||||
top.scrollIntoView(); |
||||
} |
||||
onMount(() => scrollToTop()); |
||||
</script> |
||||
|
||||
<CenteredLayout> |
||||
<div class="container3" bind:this={top}> |
||||
<div |
||||
class="flex flex-col justify-center max-w-md mx-6 mb-20 bg-gray-60 overflow-y-auto py-4 dark:bg-gray-800" |
||||
> |
||||
<!-- Title --> |
||||
<div> |
||||
<h2 class="text-xl mb-6">{$t("pages.wallet_login_qr.title")}</h2> |
||||
</div> |
||||
|
||||
<!-- Checking, if camera is available... --> |
||||
{#if login_method === undefined} |
||||
<!-- TODO: Check connectivity here--> |
||||
<div><Spinner /></div> |
||||
{:else if false} |
||||
<!-- Warning, if offline --> |
||||
<!-- TODO: get connection status to nextgraph.one --> |
||||
<div class="text-left"> |
||||
<Alert color="red"> |
||||
{@html $t("pages.wallet_login_qr.offline_warning")} |
||||
</Alert> |
||||
</div> |
||||
{:else if login_method === "scan"} |
||||
<!-- Scan Mode --> |
||||
{#if scan_state === "before_start"} |
||||
<!-- Notes about QR --> |
||||
<div class="text-left text-sm"> |
||||
{@html $t("pages.wallet_login_qr.scan.description")} |
||||
</div> |
||||
{:else if scan_state === "scanning"} |
||||
<!-- Modal is down at the bottom --> |
||||
{:else if scan_state === "has_scanned"} |
||||
<!-- Scanned QR --> |
||||
<div> |
||||
<Spinner /> |
||||
</div> |
||||
<div class="mt-2"> |
||||
{$t("pages.wallet_login_qr.scan.syncing")} |
||||
</div> |
||||
{:else if scan_state === "success"} |
||||
<div class="mt-4"> |
||||
<CheckBadge class="w-full" color="green" size="3em" /> |
||||
</div> |
||||
<div class="mt-4"> |
||||
{@html $t("pages.wallet_login_qr.scan.success")} |
||||
</div> |
||||
{:else} |
||||
<!-- Error --> |
||||
{$t("pages.wallet_login_qr.scan.error", { |
||||
values: { error: $t("errors." + scan_state) }, |
||||
})} |
||||
{/if} |
||||
{:else if login_method === "gen"} |
||||
<!-- Generate QR Code to log in with another device --> |
||||
{#if gen_state == "before_start"} |
||||
<!-- Notes about QR Generation --> |
||||
<div class="text-left text-sm"> |
||||
{@html $t("pages.wallet_login_qr.gen.description")} |
||||
</div> |
||||
{:else if gen_state === "generating"} |
||||
<div> |
||||
<Spinner class="w-full" /> |
||||
</div> |
||||
{:else if gen_state === "generated"} |
||||
<!-- Notes about generated QR --> |
||||
<div class="text-left text-sm"> |
||||
{@html $t("pages.wallet_login_qr.gen.generated")} |
||||
</div> |
||||
|
||||
<!-- Generated QR Code --> |
||||
<div> |
||||
{#if generated_qr === "dummy"} |
||||
<div title={$t("pages.wallet_info.gen_qr.img_title")}> |
||||
<QrCode class="w-full h-full" /> |
||||
</div> |
||||
{:else} |
||||
<img |
||||
src={generated_qr} |
||||
title={$t("pages.wallet_info.gen_qr.img_title")} |
||||
alt="pages.wallet_info.gen_qr_alt" |
||||
class="w-full h-full" |
||||
/> |
||||
{/if} |
||||
</div> |
||||
{:else if gen_state === "success"} |
||||
<div class="mt-4"> |
||||
<CheckBadge class="w-full" color="green" size="3em" /> |
||||
</div> |
||||
<div class="mt-4"> |
||||
{@html $t("pages.wallet_login_qr.gen.success")} |
||||
</div> |
||||
{:else} |
||||
<!-- gen_state has Error --> |
||||
{$t("pages.wallet_login_qr.gen.error")} |
||||
{/if} |
||||
{/if} |
||||
|
||||
<div class="mx-auto"> |
||||
<div class="my-4"> |
||||
{#if login_method === "scan" && scan_state === "before_start"} |
||||
<!-- Open Scanner Button--> |
||||
<button |
||||
on:click={open_scanner} |
||||
class="mt-4 w-full text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<Camera |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/> |
||||
{$t("pages.wallet_login_qr.scan.button")} |
||||
</button> |
||||
{:else if login_method === "gen" && gen_state === "before_start"} |
||||
<!-- Generate QR Button --> |
||||
<button |
||||
on:click={generate_qr} |
||||
class="mt-4 w-full text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<QrCode |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/> |
||||
{$t("pages.wallet_login_qr.gen.button")} |
||||
</button> |
||||
{:else if scan_state === "success" || gen_state === "success"} |
||||
<a href="#/wallet/login"> |
||||
<button |
||||
class="mt-4 w-full text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<ArrowRightCircle |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/> |
||||
{$t("pages.wallet_login_qr.success_btn")} |
||||
</button> |
||||
</a> |
||||
{/if} |
||||
|
||||
<!-- Go Back --> |
||||
{#if scan_state !== "success" && gen_state !== "success"} |
||||
<button |
||||
on:click={() => window.history.go(-1)} |
||||
class="mt-8 w-full text-gray-500 dark:text-gray-400 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||
><ArrowLeft |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/>{$t("buttons.back")}</button |
||||
> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<!-- Scanner Open--> |
||||
<Modal |
||||
title={$t("pages.wallet_login_qr.scan.modal.title")} |
||||
placement="center" |
||||
on:hide={close_scanner_modal} |
||||
open={scan_state === "scanning"} |
||||
class="h-[85vh]" |
||||
> |
||||
<div id="scanner-div" class="h-full"> |
||||
{$t("pages.wallet_login_qr.scan.modal.loading")}... |
||||
</div> |
||||
</Modal> |
||||
</div> |
||||
</CenteredLayout> |
@ -0,0 +1,310 @@ |
||||
<script lang="ts"> |
||||
import { t } from "svelte-i18n"; |
||||
import { |
||||
type Html5QrcodeResult, |
||||
type Html5QrcodeScanner, |
||||
} from "html5-qrcode"; |
||||
import { |
||||
Alert, |
||||
Modal, |
||||
Sidebar, |
||||
SidebarGroup, |
||||
SidebarWrapper, |
||||
Spinner, |
||||
} from "flowbite-svelte"; |
||||
import { |
||||
ArrowLeft, |
||||
ArrowRightCircle, |
||||
Camera, |
||||
CheckBadge, |
||||
QrCode, |
||||
} from "svelte-heros-v2"; |
||||
import CenteredLayout from "../lib/CenteredLayout.svelte"; |
||||
import { onMount } from "svelte"; |
||||
import { push } from "svelte-spa-router"; |
||||
|
||||
let WebQRScannerClassPromise: Promise<typeof Html5QrcodeScanner>; |
||||
let html5QrcodeScanner: Html5QrcodeScanner; |
||||
async function load_qr_scanner_lib() { |
||||
// Load in browser only |
||||
if (!tauri_platform && !WebQRScannerClassPromise) { |
||||
WebQRScannerClassPromise = new Promise((resolve) => { |
||||
import("html5-qrcode").then((lib) => resolve(lib.Html5QrcodeScanner)); |
||||
}); |
||||
} |
||||
// TODO: Load alternative for native apps? |
||||
} |
||||
|
||||
let top; |
||||
const tauri_platform: string | undefined = import.meta.env.TAURI_PLATFORM; |
||||
let has_camera: boolean | "checking" = "checking"; |
||||
let login_method: "scan" | "gen" | undefined = undefined; |
||||
|
||||
let scan_state: |
||||
| "before_start" |
||||
| "scanning" |
||||
| "has_scanned" |
||||
| "success" |
||||
| Error = "before_start"; |
||||
let scanned_qr: string | undefined = undefined; |
||||
|
||||
let gen_state: |
||||
| "before_start" |
||||
| "generating" |
||||
| "generated" |
||||
| "success" |
||||
| Error = "before_start"; |
||||
let generated_qr: string | undefined = undefined; |
||||
|
||||
const check_has_camera = async () => { |
||||
if (!tauri_platform) { |
||||
// If there is a camera, go to scan mode, else gen mode. |
||||
try { |
||||
const devices = await navigator.mediaDevices.enumerateDevices(); |
||||
has_camera = |
||||
devices.filter((device) => device.kind === "videoinput").length > 0; |
||||
} catch { |
||||
has_camera = false; |
||||
} |
||||
login_method = has_camera ? "scan" : "gen"; |
||||
// Load Scanner lib, if necessary. |
||||
if (has_camera) load_qr_scanner_lib(); |
||||
} else { |
||||
// TODO: rust API @niko |
||||
} |
||||
}; |
||||
check_has_camera(); |
||||
|
||||
function on_qr_scanned(text: string) { |
||||
scan_state = "has_scanned"; |
||||
scanned_qr = text; |
||||
// TODO: API calls for synchronization @niko |
||||
// ToRemove: |
||||
setTimeout(() => { |
||||
scan_state = "success"; |
||||
}, 2_000); |
||||
} |
||||
|
||||
function clear_scanner() { |
||||
if (html5QrcodeScanner) html5QrcodeScanner.clear(); |
||||
html5QrcodeScanner = null; |
||||
} |
||||
|
||||
async function open_scanner() { |
||||
scan_state = "scanning"; |
||||
|
||||
const WebQRScanner = await WebQRScannerClassPromise; |
||||
html5QrcodeScanner = new WebQRScanner( |
||||
"scanner-div", |
||||
{ fps: 10, qrbox: { width: 300, height: 300 }, formatsToSupport: [0] }, |
||||
false |
||||
); |
||||
|
||||
html5QrcodeScanner.render((decoded_text, decoded_result) => { |
||||
// Handle scan result |
||||
on_qr_scanned(decoded_text); |
||||
clear_scanner(); |
||||
}, undefined); |
||||
|
||||
// Auto-Request camera permissions (there's no native way, unfortunately...) |
||||
setTimeout(() => { |
||||
// Auto-start by clicking button |
||||
document.getElementById("html5-qrcode-button-camera-permission")?.click(); |
||||
}, 100); |
||||
} |
||||
|
||||
function close_scanner_modal() { |
||||
clear_scanner(); |
||||
if (scanned_qr) { |
||||
scan_state = "has_scanned"; |
||||
} else { |
||||
scan_state = "before_start"; |
||||
} |
||||
} |
||||
|
||||
function generate_qr() { |
||||
gen_state = "generating"; |
||||
// TODO: @niko generated_qr = await ng.generate_export_qr(); |
||||
// ToRemove: |
||||
setTimeout(() => { |
||||
gen_state = "generated"; |
||||
generated_qr = "dummy"; |
||||
}, 1500); |
||||
setTimeout(() => { |
||||
gen_state = "success"; |
||||
}, 3500); |
||||
} |
||||
|
||||
function continue_to_login(wallet) {} |
||||
|
||||
function scrollToTop() { |
||||
top.scrollIntoView(); |
||||
} |
||||
onMount(() => scrollToTop()); |
||||
</script> |
||||
|
||||
<CenteredLayout> |
||||
<div class="container3" bind:this={top}> |
||||
<div |
||||
class="flex flex-col justify-center max-w-md mx-6 mb-20 bg-gray-60 overflow-y-auto py-4 dark:bg-gray-800" |
||||
> |
||||
<!-- Title --> |
||||
<div> |
||||
<h2 class="text-xl mb-6">{$t("pages.wallet_login_qr.title")}</h2> |
||||
</div> |
||||
|
||||
<!-- Checking, if camera is available... --> |
||||
{#if login_method === undefined} |
||||
<!-- TODO: Check connectivity here--> |
||||
<div><Spinner /></div> |
||||
{:else if false} |
||||
<!-- Warning, if offline --> |
||||
<!-- TODO: get connection status to nextgraph.one --> |
||||
<div class="text-left"> |
||||
<Alert color="red"> |
||||
{@html $t("pages.wallet_login_qr.offline_warning")} |
||||
</Alert> |
||||
</div> |
||||
{:else if login_method === "scan"} |
||||
<!-- Scan Mode --> |
||||
{#if scan_state === "before_start"} |
||||
<!-- Notes about QR --> |
||||
<div class="text-left text-sm"> |
||||
{@html $t("pages.wallet_login_qr.scan.description")} |
||||
</div> |
||||
{:else if scan_state === "scanning"} |
||||
<!-- Modal is down at the bottom --> |
||||
{:else if scan_state === "has_scanned"} |
||||
<!-- Scanned QR --> |
||||
<div> |
||||
<Spinner /> |
||||
</div> |
||||
<div class="mt-2"> |
||||
{$t("pages.wallet_login_qr.scan.syncing")} |
||||
</div> |
||||
{:else if scan_state === "success"} |
||||
<div class="mt-4"> |
||||
<CheckBadge class="w-full" color="green" size="3em" /> |
||||
</div> |
||||
<div class="mt-4"> |
||||
{@html $t("pages.wallet_login_qr.scan.success")} |
||||
</div> |
||||
{:else} |
||||
<!-- Error --> |
||||
{$t("pages.wallet_login_qr.scan.error", { |
||||
values: { error: $t("errors." + scan_state) }, |
||||
})} |
||||
{/if} |
||||
{:else if login_method === "gen"} |
||||
<!-- Generate QR Code to log in with another device --> |
||||
{#if gen_state == "before_start"} |
||||
<!-- Notes about QR Generation --> |
||||
<div class="text-left text-sm"> |
||||
{@html $t("pages.wallet_login_qr.gen.description")} |
||||
</div> |
||||
{:else if gen_state === "generating"} |
||||
<div> |
||||
<Spinner class="w-full" /> |
||||
</div> |
||||
{:else if gen_state === "generated"} |
||||
<!-- Notes about generated QR --> |
||||
<div class="text-left text-sm"> |
||||
{@html $t("pages.wallet_login_qr.gen.generated")} |
||||
</div> |
||||
|
||||
<!-- Generated QR Code --> |
||||
<div> |
||||
{#if generated_qr === "dummy"} |
||||
<div title={$t("pages.wallet_info.gen_qr.img_title")}> |
||||
<QrCode class="w-full h-full" /> |
||||
</div> |
||||
{:else} |
||||
<img |
||||
src={generated_qr} |
||||
title={$t("pages.wallet_info.gen_qr.img_title")} |
||||
alt="pages.wallet_info.gen_qr_alt" |
||||
class="w-full h-full" |
||||
/> |
||||
{/if} |
||||
</div> |
||||
{:else if gen_state === "success"} |
||||
<div class="mt-4"> |
||||
<CheckBadge class="w-full" color="green" size="3em" /> |
||||
</div> |
||||
<div class="mt-4"> |
||||
{@html $t("pages.wallet_login_qr.gen.success")} |
||||
</div> |
||||
{:else} |
||||
<!-- gen_state has Error --> |
||||
{$t("pages.wallet_login_qr.gen.error")} |
||||
{/if} |
||||
{/if} |
||||
|
||||
<div class="mx-auto"> |
||||
<div class="my-4"> |
||||
{#if login_method === "scan" && scan_state === "before_start"} |
||||
<!-- Open Scanner Button--> |
||||
<button |
||||
on:click={open_scanner} |
||||
class="mt-4 w-full text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<Camera |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/> |
||||
{$t("pages.wallet_login_qr.scan.button")} |
||||
</button> |
||||
{:else if login_method === "gen" && gen_state === "before_start"} |
||||
<!-- Generate QR Button --> |
||||
<button |
||||
on:click={generate_qr} |
||||
class="mt-4 w-full text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<QrCode |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/> |
||||
{$t("pages.wallet_login_qr.gen.button")} |
||||
</button> |
||||
{:else if scan_state === "success" || gen_state === "success"} |
||||
<a href="#/wallet/login"> |
||||
<button |
||||
class="mt-4 w-full text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2" |
||||
> |
||||
<ArrowRightCircle |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/> |
||||
{$t("pages.wallet_login_qr.success_btn")} |
||||
</button> |
||||
</a> |
||||
{/if} |
||||
|
||||
<!-- Go Back --> |
||||
{#if scan_state !== "success" && gen_state !== "success"} |
||||
<button |
||||
on:click={() => window.history.go(-1)} |
||||
class="mt-8 w-full text-gray-500 dark:text-gray-400 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||
><ArrowLeft |
||||
tabindex="-1" |
||||
class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white" |
||||
/>{$t("buttons.back")}</button |
||||
> |
||||
{/if} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<!-- Scanner Open--> |
||||
<Modal |
||||
title={$t("pages.wallet_login_qr.scan.modal.title")} |
||||
placement="center" |
||||
on:hide={close_scanner_modal} |
||||
open={scan_state === "scanning"} |
||||
class="h-[85vh]" |
||||
> |
||||
<div id="scanner-div" class="h-full"> |
||||
{$t("pages.wallet_login_qr.scan.modal.loading")}... |
||||
</div> |
||||
</Modal> |
||||
</div> |
||||
</CenteredLayout> |
Loading…
Reference in new issue