diff --git a/package-lock.json b/package-lock.json index 85c20da7..04c24fa6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3052,6 +3052,7 @@ "version": "2.10.1", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "optional": true, "requires": { "hoek": "2.x.x" } @@ -3419,6 +3420,12 @@ "safe-buffer": "^5.0.1" } }, + "circular-dependency-plugin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", + "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", + "dev": true + }, "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", @@ -3493,6 +3500,10 @@ } } }, + "classie": { + "version": "github:eiriklv/classie#da1d3019904433872a8656d3cd69fc41d69c477a", + "from": "github:eiriklv/classie" + }, "classnames": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", @@ -4963,6 +4974,10 @@ "minimalistic-assert": "^1.0.0" } }, + "desandro-get-style-property": { + "version": "github:eiriklv/get-style-property#a5a74ad48d96c7d5ddcf652e9fa5d4283af37823", + "from": "github:eiriklv/get-style-property" + }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -5030,6 +5045,19 @@ "buffer-indexof": "^1.0.0" } }, + "doc-ready": { + "version": "github:eiriklv/doc-ready#31c2481101af5dab33311fea4c7fc684b58fa8ad", + "from": "github:eiriklv/doc-ready", + "requires": { + "eventie": "github:eiriklv/eventie" + }, + "dependencies": { + "eventie": { + "version": "github:eiriklv/eventie#c9d290c6aa57712257dc8c0b6bf21c9374190a3c", + "from": "github:eiriklv/eventie" + } + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -5706,12 +5734,21 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "eventemitter": { + "version": "github:braznaavtrav/EventEmitter#7169056a2f8b3b55d78ab1b85bad39277e7e88b2", + "from": "github:braznaavtrav/EventEmitter" + }, "eventemitter3": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", "dev": true }, + "eventie": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/eventie/-/eventie-1.0.6.tgz", + "integrity": "sha1-1P/IsMK15JPCqhsiy+kY067nRDc=" + }, "events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", @@ -6294,7 +6331,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6709,7 +6747,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6765,6 +6804,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6808,12 +6848,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -6899,6 +6941,13 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-size": { + "version": "github:eiriklv/get-size#c1ebd019815fc6247c094c17a41b61d0e8191b08", + "from": "github:eiriklv/get-size", + "requires": { + "desandro-get-style-property": "github:eiriklv/get-style-property" + } + }, "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -7233,7 +7282,8 @@ "hoek": { "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "optional": true }, "hoist-non-react-statics": { "version": "3.3.0", @@ -7541,6 +7591,14 @@ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", "optional": true }, + "imagesloaded": { + "version": "github:eiriklv/imagesloaded#04535a148206e58790927e133f24ca199163b995", + "from": "github:eiriklv/imagesloaded", + "requires": { + "eventie": ">=1.0.4 <2", + "wolfy87-eventemitter": "4.x" + } + }, "immutable": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", @@ -8644,6 +8702,10 @@ "object-visit": "^1.0.0" } }, + "matches-selector": { + "version": "github:desandro/matches-selector#082376f4bbe7ff8c5c6bb258ec43259c9a80a7c3", + "from": "github:desandro/matches-selector#v1.0.3" + }, "math-expression-evaluator": { "version": "1.2.17", "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", @@ -9614,6 +9676,24 @@ "os-tmpdir": "^1.0.0" } }, + "outlayer": { + "version": "github:eiriklv/outlayer#aea1c8239d30ccc1d3664ca3fff2f6d4b20fb812", + "from": "github:eiriklv/outlayer", + "requires": { + "desandro-get-style-property": "github:eiriklv/get-style-property", + "doc-ready": "github:eiriklv/doc-ready", + "eventemitter": "github:braznaavtrav/EventEmitter", + "eventie": "github:eiriklv/eventie", + "get-size": "github:eiriklv/get-size", + "matches-selector": "github:desandro/matches-selector#v1.0.3" + }, + "dependencies": { + "eventie": { + "version": "github:eiriklv/eventie#c9d290c6aa57712257dc8c0b6bf21c9374190a3c", + "from": "github:eiriklv/eventie" + } + } + }, "output-file-sync": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz", @@ -9679,6 +9759,16 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, + "packery": { + "version": "github:eiriklv/packery#8e812a0a16575ef923f5e72efcea85aadc6fea67", + "from": "github:eiriklv/packery", + "requires": { + "classie": "github:eiriklv/classie", + "desandro-get-style-property": "github:eiriklv/get-style-property", + "get-size": "github:eiriklv/get-size", + "outlayer": "github:eiriklv/outlayer" + } + }, "pako": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", @@ -10993,25 +11083,9 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/react-packery-component/-/react-packery-component-1.0.2.tgz", "integrity": "sha1-FSEiHaSRZ8s87fR9W1UsM0iBgKk=", - "dependencies": { - "imagesloaded": { - "version": "github:eiriklv/imagesloaded#04535a148206e58790927e133f24ca199163b995", - "from": "github:eiriklv/imagesloaded#04535a148206e58790927e133f24ca199163b995", - "requires": { - "eventie": ">=1.0.4 <2", - "wolfy87-eventemitter": "4.x" - } - }, - "packery": { - "version": "github:eiriklv/packery#8e812a0a16575ef923f5e72efcea85aadc6fea67", - "from": "github:eiriklv/packery#8e812a0a16575ef923f5e72efcea85aadc6fea67", - "requires": { - "classie": "github:eiriklv/classie#da1d3019904433872a8656d3cd69fc41d69c477a", - "desandro-get-style-property": "github:eiriklv/get-style-property#a5a74ad48d96c7d5ddcf652e9fa5d4283af37823", - "get-size": "github:eiriklv/get-size#c1ebd019815fc6247c094c17a41b61d0e8191b08", - "outlayer": "github:eiriklv/outlayer#aea1c8239d30ccc1d3664ca3fff2f6d4b20fb812" - } - } + "requires": { + "imagesloaded": "github:eiriklv/imagesloaded", + "packery": "github:eiriklv/packery" } }, "react-redux": { @@ -14433,6 +14507,11 @@ "string-width": "^1.0.2 || 2" } }, + "wolfy87-eventemitter": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-4.3.0.tgz", + "integrity": "sha1-ZJc5bJXnQ1nwa241QJM5MY2Nlk8=" + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index 0e10bc8c..6e1acd2b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "babel-preset-stage-2": "^6.24.1", + "circular-dependency-plugin": "^5.2.0", "css-loader": "^0.28.11", "file-loader": "^1.1.11", "html-webpack-plugin": "^3.2.0", diff --git a/src/components/editors/AudioEditor/index.tsx b/src/components/editors/AudioEditor/index.tsx index 9826c74c..efa12941 100644 --- a/src/components/editors/AudioEditor/index.tsx +++ b/src/components/editors/AudioEditor/index.tsx @@ -4,8 +4,9 @@ import { connect } from 'react-redux'; import { UPLOAD_TYPES } from '~/redux/uploads/constants'; import { ImageGrid } from '../ImageGrid'; import { AudioGrid } from '../AudioGrid'; -import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; import { selectUploads } from '~/redux/uploads/selectors'; + +import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; import * as styles from './styles.scss'; const mapStateToProps = selectUploads; diff --git a/src/components/editors/AudioGrid/index.tsx b/src/components/editors/AudioGrid/index.tsx index 8dd8556d..c3ca845e 100644 --- a/src/components/editors/AudioGrid/index.tsx +++ b/src/components/editors/AudioGrid/index.tsx @@ -1,11 +1,12 @@ import React, { FC, useCallback } from 'react'; import { SortEnd } from 'react-sortable-hoc'; -import * as styles from './styles.scss'; import { IFile } from '~/redux/types'; import { IUploadStatus } from '~/redux/uploads/reducer'; import { moveArrItem } from '~/utils/fn'; import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid'; +import * as styles from './styles.scss'; + interface IProps { files: IFile[]; setFiles: (val: IFile[]) => void; diff --git a/src/constants/dialogs.ts b/src/constants/dialogs.ts new file mode 100644 index 00000000..d0c6b5b7 --- /dev/null +++ b/src/constants/dialogs.ts @@ -0,0 +1,30 @@ +import { NODE_TYPES } from '~/redux/node/constants'; +import { EditorDialogImage } from '~/containers/editors/EditorDialogImage'; +import { EditorDialogText } from '~/containers/editors/EditorDialogText'; +import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo'; +import { EditorDialogAudio } from '~/containers/editors/EditorDialogAudio'; +import { LoginDialog } from '~/containers/dialogs/LoginDialog'; +import { LoadingDialog } from '~/containers/dialogs/LoadingDialog'; +import { TestDialog } from '~/containers/dialogs/TestDialog'; +import { ProfileDialog } from '~/containers/dialogs/ProfileDialog'; +import { RestoreRequestDialog } from '~/containers/dialogs/RestoreRequestDialog'; +import { DIALOGS } from '~/redux/modal/constants'; + +export const DIALOG_CONTENT = { + [DIALOGS.EDITOR_IMAGE]: EditorDialogImage, + [DIALOGS.EDITOR_TEXT]: EditorDialogText, + [DIALOGS.EDITOR_VIDEO]: EditorDialogVideo, + [DIALOGS.EDITOR_AUDIO]: EditorDialogAudio, + [DIALOGS.LOGIN]: LoginDialog, + [DIALOGS.LOADING]: LoadingDialog, + [DIALOGS.TEST]: TestDialog, + [DIALOGS.PROFILE]: ProfileDialog, + [DIALOGS.RESTORE_REQUEST]: RestoreRequestDialog, +}; + +export const NODE_EDITOR_DIALOGS = { + [NODE_TYPES.IMAGE]: DIALOGS.EDITOR_IMAGE, + [NODE_TYPES.TEXT]: DIALOGS.EDITOR_TEXT, + [NODE_TYPES.VIDEO]: DIALOGS.EDITOR_VIDEO, + [NODE_TYPES.AUDIO]: DIALOGS.EDITOR_AUDIO, +}; diff --git a/src/constants/errors.ts b/src/constants/errors.ts index 5e10b468..4efef113 100644 --- a/src/constants/errors.ts +++ b/src/constants/errors.ts @@ -1,35 +1,37 @@ export const ERRORS = { - NOT_AN_EMAIL: "Not_An_Email", - TOO_SHIRT: "Is_Too_Shirt", - EMPTY_RESPONSE: "Empty_Response", - NO_COMMENTS: "No_Comments", - FILES_REQUIRED: "Files_Required", - TEXT_REQUIRED: "Text_Required", - UNKNOWN_NODE_TYPE: "Unknown_Node_Type", - URL_INVALID: "Url_Invalid", - FILES_AUDIO_REQUIRED: "Files_Audio_Required", - NOT_ENOUGH_RIGHTS: "Not_Enough_Rights", - INCORRECT_DATA: "Incorrect_Data", - IMAGE_CONVERSION_FAILED: "Image_Conversion_Failed", - USER_NOT_FOUND: "User_Not_found", - USER_EXIST: "User_Exist", - INCORRECT_PASSWORD: "Incorrect_Password" + NOT_AN_EMAIL: 'Not_An_Email', + TOO_SHIRT: 'Is_Too_Shirt', + EMPTY_RESPONSE: 'Empty_Response', + NO_COMMENTS: 'No_Comments', + FILES_REQUIRED: 'Files_Required', + TEXT_REQUIRED: 'Text_Required', + UNKNOWN_NODE_TYPE: 'Unknown_Node_Type', + URL_INVALID: 'Url_Invalid', + FILES_AUDIO_REQUIRED: 'Files_Audio_Required', + NOT_ENOUGH_RIGHTS: 'Not_Enough_Rights', + INCORRECT_DATA: 'Incorrect_Data', + IMAGE_CONVERSION_FAILED: 'Image_Conversion_Failed', + USER_NOT_FOUND: 'User_Not_found', + USER_EXIST: 'User_Exist', + INCORRECT_PASSWORD: 'Incorrect_Password', + CODE_IS_INVALID: 'Code_Is_Invalid', }; export const ERROR_LITERAL = { - [ERRORS.NOT_AN_EMAIL]: "Введите правильный e-mail", - [ERRORS.TOO_SHIRT]: "Слишком короткий", - [ERRORS.NO_COMMENTS]: "Комментариев пока нет", - [ERRORS.EMPTY_RESPONSE]: "Пустой ответ сервера", - [ERRORS.FILES_REQUIRED]: "Добавьте файлы", - [ERRORS.TEXT_REQUIRED]: "Нужно немного текста", - [ERRORS.UNKNOWN_NODE_TYPE]: "Неизвестный тип поста", - [ERRORS.URL_INVALID]: "Неизвестный адрес", - [ERRORS.FILES_AUDIO_REQUIRED]: "Нужна хотя бы одна песня", - [ERRORS.NOT_ENOUGH_RIGHTS]: "У вас недостаточно прав", - [ERRORS.INCORRECT_DATA]: "Недопустимые данные", - [ERRORS.IMAGE_CONVERSION_FAILED]: "Не удалось изменить изображение", - [ERRORS.USER_NOT_FOUND]: "Пользователь не найден", - [ERRORS.USER_EXIST]: "Такой пользователь уже существует", - [ERRORS.INCORRECT_PASSWORD]: "Неправильный пароль" + [ERRORS.NOT_AN_EMAIL]: 'Введите правильный e-mail', + [ERRORS.TOO_SHIRT]: 'Слишком короткий', + [ERRORS.NO_COMMENTS]: 'Комментариев пока нет', + [ERRORS.EMPTY_RESPONSE]: 'Пустой ответ сервера', + [ERRORS.FILES_REQUIRED]: 'Добавьте файлы', + [ERRORS.TEXT_REQUIRED]: 'Нужно немного текста', + [ERRORS.UNKNOWN_NODE_TYPE]: 'Неизвестный тип поста', + [ERRORS.URL_INVALID]: 'Неизвестный адрес', + [ERRORS.FILES_AUDIO_REQUIRED]: 'Нужна хотя бы одна песня', + [ERRORS.NOT_ENOUGH_RIGHTS]: 'У вас недостаточно прав', + [ERRORS.INCORRECT_DATA]: 'Недопустимые данные', + [ERRORS.IMAGE_CONVERSION_FAILED]: 'Не удалось изменить изображение', + [ERRORS.USER_NOT_FOUND]: 'Пользователь не найден', + [ERRORS.USER_EXIST]: 'Такой пользователь уже существует', + [ERRORS.INCORRECT_PASSWORD]: 'Неправильный пароль', + [ERRORS.CODE_IS_INVALID]: 'Код не существует или устарел', }; diff --git a/src/containers/dialogs/LoginDialog/index.tsx b/src/containers/dialogs/LoginDialog/index.tsx index c30ba655..3099e839 100644 --- a/src/containers/dialogs/LoginDialog/index.tsx +++ b/src/containers/dialogs/LoginDialog/index.tsx @@ -1,37 +1,43 @@ -import React, { FC, FormEvent, useCallback, useEffect, useState } from "react"; -import { connect } from "react-redux"; -import { ScrollDialog } from "../ScrollDialog"; -import { IDialogProps } from "~/redux/modal/constants"; -import { useCloseOnEscape } from "~/utils/hooks"; -import { Group } from "~/components/containers/Group"; -import { InputText } from "~/components/input/InputText"; -import { Button } from "~/components/input/Button"; -import { Padder } from "~/components/containers/Padder"; -import * as styles from "./styles.scss"; -import { selectAuthLogin } from "~/redux/auth/selectors"; -import * as ACTIONS from "~/redux/auth/actions"; -import { API } from "~/constants/api"; -import { BetterScrollDialog } from "../BetterScrollDialog"; +import React, { FC, FormEvent, useCallback, useEffect, useState, useMemo } from 'react'; +import { connect } from 'react-redux'; +import { IDialogProps } from '~/redux/modal/constants'; +import { DIALOGS } from '~/redux/modal/constants'; +import { useCloseOnEscape } from '~/utils/hooks'; +import { Group } from '~/components/containers/Group'; +import { InputText } from '~/components/input/InputText'; +import { Button } from '~/components/input/Button'; +import { Padder } from '~/components/containers/Padder'; +import { selectAuthLogin } from '~/redux/auth/selectors'; +import { API } from '~/constants/api'; +import { BetterScrollDialog } from '../BetterScrollDialog'; + +import * as styles from './styles.scss'; +import * as ACTIONS from '~/redux/auth/actions'; +import * as MODAL_ACTIONS from '~/redux/modal/actions'; const mapStateToProps = selectAuthLogin; const mapDispatchToProps = { userSendLoginRequest: ACTIONS.userSendLoginRequest, - userSetLoginError: ACTIONS.userSetLoginError + userSetLoginError: ACTIONS.userSetLoginError, + modalShowDialog: MODAL_ACTIONS.modalShowDialog, }; -type IProps = ReturnType & - typeof mapDispatchToProps & - IDialogProps & {}; +type IProps = ReturnType & typeof mapDispatchToProps & IDialogProps & {}; + +console.log('initial', MODAL_ACTIONS); const LoginDialogUnconnected: FC = ({ onRequestClose, error, userSendLoginRequest, - userSetLoginError + userSetLoginError, + modalShowDialog, }) => { - const [username, setUserName] = useState(""); - const [password, setPassword] = useState(""); + console.log({ modalShowDialog, MODAL_ACTIONS }); + + const [username, setUserName] = useState(''); + const [password, setPassword] = useState(''); const onSubmit = useCallback( (event: FormEvent) => { @@ -41,61 +47,60 @@ const LoginDialogUnconnected: FC = ({ [userSendLoginRequest, username, password] ); + const onRestoreRequest = useCallback( + event => { + console.log('a', { MODAL_ACTIONS, modalShowDialog, userSetLoginError }); + event.preventDefault(); + modalShowDialog(DIALOGS.RESTORE_REQUEST); + }, + [modalShowDialog, userSetLoginError] + ); + const onSocialLogin = useCallback(() => { - window.open(API.USER.VKONTAKTE_LOGIN, "", "width=600,height=400"); + window.open(API.USER.VKONTAKTE_LOGIN, '', 'width=600,height=400'); }, []); useEffect(() => { if (error) userSetLoginError(null); }, [username, password]); - const buttons = ( - - - + const buttons = useMemo( + () => ( + + + + + + ), + [onSocialLogin] ); useCloseOnEscape(onRequestClose); return (
- +

