sanity-plugin-media 4.3.6 → 5.0.0

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.
Files changed (162) hide show
  1. package/package.json +6 -15
  2. package/dist/index.cjs +0 -4721
  3. package/dist/index.cjs.map +0 -1
  4. package/dist/index.d.cts +0 -239
  5. package/dist/index.d.cts.map +0 -1
  6. package/sanity.json +0 -8
  7. package/src/__tests__/fixtures/createEpicTestStore.ts +0 -28
  8. package/src/__tests__/fixtures/listenMock.ts +0 -9
  9. package/src/__tests__/fixtures/mockSanityClient.ts +0 -84
  10. package/src/__tests__/fixtures/renderWithProviders.tsx +0 -55
  11. package/src/__tests__/fixtures/rootState.ts +0 -27
  12. package/src/__tests__/fixtures/withinDialog.ts +0 -28
  13. package/src/components/AssetGridVirtualized/index.tsx +0 -94
  14. package/src/components/AssetMetadata/index.tsx +0 -122
  15. package/src/components/AssetTableVirtualized/index.tsx +0 -73
  16. package/src/components/AutoTagInputWrapper/index.tsx +0 -85
  17. package/src/components/Browser/Browser.test.tsx +0 -45
  18. package/src/components/Browser/index.tsx +0 -90
  19. package/src/components/Browser/useBrowserInit.ts +0 -126
  20. package/src/components/ButtonAssetCopy/index.tsx +0 -65
  21. package/src/components/ButtonViewGroup/index.tsx +0 -39
  22. package/src/components/CardAsset/CardAsset.test.tsx +0 -323
  23. package/src/components/CardAsset/index.tsx +0 -290
  24. package/src/components/CardUpload/index.tsx +0 -161
  25. package/src/components/Controls/index.tsx +0 -136
  26. package/src/components/DebugControls/index.tsx +0 -80
  27. package/src/components/Dialog/index.tsx +0 -11
  28. package/src/components/DialogAssetEdit/Details.tsx +0 -181
  29. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +0 -216
  30. package/src/components/DialogAssetEdit/index.tsx +0 -493
  31. package/src/components/DialogConfirm/index.tsx +0 -90
  32. package/src/components/DialogSearchFacets/index.tsx +0 -42
  33. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +0 -121
  34. package/src/components/DialogTagCreate/index.tsx +0 -111
  35. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +0 -165
  36. package/src/components/DialogTagEdit/index.tsx +0 -201
  37. package/src/components/DialogTags/index.tsx +0 -45
  38. package/src/components/Dialogs/index.tsx +0 -76
  39. package/src/components/DocumentList/index.tsx +0 -62
  40. package/src/components/FileAssetPreview/index.tsx +0 -37
  41. package/src/components/FileIcon/index.tsx +0 -43
  42. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +0 -63
  43. package/src/components/FormBuilderTool/index.tsx +0 -69
  44. package/src/components/FormFieldInputLabel/index.tsx +0 -66
  45. package/src/components/FormFieldInputTags/index.tsx +0 -98
  46. package/src/components/FormFieldInputText/index.tsx +0 -41
  47. package/src/components/FormFieldInputTextarea/index.tsx +0 -43
  48. package/src/components/FormSubmitButton/index.tsx +0 -59
  49. package/src/components/Header/index.tsx +0 -80
  50. package/src/components/Image/index.tsx +0 -41
  51. package/src/components/Items/index.tsx +0 -68
  52. package/src/components/Notifications/index.tsx +0 -24
  53. package/src/components/OrderSelect/index.tsx +0 -66
  54. package/src/components/PickedBar/index.tsx +0 -77
  55. package/src/components/Progress/index.tsx +0 -38
  56. package/src/components/ReduxProvider/index.tsx +0 -96
  57. package/src/components/SearchFacet/index.tsx +0 -66
  58. package/src/components/SearchFacetNumber/index.tsx +0 -133
  59. package/src/components/SearchFacetSelect/index.tsx +0 -110
  60. package/src/components/SearchFacetString/index.tsx +0 -88
  61. package/src/components/SearchFacetTags/index.tsx +0 -121
  62. package/src/components/SearchFacets/index.tsx +0 -72
  63. package/src/components/SearchFacetsControl/index.tsx +0 -140
  64. package/src/components/TableHeader/index.tsx +0 -110
  65. package/src/components/TableHeaderItem/index.tsx +0 -61
  66. package/src/components/TableRowAsset/index.tsx +0 -419
  67. package/src/components/TableRowUpload/index.tsx +0 -164
  68. package/src/components/Tag/index.tsx +0 -200
  69. package/src/components/TagIcon/index.tsx +0 -22
  70. package/src/components/TagView/index.tsx +0 -39
  71. package/src/components/TagViewHeader/index.tsx +0 -70
  72. package/src/components/TagsPanel/index.tsx +0 -40
  73. package/src/components/TagsVirtualized/index.tsx +0 -160
  74. package/src/components/TextInputNumber/index.tsx +0 -32
  75. package/src/components/TextInputSearch/index.tsx +0 -60
  76. package/src/components/Tool/index.tsx +0 -13
  77. package/src/components/UploadDropzone/UploadDropzone.test.tsx +0 -40
  78. package/src/components/UploadDropzone/index.tsx +0 -173
  79. package/src/config/orders.ts +0 -28
  80. package/src/config/searchFacets.ts +0 -312
  81. package/src/constants.ts +0 -87
  82. package/src/contexts/AssetSourceDispatchContext.tsx +0 -38
  83. package/src/contexts/DropzoneDispatchContext.tsx +0 -32
  84. package/src/contexts/ToolOptionsContext.tsx +0 -66
  85. package/src/formSchema/index.test.ts +0 -56
  86. package/src/formSchema/index.ts +0 -39
  87. package/src/hooks/useBreakpointIndex.ts +0 -50
  88. package/src/hooks/useKeyPress.ts +0 -39
  89. package/src/hooks/usePortalPopoverProps.ts +0 -13
  90. package/src/hooks/useTypedSelector.ts +0 -7
  91. package/src/hooks/useVersionedClient.ts +0 -6
  92. package/src/index.ts +0 -5
  93. package/src/modules/assets/actions.ts +0 -42
  94. package/src/modules/assets/deleteAndUpdateEpics.test.ts +0 -87
  95. package/src/modules/assets/fetchEpic.test.ts +0 -73
  96. package/src/modules/assets/index.ts +0 -782
  97. package/src/modules/assets/reducer.test.ts +0 -91
  98. package/src/modules/assets/tagsAndListenerEpics.test.ts +0 -206
  99. package/src/modules/debug/index.ts +0 -28
  100. package/src/modules/dialog/actions.ts +0 -10
  101. package/src/modules/dialog/epics.test.ts +0 -168
  102. package/src/modules/dialog/index.ts +0 -238
  103. package/src/modules/dialog/reducer.test.ts +0 -185
  104. package/src/modules/index.ts +0 -117
  105. package/src/modules/notifications/epics.test.ts +0 -374
  106. package/src/modules/notifications/index.ts +0 -199
  107. package/src/modules/notifications/reducer.test.ts +0 -54
  108. package/src/modules/search/index.test.ts +0 -36
  109. package/src/modules/search/index.ts +0 -167
  110. package/src/modules/selected/index.ts +0 -22
  111. package/src/modules/selectors.test.ts +0 -21
  112. package/src/modules/selectors.ts +0 -17
  113. package/src/modules/tags/epics.test.ts +0 -96
  114. package/src/modules/tags/index.test.ts +0 -42
  115. package/src/modules/tags/index.ts +0 -540
  116. package/src/modules/types.ts +0 -3
  117. package/src/modules/uploads/actions.ts +0 -13
  118. package/src/modules/uploads/epics.test.ts +0 -109
  119. package/src/modules/uploads/index.test.ts +0 -59
  120. package/src/modules/uploads/index.ts +0 -272
  121. package/src/operators/checkTagName.test.ts +0 -29
  122. package/src/operators/checkTagName.ts +0 -33
  123. package/src/operators/debugThrottle.ts +0 -25
  124. package/src/plugin.tsx +0 -54
  125. package/src/schemas/tag.ts +0 -28
  126. package/src/styled/GlobalStyles/index.tsx +0 -40
  127. package/src/styled/react-select/creatable.tsx +0 -184
  128. package/src/styled/react-select/single.tsx +0 -184
  129. package/src/types/index.ts +0 -346
  130. package/src/types/sanity-ui.d.ts +0 -5
  131. package/src/utils/applyMediaTags.ts +0 -87
  132. package/src/utils/blocksToText.test.ts +0 -43
  133. package/src/utils/blocksToText.ts +0 -27
  134. package/src/utils/constructFilter.test.ts +0 -120
  135. package/src/utils/constructFilter.ts +0 -98
  136. package/src/utils/generatePreviewBlobUrl.test.ts +0 -68
  137. package/src/utils/generatePreviewBlobUrl.ts +0 -53
  138. package/src/utils/getAssetResolution.test.ts +0 -13
  139. package/src/utils/getAssetResolution.ts +0 -7
  140. package/src/utils/getDocumentAssetIds.test.ts +0 -50
  141. package/src/utils/getDocumentAssetIds.ts +0 -35
  142. package/src/utils/getSchemeColor.test.ts +0 -12
  143. package/src/utils/getSchemeColor.ts +0 -43
  144. package/src/utils/getTagSelectOptions.test.ts +0 -44
  145. package/src/utils/getTagSelectOptions.ts +0 -16
  146. package/src/utils/getUniqueDocuments.test.ts +0 -26
  147. package/src/utils/getUniqueDocuments.ts +0 -15
  148. package/src/utils/imageDprUrl.test.ts +0 -46
  149. package/src/utils/imageDprUrl.ts +0 -27
  150. package/src/utils/isSupportedAssetType.test.ts +0 -16
  151. package/src/utils/isSupportedAssetType.ts +0 -15
  152. package/src/utils/mediaField.ts +0 -73
  153. package/src/utils/sanitizeFormData.test.ts +0 -59
  154. package/src/utils/sanitizeFormData.ts +0 -26
  155. package/src/utils/typeGuards.test.ts +0 -18
  156. package/src/utils/typeGuards.ts +0 -9
  157. package/src/utils/uploadSanityAsset.test.ts +0 -29
  158. package/src/utils/uploadSanityAsset.ts +0 -97
  159. package/src/utils/withMaxConcurrency.test.ts +0 -43
  160. package/src/utils/withMaxConcurrency.ts +0 -55
  161. package/src/utils/zodFormResolver.ts +0 -17
  162. package/v2-incompatible.js +0 -11
