Skip to content
Snippets Groups Projects
ManuscriptPage.js 13.8 KiB
Newer Older
import { connect } from 'react-redux'
import { actions } from 'pubsweet-client'
import { ConnectPage } from 'xpub-connect'
import { withJournal } from 'xpub-journal'
import { withRouter } from 'react-router-dom'
import { head, get, isUndefined } from 'lodash'
import { getFormValues, change as changeForm } from 'redux-form'
  selectCollection,
  selectCurrentUser,
import { get as apiGet } from 'pubsweet-client/src/helpers/api'
import { getSignedUrl } from 'pubsweet-components-faraday/src/redux/files'
  reviewerDecision,
} from 'pubsweet-components-faraday/src/redux/reviewers'
import {
  hasManuscriptFailure,
  clearCustomError,
} from 'pubsweet-components-faraday/src/redux/errors'
import {
  createRecommendation,
  selectReviewRecommendations,
  selectEditorialRecommendations,
} from 'pubsweet-components-faraday/src/redux/recommendations'
  canMakeRevision,
  canMakeDecision,
Bogdan Cochior's avatar
Bogdan Cochior committed
  parseCollectionDetails,
  canViewReviewersDetails,
  getOwnPendingRecommendation,
  getOwnSubmittedRecommendation,
  getFragmentReviewerRecommendations,
} from 'pubsweet-component-faraday-selectors'
import { RemoteOpener, handleError } from 'pubsweet-component-faraday-ui'

