mirror of https://github.com/zedeus/nitter
notes: implement lists, headers and twemoji
This commit is contained in:
parent
63bb30ead7
commit
7127054a12
|
@ -432,13 +432,14 @@ proc parseGraphArticle*(js: JsonNode): Article =
|
|||
|
||||
for p in content{"blocks"}:
|
||||
var paragraph = ArticleParagraph(
|
||||
text: p{"text"}.getStr
|
||||
text: p{"text"}.getStr,
|
||||
baseType: parseEnum[ArticleType](p{"type"}.getStr)
|
||||
)
|
||||
for sr in p{"inlineStyleRanges"}:
|
||||
paragraph.inlineStyleRanges.add ArticleStyleRange(
|
||||
offset: sr{"offset"}.getInt,
|
||||
length: sr{"length"}.getInt,
|
||||
style: sr{"style"}.getStr
|
||||
style: parseEnum[ArticleStyle](sr{"style"}.getStr)
|
||||
)
|
||||
for er in p{"entityRanges"}:
|
||||
paragraph.entityRanges.add ArticleEntityRange(
|
||||
|
@ -461,6 +462,8 @@ proc parseGraphArticle*(js: JsonNode): Article =
|
|||
entity.mediaIds.add jMedia{"mediaId"}.getStr
|
||||
of ArticleEntityType.tweet:
|
||||
entity.tweetId = jEntity{"data", "tweetId"}.getStr
|
||||
of ArticleEntityType.twemoji:
|
||||
entity.twemoji = jEntity{"data", "url"}.getStr
|
||||
else: discard
|
||||
|
||||
result.entities.add entity
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: var(--bg_panel);
|
||||
font-family: sans-serif;
|
||||
|
||||
article {
|
||||
padding: 20px;
|
||||
|
@ -18,9 +19,12 @@
|
|||
margin: 30px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
p,
|
||||
li {
|
||||
font-size: 18px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5em;
|
||||
margin: 30px 0;
|
||||
word-wrap: break-word;
|
||||
|
@ -37,6 +41,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
|
|
|
@ -127,13 +127,30 @@ type
|
|||
|
||||
ArticleParagraph* = object
|
||||
text*: string
|
||||
baseType*: ArticleType
|
||||
inlineStyleRanges*: seq[ArticleStyleRange]
|
||||
entityRanges*: seq[ArticleEntityRange]
|
||||
|
||||
ArticleType* {.pure.} = enum
|
||||
headerOne = "header-one"
|
||||
headerTwo = "header-two"
|
||||
headerThree = "header-three"
|
||||
orderedListItem = "ordered-list-item"
|
||||
unorderedListItem = "unordered-list-item"
|
||||
unstyled = "unstyled"
|
||||
atomic = "atomic"
|
||||
unknown
|
||||
|
||||
ArticleStyleRange* = object
|
||||
offset*: int
|
||||
length*: int
|
||||
style*: string
|
||||
style*: ArticleStyle
|
||||
|
||||
ArticleStyle* {.pure.} = enum
|
||||
bold = "BOLD"
|
||||
italic = "ITALIC"
|
||||
strikethrough = "STRIKETHROUGH"
|
||||
unknown
|
||||
|
||||
ArticleEntityRange* = object
|
||||
offset*: int
|
||||
|
@ -145,11 +162,13 @@ type
|
|||
url*: string
|
||||
mediaIds*: seq[string]
|
||||
tweetId*: string
|
||||
twemoji*: string
|
||||
|
||||
ArticleEntityType* {.pure.} = enum
|
||||
ArticleEntityType* {.pure.} = enum
|
||||
link = "LINK"
|
||||
media = "MEDIA"
|
||||
tweet = "TWEET"
|
||||
twemoji = "TWEMOJI"
|
||||
unknown
|
||||
|
||||
Poll* = object
|
||||
|
|
|
@ -21,11 +21,31 @@ proc renderMiniAvatar(user: User; prefs: Prefs): VNode =
|
|||
|
||||
proc renderNoteParagraph(articleParagraph: ArticleParagraph; article: Article): VNode =
|
||||
let text = articleParagraph.text
|
||||
result = p.newVNode()
|
||||
|
||||
if articleParagraph.inlineStyleRanges.len > 0:
|
||||
# Assume the style applies for the entire paragraph
|
||||
result.setAttr("style", "font-style:" & articleParagraph.inlineStyleRanges[0].style.toLowerAscii)
|
||||
case articleParagraph.baseType
|
||||
of ArticleType.headerOne:
|
||||
result = h1.newVNode()
|
||||
of ArticleType.headerTwo:
|
||||
result = h2.newVNode()
|
||||
of ArticleType.headerThree:
|
||||
result = h3.newVNode()
|
||||
of ArticleType.orderedListItem:
|
||||
result = li.newVNode()
|
||||
of ArticleType.unorderedListItem:
|
||||
result = li.newVNode()
|
||||
else:
|
||||
result = p.newVNode()
|
||||
|
||||
# Assume the style applies for the entire paragraph
|
||||
for styleRange in articleParagraph.inlineStyleRanges:
|
||||
case styleRange.style
|
||||
of ArticleStyle.bold:
|
||||
result.setAttr("style", "font-weight:bold")
|
||||
of ArticleStyle.italic:
|
||||
result.setAttr("style", "font-style:italic")
|
||||
of ArticleStyle.strikethrough:
|
||||
result.setAttr("style", "text-decoration:line-through")
|
||||
else: discard
|
||||
|
||||
var last = 0
|
||||
for er in articleParagraph.entityRanges:
|
||||
|
@ -45,6 +65,10 @@ proc renderNoteParagraph(articleParagraph: ArticleParagraph; article: Article):
|
|||
let image = buildHtml(span(class="image")):
|
||||
img(src=url, alt="")
|
||||
result.add image
|
||||
of ArticleEntityType.twemoji:
|
||||
let url = entity.twemoji
|
||||
let emoji = buildHtml(img(src=url, alt=""))
|
||||
result.add emoji
|
||||
of ArticleEntityType.tweet:
|
||||
let url = fmt"/i/status/{entity.tweetId}/embed"
|
||||
let iframe = buildHtml(iframe(src=url, loading="lazy", frameborder="0", style={maxWidth: "100%"}))
|
||||
|
@ -61,18 +85,47 @@ proc renderNote*(article: Article; prefs: Prefs): VNode =
|
|||
let cover = getSmallPic(article.coverImage)
|
||||
let author = article.user
|
||||
|
||||
buildHtml(tdiv(class="note")):
|
||||
img(class="cover", src=(cover), alt="")
|
||||
|
||||
article:
|
||||
h1: text article.title
|
||||
|
||||
tdiv(class="author"):
|
||||
renderMiniAvatar(author, prefs)
|
||||
linkUser(author, class="fullname")
|
||||
linkUser(author, class="username")
|
||||
text " · "
|
||||
text article.time.getShortTime
|
||||
# build header
|
||||
let main = buildHtml(article):
|
||||
h1: text article.title
|
||||
|
||||
tdiv(class="author"):
|
||||
renderMiniAvatar(author, prefs)
|
||||
linkUser(author, class="fullname")
|
||||
linkUser(author, class="username")
|
||||
text " · "
|
||||
text article.time.getShortTime
|
||||
|
||||
for paragraph in article.paragraphs:
|
||||
renderNoteParagraph(paragraph, article)
|
||||
# add paragraphs
|
||||
var listType = ArticleType.unknown
|
||||
var list: VNode = nil
|
||||
|
||||
for paragraph in article.paragraphs:
|
||||
let node = renderNoteParagraph(paragraph, article)
|
||||
|
||||
let currentType = paragraph.baseType
|
||||
if currentType in [ArticleType.orderedListItem, ArticleType.unorderedListItem]:
|
||||
if currentType != listType:
|
||||
# flush last list
|
||||
if list != nil:
|
||||
main.add list
|
||||
list = nil
|
||||
|
||||
case currentType:
|
||||
of ArticleType.orderedListItem:
|
||||
list = ol.newVNode()
|
||||
of ArticleType.unorderedListItem:
|
||||
list = ul.newVNode()
|
||||
else: discard
|
||||
listType = currentType
|
||||
list.add node
|
||||
else:
|
||||
if list != nil:
|
||||
main.add list
|
||||
list = nil
|
||||
main.add node
|
||||
|
||||
buildHtml(tdiv(class="note")):
|
||||
img(class="cover", src=(cover), alt="")
|
||||
|
||||
main
|
||||
|
|
Loading…
Reference in New Issue