From 9860b329f0a015b32b5dc0ee05c9775be2c9a4bb Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nwpope@utu.fi>
Date: Thu, 9 Jul 2020 18:35:02 +0300
Subject: [PATCH] Add recent files to add source dialog

---
 applications/gui2/src/modules/addsource.cpp   |  4 ++
 applications/gui2/src/modules/addsource.hpp   |  1 +
 applications/gui2/src/views/addsource.cpp     | 14 +++++++
 .../common/cpp/include/ftl/configuration.hpp  |  4 ++
 components/common/cpp/src/configuration.cpp   | 37 +++++++++++++++++++
 .../streams/include/ftl/streams/feed.hpp      |  1 -
 components/streams/src/feed.cpp               | 23 +++++++++++-
 7 files changed, 81 insertions(+), 3 deletions(-)

diff --git a/applications/gui2/src/modules/addsource.cpp b/applications/gui2/src/modules/addsource.cpp
index f584274c5..897af5be7 100644
--- a/applications/gui2/src/modules/addsource.cpp
+++ b/applications/gui2/src/modules/addsource.cpp
@@ -29,6 +29,10 @@ std::vector<std::string> AddCtrl::getNetSources() {
 	return std::move(io->feed()->availableNetworkSources());
 }
 
+std::vector<std::string> AddCtrl::getFileSources() {
+	return std::move(io->feed()->availableFileSources());
+}
+
 std::string AddCtrl::getSourceName(const std::string &uri) {
 	return io->feed()->getName(uri);
 }
diff --git a/applications/gui2/src/modules/addsource.hpp b/applications/gui2/src/modules/addsource.hpp
index e243a2ec2..25064abe8 100644
--- a/applications/gui2/src/modules/addsource.hpp
+++ b/applications/gui2/src/modules/addsource.hpp
@@ -22,6 +22,7 @@ public:
 	ftl::Configurable *add(const std::string &uri);
 
 	std::vector<std::string> getNetSources();
+	std::vector<std::string> getFileSources();
 	std::string getSourceName(const std::string &uri);
 	bool isSourceActive(const std::string &uri);
 
diff --git a/applications/gui2/src/views/addsource.cpp b/applications/gui2/src/views/addsource.cpp
index c67f3e5f6..a1115d743 100644
--- a/applications/gui2/src/views/addsource.cpp
+++ b/applications/gui2/src/views/addsource.cpp
@@ -83,6 +83,20 @@ void AddSourceWindow::rebuild() {
                     { {"ftl", "FTL Captures"} }, true));
 		close();
 	});
