|
@@ -6,7 +6,8 @@
|
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
- function jwtDecode(token) {
|
|
|
+ const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms)) // для наглядности отладки
|
|
|
+ function jwtDecode(token) { // расщифровки токена авторизации
|
|
|
if (!token || typeof token != "string")
|
|
|
return undefined;
|
|
|
let tokenArr = token.split(".");
|
|
@@ -21,7 +22,7 @@
|
|
|
return undefined;
|
|
|
}
|
|
|
}
|
|
|
- function gql(url, query, vars) {
|
|
|
+ function gql(url, query, vars) { // формирование запроса GQl
|
|
|
let fetchSettings =
|
|
|
{
|
|
|
method: "POST",
|
|
@@ -39,7 +40,7 @@
|
|
|
};
|
|
|
return fetch(url, fetchSettings).then(res => res.json());
|
|
|
}
|
|
|
- function signIn(login, password, url) {
|
|
|
+ function signIn(login, password, url) { // авторизация через GQl
|
|
|
const loginQuery =
|
|
|
`query login($login:String, $password:String){
|
|
|
login(login:$login, password:$password)
|
|
@@ -52,40 +53,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
- function TreatAuth({ payload }, state) {
|
|
|
+ function treatAuth(action, state) { // обработка успешной авторизации и смена состояния
|
|
|
+ let payload = action.payload;
|
|
|
+ return setAuthState({ token: payload.data.login, payload: jwtDecode(payload.data.login) })
|
|
|
+ }
|
|
|
+ function setAuthState(authState, state) { //смена состояния авторизации
|
|
|
let newState = { ...state };
|
|
|
newState.auth = { ...state.auth };
|
|
|
- newState.auth.state = { token: payload.data.login, payload: jwtDecode(payload.data.login) };
|
|
|
+ newState.auth.state = authState;
|
|
|
return newState;
|
|
|
}
|
|
|
|
|
|
- function TreatBuy({ status, payload }) {
|
|
|
+ function treatBuy(action, state) { // обработка покупки
|
|
|
|
|
|
}
|
|
|
|
|
|
- const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
|
|
|
- function promiseReducer(state = {}, action) {
|
|
|
+ function promiseReducer(state = {}, action) { // диспетчер обработки
|
|
|
if (action) {
|
|
|
if (action.type === 'PROMISE') {
|
|
|
- let newState = TreatPayload(action, "buy", state, TreatBuy);
|
|
|
- newState = TreatPayload(action, "auth", newState, TreatAuth);
|
|
|
+ let newState = treatPayload(action, "buy", state, treatBuy);
|
|
|
+ newState = treatPayload(action, "auth", newState, treatAuth, () => setAuthState(undefined, state));
|
|
|
return newState;
|
|
|
}
|
|
|
}
|
|
|
return state;
|
|
|
}
|
|
|
|
|
|
- function TreatPayload(action, key, state, onPayloadFunc) {
|
|
|
- let result = state;
|
|
|
+
|
|
|
+ function treatPayload(action, key, state, onPayloadFunc, resetStateFunc) { //вызов функций обработки состояния по статусу с сбросом текущего состояния и обработки payload
|
|
|
+ let resultState = state;
|
|
|
let keyAction = action[key];
|
|
|
if (keyAction) {
|
|
|
- result = changeStatus(key, state, action);
|
|
|
+ resultState = changeStatus(key, state, action, resetStateFunc);
|
|
|
if (state !== result && keyAction.status == "FULLFILLED")
|
|
|
- onPayloadFunc(keyAction, result)
|
|
|
+ onPayloadFunc(keyAction, resultState)
|
|
|
}
|
|
|
- return result;
|
|
|
+ return resultState;
|
|
|
}
|
|
|
- function changeStatus(key, state, action) {
|
|
|
+ function changeStatus(key, state, action, resetStateFunc) { //обработка состояния по статусу
|
|
|
let result = state;
|
|
|
let actionData = action[key];
|
|
|
if (actionData) {
|
|
@@ -95,6 +100,11 @@
|
|
|
let newStateForKey = { ...stateData, ...{ status: status } };
|
|
|
result = { ...state };
|
|
|
result[key] = newStateForKey;
|
|
|
+ if (status == "PENDING") {
|
|
|
+ if (resetStateFunc)
|
|
|
+ resetStateFunc();
|
|
|
+ newStateForKey.error = undefined;
|
|
|
+ }
|
|
|
if (status == "REJECTED")
|
|
|
newStateForKey.error = actionData.error;
|
|
|
}
|
|
@@ -103,51 +113,59 @@
|
|
|
}
|
|
|
|
|
|
function createStore(reducer) {
|
|
|
- let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
|
|
|
- let cbs = [] //массив подписчиков
|
|
|
+ let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
|
|
|
+ let cbs = [] //массив подписчиков
|
|
|
|
|
|
- const getState = () => state //функция, возвращающая переменную из замыкания
|
|
|
- const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
|
|
|
- () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
|
|
|
+ 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 для работы
|
|
|
+ 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() //и запускаем подписчиков
|
|
|
+ const newState = reducer(state, action) //пробуем запустить редьюсер
|
|
|
+ if (newState !== state) { //проверяем, смог ли редьюсер обработать action
|
|
|
+ state = newState //если смог, то обновляем state
|
|
|
+ for (let cb of cbs) cb() //и запускаем подписчиков
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
- getState, //добавление функции getState в результирующий объект
|
|
|
+ getState, //добавление функции getState в результирующий объект
|
|
|
dispatch,
|
|
|
- subscribe //добавление subscribe в объект
|
|
|
+ subscribe //добавление subscribe в объект
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function actionPromise({ name, promise }) {
|
|
|
return async function Exec(dispatch) {
|
|
|
- let action = { type: "PROMISE" };
|
|
|
- action[name] = { status: "PENDING" };
|
|
|
- dispatch(action) //сигнализируем redux, что промис начался
|
|
|
-
|
|
|
+ dispatch(actionPending(name)) //сигнализируем redux, что промис начался
|
|
|
try {
|
|
|
- const payload = await promise //ожидаем промиса;
|
|
|
- action = { type: "PROMISE" };
|
|
|
- action[name] = { status: "FULLFILLED", payload: payload };
|
|
|
- dispatch(action) //сигнализируем redux, что промис успешно выполнен
|
|
|
- return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
|
|
|
+ const payload = await promise //ожидаем промиса;
|
|
|
+ dispatch(actionFulfilled(name, payload)); //сигнализируем redux, что промис успешно выполнен
|
|
|
+ return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
|
|
|
}
|
|
|
catch (error) {
|
|
|
- action = { type: "PROMISE" };
|
|
|
- action[name] = { status: "REJECTED", error: error };
|
|
|
- dispatch(action) //в случае ошибки - сигнализируем redux, что промис несложился
|
|
|
+ dispatch(actionRejected(name, error)) //в случае ошибки - сигнализируем redux, что промис несложился
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
+ const actionPending = (name, error) => {
|
|
|
+ let result = { type: 'PROMISE' };
|
|
|
+ result[name] = { status: 'REJECTED' };
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ const actionFulfilled = (name, payload) => {
|
|
|
+ let result = { type: 'PROMISE' };
|
|
|
+ result[name] = { status: 'FULFILLED', payload };
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ const actionRejected = (name, error) => {
|
|
|
+ let result = { type: 'PROMISE' };
|
|
|
+ result[name] = { status: 'REJECTED', error };
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
const store = createStore(promiseReducer)
|
|
|
|