friendship ended with social-app. php is my new best friend

add the popover for copying

Changed files
+42 -8
css
_partials
js
templates
_partials
+21
css/_partials/_globals.scss
···
.alt {
padding: 10px;
}
}
···
.alt {
padding: 10px;
}
+
}
+
+
@keyframes fadeOut {
+
0% {
+
opacity: 100%;
+
}
+
+
100% {
+
opacity: 0;
+
}
+
}
+
+
.copy-tooltip {
+
position: absolute;
+
z-index: 99;
+
padding: 3px;
+
text-transform: lowercase;
+
background-color: var(--btn-primary-bg);
+
color: var(--text-color);
+
animation: fadeOut 1s;
+
animation-delay: 2s;
}
+20 -7
js/post.mjs
···
-
const loadPost = async (post) => {
post.setAttribute('data-status', 'loading');
const urlComponents = post.getAttribute('data-uri').split(/at:\/\/(did:plc:[a-z0-9]+)\/app\.bsky\.feed\.post\/([a-z0-9]+)/);
fetch('/api/post/'+urlComponents[1]+'/'+urlComponents[2])
.then((data) => {
data.text().then((content) => {
post.innerHTML = content;
-
post.setAttribute('data-status', 'loaded');
Promise.all([
fetch('/api/likes/'+urlComponents[1]+"/"+urlComponents[2]),
fetch('/api/replies/'+urlComponents[1]+"/"+urlComponents[2]),
···
]).then((values) => {
values[0].json().then((likes) => {
post.querySelector('.like-count').textContent = likes.total;
-
if (document.getElementById('interactions')) {
document.getElementById('likes').querySelector('.inner').innerHTML = likes.rendered;
}
});
values[1].json().then((replies) => {
post.querySelector('.reply-count').textContent = replies.total;
-
if (document.getElementById('interactions')) {
document.getElementById('replies').querySelector('.inner').innerHTML = replies.rendered;
}
});
values[2].json().then((reposts) => {
post.querySelector('.repost-count').textContent = reposts.total;
-
if (document.getElementById('interactions')) {
document.getElementById('reposts').querySelector('.inner').innerHTML = reposts.rendered;
}
});
values[3].json().then((quotes) => {
post.querySelector('.quote-count').textContent = quotes.total;
-
if (document.getElementById('interactions')) {
document.getElementById('quotes').querySelector('.inner').innerHTML = quotes.rendered;
}
});
})
});
});
···
document.addEventListener('click', (e) => {
if (e.target.closest('.post .postSharing a')) {
e.preventDefault();
const link = e.target.closest('.post .postSharing a');
const url = link.getAttribute('data-share');
navigator.clipboard.writeText(url);
}
});
window.addEventListener('load', (_e) => {
document.querySelectorAll('[data-status="unloaded"]').forEach(async (post) => {
-
loadPost(post);
});
});
···
+
const loadPost = async (post, toplevel = false) => {
post.setAttribute('data-status', 'loading');
const urlComponents = post.getAttribute('data-uri').split(/at:\/\/(did:plc:[a-z0-9]+)\/app\.bsky\.feed\.post\/([a-z0-9]+)/);
fetch('/api/post/'+urlComponents[1]+'/'+urlComponents[2])
.then((data) => {
data.text().then((content) => {
post.innerHTML = content;
Promise.all([
fetch('/api/likes/'+urlComponents[1]+"/"+urlComponents[2]),
fetch('/api/replies/'+urlComponents[1]+"/"+urlComponents[2]),
···
]).then((values) => {
values[0].json().then((likes) => {
post.querySelector('.like-count').textContent = likes.total;
+
if (toplevel) {
document.getElementById('likes').querySelector('.inner').innerHTML = likes.rendered;
}
});
values[1].json().then((replies) => {
post.querySelector('.reply-count').textContent = replies.total;
+
if (toplevel) {
document.getElementById('replies').querySelector('.inner').innerHTML = replies.rendered;
+
document.getElementById('replies').querySelectorAll('.inner .post').forEach((reply) => {
+
loadPost(reply);
+
});
}
});
values[2].json().then((reposts) => {
post.querySelector('.repost-count').textContent = reposts.total;
+
if (toplevel) {
document.getElementById('reposts').querySelector('.inner').innerHTML = reposts.rendered;
}
});
values[3].json().then((quotes) => {
post.querySelector('.quote-count').textContent = quotes.total;
+
if (toplevel) {
document.getElementById('quotes').querySelector('.inner').innerHTML = quotes.rendered;
}
});
+
post.setAttribute('data-status', 'loaded');
})
});
});
···
document.addEventListener('click', (e) => {
if (e.target.closest('.post .postSharing a')) {
e.preventDefault();
+
console.log(e);
const link = e.target.closest('.post .postSharing a');
const url = link.getAttribute('data-share');
navigator.clipboard.writeText(url);
+
const tooltip = document.createElement('span');
+
tooltip.classList.add("copy-tooltip");
+
tooltip.textContent = "Copied!";
+
tooltip.style.top = e.y+"px";
+
tooltip.style.left = e.x+"px";
+
document.body.append(tooltip);
+
setTimeout(() => {
+
tooltip.remove();
+
}, 3000);
}
});
window.addEventListener('load', (_e) => {
document.querySelectorAll('[data-status="unloaded"]').forEach(async (post) => {
+
loadPost(post, true);
});
});
+1 -1
templates/_partials/post.latte
···
<div class="postHeader">
<div class="avatar">
-
<a href="{$post->author->profileLink}"><img src="{$post->author->avatar}" alt="{$post->author->displayName}'s user icon" /></a>
</div>
<div class="displayName"><a href="{$post->author->profileLink}">{$post->author->displayName|noescape}</a></div>
<div class="handle"><a href="{$post->author->profileLink}">{$post->author->handle}</a></div>
···
<div class="postHeader">
<div class="avatar">
+
<a href="{$post->author->profileLink}"><img src="{$post->author->avatar}" alt="{$post->author->displayName}'s user icon" loading="lazy" /></a>
</div>
<div class="displayName"><a href="{$post->author->profileLink}">{$post->author->displayName|noescape}</a></div>
<div class="handle"><a href="{$post->author->profileLink}">{$post->author->handle}</a></div>