Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: Detecting password presence for users registered via social providers #392

Merged
merged 1 commit into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions public/locales/en/user.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"account": {
"about_you": "Profile info",
"add_password": "Add password",
"add_password_error": "Failed to proceed to the Add Password screen. Please try again.",
"add_password_modal_text": "We sent instructions to {{email}} on how to add a password. If you don’t receive the email within a few minutes, check your spam folder or",
"add_profile": "Add profile",
"cancel": "Cancel",
"confirm_password": "Confirm password",
Expand All @@ -20,6 +23,10 @@
},
"title": "Delete account"
},
"delete_account_password_warning": {
"text": "To delete your account when using social media account(s) for sign-in, you will first need to add an account password and then initiate the deletion process again.",
"title": "Warning"
},
"edit_account": "Edit account",
"edit_information": "Edit information",
"edit_password": "Edit password",
Expand All @@ -44,6 +51,9 @@
"manage_profiles": "Manage profiles",
"other_registration_details": "Other registration details",
"password": "Password",
"proceed_to_adding_a_password": "Proceed to adding a password",
"resend_mail": "resend the email.",
"resend_mail_error": "An error occurred while resending the email. Please try again.",
"save": "Save",
"security": "Password",
"terms_and_tracking": "Legal & Marketing",
Expand Down
10 changes: 10 additions & 0 deletions public/locales/es/user.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"account": {
"about_you": "Información de perfil",
"add_password": "Agregar contraseña",
"add_password_error": "No se pudo pasar a la pantalla Agregar contraseña. Inténtalo de nuevo.",
"add_password_modal_text": "Enviamos instrucciones a {{email}} sobre cómo agregar una contraseña. Si no recibes el correo electrónico en unos minutos, revisa tu carpeta de spam o",
"add_profile": "Añadir perfil",
"cancel": "Cancelar",
"confirm_password": "Confirmar contraseña",
Expand All @@ -20,6 +23,10 @@
},
"title": "Borrar cuenta"
},
"delete_account_password_warning": {
"text": "Para eliminar su cuenta cuando utiliza cuentas de redes sociales para iniciar sesión, primero deberá agregar una contraseña de cuenta y luego iniciar el proceso de eliminación nuevamente.",
"title": "Advertencia"
},
"edit_account": "Editar cuenta",
"edit_information": "Editar información",
"edit_password": "Editar contraseña",
Expand All @@ -44,6 +51,9 @@
"manage_profiles": "Administrar perfiles",
"other_registration_details": "Otros detalles de registro",
"password": "Contraseña",
"proceed_to_adding_a_password": "Proceda a agregar una contraseña",
"resend_mail": "reenvíe el correo electrónico.",
"resend_mail_error": "Se produjo un error al reenviar el correo electrónico. Inténtalo de nuevo.",
"save": "Guardar",
"security": "Contraseña",
"terms_and_tracking": "Jurídico y marketing",
Expand Down
26 changes: 22 additions & 4 deletions src/components/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import useToggle from '#src/hooks/useToggle';
import { formatConsentsFromValues, formatConsents, formatConsentValues, formatConsentsToRegisterFields } from '#src/utils/collection';
import { addQueryParam } from '#src/utils/location';
import { useAccountStore } from '#src/stores/AccountStore';
import { exportAccountData, updateConsents, updateUser } from '#src/stores/AccountController';
import { exportAccountData, resetPassword, updateConsents, updateUser } from '#src/stores/AccountController';

type Props = {
panelClassName?: string;
Expand Down Expand Up @@ -67,6 +67,11 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
shallow,
);

borkopetrovicc marked this conversation as resolved.
Show resolved Hide resolved
// users authenticated with social (register_source: facebook, google, twitter) do not have password by default
const registerSource = customer?.metadata?.register_source;
const isSocialLogin = (registerSource && registerSource !== 'inplayer') || false;
const shouldAddPassword = (isSocialLogin && !customer?.metadata?.has_password) || false;

const [termsConsents, nonTermsConsents] = useMemo(() => {
const terms: Consent[] = [];
const nonTerms: Consent[] = [];
Expand Down Expand Up @@ -173,7 +178,14 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
};
}

