working on replies
This commit is contained in:
120
static/App.js
120
static/App.js
@@ -5,7 +5,7 @@ import { openDatabase, getData, addData, deleteData, getAllData, getPostForUser
|
||||
import { arrayBufferToBase64, compressString } from "dataUtils";
|
||||
import { log, logID, renderLog, setLogVisibility } from "log";
|
||||
class Post {
|
||||
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null) {
|
||||
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null, reply_to_id = null) {
|
||||
this.post_timestamp = post_timestamp;
|
||||
this.post_id = generateID();
|
||||
this.author = author;
|
||||
@@ -14,6 +14,7 @@ class Post {
|
||||
this.image_data = imageData;
|
||||
this.importedFrom = importedFrom;
|
||||
this.importSource = importSource;
|
||||
this.reply_to_id = reply_to_id;
|
||||
}
|
||||
}
|
||||
class StatusBar {
|
||||
@@ -68,6 +69,7 @@ export class App {
|
||||
this.peername = '';
|
||||
this.userID = '';
|
||||
this.peerID = '';
|
||||
this.replyToID = null;
|
||||
this.following = new Set();
|
||||
this.posts = [];
|
||||
this.isHeadless = false;
|
||||
@@ -252,20 +254,14 @@ export class App {
|
||||
await this.peerManager?.rpc.sendPostForUser(requestingPeerID, this.peerID, userID, post);
|
||||
}
|
||||
return true;
|
||||
// return posts;
|
||||
// return postIDs;
|
||||
});
|
||||
this.peerManager.registerRPC('sendPostForUser', async (sendingPeerID, userID, post) => {
|
||||
console.log.apply(null, log(`[app] sendPostForUser got post[${logID(post.post_id)}] from peer[${logID(sendingPeerID)}] for user[${logID(userID)}] author[${post.author}] text[${post.text}]`));
|
||||
// if (post.text === "image...") {
|
||||
// debugger;
|
||||
// }
|
||||
let peerData = this.statusBar.getPeerData(sendingPeerID);
|
||||
if (peerData) {
|
||||
this.statusBar.updatePeerMessage(sendingPeerID, `⬇️${logID(userID)} ${peerData.havePostCount}/${peerData.neededPostCount}}`);
|
||||
}
|
||||
await this.sync.writePostForUser(userID, post);
|
||||
// if (userID === this.userID) {
|
||||
if (peerData) {
|
||||
peerData.havePostCount++;
|
||||
this.statusBar.updatePeerMessage(sendingPeerID, `⬇️${logID(userID)} ${peerData.havePostCount}/${peerData.neededPostCount}}`);
|
||||
@@ -275,13 +271,12 @@ export class App {
|
||||
}
|
||||
this.renderTimer = setTimeout(() => { this.render(); }, 1000);
|
||||
return true;
|
||||
// }
|
||||
});
|
||||
this.statusBar.setMessageHTML("Connecting to ddln network...");
|
||||
this.statusBar.setMessageHTML("Connecting to ddln...");
|
||||
await this.peerManager.connect();
|
||||
console.log.apply(null, log("*************** after peerManager.connect"));
|
||||
;
|
||||
this.statusBar.setMessageHTML("Connected to ddln network...");
|
||||
this.statusBar.setMessageHTML("Connected to ddln.");
|
||||
if (this.isBootstrapPeer) {
|
||||
return;
|
||||
}
|
||||
@@ -530,20 +525,20 @@ export class App {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async createNewPost(userID, postText, mediaData, mimeType) {
|
||||
async createPost(userID, postText, mediaData, mimeType, replyToID = null) {
|
||||
if ((typeof postText !== "string") || postText.length === 0) {
|
||||
console.log.apply(null, log("Not posting an empty string..."));
|
||||
return;
|
||||
}
|
||||
if (mediaData &&
|
||||
(mimeType === 'image/jpg' || mimeType === 'image/jpeg' || mimeType === 'image/png') &&
|
||||
mediaData.byteLength > 500 * 1024) {
|
||||
mediaData.byteLength > 256 * 1024) {
|
||||
let compressedImage = await this.compressImage(mediaData, mimeType, 0.9);
|
||||
if (compressedImage) {
|
||||
mediaData = compressedImage;
|
||||
}
|
||||
}
|
||||
let post = new Post(this.username, userID, postText, new Date(), mediaData);
|
||||
let post = new Post(this.username, userID, postText, new Date(), mediaData, null, null, replyToID);
|
||||
// this.posts.push(post);
|
||||
// localStorage.setItem(key, JSON.stringify(posts));
|
||||
addData(userID, post);
|
||||
@@ -736,17 +731,17 @@ export class App {
|
||||
});
|
||||
let composeButton = this.div('compose-button');
|
||||
composeButton.addEventListener('click', e => {
|
||||
document.getElementById('compose').style.display = 'block';
|
||||
document.getElementById('textarea_post')?.focus();
|
||||
this.enterCompose();
|
||||
});
|
||||
let filePicker = document.getElementById('file-input');
|
||||
filePicker?.addEventListener('change', async (event) => {
|
||||
for (let file of filePicker.files) {
|
||||
let buffer = await file.arrayBuffer();
|
||||
await this.createNewPost(this.userID, 'image...', buffer, file.type);
|
||||
await this.createPost(this.userID, 'image...', buffer, file.type);
|
||||
}
|
||||
// Reset so that if they pick the same image again, we still get the change event.
|
||||
filePicker.value = '';
|
||||
this.exitCompose();
|
||||
});
|
||||
let filePickerLabel = document.getElementById('file-input-label');
|
||||
filePickerLabel?.addEventListener('click', () => {
|
||||
@@ -773,21 +768,24 @@ export class App {
|
||||
// this.render();
|
||||
// });
|
||||
// clearPostsButton.addEventListener('click', () => { clearData(userID); posts = []; this.render() });
|
||||
let postButton = document.getElementById("button_post");
|
||||
let cancelPostButton = document.getElementById("button_cancel_post");
|
||||
let postText = document.getElementById("textarea_post");
|
||||
if (!(postButton && postText)) {
|
||||
let postButton = document.getElementById("button_post");
|
||||
if (!(cancelPostButton && postButton && postText)) {
|
||||
throw new Error();
|
||||
}
|
||||
cancelPostButton.addEventListener('click', (e) => {
|
||||
this.exitCompose();
|
||||
});
|
||||
postText.addEventListener('paste', async (e) => {
|
||||
const dataTransfer = e.clipboardData;
|
||||
const file = dataTransfer.files[0];
|
||||
let buffer = await file.arrayBuffer();
|
||||
await this.createNewPost(this.userID, 'image...', buffer, file.type);
|
||||
await this.createPost(this.userID, 'image...', buffer, file.type);
|
||||
});
|
||||
postButton.addEventListener("click", () => {
|
||||
this.createNewPost(userID, postText.value);
|
||||
postText.value = "";
|
||||
document.getElementById('compose').style.display = 'none';
|
||||
this.createPost(userID, postText.value, undefined, undefined, this.replyToID);
|
||||
this.exitCompose();
|
||||
});
|
||||
// updateApp.addEventListener("click", () => {
|
||||
// registration?.active?.postMessage({ type: "update_app" });
|
||||
@@ -796,6 +794,26 @@ export class App {
|
||||
// this.showInfo()
|
||||
// });
|
||||
}
|
||||
// Change this all to a template so we're not toggling state in this crazy way!
|
||||
enterCompose(replyToID = null) {
|
||||
if (replyToID) {
|
||||
this.renderComposeReplyArea(replyToID);
|
||||
document.getElementById("compose-reply-area").style.display = "block";
|
||||
}
|
||||
replyToID = replyToID;
|
||||
document.getElementById('compose').style.display = 'block';
|
||||
document.getElementById('textarea_post')?.focus();
|
||||
document.getElementById('compose-dimmer')?.classList.add("compose-dimmer-dimmed");
|
||||
document.body.classList.add("no-scroll");
|
||||
}
|
||||
exitCompose() {
|
||||
let postText = document.getElementById("textarea_post");
|
||||
postText.value = "";
|
||||
document.getElementById('compose').style.display = 'none';
|
||||
document.getElementById('compose-dimmer')?.classList.remove("compose-dimmer-dimmed");
|
||||
document.getElementById("compose-reply-area").style.display = "none";
|
||||
document.body.classList.remove("no-scroll");
|
||||
}
|
||||
async getPostsForFeed() {
|
||||
// get N posts from each user and sort them by date.
|
||||
// This isn't really going to work very well.
|
||||
@@ -1090,6 +1108,11 @@ export class App {
|
||||
deleteData(userID, postID);
|
||||
this.render();
|
||||
}
|
||||
renderComposeReplyArea(replyToID) {
|
||||
let composeReplyArea = document.getElementById('compose-reply-area');
|
||||
composeReplyArea.innerText = replyToID;
|
||||
composeReplyArea.classList.add("show");
|
||||
}
|
||||
renderPost(post, first) {
|
||||
if (!(post.hasOwnProperty("text"))) {
|
||||
throw new Error("Post is malformed!");
|
||||
@@ -1106,6 +1129,14 @@ export class App {
|
||||
let shareUrl = `${document.location.origin}/user/${post.author_id}/post/${post.post_id}`;
|
||||
await navigator.clipboard.writeText(shareUrl);
|
||||
};
|
||||
let replyButton = document.createElement('button');
|
||||
replyButton.innerText = 'reply';
|
||||
replyButton.onclick = async () => {
|
||||
console.log(`replying to post ${post.post_id}`);
|
||||
this.enterCompose(post.post_id);
|
||||
// let shareUrl = `${document.location.origin}/user/${post.author_id}/post/${post.post_id}`;
|
||||
// await navigator.clipboard.writeText(shareUrl)
|
||||
};
|
||||
let ownPost = post.author_id === this.userID;
|
||||
let markdown = post.text;
|
||||
if (this.markedAvailable) {
|
||||
@@ -1124,11 +1155,17 @@ export class App {
|
||||
<span class='header' title='${timestamp}'><img class="logo" src="/static/favicon.ico"><a class="username" href="${userURL}">@${post.author}</a> -
|
||||
<span style="color:rgb(128,128,128)">${post.post_timestamp.toLocaleDateString()}</span>
|
||||
</span>
|
||||
${ownPost ? `<span id="deleteButton"></span>` : ''}
|
||||
${ownPost ? `<span id="editButton"></span>` : ''}
|
||||
<span id="shareButton"></span>
|
||||
</div>
|
||||
<div>${markdown}</div>
|
||||
|
||||
<div id="image"></div>
|
||||
|
||||
<span id="replyButton"></span>
|
||||
${ownPost ? `<span id="editButton"></span>` : ''}
|
||||
<span id="shareButton"></span>
|
||||
${ownPost ? `<span id="deleteButton"></span>` : ''}
|
||||
|
||||
|
||||
</div>`;
|
||||
containerDiv.innerHTML = postTemplate;
|
||||
if (ownPost) {
|
||||
@@ -1136,24 +1173,23 @@ export class App {
|
||||
// containerDiv.querySelector('#editButton')?.appendChild(editButton);
|
||||
}
|
||||
containerDiv.querySelector('#shareButton')?.appendChild(shareButton);
|
||||
if (!("image_data" in post && post.image_data)) {
|
||||
// containerDiv.appendChild(timestampDiv);
|
||||
return containerDiv;
|
||||
// return null;
|
||||
containerDiv.querySelector('#replyButton')?.appendChild(replyButton);
|
||||
let hasImage = ("image_data" in post && post.image_data);
|
||||
if (hasImage) {
|
||||
let image = document.createElement("img");
|
||||
image.title = `${(post.image_data.byteLength / 1024 / 1024).toFixed(2)}MBytes`;
|
||||
// const blob = new Blob([post.image_data as ArrayBuffer], { type: 'image/png' });
|
||||
const blob = new Blob([post.image_data]);
|
||||
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";
|
||||
// image.onclick = () => { App.maximizeElement(image) };
|
||||
containerDiv.querySelector('#image')?.appendChild(image);
|
||||
}
|
||||
let image = document.createElement("img");
|
||||
image.title = `${(post.image_data.byteLength / 1024 / 1024).toFixed(2)}MBytes`;
|
||||
// const blob = new Blob([post.image_data as ArrayBuffer], { type: 'image/png' });
|
||||
const blob = new Blob([post.image_data]);
|
||||
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";
|
||||
// image.onclick = () => { App.maximizeElement(image) };
|
||||
containerDiv.appendChild(image);
|
||||
// containerDiv.appendChild(timestampDiv);
|
||||
return containerDiv;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user