Working chuking and image syncing. Still some bugs with syncing some large posts.

This commit is contained in:
2025-05-21 11:39:02 -07:00
parent 9df18d7f35
commit a3a9682f86
5 changed files with 396 additions and 1254 deletions

View File

@@ -26,67 +26,11 @@ Restruucture the app around the data. App/WS split is messy. Clean it up.
*/
// import * as ForceGraph3D from "3d-force-graph";
import { openDatabase, getData, addData, deleteData, mergeDataArray, getAllData, checkPostIds, getAllIds, getPostsByIds } from "db";
import { openDatabase, getData, addData, deleteData, getAllData } from "db";
import { generateID } from "IDUtils";
import { PeerManager, PeerEventTypes } from "PeerManager";
import { Sync } from "Sync";
import { log, logID, renderLog, setLogVisibility } from "log";
// let posts:any;
// let keyBase = "dandelion_posts_v1_"
// let key:string = "";
// interface PostTimestamp {
// year: number,
// month: number,
// day: number,
// hour: number,
// minute: number,
// second: number,
// }
function waitMs(durationMs) {
return new Promise(resolve => setTimeout(resolve, durationMs));
}
function uuidToBytes(uuid) {
return new Uint8Array(uuid.match(/[a-fA-F0-9]{2}/g).map((hex) => parseInt(hex, 16)));
}
// Base58 character set
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
// Base58 encoding
// Base58 encoding
function encodeBase58(buffer) {
let carry;
const digits = [0];
for (const byte of buffer) {
carry = byte;
for (let i = 0; i < digits.length; i++) {
carry += digits[i] << 8;
digits[i] = carry % 58;
carry = Math.floor(carry / 58);
}
while (carry > 0) {
digits.push(carry % 58);
carry = Math.floor(carry / 58);
}
}
let result = '';
for (const digit of digits.reverse()) {
result += BASE58_ALPHABET[digit];
}
// Handle leading zero bytes
for (const byte of buffer) {
if (byte === 0x00) {
result = BASE58_ALPHABET[0] + result;
}
else {
break;
}
}
return result;
}
// Convert UUID v4 to Base58
function uuidToBase58(uuid) {
const bytes = uuidToBytes(uuid);
return encodeBase58(bytes);
}
class Post {
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null) {
this.post_timestamp = post_timestamp;
@@ -135,11 +79,11 @@ async function arrayBufferToBase64(buffer) {
var bytes = new Uint8Array(buffer);
return (await bytesToBase64DataUrl(bytes)).replace("data:application/octet-stream;base64,", "");
}
async function base64ToArrayBuffer(base64String) {
let response = await fetch("data:application/octet-stream;base64," + base64String);
let arrayBuffer = await response.arrayBuffer();
return arrayBuffer;
}
// async function base64ToArrayBuffer(base64String: string) {
// let response = await fetch("data:application/octet-stream;base64," + base64String);
// let arrayBuffer = await response.arrayBuffer();
// return arrayBuffer;
// }
async function compressString(input) {
// Convert the string to a Uint8Array
const textEncoder = new TextEncoder();
@@ -155,387 +99,6 @@ async function compressString(input) {
// Convert the compressed data to a Uint8Array
return new Uint8Array(compressedArray);
}
// Connect websocket
// send hello
// get bootstrap peer ID
// WebRTC connect to bootstrap peer
// Bootstrap peer will send the last N peers it saw.
// Connect to those new peers, tell those peers about users we know about
// ask for peers that have users we care about
// WebRTC Connect to peers that might have posts we need
// query those peers and do existing logic.
class wsConnection {
constructor(userID, peerID, IDsToSync, rtcPeerDescription) {
this.websocket = null;
this.sessionID = "";
this.userID = "";
this.peerID = "";
this.websocketPingInterval = 0;
this.helloRefreshInterval = 0;
this.retry = 10;
this.state = 'disconnected';
// peers: Map<string, string[]> = new Map();
this.messageHandlers = new Map();
this.peerMessageHandlers = new Map();
this.seenPeers = new Map();
// static async compressArrayBuffer(data: ArrayBuffer): Promise<ArrayBuffer> {
// const compressionStream = new CompressionStream('gzip'); // You can also use 'deflate', 'deflate-raw', etc.
// const compressedStream = new Response(
// new Blob([data]).stream().pipeThrough(compressionStream)
// );
// const compressedArrayBuffer = await compressedStream.arrayBuffer();
// return compressedArrayBuffer;
// }
this.postBlockList = new Set([
'1c71f53c-c467-48e4-bc8c-39005b37c0d5',
'64203497-f77b-40d6-9e76-34d17372e72a',
'243130d8-4a41-471e-8898-5075f1bd7aec',
'e01eff89-5100-4b35-af4c-1c1bcb007dd0',
'194696a2-d850-4bb0-98f7-47416b3d1662',
'f6b21eb1-a0ff-435b-8efc-6a3dd70c0dca',
'dd1d92aa-aa24-4166-a925-94ba072a9048'
]);
this.userBlockList = new Set([
'5d63f0b2-a842-41bf-bf06-e0e4f6369271',
'5f1b85c4-b14c-454c-8df1-2cacc93f8a77',
// 'bba3ad24-9181-4e22-90c8-c265c80873ea'
]);
// this.rtcPeerDescription = rtcPeerDescription;
this.sessionID = generateID();
this.userID = userID;
this.peerID = peerID;
this.UserIDsToSync = new Set(IDsToSync);
this.messageHandlers.set('hello', this.helloResponseHandler.bind(this));
this.messageHandlers.set('hello2', this.hello2ResponseHandler.bind(this));
this.messageHandlers.set('pong', this.pongHandler);
this.messageHandlers.set('peer_message', this.peerMessageHandler.bind(this));
//
this.peerMessageHandlers.set('get_post_ids_for_user', this.getPostIdsForUserHandler.bind(this));
this.peerMessageHandlers.set('get_post_ids_for_user_response', this.getPostIdsForUserResponseHandler.bind(this));
this.peerMessageHandlers.set('get_posts_for_user', this.getPostsForUserHandler.bind(this));
this.peerMessageHandlers.set('get_posts_for_user_response', this.getPostsForUserReponseHandler.bind(this));
// this.peerMessageHandlers.set('send_webrtc_offer', this.sendWebRTCOfferHandler.bind(this));
// this.peerMessageHandlers.set('send_webrtc_offer_response', this.getPostIdsForUserResponseHandler.bind(this));
window.addEventListener('beforeunload', () => this.disconnect());
this.connect();
}
// So we don't need custom logic everywhere we use this, I just wrapped it.
shouldSyncUserID(userID) {
if (app.isHeadless) {
return true;
}
return this.UserIDsToSync.has(userID);
}
async send(message) {
let json = "";
try {
json = JSON.stringify(message);
// console.log.apply(null, log("*******", (await compressString(json)).byteLength, json.length);
}
catch (e) {
console.log.apply(null, log(e, "wsConnection send: Couldn't serialize message", message));
}
// log(`ws->${json.slice(0, 240)}`)
this.websocket.send(json);
}
pongHandler(data) {
}
async sendWebRTCDescription(description) {
console.log.apply(null, log("description:", description));
this.send({ type: "rtc_session_description", description: description });
}
async getPostIdsForUserResponseHandler(data) {
// log(`getPostsForUserResponse: ${data}`)
let message = data.message;
console.log.apply(null, log(`Net: got ${message.post_ids.length} post IDs for user ${logID(message.user_id)} from peer ${logID(data.from)}`));
;
let startTime = app.timerStart();
let postIds = await checkPostIds(message.user_id, message.post_ids);
console.log.apply(null, log(`ID Check for user ${logID(message.user_id)} took ${app.timerDelta().toFixed(2)}ms`));
;
console.log.apply(null, log(`Need ${postIds.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`));
;
if (postIds.length === 0) {
return;
}
console.log.apply(null, log(`Net: Req ${postIds.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`));
let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_posts_for_user", post_ids: postIds, user_id: message.user_id } };
this.send(responseMessage);
}
async getPostIdsForUserHandler(data) {
let message = data.message;
let postIds = await getAllIds(message.user_id) ?? [];
postIds = postIds.filter((postID) => !this.postBlockList.has(postID));
if (postIds.length === 0) {
console.log.apply(null, log(`Net: I know about user ${logID(message.user_id)} but I have 0 posts, so I'm not sending any to to peer ${logID(data.from)}`));
;
return;
}
console.log.apply(null, log(`Net: Sending ${postIds.length} post Ids for user ${logID(message.user_id)} to peer ${logID(data.from)}`));
let responseMessage = { type: "peer_message", from: app.peerID, to: data.from, from_username: app.username, from_peername: app.peername, message: { type: "get_post_ids_for_user_response", post_ids: postIds, user_id: message.user_id } };
this.send(responseMessage);
}
async broadcastNewPost(userID, post) {
let newPost = { ...post };
if (post.image_data) {
newPost.image_data = await arrayBufferToBase64(post.image_data);
}
for (let [peerID, peerInfo] of this.seenPeers.entries()) {
console.log.apply(null, log(`broadcastNewPost: sending new post to ${logID(peerID)}:${peerInfo.peerName}:${peerInfo.userName}`));
;
this.sendPostsForUser(peerID, app.userID, [newPost]);
}
}
async sendPostsForUser(toPeerID, userID, posts) {
let responseMessage = {
type: "peer_message",
from: app.peerID,
to: toPeerID,
from_username: app.username,
from_peername: app.peername,
message: {
type: "get_posts_for_user_response",
posts: posts,
user_id: userID
}
};
return this.send(responseMessage);
}
// Send posts to peer
async getPostsForUserHandler(data) {
let message = data.message;
let posts = await getPostsByIds(message.user_id, message.post_ids) ?? [];
console.log.apply(null, log(`Net: Sending ${posts.length} posts for user ${logID(message.user_id)} to peer ${logID(data.from)}`));
;
app.timerStart();
let output = [];
console.log.apply(null, log("Serializing images"));
for (let post of posts) {
let newPost = post.data;
if (newPost.image_data) {
// let compressedData = await wsConnection.compressArrayBuffer(newPost.image_data);
// console.log.apply(null, log((newPost.image_data.byteLength - compressedData.byteLength) / 1024 / 1024);
// TODO don't do this, use Blobs direclty!
// https://developer.chrome.com/blog/blob-support-for-Indexeddb-landed-on-chrome-dev
newPost.image_data = await arrayBufferToBase64(newPost.image_data);
}
// let megs = JSON.stringify(newPost).length/1024/1024;
// console.log.apply(null, log(`getPostsForUserHandler id:${newPost.post_id} post length:${megs}`);
output.push(newPost);
}
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 } };
console.log.apply(null, log("Sending posts"));
await this.sendPostsForUser(data.from, message.user_id, output);
let sendTime = app.timerDelta();
console.log.apply(null, log(`getPostsForUserHandler send took: ${sendTime.toFixed(2)}ms`));
;
}
// Got posts from peer
async getPostsForUserReponseHandler(data) {
app.timerStart();
let message = data.message;
console.log.apply(null, log(`Net: got ${message.posts.length} posts for user ${logID(message.user_id)} from peer ${logID(data.from)}`));
for (let post of message.posts) {
// HACK: Some posts have insanely large images, so I'm gonna skip them.
// Once we support delete then we we could delete these posts in a sensible way.
if (this.postBlockList.has(post.post_id)) {
console.log.apply(null, log(`Skipping blocked post: ${post.post_id}`));
;
continue;
}
// HACK - some posts had the wrong author ID
if (message.user_id === app.userID) {
post.author_id = app.userID;
}
post.post_timestamp = new Date(post.post_timestamp);
if (post.image_data) {
post.image_data = await base64ToArrayBuffer(post.image_data);
}
}
console.log.apply(null, log(`Merging same user peer posts...`));
await mergeDataArray(message.user_id, data.message.posts);
let receiveTime = app.timerDelta();
console.log.apply(null, log(`getPostsForUserReponseHandler receive took: ${receiveTime.toFixed(2)}ms`));
;
if (message.user_id === app.getPreferentialUserID() || app.following.has(message.user_id)) {
app.render();
}
}
async peerMessageHandler(data) {
// log(`peerMessageHandler ${JSON.stringify(data)}`)
this.seenPeers.set(data.from, { peerName: data.from_peername, userName: data.from_username });
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);
}
// Hello2
// Goal, connect to bootstrap peer, ask bootstrap peer for peers that have posts from users that we care about. get peers, connect to those peers, sync.
// how? do "perfect negotiation" with bootstrap peer. All logic here moves to BP.
async sendHello2() {
this.send({
type: "hello2",
user_id: this.userID,
user_name: app.username,
peer_id: this.peerID,
session_id: this.sessionID,
peer_name: app.peername,
is_bootstrap_peer: app.isBootstrapPeer,
// peer_description: this.rtcPeerDescription
});
}
async getKnownUsers() {
let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
knownUsers = knownUsers
.filter(userID => this.shouldSyncUserID(userID))
.filter(userID => !this.userBlockList.has(userID))
.filter(async (userID) => (await getAllIds(userID)).length > 0); // TODO:EASYOPT getting all the IDs is unecessary, replace it with a test to get a single ID.
}
async sendHello() {
// TODO only get users you're following here. ✅
let knownUsers = [...(await indexedDB.databases())].map(db => db.name?.replace('user_', '')).filter(userID => userID !== undefined);
knownUsers = knownUsers
.filter(userID => this.shouldSyncUserID(userID))
.filter(userID => !this.userBlockList.has(userID))
.filter(async (userID) => (await getAllIds(userID)).length > 0); // TODO:EASYOPT getting all the IDs is unecessary, replace it with a test to get a single ID.
console.log.apply(null, log('Net: Sending known users', knownUsers.map(userID => logID(userID ?? ""))));
return await this.send({ type: "hello", user_id: this.userID, user_name: app.username, peer_id: this.peerID, peer_name: app.peername, known_users: knownUsers });
}
hello2ResponseHandler(data) {
}
helloResponseHandler(data) {
let users = [];
let receivedUsers = Object.entries(data.userPeers);
console.log.apply(null, log(`Net: got ${receivedUsers.length} users from bootstrap peer.`));
try {
let preferentialUserID = app.getPreferentialUserID();
let currentUserPeers = data.userPeers[preferentialUserID];
users.push([preferentialUserID, currentUserPeers]);
delete data.userPeers[preferentialUserID];
}
catch (e) {
console.log.apply(null, log('helloResponseHandler', e));
}
let getAllUsers = app.router.route !== App.Route.USER;
if (getAllUsers) {
users = [...users, ...Object.entries(data.userPeers).filter(userID => this.shouldSyncUserID(userID[0]))];
}
// log(`Net: got ${users.length} users from bootstrap peer. \n${users.map((user)=>user[0]).join('\n')}`)
for (let [userID, peerIDs] of users) {
if (this.userBlockList.has(userID)) {
console.log.apply(null, log("Skipping user on blocklist:", userID));
continue;
}
// this.peers.set(userID, [...peerIDs]);
for (let peerID of [...peerIDs]) {
if (peerID === this.peerID) {
continue;
}
console.log.apply(null, 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 }
});
}
}
}
connect() {
if (this.websocket?.readyState === WebSocket.OPEN) {
return;
}
window.clearInterval(this.websocketPingInterval);
if (this.websocket) {
this.websocket.close();
}
;
try {
this.websocket = new WebSocket(`wss://${window.location.hostname}:${window.location.port}/ws`);
}
catch (error) {
console.log.apply(null, log(error.message));
return;
}
this.websocket.onopen = async (event) => {
console.log.apply(null, log("ws:connected"));
;
await this.sendHello2();
// If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
// let helloRefreshIntervalPeriod = 120;
// if (app.isHeadless) {
// console.log.apply(null, log("wsConnection: Setting hello refresh interval to ", helloRefreshIntervalPeriod)
// this.helloRefreshInterval = window.setInterval(() => {
// console.log.apply(null, log("wsConnection: Hello refresh.")
// if (!navigator.onLine) {
// return;
// }
// this.sendHello();
// }, helloRefreshIntervalPeriod * 1000);
// }
this.websocketPingInterval = window.setInterval(() => {
if (!navigator.onLine) {
return;
}
this.send({ type: "ping", peer_id: this.peerID, peer_name: app.peername, user_id: app.userID, user_name: app.username });
}, 10000);
};
// this.websocket.onopen = async (event) => {
// console.log.apply(null, log("ws:connected"));;
// await this.sendHello();
// // If we're running as a headless peer, send a hello message every N seconds to refresh the posts we have.
// let helloRefreshIntervalPeriod = 120;
// if (app.isHeadless) {
// console.log.apply(null, log("wsConnection: Setting hello refresh interval to ", helloRefreshIntervalPeriod)
// this.helloRefreshInterval = window.setInterval(() => {
// console.log.apply(null, log("wsConnection: Hello refresh.")
// if (!navigator.onLine) {
// return;
// }
// this.sendHello();
// }, helloRefreshIntervalPeriod * 1000);
// }
// this.websocketPingInterval = window.setInterval(() => {
// if (!navigator.onLine) {
// return;
// }
// this.send({ type: "ping", peer_id: this.peerID, peer_name: app.peername, user_id: app.userID, user_name: app.username });
// }, 10_000)
// };
this.websocket.onclose = (event) => {
console.log.apply(null, log("ws:disconnected"));
;
// this.retry *= 2;
console.log.apply(null, log(`Retrying in ${this.retry} seconds`));
;
window.setTimeout(() => { this.connect(); }, this.retry * 1000);
};
this.websocket.onmessage = (event) => {
// log('ws:<-' + event.data.slice(0, 240));
let data = JSON.parse(event.data);
let { type } = data;
let handler = this.messageHandlers.get(type);
if (!handler) {
console.warn(`Got a message we can't handle:`, type);
return;
}
handler(data);
};
this.websocket.onerror = (event) => {
console.log.apply(null, log('ws:error: ' + event));
;
};
}
disconnect() {
this.websocket?.close();
}
}
class App {
constructor() {
this.username = '';
@@ -550,13 +113,14 @@ class App {
this.showLog = false;
this.markedAvailable = false;
this.limitPosts = 50;
this.websocket = null;
// websocket: wsConnection | null = null;
// vizGraph: any | null = null;
this.qrcode = null;
this.connectURL = "";
this.firstRun = false;
this.peerManager = null;
this.sync = new Sync();
this.renderTimer = 0;
this.time = 0;
this.animals = ['shrew', 'jerboa', 'lemur', 'weasel', 'possum', 'possum', 'marmoset', 'planigale', 'mole', 'narwhal'];
this.adjectives = ['snazzy', 'whimsical', 'jazzy', 'bonkers', 'wobbly', 'spiffy', 'chirpy', 'zesty', 'bubbly', 'perky', 'sassy'];
@@ -579,9 +143,9 @@ class App {
}
console.log.apply(null, log(`announceUsers from ${sendingPeerID}`, userIDs));
for (let userID of userIDs) {
console.log.apply(null, log(`[app] announceUsers, got user:${userID} from peer ${sendingPeerID}`));
// console.log.apply(null, log(`[app] announceUsers, got user:${userID} from peer ${sendingPeerID}`));
this.sync.addUserPeer(userID, sendingPeerID);
if (this.sync.shouldSyncUserID(userID)) {
if (this.sync.shouldSyncUserID(userID) || (this.router.route === App.Route.USER && userID === this.router.userID)) {
let postIDs = await this.peerManager?.rpc.getPostIDsForUser(sendingPeerID, userID);
// console.log.apply(null, log(`[app] announceUsers response, gotPostIDs`, postIDs));
let neededPostIDs = await this.sync.checkPostIds(userID, sendingPeerID, postIDs);
@@ -637,17 +201,24 @@ class App {
this.peerManager.registerRPC('getPostsForUser', async (requestingPeerID, userID, postIDs) => {
let posts = await this.sync.getPostsForUser(userID, postIDs);
for (let post of posts) {
console.log.apply(null, log(`[app] sendPostForUser sending post [${logID(post.id)}] to [${logID(requestingPeerID)}]`, userID, post));
this.peerManager?.rpc.sendPostForUser(requestingPeerID, userID, post);
}
// return posts;
// return postIDs;
});
this.peerManager.registerRPC('sendPostForUser', async (userID, post) => {
console.log.apply(null, log(`[app] sendPostForUser`, userID, post));
console.log.apply(null, log(`[app] sendPostForUser got post`, userID, post));
// if (post.text === "image...") {
// debugger;
// }
await this.sync.writePostForUser(userID, post);
if (userID === this.userID) {
await this.render();
// if (userID === this.userID) {
if (this.renderTimer) {
clearTimeout(this.renderTimer);
}
this.renderTimer = setTimeout(() => { this.render(); }, 1000);
// }
});
await this.peerManager.connect();
console.log.apply(null, log("*************** after peerManager.connect"));
@@ -917,7 +488,7 @@ class App {
// this.posts.push(post);
// localStorage.setItem(key, JSON.stringify(posts));
addData(userID, post);
this.websocket?.broadcastNewPost(userID, post);
// this.websocket?.broadcastNewPost(userID, post);
this.render();
}
getPeerID() {
@@ -987,19 +558,19 @@ class App {
textArea.style.fontFamily = fontName;
textArea.style.fontSize = fontSize;
}
initOffline(connection) {
// Event listener for going offline
window.addEventListener('offline', () => {
console.log.apply(null, log("offline"));
});
// Event listener for going online
window.addEventListener('online', async () => {
console.log.apply(null, log("online"));
// connection.connect();
this.render();
});
console.log.apply(null, log(`Online status: ${navigator.onLine ? "online" : "offline"}`));
}
// initOffline(connection: wsConnection) {
// // Event listener for going offline
// window.addEventListener('offline', () => {
// console.log.apply(null, log("offline"));
// });
// // Event listener for going online
// window.addEventListener('online', async () => {
// console.log.apply(null, log("online"));
// // connection.connect();
// this.render();
// });
// console.log.apply(null, log(`Online status: ${navigator.onLine ? "online" : "offline"}`));
// }
selectFile(contentType) {
return new Promise(resolve => {
let input = document.createElement('input');
@@ -1175,33 +746,7 @@ class App {
return posts;
}
async loadFollowersFromStorage(userID) {
// Rob
if (userID === 'b38b623c-c3fa-4351-9cab-50233c99fa4e') {
return [
'b38b623c-c3fa-4351-9cab-50233c99fa4e',
'6d774268-16cd-4e86-8bbe-847a0328893d', // Sean
'05a495a0-0dd8-4186-94c3-b8309ba6fc4c', // Martin
'a0e42390-08b5-4b07-bc2b-787f8e5f1297', // BMO
'bba3ad24-9181-4e22-90c8-c265c80873ea', // Harry
'8f6802be-c3b6-46c1-969c-5f90cbe01479', // Fiona
];
}
// Martin
if (userID === '05a495a0-0dd8-4186-94c3-b8309ba6fc4c') {
return [
'b38b623c-c3fa-4351-9cab-50233c99fa4e',
'a0e42390-08b5-4b07-bc2b-787f8e5f1297', // BMO
];
}
// Fiona
if (userID === '8f6802be-c3b6-46c1-969c-5f90cbe01479') {
return [
'b38b623c-c3fa-4351-9cab-50233c99fa4e', // Rob
'a0e42390-08b5-4b07-bc2b-787f8e5f1297', // BMO
'05a495a0-0dd8-4186-94c3-b8309ba6fc4c', // Martin
];
}
return ['a0e42390-08b5-4b07-bc2b-787f8e5f1297']; // Follow BMO by default :)
return this.sync.getFollowing(userID);
}
async loadPostsFromStorage(userID, postID) {
this.timerStart();
@@ -1300,13 +845,13 @@ class App {
this.username = this.getUsername();
this.sync.setUserID(this.userID);
this.sync.setArchive(this.isArchivePeer);
this.connect();
this.getRoute();
if (this.router.route === App.Route.CONNECT) {
console.log.apply(null, log('connect', this.router.userID));
localStorage.setItem("dandelion_id", this.router.userID);
localStorage.removeItem("dandelion_username");
}
this.connect();
await this.initDB();
this.connectURL = `${document.location.origin}/connect/${this.userID}`;
document.getElementById('connectURL').innerHTML = `<a href="${this.connectURL}">connect</a>`;
@@ -1530,7 +1075,7 @@ class App {
const blob = new Blob([post.image_data]);
const url = URL.createObjectURL(blob);
image.onload = () => {
URL.revokeObjectURL(url);
// URL.revokeObjectURL(url);
};
image.src = url;
// image.src = image.src = "data:image/png;base64," + post.image;