Skip to content

Commit

Permalink
paginate followers
Browse files Browse the repository at this point in the history
  • Loading branch information
hoytech committed Dec 19, 2024
1 parent 4d658ae commit d798320
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 7 deletions.
1 change: 0 additions & 1 deletion src/apps/web/TODO
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 6 additions & 2 deletions src/apps/web/WebData.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
};
Expand Down
35 changes: 32 additions & 3 deletions src/apps/web/WebReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions src/apps/web/static/oddbean.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/apps/web/tmpls/feed/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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>
8 changes: 8 additions & 0 deletions src/apps/web/tmpls/user/followers.tmpl
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -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>
2 changes: 2 additions & 0 deletions src/apps/web/tmpls/user/following.tmpl
Original file line number Diff line number Diff line change
@@ -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>
Expand Down

0 comments on commit d798320

Please sign in to comment.