|
@@ -0,0 +1,327 @@
|
|
|
+import logo from './logo.svg';
|
|
|
+import './App.css';
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import { Router, Route, Link, Redirect, Switch, useParams } from 'react-router-dom';
|
|
|
+import RecipeReviewCard from './post' // импортируем карточку поста в лентеы
|
|
|
+import Box from '@mui/material/Box';
|
|
|
+import Stack from '@mui/material/Stack';
|
|
|
+import Paper from '@mui/material/Paper';
|
|
|
+import { styled } from '@mui/material/styles';
|
|
|
+
|
|
|
+
|
|
|
+import { createStore, combineRedusers, applyMiddleware } from 'redux';
|
|
|
+import thunk from 'redux-thunk';
|
|
|
+import { Provider, connect, useDispatch, useSelector } from 'react-redux';
|
|
|
+
|
|
|
+// import { Provider } from 'react-redux';
|
|
|
+
|
|
|
+import createHistory from "history/createBrowserHistory";
|
|
|
+
|
|
|
+
|
|
|
+// ==============================================================================
|
|
|
+// создание promiseReducer
|
|
|
+function promiseReducer(state = {}, { type, status, payload, error, nameOfPromise }) {
|
|
|
+ if (type === 'PROMISE') {
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ [nameOfPromise]: { status, payload, error }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return state
|
|
|
+}
|
|
|
+
|
|
|
+// акшоны для promiseReducer
|
|
|
+const actionPending = nameOfPromise => ({ nameOfPromise, type: 'PROMISE', status: 'PENDING' })
|
|
|
+
|
|
|
+const actionFulfilled = (nameOfPromise, payload) => ({ nameOfPromise, type: 'PROMISE', status: 'FULFILLED', payload })
|
|
|
+
|
|
|
+const actionRejected = (nameOfPromise, error) => ({ nameOfPromise, type: 'PROMISE', status: 'REJECTED', error })
|
|
|
+
|
|
|
+const actionPromise = (nameOfPromise, promise) =>
|
|
|
+ async dispatch => {
|
|
|
+ dispatch(actionPending(nameOfPromise)) //сигнализируем redux, что промис начался
|
|
|
+ try {
|
|
|
+ const payload = await promise //ожидаем промиса
|
|
|
+ dispatch(actionFulfilled(nameOfPromise, payload)) //сигнализируем redux, что промис успешно выполнен
|
|
|
+ return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ dispatch(actionRejected(nameOfPromise, error)) //в случае ошибки - сигнализируем redux, что промис несложился
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+// =============================================================
|
|
|
+// функция getGql
|
|
|
+function getGql(endpoint) {
|
|
|
+ return async function gql(query, variables = {}) {
|
|
|
+
|
|
|
+ let headers = {
|
|
|
+ 'Content-Type': 'application/json;charset=utf-8',
|
|
|
+ 'Accept': 'application/json',
|
|
|
+ }
|
|
|
+ if (('authToken' in localStorage)) {
|
|
|
+ headers.Authorization = 'Bearer ' + localStorage.authToken
|
|
|
+ }
|
|
|
+
|
|
|
+ let result = await fetch(endpoint, {
|
|
|
+ method: 'POST',
|
|
|
+ headers,
|
|
|
+ body: JSON.stringify({
|
|
|
+ query,
|
|
|
+ variables
|
|
|
+ })
|
|
|
+ }).then(res => res.json())
|
|
|
+
|
|
|
+ if (('errors' in result) && !('data' in result)) {
|
|
|
+ throw new Error(JSON.stringify(result.errors))
|
|
|
+ }
|
|
|
+
|
|
|
+ result = Object.values(result.data)[0]
|
|
|
+
|
|
|
+ return result
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const gql = getGql('http://hipstagram.node.ed.asmer.org.ua/graphql')
|
|
|
+
|
|
|
+
|
|
|
+// запрос на все посты (для ленты общей)
|
|
|
+const actionfindPosts = () => actionPromise('PostsFind', gql(`query findPosts($allPosts: String){
|
|
|
+ PostFind(query: $allPosts){
|
|
|
+ _id createdAt title text likesCount images {url} owner{_id login nick} comments{
|
|
|
+ _id
|
|
|
+ }
|
|
|
+ }}`, {
|
|
|
+ allPosts: JSON.stringify([{}])
|
|
|
+}))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// запрос на конкретный пост
|
|
|
+// const actionFindPostOne = _id => actionPromise('PostFindOne', gql(`
|
|
|
+// query PostFindOne ($postOne: String){
|
|
|
+// PostFindOne (query: $postOne) {
|
|
|
+// _id createdAt title text likesCount images {url} owner{_id login nick}
|
|
|
+// }
|
|
|
+// }`, {
|
|
|
+// postOne: JSON.stringify([{ _id }])
|
|
|
+// }))
|
|
|
+
|
|
|
+
|
|
|
+// const actionPostsFind = () => actionPromise('PostsFind', gql(`query baseCategory($searchVariablesCategory: String){
|
|
|
+// PostsFind(query: $searchVariablesCategory){
|
|
|
+// _id name parent {
|
|
|
+// _id
|
|
|
+// name
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }`, {
|
|
|
+// searchVariablesCategory: JSON.stringify([{ parent: null }])
|
|
|
+// }))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const store = createStore(promiseReducer, applyMiddleware(thunk))
|
|
|
+store.subscribe(() => console.log(store.getState()))
|
|
|
+
|
|
|
+store.dispatch(actionfindPosts())
|
|
|
+// store.dispatch(actionCategoryFind())
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const history = createHistory()
|
|
|
+
|
|
|
+const Autorization = () =>
|
|
|
+ <div>
|
|
|
+ Авторизация
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+const Registration = () =>
|
|
|
+ <div>
|
|
|
+ Регистрация
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+// const MainPage = () =>
|
|
|
+// <div>
|
|
|
+// Главная страница
|
|
|
+// </div>
|
|
|
+
|
|
|
+
|
|
|
+const Direct = () =>
|
|
|
+ <div>
|
|
|
+ Direct
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// ===================================================
|
|
|
+// сам item для поста
|
|
|
+const Item = styled(Paper)(({ theme }) => ({
|
|
|
+ backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
|
|
+ width: 500
|
|
|
+}));
|
|
|
+
|
|
|
+// const Feed = ({ posts = arrPosts }) => {
|
|
|
+const Feed = ({ posts = [] }) => {
|
|
|
+ return (
|
|
|
+ <Box sx={{
|
|
|
+ width: '500',
|
|
|
+ height: '600',
|
|
|
+ margin: '10, 20'
|
|
|
+ }}>
|
|
|
+ {/* spacing - это отступ между карточками в ленте */}
|
|
|
+ <Stack spacing={1}>
|
|
|
+ {/* {posts.map(post => <Item key={post._id}><RecipeReviewCard date={post.createdAt} title={post.title} text={post.text} likesCount={post.likesCount} nickName={post.owner.nick} login={post.owner.login} /></Item>)} */}
|
|
|
+ {posts.map(post => <Item key={post._id}><RecipeReviewCard postData={post} /></Item>)}
|
|
|
+ </Stack>
|
|
|
+ </Box >
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+const ReduxFeed = connect(state => ({ posts: state.PostsFind.payload }))(Feed)
|
|
|
+
|
|
|
+// ================================================
|
|
|
+
|
|
|
+
|
|
|
+// ==============================================================================
|
|
|
+// страница поста
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// ===============================================================================
|
|
|
+
|
|
|
+
|
|
|
+const CreatePost = () =>
|
|
|
+ <div>
|
|
|
+ Страницв создания поста
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+const User = () =>
|
|
|
+ <div>
|
|
|
+ Страница юзера
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+const PageAbout = () =>
|
|
|
+ <div>
|
|
|
+ Страница "О нас"
|
|
|
+ </div>
|
|
|
+
|
|
|
+const Page404 = () =>
|
|
|
+ <div>
|
|
|
+ NOT FOUND
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+// ==================================================
|
|
|
+// страница одного поста
|
|
|
+
|
|
|
+const actionFindPostOne = _id => actionPromise('PostFindOne', gql(`
|
|
|
+query PostFindOne ($postOne: String){
|
|
|
+ PostFindOne (query: $postOne) {
|
|
|
+ _id createdAt title text likesCount images {url} owner{_id login nick}
|
|
|
+ }
|
|
|
+}`, {
|
|
|
+ postOne: JSON.stringify([{ _id }])
|
|
|
+}))
|
|
|
+
|
|
|
+// const Comments = ({ match: { params: { user, postId } } }) =>
|
|
|
+// const Comments = ({ post = {}, loadPost }) => {
|
|
|
+const Comments = ({ post = {}, loadPost }) => {
|
|
|
+
|
|
|
+
|
|
|
+ const { postId } = useParams()
|
|
|
+ // const post2 = useSelector(state => state?.CategoryGoodsAndSubCategoryGoods?.payload)
|
|
|
+
|
|
|
+ useEffect(() => { loadPost(postId) }, [postId])
|
|
|
+
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {/* <div>{user} and {postId}</div> */}
|
|
|
+ тут будут отображаться все комментарии под постом
|
|
|
+ <h2>{post.createdAt}</h2>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// store.dispatch(actionFindPostOne())
|
|
|
+
|
|
|
+const CComments = connect(state => ({ post: state?.PostFindOne?.payload }), { loadPost: actionFindPostOne })(Comments)
|
|
|
+// ====================================================
|
|
|
+
|
|
|
+
|
|
|
+function App() {
|
|
|
+ return (
|
|
|
+ <Provider store={store}>
|
|
|
+ <Router history={history}>
|
|
|
+ <div className="wrapper" style={{ display: 'flex', minHeight: '100hv' }}>
|
|
|
+ <aside style={{ width: '15%', minHeight: '100vh', backgroundColor: '#ffdbdb' }}>
|
|
|
+ <Link to="/">Главная(она же Лента)</Link>
|
|
|
+ <br />
|
|
|
+ <Link to="/profile">Профиль</Link>
|
|
|
+ <br />
|
|
|
+ <Link to="/createpost">Создание поста</Link>
|
|
|
+ <br />
|
|
|
+ <Link to="/direct">Директ</Link>
|
|
|
+ <br />
|
|
|
+ <Link to="/about">О нас</Link>
|
|
|
+ <br />
|
|
|
+ <Link to="/registration">Регистрация</Link>
|
|
|
+ <br />
|
|
|
+ <Link to="/autorization">Авторизация</Link>
|
|
|
+ </aside>
|
|
|
+ <section style={{ width: '50%', margin: '0 auto', minHeight: '100hv', display: 'flex', flexDirection: 'column' }}>
|
|
|
+ <header style={{ backgroundColor: '#dbffe7' }}>
|
|
|
+ Тут будут какие-то заголовки или что-то вроде этого
|
|
|
+ </header>
|
|
|
+ <main style={{ backgroundColor: '#dcdbff', flex: '1 1 auto' }}>
|
|
|
+ {/* прописывание редиректа */}
|
|
|
+ {/* <Redirect from='/direct' to='/profile' /> */}
|
|
|
+
|
|
|
+ <Switch>
|
|
|
+ <Route path="/autorization" component={Autorization} />
|
|
|
+ <Route path="/profile" component={User} />
|
|
|
+ <Route path="/registration" component={Registration} />
|
|
|
+ {/* <Route path="/" component={Feed} exact /> */}
|
|
|
+ <Route path="/" component={ReduxFeed} exact />
|
|
|
+ <Route path="/createpost" component={CreatePost} />
|
|
|
+ <Route path="/direct" component={Direct} />
|
|
|
+ <Route path="/about" component={PageAbout} />
|
|
|
+ {/* <Route path="/:postId" component={Comments} /> */}
|
|
|
+ <Route path="/:postId" component={CComments} />
|
|
|
+
|
|
|
+
|
|
|
+ <Route path="*" component={Page404} />
|
|
|
+ {/* аналогично */}
|
|
|
+ {/* <Page404 /> */}
|
|
|
+
|
|
|
+
|
|
|
+ </Switch>
|
|
|
+ </main>
|
|
|
+ <footer style={{ backgroundColor: '#dbffe7' }}>Hipstagram from Volddemar4ik</footer>
|
|
|
+ </section>
|
|
|
+ </div>
|
|
|
+ </Router >
|
|
|
+ </Provider>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default App;
|