Преглед изворни кода

Optimize usage of cookie preferences

Zed пре 6 година
родитељ
комит
db45433ec4

+ 3 - 3
src/nitter.nim

@@ -7,7 +7,7 @@ import types, config, prefs, formatters, redis_cache, tokens
 import views/[general, about]
 import routes/[
   preferences, timeline, status, media, search, rss, list,
-  unsupported, embed, resolver]
+  unsupported, embed, resolver, router_utils]
 
 const configPath {.strdefine.} = "./nitter.conf"
 let (cfg, fullCfg) = getConfig(configPath)
@@ -49,10 +49,10 @@ settings:
 
 routes:
   get "/":
-    resp renderMain(renderSearch(), request, cfg)
+    resp renderMain(renderSearch(), request, cfg, themePrefs())
 
   get "/about":
-    resp renderMain(renderAbout(), request, cfg)
+    resp renderMain(renderAbout(), request, cfg, themePrefs())
 
   get "/explore":
     redirect("/about")

+ 7 - 1
src/prefs.nim

@@ -12,4 +12,10 @@ proc updateDefaultPrefs*(cfg: parsecfg.Config) =
 
 proc getPrefs*(cookies: Table[string, string]): Prefs =
   result = defaultPrefs
-  genCookiePrefs()
+  genCookiePrefs(cookies)
+
+template getPref*(cookies: Table[string, string], pref): untyped =
+  bind genCookiePref
+  var res = defaultPrefs.`pref`
+  genCookiePref(cookies, pref, res)
+  res

+ 26 - 5
src/prefs_impl.nim

@@ -120,9 +120,8 @@ macro genDefaultPrefs*(): untyped =
     result.add quote do:
       defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`)
 
-macro genCookiePrefs*(): untyped =
+macro genCookiePrefs*(cookies): untyped =
   result = nnkStmtList.newTree()
-  let cookies = ident("cookies")
   for pref in allPrefs():
     let
       name = pref.name
@@ -132,14 +131,36 @@ macro genCookiePrefs*(): untyped =
 
     result.add quote do:
       if `name` in `cookies`:
-        let value = `cookies`[`name`]
         when `kind` == input or `name` == "theme":
-          result.`ident` = value
+          result.`ident` = `cookies`[`name`]
         elif `kind` == checkbox:
-          result.`ident` = value == "on"
+          result.`ident` = `cookies`[`name`] == "on"
         else:
+          let value = `cookies`[`name`]
           if value in `options`: result.`ident` = value
 
+macro genCookiePref*(cookies, prefName, res): untyped =
+  result = nnkStmtList.newTree()
+  for pref in allPrefs():
+    let ident = ident(pref.name)
+    if ident != prefName:
+      continue
+
+    let
+      name = pref.name
+      kind = newLit(pref.kind)
+      options = pref.options
+
+    result.add quote do:
+      if `name` in `cookies`:
+        when `kind` == input or `name` == "theme":
+          `res` = `cookies`[`name`]
+        elif `kind` == checkbox:
+          `res` = `cookies`[`name`] == "on"
+        else:
+          let value = `cookies`[`name`]
+          if value in `options`: `res` = value
+
 macro genUpdatePrefs*(): untyped =
   result = nnkStmtList.newTree()
   let req = ident("request")

+ 5 - 3
src/routes/list.nim

@@ -15,7 +15,7 @@ template respList*(list, timeline, vnode: typed) =
     html = renderList(vnode, timeline.query, list)
     rss = "/$1/lists/$2/rss" % [@"name", @"list"]
 
-  resp renderMain(html, request, cfg, rss=rss)
+  resp renderMain(html, request, cfg, prefs, rss=rss)
 
 proc createListRouter*(cfg: Config) =
   router list:
@@ -23,18 +23,20 @@ proc createListRouter*(cfg: Config) =
       cond '.' notin @"name"
       cond @"name" != "i"
       let
+        prefs = cookiePrefs()
         list = await getCachedList(@"name", @"list")
         timeline = await getListTimeline(list.id, getCursor())
-        vnode = renderTimelineTweets(timeline, cookiePrefs(), request.path)
+        vnode = renderTimelineTweets(timeline, prefs, request.path)
       respList(list, timeline, vnode)
 
     get "/@name/lists/@list/members":
       cond '.' notin @"name"
       cond @"name" != "i"
       let
+        prefs = cookiePrefs()
         list = await getCachedList(@"name", @"list")
         members = await getListMembers(list)
-      respList(list, members, renderTimelineUsers(members, cookiePrefs(), request.path))
+      respList(list, members, renderTimelineUsers(members, prefs, request.path))
 
     get "/i/lists/@id/?":
       cond '.' notin @"id"

+ 9 - 6
src/routes/media.nim

@@ -5,7 +5,6 @@ import jester
 
 import router_utils
 import ".."/[types, formatters, agents, utils]
-import ../views/general
 
 export asynchttpserver, asyncstreams, asyncfile, asyncnet
 export httpclient, os, strutils, asyncstreams, base64, re
@@ -16,6 +15,12 @@ const
 
 let mediaAgent* = getAgent()
 
+proc safeFetch*(url, agent: string): Future[string] {.async.} =
+  let client = newAsyncHttpClient(userAgent=agent)
+  try: result = await client.getContent(url)
+  except: discard
+  finally: client.close()
+
 template respond*(req: asynchttpserver.Request; headers) =
   var msg = "HTTP/1.1 200 OK\c\L"
   for k, v in headers:
@@ -57,7 +62,7 @@ proc proxyMedia*(req: jester.Request; url: string): Future[HttpCode] {.async.} =
   except HttpRequestError, ProtocolError, OSError:
     result = Http404
   finally:
-    client.safeClose()
+    client.close()
 
 template check*(code): untyped =
   if code != Http200:
@@ -111,9 +116,7 @@ proc createMediaRouter*(cfg: Config) =
           resp Http404
 
       if ".m3u8" in url:
-        let
-          vid = await safeFetch(url, mediaAgent)
-          prefs = cookiePrefs()
-        content = proxifyVideo(vid, prefs.proxyVideos)
+        let vid = await safeFetch(url, mediaAgent)
+        content = proxifyVideo(vid, cookiePref(proxyVideos))
 
       resp content, m3u8Mime

+ 4 - 2
src/routes/preferences.nim

@@ -17,8 +17,10 @@ proc findThemes*(dir: string): seq[string] =
 proc createPrefRouter*(cfg: Config) =
   router preferences:
     get "/settings":
-      let html = renderPreferences(cookiePrefs(), refPath(), findThemes(cfg.staticDir))
-      resp renderMain(html, request, cfg, "Preferences")
+      let
+        prefs = cookiePrefs()
+        html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir))
+      resp renderMain(html, request, cfg, prefs, "Preferences")
 
     get "/settings/@i?":
       redirect("/settings")

+ 17 - 14
src/routes/router_utils.nim

@@ -1,7 +1,9 @@
-import strutils, sequtils, asyncdispatch, httpclient, uri
-from jester import Request
-import ".."/[utils, prefs]
-export utils, prefs
+import strutils, sequtils, uri, tables
+from jester import Request, cookies
+
+import ../views/general
+import ".."/[utils, prefs, types]
+export utils, prefs, types
 
 template savePref*(pref, value: string; req: Request; expire=false) =
   if not expire or pref in cookies(req):
@@ -11,6 +13,17 @@ template savePref*(pref, value: string; req: Request; expire=false) =
 template cookiePrefs*(): untyped {.dirty.} =
   getPrefs(cookies(request))
 
+template cookiePref*(pref): untyped {.dirty.} =
+  getPref(cookies(request), pref)
+
+template themePrefs*(): Prefs =
+  var res = defaultPrefs
+  res.theme = cookiePref(theme)
+  res
+
+template showError*(error: string; cfg: Config): string =
+  renderMain(renderError(error), request, cfg, themePrefs(), "Error")
+
 template getPath*(): untyped {.dirty.} =
   $(parseUri(request.path) ? filterParams(request.params))
 
@@ -28,13 +41,3 @@ template getCursor*(req: Request): string =
 
 proc getNames*(name: string): seq[string] =
   name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
-
-proc safeClose*(client: AsyncHttpClient) =
-  try: client.close()
-  except: discard
-
-proc safeFetch*(url, agent: string): Future[string] {.async.} =
-  let client = newAsyncHttpClient(userAgent=agent)
-  try: result = await client.getContent(url)
-  except: discard
-  finally: client.safeClose()

+ 1 - 1
src/routes/rss.nim

@@ -3,7 +3,7 @@ import asyncdispatch, strutils, tables, times, sequtils, hashes, snappy
 import jester
 
 import router_utils, timeline
-import ../query, ../views/general
+import ../query
 
 include "../views/rss.nimf"
 

+ 8 - 6
src/routes/search.nim

@@ -16,20 +16,22 @@ proc createSearchRouter*(cfg: Config) =
       if @"q".len > 200:
         resp Http400, showError("Search input too long.", cfg)
 
-      let prefs = cookiePrefs()
-      let query = initQuery(params(request))
+      let
+        prefs = cookiePrefs()
+        query = initQuery(params(request))
 
       case query.kind
       of users:
         if "," in @"q":
           redirect("/" & @"q")
         let users = await getSearch[Profile](query, getCursor())
-        resp renderMain(renderUserSearch(users, prefs), request, cfg)
+        resp renderMain(renderUserSearch(users, prefs), request, cfg, prefs)
       of tweets:
-        let tweets = await getSearch[Tweet](query, getCursor())
-        let rss = "/search/rss?" & genQueryUrl(query)
+        let
+          tweets = await getSearch[Tweet](query, getCursor())
+          rss = "/search/rss?" & genQueryUrl(query)
         resp renderMain(renderTweetSearch(tweets, prefs, getPath()),
-                        request, cfg, rss=rss)
+                        request, cfg, prefs, rss=rss)
       else:
         resp Http404, showError("Invalid search", cfg)
 

+ 2 - 2
src/routes/status.nim

@@ -48,8 +48,8 @@ proc createStatusRouter*(cfg: Config) =
         video = getPicUrl(get(conv.tweet.gif).url)
 
       let html = renderConversation(conv, prefs, getPath() & "#m")
-      resp renderMain(html, request, cfg, title, desc,
-                      images=images, video=video, ogTitle=ogTitle)
+      resp renderMain(html, request, cfg, prefs, title, desc, ogTitle,
+                      images=images, video=video)
 
     get "/@name/@s/@id/@m/?@i?":
       cond @"s" in ["status", "statuses"]

+ 2 - 2
src/routes/timeline.nim

@@ -78,7 +78,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
     let
       timeline = await getSearch[Tweet](query, after)
       html = renderTweetSearch(timeline, prefs, getPath())
-    return renderMain(html, request, cfg, "Multi", rss=rss)
+    return renderMain(html, request, cfg, prefs, "Multi", rss=rss)
 
   var (p, t, r) = await fetchSingleTimeline(after, query)
 
@@ -86,7 +86,7 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
   if p.id.len == 0: return
 
   let pHtml = renderProfile(p, t, r, prefs, getPath())
-  result = renderMain(pHtml, request, cfg, pageTitle(p), pageDesc(p),
+  result = renderMain(pHtml, request, cfg, prefs, pageTitle(p), pageDesc(p),
                       rss=rss, images = @[p.getUserpic("_200x200")])
 
 template respTimeline*(timeline: typed) =

+ 1 - 1
src/routes/unsupported.nim

@@ -7,7 +7,7 @@ import ../views/[general, about]
 proc createUnsupportedRouter*(cfg: Config) =
   router unsupported:
     template feature {.dirty.} =
-      resp renderMain(renderFeature(), request, cfg)
+      resp renderMain(renderFeature(), request, cfg, themePrefs())
 
     get "/about/feature": feature()
     get "/intent/?@i?": feature()

+ 3 - 6
src/views/general.nim

@@ -80,9 +80,9 @@ proc renderHead*(prefs: Prefs; cfg: Config; titleText=""; desc=""; video="";
       meta(property="og:video:secure_url", content=video)
       meta(property="og:video:type", content="text/html")
 
-proc renderMain*(body: VNode; req: Request; cfg: Config; titleText=""; desc="";
-                 rss=""; video=""; images: seq[string] = @[]; ogTitle=""): string =
-  let prefs = getPrefs(req.cookies)
+proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
+                 titleText=""; desc=""; ogTitle=""; rss=""; video="";
+                 images: seq[string] = @[]): string =
   var theme = toLowerAscii(prefs.theme).replace(" ", "_")
   if "theme" in req.params:
     theme = toLowerAscii(req.params["theme"]).replace(" ", "_")
@@ -107,6 +107,3 @@ proc renderError*(error: string): VNode =
   buildHtml(tdiv(class="panel-container")):
     tdiv(class="error-panel"):
       span: text error
-
-template showError*(error: string; cfg: Config): string =
-  renderMain(renderError(error), request, cfg, "Error")