From eeae28da0c786d8ba800ad89c1ee9a4fcf2a931f Mon Sep 17 00:00:00 2001 From: Zed Date: Fri, 13 Sep 2019 19:57:27 +0200 Subject: [PATCH] Refactor tweet/timeline views --- src/sass/profile/_base.scss | 2 +- src/sass/timeline.scss | 47 ++++++++----------- src/sass/tweet/_base.scss | 21 +++------ src/sass/tweet/thread.scss | 6 +-- src/views/profile.nim | 20 +++++---- src/views/status.nim | 2 +- src/views/timeline.nim | 90 ++++++++++++++++--------------------- src/views/tweet.nim | 55 +++++++++++------------ 8 files changed, 107 insertions(+), 136 deletions(-) diff --git a/src/sass/profile/_base.scss b/src/sass/profile/_base.scss index f5c8ddb..4d4cb49 100644 --- a/src/sass/profile/_base.scss +++ b/src/sass/profile/_base.scss @@ -47,7 +47,7 @@ .profile-tabs { width: 100vw; - .timeline-tab { + .timeline-container { width: 100% !important; } } diff --git a/src/sass/timeline.scss b/src/sass/timeline.scss index a9e3433..fa536bd 100644 --- a/src/sass/timeline.scss +++ b/src/sass/timeline.scss @@ -1,30 +1,19 @@ @import '_variables'; -#posts { +.timeline-container { + @include panel(100%, 600px); +} + +.timeline { background-color: $bg_panel; -} -.timeline-tab { - float: right; - 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%; + > div:not(:last-child) { + border-bottom: 1px solid $border_grey; } + } -.multi-header { +.timeline-header { background-color: $bg_panel; text-align: center; padding: 10px; @@ -72,10 +61,6 @@ } } -.timeline-tweet { - border-bottom: 1px solid $border_grey; -} - .timeline-footer { background-color: $bg_panel; padding: 6px 0; @@ -119,11 +104,7 @@ background-color: $bg_panel; text-align: center; padding: .75em 0; - display: block; - - &.status-el { - border-bottom: 1px solid $border_grey; - } + display: block !important; a { 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; +} diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index 94f4feb..ef0b2e0 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -7,25 +7,17 @@ @import 'poll'; @import 'quote'; -.status-el { - 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 { +.tweet-body { flex: 1; min-width: 0; margin-left: 58px; } +.tweet-content { + font-family: $font_3; + line-height: 1.4em; +} + .tweet-header { padding: 0; vertical-align: bottom; @@ -79,7 +71,6 @@ float: left; margin-top: 3px; margin-left: -58px; - position: absolute; width: 48px; height: 48px; border-radius: 50%; diff --git a/src/sass/tweet/thread.scss b/src/sass/tweet/thread.scss index 95db9d8..bc66dfb 100644 --- a/src/sass/tweet/thread.scss +++ b/src/sass/tweet/thread.scss @@ -10,7 +10,7 @@ background-color: $bg_panel; } -.main-tweet .status-content { +.main-tweet .tweet-content { font-size: 20px; } @@ -20,7 +20,7 @@ } .thread-line { - .status-el::before { + .timeline-item::before { background: $accent_dark; content: ''; position: relative; @@ -53,7 +53,7 @@ } } -.thread-last .status-el::before { +.timeline-item.thread-last::before { background: unset; min-width: unset; width: 0; diff --git a/src/views/profile.nim b/src/views/profile.nim index 8b70b51..7002248 100644 --- a/src/views/profile.nim +++ b/src/views/profile.nim @@ -75,6 +75,12 @@ proc renderBanner(profile: Profile): VNode = a(href=getPicUrl(profile.banner), target="_blank"): 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; photoRail: seq[GalleryPhoto]; prefs: Prefs; path: string): VNode = buildHtml(tdiv(class="profile-tabs")): @@ -88,11 +94,9 @@ proc renderProfile*(profile: Profile; timeline: Timeline; if photoRail.len > 0: renderPhotoRail(profile, photoRail) - tdiv(class="timeline-tab"): - renderTimeline(timeline, profile.username, profile.protected, prefs, path) - -proc renderMulti*(timeline: Timeline; usernames: string; - prefs: Prefs; path: string): VNode = - buildHtml(tdiv(class="multi-timeline")): - tdiv(class="timeline-tab"): - renderTimeline(timeline, usernames, false, prefs, path, multi=true) + tdiv(class="timeline-container"): + if profile.protected: + renderProtected(profile.username) + else: + renderProfileTabs(timeline, profile.username) + renderTimelineTweets(timeline, prefs, path) diff --git a/src/views/status.nim b/src/views/status.nim index 1655e84..ec6af21 100644 --- a/src/views/status.nim +++ b/src/views/status.nim @@ -6,7 +6,7 @@ import tweet proc renderMoreReplies(thread: Thread): VNode = let num = if thread.more != -1: $thread.more & " " else: "" 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"): text $num & "more " & reply diff --git a/src/views/timeline.nim b/src/views/timeline.nim index 7209b1a..eccb16f 100644 --- a/src/views/timeline.nim +++ b/src/views/timeline.nim @@ -1,25 +1,29 @@ import strutils, strformat, sequtils, algorithm, times import karax/[karaxdsl, vdom, vstyles] -import ../types, ../search +import ".."/[types, query, formatters] import tweet, renderutils -proc getQuery(timeline: Timeline): string = - if timeline.query.isNone: "?" - else: genQueryUrl(get(timeline.query)) +proc getQuery(query: Option[Query]): string = + if query.isNone: + 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"] - if timeline.query.isNone or get(timeline.query).kind == multi: + if results.query.isNone or get(results.query).kind == multi: if tab == "posts": classes.add "active" - elif $get(timeline.query).kind == tab: + elif $get(results.query).kind == tab: classes.add "active" return classes.join(" ") -proc renderSearchTabs(timeline: Timeline; username: string): VNode = +proc renderProfileTabs*(timeline: Timeline; username: string): VNode = let link = "/" & username buildHtml(ul(class="tab")): li(class=timeline.getTabClass("posts")): @@ -29,33 +33,28 @@ proc renderSearchTabs(timeline: Timeline; username: string): VNode = li(class=timeline.getTabClass("media")): a(href=(link & "/media")): text "Media" -proc renderNewer(timeline: Timeline; username: string): VNode = - buildHtml(tdiv(class="status-el show-more")): - a(href=("/" & username & getQuery(timeline).strip(chars={'?'}))): - text "Load newest tweets" +proc renderNewer(query: Option[Query]): VNode = + buildHtml(tdiv(class="timeline-item show-more")): + a(href=(getQuery(query).strip(chars={'?', '&'}))): + text "Load newest" -proc renderOlder(timeline: Timeline; username: string): VNode = +proc renderOlder(query: Option[Query]; minId: string): VNode = buildHtml(tdiv(class="show-more")): - a(href=(&"/{username}{getQuery(timeline)}after={timeline.minId}")): - text "Load older tweets" + a(href=(&"{getQuery(query)}after={minId}")): + text "Load older" proc renderNoMore(): VNode = buildHtml(tdiv(class="timeline-footer")): h2(class="timeline-end"): - text "No more tweets." + text "No more items" proc renderNoneFound(): VNode = buildHtml(tdiv(class="timeline-header")): h2(class="timeline-none"): - text "No tweets 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." + text "No items found" 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): renderTweet(threadTweet, prefs, path, class="thread", 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 = 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; - prefs: Prefs; path: string; multi=false): VNode = - buildHtml(tdiv): - if multi: - tdiv(class="multi-header"): - text username.replace(",", " | ") +proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string): VNode = + buildHtml(tdiv(class="timeline")): + if not results.beginning: + renderNewer(results.query) - if not protected: - renderSearchTabs(timeline, username) - if not timeline.beginning: - renderNewer(timeline, username) - - if protected: - renderProtected(username) - elif timeline.content.len == 0: + if results.content.len == 0: renderNoneFound() else: - renderTweets(timeline, prefs, path) - if timeline.hasMore or timeline.query.isSome: - renderOlder(timeline, username) + var threads: seq[string] + for tweet in results.content: + 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: renderNoMore() diff --git a/src/views/tweet.nim b/src/views/tweet.nim index d56e5de..4f49537 100644 --- a/src/views/tweet.nim +++ b/src/views/tweet.nim @@ -224,43 +224,42 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; if not tweet.available: return buildHtml(tdiv(class=divClass)): - tdiv(class="status-el unavailable"): + tdiv(class="timeline-item unavailable"): tdiv(class="unavailable-box"): if tweet.tombstone.len > 0: text tweet.tombstone else: text "This tweet is unavailable" - buildHtml(tdiv(class=divClass)): - tdiv(class="status-el"): - tdiv(class="status-body"): - var views = "" - renderHeader(tweet) + buildHtml(tdiv(class=("timeline-item " & divClass))): + tdiv(class="tweet-body"): + var views = "" + renderHeader(tweet) - if index == 0 and tweet.reply.len > 0: - renderReply(tweet) + if index == 0 and tweet.reply.len > 0: + renderReply(tweet) - tdiv(class="status-content media-body"): - verbatim linkifyText(tweet.text, prefs) + tdiv(class="tweet-content media-body"): + verbatim linkifyText(tweet.text, prefs) - if tweet.quote.isSome: - renderQuote(tweet.quote.get(), prefs) + if tweet.quote.isSome: + renderQuote(tweet.quote.get(), prefs) - if tweet.card.isSome: - renderCard(tweet.card.get(), prefs, path) - elif tweet.photos.len > 0: - renderAlbum(tweet) - elif tweet.video.isSome: - renderVideo(tweet.video.get(), prefs, path) - views = tweet.video.get().views - elif tweet.gif.isSome: - renderGif(tweet.gif.get(), prefs) - elif tweet.poll.isSome: - renderPoll(tweet.poll.get()) + if tweet.card.isSome: + renderCard(tweet.card.get(), prefs, path) + elif tweet.photos.len > 0: + renderAlbum(tweet) + elif tweet.video.isSome: + renderVideo(tweet.video.get(), prefs, path) + views = tweet.video.get().views + elif tweet.gif.isSome: + renderGif(tweet.gif.get(), prefs) + elif tweet.poll.isSome: + renderPoll(tweet.poll.get()) - if not prefs.hideTweetStats: - renderStats(tweet.stats, views) + if not prefs.hideTweetStats: + renderStats(tweet.stats, views) - if tweet.hasThread and "timeline" in class: - a(class="show-thread", href=getLink(tweet)): - text "Show this thread" + if tweet.hasThread and "timeline" in class: + a(class="show-thread", href=getLink(tweet)): + text "Show this thread"