Improves post rendering to support multiple load more buttons.

This commit is contained in:
Dominik Picheta 2018-05-13 22:59:29 +01:00
commit 8b01e452b6
3 changed files with 116 additions and 70 deletions

View file

@ -9,7 +9,7 @@
import
os, strutils, times, md5, strtabs, math, db_sqlite,
scgi, jester, asyncdispatch, asyncnet, cache, sequtils,
parseutils, utils, random, rst, ranks, recaptcha, json, re
parseutils, utils, random, rst, ranks, recaptcha, json, re, sugar
import cgi except setCookie
import options
@ -1026,6 +1026,20 @@ proc selectUser(userRow: seq[string]): threadlist.User =
isOnline: isOnline
)
proc selectPost(postRow: seq[string], skippedPosts: seq[int]): Post =
return Post(
id: postRow[0].parseInt,
author: selectUser(@[postRow[4], postRow[5], postRow[6]]),
likes: @[], # TODO:
seen: false, # TODO:
history: @[], # TODO:
info: PostInfo(
creation: postRow[2].parseInt,
content: postRow[1].rstToHtml()
),
moreBefore: skippedPosts
)
proc selectThread(threadRow: seq[string]): Thread =
const postsQuery =
sql"""select count(*), strftime('%s', creation) from post
@ -1101,9 +1115,10 @@ routes:
createTFD()
var
id = getInt(@"id", -1)
start = getInt(@"start", 0)
count = getInt(@"count", 5)
anchor = getInt(@"anchor", -1)
cond id != -1
const
count = 10
const threadsQuery =
sql"""select id, name, views, strftime('%s', modified) from thread
@ -1124,34 +1139,50 @@ routes:
from post p, person u
where u.id = p.author and p.thread = ? and $#
and (u.status <> 'Spammer' or p.author = ?)
order by p.id limit ?, ?""" % modClause
order by p.id""" % modClause
)
let pstCount = getValue(
db,
sql"select count(*) from post where thread = ?;",
id
).parseInt()
let moreCount = max(0, pstCount - (start + count))
var list = PostList(
posts: @[],
history: @[],
thread: thread,
moreCount: moreCount)
for post in getAllRows(db, postsQuery, id, c.userId, c.userId,
start, count):
list.posts.add(Post(
id: post[0].parseInt,
author: selectUser(@[post[4], post[5], post[6]]),
likes: @[], # TODO:
seen: false, # TODO:
history: @[], # TODO:
info: PostInfo(
creation: post[2].parseInt,
content: post[1].rstToHtml()
)
))
thread: thread
)
let rows = getAllRows(db, postsQuery, id, c.userId, c.userId)
var skippedPosts: seq[int] = @[]
for i in 0 ..< rows.len:
let id = rows[i][0].parseInt
let addDetail = i < count or rows.len-i < count or id == anchor
if addDetail:
let post = selectPost(rows[i], skippedPosts)
list.posts.add(post)
skippedPosts = @[]
else:
skippedPosts.add(id)
resp $(%list), "application/json"
get "/karax/specific_posts.json":
createTFD()
var
ids = parseJson(@"ids")
cond ids.kind == JArray
let intIDs = ids.elems.map(x => x.getInt())
let postsQuery = sql("""
select p.id, p.content, strftime('%s', p.creation), p.author,
u.name, u.email, strftime('%s', u.lastOnline)
from post p, person u
where u.id = p.author and p.id in ($#)
order by p.id;
""" % intIDs.join(",")) # TODO: It's horrible that I have to do this.
var list: seq[Post] = @[]
for row in db.getAllRows(postsQuery):
list.add(selectPost(row, @[]))
resp $(%list), "application/json"

View file

@ -8,7 +8,7 @@ type
creation*: int64
content*: string
Post* = object
Post* = ref object
id*: int
author*: User
likes*: seq[User] ## Users that liked this post.
@ -19,7 +19,7 @@ type
history*: seq[PostInfo] ## If the post was edited this will contain the
## older versions of the post.
info*: PostInfo
moreBefore*: seq[int]
when defined(js):
import karaxutils

View file

