setup basic relay connections

This commit is contained in:
hzrd149 2023-02-07 17:04:17 -06:00
commit 302d6085d8
12 changed files with 2917 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dist
node_modules

0
.prettierrc Normal file
View File

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>nostr-client</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="src/index.jsx"></script>
</body>
</html>

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "nostr-client",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "vite serve",
"build": "vite build"
},
"devDependencies": {
"prettier": "^2.8.1",
"vite": "^4.0.2",
"vite-plugin-pwa": "^0.14.0"
},
"dependencies": {
"idb": "^7.1.1",
"noble-secp256k1": "^1.2.14",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.5.0"
}
}

26
src/helpers/signal.js Normal file
View File

@ -0,0 +1,26 @@
export class Signal {
listeners = new Set();
connections = new Set();
emit(event) {
for (const fn of this.listeners) {
fn(event);
}
for (const signal of this.connections) {
signal.emit(event);
}
}
addListener(fn) {
this.listeners.add(fn);
}
removeListener(fn) {
this.listeners.delete(fn);
}
addConnection(signal) {
this.connections.add(signal);
}
removeConnection(signal) {
this.connections.delete(signal);
}
}

17
src/index.jsx Normal file
View File

@ -0,0 +1,17 @@
import React from "react";
import { createRoot } from "react-dom/client";
import { connectToRelays, subscribeToAuthor, onEvent } from "./services/relays";
const root = createRoot(document.getElementById("root"));
root.render(<h1>Hello, world!</h1>);
await connectToRelays();
const self = "266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5";
subscribeToAuthor(self);
onEvent.addListener((event) => {
console.log(event);
});

13
src/services/db.js Normal file
View File

@ -0,0 +1,13 @@
import { openDB } from "idb";
import { upgrade } from "./migrations";
const version = 1;
const db = await openDB("storage", version, {
upgrade,
});
export async function getUsername(id) {
db.get("users", id);
}
export default db;

View File

@ -0,0 +1,22 @@
const MIGRATIONS = [
// 0 -> 1
function (db, transaction, event) {
db.createObjectStore("users", {
keyPath: "pubkey",
});
db.createObjectStore("settings");
// setup data
const settings = transaction.objectStore("settings");
settings.put(["wss://nostr.rdfriedl.com"], "relays");
},
];
export function upgrade(db, oldVersion, newVersion, transaction, event) {
for (let i = oldVersion; i <= newVersion; i++) {
if (MIGRATIONS[i]) {
console.log(`Running database migration ${i}`);
MIGRATIONS[i](db, transaction, event);
}
}
}

88
src/services/relays.js Normal file
View File

@ -0,0 +1,88 @@
import { Signal } from "../helpers/signal";
import { getRelays } from "./settings";
class RelayConnection {
constructor(url) {
this.ws = new WebSocket(url);
this.url = url;
this.onEvent = new Signal();
this.onNotice = new Signal();
this.ws.onclose = this.handleClose.bind(this);
this.ws.onmessage = this.handleMessage.bind(this);
}
send(json) {
this.ws.send(JSON.stringify(json));
}
get connected() {
return this.ws.readyState === WebSocket.OPEN;
}
get state() {
return this.ws.readyState;
}
handleMessage(event) {
const data = JSON.parse(event.data);
const type = data[0];
switch (type) {
case "EVENT":
this.onEvent.emit({ subId: data[1], body: data[2] });
break;
case "NOTICE":
this.onEvent.emit({ message: data[1] });
break;
}
}
handleClose() {
console.log(this.url, "closed");
}
}
const connections = new Map();
export const onEvent = new Signal();
export function getAllActive() {
return Array.from(connections.values()).filter((relay) => relay.connected);
}
export async function connectToRelay(url) {
if (!connections.has(url)) {
const relay = new RelayConnection(url);
connections.set(url, relay);
// send all onEvent events to the main onEvent signal
relay.onEvent.addConnection(onEvent);
}
}
export async function connectToRelays() {
const relayUrls = await getRelays();
for (const url of relayUrls) {
await connectToRelay(url);
}
}
const messageQueue = [];
export async function sendNextMessage() {
const next = messageQueue.shift();
if (!next) return;
let sent = false;
for (const [url, relay] of connections) {
if (relay.connected) {
relay.send(next);
sent = true;
}
}
if (!sent) {
messageQueue.unshift(next);
}
}
setInterval(sendNextMessage, 100);
export function subscribeToAuthor(pubkey) {
messageQueue.push(["REQ", "test", { authors: [pubkey] }]);
}

15
src/services/settings.js Normal file
View File

@ -0,0 +1,15 @@
import db from "./db";
export async function getRelays() {
return await db.get("settings", "relays");
}
export async function setRelays(relays = []) {
return await db.put("settings", relays, "relays");
}
if (import.meta.env.DEV) {
window.relayService = {
getRelays,
setRelays,
};
}

16
vite.config.js Normal file
View File

@ -0,0 +1,16 @@
import { VitePWA } from "vite-plugin-pwa";
import { defineConfig } from "vite";
export default defineConfig({
build: {
target: ["chrome89", "edge89", "firefox89", "safari15"],
},
// plugins: [
// VitePWA({
// registerType: "autoUpdate",
// devOptions: {
// enabled: true,
// },
// }),
// ],
});

2683
yarn.lock Normal file

File diff suppressed because it is too large Load Diff