~admicos/ecmelberk.com unlisted

b81c454f6f8fe67ca065bea1c30bf5b0811551cb — Ecmel Berk Canlier 3 months ago 07bc40f
Add comment system backed by my Pleroma instance
4 files changed, 166 insertions(+), 1 deletions(-)

M assets/blog.css
A assets/discuss.js
M layouts/_default/single.html
A layouts/partials/discuss.html
M assets/blog.css => assets/blog.css +61 -1
@@ 30,7 30,7 @@ code:not([class^=language]) {
}

footer {
    margin-top: 64px;
    margin-top: 35px;

    text-align: center;
    font-size: 12px;


@@ 75,3 75,63 @@ h1, h2, h3, h4, h5, h6 {
    color: inherit;
    text-decoration: none;
}

.discuss {
    margin-top: 50px;
}

.discuss > .header {
    font-size: 22px;
}

#discuss {
    margin-top: 15px;
}

#discuss > .discuss-status {
    display: block; /* required for text-align */
    text-align: center;

    font-style: italic;
    font-size: 14px;
}

#discuss > .discuss-post {
    margin-top: 34px;
}

#discuss > .discuss-post > p {
    margin: 10px 0;
}

#discuss > .discuss-post > .discuss-post-owner > b {
    font-size: 20px;
}

#discuss > .discuss-post > .discuss-post-owner > i {
    margin-left: 8px;
    font-size: 14px;
}

@media screen and (min-width: 801px) {
    #discuss > .discuss-post-by-author > .discuss-post-owner > b::before {
        content: "\2713";

        border: 2px solid;
        border-radius: 8px;

        margin-left: -29px;
        margin-right: 8px;
    }
}

@media screen and (max-width: 800px) {
    #discuss > .discuss-post-by-author > .discuss-post-owner > i::after {
        content: "\2713";

        border: 2px solid;
        border-radius: 8px;

        margin-left: 8px;
    }
}

A assets/discuss.js => assets/discuss.js +84 -0
@@ 0,0 1,84 @@
const INSTANCE = "fedi.ecmelberk.com";
const APIROOT = `https://${INSTANCE}/api/v1/`;

async function apicall(endpoint) {
    let data = await fetch(APIROOT + endpoint);
    let json = await data.json();

    return json;
}

function el(tag, attrs, children) {
    let e = document.createElement(tag);

    Object.entries(attrs).forEach(([attr, val]) => e.setAttribute(attr, val));
    e.append.apply(e, children);

    return e;
}

function textel(tag, attrs, text) {
    return el(tag, attrs, [document.createTextNode(text)]);
}

function makePostEl(post) {
    let author = post.account;

    let authorName = author.display_name;
    let authorNick = author.acct;

    // This line assumes a single-user Pleroma instance. You can check against
    // authorNick or something like that for your own case
    let authorIsAdmin = author.pleroma.is_admin;

    // This is Pleroma specific, use post.content if you can deal with raw HTML
    let content = post.pleroma.content["text/plain"];

    let postElClasses = "";
    if (authorIsAdmin) {
        postElClasses = "discuss-post-by-author";

        // The API doesn't return complete nick if the user is from the same instance.
        authorNick += `@${INSTANCE}`;
        authorName = "Post Author";
    }

    return el("div", {class: `discuss-post ${postElClasses}`}, [
        el("span", {class: "discuss-post-owner"}, [
            textel("b", {}, authorName), textel("i", {}, `@${authorNick}`),
        ]),

        textel("p", {}, content),
    ]);
}

async function main(discuss) {
    discuss.status("Please wait while the discussion loads...");

    let toot_id = discuss.dataset.toot;

    let data = await apicall(`/statuses/${toot_id}/context`);
    let replies = data.descendants;

    if (!Array.isArray(replies) || !replies.length) {
        discuss.status("Nobody seems to be discussing this post.");
        return;
    }

    discuss.querySelector(".discuss-status").remove();
    replies.forEach(reply => discuss.append(makePostEl(reply)));
}

document.addEventListener("DOMContentLoaded", _ => {
    let discussionContainer = document.querySelector("#discuss[data-toot]");
    let statusContainer = discussionContainer.appendChild(textel("span", {class: "discuss-status"}, ""));

    discussionContainer.status = function (str) {
        statusContainer.textContent = str;
    }

    main(discussionContainer).catch(e => {
        console.error("Discussion main error", e);
        discussionContainer.status(`Error loading discussion: ${e}`);
    });
});

M layouts/_default/single.html => layouts/_default/single.html +2 -0
@@ 18,6 18,8 @@

{{ .Content }}

{{ partial "discuss.html" . }}

<footer>
    <hr>
    <a href="/">Homepage & Contact Info</a>

A layouts/partials/discuss.html => layouts/partials/discuss.html +19 -0
@@ 0,0 1,19 @@
{{ if isset .Params "discussion_toot" }}
{{ $discuss_js := resources.Get "discuss.js" | resources.Minify }}
{{ $toot_id := .Params.discussion_toot }}

<div class="discuss">
    <hr>

    <span class="header">
        Discuss
        <a href="https://fedi.ecmelberk.com/notice/{{ $toot_id }}">
            in the fediverse
        </a>
    </span>

    <div id="discuss" data-toot="{{ $toot_id }}"></div>
</div>

<script src="{{ $discuss_js.Permalink }}"></script>
{{ end }}