mirror of
https://github.com/zedeus/nitter.git
synced 2026-04-03 20:32:10 -04:00
Support restoring preferences via new prefs param
Fixes #1352 Fixes #553 Fixes #249
This commit is contained in:
@@ -65,6 +65,11 @@ settings:
|
|||||||
reusePort = true
|
reusePort = true
|
||||||
|
|
||||||
routes:
|
routes:
|
||||||
|
before:
|
||||||
|
# skip all file URLs
|
||||||
|
cond "." notin request.path
|
||||||
|
applyUrlPrefs()
|
||||||
|
|
||||||
get "/":
|
get "/":
|
||||||
resp renderMain(renderSearch(), request, cfg, cookiePrefs())
|
resp renderMain(renderSearch(), request, cfg, cookiePrefs())
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import tables
|
import tables, strutils, base64
|
||||||
import types, prefs_impl
|
import types, prefs_impl
|
||||||
from config import get
|
from config import get
|
||||||
from parsecfg import nil
|
from parsecfg import nil
|
||||||
|
|
||||||
export genUpdatePrefs, genResetPrefs
|
export genUpdatePrefs, genResetPrefs, genApplyPrefs
|
||||||
|
|
||||||
var defaultPrefs*: Prefs
|
var defaultPrefs*: Prefs
|
||||||
|
|
||||||
@@ -20,3 +20,8 @@ template getPref*(cookies: Table[string, string], pref): untyped =
|
|||||||
var res = defaultPrefs.`pref`
|
var res = defaultPrefs.`pref`
|
||||||
genCookiePref(cookies, pref, res)
|
genCookiePref(cookies, pref, res)
|
||||||
res
|
res
|
||||||
|
|
||||||
|
proc encodePrefs*(prefs: Prefs): string =
|
||||||
|
var encPairs: seq[string]
|
||||||
|
genEncodePrefs(prefs)
|
||||||
|
encode(encPairs.join("&"), safe=true)
|
||||||
|
|||||||
@@ -205,6 +205,36 @@ macro genResetPrefs*(): untyped =
|
|||||||
result.add quote do:
|
result.add quote do:
|
||||||
savePref(`name`, "", `req`, expire=true)
|
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 =
|
macro genPrefsType*(): untyped =
|
||||||
let name = nnkPostfix.newTree(ident("*"), ident("Prefs"))
|
let name = nnkPostfix.newTree(ident("*"), ident("Prefs"))
|
||||||
result = quote do:
|
result = quote do:
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ proc createPrefRouter*(cfg: Config) =
|
|||||||
get "/settings":
|
get "/settings":
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
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")
|
resp renderMain(html, request, cfg, prefs, "Preferences")
|
||||||
|
|
||||||
get "/settings/@i?":
|
get "/settings/@i?":
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# 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
|
from jester import Request, cookies
|
||||||
|
|
||||||
import ../views/general
|
import ../views/general
|
||||||
import ".."/[utils, prefs, types]
|
import ".."/[utils, prefs, types]
|
||||||
export utils, prefs, types, uri
|
export utils, prefs, types, uri, base64
|
||||||
|
|
||||||
template savePref*(pref, value: string; req: Request; expire=false) =
|
template savePref*(pref, value: string; req: Request; expire=false) =
|
||||||
if not expire or pref in cookies(req):
|
if not expire or pref in cookies(req):
|
||||||
setCookie(pref, value, daysForward(when expire: -10 else: 360),
|
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.} =
|
template cookiePrefs*(): untyped {.dirty.} =
|
||||||
getPrefs(cookies(request))
|
getPrefs(cookies(request))
|
||||||
@@ -38,5 +38,31 @@ template getCursor*(req: Request): string =
|
|||||||
proc getNames*(name: string): seq[string] =
|
proc getNames*(name: string): seq[string] =
|
||||||
name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
|
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) =
|
template respJson*(node: JsonNode) =
|
||||||
resp $node, "application/json"
|
resp $node, "application/json"
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ legend {
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preferences .note {
|
.preferences {
|
||||||
|
.note {
|
||||||
border-top: 1px solid var(--border_grey);
|
border-top: 1px solid var(--border_grey);
|
||||||
border-bottom: 1px solid var(--border_grey);
|
border-bottom: 1px solid var(--border_grey);
|
||||||
padding: 6px 0 8px 0;
|
padding: 6px 0 8px 0;
|
||||||
@@ -107,6 +108,11 @@ legend {
|
|||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bookmark-note {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding-left: 1.3em;
|
padding-left: 1.3em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,4 +200,16 @@ input::-webkit-datetime-edit-year-field:focus {
|
|||||||
.pref-reset {
|
.pref-reset {
|
||||||
float: left;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ var
|
|||||||
const
|
const
|
||||||
https* = "https://"
|
https* = "https://"
|
||||||
twimg* = "pbs.twimg.com/"
|
twimg* = "pbs.twimg.com/"
|
||||||
nitterParams = ["name", "tab", "id", "list", "referer", "scroll"]
|
nitterParams* = ["name", "tab", "id", "list", "referer", "scroll", "prefs"]
|
||||||
twitterDomains = @[
|
twitterDomains = @[
|
||||||
"twitter.com",
|
"twitter.com",
|
||||||
"pic.twitter.com",
|
"pic.twitter.com",
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ macro renderPrefs*(): untyped =
|
|||||||
|
|
||||||
result[2].add stmt
|
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")):
|
buildHtml(tdiv(class="overlay-panel")):
|
||||||
fieldset(class="preferences"):
|
fieldset(class="preferences"):
|
||||||
form(`method`="post", action="/saveprefs", autocomplete="off"):
|
form(`method`="post", action="/saveprefs", autocomplete="off"):
|
||||||
@@ -40,6 +41,12 @@ proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]): VNode
|
|||||||
|
|
||||||
renderPrefs()
|
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"):
|
h4(class="note"):
|
||||||
text "Preferences are stored client-side using cookies without any personal information."
|
text "Preferences are stored client-side using cookies without any personal information."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user