|
| 1 | +# SPDX-License-Identifier: AGPL-3.0-only |
| 2 | +import strutils, tables, strformat |
| 3 | +import karax/[karaxdsl, vdom, vstyles] |
| 4 | +from jester import Request |
| 5 | + |
| 6 | +import renderutils |
| 7 | +import ".."/[types, utils, formatters] |
| 8 | + |
| 9 | +const doctype = "<!DOCTYPE html>\n" |
| 10 | + |
| 11 | +proc getSmallPic(url: string): string = |
| 12 | + result = url |
| 13 | + if "?" notin url and not url.endsWith("placeholder.png"): |
| 14 | + result &= "?name=small" |
| 15 | + result = getPicUrl(result) |
| 16 | + |
| 17 | +proc renderMiniAvatar(user: User; prefs: Prefs): VNode = |
| 18 | + let url = getPicUrl(user.getUserPic("_mini")) |
| 19 | + buildHtml(): |
| 20 | + img(class=(prefs.getAvatarClass & " mini"), src=url) |
| 21 | + |
| 22 | +proc renderNoteParagraph(articleParagraph: ArticleParagraph; article: Article): VNode = |
| 23 | + let text = articleParagraph.text |
| 24 | + result = p.newVNode() |
| 25 | + |
| 26 | + if articleParagraph.inlineStyleRanges.len > 0: |
| 27 | + # Assume the style applies for the entire paragraph |
| 28 | + result.setAttr("style", "font-style:" & articleParagraph.inlineStyleRanges[0].style.toLowerAscii) |
| 29 | + |
| 30 | + var last = 0 |
| 31 | + for er in articleParagraph.entityRanges: |
| 32 | + # flush remaining text |
| 33 | + if er.offset > last: |
| 34 | + result.add text text.substr(last, er.offset - 1) |
| 35 | + |
| 36 | + let entity = article.entities[er.key] |
| 37 | + case entity.entityType |
| 38 | + of ArticleEntityType.link: |
| 39 | + let link = buildHtml(a(href=entity.url)): |
| 40 | + text text.substr(er.offset, er.offset + er.length - 1) |
| 41 | + result.add link |
| 42 | + of ArticleEntityType.media: |
| 43 | + for id in entity.mediaIds: |
| 44 | + let url: string = article.media[id] |
| 45 | + let image = buildHtml(span(class="image")): |
| 46 | + img(src=url, alt="") |
| 47 | + result.add image |
| 48 | + of ArticleEntityType.tweet: |
| 49 | + let url = fmt"/i/status/{entity.tweetId}/embed" |
| 50 | + let iframe = buildHtml(iframe(src=url, loading="lazy", frameborder="0", style={maxWidth: "100%"})) |
| 51 | + result.add iframe |
| 52 | + else: discard |
| 53 | + |
| 54 | + last = er.offset + er.length |
| 55 | + |
| 56 | + # flush remaining text |
| 57 | + if last < text.len: |
| 58 | + result.add text text.substr(last) |
| 59 | + |
| 60 | +proc renderNote*(article: Article; prefs: Prefs): VNode = |
| 61 | + let cover = getSmallPic(article.coverImage) |
| 62 | + let author = article.user |
| 63 | + |
| 64 | + buildHtml(tdiv(class="note")): |
| 65 | + img(class="cover", src=(cover), alt="") |
| 66 | + |
| 67 | + article: |
| 68 | + h1: text article.title |
| 69 | + |
| 70 | + tdiv(class="author"): |
| 71 | + renderMiniAvatar(author, prefs) |
| 72 | + linkUser(author, class="fullname") |
| 73 | + linkUser(author, class="username") |
| 74 | + text " · " |
| 75 | + text article.time.getShortTime |
| 76 | + |
| 77 | + for paragraph in article.paragraphs: |
| 78 | + renderNoteParagraph(paragraph, article) |
0 commit comments