mirror of
https://github.com/ION606/bluesky-client.git
synced 2026-05-14 21:26:54 +00:00
added single post viewing
This commit is contained in:
+26
-11
@@ -9,9 +9,10 @@ function zoomimg(e) {
|
||||
zoomContainer.querySelector('img').style.display = '';
|
||||
|
||||
zoomContainer.querySelector('img').src = src;
|
||||
zoomContainer.classList.toggle('zoomed');
|
||||
overlayEl.classList.toggle('hidden');
|
||||
overlayEl.classList.toggle('show');
|
||||
zoomContainer.classList.toggle('zoomed');
|
||||
zoomContainer.classList.toggle('hidden');
|
||||
|
||||
document.querySelector('.profile-container').classList.toggle('.noscroll');
|
||||
}
|
||||
|
||||
@@ -374,6 +375,8 @@ function setupWorker(name = 'bktemp') {
|
||||
}
|
||||
}
|
||||
|
||||
const statArr = [0, 0];
|
||||
|
||||
/**
|
||||
* @param {*} post
|
||||
* @param {*} a
|
||||
@@ -387,11 +390,19 @@ function renderPostSingle(post, a, likes, ipcRenderer, container, idkey = 'bskyi
|
||||
const cardId = (post?.reply?.root?.uri || post.post.uri)?.trim();
|
||||
|
||||
// if the card exists, use it; otherwise, create a new card
|
||||
if (a.has(cardId)) card = a.get(cardId);
|
||||
if (a.has(cardId)) {
|
||||
card = a.get(cardId);
|
||||
statArr[0]++;
|
||||
}
|
||||
else {
|
||||
card = document.createElement('div');
|
||||
card.classList.add('post-card');
|
||||
card.dataset[`${idkey}`] = cardId;
|
||||
card.onclick = (e) => {
|
||||
if (e.target !== card && !e.target.classList.contains('post-text')) return;
|
||||
window.location.href = `../HTML/post.html?id=${encodeURI(card.dataset[`${idkey}`])}`;
|
||||
}
|
||||
statArr[1]++;
|
||||
}
|
||||
|
||||
// handle repost reason if it hasn't been added to this card
|
||||
@@ -529,7 +540,7 @@ function renderPostSingle(post, a, likes, ipcRenderer, container, idkey = 'bskyi
|
||||
card.appendChild(textContent);
|
||||
}
|
||||
|
||||
// handle video embed (if present and not added yet)
|
||||
// handle video embed
|
||||
if (post.post.embed && post.post.embed.$type === 'app.bsky.embed.video#view' && !card.querySelector('.post-video')) {
|
||||
const videoContainer = createVideoEl(), videoPlayer = videoContainer.querySelector('video');
|
||||
|
||||
@@ -599,11 +610,10 @@ function renderPostSingle(post, a, likes, ipcRenderer, container, idkey = 'bskyi
|
||||
const linkElement = document.createElement('a');
|
||||
linkElement.classList.add('external-embed-link');
|
||||
linkElement.href = embed.uri;
|
||||
linkElement.target = '_blank'; // opens in a new tab
|
||||
linkElement.rel = 'noopener noreferrer'; // improves security
|
||||
linkElement.target = '_blank';
|
||||
linkElement.rel = 'noopener noreferrer'; // improves security (apparently)
|
||||
linkElement.textContent = 'Visit';
|
||||
|
||||
// append elements to the container
|
||||
embedContainer.appendChild(titleElement);
|
||||
embedContainer.appendChild(descriptionElement);
|
||||
embedContainer.appendChild(linkElement);
|
||||
@@ -622,7 +632,7 @@ function renderPostSingle(post, a, likes, ipcRenderer, container, idkey = 'bskyi
|
||||
|
||||
// add the card to the container if it's a new card
|
||||
if (!a.has(cardId)) {
|
||||
const lurl = likes.find(o => (o.posturi === cardId));
|
||||
const lurl = likes?.find(o => (o.posturi === cardId));
|
||||
if (lurl) card.dataset.likeuri = lurl.likeuri;
|
||||
card.appendChild(createButtonEls(card, ipcRenderer, idkey, containerid));
|
||||
container.appendChild(card);
|
||||
@@ -652,12 +662,15 @@ module.exports = function renderPosts(posts, likes, pinnedPost, ipcRenderer, idk
|
||||
a = new Map();
|
||||
}
|
||||
|
||||
statArr[0] = 0;
|
||||
statArr[1] = 0;
|
||||
|
||||
for (const post of posts) {
|
||||
try {
|
||||
renderPostSingle(
|
||||
post,
|
||||
a,
|
||||
likes.map(o => ({ posturi: o.post.uri, likeuri: o.post.viewer?.like })),
|
||||
likes?.map(o => ({ posturi: o.post.uri, likeuri: o.post.viewer?.like })),
|
||||
ipcRenderer,
|
||||
container,
|
||||
idkey,
|
||||
@@ -669,8 +682,10 @@ module.exports = function renderPosts(posts, likes, pinnedPost, ipcRenderer, idk
|
||||
}
|
||||
}
|
||||
|
||||
const postids = Array.from(document.querySelectorAll(`[data-${idkey}]`)).map(o => o.dataset[`${idkey} `]);
|
||||
console.log(postids);
|
||||
console.info(`added ${statArr[1]} and updated ${statArr[0]} posts in "${containerid}"!`);
|
||||
|
||||
// const postids = Array.from(document.querySelectorAll(`[data-${idkey}]`)).map(o => o.dataset[`${idkey} `]);
|
||||
// console.log('duplicate ids', postids);
|
||||
|
||||
// "force" garbage collection
|
||||
// delete a;
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ function togglerelaxed(e) {
|
||||
container.classList.remove('cards-container-relaxed');
|
||||
container.classList.add('cards-container');
|
||||
} else {
|
||||
container.style.gridTemplateColumns = (value !== 'large') ? 'repeat(auto-fill, minmax(280px, 1fr))' : '';
|
||||
container.style.gridTemplateColumns = (value?.trim() !== 'large') ? 'repeat(auto-fill, minmax(280px, 1fr))' : 'none';
|
||||
container.classList.remove('cards-container');
|
||||
container.classList.add('cards-container-relaxed');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
const { formatStr } = require("../src/renderer.cjs");
|
||||
|
||||
|
||||
function renderPost(postData, post, isReply = false) {
|
||||
const card = document.createElement('div');
|
||||
card.classList.add(isReply ? 'reply-card' : 'post-card');
|
||||
|
||||
if (post.pinned) {
|
||||
const pinnedLabel = document.createElement('div');
|
||||
pinnedLabel.classList.add('pinned-label');
|
||||
pinnedLabel.textContent = '📌 Pinned';
|
||||
card.appendChild(pinnedLabel);
|
||||
}
|
||||
|
||||
card.dataset.bskyid = post.uri;
|
||||
card.onclick = (e) => {
|
||||
if (e.target !== card && !e.target.classList.contains('post-text')) return;
|
||||
window.location.href = `../HTML/post.html?id=${encodeURI(card.dataset.bskyid)}`;
|
||||
}
|
||||
|
||||
// Author section
|
||||
const authorSection = document.createElement('div');
|
||||
authorSection.classList.add('author-section');
|
||||
const avatar = document.createElement('img');
|
||||
avatar.loading = 'lazy';
|
||||
avatar.src = post.author.avatar;
|
||||
avatar.alt = `${post.author.displayName}'s avatar`;
|
||||
avatar.classList.add('card-avatar');
|
||||
const authorInfo = document.createElement('div');
|
||||
authorInfo.classList.add('author-info');
|
||||
const displayName = document.createElement('p');
|
||||
|
||||
displayName.innerHTML = formatStr(`@${post.author.handle}`);
|
||||
if (post.author.displayName) displayName.textContent = post.author.displayName;
|
||||
|
||||
displayName.classList.add('author-name');
|
||||
const handle = document.createElement('p');
|
||||
handle.innerHTML = formatStr(`@${post.author.handle}`);
|
||||
handle.classList.add('author-handle');
|
||||
authorInfo.append(displayName, handle);
|
||||
authorSection.append(avatar, authorInfo);
|
||||
|
||||
// Post content
|
||||
const postContent = document.createElement('p');
|
||||
postContent.textContent = post.record.text;
|
||||
postContent.classList.add('post-text');
|
||||
|
||||
// Media (if exists)
|
||||
if (post.embed?.images?.length > 0) {
|
||||
const postMedia = document.createElement('img');
|
||||
postMedia.loading = 'lazy';
|
||||
postMedia.src = post.embed.images[0].fullsize;
|
||||
postMedia.alt = post.embed.images[0].alt;
|
||||
postMedia.classList.add('post-image');
|
||||
card.append(postMedia);
|
||||
}
|
||||
|
||||
if (post.embed && post.embed.$type === 'app.bsky.embed.external#view') {
|
||||
const embedContainer = document.createElement('div'),
|
||||
embed = post.embed.external;
|
||||
|
||||
embedContainer.classList.add('external-embed-container');
|
||||
|
||||
// Check for thumbnail
|
||||
if (embed.thumb) {
|
||||
const thumbnail = document.createElement('img');
|
||||
thumbnail.loading = 'lazy';
|
||||
thumbnail.classList.add('external-embed-thumb');
|
||||
|
||||
// Use embed.uri for GIFs, otherwise use thumbnail
|
||||
thumbnail.src = embed.uri.match(/\.gif(?:\?.*|$)/) ? embed.uri : embed.thumb;
|
||||
|
||||
// Handle ALT descriptions if provided
|
||||
if (embed.description.startsWith('ALT:') && embed.title === embed.description.substring(4).trim()) {
|
||||
thumbnail.alt = embed.description.substring(4).trim();
|
||||
} else {
|
||||
thumbnail.alt = `${embed.title} thumbnail`;
|
||||
}
|
||||
|
||||
embedContainer.appendChild(thumbnail);
|
||||
}
|
||||
|
||||
// Handle the case of an image with ALT only
|
||||
if (!(embed.description.startsWith('ALT:') && embed.title === embed.description.substring(4).trim())) {
|
||||
// Title
|
||||
const titleElement = document.createElement('h3');
|
||||
titleElement.classList.add('external-embed-title');
|
||||
titleElement.textContent = embed.title;
|
||||
|
||||
// Description
|
||||
const descriptionElement = document.createElement('p');
|
||||
descriptionElement.classList.add('external-embed-description');
|
||||
descriptionElement.textContent = embed.description;
|
||||
|
||||
// Link
|
||||
const linkElement = document.createElement('a');
|
||||
linkElement.classList.add('external-embed-link');
|
||||
linkElement.href = embed.uri;
|
||||
linkElement.target = '_blank';
|
||||
linkElement.rel = 'noopener noreferrer'; // Security improvement
|
||||
linkElement.textContent = 'Visit';
|
||||
|
||||
// Append title, description, and link
|
||||
embedContainer.appendChild(titleElement);
|
||||
embedContainer.appendChild(descriptionElement);
|
||||
embedContainer.appendChild(linkElement);
|
||||
}
|
||||
|
||||
card.appendChild(embedContainer);
|
||||
}
|
||||
|
||||
let interactionSection;
|
||||
|
||||
if (isReply) {
|
||||
interactionSection = document.createElement('div');
|
||||
interactionSection.classList.add('interaction-section');
|
||||
interactionSection.innerHTML = `
|
||||
<span>💬 ${post.replyCount}</span><span>🔄 ${post.repostCount}</span><span>❤️ ${post.likeCount}</span>
|
||||
`;
|
||||
}
|
||||
else {
|
||||
interactionSection = document.createElement('div');
|
||||
interactionSection.classList.add('interaction-section');
|
||||
interactionSection.innerHTML = `
|
||||
<button class="interaction-button">💬 ${postData.post.replyCount}</button>
|
||||
<button class="interaction-button">🔄 ${postData.post.repostCount}</button>
|
||||
<button class="interaction-button">❤️ ${postData.post.likeCount}</button>
|
||||
`;
|
||||
}
|
||||
|
||||
card.prepend(authorSection);
|
||||
card.append(postContent);
|
||||
card.append(interactionSection);
|
||||
|
||||
if (!isReply) {
|
||||
card.style.cursor = 'default';
|
||||
card.onclick = (_) => null;
|
||||
}
|
||||
|
||||
return card;
|
||||
};
|
||||
|
||||
|
||||
async function renderMain(postData) {
|
||||
const container = document.getElementById('post-container');
|
||||
if (!container) return alert("ERROR! CONTAINER NOT FOUND!");
|
||||
|
||||
console.log(postData);
|
||||
|
||||
// Render main post
|
||||
const mainPost = renderPost(postData, postData.post);
|
||||
container.appendChild(mainPost);
|
||||
|
||||
// Render replies
|
||||
if (postData.replies && postData.replies.length > 0) {
|
||||
const replyThread = document.createElement('div');
|
||||
replyThread.classList.add('reply-thread');
|
||||
postData.replies.forEach(reply => {
|
||||
try {
|
||||
const replyCard = renderPost(postData, reply.post, true);
|
||||
replyThread.appendChild(replyCard);
|
||||
}
|
||||
catch (err) {
|
||||
console.error('failed to render', reply, 'with reason\n', err);
|
||||
}
|
||||
});
|
||||
container.appendChild(replyThread);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {Electron.IpcRenderer} ipcRenderer
|
||||
*/
|
||||
async function init(ipcRenderer) {
|
||||
const params = new URLSearchParams(window.location.search),
|
||||
bskyid = params.get('id');
|
||||
const r = await ipcRenderer.invoke('get-post-single', bskyid);
|
||||
|
||||
renderMain(r);
|
||||
}
|
||||
|
||||
|
||||
module.exports = init;
|
||||
Reference in New Issue
Block a user