diff options
author | Edwin van Leeuwen <edwinvanl@tuta.io> | 2022-06-18 12:41:40 +0100 |
---|---|---|
committer | Edwin van Leeuwen <edwinvanl@tuta.io> | 2022-06-18 12:41:40 +0100 |
commit | c5b87af882a6e8ecff61d97882c73c94adb114d3 (patch) | |
tree | 9194e4ccb1e35f6559fe7584c13b64722d20eab3 | |
parent | 21b9d2a9a35551229af05e43bd6895a3441094ed (diff) | |
parent | 69cca38f7e467fff8df9ab79da249acac49c56b2 (diff) |
Merge branch 'release/1.0.0'v1.0.0
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | .gitlab-ci.yml | 2 | ||||
-rw-r--r-- | CMakeLists.txt | 56 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | TODO.md | 5 | ||||
-rw-r--r-- | aur/PKGBUILD | 6 | ||||
-rw-r--r-- | src/item.hpp | 250 | ||||
-rw-r--r-- | src/logger.hpp | 45 | ||||
-rw-r--r-- | src/main.cpp | 872 | ||||
-rw-r--r-- | src/rttt.hpp | 48 | ||||
-rw-r--r-- | src/rttt/config.hpp (renamed from src/config.hpp) | 5 | ||||
-rw-r--r-- | src/rttt/future.hpp (renamed from src/future.hpp) | 13 | ||||
-rw-r--r-- | src/rttt/hackernews.hpp (renamed from src/hackernews.hpp) | 194 | ||||
-rw-r--r-- | src/rttt/item.hpp | 206 | ||||
-rw-r--r-- | src/rttt/logger.hpp | 75 | ||||
-rw-r--r-- | src/rttt/prompt.hpp (renamed from src/prompt.hpp) | 55 | ||||
-rw-r--r-- | src/rttt/reddit.hpp (renamed from src/reddit.hpp) | 215 | ||||
-rw-r--r-- | src/rttt/request.hpp (renamed from src/request.hpp) | 16 | ||||
-rw-r--r-- | src/rttt/rss.hpp (renamed from src/rss.hpp) | 98 | ||||
-rw-r--r-- | src/rttt/socket.hpp (renamed from src/socket.hpp) | 9 | ||||
-rw-r--r-- | src/rttt/storage.hpp (renamed from src/storage.hpp) | 17 | ||||
-rw-r--r-- | src/rttt/text.hpp | 101 | ||||
-rw-r--r-- | src/rttt/thing.hpp | 184 | ||||
-rw-r--r-- | src/rttt/twitter.hpp | 467 | ||||
-rw-r--r-- | src/rttt/ui_ftxui.hpp | 360 | ||||
-rw-r--r-- | src/rttt/view.hpp (renamed from src/view.hpp) | 38 | ||||
-rw-r--r-- | src/ui.hpp | 563 | ||||
-rw-r--r-- | test/catch_future.cpp | 4 | ||||
-rw-r--r-- | test/catch_hackernews.cpp | 163 | ||||
-rw-r--r-- | test/catch_item.cpp | 10 | ||||
-rw-r--r-- | test/catch_login.cpp | 59 | ||||
-rw-r--r-- | test/catch_prompt.cpp | 2 | ||||
-rw-r--r-- | test/catch_reddit.cpp | 5 | ||||
-rw-r--r-- | test/catch_rss.cpp | 10 | ||||
-rw-r--r-- | test/catch_rttt.cpp | 8 | ||||
-rw-r--r-- | test/catch_storage.cpp | 14 | ||||
-rw-r--r-- | test/catch_text.cpp | 65 | ||||
-rw-r--r-- | test/catch_thing.cpp | 18 | ||||
-rw-r--r-- | test/catch_twitter.cpp | 170 | ||||
-rw-r--r-- | test/catch_ui.cpp | 20 | ||||
-rw-r--r-- | test/catch_view.cpp | 45 | ||||
-rw-r--r-- | test/include/rttt/fake_thing.hpp | 31 | ||||
-rw-r--r-- | test/source_skeleton.hpp | 36 | ||||
-rw-r--r-- | test/tweets_by_user.json | 1 | ||||
-rw-r--r-- | test/tweets_conversation.json | 1 |
45 files changed, 2823 insertions, 1749 deletions
@@ -26,3 +26,9 @@ install_manifest.txt login_credentials.hpp aur/*/ + +CPackConfig.cmake +CPackSourceConfig.cmake +analysis.svg +analysis.txt +gmon.out
\ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f47fae7..9f753b7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ build:arch: image: testcab/yay stage: build script: - - yay -Sy --noconfirm cmake git cpr nlohmann-json pugixml + - yay -Syu --noconfirm cmake git cpr nlohmann-json pugixml fmt - cd aur # If this does not work, then try CI_COMMIT_REF_SLUG - echo $CI_COMMIT_REF_NAME diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b9914d..d664a74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,12 +46,32 @@ if (NOT nlohmann_json_FOUND) endif() endif() -# Build imtui as a static lib. -set(BUILD_SHARED_LIBS OFF) -FetchContent_Declare(imtui - GIT_REPOSITORY https://github.com/ggerganov/imtui - GIT_TAG "aa55479de6ea4d3bc0f730aaf91e20c299b61742") -FetchContent_MakeAvailable(imtui) +find_package(ftxui) + +if (NOT ftxui_FOUND) + set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE) + FetchContent_Declare(ftxui + GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui + GIT_TAG b63aa9e + #GIT_TAG v3.0.0 + ) + + FetchContent_GetProperties(ftxui) + if(NOT ftxui_POPULATED) + FetchContent_Populate(ftxui) + add_subdirectory(${ftxui_SOURCE_DIR} ${ftxui_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() +endif() + + +find_package(fmt) +if (NOT fmt_FOUND) +FetchContent_Declare(fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG master +) +FetchContent_MakeAvailable(fmt) +endif() set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) @@ -85,15 +105,20 @@ add_executable(${TARGET} ${SOURCES}) target_include_directories(${TARGET} PRIVATE . + src/ + test/include/ ) -target_link_libraries(${TARGET} PRIVATE - ${CURL_LIBRARIES} - ${CPR_LIBS} - nlohmann_json::nlohmann_json - Threads::Threads - pugixml::pugixml - imtui-ncurses +target_link_libraries(${TARGET} + PRIVATE ${CURL_LIBRARIES} + PRIVATE ${CPR_LIBS} + PRIVATE nlohmann_json::nlohmann_json + PRIVATE Threads::Threads + PRIVATE pugixml::pugixml + PRIVATE ftxui::screen + PRIVATE ftxui::dom + PRIVATE ftxui::component # No + PRIVATE fmt::fmt ) target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wpedantic -Werror) @@ -120,7 +145,10 @@ foreach(TESTFILE ${TESTFILES}) nlohmann_json::nlohmann_json Threads::Threads pugixml::pugixml - imtui-ncurses + PRIVATE ftxui::screen + PRIVATE ftxui::dom + PRIVATE ftxui::component # No + PRIVATE fmt::fmt ) endif() endforeach() @@ -2,7 +2,9 @@ Read-the-things-tui (rttt) lets you read the things from the terminal. The things currently include RSS/Atom, hackernews and Reddit. It is a terminal application written in C++ and originally based on [hnterm](https://github.com/ggerganov/hnterm.git), but with added support for other things (Reddit/RSS/Atom). -![screenshot](https://gitlab.com/BlackEdder/rttt/-/wikis/uploads/a6f6435591d28be270c8f783032dba37/rttt.png) +![screenshot1](https://gitlab.com/BlackEdder/rttt/-/wikis/uploads/974e093e08d802d484f0fb454fc5d933/rttt_hntop.png) + +![screenshot2](https://gitlab.com/BlackEdder/rttt/-/wikis/uploads/db45a0db2ba991283fb96da6e21fd574/rttt_help.png) ## Installing @@ -6,6 +6,9 @@ This is a very rough list of ideas and future todo items. # TODOs +- [ ] Twitter support +- [ ] Add gcc version information to README + ## Refactor - [ ] Use cpr getcallback support instead of my own version of it: https://docs.libcpr.org/advanced-usage.html#asynchronous-callbacks @@ -34,6 +37,8 @@ while (!myqueue.empty()) ## Features +- [ ] Save completion history +- [ ] Call from outside to open a certain path - [ ] Save colourscheme to config - [ ] Bold? text/font? - Can we rely on terminals colour/bold/etc coding? diff --git a/aur/PKGBUILD b/aur/PKGBUILD index c0441eb..36b87d1 100644 --- a/aur/PKGBUILD +++ b/aur/PKGBUILD @@ -2,19 +2,19 @@ pkgname=rttt-git _pkgname='rttt' pkgrel=1 -pkgver=v0.6.0.r8.b4e22d4 +pkgver=v0.8.8.r37.df62981 pkgdesc="Read-the-things-tui (rttt) lets you read RSS/Atom, hackernews and Reddit from the terminal." arch=("x86_64") url="https://gitlab.com/BlackEdder/rttt" license=('GPL3') -depends=('pugixml' 'curl' 'cpr' 'nlohmann-json') +depends=('pugixml' 'curl' 'cpr' 'nlohmann-json' 'fmt') makedepends=('git' 'cmake') provides=("${pkgname%-git}") conflicts=("${pkgname%-git}") install= source=( # "git+https://gitlab.com/BlackEdder/rttt#branch=${BRANCH:-master}" -# "git+https://gitlab.com/BlackEdder/rttt#branch=${BRANCH:-feature/aur}" +# "git+https://gitlab.com/BlackEdder/rttt#branch=${BRANCH:-feature/ftxui}" "git+https://gitlab.com/BlackEdder/rttt#branch=${BRANCH:-PLACEHOLDER}" ) sha512sums=('SKIP') diff --git a/src/item.hpp b/src/item.hpp deleted file mode 100644 index 0fe27a8..0000000 --- a/src/item.hpp +++ /dev/null @@ -1,250 +0,0 @@ -#pragma once - -#include <cassert> -#include <optional> -#include <string> -#include <variant> -#include <vector> - -namespace rttt { - -namespace hackernews { -using ItemId = int; -using ItemIds = std::vector<ItemId>; - -struct Story { - std::string by = ""; - int descendants = 0; - ItemId id = 0; - ItemIds kids; - int score = 0; - uint64_t time = 0; - std::string text = ""; - std::string title = ""; - std::string url = ""; - std::string domain = ""; -}; - -struct Comment { - std::string by = ""; - ItemId id = 0; - ItemIds kids; - ItemId parent = 0; - std::string text = ""; - uint64_t time = 0; -}; -} // namespace hackernews - -namespace rss { -struct Story { - std::string by = ""; - uint64_t time = 0; - std::string text = ""; - std::string title = ""; - std::string domain = ""; - std::string url = ""; - - int id; - std::string key = ""; -}; -} // namespace rss - -// Forward declarations -namespace reddit { -struct Story; -struct Comment; -} // namespace reddit - -struct unknown_type {}; - -using ItemVariant = - std::variant<reddit::Comment, reddit::Story, hackernews::Comment, - hackernews::Story, rss::Story, rttt::unknown_type>; - -namespace reddit { -struct Story { - std::string by = ""; - int descendants = 0; - int score = 0; - int vote = 0; - uint64_t time = 0; - std::string text = ""; - std::string title = ""; - std::string domain = ""; - std::string url = ""; - std::string fullname = ""; - - std::vector<ItemVariant> kids; - - std::string id = ""; -}; - -struct Comment { - std::string by = ""; - std::string text = ""; - std::string id = ""; - std::string fullname = ""; - uint64_t time = 0; - int score = 0; - int vote = 0; - - std::vector<ItemVariant> kids; -}; - -} // namespace reddit - -inline std::optional<std::string> try_id_string(const ItemVariant &item) { - if (std::holds_alternative<reddit::Story>(item)) { - auto value = std::get<reddit::Story>(item); - if (value.id.empty()) - return std::nullopt; - return value.id; - } - if (std::holds_alternative<reddit::Comment>(item)) { - auto value = std::get<reddit::Comment>(item); - if (value.id.empty()) - return std::nullopt; - return value.id; - } - if (std::holds_alternative<hackernews::Story>(item)) { - auto value = std::get<hackernews::Story>(item); - if (value.id == 0) - return std::nullopt; - return std::to_string(value.id); - } - if (std::holds_alternative<hackernews::Comment>(item)) { - auto value = std::get<hackernews::Comment>(item); - if (value.id == 0) - return std::nullopt; - return std::to_string(value.id); - } - if (std::holds_alternative<rss::Story>(item)) { - auto value = std::get<rss::Story>(item); - if (value.id == 0) - return std::nullopt; - return std::to_string(value.id); - } - return std::nullopt; -} - -inline std::optional<uint64_t> try_time(const ItemVariant &item) { - std::optional<uint64_t> opt; - std::visit( - [&opt](const auto &data) { - if constexpr (!std::is_same<const rttt::unknown_type &, - decltype(data)>::value) { - opt = data.time; - } - }, - item); - return opt; -} - -std::optional<int> try_vote(const ItemVariant &item) { - std::optional<int> opt; - std::visit( - [&opt](const auto &data) { - // TODO: make a constexpr is_reddit template function - if constexpr (std::is_same<const rttt::reddit::Story &, - decltype(data)>::value || - std::is_same<const rttt::reddit::Comment &, - decltype(data)>::value) { - opt = data.vote; - } - }, - item); - return opt; -} - -std::optional<std::string> try_fullname(const ItemVariant &item) { - std::optional<std::string> opt; - std::visit( - [&opt](const auto &data) { - // TODO: make a constexpr is_reddit template function - if constexpr (std::is_same<const rttt::reddit::Story &, - decltype(data)>::value || - std::is_same<const rttt::reddit::Comment &, - decltype(data)>::value) { - opt = data.fullname; - } - }, - item); - return opt; -} - -template <typename C, typename = void> struct has_url : std::false_type {}; - -template <typename C> -struct has_url< - C, typename std::enable_if<std::is_same<decltype(std::declval<C>().url), - std::string>::value>::type> - : std::true_type {}; - -inline std::optional<std::string> try_url(const ItemVariant &item) { - std::optional<std::string> opt; - std::visit( - [&opt](const auto &data) { - if constexpr (has_url<decltype(data)>::value) - opt = data.url; - }, - item); - return opt; -} - -template <typename C, typename = void> struct has_title : std::false_type {}; - -template <typename C> -struct has_title< - C, typename std::enable_if<std::is_same<decltype(std::declval<C>().title), - std::string>::value>::type> - : std::true_type {}; - -inline std::optional<std::string> try_title(const ItemVariant &item) { - std::optional<std::string> opt; - std::visit( - [&opt](const auto &data) { - if constexpr (has_title<decltype(data)>::value) - opt = data.title; - }, - item); - return opt; -} - -inline std::optional<std::string> try_text(const ItemVariant &item) { - std::optional<std::string> opt; - std::visit( - [&opt](const auto &data) { - if constexpr (!std::is_same<const rttt::unknown_type &, - decltype(data)>::value) { - opt = data.text; - } - }, - item); - return opt; -} - -inline std::optional<std::vector<ItemVariant>> -try_kids(const ItemVariant &item) { - if (std::holds_alternative<reddit::Story>(item)) { - return std::get<reddit::Story>(item).kids; - } - if (std::holds_alternative<reddit::Comment>(item)) { - return std::get<reddit::Comment>(item).kids; - } - if (std::holds_alternative<hackernews::Comment>(item) || - std::holds_alternative<hackernews::Story>(item)) { - assert(false); // kids work different here, so not supported (yet?) - } - - return std::nullopt; -} - -template <typename C, typename = void> struct has_text : std::false_type {}; - -template <typename C> -struct has_text< - C, typename std::enable_if<std::is_same<decltype(std::declval<C>().text), - std::string>::value>::type> - : std::true_type {}; - -} // namespace rttt diff --git a/src/logger.hpp b/src/logger.hpp deleted file mode 100644 index cac8039..0000000 --- a/src/logger.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include <locale.h> -#include <stdio.h> -#include <time.h> -#include <iostream> - -#include <chrono> -#include <deque> -#include <string> - -namespace logger { - -namespace { -// Anonymous namespace makes this private to this namespace (basically a global) -std::deque<std::string> queue; -std::optional<std::string> maybe_file; -} // namespace - -inline void push_back(const std::string &message) { - time_t t = - std::chrono::system_clock::to_time_t(std::chrono::system_clock().now()); - char buf[70]; - std::strftime(buf, 70, "[%H:%M:%S] ", std::localtime(&t)); - auto line = std::string(buf) + message; - if (maybe_file.has_value()) { - std::ofstream log_file; - log_file.open(maybe_file.value(), std::ios_base::app); - log_file << line << std::endl; - log_file.close(); - } - queue.push_back(line); -} - -inline std::string at(const size_t i) { return queue.at(i); } - -inline size_t size() { return queue.size(); } - -inline void pop_front() { queue.pop_front(); } - -void to_file(const std::string &filename) { - maybe_file = filename; -} - -} // namespace logger diff --git a/src/main.cpp b/src/main.cpp index 58728aa..c5833f0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,42 +1,32 @@ -// Documentation for imgui: -// https://pixtur.github.io/mkdocs-for-imgui/site/api-imgui/ImGui--Dear-ImGui-end-user/#settingsini-utilities - -#include "imgui/imgui.h" -#include "imtui/imtui.h" - -#include "config.hpp" -#include "hackernews.hpp" -#include "logger.hpp" -#include "prompt.hpp" -#include "reddit.hpp" -#include "rss.hpp" -#include "rttt.hpp" -#include "storage.hpp" -#include "ui.hpp" -#include "view.hpp" - -// Figure out why this is needed -#define EMSCRIPTEN_KEEPALIVE - -#include "imtui/imtui-impl-ncurses.h" - -#include <functional> -#include <list> -#include <map> -#include <ranges> +#include <chrono> +#include <iostream> +#include <sstream> #include <string> -#include <unordered_set> +#include <thread> #include <vector> -// global vars -bool g_updated = false; -ImTui::TScreen *g_screen = nullptr; +#include "ftxui/component/component.hpp" +#include "ftxui/component/screen_interactive.hpp" +#include "ftxui/dom/elements.hpp" +#include "ftxui/dom/flexbox_config.hpp" +#include "ftxui/component/captured_mouse.hpp" + +#include "rttt/config.hpp" +#include "rttt/fake_thing.hpp" +#include "rttt/logger.hpp" +#include "rttt/prompt.hpp" +#include "rttt/text.hpp" +#include "rttt/thing.hpp" +#include "rttt/ui_ftxui.hpp" + +/* +- [ ] Selecting text with the mouse in fxtui +*/ // helper functions namespace { -[[maybe_unused]] std::map<std::string, std::string> -parseCmdArguments(int argc, char **argv) { +std::map<std::string, std::string> parse_arguments(int argc, char **argv) { int last = argc; std::map<std::string, std::string> res; for (int i = 1; i < last; ++i) { @@ -51,599 +41,283 @@ parseCmdArguments(int argc, char **argv) { return res; } -} // namespace - -using namespace rttt; - -rttt::active_storage<std::string, ui::WindowData> path_cache; - -hackernews::state state_hn; -rss::state state_rss; -reddit::state state_reddit; -prompt::state state_prompt; -ui::state state_ui; -ui::WindowData switch_path(ui::WindowData &¤t_window, - const std::string &from, const std::string &to) { - assert(path_cache.contains(from)); - // Store current status of the window (highlighted item etc) - auto prev_window_mode = current_window.path.mode; - path_cache.at(from) = current_window; +rttt::active_storage<std::string, rttt::ui::scroll_window_state> + window_state_cache = + rttt::active_storage<std::string, rttt::ui::scroll_window_state>( + 0, 0, 3500); - if (path_cache.contains(to)) { - current_window = path_cache.at(to); +auto switch_path(const rttt::Path &from, const rttt::Path &to, + const rttt::ui::scroll_window_state &window_state) { + if (window_state_cache.contains(from.name)) { + window_state_cache.at(from.name) = window_state; + window_state_cache.mark_active(from.name); } else { - current_window.path = rttt::parse_path(to); - current_window.scroll_state = rttt::ui::scroll_window_state(); - - // Change default view if we switch to feed mode - if (current_window.path.mode == rttt::list_mode::feed && - prev_window_mode != rttt::list_mode::feed) - current_window.scroll_state.ui_view_mode = view::mode::text; - path_cache.insert({current_window.path.name, current_window}); + window_state_cache.insert({from.name, window_state}); } - - // We currently mark path displayed in windows already as active, so this is - // not needed here - // pathCache.mark_active(from); - // pathCache.mark_active(to); - return std::move(current_window); -} - -auto dispatch_drawing_stories(ui::WindowData &&window) { - if (window.path.type == rttt::SiteType::HN) { - auto result = hackernews::get_stories(std::move(state_hn.items), - state_hn.idsMap[window.path.name]); - state_hn.items = std::move(std::get<0>(result)); - // In case highlighted_item is not cached anymore - auto maybe_id = try_id_string(window.scroll_state.highlighted_item); - if (maybe_id.has_value()) { - auto id = std::stoi(maybe_id.value()); - if (!state_hn.items.contains(id)) - window.scroll_state.highlighted_item = ItemVariant(); - } - window.scroll_state = - rttt::ui::drawItems(std::move(window.scroll_state), std::get<1>(result), - window.path.mode, state_ui.item_view_states); - - } else if (window.path.type == rttt::SiteType::Reddit) { - if (reddit::items.contains(window.path.name)) { - auto result = reddit::get_stories(std::move(reddit::items), window.path); - reddit::items = std::move(std::get<0>(result)); - window.scroll_state = rttt::ui::drawItems( - std::move(window.scroll_state), std::get<1>(result), window.path.mode, - state_ui.item_view_states); - } - } else if (window.path.type == rttt::SiteType::RSS) { - // Technically this temporary is not needed, but compilation (type - // inference?) will fail without it - auto vec = rttt::rss::get_items(state_rss, window.path); - auto v = vec | std::views::transform( - [](const auto &item) { return std::pair(0, item); }); - window.scroll_state = - rttt::ui::drawItems(std::move(window.scroll_state), v, window.path.mode, - state_ui.item_view_states); - } else { - assert(false); + if (!window_state_cache.contains(to.name)) { + window_state_cache.insert({to.name, rttt::ui::scroll_window_state()}); + window_state_cache.at(to.name).layout = rttt::thing::default_view_mode(to); } - return window; + window_state_cache.mark_active(to.name); + return std::make_tuple(to, window_state_cache.at(to.name)); } +} // namespace -auto dispatch_drawing_comments(ui::WindowData &&window) { - if (window.path.type == rttt::SiteType::Reddit) { - if (reddit::items.contains(window.path.name)) { - reddit::items.mark_active(window.path.name); - if (!reddit::items.at(window.path.name).empty()) { - constexpr auto dig = [](auto &item) -> std::vector<ItemVariant> * { - auto maybe_id = try_id_string(item); - if (!maybe_id.has_value() || - view::is_collapsed(state_ui.item_view_states, maybe_id.value())) - return nullptr; - - if (std::holds_alternative<reddit::Story>(item)) - return &std::get<reddit::Story>(item).kids; - assert(std::holds_alternative<reddit::Comment>(item)); - return &std::get<reddit::Comment>(item).kids; - }; - auto view = - rttt::flat_view_with_depth(reddit::items.at(window.path.name), dig); - window.scroll_state = - rttt::ui::drawItems(std::move(window.scroll_state), view, - window.path.mode, state_ui.item_view_states); - } - } - } else if (window.path.type == rttt::SiteType::HN) { - int id = std::stoi(window.path.id); - if (state_hn.items.contains(id)) { - std::vector<int> item_ids{id}; - auto dig = [](auto &m_id) -> std::vector<int> * { - if (!state_hn.items.contains(m_id) || - view::is_collapsed(state_ui.item_view_states, std::to_string(m_id))) - return nullptr; - auto &item = state_hn.items.at(m_id); - if (std::holds_alternative<hackernews::Story>(item)) { - return &(std::get<hackernews::Story>(item).kids); - } - if (std::holds_alternative<hackernews::Comment>(item)) { - return &(std::get<hackernews::Comment>(item).kids); - } - return nullptr; - }; - auto view = - rttt::flat_view_with_depth(item_ids, dig) | - std::views::transform([](auto pair) { - if (!state_hn.items.contains(pair.second)) { - state_hn.items.insert({pair.second, hackernews::Comment()}); - } else { - state_hn.items.mark_active(pair.second); - } - return std::pair(pair.first, state_hn.items.at(pair.second)); - }); - window.scroll_state = - rttt::ui::drawItems(std::move(window.scroll_state), view, - window.path.mode, state_ui.item_view_states); - } else { - state_hn.items.insert({id, rttt::unknown_type()}); - } - } else { - assert(false); - } - return window; -} +rttt::ui::state ui_state; +rttt::prompt::state prompt_state; +rttt::thing::state thing_state; -void mark_update(rttt::Path &path, const ItemVariant &item) { - if (path.type == rttt::SiteType::HN) { - auto maybe = try_id_string(item); - assert(maybe); - state_hn.items.mark_for_update(std::stoi(maybe.value())); - } else if (path.type == rttt::SiteType::Reddit) { - assert(reddit::items.contains(path.name)); - reddit::items.mark_for_update(path.name); - } else if (path.type == rttt::SiteType::RSS) { - assert(std::holds_alternative<rss::Story>(item)); - state_rss.items.mark_for_update(std::get<rss::Story>(item).key); +int main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) { + auto argm = parse_arguments(argc, argv); + if (argm.find("--help") != argm.end() || argm.find("-h") != argm.end()) { + printf("Usage: rttt [-h]\n"); + printf(" -h, --help : print this help\n"); + printf(" --log : save messages to log.txt\n"); + return -1; } -} - -extern "C" { -EMSCRIPTEN_KEEPALIVE -bool render_frame() { - using namespace rttt; - - bool isActive = g_updated; - isActive |= ImTui_ImplNcurses_NewFrame(); - ImTui_ImplText_NewFrame(); - - ImGui::NewFrame(); - if (ImGui::GetIO().DisplaySize.x < 50) { - state_ui.nWindows = 1; + if (argm.find("--log") != argm.end()) { + logger::to_file("log.txt"); } - /* -Order of key bindings is quite important, because it interacts with the order -of window focus etc. We have to bind these here, because otherwise the url -popup can close before these are executed and pressing the numbers there will -also change the number |