Gennadysht 2 роки тому
батько
коміт
20c96582d2

+ 0 - 0
js/.vscode/launch.json


+ 93 - 0
js/19_Redux_pro/hw_19_01 _1.html

@@ -0,0 +1,93 @@
+<header>reducers</header>
+
+<body>
+    <div id="testDiv">
+        Test
+    </div>
+
+    <script>
+        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 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 actionPromise({ name, promise }) {
+            return async function Exec(dispatch) {
+                dispatch(actionPending(name))               //сигнализируем redux, что промис начался
+                try {
+                    const payload = await promise           //ожидаем промиса;
+                    dispatch(actionFulfilled(name, payload)); //сигнализируем redux, что промис успешно выполнен
+                    return payload                              //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
+                }
+                catch (error) {
+                    dispatch(actionRejected(name, error))           //в случае ошибки - сигнализируем 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 store = createStore(promiseReducer)
+
+        store.subscribe(() => {
+            console.log(store.getState())
+        });
+
+        store.dispatch(actionPromise('luke', fetch("https://swapi.dev/api/people/1").then(res => res.json())))
+
+
+        //let execFunc = actionPromise({ name: "auth", promise: signIn("test457", "123123", "http://shop-roles.node.ed.asmer.org.ua/graphql") });
+        store.dispatch(execFunc);
+
+        /*
+                const actionPending = () => ({ type: 'PROMISE', status: 'PENDING' })
+                const actionFulfilled = payload => ({ type: 'PROMISE', status: 'FULFILLED', payload })
+                const actionRejected = error => ({ type: 'PROMISE', status: 'REJECTED', error })
+        
+        
+                store.subscribe(() => console.log(store.getState()))
+        
+                store.dispatch({ type: 'COUNTER_INC' })
+                store.dispatch({ type: 'BOOLEAN_SET' })
+                store.dispatch({ type: 'COUNTER_INC' })
+                store.dispatch({ type: 'BOOLEAN_TOGGLE' })
+                store.dispatch({ type: 'COUNTER_DEC' })
+                store.dispatch({ type: 'ДИЧЬ' }) //не вызывает подписчика
+        */
+
+    </script>
+</body>

+ 207 - 0
js/19_Redux_pro/hw_19_main traff copy.html

@@ -0,0 +1,207 @@
+<header>reducers</header>
+
+<body>
+    <div id="testDiv">
+        Test
+    </div>
+
+    <script>
+        const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))  // для наглядности отладки
+        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 gql(url, query, vars) {             // формирование запроса GQl
+            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 signIn(login, password, url) {             // авторизация через GQl
+            const loginQuery =
+                `query login($login:String, $password:String){
+                login(login:$login, password:$password)
+            }`;
+            return gql(
+                url,
+                loginQuery,
+                { login: login, password: password });
+        }
+
+
+
+        function treatAuth(action, state) {                         // обработка успешной авторизации  и смена состояния
+            let payload = action.payload;
+            return setAuthState({ token: payload.data.login, payload: jwtDecode(payload.data.login) })
+        }
+        function setAuthState(authPayload, state) {                   //смена состояния авторизации
+            let newState = { ...state };
+            newState.auth = { ...state.auth };
+            newState.auth.payload = authPayload;
+            return newState;
+        }
+
+        function treatBuy(action, state) {                         // обработка покупки 
+
+        }
+
+        function promiseReducer(state = {}, action) {                   // диспетчер обработки
+            if (action) {
+                if (action.type === 'PROMISE') {
+                    let newState = treatPayload(action, "buy", state, treatBuy);
+                    newState = treatPayload(action, "auth", newState, treatAuth);
+                    return newState;
+                }
+            }
+            return state;
+        }
+
+
+        function treatPayload(action, name, state, onPayloadFunc) { //вызов функций обработки состояния по статусу с сбросом текущего состояния и обработки payload
+            let resultState = state;
+            let actionName = action.name;
+            if (actionName == name) {
+                resultState = changeStatus(name, state, action, resetStateFunc);
+                if (state !== resultState && action.status == "FULLFILLED")
+                    resultState = onPayloadFunc(action, resultState)
+            }
+            return resultState;
+        }
+        function changeStatus(name, state, action) {                            //обработка состояния по статусу
+            let result = { ...state };
+            result[name] = { status: status, payload: undefined, error: actionData.error };
+            return result;
+        }
+
+        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 actionPromise({ name, promise }) {
+            return async function Exec(dispatch) {
+                dispatch(actionPending(name))               //сигнализируем redux, что промис начался
+                try {
+                    const payload = await promise           //ожидаем промиса;
+                    dispatch(actionFulfilled(name, payload)); //сигнализируем redux, что промис успешно выполнен
+                    return payload                              //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
+                }
+                catch (error) {
+                    dispatch(actionRejected(name, error))           //в случае ошибки - сигнализируем 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 store = createStore(promiseReducer)
+
+        store.subscribe(() => {
+            let state = store.getState();
+            if (state.buy) {
+                if (state.buy.status == "PENDING")
+                    testDiv.style.color = "orange";
+                else if (state.buy.status == "FULLFILLED")
+                    testDiv.style.color = "green";
+                else if (state.buy.status == "REJECTED") {
+                    testDiv.style.color = "red";
+                    testDiv.innerText = state.buy.errorMessage;
+                }
+            }
+        });
+
+        let execFunc = actionPromise({ name: "auth", promise: signIn("test457", "123123", "http://shop-roles.node.ed.asmer.org.ua/graphql") });
+        store.dispatch(execFunc);
+
+        /*
+                const actionPending = () => ({ type: 'PROMISE', status: 'PENDING' })
+                const actionFulfilled = payload => ({ type: 'PROMISE', status: 'FULFILLED', payload })
+                const actionRejected = error => ({ type: 'PROMISE', status: 'REJECTED', error })
+        
+        
+                store.subscribe(() => console.log(store.getState()))
+        
+                store.dispatch({ type: 'COUNTER_INC' })
+                store.dispatch({ type: 'BOOLEAN_SET' })
+                store.dispatch({ type: 'COUNTER_INC' })
+                store.dispatch({ type: 'BOOLEAN_TOGGLE' })
+                store.dispatch({ type: 'COUNTER_DEC' })
+                store.dispatch({ type: 'ДИЧЬ' }) //не вызывает подписчика
+        */
+
+    </script>
+</body>
+
+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
+}
+
+//////
+
+
+/////

+ 59 - 41
js/19_Redux_pro/hw_19_01.html

@@ -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)
 

+ 230 - 0
js/20/lesson20.html

@@ -0,0 +1,230 @@
+<html>
+
+<head>
+    <title>GQL</title>
+    <meta charset='utf8' />
+    <!--<style>
+            #mainContainer {
+                display: flex;
+            }
+            #aside {
+                width: 30%;
+            }
+            #aside > a{
+                display: block;
+            }
+            header {
+                min-height: 100px;
+                background-color: #AAA;
+            }
+        -->
+    </style>
+</head>
+
+<body>
+    <header>
+        <div id='cartIcon'></div>
+    </header>
+    <div id='mainContainer'>
+        <aside id='aside'>
+            Категории
+            Сюда надо воткнуть список корневых категорий интернет-магазина
+            <a href="#/people/1">Luke</a>
+            <a href="#/people/2">C-3PO</a>
+            <a href="#/people/3">R2-D2</a>
+            <a href="#/people/4">Dart Vader</a>
+
+            <a href="#/films/4">new hope</a>
+            <a href="#/films/5">пятая часть</a>
+            <a href="#/films/3">шестая часть</a>
+        </aside>
+        <main id='main'>
+            Контент
+        </main>
+    </div>
+    <script>
+        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, которая удаляет подписчика из списка
+
+            const 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(state) //и запускаем подписчиков
+                }
+            }
+
+            return {
+                getState, //добавление функции getState в результирующий объект
+                dispatch,
+                subscribe //добавление subscribe в объект
+            }
+        }
+
+        function combineReducers(reducers) {
+            function totalReducer(state = {}, action) {
+                const newTotalState = {}
+                for (const [reducerName, reducer] of Object.entries(reducers)) {
+                    const newSubState = reducer(state[reducerName], action)
+                    if (newSubState !== state[reducerName]) {
+                        newTotalState[reducerName] = newSubState
+                    }
+                }
+                if (Object.keys(newTotalState).length) {
+                    return { ...state, ...newTotalState }
+                }
+                return state
+            }
+
+            return totalReducer
+        }
+
+        const reducers = {
+            promise: promiseReducer, //допилить много имен для многих промисо
+            //auth: authReducer,     //часть предыдущего ДЗ
+            //cart: cartReducer,     //часть предыдущего ДЗ
+        }
+
+        const totalReducer = combineReducers(reducers)
+        function promiseReducer(state = {}, { type, status, payload, error }) {
+            if (type === 'PROMISE') {
+                //имена добавить
+                return { status, payload, error }
+            }
+            return state
+        }
+        //имена добавить
+        const actionPending = () => ({ type: 'PROMISE', status: 'PENDING' })
+        const actionFulfilled = payload => ({ type: 'PROMISE', status: 'FULFILLED', payload })
+        const actionRejected = error => ({ type: 'PROMISE', status: 'REJECTED', error })
+
+        //имена добавить
+        const actionPromise = promise =>
+            async dispatch => {
+                dispatch(actionPending()) //сигнализируем redux, что промис начался
+                try {
+                    const payload = await promise //ожидаем промиса
+                    dispatch(actionFulfilled(payload)) //сигнализируем redux, что промис успешно выполнен
+                    return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
+                }
+                catch (error) {
+                    dispatch(actionRejected(error)) //в случае ошибки - сигнализируем redux, что промис несложился
+                }
+            }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+        const store = createStore(totalReducer) //не забудьте combineReducers если он у вас уже есть
+        store.subscribe(() => console.log(store.getState()))
+
+        const drawPeople = (state) => {
+            const [, route] = location.hash.split('/')
+            if (route !== 'people') return
+
+            const { status, payload, error } = store.getState().promise//.имя другое
+            if (status === 'PENDING') {
+                main.innerHTML = `<img src='https://cdn.dribbble.com/users/63485/screenshots/1309731/infinite-gif-preloader.gif' />`
+            }
+            if (status === 'FULFILLED') {
+                const { name, mass, eye_color, films } = payload
+                main.innerHTML = `<h1>${name}</h1>
+                         <section>ЖЫРНОСТЬ: ${mass}кг</section>
+                         <section style="color: ${eye_color}">Цвет глаз</section>
+                         `
+                for (const filmUrl of films) {
+                    const filmId = filmUrl.split('/films/')[1].slice(0, -1)
+                    main.innerHTML += `<a href="#/films/${filmId}">Фильм №${filmId}</a>`
+                }
+            }
+        }
+
+        store.subscribe(drawPeople)
+
+        store.subscribe(() => {
+            const [, route] = location.hash.split('/')
+            if (route !== 'films') return
+
+            const { status, payload, error } = store.getState().promise//.имя одно
+            if (status === 'PENDING') {
+                main.innerHTML = `<img src='https://cdn.dribbble.com/users/63485/screenshots/1309731/infinite-gif-preloader.gif' />`
+            }
+            if (status === 'FULFILLED') {
+                const { title, opening_crawl, characters } = payload
+                main.innerHTML = `<h1>${title}</h1>
+                         <p>${opening_crawl}</p>
+                         `
+                for (const peopleUrl of characters) {
+                    const peopleId = peopleUrl.split('/people/')[1].slice(0, -1)
+                    main.innerHTML += `<a href="#/people/${peopleId}">Герой №${peopleId}</a>`
+                }
+            }
+        })
+
+        const actionGetPeople = id =>  //имя другое
+            actionPromise(fetch(`https://swapi.dev/api/people/${id}`).then(res => res.json()))
+
+        const actionGetFilm = id =>
+            actionPromise(fetch(`https://swapi.dev/api/films/${id}`).then(res => res.json()))
+
+        const actionSomePeople = () =>
+            actionPromise(fetch(`https://swapi.dev/api/people/`).then(res => res.json()))
+
+        store.dispatch(actionSomePeople())
+
+        store.subscribe(() => {
+            const { status, payload, error } = store.getState().promise//.имя третье
+            if (status === 'FULFILLED' && payload.results) {
+                aside.innerHTML = ''
+                for (const { url: peopleUrl, name } of payload.results) {
+                    const peopleId = peopleUrl.split('/people/')[1].slice(0, -1)
+                    aside.innerHTML += `<a href="#/people/${peopleId}">${name}</a>`
+                }
+            }
+        })
+
+        window.onhashchange = () => {
+            const [, route, _id] = location.hash.split('/')
+
+            const routes = {
+                people() {
+                    console.log('People', _id)
+                    store.dispatch(actionGetPeople(_id))
+                },
+                films() {
+                    store.dispatch(actionGetFilm(_id))
+                },
+                //category() {
+                //store.dispatch(actionCategoryById(_id))
+                //},
+                //good(){
+                ////тут был store.dispatch goodById
+                //console.log('good', _id)
+                //},
+                login() {
+                    console.log('А ТУТ ЩА ДОЛЖНА БЫТЬ ФОРМА ЛОГИНА')
+                    //нарисовать форму логина, которая по нажатию кнопки Login делает store.dispatch(actionFullLogin(login, password))
+                },
+                //register(){
+                ////нарисовать форму регистрации, которая по нажатию кнопки Login делает store.dispatch(actionFullRegister(login, password))
+                //},
+            }
+
+            if (route in routes) {
+                routes[route]()
+            }
+        }
+
+        window.onhashchange()
+    </script>
+</body>
+
+</html>