import { selectCurrentUser } from 'xpub-selectors' import { get, has, last, chain, some, isEmpty, slice, find } from 'lodash' export const isHEToManuscript = (state, collectionId = '') => { const { id = '', isAccepted = false } = chain(state) .get('collections', []) .find(c => c.id === collectionId) .get('handlingEditor', '') .value() return isAccepted && id === get(state, 'currentUser.user.id') } export const currentUserIs = ({ currentUser: { user } }, role) => { const isAdmin = get(user, 'admin') const isEic = get(user, 'editorInChief') const isHe = get(user, 'handlingEditor') switch (role) { case 'isHE': return isHe case 'staff': return isAdmin || isEic || isHe case 'isEiC': return isEic case 'isAdmin': return isAdmin case 'adminEiC': return isAdmin || isEic default: return false } } const canInviteReviewersStatuses = [ 'heAssigned', 'reviewersInvited', 'underReview', 'reviewCompleted', ] export const canInviteReviewers = (state, collection = {}) => { if (!canInviteReviewersStatuses.includes(get(collection, 'status', 'draft'))) return false const { id: userId } = selectCurrentUser(state) const isAdminEiC = currentUserIs(state, 'adminEiC') const { isAccepted, id: heId } = get(collection, 'handlingEditor', {}) return isAccepted && (userId === heId || isAdminEiC) } const canViewContextualBoxOnOldVersionStatuses = [ 'submitted', 'heInvited', 'heAssigned', ] const canViewContextualBoxOnOldVersion = (collection, fragmentId) => { const fragments = get(collection, 'fragments', []) const oldVersions = slice(fragments, 0, fragments.length - 1) const isOldVersion = !!find(oldVersions, fragment => fragment === fragmentId) return ( isOldVersion && canViewContextualBoxOnOldVersionStatuses.includes( get(collection, 'status', 'draft'), ) ) } const canHEViewContextualBoxOnOldVersion = (collection, fragmentId) => { const fragments = get(collection, 'fragments', []) const oldVersions = slice(fragments, 0, fragments.length - 1) const isOldVersion = !!find(oldVersions, fragment => fragment === fragmentId) return isOldVersion && get(collection, 'status', 'draft') === 'heInvited' } const cannotViewReviewersDetails = [ 'draft', 'technicalChecks', 'submitted', 'heInvited', ] export const canViewReviewersDetails = (state, collection = {}) => { const isHeAssigned = !!get(collection, 'handlingEditor', false) if ( cannotViewReviewersDetails.includes(get(collection, 'status', 'draft')) || !isHeAssigned ) { return false } return canViewReports(state, get(collection, 'id', '')) } const authorAndReviewersCanViewReportsDetailsStatuses = [ 'revisionRequested', 'underReview', 'pendingApproval', 'rejected', 'accepted', 'reviewCompleted', 'reviewersInvited', 'inQa', ] export const authorCanViewReportsDetails = ( state, collection = {}, fragmentId, ) => { const isAuthor = currentUserIsAuthor(state, fragmentId) return ( isAuthor && (authorAndReviewersCanViewReportsDetailsStatuses.includes( get(collection, 'status', 'draft'), ) || canViewContextualBoxOnOldVersion(collection, fragmentId)) ) } export const reviewersCanViewReviewerReports = ( state, collection = {}, fragmentId, ) => { const isReviewer = currentUserIsReviewer(state, fragmentId) const ownRecommendation = getOwnRecommendations(state, fragmentId) return ( isReviewer && authorAndReviewersCanViewReportsDetailsStatuses.includes( get(collection, 'status', 'draft'), ) && get(ownRecommendation[0], 'submittedOn', false) ) } const canHeViewEditorialCommentsStatuses = [ 'revisionRequested', 'rejected', 'accepted', 'inQA', 'pendingApproval', 'reviewCompleted', 'underReview', ] export const canHeViewEditorialComments = (state, collection = {}) => { const isHE = isHEToManuscript(state, get(collection, 'id', '')) const status = get(collection, 'status', 'draft') return isHE && canHeViewEditorialCommentsStatuses.includes(status) } const canEICViewEditorialCommentsStatuses = [ 'rejected', 'accepted', 'inQA', 'pendingApproval', 'reviewCompleted', 'revisionRequested', 'underReview', ] export const canEICViewEditorialComments = (state, collection = {}) => { const isEIC = currentUserIs(state, 'adminEiC') const status = get(collection, 'status', 'draft') return isEIC && canEICViewEditorialCommentsStatuses.includes(status) } const decisionTakenStatuses = ['rejected', 'accepted', 'inQA'] const canReviewerViewEditorialCommentsStatuses = [ 'underReview', 'reviewCompleted', 'pendingApproval', 'revisionRequested', 'reviewersInvited', ] export const canReviewerViewEditorialComments = ( state, collection = {}, fragment = {}, ) => { const status = get(collection, 'status', 'draft') const isReviewer = currentUserIsReviewer(state, get(fragment, 'id', '')) const hasDecision = decisionTakenStatuses.includes(status) const hasSubmission = some(state.fragments, f => f.version > fragment.version) return ( isReviewer && (hasDecision || (hasSubmission && canReviewerViewEditorialCommentsStatuses.includes(status))) ) } const cannotAuthorViewEditorialCommentsStatuses = [ 'draft', 'technicalChecks', 'submitted', 'heInvited', 'heAssigned', 'reviewersInvited', ] export const canAuthorViewEditorialComments = ( state, collection = {}, fragmentId, ) => { const isAuthor = currentUserIsAuthor(state, fragmentId) return ( isAuthor && !cannotAuthorViewEditorialCommentsStatuses.includes( get(collection, 'status', 'draft'), ) ) } export const canViewEditorialComments = ( state, collection = {}, fragment = {}, ) => { const fragmentId = get(fragment, 'id', '') const editorialRecommentations = getFragmentEditorialComments( state, fragmentId, ) const isHE = currentUserIs(state, 'isHE') const canViewEditorialCommentsOnOldVersion = isHE ? !canHEViewContextualBoxOnOldVersion(collection, fragmentId) : canViewContextualBoxOnOldVersion(collection, fragmentId) return ( (canViewEditorialCommentsOnOldVersion || canHeViewEditorialComments(state, collection) || canEICViewEditorialComments(state, collection) || canReviewerViewEditorialComments(state, collection, fragment) || canAuthorViewEditorialComments(state, collection, fragmentId)) && editorialRecommentations.length > 0 ) } export const canViewResponseFromAuthor = (state, collection, fragmentId) => { const authorResponseToRevisonRequest = getFragmentAuthorResponse( state, fragmentId, ) const canHEViewResponseFromAuthor = currentUserIs(state, 'isHE') && get(collection, 'status', 'draft') === 'heInvited' const canReviewerViewResponsefromAuthor = currentUserIsReviewerInPending(state, fragmentId) && get(collection, 'status', 'draft') === 'reviewersInvited' return ( !isEmpty(authorResponseToRevisonRequest) && !canHEViewResponseFromAuthor && !canReviewerViewResponsefromAuthor ) } export const getUserToken = ({ currentUser }) => get(currentUser, 'user.token', '') export const getHERecommendation = (state, collectionId, fragmentId) => { const heId = chain(state) .get('collections', []) .find(c => c.id === collectionId) .get('handlingEditor.id', '') .value() return ( chain(state) .get(`fragments.${fragmentId}.recommendations`, []) .find(r => r.rec === 'editorRecommendation' && r.userId === heId) .value() || {} ) } export const canMakeDecision = (state, collection = {}) => { const isEIC = currentUserIs(state, 'adminEiC') return isEIC } const collectionReviewerReports = state => chain(state) .get('fragments', {}) .map(r => get(r, 'recommendations', [])) .flatten() .find(r => r.recommendationType === 'review' && r.submittedOn) .value() const cannotHEMakeRecommendationToPublishStatuses = ['heInvited'] export const canHEMakeRecommendationToPublish = (state, collection = {}) => { const status = get(collection, 'status', 'draft') return ( !!collectionReviewerReports(state) || cannotHEMakeRecommendationToPublishStatuses.includes(status) ) } const canHEOnlyRejectStatuses = [ 'reviewersInvited', 'underReview', 'revisionRequested', ] export const canHEOnlyReject = (collection = {}) => { const { status } = collection return canHEOnlyRejectStatuses.includes(status) } const cannotEditManuscriptStatuses = ['withdrawn', 'rejected', 'accepted'] export const canEditManuscript = (state, collection = {}, fragment = {}) => { const isAdmin = currentUserIs(state, 'isAdmin') if ( !isAdmin || get(fragment, 'id', '') !== last(get(collection, 'fragments', [])) ) return false const status = get(collection, 'status', 'draft') return !cannotEditManuscriptStatuses.includes(status) } const canOverrideTechnicalChecksStatuses = ['technicalChecks', 'inQA'] export const canOverrideTechnicalChecks = (state, collection = {}) => { const isAdmin = currentUserIs(state, 'isAdmin') if (!isAdmin) return false const status = get(collection, 'status', 'draft') return canOverrideTechnicalChecksStatuses.includes(status) } export const canViewReports = (state, collectionId) => { const isHE = isHEToManuscript(state, collectionId) const isEiC = currentUserIs(state, 'adminEiC') return isHE || isEiC } export const canMakeRevision = (state, collection = {}, fragment = {}) => { const currentUserId = get(state, 'currentUser.user.id') return ( get(fragment, 'revision') && get(collection, 'status', 'draft') === 'revisionRequested' && get(fragment, 'owners', []) .map(o => o.id) .includes(currentUserId) ) } export const currentUserIsAuthor = (state, fragmentId) => { const { id: userId } = selectCurrentUser(state) const authors = get(state, `fragments.${fragmentId}.authors`, []) return !!authors.find(a => a.id === userId) } export const getUserPermissions = ({ teams = [] }) => teams.map(t => ({ objectId: get(t, 'object.id', ''), objectType: get(t, 'object.type', ''), role: get(t, 'teamType.permissions', ''), })) export const userNotConfirmed = ({ currentUser }) => get(currentUser, 'isAuthenticated') && !currentUserIs({ currentUser }, 'staff') && !get(currentUser, 'user.isConfirmed') export const pendingReviewerInvitation = (state, fragmentId) => chain(state) .get(`fragments.${fragmentId}.invitations`, []) .find( inv => inv.userId === get(state, 'currentUser.user.id', '') && !inv.hasAnswer && inv.role === 'reviewer', ) .value() export const currentUserIsReviewerInPending = (state, fragmentId) => { const currentUser = selectCurrentUser(state) const invitations = get(state, `fragments.${fragmentId}.invitations`, []) return !!invitations.find( i => i.userId === currentUser.id && i.role === 'reviewer' && !i.isAccepted, ) } export const currentUserIsReviewer = (state, fragmentId) => { const currentUser = selectCurrentUser(state) const invitations = get(state, `fragments.${fragmentId}.invitations`, []) return !!invitations.find( i => i.userId === currentUser.id && i.role === 'reviewer' && i.hasAnswer && i.isAccepted, ) } export const getAdminUsers = state => chain(state) .get('users.users') .map(u => { let sortValue = -1 if (u.isActive) sortValue = 1 if (!u.isConfirmed) sortValue = 0 return { user: u, sortValue, } }) .sortBy('sortValue') .map(s => s.user) .reverse() .value() export const pendingHEInvitation = (state, collectionId) => chain(state) .get('collections', []) .find(c => c.id === collectionId) .get('invitations', []) .find( i => i.userId === get(state, 'currentUser.user.id', '') && i.role === 'handlingEditor' && !i.hasAnswer, ) .value() const parseInvitedHE = (handlingEditor, state, collectionId) => handlingEditor && { ...handlingEditor, name: pendingHEInvitation(state, collectionId) ? 'Invited' : handlingEditor.name, } const hideCustomIdStatuses = ['draft', 'technicalChecks'] export const parseCollectionDetails = (state, collection = {}) => ({ ...collection, customId: !hideCustomIdStatuses.includes(get(collection, 'status', 'draft')) && collection.customId, handlingEditor: parseInvitedHE( collection.handlingEditor, state, collection.id, ), }) export const newestFirstParseDashboard = (state = {}) => chain(state.collections) .orderBy(['created'], ['desc']) .map(item => parseCollectionDetails(state, item)) .value() export const getInvitationsWithReviewersForFragment = (state, fragmentId) => chain(state) .get(`fragments.${fragmentId}.invitations`, []) .filter(invitation => invitation.role === 'reviewer') .map(invitation => ({ ...invitation, person: get(state, 'users.users', []).find( reviewer => reviewer.id === invitation.userId, ), })) .value() export const canMakeHERecommendation = (state, { collection, statuses }) => { const validHE = isHEToManuscript(state, get(collection, 'id', '')) if (!validHE) return false const statusImportance = get( statuses, `${get(collection, 'status', 'draft')}.importance`, 1, ) if (!(statusImportance > 1 && statusImportance < 10)) return false return true } export const getFragmentAuthorResponse = (state, fragmentId) => get(state, `fragments.${fragmentId}.responseToReviewers`, {}) // #region Editorial and reviewer recommendations export const getFragmentRecommendations = (state, fragmentId) => get(state, `fragments.${fragmentId}.recommendations`, []) export const getFragmentReviewerRecommendations = (state, fragmentId) => getFragmentRecommendations(state, fragmentId).filter( r => r.recommendationType === 'review', ) const getFragmentEditorialComments = (state, fragmentId) => getFragmentRecommendations(state, fragmentId).filter( r => r.recommendationType === 'editorRecommendation', ) const getOwnRecommendations = (state, fragmentId) => chain(state) .get(`fragments.${fragmentId}.recommendations`, []) .filter(r => r.userId === get(state, 'currentUser.user.id', '')) .value() export const getOwnPendingRecommendation = (state, fragmentId) => chain(getOwnRecommendations(state, fragmentId)) .find( r => r.userId === get(state, 'currentUser.user.id', '') && !has(r, 'submittedOn'), ) .value() export const getOwnSubmittedRecommendation = (state, fragmentId) => chain(getOwnRecommendations(state, fragmentId)) .find( r => r.userId === get(state, 'currentUser.user.id', '') && has(r, 'submittedOn'), ) .value() export const canSubmitRevision = (state, fragment = {}) => { const userId = get(state, 'currentUser.user.id') const fragmentAuthors = chain(fragment) .get('authors', []) .map(a => a.id) .value() return get(fragment, 'revision', null) && fragmentAuthors.includes(userId) } // #endregion export const getVersionOptions = (state, collection = {}) => { const fragments = get(state, 'fragments', {}) return chain(collection) .get('fragments', []) .reduce( (acc, el) => [ ...acc, { value: el, label: `Version ${get(fragments, `${el}.version`)}`, }, ], [], ) .reverse() .value() } export const canReview = (state, collection = {}, fragment = {}) => { const fragmentId = get(fragment, 'id', false) if (!fragmentId) return false const ownRecommendation = getOwnRecommendations(state, fragmentId) const isReviewer = currentUserIsReviewer(state, fragmentId) if (!isReviewer) return false return ( get(collection, 'status', 'draft') === 'underReview' && !get(ownRecommendation[0], 'submittedOn', false) ) } export const isFetchingFromAutosave = state => get(state.autosave, 'isFetching', false)