-
Notifications
You must be signed in to change notification settings - Fork 392
/
PublishAction.tsx
157 lines (142 loc) · 5.02 KB
/
PublishAction.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import {DocumentActionComponent} from '@sanity/base'
import {useSyncState, useDocumentOperation, useValidationStatus} from '@sanity/react-hooks'
import {CheckmarkIcon, PublishIcon} from '@sanity/icons'
import React, {useCallback, useEffect, useState} from 'react'
import {
useCurrentUser,
unstable_useDocumentPairPermissions as useDocumentPairPermissions,
} from '@sanity/base/hooks'
import {InsufficientPermissionsMessage} from '@sanity/base/components'
import {TimeAgo} from '../components/TimeAgo'
import {useDocumentPane} from '../panes/document/useDocumentPane'
const DISABLED_REASON_TITLE = {
LIVE_EDIT_ENABLED: 'Cannot publish since liveEdit is enabled for this document type',
ALREADY_PUBLISHED: 'Already published',
NO_CHANGES: 'No unpublished changes',
}
function getDisabledReason(
reason: keyof typeof DISABLED_REASON_TITLE,
publishedAt: string | undefined
) {
if (reason === 'ALREADY_PUBLISHED' && publishedAt) {
return (
<>
<span>
Published <TimeAgo time={publishedAt} />
</span>
</>
)
}
return DISABLED_REASON_TITLE[reason]
}
// eslint-disable-next-line complexity
export const PublishAction: DocumentActionComponent = (props) => {
const {id, type, liveEdit, draft, published} = props
const [publishState, setPublishState] = useState<'publishing' | 'published' | null>(null)
const {publish}: any = useDocumentOperation(id, type)
const validationStatus = useValidationStatus(id, type)
const syncState = useSyncState(id, type)
const {changesOpen, handleHistoryOpen} = useDocumentPane()
const hasValidationErrors = validationStatus.markers.some((marker) => marker.level === 'error')
// we use this to "schedule" publish after pending tasks (e.g. validation and sync) has completed
const [publishScheduled, setPublishScheduled] = useState<boolean>(false)
const isNeitherSyncingNorValidating = !syncState.isSyncing && !validationStatus.isValidating
const [permissions, isPermissionsLoading] = useDocumentPairPermissions({
id,
type,
permission: 'publish',
})
const {value: currentUser} = useCurrentUser()
// eslint-disable-next-line no-nested-ternary
const title = publish.disabled
? getDisabledReason(publish.disabled, (published || {})._updatedAt) || ''
: hasValidationErrors
? 'There are validation errors that need to be fixed before this document can be published'
: ''
const hasDraft = Boolean(draft)
const doPublish = useCallback(() => {
publish.execute()
setPublishState('publishing')
}, [publish])
useEffect(() => {
if (publishScheduled && isNeitherSyncingNorValidating) {
if (!hasValidationErrors) {
doPublish()
}
setPublishScheduled(false)
}
}, [isNeitherSyncingNorValidating, doPublish, hasValidationErrors, publishScheduled])
useEffect(() => {
const didPublish = publishState === 'publishing' && !hasDraft
if (didPublish) {
if (changesOpen) {
// Re-open the panel
handleHistoryOpen()
}
}
const nextState = didPublish ? 'published' : null
const delay = didPublish ? 200 : 4000
const timer = setTimeout(() => {
setPublishState(nextState)
}, delay)
return () => clearTimeout(timer)
}, [changesOpen, publishState, hasDraft, handleHistoryOpen])
const handle = useCallback(() => {
if (syncState.isSyncing || validationStatus.isValidating) {
setPublishScheduled(true)
} else {
doPublish()
}
}, [syncState.isSyncing, validationStatus.isValidating, doPublish])
if (liveEdit) {
return {
color: 'success',
label: 'Publish',
title:
'Live Edit is enabled for this content type and publishing happens automatically as you make changes',
disabled: true,
}
}
if (!isPermissionsLoading && !permissions?.granted) {
return {
color: 'success',
label: 'Publish',
title: (
<InsufficientPermissionsMessage
operationLabel="publish this document"
currentUser={currentUser}
/>
),
disabled: true,
}
}
const disabled = Boolean(
publishScheduled ||
publishState === 'publishing' ||
publishState === 'published' ||
hasValidationErrors ||
publish.disabled
)
return {
disabled: disabled || isPermissionsLoading,
color: 'success',
label:
// eslint-disable-next-line no-nested-ternary
publishState === 'published'
? 'Published'
: publishScheduled || publishState === 'publishing'
? 'Publishing…'
: 'Publish',
// @todo: Implement loading state, to show a `<Button loading />` state
// loading: publishScheduled || publishState === 'publishing',
icon: publishState === 'published' ? CheckmarkIcon : PublishIcon,
// eslint-disable-next-line no-nested-ternary
title: publishScheduled
? 'Waiting for tasks to finish before publishing'
: publishState === 'published' || publishState === 'publishing'
? null
: title,
shortcut: disabled || publishScheduled ? null : 'Ctrl+Alt+P',
onHandle: handle,
}
}