-
-
+
diff --git a/main b/main
index 142e289..e865771 100755
Binary files a/main and b/main differ
diff --git a/main.go b/main.go
index 918404b..7f1bc4c 100644
--- a/main.go
+++ b/main.go
@@ -1,58 +1,20 @@
-// TODO
-// 1. Convery network messages to flatbuffers so we don't need to parse JSON in Go.
-// Use binary WS messages. Generate for Go and JS and use that everywhere. We can
-// also use this for WebRTC messages.
-// 2. Keep a list of all nodes that bootstrapped in the last N minutes.
-// 3. When a node bootstraps, send it a random list of the nodes we know about.
-// 4. Do the signalling to connect those nodes.
-// 5. Each node will know about N other nodes. To find get the data for a feed,
-// a node will need to find another node that has that data. To do that we'll need to
-// implement a search message that is sent to all currently connected nodes, and they
-// forward to all their nodes, passing back the address of the node that has the data.
-// Once we find it, we'll do the signalling to connect to it via Web RTC via our existing connected nodes.
-// -----
-// Feeds. People can curate feeds which can be any combination of hashtags, serch terms and users.
-// Invite-only communities. Just block everyone else even if they post to it.
-// Limit to friends and friends of friends
-
-// MVP
-// You connect to the person you want to get the post from to get the post
-// they give you the post
-// If they're offline, you can't get their updates.
-// This is very stupid, but it's simplest thing.
-// The bootstrap server connects you to them directly via WebRTC
-// This will make the thing actually function as a little toy for people to play with.
-// This will let us test whether background tabs respond to webrtc requests.
-
-// THEN
-// Need to have identity sorted out
-// When you read someone's posts, you also cache them locally
-// cache priority goes mutuals->people you follow->people who you folllow, follow, so you're always
-// caching your mutual's posts
-// Posts are samll, so caching per-post will work fine.
-// Then the process is for the bootstrap server to remember all nodes and what they're caching
-// This will allow distributed content delivery but put a memory and bendwidth strain on the
-// bootstrap sever. Look into Web Transport for the raspberry pi overhead. Could buy a few more RPIs
-// and make a little cluster
-
-// ✅ Domain name so we can get a certificate and serve HTTPS / HTTP3
-
-// Think about compiling Typescript on initial access and caching the JS in a service worker
-// so you don't need a build system to change things.
-// Think about self-hosting the client so the system can be completely self-hosted
-
package main
import (
+ "context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
+ "os/signal"
"path/filepath"
"strconv"
- "strings"
+ "syscall"
+ "time"
+
+ // "strings"
"github.com/andybalholm/brotli"
"github.com/gorilla/websocket"
@@ -61,7 +23,7 @@ import (
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
- return origin == "https://ddlion.net"
+ return origin == "https://ddlion.net" || origin == "https://ddln.app"
},
}
@@ -74,7 +36,7 @@ type Message struct {
Type string `json:"type"`
}
-type MessageHandler func([]byte, *websocket.Conn) error
+type MessageHandler func([]byte, *Peer) ([]byte, error)
var messageHandlers = make(map[string]MessageHandler)
@@ -82,19 +44,45 @@ func registerHandler(messageType string, handler MessageHandler) {
messageHandlers[messageType] = handler
}
-func dispatchMessage(message []byte, conn *websocket.Conn) error {
+func dispatchMessage(message []byte, peer *Peer) ([]byte, error) {
var msg Message
if err := json.Unmarshal(message, &msg); err != nil {
- return err
+ return nil, err
}
handler, ok := messageHandlers[msg.Type]
if !ok {
- log.Printf("No handler registered for message type: %s", msg.Type)
- return nil
+ err := fmt.Errorf("no handler registered for message type: %s", msg.Type)
+ return []byte(fmt.Sprintf(`{"type":"error", "message": "%s"}`, err.Error())), nil
}
- return handler(message, conn)
+ return handler(message, peer)
+}
+
+const (
+ writeWait = 10 * time.Second
+)
+
+type Peer struct {
+ conn *websocket.Conn
+ send chan []byte
+ lastActive time.Time
+}
+
+func removePeer(peerID string, peer *Peer) {
+ delete(peerConnections, peerID)
+
+ for userID, peers := range userPeers {
+ delete(peers, peerID)
+ if len(peers) == 0 {
+ delete(userPeers, userID)
+ }
+ }
+
+ delete(connectionPeers, peer.conn)
+
+ // Close the peer's send channel
+ close(peer.send)
}
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
@@ -109,39 +97,93 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
conn.SetCloseHandler(websocketCloseHandler)
+ // Create a Peer object with a buffered channel for sending messages
+ peer := &Peer{
+ conn: conn,
+ send: make(chan []byte, 256),
+ lastActive: time.Now(),
+ }
+
+ // Start the write loop in a separate goroutine
+ go writePump(peer)
+
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("ReadMessage error:", err)
break
}
- log.Printf("recv: %s", message)
- if err := dispatchMessage(message, conn); err != nil {
+ peer.lastActive = time.Now()
+
+ fmt.Println("ws<-", connectionPeers[conn], ":", string(message[:min(80, len(message))]))
+
+ response, err := dispatchMessage(message, peer)
+
+ if err != nil {
log.Printf("Error dispatching message: %v", err)
}
+
+ if response != nil {
+ // Send the response to the write loop
+ peer.send <- response
+ }
+ }
+
+ // Clean up when the connection is closed
+ close(peer.send)
+ peerID := connectionPeers[peer.conn]
+ if peerID != "" {
+ delete(peerConnections, peerID)
+ }
+}
+
+func writePump(peer *Peer) {
+ defer func() {
+ peer.conn.Close()
+ }()
+ for {
+ select {
+ case message, ok := <-peer.send:
+ if !ok {
+ // Channel closed, close the connection
+ peer.conn.WriteMessage(websocket.CloseMessage, []byte{})
+ return
+ }
+ peer.conn.SetWriteDeadline(time.Now().Add(writeWait))
+ fmt.Println("ws->", connectionPeers[peer.conn], ":", string(message[:min(80, len(message))]))
+
+ err := peer.conn.WriteMessage(websocket.TextMessage, message)
+ if err != nil {
+ log.Println("WriteMessage error:", err)
+ return
+ }
+ }
}
}
-// Example handlers
-func handlePing(message []byte, conn *websocket.Conn) error {
+func handlePing(message []byte, peer *Peer) ([]byte, error) {
var pingMsg struct {
Type string `json:"type"`
PeerID string `json:"peer_id"`
}
+
if err := json.Unmarshal(message, &pingMsg); err != nil {
- return err
+ return nil, err
}
- log.Printf("Received ping from peer: %s", pingMsg.PeerID)
- return nil
+
+ // log.Printf("Received ping from peer: %s", pingMsg.PeerID)
+
+ return []byte(`{"type":"pong"}`), nil
}
type PeerSet map[string]struct{}
var userPeers = make(map[string]PeerSet)
-var peerConnections = make(map[string]*websocket.Conn)
+var peerConnections = make(map[string]*Peer)
+var connectionPeers = make(map[*websocket.Conn]string)
-func handleHello(message []byte, conn *websocket.Conn) error {
+func handleHello(message []byte, peer *Peer) ([]byte, error) {
var m struct {
Type string `json:"type"`
@@ -150,32 +192,65 @@ func handleHello(message []byte, conn *websocket.Conn) error {
}
if err := json.Unmarshal(message, &m); err != nil {
- return err
+ return nil, err
}
+ // log.Printf("Received hello from peer: %s, user:%s", m.PeerID, m.UserID)
if userPeers[m.UserID] == nil {
userPeers[m.UserID] = make(PeerSet)
}
userPeers[m.UserID][m.PeerID] = struct{}{}
- peerConnections[m.PeerID] = conn
+ peerConnections[m.PeerID] = peer
+ connectionPeers[peer.conn] = m.PeerID
jsonData, _ := json.MarshalIndent(userPeers, "", " ")
fmt.Println(string(jsonData), peerConnections)
- log.Printf("Received connect from peer: %s, user:%s", m.PeerID, m.UserID)
- return nil
+ // return all the peers we know about, with their user_id and peer_id
+
+ return []byte(fmt.Sprintf(`{"type":"hello", "userPeers": %s}`, string(jsonData))), nil
}
-// LoggingHandler logs requests and delegates them to the underlying handler.
-// type LoggingHandler struct {
-// handler http.Handler
-// }
+func handlePeerMessage(message []byte, peer *Peer) ([]byte, error) {
-// func (lh *LoggingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-// log.Printf("Serving file: %s", r.URL.Path)
-// lh.handler.ServeHTTP(w, r)
-// }
+ type InnerMessage struct {
+ Type string `json:"type"`
+ UserID string `json:"user_id"`
+ }
+
+ type PeerMessage struct {
+ Type string `json:"type"`
+ From string `json:"from"`
+ To string `json:"to"`
+ Message InnerMessage `json:"message"`
+ }
+ var m PeerMessage
+
+ if err := json.Unmarshal(message, &m); err != nil {
+ 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))
+
+ toPeer := peerConnections[m.To]
+
+ if toPeer == nil {
+ fmt.Printf("Couldn't find peer %s\n", m.To)
+ fmt.Println(peerConnections)
+ return nil, nil
+ }
+
+ // Send the message to the recipient's send channel
+ select {
+ case toPeer.send <- message:
+ default:
+ fmt.Println("Could not send message to peer; channel full or closed")
+ }
+
+ // No response for this type of message
+ return nil, nil
+}
// BrotliResponseWriter wraps http.ResponseWriter to support Brotli compression
type brotliResponseWriter struct {
@@ -207,8 +282,11 @@ func noDirListing(h http.Handler, root string) http.HandlerFunc {
log.Printf("Serving: %s to ip %s, useragent %s", r.URL.Path, r.RemoteAddr, r.UserAgent())
+ // w.Header().Set("Cache-Control", "no-cache")
+
// Check if client supports Brotli encoding
- if strings.Contains(r.Header.Get("Accept-Encoding"), "br") {
+ // if strings.Contains(r.Header.Get("Accept-Encoding"), "br") {
+ if false {
w.Header().Set("Content-Encoding", "br")
w.Header().Del("Content-Length") // Cannot know content length with compressed data
@@ -231,31 +309,115 @@ func noDirListing(h http.Handler, root string) http.HandlerFunc {
}
}
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
func main() {
+ // Create a channel to receive OS signals for graceful shutdown
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+ // Create a channel to signal when the program should shut down
+ done := make(chan bool)
+
+ // Define the directory to serve and the port to listen on
dir := "./"
port := 6789
addr := ":" + strconv.Itoa(port)
log.Printf("Starting server on %s", addr)
- // Register handlers
+ // Register message handlers
registerHandler("hello", handleHello)
registerHandler("ping", handlePing)
+ registerHandler("peer_message", handlePeerMessage)
// Set up file server and WebSocket endpoint
fs := http.FileServer(http.Dir(dir))
- // loggingHandler := &LoggingHandler{handler: fs}
- // http.Handle("/", loggingHandler)
http.Handle("/", noDirListing(fs, dir))
http.HandleFunc("/ws", handleWebSocket)
- // Configure and start the HTTP server
+ // Configure the HTTP server
server := &http.Server{
Addr: addr,
- Handler: nil, // nil uses the default ServeMux, which we configured above
+ Handler: nil, // Use the default ServeMux
}
- log.Printf("Server is configured and serving on port %d...", port)
- log.Fatal(server.ListenAndServeTLS("/etc/letsencrypt/live/ddlion.net/fullchain.pem", "/etc/letsencrypt/live/ddlion.net/privkey.pem"))
+ // Start the inactivity monitor goroutine
+ go func() {
+ ticker := time.NewTicker(10 * time.Second)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-done:
+ return
+ case <-ticker.C:
+ now := time.Now()
+
+ // Collect inactive peers
+ var inactivePeers []string
+ for peerID, peer := range peerConnections {
+ if now.Sub(peer.lastActive) > 60*time.Second {
+ inactivePeers = append(inactivePeers, peerID)
+ }
+ }
+
+ // Remove inactive peers
+ for _, peerID := range inactivePeers {
+ peer := peerConnections[peerID]
+
+ if peer != nil {
+ log.Printf("Peer %s inactive for more than 60 seconds. Closing connection.", peerID)
+ peer.conn.Close()
+ removePeer(peerID, peer)
+ }
+ }
+ }
+ }
+ }()
+
+ // Run a goroutine to handle graceful shutdown
+ go func() {
+ sig := <-sigChan
+ fmt.Println()
+ fmt.Println("Received signal:", sig)
+
+ // Perform cleanup here
+ fmt.Println("Shutting down gracefully...")
+
+ // Create a context with timeout for the shutdown
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ // Attempt to gracefully shut down the server
+ if err := server.Shutdown(ctx); err != nil {
+ log.Fatalf("Server Shutdown Failed:%+v", err)
+ }
+
+ // Signal that shutdown is complete
+ close(done)
+ }()
+
+ // Start the HTTP server in a separate goroutine
+ go func() {
+ log.Printf("Server is configured and serving on port %d...", port)
+ if err := server.ListenAndServeTLS(
+ "/etc/letsencrypt/live/ddlion.net/fullchain.pem",
+ "/etc/letsencrypt/live/ddlion.net/privkey.pem",
+ ); err != nil && err != http.ErrServerClosed {
+ log.Fatalf("Could not listen on %s: %v\n", addr, err)
+ }
+ }()
+
+ fmt.Println("Program is running. Press Ctrl+C to exit.")
+
+ // Wait for the shutdown signal
+ <-done
+ fmt.Println("Program has exited.")
}
diff --git a/main.js b/main.js
index 9c3ce1d..b7c4f6b 100644
--- a/main.js
+++ b/main.js
@@ -1,4 +1,5 @@
-import { getData, addData, addDataArray, clearData, deleteData } from "./db.js";
+// TODO: virtual list, only rerender what's needed so things can keep playing.
+import { getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData } from "./db.js";
// let posts:any;
// let keyBase = "dandelion_posts_v1_"
// let key:string = "";
@@ -19,6 +20,7 @@ function uuidv4() {
let logLines = [];
let logLength = 10;
function log(message) {
+ console.log(message);
logLines.push(`${new Date().toLocaleTimeString()}: ${message}`);
if (logLines.length > 10) {
logLines = logLines.slice(logLines.length - logLength);
@@ -59,8 +61,9 @@ window.addEventListener('scroll', () => {
// You can perform your action here
}
});
+// let peer = await new PeerConnection(peer_id);
// let connectionReply = await wsConnection.send('hello');
-// for (let peeer of connectionReply) {
+// for (let peer of connectionReply) {
// let peerConnection = await wsConnection.send('connect', peer.id);
// if (peerConnection) {
// this.peers.push(peerConnection);
@@ -71,6 +74,67 @@ window.addEventListener('scroll', () => {
// }
// }
class wsConnection {
+ send(message) {
+ let json = "";
+ try {
+ json = JSON.stringify(message);
+ }
+ catch (e) {
+ console.log(e, "wsConnection send: Couldn't serialize message", message);
+ }
+ log(`ws->${json.slice(0, 80)}`);
+ this.websocket.send(json);
+ }
+ helloResponseHandler(data) {
+ for (let [userID, peerIDs] of Object.entries(data.userPeers)) {
+ this.peers.set(userID, [...Object.keys(peerIDs)]);
+ for (let peerID of [...Object.keys(peerIDs)]) {
+ if (peerID === this.peerID) {
+ continue;
+ }
+ this.send({
+ type: "peer_message",
+ from: this.peerID,
+ to: peerID,
+ message: { type: "get_posts_for_user", user_id: userID }
+ });
+ }
+ }
+ }
+ pongHandler(data) {
+ }
+ async getPostsForUserResponseHandler(data) {
+ // log(`getPostsForUserResponse: ${data}`)
+ let message = data.message;
+ console.log(`getPostsForUserResponseHandler Got ${message.posts.length} from peer ${data.from}`);
+ for (let post of message.posts) {
+ post.post_timestamp = new Date(post.post_timestamp);
+ }
+ console.log(`Merging same user peer posts...`);
+ await mergeDataArray(message.user_id, data.message.posts);
+ if (message.user_id === this.userID) {
+ app.posts = await app.loadPosts(this.userID) ?? [];
+ app.render(app.posts);
+ }
+ }
+ async getPostsForUserHandler(data) {
+ let message = data.message;
+ let posts = await getAllData(message.user_id) ?? []; // this doesn't get all posts!
+ posts = posts.map((post) => post.data);
+ // let posts = await getAllData(message.user_id) ?? [];
+ let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, message: { type: "get_posts_for_user_response", posts: posts, user_id: message.user_id } };
+ this.send(responseMessage);
+ }
+ async peerMessageHandler(data) {
+ log(`peerMessageHandler ${data}`);
+ let peerMessageType = data.message.type;
+ let handler = this.peerMessageHandlers.get(peerMessageType);
+ if (!handler) {
+ console.error(`got peer message type we don't have a handler for: ${peerMessageType}`);
+ return;
+ }
+ handler(data);
+ }
connect() {
if (this.websocket?.readyState === WebSocket.OPEN) {
return;
@@ -87,24 +151,32 @@ class wsConnection {
console.log(error.message);
return;
}
- this.websocket.onopen = (evt) => {
+ this.websocket.onopen = (event) => {
log("ws:connected");
- this.websocket.send(`{"type":"hello", "user_id": "${this.userID}", "peer_id":"${this.peerID}"}`);
+ this.send({ type: "hello", user_id: this.userID, peer_id: this.peerID });
this.websocketPingInterval = window.setInterval(() => {
if (!navigator.onLine) {
return;
}
- this.websocket.send(`{"type":"ping", "peer_id": "${this.peerID}"}`);
+ this.send({ type: "ping", peer_id: this.peerID });
}, 10000);
};
- this.websocket.onclose = (evt) => {
+ this.websocket.onclose = (event) => {
log("ws:disconnected");
// this.retry *= 2;
log(`Retrying in ${this.retry} seconds`);
window.setTimeout(() => { this.connect(); }, this.retry * 1000);
};
this.websocket.onmessage = (event) => {
- log('ws:response: ' + event.data);
+ log('ws:<-' + event.data.slice(0, 80));
+ 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) => {
log('ws:error: ' + event);
@@ -120,36 +192,28 @@ class wsConnection {
this.websocketPingInterval = 0;
this.retry = 10;
this.state = 'disconnected';
+ this.peers = new Map();
+ this.messageHandlers = new Map();
+ this.peerMessageHandlers = new Map();
this.userID = userID;
this.peerID = peerID;
+ this.messageHandlers.set('hello', this.helloResponseHandler.bind(this));
+ this.messageHandlers.set('pong', this.pongHandler);
+ this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
+ this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
+ this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserResponseHandler.bind(this));
this.connect();
if (!this.websocket) {
// set a timer and retry?
}
}
}
-// function connectWebsocket(userID: string) {
-// let websocket = new WebSocket(`ws://${window.location.hostname}:${window.location.port}/ws`);
-// websocket.onopen = function (evt) {
-// log("Websocket: CONNECTED");
-// websocket.send(`{"messageType":"connect", "id": "${userID}"}`);
-// let websocketPingInterval = window.setInterval(() => { websocket.send(`{"messageType":"ping", "id": "${userID}"}`); }, 5000)
-// };
-// websocket.onclose = function (evt) {
-// log("Websocket: DISCONNECTED");
-// };
-// websocket.onmessage = function (evt) {
-// log('Websocket: RESPONSE: ' + evt.data);
-// };
-// websocket.onerror = function (evt) {
-// log('Websocket: ERROR: ' + evt);
-// };
-// return websocket;
-// }
class App {
constructor() {
+ this.username = '';
this.userID = '';
this.peerID = '';
+ this.posts = [];
this.time = 0;
}
initMarkdown() {
@@ -207,9 +271,9 @@ class App {
// let tweets = JSON.parse(tweetJSON);
let count = 0;
for (let entry of tweetArchive) {
- if (entry.tweet.hasOwnProperty("in_reply_to_screen_name") || entry.tweet.retweeted || entry.tweet.full_text.startsWith("RT")) {
- continue;
- }
+ // if (entry.tweet.hasOwnProperty("in_reply_to_screen_name") || entry.tweet.retweeted || entry.tweet.full_text.startsWith("RT")) {
+ // continue;
+ // }
let mediaURL = entry.tweet?.entities?.media?.[0]?.media_url_https;
let isImage = false;
if (mediaURL) {
@@ -266,16 +330,16 @@ class App {
console.error("Service Worker registration failed:", error);
});
}
- addPost(userID, posts, postText) {
+ addPost(userID, postText) {
if ((typeof postText !== "string") || postText.length === 0) {
log("Not posting an empty string...");
return;
}
- let post = new Post(`bobbydigitales`, userID, postText, new Date());
- posts.push(post);
+ let post = new Post(this.username, userID, postText, new Date());
+ this.posts.push(post);
// localStorage.setItem(key, JSON.stringify(posts));
addData(userID, post);
- this.render(posts);
+ this.render(this.posts);
}
getPeerID() {
let id = localStorage.getItem("peer_id");
@@ -293,6 +357,14 @@ class App {
}
return id;
}
+ getUsername() {
+ let username = localStorage.getItem("dandelion_username");
+ if (!username) {
+ username = "not_set";
+ localStorage.setItem("dandelion_username", username);
+ }
+ return username;
+ }
setFont(fontName, fontSize) {
let content = document.getElementById('content');
if (!content) {
@@ -313,9 +385,11 @@ class App {
log("offline");
});
// Event listener for going online
- window.addEventListener('online', () => {
+ window.addEventListener('online', async () => {
log("online");
connection.connect();
+ this.posts = await this.loadPosts(this.userID) ?? [];
+ this.render(this.posts);
});
log(`Online status: ${navigator.onLine ? "online" : "offline"}`);
}
@@ -357,14 +431,17 @@ class App {
});
}
initButtons(userID, posts, registration) {
- let font1Button = document.getElementById("button_font1");
- let font2Button = document.getElementById("button_font2");
+ // let font1Button = document.getElementById("button_font1") as HTMLButtonElement;
+ // let font2Button = document.getElementById("button_font2") as HTMLButtonElement;
let importTweetsButton = document.getElementById("import_tweets");
let clearPostsButton = document.getElementById("clear_posts");
let updateApp = document.getElementById("update_app");
let ddlnLogoButton = document.getElementById('ddln-logo-button');
- font1Button.addEventListener('click', () => { this.setFont('Bookerly', '16px'); });
- font2Button.addEventListener('click', () => { this.setFont('Virgil', '16px'); });
+ let usernameField = document.getElementById('username');
+ usernameField?.addEventListener('input', (event) => {
+ this.username = event.target.innerText;
+ localStorage.setItem("dandelion_username", this.username);
+ });
importTweetsButton.addEventListener('click', async () => {
let file = await this.selectFile('text/*');
console.log(file);
@@ -388,7 +465,7 @@ class App {
throw new Error();
}
postButton.addEventListener("click", () => {
- this.addPost(userID, posts, postText.value);
+ this.addPost(userID, postText.value);
postText.value = "";
});
updateApp.addEventListener("click", () => {
@@ -416,40 +493,54 @@ class App {
let urlParams = (new URL(window.location.href)).searchParams;
let connection_userID = urlParams.get('connect');
let registration = undefined;
- if (urlParams.get("sw") === "true") {
- registration = await this.registerServiceWorker();
- }
+ // if (urlParams.get("sw") === "true") {
+ registration = await this.registerServiceWorker();
+ // }
if (connection_userID) {
console.log('connect', connection_userID);
localStorage.setItem("dandelion_id", connection_userID);
}
- let posts = [];
+ this.username = this.getUsername();
+ document.getElementById('username').innerText = this.username;
let userID = this.getUserID();
let peerID = this.getPeerID();
this.userID = userID;
this.peerID = peerID;
+ let connectURL = `https://${document.location.hostname}?connect=${this.userID}`;
+ document.getElementById('connectURL').innerHTML = `
connect`;
+ let qrcode = await new QRCode(document.getElementById('qrcode'), {
+ text: connectURL,
+ width: 256,
+ height: 256,
+ colorDark: "#000000",
+ colorLight: "#ffffff",
+ correctLevel: QRCode.CorrectLevel.H
+ });
+ let qrcodeImage = document.querySelector('#qrcode > img');
+ qrcodeImage.classList.add('qrcode_image');
log(`user:${userID} peer:${peerID}`);
let websocket = new wsConnection(userID, peerID);
window.addEventListener('beforeunload', () => { websocket.disconnect(); });
this.initOffline(websocket);
- this.initButtons(userID, posts, registration);
+ this.initButtons(userID, this.posts, registration);
let time = 0;
let delta = 0;
- if (navigator.storage && navigator.storage.persist && !navigator.storage.persisted) {
- debugger;
- const isPersisted = await navigator.storage.persist();
- log(`Persisted storage granted: ${isPersisted}`);
- }
- log(`Persisted: ${(await navigator?.storage?.persisted())?.toString()}`);
+ // let isPersisted = await navigator?.storage?.persisted();
+ // if (!isPersisted) {
+ // debugger;
+ // const isPersisted = await navigator.storage.persist();
+ // log(`Persisted storage granted: ${isPersisted}`);
+ // }
+ // log(`Persisted: ${(await navigator?.storage?.persisted())?.toString()}`);
this.initMarkdown();
// let main = await fetch("/main.js");
// let code = await main.text();
// console.log(code);
// registration.active.postMessage({type:"updateMain", code:code});
- posts = await this.loadPosts(userID) ?? [];
+ this.posts = await this.loadPosts(userID) ?? [];
// debugger;
this.timerStart();
- this.render(posts); // , (postID:string)=>{this.deletePost(userID, postID)}
+ this.render(this.posts); // , (postID:string)=>{this.deletePost(userID, postID)}
let renderTime = this.timerDelta();
log(`render took: ${renderTime.toFixed(2)}ms`);
if (performance?.memory) {
@@ -476,7 +567,6 @@ class App {
}
contentDiv.innerHTML = "";
let count = 0;
- new QRCode(document.getElementById('qrcode'), `https://ddlion.net/?connect=${this.userID}`);
for (let i = posts.length - 1; i >= 0; i--) {
let postData = posts[i];
let post = this.renderPost(postData, posts);
@@ -493,8 +583,10 @@ class App {
}
contentDiv.appendChild(fragment);
}
- deletePost(userID, postID) {
+ async deletePost(userID, postID) {
deleteData(userID, postID);
+ this.posts = await this.loadPosts(userID) ?? [];
+ this.render(this.posts);
}
renderPost(post, posts) {
if (!(post.hasOwnProperty("text"))) {
@@ -504,13 +596,20 @@ class App {
let timestamp = `${post.post_timestamp.toLocaleTimeString()} · ${post.post_timestamp.toLocaleDateString()}`;
let deleteButton = document.createElement('button');
deleteButton.innerText = 'delete';
- // deleteButton.onclick = ()=>{deletefunc(post.post_id)};
+ let editButton = document.createElement('button');
+ editButton.innerText = 'edit';
+ deleteButton.onclick = () => { this.deletePost(this.userID, post.post_id); };
let postTemplate = `
-
+
+
+
${marked.parse(post.text)}
`;
containerDiv.innerHTML = postTemplate;
- containerDiv.appendChild(deleteButton);
+ containerDiv.querySelector('#deleteButton')?.appendChild(deleteButton);
+ containerDiv.querySelector('#editButton')?.appendChild(editButton);
// if (!("image_data" in post && post.image_data)) {
// containerDiv.appendChild(timestampDiv);
// return containerDiv;
diff --git a/main.js.map b/main.js.map
index a7501be..e280e51 100644
--- a/main.js.map
+++ b/main.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.js","sourceRoot":"","sources":["src/main.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"main.js","sourceRoot":"","sources":["src/main.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAI9E,OAAO,EAAgB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAMzH,iBAAiB;AACjB,sCAAsC;AACtC,uBAAuB;AAEvB,4BAA4B;AAC5B,kBAAkB;AAClB,mBAAmB;AACnB,iBAAiB;AACjB,kBAAkB;AAClB,oBAAoB;AACpB,oBAAoB;AACpB,IAAI;AAEJ,SAAS,MAAM,CAAC,UAAkB;IAChC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,MAAM;IACb,OAAO,sCAAsC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAM,EAAE,EAAE,CACzE,CAAC,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAC9E,CAAC;AACJ,CAAC;AAGD,IAAI,QAAQ,GAAa,EAAE,CAAC;AAC5B,IAAI,SAAS,GAAG,EAAE,CAAC;AACnB,SAAS,GAAG,CAAC,OAAe;IAC1B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,kBAAkB,EAAE,KAAK,OAAO,EAAE,CAAC,CAAC;IAChE,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACzB,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,GAAG,GAAG,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,EAAE,CAAC;IACpB,CAAC;IACD,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAClC,CAAC;IAED,OAAO,MAAM,EAAE,CAAC;AAClB,CAAC;AAED,MAAM,IAAI;IAYR,YAAY,MAAc,EAAE,SAAiB,EAAE,IAAY,EAAE,cAAoB,EAAE,YAAgC,IAAI,EAAE,eAAiC,IAAI,EAAE,eAAoB,IAAI;QACtL,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,UAAU,EAAE,CAAC;QAE5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAE5B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;CACF;AAED,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE;IACrC,+BAA+B;IAC/B,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;IAEnD,0BAA0B;IAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;IAExD,8BAA8B;IAC9B,IAAI,WAAW,IAAI,eAAe,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAC1C,mCAAmC;IACrC,CAAC;AACH,CAAC,CAAC,CAAC;AAIH,gDAAgD;AAEhD,0DAA0D;AAC1D,sCAAsC;AACtC,sEAAsE;AACtE,0BAA0B;AAC1B,uCAAuC;AACvC,uDAAuD;AACvD,uDAAuD;AAEvD,qEAAqE;AAErE,+BAA+B;AAE/B,MAAM;AACN,IAAI;AAEJ,MAAM,YAAY;IAahB,IAAI,CAAC,OAAY;QACf,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,+CAA+C,EAAE,OAAO,CAAC,CAAC;QAC3E,CAAC;QACD,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QAC9B,IAAI,CAAC,SAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE7B,CAAC;IAED,oBAAoB,CAAC,IAAS;QAC5B,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAc,CAAC,CAAC,CAAC,CAAC;YAEzD,KAAK,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAc,CAAC,CAAC,EAAE,CAAC;gBACpD,IAAI,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC3B,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC,IAAI,CAAC;oBACR,IAAI,EAAC,cAAc;oBACnB,IAAI,EAAC,IAAI,CAAC,MAAM;oBAChB,EAAE,EAAC,MAAM;oBACT,OAAO,EAAC,EAAC,IAAI,EAAC,oBAAoB,EAAE,OAAO,EAAC,MAAM,EAAC;iBACpD,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,WAAW,CAAC,IAAS;IACrB,CAAC;IAED,KAAK,CAAC,8BAA8B,CAAC,IAAS;QAC5C,0CAA0C;QAE1C,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,sCAAsC,OAAO,CAAC,KAAK,CAAC,MAAM,cAAc,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACjG,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAA;QAC9C,MAAM,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAGzD,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YACpC,GAAG,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACnD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,IAAS;QACpC,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3B,IAAI,KAAK,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,8BAA8B;QACnF,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAQ,EAAC,EAAE,CAAA,IAAI,CAAC,IAAI,CAAC,CAAA;QACxC,uDAAuD;QAEvD,IAAI,eAAe,GAAG,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,6BAA6B,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAA;QACzK,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,IAAS;QAChC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAA;QAEjC,IAAI,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAExC,IAAI,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAE5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,sDAAsD,eAAe,EAAE,CAAC,CAAC;YACvF,OAAO;QACT,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,SAAS,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YAClD,OAAO;QACT,CAAC;QAED,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACjD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;QAAC,CAAC;QAAA,CAAC;QAE/C,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,SAAS,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC;QACjG,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,KAAK,EAAE,EAAE;YAChC,GAAG,CAAC,cAAc,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,EAAC,IAAI,EAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAC,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;YACrE,IAAI,CAAC,qBAAqB,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE;gBACnD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;oBACtB,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,EAAC,IAAI,EAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;YACjD,CAAC,EAAE,KAAM,CAAC,CAAA;QACZ,CAAC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YACjC,GAAG,CAAC,iBAAiB,CAAC,CAAC;YACvB,mBAAmB;YACnB,GAAG,CAAC,eAAe,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC;YACzC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;QAClE,CAAC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE;YACnC,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAC,EAAE,CAAC,CAAC,CAAC;YAEtC,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAElC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;YAEpB,IAAI,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,IAAI,CAAC,CAAC;gBACrD,OAAO;YACT,CAAC;YAED,OAAO,CAAC,IAAI,CAAC,CAAC;QAEhB,CAAC,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YACjC,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;IAED,UAAU;QACR,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAGD,YAAY,MAAc,EAAE,MAAc;QArJ1C,cAAS,GAAqB,IAAI,CAAC;QACnC,WAAM,GAAG,EAAE,CAAC;QACZ,WAAM,GAAG,EAAE,CAAC;QACZ,0BAAqB,GAAW,CAAC,CAAC;QAClC,UAAK,GAAG,EAAE,CAAC;QACX,UAAK,GAAG,cAAc,CAAC;QACvB,UAAK,GAA0B,IAAI,GAAG,EAAE,CAAC;QAEzC,oBAAe,GAAsC,IAAI,GAAG,EAAE,CAAC;QAC/D,wBAAmB,GAAqC,IAAI,GAAG,EAAE,CAAC;QA6IhE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAE7E,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3F,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5G,IAAI,CAAC,OAAO,EAAE,CAAC;QAEf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,yBAAyB;QAC3B,CAAC;IAEH,CAAC;CACF;AAED,MAAM,GAAG;IAAT;QACE,aAAQ,GAAW,EAAE,CAAC;QACtB,WAAM,GAAW,EAAE,CAAC;QACpB,WAAM,GAAW,EAAE,CAAC;QACpB,UAAK,GAAW,EAAE,CAAC;QAuCnB,SAAI,GAAG,CAAC,CAAC;IA0fX,CAAC;IA/hBC,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvC,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAS,EAAE,KAAa,EAAE,IAAY,EAAE,EAAE;YACzD,OAAO,YAAY,IAAI,oBAAoB,KAAK,CAAC,CAAC,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,MAAM,CAAC;QAC5F,CAAC,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,mBAAmB,CAAC,MAAmB;QACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;YACtE,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAEhC,MAAM,CAAC,SAAS,GAAG,GAAG,EAAE;gBACtB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAgB,CAAC;gBACxC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,CAAC,IAAI,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC,CAAC;YAEF,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;gBACzB,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC;YAEF,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,aAAa,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEvE,OAAO,aAAa,CAAC;IACvB,CAAC;IAID,UAAU;QACR,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAChC,CAAC;IAED,UAAU;QACR,OAAO,WAAW,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;IACvC,CAAC;IAED,iBAAiB,CAAC,KAAU;QAG1B,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;QAErC,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC5C,YAAY,GAAG,IAAI,GAAG,CAAC,WAAW,KAAK,GAAG,CAAC,YAAY,GAAG,CAAC;YAC3D,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACrD,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,MAAc,EAAE,YAAmB;QAC1D,GAAG,CAAC,yBAAyB,CAAC,CAAA;QAC9B,IAAI,aAAa,GAAU,EAAE,CAAC;QAE9B,6CAA6C;QAC7C,0CAA0C;QAC1C,kFAAkF;QAElF,8BAA8B;QAG9B,sCAAsC;QACtC,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,IAAI,KAAK,IAAI,YAAY,EAAE,CAAC;YAC/B,kIAAkI;YAClI,cAAc;YACd,IAAI;YAEJ,IAAI,QAAQ,GAAW,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC;YAC1E,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACrC,CAAC;YAED,IAAI,SAAS,GAAG,IAAI,CAAC;YACrB,iBAAiB;YACjB,UAAU;YACV,4CAA4C;YAC5C,yBAAyB;YACzB,qCAAqC;YACrC,kDAAkD;YAClD,QAAQ;YACR,8BAA8B;YAC9B,kBAAkB;YAClB,sBAAsB;YACtB,MAAM;YAEN,IAAI;YAEJ,IAAI,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACjD,IAAI,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC9C,IAAI,OAAO,GAAG,IAAI,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YAEpG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE5B,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,YAAY,KAAK,WAAW,CAAC,CAAC;gBAClC,yBAAyB;YAC3B,CAAC;YAED,wBAAwB;YACxB,WAAW;YACX,IAAI;QAEN,CAAC;QACD,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAc;QAClC,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEzD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,IAAI,CAAC,CAAC,eAAe,IAAI,SAAS,CAAC,EAAE,CAAC;YACpC,OAAO;QACT,CAAC;QAED,IAAI,aAAa,GAAG,MAAM,SAAS,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC;QACrE,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;QAED,SAAS,CAAC,aAAa;aACpB,QAAQ,CAAC,QAAQ,CAAC;aAClB,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;YACrB,OAAO,CAAC,GAAG,CAAC,uCAAuC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YACzE,OAAO,YAAY,CAAC;QACtB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,QAAgB;QACtC,IAAI,CAAC,OAAO,QAAQ,KAAK,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5D,GAAG,CAAC,gCAAgC,CAAC,CAAA;YACrC,OAAO;QACT,CAAC;QAED,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAEjE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,oDAAoD;QACpD,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAErB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAGD,SAAS;QACP,IAAI,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,EAAE,GAAG,UAAU,EAAE,CAAC;YAClB,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,SAAS;QACP,IAAI,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAE9C,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,EAAE,GAAG,UAAU,EAAE,CAAC;YAClB,YAAY,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,WAAW;QACT,IAAI,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAE1D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,QAAQ,GAAG,SAAS,CAAA;YACpB,YAAY,CAAC,OAAO,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,QAAgB;QAExC,IAAI,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAEjD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAElC,IAAI,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO;QACT,CAAC;QAED,QAAQ,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC;QACrC,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACrC,CAAC;IAED,WAAW,CAAC,UAAwB;QAClC,mCAAmC;QACnC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE;YACtC,GAAG,CAAC,SAAS,CAAC,CAAA;QAChB,CAAC,CAAC,CAAC;QAEH,kCAAkC;QAClC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC3C,GAAG,CAAC,QAAQ,CAAC,CAAA;YACb,UAAU,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,kBAAkB,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAA;IAElE,CAAC;IAED,UAAU,CAAC,WAAmB;QAC5B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC3B,IAAI,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC5C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;YACpB,6BAA6B;YAC7B,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC;YAE3B,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE;gBACpB,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;oBACxB,OAAO,CAAC,IAAI,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBAED,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAEpC,gBAAgB;gBAChB,sBAAsB;gBACtB,OAAO;gBACP,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACpB,CAAC,CAAC;YAEF,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,IAAU;QACjB,0BAA0B;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,qBAAqB;YACrB,MAAM,CAAC,SAAS,GAAG,UAAU,CAAM;gBACjC,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;gBAC1B,OAAO,CAAC,OAAO,CAAC,CAAC;YACnB,CAAC,CAAC;YACF,mCAAmC;YACnC,MAAM,CAAC,OAAO,GAAG,UAAU,CAAM;gBAC/B,MAAM,CAAC,CAAC,CAAC,CAAC;YACZ,CAAC,CAAC;YACF,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW,CAAC,MAAc,EAAE,KAAa,EAAE,YAAmD;QAC5F,kFAAkF;QAClF,kFAAkF;QAClF,IAAI,kBAAkB,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAsB,CAAC;QACvF,IAAI,gBAAgB,GAAG,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAsB,CAAC;QACnF,IAAI,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,YAAY,CAAsB,CAAC;QAC3E,IAAI,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC,kBAAkB,CAAmB,CAAC;QAEnF,IAAI,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACxD,aAAa,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAS,EAAC,EAAE;YACpD,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC;YACvC,YAAY,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAA;QAEF,kBAAkB,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACtD,IAAI,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAE3C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBACjB,OAAO;YACT,CAAC;YAED,IAAI,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC1C,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAErC,IAAI,cAAc,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACnE,SAAS,CAAC,MAAM,CAAC,CAAC;YAClB,2BAA2B;YAC3B,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;YACrC,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAErB,CAAC,CAAC,CAAC;QAEH,gBAAgB,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAC,CAAC;QAGxG,IAAI,UAAU,GAAG,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAsB,CAAC;QAC7E,IAAI,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAwB,CAAC;QAE/E,IAAI,CAAC,CAAC,UAAU,IAAI,QAAQ,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,EAAE,CAAC;QACpB,CAAC;QAED,UAAU,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACxC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;YACrC,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACvC,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,IAAI,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAElD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,cAAc,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACtK,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAc;QAE5B,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,KAAK,GAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAEtE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,UAAU,KAAK,CAAC,MAAM,aAAa,IAAI,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,yCAAyC;QAEzC,8BAA8B;QAC9B,+BAA+B;QAC/B,+DAA+D;IACjE,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,SAAS,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC;QAC7D,IAAI,iBAAiB,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAEjD,IAAI,YAAY,GAAG,SAAS,CAAC;QAC7B,wCAAwC;QACxC,YAAY,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAClD,IAAI;QAEJ,IAAI,iBAAiB,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;YAC1C,YAAY,CAAC,OAAO,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAE,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/D,IAAI,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC9B,IAAI,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,UAAU,GAAG,WAAW,QAAQ,CAAC,QAAQ,CAAC,QAAQ,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC;QAChF,QAAQ,CAAC,cAAc,CAAC,YAAY,CAAE,CAAC,SAAS,GAAG,YAAY,UAAU,eAAe,CAAC;QAEzF,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE;YAC/D,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,GAAG;YACX,SAAS,EAAE,SAAS;YACpB,UAAU,EAAE,SAAS;YACrB,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;SACpC,CAAC,CAAC;QAGH,IAAI,WAAW,GAAoB,QAAQ,CAAC,aAAa,CAAC,eAAe,CAAqB,CAAC;QAC/F,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAE1C,GAAG,CAAC,QAAQ,MAAM,SAAS,MAAM,EAAE,CAAC,CAAC;QACrC,IAAI,SAAS,GAAG,IAAI,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,UAAU,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QACzE,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAEnD,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,2DAA2D;QAC3D,sBAAsB;QACtB,cAAc;QACd,2DAA2D;QAC3D,sDAAsD;QACtD,IAAI;QAEJ,4EAA4E;QAE5E,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,sCAAsC;QACtC,gCAAgC;QAChC,uBAAuB;QACvB,mEAAmE;QAEnE,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAEhD,YAAY;QAEZ,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,uDAAuD;QAChF,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAEnC,GAAG,CAAC,gBAAgB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAK,WAAmB,EAAE,MAAM,EAAE,CAAC;YACjC,GAAG,CAAC,gBAAgB,CAAE,WAAmB,CAAC,MAAM,CAAC,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAChG,CAAC;QAGD,kCAAkC;QAElC,4CAA4C;QAC5C,0aAA0a;QAE1a,kDAAkD;QAClD,gEAAgE;QAChE,2DAA2D;QAC3D,wCAAwC;QACxC,OAAO;QAEP,iDAAiD;QACjD,qDAAqD;QACrD,+DAA+D;QAC/D,KAAK;IACP,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,MAAM,QAAQ,GAAG,QAAQ,CAAC,sBAAsB,EAAE,CAAC;QACnD,IAAI,UAAU,GAAG,QAAQ,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,EAAE,CAAC;QACpB,CAAC;QACD,UAAU,CAAC,SAAS,GAAG,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,IAAI,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAExB,IAAI,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAE5C,IAAI,IAAI,EAAE,CAAC;gBACT,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBAC3B,KAAK,EAAE,CAAC;YACV,CAAC;YACD,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;gBAChB,MAAM;YACR,CAAC;QACH,CAAC;QAGD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QAED,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEnC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,MAAc;QAC7C,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,UAAU,CAAC,IAAU,EAAE,KAAa;QAClC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,SAAS,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE,EAAE,CAAC;QAE5G,IAAI,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAAC,YAAY,CAAC,SAAS,GAAG,QAAQ,CAAC;QACvF,IAAI,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAAC,UAAU,CAAC,SAAS,GAAG,MAAM,CAAC;QACjF,YAAY,CAAC,OAAO,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA,CAAC,CAAC,CAAC;QAE5E,IAAI,YAAY,GACd;;oCAE8B,SAAS,MAAM,IAAI,CAAC,MAAM;+CACf,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE;;;WAG5E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;WACvB,CAAA;QAEP,YAAY,CAAC,SAAS,GAAG,YAAY,CAAC;QAEtC,YAAY,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;QACvE,YAAY,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;QAEnE,oDAAoD;QACpD,4CAA4C;QAC5C,yBAAyB;QACzB,oBAAoB;QACpB,IAAI;QAEJ,6CAA6C;QAC7C,kFAAkF;QAClF,yCAAyC;QACzC,yBAAyB;QACzB,8BAA8B;QAC9B,KAAK;QAKL,mBAAmB;QACnB,oEAAoE;QACpE,iCAAiC;QAEjC,mCAAmC;QACnC,0CAA0C;QAE1C,OAAO,YAAY,CAAC;IACtB,CAAC;CAEF;AAED,IAAI,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;AAEpB,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC"}
\ No newline at end of file
diff --git a/privkey.pem b/privkey.pem
deleted file mode 100644
index 4af4d5c..0000000
--- a/privkey.pem
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgh092YwNP2/D6bshI
-vzZNcEn/bpxtz7h3Otown6UXP6GhRANCAASEd7+0ppvG0iyaAjv8urB8LVYKSIYO
-8GLp6H9P2fwZjzWXfC8iQMhP9jaWaQaAl73gS4iXpODDzYJ/Hwi6siPt
------END PRIVATE KEY-----
diff --git a/src/db.ts b/src/db.ts
index 0bcfd5d..99a70c5 100644
--- a/src/db.ts
+++ b/src/db.ts
@@ -4,6 +4,9 @@
// email: string;
// }
+
+// Efficiently storing data in indexdb: https://stackoverflow.com/a/62975917
+
const postStoreName: string = "posts";
let keyBase = "dandelion_posts_v1_"
let key = "";
@@ -156,6 +159,60 @@ export async function addDataArray(userID: string, array: any[]): Promise
}
}
+export async function mergeDataArray(userID: string, array:any[]): Promise {
+ try {
+ const db = await openDatabase(userID);
+ const transaction = db.transaction(postStoreName, "readwrite");
+ const store = transaction.objectStore(postStoreName);
+ const index = store.index("postIDIndex");
+
+ transaction.oncomplete = () => {
+ console.log("Transaction completed successfully");
+ db.close();
+ };
+
+ transaction.onerror = (event) => {
+ console.error("Transaction error:", (event.target as any).error);
+ db.close();
+ };
+
+ let postsToWrite:any = [];
+
+ for (let post of array) {
+ try {
+ let havePost = await new Promise((resolve, reject) => {
+ const getRequest = index.getKey(post.post_id);
+
+ getRequest.onerror = (e) => {
+ console.log((e.target as IDBRequest).error);
+ reject(e);
+ };
+
+ getRequest.onsuccess = async (e) => {
+ const key = (e.target as IDBRequest).result;
+ resolve(key !== undefined)
+ };
+
+ });
+
+ // console.log(post.post_id, havePost);
+ if (!havePost ) {
+ postsToWrite.push(post);
+ }
+
+ } catch (error) {
+ console.error("Error processing post:", error);
+ }
+ }
+
+ console.log(`Writing ${postsToWrite.length} posts`);
+
+ await addDataArray(userID, postsToWrite);
+ } catch (error) {
+ console.error("Error in opening database:", error);
+ }
+}
+
export async function getData(userID: string, lowerID: Date, upperID: Date): Promise {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readonly");
@@ -191,6 +248,7 @@ export async function getData(userID: string, lowerID: Date, upperID: Date): Pro
});
}
+
export async function getAllData(userID: string): Promise {
const db = await openDatabase(userID);
const transaction = db.transaction(postStoreName, "readonly");
diff --git a/src/main.ts b/src/main.ts
index 3ddab61..e83c748 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,8 @@
-import { openDatabase, getData, addData, addDataArray, clearData, deleteData} from "./db.js"
+// TODO: virtual list, only rerender what's needed so things can keep playing.
+
+
+
+import { openDatabase, getData, addData, addDataArray, clearData, deleteData, mergeDataArray, getAllData } from "./db.js"
declare let WebTorrent: any;
@@ -31,6 +35,7 @@ function uuidv4() {
let logLines: string[] = [];
let logLength = 10;
function log(message: string) {
+ console.log(message);
logLines.push(`${new Date().toLocaleTimeString()}: ${message}`);
if (logLines.length > 10) {
logLines = logLines.slice(logLines.length - logLength);
@@ -93,14 +98,16 @@ window.addEventListener('scroll', () => {
+// let peer = await new PeerConnection(peer_id);
+
// let connectionReply = await wsConnection.send('hello');
-// for (let peeer of connectionReply) {
+// for (let peer of connectionReply) {
// let peerConnection = await wsConnection.send('connect', peer.id);
// if (peerConnection) {
// this.peers.push(peerConnection);
// let postIDs = await peerConnection.getPostIDs();
// let postsWeDontHave = this.diffPostIDs(postIDs);
-
+
// let newPosts = await peerConnection.getPosts(postsWeDontHave);
// this.addPosts(newPosts);
@@ -108,7 +115,6 @@ window.addEventListener('scroll', () => {
// }
// }
-
class wsConnection {
websocket: WebSocket | null = null;
userID = "";
@@ -116,7 +122,88 @@ class wsConnection {
websocketPingInterval: number = 0;
retry = 10;
state = 'disconnected';
+ peers: Map = new Map();
+ messageHandlers: Map void> = new Map();
+ peerMessageHandlers: Map void> = new Map();
+
+
+ send(message: any) {
+ let json = ""
+ try {
+ json = JSON.stringify(message);
+ } catch (e) {
+ console.log(e, "wsConnection send: Couldn't serialize message", message);
+ }
+ log(`ws->${json.slice(0,80)}`)
+ this.websocket!.send(json);
+
+ }
+
+ helloResponseHandler(data: any) {
+ for (let [userID, peerIDs] of Object.entries(data.userPeers)) {
+ this.peers.set(userID, [...Object.keys(peerIDs as any)]);
+
+ for (let peerID of [...Object.keys(peerIDs as any)]) {
+ if (peerID === this.peerID) {
+ continue;
+ }
+
+ this.send({
+ type:"peer_message",
+ from:this.peerID,
+ to:peerID,
+ message:{type:"get_posts_for_user", user_id:userID}
+ })
+ }
+ }
+ }
+
+ pongHandler(data: any) {
+ }
+
+ async getPostsForUserResponseHandler(data: any) {
+ // log(`getPostsForUserResponse: ${data}`)
+
+ let message = data.message;
+ console.log(`getPostsForUserResponseHandler Got ${message.posts.length} from peer ${data.from}`);
+ for (let post of message.posts) {
+ post.post_timestamp = new Date(post.post_timestamp);
+ }
+ console.log(`Merging same user peer posts...`)
+ await mergeDataArray(message.user_id, data.message.posts)
+
+
+ if (message.user_id === this.userID) {
+ app.posts = await app.loadPosts(this.userID) ?? [];
+ app.render(app.posts);
+ }
+ }
+
+ async getPostsForUserHandler(data: any) {
+ let message = data.message;
+ let posts = await getAllData(message.user_id) ?? []; // this doesn't get all posts!
+ posts = posts.map((post:any)=>post.data)
+ // let posts = await getAllData(message.user_id) ?? [];
+
+ let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, message: { type: "get_posts_for_user_response", posts: posts, user_id: message.user_id } }
+ this.send(responseMessage)
+ }
+
+ async peerMessageHandler(data: any) {
+ log(`peerMessageHandler ${data}`)
+
+ let peerMessageType = data.message.type;
+
+ let handler = this.peerMessageHandlers.get(peerMessageType);
+
+ if (!handler) {
+ console.error(`got peer message type we don't have a handler for: ${peerMessageType}`);
+ return;
+ }
+
+ handler(data);
+ }
connect(): void {
if (this.websocket?.readyState === WebSocket.OPEN) {
@@ -133,18 +220,18 @@ class wsConnection {
return;
}
- this.websocket.onopen = (evt) => {
+ this.websocket.onopen = (event) => {
log("ws:connected");
- this.websocket!.send(`{"type":"hello", "user_id": "${this.userID}", "peer_id":"${this.peerID}"}`);
+ this.send({type:"hello", user_id: this.userID, peer_id:this.peerID});
this.websocketPingInterval = window.setInterval(() => {
if (!navigator.onLine) {
return;
}
- this.websocket!.send(`{"type":"ping", "peer_id": "${this.peerID}"}`);
+ this.send({type:"ping", peer_id: this.peerID});
}, 10_000)
};
- this.websocket.onclose = (evt) => {
+ this.websocket.onclose = (event) => {
log("ws:disconnected");
// this.retry *= 2;
log(`Retrying in ${this.retry} seconds`);
@@ -152,7 +239,20 @@ class wsConnection {
};
this.websocket.onmessage = (event) => {
- log('ws:response: ' + event.data);
+ log('ws:<-' + event.data.slice(0,80));
+
+ 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) => {
@@ -168,6 +268,13 @@ class wsConnection {
constructor(userID: string, peerID: string) {
this.userID = userID;
this.peerID = peerID;
+
+ this.messageHandlers.set('hello', this.helloResponseHandler.bind(this));
+ this.messageHandlers.set('pong', this.pongHandler);
+ this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
+
+ this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
+ this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserResponseHandler.bind(this));
this.connect();
if (!this.websocket) {
@@ -177,34 +284,11 @@ class wsConnection {
}
}
-// function connectWebsocket(userID: string) {
-// let websocket = new WebSocket(`ws://${window.location.hostname}:${window.location.port}/ws`);
-
-// websocket.onopen = function (evt) {
-// log("Websocket: CONNECTED");
-// websocket.send(`{"messageType":"connect", "id": "${userID}"}`);
-
-// let websocketPingInterval = window.setInterval(() => { websocket.send(`{"messageType":"ping", "id": "${userID}"}`); }, 5000)
-// };
-
-// websocket.onclose = function (evt) {
-// log("Websocket: DISCONNECTED");
-// };
-
-// websocket.onmessage = function (evt) {
-// log('Websocket: RESPONSE: ' + evt.data);
-// };
-
-// websocket.onerror = function (evt) {
-// log('Websocket: ERROR: ' + evt);
-// };
-
-// return websocket;
-// }
-
class App {
- userID:string = '';
- peerID:string = '';
+ username: string = '';
+ userID: string = '';
+ peerID: string = '';
+ posts: Post[] = [];
initMarkdown() {
const renderer = new marked.Renderer();
@@ -213,12 +297,12 @@ class App {
};
marked.setOptions({ renderer: renderer });
}
-
+
arrayBufferToBase64(buffer: ArrayBuffer) {
return new Promise((resolve, reject) => {
const blob = new Blob([buffer], { type: 'application/octet-stream' });
const reader = new FileReader();
-
+
reader.onloadend = () => {
const dataUrl = reader.result as string;
if (!dataUrl) {
@@ -228,70 +312,70 @@ class App {
const base64 = dataUrl.split(',')[1];
resolve(base64);
};
-
+
reader.onerror = (error) => {
reject(error);
};
-
+
reader.readAsDataURL(blob);
});
}
-
+
async createTestData() {
let postsTestData = await (await fetch("./postsTestData.json")).json();
-
+
return postsTestData;
}
-
+
time = 0;
-
+
timerStart() {
this.time = performance.now();
}
-
+
timerDelta() {
return performance.now() - this.time;
}
-
+
getFixedTweetText(entry: any) {
-
-
+
+
let fullText = entry.tweet.full_text;
-
+
let linkMarkdown = "";
for (const url of entry.tweet.entities.urls) {
linkMarkdown = `[${url.display_url}](${url.expanded_url})`;
fullText = fullText.replace(url.url, linkMarkdown);
}
-
+
return fullText
}
-
- async importTweetArchive(userID: string, tweetArchive: any[]) {
+
+ async importTweetArchive(userID: string, tweetArchive: any[]) {
log("Importing tweet archive")
let postsTestData: any[] = [];
-
+
// let response = await fetch("./tweets.js");
// let tweetsText = await response.text();
// tweetsText = tweetsText.replace("window.YTD.tweets.part0", "window.tweetData");
-
+
// new Function(tweetsText)();
-
-
+
+
// let tweets = JSON.parse(tweetJSON);
let count = 0;
-
+
for (let entry of tweetArchive) {
- if (entry.tweet.hasOwnProperty("in_reply_to_screen_name") || entry.tweet.retweeted || entry.tweet.full_text.startsWith("RT")) {
- continue;
- }
-
+ // if (entry.tweet.hasOwnProperty("in_reply_to_screen_name") || entry.tweet.retweeted || entry.tweet.full_text.startsWith("RT")) {
+ // continue;
+ // }
+
let mediaURL: string = entry.tweet?.entities?.media?.[0]?.media_url_https;
let isImage = false;
if (mediaURL) {
isImage = mediaURL.includes('jpg');
}
-
+
let imageData = null;
// if (isImage) {
// try {
@@ -304,46 +388,46 @@ class App {
// } catch (e) {
// console.log(e);
// }
-
+
// }
-
+
let timeStamp = new Date(entry.tweet.created_at);
let tweetText = this.getFixedTweetText(entry);
let newPost = new Post('bobbydigitales', userID, tweetText, timeStamp, imageData, 'twitter', entry);
-
+
postsTestData.push(newPost);
-
+
count++;
if (count % 100 === 0) {
log(`Imported ${count} posts...`);
// render(postsTestData);
}
-
+
// if (count == 100-1) {
// break;
// }
-
+
}
return postsTestData;
}
-
+
async createTestData3(userID: string) {
let posts = await (await (fetch('./posts.json'))).json();
-
+
return posts;
}
-
+
async registerServiceWorker() {
if (!("serviceWorker" in navigator)) {
return;
}
-
+
let registrations = await navigator.serviceWorker.getRegistrations();
if (registrations.length > 0) {
console.log("Service worker already registered.");
return registrations[0];
}
-
+
navigator.serviceWorker
.register("/sw.js")
.then((registration) => {
@@ -354,107 +438,119 @@ class App {
console.error("Service Worker registration failed:", error);
});
}
-
- addPost(userID: string, posts: Post[], postText: string) {
+
+ addPost(userID: string, postText: string) {
if ((typeof postText !== "string") || postText.length === 0) {
log("Not posting an empty string...")
return;
}
-
- let post = new Post(`bobbydigitales`, userID, postText, new Date());
-
- posts.push(post);
+
+ let post = new Post(this.username, userID, postText, new Date());
+
+ this.posts.push(post);
// localStorage.setItem(key, JSON.stringify(posts));
addData(userID, post)
-
- this.render(posts);
+
+ this.render(this.posts);
}
-
-
+
+
getPeerID() {
let id = localStorage.getItem("peer_id");
-
+
if (!id) {
id = generateID();
localStorage.setItem("peer_id", id);
}
-
+
return id;
}
-
+
getUserID() {
let id = localStorage.getItem("dandelion_id");
-
+
if (!id) {
id = generateID();
localStorage.setItem("dandelion_id", id);
}
-
+
return id;
}
+ getUsername() {
+ let username = localStorage.getItem("dandelion_username");
+
+ if (!username) {
+ username = "not_set"
+ localStorage.setItem("dandelion_username", username);
+ }
+
+ return username;
+ }
+
setFont(fontName: string, fontSize: string) {
let content = document.getElementById('content');
-
+
if (!content) {
return;
}
-
+
content.style.fontFamily = fontName;
content.style.fontSize = fontSize;
-
+
let textArea = document.getElementById('textarea_post');
if (!textArea) {
return;
}
-
+
textArea.style.fontFamily = fontName;
textArea.style.fontSize = fontSize;
}
-
+
initOffline(connection: wsConnection) {
// Event listener for going offline
window.addEventListener('offline', () => {
log("offline")
});
-
+
// Event listener for going online
- window.addEventListener('online', () => {
+ window.addEventListener('online', async () => {
log("online")
connection.connect();
-
+ this.posts = await this.loadPosts(this.userID) ?? [];
+ this.render(this.posts);
});
-
+
log(`Online status: ${navigator.onLine ? "online" : "offline"}`)
-
+
}
-
+
selectFile(contentType: string): Promise {
return new Promise(resolve => {
let input = document.createElement('input');
input.type = 'file';
// input.multiple = multiple;
input.accept = contentType;
-
+
input.onchange = () => {
if (input.files == null) {
resolve(null);
return;
}
-
+
let files = Array.from(input.files);
-
+
// if (multiple)
// resolve(files);
// else
resolve(files[0]);
};
-
+
input.click();
});
}
-
+
readFile(file: File): Promise {
// Always return a Promise
return new Promise((resolve, reject) => {
@@ -472,161 +568,181 @@ class App {
reader.readAsText(file);
});
}
-
+
initButtons(userID: string, posts: Post[], registration: ServiceWorkerRegistration | undefined) {
- let font1Button = document.getElementById("button_font1") as HTMLButtonElement;
- let font2Button = document.getElementById("button_font2") as HTMLButtonElement;
+ // let font1Button = document.getElementById("button_font1") as HTMLButtonElement;
+ // let font2Button = document.getElementById("button_font2") as HTMLButtonElement;
let importTweetsButton = document.getElementById("import_tweets") as HTMLButtonElement;
let clearPostsButton = document.getElementById("clear_posts") as HTMLButtonElement;
let updateApp = document.getElementById("update_app") as HTMLButtonElement;
let ddlnLogoButton = document.getElementById('ddln-logo-button') as HTMLDivElement;
-
- font1Button.addEventListener('click', () => { this.setFont('Bookerly', '16px') });
- font2Button.addEventListener('click', () => { this.setFont('Virgil', '16px') });
+
+ let usernameField = document.getElementById('username');
+ usernameField?.addEventListener('input', (event:any)=>{
+ this.username = event.target.innerText;
+ localStorage.setItem("dandelion_username", this.username);
+ })
importTweetsButton.addEventListener('click', async () => {
let file = await this.selectFile('text/*');
-
+
console.log(file);
if (file == null) {
return;
}
-
+
let tweetData = await this.readFile(file);
tweetData = tweetData.replace('window.YTD.tweets.part0 = ', '');
const tweets = JSON.parse(tweetData);
-
+
let imported_posts = await this.importTweetArchive(userID, tweets);
clearData(userID);
// posts = posts.reverse();
addDataArray(userID, imported_posts);
posts = await this.loadPosts(userID) ?? [];
this.render(posts);
-
+
});
-
+
clearPostsButton.addEventListener('click', () => { clearData(userID); posts = []; this.render(posts) });
-
-
+
+
let postButton = document.getElementById("button_post") as HTMLButtonElement;
let postText = document.getElementById("textarea_post") as HTMLTextAreaElement;
-
+
if (!(postButton && postText)) {
throw new Error();
}
-
+
postButton.addEventListener("click", () => {
- this.addPost(userID, posts, postText.value);
+ this.addPost(userID, postText.value);
postText.value = "";
});
-
+
updateApp.addEventListener("click", () => {
registration?.active?.postMessage({ type: "update_app" });
});
-
+
let infoElement = document.getElementById('info');
-
+
if (infoElement === null) {
return;
}
-
- ddlnLogoButton.addEventListener('click', ()=>{infoElement.style.display=='none'? infoElement.style.display='block' : infoElement.style.display='none';});
+
+ ddlnLogoButton.addEventListener('click', () => { infoElement.style.display == 'none' ? infoElement.style.display = 'block' : infoElement.style.display = 'none'; });
}
-
+
async loadPosts(userID: string) {
-
+
this.timerStart();
let posts: any = await getData(userID, new Date(2022, 8), new Date());
-
+
if (posts.length > 0) {
log(`Loaded ${posts.length} posts in ${this.timerDelta().toFixed(2)}ms`);
return posts;
}
-
+
// posts = await createTestData2(userID);
-
+
// log("Adding test data...");
// addDataArray(userID, posts);
// return await getData(userID, new Date(2022, 8), new Date());
}
-
- async main() {
+
+ async main() {
let urlParams = (new URL(window.location.href)).searchParams;
let connection_userID = urlParams.get('connect');
let registration = undefined;
- if (urlParams.get("sw") === "true") {
+ // if (urlParams.get("sw") === "true") {
registration = await this.registerServiceWorker();
- }
+ // }
if (connection_userID) {
console.log('connect', connection_userID);
localStorage.setItem("dandelion_id", connection_userID);
}
- let posts: Post[] = [];
-
+ this.username = this.getUsername();
+ document.getElementById('username')!.innerText = this.username;
let userID = this.getUserID();
let peerID = this.getPeerID();
this.userID = userID;
this.peerID = peerID;
+ let connectURL = `https://${document.location.hostname}?connect=${this.userID}`;
+ document.getElementById('connectURL')!.innerHTML = `connect`;
+
+ let qrcode = await new QRCode(document.getElementById('qrcode'), {
+ text: connectURL,
+ width: 256,
+ height: 256,
+ colorDark: "#000000",
+ colorLight: "#ffffff",
+ correctLevel: QRCode.CorrectLevel.H
+ });
+
+
+ let qrcodeImage:HTMLImageElement = document.querySelector('#qrcode > img') as HTMLImageElement;
+ qrcodeImage.classList.add('qrcode_image');
+
log(`user:${userID} peer:${peerID}`);
let websocket = new wsConnection(userID, peerID);
window.addEventListener('beforeunload', () => { websocket.disconnect() })
this.initOffline(websocket);
- this.initButtons(userID, posts, registration);
-
+ this.initButtons(userID, this.posts, registration);
+
let time = 0;
let delta = 0;
- if (navigator.storage && navigator.storage.persist && !navigator.storage.persisted) {
- debugger;
- const isPersisted = await navigator.storage.persist();
- log(`Persisted storage granted: ${isPersisted}`);
- }
-
- log(`Persisted: ${(await navigator?.storage?.persisted())?.toString()}`);
-
+ // let isPersisted = await navigator?.storage?.persisted();
+ // if (!isPersisted) {
+ // debugger;
+ // const isPersisted = await navigator.storage.persist();
+ // log(`Persisted storage granted: ${isPersisted}`);
+ // }
+
+ // log(`Persisted: ${(await navigator?.storage?.persisted())?.toString()}`);
+
this.initMarkdown();
-
+
// let main = await fetch("/main.js");
// let code = await main.text();
// console.log(code);
// registration.active.postMessage({type:"updateMain", code:code});
-
- posts = await this.loadPosts(userID) ?? [];
-
+
+ this.posts = await this.loadPosts(userID) ?? [];
+
// debugger;
-
+
this.timerStart();
- this.render(posts); // , (postID:string)=>{this.deletePost(userID, postID)}
+ this.render(this.posts); // , (postID:string)=>{this.deletePost(userID, postID)}
let renderTime = this.timerDelta();
-
+
log(`render took: ${renderTime.toFixed(2)}ms`);
-
+
if ((performance as any)?.memory) {
log(`memory used: ${((performance as any).memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}Mb`)
}
-
-
+
+
// const client = new WebTorrent()
-
+
// // Sintel, a free, Creative Commons movie
// const torrentId = 'magnet:?xt=urn:btih:6091e199a8d9272a40dd9a25a621a5c355d6b0be&dn=WING+IT!+-+Blender+Open+Movie+1080p.mp4&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337';
-
+
// client.add(torrentId, function (torrent: any) {
// // Torrents can contain many files. Let's use the .mp4 file
// const file = torrent.files.find(function (file: any) {
// return file.name.endsWith('.mp4')
// })
-
+
// // Display the file by adding it to the DOM.
// // Supports video, audio, image files, and more!
// file.appendTo(document.getElementById('torrent-content'));
// })
}
-
- render(posts: Post[]) {
+
+ render(posts: Post[]) {
const fragment = document.createDocumentFragment();
let contentDiv = document.getElementById("content");
if (!contentDiv) {
@@ -634,15 +750,12 @@ class App {
}
contentDiv.innerHTML = "";
let count = 0;
-
-
- new QRCode(document.getElementById('qrcode'), `https://ddlion.net/?connect=${this.userID}`);
for (let i = posts.length - 1; i >= 0; i--) {
let postData = posts[i];
-
+
let post = this.renderPost(postData, posts);
-
+
if (post) {
fragment.appendChild(post);
count++;
@@ -651,65 +764,72 @@ class App {
break;
}
}
-
-
+
+
if (!contentDiv) {
throw new Error("Couldn't get content div!");
}
-
+
contentDiv.appendChild(fragment);
-
+
}
-
- deletePost(userID:string, postID:string) {
+
+ async deletePost(userID: string, postID: string) {
deleteData(userID, postID)
+ this.posts = await this.loadPosts(userID) ?? [];
+ this.render(this.posts);
}
-
- renderPost(post: Post, posts:Post[]) {
+
+ renderPost(post: Post, posts: Post[]) {
if (!(post.hasOwnProperty("text"))) {
throw new Error("Post is malformed!");
}
let containerDiv = document.createElement("div");
-
+
let timestamp = `${post.post_timestamp.toLocaleTimeString()} · ${post.post_timestamp.toLocaleDateString()}`;
-
- let deleteButton = document.createElement('button');
- deleteButton.innerText = 'delete';
- // deleteButton.onclick = ()=>{deletefunc(post.post_id)};
-
+
+ let deleteButton = document.createElement('button'); deleteButton.innerText = 'delete';
+ let editButton = document.createElement('button'); editButton.innerText = 'edit';
+ deleteButton.onclick = () => { this.deletePost(this.userID, post.post_id) };
+
let postTemplate =
- `
-
+ `
+
+
+
${marked.parse(post.text)}
`
-
+
containerDiv.innerHTML = postTemplate;
-
- containerDiv.appendChild(deleteButton);
-
+
+ containerDiv.querySelector('#deleteButton')?.appendChild(deleteButton);
+ containerDiv.querySelector('#editButton')?.appendChild(editButton);
+
// if (!("image_data" in post && post.image_data)) {
// containerDiv.appendChild(timestampDiv);
// return containerDiv;
// // return null;
// }
-
+
// let image = document.createElement("img");
// const blob = new Blob([post.image_data as ArrayBuffer], { type: 'image/jpg' });
// const url = URL.createObjectURL(blob);
// image.onload = () => {
// URL.revokeObjectURL(url);
// };
-
-
-
-
+
+
+
+
// image.src = url;
// // image.src = image.src = "data:image/png;base64," + post.image;
// image.className = "postImage";
-
+
// containerDiv.appendChild(image);
// containerDiv.appendChild(timestampDiv);
-
+
return containerDiv;
}
diff --git a/src/sw.ts b/src/sw.ts
index e46cb35..55d600d 100644
--- a/src/sw.ts
+++ b/src/sw.ts
@@ -2,16 +2,16 @@
const cacheName = "dandelion_cache_v1";
const contentToCache = [
-"/index.html",
-"/main.js",
-"/marked.min.js",
-"/db.js",
-"/bookerly.woff2",
-"/virgil.woff2",
-"/favicon.ico"
+ '/index.html',
+ '/main.css',
+ '/main.js',
+ 'lib//marked.min.js',
+ 'lib/qrcode.min.js',
+ '/db.js',
+ '/favicon.ico'
];
-self.addEventListener("install", (e:any) => {
+self.addEventListener("install", (e: any) => {
e.waitUntil(
(async () => {
const cache = await caches.open(cacheName);
@@ -24,49 +24,53 @@ self.addEventListener("install", (e:any) => {
);
});
-self.addEventListener("fetch", (e:any) => {
- e.respondWith(
- (async () => {
- const r = await caches.match(e.request);
- if (r) {
- console.log(
- `[Service Worker] Cache hit for resource: ${e.request.url}`
- );
- return r;
- }
+async function responder(event: any) {
+ console.log('Fetching', event.request.url);
- let response;
- try {
- console.log(
- `[Service Worker] Cache miss, attempting to fetch resource: ${e.request.url}`
- );
+ let response = await fetch(event.request);
- response = await fetch(e.request);
- } catch (e) {
- console.warn(e);
- }
- const cache = await caches.open(cacheName);
- console.log(
- `[Service Worker] Adding resource to cache: ${e.request.url}`
- );
+ if (!response) {
+ console.log('Fetch failed, falling back to cache', event.request.url);
+ let cacheMatch = await caches.match(event.request);
+ if (!cacheMatch) {
+ // DUnno what to return here!
+ }
+ return cacheMatch;
+ }
- if (!response) {
- throw new Error(`Failed to fetch resource: ${e.request.url}`)
- }
- cache.put(e.request, response.clone());
- return response;
- })()
- );
+ if (response.status === 206) {
+ console.log('Not caching partial content');
+ return response;
+ }
+
+ console.log('Fetch successful, updating cache');
+ const cache = await caches.open(cacheName);
+ try {
+ cache.put(event.request, response.clone()).catch((error)=>console.log('failed to cache', event.request, error));
+ } catch (e) {
+ console.log('failed to cache', event.request)
+ }
+ return response;
+}
+
+self.addEventListener('fetch', function (event: any) {
+ event.respondWith(responder(event));
});
addEventListener("message", async (e) => {
- console.log(`Message received: ${e.data}`);
+ console.log(`Message received:`, e.data);
switch (e.data.type) {
- case "updateMain":
+ case "update_app":
const cache = await caches.open(cacheName);
- console.log(`[Service Worker] Caching new resource: main.js`);
- cache.put("/main.js", new Response());
+ console.log(`[Service Worker] Caching resources`);
+ // cache.put("/main.js", new Response());
+
+ for (let item of contentToCache) {
+ cache.delete(item);
+ }
+
+ await cache.addAll(contentToCache);
break;
}
});
diff --git a/src/webRTC.ts b/src/webRTC.ts
index 06c4926..fa82714 100644
--- a/src/webRTC.ts
+++ b/src/webRTC.ts
@@ -1,83 +1,112 @@
-const config = {
- iceServers: [{ urls: "stun: stun.l.google.com" }],
-};
+class PeerManager {
+ connect(peerID:string) {
+ // Connect to the peer that has the peer id peerID
+ }
-let localConnection = new RTCPeerConnection();
-
-function handleSendChannelStatusChange() {
- console.log(handleSendChannelStatusChange);
+ disconnect(peerID:string) {
+ }
}
-let sendChannel = localConnection.createDataChannel("sendChannel");
-sendChannel.onopen = handleSendChannelStatusChange;
-sendChannel.onclose = handleSendChannelStatusChange;
-let remoteConnection = new RTCPeerConnection();
-remoteConnection.ondatachannel = receiveChannelCallback;
-
-
-localConnection.onicecandidate = (e:any) =>
- !e.candidate ||
- remoteConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
-
-remoteConnection.onicecandidate = (e) =>
- !e.candidate ||
- localConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
+class PeerConnection {
+ static config = {
+ iceServers: [
+ { urls: "stun:stun.l.google.com" },
+ { urls: "stun:stun1.l.google.com" },
+ { urls: "stun:stun2.l.google.com" },
+ { urls: "stun:stun3.l.google.com" },
+ { urls: "stun:stun4.l.google.com" },
+ ],};
+}
-function handleCreateDescriptionError(error:any) {
- console.log(`Unable to create an offer: ${error.toString()}`);
- }
-
- function handleLocalAddCandidateSuccess() {
- console.log('handleLocalAddCandidateSuccess');
- }
-
- function handleRemoteAddCandidateSuccess() {
- console.log('handleRemoteAddCandidateSuccess');
- }
-
- function handleAddCandidateError() {
- console.log("Oh noes! addICECandidate failed!");
- }
-localConnection
- .createOffer()
- .then((offer) => localConnection.setLocalDescription(offer))
- .then(() =>
- remoteConnection.setRemoteDescription(localConnection.localDescription as RTCSessionDescriptionInit),
- )
- .then(() => remoteConnection.createAnswer())
- .then((answer) => remoteConnection.setLocalDescription(answer))
- .then(() =>
- localConnection.setRemoteDescription(remoteConnection.localDescription as RTCSessionDescriptionInit),
- )
- .catch(handleCreateDescriptionError);
+const config = {
+ iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
+};
- function handleReceiveChannelStatusChange(event:any) {
- let receiveChannel = event.channel;
+let polite = true;
- if (receiveChannel) {
- console.log(
- `Receive channel's status has changed to ${receiveChannel.readyState}`,
- );
+// const signaler = new SignalingChannel();
+const signaler:any = {}
+const pc = new RTCPeerConnection(config);
+
+
+const constraints = { audio: true, video: true };
+const selfVideo = document.querySelector("video.selfview");
+const remoteVideo = document.querySelector("video.remoteview");
+
+async function start() {
+ try {
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
+
+ for (const track of stream.getTracks()) {
+ pc.addTrack(track, stream);
}
+ // selfVideo.srcObject = stream;
+ } catch (err) {
+ console.error(err);
}
+}
- function receiveChannelCallback(event:any) {
- let receiveChannel = event.channel;
- receiveChannel.onmessage = handleReceiveMessage;
- receiveChannel.onopen = handleReceiveChannelStatusChange;
- receiveChannel.onclose = handleReceiveChannelStatusChange;
+
+pc.ontrack = ({ track, streams }) => {
+ track.onunmute = () => {
+ // if (remoteVideo.srcObject) {
+ // return;
+ // }
+ // remoteVideo.srcObject = streams[0];
+ };
+};
+
+let makingOffer = false;
+
+pc.onnegotiationneeded = async () => {
+ try {
+ makingOffer = true;
+ await pc.setLocalDescription();
+ signaler.send({ description: pc.localDescription });
+ } catch (err) {
+ console.error(err);
+ } finally {
+ makingOffer = false;
}
+};
- function sendMessage(message:string) {
- sendChannel.send(message);
+pc.onicecandidate = ({ candidate }) => signaler.send({ candidate });
+
+let ignoreOffer = false;
+
+signaler.onmessage = async ({ data: { description, candidate } }: MessageEvent) => {
+ try {
+ if (description) {
+ const offerCollision =
+ description.type === "offer" &&
+ (makingOffer || pc.signalingState !== "stable");
+
+ ignoreOffer = !polite && offerCollision;
+ if (ignoreOffer) {
+ return;
+ }
+
+ await pc.setRemoteDescription(description);
+ if (description.type === "offer") {
+ await pc.setLocalDescription();
+ signaler.send({ description: pc.localDescription });
+ }
+ } else if (candidate) {
+ try {
+ await pc.addIceCandidate(candidate);
+ } catch (err) {
+ if (!ignoreOffer) {
+ throw err;
+ }
+ }
+ }
+ } catch (err) {
+ console.error(err);
}
-
- function handleReceiveMessage(event:any) {
- console.log(event.data);
- }
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/sw.js b/sw.js
index f8399e1..c98eccc 100644
--- a/sw.js
+++ b/sw.js
@@ -2,13 +2,13 @@
// Establish a cache name
const cacheName = "dandelion_cache_v1";
const contentToCache = [
- "/index.html",
- "/main.js",
- "/marked.min.js",
- "/db.js",
- "/bookerly.woff2",
- "/virgil.woff2",
- "/favicon.ico"
+ '/index.html',
+ '/main.css',
+ '/main.js',
+ 'lib//marked.min.js',
+ 'lib/qrcode.min.js',
+ '/db.js',
+ '/favicon.ico'
];
self.addEventListener("install", (e) => {
e.waitUntil((async () => {
@@ -17,37 +17,45 @@ self.addEventListener("install", (e) => {
await cache.addAll(contentToCache);
})());
});
-self.addEventListener("fetch", (e) => {
- e.respondWith((async () => {
- const r = await caches.match(e.request);
- if (r) {
- console.log(`[Service Worker] Cache hit for resource: ${e.request.url}`);
- return r;
+async function responder(event) {
+ console.log('Fetching', event.request.url);
+ let response = await fetch(event.request);
+ if (!response) {
+ console.log('Fetch failed, falling back to cache', event.request.url);
+ let cacheMatch = await caches.match(event.request);
+ if (!cacheMatch) {
+ // DUnno what to return here!
}
- let response;
- try {
- console.log(`[Service Worker] Cache miss, attempting to fetch resource: ${e.request.url}`);
- response = await fetch(e.request);
- }
- catch (e) {
- console.warn(e);
- }
- const cache = await caches.open(cacheName);
- console.log(`[Service Worker] Adding resource to cache: ${e.request.url}`);
- if (!response) {
- throw new Error(`Failed to fetch resource: ${e.request.url}`);
- }
- cache.put(e.request, response.clone());
+ return cacheMatch;
+ }
+ if (response.status === 206) {
+ console.log('Not caching partial content');
return response;
- })());
+ }
+ console.log('Fetch successful, updating cache');
+ const cache = await caches.open(cacheName);
+ try {
+ cache.put(event.request, response.clone()).catch((error) => console.log('failed to cache', event.request, error));
+ }
+ catch (e) {
+ console.log('failed to cache', event.request);
+ }
+ return response;
+}
+self.addEventListener('fetch', function (event) {
+ event.respondWith(responder(event));
});
addEventListener("message", async (e) => {
- console.log(`Message received: ${e.data}`);
+ console.log(`Message received:`, e.data);
switch (e.data.type) {
- case "updateMain":
+ case "update_app":
const cache = await caches.open(cacheName);
- console.log(`[Service Worker] Caching new resource: main.js`);
- cache.put("/main.js", new Response());
+ console.log(`[Service Worker] Caching resources`);
+ // cache.put("/main.js", new Response());
+ for (let item of contentToCache) {
+ cache.delete(item);
+ }
+ await cache.addAll(contentToCache);
break;
}
});
diff --git a/sw.js.map b/sw.js.map
index 1700057..4752727 100644
--- a/sw.js.map
+++ b/sw.js.map
@@ -1 +1 @@
-{"version":3,"file":"sw.js","sourceRoot":"","sources":["src/sw.ts"],"names":[],"mappings":";AAAA,yBAAyB;AACzB,MAAM,SAAS,GAAG,oBAAoB,CAAC;AAEvC,MAAM,cAAc,GAAG;IACvB,aAAa;IACb,UAAU;IACV,gBAAgB;IAChB,QAAQ;IACR,iBAAiB;IACjB,eAAe;IACf,cAAc;CACb,CAAC;AAEF,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAK,EAAE,EAAE;IACzC,CAAC,CAAC,SAAS,CACT,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CACT,qDAAqD,EACrD,cAAc,CACf,CAAC;QACF,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACrC,CAAC,CAAC,EAAE,CACL,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAK,EAAE,EAAE;IACvC,CAAC,CAAC,WAAW,CACX,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC;YACN,OAAO,CAAC,GAAG,CACT,4CAA4C,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAC5D,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC;QAED,IAAI,QAAQ,CAAC;QACb,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CACT,8DAA8D,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAC9E,CAAC;YAEF,QAAQ,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CACT,8CAA8C,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAC9D,CAAC;QAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;QAC/D,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;QACvC,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC,EAAE,CACL,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,gBAAgB,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACtC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAE3C,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,YAAY;YACf,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;YAC9D,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,QAAQ,EAAE,CAAC,CAAC;YACtC,MAAM;IACV,CAAC;AACH,CAAC,CAAC,CAAC"}
\ No newline at end of file
+{"version":3,"file":"sw.js","sourceRoot":"","sources":["src/sw.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/webRTC.js b/webRTC.js
index 35a03e6..02af802 100644
--- a/webRTC.js
+++ b/webRTC.js
@@ -1,56 +1,96 @@
"use strict";
-const config = {
- iceServers: [{ urls: "stun: stun.l.google.com" }],
-};
-let localConnection = new RTCPeerConnection();
-function handleSendChannelStatusChange() {
- console.log(handleSendChannelStatusChange);
-}
-let sendChannel = localConnection.createDataChannel("sendChannel");
-sendChannel.onopen = handleSendChannelStatusChange;
-sendChannel.onclose = handleSendChannelStatusChange;
-let remoteConnection = new RTCPeerConnection();
-remoteConnection.ondatachannel = receiveChannelCallback;
-localConnection.onicecandidate = (e) => !e.candidate ||
- remoteConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
-remoteConnection.onicecandidate = (e) => !e.candidate ||
- localConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
-function handleCreateDescriptionError(error) {
- console.log(`Unable to create an offer: ${error.toString()}`);
-}
-function handleLocalAddCandidateSuccess() {
- console.log('handleLocalAddCandidateSuccess');
-}
-function handleRemoteAddCandidateSuccess() {
- console.log('handleRemoteAddCandidateSuccess');
-}
-function handleAddCandidateError() {
- console.log("Oh noes! addICECandidate failed!");
-}
-localConnection
- .createOffer()
- .then((offer) => localConnection.setLocalDescription(offer))
- .then(() => remoteConnection.setRemoteDescription(localConnection.localDescription))
- .then(() => remoteConnection.createAnswer())
- .then((answer) => remoteConnection.setLocalDescription(answer))
- .then(() => localConnection.setRemoteDescription(remoteConnection.localDescription))
- .catch(handleCreateDescriptionError);
-function handleReceiveChannelStatusChange(event) {
- let receiveChannel = event.channel;
- if (receiveChannel) {
- console.log(`Receive channel's status has changed to ${receiveChannel.readyState}`);
+class PeerManager {
+ connect(peerID) {
+ // Connect to the peer that has the peer id peerID
+ }
+ disconnect(peerID) {
}
}
-function receiveChannelCallback(event) {
- let receiveChannel = event.channel;
- receiveChannel.onmessage = handleReceiveMessage;
- receiveChannel.onopen = handleReceiveChannelStatusChange;
- receiveChannel.onclose = handleReceiveChannelStatusChange;
+class PeerConnection {
}
-function sendMessage(message) {
- sendChannel.send(message);
-}
-function handleReceiveMessage(event) {
- console.log(event.data);
+PeerConnection.config = {
+ iceServers: [
+ { urls: "stun:stun.l.google.com" },
+ { urls: "stun:stun1.l.google.com" },
+ { urls: "stun:stun2.l.google.com" },
+ { urls: "stun:stun3.l.google.com" },
+ { urls: "stun:stun4.l.google.com" },
+ ],
+};
+const config = {
+ iceServers: [{ urls: "stun:stun.mystunserver.tld" }],
+};
+let polite = true;
+// const signaler = new SignalingChannel();
+const signaler = {};
+const pc = new RTCPeerConnection(config);
+const constraints = { audio: true, video: true };
+const selfVideo = document.querySelector("video.selfview");
+const remoteVideo = document.querySelector("video.remoteview");
+async function start() {
+ try {
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
+ for (const track of stream.getTracks()) {
+ pc.addTrack(track, stream);
+ }
+ // selfVideo.srcObject = stream;
+ }
+ catch (err) {
+ console.error(err);
+ }
}
+pc.ontrack = ({ track, streams }) => {
+ track.onunmute = () => {
+ // if (remoteVideo.srcObject) {
+ // return;
+ // }
+ // remoteVideo.srcObject = streams[0];
+ };
+};
+let makingOffer = false;
+pc.onnegotiationneeded = async () => {
+ try {
+ makingOffer = true;
+ await pc.setLocalDescription();
+ signaler.send({ description: pc.localDescription });
+ }
+ catch (err) {
+ console.error(err);
+ }
+ finally {
+ makingOffer = false;
+ }
+};
+pc.onicecandidate = ({ candidate }) => signaler.send({ candidate });
+let ignoreOffer = false;
+signaler.onmessage = async ({ data: { description, candidate } }) => {
+ try {
+ if (description) {
+ const offerCollision = description.type === "offer" &&
+ (makingOffer || pc.signalingState !== "stable");
+ ignoreOffer = !polite && offerCollision;
+ if (ignoreOffer) {
+ return;
+ }
+ await pc.setRemoteDescription(description);
+ if (description.type === "offer") {
+ await pc.setLocalDescription();
+ signaler.send({ description: pc.localDescription });
+ }
+ }
+ else if (candidate) {
+ try {
+ await pc.addIceCandidate(candidate);
+ }
+ catch (err) {
+ if (!ignoreOffer) {
+ throw err;
+ }
+ }
+ }
+ }
+ catch (err) {
+ console.error(err);
+ }
+};
//# sourceMappingURL=webRTC.js.map
\ No newline at end of file
diff --git a/webRTC.js.map b/webRTC.js.map
index 424d211..4d2dfc9 100644
--- a/webRTC.js.map
+++ b/webRTC.js.map
@@ -1 +1 @@
-{"version":3,"file":"webRTC.js","sourceRoot":"","sources":["src/webRTC.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"webRTC.js","sourceRoot":"","sources":["src/webRTC.ts"],"names":[],"mappings":"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"}
\ No newline at end of file