main.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. //debugger;
  2. function createStore(reducer){
  3. let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
  4. let cbs = [] //массив подписчиков
  5. const getState = () => state //функция, возвращающая переменную из замыкания
  6. const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
  7. () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  8. const dispatch = action => {
  9. if (typeof action === 'function'){ //если action - не объект, а функция
  10. return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  11. }
  12. const newState = reducer(state, action) //пробуем запустить редьюсер
  13. if (newState !== state){ //проверяем, смог ли редьюсер обработать action
  14. state = newState //если смог, то обновляем state
  15. for (let cb of cbs) cb() //и запускаем подписчиков
  16. }
  17. }
  18. return {
  19. getState, //добавление функции getState в результирующий объект
  20. dispatch,
  21. subscribe //добавление subscribe в объект
  22. }
  23. }
  24. function promiseReducer(state={}, {type, name, status, payload, error}){
  25. // {
  26. // login: {status, payload, error}
  27. // catById: {status, payload, error}
  28. // }
  29. if (type === 'PROMISE'){
  30. return {
  31. ...state,
  32. [name]:{status, payload, error}
  33. }
  34. }
  35. return state
  36. }
  37. const store = createStore(promiseReducer)
  38. store.subscribe(() => console.log(store.getState()))
  39. const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name})
  40. const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
  41. const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
  42. const actionPromise = (name, promise) =>
  43. async (dispatch) => {
  44. dispatch(actionPending(name)) // 1. {delay1000: {status: 'PENDING'}}
  45. try{
  46. let payload = await promise
  47. dispatch(actionResolved(name, payload))
  48. return payload
  49. }
  50. catch(error){
  51. dispatch(actionRejected(name, error))
  52. }
  53. }
  54. const getGQL = url =>
  55. (query, variables = {}) =>
  56. fetch(url, {
  57. //метод
  58. method: 'POST',
  59. headers: {
  60. //заголовок content-type
  61. "Content-Type": "application/json",
  62. ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} :
  63. {})
  64. },
  65. //body с ключами query и variables
  66. body: JSON.stringify({query, variables})
  67. })
  68. .then(res => res.json())
  69. .then(data => {
  70. if (data.errors && !data.data)
  71. throw new Error(JSON.stringify(data.errors))
  72. return data.data[Object.keys(data.data)[0]]
  73. })
  74. const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
  75. const gql = getGQL(`${backURL}/graphql`)
  76. const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
  77. const actionRootCats = () =>
  78. actionPromise('rootCats', gql(`query {
  79. CategoryFind(query: "[{\\"parent\\":null}]"){
  80. _id name
  81. }
  82. }`))
  83. const actionCatById = (_id) => //добавить подкатегории
  84. actionPromise('catById', gql(`query catById($q: String){
  85. CategoryFindOne(query: $q){
  86. _id name subCategories {
  87. _id name
  88. }
  89. goods {
  90. _id price name images {
  91. url
  92. }
  93. }
  94. }
  95. }`, {"q": JSON.stringify([{_id}])}))
  96. store.dispatch(actionRootCats())
  97. const actionGoodById = (_id) =>
  98. actionPromise('goodById', gql(`query goodById($goodId:String){
  99. GoodFindOne(query:$goodId){
  100. _id name description price images{
  101. _id text url
  102. }
  103. }
  104. }`, {"goodId": JSON.stringify([{_id}])}))
  105. store.subscribe(() => {
  106. const {rootCats} = store.getState()
  107. if (rootCats?.payload){
  108. aside.innerHTML = ''
  109. for (const {_id, name} of rootCats?.payload){
  110. const link = document.createElement('a')
  111. link.href = `#/category/${_id}`
  112. link.innerText = name
  113. aside.append(link)
  114. }
  115. }
  116. })
  117. window.onhashchange = () => {
  118. const [, route, _id] = location.hash.split('/');
  119. const routes = {
  120. category(){
  121. store.dispatch(actionCatById(_id))
  122. },
  123. good(){
  124. store.dispatch(actionGoodById(_id));//задиспатчить actionGoodById
  125. console.log('ТОВАРОСТРАНИЦА')
  126. },
  127. }
  128. if (route in routes)
  129. routes[route]()
  130. }
  131. window.onhashchange()
  132. store.subscribe(() => {
  133. const {catById} = store.getState()
  134. const [,route, _id] = location.hash.split('/')
  135. if (catById?.payload && route === 'category'){
  136. const {name, subCategories} = catById.payload;
  137. let str = '';
  138. if (subCategories){
  139. for(let subCateg of subCategories){
  140. str += `<h4><a href="#/subCategory/${subCateg._id}"> ${subCateg.name}</a></h4>`;
  141. }
  142. } else {str += 'Подкатегории отсутствуют'}
  143. main.innerHTML = `<h1>${name}</h1> ${str} ` //ТУТ ДОЛЖНЫ БЫТЬ ПОДКАТЕГОРИИ
  144. for (const {_id, name, price, images} of catById.payload.goods){
  145. const card = document.createElement('div')
  146. card.innerHTML = `<h2>${name}</h2>
  147. <img src="${backURL}/${images[0].url}" />
  148. <strong>${price}</strong>
  149. <a href="#/good/${_id}">${name}</a>
  150. `
  151. main.append(card)
  152. }
  153. }
  154. })
  155. store.subscribe(() => {
  156. const {goodById} = store.getState(); //console.log("dfsdfs", store.getState().goodById)
  157. if(goodById) {
  158. const [, , _id] = location.hash.split('/'); //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
  159. if(goodById?.payload && location.hash == `#/good/${_id}`){ //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
  160. document.getElementById('main').innerHTML = "";
  161. const {name, description, price, images} = goodById.payload;
  162. const card = document.createElement('div'); //в таком случае очищаем main и рисуем информацию про товар с подробностями
  163. card.innerHTML = `<h2>${name}</h2>
  164. <img src="${backURL}/${images[0].url}" />
  165. <strong>${price} $</strong>
  166. <p>${description}</p>`
  167. main.append(card);
  168. }
  169. }
  170. })
  171. //store.dispatch(actionPromise('delay1000', delay(1000)))
  172. //store.dispatch(actionPromise('delay2000', delay(2000)))
  173. //store.dispatch(actionPromise('failedfetch', fetch('https://swapi.dev/api/people/1/')
  174. //.then(res => res.json())))