Improves post rendering to support multiple load more buttons.
This commit is contained in:
parent
e13a4425f7
commit
8b01e452b6
3 changed files with 116 additions and 70 deletions
83
forum.nim
83
forum.nim
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue