This commit is contained in:
daudix
2024-01-28 03:16:21 +03:00
parent 777f200f09
commit e4b78d5d03
4 changed files with 185 additions and 438 deletions

View File

@ -72,7 +72,7 @@ show_powered_by = true
# Link to website source # Link to website source
show_source = true show_source = true
# Based on https://github.com/cassidyjames/cassidyjames.github.io/blob/75cd2a202de7a1026de1ea00a4ef1d5b2675c7ff/_config.yaml#L35-L74 # Based on https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/
# #
# Mastodon-powered commenting. # Mastodon-powered commenting.
# Values can be overridden in front-matter, e.g. # Values can be overridden in front-matter, e.g.
@ -86,9 +86,3 @@ host = "mstdn.social"
# Used to determine who the original/verified poster is; # Used to determine who the original/verified poster is;
# role may be expanded in the future (e.g. for moderation). # role may be expanded in the future (e.g. for moderation).
user = "Daudix" user = "Daudix"
# Optional; required to fetch more than 60 replies to any given blog post.
# Application access token with read:statuses scope; NOTE: IF INCLUDED, ANYONE
# WILL BE ABLE TO READ THE ASSOCIATED ACCOUNT'S PRIVATE STATUSES. It is highly
# recommended to use a dedicated bot/API account to create an application with
# scope read:statuses.
token = "jTNX9pAV8XEPBby0cPWF6CmGY60kkIy4vidggfxXmoQ"

View File

@ -1,19 +1,11 @@
.instance.op::before, section#comments {
.reblogs::before, #comments-wrapper {
.favourites::before { display: flex;
-moz-osx-font-smoothing: grayscale; flex-direction: column;
-webkit-font-smoothing: antialiased; gap: 2rem;
display: inline-block; margin-top: 2rem;
font-family: "bootstrap-icons" !important;
font-style: normal;
font-variant: normal;
font-weight: normal !important;
line-height: 1;
text-transform: none;
vertical-align: -0.125em;
} }
section#comments {
.comment { .comment {
display: grid; display: grid;
column-gap: 1rem; column-gap: 1rem;
@ -21,25 +13,31 @@ section#comments {
"avatar name " "avatar name "
"avatar time " "avatar time "
"avatar post " "avatar post "
"...... card"
"...... interactions"; "...... interactions";
grid-template-columns: min-content; grid-template-columns: min-content;
justify-items: start; justify-items: start;
margin: 2rem 0 2rem 0;
&.comment-reply {
border-left: 0.25rem solid var(--fg-muted-2);
border-radius: 0.2rem;
padding-left: 1rem;
}
.avatar-link { .avatar-link {
grid-area: avatar; grid-area: avatar;
width: 4rem;
height: 4rem; height: 4rem;
position: relative; position: relative;
width: 4rem;
.avatar { .avatar {
all: unset; all: unset;
background-color: var(--fg-muted-1); background-color: var(--fg-muted-1);
background-position: 50%;
background-size: cover;
border-radius: var(--rounded-corner); border-radius: var(--rounded-corner);
box-shadow: var(--shadow); box-shadow: var(--shadow);
display: block;
height: 100%; height: 100%;
overflow: hidden;
transition: var(--transition); transition: var(--transition);
width: 100%; width: 100%;
@ -57,9 +55,8 @@ section#comments {
.author { .author {
align-items: center; align-items: center;
cursor: default;
display: flex; display: flex;
font-weight: bold; font-weight: 600;
gap: 0.25rem; gap: 0.25rem;
grid-area: name; grid-area: name;
@ -68,7 +65,6 @@ section#comments {
border-radius: 999px; border-radius: 999px;
color: var(--fg-color); color: var(--fg-color);
font-size: 0.8rem; font-size: 0.8rem;
font-weight: 600;
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
text-decoration: none; text-decoration: none;
transition: var(--transition); transition: var(--transition);
@ -78,8 +74,12 @@ section#comments {
text-decoration: none; text-decoration: none;
} }
&:active {
transform: scale(var(--active));
}
&.op { &.op {
background-color: unset; background-color: transparent;
color: var(--primary-color); color: var(--primary-color);
&:hover { &:hover {
@ -87,116 +87,52 @@ section#comments {
} }
&::before { &::before {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
content: "\F4B5"; content: "\F4B5";
display: inline-block; display: inline-block;
font-family: "bootstrap-icons" !important;
font-style: normal;
font-variant: normal;
font-weight: normal !important;
line-height: 1;
margin-inline-end: 0.25rem; margin-inline-end: 0.25rem;
margin-inline-start: -0.25rem; text-transform: none;
vertical-align: -0.125em;
} }
} }
} }
} }
.emoji {
display: inline;
height: 1.25rem;
vertical-align: middle;
width: 1.25rem;
}
time { time {
@extend small; color: var(--fg-muted-5);
font-size: 0.8rem; font-size: 0.8rem;
grid-area: time; grid-area: time;
&.edited::after {
content: " *";
}
} }
main { main {
grid-area: post; grid-area: post;
justify-self: stretch;
p:first-child {
margin-top: 0.25rem;
}
p:last-child { p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
.card {
color: inherit;
grid-area: card;
max-width: 400px;
text-decoration: none;
&:hover {
text-decoration: none;
}
figure {
border-radius: var(--rounded-corner);
background-color: var(--fg-muted-1);
box-shadow: var(--shadow);
margin-left: 0;
margin-right: 0;
overflow: hidden;
transition: var(--transition);
img {
all: unset;
display: block;
max-width: 100%;
}
&:hover {
transform: translateY(-0.5rem);
}
}
figcaption {
color: var(--fg-color);
display: grid;
gap: 0.5rem;
margin: 0;
padding: 1rem;
text-align: left;
h5 {
font-weight: 600;
all: unset;
font-size: 1rem;
}
p {
font-weight: 400;
color: var(--fg-muted-5);
font-size: 0.8rem;
}
* {
display: inline-block;
margin: 0;
padding: 0;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
}
// Hide the card from the shared post
&:first-of-type .card {
display: none;
}
footer { footer {
display: flex;
gap: 0.5rem;
grid-area: interactions; grid-area: interactions;
margin-top: 1rem; margin-top: 1rem;
.reblogs, .faves {
.favourites { background-color: transparent;
border-radius: var(--rounded-corner); border-radius: var(--rounded-corner);
font-size: 1rem; color: var(--red-fg);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
font-weight: 400; font-weight: 400;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
@ -204,6 +140,7 @@ section#comments {
transition: var(--transition); transition: var(--transition);
&:hover { &:hover {
background-color: var(--red-bg);
text-decoration: none; text-decoration: none;
} }
@ -212,58 +149,20 @@ section#comments {
} }
&::before { &::before {
margin-inline-end: 0.25rem; -moz-osx-font-smoothing: grayscale;
} -webkit-font-smoothing: antialiased;
}
.reblogs {
color: var(--orange-fg);
&:hover {
background-color: var(--orange-bg);
}
&::before {
content: "\F813";
}
}
.favourites {
color: var(--red-fg);
&:hover {
background-color: var(--red-bg);
}
&::before {
content: "\F417"; content: "\F417";
display: inline-block;
font-family: "bootstrap-icons" !important;
font-style: normal;
font-variant: normal;
font-weight: normal !important;
line-height: 1;
margin-inline-end: 0.25rem;
text-transform: none;
vertical-align: -0.125em;
} }
} }
} }
.emoji {
all: unset;
display: inline;
width: 1.25rem;
height: 1.25rem;
vertical-align: middle;
}
.invisible {
display: none;
}
.ellipsis::after {
content: "";
}
details {
summary {
color: var(--yellow-fg);
}
margin-top: 1rem;
background-color: var(--yellow-bg);
}
} }
} }

View File

@ -2,7 +2,8 @@
background-color: white; background-color: white;
float: right; float: right;
height: 155px; height: 155px;
margin: 3rem 0 0 0; margin-left: 1rem;
margin-top: 3rem;
padding: 15px; padding: 15px;
width: 155px; width: 155px;

View File

@ -1,7 +1,4 @@
<!-- <!-- Taken from the https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/ -->
Based on https://github.com/cassidyjames/cassidyjames.github.io/blob/99782788a7e3ba3cc52d6803010873abd1b02b9e/_includes/comments.html
which were inspired by https://codeberg.org/jwildeboer/jwildeboersource/src/commit/45f9750bb53b9f0f6f28399ce4d21785a3bb7d22/_includes/fediverse_comments.html
-->
{% if page.extra.comments.host %} {% if page.extra.comments.host %}
{% set host = page.extra.comments.host %} {% set host = page.extra.comments.host %}
@ -9,123 +6,144 @@ which were inspired by https://codeberg.org/jwildeboer/jwildeboersource/src/comm
{% set host = config.extra.comments.host %} {% set host = config.extra.comments.host %}
{% endif %} {% endif %}
{% if page.extra.comments.domain %}
{% set domain = page.extra.comments.domain %}
{% elif config.extra.comments.domain %}
{% set domain = config.extra.comments.domain %}
{% else %}
{% set domain = host %}
{% endif %}
{% if page.extra.comments.user %} {% if page.extra.comments.user %}
{% set username = page.extra.comments.user %} {% set username = page.extra.comments.user %}
{% else %} {% else %}
{% set username = config.extra.comments.user %} {% set username = config.extra.comments.user %}
{% endif %} {% endif %}
{% if page.extra.comments.token %}
{% set token = page.extra.comments.token %}
{% else %}
{% set token = config.extra.comments.token %}
{% endif %}
{% set id = page.extra.comments.id %} {% set id = page.extra.comments.id %}
{% if config.extra.comments.verified %} <section id="comments">
{% set verified = config.extra.comments.verified | jsonify %} <img id="qrcode" class="no-hover pixels"
{% else %} src="https://api.qrserver.com/v1/create-qr-code/?data=https://{{ host }}/@{{ username }}/{{ id }}">
{% set verified = "[]" %}
{% endif %}
<section id="comments" class="article comments">
<img id="qrcode" class="no-hover pixels" src="https://api.qrserver.com/v1/create-qr-code/?data=https://{{ host }}/@{{ username }}/{{ id }}">
<h2>Comments</h2> <h2>Comments</h2>
<p>Comment on this blog post by publicly replying to <a href="https://{{ host }}/@{{ username }}/{{ id }}">this Mastodon post</a> using a Mastodon or other ActivityPub/Fediverse account. Known non-private replies are displayed below.</p> <p>
You can respond to this <a href="https://{{ host }}/@{{ username }}/{{ id }}">post</a> with an account on the
Fediverse (e.g Mastodon, Akkoma, Sharkey).
</p>
<p>
<small>
Since Fediverse is decentralized, you can use your existing account hosted by a Mastodon or a compatible server if
you
don't have an account on this one. Known non-private replies are displayed below.
</small>
</p>
<p>
<a id="load-comments" class="inline-button" onclick="loadComments()">Load comments…</a>
</p>
<div id="comments-wrapper"> <div id="comments-wrapper">
<p><small>Press the button below to load comments if they exist, if not, be the first!</small></p> <noscript>
<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> <p>
<a onclick="loadComments()" class="inline-button">Load comments…</a> 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> </div>
<noscript>You need JavaScript to view the comments.</noscript>
<script type="text/javascript">
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function emojify(input, emojis) {
let output = input;
<script> emojis.forEach((emoji) => {
// loadComments(); let picture = document.createElement("picture");
function loadComments() { let source = document.createElement("source");
const HOST = "{{ host }}"; source.setAttribute("srcset", escapeHtml(emoji.url));
const DOMAIN = "{{ domain }}"; source.setAttribute("media", "(prefers-reduced-motion: no-preference)");
const USERNAME = "{{ username }}";
const TOKEN = "{{ token }}";
const VERIFIED = "{{ verified }}";
const ID = "{{ id }}";
const SUPPORTED_MEDIA = [ let img = document.createElement("img");
"image", img.className = "emoji";
"gifv", 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");
const STATUS_URL = `https://${ HOST }/api/v1/statuses/${ ID }`; picture.appendChild(source);
picture.appendChild(img);
const REQUEST_HEADERS = new Headers(); output = output.replace(`:${emoji.shortcode}:`, picture.outerHTML);
if(TOKEN != ""){ });
REQUEST_HEADERS.append("Authorization", "Bearer " + TOKEN);
return output;
} }
const requestOptions = { function loadComments() {
method: "GET",
headers: REQUEST_HEADERS,
mode: "cors",
cache: "default",
};
const STATUS_REQUEST = new Request(STATUS_URL, requestOptions);
const CONTEXT_REQUEST = new Request(STATUS_URL + "/context", requestOptions);
let commentsWrapper = document.getElementById("comments-wrapper"); let commentsWrapper = document.getElementById("comments-wrapper");
document.getElementById("load-comments").innerHTML = "Loading…";
fetch(STATUS_REQUEST).then((response) => { fetch("https://{{ host }}/api/v1/statuses/{{ id }}/context")
.then(function (response) {
return response.json(); return response.json();
}).then((status) => { })
fetch(CONTEXT_REQUEST).then((response) => { .then(function (data) {
return response.json(); let descendants = data["descendants"];
}).then((data) => {
let descendants = data['descendants'];
if ( if (
descendants && descendants &&
Array.isArray(descendants) && Array.isArray(descendants) &&
descendants.length > 0 descendants.length > 0
) { ) {
commentsWrapper.innerHTML = ""; commentsWrapper.innerHTML = "";
descendants.unshift(status);
descendants.forEach((status) => { descendants.forEach(function (status) {
console.log(descendants);
if (status.account.display_name.length > 0) { if (status.account.display_name.length > 0) {
status.account.display_name = escapeHtml(status.account.display_name); status.account.display_name = escapeHtml(
status.account.display_name
);
status.account.display_name = emojify( status.account.display_name = emojify(
status.account.display_name, status.account.display_name,
status.account.emojis status.account.emojis
); );
} else { } else {
status.account.display_name = status.account.username; status.account.display_name = status.account.username;
}; }
let instance = ""; let instance = "";
if (status.account.acct.includes("@")) { if (status.account.acct.includes("@")) {
instance = status.account.acct.split("@")[1]; instance = status.account.acct.split("@")[1];
} else { } else {
instance = DOMAIN; 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); status.content = emojify(status.content, status.emojis);
let avatarSource = document.createElement("source"); let avatarSource = document.createElement("source");
avatarSource.setAttribute("srcset", escapeHtml(status.account.avatar)); avatarSource.setAttribute(
avatarSource.setAttribute("media", "(prefers-reduced-motion: no-preference)"); "srcset",
escapeHtml(status.account.avatar)
);
avatarSource.setAttribute(
"media",
"(prefers-reduced-motion: no-preference)"
);
let avatarImg = document.createElement("img"); let avatarImg = document.createElement("img");
avatarImg.className = "avatar"; avatarImg.className = "avatar";
avatarImg.setAttribute("src", escapeHtml(status.account.avatar_static)); avatarImg.setAttribute(
"src",
escapeHtml(status.account.avatar_static)
);
avatarImg.setAttribute( avatarImg.setAttribute(
"alt", "alt",
`@${status.account.username}@${instance} avatar` `@${status.account.username}@${instance} avatar`
@ -171,7 +189,9 @@ which were inspired by https://codeberg.org/jwildeboer/jwildeboersource/src/comm
permalink.setAttribute("itemprop", "url"); permalink.setAttribute("itemprop", "url");
permalink.setAttribute("title", `View comment at ${instance}`); permalink.setAttribute("title", `View comment at ${instance}`);
permalink.setAttribute("rel", "external nofollow"); permalink.setAttribute("rel", "external nofollow");
permalink.textContent = new Date(status.created_at).toLocaleString('en-IE', { permalink.textContent = new Date(
status.created_at
).toLocaleString("en-IE", {
dateStyle: "long", dateStyle: "long",
timeStyle: "short", timeStyle: "short",
}); });
@ -180,150 +200,33 @@ which were inspired by https://codeberg.org/jwildeboer/jwildeboersource/src/comm
timestamp.setAttribute("datetime", status.created_at); timestamp.setAttribute("datetime", status.created_at);
timestamp.appendChild(permalink); timestamp.appendChild(permalink);
if(status.edited_at != null) {
timestamp.classList.add("edited");
timestamp.setAttribute(
"title",
"Edited " + new Date(status.edited_at).toLocaleString('en-IE', {
dateStyle: "long",
timeStyle: "short",
})
)
};
let main = document.createElement("main"); let main = document.createElement("main");
main.setAttribute("itemprop", "text"); main.setAttribute("itemprop", "text");
if(status.sensitive == true || status.spoiler_text != "") {
let summary = document.createElement("summary");
if(status.spoiler_text == "") {
status.spoiler_text == "Sensitive";
}
summary.innerHTML = status.spoiler_text;
let spoiler = document.createElement("details");
spoiler.appendChild(summary);
spoiler.innerHTML += status.content;
main.appendChild(spoiler);
} else {
main.innerHTML = status.content; main.innerHTML = status.content;
}
let interactions = document.createElement("footer"); let interactions = document.createElement("footer");
if(status.reblogs_count > 0) { let faves = document.createElement("a");
let reblogs = document.createElement("a"); faves.className = "faves";
reblogs.setAttribute("href", status.url + "/reblogs"); faves.setAttribute("href", `${status.url}/favourites`);
reblogs.className = "reblogs"; faves.setAttribute("title", `Favorites from ${instance}`);
reblogs.setAttribute("title", "Boosts"); faves.textContent = status.favourites_count;
reblogs.textContent = status.reblogs_count; interactions.appendChild(faves);
interactions.appendChild(reblogs);
}
if(status.favourites_count > 0) {
let favourites = document.createElement("a");
favourites.setAttribute("href", status.url + "/favourites");
favourites.className = "favourites";
favourites.setAttribute("title", "Favorites");
favourites.textContent = status.favourites_count;
interactions.appendChild(favourites);
}
let comment = document.createElement("article"); let comment = document.createElement("article");
comment.id = `comment-${status.id}`; comment.id = `comment-${status.id}`;
comment.className = "comment"; comment.className = isReply ? "comment comment-reply" : "comment";
comment.setAttribute("itemprop", "comment"); comment.setAttribute("itemprop", "comment");
comment.setAttribute("itemtype", "http://schema.org/Comment"); comment.setAttribute("itemtype", "http://schema.org/Comment");
comment.appendChild(avatar); comment.appendChild(avatar);
comment.appendChild(header); comment.appendChild(header);
comment.appendChild(timestamp); comment.appendChild(timestamp);
comment.appendChild(main); comment.appendChild(main);
if (status.favourites_count > 0) {
let attachments = status.media_attachments;
if(
attachments &&
Array.isArray(attachments) &&
attachments.length > 0
) {
attachments.forEach((attachment) => {
if( SUPPORTED_MEDIA.includes(attachment.type) ){
let media = document.createElement("a");
media.className = "card";
media.setAttribute("href", attachment.url);
media.setAttribute("rel", "external nofollow");
let mediaElement;
switch(attachment.type){
case "image":
mediaElement = document.createElement("img");
mediaElement.setAttribute("src", attachment.preview_url);
if(attachment.description != null) {
mediaElement.setAttribute("alt", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
media.appendChild(mediaElement);
break;
case "gifv":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("autoplay", "");
mediaElement.setAttribute("playsinline", "");
mediaElement.setAttribute("loop", "");
if(attachment.description != null) {
mediaElement.setAttribute("aria-title", attachment.description);
mediaElement.setAttribute("title", attachment.description);
}
media.appendChild(mediaElement);
break;
}
comment.appendChild(media);
}
});
} else if(
status.card != null &&
status.card.image != null &&
!status.card.url.startsWith("{{ get_url(path='') }}")
) {
let cardImg = document.createElement("img");
cardImg.setAttribute("src", status.card.image);
let cardTitle = document.createElement("h5");
cardTitle.innerHTML = status.card.title;
let cardDescription = document.createElement("p");
cardDescription.innerHTML = status.card.description;
let cardCaption = document.createElement("figcaption");
cardCaption.appendChild(cardTitle);
cardCaption.appendChild(cardDescription);
let cardFigure = document.createElement("figure");
cardFigure.appendChild(cardImg);
cardFigure.appendChild(cardCaption);
let card = document.createElement("a");
card.className = "card";
card.setAttribute("href", status.card.url);
card.setAttribute("rel", "external nofollow");
card.appendChild(cardFigure);
comment.appendChild(card);
}
comment.appendChild(interactions); comment.appendChild(interactions);
}
if(status.account.acct == USERNAME) { if (op === true) {
comment.classList.add("op"); comment.classList.add("op");
avatar.classList.add("op"); avatar.classList.add("op");
@ -337,64 +240,14 @@ which were inspired by https://codeberg.org/jwildeboer/jwildeboersource/src/comm
"title", "title",
"Blog post author: " + instanceBadge.getAttribute("title") "Blog post author: " + instanceBadge.getAttribute("title")
); );
} else if( VERIFIED.includes(status.account.acct) ) {
comment.classList.add("verified");
avatar.classList.add("verified");
avatar.setAttribute(
"title",
avatar.getAttribute("title") + " (verified by site owner)"
);
instanceBadge.classList.add("verified");
instanceBadge.setAttribute(
"title",
instanceBadge.getAttribute("title") + " (verified by site owner)"
);
} }
commentsWrapper.innerHTML += comment.outerHTML; commentsWrapper.innerHTML += comment.outerHTML;
}); });
} }
let loadCommentsButton = document.getElementById("load-comments");
loadCommentsButton.remove();
}); });
});
}
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 escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/'/g, "&apos;")
.replace(/"/g, "&quot;")
;
} }
</script> </script>
</section> </section>