mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-30 03:15:38 -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 "Server.hpp"
|
||||||
#include <Geode/utils/JsonValidation.hpp>
|
#include <Geode/utils/JsonValidation.hpp>
|
||||||
#include <loader/ModMetadataImpl.hpp>
|
#include <loader/ModMetadataImpl.hpp>
|
||||||
|
#include <fmt/chrono.h>
|
||||||
|
|
||||||
using namespace server;
|
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) {
|
Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
|
||||||
auto json = raw;
|
auto json = raw;
|
||||||
JsonChecker checker(json);
|
JsonChecker checker(json);
|
||||||
|
@ -167,6 +203,12 @@ Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
|
||||||
root.needs("download_count").into(res.downloadCount);
|
root.needs("download_count").into(res.downloadCount);
|
||||||
root.has("about").into(res.about);
|
root.has("about").into(res.about);
|
||||||
root.has("changelog").into(res.changelog);
|
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;
|
std::vector<std::string> developerNames;
|
||||||
for (auto item : root.needs("developers").iterate()) {
|
for (auto item : root.needs("developers").iterate()) {
|
||||||
|
|
|
@ -7,6 +7,17 @@
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
namespace server {
|
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 {
|
struct ServerDeveloper {
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string displayName;
|
std::string displayName;
|
||||||
|
@ -30,6 +41,8 @@ namespace server {
|
||||||
std::unordered_set<std::string> tags;
|
std::unordered_set<std::string> tags;
|
||||||
std::optional<std::string> about;
|
std::optional<std::string> about;
|
||||||
std::optional<std::string> changelog;
|
std::optional<std::string> changelog;
|
||||||
|
std::optional<ServerDateTime> createdAt;
|
||||||
|
std::optional<ServerDateTime> updatedAt;
|
||||||
|
|
||||||
static Result<ServerModMetadata> parse(matjson::Value const& json);
|
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_downloadsIcon_001.png", "Downloads", "downloads", std::nullopt },
|
||||||
{ "GJ_timeIcon_001.png", "Released", "release-date", std::nullopt },
|
{ "GJ_timeIcon_001.png", "Released", "release-date", std::nullopt },
|
||||||
{ "GJ_timeIcon_001.png", "Updated", "update-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();
|
auto container = CCNode::create();
|
||||||
container->setContentSize({ m_stats->getContentWidth(), 10 });
|
container->setContentSize({ m_stats->getContentWidth(), 10 });
|
||||||
|
container->setID(std::get<2>(stat));
|
||||||
|
|
||||||
auto iconSize = container->getContentHeight();
|
auto iconSize = container->getContentHeight();
|
||||||
auto icon = CCSprite::createWithSpriteFrameName(std::get<0>(stat));
|
auto icon = CCSprite::createWithSpriteFrameName(std::get<0>(stat));
|
||||||
|
@ -189,14 +190,22 @@ void ModPopup::setStatValue(CCNode* stat, std::string const& value) {
|
||||||
|
|
||||||
void ModPopup::onLoadServerInfo(PromiseEvent<server::ServerModMetadata, server::ServerError>* event) {
|
void ModPopup::onLoadServerInfo(PromiseEvent<server::ServerModMetadata, server::ServerError>* event) {
|
||||||
if (auto data = event->getResolve()) {
|
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>> {
|
for (auto id : std::initializer_list<std::pair<const char*, std::string>> {
|
||||||
{ "downloads", std::to_string(data->downloadCount) },
|
{ "downloads", std::to_string(data->downloadCount) },
|
||||||
{ "release-date", "todo" },
|
{ "release-date", timeToString(data->createdAt) },
|
||||||
{ "update-date", "todo" },
|
{ "update-date", timeToString(data->updatedAt) },
|
||||||
}) {
|
}) {
|
||||||
auto stat = m_stats->getChildByID(id.first);
|
if (auto stat = m_stats->getChildByID(id.first)) {
|
||||||
stat->removeChildByID("loading-spinner");
|
stat->removeChildByID("loading-spinner");
|
||||||
this->setStatValue(stat, id.second);
|
this->setStatValue(stat, id.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto err = event->getReject()) {
|
else if (auto err = event->getReject()) {
|
||||||
|
|
Loading…
Reference in a new issue