Add poll support
This commit is contained in:
		
							parent
							
								
									3c28b81405
								
							
						
					
					
						commit
						dc3758004b
					
				
							
								
								
									
										16
									
								
								models.cpp
								
								
								
								
							
							
						
						
									
										16
									
								
								models.cpp
								
								
								
								
							|  | @ -71,6 +71,19 @@ void from_json(const json& j, Media& media) { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void from_json(const json& j, PollOption& option) { | ||||
|     j.at("title").get_to(option.title); | ||||
|     j.at("votes_count").get_to(option.votes_count); | ||||
| } | ||||
| 
 | ||||
| void from_json(const json& j, Poll& poll) { | ||||
|     poll.expires_at = parse_rfc3339(j.at("expires_at").get_ref<const std::string&>()); | ||||
|     j.at("expired").get_to(poll.expired); | ||||
|     j.at("voters_count").get_to(poll.voters_count); | ||||
|     j.at("options").get_to(poll.options); | ||||
|     j.at("emojis").get_to(poll.emojis); | ||||
| } | ||||
| 
 | ||||
| void from_json(const json& j, Post& post) { | ||||
|     j.at("id").get_to(post.id); | ||||
|     post.created_at = parse_rfc3339(j.at("created_at").get_ref<const std::string&>()); | ||||
|  | @ -98,6 +111,9 @@ void from_json(const json& j, Post& post) { | |||
|     j.at("account").get_to(post.account); | ||||
|     j.at("media_attachments").get_to(post.media_attachments); | ||||
|     j.at("emojis").get_to(post.emojis); | ||||
|     if (!j.at("poll").is_null()) { | ||||
|         post.poll = j["poll"].get<Poll>(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void from_json(const json& j, PostContext& context) { | ||||
|  |  | |||
							
								
								
									
										15
									
								
								models.h
								
								
								
								
							
							
						
						
									
										15
									
								
								models.h
								
								
								
								
							|  | @ -58,6 +58,18 @@ struct Media { | |||
|     std::optional<std::string> description; | ||||
| }; | ||||
| 
 | ||||
| struct PollOption { | ||||
|     std::string title; | ||||
|     uint64_t votes_count; | ||||
| }; | ||||
| struct Poll { | ||||
|     time_t expires_at; | ||||
|     bool expired; | ||||
|     uint64_t voters_count; | ||||
|     std::vector<PollOption> options; | ||||
|     std::vector<Emoji> emojis; | ||||
| }; | ||||
| 
 | ||||
| struct Post { | ||||
|     std::string id; | ||||
|     time_t created_at; | ||||
|  | @ -74,6 +86,7 @@ struct Post { | |||
|     Account account; | ||||
|     std::vector<Media> media_attachments; | ||||
|     std::vector<Emoji> emojis; | ||||
|     std::optional<Poll> poll; | ||||
| }; | ||||
| 
 | ||||
| struct PostContext { | ||||
|  | @ -85,5 +98,7 @@ void from_json(const nlohmann::json& j, Emoji& emoji); | |||
| void from_json(const nlohmann::json& j, AccountField& field); | ||||
| void from_json(const nlohmann::json& j, Account& account); | ||||
| void from_json(const nlohmann::json& j, Media& media); | ||||
| void from_json(const nlohmann::json& j, PollOption& option); | ||||
| void from_json(const nlohmann::json& j, Poll& poll); | ||||
| void from_json(const nlohmann::json& j, Post& post); | ||||
| void from_json(const nlohmann::json& j, PostContext& context); | ||||
|  |  | |||
|  | @ -125,6 +125,25 @@ svg { | |||
|     height: 40px; | ||||
| } | ||||
| 
 | ||||
| /* POLL */ | ||||
| .poll-option { | ||||
|     margin-top: 0.5em; | ||||
|     margin-bottom: 0.5em; | ||||
| } | ||||
| .poll-percentage { | ||||
|     margin-right: 0.5em; | ||||
| } | ||||
| .poll-bar { | ||||
|     height: 0.5em; | ||||
|     display: block; | ||||
|     background-color: lightgray; | ||||
|     border-radius: 10em; | ||||
|     margin-top: 0.25em; | ||||
| } | ||||
| .poll-bar[width="0%"] { | ||||
|     width: 0.5em; | ||||
| } | ||||
| 
 | ||||
| /* ERROR PAGE */ | ||||
| .error { | ||||
|     text-align: center; | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ void home_route(const httplib::Request& req, httplib::Response& res) { | |||
|         Element("ul", { | ||||
|             Element("li", {Element("a", {{"href", get_origin(req) + "/vt.social/@lina"}}, {"Asahi Lina (朝日リナ) // nullptr::live"})}), | ||||
|             Element("li", {Element("a", {{"href", get_origin(req) + "/vt.social/@LucydiaLuminous/111290028216105435"}}, {"\"I love kids and their creativity. So I was explain…\""})}), | ||||
|             Element("li", {Element("a", {{"href", get_origin(req) + "/ruby.social/@CoralineAda/109951421922797743"}}, {"\"My partner just said \"I'm starting to think nostal…\""})}), | ||||
|         }), | ||||
|         Element("hr"), | ||||
|         Element("p", { | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ struct PostStatus { | |||
| }; | ||||
| static Element serialize_post(const httplib::Request& req, const std::string& server, const Post& post, bool main_post, const std::optional<PostStatus>& post_status); | ||||
| static inline Element serialize_media(const Media& media); | ||||
| static inline Element serialize_poll(const httplib::Request& req, const Poll& poll); | ||||
| 
 | ||||
| class CurlUrlException : public std::exception { | ||||
| public: | ||||
|  | @ -370,6 +371,7 @@ static Element serialize_post(const httplib::Request& req, const std::string& se | |||
|     const char* time_badge = post.edited_at < 0 ? "" : " (edited)"; | ||||
| 
 | ||||
|     Element contents("div", {{"class", "post-contents"}}, {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) { | ||||
|  | @ -377,6 +379,10 @@ static Element serialize_post(const httplib::Request& req, const std::string& se | |||
|     } | ||||
|     contents.nodes.push_back(std::move(post_attachments)); | ||||
| 
 | ||||
|     if (post.poll) { | ||||
|         contents.nodes.push_back(serialize_poll(req, *post.poll)); | ||||
|     } | ||||
| 
 | ||||
|     if (post.sensitive) { | ||||
|         std::string spoiler_text = !post.spoiler_text.empty() ? post.spoiler_text : "See more"; | ||||
|         contents = Element("details", { | ||||
|  | @ -453,3 +459,37 @@ static inline Element serialize_media(const Media& media) { | |||
| 
 | ||||
|     return element; | ||||
| } | ||||
| 
 | ||||
| static inline Element serialize_poll(const httplib::Request& req, const Poll& poll) { | ||||
|     Element div("div"); | ||||
| 
 | ||||
|     auto pick_form = [](uint64_t count, const char* singular, const char* plural) { | ||||
|         return count == 1 ? singular : plural; | ||||
|     }; | ||||
| 
 | ||||
|     div.nodes.reserve(poll.options.size() + 1); | ||||
|     for (const PollOption& option : poll.options) { | ||||
|         std::string percentage = poll.voters_count | ||||
|             ? std::to_string(option.votes_count * 100 / poll.voters_count) + '%' | ||||
|             : "0%"; | ||||
| 
 | ||||
|         div.nodes.push_back(Element("div", {{"class", "poll-option"}, {"title", std::to_string(option.votes_count) + pick_form(option.votes_count, " vote", " votes")}}, { | ||||
|             Element("b", {{"class", "poll-percentage"}}, {percentage}), " ", preprocess_html(req, poll.emojis, option.title), | ||||
|             Element("object", {{"class", "poll-bar"}, {"width", percentage}}, {}), | ||||
|         })); | ||||
|     } | ||||
| 
 | ||||
|     Element p("p", { | ||||
|         std::to_string(poll.voters_count), " ", pick_form(poll.voters_count, "voter", "voters"), | ||||
|         " / ", | ||||
|     }); | ||||
|     if (poll.expired) { | ||||
|         p.nodes.push_back("Expired"); | ||||
|     } else { | ||||
|         p.nodes.push_back("Expires in "); | ||||
|         p.nodes.push_back(relative_time(current_time(), poll.expires_at)); | ||||
|     } | ||||
|     div.nodes.push_back(std::move(p)); | ||||
| 
 | ||||
|     return div; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue