main.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. function createStore(reducer){
  2. let state = reducer(undefined, {})
  3. let cbs = []
  4. function dispatch(action){
  5. if (typeof action === 'function'){
  6. return action(dispatch)
  7. }
  8. const newState = reducer(state, action)
  9. if (state !== newState){
  10. state = newState
  11. cbs.forEach(cb => cb())
  12. }
  13. }
  14. return {
  15. dispatch,
  16. subscribe(cb){
  17. cbs.push(cb)
  18. return () => cbs = cbs.filter(c => c !== cb)
  19. },
  20. getState(){
  21. return state
  22. }
  23. }
  24. }
  25. function promiseReducer(state={}, {type, status, payload, error, name}){
  26. if (type === 'PROMISE'){
  27. return {
  28. ...state,
  29. [name]:{status, payload, error}
  30. }
  31. }
  32. return state
  33. }
  34. const store = createStore(promiseReducer)
  35. const unsubscribe1 = store.subscribe(() => console.log(store.getState()))
  36. const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name})
  37. const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
  38. const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
  39. const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
  40. const actionPromise = (name, promise) =>
  41. async dispatch => {
  42. dispatch(actionPending(name))
  43. try{
  44. let payload = await promise
  45. dispatch(actionResolved(name, payload))
  46. return payload
  47. }
  48. catch(error){
  49. dispatch(actionRejected(name, error))
  50. }
  51. }
  52. const getGQL = url => {
  53. return function(query, variables={}) {
  54. return fetch(url,
  55. {
  56. method: "POST",
  57. headers:
  58. {"Content-Type": "application/json",
  59. ...(localStorage.authToken ? {Authorization: localStorage.authToken} : {})
  60. },
  61. body: JSON.stringify({query, variables})
  62. }).then(resp => resp.json())
  63. .then(data => {
  64. if ("errors" in data) {
  65. throw new Error('ашипка, угадывай што не так')
  66. }
  67. else {
  68. return data.data[Object.keys(variables)[0]]
  69. }
  70. })
  71. }
  72. }
  73. let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql')
  74. const goodById = goodId => {
  75. let id = `[{"_id":"${goodId}"}]`
  76. return shopGQL(`
  77. query good($id:String){
  78. GoodFindOne(query: $id) {
  79. name description price images {
  80. _id text url
  81. }
  82. categories {
  83. _id name
  84. }
  85. }
  86. }`, {GoodFindOne: '', id })
  87. }
  88. const actionGoodById = id =>
  89. actionPromise('goodById', goodById(id))
  90. const actionRootCategories = () =>
  91. actionPromise('rootCategories', shopGQL(`
  92. query cats($query:String){
  93. CategoryFind(query:$query){
  94. _id name
  95. }
  96. }
  97. `, {CategoryFind:'', query: JSON.stringify([{parent:null}])}))
  98. const actionCategoryById = (_id) =>
  99. actionPromise('catById', shopGQL(`query catById($query:String){
  100. CategoryFindOne(query:$query){
  101. _id name goods{
  102. _id name price description images{
  103. url
  104. }
  105. }
  106. }
  107. }`, {CategoryFindOne:'', query: JSON.stringify([{_id}])}))
  108. store.dispatch(actionRootCategories())
  109. window.onhashchange = () => {
  110. let {1: route, 2:id} = location.hash.split('/')
  111. if (route === 'categories'){
  112. store.dispatch(actionCategoryById(id))
  113. }
  114. if (route === 'good'){
  115. store.dispatch(actionGoodById(id))
  116. }
  117. }
  118. function drawMainMenu(){
  119. let cats = store.getState().rootCategories.payload
  120. if (cats){ //каждый раз дорисовываются в body
  121. aside.innerText = ''
  122. for (let {_id, name} of cats){
  123. let catA = document.createElement('a')
  124. catA.href = `#/categories/${_id}`
  125. catA.innerText = name
  126. aside.append(catA)
  127. }
  128. }
  129. }
  130. store.subscribe(drawMainMenu)
  131. store.subscribe(() => {
  132. const {1: route, 2:id} = location.hash.split('/')
  133. if (route === 'categories'){
  134. const catById = store.getState().catById?.payload
  135. if (catById){
  136. main.innerText = ''
  137. let categoryName = document.createElement('div')
  138. categoryName.innerText = catById.name
  139. categoryName.style.fontSize = '25px'
  140. categoryName.style.fontWeight = 'bold'
  141. main.append(categoryName)
  142. for (let {_id, name} of catById.goods){
  143. let good = document.createElement('a')
  144. good.href = `#/good/${_id}`
  145. good.innerText = name
  146. let btn = document.createElement('button')
  147. btn.style.cursor = 'pointer'
  148. btn.innerText = 'купыть'
  149. main.append(good, btn)
  150. }
  151. }
  152. }
  153. if (route === 'good'){
  154. const goodById = store.getState().goodById?.payload
  155. if (goodById){
  156. main.innerText = ''
  157. let {name, description, price} = goodById
  158. let goodName = document.createElement('div')
  159. goodName.innerText = name
  160. goodName.style.fontSize = '35px'
  161. goodName.style.fontWeight = 'bold'
  162. goodName.style.marginBottom = '25px'
  163. let goodDescription = document.createElement('div')
  164. goodDescription.innerText = description
  165. goodDescription.style.marginBottom = '25px'
  166. let goodPrice = document.createElement('div')
  167. goodPrice.innerText = 'Цена: ' + price
  168. goodPrice.style.marginBottom = '5px'
  169. let btn = document.createElement('button')
  170. btn.style.cursor = 'pointer'
  171. btn.innerText = 'купыть'
  172. main.append(goodName, goodDescription, goodPrice, btn)
  173. }
  174. }
  175. })