|
@@ -0,0 +1,210 @@
|
|
|
+import logo from './logo.svg';
|
|
|
+import './App.css';
|
|
|
+import React, { useState, Component, useEffect, useRef } from "react";
|
|
|
+import {Provider, connect} from 'react-redux';
|
|
|
+import thunk from 'redux-thunk';
|
|
|
+import {createStore, combineReducers,applyMiddleware } from 'redux';
|
|
|
+import Form from './components/Form';
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const getGQL = url =>
|
|
|
+ (query, variables) =>
|
|
|
+ fetch(url, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ "content-type": "application/json",
|
|
|
+ ...(localStorage.authToken ? {Authorization: "Bearer " + localStorage.authToken} : {})
|
|
|
+ },
|
|
|
+ body: JSON.stringify({query, variables})
|
|
|
+ }).then(res => res.json())
|
|
|
+
|
|
|
+ let gql = getGQL("http://snippet.asmer.fs.a-level.com.ua/graphql")
|
|
|
+
|
|
|
+function promiseReducer(state , {type, name ,status , payload, error}) {
|
|
|
+ if (!state){
|
|
|
+ return {} //{status , payload , error}
|
|
|
+ }
|
|
|
+ if (type === 'PROMISE') {
|
|
|
+ return {
|
|
|
+ ...state,
|
|
|
+ [name]: [status,payload , error]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return state
|
|
|
+}
|
|
|
+
|
|
|
+let reg = async(login,password) => {
|
|
|
+ let query = `mutation reg($l:String! , $p:String!) {
|
|
|
+ createUser(login:$l,password:$p){
|
|
|
+ _id
|
|
|
+}
|
|
|
+}`
|
|
|
+
|
|
|
+ let qVariables = {
|
|
|
+ "l": login,
|
|
|
+ "p": password
|
|
|
+ }
|
|
|
+ let result = await gql(query,qVariables)
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ let log = async(login , password) => {
|
|
|
+ let query = ` query log($l:String!,$p:String!) {
|
|
|
+ login(login:$l,password:$p)
|
|
|
+ }`
|
|
|
+let qVariables = {
|
|
|
+ "l": login,
|
|
|
+ "p": password
|
|
|
+}
|
|
|
+let result = await gql(query,qVariables)
|
|
|
+console.log(result)
|
|
|
+localStorage.authToken = result.data.login
|
|
|
+}
|
|
|
+
|
|
|
+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})
|
|
|
+
|
|
|
+const actionPromise = (name, promise) =>
|
|
|
+ async dispatch => {
|
|
|
+ dispatch(actionPending(name))
|
|
|
+ try {
|
|
|
+ let payload = await promise
|
|
|
+ dispatch(actionResolved(name , payload))
|
|
|
+ return payload
|
|
|
+ }
|
|
|
+ catch(error){
|
|
|
+ dispatch(actionRejected(name , error))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function authReducer(state, action){ //....
|
|
|
+ if (state === undefined){
|
|
|
+ if (!localStorage.authToken){
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+ action.token = localStorage.authToken
|
|
|
+ action.type = 'LOGIN'
|
|
|
+ // добавить в action token из localStorage, и проимитировать LOGIN
|
|
|
+ }
|
|
|
+ if (action.type === 'LOGIN'){
|
|
|
+ console.log('ЛОГИН')
|
|
|
+ localStorage.authToken = action.token
|
|
|
+ console.log(action.token)
|
|
|
+ function jwt_decode (token) {
|
|
|
+ var start64Url = token.split('.')[1]
|
|
|
+ return JSON.parse(atob(start64Url))
|
|
|
+ }
|
|
|
+ return {token: action.token, payload: jwt_decode(action.token)}
|
|
|
+ }
|
|
|
+ if (action.type === 'LOGOUT'){
|
|
|
+ console.log('ЛОГАУТ')
|
|
|
+ localStorage.removeItem("authToken")
|
|
|
+ //вернуть пустой объект
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+ return state
|
|
|
+}
|
|
|
+
|
|
|
+const actionAuthLogin = token => ({type:'LOGIN', token})
|
|
|
+const actionAuthLogout = () => ({type:'LOGOUT'})
|
|
|
+const actionFullLogin = (login , password) => async dispatch => {
|
|
|
+ let result = await dispatch(actionPromise("login",log(login,password)))
|
|
|
+ if (result.data.login !== null){
|
|
|
+ dispatch(actionAuthLogin(result.data.login))
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ alert ('такого пользователя не существует')
|
|
|
+ }
|
|
|
+}
|
|
|
+const actionRegister = (login,password) => async dispatch => {
|
|
|
+ return await dispatch (actionPromise('register' , reg(login,password)))
|
|
|
+}
|
|
|
+
|
|
|
+const actionFullRegister = (login,password) => async dispatch => {
|
|
|
+ let result = await dispatch (actionRegister(login,password))
|
|
|
+ if (result.errors === undefined) {
|
|
|
+ await dispatch (actionFullLogin(login,password))
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ alert("Такой пользователь уже есть")
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+let reducers = {
|
|
|
+ promise:promiseReducer,
|
|
|
+ auth:authReducer
|
|
|
+}
|
|
|
+
|
|
|
+const store = createStore(combineReducers(reducers), applyMiddleware(thunk))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const LoginForm = ({ onLogin }) => {
|
|
|
+ const [login, setLogin] = useState("");
|
|
|
+ const [password, setPassword] = useState("");
|
|
|
+ //надо тип инпуту
|
|
|
+ //надо проверку на пустоту инпутов и запрет кнопки (disabled)
|
|
|
+ //надо при кнопке отправить в onLogin login и пароль. onLogin - это функция-колбэк
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <input value={login} onChange={(e) => setLogin(e.target.value)} />
|
|
|
+ <input
|
|
|
+ type="password"
|
|
|
+ value={password}
|
|
|
+ onChange={(e) => setPassword(e.target.value)}
|
|
|
+ />
|
|
|
+ <button
|
|
|
+ onClick={() => onLogin(login, password)}
|
|
|
+ disabled={!login || !password}
|
|
|
+ >
|
|
|
+ Sign in
|
|
|
+ </button>
|
|
|
+
|
|
|
+ </>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+const ConnectedLoginForm = connect(null, {onLogin: actionAuthLogin})(LoginForm)
|
|
|
+
|
|
|
+const unsubscribe = store.subscribe(() => console.log('result here',store.getState()))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const NickName = ({nick}) => {
|
|
|
+ return (
|
|
|
+ <p></p>,
|
|
|
+ <a href = '#'>{nick || 'anon'}</a>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ const ConnectedNick = connect(state => ({nick:state?.auth?.payload?.sub?.login }))(NickName)
|
|
|
+
|
|
|
+ const LogOut = connect ((state) => ({children:'Logout' , disabled:!state.auth.token }) , ({onClick:actionAuthLogout}))('button')
|
|
|
+
|
|
|
+
|
|
|
+function App() {
|
|
|
+ return (
|
|
|
+ <div className="App">
|
|
|
+ <Provider store = {store}>
|
|
|
+ <ConnectedNick/> <br/>
|
|
|
+ <ConnectedLoginForm/>
|
|
|
+ <LogOut/>
|
|
|
+ {/* <Form/> */}
|
|
|
+ </Provider>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default App;
|