Add frontend for category picker when admin or thread author

This commit is contained in:
Joey Yakimowich-Payne 2018-08-07 20:26:48 +09:00
commit cb7418f825
2 changed files with 61 additions and 6 deletions

View file

@ -14,6 +14,7 @@ when defined(js):
selectedCategoryID*: int selectedCategoryID*: int
loading: bool loading: bool
status: HttpCode status: HttpCode
onCategoryChange: proc (oldCategory: Category, newCategory: Category)
proc slug(name: string): string = proc slug(name: string): string =
name.strip().replace(" ", "-").toLowerAscii name.strip().replace(" ", "-").toLowerAscii
@ -40,20 +41,32 @@ when defined(js):
state.loading = true state.loading = true
ajaxGet(makeUri("categories.json"), @[], onCategoryList(state)) ajaxGet(makeUri("categories.json"), @[], onCategoryList(state))
proc newCategoryPicker*(): CategoryPicker = proc `[]`*(state: CategoryPicker, id: int): Category =
state.list.get().categories[id]
proc newCategoryPicker*(
onCategoryChange: proc (oldCategory: Category, newCategory: Category) =
proc (oldCategory: Category, newCategory: Category) = discard
): CategoryPicker =
result = CategoryPicker( result = CategoryPicker(
list: none[CategoryList](), list: none[CategoryList](),
selectedCategoryID: 0, selectedCategoryID: 0,
loading: false, loading: false,
status: Http200 status: Http200,
onCategoryChange: onCategoryChange
) )
proc select*(state: CategoryPicker, id: int) =
state.selectedCategoryID = id
state.markDirty()
proc onCategoryClick(state: CategoryPicker, category: Category): proc (ev: Event, n: VNode) = proc onCategoryClick(state: CategoryPicker, category: Category): proc (ev: Event, n: VNode) =
# this is necessary to capture the right value # this is necessary to capture the right value
let cat = category let cat = category
return proc (ev: Event, n: VNode) = return proc (ev: Event, n: VNode) =
state.selectedCategoryID = cat.id let oldCategory = state[state.selectedCategoryID]
state.markDirty() state.select(cat.id)
state.onCategoryChange(oldCategory, cat)
proc render*(state: CategoryPicker): VNode = proc render*(state: CategoryPicker): VNode =
if state.status != Http200: if state.status != Http200:

View file

@ -20,12 +20,14 @@ when defined(js):
import karax / [vstyles, kajax, kdom] import karax / [vstyles, kajax, kdom]
import karaxutils, error, replybox, editbox, postbutton, delete import karaxutils, error, replybox, editbox, postbutton, delete
import categorypicker
type type
State = ref object State = ref object
list: Option[PostList] list: Option[PostList]
loading: bool loading: bool
status: HttpCode status: HttpCode
error: Option[PostError]
replyingTo: Option[Post] replyingTo: Option[Post]
replyBox: ReplyBox replyBox: ReplyBox
editing: Option[Post] ## If in edit mode, this contains the post. editing: Option[Post] ## If in edit mode, this contains the post.
@ -33,8 +35,10 @@ when defined(js):
likeButton: LikeButton likeButton: LikeButton
deleteModal: DeleteModal deleteModal: DeleteModal
lockButton: LockButton lockButton: LockButton
categoryPicker: CategoryPicker
proc onReplyPosted(id: int) proc onReplyPosted(id: int)
proc onCategoryChanged(oldCategory: Category, newCategory: Category)
proc onEditPosted(id: int, content: string, subject: Option[string]) proc onEditPosted(id: int, content: string, subject: Option[string])
proc onEditCancelled() proc onEditCancelled()
proc onDeletePost(post: Post) proc onDeletePost(post: Post)
@ -44,17 +48,37 @@ when defined(js):
list: none[PostList](), list: none[PostList](),
loading: false, loading: false,
status: Http200, status: Http200,
error: none[PostError](),
replyingTo: none[Post](), replyingTo: none[Post](),
replyBox: newReplyBox(onReplyPosted), replyBox: newReplyBox(onReplyPosted),
editBox: newEditBox(onEditPosted, onEditCancelled), editBox: newEditBox(onEditPosted, onEditCancelled),
likeButton: newLikeButton(), likeButton: newLikeButton(),
deleteModal: newDeleteModal(onDeletePost, onDeleteThread, nil), deleteModal: newDeleteModal(onDeletePost, onDeleteThread, nil),
lockButton: newLockButton() lockButton: newLockButton(),
categoryPicker: newCategoryPicker(onCategoryChanged)
) )
var var
state = newState() state = newState()
proc onCategoryPost(httpStatus: int, response: kstring, state: State) =
state.loading = false
postFinished:
discard
# TODO: show success message
proc onCategoryChanged(oldCategory: Category, newCategory: Category) =
let uri = makeUri("/updateThread")
let formData = newFormData()
formData.append("threadId", $state.list.get().thread.id)
formData.append("category", $newCategory.id)
state.loading = true
ajaxPost(uri, @[], cast[cstring](formData),
(s: int, r: kstring) => onCategoryPost(s, r, state))
proc onPostList(httpStatus: int, response: kstring, postId: Option[int]) = proc onPostList(httpStatus: int, response: kstring, postId: Option[int]) =
state.loading = false state.loading = false
state.status = httpStatus.HttpCode state.status = httpStatus.HttpCode
@ -66,6 +90,7 @@ when defined(js):
state.list = some(list) state.list = some(list)
dom.document.title = list.thread.topic & " - " & dom.document.title dom.document.title = list.thread.topic & " - " & dom.document.title
state.categoryPicker.select(list.thread.category.id)
# The anchor should be jumped to once all the posts have been loaded. # The anchor should be jumped to once all the posts have been loaded.
if postId.isSome(): if postId.isSome():
@ -179,6 +204,20 @@ when defined(js):
span(class="more-post-count"): span(class="more-post-count"):
text "(" & $post.moreBefore.len & ")" text "(" & $post.moreBefore.len & ")"
proc genCategories(thread: Thread, currentUser: Option[User]): VNode =
let loggedIn = currentUser.isSome()
let authoredByUser =
loggedIn and currentUser.get().name == thread.author.name
let currentAdmin =
currentUser.isSome() and currentUser.get().rank == Admin
result = buildHtml():
tdiv():
if authoredByUser or currentAdmin:
render(state.categoryPicker)
else:
render(thread.category)
proc genPostButtons(post: Post, currentUser: Option[User]): Vnode = proc genPostButtons(post: Post, currentUser: Option[User]): Vnode =
let loggedIn = currentUser.isSome() let loggedIn = currentUser.isSome()
let authoredByUser = let authoredByUser =
@ -330,6 +369,9 @@ when defined(js):
result = buildHtml(): result = buildHtml():
section(class="container grid-xl"): section(class="container grid-xl"):
tdiv(id="thread-title", class="title"): tdiv(id="thread-title", class="title"):
if state.error.isSome():
span(class="text-error"):
text state.error.get().message
p(): text list.thread.topic p(): text list.thread.topic
if list.thread.isLocked: if list.thread.isLocked:
italic(class="fas fa-lock fa-xs", italic(class="fas fa-lock fa-xs",
@ -343,7 +385,7 @@ when defined(js):
italic(class="fas fa-check-square fa-xs", italic(class="fas fa-check-square fa-xs",
title="Thread has a solution") title="Thread has a solution")
text "Solved" text "Solved"
render(list.thread.category) genCategories(list.thread, currentUser)
tdiv(class="posts"): tdiv(class="posts"):
var prevPost: Option[Post] = none[Post]() var prevPost: Option[Post] = none[Post]()
for i, post in list.posts: for i, post in list.posts: