first commit
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import { useState, Dispatch, SetStateAction } from 'react'
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import LeaveModalForm, { LeaveModalFormProps } from './modal-form'
|
||||
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||
import {
|
||||
OLModalBody,
|
||||
OLModalFooter,
|
||||
OLModalHeader,
|
||||
OLModalTitle,
|
||||
} from '@/features/ui/components/ol/ol-modal'
|
||||
|
||||
const WRITEFULL_SUPPORT_EMAIL = 'support@writefull.com'
|
||||
|
||||
type LeaveModalContentProps = {
|
||||
handleHide: () => void
|
||||
inFlight: boolean
|
||||
setInFlight: Dispatch<SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
function LeaveModalContentBlock({
|
||||
setInFlight,
|
||||
isFormValid,
|
||||
setIsFormValid,
|
||||
}: LeaveModalFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const { isOverleaf } = getMeta('ol-ExposedSettings')
|
||||
const hasPassword = getMeta('ol-hasPassword')
|
||||
|
||||
if (isOverleaf && !hasPassword) {
|
||||
return (
|
||||
<p>
|
||||
<b>
|
||||
<a href="/user/password/reset">{t('delete_acct_no_existing_pw')}</a>
|
||||
</b>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<LeaveModalForm
|
||||
setInFlight={setInFlight}
|
||||
isFormValid={isFormValid}
|
||||
setIsFormValid={setIsFormValid}
|
||||
/>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="to_delete_your_writefull_account"
|
||||
values={{ email: WRITEFULL_SUPPORT_EMAIL }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
components={{
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
a: <a href={`mailto:${WRITEFULL_SUPPORT_EMAIL}`} />,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function LeaveModalContent({
|
||||
handleHide,
|
||||
inFlight,
|
||||
setInFlight,
|
||||
}: LeaveModalContentProps) {
|
||||
const { t } = useTranslation()
|
||||
const [isFormValid, setIsFormValid] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<OLModalHeader closeButton>
|
||||
<OLModalTitle>{t('delete_account')}</OLModalTitle>
|
||||
</OLModalHeader>
|
||||
|
||||
<OLModalBody>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="delete_account_warning_message_3"
|
||||
components={{ strong: <strong /> }}
|
||||
/>
|
||||
</p>
|
||||
<LeaveModalContentBlock
|
||||
setInFlight={setInFlight}
|
||||
isFormValid={isFormValid}
|
||||
setIsFormValid={setIsFormValid}
|
||||
/>
|
||||
</OLModalBody>
|
||||
|
||||
<OLModalFooter>
|
||||
<OLButton disabled={inFlight} onClick={handleHide} variant="secondary">
|
||||
{t('cancel')}
|
||||
</OLButton>
|
||||
|
||||
<OLButton
|
||||
form="leave-form"
|
||||
type="submit"
|
||||
variant="danger"
|
||||
disabled={inFlight || !isFormValid}
|
||||
>
|
||||
{inFlight ? <>{t('deleting')}…</> : t('delete')}
|
||||
</OLButton>
|
||||
</OLModalFooter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LeaveModalContent
|
@@ -0,0 +1,51 @@
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import { FetchError } from '../../../../infrastructure/fetch-json'
|
||||
import OLNotification from '@/features/ui/components/ol/ol-notification'
|
||||
|
||||
type LeaveModalFormErrorProps = {
|
||||
error: FetchError
|
||||
}
|
||||
|
||||
function LeaveModalFormError({ error }: LeaveModalFormErrorProps) {
|
||||
const { t } = useTranslation()
|
||||
const { isOverleaf } = getMeta('ol-ExposedSettings')
|
||||
|
||||
let errorMessage
|
||||
let errorTip = null
|
||||
if (error.response?.status === 403) {
|
||||
errorMessage = t('email_or_password_wrong_try_again')
|
||||
if (isOverleaf) {
|
||||
errorTip = (
|
||||
<Trans
|
||||
i18nKey="user_deletion_password_reset_tip"
|
||||
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
|
||||
components={[<a href="/user/password/reset" />]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
} else if (error.data?.error === 'SubscriptionAdminDeletionError') {
|
||||
errorMessage = t('subscription_admins_cannot_be_deleted')
|
||||
} else {
|
||||
errorMessage = t('user_deletion_error')
|
||||
}
|
||||
|
||||
return (
|
||||
<OLNotification
|
||||
type="error"
|
||||
content={
|
||||
<>
|
||||
{errorMessage}
|
||||
{errorTip ? (
|
||||
<>
|
||||
<br />
|
||||
{errorTip}
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default LeaveModalFormError
|
@@ -0,0 +1,118 @@
|
||||
import { useState, useEffect, Dispatch, SetStateAction } from 'react'
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import { postJSON, FetchError } from '../../../../infrastructure/fetch-json'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import LeaveModalFormError from './modal-form-error'
|
||||
import { useLocation } from '../../../../shared/hooks/use-location'
|
||||
import OLFormGroup from '@/features/ui/components/ol/ol-form-group'
|
||||
import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
|
||||
import OLFormControl from '@/features/ui/components/ol/ol-form-control'
|
||||
import OLFormCheckbox from '@/features/ui/components/ol/ol-form-checkbox'
|
||||
|
||||
export type LeaveModalFormProps = {
|
||||
setInFlight: Dispatch<SetStateAction<boolean>>
|
||||
isFormValid: boolean
|
||||
setIsFormValid: Dispatch<SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
function LeaveModalForm({
|
||||
setInFlight,
|
||||
isFormValid,
|
||||
setIsFormValid,
|
||||
}: LeaveModalFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const userDefaultEmail = getMeta('ol-usersEmail')!
|
||||
const location = useLocation()
|
||||
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [confirmation, setConfirmation] = useState(false)
|
||||
const [error, setError] = useState<FetchError | null>(null)
|
||||
|
||||
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEmail(event.target.value)
|
||||
}
|
||||
|
||||
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPassword(event.target.value)
|
||||
}
|
||||
|
||||
const handleConfirmationChange = () => {
|
||||
setConfirmation(prev => !prev)
|
||||
}
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault()
|
||||
if (!isFormValid) {
|
||||
return
|
||||
}
|
||||
setError(null)
|
||||
setInFlight(true)
|
||||
postJSON('/user/delete', {
|
||||
body: {
|
||||
password,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
location.assign('/')
|
||||
})
|
||||
.catch(setError)
|
||||
.finally(() => {
|
||||
setInFlight(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setIsFormValid(
|
||||
!!email &&
|
||||
email.toLowerCase() === userDefaultEmail.toLowerCase() &&
|
||||
password.length > 0 &&
|
||||
confirmation
|
||||
)
|
||||
}, [setIsFormValid, userDefaultEmail, email, password, confirmation])
|
||||
|
||||
return (
|
||||
<form id="leave-form" onSubmit={handleSubmit}>
|
||||
<OLFormGroup controlId="email-input">
|
||||
<OLFormLabel>{t('email')}</OLFormLabel>
|
||||
<OLFormControl
|
||||
type="text"
|
||||
placeholder={t('email')}
|
||||
required
|
||||
value={email}
|
||||
onChange={handleEmailChange}
|
||||
/>
|
||||
</OLFormGroup>
|
||||
<OLFormGroup controlId="password-input">
|
||||
<OLFormLabel>{t('password')}</OLFormLabel>
|
||||
<OLFormControl
|
||||
type="password"
|
||||
placeholder={t('password')}
|
||||
required
|
||||
value={password}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</OLFormGroup>
|
||||
<OLFormCheckbox
|
||||
id="confirm-account-deletion"
|
||||
required
|
||||
checked={confirmation}
|
||||
onChange={handleConfirmationChange}
|
||||
label={
|
||||
<Trans
|
||||
i18nKey="delete_account_confirmation_label"
|
||||
components={[<i />]} // eslint-disable-line react/jsx-key
|
||||
values={{
|
||||
userDefaultEmail,
|
||||
}}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{error ? <LeaveModalFormError error={error} /> : null}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default LeaveModalForm
|
@@ -0,0 +1,30 @@
|
||||
import { useState, useCallback } from 'react'
|
||||
import LeaveModalContent from './modal-content'
|
||||
import OLModal from '@/features/ui/components/ol/ol-modal'
|
||||
|
||||
type LeaveModalProps = {
|
||||
isOpen: boolean
|
||||
handleClose: () => void
|
||||
}
|
||||
|
||||
function LeaveModal({ isOpen, handleClose }: LeaveModalProps) {
|
||||
const [inFlight, setInFlight] = useState(false)
|
||||
|
||||
const handleHide = useCallback(() => {
|
||||
if (!inFlight) {
|
||||
handleClose()
|
||||
}
|
||||
}, [handleClose, inFlight])
|
||||
|
||||
return (
|
||||
<OLModal animation show={isOpen} onHide={handleHide} id="leave-modal">
|
||||
<LeaveModalContent
|
||||
handleHide={handleHide}
|
||||
inFlight={inFlight}
|
||||
setInFlight={setInFlight}
|
||||
/>
|
||||
</OLModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default LeaveModal
|
Reference in New Issue
Block a user