Update /my_account/index.html.jst to latest way, minor consistency changes in /my_acc...
authorNick Downing <nick@ndcode.org>
Thu, 27 Jan 2022 03:48:01 +0000 (14:48 +1100)
committerNick Downing <nick@ndcode.org>
Thu, 27 Jan 2022 03:51:29 +0000 (14:51 +1100)
_lib/navbar.jst
_svg/fa_cloud-upload-alt.svg [new file with mode: 0644]
_svg/fa_trash.svg [new file with mode: 0644]
api/account/change_details/set_draft.json.jst
my_account/index.html.jst
my_account/sign_up/index.html.jst

index 8dc8225..02bbab5 100644 (file)
@@ -399,6 +399,7 @@ return async (env, head, body, scripts) => {
       script {
         // this function can be overridden in a further script
         function sign_in_out(status) {
+          return false
         }
 
         document.addEventListener(
@@ -524,11 +525,15 @@ return async (env, head, body, scripts) => {
                 //id_navbar_sign_in_message.classList.remove('text-danger')
                 //id_navbar_sign_in_message.hidden = false
 
+                if (sign_in_out(true))
+                  // if location has been changed, leave the spinner and do
+                  // not show status/dialog, as it causes an annoying flicker
+                  return
+
                 id_navbar_signed_in_status.textContent = 'Signed in.' //`Signed in as ${email}.`
                 id_navbar_sign_in.hidden = true
                 id_navbar_sign_up.hidden = true
                 id_navbar_sign_out.hidden = false
-                sign_in_out(true)
 
                 id_navbar_sign_in_icon.hidden = false
                 id_navbar_sign_in_spinner.hidden = true
@@ -557,11 +562,15 @@ return async (env, head, body, scripts) => {
                   return
                 }
 
+                if (sign_in_out(false))
+                  // if location has been changed, leave the spinner and do
+                  // not show status/dialog, as it causes an annoying flicker
+                  return
+
                 id_navbar_signed_in_status.textContent = 'Browsing as guest.'
                 id_navbar_sign_in.hidden = false
                 id_navbar_sign_up.hidden = false
                 id_navbar_sign_out.hidden = true
-                sign_in_out(false)
 
                 id_navbar_message_modal_message.textContent = `You are now signed out.`
                 $('#navbar-sign-in-modal').modal('hide')
diff --git a/_svg/fa_cloud-upload-alt.svg b/_svg/fa_cloud-upload-alt.svg
new file mode 100644 (file)
index 0000000..6a57f68
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zM393.4 288H328v112c0 8.8-7.2 16-16 16h-48c-8.8 0-16-7.2-16-16V288h-65.4c-14.3 0-21.4-17.2-11.3-27.3l105.4-105.4c6.2-6.2 16.4-6.2 22.6 0l105.4 105.4c10.1 10.1 2.9 27.3-11.3 27.3z"/></svg>
\ No newline at end of file
diff --git a/_svg/fa_trash.svg b/_svg/fa_trash.svg
new file mode 100644 (file)
index 0000000..18f52c5
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zM53.2 467a48 48 0 0 0 47.9 45h245.8a48 48 0 0 0 47.9-45L416 128H32z"/></svg>
\ No newline at end of file
index 76503c6..67e2f3e 100644 (file)
@@ -26,7 +26,7 @@ return async env => {
         if (details) {
           let expires = new XDate()
           expires.addDays(1)
-          session.set(
+          session.set_json(
             'change_details_draft',
             {
               given_names: details.given_names,
index 724ed5c..d7deb7e 100644 (file)
@@ -1,7 +1,11 @@
-let XDate = require('xdate')
-
 return async env => {
   let breadcrumbs = await _require('/_lib/breadcrumbs.jst')
+  let fa_arrow_circle_left = await env.site.get_min_svg('/_svg/fa_arrow-circle-left.svg')
+  let fa_cloud_upload_alt = await env.site.get_min_svg('/_svg/fa_cloud-upload-alt.svg')
+  let fa_envelope = await env.site.get_min_svg('/_svg/fa_envelope.svg')
+  let fa_redo = await env.site.get_min_svg('/_svg/fa_redo.svg')
+  let fa_trash = await env.site.get_min_svg('/_svg/fa_trash.svg')
+  let get_placeholder = await _require('/_lib/get_placeholder.jst')
   let get_account = await _require('/_lib/get_account.jst')
   let get_session = await _require('/_lib/get_session.jst')
   let icon_cross = await env.site.get_min_svg('/_svg/icon_cross.svg')
@@ -9,45 +13,48 @@ return async env => {
   let menu = await env.site.get_menu('/my_account/_menu.json')
   let navbar = await _require('/_lib/navbar.jst')
 
-  // see whether signed in, if so preload details, and draft details if any
+  // preload draft details if any
   let transaction = await env.site.database.Transaction()
-  let signed_in_as
-  let details, draft_details
+  let placeholder
+  let signed_in_as, details, change_details_draft
   try {
     let root = await transaction.get({})
-
     let session = await get_session(env, root)
+
+    placeholder = await get_placeholder(env, session)
+
     signed_in_as = await session.get_json('signed_in_as')
 
-    let account = await get_account(root, session)
-    if (account !== undefined) {
-      details = {
+    account = await get_account(root, session)
+    details =
+      account === undefined ?
+      null :
+      {
         given_names: await account.get_json('given_names'),
         family_name: await account.get_json('family_name'),
         contact_me: await account.get_json('contact_me')
       }
 
-      let change_details_draft = await session.get('change_details_draft')
-      draft_details =
-        change_details_draft !== undefined &&
-          XDate.now() < await change_details_draft.get_json('expires') ?
-          {
-            given_names: await change_details_draft.get_json('given_names'),
-            family_name: await change_details_draft.get_json('family_name'),
-            contact_me: await change_details_draft.get_json('contact_me')
-          } :
-          null
-    }
+    change_details_draft = await session.get_json('change_details_draft')
+    if (change_details_draft === undefined || env.now >= change_details_draft.expires)
+      change_details_draft = null
+
+    transaction.commit()
   }
-  finally {
+  catch (error) {
     transaction.rollback()
+    throw error
   }
-  console.log(
-    'details',
-    JSON.stringify(details),
-    'draft_details',
-    JSON.stringify(draft_details)
+
+  // cheat a little by ignoring the draft if it matches the original details,
+  // because user might have saved the details and left page before the timeout
+  if (
+    change_details_draft &&
+      change_details_draft.given_names === details.given_names &&
+      change_details_draft.family_name === details.family_name &&
+      change_details_draft.contact_me === details.contact_me
   )
+    change_details_draft = null
 
   await navbar(
     env,
@@ -61,114 +68,160 @@ return async env => {
         // signed in
         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.'}
 
-        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") {
+        div.accordion#accordion(role="tablist" aria-multiselectable="true") {
+          div.card#card-1 {
+            div.card-header#card-1-heading(role="tab") {
+              a.h5(data-toggle="collapse" data-parent="#accordion" href="#card-1-collapse" aria-expanded="true" aria-controls="card-1-collapse") {
                 'Change details'
               }
             }
-            div#step-1-collapse.collapse.show(role="tabpanel" aria-labelledby="step-1-heading" data-parent="#accordion") {
+            div#card-1-collapse.collapse.show(role="tabpanel" aria-labelledby="card-1-heading" data-parent="#accordion") {
               div.card-body {
-                div.row {
-                  div.col-md-6 {
-                    div.form-group {
-                      label.form-label(for="given-names") {'Given names *'}
-                      input.form-control#given-names(type="text" value=draft_details ? draft_details.given_names : details.given_names placeholder="Your given names" required="required" maxlength=256) {}
+                form#card-1-form {
+                  div.row {
+                    div.col-md-6 {
+                      div.form-group {
+                        label.form-label(for="given-names") {'Given names *'}
+                        input.form-control#given-names(type="text" value=change_details_draft ? change_details_draft.given_names : details.given_names placeholder=placeholder.given_names required maxlength=256) {}
+                        div.invalid-feedback {'Please enter a name we can address you by.'}
+                      }
                     }
-                  }
-                  div.col-md-6 {
-                    div.form-group {
-                      label.form-label(for="family-name") {'Family name'}
-                      input.form-control#family-name(type="text" value=draft_details ? draft_details.family_name : details.family_name placeholder="Your family name" maxlength=256) {}
+                    div.col-md-6 {
+                      div.form-group {
+                        label.form-label(for="family-name") {'Family name'}
+                        input.form-control#family-name(type="text" value=change_details_draft ? change_details_draft.family_name : details.family_name placeholder=placeholder.family_name maxlength=256) {}
+                      }
                     }
                   }
-                }
-                div.row.mb-3 {
-                  div.col-md-12 {
-                    div.custom-control.custom-checkbox {
-                      if (draft_details ? draft_details.contact_me : details.contact_me)
-                        input.custom-control-input#contact-me(type="checkbox" checked="checked") {}
-                      else
-                        input.custom-control-input#contact-me(type="checkbox") {}
-                      ' '
-                      label.custom-control-label(for="contact-me") {
-                        'Contact me by email with updates and special offers'
+                  div.row.mb-3 {
+                    div.col-md-12 {
+                      div.custom-control.custom-checkbox {
+                        if (change_details_draft ? change_details_draft.contact_me : details.contact_me)
+                          input.custom-control-input#contact-me(type="checkbox" checked) {}
+                        else
+                          input.custom-control-input#contact-me(type="checkbox") {}
+                        ' '
+                        label.custom-control-label(for="contact-me") {
+                          'Contact me by email with updates and special offers'
+                        }
                       }
                     }
                   }
                 }
 
-                if (draft_details)
-                  button.btn.btn-outline-secondary#step-1-revert(type="button") {'Revert'}
+                if (change_details_draft)
+                  button.btn.btn-outline-secondary#card-1-revert(type="button") {
+                    div.icon24-outer.mr-2 {
+                      div.icon24-inner {_out.push(fa_trash)}
+                    }
+                    'Revert'
+                  }
                 else
-                  button.btn.btn-outline-secondary#step-1-revert(type="button" disabled="disabled") {'Revert'}
-                if (draft_details)
-                  button.btn.btn-success.ml-3#step-1-save(type="button") {'Save'}
+                  button.btn.btn-outline-secondary#card-1-revert(type="button" disabled) {
+                    div.icon24-outer.mr-2 {
+                      div.icon24-inner {_out.push(fa_trash)}
+                    }
+                    'Revert'
+                  }
+                if (change_details_draft)
+                  button.btn.btn-success.ml-3#card-1-save(type="button") {
+                    div.icon24-outer.mr-2#card-1-icon {
+                      div.icon24-inner {_out.push(fa_cloud_upload_alt)}
+                    }
+                    div.icon24-outer.mr-2#card-1-tick(hidden) {
+                      div.icon24-inner {_out.push(icon_tick)}
+                    }
+                    div.icon24-outer.mr-2#card-1-cross(hidden) {
+                      div.icon24-inner {_out.push(icon_cross)}
+                    }
+                    div.icon24-outer.mr-2#card-1-spinner(hidden) {
+                      div.icon24-inner {
+                        div.spinner-border.spinner-border-sm(role="status") {}
+                      }
+                    }
+                    'Save'
+                  }
                 else
-                  button.btn.btn-success.ml-3#step-1-save(type="button" disabled="disabled") {'Save'}
+                  button.btn.btn-success.ml-3#card-1-save(type="button" disabled) {
+                    div.icon24-outer.mr-2#card-1-icon {
+                      div.icon24-inner {_out.push(fa_cloud_upload_alt)}
+                    }
+                    div.icon24-outer.mr-2#card-1-tick(hidden) {
+                      div.icon24-inner {_out.push(icon_tick)}
+                    }
+                    div.icon24-outer.mr-2#card-1-cross(hidden) {
+                      div.icon24-inner {_out.push(icon_cross)}
+                    }
+                    div.icon24-outer.mr-2#card-1-spinner(hidden) {
+                      div.icon24-inner {
+                        div.spinner-border.spinner-border-sm(role="status") {}
+                      }
+                    }
+                    'Save'
+                  }
 
-                p.'mt-3'.mb-0 {'* These fields are required.'}
+                p.'mt-3'.mb-0#card-1-message(hidden) {}
               }
             }
           }
-          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") {
+          div.card#card-2 {
+            div.card-header#card-2-heading(role="tab") {
+              a.h5.collapsed(data-toggle="collapse" data-parent="#accordion" href="#card-2-collapse" aria-expanded="false" aria-controls="card-2-collapse") {
                 'Change password'
               }
             }
-            div#step-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
+            div#card-2-collapse.collapse(role="tabpanel" aria-labelledby="step-2-heading" data-parent="#accordion") {
               div.card-body {
-                div.row.mb-3 {
-                  div.col-md-6 {
-                    div.form-group {
-                      label.form-label(for="old-password") {'Old password *'}
-                      input.form-control#old-password(type="password" placeholder="Old password" required="required" minlength=8 maxlength=256) {}
+                form#card-2-form {
+                  div.row {
+                    div.col-md-6 {
+                      div.form-group {
+                        label.form-label(for="old-password") {'Old password *'}
+                        input.form-control#old-password(type="password" placeholder="Verify" required minlength=8 maxlength=256) {}
+                        div.invalid-feedback {'Please enter your account\'s password of at least 8 characters.'}
+                      }
                     }
-                  }
-                  div.col-md-6 {
-                    div.form-group {
-                      label.form-label(for="new-password") {'New password *'}
-                      input.form-control#'new-password'(type="password" placeholder="New password" required="required" minlength=8 maxlength=256) {}
+                    div.col-md-6 {
+                      div.form-group {
+                        label.form-label(for="new-password") {'New password *'}
+                        input.form-control#'new-password'(type="password" placeholder="Choose" required minlength=8 maxlength=256) {}
+                        div.invalid-feedback {'Please choose a secure password of at least 8 characters.'}
+                      }
                     }
                   }
                 }
 
-                button.btn.btn-outline-secondary#step-2-clear(type="button" disabled="disabled") {'Clear'}
-                button.btn.btn-success.ml-3#step-2-save(type="button" disabled="disabled") {'Save'}
+                button.btn.btn-outline-secondary#card-2-clear(type="button" disabled) {
+                  div.icon24-outer.mr-2 {
+                    div.icon24-inner {_out.push(fa_trash)}
+                  }
+                  'Clear'
+                }
+                button.btn.btn-success.ml-3#card-2-save(type="button" disabled) {
+                  div.icon24-outer.mr-2#card-2-icon {
+                    div.icon24-inner {_out.push(fa_cloud_upload_alt)}
+                  }
+                  div.icon24-outer.mr-2#card-2-tick(hidden) {
+                    div.icon24-inner {_out.push(icon_tick)}
+                  }
+                  div.icon24-outer.mr-2#card-2-cross(hidden) {
+                    div.icon24-inner {_out.push(icon_cross)}
+                  }
+                  div.icon24-outer.mr-2#card-2-spinner(hidden) {
+                    div.icon24-inner {
+                      div.spinner-border.spinner-border-sm(role="status") {}
+                    }
+                  }
+                  'Save'
+                }
 
-                p.'mt-3'.mb-0 {'* These fields are required.'}
+                p.'mt-3'.mb-0#card-2-message(hidden) {}
               }
             }
           }
         }
