| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759 | /* * << * Davinci * == * Copyright (C) 2016 - 2017 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> */import {  call,  put,  all,  select,  takeLatest,  takeEvery,  take} from 'redux-saga/effects'import produce from 'immer'import request from 'utils/request'import api from 'utils/api'import { errorHandler } from 'utils/util'import { ActionTypes, DragTriggerTypes } from './constants'import DisplayActions, { DisplayActionType } from './actions'import {  ILayerRaw,  ILayerParams,  Layer,  ILayerFormed,  LayersOperationInfo,  DeltaPosition,  DeltaSize} from './components/types'import { IWidgetRaw } from '../Widget/types'import VizActions from 'containers/Viz/actions'import ViewActions from 'containers/View/actions'import { getViewsDetail } from 'containers/View/sagas'import {  makeSelectLayersBySlide,  makeSelectCurrentSelectedLayerList,  makeSelectCurrentLayerList,  makeSelectCurrentSelectedLayerIds,  makeSelectCurrentOperatingLayerList,  makeSelectCurrentOtherLayerList,  makeSelectClipboardLayers,  makeSelectCurrentOperateItemParams,  makeSelectCurrentEditLayerOperationInfo} from './selectors'import { makeSelectFormedViews } from 'containers/View/selectors'import { bringToFront, sendToBottom, bringToUpper, sendToNext } from './util'import { LayerOperations } from './components/constants'import {  makeSelectCurrentDisplay,  makeSelectCurrentSlide} from '../Viz/selectors'import { computeEditorBaselines, setLayersAlignment } from './components/util'import { DefaultDisplayParams } from './components/Setting/constants'import { IDisplayFormed } from '../Viz/types'import { IWidgetConfig } from '../Widget/components/Widget'import {  widgetConfigMigrationRecorder,  displayParamsMigrationRecorder} from 'app/utils/migrationRecorders'import { OperationInfo } from './components/Layer/types'import { SecondaryGraphTypes } from './components/Setting'import { IFormedViews } from '../View/types'export function* getSlideDetail (action: DisplayActionType) {  if (action.type !== ActionTypes.LOAD_SLIDE_DETAIL) {    return  }  const { displayId, slideId } = action.payload  const layers: LayersOperationInfo = yield select((state) =>    makeSelectLayersBySlide()(state, slideId)  )  if (layers) {    return  }  const { slideDetailLoaded, loadSlideDetailFail } = DisplayActions  try {    const slideDetail = yield call(      request,      `${api.display}/${displayId}/slides/${slideId}`    )    const { items, views, widgets } = slideDetail.payload || {      items: [],      views: [],      widgets: []    }    items.forEach((item: ILayerRaw) => {      const { subType } = item      const parsedParams: ILayerParams = JSON.parse(item.params)      item.params =        SecondaryGraphTypes.Label === subType          ? displayParamsMigrationRecorder(parsedParams)          : parsedParams    })    widgets.forEach((widget: IWidgetRaw) => {      const parsedConfig: IWidgetConfig = JSON.parse(widget.config)      widget.config = widgetConfigMigrationRecorder(parsedConfig, {        viewId: widget.viewId      })    })    const formedViews: IFormedViews = views.reduce(      (obj, view) => {        obj[view.id] = {          ...view,          model: JSON.parse(view.model || '{}'),          variable: JSON.parse(view.variable || '[]')        }        return obj      },      {}    )    yield put(slideDetailLoaded(slideId, items, widgets, formedViews))  } catch (err) {    yield put(loadSlideDetailFail(err))  }}export function* uploadCurrentSlideCover (action: DisplayActionType) {  if (action.type !== ActionTypes.UPLOAD_CURRENT_SLIDE_COVER) {    return  }  const { cover, slide } = action.payload  const {    currentSlideCoverUploaded,    uploadCurrentSlideCoverFail  } = DisplayActions  try {    const formData = new FormData()    formData.append('coverImage', new File([cover], 'coverImage.png'))    const asyncData = yield call(request, `${api.display}/upload/coverImage`, {      method: 'post',      data: formData    })    const coverPath = asyncData.payload    yield put(currentSlideCoverUploaded(coverPath))    const editedSlide = produce(slide, (draft) => {      draft.config.slideParams.avatar = coverPath    })    yield put(VizActions.editSlides([editedSlide]))  } catch (err) {    yield put(uploadCurrentSlideCoverFail(err))    errorHandler(err)  }}export function* addSlideLayers (action: DisplayActionType) {  if (action.type !== ActionTypes.ADD_SLIDE_LAYERS) {    return  }  const { displayId, slideId, layers, widgets } = action.payload  const { slideLayersAdded, addSlideLayersFail } = DisplayActions  const formedViews = yield select(makeSelectFormedViews())  const requestViewIds = []  if (Array.isArray(widgets)) {    widgets.forEach((w) => {      if (w.viewId && !formedViews[w.viewId]) {        requestViewIds.push(w.viewId)      }    })  }  try {    if (requestViewIds.length) {      yield getViewsDetail(ViewActions.loadViewsDetail(requestViewIds))    }    const asyncData = yield call(      request,      `${api.display}/${displayId}/slides/${slideId}/widgets`,      {        method: 'post',        data: layers.map<Omit<ILayerRaw, 'id'>>((layer) => ({          ...layer,          params: JSON.stringify(layer.params)        }))      }    )    const result: Layer[] = asyncData.payload    result.forEach((item) => (item.params = JSON.parse(item.params as string)))    yield put(slideLayersAdded(slideId, result as ILayerFormed[], widgets))    return result  } catch (err) {    yield put(addSlideLayersFail())    errorHandler(err)  }}export function* editSlideLayers (action: DisplayActionType) {  if (action.type !== ActionTypes.EDIT_SLIDE_LAYERS) {    return  }  const { displayId, slideId, layers, layerParamsUnChanged } = action.payload  const { slideLayersEdited, editSlideLayersFail } = DisplayActions  try {    if (!layerParamsUnChanged) {      yield call(        request,        `${api.display}/${displayId}/slides/${slideId}/widgets`,        {          method: 'put',          data: layers.map<ILayerRaw>((layer) => ({            ...layer,            params: JSON.stringify(layer.params)          }))        }      )    }    yield put(slideLayersEdited(slideId, layers))  } catch (err) {    yield put(editSlideLayersFail())    errorHandler(err)  }}export function* editSlideLayerParams (action: DisplayActionType) {  if (action.type !== ActionTypes.EDIT_SLIDE_LAYER_PARAMS) {    return  }  const layerList: ILayerFormed[] = yield select(makeSelectCurrentLayerList())  if (!layerList.length) {    return  }  const layer = layerList.find(({ id }) => id === action.payload.layerId)  const { id: displayId } = yield select(makeSelectCurrentDisplay())  const { id: slideId } = yield select(makeSelectCurrentSlide())  const updateLayer = {    ...layer,    params: {      ...layer.params,      ...action.payload.changedParams    }  }  yield put(DisplayActions.editSlideLayers(displayId, slideId, [updateLayer]))}export function* deleteSlideLayers (action: DisplayActionType) {  if (action.type !== ActionTypes.DELETE_SLIDE_LAYERS) {    return  }  const { displayId, slideId } = action.payload  const { slideLayersDeleted, deleteSlideLayersFail } = DisplayActions  const selectedLayerIds: number[] = yield select(    makeSelectCurrentSelectedLayerIds()  )  const editedLayerInfo: OperationInfo[] = yield select(    makeSelectCurrentEditLayerOperationInfo()  )  if (editedLayerInfo.length) {    return  }  if (!selectedLayerIds.length) {    return  }  try {    yield call(      request,      `${api.display}/${displayId}/slides/${slideId}/widgets`,      {        method: 'delete',        data: selectedLayerIds      }    )    yield put(slideLayersDeleted(slideId, selectedLayerIds))  } catch (err) {    yield put(deleteSlideLayersFail())    errorHandler(err)  }}export function* dragLayer (action: DisplayActionType) {  if (action.type !== ActionTypes.DRAG_LAYER) {    return  }  const {    deltaPosition,    layerId,    finish,    slideSize,    scale,    eventTrigger  } = action.payload  const movingLayers: ILayerFormed[] = yield select((state) =>    makeSelectCurrentOperatingLayerList()(state, layerId)  )  const editedLayerInfo: OperationInfo[] = yield select(    makeSelectCurrentEditLayerOperationInfo()  )  if (!movingLayers.length) {    return  }  if (editedLayerInfo.length) {    return  }  const operateItemParams = yield select(makeSelectCurrentOperateItemParams())  const layerParamsUnChanged = operateItemParams.length === 0  const operateParamsMap = new Map()  operateItemParams.forEach((item) => {    operateParamsMap.set(item.id, item)  })  const updateMovingLayers = movingLayers.map((layer) => {    const id = layer.id    if (operateParamsMap.has(id)) {      layer.params = operateParamsMap.get(id).params      return { ...{}, ...layer, ...{ params: operateParamsMap.get(id).params } }    }    return layer  })  const otherLayers = yield select((state) =>    makeSelectCurrentOtherLayerList()(state, layerId)  )  const {    id: displayId,    config: { displayParams }  }: IDisplayFormed = yield select(makeSelectCurrentDisplay())  const { id: slideId } = yield select(makeSelectCurrentSlide())  const [updateMovingLayerItem] = updateMovingLayers  const { subType } = updateMovingLayerItem  const baselines = computeEditorBaselines(    updateMovingLayers,    otherLayers,    slideSize,    (displayParams || DefaultDisplayParams).grid,    scale,    { ...deltaPosition, deltaWidth: 0, deltaHeight: 0 },    'position'  )  const { deltaX, deltaY } = deltaPosition  const needSnapToGrid = eventTrigger === DragTriggerTypes.MouseMove  const deltaPositionAdjusted: DeltaPosition = baselines.reduce<DeltaPosition>(    (acc, bl) => ({      deltaX: acc.deltaX + (needSnapToGrid && bl.adjust[0]),      deltaY: acc.deltaY + (needSnapToGrid && bl.adjust[1])    }),    { deltaX, deltaY }  )  yield put(    DisplayActions.dragLayerAdjusted(      updateMovingLayers.map(({ id }) => id),      slideSize,      deltaPositionAdjusted,      finish    )  )  if (subType !== SecondaryGraphTypes.Label) {    yield put(DisplayActions.showEditorBaselines(baselines))  }  if (finish) {    const updateLayers = produce(updateMovingLayers, (draft) => {      draft.forEach((layer) => {        const item = operateParamsMap.get(layer.id)        if (item) {          layer.params.positionX += deltaX          layer.params.positionY += deltaY        }      })    })    yield put(      DisplayActions.editSlideLayers(        displayId,        slideId,        updateLayers,        layerParamsUnChanged      )    )    yield take(ActionTypes.EDIT_SLIDE_LAYERS_SUCCESS)    yield put(DisplayActions.clearEditorBaselines())  }}export function* resizeLayer (action: DisplayActionType) {  if (action.type !== ActionTypes.RESIZE_LAYER) {    return  }  const { deltaSize, layerId, finish, slideSize, scale } = action.payload  const resizingLayers: ILayerFormed[] = yield select((state) =>    makeSelectCurrentOperatingLayerList()(state, layerId)  )  if (!resizingLayers.length) {    return  }  const otherLayers = yield select((state) =>    makeSelectCurrentOtherLayerList()(state, layerId)  )  const {    id: displayId,    config: { displayParams }  }: IDisplayFormed = yield select(makeSelectCurrentDisplay())  const { id: slideId } = yield select(makeSelectCurrentSlide())  const [resizingLayerItem] = resizingLayers  const { subType } = resizingLayerItem  const baselines = computeEditorBaselines(    resizingLayers,    otherLayers,    slideSize,    (displayParams || DefaultDisplayParams).grid,    scale,    { ...deltaSize, deltaX: 0, deltaY: 0 },    'size'  )  const { deltaWidth, deltaHeight } = deltaSize  const deltaSizeAdjusted: DeltaSize = baselines.reduce<DeltaSize>(    (acc, bl) => ({      deltaWidth: acc.deltaWidth + bl.adjust[0],      deltaHeight: acc.deltaHeight + bl.adjust[1]    }),    { deltaWidth, deltaHeight }  )  if (finish) {    const updateLayers = produce(resizingLayers, (draft) => {      draft.forEach((layer) => {        layer.params.width += deltaWidth        layer.params.height += deltaHeight      })    })    yield put(DisplayActions.editSlideLayers(displayId, slideId, updateLayers))    yield put(DisplayActions.clearEditorBaselines())  } else {    if (subType !== SecondaryGraphTypes.Label) {      yield put(DisplayActions.showEditorBaselines(baselines))    }  }  yield put(    DisplayActions.resizeLayerAdjusted(      resizingLayers.map(({ id }) => id),      deltaSizeAdjusted,      finish    )  )}export function* copySlideLayers (action: DisplayActionType) {  if (action.type !== ActionTypes.COPY_SLIDE_LAYERS) {    return  }  const selectedLayers: ILayerFormed[] = yield select(    makeSelectCurrentSelectedLayerList()  )  if (!selectedLayers) {    return  }  yield put(DisplayActions.slideLayersCopied(selectedLayers))}export function* pasteSlideLayers (action: DisplayActionType) {  if (action.type !== ActionTypes.PASTE_SLIDE_LAYERS) {    return  }  const clipboardLayers: ILayerFormed[] = yield select(    makeSelectClipboardLayers()  )  if (!clipboardLayers.length) {    return  }  const { id: displayId } = yield select(makeSelectCurrentDisplay())  const { id: slideId } = yield select(makeSelectCurrentSlide())  const layers = produce(clipboardLayers, (draft) => {    draft.forEach((layer) => {      layer.id = undefined      layer.displaySlideId = slideId    })  })  yield put(DisplayActions.addSlideLayers(displayId, slideId, layers))}export function* changeLayersStack (action: DisplayActionType) {  if (action.type !== ActionTypes.CHANGE_LAYERS_STACK) {    return  }  const selectedLayerList = yield select(makeSelectCurrentSelectedLayerList())  if (!selectedLayerList.length) {    return  }  const { operation } = action.payload  const { id: displayId } = yield select(makeSelectCurrentDisplay())  const { id: slideId } = yield select(makeSelectCurrentSlide())  const layers = yield select(makeSelectCurrentLayerList())  let updateLayers: ILayerFormed[]  switch (operation) {    case LayerOperations.BringToFront:      updateLayers = bringToFront(selectedLayerList, layers)      break    case LayerOperations.SendToBottom:      updateLayers = sendToBottom(selectedLayerList, layers)      break    case LayerOperations.BringToUpper:      updateLayers = bringToUpper(selectedLayerList, layers)      break    case LayerOperations.SendToNext:      updateLayers = sendToNext(selectedLayerList, layers)      break  }  if (!updateLayers.length) {    return  }  yield put(DisplayActions.editSlideLayers(displayId, slideId, updateLayers))}export function* updateLayersAlignment (action: DisplayActionType) {  if (action.type !== ActionTypes.SET_LAYERS_ALIGNMENT) {    return  }  const selectedLayerList = yield select(makeSelectCurrentSelectedLayerList())  if (!selectedLayerList.length) {    return  }  const { id: displayId } = yield select(makeSelectCurrentDisplay())  const { id: slideId } = yield select(makeSelectCurrentSlide())  const updateLayers = setLayersAlignment(    selectedLayerList,    action.payload.alignmentType  )  yield put(DisplayActions.editSlideLayers(displayId, slideId, updateLayers))}export function* getDisplayShareLink (action: DisplayActionType) {  if (action.type !== ActionTypes.LOAD_DISPLAY_SHARE_LINK) {    return  }  const { id, mode, expired, permission, roles, viewers } = action.payload.params  const {    displayAuthorizedShareLinkLoaded,    displayShareLinkLoaded,    loadDisplayShareLinkFail,    displayPasswordShareLinkLoaded  } = DisplayActions  let requestData = null  switch (mode) {    case 'AUTH':      requestData = { mode, expired, permission, roles, viewers }      break    case 'PASSWORD':    case 'NORMAL':      requestData = { mode, expired }      break    default:      break  }  try {    const asyncData = yield call(request, {      method: 'POST',      url: `${api.display}/${id}/share`,      data: requestData    })    const { token, password } = asyncData.payload    switch (mode) {      case 'AUTH':        yield put(displayAuthorizedShareLinkLoaded(token))        break      case 'PASSWORD':        yield put(displayPasswordShareLinkLoaded(token, password))        break      case 'NORMAL':        yield put(displayShareLinkLoaded(token))        break      default:        break    }  } catch (err) {    yield put(loadDisplayShareLinkFail())    errorHandler(err)  }}export function* undoOperation (action: DisplayActionType) {  if (action.type !== ActionTypes.UNDO_OPERATION) {    return  }  const { currentState } = action.payload  const { displayId, slide, lastOperationType, lastLayers } = currentState  const slideId = slide.id  const { undoOperationDone, undoOperationFail } = DisplayActions  try {    switch (lastOperationType) {      // case ActionTypes.EDIT_CURRENT_SLIDE_SUCCESS:      //   yield call(request, `${api.display}/${displayId}/slides`, {      //     method: 'put',      //     data: [      //       {      //         ...slide,      //         displayId      //       }      //     ]      //   })      //   break      case ActionTypes.ADD_SLIDE_LAYERS_SUCCESS:      // case ActionTypes.PASTE_SLIDE_LAYERS_SUCCESS:        const deleteLayerIds = lastLayers.map((l) => l.id)        yield call(          request,          `${api.display}/${displayId}/slides/${slideId}/widgets`,          {            method: 'delete',            data: deleteLayerIds          }        )        break      case ActionTypes.DELETE_SLIDE_LAYERS_SUCCESS:        yield call(          request,          `${api.display}/${displayId}/slides/${slideId}/widgets`,          {            method: 'post',            data: lastLayers          }        )        break      case ActionTypes.EDIT_SLIDE_LAYERS_SUCCESS:        yield call(          request,          `${api.display}/${displayId}/slides/${slideId}/widgets`,          {            method: 'put',            data: lastLayers          }        )        break    }    yield put(undoOperationDone())  } catch (err) {    yield put(undoOperationFail())    errorHandler(err)  }}export function* redoOperation (action: DisplayActionType) {  if (action.type !== ActionTypes.REDO_OPERATION) {    return  }  const { nextState } = action.payload  const { displayId, slide, lastOperationType, lastLayers } = nextState  const slideId = slide.id  const { redoOperationDone, redoOperationFail } = DisplayActions  try {    switch (lastOperationType) {      // case ActionTypes.EDIT_CURRENT_SLIDE_SUCCESS:      //   yield call(request, `${api.display}/${displayId}/slides`, {      //     method: 'put',      //     data: [      //       {      //         ...slide,      //         displayId      //       }      //     ]      //   })      //   break      case ActionTypes.ADD_SLIDE_LAYERS_SUCCESS:      // case ActionTypes.PASTE_SLIDE_LAYERS_SUCCESS:        yield call(          request,          `${api.display}/${displayId}/slides/${slideId}/widgets`,          {            method: 'post',            data: lastLayers          }        )        break      case ActionTypes.DELETE_SLIDE_LAYERS_SUCCESS:        const deleteLayerIds = lastLayers.map((l) => l.id)        yield call(          request,          `${api.display}/${displayId}/slides/${slideId}/widgets`,          {            method: 'delete',            data: deleteLayerIds          }        )        break      case ActionTypes.EDIT_SLIDE_LAYERS_SUCCESS:        yield call(          request,          `${api.display}/${displayId}/slides/${slideId}/widgets`,          {            method: 'put',            data: lastLayers          }        )        break    }    yield put(redoOperationDone())  } catch (err) {    yield put(redoOperationFail())    errorHandler(err)  }}export default function* rootDisplaySaga () {  yield all([    takeLatest(ActionTypes.LOAD_SLIDE_DETAIL, getSlideDetail),    takeEvery(ActionTypes.UPLOAD_CURRENT_SLIDE_COVER, uploadCurrentSlideCover),    takeEvery(ActionTypes.ADD_SLIDE_LAYERS, addSlideLayers),    takeEvery(ActionTypes.EDIT_SLIDE_LAYERS, editSlideLayers),    takeEvery(ActionTypes.EDIT_SLIDE_LAYER_PARAMS, editSlideLayerParams),    takeEvery(ActionTypes.DELETE_SLIDE_LAYERS, deleteSlideLayers),    takeEvery(ActionTypes.COPY_SLIDE_LAYERS, copySlideLayers),    takeEvery(ActionTypes.PASTE_SLIDE_LAYERS, pasteSlideLayers),    takeLatest(ActionTypes.DRAG_LAYER, dragLayer),    takeLatest(ActionTypes.RESIZE_LAYER, resizeLayer),    takeEvery(ActionTypes.CHANGE_LAYERS_STACK, changeLayersStack),    takeLatest(ActionTypes.SET_LAYERS_ALIGNMENT, updateLayersAlignment),    takeLatest(ActionTypes.LOAD_DISPLAY_SHARE_LINK, getDisplayShareLink),    takeEvery(ActionTypes.UNDO_OPERATION, undoOperation),    takeEvery(ActionTypes.REDO_OPERATION, redoOperation)  ])}
 |