working on replies
This commit is contained in:
145
src/App.ts
145
src/App.ts
@@ -13,12 +13,11 @@ type PeerID = string;
|
||||
class Post {
|
||||
post_timestamp: Date;
|
||||
post_id: string;
|
||||
reply_to_id: string|null;
|
||||
author: string;
|
||||
author_id: string;
|
||||
text: string;
|
||||
image_data: ArrayBuffer | null;
|
||||
|
||||
|
||||
importedFrom: "twitter" | null;
|
||||
importSource: any;
|
||||
|
||||
@@ -29,7 +28,8 @@ class Post {
|
||||
post_timestamp: Date,
|
||||
imageData: ArrayBuffer | null = null,
|
||||
importedFrom: "twitter" | null = null,
|
||||
importSource: any = null) {
|
||||
importSource: any = null,
|
||||
reply_to_id:string|null = null) {
|
||||
|
||||
this.post_timestamp = post_timestamp;
|
||||
this.post_id = generateID();
|
||||
@@ -41,6 +41,7 @@ class Post {
|
||||
|
||||
this.importedFrom = importedFrom;
|
||||
this.importSource = importSource;
|
||||
this.reply_to_id = reply_to_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +119,7 @@ export class App {
|
||||
peername: string = '';
|
||||
userID: string = '';
|
||||
peerID: string = '';
|
||||
replyToID: string|null = null;
|
||||
following: Set<string> = new Set();
|
||||
posts: StoragePost[] = [];
|
||||
isHeadless: boolean = false;
|
||||
@@ -342,23 +344,16 @@ export class App {
|
||||
}
|
||||
|
||||
return true;
|
||||
// return posts;
|
||||
|
||||
// return postIDs;
|
||||
});
|
||||
|
||||
this.peerManager.registerRPC('sendPostForUser', async (sendingPeerID: string, userID: string, post: 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++
|
||||
@@ -372,14 +367,13 @@ 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.")
|
||||
|
||||
|
||||
|
||||
@@ -729,7 +723,7 @@ export class App {
|
||||
}
|
||||
}
|
||||
|
||||
async createNewPost(userID: string, postText: string, mediaData?: ArrayBuffer, mimeType?: "image/png" | "image/gif" | "image/jpg" | "image/jpeg" | "video/mp4") {
|
||||
async createPost(userID: string, postText: string, mediaData?: ArrayBuffer, mimeType?: "image/svg+xml" | "image/png" | "image/gif" | "image/jpg" | "image/jpeg" | "video/mp4", replyToID:string|null = null) {
|
||||
if ((typeof postText !== "string") || postText.length === 0) {
|
||||
console.log.apply(null, log("Not posting an empty string..."));
|
||||
return;
|
||||
@@ -737,14 +731,14 @@ export class App {
|
||||
|
||||
if (mediaData &&
|
||||
(mimeType === 'image/jpg' || mimeType === 'image/jpeg' || mimeType === 'image/png') &&
|
||||
(mediaData as ArrayBuffer).byteLength > 500 * 1024) {
|
||||
(mediaData as ArrayBuffer).byteLength > 256 * 1024) {
|
||||
let compressedImage = await this.compressImage(mediaData as ArrayBuffer, mimeType, 0.9);
|
||||
if (compressedImage) {
|
||||
mediaData = compressedImage as ArrayBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -990,8 +984,7 @@ 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();
|
||||
});
|
||||
|
||||
|
||||
@@ -999,11 +992,13 @@ export class App {
|
||||
filePicker?.addEventListener('change', async (event: any) => {
|
||||
for (let file of filePicker.files as any) {
|
||||
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');
|
||||
@@ -1040,25 +1035,28 @@ export class App {
|
||||
|
||||
// clearPostsButton.addEventListener('click', () => { clearData(userID); posts = []; this.render() });
|
||||
|
||||
|
||||
let postButton = document.getElementById("button_post") as HTMLButtonElement;
|
||||
let cancelPostButton = document.getElementById("button_cancel_post") as HTMLElement;
|
||||
let postText = document.getElementById("textarea_post") as HTMLTextAreaElement;
|
||||
|
||||
if (!(postButton && postText)) {
|
||||
let postButton = document.getElementById("button_post") as HTMLButtonElement;
|
||||
|
||||
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 as any);
|
||||
await this.createPost(this.userID, 'image...', buffer, file.type as any);
|
||||
});
|
||||
|
||||
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", () => {
|
||||
@@ -1072,6 +1070,31 @@ export class App {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Change this all to a template so we're not toggling state in this crazy way!
|
||||
enterCompose(replyToID:string|null=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") as HTMLTextAreaElement;
|
||||
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.
|
||||
@@ -1486,6 +1509,12 @@ export class App {
|
||||
this.render();
|
||||
}
|
||||
|
||||
renderComposeReplyArea(replyToID:string) {
|
||||
let composeReplyArea = document.getElementById('compose-reply-area') as HTMLElement;
|
||||
composeReplyArea.innerText = replyToID;
|
||||
composeReplyArea.classList.add("show");
|
||||
}
|
||||
|
||||
renderPost(post: Post, first: boolean) {
|
||||
if (!(post.hasOwnProperty("text"))) {
|
||||
throw new Error("Post is malformed!");
|
||||
@@ -1505,6 +1534,16 @@ export class App {
|
||||
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;
|
||||
@@ -1530,11 +1569,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;
|
||||
@@ -1545,33 +1590,33 @@ export class App {
|
||||
// containerDiv.querySelector('#editButton')?.appendChild(editButton);
|
||||
}
|
||||
|
||||
|
||||
containerDiv.querySelector('#shareButton')?.appendChild(shareButton);
|
||||
containerDiv.querySelector('#replyButton')?.appendChild(replyButton);
|
||||
|
||||
|
||||
if (!("image_data" in post && post.image_data)) {
|
||||
// containerDiv.appendChild(timestampDiv);
|
||||
return containerDiv;
|
||||
// return null;
|
||||
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 as ArrayBuffer]);
|
||||
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 as ArrayBuffer]);
|
||||
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