changing user password and email; notification messages redesign; some fixes
This commit is contained in:
@@ -13,3 +13,11 @@ export const login = (login: string, password: string, recaptcha: string) => pos
|
||||
recaptcha
|
||||
});
|
||||
export const userLogout = () => post<string>('auth/logout');
|
||||
export const changeEmail = (old_email: string, new_email: string) => post<string>('auth/changeEmail', {
|
||||
old_email,
|
||||
new_email
|
||||
});
|
||||
export const changePassword = (old_password: string, new_password: string) => post<string>('auth/changePassword', {
|
||||
old_password,
|
||||
new_password
|
||||
});
|
||||
|
||||
+3
-1
@@ -6,7 +6,9 @@ const errors: { [key: number]: string } = {
|
||||
6: 'Токен истек!',
|
||||
8: 'Новый и старый пароли совпадают!',
|
||||
9: 'Необходима проверка ReCaptcha',
|
||||
10: 'Неверно указан старый E-Mail',
|
||||
11: 'Неверно указан старый пароль',
|
||||
100: 'Не найдено!'
|
||||
};
|
||||
|
||||
export default errors;
|
||||
export default errors;
|
||||
|
||||
@@ -85,11 +85,6 @@ a
|
||||
margin: .5rem auto
|
||||
|
||||
.Notifications
|
||||
position: fixed
|
||||
bottom: 0
|
||||
left: 0
|
||||
z-index: 100000
|
||||
|
||||
@media (min-width: 500px)
|
||||
padding: 1rem
|
||||
box-sizing: border-box
|
||||
|
||||
@@ -43,11 +43,17 @@ class App extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
parser.registerTag('spoiler', SpoilerTag);
|
||||
parser.registerTag('color', ColorTag);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { setUser, setSettings } = this.props;
|
||||
|
||||
const cachedTheme = localStorage.getItem('theme');
|
||||
if(cachedTheme)
|
||||
document.body.setAttribute('theme', cachedTheme);
|
||||
|
||||
const { setUser, setSettings } = props;
|
||||
fetchCurrentUser().then(user => {
|
||||
setUser(user);
|
||||
fetchUserSettings().then(settings => {
|
||||
@@ -62,9 +68,6 @@ class App extends React.Component<Props, State> {
|
||||
console.error(err.text);
|
||||
});
|
||||
|
||||
parser.registerTag('spoiler', SpoilerTag);
|
||||
parser.registerTag('color', ColorTag);
|
||||
|
||||
getVersion().then(res => {
|
||||
if(version !== res.version) {
|
||||
console.debug(`Current back-end version: ${res.version}. Clearing cache`);
|
||||
@@ -80,13 +83,12 @@ class App extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const { modal } = this.props;
|
||||
const notificationWidth = window.screen.width >= 500 ? 500 : window.screen.width;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar/>
|
||||
<div className={'Container'}>
|
||||
<div className="Notifications" style={{ width: notificationWidth }}>
|
||||
<div className="Notifications">
|
||||
{this.props.notifications.map((n, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{n}
|
||||
|
||||
@@ -2,23 +2,40 @@ import React from "react";
|
||||
import Group from "../../../ui/Group/Group";
|
||||
import Input from "../../../ui/Input/Input";
|
||||
import Button from "../../../ui/Button/Button";
|
||||
import { changeEmail, changePassword } from "../../../api/auth";
|
||||
import { connect } from "react-redux";
|
||||
import { addNotification } from "../../../store/ducks/notifications";
|
||||
import NotificationMessage from "../../../ui/Notifications/NotificationMessage";
|
||||
import errors from "../../../api/errors";
|
||||
|
||||
type Props = {
|
||||
addNotification: AddNotification
|
||||
}
|
||||
|
||||
type State = {
|
||||
oldEmail: string
|
||||
email1: string
|
||||
email2: string
|
||||
error: string
|
||||
emailError: string
|
||||
|
||||
oldPassword: string
|
||||
password1: string
|
||||
password2: string
|
||||
passwordError: string
|
||||
}
|
||||
|
||||
class Security extends React.Component<{}, State> {
|
||||
class Security extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
oldEmail: '', email1: '', email2: '',
|
||||
error: ''
|
||||
oldPassword: '', password1: '', password2: '',
|
||||
emailError: '', passwordError: ''
|
||||
}
|
||||
|
||||
changeEmail = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const { oldEmail, email1, email2 } = this.state;
|
||||
const { addNotification } = this.props;
|
||||
|
||||
let error = '';
|
||||
if(!oldEmail) error += 'Введите старый E-mail\n';
|
||||
if(!email1) error += 'Введите новый E-mail\n';
|
||||
@@ -27,17 +44,64 @@ class Security extends React.Component<{}, State> {
|
||||
if(oldEmail === email1) error += 'Старый и новый E-mail не должны совпадать';
|
||||
|
||||
if(error) {
|
||||
this.setState({ error });
|
||||
return;
|
||||
this.setState({ emailError: error });
|
||||
} else {
|
||||
changeEmail(oldEmail, email1).then(() => {
|
||||
addNotification(
|
||||
<NotificationMessage>
|
||||
E-mail успешно сменен!
|
||||
</NotificationMessage>
|
||||
);
|
||||
this.setState({
|
||||
oldEmail: '', email1: '', email2: ''
|
||||
});
|
||||
}).catch((err: ApiError) => {
|
||||
this.setState({ emailError: errors[err.code] })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changePassword = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { oldPassword, password1, password2 } = this.state;
|
||||
const { addNotification } = this.props;
|
||||
|
||||
let error = '';
|
||||
if(!oldPassword) error += 'Введите старый пароль\n';
|
||||
if(!password1) error += 'Введите новый пароль\n';
|
||||
if(!password2) error += 'Введите повтор нового пароля\n';
|
||||
if(password1 !== password2) error += 'Пароли не совпадают\n';
|
||||
if(oldPassword === password1) error += 'Старый и новый пароль не должны совпадать';
|
||||
|
||||
if(error) {
|
||||
this.setState({ passwordError: error });
|
||||
} else {
|
||||
changePassword(oldPassword, password1).then(() => {
|
||||
addNotification(
|
||||
<NotificationMessage>
|
||||
Пароль успешно сменен!
|
||||
</NotificationMessage>
|
||||
);
|
||||
this.setState({
|
||||
oldPassword: '', password1: '', password2: ''
|
||||
});
|
||||
}).catch((err: ApiError) => {
|
||||
this.setState({ passwordError: errors[err.code] })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { oldEmail, email1, email2, error } = this.state;
|
||||
const {
|
||||
oldEmail, email1, email2,
|
||||
oldPassword, password1, password2,
|
||||
emailError, passwordError
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
return (<>
|
||||
<Group title={'Смена E-mail'} className={'Settings_Group'}>
|
||||
{error && <pre className={'Error'}>{error}</pre>}
|
||||
{emailError && <pre className={'Error'}>{emailError}</pre>}
|
||||
<form onSubmit={this.changeEmail}>
|
||||
<Input
|
||||
placeholder={'Старый E-mail'}
|
||||
@@ -60,8 +124,38 @@ class Security extends React.Component<{}, State> {
|
||||
<Button>Сохранить</Button>
|
||||
</form>
|
||||
</Group>
|
||||
);
|
||||
|
||||
<Group title={'Смена пароля'} className={'Settings_Group'}>
|
||||
{passwordError && <pre className={'Error'}>{passwordError}</pre>}
|
||||
<form onSubmit={this.changePassword}>
|
||||
<Input
|
||||
placeholder={'Старый пароль'}
|
||||
type={'password'}
|
||||
value={oldPassword}
|
||||
onChange={e => this.setState({ oldPassword: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder={'Новый пароль'}
|
||||
type={'password'}
|
||||
value={password1}
|
||||
onChange={e => this.setState({ password1: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder={'Новый пароль ещё раз'}
|
||||
type={'password'}
|
||||
value={password2}
|
||||
onChange={e => this.setState({ password2: e.target.value })}
|
||||
/>
|
||||
<Button>Сохранить</Button>
|
||||
</form>
|
||||
</Group>
|
||||
</>);
|
||||
}
|
||||
}
|
||||
|
||||
export default Security;
|
||||
export default connect(
|
||||
null,
|
||||
dispatch => ({
|
||||
addNotification: (notification: React.ReactNode) => dispatch(addNotification(notification))
|
||||
})
|
||||
)(Security);
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
@import "../../Constants"
|
||||
|
||||
.Animate-Notification-exit
|
||||
opacity: 0
|
||||
|
||||
.Notification
|
||||
background-color: var(--block-color)
|
||||
border: 1px solid var(--link-color)
|
||||
border-radius: 5px
|
||||
padding: .5rem
|
||||
box-sizing: border-box
|
||||
max-width: 500px
|
||||
width: 500px
|
||||
min-width: 288px
|
||||
cursor: pointer
|
||||
margin-top: 1rem
|
||||
margin: 1rem
|
||||
position: fixed
|
||||
z-index: 100000
|
||||
|
||||
@media (max-width: 500px)
|
||||
width: 100%
|
||||
|
||||
@media (min-width: 501px)
|
||||
width: 500px
|
||||
|
||||
&--Error
|
||||
background: var(--error-bg-color)
|
||||
@@ -24,4 +27,4 @@
|
||||
|
||||
.Progress
|
||||
height: 5px
|
||||
background: white
|
||||
background: white
|
||||
|
||||
@@ -5,7 +5,9 @@ import { capitalize } from "../../utils";
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
level?: 'info' | 'success' | 'error'
|
||||
}
|
||||
hideTime?: number
|
||||
position?: 'left' | 'bottom' | 'right'
|
||||
} & React.DetailedHTMLProps<React.AllHTMLAttributes<HTMLDivElement>, HTMLDivElement>
|
||||
|
||||
type State = {
|
||||
time: number
|
||||
@@ -16,7 +18,7 @@ class NotificationMessage extends React.Component<Props, State> {
|
||||
timer: NodeJS.Timeout | undefined;
|
||||
|
||||
state = {
|
||||
time: 5000, paused: false
|
||||
time: this.props.hideTime || 5000, paused: false
|
||||
};
|
||||
|
||||
onMouseOver = () => {
|
||||
@@ -28,32 +30,60 @@ class NotificationMessage extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
this.timer = setInterval(() => this.interval(80), 1);
|
||||
this.timer = setInterval(() => {
|
||||
this.interval(80);
|
||||
}, 1);
|
||||
this.setState({ paused: false });
|
||||
};
|
||||
|
||||
interval = (step = 50) => {
|
||||
const { time, paused } = this.state;
|
||||
if(time <= 0 && this.timer) clearInterval(this.timer);
|
||||
if(!paused) this.setState({ time: time - step });
|
||||
|
||||
if(time <= 0 && this.timer)
|
||||
clearInterval(this.timer);
|
||||
if(!paused)
|
||||
this.setState({ time: time - step });
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
this.timer = setInterval(() => this.interval(10), 1)
|
||||
this.timer = setInterval(() => {
|
||||
this.interval(10);
|
||||
}, 10);
|
||||
};
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if(this.timer) clearTimeout(this.timer);
|
||||
if(this.timer)
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { time } = this.state;
|
||||
const { children, level } = this.props;
|
||||
const {
|
||||
children,
|
||||
level,
|
||||
hideTime,
|
||||
position,
|
||||
...props
|
||||
} = this.props;
|
||||
|
||||
if(time <= 0)
|
||||
return null;
|
||||
|
||||
const styleClass = level ? `Notification--${capitalize(level)}` : '';
|
||||
const width = window.screen.width >= 500 ? 500 : window.screen.width;
|
||||
|
||||
let style = {};
|
||||
switch(position) {
|
||||
default:
|
||||
case "left":
|
||||
Object.assign(style, { bottom: 0, left: 0 });
|
||||
break;
|
||||
case "bottom":
|
||||
Object.assign(style, { bottom: 0, left: '50%', transform: 'translateX(-50%)' });
|
||||
break;
|
||||
case "right":
|
||||
Object.assign(style, { bottom: 0, right: 0 });
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -61,10 +91,11 @@ class NotificationMessage extends React.Component<Props, State> {
|
||||
onMouseOver={this.onMouseOver}
|
||||
onMouseOut={this.onMouseOut}
|
||||
onClick={this.onClick}
|
||||
style={{ width }}
|
||||
style={style}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<div className="Progress" style={{ width: `${time / 5000 * 100}%` }}/>
|
||||
<div className="Progress" style={{ width: `${time / (5000 || hideTime) * 100}%` }}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user