diff --git a/main b/main index 8e30364..2f1141a 100755 Binary files a/main and b/main differ diff --git a/main.go b/main.go index fc4365f..02fe8b4 100644 --- a/main.go +++ b/main.go @@ -77,7 +77,7 @@ func removePeer(peerID string, peer *Peer) { for userID, peers := range userPeers { delete(peers, peerID) if len(peers) == 0 { - delete(userPeers, userID) + delete(userPeers, userID) // not safe need mutex } } @@ -114,13 +114,13 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) { for { _, message, err := conn.ReadMessage() if err != nil { - log.Println("ReadMessage error:", err) + log.Println("ReadMessage error:", err, connectionPeers[conn]) break } peer.lastActive = time.Now() - fmt.Println("ws<-", connectionPeers[conn], ":", string(message[:min(80, len(message))])) + // fmt.Println("ws<-", connectionPeers[conn], ":", string(message[:min(80, len(message))])) response, err := dispatchMessage(message, peer) @@ -158,7 +158,7 @@ func writePump(peer *Peer) { return } peer.conn.SetWriteDeadline(time.Now().Add(writeWait)) - fmt.Println("ws->", connectionPeers[peer.conn], ":", string(message[:min(80, len(message))])) + // fmt.Println("ws->", connectionPeers[peer.conn], ":", string(message[:min(80, len(message))])) err := peer.conn.WriteMessage(websocket.TextMessage, message) if err != nil { @@ -195,7 +195,9 @@ func handleHello(message []byte, peer *Peer) ([]byte, error) { var m struct { Type string `json:"type"` UserID string `json:"user_id"` + UserName string `json:"user_name"` PeerID string `json:"peer_id"` + PeerName string `json:"peer_name"` KnownUsers []string `json:"known_users"` } @@ -203,7 +205,7 @@ func handleHello(message []byte, peer *Peer) ([]byte, error) { return nil, err } - // log.Printf("Received hello from peer: %s, user:%s", m.PeerID, m.UserID) + log.Printf("Received hello from peer %s:%s, user %s:%s", m.PeerID[0:5], m.PeerName, m.UserID[0:5], m.UserName) if userPeers[m.UserID] == nil { userPeers[m.UserID] = make(PeerSet) } @@ -238,10 +240,12 @@ func handlePeerMessage(message []byte, peer *Peer) ([]byte, error) { } type PeerMessage struct { - Type string `json:"type"` - From string `json:"from"` - To string `json:"to"` - Message InnerMessage `json:"message"` + Type string `json:"type"` + From string `json:"from"` + FromUserName string `json:"from_username"` + FromPeerName string `json:"from_peername"` + To string `json:"to"` + Message InnerMessage `json:"message"` } var m PeerMessage @@ -249,7 +253,7 @@ func handlePeerMessage(message []byte, peer *Peer) ([]byte, error) { return nil, err } - fmt.Printf("peer message type %s from %s to %s with message length %d\n", m.Message.Type, m.From, m.To, len(message)) + fmt.Printf("peer message type %s from %s:%s:%s to %s with message length %d\n", m.Message.Type, m.From[0:5], m.FromPeerName, m.FromUserName, m.To[0:5], len(message)) toPeer := peerConnections[m.To] diff --git a/src/main.ts b/src/main.ts index c7f0aa6..dff45d9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,4 @@ // TODO: virtual list, only rerender what's needed so things can keep playing. - - - import { getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "./db.js" declare let WebTorrent: any; @@ -80,7 +77,9 @@ function uuidToBase58(uuid: string): string { } - +function logID(ID: string) { + return ID.substring(0, 5); +} let logLines: string[] = []; let logLength = 10; @@ -205,6 +204,26 @@ async function base64ToArrayBuffer(base64String: string) { // return buffer; } +async function compressString(input: string) { + // Convert the string to a Uint8Array + const textEncoder = new TextEncoder(); + const inputArray = textEncoder.encode(input); + + // Create a CompressionStream + const compressionStream = new CompressionStream('gzip'); + const writer = compressionStream.writable.getWriter(); + + // Write the data and close the stream + writer.write(inputArray); + writer.close(); + + // Read the compressed data from the stream + const compressedArray = await new Response(compressionStream.readable).arrayBuffer(); + + // Convert the compressed data to a Uint8Array + return new Uint8Array(compressedArray); +} + class wsConnection { websocket: WebSocket | null = null; userID = ""; @@ -218,10 +237,11 @@ class wsConnection { peerMessageHandlers: Map void> = new Map(); - send(message: any) { + async send(message: any) { let json = "" try { json = JSON.stringify(message); + // console.log("*******", (await compressString(json)).byteLength, json.length); } catch (e) { console.log(e, "wsConnection send: Couldn't serialize message", message); } @@ -242,7 +262,7 @@ class wsConnection { } users = [...users, ...Object.entries(data.userPeers)]; - log(`Network: got ${users.length} users from bootstrap peer. ${users.join(',')}`) + log(`Net: got ${users.length} users from bootstrap peer. ${users.join(',')}`) for (let [userID, peerIDs] of users) { this.peers.set(userID, [...Object.keys(peerIDs as any)]); @@ -252,10 +272,12 @@ class wsConnection { continue; } - log(`Network: Requesting post IDs for user ${userID} from peer ${peerID}`); + log(`Net: Req post IDs for user ${logID(userID)} from peer ${logID(peerID)}`); this.send({ type: "peer_message", from: this.peerID, + from_username: app.username, + from_peername: app.peername, to: peerID, message: { type: "get_post_ids_for_user", user_id: userID } }) @@ -270,23 +292,24 @@ class wsConnection { // log(`getPostsForUserResponse: ${data}`) let message = data.message; - log(`Network: got ${message.post_ids.length} post IDs for user ${data.message.user_id} from peer ${data.from}`); + log(`Net: got ${message.post_ids.length} post IDs for user ${logID(data.message.user_id)} from peer ${logID(data.from)}`); - console.log(`Checking post IDs...`); + // console.log(`Checking post IDs...`); let postIds = await checkPostIds(message.user_id, data.message.post_ids); if (postIds.length === 0) { - log(`Don't need any posts from peer ${data.from}`); + log(`Don't need any posts for user ${logID(data.message.user_id)} from peer ${logID(data.from)}`); return; } - log(`Network: requesting ${postIds.length} posts for user ${message.user_id} from peer ${data.from}`) - let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, message: { type: "get_posts_for_user", post_ids: postIds, user_id: message.user_id } } + 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 } } this.send(responseMessage); } + // static async compressArrayBuffer(data: ArrayBuffer): Promise { // const compressionStream = new CompressionStream('gzip'); // You can also use 'deflate', 'deflate-raw', etc. @@ -303,12 +326,12 @@ class wsConnection { let message = data.message; let postIds = await getAllIds(message.user_id) ?? []; if (postIds.length === 0) { - log(`Network: I know about user ${message.user_id} but I have 0 posts, so I'm not sending any to to peer ${data.from}`); + 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; } - log(`Network: Sending ${postIds.length} post Ids for user ${message.user_id} to peer ${data.from}`) + 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, 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); } @@ -317,7 +340,7 @@ class wsConnection { let message = data.message; let posts = await getPostsByIds(message.user_id, message.post_ids) ?? []; - log(`Network: Sending ${posts.length} posts for user ${message.user_id} to peer ${data.from}`); + log(`Net: Sending ${posts.length} posts for user ${logID(message.user_id)} to peer ${logID(data.from)}`); app.timerStart(); let output = []; @@ -338,7 +361,7 @@ class wsConnection { // posts = posts.map((post:any)=>{let newPost = post.data; if (newPost.image_data){newPost.image_data = arraybufferto};return newPost}); // posts = posts.map((post:any)=>{}) - let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, message: { type: "get_posts_for_user_response", posts: output, 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_response", posts: output, user_id: message.user_id } } this.send(responseMessage) let sendTime = app.timerDelta(); @@ -350,8 +373,7 @@ class wsConnection { async getPostsForUserReponseHandler(data: any) { app.timerStart(); let message = data.message; - console.log(`Network: got ${message.posts.length} posts for user ${message.user_id} from peer ${data.from}`); - console.log(`getPostsForUserResponseHandler Got ${message.posts.length} from peer ${data.from}`); + console.log(`Net: got ${message.posts.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`); for (let post of message.posts) { if (message.user_id === app.userID) { post.author_id = app.userID; @@ -406,9 +428,10 @@ class wsConnection { this.websocket.onopen = async (event) => { log("ws:connected"); + let knownUsers = [...(await indexedDB.databases())].map((db) => db.name?.replace('user_', '')); - console.log('Network: Sending known users', knownUsers); - this.send({ type: "hello", user_id: this.userID, peer_id: this.peerID, known_users: knownUsers }); + console.log('Net: Sending known users', knownUsers); + this.send({ type: "hello", user_id: this.userID, user_name: app.username, peer_id: this.peerID, peer_name: app.peername, known_users: knownUsers }); this.websocketPingInterval = window.setInterval(() => { if (!navigator.onLine) { return; @@ -477,8 +500,10 @@ class wsConnection { class App { username: string = ''; + peername: string = ''; userID: string = ''; peerID: string = ''; + following: string[] = []; posts: Post[] = []; isHeadless: boolean = false; showLog: boolean = false; @@ -640,7 +665,7 @@ class App { let post = new Post(this.username, userID, postText, new Date(), mediaData); - this.posts.push(post); + // this.posts.push(post); // localStorage.setItem(key, JSON.stringify(posts)); addData(userID, post) @@ -674,10 +699,11 @@ class App { animals = ['shrew', 'jerboa', 'lemur', 'weasel', 'possum', 'possum', 'marmoset', 'planigale', 'mole', 'narwhal']; adjectives = ['snazzy', 'whimsical', 'jazzy', 'bonkers', 'wobbly', 'spiffy', 'chirpy', 'zesty', 'bubbly', 'perky', 'sassy']; + snakes = ['mamba', 'cobra', 'python', 'viper', 'krait', 'sidewinder', 'constrictor', 'boa', 'asp', 'anaconda', 'krait'] - hashUserIdToIndices() { + hashIdToIndices(id: string) { let indices = []; - for (let char of this.userID) { + for (let char of id) { if (char !== '0' && char !== '-') { indices.push(parseInt(char, 16)); if (indices.length == 2) { @@ -688,20 +714,33 @@ class App { return [indices[0], indices[1]]; } + funkyName(id: string, listOne: string[], listTwo: string[]) { + let [one, two] = this.hashIdToIndices(id); + let first = listOne[one % this.adjectives.length]; + let second = listTwo[two % this.animals.length]; + return { first, second } + } + getUsername() { let username = localStorage.getItem("dandelion_username"); - if (!username || username === "not_set") { - let [one, two] = this.hashUserIdToIndices(); - let adjective = this.adjectives[one % this.adjectives.length] - let animal = this.animals[two % this.animals.length] - username = `${adjective}_${animal}` - localStorage.setItem("dandelion_username", username); + if (username && username !== "not_set") { + return username; } + let { first: adjective, second: animal } = this.funkyName(this.userID, this.adjectives, this.animals); + username = `${adjective}_${animal}` + localStorage.setItem("dandelion_username", username); + return username; } + getPeername() { + let { first: adjective, second: snake } = this.funkyName(this.peerID, this.adjectives, this.snakes); + let peername = `${adjective}_${snake}` + return peername; + } + setFont(fontName: string, fontSize: string) { let content = document.getElementById('content'); @@ -972,7 +1011,14 @@ class App { // } this.username = this.getUsername(); - document.getElementById('username')!.innerText = this.username; + document.getElementById('username')!.innerText = `${this.username}`; + this.peername = this.getPeername(); + document.getElementById('peername')!.innerText = `peername:${this.peername}`; + + document.getElementById('user_id')!.innerText = `user_id:${this.userID}`; + document.getElementById('peer_id')!.innerText = `peer_id:${this.peerID}`; + + this.initButtons(userID, this.posts, registration); @@ -1024,7 +1070,14 @@ class App { // posts that are not in our list that we need at add // posts that are in our list that we need to remove - // postsSet = new Set(); + + renderedPosts = new Map(); + computeDiff(newPosts: []) { + + + + // return {added, deleted, same} + } async render() { if (this.isHeadless) { @@ -1033,37 +1086,51 @@ class App { } this.timerStart(); - let posts = []; + + let existingPosts = this.posts; + + this.posts = []; switch (this.router.route) { case App.Route.HOME: case App.Route.CONNECT: { - posts = await this.loadPosts(this.userID) ?? []; + this.posts= await this.loadPosts(this.userID) ?? []; break; } case App.Route.USER: { - posts = await this.loadPosts(this.router.userID) ?? []; + this.posts= await this.loadPosts(this.router.userID) ?? []; break; } case App.Route.POST: { - posts = await this.loadPosts(this.router.userID, this.router.postID) ?? []; + this.posts= await this.loadPosts(this.router.userID, this.router.postID) ?? []; break; } default: { console.log("Render: got a route I didn't understand. Rendering HOME:", this.router.route); - posts = await this.loadPosts(this.userID) ?? []; + this.posts= await this.loadPosts(this.userID) ?? []; break; } - } - // let newPostsSet = new Set(posts.map(post=>post.post_id)); - // let newPosts = (newPostsSet as any).difference(this.postsSet); - // // let removedPosts = (this.postsSet as any).difference(newPosts); - // let keepPosts = (this.postsSet as any).intersection(newPostsSet); + - // let renderPosts = keepPosts.union(newPosts); + let existingPostSet = new Set(existingPosts.map(post=>post.post_id)); + let incomingPostSet = new Set(this.posts.map(post=>post.post_id)); + let addedPosts = []; + for (let post of this.posts) { + if (!existingPostSet.has(post.post_id)){ + addedPosts.push(post); + } + } + let deletedPosts = []; + for (let post of existingPosts) { + if (!incomingPostSet.has(post.post_id)) { + deletedPosts.push(post); + } + } + + console.log("added:", addedPosts, "removed:", deletedPosts); const fragment = document.createDocumentFragment(); let contentDiv = document.getElementById("content"); if (!contentDiv) { @@ -1072,10 +1139,12 @@ class App { contentDiv.innerHTML = ""; // let count = 0; - for (let i = posts.length - 1; i >= 0; i--) { - let postData = posts[i]; + for (let i = this.posts.length - 1; i >= 0; i--) { + let postData = this.posts[i]; // this.postsSet.add(postData); + + // return promises for all image loads and await those. let post = this.renderPost(postData); if (post) { diff --git a/static/index.html b/static/index.html index 92ababd..c7b9093 100644 --- a/static/index.html +++ b/static/index.html @@ -30,6 +30,8 @@