Make queries non-optional
This commit is contained in:
		
							parent
							
								
									53c6247d8b
								
							
						
					
					
						commit
						bd774cf0ca
					
				| 
						 | 
				
			
			@ -9,7 +9,7 @@ proc getResult[T](json: JsonNode; query: Query; after: string): Result[T] =
 | 
			
		|||
    hasMore: json["has_more_items"].to(bool),
 | 
			
		||||
    maxId: json.getOrDefault("max_position").getStr(""),
 | 
			
		||||
    minId: json.getOrDefault("min_position").getStr("").cleanPos(),
 | 
			
		||||
    query: some query,
 | 
			
		||||
    query: query,
 | 
			
		||||
    beginning: after.len == 0
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,14 +42,14 @@ proc getSearch*[T](query: Query; after, agent: string): Future[Result[T]] {.asyn
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
  let json = await fetchJson(base / searchUrl ? params, headers)
 | 
			
		||||
  if json == nil: return Result[T](query: some query)
 | 
			
		||||
  if json == nil: return Result[T](query: query)
 | 
			
		||||
 | 
			
		||||
  result = getResult[T](json, query, after)
 | 
			
		||||
  if not json.hasKey("items_html"): return
 | 
			
		||||
  let html = parseHtml(json["items_html"].to(string))
 | 
			
		||||
 | 
			
		||||
  when T is Tweet:
 | 
			
		||||
    result = await finishTimeline(json, some query, after, agent)
 | 
			
		||||
    result = await finishTimeline(json, query, after, agent)
 | 
			
		||||
  elif T is Profile:
 | 
			
		||||
    result.hasMore = json["items_html"].to(string) != "\n"
 | 
			
		||||
    for p in html.selectAll(".js-stream-item"):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import sequtils, strutils, json, xmltree, uri
 | 
			
		|||
import ".."/[types, parser, parserutils, formatters, query]
 | 
			
		||||
import utils, consts, media
 | 
			
		||||
 | 
			
		||||
proc finishTimeline*(json: JsonNode; query: Option[Query]; after, agent: string): Future[Timeline] {.async.} =
 | 
			
		||||
proc finishTimeline*(json: JsonNode; query: Query; after, agent: string): Future[Timeline] {.async.} =
 | 
			
		||||
  if json == nil: return Timeline()
 | 
			
		||||
 | 
			
		||||
  result = Timeline(
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ proc getTimeline*(username, after, agent: string): Future[Timeline] {.async.} =
 | 
			
		|||
    params.add {"max_position": after}
 | 
			
		||||
 | 
			
		||||
  let json = await fetchJson(base / (timelineUrl % username) ? params, headers)
 | 
			
		||||
  result = await finishTimeline(json, none Query, after, agent)
 | 
			
		||||
  result = await finishTimeline(json, Query(), after, agent)
 | 
			
		||||
 | 
			
		||||
proc getProfileAndTimeline*(username, agent, after: string): Future[(Profile, Timeline)] {.async.} =
 | 
			
		||||
  let headers = newHttpHeaders({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ proc parseConversation*(node: XmlNode): Conversation =
 | 
			
		|||
      result.replies.add parseThread(thread)
 | 
			
		||||
 | 
			
		||||
proc parseTimeline*(node: XmlNode; after: string): Timeline =
 | 
			
		||||
  if node == nil: return
 | 
			
		||||
  if node == nil: return Timeline()
 | 
			
		||||
  result = Timeline(
 | 
			
		||||
    content: parseThread(node.select(".stream > .stream-items")).content,
 | 
			
		||||
    minId: node.attr("data-min-position"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ proc genQueryUrl*(query: Query): string =
 | 
			
		|||
  if query.fromUser.len > 0:
 | 
			
		||||
    result = "/" & query.fromUser.join(",")
 | 
			
		||||
 | 
			
		||||
  if query.kind == multi:
 | 
			
		||||
  if query.fromUser.len > 1:
 | 
			
		||||
    return result & "?"
 | 
			
		||||
 | 
			
		||||
  if query.kind notin {custom, users}:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ import ../views/general
 | 
			
		|||
 | 
			
		||||
include "../views/rss.nimf"
 | 
			
		||||
 | 
			
		||||
proc showRss*(name: string; query: Option[Query]): Future[string] {.async.} =
 | 
			
		||||
proc showRss*(name: string; query: Query): Future[string] {.async.} =
 | 
			
		||||
  let (profile, timeline, _) = await fetchSingleTimeline(name, "", getAgent(), query)
 | 
			
		||||
  return renderTimelineRss(timeline.content, profile)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -21,12 +21,12 @@ proc createRssRouter*(cfg: Config) =
 | 
			
		|||
  router rss:
 | 
			
		||||
    get "/@name/rss":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      respRss(await showRss(@"name", none Query))
 | 
			
		||||
      respRss(await showRss(@"name", Query()))
 | 
			
		||||
 | 
			
		||||
    get "/@name/replies/rss":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      respRss(await showRss(@"name", some getReplyQuery(@"name")))
 | 
			
		||||
      respRss(await showRss(@"name", getReplyQuery(@"name")))
 | 
			
		||||
 | 
			
		||||
    get "/@name/media/rss":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      respRss(await showRss(@"name", some getMediaQuery(@"name")))
 | 
			
		||||
      respRss(await showRss(@"name", getMediaQuery(@"name")))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ export profile, timeline, status
 | 
			
		|||
type ProfileTimeline = (Profile, Timeline, seq[GalleryPhoto])
 | 
			
		||||
 | 
			
		||||
proc fetchSingleTimeline*(name, after, agent: string;
 | 
			
		||||
                          query: Option[Query]): Future[ProfileTimeline] {.async.} =
 | 
			
		||||
                          query: Query): Future[ProfileTimeline] {.async.} =
 | 
			
		||||
  let railFut = getPhotoRail(name, agent)
 | 
			
		||||
 | 
			
		||||
  var timeline: Timeline
 | 
			
		||||
| 
						 | 
				
			
			@ -26,14 +26,14 @@ proc fetchSingleTimeline*(name, after, agent: string;
 | 
			
		|||
  if cachedProfile.isSome:
 | 
			
		||||
    profile = get(cachedProfile)
 | 
			
		||||
 | 
			
		||||
  if query.isNone:
 | 
			
		||||
  if query.kind == posts:
 | 
			
		||||
    if cachedProfile.isSome:
 | 
			
		||||
      timeline = await getTimeline(name, after, agent)
 | 
			
		||||
    else:
 | 
			
		||||
      (profile, timeline) = await getProfileAndTimeline(name, agent, after)
 | 
			
		||||
      cache(profile)
 | 
			
		||||
  else:
 | 
			
		||||
    var timelineFut = getSearch[Tweet](get(query), after, agent)
 | 
			
		||||
    var timelineFut = getSearch[Tweet](query, after, agent)
 | 
			
		||||
    if cachedProfile.isNone:
 | 
			
		||||
      profile = await getCachedProfile(name, agent)
 | 
			
		||||
    timeline = await timelineFut
 | 
			
		||||
| 
						 | 
				
			
			@ -42,16 +42,14 @@ proc fetchSingleTimeline*(name, after, agent: string;
 | 
			
		|||
  return (profile, timeline, await railFut)
 | 
			
		||||
 | 
			
		||||
proc fetchMultiTimeline*(names: seq[string]; after, agent: string;
 | 
			
		||||
                         query: Option[Query]): Future[Timeline] {.async.} =
 | 
			
		||||
                         query: Query): Future[Timeline] {.async.} =
 | 
			
		||||
  var q = query
 | 
			
		||||
  if q.isSome:
 | 
			
		||||
    get(q).fromUser = names
 | 
			
		||||
  else:
 | 
			
		||||
    q = some Query(kind: multi, fromUser: names, excludes: @["replies"])
 | 
			
		||||
  q.fromUser = names
 | 
			
		||||
  if q.kind == posts and "replies" notin q.excludes:
 | 
			
		||||
    q.excludes.add "replies"
 | 
			
		||||
  return await getSearch[Tweet](q, after, agent)
 | 
			
		||||
 | 
			
		||||
  return await getSearch[Tweet](get(q), after, agent)
 | 
			
		||||
 | 
			
		||||
proc showTimeline*(name, after: string; query: Option[Query];
 | 
			
		||||
proc showTimeline*(name, after: string; query: Query;
 | 
			
		||||
                   prefs: Prefs; path, title, rss: string): Future[string] {.async.} =
 | 
			
		||||
  let agent = getAgent()
 | 
			
		||||
  let names = name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -79,25 +77,25 @@ proc createTimelineRouter*(cfg: Config) =
 | 
			
		|||
    get "/@name/?":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      let rss = "/$1/rss" % @"name"
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", none Query, cookiePrefs(),
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", Query(), cookiePrefs(),
 | 
			
		||||
                                      getPath(), cfg.title, rss))
 | 
			
		||||
 | 
			
		||||
    get "/@name/search":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      let query = some initQuery(params(request), name=(@"name"))
 | 
			
		||||
      let query = initQuery(params(request), name=(@"name"))
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", query, cookiePrefs(),
 | 
			
		||||
                                      getPath(), cfg.title, ""))
 | 
			
		||||
 | 
			
		||||
    get "/@name/replies":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      let rss = "/$1/replies/rss" % @"name"
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", some getReplyQuery(@"name"),
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", getReplyQuery(@"name"),
 | 
			
		||||
                                      cookiePrefs(), getPath(), cfg.title, rss))
 | 
			
		||||
 | 
			
		||||
    get "/@name/media":
 | 
			
		||||
      cond '.' notin @"name"
 | 
			
		||||
      let rss = "/$1/media/rss" % @"name"
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", some getMediaQuery(@"name"),
 | 
			
		||||
      respTimeline(await showTimeline(@"name", @"after", getMediaQuery(@"name"),
 | 
			
		||||
                                      cookiePrefs(), getPath(), cfg.title, rss))
 | 
			
		||||
 | 
			
		||||
    get "/@name/status/@id":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ dbFromTypes("cache.db", "", "", "", [Profile, Video])
 | 
			
		|||
 | 
			
		||||
type
 | 
			
		||||
  QueryKind* = enum
 | 
			
		||||
    posts, replies, media, multi, users, custom
 | 
			
		||||
    posts, replies, media, users, custom
 | 
			
		||||
 | 
			
		||||
  Query* = object
 | 
			
		||||
    kind*: QueryKind
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +74,7 @@ type
 | 
			
		|||
    maxId*: string
 | 
			
		||||
    hasMore*: bool
 | 
			
		||||
    beginning*: bool
 | 
			
		||||
    query*: Option[Query]
 | 
			
		||||
    query*: Query
 | 
			
		||||
 | 
			
		||||
  Gif* = object
 | 
			
		||||
    url*: string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,9 +83,7 @@ proc renderProtected(username: string): VNode =
 | 
			
		|||
 | 
			
		||||
proc renderProfile*(profile: Profile; timeline: Timeline;
 | 
			
		||||
                    photoRail: seq[GalleryPhoto]; prefs: Prefs; path: string): VNode =
 | 
			
		||||
  if timeline.query.isNone:
 | 
			
		||||
    timeline.query = some Query(fromUser: @[profile.username])
 | 
			
		||||
 | 
			
		||||
  timeline.query.fromUser = @[profile.username]
 | 
			
		||||
  buildHtml(tdiv(class="profile-tabs")):
 | 
			
		||||
    if not prefs.hideBanner:
 | 
			
		||||
      tdiv(class="profile-banner"):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,36 +27,31 @@ proc renderSearch*(): VNode =
 | 
			
		|||
        input(`type`="text", name="text", autofocus="", placeholder="Enter username...")
 | 
			
		||||
        button(`type`="submit"): icon "search"
 | 
			
		||||
 | 
			
		||||
proc getTabClass(query: Option[Query]; tab: string): string =
 | 
			
		||||
proc getTabClass(query: Query; tab: QueryKind): string =
 | 
			
		||||
  var classes = @["tab-item"]
 | 
			
		||||
 | 
			
		||||
  if query.isNone or get(query).kind == multi:
 | 
			
		||||
    if tab == "posts":
 | 
			
		||||
      classes.add "active"
 | 
			
		||||
  elif $get(query).kind == tab:
 | 
			
		||||
  if query.kind == tab:
 | 
			
		||||
    classes.add "active"
 | 
			
		||||
 | 
			
		||||
  return classes.join(" ")
 | 
			
		||||
 | 
			
		||||
proc renderProfileTabs*(query: Option[Query]; username: string): VNode =
 | 
			
		||||
proc renderProfileTabs*(query: Query; username: string): VNode =
 | 
			
		||||
  let link = "/" & username
 | 
			
		||||
  buildHtml(ul(class="tab")):
 | 
			
		||||
    li(class=query.getTabClass("posts")):
 | 
			
		||||
    li(class=query.getTabClass(posts)):
 | 
			
		||||
      a(href=link): text "Tweets"
 | 
			
		||||
    li(class=query.getTabClass("replies")):
 | 
			
		||||
    li(class=query.getTabClass(replies)):
 | 
			
		||||
      a(href=(link & "/replies")): text "Tweets & Replies"
 | 
			
		||||
    li(class=query.getTabClass("media")):
 | 
			
		||||
    li(class=query.getTabClass(media)):
 | 
			
		||||
      a(href=(link & "/media")): text "Media"
 | 
			
		||||
    li(class=query.getTabClass("custom")):
 | 
			
		||||
    li(class=query.getTabClass(custom)):
 | 
			
		||||
      a(href=(link & "/search")): text "Custom"
 | 
			
		||||
 | 
			
		||||
proc renderSearchTabs*(query: Option[Query]): VNode =
 | 
			
		||||
  var q = if query.isSome: get(query) else: Query()
 | 
			
		||||
proc renderSearchTabs*(query: Query): VNode =
 | 
			
		||||
  var q = query
 | 
			
		||||
  buildHtml(ul(class="tab")):
 | 
			
		||||
    li(class=query.getTabClass("custom")):
 | 
			
		||||
    li(class=query.getTabClass(custom)):
 | 
			
		||||
      q.kind = custom
 | 
			
		||||
      a(href=genQueryUrl(q)): text "Tweets"
 | 
			
		||||
    li(class=query.getTabClass("users")):
 | 
			
		||||
    li(class=query.getTabClass(users)):
 | 
			
		||||
      q.kind = users
 | 
			
		||||
      a(href=genQueryUrl(q)): text "Users"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,10 +76,7 @@ proc renderSearchPanel*(query: Query): VNode =
 | 
			
		|||
            genCheckbox(&"{f[0]}-{k}", v, state)
 | 
			
		||||
 | 
			
		||||
proc renderTweetSearch*(tweets: Result[Tweet]; prefs: Prefs; path: string): VNode =
 | 
			
		||||
  let query =
 | 
			
		||||
    if tweets.query.isSome: get(tweets.query)
 | 
			
		||||
    else: Query(kind: custom)
 | 
			
		||||
 | 
			
		||||
  let query = tweets.query
 | 
			
		||||
  buildHtml(tdiv(class="timeline-container")):
 | 
			
		||||
    if query.fromUser.len > 1:
 | 
			
		||||
      tdiv(class="timeline-header"):
 | 
			
		||||
| 
						 | 
				
			
			@ -94,22 +86,18 @@ proc renderTweetSearch*(tweets: Result[Tweet]; prefs: Prefs; path: string): VNod
 | 
			
		|||
        renderSearchPanel(query)
 | 
			
		||||
 | 
			
		||||
    if query.fromUser.len > 0:
 | 
			
		||||
      renderProfileTabs(tweets.query, query.fromUser.join(","))
 | 
			
		||||
      renderProfileTabs(query, query.fromUser.join(","))
 | 
			
		||||
    else:
 | 
			
		||||
      renderSearchTabs(tweets.query)
 | 
			
		||||
      renderSearchTabs(query)
 | 
			
		||||
 | 
			
		||||
    renderTimelineTweets(tweets, prefs, path)
 | 
			
		||||
 | 
			
		||||
proc renderUserSearch*(users: Result[Profile]; prefs: Prefs): VNode =
 | 
			
		||||
  let searchText =
 | 
			
		||||
    if users.query.isSome: get(users.query).text
 | 
			
		||||
    else: ""
 | 
			
		||||
 | 
			
		||||
  buildHtml(tdiv(class="timeline-container")):
 | 
			
		||||
    tdiv(class="timeline-header"):
 | 
			
		||||
      form(`method`="get", action="/search", class="search-field"):
 | 
			
		||||
        hiddenField("kind", "users")
 | 
			
		||||
        genInput("text", "", searchText, "Enter username...", class="pref-inline")
 | 
			
		||||
        genInput("text", "", users.query.text, "Enter username...", class="pref-inline")
 | 
			
		||||
        button(`type`="submit"): icon "search"
 | 
			
		||||
 | 
			
		||||
    renderSearchTabs(users.query)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,20 +4,20 @@ import karax/[karaxdsl, vdom, vstyles]
 | 
			
		|||
import ".."/[types, query, formatters]
 | 
			
		||||
import tweet, renderutils
 | 
			
		||||
 | 
			
		||||
proc getQuery(query: Option[Query]): string =
 | 
			
		||||
  if query.isNone:
 | 
			
		||||
proc getQuery(query: Query): string =
 | 
			
		||||
  if query.kind == posts:
 | 
			
		||||
    result = "?"
 | 
			
		||||
  else:
 | 
			
		||||
    result = genQueryUrl(get(query))
 | 
			
		||||
    result = genQueryUrl(query)
 | 
			
		||||
    if result[^1] != '?':
 | 
			
		||||
      result &= "&"
 | 
			
		||||
 | 
			
		||||
proc renderNewer(query: Option[Query]): VNode =
 | 
			
		||||
proc renderNewer(query: Query): VNode =
 | 
			
		||||
  buildHtml(tdiv(class="timeline-item show-more")):
 | 
			
		||||
    a(href=(getQuery(query).strip(chars={'?', '&'}))):
 | 
			
		||||
      text "Load newest"
 | 
			
		||||
 | 
			
		||||
proc renderOlder(query: Option[Query]; minId: string): VNode =
 | 
			
		||||
proc renderOlder(query: Query; minId: string): VNode =
 | 
			
		||||
  buildHtml(tdiv(class="show-more")):
 | 
			
		||||
    a(href=(&"{getQuery(query)}after={minId}")):
 | 
			
		||||
      text "Load older"
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ proc renderTimelineTweets*(results: Result[Tweet]; prefs: Prefs; path: string):
 | 
			
		|||
          renderThread(thread, prefs, path)
 | 
			
		||||
          threads &= tweet.threadId
 | 
			
		||||
 | 
			
		||||
      if results.hasMore or results.query.isSome:
 | 
			
		||||
      if results.hasMore or results.query.kind != posts:
 | 
			
		||||
        renderOlder(results.query, results.minId)
 | 
			
		||||
      else:
 | 
			
		||||
        renderNoMore()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue