timeline.nim 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import asyncdispatch, strutils, sequtils, uri
  2. import jester
  3. import router_utils
  4. import ".."/[api, prefs, types, utils, cache, formatters, agents, query]
  5. import ../views/[general, profile, timeline, status, search]
  6. include "../views/rss.nimf"
  7. export uri, sequtils
  8. export router_utils
  9. export api, cache, formatters, query, agents
  10. export profile, timeline, status
  11. type ProfileTimeline = (Profile, Timeline, seq[GalleryPhoto])
  12. proc fetchSingleTimeline*(name, after, agent: string;
  13. query: Query): Future[ProfileTimeline] {.async.} =
  14. let railFut = getPhotoRail(name, agent)
  15. var timeline: Timeline
  16. var profile: Profile
  17. var cachedProfile = hasCachedProfile(name)
  18. if cachedProfile.isSome:
  19. profile = get(cachedProfile)
  20. if query.kind == posts:
  21. if cachedProfile.isSome:
  22. timeline = await getTimeline(name, after, agent)
  23. else:
  24. (profile, timeline) = await getProfileAndTimeline(name, agent, after)
  25. cache(profile)
  26. else:
  27. var timelineFut = getSearch[Tweet](query, after, agent)
  28. if cachedProfile.isNone:
  29. profile = await getCachedProfile(name, agent)
  30. timeline = await timelineFut
  31. if profile.username.len == 0: return
  32. return (profile, timeline, await railFut)
  33. proc fetchMultiTimeline*(names: seq[string]; after, agent: string;
  34. query: Query): Future[Timeline] {.async.} =
  35. var q = query
  36. q.fromUser = names
  37. if q.kind == posts and "replies" notin q.excludes:
  38. q.excludes.add "replies"
  39. return await getSearch[Tweet](q, after, agent)
  40. proc showTimeline*(name, after: string; query: Query;
  41. prefs: Prefs; path, title, rss: string): Future[string] {.async.} =
  42. let agent = getAgent()
  43. let names = name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
  44. if names.len == 1:
  45. let (p, t, r) = await fetchSingleTimeline(names[0], after, agent, query)
  46. if p.username.len == 0: return
  47. let pHtml = renderProfile(p, t, r, prefs, path)
  48. return renderMain(pHtml, prefs, title, pageTitle(p), pageDesc(p), path, rss=rss)
  49. else:
  50. let
  51. timeline = await fetchMultiTimeline(names, after, agent, query)
  52. html = renderTweetSearch(timeline, prefs, path)
  53. return renderMain(html, prefs, title, "Multi")
  54. template respTimeline*(timeline: typed) =
  55. if timeline.len == 0:
  56. resp Http404, showError("User \"" & @"name" & "\" not found", cfg.title)
  57. resp timeline
  58. proc createTimelineRouter*(cfg: Config) =
  59. setProfileCacheTime(cfg.profileCacheTime)
  60. router timeline:
  61. get "/@name/?":
  62. cond '.' notin @"name"
  63. let rss = "/$1/rss" % @"name"
  64. respTimeline(await showTimeline(@"name", @"after", Query(), cookiePrefs(),
  65. getPath(), cfg.title, rss))
  66. get "/@name/search":
  67. cond '.' notin @"name"
  68. let query = initQuery(params(request), name=(@"name"))
  69. respTimeline(await showTimeline(@"name", @"after", query, cookiePrefs(),
  70. getPath(), cfg.title, ""))
  71. get "/@name/replies":
  72. cond '.' notin @"name"
  73. let rss = "/$1/replies/rss" % @"name"
  74. respTimeline(await showTimeline(@"name", @"after", getReplyQuery(@"name"),
  75. cookiePrefs(), getPath(), cfg.title, rss))
  76. get "/@name/media":
  77. cond '.' notin @"name"
  78. let rss = "/$1/media/rss" % @"name"
  79. respTimeline(await showTimeline(@"name", @"after", getMediaQuery(@"name"),
  80. cookiePrefs(), getPath(), cfg.title, rss))
  81. get "/@name/status/@id":
  82. cond '.' notin @"name"
  83. let prefs = cookiePrefs()
  84. let conversation = await getTweet(@"name", @"id", getAgent())
  85. if conversation == nil or conversation.tweet.id.len == 0:
  86. if conversation != nil and conversation.tweet.tombstone.len > 0:
  87. resp Http404, showError(conversation.tweet.tombstone, cfg.title)
  88. else:
  89. resp Http404, showError("Tweet not found", cfg.title)
  90. let path = getPath()
  91. let title = pageTitle(conversation.tweet.profile)
  92. let desc = conversation.tweet.text
  93. let html = renderConversation(conversation, prefs, path)
  94. if conversation.tweet.video.isSome():
  95. let thumb = get(conversation.tweet.video).thumb
  96. let vidUrl = getVideoEmbed(conversation.tweet.id)
  97. resp renderMain(html, prefs, cfg.title, title, desc, path, images = @[thumb],
  98. `type`="video", video=vidUrl)
  99. elif conversation.tweet.gif.isSome():
  100. let thumb = get(conversation.tweet.gif).thumb
  101. let vidUrl = getVideoEmbed(conversation.tweet.id)
  102. resp renderMain(html, prefs, cfg.title, title, desc, path, images = @[thumb],
  103. `type`="video", video=vidUrl)
  104. else:
  105. resp renderMain(html, prefs, cfg.title, title, desc, path,
  106. images=conversation.tweet.photos, `type`="photo")
  107. get "/i/web/status/@id":
  108. redirect("/i/status/" & @"id")