diff --git a/redesign/error.nim b/redesign/error.nim index e921a1a..a534bde 100644 --- a/redesign/error.nim +++ b/redesign/error.nim @@ -5,6 +5,7 @@ type message*: string when defined(js): + import json include karax/prelude import karax / [vstyles, kajax, kdom] diff --git a/redesign/postbutton.nim b/redesign/postbutton.nim new file mode 100644 index 0000000..913b392 --- /dev/null +++ b/redesign/postbutton.nim @@ -0,0 +1,77 @@ +## Simple generic button that can be clicked to make a post request. +## The button will show a loading indicator and a tick on success. +## +## Used for password reset emails. + +import options, httpcore, json, sugar +when defined(js): + include karax/prelude + import karax/[kajax, kdom] + + import error, karaxutils + + type + PostButton* = ref object + uri, title, icon: string + formData: FormData + error: Option[PostError] + loading: bool + posted: bool + + proc newPostButton*(uri: string, formData: FormData, + title: string, icon: string): PostButton = + PostButton( + uri: uri, + formData: formData, + title: title, + icon: icon + ) + + proc newResetPasswordButton*(email: string): PostButton = + var formData = newFormData() + formData.append("email", email) + result = newPostButton( + makeUri("/resetPassword"), + formData, + "Send password reset email", + "fas fa-envelope", + ) + + proc onPost(httpStatus: int, response: kstring, state: PostButton) = + postFinished: + discard + + proc onClick(ev: Event, n: VNode, state: PostButton) = + if state.loading or state.posted: return + + state.loading = true + state.posted = true + state.error = none[PostError]() + + # TODO: This is a hack, karax should support this. + ajaxPost(state.uri, @[], cast[cstring](state.formData), + (s: int, r: kstring) => onPost(s, r, state)) + + ev.preventDefault() + + proc render*(state: PostButton, disabled: bool): VNode = + result = buildHtml(tdiv()): + button(class=class({ + "loading": state.loading, + "disabled": disabled + }, + "btn btn-secondary" + ), + onClick=(e: Event, n: VNode) => (onClick(e, n, state))): + if state.posted: + if state.error.isNone(): + italic(class="fas fa-check") + else: + italic(class="fas fa-times") + else: + italic(class=state.icon) + text " " & state.title + + if state.error.isSome(): + p(class="text-error"): + text state.error.get().message \ No newline at end of file diff --git a/redesign/profile.nim b/redesign/profile.nim index 6d4fa05..b531aa4 100644 --- a/redesign/profile.nim +++ b/redesign/profile.nim @@ -16,7 +16,7 @@ type when defined(js): include karax/prelude import karax/[kajax] - import karaxutils + import karaxutils, postbutton type ProfileTab* = enum @@ -32,6 +32,7 @@ when defined(js): currentTab: ProfileTab loading: bool status: HttpCode + resetPassword: Option[PostButton] proc newProfileState*(): ProfileState = ProfileState( @@ -63,6 +64,8 @@ when defined(js): state.profile = some(profile) resetSettings(state) + if profile.email.isSome(): + state.resetPassword = some(newResetPasswordButton(profile.email.get())) proc genPostLink(link: PostLink): VNode = let url = renderPostUrl(link) @@ -244,6 +247,14 @@ when defined(js): text "Rank" tdiv(class="col-9 col-sm-12"): rankSelect + if state.resetPassword.isSome(): + tdiv(class="form-group"): + tdiv(class="col-3 col-sm-12"): + label(class="form-label"): + text "Password" + tdiv(class="col-9 col-sm-12"): + render(state.resetPassword.get(), + disabled=state.settings.rank==EmailUnconfirmed) tdiv(class="float-right"): button(class="btn btn-link",