mirror of
https://github.com/hoytech/strfry.git
synced 2025-06-18 09:17:12 +00:00
paginated user comment screens
This commit is contained in:
@ -595,11 +595,14 @@ struct UserEvents {
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
if (parsedKey.s != pubkey || parsedKey.n1 != 1) return false;
|
||||
|
||||
@ -617,6 +620,7 @@ struct UserEvents {
|
||||
cluster.isRootEventFromUser = rootEvent.getPubkey() == u.pubkey;
|
||||
cluster.rootEventTimestamp = rootEvent.getCreatedAt();
|
||||
cluster.eventCache.emplace(rootId, std::move(rootEvent));
|
||||
totalEvents++;
|
||||
};
|
||||
|
||||
if (ev.root.size()) {
|
||||
@ -635,6 +639,7 @@ struct UserEvents {
|
||||
}
|
||||
|
||||
eventClusters.at(ev.root).eventCache.emplace(id, std::move(ev));
|
||||
totalEvents++;
|
||||
} else {
|
||||
// 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;
|
||||
}, true);
|
||||
|
||||
@ -666,9 +680,11 @@ struct UserEvents {
|
||||
struct {
|
||||
std::vector<TemplarResult> &renderedThreads;
|
||||
User &u;
|
||||
std::optional<uint64_t> nextResumeTime;
|
||||
} ctx = {
|
||||
renderedThreads,
|
||||
u,
|
||||
nextResumeTime,
|
||||
};
|
||||
|
||||
return tmpl::user::comments(ctx);
|
||||
|
@ -217,7 +217,14 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom
|
||||
}
|
||||
|
||||
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;
|
||||
body = uc.render(txn, decomp);
|
||||
} else if (u.path[2] == "export.jsonl") {
|
||||
|
@ -22,6 +22,34 @@ struct Url {
|
||||
|
||||
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) {
|
||||
|
@ -180,9 +180,16 @@ table.vert {
|
||||
|
||||
/* user comments */
|
||||
|
||||
.user-comments > div {
|
||||
.user-comments {
|
||||
.note-thread {
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.more-notes {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,10 +1,14 @@
|
||||
<div class="user-comments">
|
||||
<h2>
|
||||
<h2 class="notes-header">
|
||||
Notes by <a href="/u/$(ctx.u.npubId)">$(ctx.u.username)</a>
|
||||
| <a href="/u/$(ctx.u.npubId)/export.jsonl">export</a>
|
||||
</h2>
|
||||
|
||||
<div> @(auto &r : ctx.renderedThreads)
|
||||
<div class="note-thread"> @(auto &r : ctx.renderedThreads)
|
||||
$(r)
|
||||
</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>
|
||||
|
Reference in New Issue
Block a user