From 5ed7d9b53a65aec1f4793f62ac15195c44224b39 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Tue, 18 Jul 2023 00:00:20 +0200 Subject: Implement Question object type support This commit adds support for dealing with ActivityPub Questions, which are polls. They are like Notes (posts) but contain a few more keys that record information about the poll options, number of votes and poll closure date. --- CMakeLists.txt | 3 +++ src/activitypub/appost.h | 22 ++------------------ src/activitypub/apquestion.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++ src/activitypub/apquestion.h | 34 ++++++++++++++++++++++++++++++ src/activitypub/fields.h | 38 ++++++++++++++++++++++++++++++++++ src/archive_parser.cpp | 35 ++++++++++++++++++++++++++++--- src/templates/appoll.html | 9 ++++++++ src/templates/appoll_item.html | 1 + 8 files changed, 166 insertions(+), 23 deletions(-) create mode 100644 src/activitypub/apquestion.cpp create mode 100644 src/activitypub/apquestion.h create mode 100644 src/activitypub/fields.h create mode 100644 src/templates/appoll.html create mode 100644 src/templates/appoll_item.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bcbe6e..5bab400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(PROJECT_SOURCES src/list_item.h src/command_line.cpp src/command_line.h + src/activitypub/fields.h src/activitypub/apactivity.h src/activitypub/apactor.h src/activitypub/apattachment.h @@ -35,6 +36,7 @@ set(PROJECT_SOURCES src/activitypub/apobject.h src/activitypub/appost.h src/activitypub/apreblog.h + src/activitypub/apquestion.h src/activitypub/apactivity.cpp src/activitypub/apactor.cpp src/activitypub/apattachment.cpp @@ -42,6 +44,7 @@ set(PROJECT_SOURCES src/activitypub/apobject.cpp src/activitypub/appost.cpp src/activitypub/apreblog.cpp + src/activitypub/apquestion.cpp ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) diff --git a/src/activitypub/appost.h b/src/activitypub/appost.h index a8b99c5..ccbedd9 100644 --- a/src/activitypub/appost.h +++ b/src/activitypub/appost.h @@ -1,28 +1,10 @@ #pragma once #include "apobject.h" -#include "src/activitypub/apbase.h" -#include "src/types.h" +#include "apbase.h" +#include "fields.h" #include -// All these fields (including APAttachmentFields, etc. in other files) are made to map to the fields present in the ActivityStream JSON's "object" for very easy construction. -// There are additional fields that we don't use so they are not included in the objects (including parent and APActivity). - -struct APObjectFields { - QStringList to_actors; // Start APObject - QStringList cc_actors; - QString by_actor; - QString object_url; - QString web_url; - QString reply_to_url; - QString published; - std::vector attachments; // End APObject - QStringList languages; // This is taken from "content_map" - QString content; - QString summary; - StatusType visibility; // status type discovery is better left to the archive/API parser as this can very between fedi implementations and archive formats -}; - // APPost represents an ActivityPub Note Object class APPost : public APObject { public: diff --git a/src/activitypub/apquestion.cpp b/src/activitypub/apquestion.cpp new file mode 100644 index 0000000..d519947 --- /dev/null +++ b/src/activitypub/apquestion.cpp @@ -0,0 +1,47 @@ +#include "apquestion.h" +#include + +APQuestion::APQuestion(APObjectFields fields) : APPost(fields) { + end_time = QDateTime::fromString(fields.question.end_time, Qt::ISODate); + closed_time = QDateTime::fromString(fields.question.closed_time, Qt::ISODate); + total_votes = fields.question.total_votes; + + for (APQuestionFields::Option elem : fields.question.poll_options) { + options.push_back({elem.name, elem.votes}); + } + + qDebug() << options.size(); +} + +QString APQuestion::get_html_render(HtmlRenderDetails render_info) { + QString html(APPost::get_html_render(render_info)); + html.append(get_html_template("appoll")); + + if (end_time.isValid()) { + // TODO: add a UI setting for configuring the display of local time or UTC time. + html.replace("{{end-time}}", render_info.locale->toString(end_time.toLocalTime())); + } + if (closed_time.isValid()) { + // TODO: add a UI setting for configuring the display of local time or UTC time. + html.replace("{{closed-time}}", render_info.locale->toString(closed_time.toLocalTime())); + } + + html.replace("{{total-votes}}", render_info.locale->toString(total_votes)); + html.replace("{{options}}", get_html_poll_options(render_info)); + + return html; +} + +QString APQuestion::get_html_poll_options(HtmlRenderDetails render_info) { + QString full; + + for (PollOption option : options) { + QString html(get_html_template("appoll_item")); + html.replace("{{name}}", option.name); + html.replace("{{votes}}", render_info.locale->toString(option.votes)); + html.replace("{{votes-percent}}", render_info.locale->toString((int)((float)option.votes/(float)total_votes * 100))); + full.append(html); + } + + return full; +} diff --git a/src/activitypub/apquestion.h b/src/activitypub/apquestion.h new file mode 100644 index 0000000..350ceec --- /dev/null +++ b/src/activitypub/apquestion.h @@ -0,0 +1,34 @@ +#pragma once + +#include "appost.h" +#include "apbase.h" +#include "fields.h" +#include +#include + +// APQuestion represents an ActivityPub Question Object +class APQuestion : public APPost { +public: + APQuestion(); + // A post that will be built from strings, including attachments + APQuestion(APObjectFields fields); + ~APQuestion() {}; + + QString get_html_render(HtmlRenderDetails render_info); + +protected: + struct PollOption { + QString name; + int votes; + }; + typedef std::vector PollOptionList; + + QDateTime end_time; + QDateTime closed_time; + + int total_votes = 0; + + PollOptionList options; + + QString get_html_poll_options(HtmlRenderDetails render_info); +}; diff --git a/src/activitypub/fields.h b/src/activitypub/fields.h new file mode 100644 index 0000000..25ed246 --- /dev/null +++ b/src/activitypub/fields.h @@ -0,0 +1,38 @@ +#pragma once + +#include "src/types.h" +#include +#include + +enum struct APObjectType {NOTE, QUESTION, UNKNOWN}; + +// All these fields (including APAttachmentFields, etc. in other files) are made to map to the fields present in the ActivityStream JSON's "object" for very easy construction. +// There are additional fields that we don't use so they are not included in the objects (including parent and APActivity). + +struct APQuestionFields { + QString end_time; + QString closed_time; + int total_votes; + struct Option { + QString name; + int votes; + }; + std::vector