import { mergeDataArray, checkPostIds, getAllIds, getPostsByIds } from "db"; import { log, logID } from "log"; async function bytesToBase64DataUrl(bytes, type = "application/octet-stream") { return await new Promise((resolve, reject) => { const reader = Object.assign(new FileReader(), { onload: () => resolve(reader.result), onerror: () => reject(reader.error), }); reader.readAsDataURL(new File([bytes], "", { type })); }); } 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; try { response = await fetch("data:application/octet-stream;base64," + base64String); } catch (e) { console.log("error", e, base64String); return null; } let arrayBuffer = await response.arrayBuffer(); return arrayBuffer; } export class Sync { constructor() { this.isArchivePeer = false; this.userID = ""; this.userPeers = new Map(); this.userIDsToSync = new Set(); this.syncSuperlog = false; this.userBlockList = new Set([ '5d63f0b2-a842-41bf-bf06-e0e4f6369271', '5f1b85c4-b14c-454c-8df1-2cacc93f8a77', // 'bba3ad24-9181-4e22-90c8-c265c80873ea' ]); 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' ]); // async getPostIdsForUserHandler(data: any) { // let message = data.message; // let postIds = await getAllIds(message.user_id) ?? []; // postIds = postIds.filter((postID: string) => !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); // } } setArchive(isArchive) { this.isArchivePeer = isArchive; } setUserID(userID) { this.userID = userID; this.userIDsToSync = new Set(this.getFollowing(userID)); } shouldSyncUserID(userID) { let shouldSyncAllUsers = this.isArchivePeer; if (shouldSyncAllUsers) { return true; } return this.userIDsToSync.has(userID); } getPeersForUser(userID) { let peers = this.userPeers.get(userID); if (!peers) { return []; } return [...peers.keys()]; } addUserPeer(userID, peerID) { this.syncSuperlog && console.log.apply(null, log(`[sync] addUserPeer user:${logID(userID)} peer:${logID(peerID)}`)); ; if (!this.userPeers.has(userID)) { this.userPeers.set(userID, new Set()); } let peers = this.userPeers.get(userID); peers.add(peerID); // this.syncSuperlog && console.log.apply(null, log(this.userPeers)); } deleteUserPeer(peerIDToDelete) { for (const peers of this.userPeers.values()) { for (const peerID of peers) { if (peerID === peerIDToDelete) { peers.delete(peerIDToDelete); } } } } // shouldSyncUserID(userID: string) { // if (app.isHeadless) { // return true; // } // return this.UserIDsTothis.has(userID); // } 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. return knownUsers; } getFollowing(userID) { let following = ['a0e42390-08b5-4b07-bc2b-787f8e5f1297']; // Follow BMO by default :) following.push(this.userID); // Hazel if (userID == '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5') { following.push(...[ '8f6802be-c3b6-46c1-969c-5f90cbe01479', // Fiona '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5', // Hazel ]); } // Rob if (userID === 'b38b623c-c3fa-4351-9cab-50233c99fa4e') { following.push(...[ '6d774268-16cd-4e86-8bbe-847a0328893d', // Sean '05a495a0-0dd8-4186-94c3-b8309ba6fc4c', // Martin 'bba3ad24-9181-4e22-90c8-c265c80873ea', // Harry '8f6802be-c3b6-46c1-969c-5f90cbe01479', // Fiona '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5', // Hazel ]); } // Martin if (userID === '05a495a0-0dd8-4186-94c3-b8309ba6fc4c') { following.push(...[ 'b38b623c-c3fa-4351-9cab-50233c99fa4e', // Rob ]); } // Fiona if (userID === '8f6802be-c3b6-46c1-969c-5f90cbe01479') { following.push(...[ 'b38b623c-c3fa-4351-9cab-50233c99fa4e', // Rob '05a495a0-0dd8-4186-94c3-b8309ba6fc4c', // Martin '622ecc28-2eff-44b9-b89d-fdea7c8dd2d5', // Hazel ]); } return following; } async getPostIdsForUser(userID) { let postIds = await getAllIds(userID) ?? []; postIds = postIds.filter((postID) => !this.postBlockList.has(postID)); if (postIds.length === 0) { console.log.apply(null, log(`Net: I know about user ${logID(userID)} but I have 0 posts`)); return null; } return postIds; } async checkPostIds(userID, peerID, postIDs) { let startTime = performance.now(); let neededPostIds = await checkPostIds(userID, postIDs); this.syncSuperlog && console.log.apply(null, log(`[sync] ID Check for user ${logID(userID)} with IDs from peer[${logID(peerID)}] took ${(performance.now() - startTime).toFixed(2)}ms`)); if (neededPostIds.length > 0) { this.syncSuperlog && console.log.apply(null, log(`[sync] Need posts (${neededPostIds.length}) for user[${logID(userID)}] from peer[${logID(peerID)}]`)); ; } else { this.syncSuperlog && console.log.apply(null, log(`[sync] Don't need any posts for user[${logID(userID)}] from peer[${logID(peerID)}]`)); ; } // if (postIds.length === 0) { // return []; // } return neededPostIds; } async getPostsForUser(userID, postIDs) { let posts = await getPostsByIds(userID, postIDs) ?? []; console.log.apply(null, log(`[sync] got ${posts.length} posts for user ${logID(userID)}`)); ; // 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); } return output; // console.log.apply(null, log(`getPostsForUser`,output)); } async writePostForUser(userID, post) { // 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}`)); ; return; } // HACK - some posts had the wrong author ID if (userID === this.userID) { post.author_id = this.userID; } post.post_timestamp = new Date(post.post_timestamp); if (post.image_data) { let imageDataArrayBuffer = await base64ToArrayBuffer(post.image_data); if (imageDataArrayBuffer === null) { this.syncSuperlog && console.log(`[sync] Failed to create arraybuffer for image for post userID:${userID} postID:${post.post_id} `); return; } post.image_data = imageDataArrayBuffer; // skip posts with images for now. // return; } console.log.apply(null, log(`Merging same user peer posts...`)); await mergeDataArray(userID, [post]); } } //# sourceMappingURL=Sync.js.map