diff --git a/sb/layouts/partials/comments.html b/sb/layouts/partials/comments.html new file mode 100644 index 0000000..69d4063 --- /dev/null +++ b/sb/layouts/partials/comments.html @@ -0,0 +1,192 @@ +{{ with .Params.comments }} +<section id="comments" class="article-content"> + <h2>Comments</h2> + <p>Mit einem Mastodon- oder Fediverse-Account kannst du hier antworten <a href="https://{{ .host }}/@{{ .username }}/{{ .id }}">post</a>. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one. Known non-private replies are displayed below.</p> + <p>Learn how this is implemented <a class="link" href="/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/">here.</a></p> + + <p id="mastodon-comments-list"><button id="load-comment">Load comments</button></p> + <div id="comments-wrapper"> + <noscript><p>Loading comments relies on JavaScript. Try enabling JavaScript and reloading, or visit <a href="https://{{ .host }}/@{{ .username }}/{{ .id }}">the original post</a> on Mastodon.</p></noscript> + </div> + <noscript>You need JavaScript to view the comments.</noscript> + <script src="/js/purify.min.js"></script> + <script type="text/javascript"> + function escapeHtml(unsafe) { + return unsafe + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + function emojify(input, emojis) { + let output = input; + + emojis.forEach(emoji => { + let picture = document.createElement("picture"); + + let source = document.createElement("source"); + source.setAttribute("srcset", escapeHtml(emoji.url)); + source.setAttribute("media", "(prefers-reduced-motion: no-preference)"); + + let img = document.createElement("img"); + img.className = "emoji"; + img.setAttribute("src", escapeHtml(emoji.static_url)); + img.setAttribute("alt", `:${ emoji.shortcode }:`); + img.setAttribute("title", `:${ emoji.shortcode }:`); + img.setAttribute("width", "20"); + img.setAttribute("height", "20"); + + picture.appendChild(source); + picture.appendChild(img); + + output = output.replace(`:${ emoji.shortcode }:`, picture.outerHTML); + }); + + return output; + } + + function loadComments() { + let commentsWrapper = document.getElementById("comments-wrapper"); + document.getElementById("load-comment").innerHTML = "Loading"; + fetch('https://{{ .host }}/api/v1/statuses/{{ .id }}/context') + .then(function(response) { + return response.json(); + }) + .then(function(data) { + let descendants = data['descendants']; + if( + descendants && + Array.isArray(descendants) && + descendants.length > 0 + ) { + commentsWrapper.innerHTML = ""; + + descendants.forEach(function(status) { + console.log(descendants) + if( status.account.display_name.length > 0 ) { + status.account.display_name = escapeHtml(status.account.display_name); + status.account.display_name = emojify(status.account.display_name, status.account.emojis); + } else { + status.account.display_name = status.account.username; + }; + + let instance = ""; + if( status.account.acct.includes("@") ) { + instance = status.account.acct.split("@")[1]; + } else { + instance = "{{ .host }}"; + } + + const isReply = status.in_reply_to_id !== "{{ .id }}"; + + let op = false; + if( status.account.acct == "{{ .username }}" ) { + op = true; + } + + status.content = emojify(status.content, status.emojis); + + let avatarSource = document.createElement("source"); + avatarSource.setAttribute("srcset", escapeHtml(status.account.avatar)); + avatarSource.setAttribute("media", "(prefers-reduced-motion: no-preference)"); + + let avatarImg = document.createElement("img"); + avatarImg.className = "avatar"; + avatarImg.setAttribute("src", escapeHtml(status.account.avatar_static)); + avatarImg.setAttribute("alt", `@${ status.account.username }@${ instance } avatar`); + + let avatarPicture = document.createElement("picture"); + avatarPicture.appendChild(avatarSource); + avatarPicture.appendChild(avatarImg); + + let avatar = document.createElement("a"); + avatar.className = "avatar-link"; + avatar.setAttribute("href", status.account.url); + avatar.setAttribute("rel", "external nofollow"); + avatar.setAttribute("title", `View profile at @${ status.account.username }@${ instance }`); + avatar.appendChild(avatarPicture); + + let instanceBadge = document.createElement("a"); + instanceBadge.className = "instance"; + instanceBadge.setAttribute("href", status.account.url); + instanceBadge.setAttribute("title", `@${ status.account.username }@${ instance }`); + instanceBadge.setAttribute("rel", "external nofollow"); + instanceBadge.textContent = instance; + + let display = document.createElement("span"); + display.className = "display"; + display.setAttribute("itemprop", "author"); + display.setAttribute("itemtype", "http://schema.org/Person"); + display.innerHTML = status.account.display_name; + + let header = document.createElement("header"); + header.className = "author"; + header.appendChild(display); + header.appendChild(instanceBadge); + + let permalink = document.createElement("a"); + permalink.setAttribute("href", status.url); + permalink.setAttribute("itemprop", "url"); + permalink.setAttribute("title", `View comment at ${ instance }`); + permalink.setAttribute("rel", "external nofollow"); + permalink.textContent = new Date( status.created_at ).toLocaleString('en-US', { + dateStyle: "long", + timeStyle: "short", + }); + + let timestamp = document.createElement("time"); + timestamp.setAttribute("datetime", status.created_at); + timestamp.appendChild(permalink); + + let main = document.createElement("main"); + main.setAttribute("itemprop", "text"); + main.innerHTML = status.content; + + let interactions = document.createElement("footer"); + if(status.favourites_count > 0) { + let faves = document.createElement("a"); + faves.className = "faves"; + faves.setAttribute("href", `${ status.url }/favourites`); + faves.setAttribute("title", `Favorites from ${ instance }`); + faves.textContent = status.favourites_count; + + interactions.appendChild(faves); + } + + let comment = document.createElement("article"); + comment.id = `comment-${ status.id }`; + comment.className = isReply ? "comment comment-reply" : "comment"; + comment.setAttribute("itemprop", "comment"); + comment.setAttribute("itemtype", "http://schema.org/Comment"); + comment.appendChild(avatar); + comment.appendChild(header); + comment.appendChild(timestamp); + comment.appendChild(main); + comment.appendChild(interactions); + + if(op === true) { + comment.classList.add("op"); + + avatar.classList.add("op"); + avatar.setAttribute( + "title", + "Blog post author; " + avatar.getAttribute("title") + ); + + instanceBadge.classList.add("op"); + instanceBadge.setAttribute( + "title", + "Blog post author: " + instanceBadge.getAttribute("title") + ); + } + + commentsWrapper.innerHTML += DOMPurify.sanitize(comment.outerHTML); + }); + } + }); + } + document.getElementById("load-comment").addEventListener("click", loadComments); + </script> +</section> +{{ end }}