general.nim 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import uri, strutils, strformat
  2. import karax/[karaxdsl, vdom]
  3. import renderutils
  4. import ../utils, ../types, ../prefs, ../formatters
  5. import jester
  6. const
  7. doctype = "<!DOCTYPE html>\n"
  8. lp = readFile("public/lp.svg")
  9. proc renderNavbar*(title, rss: string; req: Request): VNode =
  10. let twitterPath = getTwitterLink(req.path, req.params)
  11. var path = $(parseUri(req.path) ? filterParams(req.params))
  12. if "/status" in path: path.add "#m"
  13. buildHtml(nav):
  14. tdiv(class="inner-nav"):
  15. tdiv(class="nav-item"):
  16. a(class="site-name", href="/"): text title
  17. a(href="/"): img(class="site-logo", src="/logo.png")
  18. tdiv(class="nav-item right"):
  19. icon "search", title="Search", href="/search"
  20. if rss.len > 0:
  21. icon "rss-feed", title="RSS Feed", href=rss
  22. icon "bird", title="Open in Twitter", href=twitterPath
  23. a(href="https://liberapay.com/zedeus"): verbatim lp
  24. icon "info", title="About", href="/about"
  25. iconReferer "cog", "/settings", path, title="Preferences"
  26. proc renderHead*(prefs: Prefs; cfg: Config; titleText=""; desc=""; video="";
  27. images: seq[string] = @[]; banner=""; ogTitle=""; theme="";
  28. rss=""): VNode =
  29. let ogType =
  30. if video.len > 0: "video"
  31. elif rss.len > 0: "object"
  32. elif images.len > 0: "photo"
  33. else: "article"
  34. let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
  35. buildHtml(head):
  36. link(rel="stylesheet", type="text/css", href="/css/style.css?v=3")
  37. link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2")
  38. if theme.len > 0:
  39. link(rel="stylesheet", type="text/css", href=(&"/css/themes/{theme}.css"))
  40. link(rel="apple-touch-icon", sizes="180x180", href="/apple-touch-icon.png")
  41. link(rel="icon", type="image/png", sizes="32x32", href="/favicon-32x32.png")
  42. link(rel="icon", type="image/png", sizes="16x16", href="/favicon-16x16.png")
  43. link(rel="manifest", href="/site.webmanifest")
  44. link(rel="mask-icon", href="/safari-pinned-tab.svg", color="#ff6c60")
  45. link(rel="search", type="application/opensearchdescription+xml", title=cfg.title,
  46. href=opensearchUrl)
  47. if rss.len > 0:
  48. link(rel="alternate", type="application/rss+xml", href=rss, title="RSS feed")
  49. if prefs.hlsPlayback:
  50. script(src="/js/hls.light.min.js", `defer`="")
  51. script(src="/js/hlsPlayback.js", `defer`="")
  52. if prefs.infiniteScroll:
  53. script(src="/js/infiniteScroll.js", `defer`="")
  54. title:
  55. if titleText.len > 0:
  56. text titleText & " | " & cfg.title
  57. else:
  58. text cfg.title
  59. meta(name="viewport", content="width=device-width, initial-scale=1.0")
  60. meta(property="og:type", content=ogType)
  61. meta(property="og:title", content=(if ogTitle.len > 0: ogTitle else: titleText))
  62. meta(property="og:description", content=stripHtml(desc))
  63. meta(property="og:site_name", content="Nitter")
  64. meta(property="og:locale", content="en_US")
  65. if banner.len > 0:
  66. let bannerUrl = getPicUrl(banner)
  67. link(rel="preload", type="image/png", href=getPicUrl(banner), `as`="image")
  68. for url in images:
  69. let suffix = if "400x400" in url: "" else: "?name=small"
  70. let preloadUrl = getPicUrl(url & suffix)
  71. link(rel="preload", type="image/png", href=preloadUrl, `as`="image")
  72. let image = getUrlPrefix(cfg) & getPicUrl(url)
  73. meta(property="og:image", content=image)
  74. meta(property="twitter:image:src", content=image)
  75. if rss.len > 0:
  76. meta(property="twitter:card", content="summary")
  77. else:
  78. meta(property="twitter:card", content="summary_large_image")
  79. if video.len > 0:
  80. meta(property="og:video:url", content=video)
  81. meta(property="og:video:secure_url", content=video)
  82. meta(property="og:video:type", content="text/html")
  83. # this is last so images are also preloaded
  84. # if this is done earlier, Chrome only preloads one image for some reason
  85. link(rel="preload", type="font/woff2", `as`="font",
  86. href="/fonts/fontello.woff2?21002321", crossorigin="anonymous")
  87. proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
  88. titleText=""; desc=""; ogTitle=""; rss=""; video="";
  89. images: seq[string] = @[]; banner=""): string =
  90. var theme = toLowerAscii(prefs.theme).replace(" ", "_")
  91. if "theme" in req.params:
  92. theme = toLowerAscii(req.params["theme"]).replace(" ", "_")
  93. let node = buildHtml(html(lang="en")):
  94. renderHead(prefs, cfg, titleText, desc, video, images, banner, ogTitle, theme, rss)
  95. body:
  96. renderNavbar(cfg.title, rss, req)
  97. tdiv(class="container"):
  98. body
  99. result = doctype & $node
  100. proc renderError*(error: string): VNode =
  101. buildHtml(tdiv(class="panel-container")):
  102. tdiv(class="error-panel"):
  103. span: verbatim error