html and css updates

This commit is contained in:
Zed 2020-06-01 02:22:22 +02:00
parent 762d00b21d
commit 2950c0de35
14 changed files with 246 additions and 224 deletions

View File

@ -42,7 +42,8 @@
img { img {
display: block; display: block;
width: calc(100% - 8px); box-sizing: border-box;
width: 100%;
height: 100%; height: 100%;
margin: 0; margin: 0;
border: 4px solid var(--darker_grey); border: 4px solid var(--darker_grey);

View File

@ -14,9 +14,10 @@
} }
&-header-mobile { &-header-mobile {
padding: 5px 12px 0;
display: none; display: none;
width: calc(100% - 24px); box-sizing: border-box;
padding: 5px 12px 0;
width: 100%;
float: unset; float: unset;
color: var(--accent); color: var(--accent);
justify-content: space-between; justify-content: space-between;

View File

@ -13,18 +13,28 @@
} }
.timeline-header { .timeline-header {
width: 100%;
background-color: var(--bg_panel); background-color: var(--bg_panel);
text-align: center; text-align: center;
padding: 8px; padding: 8px;
display: block; display: block;
font-weight: bold; font-weight: bold;
margin-bottom: 5px; margin-bottom: 5px;
box-sizing: border-box;
button { button {
float: unset; float: unset;
} }
} }
.timeline-banner img {
width: 100%;
}
.timeline-description {
font-weight: normal;
}
.tab { .tab {
align-items: center; align-items: center;
display: flex; display: flex;

View File

@ -76,7 +76,7 @@
left: 0; left: 0;
bottom: 0; bottom: 0;
right: 0; right: 0;
background-color: var(--fg_color); background-color: var(--bg_overlays);
img { img {
width: 100%; width: 100%;

View File

@ -13,7 +13,7 @@
.still-image { .still-image {
width: 100%; width: 100%;
display: block; display: flex;
} }
} }
@ -67,17 +67,17 @@
display: inline-block; display: inline-block;
} }
.single-image { // .single-image {
display: inline-block; // display: inline-block;
width: 100%; // width: 100%;
max-height: 600px; // max-height: 600px;
.attachments { // .attachments {
width: unset; // width: unset;
max-height: unset; // max-height: unset;
display: inherit; // display: inherit;
} // }
} // }
.overlay-circle { .overlay-circle {
border-radius: 50%; border-radius: 50%;

View File

@ -5,10 +5,10 @@
border: solid 1px var(--dark_grey); border: solid 1px var(--dark_grey);
border-radius: 10px; border-radius: 10px;
background-color: var(--bg_elements); background-color: var(--bg_elements);
overflow: auto; overflow: hidden;
padding: 6px;
position: relative;
pointer-events: all; pointer-events: all;
position: relative;
width: 100%;
&:hover { &:hover {
border-color: var(--grey); border-color: var(--grey);
@ -17,91 +17,73 @@
&.unavailable:hover { &.unavailable:hover {
border-color: var(--dark_grey); border-color: var(--dark_grey);
} }
.tweet-name-row {
padding: 6px 6px 0px 6px;
}
.quote-text {
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
padding: 3px 6px 6px 6px;
}
.show-thread {
padding: 0px 6px 3px 6px;
margin-top: -3px;
}
.replying-to {
padding: 0px 6px;
margin: unset;
}
} }
.unavailable-quote { .unavailable-quote {
padding: 6px; padding: 12px;
} }
.quote-link { .quote-link {
height: 100%;
width: 100%; width: 100%;
height: 100%;
left: 0; left: 0;
top: 0; top: 0;
position: absolute; position: absolute;
} }
.quote .quote-link {
z-index: 1;
}
.quote-text {
overflow: hidden;
white-space: pre-wrap;
word-wrap: break-word;
}
.quote-media-container { .quote-media-container {
max-height: 300px;
display: flex; display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
max-height: 102px;
width: 102px;
float: left;
margin-right: 7px;
border-radius: 7px;
position: relative;
}
.quote-media { .card {
display: flex; margin: unset;
justify-content: center; }
pointer-events: none;
img { .media-gif {
width: 100%; width: 100%;
height: 100%; }
align-self: center;
} .attachments {
} border-radius: 0;
}
.quote-badge {
left: 0; .gallery-gif .attachment {
bottom: 0; display: flex;
position: absolute; justify-content: center;
z-index: 1; background-color: var(--bg_color);
align-self: flex-end;
} video {
height: unset;
.quote-badge-text { width: unset;
margin: 4px; }
background: $shadow; }
border-radius: 4px;
color: #fffffff0; .gallery-video, .gallery-gif {
padding: 1px 3px; max-height: 300px;
font-size: 12px; }
font-weight: bold;
} .still-image img {
max-height: 250px
.quote-sensitive {
background: var(--darker_grey);
width: 102px;
height: 102px;
border-radius: 12px;
display: flex;
justify-content: center;
align-items: center;
}
.quote-sensitive-icon {
font-size: 40px;
color: var(--grey);
}
@media(max-width: 600px) {
.quote-media-container {
width: 70px;
max-height: 70px;
} }
} }

View File

@ -2,7 +2,7 @@ import strformat
import karax/[karaxdsl, vdom] import karax/[karaxdsl, vdom]
import renderutils import renderutils
import ".."/[types] import ".."/[types, utils]
proc renderListTabs*(query: Query; path: string): VNode = proc renderListTabs*(query: Query; path: string): VNode =
buildHtml(ul(class="tab")): buildHtml(ul(class="tab")):
@ -11,10 +11,18 @@ proc renderListTabs*(query: Query; path: string): VNode =
li(class=query.getTabClass(userList)): li(class=query.getTabClass(userList)):
a(href=(path & "/members")): text "Members" a(href=(path & "/members")): text "Members"
proc renderList*(body: VNode; query: Query; name, list: string): VNode = proc renderList*(body: VNode; query: Query; list: List): VNode =
buildHtml(tdiv(class="timeline-container")): buildHtml(tdiv(class="timeline-container")):
tdiv(class="timeline-header"): if list.banner.len > 0:
text &"\"{list}\" by @{name}" tdiv(class="timeline-banner"):
a(href=getPicUrl(list.banner), target="_blank"):
genImg(list.banner)
renderListTabs(query, &"/{name}/lists/{list}") tdiv(class="timeline-header"):
text &"\"{list.name}\" by @{list.username}"
tdiv(class="timeline-description"):
text list.description
renderListTabs(query, &"/{list.username}/lists/{list.name}")
body body

View File

@ -58,14 +58,15 @@ proc renderProfileCard*(profile: Profile; prefs: Prefs): VNode =
renderStat(profile.likes, "likes") renderStat(profile.likes, "likes")
proc renderPhotoRail(profile: Profile; photoRail: PhotoRail): VNode = proc renderPhotoRail(profile: Profile; photoRail: PhotoRail): VNode =
let count = insertSep($profile.media, ',')
buildHtml(tdiv(class="photo-rail-card")): buildHtml(tdiv(class="photo-rail-card")):
tdiv(class="photo-rail-header"): tdiv(class="photo-rail-header"):
a(href=(&"/{profile.username}/media")): a(href=(&"/{profile.username}/media")):
icon "picture", $profile.media & " Photos and videos" icon "picture", count & " Photos and videos"
input(id="photo-rail-grid-toggle", `type`="checkbox") input(id="photo-rail-grid-toggle", `type`="checkbox")
label(`for`="photo-rail-grid-toggle", class="photo-rail-header-mobile"): label(`for`="photo-rail-grid-toggle", class="photo-rail-header-mobile"):
icon "picture", $profile.media & " Photos and videos" icon "picture", count & " Photos and videos"
icon "down" icon "down"
tdiv(class="photo-rail-grid"): tdiv(class="photo-rail-grid"):
@ -73,7 +74,7 @@ proc renderPhotoRail(profile: Profile; photoRail: PhotoRail): VNode =
if i == 16: break if i == 16: break
a(href=(&"/{profile.username}/status/{photo.tweetId}#m"), a(href=(&"/{profile.username}/status/{photo.tweetId}#m"),
style={backgroundColor: photo.color}): style={backgroundColor: photo.color}):
genImg(photo.url & ":thumb") genImg(photo.url & (if "format" in photo.url: "" else: ":thumb"))
proc renderBanner(profile: Profile): VNode = proc renderBanner(profile: Profile): VNode =
buildHtml(): buildHtml():
@ -89,7 +90,7 @@ proc renderProtected(username: string): VNode =
h2: text "This account's tweets are protected." h2: text "This account's tweets are protected."
p: text &"Only confirmed followers have access to @{username}'s tweets." p: text &"Only confirmed followers have access to @{username}'s tweets."
proc renderProfile*(profile: Profile; timeline: Timeline; proc renderProfile*(profile: Profile; timeline: var Timeline;
photoRail: PhotoRail; prefs: Prefs; path: string): VNode = photoRail: PhotoRail; prefs: Prefs; path: string): VNode =
timeline.query.fromUser = @[profile.username] timeline.query.fromUser = @[profile.username]
buildHtml(tdiv(class="profile-tabs")): buildHtml(tdiv(class="profile-tabs")):

View File

@ -1,7 +1,6 @@
import strutils, xmltree import strutils
import karax/[karaxdsl, vdom, vstyles] import karax/[karaxdsl, vdom, vstyles]
import ".."/[types, utils]
import ../types, ../utils
proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode = proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode =
var c = "icon-" & icon var c = "icon-" & icon

View File

@ -82,24 +82,24 @@
<width>128</width> <width>128</width>
<height>128</height> <height>128</height>
</image> </image>
#if timeline != nil: # if timeline.content.len > 0:
${renderRssTweets(timeline.content, prefs, hostname)} ${renderRssTweets(timeline.content, prefs, hostname)}
#end if #end if
</channel> </channel>
</rss> </rss>
#end proc #end proc
# #
#proc renderListRss*(tweets: seq[Tweet]; name, list, hostname: string): string = #proc renderListRss*(tweets: seq[Tweet]; list: List; hostname: string): string =
#let prefs = Prefs(replaceTwitter: hostname, replaceYouTube: "invidio.us") #let prefs = Prefs(replaceTwitter: hostname, replaceYouTube: "invidio.us")
#let link = &"https://{hostname}/{name}/lists/{list}" #let link = &"https://{hostname}/{list.username}/lists/{list.name}"
#result = "" #result = ""
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> <rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel> <channel>
<atom:link href="${link}" rel="self" type="application/rss+xml" /> <atom:link href="${link}" rel="self" type="application/rss+xml" />
<title>${list} / @${name}</title> <title>${list.name} / @${list.username}</title>
<link>${link}</link> <link>${link}</link>
<description>Twitter feed for: ${list} by @${name}. Generated by ${hostname}</description> <description>Twitter feed for: ${list.name} by @${list.username}. Generated by ${hostname}</description>
<language>en-us</language> <language>en-us</language>
<ttl>40</ttl> <ttl>40</ttl>
${renderRssTweets(tweets, prefs, hostname)} ${renderRssTweets(tweets, prefs, hostname)}

View File

@ -1,8 +1,8 @@
import strutils, strformat, sequtils, unicode, tables import strutils, strformat, sequtils, unicode, tables
import karax/[karaxdsl, vdom, vstyles] import karax/[karaxdsl, vdom]
import renderutils, timeline import renderutils, timeline
import ".."/[types, formatters, query] import ".."/[types, query]
let toggles = { let toggles = {
"nativeretweets": "Retweets", "nativeretweets": "Retweets",
@ -93,13 +93,15 @@ proc renderTweetSearch*(results: Result[Tweet]; prefs: Prefs; path: string): VNo
if query.fromUser.len > 1: if query.fromUser.len > 1:
tdiv(class="timeline-header"): tdiv(class="timeline-header"):
text query.fromUser.join(" | ") text query.fromUser.join(" | ")
if query.fromUser.len > 0:
renderProfileTabs(query, query.fromUser.join(","))
if query.fromUser.len == 0 or query.kind == tweets: if query.fromUser.len == 0 or query.kind == tweets:
tdiv(class="timeline-header"): tdiv(class="timeline-header"):
renderSearchPanel(query) renderSearchPanel(query)
if query.fromUser.len > 0: if query.fromUser.len == 0:
renderProfileTabs(query, query.fromUser.join(","))
else:
renderSearchTabs(query) renderSearchTabs(query)
renderTimelineTweets(results, prefs, path) renderTimelineTweets(results, prefs, path)

View File

@ -1,3 +1,4 @@
import uri
import karax/[karaxdsl, vdom] import karax/[karaxdsl, vdom]
import ".."/[types, formatters] import ".."/[types, formatters]
@ -13,7 +14,7 @@ proc renderMoreReplies(thread: Chain): VNode =
let reply = if thread.more == 1: "reply" else: "replies" let reply = if thread.more == 1: "reply" else: "replies"
let link = getLink(thread.content[^1]) let link = getLink(thread.content[^1])
buildHtml(tdiv(class="timeline-item more-replies")): buildHtml(tdiv(class="timeline-item more-replies")):
if link.len > 0: if thread.content[^1].available:
a(class="more-replies-text", href=link): a(class="more-replies-text", href=link):
text $num & "more " & reply text $num & "more " & reply
else: else:
@ -32,41 +33,45 @@ proc renderReplyThread(thread: Chain; prefs: Prefs; path: string): VNode =
proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string): VNode = proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="replies", id="r")): buildHtml(tdiv(class="replies", id="r")):
for thread in replies.content: for thread in replies.content:
if thread == nil: continue if thread.content.len == 0: continue
renderReplyThread(thread, prefs, path) renderReplyThread(thread, prefs, path)
if replies.hasMore: if replies.bottom.len > 0:
renderMore(Query(), replies.minId, focus="#r") renderMore(Query(), encodeUrl(replies.bottom), focus="#r")
proc renderConversation*(conversation: Conversation; prefs: Prefs; path: string): VNode = proc renderConversation*(conv: Conversation; prefs: Prefs; path: string): VNode =
let hasAfter = conversation.after != nil let hasAfter = conv.after.content.len > 0
let showReplies = not prefs.hideReplies let threadId = conv.tweet.threadId
buildHtml(tdiv(class="conversation")): buildHtml(tdiv(class="conversation")):
tdiv(class="main-thread"): tdiv(class="main-thread"):
if conversation.before != nil: if conv.before.content.len > 0:
tdiv(class="before-tweet thread-line"): tdiv(class="before-tweet thread-line"):
if conversation.before.more == -1: let first = conv.before.content[0]
renderEarlier(conversation.before) if threadId != first.id and (first.replyId > 0 or not first.available):
for i, tweet in conversation.before.content: renderEarlier(conv.before)
for i, tweet in conv.before.content:
renderTweet(tweet, prefs, path, index=i) renderTweet(tweet, prefs, path, index=i)
tdiv(class="main-tweet", id="m"): tdiv(class="main-tweet", id="m"):
let afterClass = if hasAfter: "thread thread-line" else: "" let afterClass = if hasAfter: "thread thread-line" else: ""
renderTweet(conversation.tweet, prefs, path, class=afterClass, renderTweet(conv.tweet, prefs, path, class=afterClass, mainTweet=true)
mainTweet=true)
if hasAfter: if hasAfter:
tdiv(class="after-tweet thread-line"): tdiv(class="after-tweet thread-line"):
let total = conversation.after.content.high let
let more = conversation.after.more total = conv.after.content.high
for i, tweet in conversation.after.content: more = conv.after.more
renderTweet(tweet, prefs, path, index=i, last=(i == total and more == 0)) for i, tweet in conv.after.content:
renderTweet(tweet, prefs, path, index=i,
last=(i == total and more == 0), afterTweet=true)
if more != 0: if more != 0:
renderMoreReplies(conversation.after) renderMoreReplies(conv.after)
if conversation.replies != nil and showReplies: if not prefs.hideReplies:
if not conversation.replies.beginning: if not conv.replies.beginning:
renderNewer(Query(), getLink(conversation.tweet)) renderNewer(Query(), getLink(conv.tweet), focus="#r")
if conversation.replies.content.len > 0: if conv.replies.content.len > 0 or conv.replies.bottom.len > 0:
renderReplies(conversation.replies, prefs, path) renderReplies(conv.replies, prefs, path)
renderToTop(focus="#m")

