006d9a71d5c4931bd0e680b4716ece10b0bfddaf
[ndcode_site.git] / my_account / sign_up / index.html.jst
1 return async env => {
2   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
3   let fa_arrow_circle_left = await env.site.get_min_svg('/_svg/fa_arrow-circle-left.svg')
4   let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
5   let fa_redo = await env.site.get_min_svg('/_svg/fa_redo.svg')
6   let fa_user_plus = await env.site.get_min_svg('/_svg/fa_user-plus.svg')
7   let get_placeholder = await _require('/_lib/get_placeholder.jst')
8   let get_session = await _require('/_lib/get_session.jst')
9   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
10   let icon_tick = await env.site.get_min_svg('/_svg/icon_tick.svg')
11   let navbar = await _require('/_lib/navbar.jst')
12
13   // preload draft details if any
14   let transaction = await env.site.database.Transaction()
15   let placeholder
16   let sign_up_draft
17   try {
18     let root = await transaction.get({})
19     let session = await get_session(env, root)
20
21     placeholder = await get_placeholder(env, session)
22
23     sign_up_draft = await session.get_json('sign_up_draft')
24     if (sign_up_draft === undefined || env.now >= sign_up_draft.expires)
25       sign_up_draft = null
26     transaction.commit()
27   }
28   catch (error) {
29     transaction.rollback()
30     throw error
31   }
32
33   await navbar(
34     env,
35     // head
36     async _out => {},
37     // body
38     async _out => {
39       await breadcrumbs(env, _out)
40
41       p {'Signing up allows you to leave comments on our blog and receive communications from us.'}
42
43       p {'Your given names are visible to other users if you comment on our blog. Your email and family name remain private. If your name is one word or does not fit given names/family name pattern, then please enter given names only.'}
44
45       div.accordion#accordion(role="tablist" aria-multiselectable="true") {
46         div.card#card-1 {
47           div.card-header#card-1-heading(role="tab") {
48             div.icon32-outer.mr-2#card-1-tick(hidden) {
49               div.icon32-inner {_out.push(icon_tick)}
50             }
51             div.icon32-outer.mr-2#card-1-cross(hidden) {
52               div.icon32-inner {_out.push(icon_cross)}
53             }
54             div.icon32-outer.mr-2#card-1-spinner(hidden) {
55               div.icon32-inner {
56                 div.spinner-border(role="status") {}
57               }
58             }
59             a.h5(data-toggle="collapse" data-parent="#accordion" href="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
60               'Create account'
61             }
62           }
63           div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
64             div.card-body {
65               form#form {
66                 div.row {
67                   div.col-md-6 {
68                     div.form-group {
69                       label.form-label(for="given-names") {'Given names *'}
70                       input.form-control#given-names(type="text" value=sign_up_draft !== null ? sign_up_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
71                       div.invalid-feedback {'Please enter a name we can address you by.'}
72                     }
73                   }
74                   div.col-md-6 {
75                     div.form-group {
76                       label.form-label(for="family-name") {'Family name'}
77                       input.form-control#family-name(type="text" value=sign_up_draft !== null ? sign_up_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
78                     }
79                   }
80                 }
81                 div.row {
82                   div.col-md-6 {
83                     div.form-group {
84                       label.form-label(for="email") {'Email *'}
85                       input.form-control#email(type="email" value=sign_up_draft !== null ? sign_up_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
86                       div.invalid-feedback {'Please enter an email address we can contact you on.'}
87                     }
88                   }
89                   div.col-md-6 {
90                     div.form-group {
91                       label.form-label(for="password") {'Password *'}
92                       input.form-control#password(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
93                       div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
94                     }
95                   }
96                 }
97                 div.row {
98                   div.col-md-12 {
99                     div.custom-control.custom-checkbox {
100                       if (sign_up_draft === null || sign_up_draft.contact_me)
101                         input.custom-control-input#contact-me(type="checkbox" checked) {}
102                       else
103                         input.custom-control-input#contact-me(type="checkbox") {}
104                       ' '
105                       label.custom-control-label(for="contact-me") {
106                         'Contact me by email with updates and special offers'
107                       }
108                     }
109                   }
110                 }
111                 div.row.align-items-center {
112                   div.col-md-6 {
113                     div.form-group {
114                       label.form-label(for="verification-code") {'Verification code *'}
115                       input.form-control#verification-code(type="text" placeholder=placeholder.captcha_text required minlength=6 maxlength=6) {}
116                       div.invalid-feedback {'Please enter the 6 characters from the verification image to right or below. We need this to protect us from spam and bots.'}
117                     }
118                   }
119                   div.'col-md-6'.my-3 {
120                     img#verification-image(src="/api/verification_image.png?seq=0" width=300 height=150) {}
121                   }
122                 }
123               }
124
125               button.btn.btn-outline-secondary#'card-1-new-code'(type="button") {
126                 div.icon24-outer.mr-2 {
127                   div.icon24-inner {_out.push(fa_redo)}
128                 }
129                 'New code'
130               }
131               if (sign_up_draft !== null)
132                 button.btn.btn-success.ml-3#card-1-create-account(type="button") {
133                   div.icon24-outer.mr-2 {
134                     div.icon24-inner {_out.push(fa_user_plus)}
135                   }
136                   'Create account'
137                 }
138               else
139                 button.btn.btn-success.ml-3#card-1-create-account(type="button" disabled) {
140                   div.icon24-outer.rm-2 {
141                     div.icon24-inner {_out.push(fa_user_plus)}
142                   }
143                   'Create account'
144                 }
145
146               p.'mt-3'.mb-0#card-1-message(hidden) {}
147             }
148           }
149         }
150         div.card#card-2 {
151           div.card-header#card-2-heading(role="tab") {
152             div.icon32-outer.mr-2#card-2-tick(hidden) {
153               div.icon32-inner {_out.push(icon_tick)}
154             }
155             div.icon32-outer.mr-2#card-2-cross(hidden) {
156               div.icon32-inner {_out.push(icon_cross)}
157             }
158             div.icon32-outer.mr-2#card-2-spinner(hidden) {
159               div.icon32-inner {
160                 div.spinner-border(role="status") {}
161               }
162             }
163             a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
164               'Send email verification link'
165             }
166           }
167           div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="card-2-heading" data-parent="#accordion") {
168             div.card-body {
169               button.btn.btn-outline-secondary#card-2-back(type="button") {
170                 div.icon24-outer.mr-2 {
171                   div.icon24-inner {_out.push(fa_arrow_circle_left)}
172                 }
173                 'Back'
174               }
175               button.btn.btn-outline-secondary.ml-3#card-2-resend-email(type="button" disabled) {
176                 div.icon24-outer.mr-2 {
177                   div.icon24-inner {_out.push(fa_envelope)}
178                 }
179                 'Re-send email'
180               }
181
182               p.'mt-3'.mb-0#card-2-message(hidden) {}
183             }
184           }
185         }
186       }
187
188       p.mt-3 {'* These fields are required.'}
189     },
190     // scripts
191     async _out => {
192       //script(src="/js/utils.js") {}
193
194       script {
195         let input_semaphore = new BinarySemaphore(false)
196         ;(
197           async () => {
198             while (true) {
199               await input_semaphore.acquire()
200               await new Promise(resolve => setTimeout(resolve, 3000))
201               input_semaphore.try_acquire()
202               await api_call(
203                 '/api/account/sign_up/set_draft.json',
204                 document.getElementById('card-1-create-account').disabled ?
205                   null :
206                   {
207                     email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
208                     given_names: document.getElementById('given-names').value.slice(0, 256),
209                     family_name: document.getElementById('family-name').value.slice(0, 256),
210                     contact_me: document.getElementById('contact-me').checked ? true : false
211                   }
212               )
213             }
214           }
215         )() // ignore returned promise (start thread)
216
217         let card_1_edited = in_draft => {
218           if (in_draft)
219             input_semaphore.release()
220
221           document.getElementById('card-1-create-account').disabled =
222             document.getElementById('given-names').value.length === 0 &&
223               document.getElementById('family-name').value.length === 0 &&
224               document.getElementById('contact-me').checked &&
225               document.getElementById('email').value.length === 0 &&
226               document.getElementById('verification-code').value.length === 0
227           document.getElementById('card-1-tick').hidden = true
228           document.getElementById('card-1-cross').hidden = true
229           document.getElementById('card-1-spinner').hidden = true
230           document.getElementById('card-1-message').hidden = true
231
232           document.getElementById('card-2-resend-email').disabled = true
233           document.getElementById('card-2-tick').hidden = true
234           document.getElementById('card-2-cross').hidden = true
235           document.getElementById('card-2-spinner').hidden = true
236           document.getElementById('card-2-message').hidden = true
237         }
238
239         let details
240         let card_1 = async () => {
241           document.getElementById('card-1-tick').hidden = true
242           document.getElementById('card-1-cross').hidden = true
243           document.getElementById('card-1-spinner').hidden = true
244           // the below causes an ugly flicker, so just keep the message
245           //document.getElementById('card-1-message').hidden = true
246
247           if (!document.getElementById('form').checkValidity()) {
248             document.getElementById('form').classList.add('was-validated');
249             document.getElementById('card-1-cross').hidden = false
250             return false
251           }
252           document.getElementById('form').classList.remove('was-validated');
253
254           details = {
255             email: document.getElementById('email').value.slice(0, 256).toLowerCase(),
256             given_names: document.getElementById('given-names').value.slice(0, 256),
257             family_name: document.getElementById('family-name').value.slice(0, 256),
258             password: document.getElementById('password').value.slice(0, 256),
259             contact_me: document.getElementById('contact-me').checked ? true : false
260           }
261
262           document.getElementById('card-1-spinner').hidden = false
263           try {
264             await api_call(
265               '/api/account/sign_up/create_account.json',
266               document.getElementById('verification-code').value.slice(0, 6).toLowerCase(),
267               details
268             )
269           }
270           catch (error) {
271             let problem = Problem.from(error)
272
273             document.getElementById('card-1-cross').hidden = false
274             document.getElementById('card-1-spinner').hidden = true
275
276             document.getElementById('card-1-message').textContent = problem.detail
277             //document.getElementById('card-1-message').classList.remove('text-success')
278             document.getElementById('card-1-message').classList.add('text-danger')
279             document.getElementById('card-1-message').hidden = false
280
281             $('#card-1-collapse').collapse('show')
282             return false
283           }
284           document.getElementById('card-1-tick').hidden = false
285           document.getElementById('card-1-spinner').hidden = true
286           document.getElementById('card-1-message').textContent = `Your account with email "${details.email}" has been created.`
287           //document.getElementById('card-1-message').classList.add('text-success')
288           document.getElementById('card-1-message').classList.remove('text-danger')
289           document.getElementById('card-1-message').hidden = false
290
291           document.getElementById('card-2-resend-email').disabled = false
292           document.getElementById('card-2').scrollIntoView()
293           return true
294         }
295
296         let card_2 = async () => {
297           document.getElementById('card-2-tick').hidden = true
298           document.getElementById('card-2-cross').hidden = true
299           document.getElementById('card-2-spinner').hidden = false
300           // the below causes an ugly flicker, so just keep the message
301           //document.getElementById('card-2-message').hidden = true
302
303           try {
304             await api_call(
305               '/api/account/sign_up/send_email_verification_link.json',
306               details.email
307             )
308           }
309           catch (error) {
310             let problem = Problem.from(error)
311
312             document.getElementById('card-2-cross').hidden = false
313             document.getElementById('card-2-spinner').hidden = true
314
315             document.getElementById('card-2-message').textContent = problem.detail
316             //document.getElementById('card-2-message').classList.remove('text-success')
317             document.getElementById('card-2-message').classList.add('text-danger')
318             document.getElementById('card-2-message').hidden = false
319
320             $('#card-2-collapse').collapse('show')
321             return false
322           }
323           document.getElementById('card-2-tick').hidden = false
324           document.getElementById('card-2-spinner').hidden = true
325
326           document.getElementById('card-2-message').textContent = `Email verification link has been sent to "${details.email}". Please check your email for next steps.`
327           //document.getElementById('card-2-message').classList.add('text-success')
328           document.getElementById('card-2-message').classList.remove('text-danger')
329           document.getElementById('card-2-message').hidden = false
330           return true
331         }
332
333         document.addEventListener(
334           'DOMContentLoaded',
335           () => {
336             document.getElementById('given-names').addEventListener(
337               'input',
338               () => {card_1_edited(true)}
339             )
340             document.getElementById('family-name').addEventListener(
341               'input',
342               () => {card_1_edited(true)}
343             )
344             document.getElementById('email').addEventListener(
345               'input',
346               () => {card_1_edited(true)}
347             )
348             document.getElementById('password').addEventListener(
349               'input',
350               () => {card_1_edited(false)}
351             )
352             document.getElementById('contact-me').addEventListener(
353               'input',
354               () => {card_1_edited(true)}
355             )
356             document.getElementById('verification-code').addEventListener(
357               'input',
358               () => {card_1_edited(false)}
359             )
360
361             let image_seq = 1
362             document.getElementById('card-1-new-code').addEventListener(
363               'click',
364               () => {
365                 document.getElementById('verification-image').src = `/api/verification_image.png?seq=${image_seq}`
366                 image_seq += 1
367               }
368             )
369
370             document.getElementById('card-1-create-account').addEventListener(
371               'click',
372               async () => {
373                 if (await card_1() && await card_2())
374                   $('#card-2-collapse').collapse('show')
375               }
376             )
377
378             document.getElementById('card-2-back').addEventListener(
379               'click',
380               () => {$('#card-1-collapse').collapse('show')}
381             )
382
383             document.getElementById('card-2-resend-email').addEventListener(
384               'click',
385               async () => {
386                 if (await card_2())
387                   $('#card-2-collapse').collapse('show')
388               }
389             )
390           }
391         )
392       }
393     }
394   )
395 }