diff --git a/font_awesome.h b/font_awesome.h
new file mode 100644
index 0000000..ec65ce5
--- /dev/null
+++ b/font_awesome.h
@@ -0,0 +1,10 @@
+/*
+ * The icons here are from the free edition of Font Awesome, which are created
+ * by Fonticons, Inc. and is available under the CC-BY 4.0 license.
+ *
+ * https://fontawesome.com/license/free#icons
+ * https://creativecommons.org/licenses/by/4.0/
+ */
+
+const char fa_retweet[] = R"EOF()EOF";
+const char fa_reply[] = R"EOF()EOF";
diff --git a/routes/css.cpp b/routes/css.cpp
index 9bc0f45..9b7f856 100644
--- a/routes/css.cpp
+++ b/routes/css.cpp
@@ -49,13 +49,17 @@ a:hover {
color: var(--bright-accent-color);
}
-/* CUSTOM EMOJI */
-.custom_emoji {
+/* CUSTOM EMOJI and FONT AWESOME ICONS */
+.custom_emoji, svg {
height: 1em;
width: 1em;
/* https://stackoverflow.com/a/489394 */
vertical-align: middle;
}
+/* FONT AWESOME ICONS */
+svg {
+ filter: invert(1);
+}
/* POST */
.post {
diff --git a/servehelper.cpp b/servehelper.cpp
index 015eb7e..c083bbb 100644
--- a/servehelper.cpp
+++ b/servehelper.cpp
@@ -4,6 +4,7 @@
#include
#include
+#include "font_awesome.h"
#include "config.h"
#include "models.h"
#include "timeutils.h"
@@ -17,6 +18,12 @@ static inline void preprocess_link(const httplib::Request& req, const std::strin
static inline bool should_fix_link(lxb_dom_element_t* element, const std::string& cls);
static inline lxb_dom_node_t* emojify(lxb_dom_node_t* child, const std::vector& emojis);
static inline std::vector emojify(lxb_dom_document_t* document, std::string str, const std::vector& emojis);
+
+struct PostStatus {
+ const char* icon_html;
+ Node info_node;
+};
+static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, const std::optional& post_status);
static inline Element serialize_media(const Media& media);
class CurlUrlException : public std::exception {
@@ -169,47 +176,20 @@ Element serialize_post(const httplib::Request& req, const std::string& server, c
using namespace std::string_literals;
if (post.reblog) {
- return serialize_post(req, server, *post.reblog);
+ PostStatus post_status = {
+ fa_retweet,
+ preprocess_html(req, post.account.emojis, post.account.display_name + " boosted"),
+ };
+ return serialize_post(req, server, *post.reblog, post_status);
+ } else if (post.in_reply_to_id && post.in_reply_to_account_id && post.account.id == *post.in_reply_to_account_id) {
+ PostStatus post_status = {
+ fa_reply,
+ preprocess_html(req, post.account.emojis, "Replied to "s + post.account.display_name),
+ };
+ return serialize_post(req, server, post, post_status);
+ } else {
+ return serialize_post(req, server, post, std::nullopt);
}
-
- std::string time_title = post.edited_at < 0
- ? full_time(post.created_at)
- : "Created: "s + full_time(post.created_at) + "\nEdited: " + full_time(post.edited_at);
- const char* time_badge = post.edited_at < 0 ? "" : " (edited)";
-
- Element contents("div", {preprocess_html(req, server, post.emojis, post.content)});
- Element post_attachments("div", {{"class", "post-attachments"}}, {});
- post_attachments.nodes.reserve(post.media_attachments.size());
- for (const Media& media : post.media_attachments) {
- post_attachments.nodes.push_back(serialize_media(media));
- }
- contents.nodes.push_back(std::move(post_attachments));
-
- if (post.sensitive) {
- std::string spoiler_text = !post.spoiler_text.empty() ? post.spoiler_text : "See more";
- contents = Element("details", {
- Element("summary", {preprocess_html(req, post.emojis, std::move(spoiler_text))}),
- std::move(contents),
- });
- }
-
- Element div("div", {{"class", "post"}}, {
- Element("div", {{"class", "post-header"}}, {
- Element("a", {{"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false)}}, {
- Element("img", {{"class", "post-avatar"}, {"alt", "User profile picture"}, {"src", post.account.avatar_static}}, {}),
- Element("span", {
- Element("b", {preprocess_html(req, post.account.emojis, post.account.display_name)}),
- Element("br"), "@", post.account.acct(),
- }),
- }),
- Element("a", {{"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false) + '/' + post.id}, {"title", time_title}}, {
- Element("time", {{"datetime", to_rfc3339(post.created_at)}}, {relative_time(post.created_at, current_time()), time_badge}),
- }),
- }),
-
- contents,
- });
- return div;
}
blankie::html::HTMLString preprocess_html(const httplib::Request& req, const std::string& domain_name, const std::vector& emojis, const blankie::html::HTMLString& str) {
@@ -375,6 +355,55 @@ static inline std::vector emojify(lxb_dom_document_t* document,
return res;
}
+static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, const std::optional& post_status) {
+ using namespace std::string_literals;
+
+ std::string time_title = post.edited_at < 0
+ ? full_time(post.created_at)
+ : "Created: "s + full_time(post.created_at) + "\nEdited: " + full_time(post.edited_at);
+ const char* time_badge = post.edited_at < 0 ? "" : " (edited)";
+
+ Element contents("div", {preprocess_html(req, server, post.emojis, post.content)});
+ Element post_attachments("div", {{"class", "post-attachments"}}, {});
+ post_attachments.nodes.reserve(post.media_attachments.size());
+ for (const Media& media : post.media_attachments) {
+ post_attachments.nodes.push_back(serialize_media(media));
+ }
+ contents.nodes.push_back(std::move(post_attachments));
+
+ if (post.sensitive) {
+ std::string spoiler_text = !post.spoiler_text.empty() ? post.spoiler_text : "See more";
+ contents = Element("details", {
+ Element("summary", {preprocess_html(req, post.emojis, std::move(spoiler_text))}),
+ std::move(contents),
+ });
+ }
+
+ Element div("div", {{"class", "post"}}, {
+ Element("div", {{"class", "post-header"}}, {
+ Element("a", {{"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false)}}, {
+ Element("img", {{"class", "post-avatar"}, {"alt", "User profile picture"}, {"src", post.account.avatar_static}}, {}),
+ Element("span", {
+ Element("b", {preprocess_html(req, post.account.emojis, post.account.display_name)}),
+ Element("br"), "@", post.account.acct(),
+ }),
+ }),
+ Element("a", {{"href", get_origin(req) + '/' + server + "/@" + post.account.acct(false) + '/' + post.id}, {"title", time_title}}, {
+ Element("time", {{"datetime", to_rfc3339(post.created_at)}}, {relative_time(post.created_at, current_time()), time_badge}),
+ }),
+ }),
+
+ contents,
+ });
+ if (post_status) {
+ div.nodes.insert(div.nodes.begin(), Element("p", {
+ blankie::html::HTMLString(post_status->icon_html), " ", post_status->info_node,
+ }));
+ }
+
+ return div;
+}
+
static inline Element serialize_media(const Media& media) {
Element element = [&]() {
if (media.type == "image") {