View File

@ -10,16 +10,22 @@ proc getQuery(query: Query): string =
if result.len > 0: if result.len > 0:
result &= "&" result &= "&"
proc renderNewer*(query: Query; path: string): VNode = proc renderToTop*(focus="#"): VNode =
let q = genQueryUrl(query) buildHtml(tdiv(class="top-ref")):
let url = if q.len > 0: "?" & q else: "" icon "down", href=focus
proc renderNewer*(query: Query; path: string; focus=""): VNode =
let
q = genQueryUrl(query)
url = if q.len > 0: "?" & q else: ""
p = if focus.len > 0: path.replace("#m", focus) else: path
buildHtml(tdiv(class="timeline-item show-more")): buildHtml(tdiv(class="timeline-item show-more")):
a(href=(path & url)): a(href=(p & url)):
text "Load newest" text "Load newest"
proc renderMore*(query: Query; minId: string; focus=""): VNode = proc renderMore*(query: Query; cursor: string; focus=""): VNode =
buildHtml(tdiv(class="show-more")): buildHtml(tdiv(class="show-more")):
a(href=(&"?{getQuery(query)}max_position={minId}{focus}")): a(href=(&"?{getQuery(query)}cursor={encodeUrl(cursor)}{focus}")):
text "Load more" text "Load more"
proc renderNoMore(): VNode = proc renderNoMore(): VNode =
@ -32,10 +38,6 @@ proc renderNoneFound(): VNode =
h2(class="timeline-none"): h2(class="timeline-none"):
text "No items found" text "No items found"
proc renderToTop(): VNode =
buildHtml(tdiv(class="top-ref")):
icon "down", href="#"
proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode = proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="thread-line")): buildHtml(tdiv(class="thread-line")):
let sortedThread = thread.sortedByIt(it.id) let sortedThread = thread.sortedByIt(it.id)
@ -43,10 +45,16 @@ proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode =
let show = i == thread.high and sortedThread[0].id != tweet.threadId let show = i == thread.high and sortedThread[0].id != tweet.threadId
let header = if tweet.pinned or tweet.retweet.isSome: "with-header " else: "" let header = if tweet.pinned or tweet.retweet.isSome: "with-header " else: ""
renderTweet(tweet, prefs, path, class=(header & "thread"), renderTweet(tweet, prefs, path, class=(header & "thread"),
index=i, total=thread.high, showThread=show) index=i, last=(i == thread.high), showThread=show)
proc threadFilter(it: Tweet; thread: int64): bool = proc threadFilter(tweets: openArray[Tweet]; threads: openArray[int64]; it: Tweet): seq[Tweet] =
it.retweet.isNone and it.reply.len == 0 and it.threadId == thread result = @[it]
if it.retweet.isSome or it.replyId in threads: return
for t in tweets:
if t.id == result[0].replyId:
result.insert t
elif t.replyId == result[0].id:
result.add t
proc renderUser(user: Profile; prefs: Prefs): VNode = proc renderUser(user: Profile; prefs: Prefs): VNode =
buildHtml(tdiv(class="timeline-item")): buildHtml(tdiv(class="timeline-item")):
@ -72,8 +80,8 @@ proc renderTimelineUsers*(results: Result[Profile]; prefs: Prefs; path=""): VNod
if results.content.len > 0: if results.content.len > 0:
for user in results.content: for user in results.content:
renderUser(user, prefs) renderUser(user, prefs)
if results.minId != "0": if results.bottom.len > 0:
renderMore(results.query, results.minId) renderMore(results.query, results.bottom)
renderToTop() renderToTop()
elif results.beginning: elif results.beginning:
renderNoneFound() renderNoneFound()
@ -86,24 +94,31 @@ proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string):
renderNewer(results.query, parseUri(path).path) renderNewer(results.query, parseUri(path).path)
if results.content.len == 0: if results.content.len == 0:
renderNoneFound() if not results.beginning:
renderNoMore()
else:
renderNoneFound()
else: else:
var threads: seq[int64] var
var retweets: seq[int64] threads: seq[int64]
retweets: seq[int64]
for tweet in results.content: for tweet in results.content:
if tweet.threadId in threads or tweet.id in retweets: continue let rt = if tweet.retweet.isSome: get(tweet.retweet).id else: 0
if tweet.pinned and prefs.hidePins: continue
let thread = results.content.filterIt(threadFilter(it, tweet.threadId)) if tweet.id in threads or rt in retweets or
tweet.pinned and prefs.hidePins: continue
let thread = results.content.threadFilter(threads, tweet)
if thread.len < 2: if thread.len < 2:
if tweet.retweet.isSome: var hasThread = tweet.hasThread
retweets &= tweet.id if rt != 0:
renderTweet(tweet, prefs, path, showThread=tweet.hasThread) retweets &= rt
hasThread = get(tweet.retweet).hasThread
renderTweet(tweet, prefs, path, showThread=hasThread)
else: else:
renderThread(thread, prefs, path) renderThread(thread, prefs, path)
threads &= tweet.threadId threads &= thread.mapIt(it.id)
if results.hasMore or results.query.kind != posts: renderMore(results.query, results.bottom)
renderMore(results.query, results.minId)
else:
renderNoMore()
renderToTop() renderToTop()

