Implements edit history.
This commit is contained in:
parent
8acaca298b
commit
d5f1c674c5
5 changed files with 73 additions and 9 deletions
43
forum.nim
43
forum.nim
|
|
@ -889,14 +889,14 @@ proc selectUser(userRow: seq[string], avatarSize: int=80): User =
|
||||||
)
|
)
|
||||||
|
|
||||||
proc selectPost(postRow: seq[string], skippedPosts: seq[int],
|
proc selectPost(postRow: seq[string], skippedPosts: seq[int],
|
||||||
replyingTo: Option[PostLink]): Post =
|
replyingTo: Option[PostLink], history: seq[PostInfo]): Post =
|
||||||
return Post(
|
return Post(
|
||||||
id: postRow[0].parseInt,
|
id: postRow[0].parseInt,
|
||||||
replyingTo: replyingTo,
|
replyingTo: replyingTo,
|
||||||
author: selectUser(@[postRow[5], postRow[6], postRow[7], postRow[8]]),
|
author: selectUser(@[postRow[5], postRow[6], postRow[7], postRow[8]]),
|
||||||
likes: @[], # TODO:
|
likes: @[], # TODO:
|
||||||
seen: false, # TODO:
|
seen: false, # TODO:
|
||||||
history: @[], # TODO:
|
history: history,
|
||||||
info: PostInfo(
|
info: PostInfo(
|
||||||
creation: postRow[2].parseInt,
|
creation: postRow[2].parseInt,
|
||||||
content: postRow[1].rstToHtml()
|
content: postRow[1].rstToHtml()
|
||||||
|
|
@ -926,6 +926,20 @@ proc selectReplyingTo(replyingTo: string): Option[PostLink] =
|
||||||
author: some(selectUser(@[row[3], row[4], row[5], row[6]]))
|
author: some(selectUser(@[row[3], row[4], row[5], row[6]]))
|
||||||
))
|
))
|
||||||
|
|
||||||
|
proc selectHistory(postId: int): seq[PostInfo] =
|
||||||
|
const historyQuery = sql"""
|
||||||
|
select strftime('%s', creation), content from postRevision
|
||||||
|
where original = ?
|
||||||
|
order by creation asc;
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = @[]
|
||||||
|
for row in getAllRows(db, historyQuery, $postId):
|
||||||
|
result.add(PostInfo(
|
||||||
|
creation: row[0].parseInt(),
|
||||||
|
content: row[1].rstToHtml()
|
||||||
|
))
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -1032,7 +1046,15 @@ proc updatePost(c: TForumData, postId: int, content: string,
|
||||||
raise newForumError("Message needs to be valid RST", @["msg"])
|
raise newForumError("Message needs to be valid RST", @["msg"])
|
||||||
|
|
||||||
# Update post.
|
# Update post.
|
||||||
exec(db, crud(crUpdate, "post", "content"), content, $postId)
|
# - We create a new postRevision entry for our edit.
|
||||||
|
exec(
|
||||||
|
db,
|
||||||
|
crud(crCreate, "postRevision", "content", "original"),
|
||||||
|
content,
|
||||||
|
$postId
|
||||||
|
)
|
||||||
|
# - We set the FTS to the latest content as searching for past edits is not
|
||||||
|
# supported.
|
||||||
exec(db, crud(crUpdate, "post_fts", "content"), content, $postId)
|
exec(db, crud(crUpdate, "post_fts", "content"), content, $postId)
|
||||||
# Check if post is the first post of the thread.
|
# Check if post is the first post of the thread.
|
||||||
if subject.isSome():
|
if subject.isSome():
|
||||||
|
|
@ -1234,7 +1256,8 @@ routes:
|
||||||
|
|
||||||
if addDetail:
|
if addDetail:
|
||||||
let replyingTo = selectReplyingTo(rows[i][4])
|
let replyingTo = selectReplyingTo(rows[i][4])
|
||||||
let post = selectPost(rows[i], skippedPosts, replyingTo)
|
let history = selectHistory(rows[i][0].parseInt())
|
||||||
|
let post = selectPost(rows[i], skippedPosts, replyingTo, history)
|
||||||
list.posts.add(post)
|
list.posts.add(post)
|
||||||
skippedPosts = @[]
|
skippedPosts = @[]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1261,7 +1284,8 @@ routes:
|
||||||
var list: seq[Post] = @[]
|
var list: seq[Post] = @[]
|
||||||
|
|
||||||
for row in db.getAllRows(postsQuery):
|
for row in db.getAllRows(postsQuery):
|
||||||
list.add(selectPost(row, @[], selectReplyingTo(row[4])))
|
let history = selectHistory(row[0].parseInt())
|
||||||
|
list.add(selectPost(row, @[], selectReplyingTo(row[4]), history))
|
||||||
|
|
||||||
resp $(%list), "application/json"
|
resp $(%list), "application/json"
|
||||||
|
|
||||||
|
|
@ -1271,10 +1295,15 @@ routes:
|
||||||
cond postId != -1
|
cond postId != -1
|
||||||
|
|
||||||
let postQuery = sql"""
|
let postQuery = sql"""
|
||||||
select content from post where id = ?;
|
select content from (
|
||||||
|
select content, creation from post where id = ?
|
||||||
|
union
|
||||||
|
select content, creation from postRevision where original = ?
|
||||||
|
)
|
||||||
|
order by creation desc limit 1;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
let content = getValue(db, postQuery, postId)
|
let content = getValue(db, postQuery, postId, postId)
|
||||||
if content.len == 0:
|
if content.len == 0:
|
||||||
resp Http404, "Post not found"
|
resp Http404, "Post not found"
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@ when defined(js):
|
||||||
(s: int, r: kstring) => onEditPost(s, r, state))
|
(s: int, r: kstring) => onEditPost(s, r, state))
|
||||||
|
|
||||||
proc render*(state: EditBox, post: Post): VNode =
|
proc render*(state: EditBox, post: Post): VNode =
|
||||||
|
if state.status != Http200:
|
||||||
|
return renderError("Couldn't retrieve raw post")
|
||||||
|
|
||||||
if state.rawContent.isNone() or state.post.id != post.id:
|
if state.rawContent.isNone() or state.post.id != post.id:
|
||||||
state.post = post
|
state.post = post
|
||||||
state.rawContent = none[kstring]()
|
state.rawContent = none[kstring]()
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,19 @@ $views-color: #545d70;
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post-history {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: $control-padding-x;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-count {
|
||||||
|
margin-right: $control-padding-x-sm/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ type
|
||||||
postId*: int
|
postId*: int
|
||||||
author*: Option[User] ## Only used for `replyingTo`.
|
author*: Option[User] ## Only used for `replyingTo`.
|
||||||
|
|
||||||
|
proc lastEdit*(post: Post): PostInfo =
|
||||||
|
post.history[^1]
|
||||||
|
|
||||||
proc isModerated*(post: Post): bool =
|
proc isModerated*(post: Post): bool =
|
||||||
## Determines whether the specified thread is under moderation.
|
## Determines whether the specified thread is under moderation.
|
||||||
post.author.rank <= Moderated
|
post.author.rank <= Moderated
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,10 @@ when defined(js):
|
||||||
let list = state.list.get()
|
let list = state.list.get()
|
||||||
for i in 0 ..< list.posts.len:
|
for i in 0 ..< list.posts.len:
|
||||||
if list.posts[i].id == id:
|
if list.posts[i].id == id:
|
||||||
list.posts[i].info.content = content
|
list.posts[i].history.add(PostInfo(
|
||||||
|
creation: getTime().toUnix(),
|
||||||
|
content: content
|
||||||
|
))
|
||||||
break
|
break
|
||||||
|
|
||||||
proc onReplyClick(e: Event, n: VNode, p: Option[Post]) =
|
proc onReplyClick(e: Event, n: VNode, p: Option[Post]) =
|
||||||
|
|
@ -219,6 +222,14 @@ when defined(js):
|
||||||
a(href=renderPostUrl(replyingTo)):
|
a(href=renderPostUrl(replyingTo)):
|
||||||
italic(class="fas fa-reply")
|
italic(class="fas fa-reply")
|
||||||
renderUserMention(replyingTo.author.get())
|
renderUserMention(replyingTo.author.get())
|
||||||
|
if post.history.len > 0:
|
||||||
|
let title = post.lastEdit.creation.fromUnix().local.
|
||||||
|
format("'Last modified' MMM d, yyyy HH:mm")
|
||||||
|
tdiv(class="post-history", title=title):
|
||||||
|
span(class="edit-count"):
|
||||||
|
text $post.history.len
|
||||||
|
italic(class="fas fa-pencil-alt")
|
||||||
|
|
||||||
let title = post.info.creation.fromUnix().local.
|
let title = post.info.creation.fromUnix().local.
|
||||||
format("MMM d, yyyy HH:mm")
|
format("MMM d, yyyy HH:mm")
|
||||||
a(href=renderPostUrl(post, thread), title=title):
|
a(href=renderPostUrl(post, thread), title=title):
|
||||||
|
|
@ -227,7 +238,12 @@ when defined(js):
|
||||||
if state.editing.isSome() and state.editing.get() == post:
|
if state.editing.isSome() and state.editing.get() == post:
|
||||||
render(state.editBox, postCopy)
|
render(state.editBox, postCopy)
|
||||||
else:
|
else:
|
||||||
verbatim(post.info.content)
|
let content =
|
||||||
|
if post.history.len > 0:
|
||||||
|
post.lastEdit.content
|
||||||
|
else:
|
||||||
|
post.info.content
|
||||||
|
verbatim(content)
|
||||||
|
|
||||||
genPostButtons(postCopy, currentUser)
|
genPostButtons(postCopy, currentUser)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue