Show related tweets under replies

Fixes #1334
This commit is contained in:
Zed 2026-07-11 00:11:46 +02:00
commit 092397cd6f
6 changed files with 46 additions and 3 deletions

View file

@ -710,10 +710,26 @@ proc parseGraphTweet*(js: JsonNode): Tweet =
with birdwatch, js{"birdwatch_pivot"}: with birdwatch, js{"birdwatch_pivot"}:
result.note = parseCommunityNote(birdwatch) result.note = parseCommunityNote(birdwatch)
proc getConvSection(js: JsonNode): string =
let details = select(
js{"item", "client_event_info", "details"},
js{"item", "clientEventInfo", "details"}
)
select(
details{"conversation_details", "conversation_section"},
details{"conversationDetails", "conversationSection"}
).getStr
proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] = proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] =
var checkedSection = false
for t in ? js{"content", "items"}: for t in ? js{"content", "items"}:
let entryId = t.getEntryId let entryId = t.getEntryId
if "tweet-" in entryId and "promoted" notin entryId: if "tweet-" in entryId and "promoted" notin entryId:
if not checkedSection:
checkedSection = true
if getConvSection(t) == "RelatedTweet":
result.thread.related = true
let tweet = t.getTweetResult("item") let tweet = t.getTweetResult("item")
if tweet.notNull: if tweet.notNull:
result.thread.content.add parseGraphTweet(tweet) result.thread.content.add parseGraphTweet(tweet)
@ -774,7 +790,8 @@ proc parseGraphConversation*(js: JsonNode; tweetId: string): Conversation =
result.before.content.add tweet result.before.content.add tweet
elif not entryId.endsWith(tweetId): elif not entryId.endsWith(tweetId):
result.before.content.add Tweet(id: entryId.getId) result.before.content.add Tweet(id: entryId.getId)
elif entryId.startsWith("conversationthread"): elif entryId.startsWith("conversationthread") or
entryId.startsWith("tweetdetailrelatedtweets"):
let (thread, self) = parseGraphThread(e) let (thread, self) = parseGraphThread(e)
if self: if self:
result.after = thread result.after = thread

View file

@ -78,6 +78,9 @@ genPrefs:
hideReplies(checkbox, false): hideReplies(checkbox, false):
"Hide tweet replies" "Hide tweet replies"
hideRelated(checkbox, true):
"Hide related tweets under replies"
hideCommunityNotes(checkbox, false): hideCommunityNotes(checkbox, false):
"Hide community notes" "Hide community notes"

View file

@ -185,3 +185,12 @@
} }
} }
} }
.related-header {
padding: 8px 12px;
margin-top: 10px;
background-color: var(--bg_panel);
color: var(--fg_faded);
font-size: 14px;
border-bottom: 1px solid var(--border_grey);
}

View file

@ -359,6 +359,7 @@ type
content*: Tweets content*: Tweets
hasMore*: bool hasMore*: bool
cursor*: string cursor*: string
related*: bool
Conversation* = ref object Conversation* = ref object
tweet*: Tweet tweet*: Tweet

View file

@ -50,7 +50,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
let opensearchUrl = getUrlPrefix(cfg) & "/opensearch" let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
buildHtml(head): buildHtml(head):
link(rel="stylesheet", type="text/css", href="/css/style.css?v=50") link(rel="stylesheet", type="text/css", href="/css/style.css?v=51")
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=7") link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=7")
if theme.len > 0: if theme.len > 0:

View file

@ -1,4 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
import sequtils
import karax/[karaxdsl, vdom] import karax/[karaxdsl, vdom]
import ".."/[types, formatters] import ".."/[types, formatters]
@ -48,7 +49,7 @@ proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string;
var hasReplies = false var hasReplies = false
var replyCount = 0 var replyCount = 0
for thread in replies.content: for thread in replies.content:
if thread.content.len == 0: continue if thread.content.len == 0 or thread.related: continue
hasReplies = true hasReplies = true
replyCount += thread.content.len replyCount += thread.content.len
renderReplyThread(thread, prefs, path) renderReplyThread(thread, prefs, path)
@ -58,6 +59,14 @@ proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string;
let extra = if sort == Relevance: "" else: "sort=" & $sort & "&" let extra = if sort == Relevance: "" else: "sort=" & $sort & "&"
renderMore(Query(), replies.bottom, focus="#r", extra=extra) renderMore(Query(), replies.bottom, focus="#r", extra=extra)
proc renderRelated(replies: Result[Chain]; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="related-tweets")):
tdiv(class="related-header"):
text "Related tweets"
for thread in replies.content:
if thread.content.len == 0 or not thread.related: continue
renderReplyThread(thread, prefs, path)
proc renderConversation*(conv: Conversation; prefs: Prefs; path: string; proc renderConversation*(conv: Conversation; prefs: Prefs; path: string;
sort = Relevance): VNode = sort = Relevance): VNode =
let hasAfter = conv.after.content.len > 0 let hasAfter = conv.after.content.len > 0
@ -95,6 +104,10 @@ proc renderConversation*(conv: Conversation; prefs: Prefs; path: string;
renderReplySort(sort) renderReplySort(sort)
renderReplies(conv.replies, prefs, path, conv.tweet, sort) renderReplies(conv.replies, prefs, path, conv.tweet, sort)
if not prefs.hideRelated:
if conv.replies.content.anyIt(it.related and it.content.len > 0):
renderRelated(conv.replies, prefs, path)
renderToTop(focus="#m") renderToTop(focus="#m")
proc renderEditHistory*(edits: EditHistory; prefs: Prefs; path: string): VNode = proc renderEditHistory*(edits: EditHistory; prefs: Prefs; path: string): VNode =