mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-27 01:45:35 -05:00
impl uploaded at and updated at stats
This commit is contained in:
parent
abe41a79a3
commit
38852566a6
3 changed files with 70 additions and 6 deletions
|
@ -1,6 +1,7 @@
|
|||
#include "Server.hpp"
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <loader/ModMetadataImpl.hpp>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
using namespace server;
|
||||
|
||||
|
@ -75,6 +76,41 @@ const char* server::sortToString(ModsSort sorting) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string ServerDateTime::toAgoString() const {
|
||||
auto const fmtPlural = [](auto count, auto unit) {
|
||||
if (count == 1) {
|
||||
return fmt::format("{} {} ago", count, unit);
|
||||
}
|
||||
return fmt::format("{} {}s ago", count, unit);
|
||||
};
|
||||
auto now = Clock::now();
|
||||
auto len = std::chrono::duration_cast<std::chrono::minutes>(now - value).count();
|
||||
if (len < 60) {
|
||||
return fmtPlural(len, "minute");
|
||||
}
|
||||
len = std::chrono::duration_cast<std::chrono::hours>(now - value).count();
|
||||
if (len < 24) {
|
||||
return fmtPlural(len, "hour");
|
||||
}
|
||||
len = std::chrono::duration_cast<std::chrono::days>(now - value).count();
|
||||
if (len < 31) {
|
||||
return fmtPlural(len, "day");
|
||||
}
|
||||
// todo: will our pissbaby american users beg us to add an option for their stupid ass incorrect date format
|
||||
return fmt::format("{:&d.%m.&Y}", value);
|
||||
}
|
||||
|
||||
Result<ServerDateTime> ServerDateTime::parse(std::string const& str) {
|
||||
std::stringstream ss(str);
|
||||
Value value;
|
||||
if (std::chrono::from_stream(ss, "%FT%TZ", value)) {
|
||||
return Ok(ServerDateTime {
|
||||
.value = value,
|
||||
});
|
||||
}
|
||||
return Err("Invalid date time format '{}'", str);
|
||||
}
|
||||
|
||||
Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
|
||||
auto json = raw;
|
||||
JsonChecker checker(json);
|
||||
|
@ -167,6 +203,12 @@ Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
|
|||
root.needs("download_count").into(res.downloadCount);
|
||||
root.has("about").into(res.about);
|
||||
root.has("changelog").into(res.changelog);
|
||||
if (root.has("created_at")) {
|
||||
GEODE_UNWRAP_INTO(res.createdAt, ServerDateTime::parse(root.has("created_at").template get<std::string>()));
|
||||
}
|
||||
if (root.has("updated_at")) {
|
||||
GEODE_UNWRAP_INTO(res.updatedAt, ServerDateTime::parse(root.has("updated_at").template get<std::string>()));
|
||||
}
|
||||
|
||||
std::vector<std::string> developerNames;
|
||||
for (auto item : root.needs("developers").iterate()) {
|
||||
|
|
|
@ -7,6 +7,17 @@
|
|||
using namespace geode::prelude;
|
||||
|
||||
namespace server {
|
||||
struct ServerDateTime final {
|
||||
using Clock = std::chrono::system_clock;
|
||||
using Value = std::chrono::time_point<Clock>;
|
||||
|
||||
Value value;
|
||||
|
||||
std::string toAgoString() const;
|
||||
|
||||
static Result<ServerDateTime> parse(std::string const& str);
|
||||
};
|
||||
|
||||
struct ServerDeveloper {
|
||||
std::string username;
|
||||
std::string displayName;
|
||||
|
@ -30,6 +41,8 @@ namespace server {
|
|||
std::unordered_set<std::string> tags;
|
||||
std::optional<std::string> about;
|
||||
std::optional<std::string> changelog;
|
||||
std::optional<ServerDateTime> createdAt;
|
||||
std::optional<ServerDateTime> updatedAt;
|
||||
|
||||
static Result<ServerModMetadata> parse(matjson::Value const& json);
|
||||
};
|
||||
|
|
|
@ -66,10 +66,11 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
{ "GJ_downloadsIcon_001.png", "Downloads", "downloads", std::nullopt },
|
||||
{ "GJ_timeIcon_001.png", "Released", "release-date", std::nullopt },
|
||||
{ "GJ_timeIcon_001.png", "Updated", "update-date", std::nullopt },
|
||||
{ "version.png"_spr, "Version", "version", src.getMetadata().getVersion().toString() },
|
||||
{ "version.png"_spr, "Version", "version", m_source.getMetadata().getVersion().toString() },
|
||||
}) {
|
||||
auto container = CCNode::create();
|
||||
container->setContentSize({ m_stats->getContentWidth(), 10 });
|
||||
container->setID(std::get<2>(stat));
|
||||
|
||||
auto iconSize = container->getContentHeight();
|
||||
auto icon = CCSprite::createWithSpriteFrameName(std::get<0>(stat));
|
||||
|
@ -189,16 +190,24 @@ void ModPopup::setStatValue(CCNode* stat, std::string const& value) {
|
|||
|
||||
void ModPopup::onLoadServerInfo(PromiseEvent<server::ServerModMetadata, server::ServerError>* event) {
|
||||
if (auto data = event->getResolve()) {
|
||||
auto timeToString = [](auto const& time) {
|
||||
if (time.has_value()) {
|
||||
return time.value().toAgoString();
|
||||
}
|
||||
return std::string("N/A");
|
||||
};
|
||||
|
||||
for (auto id : std::initializer_list<std::pair<const char*, std::string>> {
|
||||
{ "downloads", std::to_string(data->downloadCount) },
|
||||
{ "release-date", "todo" },
|
||||
{ "update-date", "todo" },
|
||||
{ "release-date", timeToString(data->createdAt) },
|
||||
{ "update-date", timeToString(data->updatedAt) },
|
||||
}) {
|
||||
auto stat = m_stats->getChildByID(id.first);
|
||||
if (auto stat = m_stats->getChildByID(id.first)) {
|
||||
stat->removeChildByID("loading-spinner");
|
||||
this->setStatValue(stat, id.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto err = event->getReject()) {
|
||||
for (auto child : CCArrayExt<CCNode*>(m_stats->getChildren())) {
|
||||
if (auto spinner = child->getChildByID("loading-spinner")) {
|
||||
|
|
Loading…
Reference in a new issue