script.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. // function promiseReducer(state={}, {type, name, status, payload, error}){
  2. // if (type === 'PROMISE'){
  3. // return {
  4. // ...state, [name]: {status, payload, error}
  5. // }
  6. // }
  7. // return state
  8. // }
  9. // const actionPending = name => ({type:'PROMISE',name, status: 'PENDING'})
  10. // const actionFulfilled = (name,payload) => ({type:'PROMISE',name, status: 'FULFILLED', payload})
  11. // const actionRejected = (name,error) => ({type:'PROMISE',name, status: 'REJECTED', error})
  12. // const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
  13. // function createStore(reducer){
  14. // let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
  15. // let cbs = [] //массив подписчиков
  16. // const getState = () => state //функция, возвращающая переменную из замыкания
  17. // const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
  18. // () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  19. // const dispatch = action => {
  20. // if (typeof action === 'function'){ //если action - не объект, а функция
  21. // return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  22. // }
  23. // const newState = reducer(state, action) //пробуем запустить редьюсер
  24. // if (newState !== state){ //проверяем, смог ли редьюсер обработать action
  25. // state = newState //если смог, то обновляем state
  26. // for (let cb of cbs) cb() //и запускаем подписчиков
  27. // }
  28. // }
  29. // return {
  30. // getState, //добавление функции getState в результирующий объект
  31. // dispatch,
  32. // subscribe //добавление subscribe в объект
  33. // }
  34. // }
  35. // let store = createStore(promiseReducer);
  36. // store.subscribe(() => console.log(store.getState()))
  37. // // store.dispatch(actionPending('delay1000'))
  38. // // delay(1000).then(result => store.dispatch(actionFulfilled('delay1000', result)))
  39. // // store.dispatch(actionPending('delay2000'))
  40. // // delay(2000).then(result => store.dispatch(actionFulfilled('delay2000', result)))
  41. // const actionPromise = (name, promise) =>
  42. // async dispatch => {
  43. // dispatch(actionPending(name))
  44. // try {
  45. // let payload = await promise
  46. // dispatch(actionFulfilled(name, payload))
  47. // return payload
  48. // }
  49. // catch(error){
  50. // dispatch(actionRejected(name, error))
  51. // }
  52. // }
  53. // store.dispatch(actionPromise('delay1000', delay(1000)))
  54. // store.dispatch(actionPromise('delay2000', delay(2000)))
  55. // const actionLuke = () => actionPromise('luke',
  56. // fetch('https://swapi.dev/api/people/1')
  57. // .then(res => res.json()))
  58. // store.dispatch(actionLuke())
  59. // // const actionRegister = (login, password) =>
  60. // // actionPromise('register', gql(`mutation ......
  61. // // ......
  62. // // ....`, {login, password}))
  63. // // store.dispatch(actionRegister('anon100500', '123123'))
  64. // // const actionCategoryById = _id =>
  65. // // actionPromise('catById', gql(`aaaaaaaa $query.....`, {query: JSON.stringify([{_id}])}))
  66. // // store.dispatch(actionCategoryById('АЙДИКАТЕГОРИИ НАТЫРИТЬ В GRAPHQL'))
  67. // // const actionGoodById = _id =>
  68. // // actionPromise('goodById', gql(`aaaaaaaa $query.....`, {query: JSON.stringify([{_id}])}))
  69. // // const actionRootCategories = () =>
  70. // // actionPromise('rootCats', gql(`CategoryFind так, что бы parent: null`)}))
  71. // // //сделать const actionOrders для истории заказов (нужен токен в localStorage)
  72. // // //шобы сработало, надо добавить в createStore в dispatch один if:
  73. // // //if (typeof action === 'function'){ //если action - не объект, а функция
  74. // // //return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  75. // // //}
  76. // // //надо const newState
  77. function createStore(reducer){
  78. let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
  79. let cbs = [] //массив подписчиков
  80. const getState = () => state //функция, возвращающая переменную из замыкания
  81. const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
  82. () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  83. const dispatch = action => {
  84. if (typeof action === 'function'){ //если action - не объект, а функция
  85. return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  86. }
  87. const newState = reducer(state, action) //пробуем запустить редьюсер
  88. if (newState !== state){ //проверяем, смог ли редьюсер обработать action
  89. state = newState //если смог, то обновляем state
  90. for (let cb of cbs) cb() //и запускаем подписчиков
  91. }
  92. }
  93. return {
  94. getState, //добавление функции getState в результирующий объект
  95. dispatch,
  96. subscribe //добавление subscribe в объект
  97. }
  98. }
  99. function promiseReducer(state={}, {type, name, status, payload, error}){
  100. if (type === 'PROMISE'){
  101. return {
  102. ...state,
  103. [name]:{status, payload, error}
  104. }
  105. }
  106. return state
  107. }
  108. const store = createStore(promiseReducer)
  109. //не забудьте combineReducers если он у вас уже есть
  110. //const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer}))
  111. //store.subscribe(() => console.log(store.getState()))
  112. const actionPending = name => ({type:'PROMISE',name, status: 'PENDING'})
  113. const actionFulfilled = (name,payload) => ({type:'PROMISE',name, status: 'FULFILLED', payload})
  114. const actionRejected = (name,error) => ({type:'PROMISE',name, status: 'REJECTED', error})
  115. const actionPromise = (name, promise) =>
  116. async dispatch => {
  117. dispatch(actionPending(name))
  118. try {
  119. let payload = await promise
  120. dispatch(actionFulfilled(name, payload))
  121. return payload
  122. }
  123. catch(error){
  124. dispatch(actionRejected(name, error))
  125. }
  126. }
  127. const getGQL = url =>
  128. (query, variables) => fetch(url, {
  129. method: 'POST',
  130. headers: {
  131. "Content-Type": "application/json",
  132. ...(localStorage.authToken ? {"Authorization": "Bearer " + localStorage.authToken} : {})
  133. },
  134. body: JSON.stringify({query, variables})
  135. }).then(res => res.json())
  136. .then(data => {
  137. if (data.data){
  138. return Object.values(data.data)[0]
  139. }
  140. else throw new Error(JSON.stringify(data.errors))
  141. })
  142. const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
  143. const gql = getGQL(backendURL + '/graphql')
  144. const actionRootCats = () =>
  145. actionPromise('rootCats', gql(`query {
  146. CategoryFind(query: "[{\\"parent\\":null}]"){
  147. _id name
  148. }
  149. }`))
  150. const actionCatById = (_id) =>
  151. //добавить подкатегории
  152. actionPromise('catById', gql(`query catById($q: String){
  153. CategoryFindOne(query: $q){
  154. _id subCategories{
  155. name _id
  156. } name goods {
  157. _id name price images {
  158. url
  159. }
  160. }
  161. }
  162. }`, {q: JSON.stringify([{_id}])}))
  163. const actionGoodById = (_id) =>
  164. actionPromise('goodById', gql(`query findGoodById($q: String) {
  165. GoodFindOne(query: $q) {
  166. _id
  167. name
  168. description
  169. price
  170. images{
  171. url
  172. }
  173. }
  174. }`, {q: JSON.stringify([{_id}])}))
  175. store.dispatch(actionRootCats())
  176. store.subscribe(() => {
  177. const {rootCats} = store.getState()
  178. if (rootCats?.payload){
  179. aside.innerHTML = ''
  180. for (const {_id, name} of rootCats?.payload){
  181. const link = document.createElement('a')
  182. link.href = `#/category/${_id}`
  183. link.innerText = name
  184. aside.append(link)
  185. }
  186. }
  187. })
  188. window.onhashchange = () => {
  189. const [, route, _id] = location.hash.split('/')
  190. const routes = {
  191. category(){
  192. store.dispatch(actionCatById(_id))
  193. },
  194. good(){
  195. store.dispatch(actionGoodById(_id))
  196. },
  197. login(){
  198. //отрисовка тут
  199. //по кнопке - store.dispatch(actionFullLogin(login, password))
  200. },
  201. register(){
  202. //отрисовка тут
  203. //по кнопке - store.dispatch(actionFullRegister(login, password))
  204. },
  205. // dashboard() {
  206. //actionOrders
  207. // #/dashboard
  208. // orders page
  209. // }
  210. }
  211. if (route in routes)
  212. routes[route]()
  213. }
  214. window.onhashchange()
  215. store.subscribe(() => {
  216. const {catById} = store.getState()
  217. const [,route, _id] = location.hash.split('/')
  218. if (catById?.payload && route === 'category'){
  219. const {name, subCategories} = catById.payload
  220. main.innerHTML = `<h1>${name}</h1>
  221. ${(subCategories ? subCategories.map(item => {
  222. return `<a href='#/category/${item._id}'>${item.name}</a>`} ) : '')}
  223. `
  224. for (const {_id, name, price, images} of catById.payload.goods){
  225. const card = document.createElement('div');
  226. card.innerHTML = `<h2>${name}</h2>
  227. <img src="${backendURL}/${images[0].url}" width="380"/>
  228. <div><strong>${price}грн</strong></div>
  229. <a href= '#/good/${_id}'> Купить </a>
  230. `
  231. main.append(card)
  232. }
  233. }
  234. })
  235. store.subscribe(() => {
  236. const {goodById} = store.getState()
  237. const [,route, _id] = location.hash.split('/')
  238. if (goodById?.payload && route === 'good'){
  239. const {name, description, price, images} = goodById.payload
  240. main.innerHTML = ``
  241. const card = document.createElement('div');
  242. card.innerHTML = `<h2>${name}</h2>
  243. <div> <strong>${price}грн</strong></div>
  244. <img src="${backendURL}/${images[0].url}"/>
  245. <p>${description} </p> `
  246. main.append(card)
  247. }
  248. })
  249. const jwtDecode = token => {
  250. try{
  251. const payload = JSON.parse(atob(token.split('.')[1]))
  252. return payload;
  253. }
  254. catch(e){
  255. }
  256. }
  257. const store2 = createStore(authReducer);
  258. //console.log(jwtDecode(token));
  259. function authReducer(state, {type, token}){
  260. if (state === undefined && localStorage.authToken){
  261. token = localStorage.authToken;
  262. type = 'AUTH_LOGIN';
  263. }
  264. if (type === 'AUTH_LOGIN'){
  265. let decodeToken = jwtDecode(token)
  266. if(decodeToken){
  267. localStorage.authToken = token;
  268. return {token, payload: decodeToken}
  269. }
  270. }
  271. if (type === 'AUTH_LOGOUT'){
  272. localStorage.authToken = ''
  273. return {}
  274. }
  275. return state || {}
  276. }
  277. const actionAuthLogin = (token) => ({
  278. type:'AUTH_LOGIN',
  279. token
  280. })
  281. const actionAuthLogout = () => ({
  282. type:'AUTH_LOGOUT',
  283. })
  284. // //поменять в createStore редьюсер на auth
  285. console.log(store2.getState())
  286. store2.subscribe(() => console.log(store2.getState()))
  287. // store2.dispatch(actionAuthLogin('где-то скопипастить токен'))
  288. //store2.dispatch(actionAuthLogout())