sagas.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import {
  21. call,
  22. put,
  23. all,
  24. select,
  25. takeLatest,
  26. takeEvery
  27. } from 'redux-saga/effects'
  28. import produce from 'immer'
  29. import { message } from 'antd'
  30. import { push, replace } from 'connected-react-router'
  31. import { Location } from 'history'
  32. import { matchDisplayPath, matchDisplaySlidePath } from 'utils/router'
  33. import { ActionTypes } from './constants'
  34. import { VizActions, VizActionType } from './actions'
  35. import { makeSelectLocation } from 'containers/App/selectors'
  36. import {
  37. makeSelectCurrentDisplay,
  38. makeSelectCurrentSlides,
  39. makeSelectCurrentSlide
  40. } from './selectors'
  41. import { makeSelectCurrentProject } from 'containers/Projects/selectors'
  42. import request from 'utils/request'
  43. import api from 'utils/api'
  44. import { errorHandler } from 'utils/util'
  45. import { getDashboardNodes } from './util'
  46. import { ISlideRaw, ISlideFormed, Slide } from './types'
  47. import { getDefaultSlideParams } from 'containers/Display/components/util'
  48. import { IDashboard } from '../Dashboard/types'
  49. export function* getPortals(action: VizActionType) {
  50. if (action.type !== ActionTypes.LOAD_PORTALS) {
  51. return
  52. }
  53. const { payload } = action
  54. try {
  55. const asyncData = yield call(
  56. request,
  57. `${api.portal}?projectId=${payload.projectId}`
  58. )
  59. const portals = asyncData.payload
  60. yield put(VizActions.portalsLoaded(portals))
  61. } catch (err) {
  62. yield put(VizActions.loadPortalsFail())
  63. errorHandler(err)
  64. }
  65. }
  66. export function* addPortal(action: VizActionType) {
  67. if (action.type !== ActionTypes.ADD_PORTAL) {
  68. return
  69. }
  70. const { payload } = action
  71. try {
  72. const asyncData = yield call(request, {
  73. method: 'post',
  74. url: api.portal,
  75. data: payload.portal
  76. })
  77. yield put(VizActions.portalAdded(asyncData.payload))
  78. payload.resolve()
  79. } catch (err) {
  80. yield put(VizActions.addPortalFail())
  81. errorHandler(err)
  82. }
  83. }
  84. export function* deletePortal(action: VizActionType) {
  85. if (action.type !== ActionTypes.DELETE_PORTAL) {
  86. return
  87. }
  88. const { payload } = action
  89. try {
  90. yield call(request, {
  91. method: 'delete',
  92. url: `${api.portal}/${payload.id}`
  93. })
  94. yield put(VizActions.portalDeleted(payload.id))
  95. } catch (err) {
  96. yield put(VizActions.deletePortalFail())
  97. errorHandler(err)
  98. }
  99. }
  100. export function* editPortal(action: VizActionType) {
  101. if (action.type !== ActionTypes.EDIT_PORTAL) {
  102. return
  103. }
  104. const { payload } = action
  105. try {
  106. yield call(request, {
  107. method: 'put',
  108. url: `${api.portal}/${payload.values.id}`,
  109. data: payload.values
  110. })
  111. yield put(VizActions.portalEdited(payload.values))
  112. payload.resolve()
  113. } catch (err) {
  114. yield put(VizActions.editPortalFail())
  115. errorHandler(err)
  116. }
  117. }
  118. export function* getPortalDashboards(action: VizActionType) {
  119. if (action.type !== ActionTypes.LOAD_PORTAL_DASHBOARDS) {
  120. return
  121. }
  122. const { portalId, resolve, convertTree } = action.payload
  123. try {
  124. const asyncData = yield call(
  125. request,
  126. `${api.portal}/${portalId}/dashboards`
  127. )
  128. const dashboardNodes = convertTree
  129. ? getDashboardNodes(asyncData.payload)
  130. : asyncData.payload
  131. yield put(VizActions.portalDashboardsLoaded(portalId, dashboardNodes))
  132. if (resolve) {
  133. resolve(dashboardNodes)
  134. }
  135. } catch (err) {
  136. yield put(VizActions.loadPortalDashboardsFail(portalId))
  137. errorHandler(err)
  138. }
  139. }
  140. export function* getDisplays(action: VizActionType) {
  141. if (action.type !== ActionTypes.LOAD_DISPLAYS) {
  142. return
  143. }
  144. const { projectId } = action.payload
  145. try {
  146. const asyncData = yield call(
  147. request,
  148. `${api.display}?projectId=${projectId}`
  149. )
  150. const displays = asyncData.payload
  151. yield put(VizActions.displaysLoaded(displays))
  152. } catch (err) {
  153. yield put(VizActions.loadDisplaysFail(err))
  154. errorHandler(err)
  155. }
  156. }
  157. export function* addDisplay(action: VizActionType) {
  158. if (action.type !== ActionTypes.ADD_DISPLAY) {
  159. return
  160. }
  161. const { display, resolve } = action.payload
  162. try {
  163. const asyncDisplayData = yield call(request, api.display, {
  164. method: 'post',
  165. data: display
  166. })
  167. const resultDisplay = asyncDisplayData.payload
  168. const { id } = resultDisplay
  169. const slide = {
  170. displayId: id,
  171. index: 0,
  172. config: JSON.stringify({ slideParams: getDefaultSlideParams() })
  173. }
  174. yield call(request, `${api.display}/${id}/slides`, {
  175. method: 'post',
  176. data: slide
  177. })
  178. yield put(VizActions.displayAdded(resultDisplay))
  179. resolve()
  180. } catch (err) {
  181. yield put(VizActions.addDisplayFail())
  182. errorHandler(err)
  183. }
  184. }
  185. export function* editDisplay(action: VizActionType) {
  186. if (action.type !== ActionTypes.EDIT_DISPLAY) {
  187. return
  188. }
  189. const { display, resolve } = action.payload
  190. try {
  191. yield call(request, `${api.display}/${display.id}`, {
  192. method: 'put',
  193. data: { ...display, config: JSON.stringify(display.config) }
  194. })
  195. yield put(VizActions.displayEdited(display))
  196. if (resolve) {
  197. resolve()
  198. }
  199. } catch (err) {
  200. yield put(VizActions.editDisplayFail(err))
  201. errorHandler(err)
  202. }
  203. }
  204. export function* deleteDisplay(action: VizActionType) {
  205. if (action.type !== ActionTypes.DELETE_DISPLAY) {
  206. return
  207. }
  208. const { id } = action.payload
  209. try {
  210. yield call(request, `${api.display}/${id}`, {
  211. method: 'delete'
  212. })
  213. yield put(VizActions.displayDeleted(id))
  214. } catch (err) {
  215. yield put(VizActions.deleteDisplayFail())
  216. errorHandler(err)
  217. }
  218. }
  219. export function* copyDisplay(action: VizActionType) {
  220. if (action.type !== ActionTypes.COPY_DISPLAY) {
  221. return
  222. }
  223. const { display, resolve } = action.payload
  224. const { id, name, description, publish, roleIds } = display
  225. try {
  226. const asyncData = yield call(request, `${api.display}/copy/${id}`, {
  227. method: 'post',
  228. data: {
  229. name,
  230. description,
  231. publish,
  232. roleIds
  233. }
  234. })
  235. yield put(VizActions.displayCopied(asyncData.payload))
  236. resolve()
  237. message.success('大屏 复制成功')
  238. } catch (err) {
  239. yield put(VizActions.copyDisplayFail())
  240. errorHandler(err)
  241. }
  242. }
  243. export function* getDisplaySlides(action: VizActionType) {
  244. if (action.type !== ActionTypes.LOAD_DISPLAY_SLIDES) {
  245. return
  246. }
  247. const { displayId } = action.payload
  248. try {
  249. const asyncData = yield call(request, `${api.display}/${displayId}/slides`)
  250. const { slides, ...rest } = asyncData.payload
  251. slides.forEach((slide: ISlideRaw) => {
  252. slide.config = JSON.parse(slide.config || '{}')
  253. })
  254. rest.config = JSON.parse(rest.config || '{}')
  255. yield put(VizActions.displaySlidesLoaded(rest, slides))
  256. const location: Location = yield select(makeSelectLocation())
  257. const matchDisplay = matchDisplayPath(location.pathname)
  258. const matchDisplaySlide = matchDisplaySlidePath(location.pathname)
  259. if (!matchDisplay && !matchDisplaySlide) {
  260. return
  261. }
  262. let previewSubPath: string = ''
  263. if (matchDisplay) {
  264. previewSubPath = matchDisplay.params[0]
  265. previewSubPath = previewSubPath ? `/${previewSubPath}` : ''
  266. }
  267. let nextSlideId: number = slides[0].id
  268. let paramSlideId: number
  269. if (matchDisplaySlide) {
  270. paramSlideId = +matchDisplaySlide.params.slideId
  271. if (paramSlideId) {
  272. const slideExists = ~(slides as ISlideFormed[]).findIndex(
  273. ({ id }) => id === paramSlideId
  274. )
  275. if (slideExists) {
  276. nextSlideId = paramSlideId
  277. }
  278. }
  279. }
  280. const { id: projectId } = yield select(makeSelectCurrentProject())
  281. const nextPath = `/project/${projectId}/display/${displayId}${previewSubPath}/slide/${nextSlideId}`
  282. yield put(replace(nextPath))
  283. yield put(VizActions.updateCurrentDisplay(rest))
  284. } catch (err) {
  285. yield put(VizActions.loadDisplaySlidesFail(displayId))
  286. errorHandler(err)
  287. }
  288. }
  289. export function* addDashboard(action: VizActionType) {
  290. if (action.type !== ActionTypes.ADD_DASHBOARD) {
  291. return
  292. }
  293. const { payload } = action
  294. const { dashboard, resolve } = payload
  295. try {
  296. const asyncData = yield call(request, {
  297. method: 'post',
  298. url: `${api.portal}/${dashboard.dashboardPortalId}/dashboards`,
  299. data: dashboard
  300. })
  301. yield put(VizActions.dashboardAdded(asyncData.payload))
  302. resolve(asyncData.payload.id)
  303. } catch (err) {
  304. yield put(VizActions.addDashboardFail())
  305. errorHandler(err)
  306. }
  307. }
  308. export function* editDashboard(action: VizActionType) {
  309. if (action.type !== ActionTypes.EDIT_DASHBOARD) {
  310. return
  311. }
  312. const { payload } = action
  313. const { formType, dashboard, resolve } = payload
  314. try {
  315. yield call(request, {
  316. method: 'put',
  317. url: `${api.portal}/${dashboard[0].dashboardPortalId}/dashboards`,
  318. data: dashboard
  319. })
  320. yield put(VizActions.dashboardEdited(dashboard, formType))
  321. resolve(dashboard)
  322. } catch (err) {
  323. yield put(VizActions.editDashboardFail())
  324. errorHandler(err)
  325. }
  326. }
  327. export function* editCurrentDashboard(action: VizActionType) {
  328. if (action.type !== ActionTypes.EDIT_CURRENT_DASHBOARD) {
  329. return
  330. }
  331. const { dashboard, type, resolve } = action.payload
  332. const { config, ...rest } = dashboard as IDashboard
  333. try {
  334. yield call(request, {
  335. method: 'put',
  336. url: `${api.portal}/${dashboard.dashboardPortalId}/dashboards`,
  337. data: [
  338. {
  339. ...rest,
  340. config: JSON.stringify(config)
  341. }
  342. ]
  343. })
  344. yield put(VizActions.currentDashboardEdited(dashboard, type))
  345. resolve()
  346. } catch (err) {
  347. yield put(VizActions.editCurrentDashboardFail())
  348. errorHandler(err)
  349. }
  350. }
  351. export function* deleteDashboard(action: VizActionType) {
  352. if (action.type !== ActionTypes.DELETE_DASHBOARD) {
  353. return
  354. }
  355. const { payload } = action
  356. try {
  357. yield call(request, {
  358. method: 'delete',
  359. url: `${api.portal}/dashboards/${payload.id}`
  360. })
  361. yield put(VizActions.dashboardDeleted(payload.id, payload.portalId))
  362. if (payload.resolve) {
  363. payload.resolve()
  364. }
  365. } catch (err) {
  366. yield put(VizActions.deleteDashboardFail())
  367. errorHandler(err)
  368. }
  369. }
  370. export function* addSlide(action: VizActionType) {
  371. if (action.type !== ActionTypes.ADD_SLIDE) {
  372. return
  373. }
  374. const { id: displayId } = yield select(makeSelectCurrentDisplay())
  375. const currentSlides: ISlideFormed[] = yield select(makeSelectCurrentSlides())
  376. const currentSlide: ISlideFormed = yield select(makeSelectCurrentSlide())
  377. const slide: Omit<ISlideFormed, 'id'> = produce(currentSlide, (draft) => {
  378. draft.id = undefined
  379. draft.displayId = displayId
  380. draft.index = currentSlide.index + 1
  381. draft.config.slideParams.avatar = undefined
  382. })
  383. const insertSlideIdx =
  384. currentSlides.findIndex(({ id }) => id === currentSlide.id) + 1
  385. const afterSlides = produce(currentSlides.slice(insertSlideIdx), (draft) => {
  386. let cursorIndex = slide.index + 1
  387. draft.forEach((s) => {
  388. s.index = cursorIndex++
  389. })
  390. })
  391. try {
  392. const result = yield all({
  393. slideResponse: call(request, {
  394. method: 'post',
  395. url: `${api.display}/${displayId}/slides`,
  396. data: slide
  397. }),
  398. slidesUpdate:
  399. afterSlides.length &&
  400. call(request, {
  401. method: 'put',
  402. url: `${api.display}/${displayId}/slides`,
  403. data: afterSlides.map<ISlideRaw>((s) => ({
  404. ...s,
  405. displayId,
  406. config: JSON.stringify(s.config)
  407. }))
  408. })
  409. })
  410. const slideReponse: Slide = result.slideResponse.payload
  411. slideReponse.config = JSON.parse(slideReponse.config)
  412. yield put(VizActions.slideAdded(slideReponse, insertSlideIdx, afterSlides))
  413. const { id: projectId } = yield select(makeSelectCurrentProject())
  414. const nextPath = `/project/${projectId}/display/${displayId}/slide/${
  415. slideReponse.id
  416. }`
  417. yield put(push(nextPath))
  418. } catch (err) {
  419. yield put(VizActions.addSlideFail())
  420. errorHandler(err)
  421. }
  422. }
  423. export function* editSlides(action: VizActionType) {
  424. if (action.type !== ActionTypes.EDIT_SLIDES) {
  425. return
  426. }
  427. const { slides } = action.payload
  428. const { id: displayId } = yield select(makeSelectCurrentDisplay())
  429. try {
  430. yield call(request, {
  431. method: 'put',
  432. url: `${api.display}/${displayId}/slides`,
  433. data: slides.map((slide) => ({
  434. ...slide,
  435. config: JSON.stringify(slide.config),
  436. displayId
  437. }))
  438. })
  439. yield put(VizActions.slidesEdited(displayId, slides))
  440. } catch (err) {
  441. yield put(VizActions.editSlidesFail())
  442. errorHandler(err)
  443. }
  444. }
  445. export function* editCurrentSlideParams(action: VizActionType) {
  446. if (action.type !== ActionTypes.EDIT_CURRENT_SLIDE_PARAMS) {
  447. return
  448. }
  449. const currentSlide: ISlideFormed = yield select(makeSelectCurrentSlide())
  450. const updateSlide = produce(currentSlide, (draft) => {
  451. draft.config.slideParams = {
  452. ...draft.config.slideParams,
  453. ...action.payload.changedParams
  454. }
  455. })
  456. yield put(VizActions.editSlides([updateSlide]))
  457. }
  458. export function* deleteSlides(action: VizActionType) {
  459. if (action.type !== ActionTypes.DELETE_SLIDES) {
  460. return
  461. }
  462. const { displayId, slideIds } = action.payload
  463. try {
  464. yield all(
  465. slideIds.map((id) =>
  466. call(request, {
  467. method: 'delete',
  468. url: `${api.display}/slides/${id}`
  469. })
  470. )
  471. )
  472. const currentSlides: ISlideFormed[] = yield select(
  473. makeSelectCurrentSlides()
  474. )
  475. const { id: projectId } = yield select(makeSelectCurrentProject())
  476. const lastDeleteSlideId = slideIds[slideIds.length - 1]
  477. const lastDeleteSlideIdx = currentSlides.findIndex(
  478. ({ id }) => id === lastDeleteSlideId
  479. )
  480. let i = lastDeleteSlideIdx
  481. let nextSlideId = currentSlides[0].id
  482. while (++i < currentSlides.length) {
  483. if (!slideIds.includes(currentSlides[i].id)) {
  484. nextSlideId = currentSlides[i].id
  485. break
  486. }
  487. }
  488. if (nextSlideId === currentSlides[0].id) {
  489. i = lastDeleteSlideIdx
  490. while (--i < currentSlides.length) {
  491. if (!slideIds.includes(currentSlides[i].id)) {
  492. nextSlideId = currentSlides[i].id
  493. break
  494. }
  495. }
  496. }
  497. yield put(
  498. replace(`/project/${projectId}/display/${displayId}/slide/${nextSlideId}`)
  499. )
  500. yield put(VizActions.slidesDeleted(displayId, slideIds))
  501. } catch (err) {
  502. yield put(VizActions.deleteSlidesFail())
  503. errorHandler(err)
  504. }
  505. }
  506. export default function* rootVizSaga() {
  507. yield all([
  508. takeLatest(ActionTypes.LOAD_PORTALS, getPortals),
  509. takeEvery(ActionTypes.ADD_PORTAL, addPortal),
  510. takeEvery(ActionTypes.EDIT_PORTAL, editPortal),
  511. takeEvery(ActionTypes.DELETE_PORTAL, deletePortal),
  512. takeEvery(ActionTypes.LOAD_PORTAL_DASHBOARDS, getPortalDashboards),
  513. takeLatest(ActionTypes.LOAD_DISPLAYS, getDisplays),
  514. takeEvery(ActionTypes.ADD_DISPLAY, addDisplay),
  515. takeEvery(ActionTypes.EDIT_DISPLAY, editDisplay),
  516. takeEvery(ActionTypes.DELETE_DISPLAY, deleteDisplay),
  517. takeEvery(ActionTypes.COPY_DISPLAY, copyDisplay),
  518. takeEvery(ActionTypes.LOAD_DISPLAY_SLIDES, getDisplaySlides),
  519. takeLatest(ActionTypes.ADD_DASHBOARD, addDashboard),
  520. takeEvery(ActionTypes.EDIT_DASHBOARD, editDashboard),
  521. takeEvery(ActionTypes.EDIT_CURRENT_DASHBOARD, editCurrentDashboard),
  522. takeEvery(ActionTypes.DELETE_DASHBOARD, deleteDashboard),
  523. takeLatest(ActionTypes.ADD_SLIDE, addSlide),
  524. takeEvery(ActionTypes.EDIT_SLIDES, editSlides),
  525. takeEvery(ActionTypes.EDIT_CURRENT_SLIDE_PARAMS, editCurrentSlideParams),
  526. takeEvery(ActionTypes.DELETE_SLIDES, deleteSlides)
  527. ])
  528. }