sanity-plugin-workflow 1.0.0-beta.7 → 1.0.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-workflow",
3
- "version": "1.0.0-beta.7",
3
+ "version": "1.0.0-beta.8",
4
4
  "description": "A demonstration of a custom content publishing workflow using Sanity.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -87,7 +87,7 @@
87
87
  "rimraf": "^4.1.2",
88
88
  "sanity": "^3.3.1",
89
89
  "semantic-release": "^20.1.0",
90
- "typescript": "^4.9.5"
90
+ "typescript": "^5.0.0"
91
91
  },
92
92
  "peerDependencies": {
93
93
  "@sanity/ui": "^1.2.2",
@@ -94,7 +94,8 @@ export default function DocumentList(props: DocumentListProps) {
94
94
  return (
95
95
  <Draggable
96
96
  // The metadata's documentId is always the published one to avoid rerendering
97
- key={documentId}
97
+ // key={documentId}
98
+ key={virtualItem.key}
98
99
  draggableId={documentId}
99
100
  index={virtualItem.index}
100
101
  isDragDisabled={isDragDisabled}
@@ -5,6 +5,7 @@ import {useClient} from 'sanity'
5
5
  import {UserExtended} from 'sanity-plugin-utils'
6
6
 
7
7
  import {API_VERSION} from '../constants'
8
+ import {generateMultipleOrderRanks} from '../helpers/generateMultipleOrderRanks'
8
9
  import {SanityDocumentWithMetadata, State} from '../types'
9
10
  import FloatingCard from './FloatingCard'
10
11
 
@@ -32,16 +33,17 @@ export default function Verify(props: VerifyProps) {
32
33
  }, [] as string[])
33
34
  : []
34
35
 
35
- const documentsWithInvalidUserIds = data?.length
36
- ? data.reduce((acc, cur) => {
37
- const {documentId, assignees} = cur._metadata ?? {}
38
- const allAssigneesExist = assignees?.length
39
- ? assignees?.every((a) => userList.find((u) => u.id === a))
40
- : true
36
+ const documentsWithInvalidUserIds =
37
+ data?.length && userList?.length
38
+ ? data.reduce((acc, cur) => {
39
+ const {documentId, assignees} = cur._metadata ?? {}
40
+ const allAssigneesExist = assignees?.length
41
+ ? assignees?.every((a) => userList.find((u) => u.id === a))
42
+ : true
41
43
 
42
- return !allAssigneesExist && documentId ? [...acc, documentId] : acc
43
- }, [] as string[])
44
- : []
44
+ return !allAssigneesExist && documentId ? [...acc, documentId] : acc
45
+ }, [] as string[])
46
+ : []
45
47
 
46
48
  const documentsWithoutOrderIds = data?.length
47
49
  ? data.reduce((acc, cur) => {
@@ -130,29 +132,51 @@ export default function Verify(props: VerifyProps) {
130
132
  status: 'info',
131
133
  })
132
134
 
133
- // Get first and second order values
135
+ // Get first and second order values, if they exist
134
136
  const [firstOrder, secondOrder] = [...data]
135
137
  .slice(0, 2)
136
138
  .map((d) => d._metadata?.orderRank)
137
- const minLexo =
138
- firstOrder && data.length !== ids.length
139
- ? LexoRank.parse(firstOrder)
140
- : LexoRank.min()
141
- const maxLexo =
142
- secondOrder && data.length !== ids.length
143
- ? LexoRank.parse(secondOrder)
144
- : LexoRank.max()
145
- let newLexo = minLexo.between(maxLexo)
146
- const lastLexo = maxLexo
139
+ const minLexo = firstOrder ? LexoRank.parse(firstOrder) : undefined
140
+ const maxLexo = secondOrder ? LexoRank.parse(secondOrder) : undefined
141
+ const ranks = generateMultipleOrderRanks(ids.length, minLexo, maxLexo)
147
142
 
148
143
  const tx = client.transaction()
149
144
 
150
145
  // Create a new in-between value for each document
151
146
  for (let index = 0; index < ids.length; index += 1) {
152
- newLexo = newLexo.between(lastLexo)
147
+ tx.patch(`workflow-metadata.${ids[index]}`, {
148
+ set: {orderRank: ranks[index].toString()},
149
+ })
150
+ }
151
+
152
+ await tx.commit()
153
153
 
154
+ toast.push({
155
+ title: `Added order to ${
156
+ ids.length === 1 ? `1 Document` : `${ids.length} Documents`
157
+ }`,
158
+ status: 'success',
159
+ })
160
+ },
161
+ [data, client, toast]
162
+ )
163
+
164
+ // Reset order value on all metadata documents
165
+ const resetOrderOfAllDocuments = React.useCallback(
166
+ async (ids: string[]) => {
167
+ toast.push({
168
+ title: 'Adding ordering...',
169
+ status: 'info',
170
+ })
171
+
172
+ const ranks = generateMultipleOrderRanks(ids.length)
173
+
174
+ const tx = client.transaction()
175
+
176
+ // Create a new in-between value for each document
177
+ for (let index = 0; index < ids.length; index += 1) {
154
178
  tx.patch(`workflow-metadata.${ids[index]}`, {
155
- set: {orderRank: newLexo.toString()},
179
+ set: {orderRank: ranks[index].toString()},
156
180
  })
157
181
  }
158
182
 
@@ -199,6 +223,7 @@ export default function Verify(props: VerifyProps) {
199
223
  {documentsWithoutValidMetadataIds.length > 0 ? (
200
224
  <Button
201
225
  tone="caution"
226
+ mode="ghost"
202
227
  onClick={() => correctDocuments(documentsWithoutValidMetadataIds)}
203
228
  text={
204
229
  documentsWithoutValidMetadataIds.length === 1
@@ -210,6 +235,7 @@ export default function Verify(props: VerifyProps) {
210
235
  {documentsWithInvalidUserIds.length > 0 ? (
211
236
  <Button
212
237
  tone="caution"
238
+ mode="ghost"
213
239
  onClick={() => removeUsersFromDocuments(documentsWithInvalidUserIds)}
214
240
  text={
215
241
  documentsWithInvalidUserIds.length === 1
@@ -221,6 +247,7 @@ export default function Verify(props: VerifyProps) {
221
247
  {documentsWithoutOrderIds.length > 0 ? (
222
248
  <Button
223
249
  tone="caution"
250
+ mode="ghost"
224
251
  onClick={() => addOrderToDocuments(documentsWithoutOrderIds)}
225
252
  text={
226
253
  documentsWithoutOrderIds.length === 1
@@ -230,36 +257,41 @@ export default function Verify(props: VerifyProps) {
230
257
  />
231
258
  ) : null}
232
259
  {documentsWithDuplicatedOrderIds.length > 0 ? (
233
- <Button
234
- tone="caution"
235
- onClick={() => addOrderToDocuments(documentsWithDuplicatedOrderIds)}
236
- text={
237
- documentsWithDuplicatedOrderIds.length === 1
238
- ? `Set Unique Order for 1 Document`
239
- : `Set Unique Order for ${documentsWithDuplicatedOrderIds.length} Documents`
240
- }
241
- />
260
+ <>
261
+ <Button
262
+ tone="caution"
263
+ mode="ghost"
264
+ onClick={() => addOrderToDocuments(documentsWithDuplicatedOrderIds)}
265
+ text={
266
+ documentsWithDuplicatedOrderIds.length === 1
267
+ ? `Set Unique Order for 1 Document`
268
+ : `Set Unique Order for ${documentsWithDuplicatedOrderIds.length} Documents`
269
+ }
270
+ />
271
+ <Button
272
+ tone="caution"
273
+ mode="ghost"
274
+ onClick={() =>
275
+ resetOrderOfAllDocuments(
276
+ data.map((doc) => String(doc._metadata?.documentId))
277
+ )
278
+ }
279
+ text={
280
+ data.length === 1
281
+ ? `Reset Order for 1 Document`
282
+ : `Reset Order for all ${data.length} Documents`
283
+ }
284
+ />
285
+ </>
242
286
  ) : null}
243
287
  {orphanedMetadataDocumentIds.length > 0 ? (
244
288
  <Button
245
289
  text="Cleanup orphaned metadata"
246
290
  onClick={handleOrphans}
247
291
  tone="caution"
292
+ mode="ghost"
248
293
  />
249
294
  ) : null}
250
- {/* <Button
251
- tone="caution"
252
- onClick={() =>
253
- addOrderToDocuments(
254
- data.map((doc) => String(doc._metadata?.documentId))
255
- )
256
- }
257
- text={
258
- data.length === 1
259
- ? `Reset Order for 1 Document`
260
- : `Reset Order for all ${data.length} Documents`
261
- }
262
- /> */}
263
295
  </FloatingCard>
264
296
  )
265
297
  }
@@ -0,0 +1,80 @@
1
+ import {LexoRank} from 'lexorank'
2
+
3
+ function generateMiddleValue(ranks: (LexoRank | undefined)[]) {
4
+ // Has no undefined values
5
+ if (!ranks.some((rank) => !rank)) {
6
+ return ranks
7
+ }
8
+
9
+ // Find the first undefined value
10
+ const firstUndefined = ranks.findIndex((rank) => !rank)
11
+
12
+ // Find the first defined value after the undefined value
13
+ const firstDefinedAfter = ranks.findIndex(
14
+ (rank, index) => rank && index > firstUndefined
15
+ )
16
+ // Find the first defined value before the undefined value
17
+ const firstDefinedBefore = ranks.findLastIndex(
18
+ (rank, index) => rank && index < firstUndefined
19
+ )
20
+
21
+ if (firstDefinedAfter === -1 || firstDefinedBefore === -1) {
22
+ throw new Error(
23
+ `Unable to generate middle value between indexes ${firstDefinedBefore} and ${firstDefinedAfter}`
24
+ )
25
+ }
26
+
27
+ const beforeRank = ranks[firstDefinedBefore]
28
+ const afterRank = ranks[firstDefinedAfter]
29
+
30
+ if (
31
+ !beforeRank ||
32
+ typeof beforeRank === 'undefined' ||
33
+ !afterRank ||
34
+ typeof afterRank === 'undefined'
35
+ ) {
36
+ throw new Error(
37
+ `Unable to generate middle value between indexes ${firstDefinedBefore} and ${firstDefinedAfter}`
38
+ )
39
+ }
40
+
41
+ // Generate a new value between the two
42
+ const between = beforeRank.between(afterRank)
43
+
44
+ // Calculate the middle index between the defined values
45
+ const middle = Math.floor((firstDefinedAfter + firstDefinedBefore) / 2)
46
+
47
+ if (ranks[middle]) {
48
+ throw new Error(`Should not have overwritten value at index ${middle}`)
49
+ }
50
+
51
+ // Insert the new value into the array
52
+ ranks[middle] = between
53
+
54
+ // Return as a new array
55
+ return ranks
56
+ }
57
+
58
+ // Generates an array of LexoRanks between two values
59
+ export function generateMultipleOrderRanks(
60
+ count: number,
61
+ start?: LexoRank,
62
+ end?: LexoRank
63
+ ): LexoRank[] {
64
+ // Begin array with correct size
65
+ let ranks = [...Array(count)]
66
+
67
+ // Use or create default values
68
+ const rankStart = start ?? LexoRank.min().genNext().genNext()
69
+ const rankEnd = end ?? LexoRank.max().genPrev().genPrev()
70
+
71
+ ranks[0] = rankStart
72
+ ranks[count - 1] = rankEnd
73
+
74
+ // Keep processing the array until every value between undefined values is defined
75
+ for (let i = 0; i < count; i++) {
76
+ ranks = generateMiddleValue(ranks)
77
+ }
78
+
79
+ return ranks.sort((a, b) => a.toString().localeCompare(b.toString()))
80
+ }
@@ -131,7 +131,10 @@ export function useWorkflowDocuments(schemaTypes: string[]): WorkflowDocuments {
131
131
  .commit()
132
132
  .then((res) => {
133
133
  toast.push({
134
- title: `Moved to "${newState?.title ?? newStateId}"`,
134
+ title:
135
+ newState.id === document._metadata.state
136
+ ? `Reordered in "${newState?.title ?? newStateId}"`
137
+ : `Moved to "${newState?.title ?? newStateId}"`,
135
138
  status: 'success',
136
139
  })
137
140
  return res