Add events to peer manager

This commit is contained in:
2025-04-27 22:38:42 -07:00
parent 63d7b276db
commit 0005f06b2b
3 changed files with 115 additions and 202 deletions

View File

@@ -10,6 +10,13 @@ import { log, logID } from "log";
// Use a broadcast channel to only have one peer manager for multiple tabs, // Use a broadcast channel to only have one peer manager for multiple tabs,
// then we won't need to have a session ID as all queries for a peerID will be coming from the same peer manager // then we won't need to have a session ID as all queries for a peerID will be coming from the same peer manager
type PeerID = string;
export enum PeerEventTypes {
PEER_CONNECTED,
PEER_DISCONNECTED,
}
export class PeerManager { export class PeerManager {
routingTable: Map<string, string>; routingTable: Map<string, string>;
@@ -22,7 +29,7 @@ export class PeerManager {
bootstrapPeerConnection: PeerConnection | null = null; bootstrapPeerConnection: PeerConnection | null = null;
sessionID = generateID(); sessionID = generateID();
userID: string; userID: string;
peerID: string; peerID: PeerID;
websocket: WebSocket | null = null; websocket: WebSocket | null = null;
bootstrapPeerID: string | null = null; bootstrapPeerID: string | null = null;
@@ -30,6 +37,7 @@ export class PeerManager {
pingPeers: RTCPeerConnection[] = []; pingPeers: RTCPeerConnection[] = [];
watchdogPeriodSeconds: number = 10; watchdogPeriodSeconds: number = 10;
eventListeners: Map<PeerEventTypes, Function[]> = new Map();
// async watchdog() { // async watchdog() {
// // Check that we're connected to at least N peers. If not, reconnect to the bootstrap server. // // Check that we're connected to at least N peers. If not, reconnect to the bootstrap server.
@@ -261,10 +269,35 @@ export class PeerManager {
let peerConnection = new PeerConnection(this, remotePeerID, this.websocketSendPeerMessage.bind(this)); let peerConnection = new PeerConnection(this, remotePeerID, this.websocketSendPeerMessage.bind(this));
this.peers.set(remotePeerID, peerConnection); this.peers.set(remotePeerID, peerConnection);
await peerConnection.connect(); await peerConnection.connect();
this.onPeerConnected(this.peerID);
return peerConnection; return peerConnection;
} }
onPeerDisconnected(remotePeerID: string) { onPeerConnected(peerID: PeerID) {
this.dispatchEvent(PeerEventTypes.PEER_CONNECTED, {peerID:peerID});
}
dispatchEvent(event:PeerEventTypes, parameters:any) {
let listeners = this.eventListeners.get(event);
if (!listeners) {
return;
}
for (let listener of listeners) {
listener(parameters);
}
}
addEventListener(eventName:PeerEventTypes, func:Function) {
let listeners = this.eventListeners.get(eventName);
if (!listeners) {
this.eventListeners.set(eventName, [func]);
}
}
onPeerDisconnected(remotePeerID: PeerID) {
let deleted = this.peers.delete(remotePeerID); let deleted = this.peers.delete(remotePeerID);
if (!deleted) { if (!deleted) {

View File

@@ -33,9 +33,9 @@ Restruucture the app around the data. App/WS split is messy. Clean it up.
// import * as ForceGraph3D from "3d-force-graph"; // import * as ForceGraph3D from "3d-force-graph";
import { openDatabase, getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db"; import { openDatabase, getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db";
import { generateID } from "IDUtils"; import { generateID } from "IDUtils";
import { PeerManager } from "PeerManager"; import { PeerManager, PeerEventTypes } from "PeerManager";
import {log, logID, renderLog, setLogVisibility} from "log" import { log, logID, renderLog, setLogVisibility } from "log"
// import {PeerConnection} from "webRTC"; // import {PeerConnection} from "webRTC";
@@ -244,95 +244,14 @@ interface PeerMessage {
message: any; message: any;
} }
// class Signaler {
// websocket: WebSocket | null = null;
// websocketPingInterval: number = 0;
// connect() {
// if (this.websocket?.readyState === WebSocket.OPEN) {
// return;
// }
// window.clearInterval(this.websocketPingInterval);
// if (this.websocket) { this.websocket.close() };
// try {
// this.websocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/ws`);
// } catch (error: any) {
// console.log.apply(null, log(error.message);
// return;
// }
// this.websocket.onopen = async (event) => {
// console.log.apply(null, log("ws:connected"));;
// await this.sendHello();
// // If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
// let helloRefreshIntervalPeriod = 120;
// if (app.isHeadless) {
// console.log.apply(null, log("wsConnection: Setting hello refresh interval to ", helloRefreshIntervalPeriod)
// this.helloRefreshInterval = window.setInterval(() => {
// console.log.apply(null, log("wsConnection: Hello refresh.")
// if (!navigator.onLine) {
// return;
// }
// this.sendHello();
// }, helloRefreshIntervalPeriod * 1000);
// }
// this.websocketPingInterval = window.setInterval(() => {
// if (!navigator.onLine) {
// return;
// }
// this.send({ type: "ping", peer_id: this.peerID, peer_name: app.peername, user_id: app.userID, user_name: app.username });
// }, 10_000)
// };
// this.websocket.onclose = (event) => {
// console.log.apply(null, log("ws:disconnected"));;
// // this.retry *= 2;
// console.log.apply(null, log(`Retrying in ${this.retry} seconds`));;
// window.setTimeout(() => { this.connect(); }, this.retry * 1000);
// };
// this.websocket.onmessage = (event) => {
// // log('ws:<-' + event.data.slice(0, 240));
// let data = JSON.parse(event.data);
// let { type } = data;
// let handler = this.messageHandlers.get(type);
// if (!handler) {
// console.warn(`Got a message we can't handle:`, type);
// return;
// }
// handler(data);
// };
// this.websocket.onerror = (event) => {
// console.log.apply(null, log('ws:error: ' + event));;
// };
// }
// }
// disconnect() {
// this.websocket?.close();
// }
// }
// Connect websocket // Connect websocket
// send hello // send hello
// get bootstrap peer ID // get bootstrap peer ID
// WebRTC connect to bootstrap peer // WebRTC connect to bootstrap peer
// ask Bootstrap peer for peers that have users we care about. // Bootstrap peer will send the last N peers it saw.
// for now, bootstrap peer will connect to all peers and will tell us about them, moving all logic from the server to the BSP // Connect to those new peers, tell those peers about users we know about
// ask for peers that have users we care about
// WebRTC Connect to peers that might have posts we need // WebRTC Connect to peers that might have posts we need
// query those peers and do existing logic. // query those peers and do existing logic.
@@ -341,7 +260,7 @@ class wsConnection {
sessionID = ""; sessionID = "";
userID = ""; userID = "";
peerID = ""; peerID = "";
rtcPeerDescription: RTCSessionDescription | null = null; // rtcPeerDescription: RTCSessionDescription | null = null;
UserIDsToSync: Set<string>; UserIDsToSync: Set<string>;
websocketPingInterval: number = 0; websocketPingInterval: number = 0;
helloRefreshInterval: number = 0; helloRefreshInterval: number = 0;
@@ -355,7 +274,7 @@ class wsConnection {
constructor(userID: string, peerID: string, IDsToSync: Set<string>, rtcPeerDescription: RTCSessionDescription) { constructor(userID: string, peerID: string, IDsToSync: Set<string>, rtcPeerDescription: RTCSessionDescription) {
this.rtcPeerDescription = rtcPeerDescription; // this.rtcPeerDescription = rtcPeerDescription;
this.sessionID = generateID(); this.sessionID = generateID();
this.userID = userID; this.userID = userID;
this.peerID = peerID; this.peerID = peerID;
@@ -420,19 +339,19 @@ class wsConnection {
// log(`getPostsForUserResponse: ${data}`) // log(`getPostsForUserResponse: ${data}`)
let message = data.message; let message = data.message;
console.log.apply(null, log(`Net: got ${message.post_ids.length} post IDs for user ${logID(message.user_id)} from peer ${logID(data.from)}`));; console.log.apply(null, log(`Net: got ${message.post_ids.length} post IDs for user ${logID(message.user_id)} from peer ${logID(data.from)}`));;
let startTime = app.timerStart(); let startTime = app.timerStart();
let postIds = await checkPostIds(message.user_id, message.post_ids); let postIds = await checkPostIds(message.user_id, message.post_ids);
console.log.apply(null, log(`ID Check for user ${logID(message.user_id)} took ${app.timerDelta().toFixed(2)}ms`));; console.log.apply(null, log(`ID Check for user ${logID(message.user_id)} took ${app.timerDelta().toFixed(2)}ms`));;
console.log.apply(null, log(`Need ${postIds.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`));; console.log.apply(null, log(`Need ${postIds.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`));;
if (postIds.length === 0) { if (postIds.length === 0) {
return; return;
} }
console.log.apply(null, log(`Net: Req ${postIds.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`)); console.log.apply(null, log(`Net: Req ${postIds.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`));
let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_posts_for_user", post_ids: postIds, user_id: message.user_id } } let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_posts_for_user", post_ids: postIds, user_id: message.user_id } }
this.send(responseMessage); this.send(responseMessage);
@@ -466,10 +385,10 @@ class wsConnection {
let postIds = await getAllIds(message.user_id) ?? []; let postIds = await getAllIds(message.user_id) ?? [];
postIds = postIds.filter((postID: string) => !this.postBlockList.has(postID)); postIds = postIds.filter((postID: string) => !this.postBlockList.has(postID));
if (postIds.length === 0) { if (postIds.length === 0) {
console.log.apply(null, log(`Net: I know about user ${logID(message.user_id)} but I have 0 posts, so I'm not sending any to to peer ${logID(data.from)}`));; console.log.apply(null, log(`Net: I know about user ${logID(message.user_id)} but I have 0 posts, so I'm not sending any to to peer ${logID(data.from)}`));;
return; return;
} }
console.log.apply(null, log(`Net: Sending ${postIds.length} post Ids for user ${logID(message.user_id)} to peer ${logID(data.from)}`)); console.log.apply(null, log(`Net: Sending ${postIds.length} post Ids for user ${logID(message.user_id)} to peer ${logID(data.from)}`));
let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_post_ids_for_user_response", post_ids: postIds, user_id: message.user_id } } let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_post_ids_for_user_response", post_ids: postIds, user_id: message.user_id } }
this.send(responseMessage); this.send(responseMessage);
@@ -483,7 +402,7 @@ class wsConnection {
} }
for (let [peerID, peerInfo] of this.seenPeers.entries()) { for (let [peerID, peerInfo] of this.seenPeers.entries()) {
console.log.apply(null, log(`broadcastNewPost: sending new post to ${logID(peerID)}:${peerInfo.peerName}:${peerInfo.userName}`));; console.log.apply(null, log(`broadcastNewPost: sending new post to ${logID(peerID)}:${peerInfo.peerName}:${peerInfo.userName}`));;
this.sendPostsForUser(peerID, app.userID, [newPost]) this.sendPostsForUser(peerID, app.userID, [newPost])
} }
@@ -513,7 +432,7 @@ class wsConnection {
let message = data.message; let message = data.message;
let posts = await getPostsByIds(message.user_id, message.post_ids) ?? []; let posts = await getPostsByIds(message.user_id, message.post_ids) ?? [];
console.log.apply(null, log(`Net: Sending ${posts.length} posts for user ${logID(message.user_id)} to peer ${logID(data.from)}`));; console.log.apply(null, log(`Net: Sending ${posts.length} posts for user ${logID(message.user_id)} to peer ${logID(data.from)}`));;
app.timerStart(); app.timerStart();
let output = []; let output = [];
@@ -543,7 +462,7 @@ class wsConnection {
console.log.apply(null, log("Sending posts")); console.log.apply(null, log("Sending posts"));
await this.sendPostsForUser(data.from, message.user_id, output); await this.sendPostsForUser(data.from, message.user_id, output);
let sendTime = app.timerDelta(); let sendTime = app.timerDelta();
console.log.apply(null, log(`getPostsForUserHandler send took: ${sendTime.toFixed(2)}ms`));; console.log.apply(null, log(`getPostsForUserHandler send took: ${sendTime.toFixed(2)}ms`));;
} }
@@ -559,7 +478,7 @@ class wsConnection {
// HACK: Some posts have insanely large images, so I'm gonna skip them. // HACK: Some posts have insanely large images, so I'm gonna skip them.
// Once we support delete then we we could delete these posts in a sensible way. // Once we support delete then we we could delete these posts in a sensible way.
if (this.postBlockList.has(post.post_id)) { if (this.postBlockList.has(post.post_id)) {
console.log.apply(null, log(`Skipping blocked post: ${post.post_id}`));; console.log.apply(null, log(`Skipping blocked post: ${post.post_id}`));;
continue; continue;
} }
@@ -578,7 +497,7 @@ class wsConnection {
let receiveTime = app.timerDelta(); let receiveTime = app.timerDelta();
console.log.apply(null, log(`getPostsForUserReponseHandler receive took: ${receiveTime.toFixed(2)}ms`));; console.log.apply(null, log(`getPostsForUserReponseHandler receive took: ${receiveTime.toFixed(2)}ms`));;
if (message.user_id === app.getPreferentialUserID() || app.following.has(message.user_id)) { if (message.user_id === app.getPreferentialUserID() || app.following.has(message.user_id)) {
@@ -626,10 +545,18 @@ class wsConnection {
session_id: this.sessionID, session_id: this.sessionID,
peer_name: app.peername, peer_name: app.peername,
is_bootstrap_peer: app.isBootstrapPeer, is_bootstrap_peer: app.isBootstrapPeer,
peer_description: this.rtcPeerDescription // peer_description: this.rtcPeerDescription
}); });
} }
async getKnownUsers() {
let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
knownUsers = knownUsers
.filter(userID => this.shouldSyncUserID(userID))
.filter(userID => !this.userBlockList.has(userID))
.filter(async userID => (await getAllIds(userID)).length > 0); // TODO:EASYOPT getting all the IDs is unecessary, replace it with a test to get a single ID.
}
async sendHello() { async sendHello() {
// TODO only get users you're following here. ✅ // TODO only get users you're following here. ✅
let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined); let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
@@ -650,7 +577,7 @@ class wsConnection {
let users = []; let users = [];
let receivedUsers = Object.entries(data.userPeers); let receivedUsers = Object.entries(data.userPeers);
console.log.apply(null, log(`Net: got ${receivedUsers.length} users from bootstrap peer.`)); console.log.apply(null, log(`Net: got ${receivedUsers.length} users from bootstrap peer.`));
try { try {
let preferentialUserID = app.getPreferentialUserID(); let preferentialUserID = app.getPreferentialUserID();
@@ -681,7 +608,7 @@ class wsConnection {
continue; continue;
} }
console.log.apply(null, log(`Net: Req post IDs for user ${logID(userID)} from peer ${logID(peerID)}`));; console.log.apply(null, log(`Net: Req post IDs for user ${logID(userID)} from peer ${logID(peerID)}`));;
this.send({ this.send({
type: "peer_message", type: "peer_message",
from: this.peerID, from: this.peerID,
@@ -710,7 +637,7 @@ class wsConnection {
} }
this.websocket.onopen = async (event) => { this.websocket.onopen = async (event) => {
console.log.apply(null, log("ws:connected"));; console.log.apply(null, log("ws:connected"));;
await this.sendHello2(); await this.sendHello2();
// If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have. // If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
@@ -762,9 +689,9 @@ class wsConnection {
// }; // };
this.websocket.onclose = (event) => { this.websocket.onclose = (event) => {
console.log.apply(null, log("ws:disconnected"));; console.log.apply(null, log("ws:disconnected"));;
// this.retry *= 2; // this.retry *= 2;
console.log.apply(null, log(`Retrying in ${this.retry} seconds`));; console.log.apply(null, log(`Retrying in ${this.retry} seconds`));;
window.setTimeout(() => { this.connect(); }, this.retry * 1000); window.setTimeout(() => { this.connect(); }, this.retry * 1000);
}; };
@@ -785,7 +712,7 @@ class wsConnection {
}; };
this.websocket.onerror = (event) => { this.websocket.onerror = (event) => {
console.log.apply(null, log('ws:error: ' + event));; console.log.apply(null, log('ws:error: ' + event));;
}; };
} }
@@ -816,7 +743,13 @@ class App {
async connect() { async connect() {
this.peerManager = new PeerManager(this.userID, this.peerID, this.isBootstrapPeer); this.peerManager = new PeerManager(this.userID, this.peerID, this.isBootstrapPeer);
this.registerRPCs(); this.registerRPCs();
console.log.apply(null, log("*************** before peerManager.connect"));;
this.peerManager.addEventListener(PeerEventTypes.PEER_CONNECTED, (event: any) => {
console.log.apply(null, log(`[app]: peer connected:${event.peerID}`));
// rpc saying what peers we have
})
console.log.apply(null, log("*************** before peerManager.connect"));
// We use promises here to only return from this call once we're connected to the boostrap peer // We use promises here to only return from this call once we're connected to the boostrap peer
// and the datachannel is open. // and the datachannel is open.
@@ -825,13 +758,13 @@ class App {
// Would be lovely to show a little display of peers connecting, whether you're connected directly to a friend's peer etc. // Would be lovely to show a little display of peers connecting, whether you're connected directly to a friend's peer etc.
// Basically that live "dandelion" display. // Basically that live "dandelion" display.
this.peerManager.registerRPC('getPostIDsForUser', (userID: any) => { this.peerManager.registerRPC('getPostIDsForUser', (userID: any) => {
return [1, 2, 3, 4, 5] return [1, 2, 3, 4, 5]
}); });
await this.peerManager.connect(); await this.peerManager.connect();
console.log.apply(null, log("*************** after peerManager.connect"));; console.log.apply(null, log("*************** after peerManager.connect"));;
if (!this.isBootstrapPeer) { if (!this.isBootstrapPeer) {
@@ -984,7 +917,7 @@ class App {
} }
async importTweetArchive(userID: string, tweetArchive: any[]) { async importTweetArchive(userID: string, tweetArchive: any[]) {
console.log.apply(null, log("Importing tweet archive")); console.log.apply(null, log("Importing tweet archive"));
let postsTestData: any[] = []; let postsTestData: any[] = [];
// let response = await fetch("./tweets.js"); // let response = await fetch("./tweets.js");
@@ -1031,7 +964,7 @@ class App {
count++; count++;
if (count % 100 === 0) { if (count % 100 === 0) {
console.log.apply(null, log(`Imported ${count} posts...`));; console.log.apply(null, log(`Imported ${count} posts...`));;
// render(postsTestData); // render(postsTestData);
} }
@@ -1073,7 +1006,7 @@ class App {
async compressImage(imageData: ArrayBuffer, mimeType: string, quality = 0.5): Promise<ArrayBuffer | null> { async compressImage(imageData: ArrayBuffer, mimeType: string, quality = 0.5): Promise<ArrayBuffer | null> {
let uncompressedByteLength = imageData.byteLength; let uncompressedByteLength = imageData.byteLength;
console.log.apply(null, log(`compressImage input:${mimeType} size:${(uncompressedByteLength / 1024).toFixed(2)}KBi quality:${quality}`));; console.log.apply(null, log(`compressImage input:${mimeType} size:${(uncompressedByteLength / 1024).toFixed(2)}KBi quality:${quality}`));;
try { try {
// Convert ArrayBuffer to Blob // Convert ArrayBuffer to Blob
@@ -1139,7 +1072,7 @@ class App {
let compressedByteLength = compressedArrayBuffer.byteLength; let compressedByteLength = compressedArrayBuffer.byteLength;
let percent = (uncompressedByteLength / compressedByteLength) let percent = (uncompressedByteLength / compressedByteLength)
console.log.apply(null, log(`compressImage: compressedSize:${(compressedArrayBuffer.byteLength / 1024).toFixed(2)}KBi ${percent.toFixed(2)}:1 compression`));; console.log.apply(null, log(`compressImage: compressedSize:${(compressedArrayBuffer.byteLength / 1024).toFixed(2)}KBi ${percent.toFixed(2)}:1 compression`));;
return compressedArrayBuffer; return compressedArrayBuffer;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@@ -1149,7 +1082,7 @@ class App {
async createNewPost(userID: string, postText: string, mediaData?: ArrayBuffer, mimeType?: "image/png" | "image/gif" | "image/jpg" | "image/jpeg" | "video/mp4") { async createNewPost(userID: string, postText: string, mediaData?: ArrayBuffer, mimeType?: "image/png" | "image/gif" | "image/jpg" | "image/jpeg" | "video/mp4") {
if ((typeof postText !== "string") || postText.length === 0) { if ((typeof postText !== "string") || postText.length === 0) {
console.log.apply(null, log("Not posting an empty string...")); console.log.apply(null, log("Not posting an empty string..."));
return; return;
} }
@@ -1177,7 +1110,7 @@ class App {
let id = localStorage.getItem("peer_id"); let id = localStorage.getItem("peer_id");
if (!id) { if (!id) {
console.log.apply(null, log(`Didn't find a peer ID, generating one`));; console.log.apply(null, log(`Didn't find a peer ID, generating one`));;
id = generateID(); id = generateID();
localStorage.setItem("peer_id", id); localStorage.setItem("peer_id", id);
} }
@@ -1189,7 +1122,7 @@ class App {
let id = localStorage.getItem("dandelion_id"); let id = localStorage.getItem("dandelion_id");
if (!id) { if (!id) {
console.log.apply(null, log(`Didn't find a user ID, generating one`));; console.log.apply(null, log(`Didn't find a user ID, generating one`));;
id = generateID(); id = generateID();
localStorage.setItem("dandelion_id", id); localStorage.setItem("dandelion_id", id);
} }
@@ -1264,17 +1197,17 @@ class App {
initOffline(connection: wsConnection) { initOffline(connection: wsConnection) {
// Event listener for going offline // Event listener for going offline
window.addEventListener('offline', () => { window.addEventListener('offline', () => {
console.log.apply(null, log("offline")); console.log.apply(null, log("offline"));
}); });
// Event listener for going online // Event listener for going online
window.addEventListener('online', async () => { window.addEventListener('online', async () => {
console.log.apply(null, log("online")); console.log.apply(null, log("online"));
// connection.connect(); // connection.connect();
this.render(); this.render();
}); });
console.log.apply(null, log(`Online status: ${navigator.onLine ? "online" : "offline"}`)); console.log.apply(null, log(`Online status: ${navigator.onLine ? "online" : "offline"}`));
} }
@@ -1545,7 +1478,7 @@ class App {
posts = await getData(userID, new Date(2022, 8), new Date()); posts = await getData(userID, new Date(2022, 8), new Date());
if (posts.length > 0) { if (posts.length > 0) {
console.log.apply(null, log(`Loaded ${posts.length} posts in ${this.timerDelta().toFixed(2)}ms`));; console.log.apply(null, log(`Loaded ${posts.length} posts in ${this.timerDelta().toFixed(2)}ms`));;
return posts; return posts;
} }
@@ -1596,13 +1529,13 @@ class App {
return false; return false;
} }
async registerRPCs() { async registerRPCs() {
if (!this.peerManager) { if (!this.peerManager) {
throw new Error(); throw new Error();
} }
this.peerManager.registerRPC('ping', (args: any) => { this.peerManager.registerRPC('ping', (args: any) => {
return {id:this.peerID, user:this.userID, user_name:this.username, peer_name:this.peername}; return { id: this.peerID, user: this.userID, user_name: this.username, peer_name: this.peername };
}); });
if (!this.isBootstrapPeer) { if (!this.isBootstrapPeer) {
@@ -1627,7 +1560,7 @@ class App {
this.peerManager.registerRPC('getPostIDsForUser', (args: any) => { this.peerManager.registerRPC('getPostIDsForUser', (args: any) => {
return [1, 2, 3, 4, 5] return [1, 2, 3, 4, 5]
}); });
let postIDs = await this.peerManager.rpc.getPostIDsForUser("dummy_peer", "bloop"); let postIDs = await this.peerManager.rpc.getPostIDsForUser("dummy_peer", "bloop");
console.log.apply(null, log("peerManager.rpc.getPostIDsForUser", postIDs)); console.log.apply(null, log("peerManager.rpc.getPostIDsForUser", postIDs));
@@ -1650,7 +1583,7 @@ class App {
this.isHeadless = /\bHeadlessChrome\//.test(navigator.userAgent); this.isHeadless = /\bHeadlessChrome\//.test(navigator.userAgent);
this.isBootstrapPeer = urlParams.has("bootstrap"); this.isBootstrapPeer = urlParams.has("bootstrap");
if (this.isBootstrapPeer) { if (this.isBootstrapPeer) {
console.log.apply(null, log(`This is a bootstrap peer`));; console.log.apply(null, log(`This is a bootstrap peer`));;
} }
this.peerID = this.getPeerID(); this.peerID = this.getPeerID();
@@ -1659,7 +1592,7 @@ class App {
this.username = this.getUsername(); this.username = this.getUsername();
this.connect(); this.connect();
// this.registerRPCs(); // this.registerRPCs();
// this.testPeerManager(); // this.testPeerManager();
@@ -1748,7 +1681,7 @@ class App {
localStorage.removeItem("dandelion_username"); localStorage.removeItem("dandelion_username");
} }
await this.initDB(); await this.initDB();
this.connectURL = `${document.location.origin}/connect/${this.userID}`; this.connectURL = `${document.location.origin}/connect/${this.userID}`;
@@ -1788,7 +1721,7 @@ class App {
await this.render(); // , (postID:string)=>{this.deletePost(userID, postID)} await this.render(); // , (postID:string)=>{this.deletePost(userID, postID)}
if ((performance as any)?.memory) { if ((performance as any)?.memory) {
console.log.apply(null, log(`memory used: ${((performance as any).memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}Mb`)); console.log.apply(null, log(`memory used: ${((performance as any).memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}Mb`));
} }
// if (navigator?.storage) { // if (navigator?.storage) {
@@ -1809,7 +1742,7 @@ class App {
console.log.apply(null, log(`username:${this.username} user:${this.userID} peername:${this.peername} peer:${this.peerID}`));; console.log.apply(null, log(`username:${this.username} user:${this.userID} peername:${this.peername} peer:${this.peerID}`));;
// await this.purgeEmptyUsers(); // await this.purgeEmptyUsers();
@@ -1966,7 +1899,7 @@ class App {
let renderTime = this.timerDelta(); let renderTime = this.timerDelta();
console.log.apply(null, log(`render took: ${renderTime.toFixed(2)}ms`));; console.log.apply(null, log(`render took: ${renderTime.toFixed(2)}ms`));;
performance.mark("render-end"); performance.mark("render-end");
performance.measure('render-time', 'render-start', 'render-end'); performance.measure('render-time', 'render-start', 'render-end');

View File

@@ -28,7 +28,7 @@ Restruucture the app around the data. App/WS split is messy. Clean it up.
// import * as ForceGraph3D from "3d-force-graph"; // import * as ForceGraph3D from "3d-force-graph";
import { openDatabase, getData, addData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db"; import { openDatabase, getData, addData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db";
import { generateID } from "IDUtils"; import { generateID } from "IDUtils";
import { PeerManager } from "PeerManager"; import { PeerManager, PeerEventTypes } from "PeerManager";
import { log, logID, renderLog, setLogVisibility } from "log"; import { log, logID, renderLog, setLogVisibility } from "log";
// let posts:any; // let posts:any;
// let keyBase = "dandelion_posts_v1_" // let keyBase = "dandelion_posts_v1_"
@@ -154,75 +154,13 @@ async function compressString(input) {
// Convert the compressed data to a Uint8Array // Convert the compressed data to a Uint8Array
return new Uint8Array(compressedArray); return new Uint8Array(compressedArray);
} }
// class Signaler {
// websocket: WebSocket | null = null;
// websocketPingInterval: number = 0;
// connect() {
// if (this.websocket?.readyState === WebSocket.OPEN) {
// return;
// }
// window.clearInterval(this.websocketPingInterval);
// if (this.websocket) { this.websocket.close() };
// try {
// this.websocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/ws`);
// } catch (error: any) {
// console.log.apply(null, log(error.message);
// return;
// }
// this.websocket.onopen = async (event) => {
// console.log.apply(null, log("ws:connected"));;
// await this.sendHello();
// // If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
// let helloRefreshIntervalPeriod = 120;
// if (app.isHeadless) {
// console.log.apply(null, log("wsConnection: Setting hello refresh interval to ", helloRefreshIntervalPeriod)
// this.helloRefreshInterval = window.setInterval(() => {
// console.log.apply(null, log("wsConnection: Hello refresh.")
// if (!navigator.onLine) {
// return;
// }
// this.sendHello();
// }, helloRefreshIntervalPeriod * 1000);
// }
// this.websocketPingInterval = window.setInterval(() => {
// if (!navigator.onLine) {
// return;
// }
// this.send({ type: "ping", peer_id: this.peerID, peer_name: app.peername, user_id: app.userID, user_name: app.username });
// }, 10_000)
// };
// this.websocket.onclose = (event) => {
// console.log.apply(null, log("ws:disconnected"));;
// // this.retry *= 2;
// console.log.apply(null, log(`Retrying in ${this.retry} seconds`));;
// window.setTimeout(() => { this.connect(); }, this.retry * 1000);
// };
// this.websocket.onmessage = (event) => {
// // log('ws:<-' + event.data.slice(0, 240));
// let data = JSON.parse(event.data);
// let { type } = data;
// let handler = this.messageHandlers.get(type);
// if (!handler) {
// console.warn(`Got a message we can't handle:`, type);
// return;
// }
// handler(data);
// };
// this.websocket.onerror = (event) => {
// console.log.apply(null, log('ws:error: ' + event));;
// };
// }
// }
// disconnect() {
// this.websocket?.close();
// }
// }
// Connect websocket // Connect websocket
// send hello // send hello
// get bootstrap peer ID // get bootstrap peer ID
// WebRTC connect to bootstrap peer // WebRTC connect to bootstrap peer
// ask Bootstrap peer for peers that have users we care about. // Bootstrap peer will send the last N peers it saw.
// for now, bootstrap peer will connect to all peers and will tell us about them, moving all logic from the server to the BSP // Connect to those new peers, tell those peers about users we know about
// ask for peers that have users we care about
// WebRTC Connect to peers that might have posts we need // WebRTC Connect to peers that might have posts we need
// query those peers and do existing logic. // query those peers and do existing logic.
class wsConnection { class wsConnection {
@@ -231,7 +169,6 @@ class wsConnection {
this.sessionID = ""; this.sessionID = "";
this.userID = ""; this.userID = "";
this.peerID = ""; this.peerID = "";
this.rtcPeerDescription = null;
this.websocketPingInterval = 0; this.websocketPingInterval = 0;
this.helloRefreshInterval = 0; this.helloRefreshInterval = 0;
this.retry = 10; this.retry = 10;
@@ -262,7 +199,7 @@ class wsConnection {
'5f1b85c4-b14c-454c-8df1-2cacc93f8a77', '5f1b85c4-b14c-454c-8df1-2cacc93f8a77',
// 'bba3ad24-9181-4e22-90c8-c265c80873ea' // 'bba3ad24-9181-4e22-90c8-c265c80873ea'
]); ]);
this.rtcPeerDescription = rtcPeerDescription; // this.rtcPeerDescription = rtcPeerDescription;
this.sessionID = generateID(); this.sessionID = generateID();
this.userID = userID; this.userID = userID;
this.peerID = peerID; this.peerID = peerID;
@@ -446,9 +383,16 @@ class wsConnection {
session_id: this.sessionID, session_id: this.sessionID,
peer_name: app.peername, peer_name: app.peername,
is_bootstrap_peer: app.isBootstrapPeer, is_bootstrap_peer: app.isBootstrapPeer,
peer_description: this.rtcPeerDescription // peer_description: this.rtcPeerDescription
}); });
} }
async getKnownUsers() {
let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
knownUsers = knownUsers
.filter(userID => this.shouldSyncUserID(userID))
.filter(userID => !this.userBlockList.has(userID))
.filter(async (userID) => (await getAllIds(userID)).length > 0); // TODO:EASYOPT getting all the IDs is unecessary, replace it with a test to get a single ID.
}
async sendHello() { async sendHello() {
// TODO only get users you're following here. ✅ // TODO only get users you're following here. ✅
let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined); let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
@@ -629,8 +573,11 @@ class App {
async connect() { async connect() {
this.peerManager = new PeerManager(this.userID, this.peerID, this.isBootstrapPeer); this.peerManager = new PeerManager(this.userID, this.peerID, this.isBootstrapPeer);
this.registerRPCs(); this.registerRPCs();
this.peerManager.addEventListener(PeerEventTypes.PEER_CONNECTED, (event) => {
console.log.apply(null, log(`[app]: peer connected:${event.peerID}`));
// rpc saying what peers we have
});
console.log.apply(null, log("*************** before peerManager.connect")); console.log.apply(null, log("*************** before peerManager.connect"));
;
// We use promises here to only return from this call once we're connected to the boostrap peer // We use promises here to only return from this call once we're connected to the boostrap peer
// and the datachannel is open. // and the datachannel is open.
// Might want to take this a step further and only return once we're connected to an initial set of peers? // Might want to take this a step further and only return once we're connected to an initial set of peers?