mirror of
https://github.com/zedeus/nitter
synced 2026-09-05 22:59:31 +00:00
Merge branch 'master' into feature/mp4-streaming
This commit is contained in:
commit
93208908e6
50 changed files with 842 additions and 492 deletions
|
|
@ -15,7 +15,8 @@ proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string =
|
|||
let node = buildHtml(html(lang="en")):
|
||||
renderHead(prefs, cfg, req, video=vidUrl, images=(@[thumb]))
|
||||
|
||||
tdiv(class="embed-video"):
|
||||
renderVideo(get(tweet.video), prefs, "")
|
||||
body:
|
||||
tdiv(class="embed-video"):
|
||||
renderVideo(get(tweet.video), prefs, "")
|
||||
|
||||
result = doctype & $node
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
|||
|
||||
title:
|
||||
if titleText.len > 0:
|
||||
text &"{titleText}|{cfg.title}"
|
||||
text titleText & " | " & cfg.title
|
||||
else:
|
||||
text cfg.title
|
||||
|
||||
|
|
@ -98,9 +98,8 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
|
|||
link(rel="preload", type="image/png", href=bannerUrl, `as`="image")
|
||||
|
||||
for url in images:
|
||||
let suffix = if "400x400" in url or url.endsWith("placeholder.png"): ""
|
||||
else: "?name=small"
|
||||
let preloadUrl = getPicUrl(url & suffix)
|
||||
let preloadUrl = if "400x400" in url: getPicUrl(url)
|
||||
else: getSmallPic(url)
|
||||
link(rel="preload", type="image/png", href=preloadUrl, `as`="image")
|
||||
|
||||
let image = getUrlPrefix(cfg) & getPicUrl(url)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ proc renderUserCard*(user: User; prefs: Prefs): VNode =
|
|||
span:
|
||||
let url = replaceUrls(user.website, prefs)
|
||||
icon "link"
|
||||
a(href=url): text shortLink(url)
|
||||
a(href=url): text url.shortLink
|
||||
|
||||
tdiv(class="profile-joindate"):
|
||||
span(title=getJoinDateFull(user)):
|
||||
|
|
@ -108,7 +108,7 @@ proc renderProfile*(profile: var Profile; prefs: Prefs; path: string): VNode =
|
|||
renderBanner(profile.user.banner)
|
||||
|
||||
let sticky = if prefs.stickyProfile: " sticky" else: ""
|
||||
tdiv(class=(&"profile-tab{sticky}")):
|
||||
tdiv(class=("profile-tab" & sticky)):
|
||||
renderUserCard(profile.user, prefs)
|
||||
if profile.photoRail.len > 0:
|
||||
renderPhotoRail(profile)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@ import strutils, strformat
|
|||
import karax/[karaxdsl, vdom, vstyles]
|
||||
import ".."/[types, utils]
|
||||
|
||||
const smallWebp* = "?name=small&format=webp"
|
||||
|
||||
proc getSmallPic*(url: string): string =
|
||||
result = url
|
||||
if "?" notin url and not url.endsWith("placeholder.png"):
|
||||
result &= smallWebp
|
||||
result = getPicUrl(result)
|
||||
|
||||
proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode =
|
||||
var c = "icon-" & icon
|
||||
if class.len > 0: c = &"{c} {class}"
|
||||
|
|
@ -51,29 +59,23 @@ proc buttonReferer*(action, text, path: string; class=""; `method`="post"): VNod
|
|||
proc genCheckbox*(pref, label: string; state: bool): VNode =
|
||||
buildHtml(label(class="pref-group checkbox-container")):
|
||||
text label
|
||||
if state: input(name=pref, `type`="checkbox", checked="")
|
||||
else: input(name=pref, `type`="checkbox")
|
||||
input(name=pref, `type`="checkbox", checked=state)
|
||||
span(class="checkbox")
|
||||
|
||||
proc genInput*(pref, label, state, placeholder: string; class=""): VNode =
|
||||
proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=true): VNode =
|
||||
let p = placeholder
|
||||
buildHtml(tdiv(class=("pref-group pref-input " & class))):
|
||||
if label.len > 0:
|
||||
label(`for`=pref): text label
|
||||
if state.len == 0:
|
||||
input(name=pref, `type`="text", placeholder=p, value=state, autofocus="")
|
||||
else:
|
||||
input(name=pref, `type`="text", placeholder=p, value=state)
|
||||
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 =
|
||||
buildHtml(tdiv(class="pref-group pref-input")):
|
||||
label(`for`=pref): text label
|
||||
select(name=pref):
|
||||
for opt in options:
|
||||
if opt == state:
|
||||
option(value=opt, selected=""): text opt
|
||||
else:
|
||||
option(value=opt): text opt
|
||||
option(value=opt, selected=(opt == state)):
|
||||
text opt
|
||||
|
||||
proc genDate*(pref, state: string): VNode =
|
||||
buildHtml(span(class="date-input")):
|
||||
|
|
@ -85,12 +87,9 @@ proc genImg*(url: string; class=""): VNode =
|
|||
img(src=getPicUrl(url), class=class, alt="", loading="lazy", decoding="async")
|
||||
|
||||
proc getTabClass*(query: Query; tab: QueryKind): string =
|
||||
result = "tab-item"
|
||||
if query.kind == tab:
|
||||
result &= " active"
|
||||
if query.kind == tab: "tab-item active"
|
||||
else: "tab-item"
|
||||
|
||||
proc getAvatarClass*(prefs: Prefs): string =
|
||||
if prefs.squareAvatars:
|
||||
"avatar"
|
||||
else:
|
||||
"avatar round"
|
||||
if prefs.squareAvatars: "avatar"
|
||||
else: "avatar round"
|
||||
|
|
|
|||
|
|
@ -63,12 +63,10 @@ proc renderSearchPanel*(query: Query): VNode =
|
|||
hiddenField("f", "tweets")
|
||||
genInput("q", "", query.text, "Enter search...", class="pref-inline")
|
||||
button(`type`="submit"): icon "search"
|
||||
if isPanelOpen(query):
|
||||
input(id="search-panel-toggle", `type`="checkbox", checked="")
|
||||
else:
|
||||
input(id="search-panel-toggle", `type`="checkbox")
|
||||
label(`for`="search-panel-toggle"):
|
||||
icon "down"
|
||||
|
||||
input(id="search-panel-toggle", `type`="checkbox", checked=isPanelOpen(query))
|
||||
label(`for`="search-panel-toggle"): icon "down"
|
||||
|
||||
tdiv(class="search-panel"):
|
||||
for f in @["filter", "exclude"]:
|
||||
span(class="search-title"): text capitalize(f)
|
||||
|
|
@ -88,7 +86,7 @@ proc renderSearchPanel*(query: Query): VNode =
|
|||
genDate("until", query.until)
|
||||
tdiv:
|
||||
span(class="search-title"): text "Near"
|
||||
genInput("near", "", query.near, placeholder="Location...")
|
||||
genInput("near", "", query.near, "Location...", autofocus=false)
|
||||
|
||||
proc renderTweetSearch*(results: Result[Tweet]; prefs: Prefs; path: string;
|
||||
pinned=none(Tweet)): VNode =
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ import renderutils
|
|||
import ".."/[types, utils, formatters]
|
||||
import general
|
||||
|
||||
proc getSmallPic(url: string): string =
|
||||
result = url
|
||||
if "?" notin url and not url.endsWith("placeholder.png"):
|
||||
result &= "?name=small"
|
||||
result = getPicUrl(result)
|
||||
const doctype = "<!DOCTYPE html>\n"
|
||||
|
||||
proc renderMiniAvatar(user: User; prefs: Prefs): VNode =
|
||||
let url = getPicUrl(user.getUserPic("_mini"))
|
||||
|
|
@ -57,9 +53,8 @@ proc renderAlbum(tweet: Tweet): VNode =
|
|||
tdiv(class="attachment image"):
|
||||
let
|
||||
named = "name=" in photo
|
||||
orig = photo
|
||||
small = if named: photo else: photo & "?name=small"
|
||||
a(href=getOrigPicUrl(orig), class="still-image", target="_blank"):
|
||||
small = if named: photo else: photo & smallWebp
|
||||
a(href=getOrigPicUrl(photo), class="still-image", target="_blank"):
|
||||
genImg(small)
|
||||
|
||||
proc isPlaybackEnabled(prefs: Prefs; playbackType: VideoType): bool =
|
||||
|
|
@ -106,12 +101,10 @@ proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode =
|
|||
else: vidUrl
|
||||
case playbackType
|
||||
of mp4:
|
||||
if prefs.muteVideos:
|
||||
video(src=source, poster=thumb, controls="", preload="none", muted=""):
|
||||
else:
|
||||
video(src=source, poster=thumb, controls="", preload="none"):
|
||||
video(poster=thumb, controls="", muted=prefs.muteVideos):
|
||||
source(src=source, `type`="video/mp4")
|
||||
of m3u8, vmap:
|
||||
video(poster=thumb, data-url=source, data-autoload="false")
|
||||
video(poster=thumb, data-url=source, data-autoload="false", muted=prefs.muteVideos)
|
||||
verbatim "<div class=\"video-overlay\" onclick=\"playVideo(this)\">"
|
||||
tdiv(class="overlay-circle"): span(class="overlay-triangle")
|
||||
verbatim "</div>"
|
||||
|
|
@ -132,12 +125,9 @@ proc renderGif(gif: Gif; prefs: Prefs): VNode =
|
|||
buildHtml(tdiv(class="attachments media-gif")):
|
||||
tdiv(class="gallery-gif", style={maxHeight: "unset"}):
|
||||
tdiv(class="attachment"):
|
||||
let thumb = getSmallPic(gif.thumb)
|
||||
let url = getPicUrl(gif.url)
|
||||
if prefs.autoplayGifs:
|
||||
video(src=url, class="gif", poster=thumb, controls="", muted="", loop="", playsinline="", autoplay="")
|
||||
else:
|
||||
video(src=url, class="gif", poster=thumb, controls="", muted="", loop="", playsinline="")
|
||||
video(class="gif", poster=getSmallPic(gif.thumb), autoplay=prefs.autoplayGifs,
|
||||
controls="", muted="", loop=""):
|
||||
source(src=getPicUrl(gif.url), `type`="video/mp4")
|
||||
|
||||
proc renderPoll(poll: Poll): VNode =
|
||||
buildHtml(tdiv(class="poll")):
|
||||
|
|
@ -331,7 +321,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
|
|||
if tweet.attribution.isSome:
|
||||
renderAttribution(tweet.attribution.get(), prefs)
|
||||
|
||||
if tweet.card.isSome:
|
||||
if tweet.card.isSome and tweet.card.get().kind != hidden:
|
||||
renderCard(tweet.card.get(), prefs, path)
|
||||
|
||||
if tweet.photos.len > 0:
|
||||
|
|
@ -350,7 +340,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
|
|||
renderQuote(tweet.quote.get(), prefs, path)
|
||||
|
||||
if mainTweet:
|
||||
p(class="tweet-published"): text &"{getTime(tweet)} · {tweet.source}"
|
||||
p(class="tweet-published"): text &"{getTime(tweet)}"
|
||||
|
||||
if tweet.mediaTags.len > 0:
|
||||
renderMediaTags(tweet.mediaTags)
|
||||
|
|
@ -362,7 +352,12 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
|
|||
a(class="show-thread", href=("/i/status/" & $tweet.threadId)):
|
||||
text "Show this thread"
|
||||
|
||||
proc renderTweetEmbed*(tweet: Tweet; path: string; prefs: Prefs; cfg: Config; req: Request): VNode =
|
||||
buildHtml(tdiv(class="tweet-embed")):
|
||||
proc renderTweetEmbed*(tweet: Tweet; path: string; prefs: Prefs; cfg: Config; req: Request): string =
|
||||
let node = buildHtml(html(lang="en")):
|
||||
renderHead(prefs, cfg, req)
|
||||
renderTweet(tweet, prefs, path, mainTweet=true)
|
||||
|
||||
body:
|
||||
tdiv(class="tweet-embed"):
|
||||
renderTweet(tweet, prefs, path, mainTweet=true)
|
||||
|
||||
result = doctype & $node
|
||||
|
|
|
|||
Loading…
Reference in a new issue