From 426adc1e12858a9f3ed430ac52d47c6c4695d9f0 Mon Sep 17 00:00:00 2001 From: Nikita Voloshin Date: Thu, 6 Jun 2019 14:53:08 +0300 Subject: [PATCH 1/2] Implement Task 5 --- src/app/app.css | 27 ---- src/app/app.jsx | 25 ---- src/app/app.test.jsx | 11 -- src/app/index.js | 1 - src/components/actions-box/actions-box.jsx | 0 .../actions-box/actions-box.module.css | 0 src/components/actions-box/actions/action.jsx | 0 .../actions-box/actions/action.module.css | 0 .../delete-letters-action/delete-letters.jsx | 14 ++ .../actions/delete-letters-action/index.js | 1 + src/components/actions-box/actions/index.js | 0 .../actions/read-letters-action/index.js | 0 .../read-letters-action/read-letters.jsx | 0 src/components/actions-box/index.js | 0 .../animation-wrapper/animation-wrapper.jsx | 0 src/components/animation-wrapper/index.js | 0 src/components/app/app.jsx | 0 src/components/app/index.js | 0 src/components/button/button.jsx | 0 src/components/button/button.module.css | 0 src/components/button/index.js | 0 src/components/checkbox/checkbox.jsx | 0 src/components/checkbox/checkbox.module.css | 0 src/components/checkbox/checkbox.svg | 3 + src/components/checkbox/index.js | 0 src/components/content/content.jsx | 0 src/components/content/index.js | 0 src/components/date/date.jsx | 0 src/components/date/date.module.css | 0 src/components/date/index.js | 0 src/components/footer/footer.jsx | 0 src/components/footer/footer.module.css | 0 src/components/footer/index.js | 0 src/components/header/hamburger.svg | 5 + src/components/header/header.jsx | 0 src/components/header/image.svg | 5 + src/components/header/index.js | 0 src/components/header/mail-yandex.svg | 3 + src/components/letter-window/index.js | 0 .../letter-window/letter-window.jsx | 0 .../letter-window/letter-window.module.css | 0 .../letter/deleted/deleted-letter.jsx | 21 +++ .../letter/deleted/deleted-letter.module.css | 20 +++ src/components/letter/deleted/index.js | 1 + src/components/letter/index.js | 0 src/components/letter/letter.jsx | 0 src/components/letter/letter.module.css | 0 src/components/letter/new/index.js | 0 src/components/letter/new/new-letter.jsx | 0 .../letter/new/new-letter.module.css | 0 src/components/letter/unread-flag/index.js | 0 .../letter/unread-flag/unread-flag.jsx | 0 .../letter/unread-flag/unread-flag.module.css | 0 src/components/letters-context/index.js | 1 + .../letters-context/letters-context.jsx | 120 ++++++++++++++++++ src/components/letters-page/index.js | 0 .../letters-page-component/index.js | 0 .../letters-list/index.js | 1 + .../letters-list/letters-list.jsx | 12 ++ .../letters-list/letters-list.module.css | 0 .../letters-page-component.jsx | 0 .../letters-page.module.css | 18 +++ .../letters-page/letterss-page-container.jsx | 16 +++ src/components/letters/index.js | 0 src/components/letters/letters.jsx | 0 src/components/letters/letters.module.css | 0 src/components/link/index.js | 0 src/components/link/link.jsx | 0 src/components/link/link.module.css | 0 src/components/list/index.js | 0 src/components/list/list.jsx | 0 src/components/list/list.module.css | 0 src/components/nav/index.js | 1 + src/components/nav/nav.jsx | 45 +++++++ src/components/profile-image/index.js | 0 .../profile-image/profile-image.jsx | 0 .../profile-image/profile-image.module.css | 16 +++ src/components/profile-name/index.js | 0 src/components/profile-name/profile-name.jsx | 0 src/components/search/cross-icon.svg | 3 + src/components/search/index.js | 0 src/components/search/search.jsx | 0 src/components/show-letter-context/index.js | 0 .../show-letter-context.jsx | 0 src/components/sidebar/index.js | 0 src/components/sidebar/sidebar.jsx | 0 src/components/text/index.js | 1 + src/components/text/text.jsx | 0 src/components/text/text.module.css | 5 + src/components/write-button/index.js | 1 + src/components/write-button/write-button.jsx | 10 ++ src/react-utils/index.js | 0 src/react-utils/react-utils.jsx | 0 src/utils/countdown-latch.js | 0 src/utils/generators/index.js | 0 src/utils/generators/letter-generator.js | 14 ++ src/utils/generators/markov-chain.js | 63 +++++++++ src/utils/generators/random.js | 15 +++ src/utils/generators/text-generator.js | 26 ++++ src/utils/generators/user-generator.js | 31 +++++ src/utils/index.js | 0 src/utils/utils.js | 0 102 files changed, 472 insertions(+), 64 deletions(-) delete mode 100644 src/app/app.css delete mode 100644 src/app/app.jsx delete mode 100644 src/app/app.test.jsx delete mode 100644 src/app/index.js create mode 100644 src/components/actions-box/actions-box.jsx create mode 100644 src/components/actions-box/actions-box.module.css create mode 100644 src/components/actions-box/actions/action.jsx create mode 100644 src/components/actions-box/actions/action.module.css create mode 100644 src/components/actions-box/actions/delete-letters-action/delete-letters.jsx create mode 100644 src/components/actions-box/actions/delete-letters-action/index.js create mode 100644 src/components/actions-box/actions/index.js create mode 100644 src/components/actions-box/actions/read-letters-action/index.js create mode 100644 src/components/actions-box/actions/read-letters-action/read-letters.jsx create mode 100644 src/components/actions-box/index.js create mode 100644 src/components/animation-wrapper/animation-wrapper.jsx create mode 100644 src/components/animation-wrapper/index.js create mode 100644 src/components/app/app.jsx create mode 100644 src/components/app/index.js create mode 100644 src/components/button/button.jsx create mode 100644 src/components/button/button.module.css create mode 100644 src/components/button/index.js create mode 100644 src/components/checkbox/checkbox.jsx create mode 100644 src/components/checkbox/checkbox.module.css create mode 100644 src/components/checkbox/checkbox.svg create mode 100644 src/components/checkbox/index.js create mode 100644 src/components/content/content.jsx create mode 100644 src/components/content/index.js create mode 100644 src/components/date/date.jsx create mode 100644 src/components/date/date.module.css create mode 100644 src/components/date/index.js create mode 100644 src/components/footer/footer.jsx create mode 100644 src/components/footer/footer.module.css create mode 100644 src/components/footer/index.js create mode 100644 src/components/header/hamburger.svg create mode 100644 src/components/header/header.jsx create mode 100644 src/components/header/image.svg create mode 100644 src/components/header/index.js create mode 100644 src/components/header/mail-yandex.svg create mode 100644 src/components/letter-window/index.js create mode 100644 src/components/letter-window/letter-window.jsx create mode 100644 src/components/letter-window/letter-window.module.css create mode 100644 src/components/letter/deleted/deleted-letter.jsx create mode 100644 src/components/letter/deleted/deleted-letter.module.css create mode 100644 src/components/letter/deleted/index.js create mode 100644 src/components/letter/index.js create mode 100644 src/components/letter/letter.jsx create mode 100644 src/components/letter/letter.module.css create mode 100644 src/components/letter/new/index.js create mode 100644 src/components/letter/new/new-letter.jsx create mode 100644 src/components/letter/new/new-letter.module.css create mode 100644 src/components/letter/unread-flag/index.js create mode 100644 src/components/letter/unread-flag/unread-flag.jsx create mode 100644 src/components/letter/unread-flag/unread-flag.module.css create mode 100644 src/components/letters-context/index.js create mode 100644 src/components/letters-context/letters-context.jsx create mode 100644 src/components/letters-page/index.js create mode 100644 src/components/letters-page/letters-page-component/index.js create mode 100644 src/components/letters-page/letters-page-component/letters-list/index.js create mode 100644 src/components/letters-page/letters-page-component/letters-list/letters-list.jsx create mode 100644 src/components/letters-page/letters-page-component/letters-list/letters-list.module.css create mode 100644 src/components/letters-page/letters-page-component/letters-page-component.jsx create mode 100644 src/components/letters-page/letters-page-component/letters-page.module.css create mode 100644 src/components/letters-page/letterss-page-container.jsx create mode 100644 src/components/letters/index.js create mode 100644 src/components/letters/letters.jsx create mode 100644 src/components/letters/letters.module.css create mode 100644 src/components/link/index.js create mode 100644 src/components/link/link.jsx create mode 100644 src/components/link/link.module.css create mode 100644 src/components/list/index.js create mode 100644 src/components/list/list.jsx create mode 100644 src/components/list/list.module.css create mode 100644 src/components/nav/index.js create mode 100644 src/components/nav/nav.jsx create mode 100644 src/components/profile-image/index.js create mode 100644 src/components/profile-image/profile-image.jsx create mode 100644 src/components/profile-image/profile-image.module.css create mode 100644 src/components/profile-name/index.js create mode 100644 src/components/profile-name/profile-name.jsx create mode 100644 src/components/search/cross-icon.svg create mode 100644 src/components/search/index.js create mode 100644 src/components/search/search.jsx create mode 100644 src/components/show-letter-context/index.js create mode 100644 src/components/show-letter-context/show-letter-context.jsx create mode 100644 src/components/sidebar/index.js create mode 100644 src/components/sidebar/sidebar.jsx create mode 100644 src/components/text/index.js create mode 100644 src/components/text/text.jsx create mode 100644 src/components/text/text.module.css create mode 100644 src/components/write-button/index.js create mode 100644 src/components/write-button/write-button.jsx create mode 100644 src/react-utils/index.js create mode 100644 src/react-utils/react-utils.jsx create mode 100644 src/utils/countdown-latch.js create mode 100644 src/utils/generators/index.js create mode 100644 src/utils/generators/letter-generator.js create mode 100644 src/utils/generators/markov-chain.js create mode 100644 src/utils/generators/random.js create mode 100644 src/utils/generators/text-generator.js create mode 100644 src/utils/generators/user-generator.js create mode 100644 src/utils/index.js create mode 100644 src/utils/utils.js diff --git a/src/app/app.css b/src/app/app.css deleted file mode 100644 index 1c4d511..0000000 --- a/src/app/app.css +++ /dev/null @@ -1,27 +0,0 @@ -.app { - text-align: center; -} - -.app-header { - display: flex; - min-height: 100vh; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: #282c34; - color: #fff; - font-size: calc(10px + 2vmin); -} - -.app-link { - color: #61dafb; -} - -@keyframes app-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/app/app.jsx b/src/app/app.jsx deleted file mode 100644 index f759eed..0000000 --- a/src/app/app.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { Component } from 'react'; - -import './app.css'; - -export class App extends Component { - render() { - return ( -
-
-

- Edit src/app/app.jsx and save to reload. -

- - Learn React - -
-
- ); - } -} diff --git a/src/app/app.test.jsx b/src/app/app.test.jsx deleted file mode 100644 index 81be6fa..0000000 --- a/src/app/app.test.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import { App } from './app'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); -}); diff --git a/src/app/index.js b/src/app/index.js deleted file mode 100644 index 665a3d9..0000000 --- a/src/app/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './app'; diff --git a/src/components/actions-box/actions-box.jsx b/src/components/actions-box/actions-box.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/actions-box.module.css b/src/components/actions-box/actions-box.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/actions/action.jsx b/src/components/actions-box/actions/action.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/actions/action.module.css b/src/components/actions-box/actions/action.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx b/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx new file mode 100644 index 0000000..6c70088 --- /dev/null +++ b/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx @@ -0,0 +1,14 @@ +import React, { useContext } from 'react'; +import { Action } from '../action'; +import { LettersContext } from '../../../contexts/lettersContext'; + +export const DeleteLetterAction = props => { + const { letters, changeLetter } = useContext(LettersContext); + + const handleAction = () => { + const ids = letters.filter(letter => letter.checked).map(letter => letter.id); + ids.forEach(id => changeLetter(id, () => ({ deleting: true }))); + }; + + return ; +}; diff --git a/src/components/actions-box/actions/delete-letters-action/index.js b/src/components/actions-box/actions/delete-letters-action/index.js new file mode 100644 index 0000000..59c3373 --- /dev/null +++ b/src/components/actions-box/actions/delete-letters-action/index.js @@ -0,0 +1 @@ +export * from './delete-letter'; diff --git a/src/components/actions-box/actions/index.js b/src/components/actions-box/actions/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/actions/read-letters-action/index.js b/src/components/actions-box/actions/read-letters-action/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/actions/read-letters-action/read-letters.jsx b/src/components/actions-box/actions/read-letters-action/read-letters.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/actions-box/index.js b/src/components/actions-box/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/animation-wrapper/animation-wrapper.jsx b/src/components/animation-wrapper/animation-wrapper.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/animation-wrapper/index.js b/src/components/animation-wrapper/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/app/app.jsx b/src/components/app/app.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/app/index.js b/src/components/app/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/button/button.jsx b/src/components/button/button.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/button/button.module.css b/src/components/button/button.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/button/index.js b/src/components/button/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/checkbox/checkbox.jsx b/src/components/checkbox/checkbox.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/checkbox/checkbox.module.css b/src/components/checkbox/checkbox.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/checkbox/checkbox.svg b/src/components/checkbox/checkbox.svg new file mode 100644 index 0000000..df2781b --- /dev/null +++ b/src/components/checkbox/checkbox.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/checkbox/index.js b/src/components/checkbox/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/content/content.jsx b/src/components/content/content.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/content/index.js b/src/components/content/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/date/date.jsx b/src/components/date/date.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/date/date.module.css b/src/components/date/date.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/date/index.js b/src/components/date/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/footer/footer.jsx b/src/components/footer/footer.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/footer/footer.module.css b/src/components/footer/footer.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/footer/index.js b/src/components/footer/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/header/hamburger.svg b/src/components/header/hamburger.svg new file mode 100644 index 0000000..0f4bd63 --- /dev/null +++ b/src/components/header/hamburger.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/header/header.jsx b/src/components/header/header.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/header/image.svg b/src/components/header/image.svg new file mode 100644 index 0000000..03c806b --- /dev/null +++ b/src/components/header/image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/header/index.js b/src/components/header/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/header/mail-yandex.svg b/src/components/header/mail-yandex.svg new file mode 100644 index 0000000..c67cd1a --- /dev/null +++ b/src/components/header/mail-yandex.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/letter-window/index.js b/src/components/letter-window/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter-window/letter-window.jsx b/src/components/letter-window/letter-window.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter-window/letter-window.module.css b/src/components/letter-window/letter-window.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/deleted/deleted-letter.jsx b/src/components/letter/deleted/deleted-letter.jsx new file mode 100644 index 0000000..039f4c6 --- /dev/null +++ b/src/components/letter/deleted/deleted-letter.jsx @@ -0,0 +1,21 @@ +import React, { useContext } from 'react'; +import { LettersContext } from '../../contexts/lettersContext'; +import { Letter } from '../letter'; +import styles from './deleting-letter.module.css'; + +export const DeletingLetter = props => { + const { changeLetter, deleteLetters } = useContext(LettersContext); + + const handleAnimationEnd = () => { + deleteLetters([props.id]); + }; + + return ( +
+ +
+ ); +}; diff --git a/src/components/letter/deleted/deleted-letter.module.css b/src/components/letter/deleted/deleted-letter.module.css new file mode 100644 index 0000000..953fc7f --- /dev/null +++ b/src/components/letter/deleted/deleted-letter.module.css @@ -0,0 +1,20 @@ +.deletingLetter { + animation: delete-letter-animation 1s cubic-bezier(0.5, 0, 0, 0); +} + +@keyframes delete-letter-animation { + 0% { + height: 41px; + opacity: 1; + } + + 30% { + height: 41px; + opacity: 0; + } + + 100% { + height: 0; + opacity: 0; + } +} diff --git a/src/components/letter/deleted/index.js b/src/components/letter/deleted/index.js new file mode 100644 index 0000000..3c2b91b --- /dev/null +++ b/src/components/letter/deleted/index.js @@ -0,0 +1 @@ +export * from './deletingLetter'; diff --git a/src/components/letter/index.js b/src/components/letter/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/letter.jsx b/src/components/letter/letter.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/letter.module.css b/src/components/letter/letter.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/new/index.js b/src/components/letter/new/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/new/new-letter.jsx b/src/components/letter/new/new-letter.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/new/new-letter.module.css b/src/components/letter/new/new-letter.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/unread-flag/index.js b/src/components/letter/unread-flag/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/unread-flag/unread-flag.jsx b/src/components/letter/unread-flag/unread-flag.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letter/unread-flag/unread-flag.module.css b/src/components/letter/unread-flag/unread-flag.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters-context/index.js b/src/components/letters-context/index.js new file mode 100644 index 0000000..2039439 --- /dev/null +++ b/src/components/letters-context/index.js @@ -0,0 +1 @@ +export * from './lettersContext'; diff --git a/src/components/letters-context/letters-context.jsx b/src/components/letters-context/letters-context.jsx new file mode 100644 index 0000000..281e480 --- /dev/null +++ b/src/components/letters-context/letters-context.jsx @@ -0,0 +1,120 @@ +import React, { createContext, useCallback, useMemo } from 'react'; +import { useSessionStorage } from '../../react-utils'; +import { generateLetter } from '../../utils/generators'; + +export const LettersContext = createContext(null); + +export const LettersProvider = ({ children }) => { + const [state, setState] = useSessionStorage('letters', { counter: 0, letters: [] }); + + const onPage = useMemo(() => 1500, []); + + const merge = useCallback((obj1, obj2) => Object.assign({}, obj1, obj2), []); + + const createLetter = useCallback( + (letterContent, id) => ({ + id, + state: 1, // 1 - just received, 2 - usual, 3 - gonna be removed + unread: true, + checked: false, + content: letterContent + }), + [state] + ); + + const getLetters = useCallback(() => state.letters.slice(0, onPage), [state]); + + const getLetterByID = useCallback(id => getLetters().find(letter => letter.id === id), [ + getLetters + ]); + + const setStateSlice = useCallback( + (counterFunc, lettersFunc) => + setState(prevState => ({ + counter: counterFunc(prevState.counter), + letters: lettersFunc(prevState.letters.slice(0, onPage)).concat( + prevState.letters.slice(onPage) + ) + })), + [setState] + ); + + const setLettersSlice = useCallback( + lettersFunc => setStateSlice(counter => counter, lettersFunc), + [setStateSlice] + ); + + const filterLetters = useCallback( + predicate => setLettersSlice(letters => letters.filter(predicate)), + [setLettersSlice] + ); + + const mapLetters = useCallback( + func => setLettersSlice(letters => letters.map(letter => merge(letter, func(letter)))), + [setLettersSlice] + ); + + const addLetter = useCallback( + letterContent => + setStateSlice( + counter => counter + 1, + letters => [createLetter(letterContent, state.counter), ...letters] + ), + [setLettersSlice, createLetter] + ); + + const generateRandomLetters = useCallback( + count => + setState(() => { + const newState = { counter: 0, letters: [] }; + for (let i = 0; i < count; i++) { + const newLetter = createLetter(generateLetter(), newState.counter); + newLetter.state = 2; + newState.letters.unshift(newLetter); + newState.counter++; + } + return newState; + }), + [setState, createLetter, generateLetter] + ); + + const deleteLetterByID = useCallback(id => filterLetters(letter => letter.id !== id), [ + filterLetters + ]); + + const deleteLettersByIDs = useCallback(ids => filterLetters(letter => !ids.includes(letter.id)), [ + filterLetters + ]); + + const changeLetterByID = useCallback( + (id, func) => mapLetters(letter => (letter.id === id ? func(letter) : letter)), + [mapLetters] + ); + + const changeLetters = useCallback(func => mapLetters(func), [mapLetters]); + + const contextValue = useMemo( + () => ({ + getLetters, + getLetterByID, + addLetter, + deleteLetterByID, + deleteLettersByIDs, + changeLetterByID, + changeLetters, + generateRandomLetters + }), + [ + getLetters, + getLetterByID, + addLetter, + deleteLetterByID, + deleteLettersByIDs, + changeLetterByID, + changeLetters, + generateRandomLetters + ] + ); + + return {children}; +}; diff --git a/src/components/letters-page/index.js b/src/components/letters-page/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters-page/letters-page-component/index.js b/src/components/letters-page/letters-page-component/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters-page/letters-page-component/letters-list/index.js b/src/components/letters-page/letters-page-component/letters-list/index.js new file mode 100644 index 0000000..f619923 --- /dev/null +++ b/src/components/letters-page/letters-page-component/letters-list/index.js @@ -0,0 +1 @@ +export * from './letters-list'; diff --git a/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx b/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx new file mode 100644 index 0000000..9e16da5 --- /dev/null +++ b/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx @@ -0,0 +1,12 @@ +import React, { Component } from 'react'; + +export class LettersList extends Component { + render() { + return ( +
+
    +
+
+ ); + } +} diff --git a/src/components/letters-page/letters-page-component/letters-list/letters-list.module.css b/src/components/letters-page/letters-page-component/letters-list/letters-list.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters-page/letters-page-component/letters-page-component.jsx b/src/components/letters-page/letters-page-component/letters-page-component.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters-page/letters-page-component/letters-page.module.css b/src/components/letters-page/letters-page-component/letters-page.module.css new file mode 100644 index 0000000..4574bdc --- /dev/null +++ b/src/components/letters-page/letters-page-component/letters-page.module.css @@ -0,0 +1,18 @@ +.letters_page { + display: flex; + min-width: 600px; + flex-direction: column; + + background-color: #fff; + + border-radius: 3px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.34); +} + +.actions_box { + border-bottom: 1px solid #e2e2e2; +} + +.footer { + border-top: 1px solid #e2e2e2; +} diff --git a/src/components/letters-page/letterss-page-container.jsx b/src/components/letters-page/letterss-page-container.jsx new file mode 100644 index 0000000..86dbd3d --- /dev/null +++ b/src/components/letters-page/letterss-page-container.jsx @@ -0,0 +1,16 @@ +import React, { Component } from 'react'; +import { LettersPageComponent } from './letters-page-component/letters-page-component'; + +export class LettersPage extends Component { + constructor(props) { + super(props); + this.state = { + letters: [] + }; + } + render() { + return ( + + ); + } +} diff --git a/src/components/letters/index.js b/src/components/letters/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters/letters.jsx b/src/components/letters/letters.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/letters/letters.module.css b/src/components/letters/letters.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/link/index.js b/src/components/link/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/link/link.jsx b/src/components/link/link.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/link/link.module.css b/src/components/link/link.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/list/index.js b/src/components/list/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/list/list.jsx b/src/components/list/list.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/list/list.module.css b/src/components/list/list.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/nav/index.js b/src/components/nav/index.js new file mode 100644 index 0000000..8267df7 --- /dev/null +++ b/src/components/nav/index.js @@ -0,0 +1 @@ +export * from './menu'; diff --git a/src/components/nav/nav.jsx b/src/components/nav/nav.jsx new file mode 100644 index 0000000..c950f0f --- /dev/null +++ b/src/components/nav/nav.jsx @@ -0,0 +1,45 @@ +import React, { Component } from 'react'; +import classNames from 'classnames'; + +import styles from './menu.module.css'; + +export class Menu extends Component { + render() { + return ( + + ); + } +} diff --git a/src/components/profile-image/index.js b/src/components/profile-image/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/profile-image/profile-image.jsx b/src/components/profile-image/profile-image.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/profile-image/profile-image.module.css b/src/components/profile-image/profile-image.module.css new file mode 100644 index 0000000..06d98ba --- /dev/null +++ b/src/components/profile-image/profile-image.module.css @@ -0,0 +1,16 @@ +.profileImage { + display: flex; + + width: 30px; + height: 30px; + + align-items: center; + justify-content: center; + + background-color: #9b9b9b; + background-size: contain; + border-radius: 50%; + + color: #fff; + font: 500 16px/35px 'Yandex Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} diff --git a/src/components/profile-name/index.js b/src/components/profile-name/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/profile-name/profile-name.jsx b/src/components/profile-name/profile-name.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/search/cross-icon.svg b/src/components/search/cross-icon.svg new file mode 100644 index 0000000..826e56a --- /dev/null +++ b/src/components/search/cross-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/search/index.js b/src/components/search/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/search/search.jsx b/src/components/search/search.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/show-letter-context/index.js b/src/components/show-letter-context/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/show-letter-context/show-letter-context.jsx b/src/components/show-letter-context/show-letter-context.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/sidebar/index.js b/src/components/sidebar/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/sidebar/sidebar.jsx b/src/components/sidebar/sidebar.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/text/index.js b/src/components/text/index.js new file mode 100644 index 0000000..53aaa9f --- /dev/null +++ b/src/components/text/index.js @@ -0,0 +1 @@ +export * from './with-overflow-ellipsis'; diff --git a/src/components/text/text.jsx b/src/components/text/text.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/text/text.module.css b/src/components/text/text.module.css new file mode 100644 index 0000000..c3f03c3 --- /dev/null +++ b/src/components/text/text.module.css @@ -0,0 +1,5 @@ +.withOverflowEllipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/src/components/write-button/index.js b/src/components/write-button/index.js new file mode 100644 index 0000000..49e489c --- /dev/null +++ b/src/components/write-button/index.js @@ -0,0 +1 @@ +export * from './main-button'; diff --git a/src/components/write-button/write-button.jsx b/src/components/write-button/write-button.jsx new file mode 100644 index 0000000..300d141 --- /dev/null +++ b/src/components/write-button/write-button.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { Button } from '../button'; + +import styles from './main-button.module.css'; + +export const MainButton = props => { + return ; +}; diff --git a/src/react-utils/index.js b/src/react-utils/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/react-utils/react-utils.jsx b/src/react-utils/react-utils.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/countdown-latch.js b/src/utils/countdown-latch.js new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/generators/index.js b/src/utils/generators/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/generators/letter-generator.js b/src/utils/generators/letter-generator.js new file mode 100644 index 0000000..ab503b8 --- /dev/null +++ b/src/utils/generators/letter-generator.js @@ -0,0 +1,14 @@ +import generateText from './text-generator'; +import random from './random'; +import generateUser from './user-generator'; + +export default function generateLetter() { + return { + user: generateUser(), + content: { + subject: generateText(1, 1), + body: generateText(random.int(4, 5), random.int(5, 10)) + }, + date: new Date() + }; +} diff --git a/src/utils/generators/markov-chain.js b/src/utils/generators/markov-chain.js new file mode 100644 index 0000000..82ef56b --- /dev/null +++ b/src/utils/generators/markov-chain.js @@ -0,0 +1,63 @@ +function generateChain(text) { + const sentences = text.split(/(\.+)/).filter(s => s.length > 0 && s !== '.'); + + const graph = []; + + for (const sentence of sentences.values()) { + const addSymbol = (from, to) => { + if (graph[from] == null) graph[from] = []; + + if (graph[from][to] == null) graph[from][to] = 1; + else graph[from][to]++; + }; + + const symbols = sentence.split(/\s+/).filter(s => s.length > 0); + if (symbols.length > 0) { + addSymbol('markov-start', symbols[0]); + for (let cur = 0, next = 1; next < symbols.length; cur++, next++) { + addSymbol(symbols[cur], symbols[next]); + } + addSymbol(symbols.slice(-1), 'markov-end'); + } + } + + for (const from of graph.keys()) { + let sum = 0; + for (const to of graph[from].keys()) sum += graph[from][to]; + for (const to of graph[from].keys()) graph[from][to] /= sum; + } + + return graph; +} + +export default function createMarkovChain(sampleText) { + const chain = generateChain(sampleText); + + return { + sequence() { + const choose = probs => { + const prob = Math.random(); + + let sum = 0; + for (const to of probs.keys()) { + if (prob < sum + probs[to]) return to; + sum += probs[to]; + } + return 'markov-end'; + }; + + const seq = ['markov-start']; + + let curSymbol = seq[0]; + while (curSymbol !== 'markov-end') { + curSymbol = choose(chain[curSymbol]); + seq.push(curSymbol); + } + + seq.shift(); + seq.pop(); + + return seq; + } + }; +} diff --git a/src/utils/generators/random.js b/src/utils/generators/random.js new file mode 100644 index 0000000..ada9266 --- /dev/null +++ b/src/utils/generators/random.js @@ -0,0 +1,15 @@ +const random = { + int(from, to) { + return Math.floor(Math.random() * (to - from)) + from; + }, + + boolean() { + return this.int(0, 2) === 0; + }, + + element(array) { + return array.length === 0 ? undefined : array[this.int(0, array.length)]; + } +}; + +export default random; diff --git a/src/utils/generators/text-generator.js b/src/utils/generators/text-generator.js new file mode 100644 index 0000000..69f9872 --- /dev/null +++ b/src/utils/generators/text-generator.js @@ -0,0 +1,26 @@ +import createMarkovChain from './markov-chain'; + +const sampleText = `Согласно ежегодному исследованию StackOverflow, самая популярная профессия среди пользователей сервиса в 2017 году — это Web developer. Именно в эту категорию входят все фронтенд-разработчики. +Если зайти на первый попавшийся сайт по поиску работы, например, на hh.ru, создастся впечатление, что фронтенд-разработчик — это специалист-хамелеон. +Начинается все с путаницы в названиях вакансий: можно встретить и «front-end developer», и «front end разработчик», и «фронтендщик», и «фронтенд девелопер», и «web developer», и «фронтенд-разработчик». Иногда даже можно увидеть какого-нибудь «веб-верстальщика» с требованиями под фулстак-разработчика. Реакция на это одна: WTF?! +Беда в том, что часть работодателей не отличают (или не хотят отличать) верстальщика от фронтенд-разработчика, — это понятно по описанию вакансий. Разберемся, какие умения отделяют фронтенд-разработчика от «верстака» (верстальщики, не обижайтесь, вы тоже хорошие). +Верстальщик — боец узкого фронта. Его задача — сверстать полученный от дизайнера макет, используя HTML+CSS. Он, возможно, немного умеет в JavaScript, но чаще ограничивается умением прикрутить какой-нибудь плагин jQuery. +Фронтенд-разработчик не просто верстает макеты. Он хорошо знает JavaScript, разбирается во фреймворках и библиотеках (и активно юзает часть из них), понимает, что находится «под капотом» на серверной стороне. Его не пугают препроцессоры и сборщики LESS, SASS, GRUNT, GULP, он умеет работать с DOM, API, SVG-объектами, AJAX и CORS, может составлять SQL-запросы и копаться в данных. Получается сборная солянка навыков, к которым добавляется понимание принципов UI/UX-проектирования, адаптивной и отзывчивой верстки, кросс-браузерности и кросс-платформенности, а иногда и навыков мобильной разработки. +Фронтендщик в обязательном порядке умеет работать с контролем версий (Git, GitHub, CVS и т. д.), использовать графические редакторы, «играть» с шаблонами различных CMS. +Еще крайне желательно знать английский язык, чтобы не переводить спецификацию в Гугл-переводчике, уметь работать в команде, иногда мультиязычной, разбираться в веб-шрифтах, ну и понимать тестировщиков и сам процесс тестирования. `; + +const markovChain = createMarkovChain(sampleText); + +export default function generateText(paragraphsInText, sentencesInParagraph) { + const paragraphs = []; + for (let p = 0; p < paragraphsInText; p++) { + const sentences = []; + for (let s = 0; s < sentencesInParagraph; s++) { + const sentence = markovChain.sequence().join(' '); + sentences[s] = `${sentence[0].toUpperCase() + sentence.substr(1)}.`; + } + paragraphs[p] = sentences.join(' '); + } + + return paragraphs.join(' '); +} diff --git a/src/utils/generators/user-generator.js b/src/utils/generators/user-generator.js new file mode 100644 index 0000000..cddcbd2 --- /dev/null +++ b/src/utils/generators/user-generator.js @@ -0,0 +1,31 @@ +import random from './random'; + +const names = { + ru: { + firstNames: [], + lastNames: [] + }, + en: { + firstNames: [], + lastNames: [] + } +}; + +function randomName() { + const lang = random.boolean() ? 'ru' : 'en'; + const firstName = random.element(names[lang].firstNames); + const lastName = random.element(names[lang].lastNames); + + return `${firstName} ${lastName}`; +} + +function imageOrNull() { + return random.boolean() ? 'https://thispersondoesnotexist.com/image' : null; +} + +export default function generateUser() { + return { + name: randomName(), + imageUrl: imageOrNull() + }; +} diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/utils.js b/src/utils/utils.js new file mode 100644 index 0000000..e69de29 From b68b1f9e58b527c3ee69c8de0bc401dc2c4f687c Mon Sep 17 00:00:00 2001 From: Nikita Voloshin Date: Thu, 6 Jun 2019 14:53:52 +0300 Subject: [PATCH 2/2] Implement Task 5 --- now.json | 3 +- package-lock.json | 219 ++++++++++++++++-- package.json | 5 +- public/favicon.ico | Bin 3870 -> 100748 bytes public/index.html | 3 +- src/components/actions-box/actions-box.jsx | 50 ++++ .../actions-box/actions-box.module.css | 35 +++ src/components/actions-box/actions/action.jsx | 11 + .../actions-box/actions/action.module.css | 17 ++ .../delete-letters-action/delete-letters.jsx | 19 +- .../actions/delete-letters-action/index.js | 2 +- src/components/actions-box/actions/index.js | 1 + .../actions/read-letters-action/index.js | 1 + .../read-letters-action/read-letters.jsx | 21 ++ src/components/actions-box/index.js | 1 + .../animation-wrapper/animation-wrapper.jsx | 16 ++ src/components/animation-wrapper/index.js | 1 + src/components/app/app.jsx | 26 +++ src/components/app/app.module.css | 37 +++ src/components/app/index.js | 1 + src/components/button/button.jsx | 17 ++ src/components/button/button.module.css | 7 + src/components/button/index.js | 1 + src/components/checkbox/checkbox.jsx | 22 ++ src/components/checkbox/checkbox.module.css | 68 ++++++ src/components/checkbox/index.js | 1 + src/components/content/content.jsx | 33 +++ src/components/content/content.module.css | 28 +++ src/components/content/index.js | 1 + src/components/date/date.jsx | 11 + src/components/date/date.module.css | 4 + src/components/date/index.js | 1 + src/components/footer/footer.jsx | 20 ++ src/components/footer/footer.module.css | 14 ++ src/components/footer/index.js | 1 + src/components/header/hamburger.svg | 6 +- src/components/header/header.jsx | 25 ++ src/components/header/header.module.css | 25 ++ src/components/header/index.js | 1 + src/components/header/mail-yandex.svg | 4 +- src/components/letter-window/index.js | 1 + .../letter-window/letter-window.jsx | 37 +++ .../letter-window/letter-window.module.css | 56 +++++ .../letter/deleted/deleted-letter.jsx | 26 +-- .../letter/deleted/deleted-letter.module.css | 6 +- src/components/letter/deleted/index.js | 2 +- src/components/letter/index.js | 1 + src/components/letter/letter.jsx | 50 ++++ src/components/letter/letter.module.css | 76 ++++++ src/components/letter/new/index.js | 1 + src/components/letter/new/new-letter.jsx | 17 ++ .../letter/new/new-letter.module.css | 20 ++ src/components/letter/unread-flag/index.js | 1 + .../letter/unread-flag/unread-flag.jsx | 9 + .../letter/unread-flag/unread-flag.module.css | 11 + src/components/letters-context/index.js | 2 +- .../letters-context/letters-context.jsx | 159 +++++-------- src/components/letters-page/index.js | 1 + .../letters-page-component/index.js | 1 + .../letters-list/letters-list.jsx | 13 +- .../letters-list/letters-list.module.css | 10 + .../letters-page-component.jsx | 19 ++ .../letters-page.module.css | 10 + .../letters-page/letterss-page-container.jsx | 32 ++- src/components/letters/index.js | 1 + src/components/letters/letters.jsx | 65 ++++++ src/components/link/index.js | 1 + src/components/link/link.jsx | 17 ++ src/components/link/link.module.css | 3 + src/components/list/index.js | 1 + src/components/list/list.jsx | 14 ++ src/components/list/list.module.css | 6 + src/components/nav/index.js | 2 +- src/components/nav/nav.jsx | 81 +++---- src/components/nav/nav.module.css | 33 +++ src/components/profile-image/index.js | 1 + .../profile-image/profile-image.jsx | 32 +++ src/components/profile-name/index.js | 1 + src/components/profile-name/profile-name.jsx | 7 + src/components/search/cross-icon.svg | 2 +- src/components/search/index.js | 1 + src/components/search/search.jsx | 19 ++ src/components/search/search.module.css | 26 +++ src/components/show-letter-context/index.js | 1 + .../show-letter-context.jsx | 20 ++ src/components/sidebar/index.js | 1 + src/components/sidebar/sidebar.jsx | 16 ++ src/components/sidebar/sidebar.module.css | 14 ++ src/components/text/index.js | 2 +- src/components/text/text.jsx | 11 + src/components/text/text.module.css | 1 + src/components/write-button/index.js | 2 +- src/components/write-button/write-button.jsx | 18 +- .../write-button/write-button.module.css | 9 + src/fonts/HelveticaNeue-Bold.ttf | Bin 0 -> 524452 bytes src/fonts/HelveticaNeue-Bold.woff | Bin 0 -> 205876 bytes src/fonts/HelveticaNeue-Medium.ttf | Bin 0 -> 254728 bytes src/fonts/HelveticaNeue-Medium.woff | Bin 0 -> 104100 bytes src/fonts/HelveticaNeue.ttf | Bin 0 -> 568204 bytes src/fonts/HelveticaNeue.woff | Bin 0 -> 216580 bytes src/fonts/YandexSansText-Bold.ttf | Bin 0 -> 183648 bytes src/fonts/YandexSansText-Bold.woff | Bin 0 -> 68028 bytes src/fonts/YandexSansText-Light.ttf | Bin 0 -> 184564 bytes src/fonts/YandexSansText-Light.woff | Bin 0 -> 69188 bytes src/fonts/YandexSansText-Medium.ttf | Bin 0 -> 184564 bytes src/fonts/YandexSansText-Medium.woff | Bin 0 -> 69060 bytes src/fonts/YandexSansText-Regular.ttf | Bin 0 -> 183592 bytes src/fonts/YandexSansText-Regular.woff | Bin 0 -> 67864 bytes src/index.css | 46 +++- src/index.jsx | 10 +- src/react-utils/index.js | 1 + src/react-utils/react-utils.jsx | 20 ++ src/utils/countdown-latch.js | 11 + src/utils/generators/index.js | 1 + src/utils/generators/letter-generator.js | 4 +- src/utils/generators/markov-chain.js | 8 +- src/utils/generators/user-generator.js | 21 +- src/utils/index.js | 3 + src/utils/utils.js | 33 +++ 119 files changed, 1658 insertions(+), 228 deletions(-) create mode 100644 src/components/app/app.module.css create mode 100644 src/components/content/content.module.css create mode 100644 src/components/header/header.module.css create mode 100644 src/components/nav/nav.module.css create mode 100644 src/components/search/search.module.css create mode 100644 src/components/sidebar/sidebar.module.css create mode 100644 src/components/write-button/write-button.module.css create mode 100644 src/fonts/HelveticaNeue-Bold.ttf create mode 100644 src/fonts/HelveticaNeue-Bold.woff create mode 100644 src/fonts/HelveticaNeue-Medium.ttf create mode 100644 src/fonts/HelveticaNeue-Medium.woff create mode 100644 src/fonts/HelveticaNeue.ttf create mode 100644 src/fonts/HelveticaNeue.woff create mode 100644 src/fonts/YandexSansText-Bold.ttf create mode 100644 src/fonts/YandexSansText-Bold.woff create mode 100644 src/fonts/YandexSansText-Light.ttf create mode 100644 src/fonts/YandexSansText-Light.woff create mode 100644 src/fonts/YandexSansText-Medium.ttf create mode 100644 src/fonts/YandexSansText-Medium.woff create mode 100644 src/fonts/YandexSansText-Regular.ttf create mode 100644 src/fonts/YandexSansText-Regular.woff diff --git a/now.json b/now.json index 04857b4..48ebe7e 100644 --- a/now.json +++ b/now.json @@ -23,6 +23,7 @@ { "src": "/manifest.json", "dest": "/manifest.json" }, { "src": "/asset-manifest.json", "dest": "/asset-manifest.json" }, { "src": "/precache-manifest.(.*)", "dest": "/precache-manifest.$1" }, - { "src": "/(.*)", "headers": { "cache-control": "s-maxage=0" }, "dest": "/index.html" } + { "src": "/", "headers": { "cache-control": "s-maxage=0" }, "dest": "/index.html" }, + { "src": "/(.*)", "status": 301, "headers": { "Location": "/" }} ] } diff --git a/package-lock.json b/package-lock.json index 24c1378..34501a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -919,7 +919,6 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.1.tgz", "integrity": "sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==", - "dev": true, "requires": { "regenerator-runtime": "^0.12.0" }, @@ -927,8 +926,7 @@ "regenerator-runtime": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", - "dev": true + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" } } }, @@ -2000,8 +1998,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { "version": "0.2.4", @@ -3254,6 +3251,11 @@ } } }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, "character-entities": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", @@ -3997,6 +3999,11 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", @@ -4457,6 +4464,15 @@ "sha.js": "^2.4.8" } }, + "create-react-context": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", + "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", + "requires": { + "fbjs": "^0.8.0", + "gud": "^1.0.0" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -5402,6 +5418,14 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -6478,6 +6502,35 @@ "bser": "^2.0.0" } }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + } + } + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -7918,6 +7971,11 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, "gzip-size": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz", @@ -8135,6 +8193,19 @@ "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, + "history": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz", + "integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^2.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^0.4.0" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -8152,6 +8223,11 @@ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", "dev": true }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -8677,7 +8753,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -9229,8 +9304,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-supported-regexp-flag": { "version": "1.0.1", @@ -9319,6 +9393,15 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -11095,6 +11178,15 @@ "lower-case": "^1.1.1" } }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node-forge": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", @@ -13447,6 +13539,65 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-router": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.0.tgz", + "integrity": "sha512-6EQDakGdLG/it2x9EaCt9ZpEEPxnd0OCLBHQ1AcITAAx7nCnyvnzf76jKWG1s2/oJ7SSviUgfWHofdYljFexsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.2.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } + } + } + }, + "react-router-dom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.0.tgz", + "integrity": "sha512-wSpja5g9kh5dIteZT3tUoggjnsa+TPFHSMrpHXMpFsaHhQkm/JNVGh2jiF9Dkh4+duj4MKCkwO6H08u6inZYgQ==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, "react-scripts": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.8.tgz", @@ -13869,6 +14020,19 @@ "util.promisify": "^1.0.0" } }, + "recompose": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", + "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", + "requires": { + "@babel/runtime": "^7.0.0", + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "react-lifecycles-compat": "^3.0.2", + "symbol-observable": "^1.0.4" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -14272,6 +14436,11 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-pathname": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -14376,8 +14545,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sane": { "version": "2.5.2", @@ -14928,8 +15096,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { "version": "1.1.0", @@ -16245,6 +16412,11 @@ "util.promisify": "~1.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", @@ -16505,6 +16677,16 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tiny-invariant": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz", + "integrity": "sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==" + }, + "tiny-warning": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz", + "integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -16724,6 +16906,11 @@ "integrity": "sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==", "dev": true }, + "ua-parser-js": { + "version": "0.7.19", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", + "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + }, "uglify-js": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", @@ -17084,6 +17271,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "value-equal": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -17865,8 +18057,7 @@ "whatwg-fetch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==", - "dev": true + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" }, "whatwg-mimetype": { "version": "2.3.0", diff --git a/package.json b/package.json index 04a3e41..8eacc04 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,11 @@ "version": "0.1.0", "private": true, "dependencies": { + "classnames": "2.2.6", "react": "16.8.5", - "react-dom": "16.8.5" + "react-dom": "16.8.5", + "react-router-dom": "5.0.0", + "recompose": "0.30.0" }, "devDependencies": { "@hellroot/eslint-config": "1.7.1", diff --git a/public/favicon.ico b/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..8fd60d6cbbee771747eb05b1c3cfb5e1a0b9020a 100644 GIT binary patch literal 100748 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 literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/public/index.html b/public/index.html index 9a8ef8f..465746e 100644 --- a/public/index.html +++ b/public/index.html @@ -7,8 +7,7 @@ name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - - React App + Входящие — Яндекс.Почта diff --git a/src/components/actions-box/actions-box.jsx b/src/components/actions-box/actions-box.jsx index e69de29..4e317a4 100644 --- a/src/components/actions-box/actions-box.jsx +++ b/src/components/actions-box/actions-box.jsx @@ -0,0 +1,50 @@ +import React, { useContext } from 'react'; +import classNames from 'classnames'; +import { mem } from '../../react-utils'; + +import { Checkbox } from '../checkbox'; +import { List } from '../list'; +import { Action } from './actions'; +import { DeleteLetterAction } from './actions/delete-letters-action'; +import { ReadLettersAction } from './actions/read-letters-action'; +import { LettersContext } from '../letters-context'; + +import styles from './actions-box.module.css'; + +export const ActionsBox = ({ className, disabled }) => { + const { getLetters, changeLetters } = useContext(LettersContext); + const letters = mem(() => getLetters()).deps(getLetters); + + const handleChange = event => { + const { checked } = event.target; + changeLetters(() => ({ checked })); + }; + + const checkboxChecked = mem( + () => !disabled && letters.length > 0 && letters.every(letter => letter.checked) + ).deps(letters); + + const checkboxEnabled = mem(() => !disabled && letters.length > 0).deps(letters); + + const actionsEnabled = mem(() => !disabled && letters.some(letter => letter.checked)).deps( + letters + ); + + return ( +
+ + + Переслать + + Это спам! + + +
+ ); +}; diff --git a/src/components/actions-box/actions-box.module.css b/src/components/actions-box/actions-box.module.css index e69de29..33afd97 100644 --- a/src/components/actions-box/actions-box.module.css +++ b/src/components/actions-box/actions-box.module.css @@ -0,0 +1,35 @@ +.actions_box { + display: flex; + + align-items: center; + justify-content: flex-start; +} + +.checkbox { + flex: 0 0 auto; + margin: 0 10px 0 20px; +} + +.actionsList { + display: flex; + height: 100%; + flex: 0 0 auto; +} + +.action { + height: 100%; + box-sizing: border-box; + padding: 0 10px; + + color: inherit; + font: 500 13px/37px 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.action:disabled { + color: #ccc; + cursor: initial; +} + +.action:enabled:hover { + background: rgba(0, 0, 0, 0.05); +} diff --git a/src/components/actions-box/actions/action.jsx b/src/components/actions-box/actions/action.jsx index e69de29..854e075 100644 --- a/src/components/actions-box/actions/action.jsx +++ b/src/components/actions-box/actions/action.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { Button } from '../../button'; + +import styles from './action.module.css'; + +export const Action = ({ disabled, handleAction, children }) => ( + +); diff --git a/src/components/actions-box/actions/action.module.css b/src/components/actions-box/actions/action.module.css index e69de29..21fb0ec 100644 --- a/src/components/actions-box/actions/action.module.css +++ b/src/components/actions-box/actions/action.module.css @@ -0,0 +1,17 @@ +.action { + height: 100%; + box-sizing: border-box; + padding: 0 10px; + + color: inherit; + font: 500 13px/37px 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.action:disabled { + color: #ccc; + cursor: initial; +} + +.action:enabled:hover { + background: rgba(0, 0, 0, 0.05); +} diff --git a/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx b/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx index 6c70088..cd317e8 100644 --- a/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx +++ b/src/components/actions-box/actions/delete-letters-action/delete-letters.jsx @@ -1,14 +1,21 @@ import React, { useContext } from 'react'; +import { cb, mem } from '../../../../react-utils'; + import { Action } from '../action'; -import { LettersContext } from '../../../contexts/lettersContext'; +import { LettersContext } from '../../../letters-context'; export const DeleteLetterAction = props => { - const { letters, changeLetter } = useContext(LettersContext); + const { getLetters, changeLetters } = useContext(LettersContext); + const letters = mem(() => getLetters()).deps(getLetters); - const handleAction = () => { + const handleAction = cb(() => { const ids = letters.filter(letter => letter.checked).map(letter => letter.id); - ids.forEach(id => changeLetter(id, () => ({ deleting: true }))); - }; + changeLetters(letter => (ids.includes(letter.id) ? { state: 3, checked: false } : {})); + }).deps(letters, changeLetters); - return ; + return ( + + Удалить + + ); }; diff --git a/src/components/actions-box/actions/delete-letters-action/index.js b/src/components/actions-box/actions/delete-letters-action/index.js index 59c3373..7c66ce9 100644 --- a/src/components/actions-box/actions/delete-letters-action/index.js +++ b/src/components/actions-box/actions/delete-letters-action/index.js @@ -1 +1 @@ -export * from './delete-letter'; +export * from './delete-letters'; diff --git a/src/components/actions-box/actions/index.js b/src/components/actions-box/actions/index.js index e69de29..2c0622f 100644 --- a/src/components/actions-box/actions/index.js +++ b/src/components/actions-box/actions/index.js @@ -0,0 +1 @@ +export * from './action'; diff --git a/src/components/actions-box/actions/read-letters-action/index.js b/src/components/actions-box/actions/read-letters-action/index.js index e69de29..787dcfe 100644 --- a/src/components/actions-box/actions/read-letters-action/index.js +++ b/src/components/actions-box/actions/read-letters-action/index.js @@ -0,0 +1 @@ +export * from './read-letters'; diff --git a/src/components/actions-box/actions/read-letters-action/read-letters.jsx b/src/components/actions-box/actions/read-letters-action/read-letters.jsx index e69de29..59245d6 100644 --- a/src/components/actions-box/actions/read-letters-action/read-letters.jsx +++ b/src/components/actions-box/actions/read-letters-action/read-letters.jsx @@ -0,0 +1,21 @@ +import React, { useContext } from 'react'; +import { cb, mem } from '../../../../react-utils'; + +import { Action } from '../action'; +import { LettersContext } from '../../../letters-context'; + +export const ReadLettersAction = props => { + const { getLetters, changeLetters } = useContext(LettersContext); + const letters = mem(() => getLetters()).deps(getLetters); + + const handleAction = cb(() => { + const ids = letters.filter(letter => letter.checked).map(letter => letter.id); + changeLetters(letter => (ids.includes(letter.id) ? { unread: false } : {})); + }).deps(letters, changeLetters); + + return ( + + Прочитано + + ); +}; diff --git a/src/components/actions-box/index.js b/src/components/actions-box/index.js index e69de29..7111ee3 100644 --- a/src/components/actions-box/index.js +++ b/src/components/actions-box/index.js @@ -0,0 +1 @@ +export * from './actions-box'; diff --git a/src/components/animation-wrapper/animation-wrapper.jsx b/src/components/animation-wrapper/animation-wrapper.jsx index e69de29..cd7f967 100644 --- a/src/components/animation-wrapper/animation-wrapper.jsx +++ b/src/components/animation-wrapper/animation-wrapper.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { pure } from 'recompose'; + +export const AnimationWrapper = pure( + ({ animationClass, handleAnimationStart, handleAnimationEnd, children }) => { + return ( +
+ {children} +
+ ); + } +); diff --git a/src/components/animation-wrapper/index.js b/src/components/animation-wrapper/index.js index e69de29..2e4e9cb 100644 --- a/src/components/animation-wrapper/index.js +++ b/src/components/animation-wrapper/index.js @@ -0,0 +1 @@ +export * from './animation-wrapper'; diff --git a/src/components/app/app.jsx b/src/components/app/app.jsx index e69de29..466bce9 100644 --- a/src/components/app/app.jsx +++ b/src/components/app/app.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Redirect, Route, Switch } from 'react-router-dom'; + +import { Header } from '../header'; +import { Content } from '../content'; +import { Sidebar } from '../sidebar'; +import { LettersProvider } from '../letters-context'; + +import styles from './app.module.css'; + +export const App = () => ( + + + + + +
+ +
+ + + +
+
+
+); diff --git a/src/components/app/app.module.css b/src/components/app/app.module.css new file mode 100644 index 0000000..13b09b1 --- /dev/null +++ b/src/components/app/app.module.css @@ -0,0 +1,37 @@ +.app { + display: grid; + + width: 100%; + min-width: 800px; + height: auto; + min-height: 100vh; + + box-sizing: border-box; + + padding: 12px 40px 30px 20px; + margin: 0; + + background-color: #e5eaf0; + + grid-column-gap: 25px; + grid-row-gap: 20px; + + grid-template-areas: + 'header header' + 'sidebar content'; + + grid-template-columns: 150px 1fr; + grid-template-rows: 32px minmax(400px, auto); +} + +.header { + grid-area: header; +} + +.sidebar { + grid-area: sidebar; +} + +.content { + grid-area: content; +} diff --git a/src/components/app/index.js b/src/components/app/index.js index e69de29..665a3d9 100644 --- a/src/components/app/index.js +++ b/src/components/app/index.js @@ -0,0 +1 @@ +export * from './app'; diff --git a/src/components/button/button.jsx b/src/components/button/button.jsx index e69de29..f6ffd93 100644 --- a/src/components/button/button.jsx +++ b/src/components/button/button.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import classNames from 'classnames'; + +import styles from './button.module.css'; + +export const Button = ({ className, handleClick, disabled, type, children }) => { + return ( + + ); +}; diff --git a/src/components/button/button.module.css b/src/components/button/button.module.css index e69de29..03658b8 100644 --- a/src/components/button/button.module.css +++ b/src/components/button/button.module.css @@ -0,0 +1,7 @@ +.button { + padding: 0; + + border: none; + background: initial; + cursor: pointer; +} diff --git a/src/components/button/index.js b/src/components/button/index.js index e69de29..eaf5eea 100644 --- a/src/components/button/index.js +++ b/src/components/button/index.js @@ -0,0 +1 @@ +export * from './button'; diff --git a/src/components/checkbox/checkbox.jsx b/src/components/checkbox/checkbox.jsx index e69de29..a53b058 100644 --- a/src/components/checkbox/checkbox.jsx +++ b/src/components/checkbox/checkbox.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { pure } from 'recompose'; + +import styles from './checkbox.module.css'; + +export const Checkbox = pure(({ className, id, handleChange, checked, disabled }) => { + return ( +
+ +
+ ); +}); diff --git a/src/components/checkbox/checkbox.module.css b/src/components/checkbox/checkbox.module.css index e69de29..d59ed4a 100644 --- a/src/components/checkbox/checkbox.module.css +++ b/src/components/checkbox/checkbox.module.css @@ -0,0 +1,68 @@ +.label { + position: relative; + + display: block; + + width: 16px; + margin: 0 auto; + + cursor: pointer; +} + +.checkbox_view { + display: block; + + width: 16px; + height: 16px; + + border: 1px solid #ccc; + background-color: #fff; + border-radius: 3px; +} + +.checkbox_view::after { + position: absolute; + + bottom: 4px; + left: 1px; + + width: 18px; + height: 18px; + + background-image: url(./checkbox.svg); + content: ''; + visibility: hidden; +} + +.checkbox_view:enabled:hover { + border-color: #999; +} + +.real_checkbox { + position: absolute; + appearance: none; +} + +.real_checkbox:active + .checkbox_view { + background-color: #f6f5f3; +} + +.real_checkbox:checked + .checkbox_view { + border-color: #c2b275; + background-color: #ffeba0; +} + +.real_checkbox:checked + .checkbox_view::after { + visibility: visible; +} + +.real_checkbox:enabled:focus + .checkbox_view { + border-color: #e4c23b; + box-shadow: 0 0 15px 1px #e4c23b; +} + +.real_checkbox:disabled + .checkbox_view { + background-color: #fff; + cursor: initial; + opacity: 0.3; +} diff --git a/src/components/checkbox/index.js b/src/components/checkbox/index.js index e69de29..8d78b3e 100644 --- a/src/components/checkbox/index.js +++ b/src/components/checkbox/index.js @@ -0,0 +1 @@ +export * from './checkbox'; diff --git a/src/components/content/content.jsx b/src/components/content/content.jsx index e69de29..6070d61 100644 --- a/src/components/content/content.jsx +++ b/src/components/content/content.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Route, Switch } from 'react-router-dom'; + +import { ActionsBox } from '../actions-box'; +import { Footer } from '../footer'; +import { Letters } from '../letters'; + +import styles from './content.module.css'; +import { LetterWindow } from '../letter-window'; + +export const Content = ({ className }) => { + return ( +
+ + + + + + ( + <> + + + + )} + /> + +
+
+ ); +}; diff --git a/src/components/content/content.module.css b/src/components/content/content.module.css new file mode 100644 index 0000000..10ddfe0 --- /dev/null +++ b/src/components/content/content.module.css @@ -0,0 +1,28 @@ +.main { + display: grid; + height: auto; + + background-color: #fff; + border-radius: 3px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.34); + + grid-template: 42px auto 35px / 1fr; + grid-template-areas: + 'actions' + 'content' + 'footer'; +} + +.actions { + border-bottom: 1px solid #e2e2e2; + grid-area: actions; +} + +.content { + grid-area: content; +} + +.footer { + border-top: 1px solid #e2e2e2; + grid-area: footer; +} diff --git a/src/components/content/index.js b/src/components/content/index.js index e69de29..7b367d1 100644 --- a/src/components/content/index.js +++ b/src/components/content/index.js @@ -0,0 +1 @@ +export * from './content'; diff --git a/src/components/date/date.jsx b/src/components/date/date.jsx index e69de29..6635f1a 100644 --- a/src/components/date/date.jsx +++ b/src/components/date/date.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { pure } from 'recompose'; +import classNames from 'classnames'; +import { formatDatetime, toRussianDate } from '../../utils'; +import styles from './date.module.css'; + +export const Date = pure(({ date, className }) => ( + +)); diff --git a/src/components/date/date.module.css b/src/components/date/date.module.css index e69de29..e65b320 100644 --- a/src/components/date/date.module.css +++ b/src/components/date/date.module.css @@ -0,0 +1,4 @@ +.date { + color: #9b9b9b; + font: 13px 'Helvetica Neue', Arial, sans-serif; +} diff --git a/src/components/date/index.js b/src/components/date/index.js index e69de29..05b562f 100644 --- a/src/components/date/index.js +++ b/src/components/date/index.js @@ -0,0 +1 @@ +export * from './date'; diff --git a/src/components/footer/footer.jsx b/src/components/footer/footer.jsx index e69de29..c6cb2b8 100644 --- a/src/components/footer/footer.jsx +++ b/src/components/footer/footer.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { Link } from '../link'; + +import styles from './footer.module.css'; + +export const Footer = ({ className }) => { + return ( +
+ + Помощь и обратная связь + + + Реклама + + © 2001—2018, Яндекс +
+ ); +}; diff --git a/src/components/footer/footer.module.css b/src/components/footer/footer.module.css index e69de29..3535edf 100644 --- a/src/components/footer/footer.module.css +++ b/src/components/footer/footer.module.css @@ -0,0 +1,14 @@ +.footer { + display: flex; + height: 100%; + + align-items: center; + justify-content: flex-end; +} + +.item { + margin-right: 30px; + + color: #9b9b9b; + font: 11px/31px 'Helvetica Neue', Helvetica, Arial, sans-serif; +} diff --git a/src/components/footer/index.js b/src/components/footer/index.js index e69de29..a058eae 100644 --- a/src/components/footer/index.js +++ b/src/components/footer/index.js @@ -0,0 +1 @@ +export * from './footer'; diff --git a/src/components/header/hamburger.svg b/src/components/header/hamburger.svg index 0f4bd63..d783ed7 100644 --- a/src/components/header/hamburger.svg +++ b/src/components/header/hamburger.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/components/header/header.jsx b/src/components/header/header.jsx index e69de29..d849457 100644 --- a/src/components/header/header.jsx +++ b/src/components/header/header.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { Search } from '../search'; +import { Button } from '../button/button'; +import { Link } from '../link'; + +import { ReactComponent as HamburgerIcon } from './hamburger.svg'; +import { ReactComponent as YandexMailIcon } from './mail-yandex.svg'; + +import styles from './header.module.css'; + +export const Header = ({ className }) => { + return ( +
+ + + + + +
+ ); +}; diff --git a/src/components/header/header.module.css b/src/components/header/header.module.css new file mode 100644 index 0000000..e536fc0 --- /dev/null +++ b/src/components/header/header.module.css @@ -0,0 +1,25 @@ +.header { + display: grid; + align-items: center; + grid-template: 100% / auto 12px auto 1fr; + grid-template-areas: 'hamburger . logo search'; + justify-items: center; +} + +.hamburger { + grid-area: hamburger; +} + +.logo { + grid-area: logo; +} + +.search { + width: 40%; + min-width: 300px; + max-width: 600px; + + margin: 0 60px; + + grid-area: search; +} diff --git a/src/components/header/index.js b/src/components/header/index.js index e69de29..677ca79 100644 --- a/src/components/header/index.js +++ b/src/components/header/index.js @@ -0,0 +1 @@ +export * from './header'; diff --git a/src/components/header/mail-yandex.svg b/src/components/header/mail-yandex.svg index c67cd1a..aebfa92 100644 --- a/src/components/header/mail-yandex.svg +++ b/src/components/header/mail-yandex.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/components/letter-window/index.js b/src/components/letter-window/index.js index e69de29..7583f3b 100644 --- a/src/components/letter-window/index.js +++ b/src/components/letter-window/index.js @@ -0,0 +1 @@ +export * from './letter-window'; diff --git a/src/components/letter-window/letter-window.jsx b/src/components/letter-window/letter-window.jsx index e69de29..06bd993 100644 --- a/src/components/letter-window/letter-window.jsx +++ b/src/components/letter-window/letter-window.jsx @@ -0,0 +1,37 @@ +import React, { useContext } from 'react'; +import classNames from 'classnames'; +import { mem } from '../../react-utils'; + +import { LettersContext } from '../letters-context'; +import { Text } from '../text'; +import { ProfileImage } from '../profile-image'; +import { Date } from '../date'; + +import style from './letter-window.module.css'; + +export const LetterWindow = ({ className, id }) => { + if (!Number.isInteger(+id)) { + return null; + } + + const { getLetterByID } = useContext(LettersContext); + const letter = mem(() => getLetterByID(parseInt(id, 10))).deps(getLetterByID); + + return ( +
+
+ + {letter.content.content.subject} + +
+ + + {letter.content.user.name} + + +
+
+
{letter.content.content.body}
+
+ ); +}; diff --git a/src/components/letter-window/letter-window.module.css b/src/components/letter-window/letter-window.module.css index e69de29..8f9d10d 100644 --- a/src/components/letter-window/letter-window.module.css +++ b/src/components/letter-window/letter-window.module.css @@ -0,0 +1,56 @@ +.content { + width: 100%; + min-width: 700px; + max-width: calc(100vw - 250px); + height: auto; + box-sizing: border-box; + padding: 0 15%; +} + +.body { + width: 100%; + height: auto; + + box-sizing: border-box; + padding-bottom: 30px; + + font: 16px/30px 'Helvetica Neue', Helvetica, Arial, sans-serif; + text-align: justify; +} + +.header { + width: 100%; + height: auto; + + margin-bottom: 20px; +} + +.subject { + width: 100%; + font: 400 24px/80px 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.profileImage { + width: 50px; + height: 50px; + grid-area: image; +} + +.messageInfo { + display: grid; + + grid-column-gap: 20px; + grid-template-areas: 'image name date'; + grid-template-columns: 50px 300px 1fr; + grid-template-rows: 50px; +} + +.name { + font: 700 16px 'Helvetica Neue', Helvetica, Arial, sans-serif; + grid-area: name; +} + +.date { + grid-area: date; + justify-self: end; +} diff --git a/src/components/letter/deleted/deleted-letter.jsx b/src/components/letter/deleted/deleted-letter.jsx index 039f4c6..6f57fef 100644 --- a/src/components/letter/deleted/deleted-letter.jsx +++ b/src/components/letter/deleted/deleted-letter.jsx @@ -1,21 +1,17 @@ -import React, { useContext } from 'react'; -import { LettersContext } from '../../contexts/lettersContext'; -import { Letter } from '../letter'; -import styles from './deleting-letter.module.css'; - -export const DeletingLetter = props => { - const { changeLetter, deleteLetters } = useContext(LettersContext); +import React from 'react'; +import { pure } from 'recompose'; - const handleAnimationEnd = () => { - deleteLetters([props.id]); - }; +import { Letter } from '../letter'; +import styles from './deleted-letter.module.css'; +import { AnimationWrapper } from '../../animation-wrapper'; +export const DeletedLetter = pure(props => { return ( -
-
+ ); -}; +}); diff --git a/src/components/letter/deleted/deleted-letter.module.css b/src/components/letter/deleted/deleted-letter.module.css index 953fc7f..ad1bb37 100644 --- a/src/components/letter/deleted/deleted-letter.module.css +++ b/src/components/letter/deleted/deleted-letter.module.css @@ -1,5 +1,5 @@ -.deletingLetter { - animation: delete-letter-animation 1s cubic-bezier(0.5, 0, 0, 0); +.deletedLetter { + animation: delete-letter-animation 0.6s cubic-bezier(0.5, 0, 0, 0); } @keyframes delete-letter-animation { @@ -8,7 +8,7 @@ opacity: 1; } - 30% { + 50% { height: 41px; opacity: 0; } diff --git a/src/components/letter/deleted/index.js b/src/components/letter/deleted/index.js index 3c2b91b..5144c17 100644 --- a/src/components/letter/deleted/index.js +++ b/src/components/letter/deleted/index.js @@ -1 +1 @@ -export * from './deletingLetter'; +export * from './deleted-letter'; diff --git a/src/components/letter/index.js b/src/components/letter/index.js index e69de29..c37f80d 100644 --- a/src/components/letter/index.js +++ b/src/components/letter/index.js @@ -0,0 +1 @@ +export * from './letter'; diff --git a/src/components/letter/letter.jsx b/src/components/letter/letter.jsx index e69de29..8da0661 100644 --- a/src/components/letter/letter.jsx +++ b/src/components/letter/letter.jsx @@ -0,0 +1,50 @@ +import React, { useContext } from 'react'; +import classNames from 'classnames/bind'; +import { pure } from 'recompose'; +import { Checkbox } from '../checkbox'; +import { ProfileImage } from '../profile-image'; +import { Date } from '../date'; +import { UnreadFlag } from './unread-flag'; +import { Link } from '../link'; +import { Text } from '../text'; +import { LettersContext } from '../letters-context'; + +import styles from './letter.module.css'; + +const cx = classNames.bind(styles); + +export const Letter = pure(({ id, unread, checked, content }) => { + const { changeLetterByID } = useContext(LettersContext); + + const handleChange = event => { + const targetChecked = event.target.checked; + if (checked !== targetChecked) { + changeLetterByID(id, () => ({ checked: targetChecked })); + } + }; + + const handleClick = () => { + changeLetterByID(id, () => ({ unread: false })); + }; + + return ( + +
+ + + + {content.user.name} + + + + {content.content.subject} + + +
+ + ); +}); diff --git a/src/components/letter/letter.module.css b/src/components/letter/letter.module.css index e69de29..9fb221e 100644 --- a/src/components/letter/letter.module.css +++ b/src/components/letter/letter.module.css @@ -0,0 +1,76 @@ +.letter { + display: grid; + + align-items: center; + justify-content: start; + + padding: 0 20px; + + border-bottom: 1px solid #e2e2e2; + column-gap: 15px; + grid-template: 40px / 16px 30px 165px 10px 1fr 60px; +} + +.letterActive { + background-color: rgba(0, 0, 0, 0.05); +} + +.deletedLetter { + animation: delete-letter-animation 1s cubic-bezier(0.5, 0, 0, 0); +} + +@keyframes delete-letter-animation { + 0% { + height: 41px; + opacity: 1; + } + + 30% { + height: 41px; + opacity: 0; + } + + 100% { + height: 0; + opacity: 0; + } +} + +.newLetter { + animation: new-letter-animation 1s cubic-bezier(1, 1, 0.5, 1); +} + +@keyframes new-letter-animation { + 0% { + height: 0; + opacity: 0; + } + + 50% { + height: 40px; + opacity: 0; + } + + 100% { + height: 40px; + opacity: 1; + } +} + +.link { + display: block; + height: auto; +} + +.text { + color: #000; + font: 13px 'Helvetica Neue', Arial, sans-serif; +} + +.textUnread { + font-weight: bold; +} + +.date { + justify-self: end; +} diff --git a/src/components/letter/new/index.js b/src/components/letter/new/index.js index e69de29..9b16edb 100644 --- a/src/components/letter/new/index.js +++ b/src/components/letter/new/index.js @@ -0,0 +1 @@ +export * from './new-letter'; diff --git a/src/components/letter/new/new-letter.jsx b/src/components/letter/new/new-letter.jsx index e69de29..abb7b48 100644 --- a/src/components/letter/new/new-letter.jsx +++ b/src/components/letter/new/new-letter.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { pure } from 'recompose'; +import { Letter } from '../letter'; +import { AnimationWrapper } from '../../animation-wrapper'; + +import styles from './new-letter.module.css'; + +export const NewLetter = pure(props => { + return ( + + + + ); +}); diff --git a/src/components/letter/new/new-letter.module.css b/src/components/letter/new/new-letter.module.css index e69de29..dfca664 100644 --- a/src/components/letter/new/new-letter.module.css +++ b/src/components/letter/new/new-letter.module.css @@ -0,0 +1,20 @@ +.newLetter { + animation: new-letter-animation 0.6s cubic-bezier(1, 1, 0.5, 1); +} + +@keyframes new-letter-animation { + 0% { + height: 0; + opacity: 0; + } + + 50% { + height: 41px; + opacity: 0; + } + + 100% { + height: 41px; + opacity: 1; + } +} diff --git a/src/components/letter/unread-flag/index.js b/src/components/letter/unread-flag/index.js index e69de29..bec34bd 100644 --- a/src/components/letter/unread-flag/index.js +++ b/src/components/letter/unread-flag/index.js @@ -0,0 +1 @@ +export * from './unread-flag'; diff --git a/src/components/letter/unread-flag/unread-flag.jsx b/src/components/letter/unread-flag/unread-flag.jsx index e69de29..b457ca6 100644 --- a/src/components/letter/unread-flag/unread-flag.jsx +++ b/src/components/letter/unread-flag/unread-flag.jsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { pure } from 'recompose'; +import classNames from 'classnames/bind'; + +import styles from './unread-flag.module.css'; + +const cx = classNames.bind(styles); + +export const UnreadFlag = pure(({ visible }) =>
); diff --git a/src/components/letter/unread-flag/unread-flag.module.css b/src/components/letter/unread-flag/unread-flag.module.css index e69de29..a8d5424 100644 --- a/src/components/letter/unread-flag/unread-flag.module.css +++ b/src/components/letter/unread-flag/unread-flag.module.css @@ -0,0 +1,11 @@ +.flag { + width: 10px; + height: 10px; + + background-color: #6287bd; + border-radius: 50%; +} + +.hidden { + visibility: hidden; +} diff --git a/src/components/letters-context/index.js b/src/components/letters-context/index.js index 2039439..20832f9 100644 --- a/src/components/letters-context/index.js +++ b/src/components/letters-context/index.js @@ -1 +1 @@ -export * from './lettersContext'; +export * from './letters-context'; diff --git a/src/components/letters-context/letters-context.jsx b/src/components/letters-context/letters-context.jsx index 281e480..3ae4888 100644 --- a/src/components/letters-context/letters-context.jsx +++ b/src/components/letters-context/letters-context.jsx @@ -1,119 +1,82 @@ -import React, { createContext, useCallback, useMemo } from 'react'; -import { useSessionStorage } from '../../react-utils'; -import { generateLetter } from '../../utils/generators'; +import React, { createContext } from 'react'; +import { useSessionStorage, cb, mem } from '../../react-utils'; +import { mergeObjects } from '../../utils'; + +export const LetterState = Object.freeze({ + NEW: 1, + COMMON: 2, + WILL_BE_DELETED: 3 +}); + +const createLetter = (letterContent, id) => ({ + id, + state: LetterState.NEW, + unread: true, + checked: false, + content: letterContent +}); export const LettersContext = createContext(null); export const LettersProvider = ({ children }) => { const [state, setState] = useSessionStorage('letters', { counter: 0, letters: [] }); - const onPage = useMemo(() => 1500, []); + const getLetters = cb(() => state.letters).deps(state); - const merge = useCallback((obj1, obj2) => Object.assign({}, obj1, obj2), []); + const getLetterByID = cb(id => getLetters().find(letter => letter.id === id)).deps(getLetters); - const createLetter = useCallback( - (letterContent, id) => ({ - id, - state: 1, // 1 - just received, 2 - usual, 3 - gonna be removed - unread: true, - checked: false, - content: letterContent - }), - [state] - ); - - const getLetters = useCallback(() => state.letters.slice(0, onPage), [state]); - - const getLetterByID = useCallback(id => getLetters().find(letter => letter.id === id), [ - getLetters - ]); - - const setStateSlice = useCallback( - (counterFunc, lettersFunc) => - setState(prevState => ({ - counter: counterFunc(prevState.counter), - letters: lettersFunc(prevState.letters.slice(0, onPage)).concat( - prevState.letters.slice(onPage) - ) - })), - [setState] - ); - - const setLettersSlice = useCallback( - lettersFunc => setStateSlice(counter => counter, lettersFunc), - [setStateSlice] - ); + const changeState = cb((counterFunc, lettersFunc) => + setState(({ counter, letters }) => ({ + counter: counterFunc(counter, letters), + letters: lettersFunc(letters, counter) + })) + ).deps(setState); - const filterLetters = useCallback( - predicate => setLettersSlice(letters => letters.filter(predicate)), - [setLettersSlice] + const setLetters = cb(lettersFunc => changeState(counter => counter, lettersFunc)).deps( + changeState ); - const mapLetters = useCallback( - func => setLettersSlice(letters => letters.map(letter => merge(letter, func(letter)))), - [setLettersSlice] - ); + const filterLetters = cb(pred => setLetters(letters => letters.filter(pred))).deps(setLetters); - const addLetter = useCallback( - letterContent => - setStateSlice( - counter => counter + 1, - letters => [createLetter(letterContent, state.counter), ...letters] - ), - [setLettersSlice, createLetter] - ); + const mapLetters = cb(func => + setLetters(letters => letters.map(letter => mergeObjects(letter, func(letter)))) + ).deps(setLetters); - const generateRandomLetters = useCallback( - count => - setState(() => { - const newState = { counter: 0, letters: [] }; - for (let i = 0; i < count; i++) { - const newLetter = createLetter(generateLetter(), newState.counter); - newLetter.state = 2; - newState.letters.unshift(newLetter); - newState.counter++; - } - return newState; - }), - [setState, createLetter, generateLetter] - ); + const addLetter = cb(letterContent => + changeState( + counter => counter + 1, + (letters, counter) => [createLetter(letterContent, counter), ...letters] + ) + ).deps(setLetters, createLetter); - const deleteLetterByID = useCallback(id => filterLetters(letter => letter.id !== id), [ - filterLetters - ]); + const deleteLetterByID = cb(id => filterLetters(letter => letter.id !== id)).deps(filterLetters); - const deleteLettersByIDs = useCallback(ids => filterLetters(letter => !ids.includes(letter.id)), [ + const deleteLettersByIDs = cb(ids => filterLetters(letter => !ids.includes(letter.id))).deps( filterLetters - ]); - - const changeLetterByID = useCallback( - (id, func) => mapLetters(letter => (letter.id === id ? func(letter) : letter)), - [mapLetters] ); - const changeLetters = useCallback(func => mapLetters(func), [mapLetters]); - - const contextValue = useMemo( - () => ({ - getLetters, - getLetterByID, - addLetter, - deleteLetterByID, - deleteLettersByIDs, - changeLetterByID, - changeLetters, - generateRandomLetters - }), - [ - getLetters, - getLetterByID, - addLetter, - deleteLetterByID, - deleteLettersByIDs, - changeLetterByID, - changeLetters, - generateRandomLetters - ] + const changeLetterByID = cb((id, func) => + mapLetters(letter => (letter.id === id ? func(letter) : letter)) + ).deps(mapLetters); + + const changeLetters = cb(func => mapLetters(func)).deps(mapLetters); + + const contextValue = mem(() => ({ + getLetters, + getLetterByID, + addLetter, + deleteLetterByID, + deleteLettersByIDs, + changeLetterByID, + changeLetters + })).deps( + getLetters, + getLetterByID, + addLetter, + deleteLetterByID, + deleteLettersByIDs, + changeLetterByID, + changeLetters ); return {children}; diff --git a/src/components/letters-page/index.js b/src/components/letters-page/index.js index e69de29..ce7fe19 100644 --- a/src/components/letters-page/index.js +++ b/src/components/letters-page/index.js @@ -0,0 +1 @@ +export * from './letterss-page-container'; diff --git a/src/components/letters-page/letters-page-component/index.js b/src/components/letters-page/letters-page-component/index.js index e69de29..6dccf2b 100644 --- a/src/components/letters-page/letters-page-component/index.js +++ b/src/components/letters-page/letters-page-component/index.js @@ -0,0 +1 @@ +export * from './letters-page-component'; diff --git a/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx b/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx index 9e16da5..09be429 100644 --- a/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx +++ b/src/components/letters-page/letters-page-component/letters-list/letters-list.jsx @@ -1,10 +1,19 @@ import React, { Component } from 'react'; +import { Letter } from '../../../letter'; + +import styles from './letters-list.module.css'; + export class LettersList extends Component { render() { return ( -
-