diff --git a/src/apps/web/FeedReader.h b/src/apps/web/FeedReader.h index b9d2060..fc05d4a 100644 --- a/src/apps/web/FeedReader.h +++ b/src/apps/web/FeedReader.h @@ -1,6 +1,13 @@ #pragma once struct FeedReader { + std::string feedId; + + bool found = false; + tao::json::value feedJson; + std::string pubkey; + std::string feedName; + struct EventInfo { uint64_t comments = 0; double score = 0.0; @@ -13,36 +20,38 @@ struct FeedReader { EventInfo info; }; - std::vector getEvents(lmdb::txn &txn, Decompressor &decomp, const std::string &feedId, uint64_t resultsPerPage, uint64_t page) { - uint64_t skip = page * resultsPerPage; - uint64_t remaining = resultsPerPage; + FeedReader(lmdb::txn &txn, Decompressor &decomp, std::string_view feedId_) : feedId(feedId_) { + if (feedId == "homepage") feedId = cfg().web__homepageFeedId; size_t pos = feedId.find("."); if (pos == std::string_view::npos) throw herr("bad feedId: ", feedId); - std::string pubkey = from_hex(feedId.substr(0, pos)); - std::string adminTopic = feedId.substr(pos + 1); + pubkey = from_hex(feedId.substr(0, pos)); + feedName = feedId.substr(pos + 1); tao::json::value filter = tao::json::value({ { "authors", tao::json::value::array({ to_hex(pubkey) }) }, { "kinds", tao::json::value::array({ uint64_t(33800) }) }, - { "#d", tao::json::value::array({ adminTopic }) }, + { "#d", tao::json::value::array({ feedName }) }, }); - bool found = false; - tao::json::value feedJson; - foreachByFilter(txn, filter, [&](uint64_t levId){ feedJson = tao::json::from_string(getEventJson(txn, decomp, levId)); found = true; return false; }); + } + std::vector getEvents(lmdb::txn &txn, Decompressor &decomp, uint64_t resultsPerPage, uint64_t page) const { if (!found) throw herr("unable to lookup feedId: ", feedId); std::vector output; + uint64_t skip = page * resultsPerPage; + uint64_t remaining = resultsPerPage; + + tao::json::value currFeedJson = feedJson; while (true) { - const auto &tags = feedJson.at("tags").get_array(); + const auto &tags = currFeedJson.at("tags").get_array(); std::string prev; for (const auto &tag : tags) { @@ -75,7 +84,7 @@ struct FeedReader { if (remaining && prev.size()) { auto ev = lookupEventById(txn, prev); if (ev) { - feedJson = tao::json::from_string(getEventJson(txn, decomp, ev->primaryKeyId)); + currFeedJson = tao::json::from_string(getEventJson(txn, decomp, ev->primaryKeyId)); continue; } } @@ -86,7 +95,7 @@ struct FeedReader { return output; } - EventInfo buildEventInfo(lmdb::txn &txn, const std::string &id) { + EventInfo buildEventInfo(lmdb::txn &txn, const std::string &id) const { EventInfo output; std::string prefix = "e"; diff --git a/src/apps/web/WebReader.cpp b/src/apps/web/WebReader.cpp index 9a8b1b5..d13cc99 100644 --- a/src/apps/web/WebReader.cpp +++ b/src/apps/web/WebReader.cpp @@ -92,9 +92,8 @@ void doSearch(lmdb::txn &txn, Decompressor &decomp, std::string_view search, std -TemplarResult renderFeed(lmdb::txn &txn, Decompressor &decomp, UserCache &userCache, const std::string &feedId, uint64_t resultsPerPage, uint64_t page) { - FeedReader feedReader; - auto events = feedReader.getEvents(txn, decomp, feedId, resultsPerPage, page); +TemplarResult renderFeed(lmdb::txn &txn, Decompressor &decomp, UserCache &userCache, const FeedReader &feedReader, uint64_t resultsPerPage, uint64_t page) { + auto events = feedReader.getEvents(txn, decomp, resultsPerPage, page); std::vector rendered; auto now = hoytech::curr_time_s(); @@ -165,15 +164,24 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom std::optional rawBody; + // Misc: + + std::optional feedReader; + if (u.path.size() == 0) { uint64_t resultsPerPage = 30; uint64_t page = 0; auto pageStr = u.lookupQuery("p"); if (pageStr) page = std::stoull(std::string(*pageStr)); - body = renderFeed(txn, decomp, userCache, cfg().web__homepageFeedId, resultsPerPage, page); + feedReader.emplace(txn, decomp, "homepage"); - httpResp.extraHeaders += "Cache-Control: max-age=30\r\n"; + if (feedReader->found) { + body = renderFeed(txn, decomp, userCache, *feedReader, resultsPerPage, page); + httpResp.extraHeaders += "Cache-Control: max-age=30\r\n"; + } else { + rawBody = "Feed not found."; + } } else if (u.path[0] == "e") { if (u.path.size() == 2) { EventThread et(txn, decomp, decodeBech32Simple(u.path[1])); @@ -267,6 +275,26 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom body = tmpl::user::followers(ctx); } } + } else if (u.path[0] == "f") { + if (u.path.size() == 2) { + feedReader.emplace(txn, decomp, u.path[1]); + // FIXME: feed + } else if (u.path.size() == 3 && u.path[2] == "info") { + feedReader.emplace(txn, decomp, u.path[1]); + + if (feedReader->found) { + struct { + const std::optional &feedReader; + } ctx = { + feedReader, + }; + + body = tmpl::feed::info(ctx); + title = feedReader->feedName; + } else { + rawBody = "Feed not found."; + } + } } else if (u.path[0] == "search") { std::vector results; @@ -326,6 +354,7 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom std::string_view staticOddbeanCssHash; std::string_view staticOddbeanJsHash; std::string_view staticOddbeanSvgHash; + const std::optional &feedReader; } ctx = { *body, title, @@ -333,6 +362,7 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom oddbeanStatic__oddbean_css__hash().substr(0, 16), oddbeanStatic__oddbean_js__hash().substr(0, 16), oddbeanStatic__oddbean_svg__hash().substr(0, 16), + feedReader, }; responseData = std::move(tmpl::main(ctx).str); diff --git a/src/apps/web/tmpls/feed/info.tmpl b/src/apps/web/tmpls/feed/info.tmpl new file mode 100644 index 0000000..75d5f89 --- /dev/null +++ b/src/apps/web/tmpls/feed/info.tmpl @@ -0,0 +1,9 @@ +
+

+ Feed: $(ctx.feedReader->feedName) +

+ +

+ ID: $(ctx.feedReader->feedId) +

+
diff --git a/src/apps/web/tmpls/main.tmpl b/src/apps/web/tmpls/main.tmpl index 0ac4fef..44defaf 100644 --- a/src/apps/web/tmpls/main.tmpl +++ b/src/apps/web/tmpls/main.tmpl @@ -14,9 +14,10 @@ - - Oddbean - + Oddbean + <> ?(ctx.feedReader) + / $(ctx.feedReader->feedName) + new post