View File

@ -4,11 +4,11 @@ import karax/[karaxdsl, vdom, vstyles]
import renderutils import renderutils
import ".."/[types, utils, formatters] import ".."/[types, utils, formatters]
proc renderHeader(tweet: Tweet): VNode = proc renderHeader(tweet: Tweet; retweet=""): VNode =
buildHtml(tdiv): buildHtml(tdiv):
if tweet.retweet.isSome: if retweet.len > 0:
tdiv(class="retweet-header"): tdiv(class="retweet-header"):
span: icon "retweet", get(tweet.retweet).by & " retweeted" span: icon "retweet", retweet & " retweeted"
if tweet.pinned: if tweet.pinned:
tdiv(class="pinned"): tdiv(class="pinned"):
@ -24,31 +24,24 @@ proc renderHeader(tweet: Tweet): VNode =
linkUser(tweet.profile, class="username") linkUser(tweet.profile, class="username")
span(class="tweet-date"): span(class="tweet-date"):
a(href=getLink(tweet), title=tweet.getTime()): a(href=getLink(tweet), title=tweet.getTime):
text tweet.shortTime text tweet.getShortTime
proc renderAlbum(tweet: Tweet): VNode = proc renderAlbum(tweet: Tweet): VNode =
let let
groups = if tweet.photos.len < 3: @[tweet.photos] groups = if tweet.photos.len < 3: @[tweet.photos]
else: tweet.photos.distribute(2) else: tweet.photos.distribute(2)
if groups.len == 1 and groups[0].len == 1: buildHtml(tdiv(class="attachments")):
buildHtml(tdiv(class="single-image")): for i, photos in groups:
tdiv(class="attachments gallery-row"): let margin = if i > 0: ".25em" else: ""
a(href=getPicUrl(groups[0][0] & "?name=orig"), class="still-image", tdiv(class="gallery-row", style={marginTop: margin}):
target="_blank"): for photo in photos:
genImg(groups[0][0]) tdiv(class="attachment image"):
else: var url = photo
buildHtml(tdiv(class="attachments")): if "=orig" notin url: url &= "?name=orig"
for i, photos in groups: a(href=getPicUrl(url), class="still-image", target="_blank"):
let margin = if i > 0: ".25em" else: "" genImg(photo)
let flex = if photos.len > 1 or groups.len > 1: "flex" else: "block"
tdiv(class="gallery-row", style={marginTop: margin}):
for photo in photos:
tdiv(class="attachment image"):
a(href=getPicUrl(photo & "?name=orig"), class="still-image",
target="_blank", style={display: flex}):
genImg(photo)
proc isPlaybackEnabled(prefs: Prefs; video: Video): bool = proc isPlaybackEnabled(prefs: Prefs; video: Video): bool =
case video.playbackType case video.playbackType
@ -88,7 +81,8 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode =
elif not prefs.isPlaybackEnabled(video): elif not prefs.isPlaybackEnabled(video):
renderVideoDisabled(video, path) renderVideoDisabled(video, path)
else: else:
let source = getVidUrl(video.url) let vid = video.variants.filterIt(it.videoType == video.playbackType)
let source = getVidUrl(vid[0].url)
case video.playbackType case video.playbackType
of mp4: of mp4:
if prefs.muteVideos: if prefs.muteVideos:
@ -138,7 +132,7 @@ proc renderPoll(poll: Poll): VNode =
proc renderCardImage(card: Card): VNode = proc renderCardImage(card: Card): VNode =
buildHtml(tdiv(class="card-image-container")): buildHtml(tdiv(class="card-image-container")):
tdiv(class="card-image"): tdiv(class="card-image"):
img(src=getPicUrl(get(card.image))) img(src=getPicUrl(card.image), alt="")
if card.kind == player: if card.kind == player:
tdiv(class="card-overlay"): tdiv(class="card-overlay"):
tdiv(class="overlay-circle"): tdiv(class="overlay-circle"):
@ -151,9 +145,8 @@ proc renderCardContent(card: Card): VNode =
span(class="card-destination"): text card.dest span(class="card-destination"): text card.dest
proc renderCard(card: Card; prefs: Prefs; path: string): VNode = proc renderCard(card: Card; prefs: Prefs; path: string): VNode =
const largeCards = {summaryLarge, liveEvent, promoWebsite, const smallCards = {player, summary}
promoVideo, promoVideoConvo} let large = if card.kind notin smallCards: " large" else: ""
let large = if card.kind in largeCards: " large" else: ""
let url = replaceUrl(card.url, prefs) let url = replaceUrl(card.url, prefs)
buildHtml(tdiv(class=("card" & large))): buildHtml(tdiv(class=("card" & large))):
@ -164,7 +157,7 @@ proc renderCard(card: Card; prefs: Prefs; path: string): VNode =
renderCardContent(card) renderCardContent(card)
else: else:
a(class="card-container", href=url): a(class="card-container", href=url):
if card.image.isSome: if card.image.len > 0:
renderCardImage(card) renderCardImage(card)
tdiv(class="card-content-container"): tdiv(class="card-content-container"):
renderCardContent(card) renderCardContent(card)
@ -184,13 +177,6 @@ proc renderReply(tweet: Tweet): VNode =
if i > 0: text " " if i > 0: text " "
a(href=("/" & u)): text "@" & u a(href=("/" & u)): text "@" & u
proc renderReply(quote: Quote): VNode =
buildHtml(tdiv(class="replying-to")):
text "Replying to "
for i, u in quote.reply:
if i > 0: text " "
a(href=("/" & u)): text "@" & u
proc renderAttribution(profile: Profile): VNode = proc renderAttribution(profile: Profile): VNode =
let avatarUrl = getPicUrl(profile.getUserpic("_200x200")) let avatarUrl = getPicUrl(profile.getUserpic("_200x200"))
buildHtml(a(class="attribution", href=("/" & profile.username))): buildHtml(a(class="attribution", href=("/" & profile.username))):
@ -206,19 +192,17 @@ proc renderMediaTags(tags: seq[Profile]): VNode =
if i < tags.high: if i < tags.high:
text ", " text ", "
proc renderQuoteMedia(quote: Quote): VNode = proc renderQuoteMedia(quote: Tweet; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="quote-media-container")): buildHtml(tdiv(class="quote-media-container")):
if quote.thumb.len > 0: if quote.photos.len > 0:
tdiv(class="quote-media"): renderAlbum(quote)
genImg(quote.thumb) # genImg(quote.photos[0])
if quote.badge.len > 0: elif quote.video.isSome:
tdiv(class="quote-badge"): renderVideo(quote.video.get(), prefs, path)
tdiv(class="quote-badge-text"): text quote.badge elif quote.gif.isSome:
elif quote.sensitive: renderGif(quote.gif.get(), prefs)
tdiv(class="quote-sensitive"):
icon "attention", class="quote-sensitive-icon"
proc renderQuote(quote: Quote; prefs: Prefs): VNode = proc renderQuote(quote: Tweet; prefs: Prefs; path: string): VNode =
if not quote.available: if not quote.available:
return buildHtml(tdiv(class="quote unavailable")): return buildHtml(tdiv(class="quote unavailable")):
tdiv(class="unavailable-quote"): tdiv(class="unavailable-quote"):
@ -227,26 +211,32 @@ proc renderQuote(quote: Quote; prefs: Prefs): VNode =
else: else:
text "This tweet is unavailable" text "This tweet is unavailable"
buildHtml(tdiv(class="quote")): buildHtml(tdiv(class="quote quote-big")):
a(class="quote-link", href=getLink(quote)) a(class="quote-link", href=getLink(quote))
if quote.thumb.len > 0 or quote.sensitive: tdiv(class="tweet-name-row"):
renderQuoteMedia(quote) tdiv(class="fullname-and-username"):
linkUser(quote.profile, class="fullname")
linkUser(quote.profile, class="username")
tdiv(class="fullname-and-username"): span(class="tweet-date"):
linkUser(quote.profile, class="fullname") a(href=getLink(quote), title=quote.getTime):
linkUser(quote.profile, class="username") text quote.getShortTime
if quote.reply.len > 0: if quote.reply.len > 0:
renderReply(quote) renderReply(quote)
tdiv(class="quote-text"): if quote.text.len > 0:
verbatim replaceUrl(quote.text, prefs) tdiv(class="quote-text"):
verbatim replaceUrl(quote.text, prefs)
if quote.hasThread: if quote.hasThread:
a(class="show-thread", href=getLink(quote)): a(class="show-thread", href=getLink(quote)):
text "Show this thread" text "Show this thread"
if quote.photos.len > 0 or quote.video.isSome or quote.gif.isSome:
renderQuoteMedia(quote, prefs, path)
proc renderLocation*(tweet: Tweet): string = proc renderLocation*(tweet: Tweet): string =
let (place, url) = tweet.getLocation() let (place, url) = tweet.getLocation()
if place.len == 0: return if place.len == 0: return
@ -258,11 +248,10 @@ proc renderLocation*(tweet: Tweet): string =
text place text place
return $node return $node
proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
index=0; total=(-1); last=false; showThread=false; last=false; showThread=false; mainTweet=false; afterTweet=false): VNode =
mainTweet=false): VNode =
var divClass = class var divClass = class
if index == total or last: if index == -1 or last:
divClass = "thread-last " & class divClass = "thread-last " & class
if not tweet.available: if not tweet.available:
@ -273,15 +262,22 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
else: else:
text "This tweet is unavailable" text "This tweet is unavailable"
let fullTweet = tweet
var retweet: string
var tweet = fullTweet
if tweet.retweet.isSome:
tweet = tweet.retweet.get
retweet = fullTweet.profile.fullname
buildHtml(tdiv(class=("timeline-item " & divClass))): buildHtml(tdiv(class=("timeline-item " & divClass))):
if not mainTweet: if not mainTweet:
a(class="tweet-link", href=getLink(tweet)) a(class="tweet-link", href=getLink(tweet))
tdiv(class="tweet-body"): tdiv(class="tweet-body"):
var views = "" var views = ""
renderHeader(tweet) renderHeader(tweet, retweet)
if index == 0 and tweet.reply.len > 0 and if not afterTweet and index == 0 and tweet.reply.len > 0 and
(tweet.reply.len > 1 or tweet.reply[0] != tweet.profile.username): (tweet.reply.len > 1 or tweet.reply[0] != tweet.profile.username):
renderReply(tweet) renderReply(tweet)
@ -293,7 +289,8 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
if tweet.card.isSome: if tweet.card.isSome:
renderCard(tweet.card.get(), prefs, path) renderCard(tweet.card.get(), prefs, path)
elif tweet.photos.len > 0:
if tweet.photos.len > 0:
renderAlbum(tweet) renderAlbum(tweet)
elif tweet.video.isSome: elif tweet.video.isSome:
renderVideo(tweet.video.get(), prefs, path) renderVideo(tweet.video.get(), prefs, path)
@ -301,11 +298,12 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
elif tweet.gif.isSome: elif tweet.gif.isSome:
renderGif(tweet.gif.get(), prefs) renderGif(tweet.gif.get(), prefs)
views = "GIF" views = "GIF"
elif tweet.poll.isSome:
if tweet.poll.isSome:
renderPoll(tweet.poll.get()) renderPoll(tweet.poll.get())
if tweet.quote.isSome: if tweet.quote.isSome:
renderQuote(tweet.quote.get(), prefs) renderQuote(tweet.quote.get(), prefs, path)
if mainTweet: if mainTweet:
p(class="tweet-published"): text getTweetTime(tweet) p(class="tweet-published"): text getTweetTime(tweet)