From 0ade4479260944dce06afa23876cec0620fc217d Mon Sep 17 00:00:00 2001 From: Doug Hoyte Date: Thu, 7 Sep 2023 15:53:09 -0400 Subject: [PATCH] wip --- src/apps/web/WebData.h | 37 +++++++------ src/apps/web/WebReader.cpp | 20 ++++++- src/apps/web/static/oddbean.js | 85 +++++++++++++++++++++++++++++ src/apps/web/tmpls/event/event.tmpl | 7 ++- src/apps/web/tmpls/event/reply.tmpl | 12 ++++ 5 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 src/apps/web/tmpls/event/reply.tmpl diff --git a/src/apps/web/WebData.h b/src/apps/web/WebData.h index 5a95544..2f7d43a 100644 --- a/src/apps/web/WebData.h +++ b/src/apps/web/WebData.h @@ -357,6 +357,23 @@ inline std::string stripUrls(std::string &content) { } +struct ReplyCtx { + uint64_t timestamp; + TemplarResult rendered; +}; + +struct RenderedEventCtx { + std::string content; + std::string timestamp; + const Event *ev = nullptr; + const User *user = nullptr; + bool isFullThreadLoaded = false; + bool eventPresent = true; + bool abbrev = false; + bool highlight = false; + bool showActions = true; + std::vector replies; +}; struct EventThread { @@ -444,25 +461,9 @@ struct EventThread { auto now = hoytech::curr_time_s(); flat_hash_set processedLevIds; - struct Reply { - uint64_t timestamp; - TemplarResult rendered; - }; - - struct RenderedEvent { - std::string content; - std::string timestamp; - const Event *ev = nullptr; - const User *user = nullptr; - bool isFullThreadLoaded = false; - bool eventPresent = true; - bool abbrev = false; - bool highlight = false; - std::vector replies; - }; std::function process = [&](const std::string &id){ - RenderedEvent ctx; + RenderedEventCtx ctx; auto p = eventCache.find(id); if (p != eventCache.end()) { @@ -508,7 +509,7 @@ struct EventThread { struct { TemplarResult foundEvents; - std::vector orphanNodes; + std::vector orphanNodes; } ctx; ctx.foundEvents = process(rootEventId); diff --git a/src/apps/web/WebReader.cpp b/src/apps/web/WebReader.cpp index bbb45b1..a982e02 100644 --- a/src/apps/web/WebReader.cpp +++ b/src/apps/web/WebReader.cpp @@ -2,6 +2,9 @@ #include "WebData.h" +#include "WebStaticFiles.h" + + @@ -93,7 +96,7 @@ void doSearch(lmdb::txn &txn, Decompressor &decomp, std::string_view search, std TemplarResult renderCommunityEvents(lmdb::txn &txn, Decompressor &decomp, UserCache &userCache, const CommunitySpec &communitySpec) { AlgoScanner a(txn, communitySpec.algo); - auto events = a.getEvents(txn, decomp, 300); + auto events = a.getEvents(txn, decomp, 60); std::vector rendered; auto now = hoytech::curr_time_s(); @@ -176,7 +179,20 @@ HTTPResponse WebServer::generateReadResponse(lmdb::txn &txn, Decompressor &decom EventThread et(txn, decomp, decodeBech32Simple(u.path[1])); body = et.render(txn, decomp, userCache); } else if (u.path.size() == 3) { - if (u.path[2] == "raw.json") { + if (u.path[2] == "reply") { + auto ev = Event::fromIdExternal(txn, u.path[1]); + ev.populateJson(txn, decomp); + + RenderedEventCtx ctx; + + ctx.timestamp = renderTimestamp(startTime / 1'000'000, ev.getCreatedAt()); + ctx.content = templarInternal::htmlEscape(ev.json.at("content").get_string(), false); + ctx.ev = &ev; + ctx.user = userCache.getUser(txn, decomp, ev.getPubkey()); + ctx.showActions = false; + + body = tmpl::event::reply(ctx); + } else if (u.path[2] == "raw.json") { auto ev = Event::fromIdExternal(txn, u.path[1]); ev.populateJson(txn, decomp); rawBody = tao::json::to_string(ev.json, 4); diff --git a/src/apps/web/static/oddbean.js b/src/apps/web/static/oddbean.js index c7c86a7..edcd90c 100644 --- a/src/apps/web/static/oddbean.js +++ b/src/apps/web/static/oddbean.js @@ -79,6 +79,91 @@ document.addEventListener('alpine:init', () => { let json = await resp.json(); + if (json.message === 'ok' && json.written === true) { + window.location = `/e/${json.event}` + } else { + this.$refs.msg.innerText = `Sending note failed: ${json.message}`; + console.error(json); + } + }, + })); + + Alpine.data('newReply', (note) => ({ + async init() { + let resp = await fetch(`/e/${note}/raw.json`); + this.repliedTo = await resp.json(); + }, + + async submit() { + this.$refs.msg.innerText = ''; + + let ev = { + created_at: Math.floor(((new Date()) - 0) / 1000), + kind: 1, + tags: [], + content: this.$refs.post.value, + }; + + { + // e tags + + let rootId; + for (let t of this.repliedTo.tags) { + if (t[0] === 'e' && t[3] === 'root') { + rootId = t[1]; + break; + } + } + + if (!rootId) { + for (let t of this.repliedTo.tags) { + if (t[0] === 'e') { + rootId = t[1]; + break; + } + } + } + + if (rootId) { + ev.tags.push(['e', rootId, '', 'root']); + ev.tags.push(['e', this.repliedTo.id, '', 'reply']); + } else { + ev.tags.push(['e', this.repliedTo.id, '', 'root']); + } + + // p tags + + let seenPTags = {}; + + for (let t of this.repliedTo.tags) { + if (t[0] === 'p' && !seenPTags[t[1]]) { + ev.tags.push(['p', t[1]]); + seenPTags[t[1]] = true; + } + } + + if (!seenPTags[this.repliedTo.pubkey]) { + ev.tags.push(['p', this.repliedTo.pubkey]); + } + + // t tags + + ev.tags.push(['t', 'oddbean']); + } + + ev = await window.nostr.signEvent(ev); + + let resp = await fetch("/submit-post", { + method: "post", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(ev), + }); + + let json = await resp.json(); + if (json.message === 'ok' && json.written === true) { window.location = `/e/${json.event}` } else { diff --git a/src/apps/web/tmpls/event/event.tmpl b/src/apps/web/tmpls/event/event.tmpl index 5359f1a..15b0ebf 100644 --- a/src/apps/web/tmpls/event/event.tmpl +++ b/src/apps/web/tmpls/event/event.tmpl @@ -17,10 +17,11 @@ <> | parent ?(ctx.ev->parent.size()) -
+ <> + | reply + | flag + ?(ctx.showActions) - reply - | flag <>+$(ctx.ev->upVotes) ?(ctx.ev->upVotes) <>-$(ctx.ev->downVotes) ?(ctx.ev->downVotes) diff --git a/src/apps/web/tmpls/event/reply.tmpl b/src/apps/web/tmpls/event/reply.tmpl new file mode 100644 index 0000000..a4de88b --- /dev/null +++ b/src/apps/web/tmpls/event/reply.tmpl @@ -0,0 +1,12 @@ +
+ $(event::event(ctx)) + + + +
+ +
+ +
+
+