#include "routes.h" #include "../servehelper.h" #include "../client.h" #include "../models.h" static const char* sorting_method_names[3] = {"Posts", "Posts and replies", "Media"}; 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 Account& account, PostSortingMethod sorting_method); static inline Element user_link_field(const AccountField& field); static inline Element sorting_method_link(const Account& account, PostSortingMethod current_method, PostSortingMethod new_method); void user_route(const httplib::Request& req, httplib::Response& res) { std::string server = req.matches.str(1); std::string username = req.matches.str(2); PostSortingMethod sorting_method = get_sorting_method(req.matches.str(3)); std::optional account; try { account = mastodon_client.get_account_by_username(server, username); } catch (const std::exception& e) { res.status = 500; serve_error(req, res, "500: Internal server error", "Failed to fetch user information", e.what()); return; } if (!account) { res.status = 404; serve_error(req, res, "404: User not found"); return; } Element body("body", { user_header(*account, sorting_method), }); serve(req, res, account->display_name + " (" + account->username + '@' + account->domain_name + ')', std::move(body)); } static inline PostSortingMethod get_sorting_method(const std::string& method) { for (size_t i = 0; i < sizeof(sorting_method_suffixes) / sizeof(sorting_method_suffixes[0]); i++) { if (method == sorting_method_suffixes[i]) { return static_cast(i); } } __builtin_unreachable(); } static inline Element user_header(const Account& account, PostSortingMethod sorting_method) { struct tm created_at; char created_at_str[16]; gmtime_r(&account.created_at, &created_at); strftime(created_at_str, 16, "%Y-%m-%d", &created_at); 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(i)); } Element header("header", { Element("a", {{"href", account.header}}, { Element("img", {{"class", "user_page-header"}, {"alt", "User header"}, {"src", account.header}}, {}), }), Element("div", {{"class", "user_page-user_pfp"}}, { Element("a", {{"href", account.avatar}}, { Element("img", {{"alt", "User profile picture"}, {"src", account.avatar}}, {}), }), Element("span", { Element("b", {account.display_name}), " (", account.username, "@", account.domain_name, ")", Element("br"), Element("br"), Element("b", {"Joined: "}), std::string(created_at_str), Element("br"), Element("b", {std::to_string(account.statuses_count)}), " Posts", " / ", Element("b", {std::to_string(account.following_count)}), " Following", " / ", Element("b", {std::to_string(account.followers_count)}), " Followers", }), }), Element("div", {{"class", "user_page-user_description"}}, { Element("div", {{"class", "user_page-user_bio"}}, {account.note_html}), std::move(user_links), }), Element("nav", {{"class", "user_page-user_posts_nav"}}, { sorting_method_link(account, sorting_method, PostSortingMethod::Posts), sorting_method_link(account, sorting_method, PostSortingMethod::PostsAndReplies), sorting_method_link(account, sorting_method, PostSortingMethod::Media), }), }); return header; } static inline Element user_link_field(const AccountField& field) { using namespace std::string_literals; Element tr("tr", { Element("th", {field.name}), Element("td", {field.value_html}), }); if (field.verified_at >= 0) { struct tm verified_at; char verified_at_str[32]; gmtime_r(&field.verified_at, &verified_at); strftime(verified_at_str, 32, "%Y-%m-%d %H:%M:%S %Z", &verified_at); tr.attributes = {{"class", "verified"}, {"title", "Verified at "s + verified_at_str}}; } return tr; } static inline Element sorting_method_link(const Account& account, PostSortingMethod current_method, PostSortingMethod new_method) { using namespace std::string_literals; const char* method_name = sorting_method_names[new_method]; if (current_method == new_method) { return Element("b", {method_name}); } else { return Element("a", {{"href", "/"s + account.domain_name + "/@" + account.username + sorting_method_suffixes[new_method]}}, { method_name, }); } }