Browse Source

combineReducers && authLogin

Vladislav342 2 years ago
parent
commit
14471fcd1e
1 changed files with 537 additions and 0 deletions
  1. 537 0
      HW_18/index.html

+ 537 - 0
HW_18/index.html

@@ -0,0 +1,537 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<meta charset="utf-8">
+	<title>authReducer</title>
+	<style>
+        #mainContainer{
+			display:flex;
+		}
+		#aside{
+			width: 30%;
+		}
+		#aside > a{
+			display: block;
+		}
+
+		img{
+			width:300px;
+		}
+		main{
+			padding-left: 20px;
+		}
+		table{
+			border:1px solid black;
+		}
+		td{
+			text-align: center;
+			border:1px solid black;
+		}
+    </style>
+</head>
+<body>
+
+	<header>
+            <div id="formId"></div>
+            <div id="ownCab"></div>
+        </header>
+        <div id='mainContainer'>
+            <aside id='aside'>
+                Категории
+            </aside>
+            <main id='main'>
+                Контент
+            </main>
+        </div>
+
+        
+
+	<input type="submit" value="click 1" id="btn1">
+	<input type="submit" value="click 2" id="btn2">
+
+	<script>
+		
+function createStore(reducer){
+    let state       = reducer(undefined, {}) 
+    let cbs         = []                    
+    const getState  = () => state           
+    const subscribe = cb => (cbs.push(cb),   
+                             () => cbs = cbs.filter(c => c !== cb))          
+    const dispatch  = action => { 
+        if (typeof action === 'function'){ 
+            return action(dispatch, getState) 
+        }
+        const newState = reducer(state, action) 
+        if (newState !== state){
+            state = newState 
+            for (let cb of cbs)  cb()
+        }
+    } 
+    return {
+        getState, 
+        dispatch,
+        subscribe 
+    }
+}
+
+//----------------------------------------------------------------
+		//написать jwtDecode = token =>({})
+		const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWE0ZTA1MmM3NTBjMTJiYTZiYTQwMjkiLCJsb2dpbiI6InZsYWRCcmF1bjQiLCJhY2wiOlsiNjFhNGUwNTJjNzUwYzEyYmE2YmE0MDI5IiwidXNlciJdfSwiaWF0IjoxNjM4NTM5NTUzfQ.oPRus9nGS1rg69eKu8rK-tMi4V-hN5HXE0NOzAc5K4k";
+
+		//выкусить из токена серединку 
+		//сделать base64 декод (atob)
+		//с результатом сделать JSON.parse
+		const jwtDecode = token =>{
+			try{
+				let mid=token.split('.');
+				let tok=mid[1];
+				let tokenDecode=atob(tok);
+				let finalTok=JSON.parse(tokenDecode);
+				return finalTok;
+			}catch(e){
+				console.log(e);
+			}
+		}
+		console.log(jwtDecode(token));
+
+//------------------------------------------------------------
+	function authReducer(state={},{type, token}){
+		if(!state){
+			if(localStorage.authToken){
+				type="AUTH_LOGIN";
+				token=localStorage.authToken;
+			}else{
+				return {};
+			}
+		}
+		if(type==='AUTH_LOGIN'){
+			//сохранить в localStorage token
+			//вернуть {token, payload: jwtDecode}
+			//если payload не обьект, то вернуть {}
+			//вернуть {token, payload}
+			localStorage.authToken=token;
+			let payload=jwtDecode(token);
+			if(typeof payload==='object'){
+				return {
+					token,
+					payload
+				}
+			}else{
+				return {};
+			}
+		}
+		if(type==='AUTH_LOGOUT'){
+			//удалить из localStorage token и вернуть {}
+			delete localStorage.authToken;
+			return {};
+		}
+		return state
+	}
+
+	const actionAuthLogin = token => ({type:'AUTH_LOGIN', token})
+	const actionAuthLogout = () => ({type:'AUTH_LOGOUT'})
+
+//---------------------------------------------------------------
+	function combineReducers(reducers){
+		return (state={},action)=>{
+			let newState={};
+			
+			for(const [reducerName, reducer] of Object.entries(reducers)){
+				let newSubState=reducer(state[reducerName],action);
+				if(newSubState!==state[reducerName]){
+					newState[reducerName]=newSubState;
+				}
+			}
+			if(0 !==Object.keys(newState).length){
+				return{
+					...state,
+					...newState
+				}
+			}else{
+				return state;
+			}
+			//перебрать все редьюсеры
+				//запустить каждый из них
+				//передать при этом в него его Ветвь общего state и экшен как есть
+				//получить newSubState
+				//если newSubState отлиается от входящего, то записать newSubState в newState 
+			//после цикла, если newState не пуст, то вернуть {...state, ...newState}
+			//иначе вернуть old state
+		}
+	}
+
+//-------------------------------------------------------------------------------
+	function promiseReducer(state={}, {type, status, payload, error, name}){
+    	if (type === 'PROMISE'){
+        	return {
+            	...state,
+            	[name]:{status, payload, error}
+        	}
+    	}
+    	return state;
+	}	
+	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 combinedReducer= combineReducers({promise:promiseReducer,auth:authReducer});
+	const store=createStore(combinedReducer)
+	console.log(store.getState());
+	//const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+	//store.dispatch(actionPromise('delay1000',delay(1000)))//{promise:{delay1000:''},auth:{}}
+	//store.dispatch(actionAuthLogin(token))//{promise:{delay1000:''},auth:{token...}}
+	
+
+	/*let store=createStore(authReducer);
+	store.subscribe(() => console.log(store.getState()));*/
+
+	btn1.onclick = () => store.dispatch(actionAuthLogin(token));
+	btn2.onclick = () => store.dispatch(actionAuthLogout());
+
+	/*const actionLogin = (login,password) =>
+		actionPromise('catById', gql('ЗАПРОС НА ЛОГИН', {login, password}))
+
+	const actionFullLogin=(login, password) =>
+		async dispatch=>{
+			let token=await dispatch(actionLogin(login, password))
+			if(token){
+				dispatch(actionLogin(actionAuthLogin(token)))
+			}
+		}*/
+
+//----------------------------------------------------------------------------------
+// const actionRegister // actionPromise
+// const actionFullLogin = (login, pasword) => //actionRegister + actionFullLogin
+// + интерфейс к этому - форму логина, регистрации, может повесить это на #/login #/register
+// + #/orders показывает Ваши бывшие заказы 
+// сделать actionMyOrders
+
+//-----------------------------------------------------------------------------------
+    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 backURL='http://shop-roles.asmer.fs.a-level.com.ua'
+	const gql=getGQL(backURL+'/graphql')
+
+//------------------------------------------Actions------------------------------------------------
+	const actionLogin = (login, password) => 
+	  actionPromise('login', gql(`query login($login: String, $password: String){
+	    login(login: $login, password: $password)
+	  }`, {login: login, password: password})
+	) 
+
+	const actionFullLogin = (login, password) => 
+	  async dispatch => {
+	      let token = await dispatch(actionLogin(login, password))
+	      if(token) {
+	          dispatch(actionAuthLogin(token))
+	      }
+	  }
+
+	const actionRegister = (login, password) => 
+	  actionPromise('register', gql(`mutation registration($login: String, $password:String) {
+	    UserUpsert(user: {login: $login, 
+	              password:$password, 
+	              nick: $login}){
+	      _id login
+	    }
+	  }`, {login: login, password: password})
+	)
+
+	const actionFullRegister = (login, password) => 
+	  async dispatch => {
+	      let log = await dispatch(actionRegister(login, password))
+	      if (log) {
+	        let token = await dispatch(actionLogin(login, password))
+	        if (token){
+	            dispatch(actionAuthLogin(token))
+	        }
+	    }
+	}
+	const actionRootCats = () =>
+	  actionPromise('rootCats', gql(`query {
+	      CategoryFind(query: "[{\\"parent\\":null}]"){
+	          _id name
+	      }
+	  }`))
+
+	const actionCatById = (_id) =>  //добавить подкатегории
+	  actionPromise('catById', gql(`query catById($q: String){
+	    CategoryFindOne(query: $q) {
+	    _id name goods{
+	      _id name price description images {
+	        url
+	      }
+	    }
+	    subCategories {
+	      name _id goods {
+	        _id name description
+	      }
+	    }
+	  }
+	}`, { q: JSON.stringify([{ _id }]) }))
+
+	const actionGoodById = (_id) =>
+	  actionPromise('goodById', gql(`query goodById($q: String){
+	        GoodFindOne(query: $q){
+	            _id name description price images{
+	                url
+	            }
+	        }
+	    }`, { q: JSON.stringify([{ _id }]) }))
+
+	store.dispatch(actionRootCats())
+
+//--------------------front-------------------------------------------------
+	store.subscribe(() => {
+	  	const {promise} = store.getState()
+	  	console.log('------------')
+	  	console.log(promise)
+	  	if (promise?.rootCats?.payload) {
+	    	aside.innerHTML = ''
+	    	for (const { _id, name } of promise?.rootCats?.payload) {
+	      		const link = document.createElement('a')
+	      		link.href = `#/category/${_id}`
+	      		link.innerText = name
+	      		aside.append(link)
+	    	}
+	  	}
+	})
+
+	store.subscribe(() => {
+	  	const { promise } = store.getState()
+	  	const [, route, _id] = location.hash.split('/')
+	  	if (promise?.catById?.payload && route === 'category') {
+	    	const { name } = promise.catById.payload
+	    	main.innerHTML = `<h1>${name}</h1>`
+	    	if (promise.catById.payload?.subCategories) {
+	      		for (let {_id, name} of promise.catById.payload.subCategories) {
+	        		const podCat = document.createElement('a')
+	        		podCat.className = 'sub_cat'
+	        		podCat.href = `#/category/${_id}`
+	        		podCat.textContent = name
+	       	 		main.append(podCat)
+	      		}
+	    	}
+	    	for (const { _id, name, price, images } of promise.catById.payload.goods) {
+	      		const card = document.createElement('div')
+	      		card.innerHTML = `<h2>${name}</h2>
+				            <img src="${backURL}/${images[0].url}"/>
+				                <div>
+				                    <b>Стоимость:</b> <b><sub>${price}UAH</sub></b>
+				                    <br><a href=#/good/${_id}>Страница товара</a>
+				                </div>`
+	      		main.append(card)
+	    	}
+	  	}
+	})
+//-------------------opis tovara-------------------------------
+	store.subscribe(() => {
+	  	const {promise} = store.getState()
+	 	const [, route, _id] = location.hash.split('/')
+	  	if (promise?.goodById?.payload && route === 'good' && location.href.includes(`#/good/${_id}`)) {
+	    	main.innerHTML = ``
+	    	let {_id, name, price, images, description} = promise.goodById.payload
+	      		let item = document.createElement('div')
+	      		item.className = 'good_item'
+	     		item.innerHTML =`<h2>${name}</h2>
+				    <img src="${backURL}/${images[0].url}"/>
+				    <div>
+				       <p><b>Стоимость:</b> <b><sub>${price} UAH</sub></b></strong></p>
+				       <p><b>Описание:</b> <sub>${description}</sub></p>
+				    </div>`
+	      	main.append(item)
+	  	}
+	})
+
+//-----------------Orders------------------------------------------------
+	const ActionMyOrders = () =>
+	actionPromise('orderfind', gql(`query orderfind{
+	    OrderFindOne(query:"[{}]"){
+	    	_id createdAt total orderGoods{
+	      		_id createdAt price count good{
+	        		_id name description images{
+	          			_id url
+	        		}
+	      		}
+	    	}
+	  	}
+	}`))
+
+	store.subscribe(() => {
+	  	const {promise} = store.getState()
+	  	const [,route, _id] = location.hash.split('/')
+	 	if (promise?.orderfind?.payload && route === 'orders'){
+	      	let counT = 0
+	      	main.innerHTML = ''
+	      	const {total, orderGoods} = promise.orderfind.payload
+	      	let title = document.createElement('h2')
+	      	title.textContent = 'Ваши заказы';
+	      	title.className = 'title'
+	      	let table = document.createElement('table')
+	      	let tr = document.createElement('thead')
+	      	tr.innerHTML = `<th>Дата заказа</th><th>Название</th><th>Количество</th><th>Цена</th>`
+	      	table.append(tr)
+	      	for (let {createdAt, count, good, price} of orderGoods){
+	          	counT += count;
+	          	let date = new Date(+createdAt).toLocaleDateString();
+	          	let tr = document.createElement('tr')
+	          	tr.innerHTML = `<td>${date}</td>
+	          		<td><figure><img src="${backURL}/${good.images[0].url}"><figcaption>${good.name}</figcaption></figure></td>
+	          		<td>${count}</td><td>${price}</td>`
+	          	table.append(tr)
+	      	}
+	      	let tr2 = document.createElement('tr')
+	      	tr2.innerHTML = `<th>Всего товаров в заказе: ${counT}</th><th>Общая сумма заказа: ${total}</th>`
+	      	table.append(tr2)
+	      	main.append(title, table)
+	  	}else if(!promise?.orderfind?.payload && route === 'orders'){
+	      	main.innerHTML = ''
+	      	let title = document.createElement('h2')
+	      	title.textContent = 'Вы еще не сделали заказ';
+	      	main.append(title)
+	  	}
+	})
+
+//-------------------Login-------------------------------------------------
+	store.subscribe(() => {
+	  	const {auth} = store.getState()
+	  	const {payload} = auth
+	  	if (payload?.sub ) {
+	      	ownCab.innerHTML = ''
+	      	ownCab.style.marginTop=10+'px'
+	      	ownCab.style.border=5+'px solid blue'
+	      	ownCab.style.width=17+'%'
+	      	const {id, login}  = payload.sub
+	      	const userName = document.createElement('div')
+	      	userName.innerHTML = `Hello, ${login}`
+	      	const userOrders = document.createElement('a')
+	      	userOrders.innerText = 'Your Orders'
+	      	userOrders.style.marginRight=10+'px'
+	      	userOrders.href = `#/orders/`
+	      	let logout = document.createElement('button')
+	      	logout.textContent = 'Exit'
+	      	logout.onclick = () => {
+	          	formId.innerHTML = ''
+	          	store.dispatch(actionAuthLogout());
+	      	}
+	      	ownCab.append(userName, userOrders, logout)
+	  	}else{
+	    	ownCab.innerHTML = ''
+	  	}
+	})
+
+	store.subscribe(() => {
+	  	const {auth} = store.getState()
+	  	if (!auth?.payload){
+	      	formId.innerHTML = '';
+	      	let loginBtn = document.createElement('a');
+	      	loginBtn.style.border=1+'px solid green'
+	      	let regBtn = document.createElement('a');
+	      	formId.append(loginBtn, regBtn)
+	      	loginBtn.textContent = 'LogIn'
+	      	regBtn.style.marginLeft=10+'px'
+	      	regBtn.style.border=1+'px solid yellow'
+	      	regBtn.textContent = 'Registration'
+	      	loginBtn.onclick = () => {
+	          	loginBtn.style.display = 'none'
+	          	regBtn.style.display= 'none'
+	          	let form = document.createElement('form')
+	          	let login = document.createElement('input')
+	          	login.type = 'text'
+	          	login.placeholder = 'Login'
+	          	let password = document.createElement('input')
+	          	password.placeholder = 'Password'
+	          	password.type = 'password'
+	          	let button = document.createElement('a')
+	          	button.textContent = 'Enter'
+	          	button.onclick = () => {
+	              	if (login.value !== '' && password.value !== '') {
+	                  	button.href = `#/login/${login.value}*${password.value}`
+	              	}
+	          	}
+	          	form.append(login, password, button)
+	          	formId.append(form)         
+	      	}
+	      	regBtn.onclick = () => {
+	          	loginBtn.style.display = 'none'
+	          	regBtn.style.display= 'none'
+	          	let form = document.createElement('form')
+	          	let login = document.createElement('input')
+	          	login.type = 'text'
+	          	login.placeholder = 'Login'
+	          	let password = document.createElement('input')
+	          	password.placeholder = 'Password'
+	          	password.type = 'password'
+	          	let button = document.createElement('a')
+	          	button.textContent = 'Registration'
+	          	button.onclick = () => {
+	              	if (login.value !== '' && password.value !== '') {
+	                  	button.href = `#/register/${login.value}*${password.value}`
+	              	}
+	          	}
+	          	form.append(login, password, button)
+	          	formId.append(form)
+	      	}
+	  	}
+	})
+
+//-----------------------------------------------------------------------
+	window.onhashchange = () => {
+	  const [, route, _id] = location.hash.split('/')
+
+	  const routes = {
+	    category() {
+	      store.dispatch(actionCatById(_id))
+	    },
+	    good() {
+	      store.dispatch(actionGoodById(_id)) 
+	      //задиспатчить actionGoodById
+	      console.log('ТОВАРОСТРАНИЦА')
+	    },
+	    login(){
+	      let data = _id.split('*')
+	      store.dispatch(actionFullLogin(data[0], data[1]))
+	    },
+	    register(){
+	      let data = _id.split('*')
+	      store.dispatch(actionFullRegister(data[0], data[1]))
+	    },
+	    orders(){
+	      store.dispatch(ActionMyOrders())
+	    }
+	  }
+	  if (route in routes)
+	    routes[route]()
+	}
+	window.onhashchange()
+	</script>
+</body>
+</html>