const editPasswordClickHandler = () => {
const editPasswordClickHandler = async () => {
if (!customer) {
return;
}
if (isSocialLogin && shouldAddPassword) {
await resetPassword(customer.email, '');
return navigate(addQueryParam(location, 'u', 'add-password'));
}
const modal = canChangePasswordWithOldPassword ? 'edit-password' : 'reset-password';
navigate(addQueryParam(location, 'u', modal));
};
Expand Down Expand Up @@ -269,7 +281,13 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
}),
formSection({
label: t('account.security'),
editButton: <Button label={t('account.edit_password')} type="button" onClick={() => (customer ? editPasswordClickHandler() : null)} />,
editButton: (
<Button
label={shouldAddPassword ? t('account.add_password') : t('account.edit_password')}
type="button"
onClick={() => (customer ? editPasswordClickHandler() : null)}
/>
),
}),
formSection({
label: t('account.terms_and_tracking'),
Expand Down Expand Up @@ -347,7 +365,7 @@ const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }
type="button"
variant="danger"
onClick={() => {
navigate(addQueryParam(location, 'u', 'delete-account'));
navigate(addQueryParam(location, 'u', shouldAddPassword ? 'warning-account-deletion' : 'delete-account'));
}}
/>
</div>
Expand Down
1 change: 0 additions & 1 deletion src/components/DeleteAccountModal/DeleteAccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ const DeleteAccountModal = () => {
if (!location.search.includes('delete-account-confirmation') && enteredPassword) {
// handle back button
setEnteredPassword('');
deleteAccount.reset();
borkopetrovicc marked this conversation as resolved.
Show resolved Hide resolved
resetForm();
}
if (location.search.includes('delete-account-confirmation') && !enteredPassword) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@use 'src/styles/variables';

.formFeedback {
margin-bottom: -24px;
}

.formContainer {
display: flex;
flex-direction: column;
gap: 15px;
align-items: center;
padding: 27px;
}

.passwordButtonsContainer {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
width: 100%;
}

.buttonsContainer {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
width: 100%;
gap: 16px;
}

.heading {
width: 50%;
margin-bottom: 5px;
color: variables.$gray-white;
font-weight: 400;
font-size: 30px;
line-height: 28px;
text-align: center;
}

.paragraph {
width: 100%;
margin: 0;
padding: 0;
color: variables.$gray-white;
font-size: 16px;
line-height: 20px;
text-align: left;
}

.button {
margin: auto;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useTranslation } from 'react-i18next';
import { useNavigate, useLocation } from 'react-router';
import { useCallback, useState } from 'react';

import Button from '../Button/Button';

import styles from './DeleteAccountPasswordWarning.module.scss';

import { addQueryParam, removeQueryParam } from '#src/utils/location';
import { useAccountStore } from '#src/stores/AccountStore';
import { resetPassword } from '#src/stores/AccountController';
import FormFeedback from '#components/FormFeedback/FormFeedback';

const DeleteAccountPasswordWarning = () => {
const { t } = useTranslation('user');
const email = useAccountStore((state) => state.user?.email);
const [errorMessage, setErrorMessage] = useState<string>();
const navigate = useNavigate();
const location = useLocation();

const handleCancel = useCallback(() => {
navigate(removeQueryParam(location, 'u'), { replace: true });
}, [location, navigate]);
const proceedToAddPasswordClickHandler = async () => {
try {
if (email) {
await resetPassword(email, '');
navigate(addQueryParam(location, 'u', 'add-password'));
}
} catch (error: unknown) {
setErrorMessage(t('account.add_password_error'));
}
};

return (
<div className={styles.formContainer}>
{errorMessage && (
<div className={styles.formFeedback}>
<FormFeedback variant="error">{errorMessage}</FormFeedback>
</div>
)}
<h2 className={styles.heading}>{t('account.delete_account_password_warning.title')}</h2>
<p className={styles.paragraph}>{t('account.delete_account_password_warning.text')}</p>
<div className={styles.passwordButtonsContainer}>
<Button
type="submit"
onClick={proceedToAddPasswordClickHandler}
className={styles.button}
color="primary"
fullWidth
label={t('account.proceed_to_adding_a_password')}
/>
<Button type="button" onClick={handleCancel} className={styles.button} variant="text" fullWidth label={t('account.cancel')} />
</div>
</div>
);
};

export default DeleteAccountPasswordWarning;
17 changes: 17 additions & 0 deletions src/components/EditPasswordForm/EditPasswordForm.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,20 @@
.textField {
margin-bottom: 24px;
}

.paragraph {
width: 100%;
margin: 0;
padding-bottom: 20px;
color: variables.$gray-white;
font-size: 17px;
line-height: 21px;
text-align: left;
}

.resendLink {
padding-left: 4px;
color: rgb(129, 175, 254);
cursor: pointer;

}
43 changes: 32 additions & 11 deletions src/components/EditPasswordForm/EditPasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import TextField from '../TextField/TextField';

import styles from './EditPasswordForm.module.scss';

import type { FormErrors } from '#types/form';
import type { EditPasswordFormData } from '#types/account';
import FormFeedback from '#components/FormFeedback/FormFeedback';
import Button from '#components/Button/Button';
import FormFeedback from '#components/FormFeedback/FormFeedback';
import LoadingOverlay from '#components/LoadingOverlay/LoadingOverlay';
import { testId } from '#src/utils/common';
import type { EditPasswordFormData } from '#types/account';
import type { FormErrors } from '#types/form';

type Props = {
onSubmit: React.FormEventHandler<HTMLFormElement>;
Expand All @@ -24,16 +24,36 @@ type Props = {
submitting: boolean;
showOldPasswordField?: boolean;
showResetTokenField?: boolean;
email?: string;
onResendEmailClick?: () => void;
};

const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOldPasswordField, showResetTokenField, value, errors, submitting }: Props) => {
const { t } = useTranslation('account');
const EditPasswordForm: React.FC<Props> = ({
onSubmit,
onChange,
onBlur,
showOldPasswordField,
showResetTokenField,
value,
errors,
submitting,
email,
onResendEmailClick,
}: Props) => {
const { t } = useTranslation(['account', 'user']);
return (
<form onSubmit={onSubmit} data-testid={testId('forgot-password-form')} noValidate className={styles.forgotPasswordForm}>
<h2 className={styles.title}>{t('reset.password_reset')}</h2>
{errors.form ? <FormFeedback variant="error">{errors.form}</FormFeedback> : null}

{showOldPasswordField ? (
{errors.form && <FormFeedback variant="error">{errors.form}</FormFeedback>}
<h2 className={styles.title}>{showOldPasswordField && showResetTokenField ? t('user:account.add_password') : t('reset.password_reset')}</h2>
{showOldPasswordField && showResetTokenField && (
ChristiaanScheermeijer marked this conversation as resolved.
Show resolved Hide resolved
<p className={styles.paragraph}>
{t('user:account.add_password_modal_text', { email: email })}
<a className={styles.resendLink} onClick={onResendEmailClick}>
{t('user:account.resend_mail')}
</a>
</p>
)}
{showOldPasswordField && !showResetTokenField && (
<PasswordField
value={value.oldPassword}
onChange={onChange}
Expand All @@ -46,7 +66,8 @@ const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOld
showHelperText={false}
required
/>
) : showResetTokenField ? (
)}
{showResetTokenField && (
<TextField
className={styles.textField}
value={value.resetPasswordToken || ''}
Expand All @@ -58,7 +79,7 @@ const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOld
type="text"
required
/>
) : null}
)}

<PasswordField
value={value.password}
Expand Down
7 changes: 6 additions & 1 deletion src/containers/AccountModal/AccountModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import WaitingForPayment from '#components/WaitingForPayment/WaitingForPayment';
import UpdatePaymentMethod from '#src/containers/UpdatePaymentMethod/UpdatePaymentMethod';
import useEventCallback from '#src/hooks/useEventCallback';
import UpgradeSubscription from '#components/UpgradeSubscription/UpgradeSubscription';
import DeleteAccountPasswordWarning from '#components/DeleteAccountPasswordWarning/DeleteAccountPasswordWarning';

const PUBLIC_VIEWS = ['login', 'create-account', 'forgot-password', 'reset-password', 'send-confirmation', 'edit-password', 'simultaneous-logins'];

Expand Down Expand Up @@ -104,9 +105,13 @@ const AccountModal = () => {
return <ResetPassword type="reset" />;
case 'forgot-password':
return <ResetPassword type="forgot" />;
case 'add-password':
return <EditPassword type="add" />;
case 'delete-account':
case 'delete-account-confirmation':
return <DeleteAccountModal />;
case 'warning-account-deletion':
return <DeleteAccountPasswordWarning />;
case 'send-confirmation':
return <ResetPassword type="confirmation" />;
case 'edit-password':
Expand All @@ -125,7 +130,7 @@ const AccountModal = () => {
}
};

const shouldShowBanner = !['delete-account', 'delete-account-confirmation', 'edit-card'].includes(view ?? '');
const shouldShowBanner = !['delete-account', 'delete-account-confirmation', 'edit-card', 'warning-account-deletion'].includes(view ?? '');
const dialogSize = ['delete-account-confirmation'].includes(view ?? '') ? 'large' : 'small';

return (
Expand Down
Loading
Loading