final frontend parts

pull/33/head
Laurin Weger 2 months ago
parent bb6d118f34
commit 18101e4fae
No known key found for this signature in database
GPG Key ID: 9B372BB0B792770F
  1. 8
      ng-app/src/lib/components/CopyToClipboard.svelte
  2. 17
      ng-app/src/locales/en.json
  3. 2
      ng-app/src/routes/ScanQR.svelte
  4. 152
      ng-app/src/routes/WalletInfo.svelte
  5. 56
      ng-app/src/routes/WalletLogin.svelte
  6. 27
      ng-app/src/routes/WalletLoginQr.svelte
  7. 54
      ng-app/src/routes/WalletLoginTextCode.svelte

@ -11,7 +11,8 @@
<script lang="ts">
export let value: string = "";
export let id: string;
export let id: string | undefined = undefined;
export let rows: number = 3;
let has_success: boolean = false;
@ -37,10 +38,11 @@
<div class="relative">
<textarea
{id}
rows="3"
{rows}
style="resize: none;"
{value}
class="col-span-6 pr-11 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
class="col-span-6 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
class:pr-11={!tauri_platform}
disabled
readonly
/>

@ -29,19 +29,20 @@
"remove_wallet_modal.confirm": "Are you sure you want to remove this wallet from your device?",
"create_text_code": "Generate TextCode to export",
"scan_qr.title": "Export by scanning QR-Code",
"scan_qr.notes": "Scan the QR-Code on the device that you want to transfer your wallet to.<br />.",
"scan_qr.notes": "Scan the QR-Code on the device that you want to transfer your wallet to.",
"scan_qr.scan_btn": "Scan QR Code ",
"scan_qr.scanner.title": "Scan your QR Code",
"scan_qr.scanner.loading": "Loading scanner",
"scan_qr.syncing": "Synchronizing wallet",
"scan_qr.scan_successful": "Success!<br />Your wallet has been synchronized to the new device.",
"scan_qr.scan_successful": "Success!<br />Your wallet has been transferred to the new device.",
"gen_qr.title": "Export with generated QR-Code",
"gen_qr.notes": "Use the following QR-Code to scan with the device that you want to transfer your wallet to.",
"gen_qr.img_title": "Your Export QR Code to Scan",
"gen_qr.img_alt": "Your Export QR Code to Scan",
"gen_qr.gen_button": "Display QR Code",
"gen_qr.gen_button": "Show QR Code",
"gen_text_code.title": "Export with TextCode",
"gen_text_code.gen_btn": "Generate TextCode"
"gen_text_code.gen_btn": "Generate TextCode",
"gen_text_code.label": "Your TextCode:"
},
"settings": {
"title": "Settings"
@ -241,7 +242,8 @@
"wallet_login": {
"select_wallet": "Select a wallet to login with",
"from_import.title": "Your wallet has been transferred",
"from_import.description": "Your wallet has been transferred!<br />To finish the import, please log in.",
"from_import.description": "Your wallet has been received:",
"from_import.instruction": "To finish the import, please log in.",
"with_another_wallet": "Log in with another wallet",
"import_wallet": "Import your wallet",
"import_file": "Import a Wallet File",
@ -256,7 +258,7 @@
"scan.button": "Scan QR-Code",
"scan.modal.title": "Scan Wallet QR-Code",
"gen.button": "Generate",
"gen.description": "To import your wallet from another device, you have to display a QR-Code here on this device, and then scan it with your other device. On the other device, go to<br /><span class=\"path\">User Panel > Wallet > Scan QR</span> to export.",
"gen.description": "To import your wallet from another device, you have to generate a QR-Code here on this device, and then scan it with your other device. On the other device, go to<br /><span class=\"path\">User Panel > Wallet > Scan QR</span> to export.",
"gen.generated": "Scan this QR-Code from the the other device.",
"success_btn": "Continue to Login"
},
@ -349,7 +351,8 @@
"InvalidTarget": "Cannot resolve target.",
"ExportWalletTimeOut": "Export of wallet has expired.",
"ConnectionError": "Could not connect to the server.",
"IncompatibleQrCode": "You scanned a NextGraph QR-Code that is of the wrong type"
"IncompatibleQrCode": "You scanned a NextGraph QR-Code that is of the wrong type.",
"NotARendezVous": "You scanned an invalid QR-Code."
},
"connectivity": {
"stopped": "Stopped",

@ -82,7 +82,7 @@
});
</script>
<div class="text-center">
<div class="text-center max-w-4xl mx-auto">
<div>
<h2 class="text-xl mb-6">{$t("pages.scan_qr.scanning")}</h2>
</div>

