mirror of
https://github.com/zedeus/nitter.git
synced 2024-12-12 11:06:30 +00:00
html and css updates
This commit is contained in:
parent
762d00b21d
commit
2950c0de35
14 changed files with 246 additions and 224 deletions
|
@ -42,7 +42,8 @@
|
|||
|
||||
img {
|
||||
display: block;
|
||||
width: calc(100% - 8px);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border: 4px solid var(--darker_grey);
|
||||
|
|
|
@ -14,9 +14,10 @@
|
|||
}
|
||||
|
||||
&-header-mobile {
|
||||
padding: 5px 12px 0;
|
||||
display: none;
|
||||
width: calc(100% - 24px);
|
||||
box-sizing: border-box;
|
||||
padding: 5px 12px 0;
|
||||
width: 100%;
|
||||
float: unset;
|
||||
color: var(--accent);
|
||||
justify-content: space-between;
|
||||
|
|
|
@ -13,18 +13,28 @@
|
|||
}
|
||||
|
||||
.timeline-header {
|
||||
width: 100%;
|
||||
background-color: var(--bg_panel);
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
box-sizing: border-box;
|
||||
|
||||
button {
|
||||
float: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-banner img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.timeline-description {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.tab {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: var(--fg_color);
|
||||
background-color: var(--bg_overlays);
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
.still-image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,17 +67,17 @@
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.single-image {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
max-height: 600px;
|
||||
// .single-image {
|
||||
// display: inline-block;
|
||||
// width: 100%;
|
||||
// max-height: 600px;
|
||||
|
||||
.attachments {
|
||||
width: unset;
|
||||
max-height: unset;
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
// .attachments {
|
||||
// width: unset;
|
||||
// max-height: unset;
|
||||
// display: inherit;
|
||||
// }
|
||||
// }
|
||||
|
||||
.overlay-circle {
|
||||
border-radius: 50%;
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
border: solid 1px var(--dark_grey);
|
||||
border-radius: 10px;
|
||||
background-color: var(--bg_elements);
|
||||
overflow: auto;
|
||||
padding: 6px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--grey);
|
||||
|
@ -17,91 +17,73 @@
|
|||
&.unavailable:hover {
|
||||
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 {
|
||||
padding: 6px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.quote-link {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.quote .quote-link {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.quote-text {
|
||||
overflow: hidden;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.quote-media-container {
|
||||
max-height: 300px;
|
||||
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 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
.card {
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
img {
|
||||
.media-gif {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
.quote-badge {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.quote-badge-text {
|
||||
margin: 4px;
|
||||
background: $shadow;
|
||||
border-radius: 4px;
|
||||
color: #fffffff0;
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.attachments {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.gallery-gif .attachment {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: var(--bg_color);
|
||||
|
||||
video {
|
||||
height: unset;
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.gallery-video, .gallery-gif {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.still-image img {
|
||||
max-height: 250px
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import strformat
|
|||
import karax/[karaxdsl, vdom]
|
||||
|
||||
import renderutils
|
||||
import ".."/[types]
|
||||
import ".."/[types, utils]
|
||||
|
||||
proc renderListTabs*(query: Query; path: string): VNode =
|
||||
buildHtml(ul(class="tab")):
|
||||
|
@ -11,10 +11,18 @@ proc renderListTabs*(query: Query; path: string): VNode =
|
|||
li(class=query.getTabClass(userList)):
|
||||
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")):
|
||||
tdiv(class="timeline-header"):
|
||||
text &"\"{list}\" by @{name}"
|
||||
if list.banner.len > 0:
|
||||
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
|
||||
|
|
|
@ -58,14 +58,15 @@ proc renderProfileCard*(profile: Profile; prefs: Prefs): VNode =
|
|||
renderStat(profile.likes, "likes")
|
||||
|
||||
proc renderPhotoRail(profile: Profile; photoRail: PhotoRail): VNode =
|
||||
let count = insertSep($profile.media, ',')
|
||||
buildHtml(tdiv(class="photo-rail-card")):
|
||||
tdiv(class="photo-rail-header"):
|
||||
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")
|
||||
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"
|
||||
|
||||
tdiv(class="photo-rail-grid"):
|
||||
|
@ -73,7 +74,7 @@ proc renderPhotoRail(profile: Profile; photoRail: PhotoRail): VNode =
|
|||
if i == 16: break
|
||||
a(href=(&"/{profile.username}/status/{photo.tweetId}#m"),
|
||||
style={backgroundColor: photo.color}):
|
||||
genImg(photo.url & ":thumb")
|
||||
genImg(photo.url & (if "format" in photo.url: "" else: ":thumb"))
|
||||
|
||||
proc renderBanner(profile: Profile): VNode =
|
||||
buildHtml():
|
||||
|
@ -89,7 +90,7 @@ proc renderProtected(username: string): VNode =
|
|||
h2: text "This account's tweets are protected."
|
||||
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 =
|
||||
timeline.query.fromUser = @[profile.username]
|
||||
buildHtml(tdiv(class="profile-tabs")):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import strutils, xmltree
|
||||
import strutils
|
||||
import karax/[karaxdsl, vdom, vstyles]
|
||||
|
||||
import ../types, ../utils
|
||||
import ".."/[types, utils]
|
||||
|
||||
proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode =
|
||||
var c = "icon-" & icon
|
||||
|
|
|
@ -82,24 +82,24 @@
|
|||
<width>128</width>
|
||||
<height>128</height>
|
||||
</image>
|
||||
#if timeline != nil:
|
||||
# if timeline.content.len > 0:
|
||||
${renderRssTweets(timeline.content, prefs, hostname)}
|
||||
#end if
|
||||
</channel>
|
||||
</rss>
|
||||
#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 link = &"https://{hostname}/{name}/lists/{list}"
|
||||
#let link = &"https://{hostname}/{list.username}/lists/{list.name}"
|
||||
#result = ""
|
||||
<?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">
|
||||
<channel>
|
||||
<atom:link href="${link}" rel="self" type="application/rss+xml" />
|
||||
<title>${list} / @${name}</title>
|
||||
<title>${list.name} / @${list.username}</title>
|
||||
<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>
|
||||
<ttl>40</ttl>
|
||||
${renderRssTweets(tweets, prefs, hostname)}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import strutils, strformat, sequtils, unicode, tables
|
||||
import karax/[karaxdsl, vdom, vstyles]
|
||||
import karax/[karaxdsl, vdom]
|
||||
|
||||
import renderutils, timeline
|
||||
import ".."/[types, formatters, query]
|
||||
import ".."/[types, query]
|
||||
|
||||
let toggles = {
|
||||
"nativeretweets": "Retweets",
|
||||
|
@ -93,13 +93,15 @@ proc renderTweetSearch*(results: Result[Tweet]; prefs: Prefs; path: string): VNo
|
|||
if query.fromUser.len > 1:
|
||||
tdiv(class="timeline-header"):
|
||||
text query.fromUser.join(" | ")
|
||||
|
||||
if query.fromUser.len > 0:
|
||||
renderProfileTabs(query, query.fromUser.join(","))
|
||||
|
||||
if query.fromUser.len == 0 or query.kind == tweets:
|
||||
tdiv(class="timeline-header"):
|
||||
renderSearchPanel(query)
|
||||
|
||||
if query.fromUser.len > 0:
|
||||
renderProfileTabs(query, query.fromUser.join(","))
|
||||
else:
|
||||
if query.fromUser.len == 0:
|
||||
renderSearchTabs(query)
|
||||
|
||||
renderTimelineTweets(results, prefs, path)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import uri
|
||||
import karax/[karaxdsl, vdom]
|
||||
|
||||
import ".."/[types, formatters]
|
||||
|
@ -13,7 +14,7 @@ proc renderMoreReplies(thread: Chain): VNode =
|
|||
let reply = if thread.more == 1: "reply" else: "replies"
|
||||
let link = getLink(thread.content[^1])
|
||||
buildHtml(tdiv(class="timeline-item more-replies")):
|
||||
if link.len > 0:
|
||||
if thread.content[^1].available:
|
||||
a(class="more-replies-text", href=link):
|
||||
text $num & "more " & reply
|
||||
else:
|
||||
|
@ -32,41 +33,45 @@ proc renderReplyThread(thread: Chain; prefs: Prefs; path: string): VNode =
|
|||
proc renderReplies*(replies: Result[Chain]; prefs: Prefs; path: string): VNode =
|
||||
buildHtml(tdiv(class="replies", id="r")):
|
||||
for thread in replies.content:
|
||||
if thread == nil: continue
|
||||
if thread.content.len == 0: continue
|
||||
renderReplyThread(thread, prefs, path)
|
||||
|
||||
if replies.hasMore:
|
||||
renderMore(Query(), replies.minId, focus="#r")
|
||||
if replies.bottom.len > 0:
|
||||
renderMore(Query(), encodeUrl(replies.bottom), focus="#r")
|
||||
|
||||
proc renderConversation*(conversation: Conversation; prefs: Prefs; path: string): VNode =
|
||||
let hasAfter = conversation.after != nil
|
||||
let showReplies = not prefs.hideReplies
|
||||
proc renderConversation*(conv: Conversation; prefs: Prefs; path: string): VNode =
|
||||
let hasAfter = conv.after.content.len > 0
|
||||
let threadId = conv.tweet.threadId
|
||||
buildHtml(tdiv(class="conversation")):
|
||||
tdiv(class="main-thread"):
|
||||
if conversation.before != nil:
|
||||
if conv.before.content.len > 0:
|
||||
tdiv(class="before-tweet thread-line"):
|
||||
if conversation.before.more == -1:
|
||||
renderEarlier(conversation.before)
|
||||
for i, tweet in conversation.before.content:
|
||||
let first = conv.before.content[0]
|
||||
if threadId != first.id and (first.replyId > 0 or not first.available):
|
||||
renderEarlier(conv.before)
|
||||
for i, tweet in conv.before.content:
|
||||
renderTweet(tweet, prefs, path, index=i)
|
||||
|
||||
tdiv(class="main-tweet", id="m"):
|
||||
let afterClass = if hasAfter: "thread thread-line" else: ""
|
||||
renderTweet(conversation.tweet, prefs, path, class=afterClass,
|
||||
mainTweet=true)
|
||||
renderTweet(conv.tweet, prefs, path, class=afterClass, mainTweet=true)
|
||||
|
||||
if hasAfter:
|
||||
tdiv(class="after-tweet thread-line"):
|
||||
let total = conversation.after.content.high
|
||||
let more = conversation.after.more
|
||||
for i, tweet in conversation.after.content:
|
||||
renderTweet(tweet, prefs, path, index=i, last=(i == total and more == 0))
|
||||
let
|
||||
total = conv.after.content.high
|
||||
more = conv.after.more
|
||||
for i, tweet in conv.after.content:
|
||||
renderTweet(tweet, prefs, path, index=i,
|
||||
last=(i == total and more == 0), afterTweet=true)
|
||||
|
||||
if more != 0:
|
||||
renderMoreReplies(conversation.after)
|
||||
renderMoreReplies(conv.after)
|
||||
|
||||
if conversation.replies != nil and showReplies:
|
||||
if not conversation.replies.beginning:
|
||||
renderNewer(Query(), getLink(conversation.tweet))
|
||||
if conversation.replies.content.len > 0:
|
||||
renderReplies(conversation.replies, prefs, path)
|
||||
if not prefs.hideReplies:
|
||||
if not conv.replies.beginning:
|
||||
renderNewer(Query(), getLink(conv.tweet), focus="#r")
|
||||
if conv.replies.content.len > 0 or conv.replies.bottom.len > 0:
|
||||
renderReplies(conv.replies, prefs, path)
|
||||
|
||||
renderToTop(focus="#m")
|
||||
|
|
|
@ -10,16 +10,22 @@ proc getQuery(query: Query): string =
|
|||
if result.len > 0:
|
||||
result &= "&"
|
||||
|
||||
proc renderNewer*(query: Query; path: string): VNode =
|
||||
let q = genQueryUrl(query)
|
||||
let url = if q.len > 0: "?" & q else: ""
|
||||
proc renderToTop*(focus="#"): VNode =
|
||||
buildHtml(tdiv(class="top-ref")):
|
||||
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")):
|
||||
a(href=(path & url)):
|
||||
a(href=(p & url)):
|
||||
text "Load newest"
|
||||
|
||||
proc renderMore*(query: Query; minId: string; focus=""): VNode =
|
||||
proc renderMore*(query: Query; cursor: string; focus=""): VNode =
|
||||
buildHtml(tdiv(class="show-more")):
|
||||
a(href=(&"?{getQuery(query)}max_position={minId}{focus}")):
|
||||
a(href=(&"?{getQuery(query)}cursor={encodeUrl(cursor)}{focus}")):
|
||||
text "Load more"
|
||||
|
||||
proc renderNoMore(): VNode =
|
||||
|
@ -32,10 +38,6 @@ proc renderNoneFound(): VNode =
|
|||
h2(class="timeline-none"):
|
||||
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 =
|
||||
buildHtml(tdiv(class="thread-line")):
|
||||
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 header = if tweet.pinned or tweet.retweet.isSome: "with-header " else: ""
|
||||
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 =
|
||||
it.retweet.isNone and it.reply.len == 0 and it.threadId == thread
|
||||
proc threadFilter(tweets: openArray[Tweet]; threads: openArray[int64]; it: Tweet): seq[Tweet] =
|
||||
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 =
|
||||
buildHtml(tdiv(class="timeline-item")):
|
||||
|
@ -72,8 +80,8 @@ proc renderTimelineUsers*(results: Result[Profile]; prefs: Prefs; path=""): VNod
|
|||
if results.content.len > 0:
|
||||
for user in results.content:
|
||||
renderUser(user, prefs)
|
||||
if results.minId != "0":
|
||||
renderMore(results.query, results.minId)
|
||||
if results.bottom.len > 0:
|
||||
renderMore(results.query, results.bottom)
|
||||
renderToTop()
|
||||
elif results.beginning:
|
||||
renderNoneFound()
|
||||
|
@ -86,24 +94,31 @@ proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string):
|
|||
renderNewer(results.query, parseUri(path).path)
|
||||
|
||||
if results.content.len == 0:
|
||||
renderNoneFound()
|
||||
if not results.beginning:
|
||||
renderNoMore()
|
||||
else:
|
||||
renderNoneFound()
|
||||
else:
|
||||
var threads: seq[int64]
|
||||
var retweets: seq[int64]
|
||||
var
|
||||
threads: seq[int64]
|
||||
retweets: seq[int64]
|
||||
|
||||
for tweet in results.content:
|
||||
if tweet.threadId in threads or tweet.id in retweets: continue
|
||||
if tweet.pinned and prefs.hidePins: continue
|
||||
let thread = results.content.filterIt(threadFilter(it, tweet.threadId))
|
||||
let rt = if tweet.retweet.isSome: get(tweet.retweet).id else: 0
|
||||
|
||||
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 tweet.retweet.isSome:
|
||||
retweets &= tweet.id
|
||||
renderTweet(tweet, prefs, path, showThread=tweet.hasThread)
|
||||
var hasThread = tweet.hasThread
|
||||
if rt != 0:
|
||||
retweets &= rt
|
||||
hasThread = get(tweet.retweet).hasThread
|
||||
renderTweet(tweet, prefs, path, showThread=hasThread)
|
||||
else:
|
||||
renderThread(thread, prefs, path)
|
||||
threads &= tweet.threadId
|
||||
threads &= thread.mapIt(it.id)
|
||||
|
||||
if results.hasMore or results.query.kind != posts:
|
||||
renderMore(results.query, results.minId)
|
||||
else:
|
||||
renderNoMore()
|
||||
renderMore(results.query, results.bottom)
|
||||
renderToTop()
|
||||
|
|
|
@ -4,11 +4,11 @@ import karax/[karaxdsl, vdom, vstyles]
|
|||
import renderutils
|
||||
import ".."/[types, utils, formatters]
|
||||
|
||||
proc renderHeader(tweet: Tweet): VNode =
|
||||
proc renderHeader(tweet: Tweet; retweet=""): VNode =
|
||||
buildHtml(tdiv):
|
||||
if tweet.retweet.isSome:
|
||||
if retweet.len > 0:
|
||||
tdiv(class="retweet-header"):
|
||||
span: icon "retweet", get(tweet.retweet).by & " retweeted"
|
||||
span: icon "retweet", retweet & " retweeted"
|
||||
|
||||
if tweet.pinned:
|
||||
tdiv(class="pinned"):
|
||||
|
@ -24,31 +24,24 @@ proc renderHeader(tweet: Tweet): VNode =
|
|||
linkUser(tweet.profile, class="username")
|
||||
|
||||
span(class="tweet-date"):
|
||||
a(href=getLink(tweet), title=tweet.getTime()):
|
||||
text tweet.shortTime
|
||||
a(href=getLink(tweet), title=tweet.getTime):
|
||||
text tweet.getShortTime
|
||||
|
||||
proc renderAlbum(tweet: Tweet): VNode =
|
||||
let
|
||||
groups = if tweet.photos.len < 3: @[tweet.photos]
|
||||
else: tweet.photos.distribute(2)
|
||||
|
||||
if groups.len == 1 and groups[0].len == 1:
|
||||
buildHtml(tdiv(class="single-image")):
|
||||
tdiv(class="attachments gallery-row"):
|
||||
a(href=getPicUrl(groups[0][0] & "?name=orig"), class="still-image",
|
||||
target="_blank"):
|
||||
genImg(groups[0][0])
|
||||
else:
|
||||
buildHtml(tdiv(class="attachments")):
|
||||
for i, photos in groups:
|
||||
let margin = if i > 0: ".25em" else: ""
|
||||
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)
|
||||
buildHtml(tdiv(class="attachments")):
|
||||
for i, photos in groups:
|
||||
let margin = if i > 0: ".25em" else: ""
|
||||
tdiv(class="gallery-row", style={marginTop: margin}):
|
||||
for photo in photos:
|
||||
tdiv(class="attachment image"):
|
||||
var url = photo
|
||||
if "=orig" notin url: url &= "?name=orig"
|
||||
a(href=getPicUrl(url), class="still-image", target="_blank"):
|
||||
genImg(photo)
|
||||
|
||||
proc isPlaybackEnabled(prefs: Prefs; video: Video): bool =
|
||||
case video.playbackType
|
||||
|
@ -88,7 +81,8 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode =
|
|||
elif not prefs.isPlaybackEnabled(video):
|
||||
renderVideoDisabled(video, path)
|
||||
else:
|
||||
let source = getVidUrl(video.url)
|
||||
let vid = video.variants.filterIt(it.videoType == video.playbackType)
|
||||
let source = getVidUrl(vid[0].url)
|
||||
case video.playbackType
|
||||
of mp4:
|
||||
if prefs.muteVideos:
|
||||
|
@ -138,7 +132,7 @@ proc renderPoll(poll: Poll): VNode =
|
|||
proc renderCardImage(card: Card): VNode =
|
||||
buildHtml(tdiv(class="card-image-container")):
|
||||
tdiv(class="card-image"):
|
||||
img(src=getPicUrl(get(card.image)))
|
||||
img(src=getPicUrl(card.image), alt="")
|
||||
if card.kind == player:
|
||||
tdiv(class="card-overlay"):
|
||||
tdiv(class="overlay-circle"):
|
||||
|
@ -151,9 +145,8 @@ proc renderCardContent(card: Card): VNode =
|
|||
span(class="card-destination"): text card.dest
|
||||
|
||||
proc renderCard(card: Card; prefs: Prefs; path: string): VNode =
|
||||
const largeCards = {summaryLarge, liveEvent, promoWebsite,
|
||||
promoVideo, promoVideoConvo}
|
||||
let large = if card.kind in largeCards: " large" else: ""
|
||||
const smallCards = {player, summary}
|
||||
let large = if card.kind notin smallCards: " large" else: ""
|
||||
let url = replaceUrl(card.url, prefs)
|
||||
|
||||
buildHtml(tdiv(class=("card" & large))):
|
||||
|
@ -164,7 +157,7 @@ proc renderCard(card: Card; prefs: Prefs; path: string): VNode =
|
|||
renderCardContent(card)
|
||||
else:
|
||||
a(class="card-container", href=url):
|
||||
if card.image.isSome:
|
||||
if card.image.len > 0:
|
||||
renderCardImage(card)
|
||||
tdiv(class="card-content-container"):
|
||||
renderCardContent(card)
|
||||
|
@ -184,13 +177,6 @@ proc renderReply(tweet: Tweet): VNode =
|
|||
if i > 0: text " "
|
||||
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 =
|
||||
let avatarUrl = getPicUrl(profile.getUserpic("_200x200"))
|
||||
buildHtml(a(class="attribution", href=("/" & profile.username))):
|
||||
|
@ -206,19 +192,17 @@ proc renderMediaTags(tags: seq[Profile]): VNode =
|
|||
if i < tags.high:
|
||||
text ", "
|
||||
|
||||
proc renderQuoteMedia(quote: Quote): VNode =
|
||||
proc renderQuoteMedia(quote: Tweet; prefs: Prefs; path: string): VNode =
|
||||
buildHtml(tdiv(class="quote-media-container")):
|
||||
if quote.thumb.len > 0:
|
||||
tdiv(class="quote-media"):
|
||||
genImg(quote.thumb)
|
||||
if quote.badge.len > 0:
|
||||
tdiv(class="quote-badge"):
|
||||
tdiv(class="quote-badge-text"): text quote.badge
|
||||
elif quote.sensitive:
|
||||
tdiv(class="quote-sensitive"):
|
||||
icon "attention", class="quote-sensitive-icon"
|
||||
if quote.photos.len > 0:
|
||||
renderAlbum(quote)
|
||||
# genImg(quote.photos[0])
|
||||
elif quote.video.isSome:
|
||||
renderVideo(quote.video.get(), prefs, path)
|
||||
elif quote.gif.isSome:
|
||||
renderGif(quote.gif.get(), prefs)
|
||||
|
||||
proc renderQuote(quote: Quote; prefs: Prefs): VNode =
|
||||
proc renderQuote(quote: Tweet; prefs: Prefs; path: string): VNode =
|
||||
if not quote.available:
|
||||
return buildHtml(tdiv(class="quote unavailable")):
|
||||
tdiv(class="unavailable-quote"):
|
||||
|
@ -227,26 +211,32 @@ proc renderQuote(quote: Quote; prefs: Prefs): VNode =
|
|||
else:
|
||||
text "This tweet is unavailable"
|
||||
|
||||
buildHtml(tdiv(class="quote")):
|
||||
buildHtml(tdiv(class="quote quote-big")):
|
||||
a(class="quote-link", href=getLink(quote))
|
||||
|
||||
if quote.thumb.len > 0 or quote.sensitive:
|
||||
renderQuoteMedia(quote)
|
||||
tdiv(class="tweet-name-row"):
|
||||
tdiv(class="fullname-and-username"):
|
||||
linkUser(quote.profile, class="fullname")
|
||||
linkUser(quote.profile, class="username")
|
||||
|
||||
tdiv(class="fullname-and-username"):
|
||||
linkUser(quote.profile, class="fullname")
|
||||
linkUser(quote.profile, class="username")
|
||||
span(class="tweet-date"):
|
||||
a(href=getLink(quote), title=quote.getTime):
|
||||
text quote.getShortTime
|
||||
|
||||
if quote.reply.len > 0:
|
||||
renderReply(quote)
|
||||
|
||||
tdiv(class="quote-text"):
|
||||
verbatim replaceUrl(quote.text, prefs)
|
||||
if quote.text.len > 0:
|
||||
tdiv(class="quote-text"):
|
||||
verbatim replaceUrl(quote.text, prefs)
|
||||
|
||||
if quote.hasThread:
|
||||
a(class="show-thread", href=getLink(quote)):
|
||||
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 =
|
||||
let (place, url) = tweet.getLocation()
|
||||
if place.len == 0: return
|
||||
|
@ -258,11 +248,10 @@ proc renderLocation*(tweet: Tweet): string =
|
|||
text place
|
||||
return $node
|
||||
|
||||
proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
|
||||
index=0; total=(-1); last=false; showThread=false;
|
||||
mainTweet=false): VNode =
|
||||
proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
|
||||
last=false; showThread=false; mainTweet=false; afterTweet=false): VNode =
|
||||
var divClass = class
|
||||
if index == total or last:
|
||||
if index == -1 or last:
|
||||
divClass = "thread-last " & class
|
||||
|
||||
if not tweet.available:
|
||||
|
@ -273,15 +262,22 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
|
|||
else:
|
||||
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))):
|
||||
if not mainTweet:
|
||||
a(class="tweet-link", href=getLink(tweet))
|
||||
|
||||
tdiv(class="tweet-body"):
|
||||
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):
|
||||
renderReply(tweet)
|
||||
|
||||
|
@ -293,7 +289,8 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
|
|||
|
||||
if tweet.card.isSome:
|
||||
renderCard(tweet.card.get(), prefs, path)
|
||||
elif tweet.photos.len > 0:
|
||||
|
||||
if tweet.photos.len > 0:
|
||||
renderAlbum(tweet)
|
||||
elif tweet.video.isSome:
|
||||
renderVideo(tweet.video.get(), prefs, path)
|
||||
|
@ -301,11 +298,12 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
|
|||
elif tweet.gif.isSome:
|
||||
renderGif(tweet.gif.get(), prefs)
|
||||
views = "GIF"
|
||||
elif tweet.poll.isSome:
|
||||
|
||||
if tweet.poll.isSome:
|
||||
renderPoll(tweet.poll.get())
|
||||
|
||||
if tweet.quote.isSome:
|
||||
renderQuote(tweet.quote.get(), prefs)
|
||||
renderQuote(tweet.quote.get(), prefs, path)
|
||||
|
||||
if mainTweet:
|
||||
p(class="tweet-published"): text getTweetTime(tweet)
|
||||
|
|
Loading…
Reference in a new issue