forked from NextGraph/nextgraph-rs
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
8.0 KiB
233 lines
8.0 KiB
<!--
|
|
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
|
// All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0
|
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
// at your option. All files in the project carrying such
|
|
// notice may not be copied, modified, or distributed except
|
|
// according to those terms.
|
|
-->
|
|
|
|
<script lang="ts">
|
|
import { t } from "svelte-i18n";
|
|
import { Alert, Modal, Spinner, Button } from "flowbite-svelte";
|
|
import {
|
|
ArrowLeft,
|
|
Camera,
|
|
ExclamationTriangle,
|
|
QrCode,
|
|
} from "svelte-heros-v2";
|
|
import { onDestroy, onMount } from "svelte";
|
|
import { push } from "svelte-spa-router";
|
|
import CenteredLayout from "../lib/CenteredLayout.svelte";
|
|
import { wallet_from_import, scanned_qr_code, display_error, check_has_camera } from "../store";
|
|
import ng from "../api";
|
|
|
|
// <a href="/wallet/scanqr" use:link>
|
|
|
|
let top: HTMLElement;
|
|
|
|
const set_online = () => { connected = true; };
|
|
const set_offline = () => { connected = false; };
|
|
|
|
|
|
let login_method: "scan" | "gen" | undefined = undefined;
|
|
|
|
let error;
|
|
let connected = true;
|
|
|
|
let scan_state: "before_start" | "importing" = "before_start";
|
|
|
|
let gen_state: "before_start" | "generating" | "generated" = "before_start";
|
|
let qr_code_html: string | undefined = undefined;
|
|
let rendezvous_code;
|
|
|
|
const open_scanner = () => {
|
|
push("#/wallet/scanqr");
|
|
};
|
|
|
|
|
|
|
|
async function on_qr_scanned(code) {
|
|
login_method = "scan";
|
|
scan_state = "importing";
|
|
try {
|
|
const imported_wallet = await ng.wallet_import_from_code(code);
|
|
wallet_from_import.set(imported_wallet);
|
|
// Login in with imported wallet.
|
|
push("#/wallet/login");
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
}
|
|
|
|
async function generate_qr() {
|
|
gen_state = "generating";
|
|
try {
|
|
const [qr_code_el, code] = await ng.wallet_import_rendezvous(
|
|
top.clientWidth
|
|
);
|
|
rendezvous_code = code;
|
|
qr_code_html = qr_code_el;
|
|
gen_state = "generated";
|
|
const imported_wallet = await ng.wallet_import_from_code(code);
|
|
// Login with imported wallet.
|
|
wallet_from_import.set(imported_wallet);
|
|
push("#/wallet/login");
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
}
|
|
|
|
function scrollToTop() {
|
|
top.scrollIntoView();
|
|
}
|
|
|
|
onMount(async () => {
|
|
connected = window.navigator.onLine;
|
|
window.addEventListener("offline", set_offline);
|
|
window.addEventListener("online", set_online);
|
|
// Handle return from QR scanner.
|
|
if ($scanned_qr_code) {
|
|
on_qr_scanned($scanned_qr_code);
|
|
scanned_qr_code.set("");
|
|
} else {
|
|
// Or check, if a camera exists and offer scanner or QR generator, respectively.
|
|
login_method = await check_has_camera() ? "scan" : "gen";
|
|
}
|
|
scrollToTop();
|
|
});
|
|
onDestroy(() => {
|
|
window.removeEventListener("offline", set_offline);
|
|
window.removeEventListener("online", set_online);
|
|
if (rendezvous_code) {
|
|
// TODO: Destroy
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<CenteredLayout>
|
|
<div class="container3" bind:this={top}>
|
|
<div
|
|
class="flex flex-col justify-center max-w-md mb-20 bg-gray-60 overflow-y-auto py-4 dark:bg-gray-800"
|
|
>
|
|
<!-- Title -->
|
|
<div class="mx-6">
|
|
<h2 class="text-xl mb-6">{$t("pages.wallet_login_qr.title")}</h2>
|
|
</div>
|
|
|
|
<!-- Checking, if camera is available... -->
|
|
{#if login_method === undefined}
|
|
<div><Spinner /></div>
|
|
{:else if !connected}
|
|
<!-- Warning, if offline -->
|
|
<div class="text-left mx-6">
|
|
<Alert color="red">
|
|
{@html $t("wallet_sync.offline_warning")}
|
|
</Alert>
|
|
<Alert color="blue" class="mt-4">
|
|
{@html $t("pages.wallet_login_qr.offline_advice")}
|
|
</Alert>
|
|
</div>
|
|
{:else if error}
|
|
<div class="max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
|
|
<ExclamationTriangle class="animate-bounce mt-10 h-16 w-16 mx-auto" />
|
|
|
|
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
|
|
{@html $t("errors.error_occurred", {
|
|
values: { message: display_error(error) },
|
|
})}
|
|
</p>
|
|
</div>
|
|
{:else if login_method === "scan"}
|
|
<div class="mx-6">
|
|
{#if scan_state === "before_start"}
|
|
<!-- Scan Mode -->
|
|
<!-- Notes about QR -->
|
|
<div class="text-left">
|
|
{@html $t("pages.wallet_login_qr.scan.description")}
|
|
<br />
|
|
{@html $t("wallet_sync.server_transfer_notice")}
|
|
</div>
|
|
{:else if scan_state === "importing"}
|
|
<div class="mb-4 w-full">
|
|
{@html $t("wallet_sync.importing")}
|
|
</div>
|
|
|
|
<div class="w-full"><Spinner /></div>
|
|
{/if}
|
|
</div>
|
|
{: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 mx-6">
|
|
{@html $t("pages.wallet_login_qr.gen.description")}
|
|
{@html $t("wallet_sync.no_camera_alternatives")}
|
|
<br /><br />
|
|
{@html $t("pages.wallet_login_qr.gen.letsgo")}
|
|
<br /><br />
|
|
{@html $t("wallet_sync.server_transfer_notice")}
|
|
</div>
|
|
{:else if gen_state === "generating"}
|
|
<div>
|
|
<Spinner class="w-full" />
|
|
</div>
|
|
{:else if gen_state === "generated"}
|
|
<!-- Notes about generated QR -->
|
|
<div class="text-center mb-2 mx-6">
|
|
{@html $t("pages.wallet_login_qr.gen.generated")}
|
|
</div>
|
|
|
|
<!-- Generated QR Code -->
|
|
<div class="my-4 mx-auto">
|
|
{@html qr_code_html}
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
<div class="mx-6">
|
|
<div class="mx-auto">
|
|
<div class="my-4 mx-1">
|
|
{#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
|
|
disabled={!connected}
|
|
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>
|
|
{/if}
|
|
|
|
<!-- Go Back -->
|
|
<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
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CenteredLayout>
|
|
|