create wallet UX

pull/19/head
Niko 1 year ago
parent beed2b6bf5
commit 9e4c8d8108
  1. 16
      ng-app/src-tauri/src/lib.rs
  2. 2
      ng-app/src-tauri/tauri.conf.json
  3. 5
      ng-app/src/App.svelte
  4. 5
      ng-app/src/api.ts
  5. 4
      ng-app/src/assets/EU.svg
  6. 84
      ng-app/src/lib/Home.svelte
  7. 32
      ng-app/src/routes/Grid.svelte
  8. 79
      ng-app/src/routes/Home.svelte
  9. 204
      ng-app/src/routes/Install.svelte
  10. 2
      ng-app/src/routes/Test.svelte
  11. 867
      ng-app/src/routes/WalletCreate.svelte
  12. 92
      ng-app/src/styles.css
  13. 9
      ng-app/src/wallet_emojis.ts
  14. 4
      ng-sdk-js/src/lib.rs
  15. 34
      ng-wallet/src/lib.rs
  16. 11
      ng-wallet/src/types.rs
  17. 8
      ngone/src/main.rs
  18. 42
      ngone/web/index.html
  19. 13
      ngone/web/package.json
  20. 899
      ngone/web/pnpm-lock.yaml
  21. 13
      ngone/web/postcss.config.cjs
  22. 62
      ngone/web/src/App.svelte
  23. 25
      ngone/web/src/Greeting.svelte
  24. 91
      ngone/web/src/app.css
  25. 4
      ngone/web/src/app.postcss
  26. 4
      ngone/web/src/assets/EU.svg
  27. 16
      ngone/web/src/assets/nextgraph.svg
  28. 1
      ngone/web/src/assets/svelte.svg
  29. 10
      ngone/web/src/lib/Counter.svelte
  30. 3
      ngone/web/src/main.js
  31. 55
      ngone/web/src/routes/Home.svelte
  32. 20
      ngone/web/src/routes/NotFound.svelte
  33. 247
      ngone/web/src/routes/WalletCreate.svelte
  34. 23
      ngone/web/tailwind.config.cjs
  35. 33
      ngone/web/vite.config.js

@ -60,9 +60,19 @@ async fn wallet_open_wallet_with_pazzle(
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn wallet_create_wallet(params: CreateWalletV0) -> Result<CreateWalletResultV0, String> { async fn wallet_create_wallet(mut params: CreateWalletV0) -> Result<CreateWalletResultV0, String> {
log!("wallet_create_wallet from rust {:?}", params); //log!("wallet_create_wallet from rust {:?}", params);
create_wallet_v0(params).await.map_err(|e| e.to_string()) params.result_with_wallet_file = false;
let local_save = params.local_save;
let res = create_wallet_v0(params).await.map_err(|e| e.to_string());
if local_save {
// TODO save in user store
} else {
// TODO save wallet file to Downloads folder
}
res
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]

@ -42,7 +42,7 @@
"resizable": true, "resizable": true,
"title": "NextGraph", "title": "NextGraph",
"width": 800, "width": 800,
"height": 900 "height": 980
} }
] ]
} }

@ -20,6 +20,9 @@
import Grid from "./routes/Grid.svelte"; import Grid from "./routes/Grid.svelte";
import URI from "./routes/URI.svelte"; import URI from "./routes/URI.svelte";
import NotFound from "./routes/NotFound.svelte"; import NotFound from "./routes/NotFound.svelte";
import WalletCreate from "./routes/WalletCreate.svelte";
import Install from "./routes/Install.svelte";
import ng from "./api"; import ng from "./api";
ng.test(); ng.test();
@ -28,6 +31,8 @@
routes.set("/", Home); routes.set("/", Home);
routes.set("/test", Test); routes.set("/test", Test);
routes.set("/grid", Grid); routes.set("/grid", Grid);
routes.set("/wallet/create", WalletCreate);
if (import.meta.env.NG_APP_WEB) routes.set("/install", Install);
routes.set(/^\/ng(.*)/i, URI); routes.set(/^\/ng(.*)/i, URI);
routes.set("*", NotFound); routes.set("*", NotFound);
</script> </script>

