diff --git a/README.md b/README.md
index f980961..6e81217 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,6 @@
+mod is developing now!11
+# Geode Mod Comments
-# How To Install On Windows:
-- download pc game by [this link](https://mega.nz/file/d88CgDhb#LWFs7ly3J87fe4coBPzSZtWsUnTGKSehOy5VPaZiQGU)
-- unarchive it
-Thats all. You can made shortcut and stuff... **Main game executable file is a __GemetryTrash.exe__**
-# How To Install On Android:
-- download my custom geode launcher version by [this link](https://mega.nz/file/5w0SRKbD#HkixtbyHiq2yosE7JI21iR7nuVRJmJ1eZd3mDMt-hM0)
-- install apk package
-- run installed launcher
-- download main mod by [this link](https://github.com/user95401/GemetryTrash/releases/latest/download/user95401.gemetry_trash.geode) and install it manually.
-Thats all. Now you can launch game idk.
-
-### https://gt.ps.fhgdps.com
-### https://discord.gg/UyQytJsrGZ
+Add comments in mod popups.
+
+Using GitHub Rest API with OAuth.
\ No newline at end of file
diff --git a/about.md b/about.md
index 7b767a8..6e81217 100644
--- a/about.md
+++ b/about.md
@@ -1,5 +1,6 @@
+mod is developing now!11
# Geode Mod Comments
-Add comments in mod popups...
+Add comments in mod popups.
-GitHub Rest API with OAuth. idka
+Using GitHub Rest API with OAuth.
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
deleted file mode 100644
index a45c96e..0000000
--- a/changelog.md
+++ /dev/null
@@ -1 +0,0 @@
-pohui
\ No newline at end of file
diff --git a/mod.json b/mod.json
index 2bd8c4d..89d3938 100644
--- a/mod.json
+++ b/mod.json
@@ -8,7 +8,7 @@
},
"id": "user95401.geode-mod-comments",
"name": "Mod Comments",
- "version": "v1.0.0",
+ "version": "v1.0.0-alpha",
"developer": "user95401",
"description": "add comments in mod popups",
"early-load": true,
diff --git a/resources/sprites/SquareShadow_001.png b/resources/sprites/SquareShadow_001.png
new file mode 100644
index 0000000..2a13430
Binary files /dev/null and b/resources/sprites/SquareShadow_001.png differ
diff --git a/src/main.cpp b/src/main.cpp
index 57ef42e..e0485a0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,13 +4,350 @@
using namespace geode::prelude;
+#define SETTING(type, key_name) Mod::get()->getSettingValue(key_name)
+
+#define public_cast(value, member) \
+[](auto* v) { \
+ class FriendClass__; \
+ using T = std::remove_pointer::type; \
+ class FriendeeClass__: public T { \
+ protected: \
+ friend FriendClass__; \
+ }; \
+ class FriendClass__ { \
+ public: \
+ auto& get(FriendeeClass__* v) { return v->member; } \
+ } c; \
+ return c.get(reinterpret_cast(v)); \
+}(value)
+
namespace github {
- auto mod_api_repo_url = std::string(
- "https://api.github.com/repos/user95401/Ryzen-Mods/issues"
+ inline auto api_repo_url = std::string(
+ "https://api.github.com/repos/user95401/Geode-Mod-Comments"
);
+ inline std::string get_token() {
+ return Mod::get()->getSavedValue("gh_access_token");
+ }
+ inline void set_token(std::string token) {
+ Mod::get()->setSavedValue("gh_access_token", token);
+ }
+ inline bool has_token() {
+ return (get_token().size() > 3);
+ }
+ inline const char* authorization_header_name() {
+ return has_token() ? "Authorization" : "AuthDisabled";
+ }
+ inline auto get_basic_web_request() {
+ auto req = web::WebRequest();
+ req.userAgent(Mod::get()->getID());
+ req.header("X-GitHub-Api-Version", "2022-11-28");
+ req.header(authorization_header_name(), fmt::format("Bearer {}", get_token()));
+ return req;
+ }
}
-auto last_issues = matjson::Value();
+inline auto issues = matjson::Value();
+inline std::map mod_issues;
+inline std::map mod_comments;
+
+class IssueCommentItem : public CCMenuItem {
+public:
+ matjson::Value m_json;
+ static auto create(CCNode* parent, matjson::Value json) {
+ auto rtn = new IssueCommentItem();
+ rtn->m_json = json;
+ rtn->init();
+ rtn->customSetup(parent);
+ return rtn;
+ }
+ void customSetup(CCNode* parent) {
+ this->setContentWidth(parent->getContentWidth());
+ //row
+ if (auto row = CCMenu::create()) {
+ this->addChild(row);
+ row->setAnchorPoint(CCPointZero);
+ row->setLayout(
+ RowLayout::create()
+ ->setAxisAlignment(AxisAlignment::Start)
+ ->setCrossAxisLineAlignment(AxisAlignment::End)
+ );
+ row->setContentWidth(parent->getContentWidth());
+ //avatar
+ auto avatar_size = CCSize(30.f, 30.f);
+ {
+ //sprite
+ auto sprite = CCSprite::createWithSpriteFrameName("edit_eDamageSquare_001.png");
+ sprite->setScale(avatar_size.width / sprite->getContentSize().width);
+ //item
+ auto avatar = CCMenuItemSpriteExtra::create(sprite, this, menu_selector(IssueCommentItem::onAvatar));
+ avatar->m_animationEnabled = 0;
+ avatar->m_colorEnabled = 1;
+ avatar->setContentSize(avatar_size);
+ row->addChild(avatar);
+ //
+ auto filep = dirs::getTempDir() / ("." + m_json["user"]["login"].as_string());
+ auto a = [this, sprite, filep, avatar](std::monostate const& asd) {
+ if (not sprite) return;
+ sprite->initWithFile(filep.string().c_str());
+ sprite->setScale(avatar->getContentWidth() / sprite->getContentSize().width);
+ };
+ auto req = web::WebRequest();
+ auto listener = new EventListener;
+ listener->bind(
+ [this, a, filep](web::WebTask::Event* e) {
+ if (web::WebResponse* res = e->getValue()) {
+ std::string data = res->string().unwrapOr("");
+ //call the some shit
+ if (res->code() < 399) {
+ res->into(filep);
+ a(std::monostate());
+ }
+ }
+ }
+ );
+ listener->setFilter(req.send("GET", m_json["user"]["avatar_url"].as_string()));
+ /* web::AsyncWebRequest().fetch(m_json["user"]["avatar_url"].as_string())
+ .into(filep).then(a).expect(b);*/
+ }
+ //text
+ if (auto text = CCNode::create()) {
+ row->addChild(text);
+ text->setLayout(
+ ColumnLayout::create()
+ ->setCrossAxisLineAlignment(AxisAlignment::Start)
+ ->setAxisReverse(true)
+ ->setGap(0.f)
+ );
+ text->setContentWidth(parent->getContentWidth() - avatar_size.width);
+ //user
+ auto updated_at = m_json["updated_at"].as_string();
+ updated_at = string::replace(updated_at, "T", " ");
+ updated_at = string::replace(updated_at, "Z", "");
+ updated_at = string::replace(updated_at, "-", ".");
+ auto user = CCLabelTTF::create(
+ fmt::format(
+ "{} at {}",
+ m_json["user"]["login"].as_string(),
+ updated_at
+ ).c_str(),
+ "arial", 12.f
+ );
+ user->setID("user");
+ text->addChild(user);
+ //menu in end of user text
+ if (auto menu = CCMenu::create()) {
+ menu->setPosition(user->getContentSize());
+ menu->setContentHeight(user->getContentHeight());
+ menu->setAnchorPoint({ -0.01f, 1.f });
+ menu->setLayout(
+ RowLayout::create()
+ ->setAxisAlignment(AxisAlignment::Start)
+ ->setAutoScale(false)
+ ->setCrossAxisOverflow(false)
+ );
+ user->addChild(menu);
+ //delete_btn
+ auto edit_delBtn_001 = CCSprite::createWithSpriteFrameName("edit_delBtn_001.png");
+ edit_delBtn_001->setScale(0.5f);
+ auto delete_btn = CCMenuItemSpriteExtra::create(
+ edit_delBtn_001,
+ this,
+ menu_selector(IssueCommentItem::deleteComment)
+ );
+ menu->addChild(delete_btn);
+ //menu update
+ menu->updateLayout();
+ }
+ //body
+ auto body = public_cast(
+ MDTextArea::create(m_json["body"].as_string(), { text->getContentWidth(), 10 }),
+ m_content
+ );
+ body->setVisible(1);
+ auto body_container = CCNode::create();
+ body_container->addChild(body);
+ body_container->setLayout(ColumnLayout::create());
+ body_container->setContentHeight(body->getContentHeight());
+ body_container->updateLayout();
+ text->addChild(body_container);
+ //set col height
+ text->setContentHeight(user->getContentHeight() + body_container->getContentHeight());
+ text->updateLayout();
+ }
+ //upd
+ row->updateLayout();
+ }
+ this->setLayout(RowLayout::create());
+ }
+ //IssueCommentItem
+ void deleteComment(CCObject*) {
+ auto a = [this](std::string const& rtn)
+ {
+ if (auto reload = dynamic_cast(CCDirector::get()->m_pRunningScene->getChildByIDRecursive("reload")))
+ reload->activate();
+ //asd
+ if (this) {
+ auto parent = this->getParent();
+ this->removeFromParentAndCleanup(false);
+ if (parent) parent->updateLayout();
+ };
+ };
+ auto b = [this](std::string const& rtn)
+ {
+ auto message = rtn;
+ auto asd = geode::createQuickPopup(
+ "Request exception",
+ message,
+ "Nah", nullptr, 420.f, nullptr, false
+ );
+ asd->show();
+ };
+ auto req = github::get_basic_web_request();
+ auto listener = new EventListener;
+ listener->bind(
+ [this, a, b](web::WebTask::Event* e) {
+ if (web::WebResponse* res = e->getValue()) {
+ std::string data = res->string().unwrapOr("");
+ //call the some shit
+ if (res->code() < 399) a(data);
+ else b(data);
+ }
+ }
+ );
+ listener->setFilter(req.send("DELETE", m_json["url"].as_string()));
+ }
+ void onAvatar(CCObject*) {
+ if (not m_json.contains("user")) return;
+ if (not m_json["user"].contains("html_url")) return;
+ else web::openLinkInBrowser(m_json["user"]["html_url"].as_string());
+ }
+ void onCreateReaction(CCObject*) {
+ }
+};
+
+class CommentsLayer : public CCLayer {
+public:
+ static auto create(CCContentLayer* contentLayer, std::string modID) {
+
+ auto scroll = typeinfo_cast(contentLayer->getParent());
+
+ float scroll_gap = 12.f;
+
+ contentLayer->setAnchorPoint(CCPointZero);
+ contentLayer->setPositionX(0);
+ contentLayer->setLayout(
+ ColumnLayout::create()
+ ->setGap(scroll_gap)
+ ->setAxisReverse(true)
+ ->setAxisAlignment(AxisAlignment::End)
+ );
+
+ contentLayer->setContentHeight(0.f);
+ if (mod_comments[modID].is_array()) for (auto comment : mod_comments[modID].as_array()) {
+ auto item = IssueCommentItem::create(contentLayer, comment);
+ contentLayer->setContentHeight(//make content layer longer
+ contentLayer->getContentHeight() +
+ item->getContentHeight() + scroll_gap
+ );
+ contentLayer->addChild(item);
+ }
+ //fix some shit goes when content smaller than scroll
+ if (contentLayer->getContentSize().height < scroll->getContentSize().height) {
+ contentLayer->setContentSize({
+ contentLayer->getContentSize().width,
+ scroll->getContentSize().height
+ });
+ }
+ contentLayer->updateLayout();
+
+ }
+};
+
+class LoadCommentsLayer : public CCLayer {
+public:
+ EventListener m_webTaskListener;
+ static auto create(CCContentLayer* contentLayer, std::string modID) {
+ auto me = new LoadCommentsLayer();
+ me->init();
+
+ me->setAnchorPoint({ 0.0f, 0.0f });
+ me->setContentSize(contentLayer->getParent()->getContentSize());
+ contentLayer->setContentSize(me->getContentSize());
+ contentLayer->addChild(me);
+
+ if (issues.is_array()) for (auto issue : issues.as_array()) {
+ auto title = issue.try_get("title").value_or("NO_TITLE");
+ auto number = issue.try_get("number").value_or(1);
+ if (title == modID) {
+ mod_issues[modID] = issue;
+ break;
+ }
+ else if (number == 1) mod_issues[modID] = issue;
+ }
+
+ if (mod_issues.contains(modID)) void();
+ else {
+ auto label = CCLabelBMFont::create(fmt::format(
+ "Can't find any issue for {}!",
+ modID
+ ).data(), "chatFont.fnt"
+ );
+ label->setAlignment(kCCTextAlignmentCenter);
+ label->setAnchorPoint({ 0.5f, 1.f });
+ me->addChildAtPosition(label, Anchor::Top, { 0.f, -72.f });
+ }
+
+ auto bg = CCScale9Sprite::create(
+ "square02_small.png"
+ );
+ bg->setOpacity(75);
+ bg->setContentSize(me->getContentSize());
+ me->addChildAtPosition(bg, Anchor::Center);
+
+ auto label = CCLabelBMFont::create(fmt::format(
+ "Loading comments for {}...",
+ modID
+ ).data(), "chatFont.fnt"
+ );
+ label->setAlignment(kCCTextAlignmentCenter);
+ label->setAnchorPoint({ 0.5f, 1.f });
+ me->addChildAtPosition(label, Anchor::Top, {0.f, -72.f});
+
+ auto circle = LoadingCircleSprite::create();
+ circle->setScale(0.6f);
+ me->addChildAtPosition(circle, Anchor::Top, {0.f, -44.f});
+
+ me->m_webTaskListener.bind(
+ [contentLayer, modID, me, label](web::WebTask::Event* e) {
+ if (web::WebResponse* res = e->getValue()) {
+ auto json = res->json();
+ auto string = res->string();
+ if (json.has_value()) {
+ mod_comments[modID] = json.value();
+ me->removeFromParent();
+ CommentsLayer::create(contentLayer, modID);
+ }
+ else label->setString(fmt::format(
+ "{}\n{}",
+ label->getString(),
+ json.error_or("no err, str: " + string.value_or("no str"))
+ ).c_str());
+ }
+ else if (e->isCancelled()) label->setString(fmt::format(
+ "{}\n\n\nThe request was cancelled...",
+ label->getString()
+ ).c_str());
+ }
+ );
+
+ auto req = github::get_basic_web_request();
+ me->m_webTaskListener.setFilter(req.get(
+ mod_issues[modID].try_get("comments_url").value_or("")
+ ));
+
+ return me;
+ }
+};
class LoadIssuesLayer : public CCLayer {
public:
@@ -44,12 +381,14 @@ class LoadIssuesLayer : public CCLayer {
me->addChildAtPosition(circle, Anchor::Top, {0.f, -44.f});
me->m_webTaskListener.bind(
- [me, label](web::WebTask::Event* e) {
+ [contentLayer, modID, me, label](web::WebTask::Event* e) {
if (web::WebResponse* res = e->getValue()) {
auto json = res->json();
auto string = res->string();
if (json.has_value()) {
- last_issues = json.value();
+ issues = json.value();
+ me->removeFromParent();
+ LoadCommentsLayer::create(contentLayer, modID);
}
else label->setString(fmt::format(
"{}\n{}",
@@ -64,8 +403,10 @@ class LoadIssuesLayer : public CCLayer {
}
);
- auto req = web::WebRequest();
- me->m_webTaskListener.setFilter(req.get("https://pastebin.com/raw/vNi1WHNF"));
+ auto req = github::get_basic_web_request();
+ me->m_webTaskListener.setFilter(req.get(
+ github::api_repo_url + "/issues"
+ ));
return me;
}
@@ -149,7 +490,48 @@ void hi() {
tabspr->select(1);
if (sender->getParent()->getTag() == myTabID) {
-
+ auto pop = FLAlertLayer::create(
+ nullptr,
+ "Create Comment",
+ "\n \n \n \n \n ",
+ "Close", "Create",
+ 380.f
+ );
+ //md_prev
+ auto md_prev = MDTextArea::create("# input and preview here...\nJust tap on me and type your text!\n\nAlso you can type `\\n` and it will be replaced with newline char automatically. And remember, in **Markdown** you write two newlines to start new paragraph!", CCSize(290.f, 112.f));
+ md_prev->setID("md_prev");
+ md_prev->setPositionY(86.f);
+ pop->m_buttonMenu->addChild(md_prev, 1);
+ //input
+ auto input = TextInput::create(md_prev->getContentWidth(), "", "chatFont.fnt");
+ input->setID("input");
+ input->hideBG();
+ input->setContentHeight(md_prev->getContentHeight() * 2);
+ input->getInputNode()->setContentHeight(md_prev->getContentHeight() * 2);
+ input->getInputNode()->m_placeholderLabel->setOpacity(0);
+ input->getInputNode()->m_cursor->setOpacity(0);
+ input->getInputNode()->m_filterSwearWords = (0);
+ input->getInputNode()->m_allowedChars = (
+ " !\"#$ % &'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ );
+ input->setCallback(
+ [input, md_prev](std::string p0) {
+ auto endl_filtered = string::replace(p0, "\\n", "\n");
+ if (p0.find("\\n") != std::string::npos) {
+ input->setString(endl_filtered.data());
+ }
+ //p0->setString();
+ //log::debug("{}(text:{}, m_fontValue1={})", __FUNCTION__, p0->getString(), p0->m_fontValue2);
+ md_prev->setString(endl_filtered.data());
+ md_prev->getScrollLayer()->scrollLayer(9999.f);
+ }
+ );
+ input->setPosition(md_prev->getPosition() * 2);
+ pop->m_buttonMenu->addChild(input);
+ //last popup setup
+ handleTouchPriority(pop);
+ pop->setID("post_comment_popup");
+ pop->show();
}
else {
auto textarea = rcontainer->getChildByIDRecursive("textarea");
diff --git a/support.md b/support.md
deleted file mode 100644
index 6d48eff..0000000
--- a/support.md
+++ /dev/null
@@ -1 +0,0 @@
-Edit this file to change your mod's support info, or delete it if you don't need it.