From 5c3111e56477a91b9134f437df4c75c521519bb0 Mon Sep 17 00:00:00 2001
From: HJfod <60038575+HJfod@users.noreply.github.com>
Date: Mon, 22 Apr 2024 21:19:07 +0300
Subject: [PATCH] fix description & changelog not working on server mods

---
 loader/src/ui/GeodeUI.cpp                |  3 ++
 loader/src/ui/mods/popups/ModPopup.cpp   | 64 ++++++++++++++++++++++--
 loader/src/ui/mods/sources/ModSource.cpp | 58 ++++++++++++---------
 loader/src/ui/mods/sources/ModSource.hpp |  4 +-
 4 files changed, 98 insertions(+), 31 deletions(-)

diff --git a/loader/src/ui/GeodeUI.cpp b/loader/src/ui/GeodeUI.cpp
index 1448d2b8..ada7fbc8 100644
--- a/loader/src/ui/GeodeUI.cpp
+++ b/loader/src/ui/GeodeUI.cpp
@@ -147,6 +147,9 @@ protected:
                 this->setSprite(CCSprite::createWithTexture(texture));
             }
         }
+        else if (event->isCancelled()) {
+            this->setSprite(nullptr);
+        }
     }
 
 public:
diff --git a/loader/src/ui/mods/popups/ModPopup.cpp b/loader/src/ui/mods/popups/ModPopup.cpp
index 226c0fd8..a6e81b75 100644
--- a/loader/src/ui/mods/popups/ModPopup.cpp
+++ b/loader/src/ui/mods/popups/ModPopup.cpp
@@ -5,6 +5,60 @@
 #include <Geode/utils/ColorProvider.hpp>
 #include "ConfirmUninstallPopup.hpp"
 
