Support "Replying to" and "Show thread"
This commit is contained in:
parent
97166feec9
commit
a67d27e0c4
5 changed files with 83 additions and 43 deletions
|
@ -138,6 +138,11 @@ a:hover {
|
|||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.replying-to {
|
||||
color: hsla(240,1%,73%,.7);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.status-el .status-content {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.4em;
|
||||
|
|
|
@ -41,8 +41,10 @@ proc parseTweetProfile*(profile: XmlNode): Profile =
|
|||
|
||||
proc parseQuote*(quote: XmlNode): Quote =
|
||||
result = Quote(
|
||||
id: quote.attr("data-item-id"),
|
||||
text: getQuoteText(quote)
|
||||
id: quote.attr("data-item-id"),
|
||||
text: getQuoteText(quote),
|
||||
reply: parseTweetReply(quote),
|
||||
hasThread: quote.select(".self-thread-context") != nil,
|
||||
)
|
||||
|
||||
result.profile = Profile(
|
||||
|
@ -64,6 +66,8 @@ proc parseTweet*(node: XmlNode): Tweet =
|
|||
shortTime: getShortTime(tweet),
|
||||
profile: parseTweetProfile(tweet),
|
||||
stats: parseTweetStats(tweet),
|
||||
reply: parseTweetReply(tweet),
|
||||
hasThread: tweet.select(".self-thread-context") != nil,
|
||||
pinned: "pinned" in tweet.attr("class"),
|
||||
available: true
|
||||
)
|
||||
|
|
|
@ -120,11 +120,17 @@ proc parseTweetStats*(node: XmlNode): TweetStats =
|
|||
of "rep": result.replies = text[0]
|
||||
of "lik": result.likes = text[0]
|
||||
|
||||
proc parseTweetReply*(node: XmlNode): seq[string] =
|
||||
let reply = node.select(".ReplyingToContextBelowAuthor")
|
||||
if reply == nil: return
|
||||
for username in reply.selectAll("a"):
|
||||
result.add username.selectText("b")
|
||||
|
||||
proc getGif(player: XmlNode): Gif =
|
||||
let
|
||||
thumb = player.attr("style").replace(thumbRegex, "$1")
|
||||
id = thumb.replace(gifRegex, "$1")
|
||||
url = fmt"https://video.twimg.com/tweet_video/{id}.mp4"
|
||||
url = &"https://video.twimg.com/tweet_video/{id}.mp4"
|
||||
Gif(url: url, thumb: thumb)
|
||||
|
||||
proc getTweetMedia*(tweet: Tweet; node: XmlNode) =
|
||||
|
@ -146,15 +152,15 @@ proc getQuoteMedia*(quote: var Quote; node: XmlNode) =
|
|||
|
||||
let media = node.select(".QuoteMedia")
|
||||
if media != nil:
|
||||
quote.thumb = some(media.selectAttr("img", "src"))
|
||||
quote.thumb = media.selectAttr("img", "src")
|
||||
|
||||
let badge = node.select(".AdaptiveMedia-badgeText")
|
||||
let gifBadge = node.select(".Icon--gifBadge")
|
||||
|
||||
if badge != nil:
|
||||
quote.badge = some(badge.innerText())
|
||||
quote.badge = badge.innerText()
|
||||
elif gifBadge != nil:
|
||||
quote.badge = some("GIF")
|
||||
quote.badge = "GIF"
|
||||
|
||||
proc getTweetCards*(tweet: Tweet; node: XmlNode) =
|
||||
if node.attr("data-has-cards") == "false": return
|
||||
|
|
|
@ -58,9 +58,11 @@ type
|
|||
id*: string
|
||||
profile*: Profile
|
||||
text*: string
|
||||
reply*: seq[string]
|
||||
hasThread*: bool
|
||||
sensitive*: bool
|
||||
thumb*: Option[string]
|
||||
badge*: Option[string]
|
||||
thumb*: string
|
||||
badge*: string
|
||||
|
||||
Retweet* = object
|
||||
by*: string
|
||||
|
@ -77,8 +79,10 @@ type
|
|||
text*: string
|
||||
time*: Time
|
||||
shortTime*: string
|
||||
available*: bool
|
||||
reply*: seq[string]
|
||||
pinned*: bool
|
||||
available*: bool
|
||||
hasThread*: bool
|
||||
stats*: TweetStats
|
||||
retweet*: Option[Retweet]
|
||||
quote*: Option[Quote]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#? stdtmpl(subsChar = '$', metaChar = '#')
|
||||
#import xmltree, strutils, times, sequtils, uri
|
||||
#import xmltree, strutils, strformat, sequtils, times, uri
|
||||
#import ../types, ../formatters, ../utils
|
||||
#
|
||||
#proc renderHeading(tweet: Tweet): string =
|
||||
|
@ -29,38 +29,6 @@
|
|||
</div>
|
||||
#end proc
|
||||
#
|
||||
#proc renderQuote(quote: Quote): string =
|
||||
#let hasMedia = quote.thumb.isSome() or quote.sensitive
|
||||
<div class="quote">
|
||||
<div class="quote-container">
|
||||
<a class="quote-link" href="${getLink(quote)}"></a>
|
||||
#if hasMedia:
|
||||
<div class="quote-media-container">
|
||||
<div class="quote-media">
|
||||
#if quote.thumb.isSome():
|
||||
${genImg(quote.thumb.get())}
|
||||
#if quote.badge.isSome():
|
||||
<div class="quote-badge">
|
||||
<div class="quote-badge-text">${quote.badge.get()}</div>
|
||||
</div>
|
||||
#end if
|
||||
#elif quote.sensitive:
|
||||
<div class="quote-sensitive">
|
||||
<span class="icon quote-sensitive-icon">❗</span>
|
||||
</div>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
#end if
|
||||
<div class="fullname-and-username">
|
||||
${linkUser(quote.profile, class="fullname")}
|
||||
${linkUser(quote.profile, class="username")}
|
||||
</div>
|
||||
<div class="quote-text">${linkifyText(quote.text)}</div>
|
||||
</div>
|
||||
</div>
|
||||
#end proc
|
||||
#
|
||||
#proc renderMediaGroup(tweet: Tweet): string =
|
||||
#let groups = if tweet.photos.len > 2: tweet.photos.distribute(2) else: @[tweet.photos]
|
||||
#let class = if groups.len == 1 and groups[0].len == 1: "single-image" else: ""
|
||||
|
@ -140,6 +108,53 @@
|
|||
</div>
|
||||
#end proc
|
||||
#
|
||||
#proc renderShowThread(tweet: Tweet | Quote): string =
|
||||
<a href="${getLink(tweet)}">Show this thread</a>
|
||||
#end proc
|
||||
#
|
||||
#proc renderReply(tweet: Tweet | Quote): string =
|
||||
#let usernames = tweet.reply.mapIt(&"""<a href="{it}">@{it}</a>""")
|
||||
<div class="replying-to">Replying to ${usernames.join(" ")}</div>
|
||||
#end proc
|
||||
#
|
||||
#proc renderQuote(quote: Quote): string =
|
||||
#let hasMedia = quote.thumb.len > 0 or quote.sensitive
|
||||
<div class="quote">
|
||||
<div class="quote-container">
|
||||
<a class="quote-link" href="${getLink(quote)}"></a>
|
||||
#if hasMedia:
|
||||
<div class="quote-media-container">
|
||||
<div class="quote-media">
|
||||
#if quote.thumb.len > 0:
|
||||
${genImg(quote.thumb)}
|
||||
#if quote.badge.len > 0:
|
||||
<div class="quote-badge">
|
||||
<div class="quote-badge-text">${quote.badge}</div>
|
||||
</div>
|
||||
#end if
|
||||
#elif quote.sensitive:
|
||||
<div class="quote-sensitive">
|
||||
<span class="icon quote-sensitive-icon">❗</span>
|
||||
</div>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
#end if
|
||||
<div class="fullname-and-username">
|
||||
${linkUser(quote.profile, class="fullname")}
|
||||
${linkUser(quote.profile, class="username")}
|
||||
</div>
|
||||
#if quote.reply.len > 0:
|
||||
${renderReply(quote)}
|
||||
#end if
|
||||
<div class="quote-text">${linkifyText(quote.text)}</div>
|
||||
#if quote.hasThread:
|
||||
${renderShowThread(quote)}
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
#end proc
|
||||
#
|
||||
#proc renderTweet*(tweet: Tweet; class=""; last=false): string =
|
||||
#var divClass = if last: "thread-last " & class else: class
|
||||
#if divClass.len > 0:
|
||||
|
@ -149,6 +164,9 @@
|
|||
<div class="status-el">
|
||||
<div class="status-body">
|
||||
${renderHeading(tweet)}
|
||||
#if tweet.reply.len > 0:
|
||||
${renderReply(tweet)}
|
||||
#end if
|
||||
<div class="status-content-wrapper">
|
||||
<div class="status-content media-body">${linkifyText(tweet.text)}</div>
|
||||
</div>
|
||||
|
@ -161,9 +179,12 @@
|
|||
#elif tweet.quote.isSome:
|
||||
${renderQuote(tweet.quote.get())}
|
||||
#elif tweet.poll.isSome:
|
||||
${renderPoll(tweet.poll.get())}
|
||||
${renderPoll(tweet.poll.get())}
|
||||
#end if
|
||||
${renderStats(tweet.stats)}
|
||||
#if tweet.hasThread and "timeline" in class:
|
||||
${renderShowThread(tweet)}
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
#else:
|
||||
|
|
Loading…
Reference in a new issue