@ -10,7 +10,6 @@ type
## older versions of the thread (title/category
## changes).
posts*: seq[Post]
moreCount*: int
when defined(js):
include karax/prelude
@ -38,7 +37,7 @@ when defined(js):
var
state = newState()
proc onPostList(httpStatus: int, response: kstring, start: int) =
proc onPostList(httpStatus: int, response: kstring) =
state.loading = false
state.status = httpStatus.HttpCode
if state.status != Http200: return
@ -46,20 +45,62 @@ when defined(js):
let parsed = parseJson($response)
let list = to(parsed, PostList)
if state.list.isSome and state.list.get().thread.id == list.thread.id:
var old = state.list.get()
for i in 0..<list.posts.len:
old.posts.insert(list.posts[i], i+start)
state.list = some(list)
state.list = some(list)
state.list.get().posts = old.posts
else:
state.list = some(list)
proc onMorePosts(httpStatus: int, response: kstring, start: int, post: Post) =
state.loading = false
state.status = httpStatus.HttpCode
if state.status != Http200: return
let parsed = parseJson($response)
var list = to(parsed, seq[Post])
var idsLoaded: seq[int] = @[]
for i in 0..<list.len:
state.list.get().posts.insert(list[i], i+start)
idsLoaded.add(list[i].id)
# Save a list of the IDs which have not yet been loaded into the top-most
# post.
for id in post.moreBefore:
if id notin idsLoaded:
state.list.get().posts[start].moreBefore.add(id)
post.moreBefore = @[]
proc onReplyClick(e: Event, n: VNode, p: Option[Post]) =
state.replyingTo = p
state.replyBox.show()
proc onLoadMore(ev: Event, n: VNode, start: int, post: Post) =
if state.loading: return
state.loading = true
let ids = post.moreBefore # TODO: Don't load all!
let uri = makeUri(
"specific_posts.json",
[("ids", $(%ids))]
)
ajaxGet(
uri,
@[],
(s: int, r: kstring) => onMorePosts(s, r, start, post)
)
proc genLoadMore(post: Post, start: int): VNode =
result = buildHtml():
tdiv(class="information load-more-posts",
onClick=(e: Event, n: VNode) => onLoadMore(e, n, start, post)):
tdiv(class="information-icon"):
italic(class="fas fa-comment-dots")
tdiv(class="information-main"):
if state.loading:
tdiv(class="loading loading-lg")
else:
tdiv(class="information-title"):
text "Load more posts "
span(class="more-post-count"):
text "(" & $post.moreBefore.len & ")"
proc genPost(post: Post, thread: Thread, isLoggedIn: bool): VNode =
let postCopy = post # TODO: Another workaround here, closure capture :(
result = buildHtml():
@ -94,31 +135,6 @@ when defined(js):
italic(class="fas fa-reply")
text " Reply"
proc onLoadMore(ev: Event, n: VNode) =
if state.loading: return
state.loading = true
let start = n.getAttr("data-start").parseInt()
let threadId = state.list.get().thread.id
let uri = makeUri("posts.json", [("start", $start), ("id", $threadId)])
ajaxGet(uri, @[], (s: int, r: kstring) => onPostList(s, r, start))
proc genLoadMore(start: int): VNode =
result = buildHtml():
tdiv(class="information load-more-posts",
onClick=onLoadMore,
"data-start" = $start):
tdiv(class="information-icon"):
italic(class="fas fa-comment-dots")
tdiv(class="information-main"):
if state.loading:
tdiv(class="loading loading-lg")
else:
tdiv(class="information-title"):
text "Load more posts "
span(class="more-post-count"):
text "(" & $state.list.get().moreCount & ")"
proc genTimePassed(prevPost: Post, post: Option[Post]): VNode =
var latestTime =
if post.isSome: post.get().info.creation.fromUnix()
@ -153,7 +169,7 @@ when defined(js):
if state.list.isNone or state.list.get().thread.id != threadId:
let uri = makeUri("posts.json", ("id", $threadId))
ajaxGet(uri, @[], (s: int, r: kstring) => onPostList(s, r, 0))
ajaxGet(uri, @[], (s: int, r: kstring) => onPostList(s, r))
return buildHtml(tdiv(class="loading loading-lg"))
@ -165,16 +181,15 @@ when defined(js):
render(list.thread.category)
tdiv(class="posts"):
var prevPost: Option[Post] = none[Post]()
for post in list.posts:
for i, post in list.posts:
if prevPost.isSome:
genTimePassed(prevPost.get(), some(post))
if post.moreBefore.len > 0:
genLoadMore(post, i)
genPost(post, list.thread, isLoggedIn)
prevPost = some(post)
let hasMore = list.moreCount > 0
if hasMore:
genLoadMore(list.posts.len)
elif prevPost.isSome:
if prevPost.isSome:
genTimePassed(prevPost.get(), none[Post]())
render(state.replyBox, list.thread, state.replyingTo, hasMore)
render(state.replyBox, list.thread, state.replyingTo, false)