РЕШИТЕЛЬНО ВОЙТИ

- + - + - - - +
diff --git a/src/containers/dialogs/LoginDialog/styles.scss b/src/containers/dialogs/LoginDialog/styles.scss index 3e0b6dc0..176fa7c9 100644 --- a/src/containers/dialogs/LoginDialog/styles.scss +++ b/src/containers/dialogs/LoginDialog/styles.scss @@ -1,4 +1,5 @@ -$vk_color: darken(desaturate($blue, 100%), 30%); +$secondary_color: darken(desaturate($blue, 100%), 30%); +$vk_color: $secondary_color; .wrap { display: flex; @@ -15,7 +16,7 @@ $vk_color: darken(desaturate($blue, 100%), 30%); } } -.vk { +.secondary_button { background: $content_bg; box-shadow: inset $vk_color 0 0 0 2px; color: $vk_color; @@ -23,11 +24,15 @@ $vk_color: darken(desaturate($blue, 100%), 30%); svg { fill: $vk_color; margin-right: $gap; - // width: 24px; - // height: 24px; } } +.forgot_button { + background: $content_bg; + box-shadow: none; + color: $secondary_color; +} + .buttons { margin: $gap * 2 0 0 0 !important; padding: $gap * 2 0 0 0; @@ -41,3 +46,12 @@ $vk_color: darken(desaturate($blue, 100%), 30%); // text-align: left; } } + +.links { + font: $font_14_regular; + text-align: center; + + a { + color: lighten($content_bg, 40%); + } +} diff --git a/src/containers/dialogs/Modal/index.tsx b/src/containers/dialogs/Modal/index.tsx index 8d37d0f6..cbb1f69d 100644 --- a/src/containers/dialogs/Modal/index.tsx +++ b/src/containers/dialogs/Modal/index.tsx @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom'; import * as styles from './styles.scss'; import { IState } from '~/redux/store'; import * as ACTIONS from '~/redux/modal/actions'; -import { DIALOG_CONTENT } from '~/redux/modal/constants'; +import { DIALOG_CONTENT } from '~/constants/dialogs'; const mapStateToProps = ({ modal }: IState) => ({ ...modal }); const mapDispatchToProps = { diff --git a/src/containers/dialogs/RestoreRequestDialog/index.tsx b/src/containers/dialogs/RestoreRequestDialog/index.tsx new file mode 100644 index 00000000..eb6201e7 --- /dev/null +++ b/src/containers/dialogs/RestoreRequestDialog/index.tsx @@ -0,0 +1,41 @@ +import React, { FC, useState, useMemo, useCallback } from 'react'; +import { IDialogProps } from '~/redux/types'; +import { connect } from 'react-redux'; +import { BetterScrollDialog } from '../BetterScrollDialog'; +import { Group } from '~/components/containers/Group'; +import { InputText } from '~/components/input/InputText'; +import { Button } from '~/components/input/Button'; + +const mapStateToProps = () => ({}); +const mapDispatchToProps = {}; + +type IProps = IDialogProps & ReturnType & typeof mapDispatchToProps & {}; + +const RestoreRequestDialogUnconnected: FC = ({}) => { + const [field, setField] = useState(); + + const onSubmit = useCallback(event => { + event.preventDefault(); + }, []); + + const buttons = useMemo(() => , []); + + return ( + + + + + +
Введите имя пользователя или адрес почты. Мы пришлем ссылку для сброса пароля.
+
+
+ + ); +}; + +const RestoreRequestDialog = connect( + mapStateToProps, + mapDispatchToProps +)(RestoreRequestDialogUnconnected); + +export { RestoreRequestDialog }; diff --git a/src/redux/auth/reducer.ts b/src/redux/auth/reducer.ts index 1c9ba775..885bb259 100644 --- a/src/redux/auth/reducer.ts +++ b/src/redux/auth/reducer.ts @@ -34,6 +34,7 @@ const INITIAL_STATE: IAuthState = { restore: { code: '', + user: null, is_loading: false, is_succesfull: false, errors: {}, diff --git a/src/redux/auth/sagas.ts b/src/redux/auth/sagas.ts index e16cd5d2..8bcf02ec 100644 --- a/src/redux/auth/sagas.ts +++ b/src/redux/auth/sagas.ts @@ -1,17 +1,5 @@ -import { - call, - put, - takeEvery, - takeLatest, - select, - delay -} from "redux-saga/effects"; -import { - AUTH_USER_ACTIONS, - EMPTY_USER, - USER_ERRORS, - USER_ROLES -} from "~/redux/auth/constants"; +import { call, put, takeEvery, takeLatest, select, delay } from 'redux-saga/effects'; +import { AUTH_USER_ACTIONS, EMPTY_USER, USER_ERRORS, USER_ROLES } from '~/redux/auth/constants'; import { authSetToken, userSetLoginError, @@ -25,8 +13,10 @@ import { authSetUpdates, authLoggedIn, authSetLastSeenMessages, - authPatchUser -} from "~/redux/auth/actions"; + authPatchUser, + authRestorePassword, + authSetRestore, +} from '~/redux/auth/actions'; import { apiUserLogin, apiAuthGetUser, @@ -34,31 +24,19 @@ import { apiAuthGetUserMessages, apiAuthSendMessage, apiAuthGetUpdates, - apiUpdateUser -} from "~/redux/auth/api"; -import { modalSetShown, modalShowDialog } from "~/redux/modal/actions"; -import { - selectToken, - selectAuthProfile, - selectAuthUser, - selectAuthUpdates -} from "./selectors"; -import { - IResultWithStatus, - INotification, - IMessageNotification -} from "../types"; -import { IUser, IAuthState } from "./types"; -import { REHYDRATE, RehydrateAction } from "redux-persist"; -import { selectModal } from "../modal/selectors"; -import { IModalState } from "../modal/reducer"; -import { DIALOGS } from "../modal/constants"; -import { ERRORS } from "~/constants/errors"; + apiUpdateUser, +} from '~/redux/auth/api'; +import { modalSetShown, modalShowDialog } from '~/redux/modal/actions'; +import { selectToken, selectAuthProfile, selectAuthUser, selectAuthUpdates } from './selectors'; +import { IResultWithStatus, INotification, IMessageNotification } from '../types'; +import { IUser, IAuthState } from './types'; +import { REHYDRATE, RehydrateAction } from 'redux-persist'; +import { selectModal } from '~/redux/modal/selectors'; +import { IModalState } from '~/redux/modal/reducer'; +import { DIALOGS } from '~/redux/modal/constants'; +import { ERRORS } from '~/constants/errors'; -export function* reqWrapper( - requestAction, - props = {} -): ReturnType { +export function* reqWrapper(requestAction, props = {}): ReturnType { const access = yield select(selectToken); const result = yield call(requestAction, { access, ...props }); @@ -70,22 +48,16 @@ export function* reqWrapper( return result; } -function* sendLoginRequestSaga({ - username, - password -}: ReturnType) { +function* sendLoginRequestSaga({ username, password }: ReturnType) { if (!username || !password) return; const { error, - data: { token, user } - }: IResultWithStatus<{ token: string; user: IUser }> = yield call( - apiUserLogin, - { - username, - password - } - ); + data: { token, user }, + }: IResultWithStatus<{ token: string; user: IUser }> = yield call(apiUserLogin, { + username, + password, + }); if (error) { yield put(userSetLoginError(error)); @@ -101,17 +73,14 @@ function* sendLoginRequestSaga({ function* refreshUser() { const { error, - data: { user } - }: IResultWithStatus<{ user: IUser }> = yield call( - reqWrapper, - apiAuthGetUser - ); + data: { user }, + }: IResultWithStatus<{ user: IUser }> = yield call(reqWrapper, apiAuthGetUser); if (error) { yield put( authSetUser({ ...EMPTY_USER, - is_user: false + is_user: false, }) ); @@ -122,7 +91,7 @@ function* refreshUser() { } function* checkUserSaga({ key }: RehydrateAction) { - if (key !== "auth") return; + if (key !== 'auth') return; yield call(refreshUser); // yield put(authOpenProfile("gvorcek", "settings")); } @@ -142,21 +111,18 @@ function* logoutSaga() { yield put( authSetUpdates({ last: null, - notifications: [] + notifications: [], }) ); } -function* openProfile({ - username, - tab = "profile" -}: ReturnType) { +function* openProfile({ username, tab = 'profile' }: ReturnType) { yield put(modalShowDialog(DIALOGS.PROFILE)); yield put(authSetProfile({ is_loading: true, tab })); const { error, - data: { user } + data: { user }, } = yield call(reqWrapper, apiAuthGetUserProfile, { username }); if (error || !user) { @@ -176,16 +142,15 @@ function* getMessages({ username }: ReturnType) { messages: messages && messages.length > 0 && - (messages[0].to.username === username || - messages[0].from.username === username) + (messages[0].to.username === username || messages[0].from.username === username) ? messages - : [] + : [], }) ); const { error, - data + data, // data: { messages }, } = yield call(reqWrapper, apiAuthGetUserMessages, { username }); @@ -193,21 +158,19 @@ function* getMessages({ username }: ReturnType) { return yield put( authSetProfile({ is_loading_messages: false, - messages_error: ERRORS.EMPTY_RESPONSE + messages_error: ERRORS.EMPTY_RESPONSE, }) ); } - yield put( - authSetProfile({ is_loading_messages: false, messages: data.messages }) - ); + yield put(authSetProfile({ is_loading_messages: false, messages: data.messages })); const { notifications } = yield select(selectAuthUpdates); // clear viewed message from notifcation list const filtered = notifications.filter( notification => - notification.type !== "message" || + notification.type !== 'message' || (notification as IMessageNotification).content.from.username !== username ); @@ -216,23 +179,18 @@ function* getMessages({ username }: ReturnType) { } } -function* sendMessage({ - message, - onSuccess -}: ReturnType) { +function* sendMessage({ message, onSuccess }: ReturnType) { const { - user: { username } + user: { username }, } = yield select(selectAuthProfile); if (!username) return; - yield put( - authSetProfile({ is_sending_messages: true, messages_error: null }) - ); + yield put(authSetProfile({ is_sending_messages: true, messages_error: null })); const { error, data } = yield call(reqWrapper, apiAuthSendMessage, { username, - message + message, }); console.log({ error, data }); @@ -241,7 +199,7 @@ function* sendMessage({ return yield put( authSetProfile({ is_sending_messages: false, - messages_error: error || ERRORS.EMPTY_RESPONSE + messages_error: error || ERRORS.EMPTY_RESPONSE, }) ); } @@ -255,7 +213,7 @@ function* sendMessage({ yield put( authSetProfile({ is_sending_messages: false, - messages: [data.message, ...messages] + messages: [data.message, ...messages], }) ); @@ -265,35 +223,28 @@ function* sendMessage({ function* getUpdates() { const user = yield select(selectAuthUser); - if (!user || !user.is_user || user.role === USER_ROLES.GUEST || !user.id) - return; + if (!user || !user.is_user || user.role === USER_ROLES.GUEST || !user.id) return; const modal: IModalState = yield select(selectModal); - const profile: IAuthState["profile"] = yield select(selectAuthProfile); - const { last }: IAuthState["updates"] = yield select(selectAuthUpdates); + const profile: IAuthState['profile'] = yield select(selectAuthProfile); + const { last }: IAuthState['updates'] = yield select(selectAuthUpdates); const exclude_dialogs = - modal.is_shown && modal.dialog === DIALOGS.PROFILE && profile.user.id - ? profile.user.id - : null; + modal.is_shown && modal.dialog === DIALOGS.PROFILE && profile.user.id ? profile.user.id : null; - const { - error, - data - }: IResultWithStatus<{ notifications: INotification[] }> = yield call( + const { error, data }: IResultWithStatus<{ notifications: INotification[] }> = yield call( reqWrapper, apiAuthGetUpdates, { exclude_dialogs, last: last || user.last_seen_messages } ); - if (error || !data || !data.notifications || !data.notifications.length) - return; + if (error || !data || !data.notifications || !data.notifications.length) return; const { notifications } = data; yield put( authSetUpdates({ last: notifications[0].created_at, - notifications + notifications, }) ); } @@ -305,9 +256,7 @@ function* startPollingSaga() { } } -function* setLastSeenMessages({ - last_seen_messages -}: ReturnType) { +function* setLastSeenMessages({ last_seen_messages }: ReturnType) { if (!Date.parse(last_seen_messages)) return; yield call(reqWrapper, apiUpdateUser, { user: { last_seen_messages } }); @@ -323,7 +272,19 @@ function* patchUser({ user }: ReturnType) { } yield put(authSetUser({ ...me, ...data.user })); - yield put(authSetProfile({ user: { ...me, ...data.user }, tab: "profile" })); + yield put(authSetProfile({ user: { ...me, ...data.user }, tab: 'profile' })); +} + +function* restorePassword({ code }: ReturnType) { + if (!code && !code.length) { + return yield put( + authSetRestore({ + errors: { code: ERRORS.CODE_IS_INVALID }, + is_loading: false, + }) + ); + } + console.log({ code }); } function* authSaga() { @@ -336,11 +297,9 @@ function* authSaga() { yield takeLatest(AUTH_USER_ACTIONS.OPEN_PROFILE, openProfile); yield takeLatest(AUTH_USER_ACTIONS.GET_MESSAGES, getMessages); yield takeLatest(AUTH_USER_ACTIONS.SEND_MESSAGE, sendMessage); - 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.RESTORE_PASSWORD, restorePassword); } export default authSaga; diff --git a/src/redux/auth/types.ts b/src/redux/auth/types.ts index 38922761..07fd8c5d 100644 --- a/src/redux/auth/types.ts +++ b/src/redux/auth/types.ts @@ -52,6 +52,7 @@ export type IAuthState = Readonly<{ restore: { code: string; + user: Pick; is_loading: boolean; is_succesfull: boolean; errors: Record; diff --git a/src/redux/modal/constants.ts b/src/redux/modal/constants.ts index b611f962..07f723ef 100644 --- a/src/redux/modal/constants.ts +++ b/src/redux/modal/constants.ts @@ -8,12 +8,7 @@ import { EditorDialogAudio } from '~/containers/editors/EditorDialogAudio'; import { NODE_TYPES } from '../node/constants'; import { TestDialog } from '~/containers/dialogs/TestDialog'; import { ProfileDialog } from '~/containers/dialogs/ProfileDialog'; - -export const MODAL_ACTIONS = { - SET_SHOWN: 'MODAL.SET_SHOWN', - SET_DIALOG: 'SET_DIALOG', - SHOW_DIALOG: 'SHOW_DIALOG', -}; +import { RestoreRequestDialog } from '~/containers/dialogs/RestoreRequestDialog'; export const DIALOGS = { EDITOR_IMAGE: 'EDITOR_IMAGE', @@ -23,25 +18,14 @@ export const DIALOGS = { LOGIN: 'LOGIN', LOADING: 'LOADING', PROFILE: 'PROFILE', + RESTORE_REQUEST: 'RESTORE_REQUEST', TEST: 'TEST', }; -export const DIALOG_CONTENT = { - [DIALOGS.EDITOR_IMAGE]: EditorDialogImage, - [DIALOGS.EDITOR_TEXT]: EditorDialogText, - [DIALOGS.EDITOR_VIDEO]: EditorDialogVideo, - [DIALOGS.EDITOR_AUDIO]: EditorDialogAudio, - [DIALOGS.LOGIN]: LoginDialog, - [DIALOGS.LOADING]: LoadingDialog, - [DIALOGS.TEST]: TestDialog, - [DIALOGS.PROFILE]: ProfileDialog, -}; - -export const NODE_EDITOR_DIALOGS = { - [NODE_TYPES.IMAGE]: DIALOGS.EDITOR_IMAGE, - [NODE_TYPES.TEXT]: DIALOGS.EDITOR_TEXT, - [NODE_TYPES.VIDEO]: DIALOGS.EDITOR_VIDEO, - [NODE_TYPES.AUDIO]: DIALOGS.EDITOR_AUDIO, +export const MODAL_ACTIONS = { + SET_SHOWN: 'MODAL.SET_SHOWN', + SET_DIALOG: 'SET_DIALOG', + SHOW_DIALOG: 'SHOW_DIALOG', }; export interface IDialogProps { diff --git a/src/redux/node/sagas.ts b/src/redux/node/sagas.ts index 1ac51712..bd6e3a61 100644 --- a/src/redux/node/sagas.ts +++ b/src/redux/node/sagas.ts @@ -40,7 +40,8 @@ import { selectFlowNodes, selectFlow } from '../flow/selectors'; import { URLS } from '~/constants/urls'; import { selectNode } from './selectors'; import { IResultWithStatus, INode } from '../types'; -import { NODE_EDITOR_DIALOGS, DIALOGS } from '../modal/constants'; +import { NODE_EDITOR_DIALOGS } from '~/constants/dialogs'; +import { DIALOGS } from '~/redux/modal/constants'; import { INodeState } from './reducer'; import { IFlowState } from '../flow/reducer'; diff --git a/src/redux/types.ts b/src/redux/types.ts index 6a9b38c0..4e21edc9 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -1,7 +1,7 @@ -import { DetailedHTMLProps, InputHTMLAttributes } from "react"; -import { DIALOGS } from "~/redux/modal/constants"; -import { ERRORS } from "~/constants/errors"; -import { IUser } from "./auth/types"; +import { DetailedHTMLProps, InputHTMLAttributes } from 'react'; +import { DIALOGS } from '~/redux/modal/constants'; +import { ERRORS } from '~/constants/errors'; +import { IUser } from './auth/types'; export interface ITag { id: number; @@ -55,7 +55,7 @@ export interface IResultWithStatus { export type UUID = string; -export type IUploadType = "image" | "text" | "audio" | "video" | "other"; +export type IUploadType = 'image' | 'text' | 'audio' | 'video' | 'other'; export interface IFile { id?: number; @@ -96,12 +96,12 @@ export interface IFileWithUUID { } export interface IBlockText { - type: "text"; + type: 'text'; text: string; } export interface IBlockEmbed { - type: "video"; + type: 'video'; url: string; } @@ -124,7 +124,7 @@ export interface INode { is_heroic?: boolean; flow: { - display: "single" | "vertical" | "horizontal" | "quadro"; + display: 'single' | 'vertical' | 'horizontal' | 'quadro'; show_description: boolean; }; @@ -147,7 +147,7 @@ export interface IComment { update_at?: string; } -export type IMessage = Omit & { +export type IMessage = Omit & { from: IUser; to: IUser; }; @@ -155,7 +155,7 @@ export type IMessage = Omit & { export interface ICommentGroup { user: IUser; comments: IComment[]; - ids: IComment["id"][]; + ids: IComment['id'][]; } export type IUploadProgressHandler = (progress: ProgressEvent) => void; @@ -164,25 +164,25 @@ export type IValidationErrors = Record; export type InputHandler = (val: T) => void; export const NOTIFICATION_TYPES = { - message: "message", - comment: "comment", - node: "node" + message: 'message', + comment: 'comment', + node: 'node', }; export type IMessageNotification = { - type: typeof NOTIFICATION_TYPES["message"]; + type: typeof NOTIFICATION_TYPES['message']; content: Partial; created_at: string; }; export type ICommentNotification = { - type: typeof NOTIFICATION_TYPES["comment"]; + type: typeof NOTIFICATION_TYPES['comment']; content: Partial; created_at: string; }; export type INodeNotification = { - type: typeof NOTIFICATION_TYPES["node"]; + type: typeof NOTIFICATION_TYPES['node']; content: Partial; created_at: string; }; diff --git a/src/styles/colors.scss b/src/styles/colors.scss index 1fada816..0319f9ab 100644 --- a/src/styles/colors.scss +++ b/src/styles/colors.scss @@ -12,6 +12,9 @@ $grass: #41800d; $wisegreen: #007962; // $wisegreen: #006868; +$primary: $red; +$secondary: $wisegreen; + $red_gradient: linear-gradient(165deg, $orange -50%, $red 150%); $blue_gradient: linear-gradient(170deg, $green, $dark_blue); $green_gradient: linear-gradient( diff --git a/src/styles/global.scss b/src/styles/global.scss index fdbcedd5..4b112c1a 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -96,6 +96,11 @@ body { font-weight: bold; } +a { + color: $primary; + text-decoration: underline; +} + ::-webkit-scrollbar { width: 18px; height: 18px; diff --git a/webpack.config.js b/webpack.config.js index 2dc6a614..07314a45 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const { join } = require('path'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const Dotenv = require('dotenv-webpack'); +const CircularDependencyPlugin = require('circular-dependency-plugin'); const htmlPlugin = new HtmlWebPackPlugin({ template: './src/index.html', @@ -38,6 +39,13 @@ module.exports = () => { filename: '[name].[contenthash].css', chunkFilename: '[id].[contenthash].css', }), + new CircularDependencyPlugin({ + // exclude: /node_modules/, + include: /LoginDialog/, + failOnError: true, + allowAsyncCycles: false, + cwd: process.cwd(), + }), ]; return {