changeset 315:34347fd2a2de

session: allow printing status messages ...!
author Paper <paper@paper.us.eu.org>
date Tue, 11 Jun 2024 14:16:40 -0400
parents 76d7315504c4
children 180714442770
files Makefile.am include/core/session.h include/gui/window.h src/core/session.cc src/gui/pages/anime_list.cc src/gui/window.cc src/main.cc src/services/anilist.cc
diffstat 8 files changed, 140 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.am	Tue Jun 11 13:29:45 2024 -0400
+++ b/Makefile.am	Tue Jun 11 14:16:40 2024 -0400
@@ -126,6 +126,7 @@
 
 minori_qtheaders = \
 	include/core/http.h			\
+	include/core/session.h      \
 	include/gui/dialog/about.h		\
 	include/gui/dialog/information.h		\
 	include/gui/dialog/settings.h			\
@@ -162,7 +163,6 @@
 	include/core/filesystem.h			\
 	include/core/ini.h		\
 	include/core/json.h			\
-	include/core/session.h			\
 	include/core/strings.h			\
 	include/core/time.h			\
 	include/core/torrent.h		\
@@ -196,6 +196,7 @@
 	src/core/http.cc		\
 	src/core/ini.cc			\
 	src/core/json.cc		\
+	src/core/session.cc     \
 	src/core/strings.cc		\
 	src/core/time.cc		\
 	src/gui/dialog/settings/application.cc		\
--- a/include/core/session.h	Tue Jun 11 13:29:45 2024 -0400
+++ b/include/core/session.h	Tue Jun 11 14:16:40 2024 -0400
@@ -4,27 +4,42 @@
 #include "core/config.h"
 #include "gui/locale.h"
 
+#include <QObject>
 #include <QElapsedTimer>
 
 #include "semver/semver.hpp"
 
 #include <atomic>
+#include <string>
 
-struct Session {
+class MainWindow;
+
+struct Session : public QObject {
+	Q_OBJECT
+
 public:
-	Session() { timer.start(); }
+	Session();
+
+	void SetMainWindow(MainWindow* window);
+
+	void SetStatusBar(const std::string& message);
+
 	/* we literally *cannot* be lying to the user by doing this */
-	void IncrementRequests() { requests++; };
-	unsigned int GetRequests() { return requests; };
-	int uptime() { return timer.elapsed(); }
+	void IncrementRequests();
+	unsigned int GetRequests();
+	int uptime();
 
 	Config config;
 	static constexpr semver::version version{PACKAGE_VERSION};
 
+signals:
+	void StatusBarChange(const std::string& message);
+
 private:
 	/* IncrementRequests() gets called by different threads */
-	std::atomic<unsigned int> requests = 0;
-	QElapsedTimer timer;
+	std::atomic<unsigned int> requests_ = 0;
+	QElapsedTimer timer_;
+	MainWindow* window_;
 };
 
 extern Session session;
--- a/include/gui/window.h	Tue Jun 11 13:29:45 2024 -0400
+++ b/include/gui/window.h	Tue Jun 11 14:16:40 2024 -0400
@@ -27,6 +27,7 @@
 
 /* ... :) */
 Q_DECLARE_METATYPE(std::vector<std::string>);
+Q_DECLARE_METATYPE(std::string);
 
 class MainWindowPlayingThread final : public QThread {
 	Q_OBJECT
@@ -84,6 +85,9 @@
 	void showEvent(QShowEvent* event) override;
 	void closeEvent(QCloseEvent* event) override;
 
+public slots:
+	void SetStatusMessage(const std::string& message);
+
 private:
 	QWidget main_widget_;
 	QStackedWidget stack_;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/session.cc	Tue Jun 11 14:16:40 2024 -0400
@@ -0,0 +1,40 @@
+#include "core/session.h"
+#include "core/strings.h"
+#include "core/config.h"
+#include "gui/window.h"
+#include "gui/locale.h"
+
+#include <QElapsedTimer>
+#include <QStatusBar>
+
+#include "semver/semver.hpp"
+
+#include <atomic>
+
+Session session;
+
+Session::Session() {
+	timer_.start();
+}
+
+void Session::SetMainWindow(MainWindow* window) {
+	disconnect();
+	window_ = window;
+	connect(this, &Session::StatusBarChange, window_, &MainWindow::SetStatusMessage);
+}
+
+void Session::SetStatusBar(const std::string& message) {
+	emit StatusBarChange(message);
+}
+
+void Session::IncrementRequests() {
+	requests_++;
+};
+
+unsigned int Session::GetRequests() {
+	return requests_;
+};
+
+int Session::uptime() {
+	return timer_.elapsed();
+}
--- a/src/gui/pages/anime_list.cc	Tue Jun 11 13:29:45 2024 -0400
+++ b/src/gui/pages/anime_list.cc	Tue Jun 11 14:16:40 2024 -0400
@@ -305,8 +305,9 @@
 		if (!index.isValid())
 			continue;
 		Anime::Anime* anime = source_model->GetAnimeFromIndex(index);
-		if (anime)
-			animes.insert(anime);
+		if (!anime)
+			continue;
+		animes.insert(&Anime::db.items[anime->GetId()]);
 	}
 
 	menu->addAction(tr("Information"), [this, animes] {
@@ -352,10 +353,10 @@
 		reinterpret_cast<AnimeListPageModel*>(sort_models[tab_bar->currentIndex()]->sourceModel());
 
 	const QModelIndex index = source_model->index(selection.indexes().first().row());
-	Anime::Anime* anime = source_model->GetAnimeFromIndex(index);
+	Anime::Anime& anime = Anime::db.items[source_model->GetAnimeFromIndex(index)->GetId()];
 
 	InformationDialog* dialog = new InformationDialog(
-		anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
+		&anime, [this](Anime::Anime* anime) { UpdateAnime(anime->GetId()); }, InformationDialog::PAGE_MAIN_INFO, this);
 
 	dialog->show();
 	dialog->raise();
--- a/src/gui/window.cc	Tue Jun 11 13:29:45 2024 -0400
+++ b/src/gui/window.cc	Tue Jun 11 14:16:40 2024 -0400
@@ -32,6 +32,7 @@
 #include <QMessageBox>
 #include <QPlainTextEdit>
 #include <QStackedWidget>
+#include <QStatusBar>
 #include <QTextStream>
 #include <QThread>
 #include <QThreadPool>
@@ -80,6 +81,8 @@
 	sidebar_.setFixedWidth(128);
 	sidebar_.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
 
+	statusBar();
+
 	new QHBoxLayout(&main_widget_);
 
 	CreateBars();
@@ -132,6 +135,10 @@
 	playing_thread_timer_.start(5000);
 }
 
+void MainWindow::SetStatusMessage(const std::string& message) {
+	statusBar()->showMessage(Strings::ToQString(message), 2000);
+}
+
 /* Does the main part of what Qt's generic "RetranslateUI" function would do */
 void MainWindow::AddMainWidgets() {
 	int page = sidebar_.GetCurrentItem();
--- a/src/main.cc	Tue Jun 11 13:29:45 2024 -0400
+++ b/src/main.cc	Tue Jun 11 14:16:40 2024 -0400
@@ -11,8 +11,6 @@
 
 #include <iostream>
 
-Session session;
-
 int main(int argc, char** argv) {
 	QApplication app(argc, argv);
 	app.setApplicationName("minori");
@@ -21,6 +19,7 @@
 
 	qRegisterMetaType<std::vector<std::string>>(); /* window.cc */
 	qRegisterMetaType<std::vector<int>>(); /* search.cc */
+	qRegisterMetaType<std::string>();
 
 	session.config.Load();
 	Anime::db.LoadDatabaseFromDisk();
@@ -31,5 +30,8 @@
 	window.setWindowTitle("Minori");
 	window.show();
 
+	/* do this before starting the app... */
+	session.SetMainWindow(&window);
+
 	return app.exec();
 }
--- a/src/services/anilist.cc	Tue Jun 11 13:29:45 2024 -0400
+++ b/src/services/anilist.cc	Tue Jun 11 14:16:40 2024 -0400
@@ -74,28 +74,29 @@
 	return Strings::ToUtf8String(HTTP::Request("https://graphql.anilist.co", headers, data, HTTP::Type::Post));
 }
 
-static nlohmann::json SendJSONRequest(const nlohmann::json& data) {
+static bool SendJSONRequest(const nlohmann::json& data, nlohmann::json& out) {
 	std::string request = SendRequest(data.dump());
 	if (request.empty()) {
-		std::cerr << "[AniList] JSON Request returned an empty result!" << std::endl;
-		return {};
+		session.SetStatusBar("AniList: JSON request returned an empty result!");
+		return false;
 	}
 
-	auto ret = nlohmann::json::parse(request, nullptr, false);
-	if (ret.is_discarded()) {
-		std::cerr << "[AniList] Failed to parse request JSON!" << std::endl;
-		return {};
+	out = nlohmann::json::parse(request, nullptr, false);
+	if (out.is_discarded()) {
+		session.SetStatusBar("AniList: Failed to parse request JSON!");
+		return false;
 	}
 
-	if (ret.contains("/errors"_json_pointer) && ret.at("/errors"_json_pointer).is_array()) {
-		for (const auto& error : ret.at("/errors"_json_pointer))
+	if (out.contains("/errors"_json_pointer) && out.at("/errors"_json_pointer).is_array()) {
+		for (const auto& error : out.at("/errors"_json_pointer))
 			std::cerr << "[AniList] Received an error in response: "
 			          << JSON::GetString<std::string>(error, "/message"_json_pointer, "") << std::endl;
 
-		return {};
+		session.SetStatusBar("AniList: Received an error in response!");
+		return false;
 	}
 
-	return ret;
+	return true;
 }
 
 static void ParseListStatus(std::string status, Anime::Anime& anime) {
@@ -212,10 +213,12 @@
 
 int GetAnimeList() {
 	if (!account.IsValid()) {
-		std::cerr << "AniList: Account isn't valid!" << std::endl;
+		session.SetStatusBar("AniList: Account isn't valid!");
 		return 0;
 	}
 
+	session.SetStatusBar("AniList: Retrieving anime list...");
+
 	/* NOTE: these really ought to be in the qrc file */
 	constexpr std::string_view query = "query ($id: Int) {\n"
 	                                   "  MediaListCollection (userId: $id, type: ANIME) {\n"
@@ -253,11 +256,18 @@
 	};
 	// clang-format on
 
-	auto res = SendJSONRequest(json);
+	session.SetStatusBar("AniList: Parsing anime list...");
 
-	for (const auto& list : res["data"]["MediaListCollection"]["lists"].items())
+	nlohmann::json result;
+	const bool res = SendJSONRequest(json, result);
+	if (!res)
+		return 0;
+
+	for (const auto& list : result["data"]["MediaListCollection"]["lists"].items())
 		ParseList(list.value());
 
+	session.SetStatusBar("AniList: Retrieved anime list successfully!");
+
 	return 1;
 }
 
@@ -280,13 +290,16 @@
 	};
 	// clang-format on
 
-	auto res = SendJSONRequest(json);
+	nlohmann::json result;
+	const bool res = SendJSONRequest(json, result);
+	if (!res)
+		return {};
 
 	/* FIXME: error handling here */
 	std::vector<int> ret;
-	ret.reserve(res["data"]["Page"]["media"].size());
+	ret.reserve(result["/data/Page/media"_json_pointer].size());
 
-	for (const auto& media : res["data"]["Page"]["media"].items())
+	for (const auto& media : result["/data/Page/media"_json_pointer].items())
 		ret.push_back(ParseMediaJson(media.value()));
 
 	return ret;
@@ -321,13 +334,17 @@
 			}}
 		};
 
-		auto res = SendJSONRequest(json);
-		ret.reserve(ret.capacity() + res["data"]["Page"]["media"].size());
+		nlohmann::json result;
+		const bool res = SendJSONRequest(json, result);
+		if (!res)
+			return {};
 
-		for (const auto& media : res["data"]["Page"]["media"].items())
+		ret.reserve(ret.capacity() + result["data"]["Page"]["media"].size());
+
+		for (const auto& media : result["data"]["Page"]["media"].items())
 			ret.push_back(ParseMediaJson(media.value()));
 
-		has_next_page = JSON::GetBoolean(res, "/data/Page/pageInfo/hasNextPage"_json_pointer, false);
+		has_next_page = JSON::GetBoolean(result, "/data/Page/pageInfo/hasNextPage"_json_pointer, false);
 		if (has_next_page)
 			page++;
 	}