@@ -1,782 +0,0 @@
1
- import {createSelector, createSlice, type PayloadAction} from '@reduxjs/toolkit'
2
- import type {ClientError, Patch, Transaction} from '@sanity/client'
3
- import groq from 'groq'
4
- import {nanoid} from 'nanoid'
5
- import type {Selector} from 'react-redux'
6
- import {ofType} from 'redux-observable'
7
- import {EMPTY, from, of} from 'rxjs'
8
- import {
9
- bufferTime,
10
- catchError,
11
- debounceTime,
12
- filter,
13
- mergeMap,
14
- switchMap,
15
- withLatestFrom,
16
- } from 'rxjs/operators'
17
-
18
- import {getOrderTitle} from '../../config/orders'
19
- import {ORDER_OPTIONS} from '../../constants'
20
- import debugThrottle from '../../operators/debugThrottle'
21
- import type {
22
- Asset,
23
- AssetItem,
24
- AssetType,
25
- BrowserView,
26
- HttpError,
27
- MyEpic,
28
- Order,
29
- OrderDirection,
30
- Tag,
31
- } from '../../types'
32
- import constructFilter from '../../utils/constructFilter'
33
- import {searchActions} from '../search'
34
- import type {RootReducerState} from '../types'
35
- import {UPLOADS_ACTIONS} from '../uploads/actions'
36
- import {ASSETS_ACTIONS} from './actions'
37
- type ItemError = {
38
- description: string
39
- id: string
40
- referencingIDs: string[]
41
- type: string // 'documentHasExistingReferencesError'
42
- }
43
-
44
- export type AssetsReducerState = {
45
- allIds: string[]
46
- assetTypes: AssetType[]
47
- byIds: Record<string, AssetItem>
48
- fetchCount: number
49
- fetching: boolean
50
- fetchingError?: HttpError
51
- lastPicked?: string
52
- order: Order
53
- pageIndex: number
54
- pageSize: number
55
- view: BrowserView
56
- // totalCount: number
57
- }
58
-
59
- const defaultOrder = ORDER_OPTIONS[0] as {
60
- direction: OrderDirection
61
- field: string
62
- }
63
-
64
- /**
65
- * NOTE:
66
- * `fetchCount` returns the number of items retrieved in the most recent fetch.
67
- * This is a temporary workaround to be able to determine when there are no more items to retrieve.
68
- * Typically this would be done by deriving the total number of assets upfront, but currently such
69
- * queries in GROQ aren't fast enough to use on large datasets (1000s of entries).
70
- *
71
- * TODO:
72
- * When the query engine has been improved and above queries are faster, remove all instances of
73
- * of `fetchCount` and reinstate `totalCount` across the board.
74
- */
75
-
76
- export const initialState = {
77
- allIds: [],
78
- assetTypes: [],
79
- byIds: {},
80
- fetchCount: -1,
81
- fetching: false,
82
- fetchingError: undefined,
83
- lastPicked: undefined,
84
- order: {
85
- direction: defaultOrder.direction,
86
- field: defaultOrder.field,
87
- title: getOrderTitle(defaultOrder.field, defaultOrder.direction),
88
- },
89
- pageIndex: 0,
90
- pageSize: 100,
91
- // totalCount: -1,
92
- view: 'grid',
93
- } as AssetsReducerState
94
-
95
- const assetsSlice = createSlice({
96
- name: 'assets',
97
- initialState,
98
- extraReducers: (builder) => {
99
- builder //
100
- .addCase(UPLOADS_ACTIONS.uploadComplete, (state, action) => {
101
- const {asset} = action.payload
102
-
103
- state.byIds[asset._id] = {
104
- _type: 'asset',
105
- asset: asset as Asset,
106
- picked: false,
107
- updating: false,
108
- }
109
- })
110
- .addCase(ASSETS_ACTIONS.tagsAddComplete, (state, action) => {
111
- const {assets} = action.payload
112
- assets.forEach((asset) => {
113
- state.byIds[asset.asset._id]!.updating = false
114
- })
115
- })
116
- .addCase(ASSETS_ACTIONS.tagsAddError, (state, action) => {
117
- const {assets} = action.payload
118
- assets.forEach((asset) => {
119
- state.byIds[asset.asset._id]!.updating = false
120
- })
121
- })
122
- .addCase(ASSETS_ACTIONS.tagsAddRequest, (state, action) => {
123
- const {assets} = action.payload
124
- assets.forEach((asset) => {
125
- state.byIds[asset.asset._id]!.updating = true
126
- })
127
- })
128
- .addCase(ASSETS_ACTIONS.tagsRemoveComplete, (state, action) => {
129
- const {assets} = action.payload
130
- assets.forEach((asset) => {
131
- state.byIds[asset.asset._id]!.updating = false
132
- })
133
- })
134
- .addCase(ASSETS_ACTIONS.tagsRemoveError, (state, action) => {
135
- const {assets} = action.payload
136
- assets.forEach((asset) => {
137
- state.byIds[asset.asset._id]!.updating = false
138
- })
139
- })
140
- .addCase(ASSETS_ACTIONS.tagsRemoveRequest, (state, action) => {
141
- const {assets} = action.payload
142
- assets.forEach((asset) => {
143
- state.byIds[asset.asset._id]!.updating = true
144
- })
145
- })
146
- },
147
- reducers: {
148
- // Clear asset order
149
- clear(state) {
150
- state.allIds = []
151
- },
152
- // Remove assets and update page index
153
- deleteComplete(state, action: PayloadAction<{assetIds: string[]}>) {
154
- const {assetIds} = action.payload
155
-
156
- assetIds?.forEach((id) => {
157
- const deleteIndex = state.allIds.indexOf(id)
158
- if (deleteIndex >= 0) {
159
- state.allIds.splice(deleteIndex, 1)
160
- }
161
- delete state.byIds[id]
162
- })
163
-
164
- state.pageIndex = Math.floor(state.allIds.length / state.pageSize) - 1
165
- },
166
- deleteError(state, action: PayloadAction<{assetIds: string[]; error: ClientError}>) {
167
- const {assetIds, error} = action.payload
168
- const itemErrors: ItemError[] = error?.response?.body?.error?.items?.map(
169
- (item: any) => item.error,
170
- )
171
-
172
- assetIds?.forEach((id) => {
173
- state.byIds[id]!.updating = false
174
- })
175
- itemErrors?.forEach((item) => {
176
- state.byIds[item.id]!.error = item.description
177
- })
178
- },
179
- deleteRequest(state, action: PayloadAction<{assets: Asset[]; closeDialogId?: string}>) {
180
- const {assets} = action.payload
181
- assets.forEach((asset) => {
182
- state.byIds[asset?._id]!.updating = true
183
- })
184
-
185
- Object.keys(state.byIds).forEach((key) => {
186
- delete state.byIds[key]!.error
187
- })
188
- },
189
- fetchComplete(state, action: PayloadAction<{assets: Asset[]}>) {
190
- const assets = action.payload?.assets || []
191
-
192
- if (assets) {
193
- assets.forEach((asset) => {
194
- if (!state.allIds.includes(asset._id)) {
195
- state.allIds.push(asset._id)
196
- }
197
- state.byIds[asset._id] = {
198
- _type: 'asset',
199
- asset: asset,
200
- picked: false,
201
- updating: false,
202
- }
203
- })
204
- }
205
-
206
- state.fetching = false
207
- state.fetchCount = assets.length || 0
208
- delete state.fetchingError
209
- },
210
- fetchError(state, action: PayloadAction<HttpError>) {
211
- const error = action.payload
212
- state.fetching = false
213
- state.fetchingError = error
214
- },
215
- fetchRequest: {
216
- reducer: (state, _action: PayloadAction<{params: Record<string, any>; query: string}>) => {
217
- state.fetching = true
218
- delete state.fetchingError
219
- },
220
- prepare: ({
221
- params = {},
222
- queryFilter,
223
- selector = ``,
224
- sort = groq`order(_updatedAt desc)`,
225
- }: {
226
- params?: Record<string, any>
227
- queryFilter: string
228
- replace?: boolean
229
- selector?: string
230
- sort?: string
231
- }) => {
232
- const pipe = sort || selector ? '|' : ''
233
-
234
- // Construct query
235
- const query = groq`
236
- {
237
- "items": *[${queryFilter}] {
238
- _id,
239
- _type,
240
- _createdAt,
241
- _updatedAt,
242
- altText,
243
- creditLine,
244
- description,
245
- extension,
246
- metadata {
247
- dimensions,
248
- exif,
249
- isOpaque,
250
- },
251
- mimeType,
252
- opt {
253
- media
254
- },
255
- originalFilename,
256
- size,
257
- source {
258
- name,
259
- id,
260
- url,
261
- },
262
- title,
263
- url
264
- } ${pipe} ${sort} ${selector},
265
- }
266
- `
267
-
268
- return {payload: {params, query}}
269
- },
270
- },
271
- insertUploads(state, action: PayloadAction<{results: Record<string, string | null>}>) {
272
- const {results} = action.payload
273
-
274
- Object.entries(results).forEach(([hash, assetId]) => {
275
- if (assetId && !state.allIds.includes(hash)) {
276
- state.allIds.push(assetId)
277
- }
278
- })
279
- },
280
- listenerCreateQueue(_state, _action: PayloadAction<{asset: Asset}>) {
281
- //
282
- },
283
- listenerCreateQueueComplete(state, action: PayloadAction<{assets: Asset[]}>) {
284
- const {assets} = action.payload
285
- assets?.forEach((asset) => {
286
- if (state.byIds[asset?._id]?.asset) {
287
- state.byIds[asset._id]!.asset = asset
288
- }
289
- })
290
- },
291
- listenerDeleteQueue(_state, _action: PayloadAction<{assetId: string}>) {
292
- //
293
- },
294
- listenerDeleteQueueComplete(state, action: PayloadAction<{assetIds: string[]}>) {
295
- const {assetIds} = action.payload
296
- assetIds?.forEach((assetId) => {
297
- const deleteIndex = state.allIds.indexOf(assetId)
298
- if (deleteIndex >= 0) {
299
- state.allIds.splice(deleteIndex, 1)
300
- }
301
- delete state.byIds[assetId]
302
- })
303
- },
304
- listenerUpdateQueue(_state, _action: PayloadAction<{asset: Asset}>) {
305
- //
306
- },
307
- listenerUpdateQueueComplete(state, action: PayloadAction<{assets: Asset[]}>) {
308
- const {assets} = action.payload
309
- assets?.forEach((asset) => {
310
- if (state.byIds[asset?._id]?.asset) {
311
- state.byIds[asset._id]!.asset = asset
312
- }
313
- })
314
- },
315
- loadNextPage() {
316
- //
317
- },
318
- loadPageIndex(state, action: PayloadAction<{pageIndex: number}>) {
319
- //
320
- state.pageIndex = action.payload.pageIndex
321
- },
322
- orderSet(state, action: PayloadAction<{order: Order}>) {
323
- state.order = action.payload?.order
324
- state.pageIndex = 0
325
- },
326
- pick(state, action: PayloadAction<{assetId: string; picked: boolean}>) {
327
- const {assetId, picked} = action.payload
328
-
329
- state.byIds[assetId]!.picked = picked
330
- state.lastPicked = picked ? assetId : undefined
331
- },
332
- pickAll(state) {
333
- state.allIds.forEach((id) => {
334
- state.byIds[id]!.picked = true
335
- })
336
- },
337
- pickClear(state) {
338
- state.lastPicked = undefined
339
- Object.values(state.byIds).forEach((asset) => {
340
- state.byIds[asset.asset._id]!.picked = false
341
- })
342
- },
343
- pickRange(state, action: PayloadAction<{endId: string; startId: string}>) {
344
- const startIndex = state.allIds.findIndex((id) => id === action.payload.startId)
345
- const endIndex = state.allIds.findIndex((id) => id === action.payload.endId)
346
-
347
- // Sort numerically, ascending order
348
- const indices = [startIndex, endIndex].sort((a, b) => a - b)
349
-
350
- state.allIds.slice(indices[0], indices[1]! + 1).forEach((key) => {
351
- state.byIds[key]!.picked = true
352
- })
353
- state.lastPicked = state.allIds[endIndex]
354
- },
355
- sort(state) {
356
- state.allIds.sort((a, b) => {
357
- const tagA = state.byIds[a]!.asset[state.order.field]
358
- const tagB = state.byIds[b]!.asset[state.order.field]
359
-
360
- if (tagA < tagB) {
361
- return state.order.direction === 'asc' ? -1 : 1
362
- } else if (tagA > tagB) {
363
- return state.order.direction === 'asc' ? 1 : -1
364
- }
365
- return 0
366
- })
367
- },
368
- updateComplete(state, action: PayloadAction<{asset: Asset; closeDialogId?: string}>) {
369
- const {asset} = action.payload
370
- state.byIds[asset._id]!.updating = false
371
- state.byIds[asset._id]!.asset = asset
372
- },
373
- updateError(state, action: PayloadAction<{asset: Asset; error: HttpError}>) {
374
- const {asset, error} = action.payload
375
-
376
- const assetId = asset?._id
377
- state.byIds[assetId]!.error = error.message
378
- state.byIds[assetId]!.updating = false
379
- },
380
- updateRequest(
381
- state,
382
- action: PayloadAction<{asset: Asset; closeDialogId?: string; formData: Record<string, any>}>,
383
- ) {
384
- const assetId = action.payload?.asset?._id
385
- state.byIds[assetId]!.updating = true
386
- },
387
- viewSet(state, action: PayloadAction<{view: BrowserView}>) {
388
- state.view = action.payload?.view
389
- },
390
- },
391
- })
392
-
393
- // Epics
394
-
395
- export const assetsDeleteEpic: MyEpic = (action$, _state$, {client}) =>
396
- action$.pipe(
397
- filter(assetsActions.deleteRequest.match),
398
- mergeMap((action) => {
399
- const {assets} = action.payload
400
- const assetIds = assets.map((asset) => asset._id)
401
- return of(assets).pipe(
402
- mergeMap(() =>
403
- client.observable.delete({
404
- query: groq`*[_id in ${JSON.stringify(assetIds)}]`,
405
- }),
406
- ),
407
- mergeMap(() => of(assetsActions.deleteComplete({assetIds}))),
408
- catchError((error: ClientError) => {
409
- return of(assetsActions.deleteError({assetIds, error}))
410
- }),
411
- )
412
- }),
413
- )
414
-
415
- export const assetsFetchEpic: MyEpic = (action$, state$, {client}) =>
416
- action$.pipe(
417
- filter(assetsActions.fetchRequest.match),
418
- withLatestFrom(state$),
419
- switchMap(([action, state]) => {
420
- const params = action.payload?.params
421
- const query = action.payload?.query
422
-
423
- return of(action).pipe(
424
- debugThrottle(state.debug.badConnection),
425
- mergeMap(() =>
426
- client.observable.fetch<{
427
- items: Asset[]
428
- }>(query, params),
429
- ),
430
- mergeMap((result) => {
431
- const {
432
- items,
433
- // totalCount
434
- } = result
435
- return of(assetsActions.fetchComplete({assets: items}))
436
- }),
437
- catchError((error: ClientError) =>
438
- of(
439
- assetsActions.fetchError({
440
- message: error?.message || 'Internal error',
441
- statusCode: error?.statusCode || 500,
442
- }),
443
- ),
444
- ),
445
- )
446
- }),
447
- )
448
-
449
- export const assetsFetchPageIndexEpic: MyEpic = (action$, state$) =>
450
- action$.pipe(
451
- filter(assetsActions.loadPageIndex.match),
452
- withLatestFrom(state$),
453
- switchMap(([action, state]) => {
454
- const pageSize = state.assets.pageSize
455
- const start = action.payload.pageIndex * pageSize
456
- const end = start + pageSize
457
-
458
- // Document ID can be null when operating on pristine / unsaved drafts
459
- const documentId = state?.selected.document?._id
460
- const documentAssetIds = state?.selected?.documentAssetIds
461
-
462
- const constructedFilter = constructFilter({
463
- assetTypes: state.assets.assetTypes,
464
- searchFacets: state.search.facets,
465
- searchQuery: state.search.query,
466
- })
467
-
468
- const params = {
469
- ...(documentId ? {documentId} : {}),
470
- documentAssetIds,
471
- }
472
-
473
- return of(
474
- assetsActions.fetchRequest({
475
- params,
476
- queryFilter: constructedFilter,
477
- selector: groq`[${start}...${end}]`,
478
- sort: groq`order(${state.assets?.order?.field} ${state.assets?.order?.direction})`,
479
- }),
480
- )
481
- }),
482
- )
483
-
484
- export const assetsFetchNextPageEpic: MyEpic = (action$, state$) =>
485
- action$.pipe(
486
- filter(assetsActions.loadNextPage.match),
487
- withLatestFrom(state$),
488
- switchMap(([_action, state]) =>
489
- of(assetsActions.loadPageIndex({pageIndex: state.assets.pageIndex + 1})),
490
- ),
491
- )
492
-
493
- export const assetsFetchAfterDeleteAllEpic: MyEpic = (action$, state$) =>
494
- action$.pipe(
495
- filter(assetsActions.deleteComplete.match),
496
- withLatestFrom(state$),
497
- switchMap(([_action, state]) => {
498
- if (state.assets.allIds.length === 0) {
499
- const nextPageIndex = Math.floor(state.assets.allIds.length / state.assets.pageSize)
500
- return of(assetsActions.loadPageIndex({pageIndex: nextPageIndex}))
501
- }
502
-
503
- return EMPTY
504
- }),
505
- )
506
-
507
- const filterAssetWithoutTag = (tag: Tag) => (asset: AssetItem) => {
508
- const tagIndex = asset?.asset?.opt?.media?.tags?.findIndex((t) => t._ref === tag?._id) ?? -1
509
- return tagIndex < 0
510
- }
511
-
512
- const patchOperationTagAppend =
513
- ({tag}: {tag: Tag}) =>
514
- (patch: Patch) =>
515
- patch
516
- .setIfMissing({opt: {}})
517
- .setIfMissing({'opt.media': {}})
518
- .setIfMissing({'opt.media.tags': []})
519
- .append('opt.media.tags', [{_key: nanoid(), _ref: tag?._id, _type: 'reference', _weak: true}])
520
-
521
- const patchOperationTagUnset =
522
- ({asset, tag}: {asset: AssetItem; tag: Tag}) =>
523
- (patch: Patch) =>
524
- patch.ifRevisionId(asset?.asset?._rev).unset([`opt.media.tags[_ref == "${tag._id}"]`])
525
-
526
- export const assetsOrderSetEpic: MyEpic = (action$) =>
527
- action$.pipe(
528
- filter(assetsActions.orderSet.match),
529
- mergeMap(() => {
530
- return of(
531
- assetsActions.clear(), //
532
- assetsActions.loadPageIndex({pageIndex: 0}),
533
- )
534
- }),
535
- )
536
-
537
- export const assetsSearchEpic: MyEpic = (action$) =>
538
- action$.pipe(
539
- ofType(
540
- searchActions.facetsAdd.type,
541
- searchActions.facetsClear.type,
542
- searchActions.facetsRemoveById.type,
543
- searchActions.facetsRemoveByName.type,
544
- searchActions.facetsRemoveByTag.type,
545
- searchActions.facetsUpdate.type,
546
- searchActions.facetsUpdateById.type,
547
- searchActions.querySet.type,
548
- ),
549
- debounceTime(400),
550
- mergeMap(() => {
551
- return of(
552
- assetsActions.clear(), //
553
- assetsActions.loadPageIndex({pageIndex: 0}),
554
- )
555
- }),
556
- )
557
-
558
- export const assetsListenerCreateQueueEpic: MyEpic = (action$) =>
559
- action$.pipe(
560
- filter(assetsActions.listenerCreateQueue.match),
561
- bufferTime(2000),
562
- filter((actions) => actions.length > 0),
563
- mergeMap((actions) => {
564
- const assets = actions?.map((action) => action.payload.asset)
565
- return of(assetsActions.listenerCreateQueueComplete({assets}))
566
- }),
567
- )
568
-
569
- export const assetsListenerDeleteQueueEpic: MyEpic = (action$) =>
570
- action$.pipe(
571
- filter(assetsActions.listenerDeleteQueue.match),
572
- bufferTime(2000),
573
- filter((actions) => actions.length > 0),
574
- mergeMap((actions) => {
575
- const assetIds = actions?.map((action) => action.payload.assetId)
576
- return of(assetsActions.listenerDeleteQueueComplete({assetIds}))
577
- }),
578
- )
579
-
580
- export const assetsListenerUpdateQueueEpic: MyEpic = (action$) =>
581
- action$.pipe(
582
- filter(assetsActions.listenerUpdateQueue.match),
583
- bufferTime(2000),
584
- filter((actions) => actions.length > 0),
585
- mergeMap((actions) => {
586
- const assets = actions?.map((action) => action.payload.asset)
587
- return of(assetsActions.listenerUpdateQueueComplete({assets}))
588
- }),
589
- )
590
-
591
- // Re-sort on all updates (immediate and batched listener events)
592
- export const assetsSortEpic: MyEpic = (action$) =>
593
- action$.pipe(
594
- ofType(
595
- assetsActions.insertUploads.type,
596
- assetsActions.listenerUpdateQueueComplete.type,
597
- assetsActions.updateComplete.type,
598
- ),
599
- mergeMap(() => of(assetsActions.sort())),
600
- )
601
-
602
- export const assetsTagsAddEpic: MyEpic = (action$, state$, {client}) => {
603
- return action$.pipe(
604
- filter(ASSETS_ACTIONS.tagsAddRequest.match),
605
- withLatestFrom(state$),
606
- mergeMap(([action, state]) => {
607
- const {assets, tag} = action.payload
608
-
609
- return of(action).pipe(
610
- // Optionally throttle
611
- debugThrottle(state.debug.badConnection),
612
- // Add tag references to all picked assets
613
- mergeMap(() => {
614
- const pickedAssets = selectAssetsPicked(state)
615
-
616
- // Filter out picked assets which already include tag
617
- const pickedAssetsFiltered = pickedAssets?.filter(filterAssetWithoutTag(tag))
618
-
619
- const transaction: Transaction = pickedAssetsFiltered.reduce(
620
- (tx, pickedAsset) => tx.patch(pickedAsset?.asset?._id, patchOperationTagAppend({tag})),
621
- client.transaction(),
622
- )
623
-
624
- return from(transaction.commit())
625
- }),
626
- // Dispatch complete action
627
- mergeMap(() => of(ASSETS_ACTIONS.tagsAddComplete({assets, tag}))),
628
- catchError((error: ClientError) =>
629
- of(
630
- ASSETS_ACTIONS.tagsAddError({
631
- assets,
632
- error: {
633
- message: error?.message || 'Internal error',
634
- statusCode: error?.statusCode || 500,
635
- },
636
- tag,
637
- }),
638
- ),
639
- ),
640
- )
641
- }),
642
- )
643
- }
644
-
645
- export const assetsTagsRemoveEpic: MyEpic = (action$, state$, {client}) => {
646
- return action$.pipe(
647
- filter(ASSETS_ACTIONS.tagsRemoveRequest.match),
648
- withLatestFrom(state$),
649
- mergeMap(([action, state]) => {
650
- const {assets, tag} = action.payload
651
-
652
- return of(action).pipe(
653
- // Optionally throttle
654
- debugThrottle(state.debug.badConnection),
655
- // Remove tag references from all picked assets
656
- mergeMap(() => {
657
- const pickedAssets = selectAssetsPicked(state)
658
-
659
- const transaction: Transaction = pickedAssets.reduce(
660
- (tx, pickedAsset) =>
661
- tx.patch(pickedAsset?.asset?._id, patchOperationTagUnset({asset: pickedAsset, tag})),
662
- client.transaction(),
663
- )
664
-
665
- return from(transaction.commit())
666
- }),
667
- // Dispatch complete action
668
- mergeMap(() => of(ASSETS_ACTIONS.tagsRemoveComplete({assets, tag}))),
669
- catchError((error: ClientError) =>
670
- of(
671
- ASSETS_ACTIONS.tagsRemoveError({
672
- assets,
673
- error: {
674
- message: error?.message || 'Internal error',
675
- statusCode: error?.statusCode || 500,
676
- },
677
- tag,
678
- }),
679
- ),
680
- ),
681
- )
682
- }),
683
- )
684
- }
685
-
686
- export const assetsUnpickEpic: MyEpic = (action$) =>
687
- action$.pipe(
688
- ofType(
689
- assetsActions.orderSet.type,
690
- assetsActions.viewSet.type,
691
- searchActions.facetsAdd.type,
692
- searchActions.facetsClear.type,
693
- searchActions.facetsRemoveById.type,
694
- searchActions.facetsRemoveByName.type,
695
- searchActions.facetsRemoveByTag.type,
696
- searchActions.facetsUpdate.type,
697
- searchActions.facetsUpdateById.type,
698
- searchActions.querySet.type,
699
- ),
700
- mergeMap(() => {
701
- return of(assetsActions.pickClear())
702
- }),
703
- )
704
-
705
- export const assetsUpdateEpic: MyEpic = (action$, state$, {client}) =>
706
- action$.pipe(
707
- filter(assetsActions.updateRequest.match),
708
- withLatestFrom(state$),
709
- mergeMap(([action, state]) => {
710
- const {asset, closeDialogId, formData} = action.payload
711
-
712
- return of(action).pipe(
713
- debugThrottle(state.debug.badConnection),
714
- mergeMap(() =>
715
- from(
716
- client
717
- .patch(asset._id)
718
- .setIfMissing({opt: {}})
719
- .setIfMissing({'opt.media': {}})
720
- .set(formData)
721
- .commit(),
722
- ),
723
- ),
724
- mergeMap((updatedAsset: any) =>
725
- of(
726
- assetsActions.updateComplete({
727
- asset: updatedAsset,
728
- closeDialogId,
729
- }),
730
- ),
731
- ),
732
- catchError((error: ClientError) =>
733
- of(
734
- assetsActions.updateError({
735
- asset,
736
- error: {
737
- message: error?.message || 'Internal error',
738
- statusCode: error?.statusCode || 500,
739
- },
740
- }),
741
- ),
742
- ),
743
- )
744
- }),
745
- )
746
-
747
- // Selectors
748
-
749
- const selectAssetsByIds = (state: RootReducerState) => state.assets.byIds
750
-
751
- const selectAssetsAllIds = (state: RootReducerState) => state.assets.allIds
752
-
753
- export const selectAssetById = createSelector(
754
- [
755
- (state: RootReducerState) => state.assets.byIds,
756
- (_state: RootReducerState, assetId: string) => assetId,
757
- ],
758
- (byIds, assetId) => {
759
- const asset = byIds[assetId]
760
- return asset ? asset : undefined
761
- },
762
- )
763
-
764
- const selectAssets: Selector<RootReducerState, AssetItem[]> = createSelector(
765
- [selectAssetsByIds, selectAssetsAllIds],
766
- (byIds, allIds) => allIds.map((id) => byIds[id]!),
767
- )
768
-
769
- export const selectAssetsLength = createSelector([selectAssets], (assets) => assets.length)
770
-
771
- export const selectAssetsPicked = createSelector([selectAssets], (assets) =>
772
- assets.filter((item) => item?.picked),
773
- )
774
-
775
- export const selectAssetsPickedLength = createSelector(
776
- [selectAssetsPicked],
777
- (assetsPicked) => assetsPicked.length,
778
- )
779
-
780
- export const assetsActions = {...assetsSlice.actions}
781
-
782
- export default assetsSlice.reducer