jon.kelbie.scot website
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

330 lines
8.7 KiB

const styles = `
:root {
--font-color: #5d686f;
--font-size: 1.0rem;
--block-border-width: 1px;
--block-border-radius: 3px;
--block-border-color: #ededf0;
--block-background-color: #f7f8f8;
--comment-indent: 40px;
}
#mastodon-comments-list {
margin: 0 auto;
}
.mastodon-comment {
background-color: var(--block-background-color);
border-radius: var(--block-border-radius);
border: var(--block-border-width) var(--block-border-color) solid;
padding: 20px;
margin-bottom: 1.5rem;
display: flex;
flex-direction: column;
color: var(--font-color);
font-size: var(--font-size);
}
.mastodon-comment p {
margin-bottom: 0px;
}
.mastodon-comment .author {
padding-top:0;
display:flex;
}
.mastodon-comment .author a {
text-decoration: none;
color: #5c5458;
}
.mastodon-comment .author .avatar img {
margin-right:1rem;
min-width:60px;
border-radius: 5px;
}
.mastodon-comment .author .details {
display: flex;
flex-direction: column;
}
.mastodon-comment .author .details .name {
font-weight: bold;
}
.mastodon-comment .author .details .user {
color: #5d686f;
font-size: medium;
}
.mastodon-comment .author .date {
margin-left: auto;
font-size: small;
}
.mastodon-comment .content {
margin: 15px 20px;
}
.mastodon-comment .content a {
color: #5c5458;
}
.mastodon-comment .attachments {
margin: 0px 10px;
}
.mastodon-comment .attachments > * {
margin: 0px 10px;
}
.mastodon-comment .attachments img {
max-width: 100%;
}
.mastodon-comment .content p:first-child {
margin-top:0;
margin-bottom:0;
}
.mastodon-comment .status > div {
display: inline-block;
margin-right: 15px;
}
.mastodon-comment .status a {
color: #5c5458;
text-decoration: none;
}
.mastodon-comment .status .replies.active a {
color: #003eaa;
}
.mastodon-comment .status .reblogs.active a {
color: #8c8dff;
}
.mastodon-comment .status .favourites.active a {
color: #ca8f04;
}
`;
class MastodonComments extends HTMLElement {
constructor() {
super();
this.host = this.getAttribute("host");
this.user = this.getAttribute("user");
this.tootId = this.getAttribute("tootId");
this.commentsLoaded = false;
const styleElem = document.createElement("style");
styleElem.innerHTML = styles;
document.head.appendChild(styleElem);
}
connectedCallback() {
this.innerHTML = `
<h2>Comments</h2>
<noscript>
<div id="error">
Please enable JavaScript to view the comments powered by the Fediverse.
</div>
</noscript>
<p>You can use your Fediverse (i.e. GoToSocial, Mastodon, among many others) account to reply to this <a class="link"
href="https://${this.host}/@${this.user}/statuses/${this.tootId}">post</a>.
</p>
<p id="mastodon-comments-list"></p>
`;
const comments = document.getElementById("mastodon-comments-list");
const rootStyle = this.getAttribute("style");
if (rootStyle) {
comments.setAttribute("style", rootStyle);
}
this.respondToVisibility(comments, this.loadComments.bind(this));
}
escapeHtml(unsafe) {
return (unsafe || "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
toot_active(toot, what) {
var count = toot[what + "_count"];
return count > 0 ? "active" : "";
}
toot_count(toot, what) {
var count = toot[what + "_count"];
return count > 0 ? count : "";
}
user_account(account) {
var result = `@${account.acct}`;
if (account.acct.indexOf("@") === -1) {
var domain = new URL(account.url);
result += `@${domain.hostname}`;
}
return result;
}
render_toots(toots, in_reply_to, depth) {
var tootsToRender = toots
.filter((toot) => toot.in_reply_to_id === in_reply_to)
.sort((a, b) => a.created_at.localeCompare(b.created_at));
tootsToRender.forEach((toot) => this.render_toot(toots, toot, depth));
}
render_toot(toots, toot, depth) {
toot.account.display_name = this.escapeHtml(toot.account.display_name);
toot.account.emojis.forEach((emoji) => {
toot.account.display_name = toot.account.display_name.replace(
`:${emoji.shortcode}:`,
`<img src="${this.escapeHtml(emoji.static_url)}" alt="Emoji ${
emoji.shortcode
}" height="20" width="20" />`,
);
});
const mastodonComment = `<div class="mastodon-comment" style="margin-left: calc(var(--comment-indent) * ${depth})">
<div class="author">
<div class="avatar">
<img src="${this.escapeHtml(
toot.account.avatar_static,
)}" height=60 width=60 alt="">
</div>
<div class="details">
<a class="name" href="${toot.account.url}" rel="nofollow">${
toot.account.display_name
}</a>
<a class="user" href="${
toot.account.url
}" rel="nofollow">${this.user_account(toot.account)}</a>
</div>
<a class="date" href="${
toot.url
}" rel="nofollow">${toot.created_at.substr(
0,
10,
)} ${toot.created_at.substr(11, 8)}</a>
</div>
<div class="content">${toot.content}</div>
<div class="attachments">
${toot.media_attachments
.map((attachment) => {
if (attachment.type === "image") {
return `<a href="${attachment.url}" rel="nofollow"><img src="${
attachment.preview_url
}" alt="${this.escapeHtml(attachment.description)}" /></a>`;
} else if (attachment.type === "video") {
return `<video controls><source src="${attachment.url}" type="${attachment.mime_type}"></video>`;
} else if (attachment.type === "gifv") {
return `<video autoplay loop muted playsinline><source src="${attachment.url}" type="${attachment.mime_type}"></video>`;
} else if (attachment.type === "audio") {
return `<audio controls><source src="${attachment.url}" type="${attachment.mime_type}"></audio>`;
} else {
return `<a href="${attachment.url}" rel="nofollow">${attachment.type}</a>`;
}
})
.join("")}
</div>
<div class="status">
<div class="replies ${this.toot_active(toot, "replies")}">
<a href="${
toot.url
}" rel="nofollow"><i class="fa fa-comment fa-fw"></i>${this.toot_count(
toot,
"replies",
)}</a>
</div>
<div class="reblogs ${this.toot_active(toot, "reblogs")}">
<a href="${
toot.url
}" rel="nofollow"><i class="fa fa-share fa-fw"></i>${this.toot_count(
toot,
"reblogs",
)}</a>
</div>
<div class="favourites ${this.toot_active(toot, "favourites")}">
<a href="${
toot.url
}" rel="nofollow"><i class="fa fa-thumbs-up fa-fw"></i>${this.toot_count(
toot,
"favourites",
)}</a>
</div>
</div>
</div>`;
var div = document.createElement("div");
div.innerHTML =
typeof DOMPurify !== "undefined"
? DOMPurify.sanitize(mastodonComment.trim())
: mastodonComment.trim();
document
.getElementById("mastodon-comments-list")
.appendChild(div.firstChild);
this.render_toots(toots, toot.id, depth + 1);
}
loadComments() {
if (this.commentsLoaded) return;
document.getElementById("mastodon-comments-list").innerHTML =
"Loading comments from the Fediverse...";
let _this = this;
fetch(
"https://php-proxy.kelbie.scot/api/v1/statuses/" + this.tootId + "/context",
)
.then((response) => response.json())
.then((data) => {
if (
data["descendants"] &&
Array.isArray(data["descendants"]) &&
data["descendants"].length > 0
) {
document.getElementById("mastodon-comments-list").innerHTML = "";
_this.render_toots(data["descendants"], _this.tootId, 0);
} else {
document.getElementById("mastodon-comments-list").innerHTML =
"<p>Not comments found</p>";
}
_this.commentsLoaded = true;
});
}
respondToVisibility(element, callback) {
var options = {
root: null,
};
var observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
callback();
}
});
}, options);
observer.observe(element);
}
}
customElements.define("mastodon-comments", MastodonComments);