Skip to content

Commit 312ff78

Browse files
committed
Store preferences in cookies, add config defaults
1 parent 517d914 commit 312ff78

9 files changed

Lines changed: 112 additions & 112 deletions

File tree

nitter.conf

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,14 @@ directory = "./tmp"
1111
profileMinutes = 10 # how long to cache profiles
1212

1313
[Config]
14-
defaultTheme = "Nitter"
1514
hmacKey = "secretkey" # for signing video urls
15+
16+
# Change default preferences here, see src/prefs_impl.nim for a complete list
17+
[Preferences]
18+
theme = "Nitter"
19+
replaceTwitter = "nitter.net"
20+
replaceYouTube = "invidio.us"
21+
replaceInstagram = ""
22+
proxyVideos = true
23+
hlsPlayback = false
24+
infiniteScroll = false

src/config.nim

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import parsecfg except Config
2-
import net, types, strutils
2+
import types, strutils
33

4-
proc get[T](config: parseCfg.Config; s, v: string; default: T): T =
4+
proc get*[T](config: parseCfg.Config; s, v: string; default: T): T =
55
let val = config.getSectionValue(s, v)
66
if val.len == 0: return default
77

88
when T is int: parseInt(val)
99
elif T is bool: parseBool(val)
1010
elif T is string: val
1111

12-
proc getConfig*(path: string): Config =
12+
proc getConfig*(path: string): (Config, parseCfg.Config) =
1313
var cfg = loadConfig(path)
1414

