This commit is contained in:
2026-04-16 02:02:34 -07:00
21 changed files with 336 additions and 82 deletions

View File

@@ -1,11 +1,11 @@
import { generateID } from "IDUtils";
import { PeerManager, PeerEventTypes } from "PeerManager";
import { Sync } from "Sync";
import { openDatabase, getData, addData, deleteData, mergeDataArray, getAllData, getPostForUser, getRepliesForPost, buildReplyCountMap } from "db";
import { openDatabase, getData, addData, deleteData, mergeDataArray, getAllData, getPostForUser, getPostById, getRepliesForPost, buildReplyCountMap } from "db";
import { arrayBufferToBase64, compressString, decompressBuffer, base64ToArrayBuffer } from "dataUtils";
import { log, logID, renderLog, setLogVisibility } from "log";
class Post {
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null, reply_to_id = null) {
constructor(author, author_id, text, post_timestamp, imageData = null, importedFrom = null, importSource = null, reply_to_id = null, root_id = null) {
this.post_timestamp = post_timestamp;
this.post_id = generateID();
this.author = author;
@@ -15,6 +15,7 @@ class Post {
this.importedFrom = importedFrom;
this.importSource = importSource;
this.reply_to_id = reply_to_id;
this.root_id = root_id;
}
}
class StatusBar {
@@ -70,6 +71,7 @@ export class App {
this.userID = '';
this.peerID = '';
this.replyToID = null;
this.replyRootID = null;
this.following = new Set();
this.posts = [];
this.isHeadless = false;
@@ -538,7 +540,7 @@ export class App {
return null;
}
}
async createPost(userID, postText, mediaData, mimeType, replyToID = null) {
async createPost(userID, postText, mediaData, mimeType, replyToID = null, replyRootID = null) {
if ((typeof postText !== "string") || postText.length === 0) {
console.log.apply(null, log("Not posting an empty string..."));
return;
@@ -551,7 +553,7 @@ export class App {
mediaData = compressedImage;
}
}
let post = new Post(this.username, userID, postText, new Date(), mediaData, null, null, replyToID);
let post = new Post(this.username, userID, postText, new Date(), mediaData, null, null, replyToID, replyRootID);
// this.posts.push(post);
// localStorage.setItem(key, JSON.stringify(posts));
addData(userID, post);
@@ -559,23 +561,21 @@ export class App {
this.render();
}
getPeerID() {
let id = localStorage.getItem("peer_id");
if (!id) {
console.log.apply(null, log(`Didn't find a peer ID, generating one`));
;
id = generateID();
localStorage.setItem("peer_id", id);
}
const existing = localStorage.getItem("peer_id");
if (existing)
return existing;
console.log.apply(null, log(`Didn't find a peer ID, generating one`));
const id = generateID();
localStorage.setItem("peer_id", id);
return id;
}
getUserID() {
let id = localStorage.getItem("dandelion_id");
if (!id) {
console.log.apply(null, log(`Didn't find a user ID, generating one`));
;
id = generateID();
localStorage.setItem("dandelion_id", id);
}
const existing = localStorage.getItem("dandelion_id");
if (existing)
return existing;
console.log.apply(null, log(`Didn't find a user ID, generating one`));
const id = generateID();
localStorage.setItem("dandelion_id", id);
return id;
}
hashIdToIndices(id) {
@@ -760,7 +760,7 @@ export class App {
filePicker?.addEventListener('change', async (event) => {
for (let file of filePicker.files) {
let buffer = await file.arrayBuffer();
await this.createPost(this.userID, 'image...', buffer, file.type, this.replyToID);
await this.createPost(this.userID, 'image...', buffer, file.type, this.replyToID, this.replyRootID);
}
// Reset so that if they pick the same image again, we still get the change event.
filePicker.value = '';
@@ -804,10 +804,10 @@ export class App {
const dataTransfer = e.clipboardData;
const file = dataTransfer.files[0];
let buffer = await file.arrayBuffer();
await this.createPost(this.userID, 'image...', buffer, file.type, this.replyToID);
await this.createPost(this.userID, 'image...', buffer, file.type, this.replyToID, this.replyRootID);
});
const submitPost = () => {
this.createPost(userID, postText.value, undefined, undefined, this.replyToID);
this.createPost(userID, postText.value, undefined, undefined, this.replyToID, this.replyRootID);
this.exitCompose();
};
postButton.addEventListener("click", submitPost);
@@ -825,8 +825,9 @@ export class App {
// });
}
// Change this all to a template so we're not toggling state in this crazy way!
enterCompose(replyToID = null) {
enterCompose(replyToID = null, replyRootID = null) {
this.replyToID = replyToID;
this.replyRootID = replyRootID;
if (replyToID) {
this.renderComposeReplyArea(replyToID);
document.getElementById("compose-reply-area").style.display = "block";
@@ -838,6 +839,7 @@ export class App {
}
exitCompose() {
this.replyToID = null;
this.replyRootID = null;
let postText = document.getElementById("textarea_post");
postText.value = "";
document.getElementById('compose').style.display = 'none';
@@ -1133,6 +1135,14 @@ export class App {
}
contentDiv.appendChild(fragment);
if (isPostView && this.posts.length > 0) {
const currentPost = this.posts[0].data;
if (currentPost.root_id) {
const rootRecord = await getPostById(currentPost.root_id);
if (rootRecord) {
const rootEl = this.renderPost(rootRecord.data, true, replyCountMap.recursive.get(rootRecord.data.post_id) ?? 0);
contentDiv.insertBefore(rootEl, contentDiv.firstChild);
}
}
const renderReplies = async (postID, depth) => {
if (depth > 2)
return;
@@ -1152,7 +1162,7 @@ export class App {
await renderReplies(reply.data.post_id, depth + 1);
}
};
await renderReplies(this.posts[0].data.post_id, 0);
await renderReplies(this.posts[0].data.post_id, currentPost.root_id ? 1 : 0);
}
let renderTime = this.timerDelta();
console.log.apply(null, log(`render took: ${renderTime.toFixed(2)}ms`));
@@ -1167,10 +1177,27 @@ export class App {
deleteData(userID, postID);
this.render();
}
renderComposeReplyArea(replyToID) {
let composeReplyArea = document.getElementById('compose-reply-area');
composeReplyArea.innerText = replyToID;
composeReplyArea.classList.add("show");
async renderComposeReplyArea(replyToID) {
const composeReplyArea = document.getElementById('compose-reply-area');
composeReplyArea.innerHTML = '';
const record = await getPostById(replyToID);
if (!record)
return;
const postData = record.data;
const previewDiv = document.createElement('div');
previewDiv.className = 'compose-reply-preview';
const textDiv = document.createElement('div');
textDiv.className = 'compose-reply-preview-text';
textDiv.innerHTML = this.markedAvailable ? marked.parse(postData.text) : postData.text;
previewDiv.appendChild(textDiv);
if (postData.image_data) {
const img = document.createElement('img');
img.className = 'compose-reply-preview-image';
const blob = new Blob([postData.image_data]);
img.src = URL.createObjectURL(blob);
previewDiv.appendChild(img);
}
composeReplyArea.appendChild(previewDiv);
}
renderPost(post, first, replyCount = 0) {
if (!(post.hasOwnProperty("text"))) {
@@ -1192,9 +1219,8 @@ export class App {
replyButton.innerText = replyCount > 0 ? `reply (${replyCount})` : '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)
const rootID = post.root_id ?? post.post_id;
this.enterCompose(post.post_id, rootID);
};
let ownPost = post.author_id === this.userID;
let markdown = post.text;