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 } } const getGQL = url => (query, variables) => fetch(url, { method: 'POST', headers: { "Content-Type": "application/json", // 'Accept' : 'application/json', ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} : {}) }, body: JSON.stringify({query, variables}) }).then(res => res.json()) .then(data => { if (data.data){ return Object.values(data.data)[0] } else throw new Error(JSON.stringify(data.errors)) }) const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua' const gql = getGQL(backendURL + '/graphql'); const jwtDecode = token => { try{ return JSON.parse(atob(token.split('.')[1])); } catch(e){ console.log(e.name, e.message); } } function authReducer(state, {type, token}){ if (state === undefined){ if(localStorage.authToken){ type = 'AUTH_LOGIN'; token = localStorage.authToken } } if(type === 'AUTH_LOGIN'){ let payload = jwtDecode(token); if (payload){ localStorage.authToken = token return {token, payload} } } if(type === 'AUTH_LOGOUT'){ localStorage.removeItem("authToken") return {} } return state || {} } function promiseReducer(state={}, {type, name, status, payload, error}){ if (type === 'PROMISE'){ return { ...state, [name]:{status, payload, error} } } return state } const combineReducers = (reducers) => (state={}, action) => { let newState = {} for (const [reducerName, reducer] of Object.entries(reducers)){ let subNewState = reducer(state[reducerName],action) if(subNewState !== state[reducerName]){ newState = { ...newState, [reducerName] : subNewState } } } if(Object.keys(newState).length > 0){ return { ...state,...newState } } return state } const actionAuthLogin = (token) => ({type: 'AUTH_LOGIN', token}); const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'}); const actionPending = name => ({type:'PROMISE',name, status: 'PENDING'}) const actionFulfilled = (name,payload) => ({type:'PROMISE',name, status: 'FULFILLED', payload}) const actionRejected = (name,error) => ({type:'PROMISE',name, status: 'REJECTED', error}) const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms)) const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer})); store.subscribe(() => console.log(store.getState())) const actionPromise = (name, promise) => async dispatch => { dispatch(actionPending(name)) try { let payload = await promise dispatch(actionFulfilled(name, payload)) return payload } catch(error){ dispatch(actionRejected(name, error)) } } const actionFullRegister = (log, pass) => async dispatch => { let user = await dispatch( actionPromise('register', gql( `mutation register($login: String, $password: String) { UserUpsert(user: {login: $login, password: $password}) { _id login } }`, {login : log, password : pass})) ) if(user){ dispatch(actionFullLogin(log, pass)); } } const actionFullLogin = (log, pass) => async dispatch => { let token = await dispatch( actionPromise('login', gql(`query login($login: String, $password: String) { login(login: $login, password: $password) }`, {login: log, password: pass})) ) if(token){ dispatch(actionAuthLogin(token)) } } store.dispatch(actionFullRegister('bd2404', '2404')) // store.dispatch(actionAuthLogout()); const actionLuke = () => actionPromise('luke', fetch('https://swapi.dev/api/people/1') .then(res => res.json())) store.dispatch(actionLuke()) const actionCategoryById = (_id) => actionPromise('catById', gql(`query catById($q: String){ CategoryFindOne(query: $q){ _id name goods { _id name price images { url } } } }`, {q: JSON.stringify([{_id}])})); store.dispatch(actionCategoryById("5dc458985df9d670df48cc47")); const actionGoodById = (_id) => actionPromise('goodById', gql(`query goodByid($goodId: String) { GoodFindOne(query: $goodId) { name price description images { url } } }`, {goodId: JSON.stringify([{_id}])})) store.dispatch(actionGoodById("5dc4a3e15df9d670df48cc6b")); const actionRootCategories = () => actionPromise('rootCats', gql(`query { CategoryFind(query: "[{\\"parent\\":null}]"){ _id name } }`)) store.dispatch(actionRootCategories()); const actionNewOrder = (order) => actionPromise('newOrder', gql( `mutation newOrder($order: OrderInput) { OrderUpsert(order: $order) { _id total } }`, {order : order} ) ) let order = { orderGoods: [ {count: 2, good: {_id: "5dcac57d6d09c45440d14cfc"}}, {count: 2, good: {_id: "5dcac9ba6d09c45440d14d02"}}, {count: 1, good: {_id: "5dc8844e0e36db246e3049bd"}}, ] } // store.dispatch(actionNewOrder(order)); const actionOrders = () => actionPromise('allOrders', gql(`query findOrder($q: String) { OrderFind(query: $q) { _id total orderGoods { count good { name price } } } }`, {q: JSON.stringify([{}])})) // store.dispatch(actionOrders())