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
|
import
|
||||||
os, strutils, times, md5, strtabs, math, db_sqlite,
|
os, strutils, times, md5, strtabs, math, db_sqlite,
|
||||||
scgi, jester, asyncdispatch, asyncnet, cache, sequtils,
|
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 cgi except setCookie
|
||||||
import options
|
import options
|
||||||
|
|
||||||
|
|
@ -1026,6 +1026,20 @@ proc selectUser(userRow: seq[string]): threadlist.User =
|
||||||
isOnline: isOnline
|
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 =
|
proc selectThread(threadRow: seq[string]): Thread =
|
||||||
const postsQuery =
|
const postsQuery =
|
||||||
sql"""select count(*), strftime('%s', creation) from post
|
sql"""select count(*), strftime('%s', creation) from post
|
||||||
|
|
@ -1101,9 +1115,10 @@ routes:
|
||||||
createTFD()
|
createTFD()
|
||||||
var
|
var
|
||||||
id = getInt(@"id", -1)
|
id = getInt(@"id", -1)
|
||||||
start = getInt(@"start", 0)
|
anchor = getInt(@"anchor", -1)
|
||||||
count = getInt(@"count", 5)
|
|
||||||
cond id != -1
|
cond id != -1
|
||||||
|
const
|
||||||
|
count = 10
|
||||||
|
|
||||||
const threadsQuery =
|
const threadsQuery =
|
||||||
sql"""select id, name, views, strftime('%s', modified) from thread
|
sql"""select id, name, views, strftime('%s', modified) from thread
|
||||||
|
|
@ -1124,34 +1139,50 @@ routes:
|
||||||
from post p, person u
|
from post p, person u
|
||||||
where u.id = p.author and p.thread = ? and $#
|
where u.id = p.author and p.thread = ? and $#
|
||||||
and (u.status <> 'Spammer' or p.author = ?)
|
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(
|
var list = PostList(
|
||||||
posts: @[],
|
posts: @[],
|
||||||
history: @[],
|
history: @[],
|
||||||
thread: thread,
|
thread: thread
|
||||||
moreCount: moreCount)
|
)
|
||||||
for post in getAllRows(db, postsQuery, id, c.userId, c.userId,
|
let rows = getAllRows(db, postsQuery, id, c.userId, c.userId)
|
||||||
start, count):
|
|
||||||
list.posts.add(Post(
|
var skippedPosts: seq[int] = @[]
|
||||||
id: post[0].parseInt,
|
for i in 0 ..< rows.len:
|
||||||
author: selectUser(@[post[4], post[5], post[6]]),
|
let id = rows[i][0].parseInt
|
||||||
likes: @[], # TODO:
|
|
||||||
seen: false, # TODO:
|
let addDetail = i < count or rows.len-i < count or id == anchor
|
||||||
history: @[], # TODO:
|
|
||||||
info: PostInfo(
|
if addDetail:
|
||||||
creation: post[2].parseInt,
|
let post = selectPost(rows[i], skippedPosts)
|
||||||
content: post[1].rstToHtml()
|
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"
|
resp $(%list), "application/json"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ type
|
||||||
creation*: int64
|
creation*: int64
|
||||||
content*: string
|
content*: string
|
||||||
|
|
||||||
Post* = object
|
Post* = ref object
|
||||||
id*: int
|
id*: int
|
||||||
author*: User
|
author*: User
|
||||||
likes*: seq[User] ## Users that liked this post.
|
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
|
history*: seq[PostInfo] ## If the post was edited this will contain the
|
||||||
## older versions of the post.
|
## older versions of the post.
|
||||||
info*: PostInfo
|
info*: PostInfo
|
||||||
|
moreBefore*: seq[int]
|
||||||
|
|
||||||
when defined(js):
|
when defined(js):
|
||||||
import karaxutils
|
import karaxutils
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ type
|
||||||
## older versions of the thread (title/category
|
## older versions of the thread (title/category
|
||||||
## changes).
|
## changes).
|
||||||
posts*: seq[Post]
|
posts*: seq[Post]
|
||||||
moreCount*: int
|
|
||||||
|
|
||||||
when defined(js):
|
when defined(js):
|
||||||
include karax/prelude
|
include karax/prelude
|
||||||
|
|
@ -38,7 +37,7 @@ when defined(js):
|
||||||
var
|
var
|
||||||
state = newState()
|
state = newState()
|
||||||
|
|
||||||
proc onPostList(httpStatus: int, response: kstring, start: int) =
|
proc onPostList(httpStatus: int, response: kstring) =
|
||||||
state.loading = false
|
state.loading = false
|
||||||
state.status = httpStatus.HttpCode
|
state.status = httpStatus.HttpCode
|
||||||
if state.status != Http200: return
|
if state.status != Http200: return
|
||||||
|
|
@ -46,20 +45,62 @@ when defined(js):
|
||||||
let parsed = parseJson($response)
|
let parsed = parseJson($response)
|
||||||
let list = to(parsed, PostList)
|
let list = to(parsed, PostList)
|
||||||
|
|
||||||
if state.list.isSome and state.list.get().thread.id == list.thread.id:
|
state.list = some(list)
|
||||||
var old = state.list.get()
|
|
||||||
for i in 0..<list.posts.len:
|
|
||||||
old.posts.insert(list.posts[i], i+start)
|
|
||||||
|
|
||||||
state.list = some(list)
|
proc onMorePosts(httpStatus: int, response: kstring, start: int, post: Post) =
|
||||||
state.list.get().posts = old.posts
|
state.loading = false
|
||||||
else:
|
state.status = httpStatus.HttpCode
|
||||||
state.list = some(list)
|
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]) =
|
proc onReplyClick(e: Event, n: VNode, p: Option[Post]) =
|
||||||
state.replyingTo = p
|
state.replyingTo = p
|
||||||
state.replyBox.show()
|
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 =
|
proc genPost(post: Post, thread: Thread, isLoggedIn: bool): VNode =
|
||||||
let postCopy = post # TODO: Another workaround here, closure capture :(
|
let postCopy = post # TODO: Another workaround here, closure capture :(
|
||||||
result = buildHtml():
|
result = buildHtml():
|
||||||
|
|
@ -94,31 +135,6 @@ when defined(js):
|
||||||
italic(class="fas fa-reply")
|
italic(class="fas fa-reply")
|
||||||
text " 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 =
|
proc genTimePassed(prevPost: Post, post: Option[Post]): VNode =
|
||||||
var latestTime =
|
var latestTime =
|
||||||
if post.isSome: post.get().info.creation.fromUnix()
|
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:
|
if state.list.isNone or state.list.get().thread.id != threadId:
|
||||||
let uri = makeUri("posts.json", ("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"))
|
return buildHtml(tdiv(class="loading loading-lg"))
|
||||||
|
|
||||||
|
|
@ -165,16 +181,15 @@ when defined(js):
|
||||||
render(list.thread.category)
|
render(list.thread.category)
|
||||||
tdiv(class="posts"):
|
tdiv(class="posts"):
|
||||||
var prevPost: Option[Post] = none[Post]()
|
var prevPost: Option[Post] = none[Post]()
|
||||||
for post in list.posts:
|
for i, post in list.posts:
|
||||||
if prevPost.isSome:
|
if prevPost.isSome:
|
||||||
genTimePassed(prevPost.get(), some(post))
|
genTimePassed(prevPost.get(), some(post))
|
||||||
|
if post.moreBefore.len > 0:
|
||||||
|
genLoadMore(post, i)
|
||||||
genPost(post, list.thread, isLoggedIn)
|
genPost(post, list.thread, isLoggedIn)
|
||||||
prevPost = some(post)
|
prevPost = some(post)
|
||||||
|
|
||||||
let hasMore = list.moreCount > 0
|
if prevPost.isSome:
|
||||||
if hasMore:
|
|
||||||
genLoadMore(list.posts.len)
|
|
||||||
elif prevPost.isSome:
|
|
||||||
genTimePassed(prevPost.get(), none[Post]())
|
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