a "little" refactoring

This commit is contained in:
Kendell R 2023-09-09 11:17:35 -07:00
parent 95895056ef
commit a6c8804552
No known key found for this signature in database
GPG key ID: 64314E306EEF6109
11 changed files with 585 additions and 617 deletions

View file

@ -1,32 +1,31 @@
import { getDatabase, onValue, ref, set, remove, goOffline } from "firebase/database"; import {
getDatabase,
onValue,
ref,
set,
remove,
goOffline,
} from "firebase/database";
// import "firebase-config"; // import "firebase-config";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import { Answer } from "./RTCTransport"; import { Answer } from "./RTCTransport";
import { browserLocalPersistence, getAuth, setPersistence, signInWithEmailAndPassword } from "firebase/auth"; import { getAuth } from "firebase/auth";
export async function signalSwarm(offer: string): Promise<Answer> { export async function signalSwarm(offer: string): Promise<Answer> {
let id = uuid(); let id = uuid();
const db = getDatabase(); const db = getDatabase();
let reff = ref(db, `/swarm/${id}`); let reff = ref(db, `/swarm/${id}`);
set(reff, offer); set(reff, offer);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
onValue(reff, (snapshot) => { onValue(reff, (snapshot) => {
const text = snapshot.val(); const text = snapshot.val();
if (!text) if (!text) return;
return;
let data = JSON.parse(text); let data = JSON.parse(text);
console.log(data); console.log("<", data);
if (data.error) { if (data.error) {
reject(new Error(data.error)); reject(new Error(data.error));
@ -37,26 +36,18 @@ export async function signalSwarm(offer: string): Promise<Answer> {
resolve(data); resolve(data);
}); });
}); });
} }
export async function signalAccount(offer: string): Promise<Answer> { export async function signalAccount(offer: string): Promise<Answer> {
let auth = getAuth(); let auth = getAuth();
if (!auth.currentUser) if (!auth.currentUser) throw new Error("not signed in");
throw new Error("not signed in");
const db = getDatabase(); const db = getDatabase();
let peer = ref(db, `/peers/${auth.currentUser!.uid}`); let peer = ref(db, `/peers/${auth.currentUser!.uid}`);
set(peer, offer); set(peer, offer);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
onValue(peer, async (snapshot) => { onValue(peer, async (snapshot) => {
const str = snapshot.val(); const str = snapshot.val();
if (str) { if (str) {
@ -65,7 +56,6 @@ export async function signalAccount(offer: string): Promise<Answer> {
remove(peer); remove(peer);
resolve(data); resolve(data);
goOffline(db); goOffline(db);
} }
} }
}); });

View file

@ -1,19 +1,18 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head>
<title></title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Cabin"> <link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Cabin:wght@400;500;700&display=swap"
/>
<script type="module" src="src/entry.ts"></script> <script type="module" src="src/entry.ts"></script>
<script src="uv/uv.bundle.js"></script> <script src="uv/uv.bundle.js"></script>
<script src="uv.config.js"></script> <script src="uv.config.js"></script>
</head>
</head> <body>
<div id="app" class="m3-font-body-large"></div>
<body> </body>
<div id="app"></div>
</body>
</html> </html>

View file

@ -18,16 +18,17 @@
"@iconify-icons/ic": "^1.2.13", "@iconify-icons/ic": "^1.2.13",
"@iconify-icons/maki": "^1.2.18", "@iconify-icons/maki": "^1.2.18",
"@iconify/svelte": "^3.1.4", "@iconify/svelte": "^3.1.4",
"@mercuryworkshop/bare-client-custom": "workspace:*",
"@rollup/browser": "^3.28.0", "@rollup/browser": "^3.28.0",
"@sveltejs/vite-plugin-svelte": "^2.4.5", "@sveltejs/vite-plugin-svelte": "^2.4.5",
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.15",
"@mercuryworkshop/bare-client-custom": "workspace:*",
"client": "workspace:*", "client": "workspace:*",
"corium": "file:../corium", "corium": "file:../corium",
"esbuild": "^0.19.1", "esbuild": "^0.19.1",
"esbuild-plugin-inline-import": "^1.0.1", "esbuild-plugin-inline-import": "^1.0.1",
"firebase": "^10.1.0", "firebase": "^10.1.0",
"m3-svelte": "^2.0.3", "m3-svelte": "^2.0.3",
"micromatch": "^4.0.5",
"postcss": "^8.4.27", "postcss": "^8.4.27",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"protocol": "workspace:*", "protocol": "workspace:*",
@ -37,7 +38,6 @@
"tracker-list": "workspace:*", "tracker-list": "workspace:*",
"typescript": "^5.1.6", "typescript": "^5.1.6",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"vite": "^4.4.9", "vite": "^4.4.9"
"vite-plugin-svelte": "^3.0.1"
} }
} }

View file

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before After
Before After

View file

@ -1,9 +0,0 @@
<script lang="ts">
import { Card } from "m3-svelte";
</script>
<div class="h-full w-full p-5">
<Card type="elevated">
<h1>Step 1</h1>
</Card>
</div>

View file

