Browse Source

first commit

makstravm 2 years ago
parent
commit
3e19118406

+ 41 - 36
package.json

@@ -1,38 +1,43 @@
 {
-  "name": "my-app",
-  "version": "0.1.0",
-  "private": true,
-  "dependencies": {
-    "@testing-library/jest-dom": "^5.16.1",
-    "@testing-library/react": "^12.1.2",
-    "@testing-library/user-event": "^13.5.0",
-    "react": "^17.0.2",
-    "react-dom": "^17.0.2",
-    "react-scripts": "5.0.0",
-    "web-vitals": "^2.1.2"
-  },
-  "scripts": {
-    "start": "react-scripts start",
-    "build": "react-scripts build",
-    "test": "react-scripts test",
-    "eject": "react-scripts eject"
-  },
-  "eslintConfig": {
-    "extends": [
-      "react-app",
-      "react-app/jest"
-    ]
-  },
-  "browserslist": {
-    "production": [
-      ">0.2%",
-      "not dead",
-      "not op_mini all"
-    ],
-    "development": [
-      "last 1 chrome version",
-      "last 1 firefox version",
-      "last 1 safari version"
-    ]
-  }
+    "name": "shop-react",
+    "version": "0.1.0",
+    "private": true,
+    "dependencies": {
+        "@testing-library/jest-dom": "^5.16.1",
+        "@testing-library/react": "^12.1.2",
+        "@testing-library/user-event": "^13.5.0",
+        "react": "^17.0.2",
+        "react-dom": "^17.0.2",
+        "react-redux": "^7.2.6",
+        "react-router-dom": "^5.3.0",
+        "react-scripts": "5.0.0",
+        "redux": "^4.1.2",
+        "redux-thunk": "^2.4.1",
+        "sass": "^1.45.0",
+        "web-vitals": "^2.1.2"
+    },
+    "scripts": {
+        "start": "react-scripts start",
+        "build": "react-scripts build",
+        "test": "react-scripts test",
+        "eject": "react-scripts eject"
+    },
+    "eslintConfig": {
+        "extends": [
+            "react-app",
+            "react-app/jest"
+        ]
+    },
+    "browserslist": {
+        "production": [
+            ">0.2%",
+            "not dead",
+            "not op_mini all"
+        ],
+        "development": [
+            "last 1 chrome version",
+            "last 1 firefox version",
+            "last 1 safari version"
+        ]
+    }
 }

+ 0 - 38
src/App.css

@@ -1,38 +0,0 @@
-.App {
-  text-align: center;
-}
-
-.App-logo {
-  height: 40vmin;
-  pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
-  .App-logo {
-    animation: App-logo-spin infinite 20s linear;
-  }
-}
-
-.App-header {
-  background-color: #282c34;
-  min-height: 100vh;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  font-size: calc(10px + 2vmin);
-  color: white;
-}
-
-.App-link {
-  color: #61dafb;
-}
-
-@keyframes App-logo-spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}

+ 38 - 20
src/App.js

@@ -1,25 +1,43 @@
-import logo from './logo.svg';
-import './App.css';
+import './App.scss';
+import { Router, Route, Switch, Redirect } from 'react-router-dom';
+import createHistory from "history/createBrowserHistory";
+import { connect, Provider } from 'react-redux';
+import { store } from './redux/redux-store';
+import { Authorization } from './pages/Authorization';
+import { Content } from './pages/Content';
+
+
+
+
+export const history = createHistory()
+
+
+const AppContent = ({ isToken }) =>
+    <Router history={history}>
+        {!isToken
+            ?
+            <Switch>
+                <Route path='/login' component={Authorization} />
+                <Redirect from='/*' to='/login' />
+            </Switch>
+            :
+            <Switch>
+                <Route path='/' component={Content} exact />
+                {/* <Redirect from='/*' to='/' /> */}
+            </Switch>
+        }
+    </Router >
+
+const CAppContent = connect(state => ({ isToken: state.auth?.token }))(AppContent)
+
+store.subscribe(() => console.log(store.getState()))
 
 function App() {
-  return (
-    <div className="App">
-      <header className="App-header">
-        <img src={logo} className="App-logo" alt="logo" />
-        <p>
-          Edit <code>src/App.js</code> and save to reload.
-        </p>
-        <a
-          className="App-link"
-          href="https://reactjs.org"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          Learn React
-        </a>
-      </header>
-    </div>
-  );
+    return (
+        <Provider store={store}>
+            <CAppContent />
+        </Provider>
+    )
 }
 
 export default App;

