Working.
This commit is contained in:
@@ -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;
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"dataUtils.js","sourceRoot":"","sources":["../src/dataUtils.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"dataUtils.js","sourceRoot":"","sources":["../src/dataUtils.ts"],"names":[],"mappings":"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"}
|
||||
19
static/db.js
19
static/db.js
@@ -395,6 +395,25 @@ export async function buildReplyCountMap() {
|
||||
}
|
||||
return { direct, recursive };
|
||||
}
|
||||
export async function getPostById(postID) {
|
||||
const knownUsers = [...(await indexedDB.databases())]
|
||||
.map(db => db.name?.replace('user_', '')).filter(Boolean);
|
||||
for (const userID of knownUsers) {
|
||||
try {
|
||||
const { store } = await getDBTransactionStore(userID);
|
||||
const index = store.index("postIDIndex");
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
const req = index.get(postID);
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
catch (_) { }
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
export async function getRepliesForPost(postID) {
|
||||
const knownUsers = [...(await indexedDB.databases())]
|
||||
.map(db => db.name?.replace('user_', '')).filter(Boolean);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@
|
||||
:root {
|
||||
--main-bg-color: white;
|
||||
--main-hover-color: rgb(64, 64, 64);
|
||||
--post-hover-color: rgb(32,32,32);
|
||||
--border-color: rgb(132, 136, 138);
|
||||
--edge-color: rgb(60, 60, 60);
|
||||
--main-fg-color: black;
|
||||
@@ -142,7 +143,7 @@ hr {
|
||||
}
|
||||
|
||||
.post-body:hover {
|
||||
background-color: var(--main-hover-color);
|
||||
background-color: var(--post-hover-color);
|
||||
}
|
||||
|
||||
#log {
|
||||
@@ -316,7 +317,39 @@ iframe {
|
||||
}
|
||||
|
||||
#compose-reply-area {
|
||||
display:none;
|
||||
display: none;
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 8px;
|
||||
border-left: 3px solid var(--highlight-fg-color);
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.compose-reply-preview {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.compose-reply-preview-text {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
max-height: 80px;
|
||||
}
|
||||
|
||||
.compose-reply-preview-text iframe {
|
||||
width: 120px;
|
||||
height: 68px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.compose-reply-preview-image {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
max-width: 48px;
|
||||
max-height: 48px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.show {
|
||||
|
||||
@@ -689,7 +689,7 @@ class App {
|
||||
let compressedData = await compressString(JSON.stringify(output));
|
||||
const d = new Date();
|
||||
const timestamp = `${d.getFullYear()}_${String(d.getMonth() + 1).padStart(2, '0')}_${String(d.getDate()).padStart(2, '0')}_${String(d.getHours()).padStart(2, '0')}_${String(d.getMinutes()).padStart(2, '0')}_${String(d.getSeconds()).padStart(2, '0')}`;
|
||||
this.downloadBinary(compressedData, `ddln_${this.username}_export_${timestamp}.json.gz`);
|
||||
this.downloadBinary(compressedData.buffer, `ddln_${this.username}_export_${timestamp}.json.gz`);
|
||||
}
|
||||
async importTweetArchive(userID, tweetArchive) {
|
||||
log("Importing tweet archive");
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user