@ -1,165 +1,61 @@
<script lang="ts"> <script lang="ts">
import Icon from "@iconify/svelte";
import { setBareClientImplementation } from "@mercuryworkshop/bare-client-custom"; import { setBareClientImplementation } from "@mercuryworkshop/bare-client-custom";
import {
Button,
Card,
CircularProgressIndeterminate,
StyleFromScheme,
} from "m3-svelte";
import { import {
AdriftBareClient, AdriftBareClient,
Connection, Connection,
DevWsTransport, DevWsTransport,
RTCTransport, RTCTransport,
SignalFirebase,
downloadShortcut, downloadShortcut,
} from "client"; } from "client";
import {
Button,
Card,
CircularProgressIndeterminate,
Dialog,
RadioAnim3,
SnackbarAnim,
StyleFromScheme,
TextField,
} from "m3-svelte";
import iconDiscord from "@iconify-icons/ic/outline-discord";
import iconGithub from "@iconify-icons/bi/github";
import Icon from "@iconify/svelte";
import type { Transport } from "protocol"; import type { Transport } from "protocol";
import Proxy from "./Proxy.svelte"; import Proxy from "./Proxy.svelte";
import { initializeApp } from "firebase/app"; import ConnectionCmp from "./Connection.svelte";
import { UIState } from "./state";
import TrackerList from "tracker-list"; let state = UIState.Idle;
import { let stateStr = "";
browserLocalPersistence,
createUserWithEmailAndPassword,
getAuth,
setPersistence,
signInWithEmailAndPassword,
} from "firebase/auth";
import { getAnalytics } from "firebase/analytics"; let dialogConnection = false;
import logo from "./logo.png"; let devTransport: Transport;
import AccountCreation from "./AccountCreation.svelte"; async function onOpen() {
import { goOffline } from "firebase/database"; console.log("[TRANSPORT DEV] opened");
enum ReadyState { const connection = new Connection(devTransport);
Idle,
Connecting,
Connected,
AccountCreation,
}
let state = ReadyState.Idle;
let transport: Transport;
let rtctransport: RTCTransport | undefined;
let email = "";
let password = "";
let connectionState = "";
let showSwarmWarning = false;
let showLogin = false;
type TrackerID = keyof typeof TrackerList;
type Tracker = (typeof TrackerList)[TrackerID];
let chosenTracker: TrackerID | undefined;
let showTrackerList = false;
let createaccount = false;
let snackbar: (data: any) => void;
async function onTransportOpen() {
console.log("Transport opened");
let connection = new Connection(transport);
// TODO: error handling here
await connection.initialize(); await connection.initialize();
let bare = new AdriftBareClient(connection); const bare = new AdriftBareClient(connection);
setBareClientImplementation(bare); setBareClientImplementation(bare);
state = ReadyState.Connected; state = UIState.Connected;
} }
async function connectDevHttp() {
function onTransportClose() { state = UIState.Connecting;
console.warn("Transport closed"); const transport = new RTCTransport(
} onOpen,
() => console.warn("[TRANSPORT] closed"),
function createRTCTransport() {
let transport = new RTCTransport(
onTransportOpen,
onTransportClose,
() => { () => {
connectionState = `Connection ${transport.peer.connectionState}...`; stateStr = `Connection ${transport.peer.connectionState}...`;
}, },
() => { () => {
connectionState = `Signaling ${transport.peer.connectionState}...`; stateStr = `Signaling ${transport.peer.connectionState}...`;
}, },
() => { () => {
if (transport.peer.connectionState == "new") { if (transport.peer.connectionState == "new") {
connectionState = `Creating an offer...`; stateStr = `Creating an offer...`;
} else { } else {
connectionState = `Gathering ${transport.peer.connectionState}...`; stateStr = `Gathering ${transport.peer.connectionState}...`;
} }
} }
); );
return transport; devTransport = transport;
} let offer = await transport.createOffer();
console.log("offer", offer);
async function initFirebase() {
if (!chosenTracker) return;
let tracker = TrackerList[chosenTracker];
let app = initializeApp(tracker.firebase);
getAnalytics(app);
}
async function connectAccount() {
rtctransport = transport = createRTCTransport();
state = ReadyState.Connecting;
let offer = await rtctransport.createOffer();
connectionState = "Finding your node...";
let answer = await SignalFirebase.signalAccount(JSON.stringify(offer));
connectionState = "Linking to node...";
await new Promise((r) => {
setTimeout(r, 1000);
});
rtctransport.answer(answer.answer, answer.candidates);
}
async function connectSwarm() {
await initFirebase();
state = ReadyState.Connecting;
rtctransport = transport = createRTCTransport();
let offer = await rtctransport.createOffer();
connectionState = "Routing you to an available node...";
try {
let answer = await SignalFirebase.signalSwarm(JSON.stringify(offer));
connectionState = "Linking to node...";
await new Promise((r) => {
setTimeout(r, 500);
});
rtctransport.answer(answer.answer, answer.candidates);
} catch (e) {
console.error(e);
connectionState = e;
}
}
async function connectDevHttp() {
rtctransport = transport = createRTCTransport();
let offer = await rtctransport.createOffer();
console.log("offer created", offer);
console.log(JSON.stringify(offer));
const r = await fetch("http://localhost:3000/connect", { const r = await fetch("http://localhost:3000/connect", {
method: "POST", method: "POST",
@ -170,394 +66,181 @@
throw new Error("connect: " + r.status + " " + r.statusText); throw new Error("connect: " + r.status + " " + r.statusText);
} }
const { answer, candidates } = await r.json(); const { answer, candidates } = await r.json();
await rtctransport.answer(answer, candidates); await transport.answer(answer, candidates);
} }
async function connectDevWS() { async function connectDevWS() {
transport = new DevWsTransport(onTransportOpen, () => state = UIState.Connecting;
console.log("onclose") devTransport = new DevWsTransport(onOpen, () =>
console.log("[TRANSPORT DEV] closed")
); );
} }
let trackerstatuses: Partial<Record<TrackerID, object | null>> = {};
for (let id in TrackerList) {
let tracker = TrackerList[id as TrackerID];
trackerstatuses[id as TrackerID] = null;
let url = new URL(`${tracker.tracker}/stats`);
url.protocol = "https://";
fetch(url).then(async (data) => {
trackerstatuses[id as TrackerID] = await data.json();
console.log(trackerstatuses);
});
}
</script> </script>
{#if state == ReadyState.Connected} <svelte:head>
<title>Adrift</title>
</svelte:head>
{#if state == UIState.Connected}
<Proxy /> <Proxy />
{:else if state == ReadyState.Connecting} {:else if state == UIState.Connecting}
<div class="h-full w-full flex justify-center items-center"> <div class="h-full flex justify-center items-center">
<Card type="outlined"> <Card type="outlined">
<div class="flex items-center p-2"> <div class="flex items-center mb-4 gap-4">
<CircularProgressIndeterminate /> <CircularProgressIndeterminate />
<div class="p-5" /> <h2 class="text-xl">{stateStr}</h2>
<h2 class="text-xl">
{connectionState}
</h2>
</div> </div>
<br /> <p class="text-sm text-on-surface-variant">
<p class="text-sm opacity-70">
Adrift is routing you to a server available to take your requests.<br Adrift is routing you to a server available to take your requests.<br
/>The initial connection may take several minutes depending on server />The initial connection may take several minutes depending on server
load load
</p> </p>
</Card> </Card>
</div> </div>
{:else if !import.meta.env.VITE_ADRIFT_DEV} {:else if import.meta.env.VITE_ADRIFT_DEV}
<div class="flex flex-col h-full"> <div class="h-full flex justify-center items-center">
<div id="topbar" class="flex justify-between items-center p-4"> <Card type="outlined">
<div id="logo"> <h2 class="m3-font-headline-large mb-4">Adrift DEV</h2>
<Card type=""> <div class="flex gap-4">
<Button type="filled" on:click={connectDevHttp}>
Connect with localhost WebRTC transport over HTTP signaling
</Button>
<Button type="filled" on:click={connectDevWS}>
Connect with localhost websocket transport
</Button>
</div>
</Card>
</div>
{:else}
<div class="h-full flex flex-col p-4 gap-6">
<div class="flex items-center text-3xl"> <div class="flex items-center text-3xl">
<Icon icon="material-symbols:sailing" /> <Icon icon="material-symbols:sailing" />
<p class="text-2xl ml-3">Adrift</p> <p class="text-2xl ml-3">Adrift</p>
</div> </div>
</Card>
</div>
<div id="nav" />
<div id="links">
<Card type="elevated">
<div class="flex">
<a href="https://discord.gg/bAgNyGpXSx">
<Icon icon={iconDiscord} class="icon" />
</a>
<spacer />
<a href="https://github.com/MercuryWorkshop/adrift">
<Icon icon={iconGithub} class="icon" />
</a>
</div>
</Card>
</div>
</div>
<div class="flex flex-col flex-1">
<div class="flex m-4">
<Card <Card
type="elevated" type="elevated"
extraOptions={{ class: "m3-container type-elevated w-9/12" }} extraOptions={{ class: "m3-container type-elevated lg:w-3/4" }}
> >
<div class="w-full">
<h2 class="text-6xl">Surf the web, Adrift</h2> <h2 class="text-6xl">Surf the web, Adrift</h2>
<h2 class="text-2xl"> <h2 class="text-2xl">A fast and modern decentralized proxy network</h2>
A fast and modern decentralized proxy network <div class="mt-6 flex gap-4">
</h2> <Button type="filled" on:click={() => (dialogConnection = true)}>
</div> Start browsing
<div class="mt-5 flex justify-between"> </Button>
<Button type="filled" on:click={() => (showTrackerList = true)}
>Start Browsing</Button
>
{#if !import.meta.env.VITE_ADRIFT_SINGLEFILE} {#if !import.meta.env.VITE_ADRIFT_SINGLEFILE}
<Button <Button
type="text" type="text"
on:click={() => { on:click={() => downloadShortcut("adrift.html", "Homework")}
downloadShortcut("adrift.html", "Homework");
}}>Get Shortcut</Button
> >
Get shortcut
</Button>
{/if} {/if}
</div> </div>
</Card> </Card>
</div>
<div
class="flex h-full justify-end m-4 transition-all"
class:opacity-0={!showTrackerList}
>
<Card <Card
type="elevated" type="elevated"
extraOptions={{ extraOptions={{ class: "m3-container type-elevated mt-auto" }}
class: "m3-container type-elevated w-9/12 flex flex-col",
}}
> >
<h2 class="text-4xl">Select a Tracker</h2> <div class="flex gap-8 items-center">
<h2 class="text-1xl">Trackers allow you to connect to Adrift</h2> <a class="mr-auto" href="https://mercurywork.shop">
<div class="mt-5 space-y-3"> © 2023 Mercury Workshop
{#each Object.keys(TrackerList) as tracker} </a>
<Card type="outlined">
<label>
<div class="flex items-center">
<svelte:component this={RadioAnim3}>
<input
type="radio"
id={tracker}
name="tabs"
value={tracker}
bind:group={chosenTracker}
/>
</svelte:component>
<p class="ml-3 text-xl">
{tracker}
</p>
</div>
<p>
{TrackerList[tracker].description}
</p>
<p class="opacity-50">
{trackerstatuses[tracker]
? trackerstatuses[tracker]?.members?.length
: "loading"} swarm members
</p>
</label>
</Card>
{/each}
</div>
<div class="flex-1" />
<div class="mt-5 flex">
{#if chosenTracker}
<Button type="elevated" on:click={() => (showSwarmWarning = true)}
>Connect to the swarm</Button
>
<Button
type="filled"
on:click={async () => {
await initFirebase();
let auth = getAuth(); <a href="https://discord.gg/bAgNyGpXSx" class="text-2xl">
await setPersistence(auth, browserLocalPersistence); <Icon icon="ic:round-discord" />
</a>
if (!auth.currentUser) { <a href="https://github.com/MercuryWorkshop/adrift" class="text-2xl">
showLogin = true; <Icon icon="mdi:github" />
} else { </a>
await connectAccount();
}
}}>Connect with login</Button
>
{/if}
</div>
<Dialog headline="WARNING" bind:open={showSwarmWarning}>
<h2 class="text-2xl">
TLS has not currently been implemented for the Adrift Swarm. It
will later, but until then your data will not be private, and you
should not enter any sensitive information on any page
</h2>
<br />
<Button type="filled" on:click={() => (showLogin = false)}
>Cancel</Button
>
<Button type="outlined" on:click={connectSwarm}
>I understand, Connect</Button
>
</Dialog>
<Dialog headline="Log in to connect" bind:open={showLogin}>
<button
class="text-primary my-3"
on:click={() => ((createaccount = true), (showLogin = false))}
>New here? Create an account</button
>
<br />
<TextField name="email" bind:value={email} />
<TextField
name="password"
bind:value={password}
extraOptions={{ type: "password" }}
/>
<div class="flex mt-5">
<Button type="outlined" on:click={() => (showLogin = false)}
>Cancel</Button
>
<Button
type="filled"
on:click={async () => {
try {
await signInWithEmailAndPassword(
getAuth(),
email,
password
);
connectAccount();
} catch (e) {
snackbar({ message: e, closable: true });
}
}}>Connect</Button
>
</div>
</Dialog>
<Dialog bind:open={createaccount} headline="Create an account">
<TextField name="email" bind:value={email} />
<TextField
name="password"
bind:value={password}
extraOptions={{ type: "password" }}
/>
<p>
Note: to be able to connect, you'll need to download an exit node
and run it on a computer with an uncensored internet connection
</p>
<div class="flex mt-5">
<Button
type="filled"
on:click={async () => {
try {
await createUserWithEmailAndPassword(
getAuth(),
email,
password
);
createaccount = false;
} catch (e) {
snackbar({ message: e, closable: true });
}
}}>Create Account</Button
>
</div></Dialog
>
</Card>
</div>
<div class="flex m-4">
<Card
type="elevated"
extraOptions={{ class: "m3-container type-elevated w-full" }}
>
<div class="flex space-x-10">
<a class="text-1xl" href="https://mercurywork.shop"
>© 2023 Mercury Workshop</a
>
<div class="space-x-3">
<a class="text-1xl" href="https://discord.gg/bAgNyGpXSx"
>discord</a
>
<a class="text-1xl" href="https://github.com/MercuryWorkshop"
>github</a
>
</div>
<div />
</div>
</Card>
</div>
</div>
</div>
{:else}
<div class="flex items-center justify-center h-full">
<Card type="elevated">
<div class="flex flex-col h-full">
<h2 class="m3-font-headline-large m-3">Adrift DEV</h2>
<div class="flex space-evenly pad-children">
<Button type="filled" on:click={connectDevHttp}
>Connect with WebRTC transport over localhost HTTP signaling</Button
>
<Button type="filled" on:click={connectDevWS}
>Connect with localhost websocket transport</Button
>
</div>
</div> </div>
</Card> </Card>
</div> </div>
<ConnectionCmp bind:dialogConnection bind:state bind:stateStr />
{/if} {/if}
<svelte:component this={SnackbarAnim} bind:show={snackbar} />
<StyleFromScheme <StyleFromScheme
lightScheme={{ lightScheme={{
primary: 1284831119, primary: 4282411062,
onPrimary: 4294967295, onPrimary: 4294967295,
primaryContainer: 4293516799, primaryContainer: 4290834352,
onPrimaryContainer: 4280291399, onPrimaryContainer: 4278198784,
inversePrimary: 4291804670, inversePrimary: 4289057685,
secondary: 4284636017, secondary: 4283720525,
onSecondary: 4294967295, onSecondary: 4294967295,
secondaryContainer: 4293451512, secondaryContainer: 4292339917,
onSecondaryContainer: 4280162603, onSecondaryContainer: 4279377678,
tertiary: 4286468704, tertiary: 4281886056,
onTertiary: 4294967295, onTertiary: 4294967295,
tertiaryContainer: 4294957539, tertiaryContainer: 4290571246,
onTertiaryContainer: 4281405469, onTertiaryContainer: 4278198306,
error: 4290386458, error: 4290386458,
onError: 4294967295, onError: 4294967295,
errorContainer: 4294957782, errorContainer: 4294957782,
onErrorContainer: 4282449922, onErrorContainer: 4282449922,
background: 4294834175, background: 4294507505,
onBackground: 4280097568, onBackground: 4279835927,
surface: 4294834175, surface: 4294507505,
onSurface: 4280097568, onSurface: 4279835927,
surfaceVariant: 4293386475, surfaceVariant: 4292863191,
onSurfaceVariant: 4282991950, onSurfaceVariant: 4282599487,
inverseSurface: 4281478965, inverseSurface: 4281217579,
inverseOnSurface: 4294307831, inverseOnSurface: 4293915368,
outline: 4286215551, outline: 4285757806,
outlineVariant: 4291478735, outlineVariant: 4291020988,
shadow: 4278190080, shadow: 4278190080,
scrim: 4278190080, scrim: 4278190080,
surfaceDim: 4292794592, surfaceDim: 4292402130,
surfaceBright: 4294834175, surfaceBright: 4294507505,
surfaceContainerLowest: 4294967295, surfaceContainerLowest: 4294967295,
surfaceContainerLow: 4294505210, surfaceContainerLow: 4294112747,
surfaceContainer: 4294110452, surfaceContainer: 4293717989,
surfaceContainerHigh: 4293715694, surfaceContainerHigh: 4293323232,
surfaceContainerHighest: 4293320937, surfaceContainerHighest: 4292994266,
surfaceTint: 4284831119, surfaceTint: 4282411062,
}} }}
darkScheme={{ darkScheme={{
primary: 1291804670, primary: 4294945779,
onPrimary: 4281739101, onPrimary: 4284153947,
primaryContainer: 4283252085, primaryContainer: 4291690702,
onPrimaryContainer: 4293516799, onPrimaryContainer: 4294967295,
inversePrimary: 4284831119, inversePrimary: 4289265833,
secondary: 4291543771, secondary: 4294945779,
onSecondary: 4281544001, onSecondary: 4284153947,
secondaryContainer: 4283057240, secondaryContainer: 4286709890,
onSecondaryContainer: 4293451512, onSecondaryContainer: 4294956533,
tertiary: 4293900488, tertiary: 4294947764,
onTertiary: 4283049266, onTertiary: 4285005846,
tertiaryContainer: 4284693320, tertiaryContainer: 4292294218,
onTertiaryContainer: 4294957539, onTertiaryContainer: 4294967295,
error: 4294948011, error: 4294948011,
onError: 4285071365, onError: 4285071365,
errorContainer: 4287823882, errorContainer: 4287823882,
onErrorContainer: 4294957782, onErrorContainer: 4294957782,
background: 4279505432, background: 4279702038,
onBackground: 4293320937, onBackground: 4293713893,
surface: 4279505432, surface: 4279702038,
onSurface: 4293320937, onSurface: 4293713893,
surfaceVariant: 4282991950, surfaceVariant: 4283319371,
onSurfaceVariant: 4291478735, onSurfaceVariant: 4291936971,
inverseSurface: 4293320937, inverseSurface: 4293713893,
inverseOnSurface: 4281478965, inverseOnSurface: 4281675315,
outline: 4287926169, outline: 4288318869,
outlineVariant: 4282991950, outlineVariant: 4283319371,
shadow: 4278190080, shadow: 4278190080,
scrim: 4278190080, scrim: 4278190080,
surfaceDim: 4279505432, surfaceDim: 4279702038,
surfaceBright: 4282071102, surfaceBright: 4282267452,
surfaceContainerLowest: 4279176467, surfaceContainerLowest: 4279373073,
surfaceContainerLow: 4280097568, surfaceContainerLow: 4280293918,
surfaceContainer: 4280360740, surfaceContainer: 4280557090,
surfaceContainerHigh: 4281018671, surfaceContainerHigh: 4281280557,
surfaceContainerHighest: 4281742394, surfaceContainerHighest: 4282004280,
surfaceTint: 4291804670, surfaceTint: 4294030310,
}} }}
/> />
<style> <style>
:global(.icon) { :global(body, html) {
font-size: 2em;
}
:global(.pad-children > *) {
margin: 2rem;
}
:global(#nav > *) {
padding: 0.5em;
}
spacer {
margin: 1em;
}
:global(body, html, #app) {
width: 100vw;
height: 100vh; height: 100vh;
padding: 0;
margin: 0; margin: 0;
background-color: rgb(var(--m3-scheme-background));
color: rgb(var(--m3-scheme-on-background));
} }
</style> </style>

View file

@ -0,0 +1,234 @@
<script lang="ts">
import { initializeApp } from "firebase/app";
import {
Auth,
browserLocalPersistence,
createUserWithEmailAndPassword,
getAuth,
setPersistence,
signInWithEmailAndPassword,
} from "firebase/auth";
import {
Button,
Dialog,
Divider,
ListItemLabel,
RadioAnim2,
SnackbarAnim,
TextField,
} from "m3-svelte";
import { setBareClientImplementation } from "@mercuryworkshop/bare-client-custom";
import {
AdriftBareClient,
Connection,
RTCTransport,
SignalFirebase,
} from "client";
import trackerList from "tracker-list";
import { UIState } from "./state";
type valueof<T> = T[keyof T];
export let dialogConnection: boolean;
let tlsWarningShown = false;
let selectedTracker = Object.keys(trackerList)[0] as keyof typeof trackerList;
let trackerStats: Record<string, any> = {};
const getStats = async (tracker: valueof<typeof trackerList>) => {
const url = new URL(`${tracker.tracker}/stats`);
url.protocol = "https://";
const resp = await fetch(url);
return await resp.json();
};
Object.entries(trackerList).map(async ([trackerId, tracker]) => {
const stats = await getStats(tracker);
trackerStats[trackerId] = stats;
});
export let state: UIState;
export let stateStr: string;
let auth: Auth;
let transport: RTCTransport;
function createRTCTransport() {
return new RTCTransport(
async () => {
stateStr = "Transport opened";
console.log("[TRANSPORT] opened");
const connection = new Connection(transport);
await connection.initialize();
const bare = new AdriftBareClient(connection);
setBareClientImplementation(bare);
state = UIState.Connected;
},
() => console.warn("[TRANSPORT] closed"),
() => {
stateStr = `Connection ${transport.peer.connectionState}...`;
},
() => {
stateStr = `Signaling ${transport.peer.connectionState}...`;
},
() => {
if (transport.peer.connectionState == "new") {
stateStr = `Creating an offer...`;
} else {
stateStr = `Gathering ${transport.peer.connectionState}...`;
}
}
);
}
function connectToApp(tracker: valueof<typeof trackerList>) {
initializeApp(tracker.firebase);
auth = getAuth();
}
async function connectSwarm() {
connectToApp(trackerList[selectedTracker]);
state = UIState.Connecting;
transport = createRTCTransport();
const offer = await transport.createOffer();
stateStr = "Finding a node...";
try {
const answer = await SignalFirebase.signalSwarm(JSON.stringify(offer));
stateStr = "Linking to the node...";
transport.answer(answer.answer, answer.candidates);
} catch (e) {
console.error("While connecting to swarm", e);
stateStr = e;
}
}
let dialogAccount = false;
let email = "";
let password = "";
let snackbar: (data: any) => void;
async function connectNode() {
state = UIState.Connecting;
transport = createRTCTransport();
const offer = await transport.createOffer();
stateStr = "Finding your node...";
let answer = await SignalFirebase.signalAccount(JSON.stringify(offer));
stateStr = "Linking to the node...";
transport.answer(answer.answer, answer.candidates);
}
</script>
<Dialog bind:open={dialogConnection} headline="Connect to a tracker">
<div class="flex flex-col -mx-4">
{#each Object.entries(trackerList) as [trackerId, tracker], i}
{#if i != 0}
<Divider />
{/if}
<ListItemLabel
headline={trackerId}
supporting={trackerStats[trackerId]
? `${tracker.description} - ${
trackerStats[trackerId].members.length
} ${trackerStats[trackerId].members.length == 1 ? "node" : "nodes"}`
: tracker.description}
>
<RadioAnim2 slot="leading">
<input
type="radio"
name="tracker"
bind:group={selectedTracker}
value={trackerId}
/>
</RadioAnim2>
</ListItemLabel>
{/each}
</div>
<p class="font-bold mt-4" class:hide-warning={!tlsWarningShown}>
Your data is not end-to-end encrypted, and will not be private. While TLS
will be implemented later, for now do not enter any private information.
Click connect again to continue.
</p>
<svelte:fragment slot="buttons">
<Button type="text" on:click={() => (dialogConnection = false)}>
Cancel
</Button>
<Button
type="text"
on:click={() => {
connectToApp(trackerList[selectedTracker]);
setPersistence(auth, browserLocalPersistence);
console.log(browserLocalPersistence, auth.currentUser);
if (auth.currentUser) {
connectNode();
} else {
dialogConnection = false;
dialogAccount = true;
}
}}
>
Connect to your node
</Button>
<Button
type="tonal"
on:click={() => {
if (tlsWarningShown) {
connectSwarm();
dialogConnection = false;
}
tlsWarningShown = true;
}}
>
Connect
</Button>
</svelte:fragment>
</Dialog>
<Dialog bind:open={dialogAccount} headline="Sign in">
<div class="flex gap-4 flex-col">
<TextField name="Email" bind:value={email} />
<TextField
name="Password"
bind:value={password}
extraOptions={{ type: "password" }}
/>
</div>
<svelte:fragment slot="buttons">
<Button type="text" on:click={() => (dialogAccount = false)}>Cancel</Button>
<Button
type="text"
on:click={async () => {
try {
await createUserWithEmailAndPassword(auth, email, password);
await connectNode();
} catch (e) {
snackbar({ message: e.message, closable: true });
}
}}
>
Sign up
</Button>
<Button
type="text"
on:click={async () => {
try {
await signInWithEmailAndPassword(auth, email, password);
await connectNode();
} catch (e) {
snackbar({ message: e.message, closable: true });
}
}}
>
Log in
</Button>
</svelte:fragment>
</Dialog>
<SnackbarAnim bind:show={snackbar} />
<style>
.hide-warning {
margin: 0;
height: 0;
visibility: hidden;
}
</style>

View file

@ -3,9 +3,6 @@
import { Win, openWindow } from "../../corium"; import { Win, openWindow } from "../../corium";
import Icon from "@iconify/svelte"; import Icon from "@iconify/svelte";
let selectedProxy = "ultraviolet";
let url: string = "http://google.com";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { import {
Dialog, Dialog,
@ -14,10 +11,14 @@
TextField, TextField,
} from "m3-svelte"; } from "m3-svelte";
let url = "http://google.com";
let proxyIframe: HTMLIFrameElement; let proxyIframe: HTMLIFrameElement;
let settingsenabled: boolean = false; let settingsenabled: boolean = false;
let selectedProxy = "ultraviolet";
let searchengine = "https://google.com/search?q="; let searchengine = "https://google.com/search?q=";
function frameLoad() { function frameLoad() {
@ -59,35 +60,35 @@
}); });
</script> </script>
<div class="h-full w-full flex flex-col"> <div class="h-full flex flex-col">
<div class="flex p-2"> <div class="flex p-2">
<div class="flex text-xl items-center w-full">
<button <button
class="text-2xl p-2 hover:text-primary"
on:click={() => { on:click={() => {
proxyIframe.contentWindow?.history.back(); proxyIframe.contentWindow?.history.back();
}} }}
> >
<Icon icon="fluent-mdl2:back" /> <Icon icon="ic:round-arrow-back" />
</button> </button>
<div class="p-2" />
<button <button
class="text-2xl p-2 hover:text-primary"
on:click={() => { on:click={() => {
proxyIframe.contentWindow?.history.forward(); proxyIframe.contentWindow?.history.forward();
}} }}
> >
<Icon icon="fluent-mdl2:forward" /> <Icon icon="ic:round-arrow-forward" />
</button> </button>
<button <button
class="text-2xl px-4" class="text-2xl p-2 hover:text-primary"
on:click={() => { on:click={() => {
visitURL(url); visitURL(url);
}} }}
> >
<Icon icon="tabler:reload" /> <Icon icon="ic:round-refresh" />
</button> </button>
<div class="urlbar flex items-center flex-1"> <div class="flex flex-1 mx-2 border border-outline rounded-xl">
<button <button
class="text-2xl px-2" class="text-2xl px-2 hover:text-primary"
on:click={() => { on:click={() => {
visitURL(url); visitURL(url);
}} }}
@ -97,7 +98,7 @@
<input <input
bind:value={url} bind:value={url}
type="text" type="text"
class="flex-1" class="flex-1 w-0 pr-2 text-xl"
on:keydown={(e) => { on:keydown={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
visitURL(url); visitURL(url);
@ -105,12 +106,16 @@
}} }}
/> />
</div> </div>
<button
<button class="text-2xl pl-3" on:click={() => (settingsenabled = true)}> class="text-2xl p-2 hover:text-primary"
on:click={() => (settingsenabled = true)}
>
<Icon icon="ic:round-settings" /> <Icon icon="ic:round-settings" />
</button> </button>
</div> </div>
<Dialog bind:open={settingsenabled} headline="Proxy Settings"> <iframe class="flex-1" bind:this={proxyIframe} on:load={frameLoad} />
</div>
<Dialog bind:open={settingsenabled} headline="Proxy Settings">
{#if !import.meta.env.VITE_ADRIFT_SINGLEFILE} {#if !import.meta.env.VITE_ADRIFT_SINGLEFILE}
<div> <div>
<SegmentedButtonContainer> <SegmentedButtonContainer>
@ -121,9 +126,9 @@
value="ultraviolet" value="ultraviolet"
id="ultraviolet" id="ultraviolet"
/> />
<SegmentedButtonItem input="ultraviolet" <SegmentedButtonItem input="ultraviolet">
>Ultraviolet</SegmentedButtonItem Ultraviolet
> </SegmentedButtonItem>
<input <input
type="radio" type="radio"
name="selectedProxy" name="selectedProxy"
@ -138,17 +143,9 @@
<TextField name="Default Search Engine" bind:value={searchengine} /> <TextField name="Default Search Engine" bind:value={searchengine} />
{/if} {/if}
</Dialog> </Dialog>
</div>
<iframe class="flex-1" bind:this={proxyIframe} on:load={frameLoad} />
</div>
<style> <style>
.urlbar {
border: solid 0.0625rem rgb(var(--m3-scheme-outline));
padding: 0.25rem;
border-radius: 0.75rem;
}
input { input {
background-color: transparent; background-color: transparent;
outline: none; outline: none;

View file

@ -1,6 +1,18 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
*{ html {
font-family: "Cabin"; background-color: rgb(var(--m3-scheme-background));
color: rgb(var(--m3-scheme-on-background));
--m3-font-display: Cabin, system-ui, sans-serif;
--m3-font-headline: Cabin, system-ui, sans-serif;
--m3-font-title: Cabin, system-ui, sans-serif;
--m3-font-label: Cabin, system-ui, sans-serif;
--m3-font-label-large-tracking: 0.25px;
--m3-font-label-medium-tracking: 1px;
--m3-font-label-small-tracking: 1px;
--m3-font-body: Cabin, system-ui, sans-serif;
}
#app {
display: contents;
} }

5
frontend/src/state.ts Normal file
View file

@ -0,0 +1,5 @@
export enum UIState {
Idle,
Connecting,
Connected,
}

View file

@ -1,11 +1,68 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
content: [ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx,svelte,html}"],
"./index.html",
"./src/**/*.{js,ts,jsx,tsx,svelte,html}",
],
theme: { theme: {
extend: {}, extend: {
colors: {
primary: "rgb(var(--m3-scheme-primary) / <alpha-value>)",
"on-primary": "rgb(var(--m3-scheme-on-primary) / <alpha-value>)",
"primary-container":
"rgb(var(--m3-scheme-primary-container) / <alpha-value>)",
"on-primary-container":
"rgb(var(--m3-scheme-on-primary-container) / <alpha-value>)",
secondary: "rgb(var(--m3-scheme-secondary) / <alpha-value>)",
"on-secondary": "rgb(var(--m3-scheme-on-secondary) / <alpha-value>)",
"secondary-container":
"rgb(var(--m3-scheme-secondary-container) / <alpha-value>)",
"on-secondary-container":
"rgb(var(--m3-scheme-on-secondary-container) / <alpha-value>)",
tertiary: "rgb(var(--m3-scheme-tertiary) / <alpha-value>)",
"on-tertiary": "rgb(var(--m3-scheme-on-tertiary) / <alpha-value>)",
"tertiary-container":
"rgb(var(--m3-scheme-tertiary-container) / <alpha-value>)",
"on-tertiary-container":
"rgb(var(--m3-scheme-on-tertiary-container) / <alpha-value>)",
error: "rgb(var(--m3-scheme-error) / <alpha-value>)",
"on-error": "rgb(var(--m3-scheme-on-error) / <alpha-value>)",
"error-container":
"rgb(var(--m3-scheme-error-container) / <alpha-value>)",
"on-error-container":
"rgb(var(--m3-scheme-on-error-container) / <alpha-value>)",
background: "rgb(var(--m3-scheme-background) / <alpha-value>)",
"on-background": "rgb(var(--m3-scheme-on-background) / <alpha-value>)",
surface: "rgb(var(--m3-scheme-surface) / <alpha-value>)",
"on-surface": "rgb(var(--m3-scheme-on-surface) / <alpha-value>)",
"surface-variant":
"rgb(var(--m3-scheme-surface-variant) / <alpha-value>)",
"on-surface-variant":
"rgb(var(--m3-scheme-on-surface-variant) / <alpha-value>)",
outline: "rgb(var(--m3-scheme-outline) / <alpha-value>)",
"outline-variant":
"rgb(var(--m3-scheme-outline-variant) / <alpha-value>)",
shadow: "rgb(var(--m3-scheme-shadow) / <alpha-value>)",
scrim: "rgb(var(--m3-scheme-scrim) / <alpha-value>)",
"inverse-surface":
"rgb(var(--m3-scheme-inverse-surface) / <alpha-value>)",
"inverse-on-surface":
"rgb(var(--m3-scheme-inverse-on-surface) / <alpha-value>)",
"inverse-primary":
"rgb(var(--m3-scheme-inverse-primary) / <alpha-value>)",
"surface-bright":
"rgb(var(--m3-scheme-surface-bright) / <alpha-value>)",
"surface-container":
"rgb(var(--m3-scheme-surface-container) / <alpha-value>)",
"surface-container-high":
"rgb(var(--m3-scheme-surface-container-high) / <alpha-value>)",
"surface-container-highest":
"rgb(var(--m3-scheme-surface-container-highest) / <alpha-value>)",
"surface-container-low":
"rgb(var(--m3-scheme-surface-container-low) / <alpha-value>)",
"surface-container-lowest":
"rgb(var(--m3-scheme-surface-container-lowest) / <alpha-value>)",
"surface-dim": "rgb(var(--m3-scheme-surface-dim) / <alpha-value>)",
"surface-tint": "rgb(var(--m3-scheme-surface-tint) / <alpha-value>)",
},
},
}, },
plugins: [], plugins: [],
} };