mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
setup basic relay connections
This commit is contained in:
commit
302d6085d8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
dist
|
||||
node_modules
|
0
.prettierrc
Normal file
0
.prettierrc
Normal file
13
index.html
Normal file
13
index.html
Normal 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
22
package.json
Normal 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
26
src/helpers/signal.js
Normal 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
17
src/index.jsx
Normal 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
13
src/services/db.js
Normal 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;
|
22
src/services/migrations/index.js
Normal file
22
src/services/migrations/index.js
Normal 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
88
src/services/relays.js
Normal 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
15
src/services/settings.js
Normal 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
16
vite.config.js
Normal 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,
|
||||
// },
|
||||
// }),
|
||||
// ],
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user