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