Browse Source

janky inital login/registration + routing beetween implemented

miskson 2 years ago
parent
commit
73d651d134
3 changed files with 627 additions and 281 deletions
  1. 434 247
      package-lock.json
  2. 2 1
      package.json
  3. 191 33
      src/App.js

File diff suppressed because it is too large
+ 434 - 247
package-lock.json


+ 2 - 1
package.json

@@ -10,10 +10,11 @@
     "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",
-    "web-vitals": "^2.1.2"
+    "web-vitals": "^2.1.3"
   },
   "scripts": {
     "start": "react-scripts start",

+ 191 - 33
src/App.js

@@ -1,44 +1,202 @@
 import './App.css';
+import thunk from 'redux-thunk';
+import { useEffect, useState } from 'react';
+import {createStore, combineReducers, applyMiddleware} from 'redux';
+import {Provider, connect} from 'react-redux';
+import { Link, Route, Router, Switch, Redirect } from 'react-router-dom';
+import createHistory from 'history/createBrowserHistory'
 
-const LoginForm = () =>
-  <>
-    <h1>Web-player</h1>
-    <div>
-      <h2>Log-in</h2>
-      <input type="text" placeholder='Login'/>
-      <br />
-      <input type="password" placeholder='Password'/>
-      <br />
-      <button>Login</button>
-      <p>- OR -</p>
-      <a href='#'>Register new user</a>
-    </div>
-  </>
+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))
+        }
+    }
+
+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())
+         .then(data => {
+            if (data.errors && !data.data)
+               throw new Error(JSON.stringify(data.errors))
+            return data.data[Object.keys(data.data)[0]]
+         })
+
+const backendURL = "http://player.asmer.fs.a-level.com.ua" 
+const gql        = getGQL(backendURL + '/graphql')
+
+function jwtDecode(token) {
+    try {
+        let decoded = token.split('.')
+        decoded = decoded[1]
+        decoded = atob(decoded)
+        decoded = JSON.parse(decoded)
+        return decoded
+
+    } catch (e) {
+        return;
+    }
+}
+
+function authReducer(state, {type, token}){
+  if (!state) {
+      if (localStorage.authToken) {
+          type = 'AUTH_LOGIN'
+          token = localStorage.authToken
+      } else {
+          return {}
+      }
+  }
+  if (type === 'AUTH_LOGIN') {
+      let auth = jwtDecode(token)
+      if (auth) {
+          localStorage.authToken = token
+          return { token, payload: auth }
+      }
+  }
+  if (type === 'AUTH_LOGOUT') {
+      localStorage.authToken = ''
+      return {}
+  }
+
+  return state
+}
+
+function promiseReducer(state={}, {type, name, status, payload, error}){
+  if (type === 'PROMISE'){
+      return {
+          ...state,
+          [name]:{status, payload, error}
+      }
+  }
+  return state
+}
+
+const actionAuthLogin = (token) => ({ type: 'AUTH_LOGIN', token })
+const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })
 
-const RegisterForm = () =>
-  <>
-    <h1>Web-player</h1>
-    <div>
-      <h2>Registration</h2>
-      <input type="text" placeholder='Login'/>
-      <br />
-      <input type="text" placeholder='Nickname'/>
-      <br />
-      <input type="password" placeholder='Password'/>
-      <br />
-      <button>Register</button>
-      <br />
-      <a href='#'>Back to log-in</a>
-  </div>
+const actionLogin = (login, password) =>
+  actionPromise('login', gql(`
+  query log($login:String!, $password:String!) {
+      login(login: $login, password: $password)
+    }`, { login, password }))
+
+const actionFullLogin = (login = 'tst', password = '123') =>
+  async dispatch => {
+      let token = await dispatch(actionLogin(login, password))
+      if (token) {
+          dispatch(actionAuthLogin(token))
+      }
+  }
+
+const actionRegister = (login, password) =>
+  actionPromise('registration', gql(`
+      mutation register($login:String!, $password:String!) {
+          createUser(login: $login, password: $password) {
+              login, _id
+          }
+      }
+    `, { login, password }))
+
+const actionFullRegister = (login = 'tst', password = '123') =>
+  async dispatch => {
+      await dispatch(actionRegister(login, password))
+      await dispatch(actionFullLogin(login, password))
+  }
+
+const store = createStore(
+  combineReducers(
+    {
+      promise: promiseReducer,
+      auth: authReducer
+    }
+  ), applyMiddleware(thunk)
+)
+store.subscribe(() => console.log(store.getState()))
+
+const LoginForm = ({onLogin}) => {
+  let [login, setLogin] = useState()
+  let [password, setPassword] = useState()
+
+  return (
+    <>
+      <h1>Web-player</h1>
+      <div>
+        <h2>Log-in</h2>
+        <input type="text" placeholder='Login' onChange={(e) => setLogin(e.target.value)}/>
+        <br />
+        <input type="password" placeholder='Password' onChange={(e) => setPassword(e.target.value)}/>
+        <br />
+        <button disabled={!password || !login} onClick={() => onLogin(login, password)}>Login</button>
+        <p>- OR -</p>
+        <Link to="/registration">Register new user</Link>
+      </div>
+    </>
+  )
+}
+
+const LoginFormConnect = connect(null,{onLogin: actionFullLogin})(LoginForm)
+
+const RegisterForm = ({onRegister}) => {
+  let [login, setLogin] = useState()
+  let [password, setPassword] = useState()
+  let [password2, setPassword2] = useState()
+  
+  return (
+    <>
+      <h1>Web-player</h1>
+      <div>
+        <h2>Registration</h2>
+        <input type="text" placeholder='Login' onChange={(e) => setLogin(e.target.value)}/>
+        <br />
+        <input type="password" placeholder='Password'onChange={(e) => setPassword(e.target.value)}/>
+        <br />
+        <input  disabled={!password} type="password" placeholder='Repeat Password' onChange={(e) => setPassword2(e.target.value)}/>
+        <br />
+        <small style={{color: 'red'}}>{password2 && password2 !== password? 'Passwords do not match' : ''}</small>
+        <br />
+        <button disabled={!password || !login || password2 !== password } onClick={() => onRegister(login, password)}>Register</button>
+        <br />
+        <Link to="/login">Back to log-in page</Link>
+      </div>
   </>
+  )
+}
+const RegisterFormConnect = connect(null,{onRegister: actionFullRegister})(RegisterForm)
 
+const history = createHistory()
 
 function App() {
   return (
-    <div className="App">
-      <LoginForm />
-      <RegisterForm />
-    </div>
+    <Router history={history}>
+      <Provider store={store}>
+        <div className="App">
+          <Switch>
+            <Route path="/login" component={LoginFormConnect} exact/>
+            <Route path="/registration" component={RegisterFormConnect} exact/>
+          </Switch>
+        </div>
+      </Provider>
+    </Router>
   );
 }