@ -28,6 +28,7 @@
Link,
Camera,
CheckBadge,
ExclamationTriangle,
} from "svelte-heros-v2";
import { onDestroy, onMount, tick } from "svelte";
import { Sidebar, SidebarGroup, SidebarWrapper } from "flowbite-svelte";
@ -43,8 +44,7 @@
} from "../store";
import { default as ng } from "../api";
let WebQRScannerClassPromise: Promise<typeof Html5QrcodeScanner>;
import CopyToClipboard from "../lib/components/CopyToClipboard.svelte";
let tauri_platform = import.meta.env.TAURI_PLATFORM;
let error;
@ -55,12 +55,13 @@
let sub_menu: "scan_qr" | "generate_qr" | "text_code" | null = null;
let generation_state: "loading" | "generated" | null = null;
let generated_qr: string | undefined = undefined;
let generation_state: "before_start" | "loading" | "generated" =
"before_start";
let generated_qr: string | undefined = undefined;
let generated_text_code: string | null = null;
let scanner_state: null | "scanned" | "success" = null;
let scanner_state: "before_start" | "scanned" | "success" = "before_start";
async function scrollToTop() {
await tick();
@ -85,19 +86,19 @@
async function open_gen_menu() {
sub_menu = "generate_qr";
generation_state = null;
generation_state = "before_start";
}
function open_textcode_menu() {
sub_menu = "text_code";
scanner_state = null;
scanner_state = "before_start";
}
async function generate_qr_code() {
generation_state = "loading";
generated_qr = await ng.wallet_export_get_qrcode(
$active_session.session_id,
container.clientWidth
Math.ceil(container.clientWidth * 0.9)
);
generation_state = "generated";
}
@ -129,7 +130,8 @@
sub_menu = null;
generated_qr = undefined;
generated_text_code = null;
generation_state = null;
generation_state = "before_start";
scanner_state = "before_start";
}
let downloading = false;
@ -399,7 +401,7 @@
<li
tabindex="0"
role="menuitem"
class="text-left flex items-center p-2 text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
class="mb-2 text-left flex items-center p-2 text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
on:keypress={to_main_menu}
on:click={to_main_menu}
>
@ -410,21 +412,23 @@
<span class="ml-3">{$t("buttons.back")}</span>
</li>
<!-- NOTES ABOUT QR-->
<li class="text-left">
{@html $t("pages.wallet_info.scan_qr.notes")}
</li>
<!-- Warning if offline -->
{#if !$online}
{#if scanner_state === "before_start"}
<!-- NOTES ABOUT QR-->
<li class="text-left">
<Alert color="red">
{@html $t("wallet_sync.offline_warning")}
</Alert>
{@html $t("pages.wallet_info.scan_qr.notes")}
<br />
{@html $t("wallet_sync.server_transfer_notice")}
</li>
{/if}
{#if !scanner_state}
<!-- Warning if offline -->
{#if !$online}
<li class="text-left">
<Alert color="red">
{@html $t("wallet_sync.offline_warning")}
</Alert>
</li>
{/if}
<Button
on:click={open_scanner}
disabled={false || !$online}
@ -493,17 +497,15 @@
</li>
{/if}
{#if !generated_qr || generation_state === "loading"}
{#if generation_state === "before_start"}
<Button
on:click={generate_qr_code}
disabled={generation_state === "loading" || !$online}
class="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"
>
{#if generation_state === "loading"}
<Spinner class="mr-2" size="6" />
{/if}
{$t("pages.wallet_info.gen_qr.gen_button")}
</Button>
{:else if generation_state === "loading"}
<Spinner class="mx-auto" size="6" />
{:else}
<!-- QR Code -->
<div class="w-full">
@ -534,8 +536,8 @@
<span class="ml-3">{$t("buttons.back")}</span>
</li>
<!-- Warning to better use QR codes or wallet downloads -->
<div class="text-left">
<!-- Warning to prefer QR codes or wallet downloads -->
<div class="text-left my-4">
<Alert color="yellow">
{@html $t("wallet_sync.textcode.usage_warning")}
</Alert>
@ -543,35 +545,32 @@
<!-- Warning if offline -->
{#if !$online}
<li class="text-left">
<li class="text-left my-4">
<Alert color="red">
{@html $t("wallet_sync.offline_warning")}
</Alert>
</li>
{/if}
{#if generation_state !== "generated"}
<Button
on:click={generate_text_code}
disabled={generation_state === "loading" || !$online}
class="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"
>
{#if generation_state === "loading"}
<Spinner class="mr-2" size="6" />
{/if}
{$t("pages.wallet_info.gen_text_code.gen_btn")}
</Button>
{:else}
<!-- TextCode Code -->
<div>
<textarea
rows="6"
value={generated_text_code}
class="col-span-6 pr-11 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
readonly
/>
</div>
{/if}
<div class="mt-4">
{#if generation_state === "before_start"}
<Button
on:click={generate_text_code}
disabled={!$online}
class="my-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"
>
{$t("pages.wallet_info.gen_text_code.gen_btn")}
</Button>
{:else if generation_state == "loading"}
<Spinner class="mx-auto" size="6" />
{:else}
<!-- TextCode Code -->
<label>{$t("pages.wallet_info.gen_text_code.label")}</label>
<div>
<CopyToClipboard rows={8} value={generated_text_code} />
</div>
{/if}
</div>
</SidebarGroup>
{/if}
</SidebarWrapper>
@ -579,48 +578,13 @@
</div>
{#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
<svg
class="animate-bounce mt-10 h-16 w-16 mx-auto"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
/>
</svg>
{#if error == "AlreadyExists"}
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
{@html $t("errors.AlreadyExists")}
</p>
<a use:link href="/">
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
{$t("buttons.login")}
</button>
</a>
{:else}
<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>
<a use:link href="/">
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
{$t("buttons.back_to_homepage")}
</button>
</a>
{/if}
<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>
{/if}
</div>

@ -36,7 +36,7 @@
display_error,
wallet_from_import,
} from "../store";
import { CheckBadge, QrCode } from "svelte-heros-v2";
import { CheckBadge, ExclamationTriangle, QrCode } from "svelte-heros-v2";
let tauri_platform = import.meta.env.TAURI_PLATFORM;
@ -113,8 +113,8 @@
}
function start_login_from_import() {
// Login button clicked, `wallet` is was set onMount.
// Unset, to show login screen.
// Login button was clicked and `wallet` was set in `onMount`.
// Unset variable from store, to show login screen.
wallet_from_import.set(null);
}
@ -218,21 +218,7 @@
<CenteredLayout displayFooter={!wallet}>
{#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
<svg
class="animate-bounce mt-10 h-16 w-16 mx-auto"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
/>
</svg>
<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", {
@ -260,35 +246,40 @@
</h2>
</div>
<CheckBadge class="w-full h-6" />
<span class="text-green-800">
<CheckBadge class="w-full mt-4" size="3em" />
<div>
{@html $t("pages.wallet_login.from_import.description")}
</div>
<div class="mt-4">
{@html $t("pages.wallet_login.from_import.description")}
</div>
</span>
<!-- Show wallet security image and phrase. -->
<!--
<div
class="wallet-box"
class="wallet-box mt-4 mx-auto"
role="button"
tabindex="0"
title={$wallet_from_import[0]}
on:click={start_login_from_import}
on:keypress={start_login_from_import}
>
<span class="securitytxt"
>{$wallet_from_import[1].wallet.V0.content.security_txt}
>{$wallet_from_import.V0.content.security_txt}
</span>
<img
alt={$wallet_from_import[1].wallet.V0.content.security_txt}
alt={$wallet_from_import.V0.content.security_txt}
class="securityimg"
src={convert_img_to_url(
$wallet_from_import[1].wallet.V0.content.security_img
)}
src={convert_img_to_url($wallet_from_import.V0.content.security_img)}
/>
</div>
-->
<!-- Login to finish import instructions-->
<div class="my-4">
{@html $t("pages.wallet_login.from_import.instruction")}
</div>
<div>
<button
class="mt-1 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center justify-center dark:focus:ring-primary-100/55 mb-2"
class="mt-4 text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center justify-center dark:focus:ring-primary-100/55 mb-2"
on:click={start_login_from_import}
>
{$t("buttons.login")}
@ -313,7 +304,6 @@
class="wallet-box"
role="button"
tabindex="0"
title={wallet_entry[0]}
on:click={() => {
select(wallet_entry[0]);
}}

@ -1,7 +1,12 @@
<script lang="ts">
import { t } from "svelte-i18n";
import { Alert, Modal, Spinner } from "flowbite-svelte";
import { ArrowLeft, Camera, QrCode } from "svelte-heros-v2";
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";
@ -41,7 +46,7 @@
} catch {
has_camera = false;
}
has_camera = false;
login_method = has_camera ? "scan" : "gen";
} else {
// TODO: There does not seem to be an API for checking, if the native device
@ -68,7 +73,7 @@
gen_state = "generating";
try {
const [qr_code_el, code] = await ng.wallet_import_rendezvous(
top.clientWidth
Math.ceil(top.clientWidth * 0.9)
);
rendezvous_code = code;
qr_code_html = qr_code_el;
@ -127,11 +132,15 @@
</Alert>
</div>
{:else if error}
<Alert color="red">
{@html $t("wallet_sync.error", {
values: { error: display_error(error) },
})}
</Alert>
<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"}
{#if scan_state === "before_start"}
<!-- Scan Mode -->
@ -168,7 +177,7 @@
</div>
<!-- Generated QR Code -->
<div>
<div class="my-4 my-auto">
{@html qr_code_html}
</div>
{/if}

@ -4,31 +4,33 @@
import {
ArrowLeft,
ArrowRightCircle,
Camera,
CheckBadge,
QrCode,
CheckCircle,
ExclamationTriangle,
} from "svelte-heros-v2";
import CenteredLayout from "../lib/CenteredLayout.svelte";
import { onMount } from "svelte";
import { display_error, wallet_from_import } from "../store";
import { push } from "svelte-spa-router";
import { online, scanned_qr_code } from "../store";
import ng from "../api";
let top;
let gen_state:
| "before_start"
| "generating"
| "generated"
| "success"
| Error = "before_start";
let error;
let state: "importing" | null = null;
let textcode: string | undefined = undefined;
// TODO: Check connectivity to sync service.
let connected = true;
const textcode_submit = () => {
scanned_qr_code.set(textcode);
window.history.go(-1);
const textcode_submit = async () => {
state = "importing";
try {
const imported_wallet = await ng.wallet_import_from_code(textcode);
wallet_from_import.set(imported_wallet);
// Login in with imported wallet.
push("#/wallet/login");
} catch (e) {
error = e;
}
};
</script>
@ -42,7 +44,7 @@
<h2 class="text-xl mb-6">{$t("pages.wallet_login_textcode.title")}</h2>
</div>
<div class="text-left">
<div class="text-left my-4">
<Alert color="yellow">
{@html $t("wallet_sync.textcode.usage_warning")}
</Alert>
@ -58,7 +60,7 @@
{/if}
<!-- Notes about TextCode entering -->
<div class="text-left text-sm mb-4">
<div class="text-left text-sm mt-4">
{@html $t("pages.wallet_login_textcode.description")}
<br />
{@html $t("wallet_sync.server_transfer_notice")}
@ -68,9 +70,24 @@
<textarea
rows="6"
bind:value={textcode}
class="col-span-6 pr-11 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
disabled={state === "importing"}
class="my-4 col-span-6 pr-11 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
{#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 state === "importing"}
<Spinner class="mx-auto" />
{/if}
<div class="mx-auto">
<!-- Submit Button-->
<div class="my-4 mx-1">
@ -78,8 +95,9 @@
class="mt-4 w-full text-white bg-primary-700 disabled:bg-primary-700/50 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"
on:click={textcode_submit}
disabled={!connected || !textcode}
class:hidden={state === "importing" || error}
>
<ArrowRightCircle
<CheckCircle
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"
/>

Loading…
Cancel
Save