mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
added request code dialog
This commit is contained in:
parent
c0c832d158
commit
6dcb21e9e4
18 changed files with 292 additions and 88 deletions
|
@ -9,6 +9,7 @@ type IButtonProps = DetailedHTMLProps<
|
||||||
HTMLButtonElement
|
HTMLButtonElement
|
||||||
> & {
|
> & {
|
||||||
size?: 'mini' | 'normal' | 'big' | 'giant' | 'micro' | 'small';
|
size?: 'mini' | 'normal' | 'big' | 'giant' | 'micro' | 'small';
|
||||||
|
color?: 'primary' | 'secondary' | 'outline' | 'link';
|
||||||
iconLeft?: IIcon;
|
iconLeft?: IIcon;
|
||||||
iconRight?: IIcon;
|
iconRight?: IIcon;
|
||||||
seamless?: boolean;
|
seamless?: boolean;
|
||||||
|
@ -25,6 +26,7 @@ type IButtonProps = DetailedHTMLProps<
|
||||||
const Button: FC<IButtonProps> = memo(
|
const Button: FC<IButtonProps> = memo(
|
||||||
({
|
({
|
||||||
className = '',
|
className = '',
|
||||||
|
color = 'primary',
|
||||||
size = 'normal',
|
size = 'normal',
|
||||||
iconLeft,
|
iconLeft,
|
||||||
iconRight,
|
iconRight,
|
||||||
|
@ -44,7 +46,7 @@ const Button: FC<IButtonProps> = memo(
|
||||||
createElement(
|
createElement(
|
||||||
seamless || non_submitting ? 'div' : 'button',
|
seamless || non_submitting ? 'div' : 'button',
|
||||||
{
|
{
|
||||||
className: classnames(styles.button, className, styles[size], {
|
className: classnames(styles.button, className, styles[size], styles[color], {
|
||||||
red,
|
red,
|
||||||
grey,
|
grey,
|
||||||
seamless,
|
seamless,
|
||||||
|
|
|
@ -143,6 +143,34 @@
|
||||||
padding-right: $gap;
|
padding-right: $gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: $red_gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: $green_gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.outline {
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: inset transparentize(white, 0.8) 0 0 0 2px;
|
||||||
|
color: transparentize(white, 0.8);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: transparentize(white, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.link {
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
import { INode } from "~/redux/types";
|
import { INode } from '~/redux/types';
|
||||||
|
|
||||||
export const API = {
|
export const API = {
|
||||||
BASE: process.env.API_HOST,
|
BASE: process.env.API_HOST,
|
||||||
USER: {
|
USER: {
|
||||||
LOGIN: "/user/login",
|
LOGIN: '/user/login',
|
||||||
VKONTAKTE_LOGIN: `${process.env.API_HOST}/user/vkontakte`,
|
VKONTAKTE_LOGIN: `${process.env.API_HOST}/user/vkontakte`,
|
||||||
ME: "/user/",
|
ME: '/user/',
|
||||||
PROFILE: (username: string) => `/user/${username}/profile`,
|
PROFILE: (username: string) => `/user/${username}/profile`,
|
||||||
MESSAGES: (username: string) => `/user/${username}/messages`,
|
MESSAGES: (username: string) => `/user/${username}/messages`,
|
||||||
MESSAGE_SEND: (username: string) => `/user/${username}/messages`,
|
MESSAGE_SEND: (username: string) => `/user/${username}/messages`,
|
||||||
GET_UPDATES: "/user/updates",
|
GET_UPDATES: '/user/updates',
|
||||||
|
REQUEST_CODE: (code?: string) => `/user/restore/${code || ''}`,
|
||||||
|
|
||||||
UPLOAD: (target, type) => `/upload/${target}/${type}`
|
UPLOAD: (target, type) => `/upload/${target}/${type}`,
|
||||||
},
|
},
|
||||||
NODE: {
|
NODE: {
|
||||||
SAVE: "/node/",
|
SAVE: '/node/',
|
||||||
GET: "/node/",
|
GET: '/node/',
|
||||||
GET_DIFF: "/node/diff",
|
GET_DIFF: '/node/diff',
|
||||||
GET_NODE: (id: number | string) => `/node/${id}`,
|
GET_NODE: (id: number | string) => `/node/${id}`,
|
||||||
|
|
||||||
COMMENT: (id: INode["id"]) => `/node/${id}/comment`,
|
COMMENT: (id: INode['id']) => `/node/${id}/comment`,
|
||||||
RELATED: (id: INode["id"]) => `/node/${id}/related`,
|
RELATED: (id: INode['id']) => `/node/${id}/related`,
|
||||||
UPDATE_TAGS: (id: INode["id"]) => `/node/${id}/tags`,
|
UPDATE_TAGS: (id: INode['id']) => `/node/${id}/tags`,
|
||||||
POST_LIKE: (id: INode["id"]) => `/node/${id}/like`,
|
POST_LIKE: (id: INode['id']) => `/node/${id}/like`,
|
||||||
POST_STAR: (id: INode["id"]) => `/node/${id}/heroic`,
|
POST_STAR: (id: INode['id']) => `/node/${id}/heroic`,
|
||||||
SET_CELL_VIEW: (id: INode["id"]) => `/node/${id}/cell-view`
|
SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { FC, MouseEventHandler, useEffect, useRef } from 'react';
|
import React, { FC, MouseEventHandler, useEffect, useRef, ReactElement } from 'react';
|
||||||
import * as styles from './styles.scss';
|
import * as styles from './styles.scss';
|
||||||
import { enableBodyScroll, disableBodyScroll } from 'body-scroll-lock';
|
import { enableBodyScroll, disableBodyScroll } from 'body-scroll-lock';
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
|
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children: React.ReactChild;
|
children: React.ReactChild;
|
||||||
|
@ -11,6 +12,8 @@ interface IProps {
|
||||||
size?: 'medium' | 'big';
|
size?: 'medium' | 'big';
|
||||||
width?: number;
|
width?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
is_loading?: boolean;
|
||||||
|
overlay?: ReactElement;
|
||||||
|
|
||||||
onOverlayClick?: MouseEventHandler<HTMLDivElement>;
|
onOverlayClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
onRefCapture?: (ref: any) => void;
|
onRefCapture?: (ref: any) => void;
|
||||||
|
@ -25,6 +28,8 @@ const BetterScrollDialog: FC<IProps> = ({
|
||||||
width = 600,
|
width = 600,
|
||||||
error,
|
error,
|
||||||
onClose,
|
onClose,
|
||||||
|
is_loading,
|
||||||
|
overlay = null,
|
||||||
}) => {
|
}) => {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
@ -51,7 +56,15 @@ const BetterScrollDialog: FC<IProps> = ({
|
||||||
{error && <div className={styles.error}>{error}</div>}
|
{error && <div className={styles.error}>{error}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{footer && <div className={styles.header}>{footer}</div>}
|
{!!is_loading && (
|
||||||
|
<div className={styles.shade}>
|
||||||
|
<LoaderCircle size={48} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{overlay}
|
||||||
|
|
||||||
|
{footer && <div className={styles.footer}>{footer}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -109,3 +109,31 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes appear {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shade {
|
||||||
|
position: absolute;
|
||||||
|
background: transparentize($content_bg, 0.3);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: $radius;
|
||||||
|
animation: appear 1s forwards;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,12 +67,7 @@ const LoginDialogUnconnected: FC<IProps> = ({
|
||||||
const buttons = useMemo(
|
const buttons = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<Group className={styles.footer}>
|
<Group className={styles.footer}>
|
||||||
<Button
|
<Button color="outline" iconLeft="vk" type="button" onClick={onSocialLogin}>
|
||||||
className={styles.secondary_button}
|
|
||||||
iconLeft="vk"
|
|
||||||
type="button"
|
|
||||||
onClick={onSocialLogin}
|
|
||||||
>
|
|
||||||
<span>Вконтакте</span>
|
<span>Вконтакте</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
@ -88,7 +83,7 @@ const LoginDialogUnconnected: FC<IProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<BetterScrollDialog width={260} error={error} onClose={onRequestClose} footer={buttons}>
|
<BetterScrollDialog width={300} error={error} onClose={onRequestClose} footer={buttons}>
|
||||||
<Padder>
|
<Padder>
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<Group>
|
<Group>
|
||||||
|
@ -98,7 +93,12 @@ const LoginDialogUnconnected: FC<IProps> = ({
|
||||||
|
|
||||||
<InputText title="Пароль" handler={setPassword} value={password} type="password" />
|
<InputText title="Пароль" handler={setPassword} value={password} type="password" />
|
||||||
|
|
||||||
<Button className={styles.forgot_button} type="button" onClick={onRestoreRequest}>
|
<Button
|
||||||
|
color="link"
|
||||||
|
type="button"
|
||||||
|
onClick={onRestoreRequest}
|
||||||
|
className={styles.forgot_button}
|
||||||
|
>
|
||||||
Вспомнить пароль
|
Вспомнить пароль
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
|
@ -28,9 +28,7 @@ $vk_color: $secondary_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.forgot_button {
|
.forgot_button {
|
||||||
background: $content_bg;
|
opacity: 0.5;
|
||||||
box-shadow: none;
|
|
||||||
color: $secondary_color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
|
|
|
@ -1,33 +1,101 @@
|
||||||
import React, { FC, useState, useMemo, useCallback } from 'react';
|
import React, { FC, useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
import { IDialogProps } from '~/redux/types';
|
import { IDialogProps } from '~/redux/types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { BetterScrollDialog } from '../BetterScrollDialog';
|
import { BetterScrollDialog } from '../BetterScrollDialog';
|
||||||
import { Group } from '~/components/containers/Group';
|
import { Group } from '~/components/containers/Group';
|
||||||
import { InputText } from '~/components/input/InputText';
|
import { InputText } from '~/components/input/InputText';
|
||||||
import { Button } from '~/components/input/Button';
|
import { Button } from '~/components/input/Button';
|
||||||
|
import styles from './styles.scss';
|
||||||
|
|
||||||
const mapStateToProps = () => ({});
|
import * as AUTH_ACTIONS from '~/redux/auth/actions';
|
||||||
const mapDispatchToProps = {};
|
import pick from 'ramda/es/pick';
|
||||||
|
import { selectAuthRestore } from '~/redux/auth/selectors';
|
||||||
|
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
|
import { ERROR_LITERAL } from '~/constants/errors';
|
||||||
|
import { Icon } from '~/components/input/Icon';
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
restore: selectAuthRestore(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = pick(['authRequestRestoreCode', 'authSetRestore'], AUTH_ACTIONS);
|
||||||
|
|
||||||
type IProps = IDialogProps & ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
type IProps = IDialogProps & ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||||
|
|
||||||
const RestoreRequestDialogUnconnected: FC<IProps> = ({}) => {
|
const RestoreRequestDialogUnconnected: FC<IProps> = ({
|
||||||
|
restore: { error, is_loading, is_succesfull },
|
||||||
|
authSetRestore,
|
||||||
|
onRequestClose,
|
||||||
|
authRequestRestoreCode,
|
||||||
|
}) => {
|
||||||
const [field, setField] = useState();
|
const [field, setField] = useState();
|
||||||
|
|
||||||
const onSubmit = useCallback(event => {
|
const onSubmit = useCallback(
|
||||||
event.preventDefault();
|
event => {
|
||||||
}, []);
|
event.preventDefault();
|
||||||
|
|
||||||
const buttons = useMemo(() => <Button>Восстановить</Button>, []);
|
if (!field) return;
|
||||||
|
|
||||||
|
authRequestRestoreCode(field);
|
||||||
|
},
|
||||||
|
[authRequestRestoreCode, field]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (error || is_succesfull) {
|
||||||
|
authSetRestore({ error: null, is_succesfull: false });
|
||||||
|
}
|
||||||
|
}, [field]);
|
||||||
|
|
||||||
|
const buttons = useMemo(
|
||||||
|
() => (
|
||||||
|
<Group className={styles.buttons}>
|
||||||
|
<Button>Восстановить</Button>
|
||||||
|
</Group>
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const header = useMemo(() => <div className={styles.illustration}>ILLUSTRATE ME</div>, []);
|
||||||
|
|
||||||
|
const overlay = useMemo(
|
||||||
|
() =>
|
||||||
|
is_succesfull ? (
|
||||||
|
<Group className={styles.shade}>
|
||||||
|
<Icon icon="check" size={64} />
|
||||||
|
|
||||||
|
<div>Проверьте почту, мы отправили на неё код</div>
|
||||||
|
|
||||||
|
<div />
|
||||||
|
|
||||||
|
<Button color="secondary" onClick={onRequestClose}>
|
||||||
|
Отлично!
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
) : null,
|
||||||
|
[is_succesfull]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<BetterScrollDialog footer={buttons}>
|
<BetterScrollDialog
|
||||||
<Group>
|
header={header}
|
||||||
<InputText title="Имя или email" value={field} handler={setField} />
|
footer={buttons}
|
||||||
|
width={300}
|
||||||
|
onClose={onRequestClose}
|
||||||
|
is_loading={is_loading}
|
||||||
|
error={error && ERROR_LITERAL[error]}
|
||||||
|
overlay={overlay}
|
||||||
|
>
|
||||||
|
<div className={styles.wrap}>
|
||||||
|
<Group>
|
||||||
|
<InputText title="Имя или email" value={field} handler={setField} autoFocus />
|
||||||
|
|
||||||
<div>Введите имя пользователя или адрес почты. Мы пришлем ссылку для сброса пароля.</div>
|
<div className={styles.text}>
|
||||||
</Group>
|
Введите имя пользователя или адрес почты. Мы пришлем ссылку для сброса пароля.
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
</BetterScrollDialog>
|
</BetterScrollDialog>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
47
src/containers/dialogs/RestoreRequestDialog/styles.scss
Normal file
47
src/containers/dialogs/RestoreRequestDialog/styles.scss
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
.wrap {
|
||||||
|
padding: $gap $gap $gap * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
padding: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font: $font_14_regular;
|
||||||
|
padding: $gap;
|
||||||
|
color: darken(white, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.illustration {
|
||||||
|
min-height: 160px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font: $font_18_semibold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shade {
|
||||||
|
@include outer_shadow();
|
||||||
|
|
||||||
|
background: $content_bg;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: $radius;
|
||||||
|
padding: $gap * 2;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font: $font_18_semibold;
|
||||||
|
text-align: center;
|
||||||
|
color: $wisegreen;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $wisegreen;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import { render } from "react-dom";
|
import { render } from 'react-dom';
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from 'react-redux';
|
||||||
import { PersistGate } from "redux-persist/integration/react";
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
import { configureStore } from "~/redux/store";
|
import { configureStore } from '~/redux/store';
|
||||||
import App from "~/containers/App";
|
import App from '~/containers/App';
|
||||||
|
|
||||||
require("./styles/main.scss");
|
require('./styles/main.scss');
|
||||||
|
|
||||||
const { store, persistor } = configureStore();
|
const { store, persistor } = configureStore();
|
||||||
|
|
||||||
|
@ -15,12 +15,13 @@ render(
|
||||||
<App />
|
<App />
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById("app")
|
document.getElementById('app')
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
[Stage 0]:
|
[Stage 0]:
|
||||||
|
- illustrate restoreRequestDialog
|
||||||
- check if email is registered at social login
|
- check if email is registered at social login
|
||||||
- friendship
|
- friendship
|
||||||
- password restore
|
- password restore
|
||||||
|
|
|
@ -77,6 +77,11 @@ export const authPatchUser = (user: Partial<IUser>) => ({
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const authRequestRestoreCode = (field: string) => ({
|
||||||
|
type: AUTH_USER_ACTIONS.REQUEST_RESTORE_CODE,
|
||||||
|
field,
|
||||||
|
});
|
||||||
|
|
||||||
export const authSetRestore = (restore: Partial<IAuthState['restore']>) => ({
|
export const authSetRestore = (restore: Partial<IAuthState['restore']>) => ({
|
||||||
type: AUTH_USER_ACTIONS.SET_RESTORE,
|
type: AUTH_USER_ACTIONS.SET_RESTORE,
|
||||||
restore,
|
restore,
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
import {
|
import { api, errorMiddleware, resultMiddleware, configWithToken } from '~/utils/api';
|
||||||
api,
|
import { API } from '~/constants/api';
|
||||||
errorMiddleware,
|
import { IResultWithStatus, IMessage } from '~/redux/types';
|
||||||
resultMiddleware,
|
import { userLoginTransform } from '~/redux/auth/transforms';
|
||||||
configWithToken
|
import { IUser } from './types';
|
||||||
} from "~/utils/api";
|
|
||||||
import { API } from "~/constants/api";
|
|
||||||
import { IResultWithStatus, IMessage } from "~/redux/types";
|
|
||||||
import { userLoginTransform } from "~/redux/auth/transforms";
|
|
||||||
import { IUser } from "./types";
|
|
||||||
|
|
||||||
export const apiUserLogin = ({
|
export const apiUserLogin = ({
|
||||||
username,
|
username,
|
||||||
password
|
password,
|
||||||
}: {
|
}: {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
@ -22,9 +17,7 @@ export const apiUserLogin = ({
|
||||||
.catch(errorMiddleware)
|
.catch(errorMiddleware)
|
||||||
.then(userLoginTransform);
|
.then(userLoginTransform);
|
||||||
|
|
||||||
export const apiAuthGetUser = ({
|
export const apiAuthGetUser = ({ access }): Promise<IResultWithStatus<{ user: IUser }>> =>
|
||||||
access
|
|
||||||
}): Promise<IResultWithStatus<{ user: IUser }>> =>
|
|
||||||
api
|
api
|
||||||
.get(API.USER.ME, configWithToken(access))
|
.get(API.USER.ME, configWithToken(access))
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
|
@ -32,7 +25,7 @@ export const apiAuthGetUser = ({
|
||||||
|
|
||||||
export const apiAuthGetUserProfile = ({
|
export const apiAuthGetUserProfile = ({
|
||||||
access,
|
access,
|
||||||
username
|
username,
|
||||||
}): Promise<IResultWithStatus<{ user: IUser }>> =>
|
}): Promise<IResultWithStatus<{ user: IUser }>> =>
|
||||||
api
|
api
|
||||||
.get(API.USER.PROFILE(username), configWithToken(access))
|
.get(API.USER.PROFILE(username), configWithToken(access))
|
||||||
|
@ -41,7 +34,7 @@ export const apiAuthGetUserProfile = ({
|
||||||
|
|
||||||
export const apiAuthGetUserMessages = ({
|
export const apiAuthGetUserMessages = ({
|
||||||
access,
|
access,
|
||||||
username
|
username,
|
||||||
}): Promise<IResultWithStatus<{ messages: IMessage[] }>> =>
|
}): Promise<IResultWithStatus<{ messages: IMessage[] }>> =>
|
||||||
api
|
api
|
||||||
.get(API.USER.MESSAGES(username), configWithToken(access))
|
.get(API.USER.MESSAGES(username), configWithToken(access))
|
||||||
|
@ -51,7 +44,7 @@ export const apiAuthGetUserMessages = ({
|
||||||
export const apiAuthSendMessage = ({
|
export const apiAuthSendMessage = ({
|
||||||
access,
|
access,
|
||||||
username,
|
username,
|
||||||
message
|
message,
|
||||||
}): Promise<IResultWithStatus<{ message: IMessage }>> =>
|
}): Promise<IResultWithStatus<{ message: IMessage }>> =>
|
||||||
api
|
api
|
||||||
.post(API.USER.MESSAGE_SEND(username), { message }, configWithToken(access))
|
.post(API.USER.MESSAGE_SEND(username), { message }, configWithToken(access))
|
||||||
|
@ -61,21 +54,21 @@ export const apiAuthSendMessage = ({
|
||||||
export const apiAuthGetUpdates = ({
|
export const apiAuthGetUpdates = ({
|
||||||
access,
|
access,
|
||||||
exclude_dialogs,
|
exclude_dialogs,
|
||||||
last
|
last,
|
||||||
}): Promise<IResultWithStatus<{ message: IMessage }>> =>
|
}): Promise<IResultWithStatus<{ message: IMessage }>> =>
|
||||||
api
|
api
|
||||||
.get(
|
.get(API.USER.GET_UPDATES, configWithToken(access, { params: { exclude_dialogs, last } }))
|
||||||
API.USER.GET_UPDATES,
|
|
||||||
configWithToken(access, { params: { exclude_dialogs, last } })
|
|
||||||
)
|
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
export const apiUpdateUser = ({
|
export const apiUpdateUser = ({ access, user }): Promise<IResultWithStatus<{ user: IUser }>> =>
|
||||||
access,
|
|
||||||
user
|
|
||||||
}): Promise<IResultWithStatus<{ user: IUser }>> =>
|
|
||||||
api
|
api
|
||||||
.patch(API.USER.ME, { user }, configWithToken(access))
|
.patch(API.USER.ME, { user }, configWithToken(access))
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
|
export const apiRequestRestoreCode = ({ field }): Promise<IResultWithStatus<{}>> =>
|
||||||
|
api
|
||||||
|
.post(API.USER.REQUEST_CODE(), { field })
|
||||||
|
.then(resultMiddleware)
|
||||||
|
.catch(errorMiddleware);
|
||||||
|
|
|
@ -20,6 +20,7 @@ export const AUTH_USER_ACTIONS = {
|
||||||
PATCH_USER: 'PATCH_USER',
|
PATCH_USER: 'PATCH_USER',
|
||||||
|
|
||||||
SET_RESTORE: 'SET_RESTORE',
|
SET_RESTORE: 'SET_RESTORE',
|
||||||
|
REQUEST_RESTORE_CODE: 'REQUEST_RESTORE_CODE',
|
||||||
RESTORE_PASSWORD: 'RESTORE_PASSWORD',
|
RESTORE_PASSWORD: 'RESTORE_PASSWORD',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ const INITIAL_STATE: IAuthState = {
|
||||||
user: null,
|
user: null,
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
is_succesfull: false,
|
is_succesfull: false,
|
||||||
errors: {},
|
error: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
authPatchUser,
|
authPatchUser,
|
||||||
authRestorePassword,
|
authRestorePassword,
|
||||||
authSetRestore,
|
authSetRestore,
|
||||||
|
authRequestRestoreCode,
|
||||||
} from '~/redux/auth/actions';
|
} from '~/redux/auth/actions';
|
||||||
import {
|
import {
|
||||||
apiUserLogin,
|
apiUserLogin,
|
||||||
|
@ -25,6 +26,7 @@ import {
|
||||||
apiAuthSendMessage,
|
apiAuthSendMessage,
|
||||||
apiAuthGetUpdates,
|
apiAuthGetUpdates,
|
||||||
apiUpdateUser,
|
apiUpdateUser,
|
||||||
|
apiRequestRestoreCode,
|
||||||
} from '~/redux/auth/api';
|
} from '~/redux/auth/api';
|
||||||
import { modalSetShown, modalShowDialog } from '~/redux/modal/actions';
|
import { modalSetShown, modalShowDialog } from '~/redux/modal/actions';
|
||||||
import { selectToken, selectAuthProfile, selectAuthUser, selectAuthUpdates } from './selectors';
|
import { selectToken, selectAuthProfile, selectAuthUser, selectAuthUpdates } from './selectors';
|
||||||
|
@ -275,11 +277,26 @@ function* patchUser({ user }: ReturnType<typeof authPatchUser>) {
|
||||||
yield put(authSetProfile({ user: { ...me, ...data.user }, tab: 'profile' }));
|
yield put(authSetProfile({ user: { ...me, ...data.user }, tab: 'profile' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* requestRestoreCode({ field }: ReturnType<typeof authRequestRestoreCode>) {
|
||||||
|
if (!field) return;
|
||||||
|
|
||||||
|
yield put(authSetRestore({ error: null, is_loading: true }));
|
||||||
|
const { error, data } = yield call(apiRequestRestoreCode, { field });
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
if (data.error || error) {
|
||||||
|
return yield put(authSetRestore({ is_loading: false, error: data.error || error }));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(authSetRestore({ is_loading: false, is_succesfull: true }));
|
||||||
|
}
|
||||||
|
|
||||||
function* restorePassword({ code }: ReturnType<typeof authRestorePassword>) {
|
function* restorePassword({ code }: ReturnType<typeof authRestorePassword>) {
|
||||||
if (!code && !code.length) {
|
if (!code && !code.length) {
|
||||||
return yield put(
|
return yield put(
|
||||||
authSetRestore({
|
authSetRestore({
|
||||||
errors: { code: ERRORS.CODE_IS_INVALID },
|
error: ERRORS.CODE_IS_INVALID,
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -300,6 +317,7 @@ function* authSaga() {
|
||||||
yield takeLatest(AUTH_USER_ACTIONS.SET_LAST_SEEN_MESSAGES, setLastSeenMessages);
|
yield takeLatest(AUTH_USER_ACTIONS.SET_LAST_SEEN_MESSAGES, setLastSeenMessages);
|
||||||
yield takeLatest(AUTH_USER_ACTIONS.PATCH_USER, patchUser);
|
yield takeLatest(AUTH_USER_ACTIONS.PATCH_USER, patchUser);
|
||||||
yield takeLatest(AUTH_USER_ACTIONS.RESTORE_PASSWORD, restorePassword);
|
yield takeLatest(AUTH_USER_ACTIONS.RESTORE_PASSWORD, restorePassword);
|
||||||
|
yield takeLatest(AUTH_USER_ACTIONS.REQUEST_RESTORE_CODE, requestRestoreCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default authSaga;
|
export default authSaga;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { IState } from '~/redux/store';
|
import { IState } from '~/redux/store';
|
||||||
|
|
||||||
export const selectAuth = (state: IState): IState['auth'] => state.auth;
|
export const selectAuth = (state: IState) => state.auth;
|
||||||
export const selectUser = (state: IState): IState['auth']['user'] => state.auth.user;
|
export const selectUser = (state: IState) => state.auth.user;
|
||||||
export const selectToken = (state: IState): IState['auth']['token'] => state.auth.token;
|
export const selectToken = (state: IState) => state.auth.token;
|
||||||
export const selectAuthLogin = (state: IState): IState['auth']['login'] => state.auth.login;
|
export const selectAuthLogin = (state: IState) => state.auth.login;
|
||||||
export const selectAuthProfile = (state: IState): IState['auth']['profile'] => state.auth.profile;
|
export const selectAuthProfile = (state: IState) => state.auth.profile;
|
||||||
export const selectAuthUser = (state: IState): IState['auth']['user'] => state.auth.user;
|
export const selectAuthUser = (state: IState) => state.auth.user;
|
||||||
export const selectAuthUpdates = (state: IState): IState['auth']['updates'] => state.auth.updates;
|
export const selectAuthUpdates = (state: IState) => state.auth.updates;
|
||||||
|
export const selectAuthRestore = (state: IState) => state.auth.restore;
|
||||||
|
|
|
@ -55,6 +55,6 @@ export type IAuthState = Readonly<{
|
||||||
user: Pick<IUser, 'username' | 'photo'>;
|
user: Pick<IUser, 'username' | 'photo'>;
|
||||||
is_loading: boolean;
|
is_loading: boolean;
|
||||||
is_succesfull: boolean;
|
is_succesfull: boolean;
|
||||||
errors: Record<string, string>;
|
error: string;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
|
@ -9,8 +9,8 @@ export interface IModalState {
|
||||||
}
|
}
|
||||||
|
|
||||||
const INITIAL_STATE: IModalState = {
|
const INITIAL_STATE: IModalState = {
|
||||||
is_shown: false,
|
is_shown: true,
|
||||||
dialog: null,
|
dialog: DIALOGS.RESTORE_REQUEST,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createReducer(INITIAL_STATE, MODAL_HANDLERS);
|
export default createReducer(INITIAL_STATE, MODAL_HANDLERS);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue