function createStore(reducer){ let state = reducer(undefined, {}) let cbs = [] function dispatch(action){ if (typeof action === 'function'){ return action(dispatch) } const newState = reducer(state, action) if (state !== newState){ state = newState cbs.forEach(cb => cb()) } } return { dispatch, subscribe(cb){ cbs.push(cb) return () => cbs = cbs.filter(c => c !== cb) }, getState(){ return state } } } function promiseReducer(state={}, {type, status, payload, error, name}){ if (type === 'PROMISE'){ return { ...state, [name]:{status, payload, error} } } return state } const store = createStore(promiseReducer) const unsubscribe1 = store.subscribe(() => console.log(store.getState())) 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 delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms)) 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 => { return function(query, variables={}) { return fetch(url, { method: "POST", headers: {"Content-Type": "application/json", ...(localStorage.authToken ? {Authorization: localStorage.authToken} : {}) }, body: JSON.stringify({query, variables}) }).then(resp => resp.json()) .then(data => { if ("errors" in data) { throw new Error('ашипка, угадывай што не так') } else { return data.data[Object.keys(variables)[0]] } }) } } let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql') const goodById = goodId => { let id = `[{"_id":"${goodId}"}]` return shopGQL(` query good($id:String){ GoodFindOne(query: $id) { name description price images { _id text url } categories { _id name } } }`, {GoodFindOne: '', id }) } const actionGoodById = id => actionPromise('goodById', goodById(id)) const actionRootCategories = () => actionPromise('rootCategories', shopGQL(` query cats($query:String){ CategoryFind(query:$query){ _id name } } `, {CategoryFind:'', query: JSON.stringify([{parent:null}])})) const actionCategoryById = (_id) => actionPromise('catById', shopGQL(`query catById($query:String){ CategoryFindOne(query:$query){ _id name goods{ _id name price description images{ url } } } }`, {CategoryFindOne:'', query: JSON.stringify([{_id}])})) store.dispatch(actionRootCategories()) window.onhashchange = () => { let {1: route, 2:id} = location.hash.split('/') if (route === 'categories'){ store.dispatch(actionCategoryById(id)) } if (route === 'good'){ store.dispatch(actionGoodById(id)) } } function drawMainMenu(){ let cats = store.getState().rootCategories.payload if (cats){ //каждый раз дорисовываются в body aside.innerText = '' for (let {_id, name} of cats){ let catA = document.createElement('a') catA.href = `#/categories/${_id}` catA.innerText = name aside.append(catA) } } } store.subscribe(drawMainMenu) store.subscribe(() => { const {1: route, 2:id} = location.hash.split('/') if (route === 'categories'){ const catById = store.getState().catById?.payload if (catById){ main.innerText = '' let categoryName = document.createElement('div') categoryName.innerText = catById.name categoryName.style.fontSize = '25px' categoryName.style.fontWeight = 'bold' main.append(categoryName) for (let {_id, name} of catById.goods){ let good = document.createElement('a') good.href = `#/good/${_id}` good.innerText = name let btn = document.createElement('button') btn.style.cursor = 'pointer' btn.innerText = 'купыть' main.append(good, btn) } } } if (route === 'good'){ const goodById = store.getState().goodById?.payload if (goodById){ main.innerText = '' let {name, description, price} = goodById let goodName = document.createElement('div') goodName.innerText = name goodName.style.fontSize = '35px' goodName.style.fontWeight = 'bold' goodName.style.marginBottom = '25px' let goodDescription = document.createElement('div') goodDescription.innerText = description goodDescription.style.marginBottom = '25px' let goodPrice = document.createElement('div') goodPrice.innerText = 'Цена: ' + price goodPrice.style.marginBottom = '5px' let btn = document.createElement('button') btn.style.cursor = 'pointer' btn.innerText = 'купыть' main.append(goodName, goodDescription, goodPrice, btn) } } })