@ -53,6 +53,11 @@ const handler = {
res['File'].V0.content = Uint8Array.from(res['File'].V0.content); res['File'].V0.content = Uint8Array.from(res['File'].V0.content);
res['File'].V0.metadata = Uint8Array.from(res['File'].V0.metadata); res['File'].V0.metadata = Uint8Array.from(res['File'].V0.metadata);
return res return res
} else if (path[0] === "wallet_create_wallet") {
let params = args[0];
params.result_with_wallet_file = false;
params.security_img = Array.from(new Uint8Array(params.security_img));
return await tauri.invoke(path[0],{params})
} }
else { else {
let arg = {}; let arg = {};

@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg viewBox="0 0 810 540" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" ><desc>European flag</desc>
<defs><g id="s"><g id="c"><path id="t" d="M0,0v1h0.5z" transform="translate(0,-1)rotate(18)"/><use xlink:href="#t" transform="scale(-1,1)"/></g><g id="a"><use xlink:href="#c" transform="rotate(72)"/><use xlink:href="#c" transform="rotate(144)"/></g><use xlink:href="#a" transform="scale(-1,1)"/></g></defs>
<rect fill="#039" width="810" height="540"/><g fill="#fc0" transform="scale(30)translate(13.5,9)"><use xlink:href="#s" y="-6"/><use xlink:href="#s" y="6"/><g id="l"><use xlink:href="#s" x="-6"/><use xlink:href="#s" transform="rotate(150)translate(0,6)rotate(66)"/><use xlink:href="#s" transform="rotate(120)translate(0,6)rotate(24)"/><use xlink:href="#s" transform="rotate(60)translate(0,6)rotate(12)"/><use xlink:href="#s" transform="rotate(30)translate(0,6)rotate(42)"/></g><use xlink:href="#l" transform="scale(-1,1)"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,84 @@
<!--
// Copyright (c) 2022-2023 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>
import { Button } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import Logo from "../assets/nextgraph.svg?component";
import { onMount } from "svelte";
export let display_login_create = false;
</script>
<main class="container3">
<div class="row">
<Logo class="logo block h-40" alt="NextGraph Logo" />
</div>
<h1 class="text-2xl mb-10">Welcome to NextGraph</h1>
{#if display_login_create}
<p class="max-w-sm">
We could not find a wallet saved on this device.<br /> If you already have
a wallet, select "Log in", otherwise, select "Create Wallet" here below
</p>
<div class="row mt-10">
<a href="/wallet/create" use:link>
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
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="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"
/>
</svg>
Create wallet
</button>
</a>
</div>
<div class="row mt-10">
<a href="/wallet/login" use:link>
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="currentColor"
stroke="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
/>
</svg>
Log in
</button>
</a>
</div>
{/if}
</main>

@ -51,12 +51,13 @@
let img = await ng.doc_get_file_from_store_with_object_ref("ng:", ref); let img = await ng.doc_get_file_from_store_with_object_ref("ng:", ref);
let c = { let c = {
security_img: Array.from(img["File"].V0.content), security_img: img["File"].V0.content,
security_txt: " know yourself ", security_txt: " know yourself ",
pin: [5, 2, 9, 1], pin: [5, 2, 9, 1],
pazzle_length: 9, pazzle_length: 9,
send_bootstrap: undefined, send_bootstrap: undefined,
send_wallet: false, send_wallet: false,
result_with_wallet_file: true,
peer_id: { peer_id: {
Ed25519PubKey: [ Ed25519PubKey: [
119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167, 119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167,
@ -260,4 +261,33 @@
{:else if step == "end"}{/if} {:else if step == "end"}{/if}
<style> <style>
.pazzleline {
margin-right: auto;
margin-left: auto;
}
.pin {
cursor: pointer;
text-align: center;
}
.sel {
position: relative;
top: -56%;
font-size: 100px;
left: 30%;
font-weight: 700;
}
.sel-emoji {
overflow: hidden;
}
.emoji {
cursor: pointer;
/* padding: 0;
margin: 0;
border: 0;
box-shadow: none; */
}
</style> </style>

@ -12,63 +12,26 @@
<script> <script>
import { Button } from "flowbite-svelte"; import { Button } from "flowbite-svelte";
import { link } from "svelte-spa-router"; import { link } from "svelte-spa-router";
import Logo from "../assets/nextgraph.svg?component"; import Home from "../lib/Home.svelte";
</script> import { onMount } from "svelte";
let display_login_create = false;
<main class="container2"> async function bootstrap() {
<div class="row"> let bs;
<Logo class="logo block h-40" alt="NextGraph Logo" /> try {
</div> bs = localStorage.getItem("bootstrap");
<h1 class="text-2xl mb-10">Welcome to NextGraph</h1> } catch (e) {}
if (bs) {
} else {
// probe localhost and LAN
// if nothing found, displays login/create account
console.log("no wallet found");
display_login_create = true;
}
}
onMount(() => bootstrap());
</script>
<div class="row"> <Home {display_login_create} />
<a href="/test" use:link>
<button
type="button"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
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="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"
/>
</svg>
Create account
</button>
</a>
</div>
<div class="row mt-10">
<a href="/grid" use:link>
<button
type="button"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="currentColor"
stroke="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
/>
</svg>
Log in
</button>
</a>
</div>
</main>

File diff suppressed because one or more lines are too long

@ -14,7 +14,7 @@
export let params = {}; export let params = {};
</script> </script>
<main class="container2"> <main class="container3">
<h1>Welcome to test</h1> <h1>Welcome to test</h1>
<div class="row"> <div class="row">
<Greet /> <Greet />

@ -0,0 +1,867 @@
<!--
// Copyright (c) 2022-2023 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>
import { Button, Alert, Dropzone, Toggle } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import EULogo from "../assets/EU.svg?component";
import Logo from "../assets/nextgraph.svg?component";
import ng from "../api";
import { display_pazzle } from "../wallet_emojis";
import { onMount, tick } from "svelte";
let mobile =
import.meta.env.TAURI_PLATFORM == "android" ||
import.meta.env.TAURI_PLATFORM == "ios";
const onFileSelected = (image) => {
animate_bounce = false;
if (!security_txt) phrase.focus();
let reader = new FileReader();
reader.readAsArrayBuffer(image);
reader.onload = async (e) => {
security_img = e.target.result;
console.log(security_img);
var blob = new Blob([security_img], {
type: image.type,
});
image_url = URL.createObjectURL(blob);
phrase.scrollIntoView();
if (security_txt) {
await tick();
validate_button.focus();
}
};
};
const security_phrase_ok = async (e) => {
if (!e || e.key == "Enter" || e.keyCode == 13) {
phrase.blur();
if (!security_img) {
animate_bounce = true;
img_preview.scrollIntoView();
} else {
await tick();
validate_button.scrollIntoView();
validate_button.focus();
}
}
};
const dropHandle = (event) => {
event.preventDefault();
const files = event.dataTransfer.files;
if (files.length > 0) {
onFileSelected(files[0]);
}
};
const handleChange = (event) => {
const files = event.target.files;
if (files.length > 0) {
onFileSelected(files[0]);
}
};
let intro = false;
let pin = [];
let pin_confirm = [];
let security_txt = "";
let security_img;
let top;
let img_preview;
let phrase;
let validate_button;
let animate_bounce;
let image_url;
let info_options;
let options;
let creating = false;
let error;
let ready;
let download_link;
let download_name;
let cloud_link;
let animateDownload = true;
function scrollToTop() {
top.scrollIntoView();
}
function sel_pin(val) {
if (pin.length < 4) {
pin.push(val);
pin = pin;
}
}
function confirm_pin(val) {
if (pin_confirm.length < 4) {
pin_confirm.push(val);
pin_confirm = pin_confirm;
}
}
const api_url = import.meta.env.PROD
? "api/v1/"
: "http://localhost:3030/api/v1/";
let display_note_on_local_wallets = true;
async function bootstrap() {
scrollToTop();
let bs;
try {
bs = localStorage.getItem("bootstrap");
} catch (e) {}
if (bs) {
display_note_on_local_wallets = true;
}
}
function create_wallet() {
intro = false;
scrollToTop();
}
async function save_security() {
options = {
trusted: true,
cloud: false,
};
console.log("saved");
await tick();
info_options.scrollIntoView();
}
async function do_wallet() {
creating = true;
let params = {
security_img: security_img,
security_txt,
pin,
pazzle_length: 9,
send_bootstrap: undefined, //options.cloud ? : undefined,
send_wallet: options.cloud,
peer_id: {
Ed25519PubKey: [
119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167,
107, 2, 113, 98, 243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86,
],
},
nonce: 0,
local_save: options.trusted, // this is only used for tauri apps
result_with_wallet_file: false, // this will be automatically changed to true for browser app
};
console.log(params);
try {
let res = await ng.wallet_create_wallet(params);
console.log(res);
console.log(display_pazzle(res.pazzle));
ready = res;
download_name = "wallet-" + res.wallet_name + ".ngw";
if (options.cloud) {
cloud_link = "https://nextgraph.one/#/w/" + res.wallet_name;
}
if (res.wallet_file.length) {
const blob = new Blob([res.wallet_file]);
download_link = URL.createObjectURL(blob);
// we also save the wallet to localStorage here, and only if options.trusted is true
// indeed if a wallet_file is sent in the result, it means we are not on tauri app
// therefor we are on a web-browser.
if (options.trusted) {
//TODO save in localStorage
}
}
} catch (e) {
console.log(e);
error = e;
}
}
async function getWallet() {
const opts = {
method: "get",
};
const response = await fetch(
api_url + "bootstrap/I8tuoVE-LRH1wuWQpDBPivlSX8Wle39uHSL576BTxsk",
opts
);
const result = await response.json();
console.log("Result:", result);
}
onMount(() => bootstrap());
</script>
<main class="container3" bind:this={top}>
<div class="row">
<a href="#/">
<Logo class="logo block h-40" alt="NextGraph Logo" />
</a>
</div>
{#if intro}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
A <b>NextGraph Wallet</b> is unique to each individual. It stores your
credentials and authorizations to access documents. <br /><br />If you
already have a wallet, you should not create a new one, instead,
<a href="/wallet/login" use:link
>login here with your existing wallet.</a
>
If you never created a NextGraph Wallet before, please follow the instructions
below in order to create your personal wallet.
</p>
</div>
{#if display_note_on_local_wallets}
<Alert color="yellow" class="mt-5">
Some wallets are saved on this device,<br /> to log in with one of them,
<a href="/wallet/login" use:link>click here.</a>
</Alert>
{/if}
<div class="px-4 pt-5 mx-auto max-w-6xl lg:px-8 lg:pt-10 dark:bg-slate-800">
<div class="max-w-xl md:mx-auto sm:text-center lg:max-w-2xl">
<h2 class="pb-5 text-xl">
What is a wallet? <span class="text-sm">Please read</span>
</h2>
<ul class="mb-8 space-y-4 text-left text-gray-500 dark:text-gray-400">
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
stroke="currentColor"
fill="none"
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="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"
/>
</svg>
<span
>It is a secure and encrypted small file that contains some
important information that only you should have access to.</span
>
</li>
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
/>
</svg>
<span
>In it, we store all the permissions to access documents you have
been granted with, or that you have created yourself.</span
>
</li>
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z"
/>
</svg>
<span
>In order to open it, you will need to enter your <b>pazzle</b>
and a
<b>PIN code</b> of 4 digits. Your personal pazzle (contraction of puzzle
and password) is composed of 9 images you should remember. The order
of the images is important too.</span
>
</li>
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z"
/>
</svg>
<span
>Don't worry, it is easier to remember 9 images than a password
like "69$g&ms%C*%", and it has the same strength than a complex
password. The entropy of your pazzle is <b>66bits</b>, which is
considered very high by all standards.</span
>
</li>
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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"
d="M16.5 12a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zm0 0c0 1.657 1.007 3 2.25 3S21 13.657 21 12a9 9 0 10-2.636 6.364M16.5 12V8.25"
/>
</svg>
<span
>You should only create <b>one unique wallet for yourself</b>. All
your accounts, identities and permissions will be added to this
unique wallet later on. Do not create another wallet if you
already have one. Instead, you will
<b>import</b> your existing wallet in all the apps and websites where
you need it</span
>
</li>
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75"
/>
</svg>
<span
>Your wallet can be imported with the help of a small file that
you download, or with a QRcode. In any case, you should never
share this file or QRcode with anybody else.</span
>
</li>
<li class="flex space-x-3">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
<span
>We at NextGraph will never see the content of your wallet. It is
encrypted and we do not know your pazzle, so we cannot see what is
inside.</span
>
</li>
<li class="flex space-x-3 under">
<!-- Icon -->
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
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 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"
/>
</svg>
<span
>For the same reason, we won't be able to help you if you forget
your pazzle or PIN code. There is no "password recovery" option in
this case. You can note your pazzle down on a piece of paper until
you remember it, but don't forget to destroy this note after a
while.</span
>
</li>
</ul>
</div>
</div>
<div class="row mb-20">
<button
on:click|once={create_wallet}
role="button"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
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="M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z"
/>
</svg>
Ok, I create my wallet now !
</button>
</div>
{:else if pin.length < 4}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
<span class="text-xl">Let's start by choosing a PIN code</span>
<Alert color="yellow" class="mt-5">
We recommend you to choose a PIN code that you already know very well
:<br />
your credit card PIN, by example, is a good choice
</Alert>
</p>
<p class="text-left mt-5">Here are the rules for the PIN :</p>
<ul class="text-left list-disc list-inside">
<li>It cannot be a series like 1234 or 8765</li>
<li>
The same digit cannot repeat more than once. By example 4484 is
invalid
</li>
<li>
Try to avoid birth date, last digits of phone number, or zip code
</li>
</ul>
<Alert color="blue" class="mt-5">
You have chosen: {#each pin as digit}<span class="font-bold text-xl"
>{digit}</span
>{/each}
</Alert>
<div class="w-[325px] mx-auto">
{#each [0, 1, 2] as row}
<div class="">
{#each [1, 2, 3] as num}
<button
tabindex="0"
class="m-1 select-none align-bottom text-7xl w-[100px] h-[100px] p-0"
on:click={async () => sel_pin(num + row * 3)}
>
<span>{num + row * 3}</span>
</button>
{/each}
</div>
{/each}
<button
tabindex="0"
class="m-1 select-none mx-auto align-bottom text-7xl w-[100px] h-[100px] p-0"
on:click={async () => sel_pin(0)}
>
<span>0</span>
</button>
</div>
</div>
{:else if pin_confirm.length < 4}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
<span class="text-xl">Please confirm your PIN code.</span>
Enter the same PIN again
</p>
<Alert color="blue" class="mt-5">
You have chosen: {#each pin_confirm as digit}<span
class="font-bold text-xl">{digit}</span
>{/each}
</Alert>
<div class="w-[325px] mx-auto">
{#each [0, 1, 2] as row}
<div class="">
{#each [1, 2, 3] as num}
<button
tabindex="0"
class="m-1 select-none align-bottom text-7xl w-[100px] h-[100px] p-0"
on:click={async () => confirm_pin(num + row * 3)}
>
<span>{num + row * 3}</span>
</button>
{/each}
</div>
{/each}
<button
tabindex="0"
class="m-1 select-none mx-auto align-bottom text-7xl w-[100px] h-[100px] p-0"
on:click={async () => confirm_pin(0)}
>
<span>0</span>
</button>
</div>
</div>
{:else if !options}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
{#if pin.toString() === pin_confirm.toString()}
<Alert color="green" class="mt-5">
Your PIN is confirmed as : {#each pin_confirm as digit}<span
class="font-bold text-xl">{digit}</span
>{/each}
</Alert>
<h2 class="text-xl my-5">
Now let's enter a security phrase and a security image
</h2>
<p class="max-w-xl md:mx-auto lg:max-w-2xl text-left">
As a verification step, this phrase and image will be presented to you
every time you are about to enter your pazzle and PIN before you
unlock your wallet.<br />
This security measure will prevent you from entering your pazzle and PIN
on malicious sites and apps.
<Alert color="red" class="mt-5">
When you will use you wallet, if you do not see and recognize your
own security phrase and image before entering your pazzle, please
stop and DO NOT enter your pazzle, you are being the victim of a
phishing attempt.
</Alert>
</p>
<p class="text-left mt-5">
Here are the rules for the security phrase and image :
</p>
<ul class="text-left list-disc list-inside">
<li>The phrase should be at least 10 characters long</li>
<li>
It should be something you will remember, but not something too
personal.
</li>
<li>Do not enter your full name, nor address, nor phone number.</li>
<li>
Instead, you can enter a quote, a small sentence that you like, or
something meaningless to others, but unique to you.
</li>
<li>
The image should be minimum 150x150px. There is no need to provide
more than 400x400px as it will be scaled down anyway.
</li>
<li>We accept several formats like JPEG, PNG, GIF, WEBP and more.</li>
<li>
The image should be unique to you. But it should not be too personal
neither.
</li>
<li>Do not upload your face picture, this is not a profile pic.</li>
<li>
The best would be a landscape you like or any other picture that you
will recognize as unique.
</li>
<li>
Please be aware that other people who are sharing this device with
you, will be able to see this image and phrase.
</li>
</ul>
<input
bind:this={phrase}
class="mt-10 mr-0"
id="security-phrase-input"
placeholder="Type a security phrase..."
bind:value={security_txt}
on:keydown={security_phrase_ok}
/><button on:click={async () => await security_phrase_ok()}>
Ok
</button><br />
{#if security_txt && security_img}
<button
on:click|once={save_security}
bind:this={validate_button}
class="animate-bounce mt-10 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mr-2 mb-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
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="M6.633 10.5c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75A2.25 2.25 0 0116.5 4.5c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23H5.904M14.25 9h2.25M5.904 18.75c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 01-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 10.203 4.167 9.75 5 9.75h1.053c.472 0 .745.556.5.96a8.958 8.958 0 00-1.302 4.665c0 1.194.232 2.333.654 3.375z"
/>
</svg>
Save security phrase & image
</button>
{/if}
<Dropzone
class="mt-10 mb-10"
defaultClass="flex flex-col justify-center items-center w-full h-30 bg-gray-50 rounded-lg border-2 border-gray-300 border-dashed cursor-pointer dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
id="dropzone"
accept=".jpg, .jpeg, .png, .gif, .webp, .pnm, .tiff, .tif, .tga, .bmp, .avif, .qoi, .exr, .ppm"
on:drop={dropHandle}
on:dragover={(event) => {
event.preventDefault();
}}
on:change={handleChange}
>
<p class="mt-2 mb-5 text-gray-500 dark:text-gray-400">
{#if mobile}
<span class="font-semibold">Tap to upload an image</span>
{:else}
<span class="font-semibold">Click to select an image</span> or drag
and drop
{/if}
</p>
<svg
aria-hidden="true"
class="mb-3 w-20 mx-auto h-20 text-gray-400"
class:animate-bounce={animate_bounce}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/></svg
>
</Dropzone>
<img
bind:this={img_preview}
class="max-w-[300px] h-[300px] mx-auto mb-10"
src={image_url}
/>
{:else}
<Alert color="red" class="mt-5">
You didn't enter the same PIN twice
</Alert>
<button
class="select-none"
on:click={async () => {
pin_confirm = [];
pin = [];
}}
>
Start over
</button>
{/if}
</div>
{:else if !creating}
<div class=" max-w-6xl lg:px-8 mx-auto px-4" bind:this={info_options}>
<p class="max-w-xl mb-10 md:mx-auto lg:max-w-2xl">
<span class="text-xl">We are almost done !</span><br />
There are 2 options to choose before we can create your wallet. Those options
can help you to use and keep your wallet. But we also want to be careful
with your security and privacy.<br /><br />
Remember that in any case, once your wallet will be created, you will download
a file that you should keep privately somewhere on your device, USB key or
harddisk. This is the default way you can use and keep your wallet. Now let's
look at some options that can make your life a bit easier.
</p>
<p class="max-w-xl md:mx-auto lg:max-w-2xl text-left">
<span class="text-xl">Do you trust this device? </span> <br />
If you do, if this device is yours or is used by few trusted persons of your
family or workplace, then you can save your wallet in this device. To the
contrary, if this device is public and shared by strangers, do not save your
wallet here. {#if !import.meta.env.TAURI_PLATFORM}By selecting this
option, you agree to save some cookies on your browser.{/if}<br />
<Toggle class="mt-3" bind:checked={options.trusted}
>Save your wallet here?</Toggle
>
</p>
<p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left">
<span class="text-xl">Keep a copy in the cloud? </span> <br />
Are you afraid that you will loose the file containing your wallet? If this
would happen, your wallet would be lost forever, together with all your documents.
We can keep an encrypted copy of your wallet in our cloud. Only you will
be able to download it with a special link. You would have to keep this link
safely though. By selecting this option, you agree to the
<a target="_blank" href="https://nextgraph.one/tos"
>Terms and Conditions of our cloud</a
>.
<br />
<Toggle class="mt-3" bind:checked={options.cloud}
>Save your wallet in the cloud?</Toggle
>
</p>
<button
on:click|once={do_wallet}
class="mt-10 mb-8 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mr-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
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="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
Let's create this wallet
</button>
</div>
{:else if !error}
{#if !ready}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-800">
We are creating your wallet...
<svg
class="animate-spin mt-10 h-6 w-6 mx-auto"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
</div>
{:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
Your wallet is ready!
<svg
class="my-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="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
/>
</svg>
{#if download_link}
Please download your wallet and keep it in a safe location<br />
<a href={download_link} target="_blank" download={download_name}>
<button
tabindex="-1"
class:animate-bounce={animateDownload}
on:click={() => (animateDownload = false)}
class="mt-10 mb-8 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none 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 mr-2"
>
<svg
class="w-8 h-8 mr-2 -ml-1"
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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m.75 12l3 3m0 0l3-3m-3 3v-6m-1.5-9H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
/>
</svg>
Download my wallet
</button>
</a><br />
{:else if !options.trusted}
Your wallet file has been downloaded into your "Downloads" folder,
with the name<br /><span class="text-black"> {download_name}</span><br
/>
Please move it to a safe and durable place.<br /><br />
{/if}
{#each display_pazzle(ready.pazzle) as emoji}
<span>{emoji}</span><br />
{/each}
</div>
{/if}
{:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
An error occurred !
<svg
fill="none"
class="animate-bounce mt-10 h-10 w-10 mx-auto"
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>
<Alert color="red" class="mt-5">
{error}
</Alert>
<button
class="mt-10 select-none"
on:click={async () => {
pin_confirm = [];
pin = [];
options = undefined;
creating = false;
error = undefined;
animateDownload = true;
}}
>
Start over
</button>
</div>
{/if}
</main>

@ -9,6 +9,31 @@
// according to those terms. // according to those terms.
*/ */
.logo {
padding: 1.5em;
will-change: filter;
transition: 0.75s;
padding-bottom: 1em;
}
.container3 {
margin: 0;
min-width: 280px;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
div[role="alert"] div {
display: block;
}
.row {
display: flex;
justify-content: center;
}
:root { :root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px; font-size: 16px;
@ -25,56 +50,24 @@
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
} }
.pazzleline { body {
margin-right: auto;
margin-left: auto;
}
.pin {
cursor: pointer;
text-align: center;
}
.sel {
position: relative;
top: -56%;
font-size: 100px;
left: 30%;
font-weight: 700;
}
.sel-emoji {
overflow: hidden;
}
.emoji {
cursor: pointer;
/* padding: 0;
margin: 0; margin: 0;
border: 0;
box-shadow: none; */
}
.container2 {
margin: 0;
min-width: 280px;
padding-top: 10vh;
display: flex; display: flex;
flex-direction: column; place-items: center;
justify-content: center; min-width: 320px;
text-align: center; min-height: 100vh;
} }
.logo { #app {
padding: 1.5em; max-width: 1280px;
will-change: filter; margin: 0 auto;
transition: 0.75s; padding: 0rem;
text-align: center;
} }
.row { /* .container2 {
display: flex; padding-top: 10vh;
justify-content: center; } */
}
a { a {
font-weight: 500; font-weight: 500;
@ -88,6 +81,8 @@ a:hover {
h1 { h1 {
text-align: center; text-align: center;
font-size: 3.2em;
line-height: 1.1;
} }
input, input,
@ -116,13 +111,14 @@ button:active {
background-color: #e8e8e8; background-color: #e8e8e8;
} }
input, /* input,
button { button {
outline: none; outline: none;
} } */
#greet-input { button:focus,
margin-right: 5px; button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {

@ -1734,3 +1734,12 @@ export const emoji_cat = [
"emotion", "emotion",
]; ];
export function display_pazzle(pazzle) {
let res = [];
for (const emoji of pazzle) {
let cat = (emoji & 240) >> 4;
let idx = emoji & 15;
res.push(emoji_cat[cat] +":"+ emojis[emoji_cat[cat]][idx].code);
}
return res;
}

@ -67,9 +67,9 @@ pub fn wallet_open_wallet_with_pazzle(
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn wallet_create_wallet(js_params: JsValue) -> Result<JsValue, JsValue> { pub async fn wallet_create_wallet(js_params: JsValue) -> Result<JsValue, JsValue> {
let params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params) let mut params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params)
.map_err(|_| "Deserialization error of args")?; .map_err(|_| "Deserialization error of args")?;
params.result_with_wallet_file = true;
let res = create_wallet_v0(params).await; let res = create_wallet_v0(params).await;
match res { match res {
Ok(r) => Ok(serde_wasm_bindgen::to_value(&r).unwrap()), Ok(r) => Ok(serde_wasm_bindgen::to_value(&r).unwrap()),

@ -353,22 +353,20 @@ pub async fn create_wallet_v0(
// check validity of PIN // check validity of PIN
// shouldn't start with 0 // shouldn't start with 0
if params.pin[0] == 0 { // if params.pin[0] == 0 {
return Err(NgWalletError::InvalidPin); // return Err(NgWalletError::InvalidPin);
} // }
// each digit shouldnt be greater than 9 // each digit shouldnt be greater than 9
if params.pin[0] > 9 || params.pin[1] > 9 || params.pin[2] > 9 || params.pin[3] > 9 { if params.pin[0] > 9 || params.pin[1] > 9 || params.pin[2] > 9 || params.pin[3] > 9 {
return Err(NgWalletError::InvalidPin); return Err(NgWalletError::InvalidPin);
} }
// check for uniqueness of each digit // check for same digit doesnt appear 3 times
if params.pin[1] == params.pin[0] if (params.pin[0] == params.pin[1] && params.pin[0] == params.pin[2])
|| params.pin[1] == params.pin[2] || (params.pin[0] == params.pin[1] && params.pin[0] == params.pin[3])
|| params.pin[1] == params.pin[3] || (params.pin[0] == params.pin[2] && params.pin[0] == params.pin[3])
|| params.pin[2] == params.pin[0] || (params.pin[1] == params.pin[2] && params.pin[1] == params.pin[3])
|| params.pin[2] == params.pin[3]
|| params.pin[3] == params.pin[0]
{ {
return Err(NgWalletError::InvalidPin); return Err(NgWalletError::InvalidPin);
} }
@ -409,7 +407,11 @@ pub async fn create_wallet_v0(
return Err(NgWalletError::InvalidSecurityImage); return Err(NgWalletError::InvalidSecurityImage);
} }
let resized_img = decoded_img.resize_to_fill(400, 400, FilterType::Triangle); let resized_img = if decoded_img.height() == 400 && decoded_img.width() == 400 {
decoded_img
} else {
decoded_img.resize_to_fill(400, 400, FilterType::Triangle)
};
let buffer: Vec<u8> = Vec::with_capacity(100000); let buffer: Vec<u8> = Vec::with_capacity(100000);
let mut cursor = Cursor::new(buffer); let mut cursor = Cursor::new(buffer);
@ -543,11 +545,17 @@ pub async fn create_wallet_v0(
"creating of wallet took: {} ms", "creating of wallet took: {} ms",
creating_pazzle.elapsed().as_millis() creating_pazzle.elapsed().as_millis()
); );
let wallet = Wallet::V0(wallet_v0);
let wallet_file = match (params.result_with_wallet_file) {
false => vec![], // TODO: save locally
true => to_vec(&NgFile::V0(NgFileV0::Wallet(wallet.clone()))).unwrap(),
};
Ok(CreateWalletResultV0 { Ok(CreateWalletResultV0 {
wallet: Wallet::V0(wallet_v0), wallet: wallet,
wallet_file,
pazzle, pazzle,
mnemonic, mnemonic,
wallet_name: base64_url::encode(&wallet_id.slice()),
}) })
} }

@ -24,11 +24,11 @@ pub type BootstrapId = WalletId;
/// BootstrapServer type /// BootstrapServer type
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum BoostrapServerTypeV0 { pub enum BoostrapServerTypeV0 {
Localhost, Localhost(u16), // optional port number
BoxPrivate(Vec<NetAddr>), BoxPrivate(Vec<NetAddr>),
BoxPublic(Vec<NetAddr>), BoxPublic(Vec<NetAddr>),
BoxPublicDyn(Vec<NetAddr>), // can be empty BoxPublicDyn(Vec<NetAddr>), // can be empty
Domain(String), Domain(String), // accepts an option trailing ":port" number
} }
/// BootstrapServer details Version 0 /// BootstrapServer details Version 0
@ -229,6 +229,8 @@ pub struct CreateWalletV0 {
pub pazzle_length: u8, pub pazzle_length: u8,
pub send_bootstrap: Option<Bootstrap>, pub send_bootstrap: Option<Bootstrap>,
pub send_wallet: bool, pub send_wallet: bool,
pub result_with_wallet_file: bool,
pub local_save: bool,
pub peer_id: PubKey, pub peer_id: PubKey,
pub nonce: u64, pub nonce: u64,
} }
@ -245,6 +247,8 @@ impl CreateWalletV0 {
nonce: u64, nonce: u64,
) -> Self { ) -> Self {
CreateWalletV0 { CreateWalletV0 {
result_with_wallet_file: false,
local_save: true,
security_img, security_img,
security_txt, security_txt,
pin, pin,
@ -260,8 +264,11 @@ impl CreateWalletV0 {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateWalletResultV0 { pub struct CreateWalletResultV0 {
pub wallet: Wallet, pub wallet: Wallet,
#[serde(with = "serde_bytes")]
pub wallet_file: Vec<u8>,
pub pazzle: Vec<u8>, pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12], pub mnemonic: [u16; 12],
pub wallet_name: String,
} }
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]

@ -157,7 +157,7 @@ async fn main() {
let key: [u8; 32] = [0; 32]; let key: [u8; 32] = [0; 32];
println!("{}", dir.to_str().unwrap()); println!("{}", dir.to_str().unwrap());
fs::create_dir_all(dir.clone()).unwrap(); fs::create_dir_all(dir.clone()).unwrap();
let mut store = LmdbKCVStore::open(&dir, key); let store = LmdbKCVStore::open(&dir, key);
let server = Arc::new(Server { store }); let server = Arc::new(Server { store });
@ -201,7 +201,11 @@ async fn main() {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
{ {
cors = cors.allow_origin("https://nextgraph.one"); cors = cors
.allow_origin("https://nextgraph.one")
.allow_origin("https://app.nextgraph.one")
.allow_origin("https://nextgraph.eu")
.allow_origin("https://nextgraph.net");
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {

@ -12,10 +12,48 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,
PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMjUg
MjI1Ij48Y2lyY2xlIGN4PSIxMDkuODgxIiBjeT0iMTEyLjkwNSIgcj0iMTA2Ljk4IiBzdHlsZT0i
ZmlsbDogcmdiKDI1NSwgMjU1LCAyNTUpOyBzdHJva2U6IG5vbmU7IHN0cm9rZS13aWR0aDogMC4y
NjgzNzU7Ij48L2NpcmNsZT48cGF0aCBkPSJNOTguMzQzIDE5MC4yNjFjLTE3Ljk0LTIuNzI3LTMz
LjMzMS0xMC42ODMtNDUuNzM1LTIzLjYzOC0xNC4wMDYtMTQuNjI5LTIxLjQzLTMzLjIxLTIxLjQz
LTUzLjYzNSAwLTEwLjIxOCAxLjctMTkuNDQ0IDUuMjIxLTI4LjMzMiA0LjI4Ny0xMC44MiAxMC4w
MzgtMTkuMzkgMTguNTM1LTI3LjYyMiA0LjczLTQuNTgyIDYuNjA3LTYuMTA3IDExLjI4MS05LjE2
MyAxMS45LTcuNzggMjQuMTc0LTExLjg4IDM4LjA5Ni0xMi43MjUgMTkuODA1LTEuMjAxIDM5LjEx
MiA1LjExMyA1NC42MDMgMTcuODYgMS41MDcgMS4yNCAyLjczIDIuMzU4IDIuNzE2IDIuNDg2LS4w
MTMuMTI4LTMuODU4IDMuNjM1LTguNTQ0IDcuNzkzLTQuNjg2IDQuMTU3LTEwLjA0NyA4Ljk2Mi0x
MS45MTQgMTAuNjc3LTEuODY2IDEuNzE1LTMuNTQgMy4xMTktMy43MjEgMy4xMTktLjE4MSAwLTEu
NC0uNzQ2LTIuNzEtMS42NTYtNy41My01LjIzOS0xNS45OTQtNy44MjItMjUuNjI1LTcuODIyLTEy
LjczMiAwLTIzLjI1IDQuMzM4LTMyLjE0NCAxMy4yNTctNi4zOTYgNi40MTQtMTAuNzA0IDE0LjU1
Ni0xMi41IDIzLjYyNC0uNjkxIDMuNDg4LS42OSAxMy41My4wMDIgMTcuMDA5IDMuNzA1IDE4LjYy
NiAxOC4zMTggMzMuMTAyIDM2LjY0MiAzNi4yOTcgNC4xNjQuNzI2IDExLjk4LjcxMiAxNS45OS0u
MDI4IDE0LjAzMi0yLjU5NCAyNS44Ni0xMS4zNjggMzIuMjY1LTIzLjkzNi43NzQtMS41MTkgMS4y
Ni0yLjg4NSAxLjA4LTMuMDM2LS4xNzgtLjE1Mi02Ljg3NC0xLjE3OC0xNC44NzctMi4yODEtOS43
OC0xLjM0OC0xNC45MjQtMi4yMTQtMTUuNjg1LTIuNjQxLTEuNTItLjg1NC0yLjgzNi0yLjg4OC0y
LjgzNi00LjM4NiAwLTEuMTczIDIuMDI3LTE1Ljg2OSAyLjQ5LTE4LjA2LjI5OC0xLjQwMSAyLjQy
Ni0zLjQ5MyAzLjg0NC0zLjc3Ny42MjItLjEyNCA4LjgyNy44NTYgMTguMjggMi4xODQgOS40MzQg
MS4zMjUgMTcuMjYzIDIuMjk0IDE3LjM5OSAyLjE1NC4xMzYtLjE0IDEuMTE4LTYuNTQ4IDIuMTgz
LTE0LjI0IDEuMTA4LTggMi4yMDQtMTQuNjAyIDIuNTYyLTE1LjQyNi4zNDQtLjc5MyAxLjExLTEu
ODUgMS43MDMtMi4zNDggMi4wNjMtMS43MzYgMy4xNDMtMS43ODUgMTIuMjA0LS41NTMgOS42MzYg
MS4zMSAxMC43MDkgMS41NjIgMTIuMjggMi44ODUgMS42NDQgMS4zODMgMi4yNzQgMi44MSAyLjI2
IDUuMTIzLS4wMDcgMS4xMDItLjkyMiA4LjI5Ny0yLjAzMyAxNS45ODktMS4xMTIgNy42OTEtMS45
NzIgMTQuMDQtMS45MTIgMTQuMTA5LjA2MS4wNjggNy4xNjcgMS4xMTEgMTUuNzkyIDIuMzE4IDEx
LjEwNSAxLjU1NCAxNi4wMDggMi4zODcgMTYuODAyIDIuODU2IDEuNTMuOTA0IDIuNDggMi42NDgg
Mi40NSA0LjQ5OC0uMDQ2IDIuODQ0LTIuNDEzIDE4LjEyMy0yLjk3NSAxOS4yMS0uNjYyIDEuMjgt
Mi42MDMgMi41NDgtMy45MjEgMi41NjItLjUyLjAwNS03Ljg3NS0uOTYtMTYuMzQ0LTIuMTQ0LTgu
NDctMS4xODUtMTUuNDc2LTIuMDc3LTE1LjU3LTEuOTgzLS4wOTQuMDk0LTEuMTg4IDcuMzQxLTIu
NDMxIDE2LjEwNi0xLjQ0IDEwLjE1My0yLjQ5OCAxNi40MzYtMi45MTYgMTcuMzE2LS43MjUgMS41
MjgtMi43NjIgMy4wNjMtNC41MzggMy40MTgtLjk1Ny4xOTEtMTAuOS0uOTI4LTEzLjU5OC0xLjUz
LS41NDgtLjEyMy0xLjg5Mi42NzItNC41MSAyLjY2NS0xMS4yNjMgOC41NzYtMjQuMzQyIDEzLjkx
LTM4LjM1NyAxNS42NDItNC40LjU0NC0xNS43MjcuNDMzLTE5Ljg1NC0uMTk1eiIgc3R5bGU9ImZp
bGw6IHJnYig3MywgMTE0LCAxNjUpOyBmaWxsLW9wYWNpdHk6IDE7IHN0cm9rZTogcmdiKDczLCAx
MTQsIDE2NSk7IHN0cm9rZS13aWR0aDogMC4zNzc5NzY7IHN0cm9rZS1vcGFjaXR5OiAxOyI+PC9w
YXRoPjwvc3ZnPg==" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte</title> <title>NextGraph</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>

@ -8,9 +8,20 @@
"build": "vite build --base=./", "build": "vite build --base=./",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": {
"flowbite": "^1.6.5",
"flowbite-svelte": "^0.37.1",
"svelte-spa-router": "^3.3.0"
},
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.4", "@sveltejs/vite-plugin-svelte": "^2.0.4",
"svelte": "^3.58.0", "svelte": "^3.58.0",
"vite": "^4.3.9" "vite": "^4.3.9",
"postcss": "^8.4.23",
"postcss-load-config": "^4.0.1",
"svelte-preprocess": "^5.0.3",
"tailwindcss": "^3.3.1",
"autoprefixer": "^10.4.14",
"vite-plugin-svelte-svg": "^2.2.1"
} }
} }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,13 @@
const tailwindcss = require("tailwindcss");
const autoprefixer = require("autoprefixer");
const config = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer,
],
};
module.exports = config;

@ -9,54 +9,24 @@
// according to those terms. // according to those terms.
--> -->
<script> <script>
import svelteLogo from "./assets/svelte.svg"; import Router from "svelte-spa-router";
import viteLogo from "/vite.svg"; import { onMount, tick } from "svelte";
import Counter from "./lib/Counter.svelte";
import Greeting from "./Greeting.svelte";
</script>
<main>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} class="logo" alt="Vite Logo" />
</a>
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
</a>
</div>
<h1>Vite + Svelte</h1>
<Greeting /> import Home from "./routes/Home.svelte";
import WalletCreate from "./routes/WalletCreate.svelte";
import Install from "../../../ng-app/src/routes/Install.svelte";
<div class="card"> //import URI from "./routes/URI.svelte";
<Counter /> import NotFound from "./routes/NotFound.svelte";
</div>
<p> const routes = new Map();
Check out <a routes.set("/", Home);
href="https://github.com/sveltejs/kit#readme" routes.set("/wallet/create", WalletCreate);
target="_blank" routes.set("/install", Install);
rel="noreferrer">SvelteKit</a //routes.set(/^\/ng(.*)/i, URI);
>, the official Svelte app framework powered by Vite! routes.set("*", NotFound);
</p> </script>
<p class="read-the-docs">Click on the Vite and Svelte logos to learn more</p> <main class="">
<Router {routes} />
</main> </main>
<style>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.svelte:hover {
filter: drop-shadow(0 0 2em #ff3e00aa);
}
.read-the-docs {
color: #888;
}
</style>

@ -1,25 +0,0 @@
<script>
import { onMount } from "svelte";
const api_url = import.meta.env.PROD
? "api/v1/"
: "http://localhost:3030/api/v1/";
let greeting = "";
async function getWallet() {
const opts = {
method: "get",
};
const response = await fetch(
api_url + "bootstrap/I8tuoVE-LRH1wuWQpDBPivlSX8Wle39uHSL576BTxsk",
opts
);
const result = await response.json();
console.log("Result:", result);
}
onMount(() => getWallet());
</script>
<h1>{greeting}</h1>

@ -1,91 +0,0 @@
/*
// Copyright (c) 2022-2023 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.
*/
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

@ -0,0 +1,4 @@
/* Write your global styles here, in PostCSS syntax */
@tailwind base;
@tailwind components;
@tailwind utilities;

@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg viewBox="0 0 810 540" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" ><desc>European flag</desc>
<defs><g id="s"><g id="c"><path id="t" d="M0,0v1h0.5z" transform="translate(0,-1)rotate(18)"/><use xlink:href="#t" transform="scale(-1,1)"/></g><g id="a"><use xlink:href="#c" transform="rotate(72)"/><use xlink:href="#c" transform="rotate(144)"/></g><use xlink:href="#a" transform="scale(-1,1)"/></g></defs>
<rect fill="#039" width="810" height="540"/><g fill="#fc0" transform="scale(30)translate(13.5,9)"><use xlink:href="#s" y="-6"/><use xlink:href="#s" y="6"/><g id="l"><use xlink:href="#s" x="-6"/><use xlink:href="#s" transform="rotate(150)translate(0,6)rotate(66)"/><use xlink:href="#s" transform="rotate(120)translate(0,6)rotate(24)"/><use xlink:href="#s" transform="rotate(60)translate(0,6)rotate(12)"/><use xlink:href="#s" transform="rotate(30)translate(0,6)rotate(42)"/></g><use xlink:href="#l" transform="scale(-1,1)"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 225 225"
>
<g>
<circle
r="106.98013"
cy="112.90476"
cx="109.88096"
style="fill:#ffffff;stroke:none;stroke-width:0.268375" />
<path
d="M 98.343352,190.26108 C 80.403778,187.53354 65.011938,179.57839 52.608228,166.62327 38.602093,151.99448 31.178059,133.41381 31.178059,112.98841 c 0,-10.21889 1.700058,-19.44396 5.221234,-28.332119 4.28678,-10.820699 10.037295,-19.39063 18.535095,-27.62263 4.72982,-4.58187 6.60687,-6.10643 11.28099,-9.16256 11.89869,-7.779841 24.173884,-11.879991 38.095802,-12.724761 19.80437,-1.2017 39.11165,5.11306 54.60284,17.858751 1.50718,1.24006 2.72951,2.35934 2.71628,2.48729 -0.0132,0.12795 -3.85821,3.63443 -8.54442,7.79217 -4.6862,4.157729 -10.04724,8.96276 -11.91342,10.677819 -1.86617,1.715071 -3.54094,3.11831 -3.7217,3.11831 -0.18075,0 -1.39985,-0.745188 -2.70911,-1.655969 -7.53011,-5.23834 -15.99428,-7.82188 -25.62597,-7.82188 -12.731628,0 -23.249192,4.3379 -32.143882,13.257541 -6.39594,6.413868 -10.70387,14.555268 -12.50018,23.623578 -0.69099,3.48832 -0.68968,13.53072 0.002,17.00893 3.70508,18.62577 18.31886,33.10194 36.642322,36.29729 4.16439,0.72621 11.98099,0.71223 15.98975,-0.0286 14.03187,-2.59311 25.86047,-11.36806 32.26533,-23.93578 0.77379,-1.51834 1.26018,-2.88461 1.08086,-3.03616 -0.17934,-0.15156 -6.87448,-1.1779 -14.87813,-2.28078 -9.7795,-1.34758 -14.92353,-2.21379 -15.68471,-2.64117 -1.52067,-0.85379 -2.83611,-2.88806 -2.83611,-4.3859 0,-1.1732 2.02687,-15.86876 2.49085,-18.05962 0.29676,-1.40127 2.42559,-3.4934 3.84317,-3.77691 0.62227,-0.12445 8.82712,0.85555 18.28065,2.18348 9.43343,1.32511 17.26269,2.29453 17.39833,2.15427 0.13566,-0.14026 1.11808,-6.54833 2.18313,-14.24014 1.10778,-8.000208 2.20407,-14.60184 2.56177,-15.426229 0.34392,-0.792599 1.11019,-1.849131 1.70287,-2.34782 2.06321,-1.736079 3.1433,-1.785011 12.20439,-0.55291 9.63637,1.310309 10.70873,1.56224 12.28077,2.88503 1.64359,1.382979 2.2732,2.810909 2.25906,5.123309 -0.007,1.10173 -0.92172,8.29645 -2.03332,15.98826 -1.11158,7.69182 -1.97159,14.04091 -1.91113,14.1091 0.0605,0.0682 7.16644,1.11143 15.79109,2.31832 11.10566,1.55407 16.00827,2.38757 16.80223,2.85657 1.53015,0.90389 2.48023,2.64785 2.45017,4.49756 -0.0462,2.84349 -2.41252,18.12279 -2.97521,19.21089 -0.66164,1.27949 -2.60244,2.54696 -3.92109,2.56074 -0.51973,0.005 -7.87449,-0.95937 -16.34391,-2.144 -8.46944,-1.18464 -15.47588,-2.077 -15.56986,-1.98301 -0.094,0.094 -1.18792,7.34163 -2.43097,16.10589 -1.44004,10.15311 -2.49792,16.43621 -2.91556,17.31631 -0.72531,1.52848 -2.76261,3.06291 -4.53817,3.41802 -0.95688,0.19138 -10.90014,-0.92798 -13.59859,-1.53084 -0.5471,-0.12223 -1.89146,0.67252 -4.50941,2.66588 -11.2627,8.57562 -24.34195,13.90917 -38.35741,15.64164 -4.40038,0.54395 -15.72658,0.43298 -19.853658,-0.19451 z"
style="fill:#4972a5;fill-opacity:1;stroke:#4972a5;stroke-width:0.377976;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

@ -1,10 +0,0 @@
<script>
let count = 0
const increment = () => {
count += 1
}
</script>
<button on:click={increment}>
count is {count}
</button>

@ -1,4 +1,5 @@
import './app.css' import './app.postcss'
import "../../../ng-app/src/styles.css";
import App from './App.svelte' import App from './App.svelte'
const app = new App({ const app = new App({

@ -0,0 +1,55 @@
<!--
// Copyright (c) 2022-2023 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>
import { Button } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import Home from "../../../../ng-app/src/lib/Home.svelte";
import { onMount } from "svelte";
const api_url = import.meta.env.PROD
? "api/v1/"
: "http://localhost:3030/api/v1/";
let display_login_create = false;
async function bootstrap() {
let bs;
try {
bs = localStorage.getItem("bootstrap");
} catch (e) {}
if (bs) {
} else {
// probe localhost and LAN
// if nothing found, displays login/create account
console.log("no wallet found");
display_login_create = true;
}
}
async function getWallet() {
const opts = {
method: "get",
};
const response = await fetch(
api_url + "bootstrap/I8tuoVE-LRH1wuWQpDBPivlSX8Wle39uHSL576BTxsk",
opts
);
const result = await response.json();
console.log("Result:", result);
}
onMount(() => bootstrap());
</script>
<Home {display_login_create} />

@ -0,0 +1,20 @@
<!--
// Copyright (c) 2022-2023 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>
import { Alert } from "flowbite-svelte";
</script>
<div class="p-8">
<Alert color="red">
<span class="font-medium">404</span> Page not found.
</Alert>
</div>

@ -0,0 +1,247 @@
<!--
// Copyright (c) 2022-2023 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>
import { Button, Alert } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import EULogo from "../assets/EU.svg?component";
import Logo from "../assets/nextgraph.svg?component";
import { onMount } from "svelte";
let top;
function scrollToTop() {
top.scrollIntoView();
}
let display_note_on_local_wallets = false;
async function bootstrap() {
scrollToTop();
let bs;
try {
bs = localStorage.getItem("bootstrap");
} catch (e) {}
if (bs) {
display_note_on_local_wallets = true;
}
}
onMount(() => bootstrap());
</script>
<main class="container3" bind:this={top}>
<div class="row">
<a href="#/">
<Logo class="logo block h-40" alt="NextGraph Logo" />
</a>
</div>
<p class="max-w-sm">
A <b>NextGraph Wallet</b> is unique to each individual.<br /> It stores your
credentials to access documents. <br />If you already have a wallet, you
should not create a new one, instead,
{#if display_note_on_local_wallets}
<a href="/" use:link>login here with your existing wallet.</a>
{:else}
<a href="/wallet/login" use:link>login here with your existing wallet.</a>
{/if}
If you never created a NextGraph Wallet before, please choose one of the options
below to do so now.
</p>
{#if display_note_on_local_wallets}
<Alert color="yellow" class="mt-5">
Some wallets are saved on this device,<br /> to log in with one of them,
<a href="/" use:link>click here.</a>
</Alert>
{/if}
<div class="row mt-5">
<a href="https://nextgraph.eu/#/wallet/create">
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" />
For European Union citizens
</button>
</a>
</div>
<div class="row mt-5">
<a href="https://nextgraph.net/#/wallet/create">
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
/>
</svg>
For the rest of the world
</button>
</a>
</div>
<div class="row mt-5">
<a href="/install" use:link>
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class=" block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M10.5 1.5H8.25A2.25 2.25 0 006 3.75v16.5a2.25 2.25 0 002.25 2.25h7.5A2.25 2.25 0 0018 20.25V3.75a2.25 2.25 0 00-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3"
/>
</svg>
or
<svg
class="mr-4 ml-2 block h-10 w-10"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 64 50"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs />
<g
id="Page-1"
stroke-width="1"
fill="none"
fill-rule="evenodd"
sketch:type="MSPage"
>
<g
id="Laptop"
transform="translate(1.000000, 1.000000)"
stroke-width="2"
>
<path
d="M56,27 C56,28.1 55.1,29 54,29 L8,29 C6.9,29 6,28.1 6,27 L6,2 C6,0.9 6.9,0 8,0 L54,0 C55.1,0 56,0.9 56,2 L56,26.5 L56,27 L56,27 Z"
id="Shape"
/>
<path
d="M57,31 C57,29.9 56.1,29 55,29 L7,29 C5.9,29 5,29.9 5,31 L0,46 C0,47.1 0.9,48 2,48 L60,48 C61.1,48 62,47.1 62,46 L57,31 L57,31 Z"
id="Shape"
/>
<path d="M2,42.9 L61,42.9" id="Shape" />
<path
d="M52.1,24 C52.1,25.1 51.2,26 50.1,26 L12,26 C10.9,26 10,25.1 10,24 L10,6 C10,4.9 10.9,4 12,4 L50,4 C51.1,4 52,4.9 52,6 L52.1,24 L52.1,24 Z"
id="Shape"
/>
<path d="M22,39 L40,39" id="Shape" />
<path d="M17.2,39 L20,39" id="Shape" />
<path d="M12.1,39 L15,39" id="Shape" />
<path d="M7,39 L10,39" id="Shape" />
<path d="M9.2,35 L12,35" id="Shape" />
<path d="M14,35 L17,35" id="Shape" />
<path d="M19,35 L22,35" id="Shape" />
<path d="M24,35 L27,35" id="Shape" />
<path d="M29,35 L32,35" id="Shape" />
<path d="M34,35 L37,35" id="Shape" />
<path d="M39,35 L42,35" id="Shape" />
<path d="M45,35 L48,35" id="Shape" />
<path d="M50,35 L53,35" id="Shape" />
<path d="M47,32 L50,32" id="Shape" />
<path d="M42,32 L45,32" id="Shape" />
<path d="M37,32 L40,32" id="Shape" />
<path d="M32,32 L35,32" id="Shape" />
<path d="M27,32 L30,32" id="Shape" />
<path d="M22,32 L25,32" id="Shape" />
<path d="M17.1,32 L20,32" id="Shape" />
<path d="M12,32 L15,32" id="Shape" />
<path d="M42,39 L44.8,39" id="Shape" />
<path d="M47,39 L49.9,39" id="Shape" />
<path d="M52,39 L55,39" id="Shape" />
</g>
</g>
</svg>
Install the app
</button>
</a>
</div>
<div class="row mt-5">
<a href="https://docs.nextgraph.org/en/self-hosted">
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
Self-hosted or local install
</button>
</a>
</div>
<div class="row mt-5 mb-12">
<a href="#">
<button
tabindex="-1"
class="text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mr-2 mb-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 225 225"
class="mr-4 block h-10 w-10"
stroke="currentColor"
stroke-width="12"
fill="none"
>
<path
d="M 88.332599,179.77884 C 72.858008,177.42608 59.581081,170.564 48.8817,159.38898 36.800075,146.77026 30.396139,130.74266 30.396139,113.12381 c 0,-8.81477 1.466462,-16.772273 4.503812,-24.439156 3.697755,-9.333883 8.658122,-16.726264 15.988284,-23.827148 4.07992,-3.952299 5.699054,-5.267377 9.730928,-7.903581 10.263753,-6.710853 20.852276,-10.247623 32.861256,-10.976317 17.083161,-1.036581 33.737521,4.410501 47.100151,15.404873 1.30009,1.069669 2.35446,2.035155 2.34305,2.145524 -0.0114,0.110369 -3.32807,3.135042 -7.37038,6.721489 -4.04229,3.586437 -8.6667,7.731233 -10.27646,9.210635 -1.60975,1.479412 -3.05439,2.689839 -3.21032,2.689839 -0.15591,0 -1.2075,-0.642795 -2.33686,-1.428431 -6.49544,-4.518567 -13.79659,-6.747116 -22.104843,-6.747116 -10.982241,0 -20.054641,3.741852 -27.727158,11.435891 -5.517107,5.532575 -9.233107,12.555305 -10.782595,20.377588 -0.596045,3.00901 -0.594915,11.67153 0.0017,14.67182 3.195984,16.0665 15.801761,28.55358 31.607491,31.30987 3.592183,0.62643 10.334745,0.61437 13.792675,-0.0247 12.10383,-2.2368 22.30712,-9.80603 27.83192,-20.64689 0.66747,-1.30971 1.08703,-2.48825 0.93235,-2.61898 -0.1547,-0.13073 -5.9299,-1.01605 -12.83381,-1.96739 -8.43575,-1.16241 -12.87296,-1.9096 -13.52955,-2.27826 -1.31171,-0.73647 -2.44642,-2.49122 -2.44642,-3.78325 0,-1.012 1.74837,-13.68832 2.1486,-15.57814 0.25598,-1.20873 2.0923,-3.01339 3.3151,-3.25795 0.53677,-0.10735 7.61424,0.73799 15.7688,1.88346 8.13723,1.14303 14.89071,1.97925 15.00772,1.85826 0.11702,-0.12098 0.96445,-5.648553 1.88315,-12.283473 0.95557,-6.900944 1.90122,-12.59548 2.20977,-13.306594 0.29667,-0.683692 0.95765,-1.595052 1.46889,-2.025218 1.77972,-1.497534 2.7114,-1.539742 10.52745,-0.476938 8.31229,1.130266 9.2373,1.347581 10.59333,2.488613 1.41776,1.192951 1.96085,2.424677 1.94866,4.419342 -0.006,0.950347 -0.79507,7.156475 -1.75393,13.791395 -0.95885,6.634933 -1.70069,12.111623 -1.64854,12.170443 0.0522,0.0588 6.18174,0.95872 13.62132,1.99978 9.57969,1.34053 13.80866,2.0595 14.49353,2.46406 1.3199,0.77969 2.13943,2.28402 2.1135,3.87957 -0.0399,2.45278 -2.08103,15.63263 -2.5664,16.57122 -0.57073,1.10369 -2.24485,2.197 -3.38232,2.20889 -0.44831,0.004 -6.79249,-0.82755 -14.09817,-1.84941 -7.3057,-1.02186 -13.34942,-1.79161 -13.43049,-1.71053 -0.0811,0.0811 -1.02469,6.33285 -2.09694,13.89286 -1.24218,8.75802 -2.1547,14.1778 -2.51495,14.93697 -0.62565,1.31846 -2.38302,2.64205 -3.91461,2.94836 -0.8254,0.16509 -9.4024,-0.80047 -11.73007,-1.32049 -0.47193,-0.10544 -1.63157,0.58011 -3.8898,2.29957 -9.71515,7.39729 -20.99725,11.99799 -33.08692,13.49241 -3.79574,0.46921 -13.565667,0.37348 -17.125664,-0.16779 z"
/>
<rect
ry="37.596001"
y="10.583322"
x="14.363095"
height="204.86308"
width="195.79167"
/>
</svg>
On an NG Box (owned or invited)
</button>
</a>
</div>
</main>

@ -0,0 +1,23 @@
/** @type {import('tailwindcss').Config}*/
const config = {
content: [
"./src/**/*.{html,js,svelte,ts}",
"../../ng-app/src/**/*.{html,js,svelte,ts}",
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
],
theme: {
extend: {
colors: {
primary: { "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd", "400": "#60a5fa", "500": "#3b82f6", "600": "#1E88E5", "700": "#4972A5", "800": "#1e40af", "900": "#1e3a8a" }
}
},
},
plugins: [
require('flowbite/plugin')
],
darkMode: 'class',
};
module.exports = config;

@ -1,7 +1,36 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte' import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte'
import sveltePreprocess from "svelte-preprocess";
import svelteSVG from "vite-plugin-svelte-svg";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [svelte()], plugins: [svelte({
preprocess: [
vitePreprocess(),
sveltePreprocess({
typescript: false,
postcss: true,
}),
],
}),
svelteSVG({
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
// disable plugins
removeViewBox: false,
},
},
},
{
name: 'prefixIds',
}
],
}, // See https://github.com/svg/svgo#configuration
requireSuffix: true, // Set false to accept '.svg' without the '?component'
}),],
}) })

Loading…
Cancel
Save