@@ -363,6 +380,8 @@
 	if (!service_id)
 		return 0;
 
+	session.SetStatusBar("AniList: Updating anime entry...");
+
 	constexpr std::string_view query =
 	    "mutation ($media_id: Int, $progress: Int, $status: MediaListStatus, $score: Int, $notes: String, $start: "
 	    "FuzzyDateInput, $comp: FuzzyDateInput, $repeat: Int) {\n"
@@ -387,9 +406,14 @@
 	};
 	// clang-format on
 
-	auto ret = SendJSONRequest(json);
+	nlohmann::json result;
+	const bool ret = SendJSONRequest(json, result);
+	if (!ret)
+		return 0;
 
-	return JSON::GetNumber(ret, "/data/SaveMediaListEntry/id"_json_pointer, 0);
+	session.SetStatusBar("AniList: Anime entry updated successfully!");
+
+	return JSON::GetNumber(result, "/data/SaveMediaListEntry/id"_json_pointer, 0);
 }
 
 static int ParseUser(const nlohmann::json& json) {
@@ -412,6 +436,8 @@
 
 	account.SetAuthToken(Strings::ToUtf8String(token));
 
+	session.SetStatusBar("AniList: Requesting user ID...");
+
 	constexpr std::string_view query = "query {\n"
 	                                   "  Viewer {\n"
 	                                   "    id\n"
@@ -425,7 +451,13 @@
 	    {"query", query}
     };
 
-	auto ret = SendJSONRequest(json);
+    /* SendJSONRequest handles status errors */
+    nlohmann::json result;
+	const bool ret = SendJSONRequest(json, result);
+	if (!ret)
+		return 0;
+
+	session.SetStatusBar("AniList: Successfully retrieved user data!");
 
 	ParseUser(ret["data"]["Viewer"]);
 	return true;