浏览代码

Support restoring preferences via new prefs param

Fixes #1352
Fixes #553
Fixes #249
Zed 7 月之前
父节点
当前提交
db36f75519
共有 9 个文件被更改,包括 107 次插入14 次删除
  1. 5 0
      src/nitter.nim
  2. 7 2
      src/prefs.nim
  3. 30 0
      src/prefs_impl.nim
  4. 3 1
      src/routes/preferences.nim
  5. 29 3
      src/routes/router_utils.nim
  6. 12 6
      src/sass/index.scss
  7. 12 0
      src/sass/inputs.scss
  8. 1 1
      src/utils.nim
  9. 8 1
      src/views/preferences.nim

+ 5 - 0
src/nitter.nim

@@ -65,6 +65,11 @@ settings:
   reusePort = true
 
 routes:
+  before:
+    # skip all file URLs
+    cond "." notin request.path
+    applyUrlPrefs()
+
   get "/":
     resp renderMain(renderSearch(), request, cfg, cookiePrefs())
 

+ 7 - 2
src/prefs.nim

@@ -1,10 +1,10 @@
 # SPDX-License-Identifier: AGPL-3.0-only
-import tables
+import tables, strutils, base64
 import types, prefs_impl
 from config import get
 from parsecfg import nil
 
-export genUpdatePrefs, genResetPrefs
+export genUpdatePrefs, genResetPrefs, genApplyPrefs
 
 var defaultPrefs*: Prefs
 
@@ -20,3 +20,8 @@ template getPref*(cookies: Table[string, string], pref): untyped =
   var res = defaultPrefs.`pref`
   genCookiePref(cookies, pref, res)
   res
+
+proc encodePrefs*(prefs: Prefs): string =
+  var encPairs: seq[string]
+  genEncodePrefs(prefs)
+  encode(encPairs.join("&"), safe=true)

+ 30 - 0
src/prefs_impl.nim

@@ -205,6 +205,36 @@ macro genResetPrefs*(): untyped =
     result.add quote do:
       savePref(`name`, "", `req`, expire=true)
 
+macro genEncodePrefs*(prefs): untyped =
+  result = nnkStmtList.newTree()
+  for pref in allPrefs():
+    let
+      name = newLit(pref.name)
+      ident = ident(pref.name)
+      kind = newLit(pref.kind)
+      defaultIdent = nnkDotExpr.newTree(ident("defaultPrefs"), ident(pref.name))
+
+    result.add quote do:
+      when `kind` == checkbox:
+        if `prefs`.`ident` != `defaultIdent`:
+          if `prefs`.`ident`:
+            encPairs.add `name` & "=on"
+          else:
+            encPairs.add `name` & "="
+      else:
+        if `prefs`.`ident` != `defaultIdent`:
+          encPairs.add `name` & "=" & `prefs`.`ident`
+
+macro genApplyPrefs*(params, req): untyped =
+  result = nnkStmtList.newTree()
+  for pref in allPrefs():
+    let name = newLit(pref.name)
+    result.add quote do:
+      if `name` in `params`:
+        savePref(`name`, `params`[`name`], `req`)
+      else:
+        savePref(`name`, "", `req`, expire=true)
+
 macro genPrefsType*(): untyped =
   let name = nnkPostfix.newTree(ident("*"), ident("Prefs"))
   result = quote do:

+ 3 - 1
src/routes/preferences.nim

@@ -20,7 +20,9 @@ proc createPrefRouter*(cfg: Config) =
     get "/settings":
       let
         prefs = cookiePrefs()
-        html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir))
+        prefsCode = encodePrefs(prefs)
+        prefsUrl = getUrlPrefix(cfg) & "/?prefs=" & prefsCode
+        html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir), prefsUrl)
       resp renderMain(html, request, cfg, prefs, "Preferences")
 
     get "/settings/@i?":

+ 29 - 3
src/routes/router_utils.nim

@@ -1,15 +1,15 @@
 # SPDX-License-Identifier: AGPL-3.0-only
-import strutils, sequtils, uri, tables, json
+import strutils, sequtils, uri, tables, json, base64
 from jester import Request, cookies
 
 import ../views/general
 import ".."/[utils, prefs, types]
-export utils, prefs, types, uri
+export utils, prefs, types, uri, base64
 
 template savePref*(pref, value: string; req: Request; expire=false) =
   if not expire or pref in cookies(req):
     setCookie(pref, value, daysForward(when expire: -10 else: 360),
-              httpOnly=true, secure=cfg.useHttps, sameSite=None)
+              httpOnly=true, secure=cfg.useHttps, sameSite=None, path="/")
 
 template cookiePrefs*(): untyped {.dirty.} =
   getPrefs(cookies(request))
@@ -38,5 +38,31 @@ template getCursor*(req: Request): string =
 proc getNames*(name: string): seq[string] =
   name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
 
+template applyUrlPrefs*() {.dirty.} =
+  if @"prefs".len > 0:
+    try:
+      let decoded = decode(@"prefs")
+      var params = initTable[string, string]()
+      for pair in decoded.split('&'):
+        let kv = pair.split('=', maxsplit=1)
+        if kv.len == 2:
+          params[kv[0]] = kv[1]
+        elif kv.len == 1 and kv[0].len > 0:
+          params[kv[0]] = ""
+      genApplyPrefs(params, request)
+    except: discard
+
+    # Rebuild URL without prefs param
+    var params: seq[(string, string)]
+    for k, v in request.params:
+      if k != "prefs":
+        params.add (k, v)
+
+    if params.len > 0:
+      let cleanUrl = request.getNativeReq.url ? params
+      redirect($cleanUrl)
+    else:
+      redirect(request.path)
+
 template respJson*(node: JsonNode) =
   resp $node, "application/json"

+ 12 - 6
src/sass/index.scss

@@ -99,12 +99,18 @@ legend {
   margin-bottom: 8px;
 }
 
-.preferences .note {
-  border-top: 1px solid var(--border_grey);
-  border-bottom: 1px solid var(--border_grey);
-  padding: 6px 0 8px 0;
-  margin-bottom: 8px;
-  margin-top: 16px;
+.preferences {
+  .note {
+    border-top: 1px solid var(--border_grey);
+    border-bottom: 1px solid var(--border_grey);
+    padding: 6px 0 8px 0;
+    margin-bottom: 8px;
+    margin-top: 16px;
+  }
+
+  .bookmark-note {
+    margin: 0;
+  }
 }
 
 ul {

+ 12 - 0
src/sass/inputs.scss

@@ -200,4 +200,16 @@ input::-webkit-datetime-edit-year-field:focus {
   .pref-reset {
     float: left;
   }
+
+  .prefs-code {
+    background-color: var(--bg_elements);
+    border: 1px solid var(--accent_border);
+    color: var(--fg_color);
+    font-size: 12px;
+    padding: 6px 8px;
+    margin: 4px 0;
+    word-break: break-all;
+    white-space: pre-wrap;
+    user-select: all;
+  }
 }

+ 1 - 1
src/utils.nim

@@ -9,7 +9,7 @@ var
 const
   https* = "https://"
   twimg* = "pbs.twimg.com/"
-  nitterParams = ["name", "tab", "id", "list", "referer", "scroll"]
+  nitterParams* = ["name", "tab", "id", "list", "referer", "scroll", "prefs"]
   twitterDomains = @[
     "twitter.com",
     "pic.twitter.com",

+ 8 - 1
src/views/preferences.nim

@@ -32,7 +32,8 @@ macro renderPrefs*(): untyped =
 
       result[2].add stmt
 
-proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]): VNode =
+proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string];
+                        prefsUrl: string): VNode =
   buildHtml(tdiv(class="overlay-panel")):
     fieldset(class="preferences"):
       form(`method`="post", action="/saveprefs", autocomplete="off"):
@@ -40,6 +41,12 @@ proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]): VNode
 
         renderPrefs()
 
+        legend: text "Bookmark"
+        p(class="bookmark-note"):
+          text "Save this URL to restore your preferences (?prefs works on all pages)"
+        pre(class="prefs-code"):
+          text prefsUrl
+
         h4(class="note"):
           text "Preferences are stored client-side using cookies without any personal information."