mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
126 lines
3.5 KiB
JavaScript
126 lines
3.5 KiB
JavaScript
|
import classNames from 'classnames'
|
||
|
import { FetchError, postJSON } from '../../infrastructure/fetch-json'
|
||
|
import { validateCaptchaV2 } from './captcha'
|
||
|
|
||
|
// Form helper(s) to handle:
|
||
|
// - Attaching to the relevant form elements
|
||
|
// - Listening for submit event
|
||
|
// - Validating captcha
|
||
|
// - Sending fetch request
|
||
|
// - Redirect handling
|
||
|
// - Showing errors
|
||
|
// - Disabled state
|
||
|
|
||
|
function formSubmitHelper(formEl) {
|
||
|
formEl.addEventListener('submit', async e => {
|
||
|
e.preventDefault()
|
||
|
|
||
|
formEl.dispatchEvent(new Event('inflight'))
|
||
|
|
||
|
// We currently only have capacity to show 1 error, so this is probably
|
||
|
// unnecessary but I've used a similar data structure in the past and it was
|
||
|
// nice to be able to handle multiple (e.g. validation) errors at once
|
||
|
const messageBag = []
|
||
|
|
||
|
try {
|
||
|
const captchaResponse = await validateCaptcha(formEl)
|
||
|
|
||
|
const data = await sendFormRequest(formEl, captchaResponse)
|
||
|
|
||
|
// Handle redirects. From poking around, this still appears to be the
|
||
|
// "correct" way of handling redirects with fetch
|
||
|
if (data.redir) {
|
||
|
window.location = data.redir
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Show a success message (e.g. used on 2FA page)
|
||
|
if (data.message) {
|
||
|
messageBag.push({
|
||
|
type: 'message',
|
||
|
text: data.message,
|
||
|
})
|
||
|
}
|
||
|
} catch (error) {
|
||
|
let text = error.message
|
||
|
if (error instanceof FetchError) {
|
||
|
text = error.getUserFacingMessage()
|
||
|
}
|
||
|
messageBag.push({
|
||
|
type: 'error',
|
||
|
text,
|
||
|
})
|
||
|
} finally {
|
||
|
// Possibly this could be wired up through events too?
|
||
|
showMessages(formEl, messageBag)
|
||
|
|
||
|
formEl.dispatchEvent(new Event('not-inflight'))
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
async function validateCaptcha(formEl) {
|
||
|
let captchaResponse
|
||
|
if (formEl.hasAttribute('captcha')) {
|
||
|
captchaResponse = await validateCaptchaV2()
|
||
|
}
|
||
|
return captchaResponse
|
||
|
}
|
||
|
|
||
|
async function sendFormRequest(formEl, captchaResponse) {
|
||
|
const formData = new FormData(formEl)
|
||
|
if (captchaResponse) {
|
||
|
formData.set('g-recaptcha-response', captchaResponse)
|
||
|
}
|
||
|
const body = Object.fromEntries(formData.entries())
|
||
|
const url = formEl.getAttribute('action')
|
||
|
return postJSON(url, { body })
|
||
|
}
|
||
|
|
||
|
function showMessages(formEl, messageBag) {
|
||
|
const messagesEl = formEl.querySelector('[data-ol-form-messages]')
|
||
|
if (!messagesEl) return
|
||
|
|
||
|
// Clear content
|
||
|
messagesEl.textContent = ''
|
||
|
|
||
|
// Render messages
|
||
|
messageBag.forEach(message => {
|
||
|
const messageEl = document.createElement('div')
|
||
|
messageEl.className = classNames('alert', {
|
||
|
'alert-danger': message.type === 'error',
|
||
|
'alert-success': message.type !== 'error',
|
||
|
})
|
||
|
messageEl.textContent = message.text
|
||
|
messagesEl.append(messageEl)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function formInflightHelper(el) {
|
||
|
const disabledEl = el.querySelector('[data-ol-disabled-inflight]')
|
||
|
const showWhenNotInflightEl = el.querySelector('[data-ol-not-inflight-text]')
|
||
|
const showWhenInflightEl = el.querySelector('[data-ol-inflight-text]')
|
||
|
|
||
|
el.addEventListener('inflight', () => {
|
||
|
disabledEl.disabled = true
|
||
|
toggleDisplay(showWhenNotInflightEl, showWhenInflightEl)
|
||
|
})
|
||
|
|
||
|
el.addEventListener('not-inflight', () => {
|
||
|
disabledEl.disabled = false
|
||
|
toggleDisplay(showWhenInflightEl, showWhenNotInflightEl)
|
||
|
})
|
||
|
|
||
|
function toggleDisplay(hideEl, showEl) {
|
||
|
hideEl.setAttribute('hidden', '')
|
||
|
showEl.removeAttribute('hidden')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export function hydrateForm(el) {
|
||
|
formSubmitHelper(el)
|
||
|
formInflightHelper(el)
|
||
|
}
|
||
|
|
||
|
document.querySelectorAll(`[data-ol-form]`).forEach(form => hydrateForm(form))
|