mirror of
https://github.com/zedeus/nitter.git
synced 2026-03-05 13:30:19 -05:00
@@ -71,10 +71,10 @@ routes:
|
|||||||
applyUrlPrefs()
|
applyUrlPrefs()
|
||||||
|
|
||||||
get "/":
|
get "/":
|
||||||
resp renderMain(renderSearch(), request, cfg, cookiePrefs())
|
resp renderMain(renderSearch(), request, cfg, requestPrefs())
|
||||||
|
|
||||||
get "/about":
|
get "/about":
|
||||||
resp renderMain(renderAbout(), request, cfg, cookiePrefs())
|
resp renderMain(renderAbout(), request, cfg, requestPrefs())
|
||||||
|
|
||||||
get "/explore":
|
get "/explore":
|
||||||
redirect("/about")
|
redirect("/about")
|
||||||
@@ -85,7 +85,7 @@ routes:
|
|||||||
get "/i/redirect":
|
get "/i/redirect":
|
||||||
let url = decodeUrl(@"url")
|
let url = decodeUrl(@"url")
|
||||||
if url.len == 0: resp Http404
|
if url.len == 0: resp Http404
|
||||||
redirect(replaceUrls(url, cookiePrefs()))
|
redirect(replaceUrls(url, requestPrefs()))
|
||||||
|
|
||||||
error Http404:
|
error Http404:
|
||||||
resp Http404, showError("Page not found", cfg)
|
resp Http404, showError("Page not found", cfg)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import tables, strutils, base64
|
import tables, strutils
|
||||||
import types, prefs_impl
|
import types, prefs_impl
|
||||||
from config import get
|
from config import get
|
||||||
from parsecfg import nil
|
from parsecfg import nil
|
||||||
@@ -11,17 +11,12 @@ var defaultPrefs*: Prefs
|
|||||||
proc updateDefaultPrefs*(cfg: parsecfg.Config) =
|
proc updateDefaultPrefs*(cfg: parsecfg.Config) =
|
||||||
genDefaultPrefs()
|
genDefaultPrefs()
|
||||||
|
|
||||||
proc getPrefs*(cookies: Table[string, string]): Prefs =
|
proc getPrefs*(cookies, params: Table[string, string]): Prefs =
|
||||||
result = defaultPrefs
|
result = defaultPrefs
|
||||||
genCookiePrefs(cookies)
|
genParsePrefs(cookies)
|
||||||
|
genParsePrefs(params)
|
||||||
template getPref*(cookies: Table[string, string], pref): untyped =
|
|
||||||
bind genCookiePref
|
|
||||||
var res = defaultPrefs.`pref`
|
|
||||||
genCookiePref(cookies, pref, res)
|
|
||||||
res
|
|
||||||
|
|
||||||
proc encodePrefs*(prefs: Prefs): string =
|
proc encodePrefs*(prefs: Prefs): string =
|
||||||
var encPairs: seq[string]
|
var encPairs: seq[string]
|
||||||
genEncodePrefs(prefs)
|
genEncodePrefs(prefs)
|
||||||
encode(encPairs.join("&"), safe=true)
|
encPairs.join(",")
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ macro genDefaultPrefs*(): untyped =
|
|||||||
result.add quote do:
|
result.add quote do:
|
||||||
defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`)
|
defaultPrefs.`ident` = cfg.get("Preferences", `name`, `default`)
|
||||||
|
|
||||||
macro genCookiePrefs*(cookies): untyped =
|
macro genParsePrefs*(prefs): untyped =
|
||||||
result = nnkStmtList.newTree()
|
result = nnkStmtList.newTree()
|
||||||
for pref in allPrefs():
|
for pref in allPrefs():
|
||||||
let
|
let
|
||||||
@@ -140,37 +140,17 @@ macro genCookiePrefs*(cookies): untyped =
|
|||||||
options = pref.options
|
options = pref.options
|
||||||
|
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
if `name` in `cookies`:
|
if `name` in `prefs`:
|
||||||
when `kind` == input or `name` == "theme":
|
when `kind` == input or `name` == "theme":
|
||||||
result.`ident` = `cookies`[`name`]
|
result.`ident` = `prefs`[`name`]
|
||||||
elif `kind` == checkbox:
|
elif `kind` == checkbox:
|
||||||
result.`ident` = `cookies`[`name`] == "on"
|
result.`ident` = `prefs`[`name`] == "on" or
|
||||||
|
`prefs`[`name`] == "true" or
|
||||||
|
`prefs`[`name`] == "1"
|
||||||
else:
|
else:
|
||||||
let value = `cookies`[`name`]
|
let value = `prefs`[`name`]
|
||||||
if value in `options`: result.`ident` = value
|
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 =
|
macro genUpdatePrefs*(): untyped =
|
||||||
result = nnkStmtList.newTree()
|
result = nnkStmtList.newTree()
|
||||||
let req = ident("request")
|
let req = ident("request")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ proc createEmbedRouter*(cfg: Config) =
|
|||||||
get "/@user/status/@id/embed":
|
get "/@user/status/@id/embed":
|
||||||
let
|
let
|
||||||
tweet = await getGraphTweetResult(@"id")
|
tweet = await getGraphTweetResult(@"id")
|
||||||
prefs = cookiePrefs()
|
prefs = requestPrefs()
|
||||||
path = getPath()
|
path = getPath()
|
||||||
|
|
||||||
if tweet == nil:
|
if tweet == nil:
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ proc createListRouter*(cfg: Config) =
|
|||||||
get "/i/lists/@id/?":
|
get "/i/lists/@id/?":
|
||||||
cond '.' notin @"id"
|
cond '.' notin @"id"
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = requestPrefs()
|
||||||
list = await getCachedList(id=(@"id"))
|
list = await getCachedList(id=(@"id"))
|
||||||
timeline = await getGraphListTweets(list.id, getCursor())
|
timeline = await getGraphListTweets(list.id, getCursor())
|
||||||
vnode = renderTimelineTweets(timeline, prefs, request.path)
|
vnode = renderTimelineTweets(timeline, prefs, request.path)
|
||||||
@@ -45,7 +45,7 @@ proc createListRouter*(cfg: Config) =
|
|||||||
get "/i/lists/@id/members":
|
get "/i/lists/@id/members":
|
||||||
cond '.' notin @"id"
|
cond '.' notin @"id"
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = requestPrefs()
|
||||||
list = await getCachedList(id=(@"id"))
|
list = await getCachedList(id=(@"id"))
|
||||||
members = await getGraphListMembers(list, getCursor())
|
members = await getGraphListMembers(list, getCursor())
|
||||||
respList(list, members, list.title, renderTimelineUsers(members, prefs, request.path))
|
respList(list, members, list.title, renderTimelineUsers(members, prefs, request.path))
|
||||||
|
|||||||
@@ -143,6 +143,6 @@ proc createMediaRouter*(cfg: Config) =
|
|||||||
|
|
||||||
if ".m3u8" in url:
|
if ".m3u8" in url:
|
||||||
let vid = await safeFetch(url)
|
let vid = await safeFetch(url)
|
||||||
content = proxifyVideo(vid, cookiePref(proxyVideos))
|
content = proxifyVideo(vid, requestPrefs().proxyVideos)
|
||||||
|
|
||||||
resp content, m3u8Mime
|
resp content, m3u8Mime
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ proc createPrefRouter*(cfg: Config) =
|
|||||||
router preferences:
|
router preferences:
|
||||||
get "/settings":
|
get "/settings":
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = requestPrefs()
|
||||||
prefsCode = encodePrefs(prefs)
|
prefsCode = encodePrefs(prefs)
|
||||||
prefsUrl = getUrlPrefix(cfg) & "/?prefs=" & prefsCode
|
prefsUrl = getUrlPrefix(cfg) & "/?prefs=" & prefsCode
|
||||||
html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir), prefsUrl)
|
html = renderPreferences(prefs, refPath(), findThemes(cfg.staticDir), prefsUrl)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ proc createResolverRouter*(cfg: Config) =
|
|||||||
router resolver:
|
router resolver:
|
||||||
get "/cards/@card/@id":
|
get "/cards/@card/@id":
|
||||||
let url = "https://cards.twitter.com/cards/$1/$2" % [@"card", @"id"]
|
let url = "https://cards.twitter.com/cards/$1/$2" % [@"card", @"id"]
|
||||||
respResolved(await resolve(url, cookiePrefs()), "card")
|
respResolved(await resolve(url, requestPrefs()), "card")
|
||||||
|
|
||||||
get "/t.co/@url":
|
get "/t.co/@url":
|
||||||
let url = "https://t.co/" & @"url"
|
let url = "https://t.co/" & @"url"
|
||||||
respResolved(await resolve(url, cookiePrefs()), "t.co")
|
respResolved(await resolve(url, requestPrefs()), "t.co")
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import strutils, sequtils, uri, tables, json, base64
|
import strutils, sequtils, uri, tables, json
|
||||||
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, base64
|
export utils, prefs, types, uri
|
||||||
|
|
||||||
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, path="/")
|
httpOnly=true, secure=cfg.useHttps, sameSite=None, path="/")
|
||||||
|
|
||||||
template cookiePrefs*(): untyped {.dirty.} =
|
template requestPrefs*(): untyped {.dirty.} =
|
||||||
getPrefs(cookies(request))
|
getPrefs(cookies(request), params(request))
|
||||||
|
|
||||||
template cookiePref*(pref): untyped {.dirty.} =
|
|
||||||
getPref(cookies(request), pref)
|
|
||||||
|
|
||||||
template showError*(error: string; cfg: Config): string =
|
template showError*(error: string; cfg: Config): string =
|
||||||
renderMain(renderError(error), request, cfg, cookiePrefs(), "Error")
|
renderMain(renderError(error), request, cfg, requestPrefs(), "Error")
|
||||||
|
|
||||||
template getPath*(): untyped {.dirty.} =
|
template getPath*(): untyped {.dirty.} =
|
||||||
$(parseUri(request.path) ? filterParams(request.params))
|
$(parseUri(request.path) ? filterParams(request.params))
|
||||||
@@ -40,17 +37,14 @@ proc getNames*(name: string): seq[string] =
|
|||||||
|
|
||||||
template applyUrlPrefs*() {.dirty.} =
|
template applyUrlPrefs*() {.dirty.} =
|
||||||
if @"prefs".len > 0:
|
if @"prefs".len > 0:
|
||||||
try:
|
var prefParams = initTable[string, string]()
|
||||||
let decoded = decode(@"prefs")
|
for pair in @"prefs".split(','):
|
||||||
var params = initTable[string, string]()
|
|
||||||
for pair in decoded.split('&'):
|
|
||||||
let kv = pair.split('=', maxsplit=1)
|
let kv = pair.split('=', maxsplit=1)
|
||||||
if kv.len == 2:
|
if kv.len == 2:
|
||||||
params[kv[0]] = kv[1]
|
prefParams[kv[0]] = kv[1]
|
||||||
elif kv.len == 1 and kv[0].len > 0:
|
elif kv.len == 1 and kv[0].len > 0:
|
||||||
params[kv[0]] = ""
|
prefParams[kv[0]] = ""
|
||||||
genApplyPrefs(params, request)
|
genApplyPrefs(prefParams, request)
|
||||||
except: discard
|
|
||||||
|
|
||||||
# Rebuild URL without prefs param
|
# Rebuild URL without prefs param
|
||||||
var params: seq[(string, string)]
|
var params: seq[(string, string)]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ proc redisKey*(page, name, cursor: string): string =
|
|||||||
if cursor.len > 0:
|
if cursor.len > 0:
|
||||||
result &= ":" & cursor
|
result &= ":" & cursor
|
||||||
|
|
||||||
proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async.} =
|
proc timelineRss*(req: Request; cfg: Config; query: Query; prefs: Prefs): Future[Rss] {.async.} =
|
||||||
var profile: Profile
|
var profile: Profile
|
||||||
let
|
let
|
||||||
name = req.params.getOrDefault("name")
|
name = req.params.getOrDefault("name")
|
||||||
@@ -39,7 +39,7 @@ proc timelineRss*(req: Request; cfg: Config; query: Query): Future[Rss] {.async.
|
|||||||
return Rss(feed: profile.user.username, cursor: "suspended")
|
return Rss(feed: profile.user.username, cursor: "suspended")
|
||||||
|
|
||||||
if profile.user.fullname.len > 0:
|
if profile.user.fullname.len > 0:
|
||||||
let rss = renderTimelineRss(profile, cfg, multi=(names.len > 1))
|
let rss = renderTimelineRss(profile, cfg, prefs, multi=(names.len > 1))
|
||||||
return Rss(feed: rss, cursor: profile.tweets.bottom)
|
return Rss(feed: rss, cursor: profile.tweets.bottom)
|
||||||
|
|
||||||
template respRss*(rss, page) =
|
template respRss*(rss, page) =
|
||||||
@@ -64,7 +64,9 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
if @"q".len > 200:
|
if @"q".len > 200:
|
||||||
resp Http400, showError("Search input too long.", cfg)
|
resp Http400, showError("Search input too long.", cfg)
|
||||||
|
|
||||||
let query = initQuery(params(request))
|
let
|
||||||
|
prefs = requestPrefs()
|
||||||
|
query = initQuery(params(request))
|
||||||
if query.kind != tweets:
|
if query.kind != tweets:
|
||||||
resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg)
|
resp Http400, showError("Only Tweet searches are allowed for RSS feeds.", cfg)
|
||||||
|
|
||||||
@@ -78,7 +80,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
|
|
||||||
let tweets = await getGraphTweetSearch(query, cursor)
|
let tweets = await getGraphTweetSearch(query, cursor)
|
||||||
rss.cursor = tweets.bottom
|
rss.cursor = tweets.bottom
|
||||||
rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg)
|
rss.feed = renderSearchRss(tweets.content, query.text, genQueryUrl(query), cfg, prefs)
|
||||||
|
|
||||||
await cacheRss(key, rss)
|
await cacheRss(key, rss)
|
||||||
respRss(rss, "Search")
|
respRss(rss, "Search")
|
||||||
@@ -87,6 +89,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
cond cfg.enableRss
|
cond cfg.enableRss
|
||||||
cond '.' notin @"name"
|
cond '.' notin @"name"
|
||||||
let
|
let
|
||||||
|
prefs = requestPrefs()
|
||||||
name = @"name"
|
name = @"name"
|
||||||
key = redisKey("twitter", name, getCursor())
|
key = redisKey("twitter", name, getCursor())
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
if rss.cursor.len > 0:
|
if rss.cursor.len > 0:
|
||||||
respRss(rss, "User")
|
respRss(rss, "User")
|
||||||
|
|
||||||
rss = await timelineRss(request, cfg, Query(fromUser: @[name]))
|
rss = await timelineRss(request, cfg, Query(fromUser: @[name]), prefs)
|
||||||
|
|
||||||
await cacheRss(key, rss)
|
await cacheRss(key, rss)
|
||||||
respRss(rss, "User")
|
respRss(rss, "User")
|
||||||
@@ -104,6 +107,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
cond '.' notin @"name"
|
cond '.' notin @"name"
|
||||||
cond @"tab" in ["with_replies", "media", "search"]
|
cond @"tab" in ["with_replies", "media", "search"]
|
||||||
let
|
let
|
||||||
|
prefs = requestPrefs()
|
||||||
name = @"name"
|
name = @"name"
|
||||||
tab = @"tab"
|
tab = @"tab"
|
||||||
query =
|
query =
|
||||||
@@ -122,7 +126,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
if rss.cursor.len > 0:
|
if rss.cursor.len > 0:
|
||||||
respRss(rss, "User")
|
respRss(rss, "User")
|
||||||
|
|
||||||
rss = await timelineRss(request, cfg, query)
|
rss = await timelineRss(request, cfg, query, prefs)
|
||||||
|
|
||||||
await cacheRss(key, rss)
|
await cacheRss(key, rss)
|
||||||
respRss(rss, "User")
|
respRss(rss, "User")
|
||||||
@@ -147,6 +151,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
get "/i/lists/@id/rss":
|
get "/i/lists/@id/rss":
|
||||||
cond cfg.enableRss
|
cond cfg.enableRss
|
||||||
let
|
let
|
||||||
|
prefs = requestPrefs()
|
||||||
id = @"id"
|
id = @"id"
|
||||||
cursor = getCursor()
|
cursor = getCursor()
|
||||||
key = redisKey("lists", id, cursor)
|
key = redisKey("lists", id, cursor)
|
||||||
@@ -159,7 +164,7 @@ proc createRssRouter*(cfg: Config) =
|
|||||||
list = await getCachedList(id=id)
|
list = await getCachedList(id=id)
|
||||||
timeline = await getGraphListTweets(list.id, cursor)
|
timeline = await getGraphListTweets(list.id, cursor)
|
||||||
rss.cursor = timeline.bottom
|
rss.cursor = timeline.bottom
|
||||||
rss.feed = renderListRss(timeline.content, list, cfg)
|
rss.feed = renderListRss(timeline.content, list, cfg, prefs)
|
||||||
|
|
||||||
await cacheRss(key, rss)
|
await cacheRss(key, rss)
|
||||||
respRss(rss, "List")
|
respRss(rss, "List")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ proc createSearchRouter*(cfg: Config) =
|
|||||||
resp Http400, showError("Search input too long.", cfg)
|
resp Http400, showError("Search input too long.", cfg)
|
||||||
|
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = requestPrefs()
|
||||||
query = initQuery(params(request))
|
query = initQuery(params(request))
|
||||||
title = "Search" & (if q.len > 0: " (" & q & ")" else: "")
|
title = "Search" & (if q.len > 0: " (" & q & ")" else: "")
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ proc createStatusRouter*(cfg: Config) =
|
|||||||
if id.len > 19 or id.any(c => not c.isDigit):
|
if id.len > 19 or id.any(c => not c.isDigit):
|
||||||
resp Http404, showError("Invalid tweet ID", cfg)
|
resp Http404, showError("Invalid tweet ID", cfg)
|
||||||
|
|
||||||
let prefs = cookiePrefs()
|
let prefs = requestPrefs()
|
||||||
|
|
||||||
# used for the infinite scroll feature
|
# used for the infinite scroll feature
|
||||||
if @"scroll".len > 0:
|
if @"scroll".len > 0:
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ proc createTimelineRouter*(cfg: Config) =
|
|||||||
cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ','})
|
cond @"name".allCharsInSet({'a'..'z', 'A'..'Z', '0'..'9', '_', ','})
|
||||||
cond @"tab" in ["with_replies", "media", "search", ""]
|
cond @"tab" in ["with_replies", "media", "search", ""]
|
||||||
let
|
let
|
||||||
prefs = cookiePrefs()
|
prefs = requestPrefs()
|
||||||
after = getCursor()
|
after = getCursor()
|
||||||
names = getNames(@"name")
|
names = getNames(@"name")
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export feature
|
|||||||
proc createUnsupportedRouter*(cfg: Config) =
|
proc createUnsupportedRouter*(cfg: Config) =
|
||||||
router unsupported:
|
router unsupported:
|
||||||
template feature {.dirty.} =
|
template feature {.dirty.} =
|
||||||
resp renderMain(renderFeature(), request, cfg, cookiePrefs())
|
resp renderMain(renderFeature(), request, cfg, requestPrefs())
|
||||||
|
|
||||||
get "/about/feature": feature()
|
get "/about/feature": feature()
|
||||||
get "/login/?@i?": feature()
|
get "/login/?@i?": feature()
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ legend {
|
|||||||
|
|
||||||
.bookmark-note {
|
.bookmark-note {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ input::-webkit-datetime-edit-year-field:focus {
|
|||||||
background-color: var(--bg_elements);
|
background-color: var(--bg_elements);
|
||||||
border: 1px solid var(--accent_border);
|
border: 1px solid var(--accent_border);
|
||||||
color: var(--fg_color);
|
color: var(--fg_color);
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ proc renderNavbar(cfg: Config; req: Request; rss, canonical: string): VNode =
|
|||||||
proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
||||||
video=""; images: seq[string] = @[]; banner=""; ogTitle="";
|
video=""; images: seq[string] = @[]; banner=""; ogTitle="";
|
||||||
rss=""; alternate=""): VNode =
|
rss=""; alternate=""): VNode =
|
||||||
var theme = prefs.theme.toTheme
|
let theme = prefs.theme.toTheme
|
||||||
if "theme" in req.params:
|
|
||||||
theme = req.params["theme"].toTheme
|
|
||||||
|
|
||||||
let ogType =
|
let ogType =
|
||||||
if video.len > 0: "video"
|
if video.len > 0: "video"
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string];
|
|||||||
text "Save this URL to restore your preferences (?prefs works on all pages)"
|
text "Save this URL to restore your preferences (?prefs works on all pages)"
|
||||||
pre(class="prefs-code"):
|
pre(class="prefs-code"):
|
||||||
text prefsUrl
|
text prefsUrl
|
||||||
|
p(class="bookmark-note"):
|
||||||
|
verbatim "You can override preferences with query parameters (e.g. <code>?hlsPlayback=on</code>). These overrides aren't saved to cookies, and links won't retain the parameters. Intended for configuring RSS feeds and other cookieless environments. Hover over a preference to see its name."
|
||||||
|
|
||||||
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."
|
||||||
|
|||||||
@@ -65,20 +65,20 @@ proc buttonReferer*(action, text, path: string; class=""; `method`="post"): VNod
|
|||||||
text text
|
text text
|
||||||
|
|
||||||
proc genCheckbox*(pref, label: string; state: bool): VNode =
|
proc genCheckbox*(pref, label: string; state: bool): VNode =
|
||||||
buildHtml(label(class="pref-group checkbox-container")):
|
buildHtml(label(class="pref-group checkbox-container", title=pref)):
|
||||||
text label
|
text label
|
||||||
input(name=pref, `type`="checkbox", checked=state)
|
input(name=pref, `type`="checkbox", checked=state)
|
||||||
span(class="checkbox")
|
span(class="checkbox")
|
||||||
|
|
||||||
proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true): VNode =
|
proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true): VNode =
|
||||||
let p = placeholder
|
let p = placeholder
|
||||||
buildHtml(tdiv(class=("pref-group pref-input " & class))):
|
buildHtml(tdiv(class=("pref-group pref-input " & class), title=pref)):
|
||||||
if label.len > 0:
|
if label.len > 0:
|
||||||
label(`for`=pref): text label
|
label(`for`=pref): text label
|
||||||
input(name=pref, `type`="text", placeholder=p, value=state, autofocus=(autofocus and state.len == 0))
|
input(name=pref, `type`="text", placeholder=p, value=state, autofocus=(autofocus and state.len == 0))
|
||||||
|
|
||||||
proc genSelect*(pref, label, state: string; options: seq[string]): VNode =
|
proc genSelect*(pref, label, state: string; options: seq[string]): VNode =
|
||||||
buildHtml(tdiv(class="pref-group pref-input")):
|
buildHtml(tdiv(class="pref-group pref-input", title=pref)):
|
||||||
label(`for`=pref): text label
|
label(`for`=pref): text label
|
||||||
select(name=pref):
|
select(name=pref):
|
||||||
for opt in options:
|
for opt in options:
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)}
|
|||||||
#end if
|
#end if
|
||||||
#end proc
|
#end proc
|
||||||
#
|
#
|
||||||
#proc renderRssTweet(tweet: Tweet; cfg: Config): string =
|
#proc renderRssTweet(tweet: Tweet; cfg: Config; prefs: Prefs): string =
|
||||||
#let tweet = tweet.retweet.get(tweet)
|
#let tweet = tweet.retweet.get(tweet)
|
||||||
#let urlPrefix = getUrlPrefix(cfg)
|
#let urlPrefix = getUrlPrefix(cfg)
|
||||||
#let text = replaceUrls(tweet.text, defaultPrefs, absolute=urlPrefix)
|
#let text = replaceUrls(tweet.text, prefs, absolute=urlPrefix)
|
||||||
<p>${text.replace("\n", "<br>\n")}</p>
|
<p>${text.replace("\n", "<br>\n")}</p>
|
||||||
#if tweet.photos.len > 0:
|
#if tweet.photos.len > 0:
|
||||||
# for photo in tweet.photos:
|
# for photo in tweet.photos:
|
||||||
@@ -81,7 +81,7 @@ Twitter feed for: ${desc}. Generated by ${getUrlPrefix(cfg)}
|
|||||||
<blockquote>
|
<blockquote>
|
||||||
<b>${quoteTweet.user.fullname} (@${quoteTweet.user.username})</b>
|
<b>${quoteTweet.user.fullname} (@${quoteTweet.user.username})</b>
|
||||||
<p>
|
<p>
|
||||||
${renderRssTweet(quoteTweet, cfg)}
|
${renderRssTweet(quoteTweet, cfg, prefs)}
|
||||||
</p>
|
</p>
|
||||||
<footer>
|
<footer>
|
||||||
— <cite><a href="${quoteLink}">${quoteLink}</a>
|
— <cite><a href="${quoteLink}">${quoteLink}</a>
|
||||||
@@ -90,7 +90,7 @@ ${renderRssTweet(quoteTweet, cfg)}
|
|||||||
#end if
|
#end if
|
||||||
#end proc
|
#end proc
|
||||||
#
|
#
|
||||||
#proc renderRssTweets(tweets: seq[Tweets]; cfg: Config; userId=""): string =
|
#proc renderRssTweets(tweets: seq[Tweets]; cfg: Config; prefs: Prefs; userId=""): string =
|
||||||
#let urlPrefix = getUrlPrefix(cfg)
|
#let urlPrefix = getUrlPrefix(cfg)
|
||||||
#var links: seq[string]
|
#var links: seq[string]
|
||||||
#for thread in tweets:
|
#for thread in tweets:
|
||||||
@@ -108,7 +108,7 @@ ${renderRssTweet(quoteTweet, cfg)}
|
|||||||
<item>
|
<item>
|
||||||
<title>${getTitle(tweet, retweet)}</title>
|
<title>${getTitle(tweet, retweet)}</title>
|
||||||
<dc:creator>@${tweet.user.username}</dc:creator>
|
<dc:creator>@${tweet.user.username}</dc:creator>
|
||||||
<description><![CDATA[${renderRssTweet(tweet, cfg).strip(chars={'\n'})}]]></description>
|
<description><![CDATA[${renderRssTweet(tweet, cfg, prefs).strip(chars={'\n'})}]]></description>
|
||||||
<pubDate>${getRfc822Time(tweet)}</pubDate>
|
<pubDate>${getRfc822Time(tweet)}</pubDate>
|
||||||
#if useGlobalGuid:
|
#if useGlobalGuid:
|
||||||
<guid isPermaLink="false">${tweet.id}</guid>
|
<guid isPermaLink="false">${tweet.id}</guid>
|
||||||
@@ -121,7 +121,7 @@ ${renderRssTweet(quoteTweet, cfg)}
|
|||||||
#end for
|
#end for
|
||||||
#end proc
|
#end proc
|
||||||
#
|
#
|
||||||
#proc renderTimelineRss*(profile: Profile; cfg: Config; multi=false): string =
|
#proc renderTimelineRss*(profile: Profile; cfg: Config; prefs: Prefs; multi=false): string =
|
||||||
#let urlPrefix = getUrlPrefix(cfg)
|
#let urlPrefix = getUrlPrefix(cfg)
|
||||||
#result = ""
|
#result = ""
|
||||||
#let handle = (if multi: "" else: "@") & profile.user.username
|
#let handle = (if multi: "" else: "@") & profile.user.username
|
||||||
@@ -147,13 +147,13 @@ ${renderRssTweet(quoteTweet, cfg)}
|
|||||||
</image>
|
</image>
|
||||||
#let tweetsList = getTweetsWithPinned(profile)
|
#let tweetsList = getTweetsWithPinned(profile)
|
||||||
#if tweetsList.len > 0:
|
#if tweetsList.len > 0:
|
||||||
${renderRssTweets(tweetsList, cfg, userId=profile.user.id)}
|
${renderRssTweets(tweetsList, cfg, prefs, userId=profile.user.id)}
|
||||||
#end if
|
#end if
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
#end proc
|
#end proc
|
||||||
#
|
#
|
||||||
#proc renderListRss*(tweets: seq[Tweets]; list: List; cfg: Config): string =
|
#proc renderListRss*(tweets: seq[Tweets]; list: List; cfg: Config; prefs: Prefs): string =
|
||||||
#let link = &"{getUrlPrefix(cfg)}/i/lists/{list.id}"
|
#let link = &"{getUrlPrefix(cfg)}/i/lists/{list.id}"
|
||||||
#result = ""
|
#result = ""
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@@ -165,12 +165,12 @@ ${renderRssTweets(tweetsList, cfg, userId=profile.user.id)}
|
|||||||
<description>${getDescription(&"{list.name} by @{list.username}", cfg)}</description>
|
<description>${getDescription(&"{list.name} by @{list.username}", cfg)}</description>
|
||||||
<language>en-us</language>
|
<language>en-us</language>
|
||||||
<ttl>40</ttl>
|
<ttl>40</ttl>
|
||||||
${renderRssTweets(tweets, cfg)}
|
${renderRssTweets(tweets, cfg, prefs)}
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
#end proc
|
#end proc
|
||||||
#
|
#
|
||||||
#proc renderSearchRss*(tweets: seq[Tweets]; name, param: string; cfg: Config): string =
|
#proc renderSearchRss*(tweets: seq[Tweets]; name, param: string; cfg: Config; prefs: Prefs): string =
|
||||||
#let link = &"{getUrlPrefix(cfg)}/search"
|
#let link = &"{getUrlPrefix(cfg)}/search"
|
||||||
#let escName = xmltree.escape(name)
|
#let escName = xmltree.escape(name)
|
||||||
#result = ""
|
#result = ""
|
||||||
@@ -183,7 +183,7 @@ ${renderRssTweets(tweets, cfg)}
|
|||||||
<description>${getDescription(&"Search \"{escName}\"", cfg)}</description>
|
<description>${getDescription(&"Search \"{escName}\"", cfg)}</description>
|
||||||
<language>en-us</language>
|
<language>en-us</language>
|
||||||
<ttl>40</ttl>
|
<ttl>40</ttl>
|
||||||
${renderRssTweets(tweets, cfg)}
|
${renderRssTweets(tweets, cfg, prefs)}
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
#end proc
|
#end proc
|
||||||
|
|||||||
Reference in New Issue
Block a user