Add custom emoji support
This commit is contained in:
parent
462dbb1b10
commit
a090aeabcd
|
@ -41,6 +41,11 @@ a:hover {
|
|||
color: var(--bright-accent-color);
|
||||
}
|
||||
|
||||
.custom_emoji {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
/* ERROR PAGE */
|
||||
.error {
|
||||
text-align: center;
|
||||
|
@ -79,6 +84,7 @@ a:hover {
|
|||
.user_page-user_description {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ static const char* sorting_method_suffixes[3] = {"", "/with_replies", "/media"};
|
|||
static inline PostSortingMethod get_sorting_method(const std::string& method);
|
||||
|
||||
static inline Element user_header(const httplib::Request& req, const Account& account, PostSortingMethod sorting_method);
|
||||
static inline Element user_link_field(const httplib::Request& req, const std::string& domain_name, const AccountField& field);
|
||||
static inline Element user_link_field(const httplib::Request& req, const Account& account, const AccountField& field);
|
||||
static inline Element sorting_method_link(const httplib::Request& req, const Account& account, PostSortingMethod current_method, PostSortingMethod new_method);
|
||||
|
||||
|
||||
|
@ -59,7 +59,7 @@ static inline Element user_header(const httplib::Request& req, const Account& ac
|
|||
Element user_links("table", {{"class", "user_page-user_links"}}, {});
|
||||
user_links.nodes.reserve(account.fields.size());
|
||||
for (const AccountField& i : account.fields) {
|
||||
user_links.nodes.push_back(user_link_field(req, account.domain_name, i));
|
||||
user_links.nodes.push_back(user_link_field(req, account, i));
|
||||
}
|
||||
|
||||
Element header("header", {
|
||||
|
@ -82,7 +82,7 @@ static inline Element user_header(const httplib::Request& req, const Account& ac
|
|||
}),
|
||||
|
||||
Element("div", {{"class", "user_page-user_description"}}, {
|
||||
Element("div", {{"class", "user_page-user_bio"}}, {preprocess_html(req, account.domain_name, account.note_html)}),
|
||||
Element("div", {{"class", "user_page-user_bio"}}, {preprocess_html(req, account.domain_name, account.emojis, account.note_html)}),
|
||||
|
||||
std::move(user_links),
|
||||
}),
|
||||
|
@ -96,12 +96,12 @@ static inline Element user_header(const httplib::Request& req, const Account& ac
|
|||
return header;
|
||||
}
|
||||
|
||||
static inline Element user_link_field(const httplib::Request& req, const std::string& domain_name, const AccountField& field) {
|
||||
static inline Element user_link_field(const httplib::Request& req, const Account& account, const AccountField& field) {
|
||||
using namespace std::string_literals;
|
||||
|
||||
Element tr("tr", {
|
||||
Element("th", {field.name}),
|
||||
Element("td", {preprocess_html(req, domain_name, field.value_html)}),
|
||||
Element("td", {preprocess_html(req, account.domain_name, account.emojis, field.value_html)}),
|
||||
});
|
||||
if (field.verified_at >= 0) {
|
||||
struct tm verified_at;
|
||||
|
|
|
@ -5,13 +5,16 @@
|
|||
#include <curl/curl.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "models.h"
|
||||
#include "servehelper.h"
|
||||
#include "lxb_wrapper.h"
|
||||
#include "routes/routes.h"
|
||||
|
||||
static inline void preprocess_html(const httplib::Request& req, const std::string& domain_name, lxb_dom_element_t* element);
|
||||
static inline void preprocess_html(const httplib::Request& req, const std::string& domain_name, const std::vector<Emoji>& emojis, lxb_dom_element_t* element);
|
||||
static inline void preprocess_link(const httplib::Request& req, const std::string& domain_name, lxb_dom_element_t* element);
|
||||
static inline bool should_fix_link(lxb_dom_element_t* element);
|
||||
static inline lxb_dom_node_t* emojify(lxb_dom_node_t* child, const std::vector<Emoji>& emojis);
|
||||
static inline std::vector<lxb_dom_node*> emojify(lxb_dom_document_t* document, std::string str, const std::vector<Emoji>& emojis);
|
||||
|
||||
class CurlUrlException : public std::exception {
|
||||
public:
|
||||
|
@ -159,15 +162,15 @@ bool should_send_304(const httplib::Request& req, uint64_t hash) {
|
|||
return pos != std::string::npos && (pos == 0 || header[pos - 1] != '/');
|
||||
}
|
||||
|
||||
blankie::html::HTMLString preprocess_html(const httplib::Request& req, const std::string& domain_name, const blankie::html::HTMLString& str) {
|
||||
blankie::html::HTMLString preprocess_html(const httplib::Request& req, const std::string& domain_name, const std::vector<Emoji>& emojis, const blankie::html::HTMLString& str) {
|
||||
LXB::HTML::Document document(str.str);
|
||||
preprocess_html(req, domain_name, document.body_element());
|
||||
preprocess_html(req, domain_name, emojis, document.body_element());
|
||||
return blankie::html::HTMLString(document.serialize());
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline void preprocess_html(const httplib::Request& req, const std::string& domain_name, lxb_dom_element_t* element) {
|
||||
static inline void preprocess_html(const httplib::Request& req, const std::string& domain_name, const std::vector<Emoji>& emojis, lxb_dom_element_t* element) {
|
||||
const char* tag_name = reinterpret_cast<const char*>(lxb_dom_element_tag_name(element, nullptr));
|
||||
|
||||
if (strncmp(tag_name, "A", 2) == 0) {
|
||||
|
@ -179,7 +182,9 @@ static inline void preprocess_html(const httplib::Request& req, const std::strin
|
|||
lxb_dom_node_t* child = lxb_dom_node_first_child(lxb_dom_interface_node(element));
|
||||
while (child) {
|
||||
if (child->type == LXB_DOM_NODE_TYPE_ELEMENT) {
|
||||
preprocess_html(req, domain_name, lxb_dom_interface_element(child));
|
||||
preprocess_html(req, domain_name, emojis, lxb_dom_interface_element(child));
|
||||
} else if (child->type == LXB_DOM_NODE_TYPE_TEXT) {
|
||||
child = emojify(child, emojis);
|
||||
}
|
||||
|
||||
child = lxb_dom_node_next(child);
|
||||
|
@ -247,3 +252,55 @@ static inline bool should_fix_link(lxb_dom_element_t* element) {
|
|||
child = lxb_dom_node_next(child);
|
||||
return child == nullptr;
|
||||
}
|
||||
|
||||
static inline lxb_dom_node_t* emojify(lxb_dom_node_t* child, const std::vector<Emoji>& emojis) {
|
||||
size_t text_content_len;
|
||||
const char* text_content = reinterpret_cast<const char*>(lxb_dom_node_text_content(child, &text_content_len));
|
||||
|
||||
std::vector<lxb_dom_node_t*> nodes = emojify(child->owner_document, std::string(text_content, text_content_len), emojis);
|
||||
for (lxb_dom_node_t* node : nodes) {
|
||||
lxb_dom_node_insert_after(child, node);
|
||||
lxb_dom_node_destroy(child);
|
||||
child = node;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
static std::regex shortcode_re(":([a-zA-Z0-9_]+):");
|
||||
static inline std::vector<lxb_dom_node_t*> emojify(lxb_dom_document_t* document, std::string str, const std::vector<Emoji>& emojis) {
|
||||
std::string buf;
|
||||
std::smatch sm;
|
||||
std::vector<lxb_dom_node*> res;
|
||||
|
||||
while (std::regex_search(str, sm, shortcode_re)) {
|
||||
buf += sm.prefix();
|
||||
|
||||
std::string group_0 = sm.str(0);
|
||||
auto emoji = std::find_if(emojis.begin(), emojis.end(), [&](const Emoji& i) { return i.shortcode == sm.str(1); });
|
||||
if (emoji != emojis.end()) {
|
||||
res.push_back(lxb_dom_interface_node(lxb_dom_document_create_text_node(document, reinterpret_cast<const lxb_char_t*>(buf.data()), buf.size())));
|
||||
buf.clear();
|
||||
|
||||
lxb_dom_element_t* img = lxb_dom_element_create(document, reinterpret_cast<const lxb_char_t*>("img"), 3, nullptr, 0, nullptr, 0, nullptr, 0, false);
|
||||
lxb_dom_element_set_attribute(img, reinterpret_cast<const lxb_char_t*>("class"), 5, reinterpret_cast<const lxb_char_t*>("custom_emoji"), 12);
|
||||
lxb_dom_element_set_attribute(img, reinterpret_cast<const lxb_char_t*>("alt"), 3, reinterpret_cast<const lxb_char_t*>(group_0.data()), group_0.size());
|
||||
lxb_dom_element_set_attribute(img, reinterpret_cast<const lxb_char_t*>("title"), 5, reinterpret_cast<const lxb_char_t*>(group_0.data()), group_0.size());
|
||||
lxb_dom_element_set_attribute(img, reinterpret_cast<const lxb_char_t*>("src"), 3, reinterpret_cast<const lxb_char_t*>(emoji->url.data()), emoji->url.size());
|
||||
res.push_back(lxb_dom_interface_node(img));
|
||||
} else {
|
||||
buf += group_0;
|
||||
}
|
||||
|
||||
str = sm.suffix();
|
||||
}
|
||||
|
||||
if (!str.empty()) {
|
||||
buf += std::move(str);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
res.push_back(lxb_dom_interface_node(lxb_dom_document_create_text_node(document, reinterpret_cast<const lxb_char_t*>(buf.data()), buf.size())));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <httplib/httplib.h>
|
||||
#include "blankie/serializer.h"
|
||||
struct Emoji; // forward declaration from models.h
|
||||
|
||||
using Element = blankie::html::Element;
|
||||
using Node = blankie::html::Node;
|
||||
|
@ -18,4 +19,4 @@ std::string get_origin(const httplib::Request& req);
|
|||
std::string proxy_mastodon_url(const httplib::Request& req, const std::string& url_str);
|
||||
bool should_send_304(const httplib::Request& req, uint64_t hash);
|
||||
|
||||
blankie::html::HTMLString preprocess_html(const httplib::Request& req, const std::string& domain_name, const blankie::html::HTMLString& str);
|
||||
blankie::html::HTMLString preprocess_html(const httplib::Request& req, const std::string& domain_name, const std::vector<Emoji>& emojis, const blankie::html::HTMLString& str);
|
||||
|
|
Loading…
Reference in New Issue