user panel updates: wallet and account infos

pull/27/head
Laurin Weger 6 months ago
parent 6667fcd37f
commit 16e0baa90a
No known key found for this signature in database
GPG Key ID: 9B372BB0B792770F
  1. 4
      ng-app/src/App.svelte
  2. 57
      ng-app/src/lib/components/DeviceIcon.svelte
  3. 370
      ng-app/src/routes/AccountInfo.svelte
  4. 116
      ng-app/src/routes/User.svelte
  5. 351
      ng-app/src/routes/WalletInfo.svelte
  6. 7
      ng-app/src/routes/WalletLogin.svelte

@ -29,11 +29,13 @@
import WalletCreate from "./routes/WalletCreate.svelte"; import WalletCreate from "./routes/WalletCreate.svelte";
import Invitation from "./routes/Invitation.svelte"; import Invitation from "./routes/Invitation.svelte";
import WalletLogin from "./routes/WalletLogin.svelte"; import WalletLogin from "./routes/WalletLogin.svelte";
import WalletInfo from "./routes/WalletInfo.svelte";
import User from "./routes/User.svelte"; import User from "./routes/User.svelte";
import UserRegistered from "./routes/UserRegistered.svelte"; import UserRegistered from "./routes/UserRegistered.svelte";
import Install from "./routes/Install.svelte"; import Install from "./routes/Install.svelte";
import ng from "./api"; import ng from "./api";
import AccountInfo from "./routes/AccountInfo.svelte";
const routes = new Map(); const routes = new Map();
routes.set("/", Home); routes.set("/", Home);
@ -43,6 +45,8 @@
routes.set("/i/:invitation", Invitation); routes.set("/i/:invitation", Invitation);
routes.set("/user", User); routes.set("/user", User);
routes.set("/user/registered", UserRegistered); routes.set("/user/registered", UserRegistered);
routes.set("/wallet", WalletInfo);
routes.set("/user/account", AccountInfo);
if (import.meta.env.NG_APP_WEB) routes.set("/install", Install); if (import.meta.env.NG_APP_WEB) routes.set("/install", Install);
routes.set(/^\/did:ng(.*)/i, NURI); routes.set(/^\/did:ng(.*)/i, NURI);
routes.set("*", NotFound); routes.set("*", NotFound);

@ -0,0 +1,57 @@
<!--
// 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.
-->
<!--
@component DeviceIcon
Display an icon for a device class provided by the `device` attribute.
Pass `config` for custom attributes.
-->
<script lang="ts">
import {
Icon,
Cube,
GlobeAlt,
QuestionMarkCircle,
DevicePhoneMobile,
ComputerDesktop,
ServerStack,
Key,
CommandLine,
} from "svelte-heros-v2";
export let config = {};
export let device: string;
const mapping = {
Web: GlobeAlt,
NativeIos: DevicePhoneMobile,
NativeAndroid: DevicePhoneMobile,
NativeMacOS: ComputerDesktop,
NativeLinux: ComputerDesktop,
NativeWin: ComputerDesktop,
NativeService: ServerStack,
NodeService: ServerStack,
Verifier: ServerStack,
VerifierLocal: ServerStack,
ClientBroker: ServerStack,
WalletMaster: ServerStack,
Box: Cube,
Stick: Key, // VerifierStick
Cli: CommandLine,
};
const find = (dataClass: string) => {
return mapping[dataClass] || QuestionMarkCircle;
};
</script>
<Icon {...config} variation="outline" color="black" icon={find(device)} />