+ 69 - 0
src/App.scss

@@ -0,0 +1,69 @@
+//     RESET STYLE
+html {
+    box-sizing: border-box;
+}
+
+*,
+*::after,
+*::before {
+    box-sizing: inherit;
+}
+
+ul[class],
+ol[class] {
+    padding: 0;
+}
+
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+ul[class],
+ol[class],
+li,
+figure,
+figcaption,
+blockquote,
+dl,
+dd {
+    margin: 0;
+}
+
+ul[class] {
+    list-style: none;
+}
+
+img {
+    max-width: 100%;
+    display: block;
+}
+
+input,
+button,
+textarea,
+select {
+    font: inherit;
+}
+
+//
+.Header {
+    display: flex;
+    background-color: #ececec;
+    justify-content: space-between;
+    align-items: center;
+}
+.UserNav {
+    display: flex;
+    width: 25vw;
+    align-items: center;
+    img {
+        width: 100px;
+        border-radius: 50%;
+        overflow: hidden;
+        border: 1px solid #000;
+    }
+}

+ 62 - 0
src/actions/index.js

@@ -0,0 +1,62 @@
+import { actionAuthLogin } from "../redux/auth-reducer";
+import { gql } from "../helpers";
+
+
+
+const actionPending = name => ({ type: 'PROMISE', status: 'PENDING', name })
+const actionResolved = (name, payload) => ({ type: 'PROMISE', status: 'RESOLVED', name, payload })
+const actionRejected = (name, error) => ({ type: 'PROMISE', status: 'REJECTED', name, error })
+
+export const actionPromise = (name, promise) =>
+    async dispatch => {
+        dispatch(actionPending(name))
+        try {
+            let data = await promise
+            dispatch(actionResolved(name, data))
+            return data
+        }
+        catch (error) {
+            dispatch(actionRejected(name, error))
+        }
+    }
+
+
+export const actionLogin = (login, password) =>
+    actionPromise('login', gql(`query login($login:String!, $password:String!){
+            login(login:$login, password:$password)
+        }`, { login, password }))
+
+export const actionFullLogin = (login, password) =>
+    async dispatch => {
+        let token = await dispatch(actionLogin(login, password))
+        if (token) {
+            dispatch(actionAuthLogin(token))
+        }
+    }
+
+export const actionProfilData = (_id) =>
+    actionPromise('dataProfileAuth', gql(`query userOned($id:String!){
+                        UserFindOne(query: $id){
+                            _id  login avatar{ _id url }
+    }
+}`, { id: JSON.stringify([{ _id }]) }))
+
+export const myFolowingPosts = () =>
+    actionPromise('followingPosts', gql(`query allposts($query: String!){
+        PostFind(query: $query){
+            _id, text, title,
+            owner{_id, nick, login, avatar {url}
+            }, 
+            images{url},
+            comments{text},
+            createdAt
+        }
+    }`, {
+        query: JSON.stringify([{},
+        {
+            sort: [{ _id: -1 }],
+            skip: [3],
+            limit: [150],
+        }
+        ])
+    }))

File diff suppressed because it is too large
+ 46 - 0
src/components/header/Header.jsx


+ 10 - 0
src/components/header/Search.jsx

