Refactor tweet/timeline views

This commit is contained in:
Zed 2019-09-13 19:57:27 +02:00
parent a687188dd1
commit eeae28da0c
8 changed files with 107 additions and 136 deletions

View file

@ -47,7 +47,7 @@
.profile-tabs { .profile-tabs {
width: 100vw; width: 100vw;
.timeline-tab { .timeline-container {
width: 100% !important; width: 100% !important;
} }
} }

View file

@ -1,30 +1,19 @@
@import '_variables'; @import '_variables';
#posts { .timeline-container {
@include panel(100%, 600px);
}
.timeline {
background-color: $bg_panel; background-color: $bg_panel;
}
.timeline-tab { > div:not(:last-child) {
float: right; border-bottom: 1px solid $border_grey;
padding: 0;
box-sizing: border-box;
display: inline-block;
font-size: 14px;
text-align: left;
vertical-align: top;
}
.multi-timeline {
max-width: 600px;
width: 100%;
margin: 0 auto;
.timeline-tab {
width: 100%;
} }
} }
.multi-header { .timeline-header {
background-color: $bg_panel; background-color: $bg_panel;
text-align: center; text-align: center;
padding: 10px; padding: 10px;
@ -72,10 +61,6 @@
} }
} }
.timeline-tweet {
border-bottom: 1px solid $border_grey;
}
.timeline-footer { .timeline-footer {
background-color: $bg_panel; background-color: $bg_panel;
padding: 6px 0; padding: 6px 0;
@ -119,11 +104,7 @@
background-color: $bg_panel; background-color: $bg_panel;
text-align: center; text-align: center;
padding: .75em 0; padding: .75em 0;
display: block; display: block !important;
&.status-el {
border-bottom: 1px solid $border_grey;
}
a { a {
background-color: $darkest_grey; background-color: $darkest_grey;
@ -137,3 +118,11 @@
} }
} }
} }
.timeline-item {
overflow-wrap: break-word;
border-left-width: 0;
min-width: 0;
padding: .75em;
display: flex;
}

View file