@ -0,0 +1,370 @@
<!--
// 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.
-->
<!--
@component
"Account Info" user panel sub menu.
Provides info about wallet, broker, etc. and download option.
-->
<script lang="ts">
import { link, push } from "svelte-spa-router";
import CenteredLayout from "../lib/CenteredLayout.svelte";
import { ArrowLeft, ServerStack } from "svelte-heros-v2";
import { onMount, tick } from "svelte";
import { Sidebar, SidebarGroup, SidebarWrapper } from "flowbite-svelte";
import { active_session, active_wallet, connections } from "../store";
import { default as ng } from "../api";
import DeviceIcon from "../lib/components/DeviceIcon.svelte";
let error;
let nonActiveClass =
"flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700";
let top;
async function scrollToTop() {
await tick();
top.scrollIntoView();
}
onMount(async () => {
if (!$active_session) {
push("#/");
} else {
await scrollToTop();
}
});
$: wallet_unlocked = $active_wallet?.wallet?.V0;
$: personal_site_id = wallet_unlocked?.personal_site_id;
/**
* brokers:
ZBY5Y8DTyhMo5xfo5K4FCsJHVaN3O15vKeQBwZxr76YA: [
ServerV0:
can_forward: true
can_verify: false
peer_id: Object { Ed25519PubKey: (32) [] }
server_type: Object {
Domain
Localhost: 1440 // if domain not exist
BoxPrivate // What are they for? rs type Vec<BindAddress>
Public // What are they for? rs type Vec<BindAddress>
BoxPublicDyn // What are they for? rs type Vec<BindAddress>
}
]
*/
/*
* Connections Is a record of string to those objects:
error: undefined
server_id: "ZBY5Y8DTyhMo5xfo5K4FCsJHVaN3O15vKeQBwZxr76YA"
server_ip: "ws://localhost:1440"
since: Date Fri Jul 05 2024 09:46:30 GMT+0200 (Central European Summer Time)
*/
// let connections;
/**
* bootstraps: Array [ {} ]
* cores: Array [ (2) […] ]
* id: Object { Ed25519PubKey: (32) [] }
* name: "Personal"
* private: Object { id: {}, store_type: "Private" }
* protected: Object { id: {}, store_type: "Protected" }
* public: Object { id: {}, store_type: "Public" }
* site_type: Object { Individual: (2) [] } // Some key data as well
*/
$: walletSites = wallet_unlocked?.sites;
/** Type:
* client_type: "Web"
* details: '{"browser":{"name":"Firefox","version":"127.0","appVersion":"5.0 (X11)","arch":"Linux x86_64","vendor":"","ua":"Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0"},"os":{"name":"Linux"},"platform":{"type":"desktop"},"engine":{"name":"Gecko","version":"20100101","sdk":"0.1.0-preview.1"}}'
* timestamp_install: 0
* timestamp_updated: 0
* version: "0.1.0"
*/
var device_info;
$: display_sites = Object.entries(walletSites || {})
?.map(([user_id, site]) => {
// Try to extract device details (for now only of the connected device).
// TODO: API for all devices
const devices = (!device_info ? [] : [device_info.V0]).map((device) => {
const device_details = JSON.parse(device.details);
return {
name: device.name, // TODO: API device.name is not provided
peer_id: device.id, // TODO: API device id is is not provided
version: device.version,
details: device_details,
device_name:
device.client_type === "web"
? `${device_details?.browser?.name}${" - " + device_details?.browser.arch || ""}`
: `${device_details?.os?.name_uname || device_details?.os?.name_rust || device_details?.os?.name} - ${device_details?.os?.version_uname || device_details?.os?.version_rust}`,
type: device.client_type,
};
});
return {
id: user_id,
connection: $connections[user_id], // error, server_id, server_ip, since
devices,
// @ts-ignore
name: site.name,
};
})
.filter((site) => site.id === personal_site_id);
$: display_brokers = Object.entries(wallet_unlocked?.brokers || {}).map(
// @ts-ignore
([broker_id, [broker]]) => {
return {
id: broker_id,
can_forward: broker.ServerV0.can_forward,
can_verify: broker.ServerV0.can_verify,
address:
broker.ServerV0.server_type.Domain ||
`localhost:${broker.ServerV0.server_type.Localhost}`,
last_connected: new Date("1970-01-01T00:00:00Z").toLocaleString(), // TODO: API
};
}
);
$: console.info(JSON.stringify(device_info));
$: console.debug(
"info",
device_info,
"walletSites",
walletSites,
"wallet",
$active_wallet,
"connections",
$connections,
"display_brokers",
display_brokers,
"display_sites",
display_sites
);
onMount(async () => {
ng.client_info().then((res) => {
device_info = res;
});
});
</script>
<CenteredLayout>
<div class="container3" bind:this={top}>
<div class="row mb-20">
<Sidebar {nonActiveClass}>
<SidebarWrapper
divClass="bg-gray-60 overflow-y-auto py-4 px-3 rounded dark:bg-gray-800"
>
<!-- Go Back-->
<SidebarGroup ulClass="space-y-2">
<li>
<h2 class="text-xl mb-6">Account Info</h2>
</li>
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<li
tabindex="0"
class="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={() => window.history.go(-1)}
on:click={() => window.history.go(-1)}
>
<ArrowLeft
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
<span class="ml-3">Back</span>
</li>
</SidebarGroup>
<!-- For now this will only consist of the `Personal` one-->
{#each display_sites as site}
<li
class="flex items-center p-2 text-base font-normal text-gray-900"
>
<h3
class="flex items-center mt-2 text-lg font-normal text-gray-600"
>
{site.name}
</h3>
</li>
<!-- Device Details -->
<SidebarGroup ulClass="space-y-1">
<li
class="flex items-center p-2 text-base font-normal text-gray-900"
>
<h4
class="flex items-center mt-2 text-base font-normal text-gray-600"
>
Devices
</h4>
</li>
{#each site.devices as device, index}
<li
class="flex items-center p-2 text-base font-normal text-gray-900 bg-white shadow-md rounded-lg"
class:border-b={index !== site.devices.length - 1}
>
<div>
<DeviceIcon device={device.type} />
</div>
<div
class="flex flex-col ml-3 items-start text-left overflow-auto"
>
<div>
<label class="text-gray-500">Name</label>
<span>{device.name}</span>
</div>
<div>
<label class="text-gray-500">Id</label>
<span>{device.peer_id}</span>
</div>
<div>
<label class="text-gray-500">Version</label>
<span>{device.version}</span>
</div>
<div>
<label class="text-gray-500">Device Type</label>
<span> {device.device_name}</span>
</div>
</div>
</li>
{/each}
</SidebarGroup>
<!-- Broker Details -->
<SidebarGroup ulClass="space-y-1">
<li
class="flex items-center p-2 text-base font-normal text-gray-900"
>
<h4
class="flex items-center mt-2 text-base font-normal text-gray-600"
>
Brokers
</h4>
</li>
{#if display_brokers.length > 0}
{#each Object.values(display_brokers) as broker, index}
<!--
(peerId, IP/port or domain, last time connected)
-->
<li
class="flex items-center p-2 text-base font-normal text-gray-900 bg-white shadow-md rounded-lg"
class:border-b={index !== display_brokers.length - 1}
>
<div>
<ServerStack
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<div
class="flex flex-col ml-3 items-start text-left overflow-auto"
>
<div>
<label class="text-gray-500">Address</label>
<span>{broker.address}</span>
</div>
<div>
<label class="text-gray-500">Last Connected</label>
<span>{broker.last_connected}</span>
</div>
<div>
<label class="text-gray-500">ID</label>
<span>{broker.id}</span>
</div>
<div>
<label class="text-gray-500">Can Forward?</label>
<span>{broker.can_forward}</span>
</div>
<div>
<label class="text-gray-500">Can Verify?</label>
<span>{broker.can_verify}</span>
</div>
</div>
</li>
{/each}
{:else}
<li
class="flex items-center p-2 text-base font-normal text-gray-900"
>
<span class="ml-3">No brokers connected</span>
</li>
{/if}
</SidebarGroup>
{/each}
</SidebarWrapper>
</Sidebar>
</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">
The user is already registered with the selected broker.<br /> Try logging
in instead
</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"
>
Login
</button>
</a>
{:else}
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
An error occurred:<br />{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"
>
Go back to homepage
</button>
</a>
{/if}
</div>
{/if}
</div>
</CenteredLayout>
<style>
li.clickable {
cursor: pointer;
}
.site-cnx-details {
@apply mt-0 !important;
}
</style>

@ -8,6 +8,13 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
--> -->
<!--
@component
"User Panel" page.
Provides wallet, logout, offline/online switch, and other user actions.
-->
<script> <script>
// @ts-nocheck // @ts-nocheck
@ -88,30 +95,6 @@
push("#/wallet/login"); push("#/wallet/login");
} }
let downloading = false;
let wallet_file_ready = false;
let download_link = false;
let download_error = false;
async function download_wallet() {
try {
downloading = true;
let file = await ng.wallet_get_file($active_wallet.id);
// @ts-ignore
wallet_file_ready = "wallet-" + $active_wallet.id + ".ngw";
if (!tauri_platform) {
const blob = new Blob([file], {
type: "application/octet-stream",
});
// @ts-ignore
download_link = URL.createObjectURL(blob);
} else {
download_link = true;
}
} catch (e) {
download_error = e;
}
}
$: personal_site = $active_wallet?.wallet?.V0.personal_site_id; $: personal_site = $active_wallet?.wallet?.V0.personal_site_id;
$: personal_site_id = $active_wallet?.wallet?.V0.personal_site; $: personal_site_id = $active_wallet?.wallet?.V0.personal_site;
@ -203,78 +186,13 @@
/> />
<span class="ml-3">Switch wallet</span> <span class="ml-3">Switch wallet</span>
</li> --> </li> -->
{#if !downloading}
<li <SidebarItem
tabindex="0" label="Settings"
role="menuitem" href="#/user/settings"
class="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="p-2 opacity-50 pointer-events-none"
on:keypress={download_wallet} disabled
on:click={download_wallet}
>
<DocumentArrowDown
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
<span class="ml-3">Download wallet file</span>
</li>
{:else if download_error}
<li
tabindex="-1"
class="flex items-center p-2 text-base font-normal text-red-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<NoSymbol
tabindex="-1"
class="w-7 h-7 text-red-700 transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
<span class="ml-3 text-left"
>Download failed:<br /> {download_error}</span
>
</li>
{:else if !wallet_file_ready}
<li
tabindex="-1"
class="flex items-center p-2 text-base font-normal text-blue-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<DocumentArrowDown
tabindex="-1"
class="w-7 h-7 text-blue-700 transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
<span class="ml-3 text-left">Download in progress...</span>
</li>
{:else if download_link === true}
<li
tabindex="-1"
class="flex p-2 text-sm text-left break-all font-normal text-blue-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<span
>You will find the file named "{wallet_file_ready}" <br />in
your Downloads folder</span
>
</li>
{:else}
<li
tabindex="-1"
class="flex items-center text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<a
href={download_link}
target="_blank"
download={wallet_file_ready}
>
<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"
> >
<DocumentArrowDown
tabindex="-1"
class="w-14 h-14 transition duration-75 dark:text-white dark:group-hover:text-white"
/>
Click here to download the wallet file
</button>
</a>
</li>
{/if}
<SidebarItem label="Settings" href="#/user/settings" class="p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Cog6Tooth <Cog6Tooth
tabindex="-1" tabindex="-1"
@ -290,7 +208,11 @@
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Admin" href="#/user/admin" class="p-2"> <SidebarItem
label="Admin"
href="#/user/admin"
class="p-2 opacity-50 pointer-events-none"
>
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<Key <Key
tabindex="-1" tabindex="-1"
@ -298,7 +220,7 @@
/> />
</svelte:fragment> </svelte:fragment>
</SidebarItem> </SidebarItem>
<SidebarItem label="Accounts" href="#/user/accounts" class="p-2"> <SidebarItem label="Accounts" href="#/user/account" class="p-2">
<svelte:fragment slot="icon"> <svelte:fragment slot="icon">
<User <User
tabindex="-1" tabindex="-1"

@ -0,0 +1,351 @@
<!--
// 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.
-->
<!--
@component
"Wallet Info" user panel sub menu.
Provides info about wallet, broker, etc. and download option.
-->
<script>
import { Modal } from "flowbite-svelte";
import { link, push } from "svelte-spa-router";
import CenteredLayout from "../lib/CenteredLayout.svelte";
import {
ArrowLeft,
Trash,
DocumentArrowDown,
NoSymbol,
QrCode,
Link,
ArrowDownOnSquare,
} from "svelte-heros-v2";
import { onMount, tick } from "svelte";
import { Sidebar, SidebarGroup, SidebarWrapper } from "flowbite-svelte";
import { close_active_wallet, active_session, active_wallet } from "../store";
import { default as ng } from "../api";
let tauri_platform = import.meta.env.TAURI_PLATFORM;
let error;
let nonActiveClass =
"flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700";
let top;
async function scrollToTop() {
await tick();
top.scrollIntoView();
}
onMount(async () => {
if (!$active_session) {
push("#/");
} else {
await scrollToTop();
}
});
let downloading = false;
let wallet_file_ready = false;
let download_link = false;
let download_error = false;
async function download_wallet() {
try {
downloading = true;
let file = await ng.wallet_get_file($active_wallet.id);
// @ts-ignore
wallet_file_ready = "wallet-" + $active_wallet.id + ".ngw";
if (!tauri_platform) {
const blob = new Blob([file], {
type: "application/octet-stream",
});
// @ts-ignore
download_link = URL.createObjectURL(blob);
} else {
download_link = true;
}
} catch (e) {
download_error = e;
}
}
let wallet_remove_modal_open = false;
async function remove_wallet_clicked() {
wallet_remove_modal_open = true;
}
// TODO: WTF, this does not close the modal???
const close_modal = () => {
wallet_remove_modal_open = false;
};
async function remove_wallet_confirmed() {
if (!active_wallet) return;
// TODO: Wait for implementation
// ng.wallet_remove($active_wallet.id);
close_active_wallet();
}
</script>
<CenteredLayout>
<div class="container3" bind:this={top}>
<div class="row mb-20">
<Sidebar {nonActiveClass}>
<SidebarWrapper
divClass="bg-gray-60 overflow-y-auto py-4 px-3 rounded dark:bg-gray-800"
>
<SidebarGroup ulClass="space-y-2">
<li>
<h2 class="text-xl mb-6">Wallet Info</h2>
</li>
<!-- Go Back -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<li
tabindex="0"
class="text-left 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={() => window.history.go(-1)}
on:click={() => window.history.go(-1)}
>
<ArrowLeft
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
<span class="ml-3">Back</span>
</li>
<!-- Download Wallet -->
{#if !downloading}
<li
tabindex="0"
role="menuitem"
class="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={download_wallet}
on:click={download_wallet}
>
<div>
<DocumentArrowDown
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<span class="ml-3">Download Wallet File</span>
</li>
{:else if download_error}
<li
tabindex="-1"
class="flex items-center p-2 text-base font-normal text-red-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<div>
<NoSymbol
tabindex="-1"
class="w-7 h-7 text-red-700 transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<span class="ml-3 text-left"
>Download failed:<br /> {download_error}</span
>
</li>
{:else if !wallet_file_ready}
<li
tabindex="-1"
class="flex items-center p-2 text-base font-normal text-blue-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<div>
<DocumentArrowDown
tabindex="-1"
class="w-7 h-7 text-blue-700 transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<span class="ml-3 text-left">Download in progress...</span>
</li>
{:else if download_link === true}
<li
tabindex="-1"
class="flex p-2 text-sm text-left break-all font-normal text-blue-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<span
>You will find the file named "{wallet_file_ready}" <br />in
your Downloads folder</span
>
</li>
{:else}
<li
tabindex="-1"
class="flex items-center text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
>
<a
href={download_link}
target="_blank"
download={wallet_file_ready}
>
<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"
>
<div>
<DocumentArrowDown
tabindex="-1"
class="w-14 h-14 transition duration-75 dark:text-white dark:group-hover:text-white"
/>
</div>
Click here to download the wallet file
</button>
</a>
</li>
{/if}
<!-- Remove Wallet -->
<li
tabindex="0"
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"
on:keypress={remove_wallet_clicked}
on:click={remove_wallet_clicked}
>
<div>
<Trash
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<span class="ml-3">Remove Wallet from Device</span>
<Modal
autoclose
outsideclose
bind:open={wallet_remove_modal_open}
title="Remove Wallet"
>
<p class="mt-4">
Are you sure you want to remove this wallet from your device?
</p>
<div class="mt-4 flex justify-end">
<button on:click={close_modal}> Cancel </button>
<button
class="mr-2 bg-primary-700 text-white"
on:click={remove_wallet_confirmed}
>
Remove
</button>
</div>
</Modal>
</li>
<!-- TODO: Show QR-Code -->
{#if false}
<li
tabindex="0"
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"
>
<div>
<QrCode
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<span class="ml-3">Wallet QR-Code</span>
<Modal autoclose outsideclose title="My Wallet QR-Code"
>Use this QR-Code to log in with your wallet on new devices.
</Modal>
</li>
{/if}
<!-- TODO: Copy Wallet Link -->
{#if false}
<li
tabindex="0"
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"
>
<div>
<Link
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
</div>
<span class="ml-3">Copy Wallet Link</span>
</li>
{/if}
<!-- TODO: Save to Device -->
{#if false}
<li
tabindex="0"
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"
>
<!-- TODO: Same as with the trash icon, this is not same-sized as the others. -->
<ArrowDownOnSquare
tabindex="-1"
class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
/>
<span class="ml-3">Save to Device for Future Logins</span>
</li>
{/if}
</SidebarGroup>
</SidebarWrapper>
</Sidebar>
</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">
The user is already registered with the selected broker.<br /> Try logging
in instead
</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"
>
Login
</button>
</a>
{:else}
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
An error occurred:<br />{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"
>
Go back to homepage
</button>
</a>
{/if}
</div>
{/if}
</div>
</CenteredLayout>
<style>
li.clickable {
cursor: pointer;
}
.site-cnx-details {
@apply mt-0 !important;
}
</style>

@ -9,6 +9,13 @@
// according to those terms. // according to those terms.
--> -->
<!--
@component
"Select a wallet to login with" page.
This page is usually the first page the user sees when they visit the app.
It allows the user to select a wallet to login with, create, or import a wallet.
-->
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy, tick } from "svelte"; import { onMount, onDestroy, tick } from "svelte";
import { link, push } from "svelte-spa-router"; import { link, push } from "svelte-spa-router";

Loading…
Cancel
Save