Rust implementation of NextGraph, a Decentralized and local-first web 3.0 ecosystem https://nextgraph.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nextgraph-rs/ng-app/src/routes/AccountInfo.svelte

367 lines
13 KiB

<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<!--
@component
"Accounts 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 { t } from "svelte-i18n";
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 // one IPv4 and optionally one IPv6 to connect to an NGbox on private LAN rs type Vec<BindAddress>
Public // one IPv4 and optionally one IPv6 to connect to an NGbox on public (edge) internet. rs type Vec<BindAddress>
BoxPublicDyn // same but with dynamic IPs that can be retrieved with a special API. 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]]) => {
//TODO: there can be several broker definitions for the same broker_id (if the broker can be reached by different means)
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" role="menu">
<li>
<h2 class="text-xl mb-6">{$t("pages.account_info.title")}</h2>
</li>
<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={() => 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">{$t("buttons.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">
{$t("pages.account_info.site", { values: { name: 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"
>
{$t("pages.account_info.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>
<span class="text-gray-500">Name</span>
<span class="break-all">{device.name}</span>
</div>
<div>
<span class="text-gray-500">ID</span>
<span class="break-all">{device.peer_id}</span>
</div>
<div>
<span class="text-gray-500">Version</span>
<span>{device.version}</span>
</div>
<div>
<span class="text-gray-500">System</span>
<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">
<div>
<span class="text-gray-500">Address</span><br />
<span class="break-all">{broker.address}</span>
</div>
<div>
<span class="text-gray-500">Last Connected</span><br />
<span class="break-all">{broker.last_connected}</span>
</div>
<div>
<span class="text-gray-500">ID</span>
<span class="break-all">{broker.id}</span>
</div>
<!-- <div>
<span class="text-gray-500">Can Forward?</span>
<span>{broker.can_forward}</span>
</div>
<div>
<span class="text-gray-500">Can Verify?</span>
<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"
>{$t("pages.account_info.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">
{@html $t("errors.AlreadyExists")}
</p>
<a use:link href="/">
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
Login
</button>
</a>
{:else}
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
{@html $t("errors.error_occurred", {
values: { message: $t("errors." + error) },
})}
</p>
<a use:link href="/">
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
{$t("buttons.back_to_homepage")}
</button>
</a>
{/if}
</div>
{/if}
</div>
</CenteredLayout>
<style>
li.clickable {
cursor: pointer;
}
</style>