@ -7,25 +7,17 @@
@import 'poll'; @import 'poll';
@import 'quote'; @import 'quote';
.status-el { .tweet-body {
overflow-wrap: break-word;
border-left-width: 0;
min-width: 0;
padding: .75em;
display: flex;
.status-content {
font-family: $font_3;
line-height: 1.4em;
}
}
.status-body {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
margin-left: 58px; margin-left: 58px;
} }
.tweet-content {
font-family: $font_3;
line-height: 1.4em;
}
.tweet-header { .tweet-header {
padding: 0; padding: 0;
vertical-align: bottom; vertical-align: bottom;
@ -79,7 +71,6 @@
float: left; float: left;
margin-top: 3px; margin-top: 3px;
margin-left: -58px; margin-left: -58px;
position: absolute;
width: 48px; width: 48px;
height: 48px; height: 48px;
border-radius: 50%; border-radius: 50%;

View file

@ -10,7 +10,7 @@
background-color: $bg_panel; background-color: $bg_panel;
} }
.main-tweet .status-content { .main-tweet .tweet-content {
font-size: 20px; font-size: 20px;
} }
@ -20,7 +20,7 @@
} }
.thread-line { .thread-line {
.status-el::before { .timeline-item::before {
background: $accent_dark; background: $accent_dark;
content: ''; content: '';
position: relative; position: relative;
@ -53,7 +53,7 @@
} }
} }
.thread-last .status-el::before { .timeline-item.thread-last::before {
background: unset; background: unset;
min-width: unset; min-width: unset;
width: 0; width: 0;

View file

@ -75,6 +75,12 @@ proc renderBanner(profile: Profile): VNode =
a(href=getPicUrl(profile.banner), target="_blank"): a(href=getPicUrl(profile.banner), target="_blank"):
genImg(profile.banner) genImg(profile.banner)
proc renderProtected(username: string): VNode =
buildHtml(tdiv(class="timeline-container timeline")):
tdiv(class="timeline-header timeline-protected"):
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: Timeline;
photoRail: seq[GalleryPhoto]; prefs: Prefs; path: string): VNode = photoRail: seq[GalleryPhoto]; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="profile-tabs")): buildHtml(tdiv(class="profile-tabs")):
@ -88,11 +94,9 @@ proc renderProfile*(profile: Profile; timeline: Timeline;
if photoRail.len > 0: if photoRail.len > 0:
renderPhotoRail(profile, photoRail) renderPhotoRail(profile, photoRail)
tdiv(class="timeline-tab"): tdiv(class="timeline-container"):
renderTimeline(timeline, profile.username, profile.protected, prefs, path) if profile.protected:
renderProtected(profile.username)
proc renderMulti*(timeline: Timeline; usernames: string; else:
prefs: Prefs; path: string): VNode = renderProfileTabs(timeline, profile.username)
buildHtml(tdiv(class="multi-timeline")): renderTimelineTweets(timeline, prefs, path)
tdiv(class="timeline-tab"):
renderTimeline(timeline, usernames, false, prefs, path, multi=true)

View file

@ -6,7 +6,7 @@ import tweet
proc renderMoreReplies(thread: Thread): VNode = proc renderMoreReplies(thread: Thread): VNode =
let num = if thread.more != -1: $thread.more & " " else: "" let num = if thread.more != -1: $thread.more & " " else: ""
let reply = if thread.more == 1: "reply" else: "replies" let reply = if thread.more == 1: "reply" else: "replies"
buildHtml(tdiv(class="status-el more-replies")): buildHtml(tdiv(class="timeline-item more-replies")):
a(class="more-replies-text", title="Not implemented yet"): a(class="more-replies-text", title="Not implemented yet"):
text $num & "more " & reply text $num & "more " & reply

View file

@ -1,25 +1,29 @@
import strutils, strformat, sequtils, algorithm, times import strutils, strformat, sequtils, algorithm, times
import karax/[karaxdsl, vdom, vstyles] import karax/[karaxdsl, vdom, vstyles]
import ../types, ../search import ".."/[types, query, formatters]
import tweet, renderutils import tweet, renderutils
proc getQuery(timeline: Timeline): string = proc getQuery(query: Option[Query]): string =
if timeline.query.isNone: "?" if query.isNone:
else: genQueryUrl(get(timeline.query)) result = "?"
else:
result = genQueryUrl(get(query))
if result[^1] != '?':
result &= "&"
proc getTabClass(timeline: Timeline; tab: string): string = proc getTabClass(results: Result; tab: string): string =
var classes = @["tab-item"] var classes = @["tab-item"]
if timeline.query.isNone or get(timeline.query).kind == multi: if results.query.isNone or get(results.query).kind == multi:
if tab == "posts": if tab == "posts":
classes.add "active" classes.add "active"
elif $get(timeline.query).kind == tab: elif $get(results.query).kind == tab:
classes.add "active" classes.add "active"
return classes.join(" ") return classes.join(" ")
proc renderSearchTabs(timeline: Timeline; username: string): VNode = proc renderProfileTabs*(timeline: Timeline; username: string): VNode =
let link = "/" & username let link = "/" & username
buildHtml(ul(class="tab")): buildHtml(ul(class="tab")):
li(class=timeline.getTabClass("posts")): li(class=timeline.getTabClass("posts")):
@ -29,33 +33,28 @@ proc renderSearchTabs(timeline: Timeline; username: string): VNode =
li(class=timeline.getTabClass("media")): li(class=timeline.getTabClass("media")):
a(href=(link & "/media")): text "Media" a(href=(link & "/media")): text "Media"
proc renderNewer(timeline: Timeline; username: string): VNode = proc renderNewer(query: Option[Query]): VNode =
buildHtml(tdiv(class="status-el show-more")): buildHtml(tdiv(class="timeline-item show-more")):
a(href=("/" & username & getQuery(timeline).strip(chars={'?'}))): a(href=(getQuery(query).strip(chars={'?', '&'}))):
text "Load newest tweets" text "Load newest"
proc renderOlder(timeline: Timeline; username: string): VNode = proc renderOlder(query: Option[Query]; minId: string): VNode =
buildHtml(tdiv(class="show-more")): buildHtml(tdiv(class="show-more")):
a(href=(&"/{username}{getQuery(timeline)}after={timeline.minId}")): a(href=(&"{getQuery(query)}after={minId}")):
text "Load older tweets" text "Load older"
proc renderNoMore(): VNode = proc renderNoMore(): VNode =
buildHtml(tdiv(class="timeline-footer")): buildHtml(tdiv(class="timeline-footer")):
h2(class="timeline-end"): h2(class="timeline-end"):
text "No more tweets." text "No more items"
proc renderNoneFound(): VNode = proc renderNoneFound(): VNode =
buildHtml(tdiv(class="timeline-header")): buildHtml(tdiv(class="timeline-header")):
h2(class="timeline-none"): h2(class="timeline-none"):
text "No tweets found." text "No items found"
proc renderProtected(username: string): VNode =
buildHtml(tdiv(class="timeline-header timeline-protected")):
h2: text "This account's tweets are protected."
p: text &"Only confirmed followers have access to @{username}'s tweets."
proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode = proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(class="timeline-tweet thread-line")): buildHtml(tdiv(class="thread-line")):
for i, threadTweet in thread.sortedByIt(it.time): for i, threadTweet in thread.sortedByIt(it.time):
renderTweet(threadTweet, prefs, path, class="thread", renderTweet(threadTweet, prefs, path, class="thread",
index=i, total=thread.high) index=i, total=thread.high)
@ -63,37 +62,26 @@ proc renderThread(thread: seq[Tweet]; prefs: Prefs; path: string): VNode =
proc threadFilter(it: Tweet; tweetThread: string): bool = proc threadFilter(it: Tweet; tweetThread: string): bool =
it.retweet.isNone and it.reply.len == 0 and it.threadId == tweetThread it.retweet.isNone and it.reply.len == 0 and it.threadId == tweetThread
proc renderTweets(timeline: Timeline; prefs: Prefs; path: string): VNode =
buildHtml(tdiv(id="posts")):
var threads: seq[string]
for tweet in timeline.content:
if tweet.threadId in threads: continue
let thread = timeline.content.filterIt(threadFilter(it, tweet.threadId))
if thread.len < 2:
renderTweet(tweet, prefs, path, class="timeline-tweet")
else:
renderThread(thread, prefs, path)
threads &= tweet.threadId
proc renderTimeline*(timeline: Timeline; username: string; protected: bool; proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string): VNode =
prefs: Prefs; path: string; multi=false): VNode = buildHtml(tdiv(class="timeline")):
buildHtml(tdiv): if not results.beginning:
if multi: renderNewer(results.query)
tdiv(class="multi-header"):
text username.replace(",", " | ")
if not protected: if results.content.len == 0:
renderSearchTabs(timeline, username)
if not timeline.beginning:
renderNewer(timeline, username)
if protected:
renderProtected(username)
elif timeline.content.len == 0:
renderNoneFound() renderNoneFound()
else: else:
renderTweets(timeline, prefs, path) var threads: seq[string]
if timeline.hasMore or timeline.query.isSome: for tweet in results.content:
renderOlder(timeline, username) if tweet.threadId in threads: continue
let thread = results.content.filterIt(threadFilter(it, tweet.threadId))
if thread.len < 2:
renderTweet(tweet, prefs, path)
else:
renderThread(thread, prefs, path)
threads &= tweet.threadId
if results.hasMore or results.query.isSome:
renderOlder(results.query, results.minId)
else: else:
renderNoMore() renderNoMore()

