index.js 8.5 KB

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