mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-07 16:34:26 +00:00
Update AccountSettings layout
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useReducer, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
Modal,
|
Modal,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -8,187 +8,342 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Accordion,
|
Accordion,
|
||||||
TextInput,
|
TextInput,
|
||||||
Text,
|
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
Box,
|
Box,
|
||||||
LoadingOverlay,
|
Text,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
import { notifications } from '@mantine/notifications';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
import { useProfileSettings } from '../hooks/useProfileSettings';
|
import { useProfileSettings } from '../hooks/useProfileSettings';
|
||||||
|
|
||||||
|
// Reducer for managing settings state
|
||||||
|
const initialState = {
|
||||||
|
localSettings: {},
|
||||||
|
initialSettings: {},
|
||||||
|
hasUnsavedChanges: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
function settingsReducer(state, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'INIT_SETTINGS':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
localSettings: action.payload,
|
||||||
|
initialSettings: action.payload,
|
||||||
|
hasUnsavedChanges: false,
|
||||||
|
};
|
||||||
|
case 'UPDATE_LOCAL_SETTINGS':
|
||||||
|
const newLocalSettings = { ...state.localSettings, ...action.payload };
|
||||||
|
const hasChanges =
|
||||||
|
JSON.stringify(newLocalSettings) !==
|
||||||
|
JSON.stringify(state.initialSettings);
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
localSettings: newLocalSettings,
|
||||||
|
hasUnsavedChanges: hasChanges,
|
||||||
|
};
|
||||||
|
case 'MARK_SAVED':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
initialSettings: state.localSettings,
|
||||||
|
hasUnsavedChanges: false,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password confirmation modal for email changes
|
||||||
|
const EmailPasswordModal = ({ opened, onClose, onConfirm, email }) => {
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
opened={opened}
|
||||||
|
onClose={onClose}
|
||||||
|
title="Confirm Password"
|
||||||
|
centered
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
<Text size="sm">
|
||||||
|
Please enter your password to confirm changing your email to: {email}
|
||||||
|
</Text>
|
||||||
|
<PasswordInput
|
||||||
|
label="Current Password"
|
||||||
|
placeholder="Enter your current password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.currentTarget.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<Group justify="flex-end" mt="md">
|
||||||
|
<Button variant="default" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onConfirm(password);
|
||||||
|
setPassword('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete account confirmation modal
|
||||||
|
const DeleteAccountModal = ({ opened, onClose, onConfirm }) => {
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
opened={opened}
|
||||||
|
onClose={onClose}
|
||||||
|
title="Delete Account"
|
||||||
|
centered
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
<Text c="red" fw={500}>
|
||||||
|
Warning: This action cannot be undone
|
||||||
|
</Text>
|
||||||
|
<Text size="sm">
|
||||||
|
Please enter your password to confirm account deletion.
|
||||||
|
</Text>
|
||||||
|
<PasswordInput
|
||||||
|
label="Current Password"
|
||||||
|
placeholder="Enter your current password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.currentTarget.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<Group justify="flex-end" mt="md">
|
||||||
|
<Button variant="default" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onClick={() => {
|
||||||
|
onConfirm(password);
|
||||||
|
setPassword('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete Account
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const AccordionControl = ({ children }) => (
|
const AccordionControl = ({ children }) => (
|
||||||
<Accordion.Control>
|
<Accordion.Control>
|
||||||
<Title order={4}>{children}</Title>
|
<Title order={4}>{children}</Title>
|
||||||
</Accordion.Control>
|
</Accordion.Control>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ProfileSettings = ({ displayName, email, onUpdate, loading }) => {
|
const ProfileSettings = ({ settings, onInputChange }) => (
|
||||||
const [newDisplayName, setNewDisplayName] = useState(displayName || '');
|
<Box>
|
||||||
const [newEmail, setNewEmail] = useState(email);
|
|
||||||
const [currentPassword, setCurrentPassword] = useState('');
|
|
||||||
const hasEmailChanges = newEmail !== email;
|
|
||||||
const hasDisplayNameChanges = newDisplayName !== displayName;
|
|
||||||
const hasChanges = hasEmailChanges || hasDisplayNameChanges;
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
const updates = {};
|
|
||||||
if (hasDisplayNameChanges) updates.displayName = newDisplayName;
|
|
||||||
if (hasEmailChanges) {
|
|
||||||
updates.email = newEmail;
|
|
||||||
updates.currentPassword = currentPassword;
|
|
||||||
}
|
|
||||||
onUpdate(updates);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack spacing="md">
|
<Stack spacing="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Display Name"
|
label="Display Name"
|
||||||
value={newDisplayName}
|
value={settings.displayName || ''}
|
||||||
onChange={(e) => setNewDisplayName(e.currentTarget.value)}
|
onChange={(e) => onInputChange('displayName', e.currentTarget.value)}
|
||||||
placeholder="Enter display name"
|
placeholder="Enter display name"
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Email"
|
label="Email"
|
||||||
value={newEmail}
|
value={settings.email || ''}
|
||||||
onChange={(e) => setNewEmail(e.currentTarget.value)}
|
onChange={(e) => onInputChange('email', e.currentTarget.value)}
|
||||||
placeholder="Enter email"
|
placeholder="Enter email"
|
||||||
/>
|
/>
|
||||||
{hasEmailChanges && (
|
|
||||||
<PasswordInput
|
|
||||||
label="Current Password"
|
|
||||||
value={currentPassword}
|
|
||||||
onChange={(e) => setCurrentPassword(e.currentTarget.value)}
|
|
||||||
placeholder="Required to change email"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{hasChanges && (
|
|
||||||
<Button onClick={handleSave} loading={loading}>
|
|
||||||
Save Changes
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const SecuritySettings = ({ onUpdate, loading }) => {
|
const SecuritySettings = ({ settings, onInputChange }) => {
|
||||||
const [currentPassword, setCurrentPassword] = useState('');
|
|
||||||
const [newPassword, setNewPassword] = useState('');
|
|
||||||
const [confirmPassword, setConfirmPassword] = useState('');
|
const [confirmPassword, setConfirmPassword] = useState('');
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
const handlePasswordChange = () => {
|
const handlePasswordChange = (field, value) => {
|
||||||
if (newPassword !== confirmPassword) {
|
if (field === 'confirmNewPassword') {
|
||||||
|
setConfirmPassword(value);
|
||||||
|
// Check if passwords match when either password field changes
|
||||||
|
if (value !== settings.newPassword) {
|
||||||
setError('Passwords do not match');
|
setError('Passwords do not match');
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
if (newPassword.length < 8) {
|
|
||||||
setError('Password must be at least 8 characters long');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setError('');
|
setError('');
|
||||||
onUpdate({ currentPassword, newPassword });
|
}
|
||||||
|
} else {
|
||||||
|
onInputChange(field, value);
|
||||||
|
// Check if passwords match when either password field changes
|
||||||
|
if (field === 'newPassword' && value !== confirmPassword) {
|
||||||
|
setError('Passwords do not match');
|
||||||
|
} else if (value === confirmPassword) {
|
||||||
|
setError('');
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasChanges = currentPassword && newPassword && confirmPassword;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Box>
|
||||||
<Stack spacing="md">
|
<Stack spacing="md">
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label="Current Password"
|
label="Current Password"
|
||||||
value={currentPassword}
|
value={settings.currentPassword || ''}
|
||||||
onChange={(e) => setCurrentPassword(e.currentTarget.value)}
|
onChange={(e) =>
|
||||||
|
handlePasswordChange('currentPassword', e.currentTarget.value)
|
||||||
|
}
|
||||||
placeholder="Enter current password"
|
placeholder="Enter current password"
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label="New Password"
|
label="New Password"
|
||||||
value={newPassword}
|
value={settings.newPassword || ''}
|
||||||
onChange={(e) => setNewPassword(e.currentTarget.value)}
|
onChange={(e) =>
|
||||||
|
handlePasswordChange('newPassword', e.currentTarget.value)
|
||||||
|
}
|
||||||
placeholder="Enter new password"
|
placeholder="Enter new password"
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label="Confirm New Password"
|
label="Confirm New Password"
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
|
onChange={(e) =>
|
||||||
|
handlePasswordChange('confirmNewPassword', e.currentTarget.value)
|
||||||
|
}
|
||||||
placeholder="Confirm new password"
|
placeholder="Confirm new password"
|
||||||
|
error={error}
|
||||||
/>
|
/>
|
||||||
{error && (
|
|
||||||
<Text color="red" size="sm">
|
|
||||||
{error}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
Password must be at least 8 characters long
|
Password must be at least 8 characters long. Leave password fields
|
||||||
|
empty if you don't want to change it.
|
||||||
</Text>
|
</Text>
|
||||||
{hasChanges && (
|
|
||||||
<Button onClick={handlePasswordChange} loading={loading}>
|
|
||||||
Change Password
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DangerZone = ({ onDelete, loading }) => {
|
const DangerZone = ({ onDeleteClick }) => (
|
||||||
const [password, setPassword] = useState('');
|
<Box>
|
||||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
<Button color="red" variant="light" onClick={onDeleteClick} fullWidth>
|
||||||
|
Delete Account
|
||||||
const handleDelete = () => {
|
|
||||||
if (confirmDelete && password) {
|
|
||||||
onDelete(password);
|
|
||||||
} else {
|
|
||||||
setConfirmDelete(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack spacing="md">
|
|
||||||
{confirmDelete && (
|
|
||||||
<PasswordInput
|
|
||||||
label="Current Password"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.currentTarget.value)}
|
|
||||||
placeholder="Enter password to confirm"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Box mb="md">
|
|
||||||
<Button
|
|
||||||
color="red"
|
|
||||||
variant="light"
|
|
||||||
onClick={handleDelete}
|
|
||||||
fullWidth
|
|
||||||
loading={loading}
|
|
||||||
>
|
|
||||||
{confirmDelete ? 'Confirm Delete Account' : 'Delete Account'}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const AccountSettings = ({ opened, onClose }) => {
|
const AccountSettings = ({ opened, onClose }) => {
|
||||||
const { user, logout, refreshUser } = useAuth();
|
const { user, logout, refreshUser } = useAuth();
|
||||||
const { loading, updateProfile, deleteAccount } = useProfileSettings();
|
const { loading, updateProfile, deleteAccount } = useProfileSettings();
|
||||||
const [activeSection, setActiveSection] = useState(['profile']);
|
const [state, dispatch] = useReducer(settingsReducer, initialState);
|
||||||
|
const isInitialMount = useRef(true);
|
||||||
|
const [deleteModalOpened, setDeleteModalOpened] = useState(false);
|
||||||
|
const [emailModalOpened, setEmailModalOpened] = useState(false);
|
||||||
|
|
||||||
|
// Initialize settings on mount
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (isInitialMount.current && user) {
|
||||||
|
isInitialMount.current = false;
|
||||||
|
const settings = {
|
||||||
|
displayName: user.displayName,
|
||||||
|
email: user.email,
|
||||||
|
currentPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
};
|
||||||
|
dispatch({ type: 'INIT_SETTINGS', payload: settings });
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const handleInputChange = (key, value) => {
|
||||||
|
dispatch({ type: 'UPDATE_LOCAL_SETTINGS', payload: { [key]: value } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const updates = {};
|
||||||
|
const needsPasswordConfirmation =
|
||||||
|
state.localSettings.email !== state.initialSettings.email;
|
||||||
|
|
||||||
|
// Add display name if changed
|
||||||
|
if (state.localSettings.displayName !== state.initialSettings.displayName) {
|
||||||
|
updates.displayName = state.localSettings.displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle password change
|
||||||
|
if (state.localSettings.newPassword) {
|
||||||
|
if (!state.localSettings.currentPassword) {
|
||||||
|
notifications.show({
|
||||||
|
title: 'Error',
|
||||||
|
message: 'Current password is required to change password',
|
||||||
|
color: 'red',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updates.newPassword = state.localSettings.newPassword;
|
||||||
|
updates.currentPassword = state.localSettings.currentPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're only changing display name or have password already provided, proceed directly
|
||||||
|
if (!needsPasswordConfirmation || state.localSettings.currentPassword) {
|
||||||
|
if (needsPasswordConfirmation) {
|
||||||
|
updates.email = state.localSettings.email;
|
||||||
|
// If we don't have a password change, we still need to include the current password for email change
|
||||||
|
if (!updates.currentPassword) {
|
||||||
|
updates.currentPassword = state.localSettings.currentPassword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleProfileUpdate = async (updates) => {
|
|
||||||
const result = await updateProfile(updates);
|
const result = await updateProfile(updates);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
await refreshUser();
|
await refreshUser();
|
||||||
|
dispatch({ type: 'MARK_SAVED' });
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Only show the email confirmation modal if we don't already have the password
|
||||||
|
setEmailModalOpened(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEmailConfirm = async (password) => {
|
||||||
|
const updates = {
|
||||||
|
...state.localSettings,
|
||||||
|
currentPassword: password,
|
||||||
|
};
|
||||||
|
// Remove any undefined/empty values
|
||||||
|
Object.keys(updates).forEach((key) => {
|
||||||
|
if (updates[key] === undefined || updates[key] === '') {
|
||||||
|
delete updates[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Remove keys that haven't changed
|
||||||
|
if (updates.displayName === state.initialSettings.displayName) {
|
||||||
|
delete updates.displayName;
|
||||||
|
}
|
||||||
|
if (updates.email === state.initialSettings.email) {
|
||||||
|
delete updates.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await updateProfile(updates);
|
||||||
|
if (result.success) {
|
||||||
|
await refreshUser();
|
||||||
|
dispatch({ type: 'MARK_SAVED' });
|
||||||
|
setEmailModalOpened(false);
|
||||||
|
onClose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (password) => {
|
const handleDelete = async (password) => {
|
||||||
const result = await deleteAccount(password);
|
const result = await deleteAccount(password);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
setDeleteModalOpened(false);
|
||||||
onClose();
|
onClose();
|
||||||
logout();
|
logout();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
opened={opened}
|
opened={opened}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
@@ -196,11 +351,15 @@ const AccountSettings = ({ opened, onClose }) => {
|
|||||||
centered
|
centered
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
<LoadingOverlay visible={loading} />
|
|
||||||
<Stack spacing="xl">
|
<Stack spacing="xl">
|
||||||
|
{state.hasUnsavedChanges && (
|
||||||
|
<Badge color="yellow" variant="light">
|
||||||
|
Unsaved Changes
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
<Accordion
|
<Accordion
|
||||||
value={activeSection}
|
defaultValue={['profile', 'security', 'danger']}
|
||||||
onChange={setActiveSection}
|
|
||||||
multiple
|
multiple
|
||||||
styles={(theme) => ({
|
styles={(theme) => ({
|
||||||
control: {
|
control: {
|
||||||
@@ -226,10 +385,8 @@ const AccountSettings = ({ opened, onClose }) => {
|
|||||||
<AccordionControl>Profile</AccordionControl>
|
<AccordionControl>Profile</AccordionControl>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
<ProfileSettings
|
<ProfileSettings
|
||||||
displayName={user.displayName}
|
settings={state.localSettings}
|
||||||
email={user.email}
|
onInputChange={handleInputChange}
|
||||||
onUpdate={handleProfileUpdate}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
/>
|
||||||
</Accordion.Panel>
|
</Accordion.Panel>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
@@ -238,8 +395,8 @@ const AccountSettings = ({ opened, onClose }) => {
|
|||||||
<AccordionControl>Security</AccordionControl>
|
<AccordionControl>Security</AccordionControl>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
<SecuritySettings
|
<SecuritySettings
|
||||||
onUpdate={handleProfileUpdate}
|
settings={state.localSettings}
|
||||||
loading={loading}
|
onInputChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Accordion.Panel>
|
</Accordion.Panel>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
@@ -247,18 +404,39 @@ const AccountSettings = ({ opened, onClose }) => {
|
|||||||
<Accordion.Item value="danger">
|
<Accordion.Item value="danger">
|
||||||
<AccordionControl>Danger Zone</AccordionControl>
|
<AccordionControl>Danger Zone</AccordionControl>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
<DangerZone onDelete={handleDelete} loading={loading} />
|
<DangerZone onDeleteClick={() => setDeleteModalOpened(true)} />
|
||||||
</Accordion.Panel>
|
</Accordion.Panel>
|
||||||
</Accordion.Item>
|
</Accordion.Item>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Group justify="flex-end">
|
<Group justify="flex-end">
|
||||||
<Button variant="default" onClick={onClose}>
|
<Button variant="default" onClick={onClose}>
|
||||||
Close
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={loading}
|
||||||
|
disabled={!state.hasUnsavedChanges}
|
||||||
|
>
|
||||||
|
Save Changes
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<EmailPasswordModal
|
||||||
|
opened={emailModalOpened}
|
||||||
|
onClose={() => setEmailModalOpened(false)}
|
||||||
|
onConfirm={handleEmailConfirm}
|
||||||
|
email={state.localSettings.email}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DeleteAccountModal
|
||||||
|
opened={deleteModalOpened}
|
||||||
|
onClose={() => setDeleteModalOpened(false)}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user