+
+        p.text-muted.mt-3 {'* These fields are required.'}
       }
       else {
         // signed out
@@ -199,10 +252,12 @@ return async env => {
     },
     // scripts
     async _out => {
+      console.log('details', details)
       script {
         // this will be called by navbar logic after sign in/out
         function sign_in_out(status) {
           window.location.reload()
+          return true // suppresses status/dialog
         }
       }
 
@@ -210,207 +265,256 @@ return async env => {
         //script(src="/js/utils.js") {}
 
         script {
-          let step_1_dirty = ${JSON.stringify(draft_details !== null)}
-          let draft_timeout_running = false
-          let draft_timeout_handler = async () => {
-            draft_timeout_running = false
-            await api_call(
-              '/api/account/change_details/set_draft.json',
-              step_1_dirty ?
-                {
-                  given_names: document.getElementById('given-names').value.slice(0, 256),
-                  family_name: document.getElementById('family-name').value.slice(0, 256),
-                  contact_me: document.getElementById('contact-me').checked ? true : false
-                } :
-                null
-            )
-            //console.log('draft', await api_call('/api/account/change_details/get_draft.json'))
-          }
-
           document.addEventListener(
             'DOMContentLoaded',
             () => {
-              let step_1_change_handler = () => {
-                step_1_dirty = true
-                document.getElementById('step-1-revert').disabled = false
-                document.getElementById('step-1-save').disabled = false
-
-                if (!draft_timeout_running) {
-                  draft_timeout_running = true
-                  setTimeout(draft_timeout_handler, 5000)
-                }
-              }
-              document.getElementById('given-names').addEventListener(
-                'change',
-                step_1_change_handler
-              )
-              document.getElementById('family-name').addEventListener(
-                'change',
-                step_1_change_handler
-              )
-              document.getElementById('contact-me').addEventListener(
-                'change',
-                step_1_change_handler
-              )
-
-              document.getElementById('step-1-revert').addEventListener(
-                'click',
+              let id_accordion = document.getElementById('accordion')
+              let id_card_1 = document.getElementById('card-1')
+              let id_card_1_collapse = document.getElementById('card-1-collapse')
+              let id_card_1_cross = document.getElementById('card-1-cross')
+              let id_card_1_form = document.getElementById('card-1-form')
+              let id_card_1_heading = document.getElementById('card-1-heading')
+              let id_card_1_icon = document.getElementById('card-1-icon')
+              let id_card_1_message = document.getElementById('card-1-message')
+              let id_card_1_revert = document.getElementById('card-1-revert')
+              let id_card_1_save = document.getElementById('card-1-save')
+              let id_card_1_spinner = document.getElementById('card-1-spinner')
+              let id_card_1_tick = document.getElementById('card-1-tick')
+              let id_card_2 = document.getElementById('card-2')
+              let id_card_2_clear = document.getElementById('card-2-clear')
+              let id_card_2_collapse = document.getElementById('card-2-collapse')
+              let id_card_2_cross = document.getElementById('card-2-cross')
+              let id_card_2_form = document.getElementById('card-2-form')
+              let id_card_2_heading = document.getElementById('card-2-heading')
+              let id_card_2_icon = document.getElementById('card-2-icon')
+              let id_card_2_message = document.getElementById('card-2-message')
+              let id_card_2_save = document.getElementById('card-2-save')
+              let id_card_2_spinner = document.getElementById('card-2-spinner')
+              let id_card_2_tick = document.getElementById('card-2-tick')
+              let id_contact_me = document.getElementById('contact-me')
+              let id_family_name = document.getElementById('family-name')
+              let id_given_names = document.getElementById('given-names')
+              let id_new_password = document.getElementById('new-password')
+              let id_old_password = document.getElementById('old-password')
+
+              // pass original values in from server side
+              let orig_details = ${JSON.stringify(details)}
+
+              // change details card
+              let input_semaphore = new BinarySemaphore(false)
+              ;(
                 async () => {
-                  $('#step-1-tick').hide()
-                  $('#step-1-cross').hide()
-                  $('#step-1-spinner').show()
-
-                  let details
-                  try {
-                    details = await api_call(
-                      '/api/account/change_details/get.json'
+                  while (true) {
+                    await input_semaphore.acquire()
+                    await new Promise(resolve => setTimeout(resolve, 3000))
+                    input_semaphore.try_acquire()
+                    await api_call(
+                      '/api/account/change_details/set_draft.json',
+                      id_given_names.value === orig_details.given_names &&
+                        id_family_name.value === orig_details.family_name &&
+                        id_contact_me.checked === orig_details.contact_me ?
+                        null :
+                        {
+                          given_names: id_given_names.value.slice(0, 256),
+                          family_name: id_family_name.value.slice(0, 256),
+                          contact_me: id_contact_me.checked
+                        }
                     )
                   }
-                  catch (error) {
-                    let problem = Problem.from(error)
-                    console.log(problem.detail)
-
-                    $('#step-1-tick').hide()
-                    $('#step-1-cross').show()
-                    $('#step-1-spinner').hide()
-                    return
-                  }
-                  $('#step-1-tick').hide()
-                  $('#step-1-cross').hide()
-                  $('#step-1-spinner').hide()
-
-                  step_1_dirty = false
-                  document.getElementById('step-1-revert').disabled = true
-                  document.getElementById('step-1-save').disabled = true
+                }
+              )() // ignore returned promise (start thread)
+
+              let card_1_edited = () => {
+                input_semaphore.release()
+
+                let disabled =
+                  id_given_names.value === orig_details.given_names &&
+                    id_family_name.value === orig_details.family_name &&
+                    id_contact_me.checked === orig_details.contact_me
+                id_card_1_revert.disabled = disabled
+                id_card_1_save.disabled = disabled
+                id_card_1_icon.hidden = false
+                id_card_1_tick.hidden = true
+                id_card_1_cross.hidden = true
+                id_card_1_spinner.hidden = true
+                id_card_1_message.hidden = true
+              }
 
-                  document.getElementById('given-names').value = details.given_names
-                  document.getElementById('family-name').value = details.family_name
-                  document.getElementById('contact-me').checked = details.contact_me
+              id_given_names.addEventListener('input', card_1_edited)
+              id_family_name.addEventListener('input', card_1_edited)
+              id_contact_me.addEventListener('input', card_1_edited)
 
-                  if (!draft_timeout_running) {
-                    draft_timeout_running = true
-                    setTimeout(draft_timeout_handler, 5000)
-                  }
+              id_card_1_revert.addEventListener(
+                'click',
+                async () => {
+                  id_given_names.value = orig_details.given_names
+                  id_family_name.value = orig_details.family_name
+                  id_contact_me.checked = orig_details.contact_me
+
+                  // cut down form of card_1_edited() logic:
+                  input_semaphore.release()
+
+                  id_card_1_revert.disabled = true
+                  id_card_1_save.disabled = true
+                  id_card_1_icon.hidden = false
+                  id_card_1_tick.hidden = true
+                  id_card_1_cross.hidden = true
+                  id_card_1_spinner.hidden = true
+                  id_card_1_message.hidden = true
                 }
               )
 
-              document.getElementById('step-1-save').addEventListener(
+              id_card_1_save.addEventListener(
                 'click',
                 async () => {
-                  if (
-                    !document.getElementById('given-names').reportValidity() ||
-                      !document.getElementById('family-name').reportValidity()
-                  ) {
-                    $('#step-1-tick').hide()
-                    $('#step-1-cross').show()
-                    $('#step-1-spinner').hide()
-                    return false
+                  id_card_1_icon.hidden = false
+                  id_card_1_tick.hidden = true
+                  id_card_1_cross.hidden = true
+                  id_card_1_spinner.hidden = true
+                  // the below causes an ugly flicker, so just keep the message
+                  //id_card_1_message.hidden = true
+
+                  if (!id_card_1_form.checkValidity()) {
+                    id_card_1_form.classList.add('was-validated');
+
+                    id_card_1_icon.hidden = true
+                    id_card_1_cross.hidden = false
+                    return
                   }
-                  $('#step-1-tick').hide()
-                  $('#step-1-cross').hide()
-                  $('#step-1-spinner').show()
+                  id_card_1_form.classList.remove('was-validated');
 
+                  id_card_1_icon.hidden = true
+                  id_card_1_spinner.hidden = false
                   try {
                     await api_call(
                       '/api/account/change_details/set.json',
                       {
-                        given_names: document.getElementById('given-names').value.slice(0, 256),
-                        family_name: document.getElementById('family-name').value.slice(0, 256),
-                        contact_me: document.getElementById('contact-me').checked ? true : false
+                        given_names: id_given_names.value.slice(0, 256),
+                        family_name: id_family_name.value.slice(0, 256),
+                        contact_me: id_contact_me.checked
                       }
                     )
                   }
                   catch (error) {
                     let problem = Problem.from(error)
-                    console.log(problem.detail)
 
-                    $('#step-1-tick').hide()
-                    $('#step-1-cross').show()
-                    $('#step-1-spinner').hide()
+                    id_card_1_cross.hidden = false
+                    id_card_1_spinner.hidden = true
+
+                    id_card_1_message.textContent = problem.detail
+                    //id_card_1_message.classList.remove('text-success')
+                    id_card_1_message.classList.add('text-danger')
+                    id_card_1_message.hidden = false
                     return
                   }
-                  $('#step-1-tick').show()
-                  $('#step-1-cross').hide()
-                  $('#step-1-spinner').hide()
-
-                  step_1_dirty = false
-                  document.getElementById('step-1-revert').disabled = true
-                  document.getElementById('step-1-save').disabled = true
-
-                  // SHOULD execute immediately here
-                  // (because user is likely to leave the page after save)
-                  if (!draft_timeout_running) {
-                    draft_timeout_running = true
-                    setTimeout(draft_timeout_handler, 5000)
-                  }
+                  id_card_1_tick.hidden = false
+                  id_card_1_spinner.hidden = true
+
+                  orig_details.given_names = id_given_names.value
+                  orig_details.family_name = id_family_name.value
+                  orig_details.contact_me = id_contact_me.checked
+
+                  // cut down form of card_1_edited() logic:
+                  input_semaphore.release()
+
+                  id_card_1_revert.disabled = true
+                  id_card_1_save.disabled = true
+                  //id_card_1_icon.hidden = false
+                  //id_card_1_tick.hidden = true
+                  //id_card_1_cross.hidden = true
+                  //id_card_1_spinner.hidden = true
+                  id_card_1_message.hidden = true
                 }
               )
 
-              let step_2_change_handler = () => {
-                document.getElementById('step-2-clear').disabled = false
-                document.getElementById('step-2-save').disabled = false
+              // change password card
+              let card_2_edited = () => {
+                let disabled =
+                  id_old_password.value.length === 0 &&
+                    id_new_password.value.length === 0
+                id_card_2_clear.disabled = disabled
+                id_card_2_save.disabled = disabled
+                id_card_2_icon.hidden = false
+                id_card_2_tick.hidden = true
+                id_card_2_cross.hidden = true
+                id_card_2_spinner.hidden = true
+                id_card_2_message.hidden = true
               }
-              document.getElementById('old-password').addEventListener(
-                'change',
-                step_2_change_handler
-              )
-              document.getElementById('new-password').addEventListener(
-                'change',
-                step_2_change_handler
-              )
 
-              document.getElementById('step-2-clear').addEventListener(
-                'click',
-                () => {
-                  document.getElementById('step-2-clear').disabled = true
-                  document.getElementById('step-2-save').disabled = true
+              id_old_password.addEventListener('input', card_2_edited)
+              id_new_password.addEventListener('input', card_2_edited)
 
-                  document.getElementById('old-password').value = ''
-                  document.getElementById('new-password').value = ''
+              id_card_2_clear.addEventListener(
+                'click',
+                async () => {
+                  id_old_password.value = ''
+                  id_new_password.value = ''
+
+                  // cut down form of card_2_edited() logic:
+                  id_card_2_clear.disabled = true
+                  id_card_2_save.disabled = true
+                  id_card_2_icon.hidden = false
+                  id_card_2_tick.hidden = true
+                  id_card_2_cross.hidden = true
+                  id_card_2_spinner.hidden = true
+                  id_card_2_message.hidden = true
                 }
               )
 
-              document.getElementById('step-2-save').addEventListener(
+              id_card_2_save.addEventListener(
                 'click',
                 async () => {
-                  if (
-                    !document.getElementById('old-password').reportValidity() ||
-                      !document.getElementById('new-password').reportValidity()
-                  ) {
-                    $('#step-2-tick').hide()
-                    $('#step-2-cross').show()
-                    $('#step-2-spinner').hide()
-                    return false
+                  id_card_2_icon.hidden = false
+                  id_card_2_tick.hidden = true
+                  id_card_2_cross.hidden = true
+                  id_card_2_spinner.hidden = true
+                  // the below causes an ugly flicker, so just keep the message
+                  //id_card_2_message.hidden = true
+
+                  if (!id_card_2_form.checkValidity()) {
+                    id_card_2_form.classList.add('was-validated');
+
+                    id_card_2_icon.hidden = true
+                    id_card_2_cross.hidden = false
+                    return
                   }
-                  $('#step-2-tick').hide()
-                  $('#step-2-cross').hide()
-                  $('#step-2-spinner').show()
+                  id_card_2_form.classList.remove('was-validated');
 
+                  id_card_2_icon.hidden = true
+                  id_card_2_spinner.hidden = false
                   try {
                     await api_call(
                       '/api/account/change_password.json',
-                      document.getElementById('old-password').value.slice(0, 256),
-                      document.getElementById('new-password').value.slice(0, 256)
+                      id_old_password.value.slice(0, 256),
+                      id_new_password.value.slice(0, 256)
                     )
                   }
                   catch (error) {
                     let problem = Problem.from(error)
-                    console.log(problem.detail)
 
-                    $('#step-2-tick').hide()
-                    $('#step-2-cross').show()
-                    $('#step-2-spinner').hide()
+                    id_card_2_cross.hidden = false
+                    id_card_2_spinner.hidden = true
+
+                    id_card_2_message.textContent = problem.detail
+                    //id_card_2_message.classList.remove('text-success')
+                    id_card_2_message.classList.add('text-danger')
+                    id_card_2_message.hidden = false
                     return
                   }
-                  $('#step-2-tick').show()
-                  $('#step-2-cross').hide()
-                  $('#step-2-spinner').hide()
-
-                  document.getElementById('step-2-clear').disabled = true
-                  document.getElementById('step-2-save').disabled = true
-
-                  document.getElementById('old-password').value = ''
-                  document.getElementById('new-password').value = ''
+                  id_card_2_tick.hidden = false
+                  id_card_2_spinner.hidden = true
+
+                  id_old_password.value = ''
+                  id_new_password.value = ''
+
+                  // cut down form of card_2_edited() logic:
+                  id_card_2_clear.disabled = true
+                  id_card_2_save.disabled = true
+                  //id_card_2_icon.hidden = false
+                  //id_card_2_tick.hidden = true
+                  //id_card_2_cross.hidden = true
+                  //id_card_2_spinner.hidden = true
+                  id_card_2_message.hidden = true
                 }
               )
             }
index 2cd1c0e..5dc8da2 100644 (file)
@@ -67,14 +67,14 @@ return async env => {
                   div.col-md-6 {
                     div.form-group {
                       label.form-label(for="given-names") {'Given names *'}
-                      input.form-control#given-names(type="text" value=sign_up_draft !== null ? sign_up_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
+                      input.form-control#given-names(type="text" value=sign_up_draft ? sign_up_draft.given_names : '' placeholder=placeholder.given_names required maxlength=256) {}
                       div.invalid-feedback {'Please enter a name we can address you by.'}
                     }
                   }
                   div.col-md-6 {
                     div.form-group {
                       label.form-label(for="family-name") {'Family name'}
-                      input.form-control#family-name(type="text" value=sign_up_draft !== null ? sign_up_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
+                      input.form-control#family-name(type="text" value=sign_up_draft ? sign_up_draft.family_name : '' placeholder=placeholder.family_name maxlength=256) {}
                     }
                   }
                 }
@@ -82,7 +82,7 @@ return async env => {
                   div.col-md-6 {
                     div.form-group {
                       label.form-label(for="email") {'Email *'}
-                      input.form-control#email(type="email" value=sign_up_draft !== null ? sign_up_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
+                      input.form-control#email(type="email" value=sign_up_draft ? sign_up_draft.email : '' placeholder=placeholder.email required maxlength=256) {}
                       div.invalid-feedback {'Please enter an email address we can contact you on.'}
                     }
                   }
@@ -130,7 +130,7 @@ return async env => {
                 }
               }
 
-              if (sign_up_draft !== null)
+              if (sign_up_draft)
                 button.btn.btn-success#card-1-create-account(type="button") {
                   div.icon24-outer.mr-2 {
                     div.icon24-inner {_out.push(fa_user_plus)}
@@ -244,7 +244,7 @@ return async env => {
                         email: id_email.value.slice(0, 256).toLowerCase(),
                         given_names: id_given_names.value.slice(0, 256),
                         family_name: id_family_name.value.slice(0, 256),
-                        contact_me: id_contact_me.checked ? true : false
+                        contact_me: id_contact_me.checked
                       }
                   )
                 }
@@ -307,7 +307,7 @@ return async env => {
                 given_names: id_given_names.value.slice(0, 256),
                 family_name: id_family_name.value.slice(0, 256),
                 password: id_password.value.slice(0, 256),
-                contact_me: id_contact_me.checked ? true : false
+                contact_me: id_contact_me.checked
               }
 
               id_card_1_spinner.hidden = false