2019-06-20 16:16:20 +02:00
|
|
|
#? stdtmpl(subsChar = '$', metaChar = '#')
|
|
|
|
#import xmltree, strutils, times, sequtils, uri
|
|
|
|
#import ../types, ../formatters, ../utils
|
|
|
|
#
|
|
|
|
#proc renderHeading(tweet: Tweet): string =
|
2019-06-20 20:04:18 +02:00
|
|
|
#if tweet.retweetBy.isSome:
|
2019-06-20 16:16:20 +02:00
|
|
|
<div class="retweet">
|
2019-06-20 20:04:18 +02:00
|
|
|
<span>🔄 ${tweet.retweetBy.get()} retweeted</span>
|
2019-06-20 16:16:20 +02:00
|
|
|
</div>
|
|
|
|
#end if
|
|
|
|
#if tweet.pinned:
|
|
|
|
<div class="pinned">
|
|
|
|
<span>📌 Pinned Tweet</span>
|
|
|
|
</div>
|
|
|
|
#end if
|
|
|
|
<div class="media-heading">
|
|
|
|
<div class="heading-name-row">
|
2019-06-26 21:55:04 +02:00
|
|
|
<a class="tweet-avatar" href="/${tweet.profile.username}">
|
|
|
|
${genImg(tweet.profile.getUserpic("_bigger"), "avatar")}
|
|
|
|
</a>
|
2019-06-24 20:45:03 +02:00
|
|
|
<div class="fullname-and-username">
|
|
|
|
${linkUser(tweet.profile, class="fullname")}
|
|
|
|
${linkUser(tweet.profile, class="username")}
|
2019-06-20 16:16:20 +02:00
|
|
|
</div>
|
|
|
|
<span class="heading-right">
|
2019-06-25 04:52:38 +02:00
|
|
|
<a href="${tweet.link}" title="${tweet.getTime()}">${tweet.shortTime}</a>
|
2019-06-20 16:16:20 +02:00
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-24 08:07:36 +02:00
|
|
|
#proc renderQuote(quote: Quote): string =
|
2019-06-25 02:58:33 +02:00
|
|
|
#let hasMedia = quote.thumb.isSome() or quote.sensitive
|
2019-06-24 08:07:36 +02:00
|
|
|
<div class="quote">
|
2019-06-25 04:52:38 +02:00
|
|
|
<div class="quote-container">
|
2019-06-24 08:07:36 +02:00
|
|
|
<a class="quote-link" href="${quote.link}"></a>
|
|
|
|
#if hasMedia:
|
|
|
|
<div class="quote-media-container">
|
|
|
|
<div class="quote-media">
|
2019-06-25 02:58:33 +02:00
|
|
|
#if quote.thumb.isSome():
|
2019-06-25 03:48:57 +02:00
|
|
|
${genImg(quote.thumb.get())}
|
2019-06-25 02:58:33 +02:00
|
|
|
#if quote.badge.isSome():
|
2019-06-24 18:19:43 +02:00
|
|
|
<div class="quote-badge">
|
|
|
|
<div class="quote-badge-text">${quote.badge.get()}</div>
|
|
|
|
</div>
|
2019-06-24 08:07:36 +02:00
|
|
|
#end if
|
2019-06-25 02:58:33 +02:00
|
|
|
#elif quote.sensitive:
|
|
|
|
<div class="quote-sensitive">
|
|
|
|
<span class="icon quote-sensitive-icon">❗</span>
|
|
|
|
</div>
|
|
|
|
#end if
|
2019-06-24 08:07:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
#end if
|
2019-06-24 20:45:03 +02:00
|
|
|
<div class="fullname-and-username">
|
|
|
|
${linkUser(quote.profile, class="fullname")}
|
|
|
|
${linkUser(quote.profile, class="username")}
|
2019-06-24 08:07:36 +02:00
|
|
|
</div>
|
2019-06-25 04:52:38 +02:00
|
|
|
<div class="quote-text">${linkifyText(quote.text)}</div>
|
2019-06-24 08:07:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-20 16:16:20 +02:00
|
|
|
#proc renderMediaGroup(tweet: Tweet): string =
|
|
|
|
#let groups = if tweet.photos.len > 2: tweet.photos.distribute(2) else: @[tweet.photos]
|
2019-07-01 03:12:28 +02:00
|
|
|
#let display = if groups.len == 1 and groups[0].len == 1: "display: table-cell;" else: ""
|
2019-06-20 16:16:20 +02:00
|
|
|
#var first = true
|
2019-06-25 04:52:38 +02:00
|
|
|
<div class="attachments" style="${display}">
|
2019-06-20 16:16:20 +02:00
|
|
|
#for photos in groups:
|
2019-06-23 22:06:48 +02:00
|
|
|
#let margin = if not first: "margin-top: .25em;" else: ""
|
|
|
|
#let flex = if photos.len > 1 or groups.len > 1: "display: flex;" else: ""
|
|
|
|
<div class="gallery-row cover-fit" style="${margin}">
|
2019-06-20 16:16:20 +02:00
|
|
|
#for photo in photos:
|
|
|
|
<div class="attachment image">
|
|
|
|
##TODO: why doesn't this work?
|
2019-06-23 02:52:07 +02:00
|
|
|
<a href=${getSigUrl(photo & "?name=orig", "pic")} target="_blank" class="image-attachment">
|
2019-06-23 22:06:48 +02:00
|
|
|
<div class="still-image" style="${flex}">
|
2019-06-25 03:48:57 +02:00
|
|
|
${genImg(photo)}
|
2019-06-20 16:16:20 +02:00
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
#end for
|
|
|
|
</div>
|
|
|
|
#first = false
|
|
|
|
#end for
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-24 05:14:14 +02:00
|
|
|
#proc renderVideo(video: Video): string =
|
2019-06-25 04:52:38 +02:00
|
|
|
<div class="attachments">
|
2019-06-29 07:45:36 +02:00
|
|
|
<div class="gallery-video">
|
|
|
|
<div class="attachment video-container">
|
|
|
|
#case video.playbackType
|
|
|
|
#of mp4:
|
|
|
|
<video poster=${video.thumb.getSigUrl("pic")} controls>
|
|
|
|
<source src=${video.url.getSigUrl("video")} type="video/mp4">
|
|
|
|
</video>
|
|
|
|
#of m3u8, vmap:
|
2019-06-24 05:14:14 +02:00
|
|
|
<video poster=${video.thumb.getSigUrl("pic")} autoplay muted loop></video>
|
2019-06-20 17:20:32 +02:00
|
|
|
<div class="video-overlay">
|
|
|
|
<p>Video playback not supported</p>
|
|
|
|
</div>
|
2019-06-29 07:45:36 +02:00
|
|
|
#end case
|
2019-06-20 17:20:32 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-24 05:14:14 +02:00
|
|
|
#proc renderGif(gif: Gif): string =
|
2019-06-25 04:52:38 +02:00
|
|
|
<div class="attachments media-gif">
|
2019-06-29 14:33:21 +02:00
|
|
|
<div class="gallery-gif" style="max-height: unset;">
|
|
|
|
<div class="attachment">
|
2019-06-24 05:14:14 +02:00
|
|
|
<video class="gif" poster=${gif.thumb.getSigUrl("pic")} autoplay muted loop>
|
|
|
|
<source src=${gif.url.getSigUrl("video")} type="video/mp4">
|
2019-06-20 16:16:20 +02:00
|
|
|
</video>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-29 14:11:23 +02:00
|
|
|
#proc renderPoll(poll: Poll): string =
|
|
|
|
<div class="poll">
|
|
|
|
#for i in 0 ..< poll.options.len:
|
|
|
|
#let leader = if poll.leader == i: " leader" else: ""
|
|
|
|
<div class="poll-meter${leader}">
|
|
|
|
<span class="poll-choice-bar" style="width: ${poll.values[i]}%"></span>
|
|
|
|
<span class="poll-choice-value">${poll.values[i]}%</span>
|
|
|
|
<span class="poll-choice-option">${poll.options[i]}</span>
|
|
|
|
</div>
|
|
|
|
#end for
|
|
|
|
<span class="poll-info">${poll.votes} votes • ${poll.status}</span>
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-20 16:16:20 +02:00
|
|
|
#proc renderStats(tweet: Tweet): string =
|
|
|
|
<div class="tweet-stats">
|
|
|
|
<span class="tweet-stat">💬 ${$tweet.replies}</span>
|
|
|
|
<span class="tweet-stat">🔄 ${$tweet.retweets}</span>
|
|
|
|
<span class="tweet-stat">👍 ${$tweet.likes}</span>
|
|
|
|
</div>
|
|
|
|
#end proc
|
|
|
|
#
|
2019-06-25 13:07:12 +02:00
|
|
|
#proc renderTweet*(tweet: Tweet; class=""; last=false): string =
|
|
|
|
#var divClass = if last: "thread-last " & class else: class
|
|
|
|
#if divClass.len > 0:
|
|
|
|
<div class="${divClass}">
|
2019-06-21 04:35:59 +02:00
|
|
|
#end if
|
2019-06-26 21:06:42 +02:00
|
|
|
#if tweet.available:
|
2019-06-21 04:35:48 +02:00
|
|
|
<div class="status-el">
|
|
|
|
<div class="status-body">
|
|
|
|
${renderHeading(tweet)}
|
|
|
|
<div class="status-content-wrapper">
|
2019-06-25 04:52:38 +02:00
|
|
|
<div class="status-content media-body">${linkifyText(tweet.text)}</div>
|
2019-06-20 16:16:20 +02:00
|
|
|
</div>
|
2019-06-21 04:35:48 +02:00
|
|
|
#if tweet.photos.len > 0:
|
|
|
|
${renderMediaGroup(tweet)}
|
2019-06-24 05:14:14 +02:00
|
|
|
#elif tweet.video.isSome:
|
|
|
|
${renderVideo(tweet.video.get())}
|
2019-06-21 04:35:48 +02:00
|
|
|
#elif tweet.gif.isSome:
|
2019-06-24 05:14:14 +02:00
|
|
|
${renderGif(tweet.gif.get())}
|
2019-06-24 08:07:36 +02:00
|
|
|
#elif tweet.quote.isSome:
|
|
|
|
${renderQuote(tweet.quote.get())}
|
2019-06-29 14:11:23 +02:00
|
|
|
#elif tweet.poll.isSome:
|
|
|
|
${renderPoll(tweet.poll.get())}
|
2019-06-21 04:35:48 +02:00
|
|
|
#end if
|
|
|
|
${renderStats(tweet)}
|
2019-06-20 16:16:20 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2019-06-26 21:06:42 +02:00
|
|
|
#else:
|
|
|
|
<div class="status-el unavailable">
|
|
|
|
<div class="unavailable-box">This tweet is unavailable</div>
|
|
|
|
</div>
|
|
|
|
#end if
|
2019-06-25 13:07:12 +02:00
|
|
|
#if divClass.len > 0:
|
2019-06-21 04:35:59 +02:00
|
|
|
</div>
|
|
|
|
#end if
|
2019-06-20 16:16:20 +02:00
|
|
|
#end proc
|