@@ -0,0 +1,10 @@
+import React from 'react'
+
+export const Search = () => {
+    return (
+        <div className='Search'>
+            <input />
+            <button>Lupa</button>
+        </div>
+    )
+}

+ 52 - 0
src/components/main/MainContent.js

@@ -0,0 +1,52 @@
+import React, { useEffect } from 'react'
+import { connect } from 'react-redux'
+import { Link } from 'react-router-dom'
+import { myFolowingPosts } from '../../actions'
+import { backURL } from '../../helpers'
+
+const Post = ({ postData: { text, title, owner, images, createdAt, comments } }) => {
+    const date = new Date(createdAt * 1)
+    const resultDate = new Intl.DateTimeFormat('default').format(date)
+    return (
+        <div style={{ padding: '50px '}}>
+            <a href='/asd'>asd</a>
+            <Link to={`/${owner?._id}`} className='owner'>
+                {owner?.avatar?.url && <img src={backURL + '/' + owner.avatar.url} alt='avatar' />}
+                <span>{owner?.login}</span>
+            </Link>
+            {images && images[0] && images[0].url && < img src={backURL + '/' + images[0].url} alt='post' />}
+            <div>
+                <span>
+                    {resultDate}
+                </span>
+                <span>
+                    {title}
+                </span>
+                <span>
+                    {text}
+                </span>
+            </div>
+            {comments ? 'yes' : 'no'}
+        </div>
+    )
+
+}
+
+
+
+
+const MainContent = ({ posts, postsFollowing }) => {
+
+    useEffect(() => {
+        postsFollowing()
+    }, [])
+
+    console.log(posts);
+    return (
+        <div>
+            {posts.map(p => <Post key={p._id} postData={p} />)}
+        </div>
+    )
+}
+
+export const CMainContent = connect(state => ({ posts: state.promise?.followingPosts?.payload || [] }), { postsFollowing: myFolowingPosts })(MainContent)

+ 31 - 0
src/helpers/index.js

@@ -0,0 +1,31 @@
+
+export const jwtDecode = (token) => {
+    try {
+        let arrToken = token.split('.')
+        let base64Token = atob(arrToken[1])
+        return JSON.parse(base64Token)
+    }
+    catch (e) {
+        console.log('Лажа, Бро ' + e);
+    }
+}
+
+export const backURL = 'http://hipstagram.asmer.fs.a-level.com.ua'
+
+const getGQL = url =>
+    async (query, variables = {}) => {
+        let obj = await fetch(url, {
+            method: 'POST',
+            headers: {
+                "Content-Type": "application/json",
+                Authorization: localStorage.authToken ? 'Bearer ' + localStorage.authToken : {},
+            },
+            body: JSON.stringify({ query, variables })
+        })
+        let a = await obj.json()
+        if (!a.data && a.errors)
+            throw new Error(JSON.stringify(a.errors))
+        return a.data[Object.keys(a.data)[0]]
+    }
+
+export const gql = getGQL(backURL + '/graphql');

File diff suppressed because it is too large
+ 1 - 1
src/logo.svg


+ 65 - 0
src/pages/Authorization.jsx