import ManuscriptLayout from './ManuscriptLayout'
import { parseEicDecision, parseSearchParams, redirectToError } from './utils'
  getHandlingEditors,
  assignHandlingEditor,
  revokeHandlingEditor,
  selectHandlingEditors,
} from '../redux/editors'
  setDisplayName('ManuscriptPage'),
  withState('editorInChief', 'setEiC', 'N/A'),
  ConnectPage(({ match }) => [
    actions.getCollection({ id: match.params.project }),
    actions.getFragments({ id: match.params.project }),
  ]),
  connect(
    (state, { match }) => ({
      currentUser: selectCurrentUser(state),
      handlingEditors: selectHandlingEditors(state),
      hasManuscriptFailure: hasManuscriptFailure(state),
      fragment: selectFragment(state, match.params.version),
Bogdan Cochior's avatar
Bogdan Cochior committed
      collection: parseCollectionDetails(
        state,
        selectCollection(state, match.params.project),
      ),
      reviewerReports: selectReviewRecommendations(state, match.params.version),
      pendingHEInvitation: pendingHEInvitation(state, match.params.project),
      pendingOwnRecommendation: getOwnPendingRecommendation(
        state,
        match.params.version,
      ),
      submittedOwnRecommendation: getOwnSubmittedRecommendation(
        state,
        match.params.version,
      ),
      pendingReviewerInvitation: pendingReviewerInvitation(
        state,
        match.params.version,
      ),
      editorialRecommendations: selectEditorialRecommendations(
        state,
        match.params.version,
      ),
      reviewerRecommendations: getFragmentReviewerRecommendations(
        state,
        match.params.version,
      ),
      changeForm,
      assignHandlingEditor,
      revokeHandlingEditor,
      getFragment: actions.getFragment,
      getCollection: actions.getCollection,
      updateVersion: actions.updateFragment,
        fragment,
        collection,
        currentUser,
        pendingHEInvitation,
        pendingOwnRecommendation,
    ) => ({
      currentUser: {
        ...currentUser,
        token: getUserToken(state),
        isHE: currentUserIs(state, 'isHE'),
        isEIC: currentUserIs(state, 'adminEiC'),
        isInvitedHE: !isUndefined(pendingHEInvitation),
        isInvitedToReview: !isUndefined(pendingReviewerInvitation),
        isReviewer: currentUserIsReviewer(state, get(fragment, 'id', '')),
        isHEToManuscript: isHEToManuscript(state, get(collection, 'id', '')),
          canMakeHERecommendation: canMakeHERecommendation(state, {
            collection,
            statuses: get(journal, 'statuses', {}),
          }),
          canAssignHE: canAssignHE(state, match.params.project),
          canViewReports: canViewReports(state, match.params.project),
          canInviteReviewers: canInviteReviewers(state, collection),
          canMakeRecommendation: !isUndefined(pendingOwnRecommendation),
          canMakeRevision: canMakeRevision(state, collection, fragment),
          canMakeDecision: canMakeDecision(state, collection, fragment),
          canEditManuscript: canEditManuscript(state, collection, fragment),
          canViewReviewersDetails: canViewReviewersDetails(state, collection),
          canOverrideTechChecks: canOverrideTechnicalChecks(state, collection),
        },
      },
      isFetching: {
        editorsFetching: selectFetching(state),
      },
      formValues: {
        eicDecision: getFormValues('eic-decision')(state),
        reviewerReport: getFormValues('reviewerReport')(state),
        responseToInvitation: getFormValues('answer-invitation')(state),
        editorialRecommendation: getFormValues('HERecommendation')(state),
      invitationsWithReviewers: getInvitationsWithReviewersForFragment(
        state,
        get(fragment, 'id', ''),
      ),
  ConnectPage(({ currentUser }) => {
    if (currentUser.isEIC) {
      return [getHandlingEditors()]
    }
    return []
  withHandlers({
    fetchUpdatedCollection: ({
      fragment,
      collection,
      getFragment,
      getCollection,
    }) => () => {
      getCollection({ id: collection.id })
      getFragment(collection, fragment)
    updateManuscript: ({ updateVersion, collection, fragment }) => data =>
      updateVersion(collection, {
        id: fragment.id,
    setEditorInChief: ({ setEiC }) => eic => {
      if (eic) {
        const { firstName = '', lastName = '' } = eic
        setEiC(`${firstName} ${lastName}`)
      }
    },
    assignHE: ({
      assignHandlingEditor,
      collection: { id: collectionId },
    }) => (email, modalProps) =>
      assignHandlingEditor({
        email,
        collectionId,
      })
        .catch(handleError(modalProps.setModalError)),
    revokeHE: ({
      getCollection,
      revokeHandlingEditor,
      collection: { id: collectionId },
    }) => (invitationId, modalProps) =>
      revokeHandlingEditor({
        invitationId,
        collectionId,
      })
        .then(() => {
          getCollection({ id: collectionId })
          modalProps.hideModal()
        })
        .catch(handleError(modalProps.setModalError)),
    }) => (values, modalProps) => {
      const recommendation = parseEicDecision(values)
      modalProps.setFetching(true)
      createRecommendation({
        recommendation,
        fragmentId: fragment.id,
        collectionId: collection.id,
      })
        .then(() => {
          modalProps.setFetching(false)
          fetchUpdatedCollection()
        })
        .catch(err => {
          modalProps.setFetching(false)
          handleError(modalProps.setModalError)(err)
    onHEResponse: ({
      history,
      collection,
      pendingHEInvitation,
      fetchUpdatedCollection,
    }) => (values, { hideModal, setModalError, setFetching }) => {
      const isAccepted = get(values, 'decision', 'decline') === 'accept'
      setFetching(true)
      return handlingEditorDecision({
        isAccepted,
        collectionId: collection.id,
        reason: get(values, 'reason', ''),
        invitationId: pendingHEInvitation.id,
      })
        .then(() => {
          setFetching(false)
          hideModal()
          if (isAccepted) {
            fetchUpdatedCollection()
          } else {
            history.replace('/')
          }
        })
    onReviewerResponse: ({
      history,
      fragment,
      collection,
      fetchUpdatedCollection,
      pendingReviewerInvitation,
    }) => (values, { hideModal, setModalError, setFetching }) => {
      const isAccepted = get(values, 'decision', 'decline') === 'accept'
      setFetching(true)
      reviewerDecision({
        agree: isAccepted,
        fragmentId: fragment.id,
        collectionId: collection.id,
        invitationId: pendingReviewerInvitation.id,
      })
        .then(() => {
          setFetching(false)
          hideModal()
          if (isAccepted) {
            fetchUpdatedCollection()
          } else {
            history.replace('/')
          }
        })
    onInviteReviewer: ({ collection, fragment, fetchUpdatedCollection }) => (
      values,
      { hideModal, setModalError, setFetching },
    ) => {
      setFetching(true)
      inviteReviewer({
        reviewerData: values,
        fragmentId: fragment.id,
        collectionId: collection.id,
      })
        .then(() => {
          setFetching(false)
          hideModal()
          fetchUpdatedCollection()
        })
    onResendReviewerInvite: ({
      fragment,
      collection,
      fetchUpdatedCollection,
    }) => (email, { hideModal, setFetching, setModalError }) => {
      setFetching(true)
      inviteReviewer({
        reviewerData: {
          email,
          role: 'reviewer',
        },
        fragmentId: fragment.id,
        collectionId: collection.id,
      })
        .then(() => {
          setFetching(false)
          hideModal()
          fetchUpdatedCollection()
        })
        })
    },
    onRevokeReviewerInvite: ({
      fragment,
      collection,
      fetchUpdatedCollection,
    }) => (invitationId, { hideModal, setFetching, setModalError }) => {
      setFetching(true)
      revokeReviewer({
        invitationId,
        fragmentId: fragment.id,
        collectionId: collection.id,
      })
        .then(() => {
          setFetching(false)
          hideModal()
          fetchUpdatedCollection()
        })
    onEditorialRecommendation: ({
      fragment,
      collection,
      fetchUpdatedCollection,
    }) => (recommendation, { hideModal, setFetching, setModalError }) => {
      setFetching(true)
      createRecommendation({
        recommendation,
        fragmentId: fragment.id,
        collectionId: collection.id,
      })
        .then(r => {
          setFetching(false)
          hideModal()
          fetchUpdatedCollection()
        })
        .catch(e => {
          setFetching(false)
          handleError(setModalError)(e)
        })
    },
  fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
    toggleAssignHE: toggle,
    heExpanded: expanded,
  })),
  fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
    toggleHEResponse: toggle,
    heResponseExpanded: expanded,
  })),
  fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
    toggleReviewerResponse: toggle,
    reviewerResponseExpanded: expanded,
  })),
  fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
    toggleReviewerRecommendations: toggle,
    reviewerRecommendationExpanded: expanded,
  })),
  fromRenderProps(RemoteOpener, ({ toggle, expanded }) => ({
    toggleReviewerDetails: toggle,
    reviewerDetailsExpanded: expanded,
  })),
  withProps(({ currentUser, collection, submittedOwnRecommendation }) => ({
    shouldReview:
      get(currentUser, 'isReviewer', false) &&
      isUndefined(submittedOwnRecommendation),
  })),
  lifecycle({
    componentDidMount() {
      const {
        shouldReview,
        clearCustomError,
        hasManuscriptFailure,
        currentUser: { isInvitedHE, isInvitedToReview, isHEToManuscript },
      if (hasManuscriptFailure) {
        history.push('/not-found')
        clearCustomError()
      }

      const collectionId = match.params.project
      const fragmentId = match.params.version
      const { agree, invitationId } = parseSearchParams(location.search)
      if (agree === 'true') {
        reviewerDecision({ invitationId, collectionId, fragmentId })
          .then(fetchUpdatedCollection)
          .catch(redirectToError(history.replace))
      }

      apiGet(`/users?editorInChief=true`).then(res =>
        setEditorInChief(head(res.users)),
      )

      if (isInvitedHE) {
        this.props.toggleHEResponse()
      }

      if (isInvitedToReview) {
        this.props.toggleReviewerResponse()
      }

      if (shouldReview) {
        this.props.toggleReviewerRecommendations()
      }
        this.props.toggleReviewerDetails()
      }