View file

@ -224,43 +224,42 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class="";
if not tweet.available: if not tweet.available:
return buildHtml(tdiv(class=divClass)): return buildHtml(tdiv(class=divClass)):
tdiv(class="status-el unavailable"): tdiv(class="timeline-item unavailable"):
tdiv(class="unavailable-box"): tdiv(class="unavailable-box"):
if tweet.tombstone.len > 0: if tweet.tombstone.len > 0:
text tweet.tombstone text tweet.tombstone
else: else:
text "This tweet is unavailable" text "This tweet is unavailable"
buildHtml(tdiv(class=divClass)): buildHtml(tdiv(class=("timeline-item " & divClass))):
tdiv(class="status-el"): tdiv(class="tweet-body"):
tdiv(class="status-body"): var views = ""
var views = "" renderHeader(tweet)
renderHeader(tweet)
if index == 0 and tweet.reply.len > 0: if index == 0 and tweet.reply.len > 0:
renderReply(tweet) renderReply(tweet)
tdiv(class="status-content media-body"): tdiv(class="tweet-content media-body"):
verbatim linkifyText(tweet.text, prefs) verbatim linkifyText(tweet.text, prefs)
if tweet.quote.isSome: if tweet.quote.isSome:
renderQuote(tweet.quote.get(), prefs) renderQuote(tweet.quote.get(), prefs)
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: elif 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)
views = tweet.video.get().views views = tweet.video.get().views
elif tweet.gif.isSome: elif tweet.gif.isSome:
renderGif(tweet.gif.get(), prefs) renderGif(tweet.gif.get(), prefs)
elif tweet.poll.isSome: elif tweet.poll.isSome:
renderPoll(tweet.poll.get()) renderPoll(tweet.poll.get())
if not prefs.hideTweetStats: if not prefs.hideTweetStats:
renderStats(tweet.stats, views) renderStats(tweet.stats, views)
if tweet.hasThread and "timeline" in class: if tweet.hasThread and "timeline" in class:
a(class="show-thread", href=getLink(tweet)): a(class="show-thread", href=getLink(tweet)):
text "Show this thread" text "Show this thread"