paginated user comment screens

This commit is contained in:
Doug Hoyte
2023-09-28 16:20:50 -04:00
parent d850c2e546
commit c1441b0f18
5 changed files with 70 additions and 8 deletions

View File

@ -595,11 +595,14 @@ struct UserEvents {
}; };
std::vector<EventCluster> eventClusterArr; std::vector<EventCluster> eventClusterArr;
uint64_t totalEvents = 0;
std::optional<uint64_t> timestampCutoff;
std::optional<uint64_t> nextResumeTime;
UserEvents(lmdb::txn &txn, Decompressor &decomp, const std::string &pubkey) : u(txn, decomp, pubkey) { UserEvents(lmdb::txn &txn, Decompressor &decomp, const std::string &pubkey, uint64_t resumeTime) : u(txn, decomp, pubkey) {
flat_hash_map<std::string, EventCluster> eventClusters; // eventId (root) -> EventCluster flat_hash_map<std::string, EventCluster> eventClusters; // eventId (root) -> EventCluster
env.generic_foreachFull(txn, env.dbi_Event__pubkeyKind, makeKey_StringUint64Uint64(pubkey, 1, MAX_U64), "", [&](std::string_view k, std::string_view v){ env.generic_foreachFull(txn, env.dbi_Event__pubkeyKind, makeKey_StringUint64Uint64(pubkey, 1, resumeTime), "", [&](std::string_view k, std::string_view v){
ParsedKey_StringUint64Uint64 parsedKey(k); ParsedKey_StringUint64Uint64 parsedKey(k);
if (parsedKey.s != pubkey || parsedKey.n1 != 1) return false; if (parsedKey.s != pubkey || parsedKey.n1 != 1) return false;
@ -617,6 +620,7 @@ struct UserEvents {
cluster.isRootEventFromUser = rootEvent.getPubkey() == u.pubkey; cluster.isRootEventFromUser = rootEvent.getPubkey() == u.pubkey;
cluster.rootEventTimestamp = rootEvent.getCreatedAt(); cluster.rootEventTimestamp = rootEvent.getCreatedAt();
cluster.eventCache.emplace(rootId, std::move(rootEvent)); cluster.eventCache.emplace(rootId, std::move(rootEvent));
totalEvents++;
}; };
if (ev.root.size()) { if (ev.root.size()) {
@ -635,6 +639,7 @@ struct UserEvents {
} }
eventClusters.at(ev.root).eventCache.emplace(id, std::move(ev)); eventClusters.at(ev.root).eventCache.emplace(id, std::move(ev));
totalEvents++;
} else { } else {
// Event is root // Event is root
@ -643,6 +648,15 @@ struct UserEvents {
} }
} }
if (timestampCutoff) {
if (*timestampCutoff != parsedKey.n2) {
nextResumeTime = *timestampCutoff - 1;
return false;
}
} else if (totalEvents > 100) {
timestampCutoff = parsedKey.n2;
}
return true; return true;
}, true); }, true);
@ -666,9 +680,11 @@ struct UserEvents {
struct { struct {
std::vector<TemplarResult> &renderedThreads; std::vector<TemplarResult> &renderedThreads;
User &u; User &u;
std::optional<uint64_t> nextResumeTime;
} ctx = { } ctx = {
renderedThreads, renderedThreads,
u, u,
nextResumeTime,
}; };
return tmpl::user::comments(ctx); return tmpl::user::comments(ctx);

View File

@ -217,7 +217,14 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom
} }
if (u.path[2] == "notes") { if (u.path[2] == "notes") {
UserEvents uc(txn, decomp, userPubkey); uint64_t resumeTime = MAX_U64;
try {
auto resumeTimeStr = u.lookupQuery("next");
if (resumeTimeStr) resumeTime = std::stoull(std::string(*resumeTimeStr));
} catch(...) {}
UserEvents uc(txn, decomp, userPubkey, resumeTime);
title = std::string("notes: ") + uc.u.username; title = std::string("notes: ") + uc.u.username;
body = uc.render(txn, decomp); body = uc.render(txn, decomp);
} else if (u.path[2] == "export.jsonl") { } else if (u.path[2] == "export.jsonl") {

View File

@ -22,6 +22,34 @@ struct Url {
if (u.size()) path.emplace_back(u); if (u.size()) path.emplace_back(u);
} }
std::optional<std::string_view> lookupQuery(std::string_view key) {
std::string_view curr = query;
while (curr.size()) {
auto nextPos = curr.find("&");
{
std::string_view currKV = nextPos == std::string::npos ? curr : curr.substr(0, nextPos);
std::string_view k, v;
auto equalsPos = currKV.find("=");
if (equalsPos == std::string::npos) {
k = currKV;
} else {
k = currKV.substr(0, equalsPos);
v = currKV.substr(equalsPos + 1);
}
if (k == key) return v;
}
if (nextPos == std::string::npos) break;
curr = curr.substr(nextPos + 1);
}
return std::nullopt;
}
}; };
inline std::string renderTimestamp(uint64_t now, uint64_t ts) { inline std::string renderTimestamp(uint64_t now, uint64_t ts) {

View File

@ -180,9 +180,16 @@ table.vert {
/* user comments */ /* user comments */
.user-comments > div { .user-comments {
margin-bottom: 30px; .note-thread {
border-bottom: 1px solid black; margin-bottom: 30px;
border-bottom: 1px solid black;
}
.more-notes {
margin-left: 20px;
margin-bottom: 20px;
}
} }

View File

@ -1,10 +1,14 @@
<div class="user-comments"> <div class="user-comments">
<h2> <h2 class="notes-header">
Notes by <a href="/u/$(ctx.u.npubId)">$(ctx.u.username)</a> Notes by <a href="/u/$(ctx.u.npubId)">$(ctx.u.username)</a>
| <a href="/u/$(ctx.u.npubId)/export.jsonl">export</a> | <a href="/u/$(ctx.u.npubId)/export.jsonl">export</a>
</h2> </h2>
<div> @(auto &r : ctx.renderedThreads) <div class="note-thread"> @(auto &r : ctx.renderedThreads)
$(r) $(r)
</div> </div>
<div class="more-notes"> ?(ctx.nextResumeTime)
<a href="/u/$(ctx.u.npubId)/notes?next=$(*ctx.nextResumeTime)">More notes by $(ctx.u.username)</a>
</div>
</div> </div>