Add comments
This commit is contained in:
parent
9d6cc301e8
commit
a28cc7cd39
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
|||||||
const MASTODON_ACCOUNT_ID = '109285376472065471'
|
const MASTODON_ACCOUNT_ID = '109285376472065471'
|
||||||
const MASTODON_HOST = 'social.sd.ai'
|
const MASTODON_HOST = 'social.sd.ai'
|
||||||
|
|
||||||
|
// Copies the text from an element to the clipboard, and flashes the element to provide visual feedback
|
||||||
async function copyElementTextToClipboard(e)
|
async function copyElementTextToClipboard(e)
|
||||||
{
|
{
|
||||||
const text = e.textContent
|
const text = e.textContent
|
||||||
@ -12,6 +13,7 @@ async function copyElementTextToClipboard(e)
|
|||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitise text content for display
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
return unsafe
|
return unsafe
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
@ -21,16 +23,24 @@ function escapeHtml(unsafe) {
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renders the content - provides an array of toots, the element to add the content to and whether to show a copyable link for replies
|
||||||
function renderMastodonContent(toots, parentElement, showLink) {
|
function renderMastodonContent(toots, parentElement, showLink) {
|
||||||
|
// clear the parent element so that we can add the new content
|
||||||
parentElement.innerHTML = ''
|
parentElement.innerHTML = ''
|
||||||
|
// add a simple "no comments" message if there are no toots
|
||||||
if (!Array.isArray(toots) || toots.length === 0) {
|
if (!Array.isArray(toots) || toots.length === 0) {
|
||||||
document.getElementById('mastodon-comments-list').innerHTML = "<div class='mastodon-comment'>No comments (yet)!</div>"
|
document.getElementById('mastodon-comments-list').innerHTML = "<div class='mastodon-comment'>No comments (yet)!</div>"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render each toot
|
||||||
for (const toot of toots) {
|
for (const toot of toots) {
|
||||||
if (toot.sensitive) {
|
if (toot.sensitive) {
|
||||||
|
// don't display toots marked as sensitive
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitise the toot content for display, including correctly rendering custom emojis
|
||||||
toot.account.display_name = escapeHtml(toot.account.display_name)
|
toot.account.display_name = escapeHtml(toot.account.display_name)
|
||||||
toot.account.emojis.forEach(emoji => {
|
toot.account.emojis.forEach(emoji => {
|
||||||
toot.account.display_name = toot.account.display_name.replace(`:${emoji.shortcode}:`,
|
toot.account.display_name = toot.account.display_name.replace(`:${emoji.shortcode}:`,
|
||||||
@ -40,6 +50,8 @@ function renderMastodonContent(toots, parentElement, showLink) {
|
|||||||
toot.content = toot.content.replace(`:${emoji.shortcode}:`,
|
toot.content = toot.content.replace(`:${emoji.shortcode}:`,
|
||||||
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" class="mastodon-emoji" />`);
|
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" class="mastodon-emoji" />`);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// create a block of HTML content including the toot data
|
||||||
const comment =
|
const comment =
|
||||||
`<div class="mastodon-comment">
|
`<div class="mastodon-comment">
|
||||||
<div class="mastodon-avatar">
|
<div class="mastodon-avatar">
|
||||||
@ -69,30 +81,44 @@ function renderMastodonContent(toots, parentElement, showLink) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
// Use DOMPurify to create a sanitised element for the toot
|
||||||
const child = DOMPurify.sanitize(comment, {'RETURN_DOM_FRAGMENT': true});
|
const child = DOMPurify.sanitize(comment, {'RETURN_DOM_FRAGMENT': true});
|
||||||
|
|
||||||
|
// make all toot links clickable
|
||||||
const links = child.querySelectorAll('.tootlink');
|
const links = child.querySelectorAll('.tootlink');
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
link.onclick = function() { return copyElementTextToClipboard(this); }
|
link.onclick = function() { return copyElementTextToClipboard(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert the toot into the DOM
|
||||||
parentElement.appendChild(child);
|
parentElement.appendChild(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We set this in the "code injection" footer for any page for which we want to enable comments
|
||||||
let MASTODON_POST_ID
|
let MASTODON_POST_ID
|
||||||
|
|
||||||
|
// when the page has finished loading, send a request for the toots
|
||||||
document.addEventListener("DOMContentLoaded", async (event) => {
|
document.addEventListener("DOMContentLoaded", async (event) => {
|
||||||
let url, isComments
|
let url, isComments
|
||||||
|
// if we're being crawled, don't render comments - may help against spam
|
||||||
const isBot = /bot|google|baidu|bing|msn|teoma|slurp|yandex/i
|
const isBot = /bot|google|baidu|bing|msn|teoma|slurp|yandex/i
|
||||||
.test(navigator.userAgent)
|
.test(navigator.userAgent)
|
||||||
|
|
||||||
|
// if there is a sidebar, we're expecting to load the toots from the main account
|
||||||
if (document.getElementsByClassName('gh-sidebar').length > 0) {
|
if (document.getElementsByClassName('gh-sidebar').length > 0) {
|
||||||
url = `https://${MASTODON_HOST}/api/v1/accounts/${MASTODON_ACCOUNT_ID}/statuses?exclude_replies=true&exclude_reblogs=true`
|
url = `https://${MASTODON_HOST}/api/v1/accounts/${MASTODON_ACCOUNT_ID}/statuses?exclude_replies=true&exclude_reblogs=true`
|
||||||
}
|
}
|
||||||
|
// if there's a post ID and we're not a bot, we're expecting to load the replies from a specific toot
|
||||||
if (MASTODON_POST_ID && !isBot) {
|
if (MASTODON_POST_ID && !isBot) {
|
||||||
url = `https://${MASTODON_HOST}/api/v1/statuses/${MASTODON_POST_ID}/context`
|
url = `https://${MASTODON_HOST}/api/v1/statuses/${MASTODON_POST_ID}/context`
|
||||||
isComments = true
|
isComments = true
|
||||||
}
|
}
|
||||||
|
// find the element to append the content to - if there isn't one, we don't need to query
|
||||||
const element = document.getElementById('mastodon-comments-list')
|
const element = document.getElementById('mastodon-comments-list')
|
||||||
if (url && element) {
|
if (url && element) {
|
||||||
|
// populate the link to the source toot, if necessary (for replies)
|
||||||
const linkElement = document.getElementById('toot-link-top')
|
const linkElement = document.getElementById('toot-link-top')
|
||||||
const clipElement = document.getElementById('toot-link-clip')
|
const clipElement = document.getElementById('toot-link-clip')
|
||||||
const tootUrl = `https://${MASTODON_HOST}/@s/${MASTODON_POST_ID}`
|
const tootUrl = `https://${MASTODON_HOST}/@s/${MASTODON_POST_ID}`
|
||||||
@ -102,11 +128,13 @@ document.addEventListener("DOMContentLoaded", async (event) => {
|
|||||||
if (clipElement) {
|
if (clipElement) {
|
||||||
clipElement.innerText = tootUrl
|
clipElement.innerText = tootUrl
|
||||||
}
|
}
|
||||||
|
// fetch the data from Mastodon
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
let content = await response.json()
|
let content = await response.json()
|
||||||
if (isComments) {
|
if (isComments) {
|
||||||
content = content.descendants
|
content = content.descendants
|
||||||
}
|
}
|
||||||
|
// render the content into the page
|
||||||
const header = document.getElementById('mastodon-comments-header')
|
const header = document.getElementById('mastodon-comments-header')
|
||||||
if (header) {
|
if (header) {
|
||||||
header.style.display = ''
|
header.style.display = ''
|
||||||
|
Loading…
Reference in New Issue
Block a user