Browse Source

HW<MyProject> done

Levshin95 1 year ago
parent
commit
61c58c0053

BIN
Модуль JS/Loading_icon.gif


+ 32 - 0
Модуль JS/index.html

@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>GQL</title>
+        <meta charset='utf8' />
+        <style>
+            #mainContainer {
+                display: flex;
+            }
+            #aside {
+                width: 30%;
+            }
+            #aside > a{
+                display: block;
+            }
+        </style>
+    </head>
+    <body>
+        <header>
+            <div id='cartIcon'></div>
+        </header>
+        <div id='mainContainer'>
+            <aside id='aside'>
+                Категории
+            </aside>
+            <main id='main'>
+                Контент
+            </main>
+        </div>
+        <script src='index.js'></script>
+    </body>
+</html>

+ 239 - 0
Модуль JS/index.js

@@ -0,0 +1,239 @@
+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))
+    )
+                             
+    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() //и запускаем подписчиков
+        }
+    }
+    
+    return {
+        getState, //добавление функции getState в результирующий объект
+        dispatch,
+        subscribe //добавление subscribe в объект
+    }
+}
+
+function jwtDecode(token) {
+    try{
+        return JSON.parse(atob(token.split('.')[1]))
+    }
+    catch(e){
+
+    }
+}
+
+function authReducer(state = {}, {type, token}) {
+    if(type === "AUTH_LOGIN"){
+        const payload = jwtDecode(token)
+        if(payload) {
+            return {
+                token, payload
+            }
+        }
+    }
+    if(type === "AUTH_LOGOUT") {
+        return {}
+    }
+    return state
+}
+
+const actionAuthLogin = (token) =>
+    (dispatch, getState) => {
+        const oldState = getState()
+        dispatch({type: "AUTH_LOGIN", token})
+        const newState = getState()
+        if(oldState !== newState) {
+            localStorage.authToken = token
+        }
+    }
+
+const actionAuthLogout = () => 
+    dispatch => {
+        dispatch({type: "AUTH_LOGOUT"})
+        localStorage.removeItem('authToken')
+    }
+
+    function promiseReducer(state={}, {type, name, status, payload, error}){
+        ////?????
+        //ОДИН ПРОМИС:
+        //состояние: PENDING/FULFILLED/REJECTED
+        //результат
+        //ошибка:
+        //{status, payload, error}
+        //{
+        //       name1:{status, payload, error}   
+        //       name2:{status, payload, error}   
+        //       name3:{status, payload, error}   
+        //}
+        if (type === 'PROMISE'){
+            return {
+                ...state,
+                [name]:{status, payload, error}
+            }
+        }
+        return state
+    }
+
+const actionPending   = (name) => ({type: 'PROMISE', status: 'PENDING', name})
+const actionFulfilled = (name, payload) => ({type: 'PROMISE', status: 'FULFILLED', name, payload})
+const actionRejected  = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
+
+const actionPromise   = (name, promise) => 
+    async (dispatch) => {
+        try { 
+            dispatch(actionPending(name))
+            let payload = await promise
+            dispatch(actionFulfilled(name, payload))
+            return payload
+        }
+        catch(e){
+            dispatch(actionRejected(name, e))
+        }
+    }
+
+const delay = (ms) => new Promise((ok) => setTimeout(() => ok(ms), ms))
+
+function combineReducers(reducers) {
+    function combinedReducer(combinedState={}, action){
+        const newCombinedState = {}
+        for (const [reducerName, reducer] of Object.entries(reducers)){
+            const newSubState = reducer(combinedState[reducerName], action)
+            if(newSubState !== combinedState[reducerName]){
+                newCombinedState[reducerName] = newSubState
+            }
+        }
+        if (Object.keys(newCombinedState).length === 0){
+            return combinedState
+        }
+        return {...combinedState, ...newCombinedState}
+    }
+
+    return combinedReducer
+}
+
+const store = createStore(combineReducers({auth: authReducer, promise: promiseReducer})) //не забудьте combineReducers если он у вас уже есть
+if(localStorage.authToken) {
+    store.dispatch(actionAuthLogin(localStorage.authToken))
+}
+//const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}))
+store.subscribe(() => console.log(store.getState()))
+
+/* store.dispatch(actionPromise("delay1000", delay(1000)))
+store.dispatch(actionPromise("delay3000", delay(3000))) */
+
+//store.dispatch(actionPromise('delay1000')) 
+
+//delay(1000).then(result => store.dispatch(actionFulfilled('delay1000', result)),
+                 //error => store.dispatch(actionRejected('delay1000', error)))
+
+//store.dispatch(actionPending('delay3000'))
+
+//delay(3000).then(result => store.dispatch(actionFulfilled('delay3000', result)),
+                 //error => store.dispatch(actionRejected('delay3000', error)))
+
+const gql =    (url, query, variables) => fetch(url, {
+        method: 'POST',
+        headers: {
+            "Content-Type": "application/json",
+            Accept: "application/json",
+        },
+        body: JSON.stringify({query, variables})
+    }).then(res => res.json())
+
+const backendURL = 'http://shop-roles.node.ed.asmer.org.ua/graphql'
+
+const actionRootCats = () => 
+    actionPromise('rootCats', gql(backendURL, `query {
+        CategoryFind(query: "[{\\"parent\\":null}]"){
+            _id name
+        }
+    }`))
+
+const actionCatById = (_id) =>  //добавить подкатегории
+    actionPromise('catById', gql(backendURL, `query catById($q: String){
+        CategoryFindOne(query: $q){
+            _id name goods {
+                _id name price images {
+                    url
+                }
+            }
+        }
+    }`, {q: JSON.stringify([{_id}])}))
+
+const actionLogin = (login, password) =>
+    actionPromise('login', gql(backendURL, 
+        `query log($login: String, $password: String){
+            login(login:$login, password: $password)
+        }`
+        , {login, password}))
+
+store.dispatch(actionRootCats())
+store.dispatch(actionLogin('levshin95', '123123'))
+
+
+store.subscribe(() => {
+    const rootCats = store.getState().promise.rootCats?.payload?.data.CategoryFind
+    if (!rootCats) {
+        aside.innerHTML = '<img src="Loading_icon.gif">'
+    } else {
+        aside.innerHTML = ''
+        for(let {_id, name} of rootCats){
+            const a = document.createElement('a')
+            a.href = "#/category/" + _id
+            a.innerHTML = name
+            aside.append(a)
+        }
+    }
+})
+
+store.subscribe(() => {
+    const catById = store.getState().promise.catById?.payload?.data.CategoryFindOne
+    const [,route] = location.hash.split('/')
+    if (catById && route === 'category') {
+        const {name, goods, _id} = catById
+        main.innerHTML = `<h1>${name}</h1>`
+        for(let {_id, name, price, images} of goods){
+            const a = document.createElement('a')
+            a.href = "#/good/" + _id
+            a.innerHTML = name
+            main.append(a)
+        }
+    }
+})
+
+window.onhashchange = () => {
+    const [,route, _id] = location.hash.split('/')
+    console.log(route, _id)
+    const routes = {
+        category(){
+            store.dispatch(actionCatById(_id))
+        }, 
+        good(){
+
+        }
+    }
+    if(route in routes){
+        routes[route]()
+    }
+}
+    
+window.onhashchange()
+
+
+//
+
+
+//

+ 0 - 0
Модуль JS/script.js


+ 0 - 0
Модуль JS/style.css