paginate followers

This commit is contained in:
Doug Hoyte
2024-12-19 00:44:16 -05:00
parent 20440f95bb
commit 87af802aa0
7 changed files with 57 additions and 7 deletions

View File

@ -1,5 +1,4 @@
read
! paginate followers
* nostr: links not replaced in feed titles
* support nprofile/nevent/etc links
* non-500 error pages when bech32 fails to parse, for example

View File

@ -128,13 +128,15 @@ struct User {
kind3Event = loadKindEvent(txn, decomp, 3);
}
std::vector<std::string> getFollowers(lmdb::txn &txn, Decompressor &decomp, const std::string &pubkey) {
std::vector<std::string> getFollowers(lmdb::txn &txn, Decompressor &decomp, const std::string &pubkey, uint64_t offset = 0, uint64_t limit = MAX_U64, uint64_t *countOut = nullptr) {
std::vector<std::string> output;
flat_hash_set<std::string> alreadySeen;
std::string prefix = "p";
prefix += pubkey;
uint64_t curr = 0;
env.generic_foreachFull(txn, env.dbi_Event__tag, prefix, "", [&](std::string_view k, std::string_view v){
ParsedKey_StringUint64 parsedKey(k);
if (parsedKey.s != prefix) return false;
@ -148,13 +150,15 @@ struct User {
if (!alreadySeen.contains(pubkey)) {
alreadySeen.insert(pubkey);
output.emplace_back(std::move(pubkey));
curr++;
if (curr >= offset && curr - offset < limit) output.emplace_back(std::move(pubkey));
}
}
return true;
});
if (countOut) *countOut = curr;
return output;
}
};

View File

@ -186,8 +186,11 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom
auto handleFeed = [&](std::string_view feedId){
uint64_t resultsPerPage = 30;
uint64_t page = 0;
auto pageStr = u.lookupQuery("p");
if (pageStr) page = std::stoull(std::string(*pageStr));
try {
auto pageStr = u.lookupQuery("p");
if (pageStr) page = std::stoull(std::string(*pageStr));
} catch(...) {}
feedReader.emplace(txn, decomp, feedId);
@ -267,28 +270,54 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom
title = std::string("following: ") + user.username;
user.populateContactList(txn, decomp);
uint64_t numFollowing = 0;
if (user.kind3Event) {
for (const auto &tagJson : user.kind3Event->at("tags").get_array()) {
const auto &tag = tagJson.get_array();
if (tag.size() >= 2 && tag.at(0).get_string() == "p") numFollowing++;
}
}
struct {
User &user;
std::function<const User*(const std::string &)> getUser;
uint64_t numFollowing;
} ctx = {
user,
[&](const std::string &pubkey){ return userCache.getUser(txn, decomp, pubkey); },
numFollowing,
};
body = tmpl::user::following(ctx);
} else if (u.path[2] == "followers") {
uint64_t resultsPerPage = 500;
uint64_t page = 0;
try {
auto pageStr = u.lookupQuery("p");
if (pageStr) page = std::stoull(std::string(*pageStr));
} catch(...) {}
User user(txn, decomp, userPubkey);
title = std::string("followers: ") + user.username;
auto followers = user.getFollowers(txn, decomp, user.pubkey);
uint64_t numFollowers = 0;
auto followers = user.getFollowers(txn, decomp, user.pubkey, page * resultsPerPage, resultsPerPage, &numFollowers);
uint64_t numPages = (numFollowers + resultsPerPage + 1) / resultsPerPage;
struct {
const User &user;
const std::vector<std::string> &followers;
uint64_t numFollowers;
std::function<const User*(const std::string &)> getUser;
uint64_t page;
uint64_t numPages;
} ctx = {
user,
followers,
numFollowers,
[&](const std::string &pubkey){ return userCache.getUser(txn, decomp, pubkey); },
page,
numPages,
};
body = tmpl::user::followers(ctx);

View File

@ -292,6 +292,14 @@ table.vert {
margin-left: 40px;
}
.pagination-links {
margin-top: 40px;
text-align: center;
> * {
margin: 10px;
}
}
.feed-info {
.feed-id {
background-color: #dfdfdf;

View File

@ -3,6 +3,6 @@
</div>
<div class="feed-nav-links">
<a href="./?p=$(ctx.page + 1)">More</a> ?(ctx.n != 0)
<a href="?p=$(ctx.page + 1)">More</a> ?(ctx.n != 0)
<span>The end.</span> ?(ctx.n == 0)
</div>

View File

@ -1,4 +1,6 @@
<div class="user-followers">
<h2><a href="/u/$(ctx.user.npubId)">$(ctx.user.username)</a> has $(ctx.numFollowers) known followers.</h2>
<table class="vert">
<tr>
<th>user</th>
@ -16,4 +18,10 @@
</>
</table>
<div class="pagination-links">
<a href="?p=$(ctx.page - 1)">Prev</a> ?(ctx.numPages && ctx.page > 0)
<span>$(ctx.page + 1) / $(ctx.numPages)</span>
<a href="?p=$(ctx.page + 1)">Next</a> ?(ctx.numPages && ctx.page < ctx.numPages-1)
</div>
</div>

View File

@ -1,4 +1,6 @@
<div class="user-following">
<h2><a href="/u/$(ctx.user.npubId)">$(ctx.user.username)</a> is following $(ctx.numFollowing) users.</h2> ?(ctx.user.kind3Event)
<div> ?(!ctx.user.kind3Event)
No kind 3 contact list found for $(ctx.user.npubId)
</div>