parent
9c1165653b
commit
663e8d31f1
@ -0,0 +1,27 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2025 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. |
||||||
|
--> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Error page (?o=https://domain.tld was not provided or is invalid) |
||||||
|
--> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { ExclamationTriangle} from "svelte-heros-v2"; |
||||||
|
</script> |
||||||
|
|
||||||
|
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> |
||||||
|
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||||
|
<ExclamationTriangle class="animate-bounce mt-10 h-16 w-16 mx-auto" /> |
||||||
|
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5"> |
||||||
|
Invalid request |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</div> |
@ -1,26 +0,0 @@ |
|||||||
// Copyright (c) 2022-2025 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.
|
|
||||||
|
|
||||||
//import * as sdk from "@ng-org/wasm-tools-auth";
|
|
||||||
import { fromWritablePort } from 'remote-web-streams'; |
|
||||||
|
|
||||||
self.onmessage = (event) => { |
|
||||||
(async function() { |
|
||||||
const { method, args, port } = event.data; |
|
||||||
const writable = fromWritablePort(port); |
|
||||||
const writer = writable.getWriter(); |
|
||||||
//console.log("Message received by worker", method, args);
|
|
||||||
|
|
||||||
let ret = await Reflect.apply(sdk[method], null, args); |
|
||||||
writer.write(ret); |
|
||||||
writer.close(); |
|
||||||
})(); |
|
||||||
} |
|
||||||
|
|
||||||
//console.log("worker loaded");
|
|
@ -1,32 +0,0 @@ |
|||||||
var crypto = require('crypto') |
|
||||||
, fs = require('fs') |
|
||||||
const {gzip, } = require('node-gzip'); |
|
||||||
|
|
||||||
var algorithm = 'sha256' |
|
||||||
, shasum = crypto.createHash(algorithm) |
|
||||||
|
|
||||||
const sha_file = './dist/index.sha256'; |
|
||||||
const gzip_file = './dist/index.gzip'; |
|
||||||
var filename = './dist/index.html' |
|
||||||
, s = fs.ReadStream(filename) |
|
||||||
|
|
||||||
var bufs = []; |
|
||||||
s.on('data', function(data) { |
|
||||||
shasum.update(data) |
|
||||||
bufs.push(data); |
|
||||||
}) |
|
||||||
|
|
||||||
s.on('end', function() { |
|
||||||
var hash = shasum.digest('hex') |
|
||||||
console.log(hash + ' ' + filename) |
|
||||||
|
|
||||||
fs.writeFileSync(sha_file, hash, 'utf8'); |
|
||||||
|
|
||||||
var buf = Buffer.concat(bufs); |
|
||||||
gzip(buf).then((compressed) => {fs.writeFileSync(gzip_file, compressed);}); |
|
||||||
|
|
||||||
fs.rm(filename,()=>{}); |
|
||||||
|
|
||||||
}) |
|
||||||
|
|
||||||
|
|
@ -1,180 +0,0 @@ |
|||||||
<!-- |
|
||||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
|
||||||
// All rights reserved. |
|
||||||
// Licensed under the Apache License, Version 2.0 |
|
||||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
|
||||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
|
||||||
// at your option. All files in the project carrying such |
|
||||||
// notice may not be copied, modified, or distributed except |
|
||||||
// according to those terms. |
|
||||||
--> |
|
||||||
|
|
||||||
<script lang="ts"> |
|
||||||
import { push, default as Router } from "svelte-spa-router"; |
|
||||||
import { isLoading } from "svelte-i18n"; |
|
||||||
|
|
||||||
import { onMount, tick, onDestroy } from "svelte"; |
|
||||||
import { ng, brokers_info } from "./store"; |
|
||||||
import { |
|
||||||
NotFound, |
|
||||||
Test, |
|
||||||
WalletCreate, |
|
||||||
Invitation, |
|
||||||
WalletLogin, |
|
||||||
WalletInfo, |
|
||||||
User, |
|
||||||
UserRegistered, |
|
||||||
Install, |
|
||||||
ScanQRWeb, |
|
||||||
AccountInfo, |
|
||||||
WalletLoginUsername, |
|
||||||
WalletLoginQr, |
|
||||||
WalletLoginTextCode |
|
||||||
} from "@ng-org/ui-common/routes"; |
|
||||||
import { Bowser } from "../../../../sdk/js/lib-wasm/jsland/bowser.js"; |
|
||||||
|
|
||||||
import Home from "./routes/Home.svelte"; |
|
||||||
|
|
||||||
const routes = new Map(); |
|
||||||
routes.set("/", Home); |
|
||||||
// routes.set("/test", Test); |
|
||||||
// routes.set("/wallet/login", WalletLogin); |
|
||||||
// routes.set("/wallet/username", WalletLoginUsername); |
|
||||||
// routes.set("/wallet/login-qr", WalletLoginQr); |
|
||||||
// routes.set("/wallet/login-text-code", WalletLoginTextCode); |
|
||||||
// routes.set("/wallet/create", WalletCreate); |
|
||||||
// routes.set("/i/:invitation", Invitation); |
|
||||||
// routes.set("/user", User); |
|
||||||
// routes.set("/user/registered", UserRegistered); |
|
||||||
// routes.set("/wallet", WalletInfo); |
|
||||||
// routes.set("/user/accounts", AccountInfo); |
|
||||||
// routes.set("/scanqr", ScanQRWeb); |
|
||||||
// routes.set("/install", Install); |
|
||||||
routes.set("*", NotFound); |
|
||||||
|
|
||||||
// window.refresh_wallets = async () => { |
|
||||||
// let walls = await ng.get_wallets(); |
|
||||||
// wallets.set(walls); |
|
||||||
// }; |
|
||||||
|
|
||||||
let no_local_storage = false; |
|
||||||
let is_safari = false; |
|
||||||
|
|
||||||
function load_bootstraps(bs: string | null) { |
|
||||||
if (bs) { |
|
||||||
let bootstrap_map = JSON.parse(bs); |
|
||||||
brokers_info.set(bootstrap_map); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
onMount(async () => { |
|
||||||
|
|
||||||
window.document.getElementById("splash").className="noshow"; |
|
||||||
window.document.getElementById("app").className=""; |
|
||||||
|
|
||||||
let info = Bowser.parse(window.navigator.userAgent); |
|
||||||
//console.log(info); |
|
||||||
is_safari = info.browser.name == "Safari"; |
|
||||||
if (is_safari) return; |
|
||||||
|
|
||||||
window.addEventListener("storage", (event) => { |
|
||||||
//console.log("localStorage event", event); |
|
||||||
if (event.storageArea != localStorage) return; |
|
||||||
if (event.key === "ng_bootstrap") { |
|
||||||
load_bootstraps(event.newValue); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
let ls; |
|
||||||
try { |
|
||||||
ls = localStorage; |
|
||||||
|
|
||||||
try { |
|
||||||
let ret = await document.requestStorageAccess({ localStorage: true }); |
|
||||||
ls = ret.localStorage; |
|
||||||
console.log("REQUEST STORAGE ACCESS GRANTED by chrome"); |
|
||||||
} |
|
||||||
catch(e) { |
|
||||||
console.warn("requestStorageAccess of chrome failed. falling back to previous api", e) |
|
||||||
try { |
|
||||||
await document.requestStorageAccess(); |
|
||||||
localStorage; |
|
||||||
console.log("REQUEST STORAGE ACCESS GRANTED"); |
|
||||||
} catch (e) { |
|
||||||
console.error("REQUEST STORAGE ACCESS DENIED",e); |
|
||||||
no_local_storage = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} catch (e) { |
|
||||||
no_local_storage = true; |
|
||||||
console.log("no access to localStorage",e) |
|
||||||
} |
|
||||||
|
|
||||||
if (!no_local_storage) { |
|
||||||
try { |
|
||||||
load_bootstraps(ls.getItem("ng_bootstrap")); |
|
||||||
} catch (e) { |
|
||||||
console.log("load_bootstraps failed") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
</script> |
|
||||||
{#if is_safari} |
|
||||||
|
|
||||||
<div class="text-center 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> |
|
||||||
<p class="mb-5"> |
|
||||||
We are sorry but Safari is not supported yet<br/>for WebApps authentication with your Wallet.<br/>Please use another browser. |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
|
|
||||||
{:else if no_local_storage} |
|
||||||
|
|
||||||
<div class="text-center 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> |
|
||||||
<p class="mb-5"> |
|
||||||
Please give access to localStorage for the website<br/> |
|
||||||
{location.origin} |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
|
|
||||||
{:else} |
|
||||||
|
|
||||||
{#if $isLoading} |
|
||||||
<p class="text-center">Loading translations...</p> |
|
||||||
{:else} |
|
||||||
<Router {routes} /> |
|
||||||
{/if} |
|
||||||
|
|
||||||
{/if} |
|
Before Width: | Height: | Size: 1.1 KiB |
@ -1,160 +0,0 @@ |
|||||||
<!-- |
|
||||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
|
||||||
// All rights reserved. |
|
||||||
// Licensed under the Apache License, Version 2.0 |
|
||||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
|
||||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
|
||||||
// at your option. All files in the project carrying such |
|
||||||
// notice may not be copied, modified, or distributed except |
|
||||||
// according to those terms. |
|
||||||
--> |
|
||||||
<script lang="ts"> |
|
||||||
import { onMount } from "svelte"; |
|
||||||
import { t, locale } from "svelte-i18n"; |
|
||||||
import { CenteredLayout } from "@ng-org/ui-common/lib"; |
|
||||||
import { LogoSimple } from "@ng-org/ui-common/components"; |
|
||||||
import { |
|
||||||
Sidebar, |
|
||||||
SidebarGroup, |
|
||||||
SidebarItem, |
|
||||||
SidebarWrapper, |
|
||||||
} from "flowbite-svelte"; |
|
||||||
import { |
|
||||||
ComputerDesktop, |
|
||||||
GlobeAlt, |
|
||||||
ServerStack |
|
||||||
} from "svelte-heros-v2"; |
|
||||||
|
|
||||||
import { web_origin, brokers_info, selected_broker } from '../store'; |
|
||||||
import { fromWritablePort, RemoteReadableStream } from 'remote-web-streams'; |
|
||||||
|
|
||||||
let top; |
|
||||||
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"; |
|
||||||
|
|
||||||
const AUTH_HOME = "#/"; |
|
||||||
|
|
||||||
function select(broker: Object) { |
|
||||||
|
|
||||||
let url; |
|
||||||
if (import.meta.env.NG_DEV && broker.localhost === 14400) { |
|
||||||
// dev mode |
|
||||||
url = "http://localhost:1421/appauth.html"; |
|
||||||
} else if (broker.localhost) { |
|
||||||
url = `http://localhost:${broker.localhost}/auth/`; |
|
||||||
} else if (broker.private) { |
|
||||||
//TODO |
|
||||||
url = `http://unimplemented/auth/`; |
|
||||||
} else if (broker.domain) { |
|
||||||
url = `https://${broker.domain}/auth/`; |
|
||||||
} else if (broker.ngbox) { |
|
||||||
url = `https://nextgraph.app/auth/`; |
|
||||||
} else return; |
|
||||||
|
|
||||||
selected_broker.set(broker); |
|
||||||
let iframe = window.document.getElementById("nextgraph-app-auth-iframe"); |
|
||||||
iframe?.classList.add('nextgraph-app-auth-iframe--active'); |
|
||||||
window.document.getElementById("app").style["display"] = "none"; |
|
||||||
|
|
||||||
(<any>window).ng_iframe_origin = new URL(url).origin; |
|
||||||
|
|
||||||
iframe.addEventListener("load", function() { |
|
||||||
|
|
||||||
(<any>window).ng_broker_selected = this.contentWindow; |
|
||||||
const ready_handler = async function(m) { |
|
||||||
if (m.data.ready && m.origin === (<any>window).ng_iframe_origin) { |
|
||||||
//console.log("got ready message",m); |
|
||||||
//remove this listener |
|
||||||
window.removeEventListener("message",ready_handler); |
|
||||||
const { readable, writablePort } = new RemoteReadableStream(); |
|
||||||
//console.log("sending init message to app-auth"); |
|
||||||
(<any>window).ng_broker_selected.postMessage({ method: "init", manifest:window.ng_manifest, port: writablePort }, (<any>window).ng_iframe_origin, [writablePort]); |
|
||||||
const reader = readable.getReader(); |
|
||||||
for (var msg; msg = await reader.read(); ) { |
|
||||||
if (msg.done) { |
|
||||||
(<any>window).ng_status_callback.close(); |
|
||||||
break; |
|
||||||
} else { |
|
||||||
//console.log("forwarding upstream",msg.value); |
|
||||||
(<any>window).ng_status_callback.write(msg.value); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
window.addEventListener("message",ready_handler); |
|
||||||
}); |
|
||||||
|
|
||||||
iframe.src = url+"?o="+location.search.substring(3)+AUTH_HOME; |
|
||||||
} |
|
||||||
|
|
||||||
onMount(() => { |
|
||||||
if (Object.keys($brokers_info).length == 1) { |
|
||||||
select(Object.values($brokers_info)[0]); |
|
||||||
} |
|
||||||
}); |
|
||||||
</script> |
|
||||||
|
|
||||||
{#if Object.keys($brokers_info).length > 1} |
|
||||||
<CenteredLayout> |
|
||||||
<div class="container3" bind:this={top}> |
|
||||||
<div class="row"> |
|
||||||
<LogoSimple/> |
|
||||||
</div> |
|
||||||
<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" role="menu"> |
|
||||||
<li> |
|
||||||
<h2 class="text-xl mb-6">{@html $t("auth.select_broker", {values: { origin:$web_origin }})}</h2> |
|
||||||
</li> |
|
||||||
{#each Object.entries($brokers_info) as broker} |
|
||||||
<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={()=>select(broker[1])} |
|
||||||
on:click={()=>select(broker[1])} |
|
||||||
> |
|
||||||
{#if broker[1].localhost} |
|
||||||
<ComputerDesktop tabindex="-1" |
|
||||||
class="w-10 h-10 mr-4 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"/> |
|
||||||
{:else if broker[1].domain} |
|
||||||
<GlobeAlt tabindex="-1" |
|
||||||
class="w-10 min-w-10 h-10 mr-4 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"/> |
|
||||||
{:else if broker[1].private} |
|
||||||
<ServerStack tabindex="-1" |
|
||||||
class="w-10 h-10 mr-4 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"/> |
|
||||||
{:else if broker[1].ngbox} |
|
||||||
<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> |
|
||||||
{/if} |
|
||||||
<span class="text-left text-xl ml-3" style="overflow-wrap: anywhere;">{broker[0]}</span> |
|
||||||
</li> |
|
||||||
{/each} |
|
||||||
</SidebarGroup> |
|
||||||
</SidebarWrapper> |
|
||||||
</Sidebar> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</CenteredLayout> |
|
||||||
{/if} |
|
@ -1,99 +0,0 @@ |
|||||||
<!-- |
|
||||||
// Copyright (c) 2022-2025 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. |
|
||||||
--> |
|
||||||
|
|
||||||
<!-- |
|
||||||
Home page to display for logged in users. |
|
||||||
Redirects to no-wallet or login page, if not logged in. |
|
||||||
--> |
|
||||||
|
|
||||||
<script> |
|
||||||
import Home from "../lib/Home.svelte"; |
|
||||||
import { t, locale } from "svelte-i18n"; |
|
||||||
import { NoWallet } from "@ng-org/ui-common/lib"; |
|
||||||
import { push } from "svelte-spa-router"; |
|
||||||
import { onMount, onDestroy } from "svelte"; |
|
||||||
import { brokers_info, web_origin } from "../store"; |
|
||||||
|
|
||||||
</script> |
|
||||||
|
|
||||||
{#if Object.keys($brokers_info).length == 0} |
|
||||||
<!-- <NoWallet /> --> |
|
||||||
{#if import.meta.env.NG_DEV} |
|
||||||
<div class="text-center 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> |
|
||||||
<p class="mb-5"> |
|
||||||
We could not find a wallet in your browser.<br/> |
|
||||||
This is probably due to the recent changes made to the framework.<br/> |
|
||||||
Please create a new wallet from this browser and try again, |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
{:else} |
|
||||||
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-blue-600"> |
|
||||||
|
|
||||||
<svg |
|
||||||
class="y-10 h-16 w-16 mx-auto mb-3" |
|
||||||
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> |
|
||||||
We could not find a wallet in your browser.<br/> |
|
||||||
For now, creating a new wallet while a Web App<br/> |
|
||||||
is authenticating, is not implemented. Please<br/> |
|
||||||
create or import your wallet in a new tab by<br/><a href="https://nextgraph.eu" target="_blank">clicking here</a> |
|
||||||
</div> |
|
||||||
{/if} |
|
||||||
{:else if $web_origin} |
|
||||||
<Home /> |
|
||||||
{:else} |
|
||||||
<div class="text-center 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> |
|
||||||
<p class="mb-5"> |
|
||||||
{@html $t("auth.unexpected_error")} |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
{/if} |
|
@ -1,53 +0,0 @@ |
|||||||
// Copyright (c) 2022-2025 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.
|
|
||||||
|
|
||||||
import { |
|
||||||
writable, |
|
||||||
readable, |
|
||||||
readonly, |
|
||||||
derived, |
|
||||||
get, |
|
||||||
type Writable, |
|
||||||
} from "svelte/store"; |
|
||||||
|
|
||||||
import { createAsyncProxy } from "async-proxy"; |
|
||||||
import { RemoteReadableStream } from 'remote-web-streams'; |
|
||||||
|
|
||||||
export const selected_broker = writable<undefined | Object>( undefined ); |
|
||||||
|
|
||||||
export const brokers_info = writable( {} ); |
|
||||||
|
|
||||||
export const unlocked_wallet = writable(undefined); |
|
||||||
|
|
||||||
export const web_origin = writable(""); |
|
||||||
|
|
||||||
import worker_ from "./worker.js?worker&inline"; |
|
||||||
const worker = new worker_(); |
|
||||||
|
|
||||||
async function rpc( method:string, args?: any) : Promise<any> { |
|
||||||
const { readable, writablePort } = new RemoteReadableStream(); |
|
||||||
worker.postMessage({ method, args, port: writablePort }, [writablePort]); |
|
||||||
const reader = readable.getReader(); |
|
||||||
let ret = await reader.read(); |
|
||||||
await reader.read(); // the close.
|
|
||||||
return ret.value; |
|
||||||
} |
|
||||||
|
|
||||||
const handler = { |
|
||||||
async apply(_target: object, path: PropertyKey[], _caller: any, args?: any) :Promise<any> { |
|
||||||
|
|
||||||
if (path[0] === "login") { |
|
||||||
|
|
||||||
} else { |
|
||||||
return await rpc(<string>path[0], args); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
export const ng = createAsyncProxy({}, handler); |
|
@ -1,2 +1 @@ |
|||||||
/// <reference types="svelte" />
|
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
@ -1,26 +0,0 @@ |
|||||||
// Copyright (c) 2022-2025 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.
|
|
||||||
|
|
||||||
//import * as sdk from "@ng-org/wasm-tools-auth";
|
|
||||||
import { fromWritablePort } from 'remote-web-streams'; |
|
||||||
|
|
||||||
self.onmessage = (event) => { |
|
||||||
(async function() { |
|
||||||
const { method, args, port } = event.data; |
|
||||||
const writable = fromWritablePort(port); |
|
||||||
const writer = writable.getWriter(); |
|
||||||
console.log("Message received by worker", method, args); |
|
||||||
|
|
||||||
let ret = await Reflect.apply(sdk[method], null, args); |
|
||||||
writer.write(ret); |
|
||||||
writer.close(); |
|
||||||
})(); |
|
||||||
} |
|
||||||
|
|
||||||
console.log("worker loaded"); |
|
@ -1,70 +1,14 @@ |
|||||||
import { defineConfig } from 'vite' |
import { defineConfig } from 'vite' |
||||||
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte' |
|
||||||
import sveltePreprocess from "svelte-preprocess"; |
|
||||||
import svelteSVG from "vite-plugin-svelte-svg"; |
|
||||||
import wasm from "vite-plugin-wasm"; |
|
||||||
import topLevelAwait from "vite-plugin-top-level-await"; |
|
||||||
import { viteSingleFile } from "vite-plugin-singlefile" |
import { viteSingleFile } from "vite-plugin-singlefile" |
||||||
|
|
||||||
const jsToBottom = () => { |
|
||||||
return { |
|
||||||
name: "script-at-end-of-body", |
|
||||||
transformIndexHtml(html) { |
|
||||||
let scriptTag = html.match(/<script type[^>]*>(.*?)<\/script[^>]*>/)[0] |
|
||||||
//console.log("\n SCRIPT TAG", scriptTag, "\n")
|
|
||||||
html = html.replace(scriptTag, "") |
|
||||||
html = html.replace("<!-- # INSERT SCRIPT HERE -->", scriptTag) |
|
||||||
return html; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({ |
export default defineConfig({ |
||||||
envPrefix: ["VITE_", "NG_"], |
envPrefix: ["VITE_", "NG_"], |
||||||
server: { |
server: { |
||||||
port: 14402 |
port: 14404 |
||||||
}, |
}, |
||||||
worker: { |
|
||||||
format: 'es', |
|
||||||
plugins : [ |
|
||||||
topLevelAwait(), |
|
||||||
wasm(), |
|
||||||
viteSingleFile() |
|
||||||
] |
|
||||||
}, |
|
||||||
plugins: [ |
plugins: [ |
||||||
topLevelAwait(), |
viteSingleFile() |
||||||
wasm(), |
|
||||||
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'
|
|
||||||
}), |
|
||||||
viteSingleFile(), |
|
||||||
jsToBottom(), |
|
||||||
] |
] |
||||||
}) |
}) |
||||||
|
@ -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; |
@ -0,0 +1,180 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
--> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import { push, default as Router, querystring } from "svelte-spa-router"; |
||||||
|
import { onMount, tick, onDestroy } from "svelte"; |
||||||
|
import { |
||||||
|
NotFound, |
||||||
|
} from "@ng-org/ui-common/routes"; |
||||||
|
import {error} from "./store"; |
||||||
|
|
||||||
|
import Home from "./routes/Home.svelte"; |
||||||
|
import Error from "./routes/Error.svelte"; |
||||||
|
import is_ip_private from 'private-ip'; |
||||||
|
|
||||||
|
function base64UrlDecode(str) { |
||||||
|
const padding = '='.repeat((4 - str.length % 4) % 4); |
||||||
|
const base64 = str.replace(/-/g, '+').replace(/_/g, '/') + padding; |
||||||
|
return atob(base64); |
||||||
|
} |
||||||
|
|
||||||
|
const routes = new Map(); |
||||||
|
routes.set("/", Home); |
||||||
|
routes.set("/error", Error); |
||||||
|
routes.set("*", NotFound); |
||||||
|
|
||||||
|
// TODO: take this list from local API |
||||||
|
const bsp_list = [ |
||||||
|
"https://nextgraph.eu", |
||||||
|
"https://nextgraph.one", |
||||||
|
"https://stage1.nextgraph.eu", |
||||||
|
]; |
||||||
|
|
||||||
|
let channel; |
||||||
|
try { |
||||||
|
channel = new BroadcastChannel("ng_bootstrap"); |
||||||
|
channel.onmessage = (event) => { |
||||||
|
if (event.origin !== location.origin) return; |
||||||
|
if (!event.data.key) return; |
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
let bootstraps = JSON.parse(localStorage.getItem("ng_bootstrap") || "{}"); |
||||||
|
if (event.data.value){ |
||||||
|
//console.log("received added",event.data.key, event.data.value); |
||||||
|
if(!bootstraps[event.data.key]) { |
||||||
|
bootstraps[event.data.key] = event.data.value; |
||||||
|
localStorage.setItem("ng_bootstrap",JSON.stringify(bootstraps)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
//console.log("received removed", event.data.key); |
||||||
|
if ( bootstraps[event.data.key]) { |
||||||
|
delete bootstraps[event.data.key]; |
||||||
|
localStorage.setItem("ng_bootstrap",JSON.stringify(bootstraps)); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log("localStorage error in BroadcastChannel",e) |
||||||
|
} |
||||||
|
})(); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (e) { |
||||||
|
console.error("error in BroadcastChannel",e) |
||||||
|
} |
||||||
|
|
||||||
|
onMount(() => { |
||||||
|
|
||||||
|
const param = new URLSearchParams($querystring); |
||||||
|
let method = param.get("m"); |
||||||
|
let url; |
||||||
|
let msgs; |
||||||
|
try { |
||||||
|
url = new URL(decodeURIComponent(param.get("ab"))); |
||||||
|
msgs = JSON.parse(base64UrlDecode(param.get("b"))); |
||||||
|
if (!method) |
||||||
|
throw new Error("InvalidValue"); |
||||||
|
} |
||||||
|
catch (e) { |
||||||
|
console.error(e); |
||||||
|
error.set(e); |
||||||
|
push("#/error"); |
||||||
|
return; |
||||||
|
} |
||||||
|
let origin_url = url.origin; |
||||||
|
let hostname = url.hostname; |
||||||
|
|
||||||
|
|
||||||
|
let is_ng_box = origin_url === "https://nextgraph.app"; |
||||||
|
let is_domain = false; |
||||||
|
let is_lan = false; |
||||||
|
let is_local = false; |
||||||
|
if (!is_ng_box) { |
||||||
|
is_local = origin_url.startsWith("http://localhost"); |
||||||
|
if (!is_local) { |
||||||
|
is_lan = !!is_ip_private(hostname); |
||||||
|
if (!is_lan) |
||||||
|
is_domain = bsp_list.includes(origin_url); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function abort(error) { |
||||||
|
console.error(error); |
||||||
|
let u = url.toString(); |
||||||
|
window.location.href = u + "&re=" + error; |
||||||
|
throw new Error(error); |
||||||
|
} |
||||||
|
try { |
||||||
|
let keys: Array<string> = []; |
||||||
|
for (const data of msgs) { |
||||||
|
|
||||||
|
let key; |
||||||
|
|
||||||
|
//console.log("ng_bootstrap received msg",JSON.stringify(data), is_ng_box, is_domain,is_lan,is_local,new URL(origin_url).hostname, new URL(origin_url).hostname === data.domain, data.domain && is_domain && new URL(origin_url).hostname === data.domain ) |
||||||
|
|
||||||
|
if (data.ngbox && (is_ng_box || is_lan || is_local || is_domain)) { |
||||||
|
key = "Self-hosted / NGbox"; |
||||||
|
} else if (data.domain && is_domain && new URL(origin_url).hostname === data.domain ) { |
||||||
|
key = data.domain; |
||||||
|
//console.log("key for domain is", key) |
||||||
|
} else if (data.localhost && (is_local || is_lan)) { |
||||||
|
if (!data.peer_id) { |
||||||
|
abort("missing peer_id of localhost"); |
||||||
|
} |
||||||
|
let port = Number(new URL(origin_url).port || '80'); |
||||||
|
if (!import.meta.env.NG_DEV && !import.meta.env.DEV && is_local && port !== data.localhost) { |
||||||
|
abort("mismatch of localhost port"); |
||||||
|
} |
||||||
|
key = `Local port ${data.localhost}`; |
||||||
|
} else if (data.private && (is_lan || is_local )) { |
||||||
|
if (!data.peer_id) { |
||||||
|
abort("missing peer_id of LAN"); |
||||||
|
} |
||||||
|
key = `Network ${data.peer_id.substring(0,7)}`; |
||||||
|
} else { |
||||||
|
abort("mismatch between origin and msg"); |
||||||
|
} |
||||||
|
keys.push(key); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
let bootstraps = JSON.parse(localStorage.getItem("ng_bootstrap") || "{}"); |
||||||
|
for (const [i, key] of keys.entries()) { |
||||||
|
//console.log(method, method === "add",bootstraps, !bootstraps[key]) |
||||||
|
const value = msgs[i]; |
||||||
|
if ( method === "add" && !bootstraps[key]) { |
||||||
|
//console.log("adding..."+key) |
||||||
|
bootstraps[key] = value; |
||||||
|
if (channel) channel.postMessage({ key, value }); |
||||||
|
//console.log("added",key,value); |
||||||
|
} else if ( method === "remove" && bootstraps[key]) { |
||||||
|
delete bootstraps[key]; |
||||||
|
if (channel) channel.postMessage({ key }); |
||||||
|
//console.log("removed",key); |
||||||
|
} |
||||||
|
} |
||||||
|
localStorage.setItem("ng_bootstrap",JSON.stringify(bootstraps)); |
||||||
|
} catch (e) { |
||||||
|
abort("NoLocalStorage"); |
||||||
|
} |
||||||
|
|
||||||
|
let u = url.toString(); |
||||||
|
// url.searchParams.set('i', param.get("i")); |
||||||
|
// url.searchParams.set('rs', param.get("rs")); |
||||||
|
// url.searchParams.set('ab', "1"); |
||||||
|
window.location.href = u + "&ab=1"; |
||||||
|
}catch {} |
||||||
|
}); |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<Router {routes} /> |
||||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,28 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2025 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. |
||||||
|
--> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Error page (?o=https://domain.tld was not provided or is invalid) |
||||||
|
--> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { ExclamationTriangle} from "svelte-heros-v2"; |
||||||
|
import {error} from "../store"; |
||||||
|
</script> |
||||||
|
|
||||||
|
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> |
||||||
|
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||||
|
<ExclamationTriangle class="animate-bounce mt-10 h-16 w-16 mx-auto" /> |
||||||
|
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5"> |
||||||
|
{$error} |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</div> |
@ -0,0 +1,20 @@ |
|||||||
|
<!-- |
||||||
|
// Copyright (c) 2022-2025 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 Logo from "../assets/nextgraph.svg?component"; |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<a href="https://nextgraph.org"> |
||||||
|
<Logo style="margin:auto;" class="logo block h-40" alt="NextGraph" /> |
||||||
|
</a> |
||||||
|
|
@ -0,0 +1,15 @@ |
|||||||
|
// Copyright (c) 2022-2025 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.
|
||||||
|
|
||||||
|
import { |
||||||
|
writable, |
||||||
|
type Writable, |
||||||
|
} from "svelte/store"; |
||||||
|
|
||||||
|
export const error = writable(""); |
@ -1,32 +0,0 @@ |
|||||||
var crypto = require('crypto') |
|
||||||
, fs = require('fs') |
|
||||||
const {gzip, } = require('node-gzip'); |
|
||||||
|
|
||||||
var algorithm = 'sha256' |
|
||||||
, shasum = crypto.createHash(algorithm) |
|
||||||
|
|
||||||
const sha_file = './dist/index.sha256'; |
|
||||||
const gzip_file = './dist/index.gzip'; |
|
||||||
var filename = './dist/index.html' |
|
||||||
, s = fs.ReadStream(filename) |
|
||||||
|
|
||||||
var bufs = []; |
|
||||||
s.on('data', function(data) { |
|
||||||
shasum.update(data) |
|
||||||
bufs.push(data); |
|
||||||
}) |
|
||||||
|
|
||||||
s.on('end', function() { |
|
||||||
var hash = shasum.digest('hex') |
|
||||||
console.log(hash + ' ' + filename) |
|
||||||
|
|
||||||
fs.writeFileSync(sha_file, hash, 'utf8'); |
|
||||||
|
|
||||||
var buf = Buffer.concat(bufs); |
|
||||||
gzip(buf).then((compressed) => {fs.writeFileSync(gzip_file, compressed);}); |
|
||||||
|
|
||||||
fs.rm(filename,()=>{}); |
|
||||||
|
|
||||||
}) |
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.9 KiB |
@ -1,26 +0,0 @@ |
|||||||
// Copyright (c) 2022-2025 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.
|
|
||||||
|
|
||||||
//import * as sdk from "@ng-org/wasm-tools-auth";
|
|
||||||
import { fromWritablePort } from 'remote-web-streams'; |
|
||||||
|
|
||||||
self.onmessage = (event) => { |
|
||||||
(async function() { |
|
||||||
const { method, args, port } = event.data; |
|
||||||
const writable = fromWritablePort(port); |
|
||||||
const writer = writable.getWriter(); |
|
||||||
console.log("Message received by worker", method, args); |
|
||||||
|
|
||||||
let ret = await Reflect.apply(sdk[method], null, args); |
|
||||||
writer.write(ret); |
|
||||||
writer.close(); |
|
||||||
})(); |
|
||||||
} |
|
||||||
|
|
||||||
console.log("worker loaded"); |
|
Before Width: | Height: | Size: 1.1 KiB |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@ |
|||||||
|
# Logs |
||||||
|
logs |
||||||
|
*.log |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
pnpm-debug.log* |
||||||
|
lerna-debug.log* |
||||||
|
|
||||||
|
node_modules |
||||||
|
dist |
||||||
|
dist-ssr |
||||||
|
*.local |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.vscode/* |
||||||
|
!.vscode/extensions.json |
||||||
|
.idea |
||||||
|
.DS_Store |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
*.sw? |
@ -0,0 +1,57 @@ |
|||||||
|
# example-webapp-react |
||||||
|
|
||||||
|
Example of a Web app made with NextGraph, using React and LDO, and Vite |
||||||
|
|
||||||
|
## NextGraph |
||||||
|
|
||||||
|
> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. |
||||||
|
> |
||||||
|
> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. |
||||||
|
> |
||||||
|
> More info here [https://nextgraph.org](https://nextgraph.org) |
||||||
|
|
||||||
|
## For developing against a public Broker |
||||||
|
|
||||||
|
``` |
||||||
|
npm install |
||||||
|
npm run dev |
||||||
|
``` |
||||||
|
|
||||||
|
You will have to use a Wallet that was created on one of our public Broker Service Providers (nextgraph.eu by example) before you can actually login. We didn't implement yet the option to create a Wallet while you are using or developing a 3rd party app. |
||||||
|
|
||||||
|
## For developing locally |
||||||
|
|
||||||
|
you need to have a running local ngd server. See those [instructions first](https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/DEV.md#first-run). |
||||||
|
|
||||||
|
If you are running a local devenv for the frontend of nextGraph on http://localhost:1421 , then (and only then) you need to compile the @ng-org/web package in dev mode: |
||||||
|
|
||||||
|
``` |
||||||
|
pnpm run -C ../../web builddev |
||||||
|
``` |
||||||
|
|
||||||
|
Due to the way `npm link` works, you will have to run this command again, after each time you use `npm install`. |
||||||
|
|
||||||
|
Otherwise, if you are using http://localhost:14400 in your browser, just skip the line above, and continue with those: |
||||||
|
|
||||||
|
``` |
||||||
|
pnpm install |
||||||
|
pnpm dev |
||||||
|
``` |
||||||
|
|
||||||
|
Open this URL in browser : [http://localhost:5173](http://localhost:5173) |
||||||
|
|
||||||
|
See the example code in [src/main.tsx](./src/App.tsx) |
||||||
|
|
||||||
|
## License |
||||||
|
|
||||||
|
Licensed under either of |
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0) |
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) |
||||||
|
at your option. |
||||||
|
|
||||||
|
`SPDX-License-Identifier: Apache-2.0 OR MIT` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/assure) and the [NGI Zero Commons Fund](https://nlnet.nl/commonsfund/), both funds established by [NLnet](https://nlnet.nl/) Foundation with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreements No 957073 and No 101092990, respectively. |
@ -0,0 +1,28 @@ |
|||||||
|
import js from '@eslint/js' |
||||||
|
import globals from 'globals' |
||||||
|
import reactHooks from 'eslint-plugin-react-hooks' |
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh' |
||||||
|
import tseslint from 'typescript-eslint' |
||||||
|
|
||||||
|
export default tseslint.config( |
||||||
|
{ ignores: ['dist'] }, |
||||||
|
{ |
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended], |
||||||
|
files: ['**/*.{ts,tsx}'], |
||||||
|
languageOptions: { |
||||||
|
ecmaVersion: 2020, |
||||||
|
globals: globals.browser, |
||||||
|
}, |
||||||
|
plugins: { |
||||||
|
'react-hooks': reactHooks, |
||||||
|
'react-refresh': reactRefresh, |
||||||
|
}, |
||||||
|
rules: { |
||||||
|
...reactHooks.configs.recommended.rules, |
||||||
|
'react-refresh/only-export-components': [ |
||||||
|
'warn', |
||||||
|
{ allowConstantExport: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
) |
@ -0,0 +1,12 @@ |
|||||||
|
<!doctype html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8" /> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||||
|
<title>NextGraph WebApp React+LDO example</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="app"></div> |
||||||
|
<script type="module" src="/src/main.tsx"></script> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,40 @@ |
|||||||
|
{ |
||||||
|
"name": "@ng-org/example-react-ldo", |
||||||
|
"private": true, |
||||||
|
"version": "0.0.0", |
||||||
|
"type": "module", |
||||||
|
"scripts": { |
||||||
|
"dev": "vite", |
||||||
|
"build": "tsc --noEmit && vite build", |
||||||
|
"lint": "eslint .", |
||||||
|
"preview": "vite preview", |
||||||
|
"build:ldo": "ldo build --input src/.shapes --output src/.ldo" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@ldo/connected-nextgraph": "^1.0.0-alpha.15", |
||||||
|
"@ldo/ldo": "^1.0.0-alpha.15", |
||||||
|
"@ldo/react": "^1.0.0-alpha.15", |
||||||
|
"@ng-org/web": "workspace:*", |
||||||
|
"react": "^19.0.0", |
||||||
|
"react-dom": "^19.0.0" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@eslint/js": "^9.22.0", |
||||||
|
"@ldo/cli": "^1.0.0-alpha.15", |
||||||
|
"@types/jsonld": "^1.5.15", |
||||||
|
"@types/react": "^19.0.10", |
||||||
|
"@types/react-dom": "^19.0.4", |
||||||
|
"@types/shexj": "^2.1.7", |
||||||
|
"@vitejs/plugin-react": "^4.3.4", |
||||||
|
"autoprefixer": "^10.4.21", |
||||||
|
"eslint": "^9.22.0", |
||||||
|
"eslint-plugin-react-hooks": "^5.2.0", |
||||||
|
"eslint-plugin-react-refresh": "^0.4.19", |
||||||
|
"globals": "^16.0.0", |
||||||
|
"postcss": "^8.5.3", |
||||||
|
"tailwindcss": "^3.4.17", |
||||||
|
"typescript": "~5.7.2", |
||||||
|
"typescript-eslint": "^8.26.1", |
||||||
|
"vite": "^6.3.1" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
export default { |
||||||
|
plugins: { |
||||||
|
tailwindcss: {}, |
||||||
|
autoprefixer: {}, |
||||||
|
}, |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
import { LdoJsonldContext } from "@ldo/ldo"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* contactContext: JSONLD Context for contact |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const contactContext: LdoJsonldContext = { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
}, |
||||||
|
Individual: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#Individual", |
||||||
|
"@context": { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
}, |
||||||
|
fn: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
hasEmail: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
Person: { |
||||||
|
"@id": "http://schema.org/Person", |
||||||
|
"@context": { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
}, |
||||||
|
fn: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
hasEmail: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
Person2: { |
||||||
|
"@id": "http://xmlns.com/foaf/0.1/Person", |
||||||
|
"@context": { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
}, |
||||||
|
fn: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
hasEmail: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
fn: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
hasEmail: { |
||||||
|
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,115 @@ |
|||||||
|
import { Schema } from "shexj"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* contactSchema: ShexJ Schema for contact |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const contactSchema: Schema = { |
||||||
|
type: "Schema", |
||||||
|
shapes: [ |
||||||
|
{ |
||||||
|
id: "did:ng:n:g:x:social:contact#SocialContact", |
||||||
|
type: "ShapeDecl", |
||||||
|
shapeExpr: { |
||||||
|
type: "Shape", |
||||||
|
expression: { |
||||||
|
type: "EachOf", |
||||||
|
expressions: [ |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
values: ["http://www.w3.org/2006/vcard/ns#Individual"], |
||||||
|
}, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Defines the node as an Individual (from vcard)", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
values: ["http://schema.org/Person"], |
||||||
|
}, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Defines the node as a Person (from Schema.org)", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
values: ["http://xmlns.com/foaf/0.1/Person"], |
||||||
|
}, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Defines the node as a Person (from foaf)", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/2006/vcard/ns#fn", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: |
||||||
|
"The formatted name of a person. Example: John Smith", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "The person's email.", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
@ -0,0 +1,19 @@ |
|||||||
|
import { ShapeType } from "@ldo/ldo"; |
||||||
|
import { contactSchema } from "./contact.schema"; |
||||||
|
import { contactContext } from "./contact.context"; |
||||||
|
import { SocialContact } from "./contact.typings"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* LDO ShapeTypes contact |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* SocialContact ShapeType |
||||||
|
*/ |
||||||
|
export const SocialContactShapeType: ShapeType<SocialContact> = { |
||||||
|
schema: contactSchema, |
||||||
|
shape: "did:ng:n:g:x:social:contact#SocialContact", |
||||||
|
context: contactContext, |
||||||
|
}; |
@ -0,0 +1,37 @@ |
|||||||
|
import { LdoJsonldContext, LdSet } from "@ldo/ldo"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* Typescript Typings for contact |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* SocialContact Type |
||||||
|
*/ |
||||||
|
export interface SocialContact { |
||||||
|
"@id"?: string; |
||||||
|
"@context"?: LdoJsonldContext; |
||||||
|
/** |
||||||
|
* Defines the node as an Individual (from vcard) | Defines the node as a Person (from Schema.org) | Defines the node as a Person (from foaf) |
||||||
|
*/ |
||||||
|
type: LdSet< |
||||||
|
| { |
||||||
|
"@id": "Individual"; |
||||||
|
} |
||||||
|
| { |
||||||
|
"@id": "Person"; |
||||||
|
} |
||||||
|
| { |
||||||
|
"@id": "Person2"; |
||||||
|
} |
||||||
|
>; |
||||||
|
/** |
||||||
|
* The formatted name of a person. Example: John Smith |
||||||
|
*/ |
||||||
|
fn?: string; |
||||||
|
/** |
||||||
|
* The person's email. |
||||||
|
*/ |
||||||
|
hasEmail?: string; |
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
import { LdoJsonldContext } from "@ldo/ldo"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* containerContext: JSONLD Context for container |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const containerContext: LdoJsonldContext = { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
"@isCollection": true, |
||||||
|
}, |
||||||
|
Container: { |
||||||
|
"@id": "http://www.w3.org/ns/ldp#Container", |
||||||
|
"@context": { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
"@isCollection": true, |
||||||
|
}, |
||||||
|
modified: { |
||||||
|
"@id": "http://purl.org/dc/terms/modified", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
contains: { |
||||||
|
"@id": "http://www.w3.org/ns/ldp#contains", |
||||||
|
"@type": "@id", |
||||||
|
"@isCollection": true, |
||||||
|
}, |
||||||
|
mtime: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
}, |
||||||
|
size: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
Resource: { |
||||||
|
"@id": "http://www.w3.org/ns/ldp#Resource", |
||||||
|
"@context": { |
||||||
|
type: { |
||||||
|
"@id": "@type", |
||||||
|
"@isCollection": true, |
||||||
|
}, |
||||||
|
modified: { |
||||||
|
"@id": "http://purl.org/dc/terms/modified", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
contains: { |
||||||
|
"@id": "http://www.w3.org/ns/ldp#contains", |
||||||
|
"@type": "@id", |
||||||
|
"@isCollection": true, |
||||||
|
}, |
||||||
|
mtime: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
}, |
||||||
|
size: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
modified: { |
||||||
|
"@id": "http://purl.org/dc/terms/modified", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
contains: { |
||||||
|
"@id": "http://www.w3.org/ns/ldp#contains", |
||||||
|
"@type": "@id", |
||||||
|
"@isCollection": true, |
||||||
|
}, |
||||||
|
mtime: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
}, |
||||||
|
size: { |
||||||
|
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||||
|
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,124 @@ |
|||||||
|
import { Schema } from "shexj"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* containerSchema: ShexJ Schema for container |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const containerSchema: Schema = { |
||||||
|
type: "Schema", |
||||||
|
shapes: [ |
||||||
|
{ |
||||||
|
id: "http://www.w3.org/ns/lddps#Container", |
||||||
|
type: "ShapeDecl", |
||||||
|
shapeExpr: { |
||||||
|
type: "Shape", |
||||||
|
expression: { |
||||||
|
id: "http://www.w3.org/ns/lddps#ContainerShape", |
||||||
|
type: "EachOf", |
||||||
|
expressions: [ |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
values: [ |
||||||
|
"http://www.w3.org/ns/ldp#Container", |
||||||
|
"http://www.w3.org/ns/ldp#Resource", |
||||||
|
], |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: -1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "A container", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://purl.org/dc/terms/modified", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Date modified", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/ldp#contains", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
nodeKind: "iri", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: -1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "Defines a Resource", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/posix/stat#mtime", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#decimal", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "?", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: "TripleConstraint", |
||||||
|
predicate: "http://www.w3.org/ns/posix/stat#size", |
||||||
|
valueExpr: { |
||||||
|
type: "NodeConstraint", |
||||||
|
datatype: "http://www.w3.org/2001/XMLSchema#integer", |
||||||
|
}, |
||||||
|
min: 0, |
||||||
|
max: 1, |
||||||
|
annotations: [ |
||||||
|
{ |
||||||
|
type: "Annotation", |
||||||
|
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||||
|
object: { |
||||||
|
value: "size of this container", |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
@ -0,0 +1,19 @@ |
|||||||
|
import { ShapeType } from "@ldo/ldo"; |
||||||
|
import { containerSchema } from "./container.schema"; |
||||||
|
import { containerContext } from "./container.context"; |
||||||
|
import { Container } from "./container.typings"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* LDO ShapeTypes container |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Container ShapeType |
||||||
|
*/ |
||||||
|
export const ContainerShapeType: ShapeType<Container> = { |
||||||
|
schema: containerSchema, |
||||||
|
shape: "http://www.w3.org/ns/lddps#Container", |
||||||
|
context: containerContext, |
||||||
|
}; |
@ -0,0 +1,44 @@ |
|||||||
|
import { LdoJsonldContext, LdSet } from "@ldo/ldo"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* Typescript Typings for container |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Container Type |
||||||
|
*/ |
||||||
|
export interface Container { |
||||||
|
"@id"?: string; |
||||||
|
"@context"?: LdoJsonldContext; |
||||||
|
/** |
||||||
|
* A container |
||||||
|
*/ |
||||||
|
type?: LdSet< |
||||||
|
| { |
||||||
|
"@id": "Container"; |
||||||
|
} |
||||||
|
| { |
||||||
|
"@id": "Resource"; |
||||||
|
} |
||||||
|
>; |
||||||
|
/** |
||||||
|
* Date modified |
||||||
|
*/ |
||||||
|
modified?: string; |
||||||
|
/** |
||||||
|
* Defines a Resource |
||||||
|
*/ |
||||||
|
contains?: LdSet<{ |
||||||
|
"@id": string; |
||||||
|
}>; |
||||||
|
/** |
||||||
|
* ? |
||||||
|
*/ |
||||||
|
mtime?: number; |
||||||
|
/** |
||||||
|
* size of this container |
||||||
|
*/ |
||||||
|
size?: number; |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
|
||||||
|
# Platform ontologies: |
||||||
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||||
|
PREFIX owl: <http://www.w3.org/2002/07/owl#> |
||||||
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||||
|
PREFIX dc: <http://purl.org/dc/terms/> |
||||||
|
|
||||||
|
# Domain ontology for Contacts in vcard-like form |
||||||
|
PREFIX vcard: <http://www.w3.org/2006/vcard/ns#> |
||||||
|
PREFIX schem: <http://schema.org/> |
||||||
|
PREFIX foaf: <http://xmlns.com/foaf/0.1/> |
||||||
|
PREFIX ngx: <did:ng:n:g:x:social:contact#> |
||||||
|
|
||||||
|
ngx:SocialContact EXTRA a { |
||||||
|
a [vcard:Individual ] |
||||||
|
// rdfs:comment "Defines the node as an Individual (from vcard)" ; |
||||||
|
a [ schem:Person ] |
||||||
|
// rdfs:comment "Defines the node as a Person (from Schema.org)" ; |
||||||
|
a [ foaf:Person ] |
||||||
|
// rdfs:comment "Defines the node as a Person (from foaf)" ; |
||||||
|
vcard:fn xsd:string ? |
||||||
|
// rdfs:comment "The formatted name of a person. Example: John Smith" ; |
||||||
|
vcard:hasEmail xsd:string ? |
||||||
|
// rdfs:comment "The person's email." ; |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||||
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||||
|
PREFIX ldp: <http://www.w3.org/ns/ldp#> |
||||||
|
PREFIX ldps: <http://www.w3.org/ns/lddps#> |
||||||
|
PREFIX dct: <http://purl.org/dc/terms/> |
||||||
|
PREFIX stat: <http://www.w3.org/ns/posix/stat#> |
||||||
|
PREFIX tur: <http://www.w3.org/ns/iana/media-types/text/turtle#> |
||||||
|
PREFIX pim: <http://www.w3.org/ns/pim/space#> |
||||||
|
|
||||||
|
ldps:Container EXTRA a { |
||||||
|
$ldps:ContainerShape ( |
||||||
|
a [ ldp:Container ldp:Resource ]* |
||||||
|
// rdfs:comment "A container"; |
||||||
|
dct:modified xsd:string? |
||||||
|
// rdfs:comment "Date modified"; |
||||||
|
ldp:contains IRI * |
||||||
|
// rdfs:comment "Defines a Resource"; |
||||||
|
stat:mtime xsd:decimal? |
||||||
|
// rdfs:comment "?"; |
||||||
|
stat:size xsd:integer? |
||||||
|
// rdfs:comment "size of this container"; |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
|
||||||
|
|
||||||
|
.centered { |
||||||
|
/*max-width: 1280px;*/ |
||||||
|
margin: 0 auto; |
||||||
|
padding: 0rem; |
||||||
|
text-align: center; |
||||||
|
width: fit-content; |
||||||
|
} |
||||||
|
|
||||||
|
.contact { |
||||||
|
width: 300px; |
||||||
|
height: 100px; |
||||||
|
background-color: #f6f6f6; |
||||||
|
position: relative; |
||||||
|
overflow-wrap: anywhere; |
||||||
|
} |
||||||
|
|
||||||
|
.name { |
||||||
|
width: 300px; |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
top: 0; |
||||||
|
padding: 5px; |
||||||
|
height: 35px; |
||||||
|
overflow: hidden; |
||||||
|
background-color: #e0e0e0d0; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
} |
||||||
|
|
||||||
|
.email-logo { |
||||||
|
position: absolute; |
||||||
|
left: 5px; |
||||||
|
top: 35px; |
||||||
|
} |
||||||
|
|
||||||
|
.email { |
||||||
|
width: 270px; |
||||||
|
position: absolute; |
||||||
|
left: 30px; |
||||||
|
top: 30px; |
||||||
|
padding: 5px; |
||||||
|
overflow-wrap: anywhere; |
||||||
|
} |
||||||
|
|
||||||
|
input { |
||||||
|
margin: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
#save { |
||||||
|
background-color:rgb(73, 114, 165); |
||||||
|
color:white; |
||||||
|
cursor:pointer; |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
|
||||||
|
import React, { FunctionComponent } from 'react'; |
||||||
|
import { Header } from './Header'; |
||||||
|
import { Contacts } from './Contacts'; |
||||||
|
import { BrowserNGLdoProvider } from './reactMethods'; |
||||||
|
|
||||||
|
import './App.css' |
||||||
|
import "../../../../../app/ui-common/src/styles.css"; |
||||||
|
|
||||||
|
const App: FunctionComponent = () => { |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="App"> |
||||||
|
<BrowserNGLdoProvider> |
||||||
|
<Header /> |
||||||
|
<Contacts />
|
||||||
|
</BrowserNGLdoProvider> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default App |
@ -0,0 +1,29 @@ |
|||||||
|
import { FunctionComponent } from "react"; |
||||||
|
import { useNextGraphAuth } from "./reactMethods"; |
||||||
|
import { SocialContactShapeType } from "./.ldo/contact.shapeTypes.ts"; |
||||||
|
import { useSubscribeToResource, useResource, useSubject } from "./reactMethods.ts"; |
||||||
|
|
||||||
|
export const Contact: FunctionComponent = ({nuri}) => { |
||||||
|
const { session } = useNextGraphAuth(); |
||||||
|
|
||||||
|
useResource(session.sessionId && nuri ? nuri : undefined, { subscribe: true }); |
||||||
|
let contact = useSubject(SocialContactShapeType, session.sessionId && nuri ? nuri.substring(0,53) : undefined); |
||||||
|
|
||||||
|
if (!session.sessionId || !nuri) return <></>; |
||||||
|
|
||||||
|
return <> |
||||||
|
{contact.fn? (
|
||||||
|
<div className="contact" title={nuri}> |
||||||
|
<span className="name">
|
||||||
|
{contact.fn} |
||||||
|
</span> |
||||||
|
<svg className="w-6 h-6 inline email-logo" data-slot="icon" fill="none" strokeWidth="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M16.5 12a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm0 0c0 1.657 1.007 3 2.25 3S21 13.657 21 12a9 9 0 1 0-2.636 6.364M16.5 12V8.25"></path> |
||||||
|
</svg> |
||||||
|
<span className="email text-left"> |
||||||
|
email: {contact.hasEmail} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
) : <></>} |
||||||
|
</>; |
||||||
|
}; |
@ -0,0 +1,38 @@ |
|||||||
|
import { FunctionComponent } from "react"; |
||||||
|
import { useNextGraphAuth } from "./reactMethods"; |
||||||
|
import { ContainerShapeType } from "./.ldo/container.shapeTypes.ts"; |
||||||
|
import { useSubscribeToResource, useResource, useSubject } from "./reactMethods.ts"; |
||||||
|
import { Contact } from "./Contact"; |
||||||
|
import { MakeContact } from "./MakeContact"; |
||||||
|
|
||||||
|
export const Contacts: FunctionComponent = () => { |
||||||
|
const { session } = useNextGraphAuth(); |
||||||
|
|
||||||
|
let container_overlay: string; |
||||||
|
|
||||||
|
useResource(session.sessionId ? "did:ng:"+session.privateStoreId : undefined, { subscribe: true }); |
||||||
|
let myContainer = useSubject(ContainerShapeType, session.sessionId ? "did:ng:"+(session.privateStoreId.substring(0,46)) : undefined); |
||||||
|
|
||||||
|
if (session.sessionId) { |
||||||
|
container_overlay = session.privateStoreId.substring(46) as string; |
||||||
|
} |
||||||
|
|
||||||
|
if (!session.sessionId) return <></>; |
||||||
|
|
||||||
|
return <> |
||||||
|
<div className="centered"> |
||||||
|
<div className="flex flex-wrap justify-center gap-5 mt-10 mb-10"> |
||||||
|
<MakeContact/> |
||||||
|
</div> |
||||||
|
<div className="flex flex-wrap justify-center gap-5 mb-10"> |
||||||
|
{
|
||||||
|
myContainer.contains?.map( |
||||||
|
(contained) =>
|
||||||
|
<Contact key={contained["@id"]} nuri={contained["@id"]+container_overlay}/> |
||||||
|
) |
||||||
|
} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</>; |
||||||
|
}; |
||||||
|
|
@ -0,0 +1,44 @@ |
|||||||
|
import { FunctionComponent } from "react"; |
||||||
|
import { useNextGraphAuth } from "./reactMethods"; |
||||||
|
|
||||||
|
|
||||||
|
export const Header: FunctionComponent = () => { |
||||||
|
|
||||||
|
const { session, login, logout } = useNextGraphAuth(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="full-layout"> |
||||||
|
|
||||||
|
{session.sessionId ? ( |
||||||
|
// If the session is logged in
|
||||||
|
<div className="p-1 text-white text-center fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> |
||||||
|
You are logged in. |
||||||
|
{/* <span className="font-bold clickable" onClick={logout}> Log out</span> */} |
||||||
|
</div> |
||||||
|
) : ( |
||||||
|
// If the session is not logged in
|
||||||
|
<> |
||||||
|
<h1 className="text-2xl text-center mb-10">Welcome to your contact manager</h1> |
||||||
|
<div className="text-center text-xl p-1 text-white fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> |
||||||
|
Please <span className="font-bold clickable" onClick={login}> Log in</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="text-center max-w-6xl lg:px-8 mx-auto px-4 text-blue-800"> |
||||||
|
|
||||||
|
<svg className="mt-10 h-16 w-16 mx-auto" data-slot="icon" fill="none" strokeWidth="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 9V5.25A2.25 2.25 0 0 1 10.5 3h6a2.25 2.25 0 0 1 2.25 2.25v13.5A2.25 2.25 0 0 1 16.5 21h-6a2.25 2.25 0 0 1-2.25-2.25V15M12 9l3 3m0 0-3 3m3-3H2.25"></path> |
||||||
|
</svg> |
||||||
|
|
||||||
|
<button |
||||||
|
onClick={login} |
||||||
|
onKeyUp={login} |
||||||
|
className="select-none ml-0 mt-2 mb-10 text-white bg-blue-800 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||||
|
> |
||||||
|
Please Log in |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,60 @@ |
|||||||
|
import { FormEvent, FunctionComponent, useCallback, useState } from "react"; |
||||||
|
import { BrowserNGLdoProvider, useLdo, dataset } from './reactMethods'; |
||||||
|
import { SocialContactShapeType } from "./.ldo/contact.shapeTypes.ts"; |
||||||
|
import { LdSet } from "@ldo/ldo"; |
||||||
|
|
||||||
|
export const MakeContact: FunctionComponent = () => { |
||||||
|
const [name, setName] = useState(""); |
||||||
|
const [email, setEmail] = useState(""); |
||||||
|
|
||||||
|
const { createData, commitData } = useLdo(); |
||||||
|
|
||||||
|
const onSubmit = useCallback( |
||||||
|
async (e: FormEvent<HTMLFormElement>) => { |
||||||
|
e.preventDefault(); |
||||||
|
const new_name = name.trim(); |
||||||
|
const new_email = email.trim(); |
||||||
|
if (new_name.trim().length > 2 && new_email.trim().length > 6 && new_email.indexOf("@") >= 0) {
|
||||||
|
setName(""); |
||||||
|
setEmail(""); |
||||||
|
const resource = await dataset.createResource("nextgraph"); |
||||||
|
if (!resource.isError) { |
||||||
|
//console.log("Created resource:", resource.uri);
|
||||||
|
|
||||||
|
const contact = createData( |
||||||
|
SocialContactShapeType, |
||||||
|
resource.uri.substring(0,53), |
||||||
|
resource |
||||||
|
); |
||||||
|
|
||||||
|
contact.type = { "@id": "Individual" }; |
||||||
|
contact.fn = new_name; |
||||||
|
contact.hasEmail = new_email; |
||||||
|
const result = await commitData(contact); |
||||||
|
if (result.isError) { |
||||||
|
console.error(result.message); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
[name, email] |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<form onSubmit={onSubmit}> |
||||||
|
<input |
||||||
|
type="text" |
||||||
|
placeholder="Enter name" |
||||||
|
value={name} |
||||||
|
onChange={(e) => setName(e.target.value)} |
||||||
|
/> |
||||||
|
<input |
||||||
|
type="text" |
||||||
|
placeholder="Enter email address" |
||||||
|
value={email} |
||||||
|
onChange={(e) => setEmail(e.target.value)} |
||||||
|
/> |
||||||
|
<input type="submit" id="save" value="Save" /> |
||||||
|
</form> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,20 @@ |
|||||||
|
import { createContext, useContext } from "react"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Functions for authenticating with NextGraph |
||||||
|
*/ |
||||||
|
export interface NGWalletAuthFunctions { |
||||||
|
login: () => Promise<void>; |
||||||
|
logout: () => Promise<void>; |
||||||
|
session: unknown; |
||||||
|
ranInitialAuthCheck: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
// There is no initial value for this context. It will be given in the provider
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
export const NextGraphAuthContext = createContext<NGWalletAuthFunctions>(undefined); |
||||||
|
|
||||||
|
export function useNextGraphAuth(): NGWalletAuthFunctions { |
||||||
|
return useContext(NextGraphAuthContext); |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
import React, { useCallback, useEffect, useMemo, useState } from "react"; |
||||||
|
import type { FunctionComponent, PropsWithChildren } from "react"; |
||||||
|
import { NextGraphAuthContext, useNextGraphAuth } from "./NextGraphAuthContext"; |
||||||
|
|
||||||
|
import {ng, init} from "@ng-org/web"; |
||||||
|
|
||||||
|
import type { ConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; |
||||||
|
import type { NextGraphConnectedPlugin, NextGraphConnectedContext } from "@ldo/connected-nextgraph"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates special react methods specific to the NextGraph Auth |
||||||
|
* @param dataset the connectedLdoDataset with a nextGraphConnectedPlugin |
||||||
|
* @returns { BrowserNGLdoProvider, useNextGraphAuth } |
||||||
|
*/ |
||||||
|
export function createBrowserNGReactMethods( |
||||||
|
dataset: ConnectedLdoDataset<(NextGraphConnectedPlugin | ConnectedPlugin)[]>, |
||||||
|
) { |
||||||
|
|
||||||
|
const BrowserNGLdoProvider: FunctionComponent<PropsWithChildren> = ({ |
||||||
|
children, |
||||||
|
}) => { |
||||||
|
const [session, setSession] = useState<NextGraphConnectedContext>( |
||||||
|
{ |
||||||
|
ng: undefined, |
||||||
|
} |
||||||
|
); |
||||||
|
const [ranInitialAuthCheck, setRanInitialAuthCheck] = useState(false); |
||||||
|
|
||||||
|
const runInitialAuthCheck = useCallback(async () => { |
||||||
|
//console.log("runInitialAuthCheck called", ranInitialAuthCheck)
|
||||||
|
if (ranInitialAuthCheck) return; |
||||||
|
|
||||||
|
//console.log("init called");
|
||||||
|
setRanInitialAuthCheck(true); |
||||||
|
// TODO: export the types for the session object coming from NG.
|
||||||
|
await init( (event: { status: string; session: { session_id: unknown; protected_store_id: unknown; private_store_id: unknown; public_store_id: unknown; }; }) => { |
||||||
|
//console.log("called back in react", event)
|
||||||
|
|
||||||
|
// callback
|
||||||
|
// once you receive event.status == "loggedin"
|
||||||
|
// you can use the full API
|
||||||
|
if (event.status == "loggedin") { |
||||||
|
setSession({
|
||||||
|
ng,
|
||||||
|
sessionId: event.session.session_id as string, //FIXME: sessionId should be a Number.
|
||||||
|
protectedStoreId: event.session.protected_store_id as string, |
||||||
|
privateStoreId: event.session.private_store_id as string, |
||||||
|
publicStoreId: event.session.public_store_id as string |
||||||
|
}); // TODO: add event.session.user too
|
||||||
|
|
||||||
|
dataset.setContext("nextgraph", { |
||||||
|
ng, |
||||||
|
sessionId: event.session.session_id as string |
||||||
|
}); |
||||||
|
} |
||||||
|
else if (event.status == "cancelled" || event.status == "error" || event.status == "loggedout") { |
||||||
|
setSession({ ng: undefined }); |
||||||
|
dataset.setContext("nextgraph", { |
||||||
|
ng: undefined, |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
, true // singleton: boolean (should it be launched as a unique instance, or will your app create many docs in the system)
|
||||||
|
, []); //list of AccessRequests (for now, leave this empty)
|
||||||
|
|
||||||
|
}, []); |
||||||
|
|
||||||
|
|
||||||
|
const login = useCallback( |
||||||
|
async () => { |
||||||
|
await ng.login(); |
||||||
|
}, |
||||||
|
[], |
||||||
|
); |
||||||
|
|
||||||
|
const logout = useCallback(async () => { |
||||||
|
await ng.logout(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
runInitialAuthCheck(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
const nextGraphAuthFunctions = useMemo( |
||||||
|
() => ({ |
||||||
|
runInitialAuthCheck, |
||||||
|
login, |
||||||
|
logout, |
||||||
|
session, |
||||||
|
ranInitialAuthCheck, |
||||||
|
}), |
||||||
|
[ |
||||||
|
login, |
||||||
|
logout, |
||||||
|
ranInitialAuthCheck, |
||||||
|
runInitialAuthCheck, |
||||||
|
session, |
||||||
|
], |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<NextGraphAuthContext.Provider value={nextGraphAuthFunctions}> |
||||||
|
{children} |
||||||
|
</NextGraphAuthContext.Provider> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
return { |
||||||
|
BrowserNGLdoProvider, |
||||||
|
useNextGraphAuth: useNextGraphAuth |
||||||
|
}; |
||||||
|
}; |
@ -0,0 +1,3 @@ |
|||||||
|
@tailwind base; |
||||||
|
@tailwind components; |
||||||
|
@tailwind utilities; |
@ -0,0 +1,10 @@ |
|||||||
|
import { StrictMode } from 'react' |
||||||
|
import { createRoot } from 'react-dom/client' |
||||||
|
import './index.css' |
||||||
|
import App from './App.tsx' |
||||||
|
|
||||||
|
createRoot(document.getElementById('app')!).render( |
||||||
|
// <StrictMode>
|
||||||
|
<App /> |
||||||
|
// </StrictMode>,
|
||||||
|
) |
@ -0,0 +1,18 @@ |
|||||||
|
import { nextGraphConnectedPlugin } from "@ldo/connected-nextgraph"; |
||||||
|
import { createLdoReactMethods } from "@ldo/react"; |
||||||
|
import { createBrowserNGReactMethods } from "./createBrowserNGReactMethods"; |
||||||
|
|
||||||
|
export const { |
||||||
|
dataset, |
||||||
|
useLdo, |
||||||
|
useMatchObject, |
||||||
|
useMatchSubject, |
||||||
|
useResource, |
||||||
|
useSubject, |
||||||
|
useSubscribeToResource, |
||||||
|
} = createLdoReactMethods([nextGraphConnectedPlugin]); |
||||||
|
|
||||||
|
const methods = createBrowserNGReactMethods(dataset); |
||||||
|
|
||||||
|
export const { BrowserNGLdoProvider, useNextGraphAuth } = methods; |
||||||
|
|
@ -0,0 +1 @@ |
|||||||
|
/// <reference types="vite/client" />
|
@ -0,0 +1,12 @@ |
|||||||
|
/** @type {import('tailwindcss').Config} */ |
||||||
|
export default { |
||||||
|
content: [ |
||||||
|
"./index.html", |
||||||
|
"./src/**/*.{js,jsx,ts,tsx}" |
||||||
|
], |
||||||
|
theme: { |
||||||
|
extend: {}, |
||||||
|
}, |
||||||
|
plugins: [], |
||||||
|
} |
||||||
|
|
@ -0,0 +1,26 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", |
||||||
|
"target": "ES2020", |
||||||
|
"useDefineForClassFields": true, |
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"], |
||||||
|
"module": "ESNext", |
||||||
|
"skipLibCheck": true, |
||||||
|
|
||||||
|
/* Bundler mode */ |
||||||
|
"moduleResolution": "bundler", |
||||||
|
"allowImportingTsExtensions": true, |
||||||
|
"isolatedModules": true, |
||||||
|
"moduleDetection": "force", |
||||||
|
"noEmit": true, |
||||||
|
"jsx": "react-jsx", |
||||||
|
|
||||||
|
/* Linting */ |
||||||
|
"strict": true, |
||||||
|
"noUnusedLocals": true, |
||||||
|
"noUnusedParameters": true, |
||||||
|
"noFallthroughCasesInSwitch": true, |
||||||
|
"noUncheckedSideEffectImports": true |
||||||
|
}, |
||||||
|
"include": ["src"] |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"files": [], |
||||||
|
"references": [ |
||||||
|
{ "path": "./tsconfig.app.json" }, |
||||||
|
{ "path": "./tsconfig.node.json" } |
||||||
|
] |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue