Implements new thread modal.
This commit is contained in:
parent
35930799a9
commit
c966ec8f92
4 changed files with 163 additions and 45 deletions
81
redesign/newthread.nim
Normal file
81
redesign/newthread.nim
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
when defined(js):
|
||||||
|
import sugar, httpcore, options, json
|
||||||
|
import dom except Event
|
||||||
|
|
||||||
|
include karax/prelude
|
||||||
|
import karax / [kajax, kdom]
|
||||||
|
|
||||||
|
import error, replybox, threadlist, post
|
||||||
|
import karaxutils
|
||||||
|
|
||||||
|
type
|
||||||
|
NewThreadModal* = ref object
|
||||||
|
shown: bool
|
||||||
|
loading: bool
|
||||||
|
onNewThread: proc (threadId, postId: int)
|
||||||
|
error: Option[PostError]
|
||||||
|
replyBox: ReplyBox
|
||||||
|
|
||||||
|
proc onCreatePost(httpStatus: int, response: kstring, state: NewThreadModal) =
|
||||||
|
postFinished:
|
||||||
|
state.shown = false
|
||||||
|
state.onNewThread(0, 0) # TODO
|
||||||
|
|
||||||
|
proc onCreateClick(ev: Event, n: VNode, state: NewThreadModal) =
|
||||||
|
state.loading = true
|
||||||
|
state.error = none[PostError]()
|
||||||
|
|
||||||
|
let uri = makeUri("login")
|
||||||
|
# TODO: This is a hack, karax should support this.
|
||||||
|
let formData = newFormData()
|
||||||
|
#formData.append("" TODO
|
||||||
|
ajaxPost(uri, @[], cast[cstring](formData),
|
||||||
|
(s: int, r: kstring) => onCreatePost(s, r, state))
|
||||||
|
|
||||||
|
proc onClose(ev: Event, n: VNode, state: NewThreadModal) =
|
||||||
|
state.shown = false
|
||||||
|
ev.preventDefault()
|
||||||
|
|
||||||
|
proc newNewThreadModal*(
|
||||||
|
onNewThread: proc (threadId, postId: int)
|
||||||
|
): NewThreadModal =
|
||||||
|
NewThreadModal(
|
||||||
|
shown: false,
|
||||||
|
onNewThread: onNewThread,
|
||||||
|
replyBox: newReplyBox(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc show*(state: NewThreadModal) =
|
||||||
|
state.shown = true
|
||||||
|
|
||||||
|
proc render*(state: NewThreadModal): VNode =
|
||||||
|
result = buildHtml():
|
||||||
|
tdiv(class=class({"active": state.shown}, "modal modal-lg"),
|
||||||
|
id="new-thread-modal"):
|
||||||
|
a(href="", class="modal-overlay", "aria-label"="close",
|
||||||
|
onClick=(ev: Event, n: VNode) => onClose(ev, n, state))
|
||||||
|
tdiv(class="modal-container"):
|
||||||
|
tdiv(class="modal-header"):
|
||||||
|
a(href="", class="btn btn-clear float-right",
|
||||||
|
"aria-label"="close",
|
||||||
|
onClick=(ev: Event, n: VNode) => onClose(ev, n, state))
|
||||||
|
tdiv(class="modal-title h5"):
|
||||||
|
text "New Thread"
|
||||||
|
tdiv(class="modal-body"):
|
||||||
|
tdiv(class="content"):
|
||||||
|
input(class="form-input", `type`="text", name="username",
|
||||||
|
placeholder="Type the title here")
|
||||||
|
renderContent(state.replyBox, none[Thread](), none[Post]())
|
||||||
|
|
||||||
|
tdiv(class="modal-footer"):
|
||||||
|
button(class="btn",
|
||||||
|
onClick=(ev: Event, n: VNode) =>
|
||||||
|
onClose(ev, n, state)):
|
||||||
|
text "Cancel"
|
||||||
|
button(class=class(
|
||||||
|
{"loading": state.loading},
|
||||||
|
"btn btn-primary"
|
||||||
|
),
|
||||||
|
onClick=(ev: Event, n: VNode) =>
|
||||||
|
(onCreateClick(ev, n, state))):
|
||||||
|
text "Create thread"
|
||||||
|
|
@ -113,6 +113,23 @@ $logo-height: $navbar-height - 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#new-thread-modal {
|
||||||
|
.modal-container .modal-body {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
margin-bottom: $control-padding-y*2;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.form-input, .panel-body > div {
|
||||||
|
margin-top: $control-padding-y*2;
|
||||||
|
resize: none;
|
||||||
|
min-height: 40vh;
|
||||||
|
max-height: 45vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// - Thread table
|
// - Thread table
|
||||||
.thread-title {
|
.thread-title {
|
||||||
a, a:visited, a:hover {
|
a, a:visited, a:hover {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ when defined(js):
|
||||||
state.preview = true
|
state.preview = true
|
||||||
state.loading = true
|
state.loading = true
|
||||||
state.error = none[PostError]()
|
state.error = none[PostError]()
|
||||||
|
state.rendering = none[kstring]()
|
||||||
|
|
||||||
let formData = newFormData()
|
let formData = newFormData()
|
||||||
formData.append("msg", state.text)
|
formData.append("msg", state.text)
|
||||||
|
|
@ -55,6 +56,10 @@ when defined(js):
|
||||||
ajaxPost(uri, @[], cast[cstring](formData),
|
ajaxPost(uri, @[], cast[cstring](formData),
|
||||||
(s: int, r: kstring) => onPreviewPost(s, r, state))
|
(s: int, r: kstring) => onPreviewPost(s, r, state))
|
||||||
|
|
||||||
|
proc onMessageClick(e: Event, n: VNode, state: ReplyBox) =
|
||||||
|
state.preview = false
|
||||||
|
state.error = none[PostError]()
|
||||||
|
|
||||||
proc onReplyPost(httpStatus: int, response: kstring, state: ReplyBox) =
|
proc onReplyPost(httpStatus: int, response: kstring, state: ReplyBox) =
|
||||||
postFinished:
|
postFinished:
|
||||||
state.text = ""
|
state.text = ""
|
||||||
|
|
@ -84,6 +89,53 @@ when defined(js):
|
||||||
# `value` on the node? We need to document this better :)
|
# `value` on the node? We need to document this better :)
|
||||||
state.text = cast[dom.TextAreaElement](n.dom).value
|
state.text = cast[dom.TextAreaElement](n.dom).value
|
||||||
|
|
||||||
|
proc renderContent*(state: ReplyBox, thread: Option[Thread],
|
||||||
|
post: Option[Post]): VNode =
|
||||||
|
result = buildHtml():
|
||||||
|
tdiv(class="panel"):
|
||||||
|
tdiv(class="panel-nav"):
|
||||||
|
ul(class="tab tab-block"):
|
||||||
|
li(class=class({"active": not state.preview}, "tab-item"),
|
||||||
|
onClick=(e: Event, n: VNode) =>
|
||||||
|
onMessageClick(e, n, state)):
|
||||||
|
a(class="c-hand"):
|
||||||
|
text "Message"
|
||||||
|
li(class=class({"active": state.preview}, "tab-item"),
|
||||||
|
onClick=(e: Event, n: VNode) =>
|
||||||
|
onPreviewClick(e, n, state)):
|
||||||
|
a(class="c-hand"):
|
||||||
|
text "Preview"
|
||||||
|
tdiv(class="panel-body"):
|
||||||
|
if state.preview:
|
||||||
|
if state.loading:
|
||||||
|
tdiv(class="loading")
|
||||||
|
elif state.rendering.isSome():
|
||||||
|
verbatim(state.rendering.get())
|
||||||
|
else:
|
||||||
|
textarea(class="form-input", rows="5",
|
||||||
|
onChange=(e: Event, n: VNode) =>
|
||||||
|
onChange(e, n, state),
|
||||||
|
value=state.text)
|
||||||
|
|
||||||
|
if state.error.isSome():
|
||||||
|
p(class="text-error",
|
||||||
|
style=style(StyleAttr.marginTop, "0.4rem")):
|
||||||
|
text state.error.get().message
|
||||||
|
|
||||||
|
if thread.isSome:
|
||||||
|
tdiv(class="panel-footer"):
|
||||||
|
button(class=class(
|
||||||
|
{"loading": state.loading},
|
||||||
|
"btn btn-primary float-right"
|
||||||
|
),
|
||||||
|
onClick=(e: Event, n: VNode) =>
|
||||||
|
onReplyClick(e, n, state, thread.get(), post)):
|
||||||
|
text "Reply"
|
||||||
|
button(class="btn btn-link float-right",
|
||||||
|
onClick=(e: Event, n: VNode) =>
|
||||||
|
onCancelClick(e, n, state)):
|
||||||
|
text "Cancel"
|
||||||
|
|
||||||
proc render*(state: ReplyBox, thread: Thread, post: Option[Post],
|
proc render*(state: ReplyBox, thread: Thread, post: Option[Post],
|
||||||
hasMore: bool): VNode =
|
hasMore: bool): VNode =
|
||||||
if not state.shown:
|
if not state.shown:
|
||||||
|
|
@ -106,44 +158,4 @@ when defined(js):
|
||||||
button(class="btn"):
|
button(class="btn"):
|
||||||
italic(class="fas fa-arrow-up")
|
italic(class="fas fa-arrow-up")
|
||||||
tdiv(class="information-content"):
|
tdiv(class="information-content"):
|
||||||
tdiv(class="panel"):
|
renderContent(state, some(thread), post)
|
||||||
tdiv(class="panel-nav"):
|
|
||||||
ul(class="tab tab-block"):
|
|
||||||
li(class=class({"active": not state.preview}, "tab-item"),
|
|
||||||
onClick=(e: Event, n: VNode) => (state.preview = false)):
|
|
||||||
a(class="c-hand"):
|
|
||||||
text "Message"
|
|
||||||
li(class=class({"active": state.preview}, "tab-item"),
|
|
||||||
onClick=(e: Event, n: VNode) =>
|
|
||||||
onPreviewClick(e, n, state)):
|
|
||||||
a(class="c-hand"):
|
|
||||||
text "Preview"
|
|
||||||
tdiv(class="panel-body"):
|
|
||||||
if state.preview:
|
|
||||||
if state.loading:
|
|
||||||
tdiv(class="loading")
|
|
||||||
elif state.rendering.isSome():
|
|
||||||
verbatim(state.rendering.get())
|
|
||||||
else:
|
|
||||||
textarea(class="form-input", rows="5",
|
|
||||||
onChange=(e: Event, n: VNode) =>
|
|
||||||
onChange(e, n, state),
|
|
||||||
value=state.text)
|
|
||||||
|
|
||||||
if state.error.isSome():
|
|
||||||
tdiv(class="toast toast-error",
|
|
||||||
style=style(StyleAttr.marginTop, "0.4rem")):
|
|
||||||
text state.error.get().message
|
|
||||||
|
|
||||||
tdiv(class="panel-footer"):
|
|
||||||
button(class=class(
|
|
||||||
{"loading": state.loading},
|
|
||||||
"btn btn-primary float-right"
|
|
||||||
),
|
|
||||||
onClick=(e: Event, n: VNode) =>
|
|
||||||
onReplyClick(e, n, state, thread, post)):
|
|
||||||
text "Reply"
|
|
||||||
button(class="btn btn-link float-right",
|
|
||||||
onClick=(e: Event, n: VNode) =>
|
|
||||||
onCancelClick(e, n, state)):
|
|
||||||
text "Cancel"
|
|
||||||
|
|
@ -24,24 +24,30 @@ when defined(js):
|
||||||
include karax/prelude
|
include karax/prelude
|
||||||
import karax / [vstyles, kajax, kdom]
|
import karax / [vstyles, kajax, kdom]
|
||||||
|
|
||||||
import karaxutils, error
|
import karaxutils, error, newthread
|
||||||
|
|
||||||
type
|
type
|
||||||
State = ref object
|
State = ref object
|
||||||
list: Option[ThreadList]
|
list: Option[ThreadList]
|
||||||
loading: bool
|
loading: bool
|
||||||
status: HttpCode
|
status: HttpCode
|
||||||
|
newThread: NewThreadModal
|
||||||
|
|
||||||
|
proc onNewThread(threadId, postId: int)
|
||||||
proc newState(): State =
|
proc newState(): State =
|
||||||
State(
|
State(
|
||||||
list: none[ThreadList](),
|
list: none[ThreadList](),
|
||||||
loading: false,
|
loading: false,
|
||||||
status: Http200
|
status: Http200,
|
||||||
|
newThread: newNewThreadModal(onNewThread)
|
||||||
)
|
)
|
||||||
|
|
||||||
var
|
var
|
||||||
state = newState()
|
state = newState()
|
||||||
|
|
||||||
|
proc onNewThread(threadId, postId: int) =
|
||||||
|
discard
|
||||||
|
|
||||||
proc genTopButtons(): VNode =
|
proc genTopButtons(): VNode =
|
||||||
result = buildHtml():
|
result = buildHtml():
|
||||||
section(class="navbar container grid-xl", id="main-buttons"):
|
section(class="navbar container grid-xl", id="main-buttons"):
|
||||||
|
|
@ -57,7 +63,8 @@ when defined(js):
|
||||||
button(class="btn btn-link"): text "Most Active"
|
button(class="btn btn-link"): text "Most Active"
|
||||||
button(class="btn btn-link"): text "Categories"
|
button(class="btn btn-link"): text "Categories"
|
||||||
section(class="navbar-section"):
|
section(class="navbar-section"):
|
||||||
button(class="btn btn-secondary"):
|
button(class="btn btn-secondary",
|
||||||
|
onClick=(e: Event, n: VNode) => state.newThread.show()):
|
||||||
italic(class="fas fa-plus")
|
italic(class="fas fa-plus")
|
||||||
text " New Thread"
|
text " New Thread"
|
||||||
|
|
||||||
|
|
@ -175,3 +182,4 @@ when defined(js):
|
||||||
result = buildHtml(tdiv):
|
result = buildHtml(tdiv):
|
||||||
genTopButtons()
|
genTopButtons()
|
||||||
genThreadList()
|
genThreadList()
|
||||||
|
render(state.newThread)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue