lesson20.html 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <html>
  2. <head>
  3. <title>GQL</title>
  4. <meta charset='utf8' />
  5. <!--<style>
  6. #mainContainer {
  7. display: flex;
  8. }
  9. #aside {
  10. width: 30%;
  11. }
  12. #aside > a{
  13. display: block;
  14. }
  15. header {
  16. min-height: 100px;
  17. background-color: #AAA;
  18. }
  19. -->
  20. </style>
  21. </head>
  22. <body>
  23. <header>
  24. <div id='cartIcon'></div>
  25. </header>
  26. <div id='mainContainer'>
  27. <aside id='aside'>
  28. Категории
  29. Сюда надо воткнуть список корневых категорий интернет-магазина
  30. <a href="#/people/1">Luke</a>
  31. <a href="#/people/2">C-3PO</a>
  32. <a href="#/people/3">R2-D2</a>
  33. <a href="#/people/4">Dart Vader</a>
  34. <a href="#/films/4">new hope</a>
  35. <a href="#/films/5">пятая часть</a>
  36. <a href="#/films/3">шестая часть</a>
  37. </aside>
  38. <main id='main'>
  39. Контент
  40. </main>
  41. </div>
  42. <script>
  43. function createStore(reducer) {
  44. let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
  45. let cbs = [] //массив подписчиков
  46. const getState = () => state //функция, возвращающая переменную из замыкания
  47. const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
  48. () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  49. const dispatch = action => {
  50. if (typeof action === 'function') { //если action - не объект, а функция
  51. return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  52. }
  53. const newState = reducer(state, action) //пробуем запустить редьюсер
  54. if (newState !== state) { //проверяем, смог ли редьюсер обработать action
  55. state = newState //если смог, то обновляем state
  56. for (let cb of cbs) cb(state) //и запускаем подписчиков
  57. }
  58. }
  59. return {
  60. getState, //добавление функции getState в результирующий объект
  61. dispatch,
  62. subscribe //добавление subscribe в объект
  63. }
  64. }
  65. function combineReducers(reducers) {
  66. function totalReducer(state = {}, action) {
  67. const newTotalState = {}
  68. for (const [reducerName, reducer] of Object.entries(reducers)) {
  69. const newSubState = reducer(state[reducerName], action)
  70. if (newSubState !== state[reducerName]) {
  71. newTotalState[reducerName] = newSubState
  72. }
  73. }
  74. if (Object.keys(newTotalState).length) {
  75. return { ...state, ...newTotalState }
  76. }
  77. return state
  78. }
  79. return totalReducer
  80. }
  81. const reducers = {
  82. promise: promiseReducer, //допилить много имен для многих промисо
  83. //auth: authReducer, //часть предыдущего ДЗ
  84. //cart: cartReducer, //часть предыдущего ДЗ
  85. }
  86. const totalReducer = combineReducers(reducers)
  87. function promiseReducer(state = {}, { type, status, payload, error }) {
  88. if (type === 'PROMISE') {
  89. //имена добавить
  90. return { status, payload, error }
  91. }
  92. return state
  93. }
  94. //имена добавить
  95. const actionPending = () => ({ type: 'PROMISE', status: 'PENDING' })
  96. const actionFulfilled = payload => ({ type: 'PROMISE', status: 'FULFILLED', payload })
  97. const actionRejected = error => ({ type: 'PROMISE', status: 'REJECTED', error })
  98. //имена добавить
  99. const actionPromise = promise =>
  100. async dispatch => {
  101. dispatch(actionPending()) //сигнализируем redux, что промис начался
  102. try {
  103. const payload = await promise //ожидаем промиса
  104. dispatch(actionFulfilled(payload)) //сигнализируем redux, что промис успешно выполнен
  105. return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
  106. }
  107. catch (error) {
  108. dispatch(actionRejected(error)) //в случае ошибки - сигнализируем redux, что промис несложился
  109. }
  110. }
  111. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. const store = createStore(totalReducer) //не забудьте combineReducers если он у вас уже есть
  113. store.subscribe(() => console.log(store.getState()))
  114. const drawPeople = (state) => {
  115. const [, route] = location.hash.split('/')
  116. if (route !== 'people') return
  117. const { status, payload, error } = store.getState().promise//.имя другое
  118. if (status === 'PENDING') {
  119. main.innerHTML = `<img src='https://cdn.dribbble.com/users/63485/screenshots/1309731/infinite-gif-preloader.gif' />`
  120. }
  121. if (status === 'FULFILLED') {
  122. const { name, mass, eye_color, films } = payload
  123. main.innerHTML = `<h1>${name}</h1>
  124. <section>ЖЫРНОСТЬ: ${mass}кг</section>
  125. <section style="color: ${eye_color}">Цвет глаз</section>
  126. `
  127. for (const filmUrl of films) {
  128. const filmId = filmUrl.split('/films/')[1].slice(0, -1)
  129. main.innerHTML += `<a href="#/films/${filmId}">Фильм №${filmId}</a>`
  130. }
  131. }
  132. }
  133. store.subscribe(drawPeople)
  134. store.subscribe(() => {
  135. const [, route] = location.hash.split('/')
  136. if (route !== 'films') return
  137. const { status, payload, error } = store.getState().promise//.имя одно
  138. if (status === 'PENDING') {
  139. main.innerHTML = `<img src='https://cdn.dribbble.com/users/63485/screenshots/1309731/infinite-gif-preloader.gif' />`
  140. }
  141. if (status === 'FULFILLED') {
  142. const { title, opening_crawl, characters } = payload
  143. main.innerHTML = `<h1>${title}</h1>
  144. <p>${opening_crawl}</p>
  145. `
  146. for (const peopleUrl of characters) {
  147. const peopleId = peopleUrl.split('/people/')[1].slice(0, -1)
  148. main.innerHTML += `<a href="#/people/${peopleId}">Герой №${peopleId}</a>`
  149. }
  150. }
  151. })
  152. const actionGetPeople = id => //имя другое
  153. actionPromise(fetch(`https://swapi.dev/api/people/${id}`).then(res => res.json()))
  154. const actionGetFilm = id =>
  155. actionPromise(fetch(`https://swapi.dev/api/films/${id}`).then(res => res.json()))
  156. const actionSomePeople = () =>
  157. actionPromise(fetch(`https://swapi.dev/api/people/`).then(res => res.json()))
  158. store.dispatch(actionSomePeople())
  159. store.subscribe(() => {
  160. const { status, payload, error } = store.getState().promise//.имя третье
  161. if (status === 'FULFILLED' && payload.results) {
  162. aside.innerHTML = ''
  163. for (const { url: peopleUrl, name } of payload.results) {
  164. const peopleId = peopleUrl.split('/people/')[1].slice(0, -1)
  165. aside.innerHTML += `<a href="#/people/${peopleId}">${name}</a>`
  166. }
  167. }
  168. })
  169. window.onhashchange = () => {
  170. const [, route, _id] = location.hash.split('/')
  171. const routes = {
  172. people() {
  173. console.log('People', _id)
  174. store.dispatch(actionGetPeople(_id))
  175. },
  176. films() {
  177. store.dispatch(actionGetFilm(_id))
  178. },
  179. //category() {
  180. //store.dispatch(actionCategoryById(_id))
  181. //},
  182. //good(){
  183. ////тут был store.dispatch goodById
  184. //console.log('good', _id)
  185. //},
  186. login() {
  187. console.log('А ТУТ ЩА ДОЛЖНА БЫТЬ ФОРМА ЛОГИНА')
  188. //нарисовать форму логина, которая по нажатию кнопки Login делает store.dispatch(actionFullLogin(login, password))
  189. },
  190. //register(){
  191. ////нарисовать форму регистрации, которая по нажатию кнопки Login делает store.dispatch(actionFullRegister(login, password))
  192. //},
  193. }
  194. if (route in routes) {
  195. routes[route]()
  196. }
  197. }
  198. window.onhashchange()
  199. </script>
  200. </body>
  201. </html>