+class FetchTextArea : public CCNode {
+public:
+    using Request = server::ServerRequest<std::optional<std::string>>;
+
+protected:
+    EventListener<Request> m_listener;
+    MDTextArea* m_textarea;
+    CCNode* m_loading;
+    std::string m_noneText;
+
+    bool init(Request const& req, std::string const& noneText, CCSize const& size) {
+        if (!CCNode::init())
+            return false;
+        
+        this->setAnchorPoint({ .5f, .5f });
+        this->setContentSize(size);
+
+        m_noneText = noneText;
+
+        m_textarea = MDTextArea::create("", size);
+        this->addChildAtPosition(m_textarea, Anchor::Center);
+
+        m_loading = createLoadingCircle(30);
+        this->addChildAtPosition(m_loading, Anchor::Center);
+        
+        m_listener.bind(this, &FetchTextArea::onRequest);
+        m_listener.setFilter(req);
+        
+        return true;
+    }
+
+    void onRequest(Request::Event* event) {
+        if (event->getValue() && event->getValue()->isOk() && event->getValue()->unwrap()) {
+            m_loading->removeFromParent();
+            m_textarea->setString(event->getValue()->unwrap()->c_str());
+        }
+        else if (!event->getProgress()) {
+            m_loading->removeFromParent();
+            m_textarea->setString(m_noneText.c_str());
+        }
+    }
+
+public:
+    static FetchTextArea* create(Request const& req, std::string const& noneText, CCSize const& size) {
+        auto ret = new FetchTextArea();
+        if (ret && ret->init(req, noneText, size)) {
+            ret->autorelease();
+            return ret;
+        }
+        CC_SAFE_DELETE(ret);
+        return nullptr;
+    }
+};
+
 bool ModPopup::setup(ModSource&& src) {
     m_source = std::move(src);
     m_noElasticity = true;
@@ -665,16 +719,18 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
         const float mdScale = .85f;
         switch (tab) {
             case Tab::Details: {
-                m_currentTabPage = MDTextArea::create(
-                    m_source.getAbout().value_or("No description provided"),
+                m_currentTabPage = FetchTextArea::create(
+                    m_source.fetchAbout(),
+                    "No description provided",
                     size / mdScale
                 );
                 m_currentTabPage->setScale(mdScale);
             } break;
 
             case Tab::Changelog: {
-                m_currentTabPage = MDTextArea::create(
-                    m_source.getChangelog().value_or("No changelog provided"),
+                m_currentTabPage = FetchTextArea::create(
+                    m_source.fetchChangelog(),
+                    "No changelog provided",
                     size / mdScale
                 );
                 m_currentTabPage->setScale(mdScale);
diff --git a/loader/src/ui/mods/sources/ModSource.cpp b/loader/src/ui/mods/sources/ModSource.cpp
index b7301780..66275454 100644
--- a/loader/src/ui/mods/sources/ModSource.cpp
+++ b/loader/src/ui/mods/sources/ModSource.cpp
@@ -26,28 +26,6 @@ ModMetadata ModSource::getMetadata() const {
         }
     }, m_value);
 }
-std::optional<std::string> ModSource::getAbout() const {
-    return std::visit(makeVisitor {
-        [](Mod* mod) {
-            return mod->getMetadata().getDetails();
-        },
-        [](server::ServerModMetadata const& metadata) {
-            // Versions should be guaranteed to have at least one item
-            return metadata.about;
-        }
-    }, m_value);
-}
-std::optional<std::string> ModSource::getChangelog() const {
-    return std::visit(makeVisitor {
-        [](Mod* mod) {
-            return mod->getMetadata().getChangelog();
-        },
-        [](server::ServerModMetadata const& metadata) {
-            // Versions should be guaranteed to have at least one item
-            return metadata.changelog;
-        }
-    }, m_value);
-}
 CCNode* ModSource::createModLogo() const {
     return std::visit(makeVisitor {
         [](Mod* mod) {
@@ -95,16 +73,46 @@ server::ServerModMetadata const* ModSource::asServer() const {
     return std::get_if<server::ServerModMetadata>(&m_value);
 }
 
-server::ServerRequest<server::ServerModMetadata> ModSource::fetchServerInfo() const {
+server::ServerRequest<std::optional<std::string>> ModSource::fetchAbout() const {
     return std::visit(makeVisitor {
         [](Mod* mod) {
-            return server::getMod(mod->getID());
+            return server::ServerRequest<std::optional<std::string>>::immediate(Ok(mod->getMetadata().getDetails()));
         },
         [](server::ServerModMetadata const& metadata) {
-            return server::ServerRequest<server::ServerModMetadata>::immediate(Ok(metadata));
+            return server::getMod(metadata.id).map(
+                [](auto* result) -> Result<std::optional<std::string>, server::ServerError> {
+                    if (result->isOk()) {
+                        return Ok(result->unwrap().about);
+                    }
+                    return Err(result->unwrapErr());
+                }
+            );
         }
     }, m_value);
 }
+server::ServerRequest<std::optional<std::string>> ModSource::fetchChangelog() const {
+    return std::visit(makeVisitor {
+        [](Mod* mod) {
+            return server::ServerRequest<std::optional<std::string>>::immediate(Ok(mod->getMetadata().getChangelog()));
+        },
+        [](server::ServerModMetadata const& metadata) {
+            return server::getMod(metadata.id).map(
+                [](auto* result) -> Result<std::optional<std::string>, server::ServerError> {
+                    if (result->isOk()) {
+                        return Ok(result->unwrap().changelog);
+                    }
+                    return Err(result->unwrapErr());
+                }
+            );
+        }
+    }, m_value);
+}
+server::ServerRequest<server::ServerModMetadata> ModSource::fetchServerInfo() const {
+    // Request the info even if this is already a server mod because this might 
+    // not have the full details (for example changelog) and the server cache 
+    // should deal with performance issues
+    return server::getMod(this->getID());
+}
 server::ServerRequest<std::unordered_set<std::string>> ModSource::fetchValidTags() const {
     return std::visit(makeVisitor {
         [](Mod* mod) {
diff --git a/loader/src/ui/mods/sources/ModSource.hpp b/loader/src/ui/mods/sources/ModSource.hpp
index 6d4af8d3..51ebd99d 100644
--- a/loader/src/ui/mods/sources/ModSource.hpp
+++ b/loader/src/ui/mods/sources/ModSource.hpp
@@ -17,8 +17,6 @@ public:
 
     std::string getID() const;
     ModMetadata getMetadata() const;
-    std::optional<std::string> getAbout() const;
-    std::optional<std::string> getChangelog() const;
     CCNode* createModLogo() const;
     bool wantsRestart() const;
     // note: be sure to call checkUpdates first...
@@ -36,6 +34,8 @@ public:
     server::ServerModMetadata const* asServer() const;
 
     server::ServerRequest<server::ServerModMetadata> fetchServerInfo() const;
+    server::ServerRequest<std::optional<std::string>> fetchAbout() const;
+    server::ServerRequest<std::optional<std::string>> fetchChangelog() const;
     server::ServerRequest<std::unordered_set<std::string>> fetchValidTags() const;
     server::ServerRequest<std::optional<server::ServerModUpdate>> checkUpdates();
 };