Commit 2be132ad authored by Carl Schwan's avatar Carl Schwan

Add comments

parent 9215cbef
......@@ -83,10 +83,10 @@
font-weight: 600;
margin: 10px 0;
color: var(--card-text-color-main);
font-size: 2.2rem;
font-size: 3rem;
@media (min-width: $on-desktop-large) {
font-size: 2.4rem;
font-size: 3.2rem;
}
a {
......@@ -281,3 +281,44 @@
}
}
}
.button, button {
padding: 1rem;
border-radius: 4px;
border-width: 0;
background-color: var(--code-background-color);
&:hover {
background-color: var(--body-background);
}
}
.mastodon-comment {
background-color: var(--body-background);
border-radius: var(--card-border-radius);
padding: var(--card-padding);
margin-bottom: 1rem;
display: flex;
.content {
flex-grow: 2;
}
.avatar img {
margin-right: 1rem;
min-width: 60px;
}
.author {
padding-top: 0;
display: flex;
.date {
margin-left: auto;
}
}
.disabled {
color: var(--accent-color)
}
}
.mastodon-comment-content p:first-child {
margin-top: 0;
}
......@@ -36,6 +36,7 @@ a {
}
&.link {
text-decoration: underline;
box-shadow: 0px -2px 0px rgba(var(--link-background-color), var(--link-background-opacity)) inset;
transition: all 0.3s ease;
......
......@@ -19,7 +19,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--accent-color: #34495e;
--accent-color-darker: #2c3e50;
--accent-color-text: #fff;
--body-text-color: #bababa;
--body-text-color: #555;
--tag-border-radius: 4px;
......@@ -38,10 +38,10 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
* Global font family
*/
:root {
--sys-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Droid Sans", "Helvetica Neue";
--sys-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--zh-font-family: "PingFang SC", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei";
--base-font-family: "Lato", var(--sys-font-family), var(--zh-font-family);
--base-font-family: var(--sys-font-family), var(--zh-font-family);
--code-font-family: Menlo, Monaco, Consolas, "Courier New";
}
......
......@@ -5,6 +5,10 @@ locale: en
subtitle: Hugo and Gettext, a love story?
title: KDE.org migrated to Hugo
image: heading_hugo.png
comments:
host: linuxrocks.online
username: carl
id: 105105837504372590
---
[KDE.org](https://kde.org) now uses [Hugo](https://gohugo.io). Hugo is a fast and modern static site
......
......@@ -2,7 +2,6 @@
date: "2020-12-23T00:00:00Z"
title: Announcing NeoChat 1.0, the KDE Matrix client
hideHeaderImage: true
draft: true
categories:
- KDE
- Blog
......@@ -11,6 +10,10 @@ tags:
- NeoChat
- KDE
image: neochat-drawer-open.png
comments:
host: mastodon.technology
username: kde
id: 105429538591797190
---
Matrix is an instant messaging system similar to Whatsapp or Telegram, but
......@@ -62,7 +65,8 @@ A lot of care has been put into making NeoChat intuitive to use. For
example, copying with Ctrl+C and dragging and dropping images just work; and
the text field gets autofocused so that you are never writing into the void.
NeoChat also integrates an emoji picker, letting you use the greatest invention
of the 21st century.
of the <del>21st century</del>. (Note: someone in Mastodon pointed out that
Emojis are from the 20th century and appeared in 1997 in Japan.)
## Image Editor
......@@ -90,7 +94,7 @@ libQuotient library to interact with the Matrix protocol. We would like to send
out a huge thank you to these two projects and their contributors. Without
them, NeoChat wouldn't have been possible.
NeoChat uses the [Kirigami framework](https://develop.kde.org/framework/kirigami)
NeoChat uses the [Kirigami framework](https://develop.kde.org/frameworks/kirigami)
and QML to provide an elegant and convergent user interface.
## Translations
......@@ -121,8 +125,11 @@ get a cool KDE T-Shirt, feel free to say hi.
## Tarballs
Unfortunally there was an error generating the tarbals, we are working on it
and new tarbals will be created shortly.
Version 1.0 of NeoChat is availabe [here](https://download.kde.org/stable/neochat/1.0/neochat-1.0.tar.xz),
kquickimageeditor 0.1 is availabe [here](https://download.kde.org/stable/kquickimageeditor/0.1/kquickimageeditor-0.1.tar.xz).
kquickimageeditor 0.1.2 is availabe [here](https://download.kde.org/stable/kquickimageeditor/0.1/kquickimageeditor-0.1.2.tar.xz).
Both packages are signed with my gpg key [14B0ED91B5783415D0AA1E0A06B35D38387B67BE](/gpg.html).
A Flathub release will hopefully be released in the next few days. We will
......
---
title: Adding comments to your static blog with Mastodon
date: "2020-12-29T12:00:00Z"
comments:
host: linuxrocks.online
username: carl
id: 105463655803971969
---
One of the biggest disadvantages of static site generators is that
they are static and can't include comments.
There are multiples solutions to solve this problem. You could add
a third party blog engine like Disqus, but this has the drawback
of including a third-party tool with a bad privacy record in your
website. Another solution would be to host an open-source alternative
but this comes at the cost of a higher maintenance burden. Having
to host a database was something we wanted to avoid with a
static site generator.
In my opinion, a better solution is to leverage the Mastodon and
Fediverse platform. Mastodon is a decentralized social network
and it allows people to communicate with each other without
being on the same server. It is inspired by Twitter, but instead
of tweeting, you write toot.
When publishing an article, you now only need to also write a
simple toot linking to your article. Then Mastodon has a simple
API to fetch the answer to your toot. This is the code I made for
my Hugo powered blog, but it is easily adaptable for other static
site generators. It will create a button to load comments instead
of loading them for every visitor so that it decreases the load on your
mastodon server.
```html
{{ with .Params.comments }}
<div class="article-content">
<h2>Comments</h2>
<p>You can use your Mastodon account to reply to this <a class="link" href="https://{{ .host }}/@{{ .username }}/{{ .id }}">post</a>.</p>
<p><a class="button" href="https://{{ .host }}/interact/{{ .id }}?type=reply">Reply</a></p>
<p id="mastodon-comments-list"><button id="load-comment">Load comments</button></p>
<noscript><p>You need JavaScript to view the comments.</p></noscript>
<script src="/assets/js/purify.min.js"></script>
<script type="text/javascript">
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
document.getElementById("load-comment").addEventListener("click", function() {
document.getElementById("load-comment").innerHTML = "Loading";
fetch('https://{{ .host }}/api/v1/statuses/{{ .id }}/context')
.then(function(response) {
return response.json();
})
.then(function(data) {
if(data['descendants'] &&
Array.isArray(data['descendants']) &&
data['descendants'].length > 0) {
document.getElementById('mastodon-comments-list').innerHTML = "";
data['descendants'].forEach(function(reply) {
reply.account.display_name = escapeHtml(reply.account.display_name);
reply.account.emojis.forEach(emoji => {
reply.account.display_name = reply.account.display_name.replace(`:${emoji.shortcode}:`,
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" height="20" width="20" />`);
});
mastodonComment =
`<div class="mastodon-comment">
<div class="avatar">
<img src="${escapeHtml(reply.account.avatar_static)}" height=60 width=60 alt="">
</div>
<div class="content">
<div class="author">
<a href="${reply.account.url}" rel="nofollow">
<span>${reply.account.display_name}</span>
<span class="disabled">${escapeHtml(reply.account.acct)}</span>
</a>
<a class="date" href="${reply.uri}" rel="nofollow">
${reply.created_at.substr(0, 10)}
</a>
</div>
<div class="mastodon-comment-content">${reply.content}</div>
</div>
</div>`;
document.getElementById('mastodon-comments-list').appendChild(DOMPurify.sanitize(mastodonComment, {'RETURN_DOM_FRAGMENT': true}));
});
} else {
document.getElementById('mastodon-comments-list').innerHTML = "<p>Not comments found</p>";
}
});
});
</script>
</div>
{{ end }}
```
This code is using [DOMPurify](https://github.com/cure53/DOMPurify)
to sanitize the input, since it is not a great idea to load data from
third party sources without sanitizing them first. Also thanks to
[chrismorgan](https://news.ycombinator.com/item?id=25575111), the code
was optimized and is more secure.
In my blog post, I can now add the following information to my
frontmatter, to make comments appears magically.
```yaml
comments:
host: linuxrocks.online
username: carl
id: 105105837504372590
```
<article class="{{ if .Params.image }}has-image {{ end }}main-article">
{{ partial "article/components/header" . }}
{{ partial "article/components/header" . }}
{{ partial "article/components/content" . }}
{{ partial "article/components/content" . }}
{{ partial "article/components/footer" . }}
{{ with .Params.comments }}
<div class="article-content">
<h2>Comments</h2>
<p>You can use your Mastodon account to reply to this <a class="link" href="https://{{ .host }}/@{{ .username }}/{{ .id }}">post</a>.</p>
<p><a class="button" href="https://{{ .host }}/interact/{{ .id }}?type=reply">Reply</a></p>
<p id="mastodon-comments-list"><button id="load-comment">Load comments</button></p>
<noscript>You need JavaScript to view the comments.</noscript>
<script src="/assets/js/purify.min.js"></script>
<script type="text/javascript">
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
{{ if or .Params.math .Site.Params.article.math }}
{{ partialCached "article/components/math.html" . }}
{{ end }}
</article>
\ No newline at end of file
document.getElementById("load-comment").addEventListener("click", function() {
document.getElementById("load-comment").innerHTML = "Loading";
fetch('https://{{ .host }}/api/v1/statuses/{{ .id }}/context')
.then(function(response) {
return response.json();
})
.then(function(data) {
if(data['descendants'] &&
Array.isArray(data['descendants']) &&
data['descendants'].length > 0) {
document.getElementById('mastodon-comments-list').innerHTML = "";
data['descendants'].forEach(function(reply) {
reply.account.display_name = escapeHtml(reply.account.display_name);
reply.account.emojis.forEach(emoji => {
reply.account.display_name = reply.account.display_name.replace(`:${emoji.shortcode}:`,
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" height="20" width="20" />`);
});
mastodonComment =
`<div class="mastodon-comment">
<div class="avatar">
<img src="${escapeHtml(reply.account.avatar_static)}" height=60 width=60 alt="">
</div>
<div class="content">
<div class="author">
<a href="${reply.account.url}" rel="nofollow">
<span>${reply.account.display_name}</span>
<span class="disabled">${escapeHtml(reply.account.acct)}</span>
</a>
<a class="date" href="${reply.uri}" rel="nofollow">
${reply.created_at.substr(0, 10)}
</a>
</div>
<div class="mastodon-comment-content">${reply.content}</div>
</div>
</div>`;
document.getElementById('mastodon-comments-list').appendChild(DOMPurify.sanitize(mastodonComment, {'RETURN_DOM_FRAGMENT': true}));
});
} else {
document.getElementById('mastodon-comments-list').innerHTML = "<p>Not comments found</p>";
}
});
});
</script>
</div>
{{ end }}
{{ partial "article/components/footer" . }}
{{ if or .Params.math .Site.Params.article.math }}
{{ partialCached "article/components/math.html" . }}
{{ end }}
</article>
{{ $sass := resources.Get "scss/style.scss" }}
{{ $style := $sass | resources.ToCSS | minify }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
\ No newline at end of file
{{ $style := $sass | resources.ToCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}?{{ $style.Data.Integrity }}">
......@@ -18,4 +18,4 @@
{{ partialCached "footer/footer" . }}
{{- partialCached "article/components/photoswipe.html" . -}}
{{ end }}
\ No newline at end of file
{{ end }}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment