notes: implement lists, headers and twemoji

This commit is contained in:
HookedBehemoth 2022-06-29 00:35:49 +02:00
parent 63bb30ead7
commit 7127054a12
4 changed files with 107 additions and 24 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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