@@ -0,0 +1,65 @@
+import React, { useState } from 'react'
+import { connect } from 'react-redux'
+import { Link } from 'react-router-dom'
+import { actionFullLogin } from '../actions'
+
+const LogIn = ({ logInput, setLogInput, pasInput, setPasInput , onLogIn}) => {
+    return (
+        <div className='Form'>
+            <div className="LoginForm__inner">
+                <h4>Login</h4>
+                <label >
+                    Login:
+                    <input value={logInput}
+                        onChange={e => setLogInput(e.currentTarget.value)}
+                        placeholder='login' />
+                </label>
+                <label>
+                    Password:
+                    <input value={pasInput}
+                        onChange={e => setPasInput(e.currentTarget.value)}
+                        placeholder='password' />
+                </label>
+                <button onClick={()=>onLogIn(logInput, pasInput )}
+                >Login
+                </button>
+                <Link to={'/registration'}>Registration</Link>
+            </div >
+        </div >
+    )
+}
+
+
+const Form = ({ auth, onLogIn }) => {
+    const [logInput, setLogInput] = useState('')
+    const [pasInput, setPasInput] = useState('')
+
+    return (
+        <div className='UserPanel'>
+            <LogIn
+                logInput={logInput}
+                setLogInput={setLogInput}
+                pasInput={pasInput}
+                setPasInput={setPasInput}
+                onLogIn={onLogIn}
+            />
+
+        </div>
+    )
+}
+
+const CForm = connect(state => ({ auth: state.auth }), { onLogIn: actionFullLogin })(Form)
+
+
+
+
+export const Authorization = () => {
+    return (
+        <div className='Authorization'>
+            asjgsdg
+            <CForm />
+        </div>
+    )
+}
+
+

+ 27 - 0
src/pages/Content.js

@@ -0,0 +1,27 @@
+import React, { useEffect } from 'react'
+import { connect } from 'react-redux'
+import { myFolowingPosts } from '../actions'
+import Header from '../components/header/Header'
+import { CMainContent } from '../components/main/MainContent'
+
+
+
+const Main = ({ children }) =>
+    <>{children}</>
+
+
+export const CMain = connect(null, { postsFollowing: myFolowingPosts })(Main)
+
+export const Content = () => {
+    return (
+        <>
+            <Header />
+            <Main>
+                <CMainContent />
+                {/* <Aside /> */}
+            </Main>
+        </>
+    )
+}
+
+

+ 60 - 0
src/redux/auth-reducer.js

@@ -0,0 +1,60 @@
+import { jwtDecode } from '../helpers'
+
+
+export const authReducer = (state, { type, token }) => {
+    if (!state) {
+        if (localStorage.authToken) {
+            type = 'AUTH_LOGIN'
+            token = localStorage.authToken
+        } else state = {}
+    }
+
+    if (type === 'AUTH_LOGIN') {
+        localStorage.setItem('authToken', token)
+
+        let payload = jwtDecode(token)
+        if (typeof payload === 'object') {
+            return {
+                ...state,
+                token,
+                payload
+            }
+        } else return state
+    }
+    if (type === 'AUTH_LOGOUT') {
+        localStorage.removeItem('authToken')
+        return {}
+    }
+    return state
+}
+
+export const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token })
+
+export const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })
+
+
+
+// const actionRegister = (login, password) =>
+//     actionPromise('register', gql(`
+//                 mutation reg($login:String, $password:String){
+//                 UserUpsert(user:{
+//                     login:$login,
+//                         password:$password,
+//                         nick:$login}){
+//                 _id login
+//                 }
+//             }
+//             `, { login, password }))
+
+// export const actionFullRegister = (login, password) =>
+//     async dispatch => {
+//         await actionRegister(login, password)
+//         let token = await dispatch(actionLogin(login, password))
+//         if (token) {
+//             dispatch(actionAuthLogin(token))
+//         }
+//     }
+
+
+
+

+ 10 - 0
src/redux/promise-reducer.js

@@ -0,0 +1,10 @@
+
+export function promiseReducer(state = {}, { type, status, payload, error, name }) {
+    if (type === 'PROMISE') {
+        return {
+            ...state,
+            [name]: { status, payload, error }
+        }
+    }
+    return state;
+}

+ 10 - 0
src/redux/redux-store.js

@@ -0,0 +1,10 @@
+import { createStore, combineReducers, applyMiddleware } from 'redux';
+import thunk from 'redux-thunk';
+import { authReducer } from './auth-reducer';
+import { promiseReducer } from './promise-reducer';
+
+export const store = createStore(combineReducers({
+    promise: promiseReducer,
+    auth: authReducer,
+}),
+    applyMiddleware(thunk))

File diff suppressed because it is too large
+ 8837 - 0
yarn.lock