timeline.nim 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # SPDX-License-Identifier: AGPL-3.0-only
  2. import strutils, strformat, algorithm, uri, options
  3. import karax/[karaxdsl, vdom]
  4. import ".."/[types, query, formatters]
  5. import tweet, renderutils
  6. proc getQuery(query: Query): string =
  7. if query.kind != posts:
  8. result = genQueryUrl(query)
  9. if result.len > 0:
  10. result &= "&"
  11. proc renderToTop*(focus="#"): VNode =
  12. buildHtml(tdiv(class="top-ref")):
  13. icon "down", href=focus
  14. proc renderNewer*(query: Query; path: string; focus=""): VNode =
  15. let
  16. q = genQueryUrl(query)
  17. url = if q.len > 0: "?" & q else: ""
  18. p = if focus.len > 0: path.replace("#m", focus) else: path
  19. buildHtml(tdiv(class="timeline-item show-more")):
  20. a(href=(p & url)):
  21. text "Load newest"
  22. proc renderMore*(query: Query; cursor: string; focus=""): VNode =
  23. buildHtml(tdiv(class="show-more")):
  24. a(href=(&"?{getQuery(query)}cursor={encodeUrl(cursor, usePlus=false)}{focus}")):
  25. text "Load more"
  26. proc renderNoMore(): VNode =
  27. buildHtml(tdiv(class="timeline-footer")):
  28. h2(class="timeline-end"):
  29. text "No more items"
  30. proc renderNoneFound(): VNode =
  31. buildHtml(tdiv(class="timeline-header")):
  32. h2(class="timeline-none"):
  33. text "No items found"
  34. proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode =
  35. buildHtml(tdiv(class="thread-line")):
  36. let sortedThread = thread.sortedByIt(it.id)
  37. for i, tweet in sortedThread:
  38. # thread has a gap, display "more replies" link
  39. if i > 0 and tweet.replyId != sortedThread[i - 1].id:
  40. tdiv(class="timeline-item thread more-replies-thread"):
  41. tdiv(class="more-replies"):
  42. a(class="more-replies-text", href=getLink(tweet)):
  43. text "more replies"
  44. let show = i == thread.high and sortedThread[0].id != tweet.threadId
  45. let header = if tweet.pinned or tweet.retweet.isSome: "with-header " else: ""
  46. renderTweet(tweet, prefs, path, class=(header & "thread"),
  47. index=i, last=(i == thread.high), showThread=show)
  48. proc renderUser(user: User; prefs: Prefs): VNode =
  49. buildHtml(tdiv(class="timeline-item")):
  50. a(class="tweet-link", href=("/" & user.username))
  51. tdiv(class="tweet-body profile-result"):
  52. tdiv(class="tweet-header"):
  53. a(class="tweet-avatar", href=("/" & user.username)):
  54. genImg(user.getUserPic("_bigger"), class=prefs.getAvatarClass)
  55. tdiv(class="tweet-name-row"):
  56. tdiv(class="fullname-and-username"):
  57. linkUser(user, class="fullname")
  58. linkUser(user, class="username")
  59. tdiv(class="tweet-content media-body", dir="auto"):
  60. verbatim replaceUrls(user.bio, prefs)
  61. proc renderTimelineUsers*(results: Result[User]; prefs: Prefs; path=""): VNode =
  62. buildHtml(tdiv(class="timeline")):
  63. if not results.beginning:
  64. renderNewer(results.query, path)
  65. if results.content.len > 0:
  66. for user in results.content:
  67. renderUser(user, prefs)
  68. if results.bottom.len > 0:
  69. renderMore(results.query, results.bottom)
  70. renderToTop()
  71. elif results.beginning:
  72. renderNoneFound()
  73. else:
  74. renderNoMore()
  75. proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string;
  76. pinned=none(Tweet)): VNode =
  77. buildHtml(tdiv(class="timeline")):
  78. if not results.beginning:
  79. renderNewer(results.query, parseUri(path).path)
  80. if not prefs.hidePins and pinned.isSome:
  81. let tweet = get pinned
  82. renderTweet(tweet, prefs, path, showThread=tweet.hasThread)
  83. if results.content.len == 0:
  84. if not results.beginning:
  85. renderNoMore()
  86. else:
  87. renderNoneFound()
  88. else:
  89. var retweets: seq[int64]
  90. for thread in results.content:
  91. if thread.len == 1:
  92. let
  93. tweet = thread[0]
  94. retweetId = if tweet.retweet.isSome: get(tweet.retweet).id else: 0
  95. if retweetId in retweets or tweet.id in retweets or
  96. tweet.pinned and prefs.hidePins:
  97. continue
  98. var hasThread = tweet.hasThread
  99. if retweetId != 0 and tweet.retweet.isSome:
  100. retweets &= retweetId
  101. hasThread = get(tweet.retweet).hasThread
  102. renderTweet(tweet, prefs, path, showThread=hasThread)
  103. else:
  104. renderThread(thread, prefs, path)
  105. if results.bottom.len > 0:
  106. renderMore(results.query, results.bottom)
  107. renderToTop()