)
}
catch (error) {
+ if (
+ error instanceof Problem &&
+ error.title === 'Email not yet verified'
+ ) {
+ location.href = `/my_account/send_verification_email?email=${encodeURIComponent(email)}`
+ return
+ }
+
document.getElementById('message-modal-message').textContent = error.message
$('#sign-in-modal').modal('hide')
$('#message-modal').modal('show')
+Problem = class {
+ constructor(title, detail, status) {
+ this.title = title
+ this.detail = detail
+ this.status = status
+ }
+}
+
api_call = async (endpoint, ...arguments) => {
let response = await fetch(
endpoint,
- {
- method: 'POST',
- body: JSON.stringify(arguments)
- }
+ {method: 'POST', body: JSON.stringify(arguments)}
)
+ let result = await response.json()
if (!response.ok)
- throw new Error((await response.json()).detail)
- return /*await*/ response.json()
+ throw new Problem(result.title, result.detail, result.status)
+ return result
}
"name": "Sign up",
"icon": "/_svg/icon_sign_up.svg"
},
+ {"dir": "send_verification_email", "name": "Send verification email"},
{"dir": "verify_email", "name": "Verify email"},
{
"dir": "password_reset",
--- /dev/null
+let logjson = (await import('@ndcode/logjson')).default
+
+return async env => {
+ let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
+ let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
+ let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
+ let navbar = await _require('/_lib/navbar.jst')
+ let session_cookie = await _require('/_lib/session_cookie.jst')
+
+ // preload draft details if any
+ let details = {}
+ if (Object.prototype.hasOwnProperty.call(env.parsed_url.query, 'email'))
+ details.email = decodeURIComponent(env.parsed_url.query.email)
+ console.log('details', JSON.stringify(details))
+
+ await navbar(
+ env,
+ // head
+ async _out => {},
+ // body
+ async _out => {
+ await breadcrumbs(env, _out)
+
+ p {'Please verify your email address before signing in. Check your email for next steps, or re-send the verification email below.'}
+
+ div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
+ div.card#step-1 {
+ div.card-header#step-1-heading(role="tab") {
+ span#step-1-tick(style="display: none;") {
+ span.icon-color.pr-3 {_out.push(icon_tick)}
+ }
+ span#step-1-cross(style="display: none;") {
+ span.icon-color.pr-3 {_out.push(icon_cross)}
+ }
+ //span#step-1-spinner(style="display: none;") {
+ // span.icon-color.pr-3 {
+ // div.spinner-border(role="status") {
+ // span.sr-only {'Loading...'}
+ // }
+ // }
+ //}
+ a.h5(data-toggle="collapse" data-parent="#accordion" href="#step-1-collapse" aria-expanded="true" aria-controls="step-1-collapse") {
+ 'Account details'
+ }
+ }
+ div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-1-heading" data-parent="#accordion") {
+ div.card-body {
+ div.row {
+ div.col-md-6 {
+ div.form-group {
+ label.form-label(for="email") {'Email *'}
+ input.form-control#email(type="email" value=details.email || '' placeholder="Account email address" required="required" maxlength=256) {}
+ }
+ }
+ }
+
+ input.btn.btn-success#step-1-continue(type="button" value="Continue") {}
+ p.'mt-3'.mb-0 {'* This field is required.'}
+ }
+ }
+ }
+ div.card#step-2 {
+ div.card-header#step-2-heading(role="tab") {
+ span#step-2-tick(style="display: none;") {
+ span.icon-color.pr-3 {_out.push(icon_tick)}
+ }
+ span#step-2-cross(style="display: none;") {
+ span.icon-color.pr-3 {_out.push(icon_cross)}
+ }
+ span#step-2-spinner(style="display: none;") {
+ span.icon-color.pr-3 {
+ div.spinner-border(role="status") {
+ span.sr-only {'Loading...'}
+ }
+ }
+ }
+ a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#step-2-collapse" aria-expanded="false" aria-controls="step-2-collapse") {
+ 'Send verification email'
+ }
+ }
+ div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
+ div.card-body {
+ p#step-2-message {'Please enter account details first.'}
+
+ input.btn.btn-outline-secondary#step-2-back(type="button" value="Back") {}
+ input.btn.btn-outline-secondary.ml-3#step-2-resend-email(type="button" value="Re-send email") {}
+ }
+ }
+ }
+ }
+ },
+ // scripts
+ async _out => {
+ script(src="/js/api_call.js") {}
+
+ script {
+ let sign_up_send_verification_email = async (...arguments) => api_call(
+ '/api/sign_up/send_verification_email.json',
+ ...arguments
+ )
+
+ let step_1 = async () => {
+ if (!document.getElementById('email').reportValidity()) {
+ $('#step-1-tick').hide()
+ $('#step-1-cross').show()
+ //$('#step-1-spinner').hide()
+ return false
+ }
+ $('#step-1-tick').show()
+ $('#step-1-cross').hide()
+ //$('#step-1-spinner').hide()
+ return true
+ }
+
+ let step_2 = async () => {
+ $('#step-2-tick').hide()
+ $('#step-2-cross').hide()
+ $('#step-2-spinner').show()
+ document.getElementById('step-1').scrollIntoView()
+
+ let email
+ try {
+ email = document.getElementById('email').value.slice(0, 256).toLowerCase()
+ await sign_up_send_verification_email(email)
+ }
+ catch (error) {
+ let problem =
+ error instanceof Problem ?
+ error :
+ new Problem(
+ // title
+ 'Bad request',
+ // details
+ (error.stack || error.message).toString()
+ // status
+ 400
+ )
+
+ $('#step-2-tick').hide()
+ $('#step-2-cross').show()
+ $('#step-2-spinner').hide()
+
+ document.getElementById('step-2-message').textContent = problem.detail
+ $('#step-2-collapse').collapse('show')
+ return false
+ }
+ $('#step-2-tick').show()
+ $('#step-2-cross').hide()
+ $('#step-2-spinner').hide()
+
+ document.getElementById('step-2-message').textContent = `Verification email has been sent to "${email}". Please check your email for next steps.`
+ return true
+ }
+
+ document.addEventListener(
+ 'DOMContentLoaded',
+ () => {
+ document.getElementById('step-1-continue').addEventListener(
+ 'click',
+ async () => {
+ if (await step_1() && await step_2())
+ $('#step-2-collapse').collapse('show')
+ }
+ )
+
+ document.getElementById('step-2-back').addEventListener(
+ 'click',
+ () => {$('#step-1-collapse').collapse('show')}
+ )
+
+ document.getElementById('step-2-resend-email').addEventListener(
+ 'click',
+ async () => {
+ if (await step_2())
+ $('#step-3-collapse').collapse('show')
+ }
+ )
+ }
+ )
+ }
+ }
+ )
+}
p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
- div.card {
+ div.card#step-1 {
div.card-header#step-1-heading(role="tab") {
span#step-1-tick(style="display: none;") {
span.icon-color.pr-3 {_out.push(icon_tick)}
}
}
}
- div.card {
+ div.card#step-2 {
div.card-header#step-2-heading(role="tab") {
span#step-2-tick(style="display: none;") {
span.icon-color.pr-3 {_out.push(icon_tick)}
}
}
}
- div.card {
+ div.card#step-3 {
div.card-header#step-3-heading(role="tab") {
span#step-3-tick(style="display: none;") {
span.icon-color.pr-3 {_out.push(icon_tick)}
p#step-3-message {'Please create your account first.'}
input.btn.btn-outline-secondary#step-3-back(type="button" value="Back") {}
- input.btn.btn-outline-secondary.ml-3#step-3-resend-email(type="button" value="Resend email") {}
+ input.btn.btn-outline-secondary.ml-3#step-3-resend-email(type="button" value="Re-send email") {}
}
}
}
$('#step-2-tick').hide()
$('#step-2-cross').hide()
$('#step-2-spinner').show()
+ document.getElementById('step-2').scrollIntoView()
+
try {
step_2_details = coerce_details()
await sign_up_create_account(
step_2_details
)
}
- catch (e) {
+ catch (error) {
+ let problem =
+ error instanceof Problem ?
+ error :
+ new Problem(
+ // title
+ 'Bad request',
+ // details
+ (error.stack || error.message).toString()
+ // status
+ 400
+ )
+
$('#step-2-tick').hide()
$('#step-2-cross').show()
$('#step-2-spinner').hide()
- document.getElementById('step-2-message').textContent = e.message
+ document.getElementById('step-2-message').textContent = problem.detail
$('#step-2-collapse').collapse('show')
return false
}
$('#step-2-tick').show()
$('#step-2-cross').hide()
$('#step-2-spinner').hide()
-
document.getElementById('step-2-message').textContent = `Your account with email "${step_2_details.email}" has been created.`
return true
}
$('#step-3-tick').hide()
$('#step-3-cross').hide()
$('#step-3-spinner').show()
+ document.getElementById('step-3').scrollIntoView()
+
try {
await sign_up_send_verification_email(step_2_details.email)
}
- catch (e) {
+ catch (error) {
+ let problem =
+ error instanceof Problem ?
+ error :
+ new Problem(
+ // title
+ 'Bad request',
+ // details
+ (error.stack || error.message).toString()
+ // status
+ 400
+ )
+
$('#step-3-tick').hide()
$('#step-3-cross').show()
$('#step-3-spinner').hide()
- document.getElementById('step-3-message').textContent = e.message
+ document.getElementById('step-3-message').textContent = problem.detail
$('#step-3-collapse').collapse('show')
return false
}
async _out => {
await breadcrumbs(env, _out)
- p {'You will need to verify your email address via an email link before you can sign in to your account.'}
+ p {'You will need to verify your email address via an emailed link before you can sign in to your account.'}
div.accordion#accordion.mb-5(role="tablist" aria-multiselectable="true") {
- div.card {
+ div.card#step-1 {
div.card-header#step-1-heading(role="tab") {
span#step-1-tick(style="display: none;") {
span.icon-color.pr-3 {_out.push(icon_tick)}
}
}
}
- div.card {
+ div.card#step-2 {
div.card-header#step-2-heading(role="tab") {
span#step-2-tick(style="display: none;") {
span.icon-color.pr-3 {_out.push(icon_tick)}
}
div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
div.card-body {
- p#step-2-message {'Please enter email link details first.'}
+ p#step-2-message {'Please enter link details first.'}
input.btn.btn-outline-secondary#step-2-back(type="button" value="Back") {}
input.btn.btn-outline-secondary.ml-2#step-2-sign-in(type="button" value="Sign in") {}
$('#step-2-tick').hide()
$('#step-2-cross').hide()
$('#step-2-spinner').show()
+ document.getElementById('step-1').scrollIntoView()
+
let email
try {
email = document.getElementById('email').value.slice(0, 256).toLowerCase()
document.getElementById('link-code').value.slice(0, 32).toLowerCase()
)
}
- catch (e) {
+ catch (error) {
+ let problem =
+ error instanceof Problem ?
+ error :
+ new Problem(
+ // title
+ 'Bad request',
+ // details
+ (error.stack || error.message).toString()
+ // status
+ 400
+ )
+
$('#step-2-tick').hide()
$('#step-2-cross').show()
$('#step-2-spinner').hide()
- document.getElementById('step-2-message').textContent = e.message
+ document.getElementById('step-2-message').textContent = problem.detail
$('#step-2-collapse').collapse('show')
return false
}
'click',
() => {$('#step-1-collapse').collapse('show')}
)
+
+ document.getElementById('step-2-sign-in').addEventListener(
+ 'click',
+ () => {document.getElementById('sign-in').click()}
+ )
}
)
}
try {
menu = await env.site.get_menu(`${pathname.slice(0, i)}_menu.json`)
}
- catch (e) {
+ catch (error) {
return pathname // fallback
}
let dir = pathname.slice(i, j)