mirror of
https://github.com/hoytech/strfry.git
synced 2025-06-19 17:37:43 +00:00
wip
This commit is contained in:
@ -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<ReplyCtx> replies;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct EventThread {
|
struct EventThread {
|
||||||
@ -444,25 +461,9 @@ struct EventThread {
|
|||||||
auto now = hoytech::curr_time_s();
|
auto now = hoytech::curr_time_s();
|
||||||
flat_hash_set<uint64_t> processedLevIds;
|
flat_hash_set<uint64_t> 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<Reply> replies;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::function<TemplarResult(const std::string &)> process = [&](const std::string &id){
|
std::function<TemplarResult(const std::string &)> process = [&](const std::string &id){
|
||||||
RenderedEvent ctx;
|
RenderedEventCtx ctx;
|
||||||
|
|
||||||
auto p = eventCache.find(id);
|
auto p = eventCache.find(id);
|
||||||
if (p != eventCache.end()) {
|
if (p != eventCache.end()) {
|
||||||
@ -508,7 +509,7 @@ struct EventThread {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
TemplarResult foundEvents;
|
TemplarResult foundEvents;
|
||||||
std::vector<Reply> orphanNodes;
|
std::vector<ReplyCtx> orphanNodes;
|
||||||
} ctx;
|
} ctx;
|
||||||
|
|
||||||
ctx.foundEvents = process(rootEventId);
|
ctx.foundEvents = process(rootEventId);
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
#include "WebData.h"
|
#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) {
|
TemplarResult renderCommunityEvents(lmdb::txn &txn, Decompressor &decomp, UserCache &userCache, const CommunitySpec &communitySpec) {
|
||||||
AlgoScanner a(txn, communitySpec.algo);
|
AlgoScanner a(txn, communitySpec.algo);
|
||||||
auto events = a.getEvents(txn, decomp, 300);
|
auto events = a.getEvents(txn, decomp, 60);
|
||||||
|
|
||||||
std::vector<TemplarResult> rendered;
|
std::vector<TemplarResult> rendered;
|
||||||
auto now = hoytech::curr_time_s();
|
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]));
|
EventThread et(txn, decomp, decodeBech32Simple(u.path[1]));
|
||||||
body = et.render(txn, decomp, userCache);
|
body = et.render(txn, decomp, userCache);
|
||||||
} else if (u.path.size() == 3) {
|
} 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]);
|
auto ev = Event::fromIdExternal(txn, u.path[1]);
|
||||||
ev.populateJson(txn, decomp);
|
ev.populateJson(txn, decomp);
|
||||||
rawBody = tao::json::to_string(ev.json, 4);
|
rawBody = tao::json::to_string(ev.json, 4);
|
||||||
|
@ -79,6 +79,91 @@ document.addEventListener('alpine:init', () => {
|
|||||||
|
|
||||||
let json = await resp.json();
|
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) {
|
if (json.message === 'ok' && json.written === true) {
|
||||||
window.location = `/e/${json.event}`
|
window.location = `/e/${json.event}`
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
|
|
||||||
<> | <a href="/e/$(ctx.ev->getParentNoteId())">parent</a> </> ?(ctx.ev->parent.size())
|
<> | <a href="/e/$(ctx.ev->getParentNoteId())">parent</a> </> ?(ctx.ev->parent.size())
|
||||||
|
|
||||||
<br/>
|
<>
|
||||||
|
| <a href="/e/$(ctx.ev->getNoteId())/reply">reply</a>
|
||||||
<a href="">reply</a>
|
|
||||||
| <a href="" class="f">flag</a>
|
| <a href="" class="f">flag</a>
|
||||||
|
</> ?(ctx.showActions)
|
||||||
|
|
||||||
<>+$(ctx.ev->upVotes)</> ?(ctx.ev->upVotes)
|
<>+$(ctx.ev->upVotes)</> ?(ctx.ev->upVotes)
|
||||||
<>-$(ctx.ev->downVotes)</> ?(ctx.ev->downVotes)
|
<>-$(ctx.ev->downVotes)</> ?(ctx.ev->downVotes)
|
||||||
</div>
|
</div>
|
||||||
|
12
src/apps/web/tmpls/event/reply.tmpl
Normal file
12
src/apps/web/tmpls/event/reply.tmpl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<div x-data="newReply('$(ctx.ev->getNoteId())')" class="new-post">
|
||||||
|
$(event::event(ctx))
|
||||||
|
|
||||||
|
<textarea id="post-text" name="post-text" x-ref="post" rows="8" cols="80" wrap="virtual"></textarea>
|
||||||
|
|
||||||
|
<div @click="submit" class="submit-button">
|
||||||
|
<button>Reply</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-message" x-ref="msg">
|
||||||
|
</div>
|
||||||
|
</div>
|
Reference in New Issue
Block a user