123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- <Header>Gql</Header>
- <body>
- <script>
- function jwtDecode(token) { // расщифровки токена авторизации
- if (!token || typeof token != "string")
- return undefined;
- let tokenArr = token.split(".");
- if (tokenArr.length != 3)
- return undefined;
- try {
- let tokenJsonStr = atob(tokenArr[1]);
- let tokenJson = JSON.parse(tokenJsonStr);
- return tokenJson;
- }
- catch {
- return undefined;
- }
- }
- function combineReducers(reducers) {
- function totalReducer(totalState = {}, action) {
- const newTotalState = {} //объект, который будет хранить только новые состояния дочерних редьюсеров
- //цикл + квадратные скобочки позволяют написать код, который будет работать с любыми количеством дочерных редьюсеров
- for (const [reducerName, childReducer] of Object.entries(reducers)) {
- const newState = childReducer(totalState[reducerName], action) //запуск дочернего редьюсера
- if (newState !== totalState[reducerName]) { //если он отреагировал на action
- newTotalState[reducerName] = newState //добавляем его в newTotalState
- }
- }
- //Универсальная проверка на то, что хотя бы один дочерний редьюсер создал новый стейт:
- if (Object.values(newTotalState).length) {
- return { ...totalState, ...newTotalState } //создаем новый общий стейт, накладывая новый стейты дочерних редьюсеров на старые
- }
- return totalState //если экшен не был понят ни одним из дочерних редьюсеров, возвращаем общий стейт как был.
- }
- return totalReducer
- }
- function promiseReducer(state = {}, action) { // диспетчер обработки
- if (action) {
- if (action.type === 'PROMISE') {
- let newState = { ...state };
- newState[action.name] = { status: action.status, payload: action.payload, error: action.error };
- return newState;
- }
- }
- return state;
- }
- function authReducer(state = {}, action) { // диспетчер обработки login
- if (action) {
- if (action.type === 'AUTH_LOGIN') {
- let newState = { ...state };
- newState.token = action.token;
- newState.payload = jwtDecode(action.token);
- if (!newState.payload) {
- newState.token = undefined;
- }
- return newState;
- }
- else if (action.type === 'AUTH_LOGOUT') {
- let newState = { ...state };
- newState.token = undefined;
- newState.payload = undefined;
- return newState;
- }
- }
- return state;
- }
- function createStore(reducer) {
- let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
- let cbs = [] //массив подписчиков
- const getState = () => state //функция, возвращающая переменную из замыкания
- const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
- () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
- function dispatch(action) {
- if (typeof action === 'function') { //если action - не объект, а функция
- return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
- }
- const newState = reducer(state, action) //пробуем запустить редьюсер
- if (newState !== state) { //проверяем, смог ли редьюсер обработать action
- state = newState //если смог, то обновляем state
- for (let cb of cbs) cb() //и запускаем подписчиков
- }
- }
- return {
- getState, //добавление функции getState в результирующий объект
- dispatch,
- subscribe //добавление subscribe в объект
- }
- }
- function gql(url, query, vars) {
- let fetchSettings =
- {
- method: "POST",
- headers:
- {
- "Content-Type": "application/json",
- "Accept": "application/json"
- },
- body: JSON.stringify(
- {
- query: query,
- variables: vars
- })
- };
- return fetch(url, fetchSettings).then(res => res.json());
- }
- function actionPromiseGql(name, promise) {
- return async function Exec(dispatch) {
- dispatch(actionPending(name)) //сигнализируем redux, что промис начался
- try {
- const payload = await promise; //ожидаем промиса;
- let result = Object.values(payload.data)[0];
- dispatch(actionFulfilled(name, result)); //сигнализируем redux, что промис успешно выполнен
- return result; //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
- }
- catch (error) {
- dispatch(actionRejected(name, error)) //в случае ошибки - сигнализируем redux, что промис несложился
- }
- };
- }
- function actionAuthGql(promise) {
- return async function Exec(dispatch) {
- try {
- const payload = await promise //ожидаем промиса;
- let result = Object.values(payload.data)[0];
- dispatch(actionAuthLogin(result)); //сигнализируем redux, что промис успешно выполнен
- return result; //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
- }
- catch (error) {
- dispatch(actionLogOut()) //в случае ошибки - сигнализируем redux, что промис несложился
- }
- };
- }
- const actionPending = (name) => ({ type: 'PROMISE', name: name, status: 'PENDING' });
- const actionFulfilled = (name, payload) => ({ type: 'PROMISE', name: name, payload: payload, status: 'FULFILLED' });
- const actionRejected = (name, error) => ({ type: 'PROMISE', name: name, error: error, status: 'REJECTED' });
- const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token });
- const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' });
- const gqlRootCats = () => {
- const catQuery = `query roots {
- CategoryFind(query: "[{\\"parent\\": null }]") {
- _id name
- }}`;
- return gql("http://shop-roles.node.ed.asmer.org.ua/graphql", catQuery);
- }
- const actionRootCats = () =>
- actionPromiseGql('rootCats', gqlRootCats());
- ///////////////////////////////////////
- const gqlCategoryFindOne = (id) => {
- const catQuery = `query CategoryFindOne($q: String) {
- CategoryFindOne(query: $q) {
- _id name
- parent { _id name }
- subCategories { _id name }
- goods { _id name price description
- images { url }
- }
- }
- }`;
- return gql("http://shop-roles.node.ed.asmer.org.ua/graphql", catQuery, { q: `[{\"_id\": \"${id}\"}]` });
- }
- const actionCategoryFindOne = (id) =>
- actionPromiseGql('catFindOne', gqlCategoryFindOne(id));
- /////////////////////////////////////////
- const gqlGoodFindOne = (id) => {
- const catQuery = `
- query GoodFindOne($q: String) {
- GoodFindOne(query: $q) {
- _id name price description
- images {
- url
- }
- }
- }
- `;
- return gql("http://shop-roles.node.ed.asmer.org.ua/graphql", catQuery, { q: `[{\"_id\": \"${id}\"}]` });
- }
- const actionGoodFindOne = (id) =>
- actionPromiseGql('goodsFindOne', gqlGoodFindOne(id));
- //////////////////////////////////
- const gqlAuthLogin = (login, password) => {
- const loginQuery = `query login($login:String, $password:String){
- login(login:$login, password:$password)
- }`;
- return gql("http://shop-roles.node.ed.asmer.org.ua/graphql", loginQuery, { login: login, password: password });
- }
- const actionAuthLoginFunc = (login, password) =>
- actionAuthGql(gqlAuthLogin(login, password));
- ////////////////////////////////////////
- const store = createStore(combineReducers({ promiseReducer, authReducer }));
- store.subscribe(() => {
- console.log(store.getState())
- });
- //store.dispatch(actionRootCats());
- //store.dispatch(actionCategoryFindOne("6262ca7dbf8b206433f5b3d1"));
- //store.dispatch(actionGoodFindOne("62d3099ab74e1f5f2ec1a125"));
- store.dispatch(actionAuthLoginFunc("Berg", "123456789"));
- </script>
- </body>
|