+
+	auto filesrcs = ctrl_->getFileSources();
+
+	for (auto &s : filesrcs) {
+		button = new Button(filebuttons, ctrl_->getSourceName(s));
+		if (ctrl_->isSourceActive(s)) {
+			button->setBackgroundColor(Color(0, 255, 0, 25));
+		}
+
+		button->setCallback([this, s]() {
+			ctrl_->add(s);
+			close();
+		});
+	}
 }
 
 void AddSourceWindow::close() {
diff --git a/components/common/cpp/include/ftl/configuration.hpp b/components/common/cpp/include/ftl/configuration.hpp
index 1111e229e..7ad81440b 100644
--- a/components/common/cpp/include/ftl/configuration.hpp
+++ b/components/common/cpp/include/ftl/configuration.hpp
@@ -24,6 +24,10 @@ bool create_directory(const std::string &path);
 bool is_video(const std::string &file);
 std::vector<std::string> directory_listing(const std::string &path);
 
+nlohmann::json loadJSON(const std::string &path);
+
+bool saveJSON(const std::string &path, nlohmann::json &json);
+
 namespace config {
 
 typedef nlohmann::json json_t;
diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp
index 111154022..5e7bd0ea2 100644
--- a/components/common/cpp/src/configuration.cpp
+++ b/components/common/cpp/src/configuration.cpp
@@ -33,6 +33,7 @@
 
 using ftl::config::json_t;
 using std::ifstream;
+using std::ofstream;
 using std::string;
 using std::map;
 using std::vector;
@@ -175,6 +176,42 @@ optional<string> ftl::config::locateFile(const string &name) {
 	return {};
 }
 
+nlohmann::json ftl::loadJSON(const std::string &path) {
+	ifstream i(path.c_str());
+	//i.open(path);
+	if (i.is_open()) {
+		try {
+			nlohmann::json t;
+			i >> t;
+			return t;
+		} catch (nlohmann::json::parse_error& e) {
+			LOG(ERROR) << "Parse error in loading JSON: "  << e.what();
+			return {};
+		} catch (...) {
+			LOG(ERROR) << "Unknown error opening JSON file: " << path;
+		}
+		return {};
+	} else {
+		return {};
+	}
+}
+
+bool ftl::saveJSON(const std::string &path, nlohmann::json &json) {
+	ofstream o(path.c_str());
+	//i.open(path);
+	if (o.is_open()) {
+		try {
+			o << json;
+			return true;
+		} catch (...) {
+			LOG(ERROR) << "Unknown error saving JSON file: " << path;
+		}
+		return false;
+	} else {
+		return false;
+	}
+}
+
 /**
  * Combine one json config with another patch json config.
  */
diff --git a/components/streams/include/ftl/streams/feed.hpp b/components/streams/include/ftl/streams/feed.hpp
index cf24ffa61..0cb76a614 100644
--- a/components/streams/include/ftl/streams/feed.hpp
+++ b/components/streams/include/ftl/streams/feed.hpp
@@ -54,7 +54,6 @@ public:
 	};
 
 private:
-
 	// public methods acquire lock if necessary, private methods assume locking
 	// managed by caller
 	std::mutex mtx_;
diff --git a/components/streams/src/feed.cpp b/components/streams/src/feed.cpp
index 7891818a9..6c2941816 100644
--- a/components/streams/src/feed.cpp
+++ b/components/streams/src/feed.cpp
@@ -9,6 +9,8 @@
 using ftl::stream::Feed;
 using ftl::codecs::Channel;
 
+static nlohmann::json feed_config;
+
 ////////////////////////////////////////////////////////////////////////////////
 
 Feed::Filter::Filter(Feed* feed, const std::unordered_set<uint32_t>& sources, const std::unordered_set<Channel>& channels) :
@@ -46,6 +48,8 @@ Feed::Filter &Feed::Filter::select(const std::unordered_set<ftl::codecs::Channel
 Feed::Feed(nlohmann::json &config, ftl::net::Universe*net) :
 		ftl::Configurable(config), net_(net) {
 
+	feed_config = ftl::loadJSON(FTL_LOCAL_CONFIG_ROOT "/feed.json");
+
 	pool_ = std::make_unique<ftl::data::Pool>(3,5);
 
 	stream_ = std::unique_ptr<ftl::stream::Muxer>
@@ -142,6 +146,8 @@ Feed::Feed(nlohmann::json &config, ftl::net::Universe*net) :
 
 Feed::~Feed() {
 	std::unique_lock<std::mutex> lk(mtx_);
+	ftl::saveJSON(FTL_LOCAL_CONFIG_ROOT "/feed.json", feed_config);
+
 	receiver_.reset();  // Note: Force destruction first to remove filters this way
 
 	for (auto* filter : filters_) {
@@ -285,7 +291,14 @@ std::vector<std::string> Feed::availableNetworkSources() {
 }
 
 std::vector<std::string> Feed::availableFileSources() {
-	return {};
+	std::vector<std::string> files;
+	auto &recent_files = feed_config["recent_files"];
+
+	for (auto &f : recent_files.items()) {
+		files.push_back(f.key());
+	}
+
+	return files;
 }
 
 std::vector<std::string> Feed::availableDeviceSources() {
@@ -319,7 +332,7 @@ std::string Feed::getName(const std::string &puri) {
 	} else if (uri.getScheme() == ftl::URI::SCHEME_DEVICE) {
 		return "Device";
 	} else if (uri.getScheme() == ftl::URI::SCHEME_FILE) {
-		// TODO: Parse last part of file name - extension
+		return feed_config["recent_files"][uri.getBaseURI()].value("name", "FTLFile");
 	}
 
 	return "No Name";
@@ -365,6 +378,12 @@ uint32_t Feed::add(const std::string &path) {
 			fstream->set("filename", uri.getPath());
 		}
 
+		auto &recent_files = feed_config["recent_files"];
+		auto &file_details = recent_files[uri.getBaseURI()];
+		std::string fname = uri.getPathSegment(-1);
+		file_details["name"] = fname.substr(0, fname.find_last_of('.'));
+		file_details["last_open"] = ftl::timer::get_time();
+
 		// TODO: URI normalization; should happen in add(,,) or add(,,,) take
 		// ftl::URI instead of std::string as argument. Note the bug above.
 		// TODO: write unit test for uri parsing
-- 
GitLab