1
0
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:
Zed
2026-02-09 20:23:31 +01:00
parent 5d28bd18c6
commit db36f75519
9 changed files with 107 additions and 14 deletions

View File

@@ -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())

View File

@@ -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)

View File

@@ -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:

View File

@@ -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?":

View File

@@ -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"

View File

@@ -99,12 +99,18 @@ 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;
margin-bottom: 8px; margin-bottom: 8px;
margin-top: 16px; margin-top: 16px;
}
.bookmark-note {
margin: 0;
}
} }
ul { ul {

View File

@@ -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;
}
} }

View File

@@ -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",

View File

@@ -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."