15-
Config(
15+
let conf = Config(
1616
address: cfg.get("Server", "address", "0.0.0.0"),
1717
port: cfg.get("Server", "port", 8080),
1818
useHttps: cfg.get("Server", "https", true),
@@ -23,6 +23,7 @@ proc getConfig*(path: string): Config =
2323
cacheDir: cfg.get("Cache", "directory", "/tmp/nitter"),
2424
profileCacheTime: cfg.get("Cache", "profileMinutes", 10),
2525

26-
defaultTheme: cfg.get("Config", "defaultTheme", "Nitter"),
2726
hmacKey: cfg.get("Config", "hmacKey", "secretkey")
2827
)
28+
29+
return (conf, cfg)

src/nitter.nim

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import routes/[
1010
unsupported, embed, resolver]
1111

1212
const configPath {.strdefine.} = "./nitter.conf"
13-
let cfg = getConfig(configPath)
13+
let (cfg, fullCfg) = getConfig(configPath)
14+
15+
updateDefaultPrefs(fullCfg)
1416

1517
setHmacKey(cfg.hmacKey)
1618

src/prefs.nim

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,15 @@
1-
import strutils, sequtils, macros
2-
import norm/sqlite
1+
import tables
2+
import types, prefs_impl
3+
from config import get
4+
from parsecfg import nil
35

4-
import prefs_impl, types
5-
export genUpdatePrefs
6+
export genUpdatePrefs, genResetPrefs
67

7-
template safeAddColumn(field: typedesc): untyped =
8-
try: field.addColumn
9-
except DbError: discard
8+
var defaultPrefs*: Prefs
109

11-
dbFromTypes("prefs.db", "", "", "", [Prefs])
10+
proc updateDefaultPrefs*(cfg: parsecfg.Config) =
11+
genDefaultPrefs()
1212

13-
withDb:
14-
try:
15-
createTables()
16-
except DbError:
17-
discard
18-
safeAddColumn Prefs.theme
19-
safeAddColumn Prefs.hidePins
20-
safeAddColumn Prefs.hideReplies
21-
safeAddColumn Prefs.infiniteScroll
22-
safeAddColumn Prefs.replaceInstagram
23-
24-
proc getDefaultPrefs(cfg: Config): Prefs =
25-
result = genDefaultPrefs()
26-
result.replaceTwitter = cfg.hostname
27-
result.theme = cfg.defaultTheme
28-
29-
proc cache*(prefs: var Prefs) =
30-
withDb:
31-
try:
32-
doAssert prefs.id != 0
33-
discard Prefs.getOne("id = ?", prefs.id)
34-
prefs.update()
35-
except AssertionError, KeyError:
36-
prefs.insert()
37-
38-
proc getPrefs*(id: string; cfg: Config): Prefs =
39-
if id.len == 0:
40-
return getDefaultPrefs(cfg)
41-
42-
withDb:
43-
try:
44-
result.getOne("id = ?", id)
45-
if result.theme.len == 0:
46-
result.theme = cfg.defaultTheme
47-
except KeyError:
48-
result = getDefaultPrefs(cfg)
49-
50-
proc resetPrefs*(prefs: var Prefs; cfg: Config) =
51-
var defPrefs = getDefaultPrefs(cfg)
52-
defPrefs.id = prefs.id
53-
cache(defPrefs)
54-
prefs = defPrefs
13+
proc getPrefs*(cookies: Table[string, string]): Prefs =
14+
result = defaultPrefs
15+
genCookiePrefs()

src/prefs_impl.nim

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ type
1414
defaultOption*: string
1515
defaultInput*: string
1616

17-
macro genPrefs(prefDsl: untyped) =
17+
PrefList* = OrderedTable[string, seq[Pref]]
18+
19+
macro genPrefs*(prefDsl: untyped) =
1820
var table = nnkTableConstr.newTree()
1921
for category in prefDsl:
2022
table.add nnkExprColonExpr.newTree(newLit($category[0]))
@@ -41,7 +43,7 @@ macro genPrefs(prefDsl: untyped) =
4143

4244
let name = ident("prefList")
4345
result = quote do:
44-
const `name`* = toOrderedTable(`table`)
46+
const `name`*: PrefList = toOrderedTable(`table`)
4547

4648
genPrefs:
4749
Privacy:
@@ -101,46 +103,79 @@ iterator allPrefs*(): Pref =
101103
yield pref
102104

103105
macro genDefaultPrefs*(): untyped =
104-
result = nnkObjConstr.newTree(ident("Prefs"))
105-
106+
result = nnkStmtList.newTree()
106107
for pref in allPrefs():
107-
let default =
108-
case pref.kind
109-
of checkbox: newLit(pref.defaultState)
110-
of select: newLit(pref.defaultOption)
111-
of input: newLit(pref.defaultInput)
112-
113-
result.add nnkExprColonExpr.newTree(ident(pref.name), default)
108+
let
109+
ident = ident(pref.name)
110+
name = newLit(pref.name)
111+
default =
112+
case pref.kind
113+
of checkbox: newLit(pref.defaultState)
114+
of select: newLit(pref.defaultOption)
115+
of input: newLit(pref.defaultInput)
116+
117+
result.add quote do:
118+
defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`)
119+
120+
macro genCookiePrefs*(): untyped =
121+
result = nnkStmtList.newTree()
122+
let cookies = ident("cookies")
123+
for pref in allPrefs():
124+
let
125+
name = pref.name
126+
ident = ident(pref.name)
127+
kind = newLit(pref.kind)
128+
options = pref.options
129+
130+
result.add quote do:
131+
if `name` in `cookies`:
132+
let value = `cookies`[`name`]
133+
when `kind` == input or `name` == "theme":
134+
result.`ident` = value
135+
elif `kind` == checkbox:
136+
result.`ident` = value == "on"
137+
else:
138+
if value in `options`: result.`ident` = value
114139

115140
macro genUpdatePrefs*(): untyped =
116141
result = nnkStmtList.newTree()
117-
142+
let req = ident("request")
143+
for pref in allPrefs():
144+
let
145+
name = newLit(pref.name)
146+
kind = newLit(pref.kind)
147+
options = newLit(pref.options)
148+
default = nnkDotExpr.newTree(ident("defaultPrefs"), ident(pref.name))
149+
150+
result.add quote do:
151+
let val = @`name`
152+
let isDefault =
153+
when `kind` == input or `name` == "theme":
154+
if `default`.len != val.len: false
155+
else: val == `default`
156+
elif `kind` == checkbox:
157+
(val == "on") == `default`
158+
else:
159+
val notin `options` or val == `default`
160+
161+
if isDefault:
162+
savePref(`name`, "", `req`, expire=true)
163+
else:
164+
savePref(`name`, val, `req`)
165+
166+
macro genResetPrefs*(): untyped =
167+
result = nnkStmtList.newTree()
168+
let req = ident("request")
118169
for pref in allPrefs():
119-
let ident = ident(pref.name)
120-
let value = nnkPrefix.newTree(ident("@"), newLit(pref.name))
121-
122-
case pref.kind
123-
of checkbox:
124-
result.add quote do: prefs.`ident` = `value` == "on"
125-
of input:
126-
result.add quote do: prefs.`ident` = xmltree.escape(strip(`value`))
127-
of select:
128-
let name = pref.name
129-
let options = pref.options
130-
let default = pref.defaultOption
131-
result.add quote do:
132-
if `name` == "theme": prefs.`ident` = `value`
133-
elif `value` in `options`: prefs.`ident` = `value`
134-
else: prefs.`ident` = `default`
135-
136-
result.add quote do:
137-
cache(prefs)
170+
let name = newLit(pref.name)
171+
result.add quote do:
172+
savePref(`name`, "", `req`, expire=true)
138173

139174
macro genPrefsType*(): untyped =
140175
let name = nnkPostfix.newTree(ident("*"), ident("Prefs"))
141176
result = quote do:
142177
type `name` = object
143-
id* {.pk, ro.}: int
178+
discard
144179

145180
for pref in allPrefs():
146181
result[0][2][2].add nnkIdentDefs.newTree(

src/routes/preferences.nim

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ proc findThemes*(dir: string): seq[string] =
1616

1717
proc createPrefRouter*(cfg: Config) =
1818
router preferences:
19-
template savePrefs(): untyped =
20-
setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps)
21-
2219
get "/settings":
2320
let html = renderPreferences(cookiePrefs(), refPath(), findThemes(cfg.staticDir))
2421
resp renderMain(html, request, cfg, "Preferences")
@@ -27,27 +24,14 @@ proc createPrefRouter*(cfg: Config) =
2724
redirect("/settings")
2825

2926
post "/saveprefs":
30-
var prefs = cookiePrefs()
3127
genUpdatePrefs()
32-
savePrefs()
3328
redirect(refPath())
3429

3530
post "/resetprefs":
36-
var prefs = cookiePrefs()
37-
resetPrefs(prefs, cfg)
38-
savePrefs()
31+
genResetPrefs()
3932
redirect($(parseUri("/settings") ? filterParams(request.params)))
4033

4134
post "/enablehls":
42-
var prefs = cookiePrefs()
43-
prefs.hlsPlayback = true
44-
cache(prefs)
45-
savePrefs()
35+
savePref("hlsPlayback", "on", request)
4636
redirect(refPath())
4737

48-
before:
49-
if @"theme".len > 0:
50-
var prefs = cookiePrefs()
51-
prefs.theme = @"theme".capitalizeAscii.replace("_", " ")
52-
cache(prefs)
53-
savePrefs()

src/routes/router_utils.nim

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import strutils, sequtils, asyncdispatch, httpclient
2+
from jester import Request
23
import ../utils, ../prefs
34
export utils, prefs
45

6+
template savePref*(pref, value: string; req: Request; expire=false): typed =
7+
if not expire or pref in cookies(req):
8+
setCookie(pref, value, daysForward(when expire: -10 else: 360),
9+
httpOnly=true, secure=cfg.useHttps)
10+
511
template cookiePrefs*(): untyped {.dirty.} =
6-
getPrefs(request.cookies.getOrDefault("preferences"), cfg)
12+
getPrefs(cookies(request))
713

814
template getPath*(): untyped {.dirty.} =
915
$(parseUri(request.path) ? filterParams(request.params))

src/types.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import norm/sqlite
33

44
import prefs_impl
55

6+
genPrefsType()
7+
68
type
79
VideoType* = enum
810
vmap, m3u8, mp4
@@ -59,7 +61,6 @@ dbTypes:
5961
formatIt: dbValue(getTime().toUnix())
6062
.}: Time
6163

62-
genPrefsType()
6364

6465
type
6566
QueryKind* = enum
@@ -187,7 +188,6 @@ type
187188
hostname*: string
188189
cacheDir*: string
189190
profileCacheTime*: int
190-
defaultTheme*: string
191191
hmacKey*: string
192192

193193
proc contains*(thread: Chain; tweet: Tweet): bool =

src/views/general.nim

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ proc renderHead*(prefs: Prefs; cfg: Config; titleText=""; desc=""; video="";
8282

8383
proc renderMain*(body: VNode; req: Request; cfg: Config; titleText=""; desc="";
8484
rss=""; video=""; images: seq[string] = @[]; ogTitle=""): string =
85-
let prefs = getPrefs(req.cookies.getOrDefault("preferences"), cfg)
86-
let theme = toLowerAscii(prefs.theme).replace(" ", "_")
85+
let prefs = getPrefs(req.cookies)
86+
var theme = toLowerAscii(prefs.theme).replace(" ", "_")
87+
if "theme" in req.params:
88+
theme = toLowerAscii(req.params["theme"]).replace(" ", "_")
8789

8890
let node = buildHtml(html(lang="en")):
8991
renderHead(prefs, cfg, titleText, desc, video, images, ogTitle):

0 commit comments

Comments
 (0)