@pranaysahith/decap-cms-core 3.9.1

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 (299) hide show
  1. package/README.md +9 -0
  2. package/dist/@pranaysahith/decap-cms-core.js +52 -0
  3. package/dist/@pranaysahith/decap-cms-core.js.LICENSE.txt +141 -0
  4. package/dist/@pranaysahith/decap-cms-core.js.map +1 -0
  5. package/dist/decap-cms-core.js +47 -0
  6. package/dist/decap-cms-core.js.LICENSE.txt +116 -0
  7. package/dist/decap-cms-core.js.map +1 -0
  8. package/dist/esm/actions/auth.js +97 -0
  9. package/dist/esm/actions/collections.js +15 -0
  10. package/dist/esm/actions/config.js +493 -0
  11. package/dist/esm/actions/deploys.js +79 -0
  12. package/dist/esm/actions/editorialWorkflow.js +480 -0
  13. package/dist/esm/actions/entries.js +865 -0
  14. package/dist/esm/actions/media.js +147 -0
  15. package/dist/esm/actions/mediaLibrary.js +552 -0
  16. package/dist/esm/actions/notifications.js +21 -0
  17. package/dist/esm/actions/search.js +149 -0
  18. package/dist/esm/actions/status.js +74 -0
  19. package/dist/esm/actions/waitUntil.js +32 -0
  20. package/dist/esm/backend.js +1082 -0
  21. package/dist/esm/bootstrap.js +101 -0
  22. package/dist/esm/components/App/App.js +289 -0
  23. package/dist/esm/components/App/Header.js +172 -0
  24. package/dist/esm/components/App/NotFoundPage.js +19 -0
  25. package/dist/esm/components/Collection/Collection.js +198 -0
  26. package/dist/esm/components/Collection/CollectionControls.js +46 -0
  27. package/dist/esm/components/Collection/CollectionSearch.js +222 -0
  28. package/dist/esm/components/Collection/CollectionTop.js +68 -0
  29. package/dist/esm/components/Collection/ControlButton.js +17 -0
  30. package/dist/esm/components/Collection/Entries/Entries.js +73 -0
  31. package/dist/esm/components/Collection/Entries/EntriesCollection.js +241 -0
  32. package/dist/esm/components/Collection/Entries/EntriesSearch.js +113 -0
  33. package/dist/esm/components/Collection/Entries/EntryCard.js +177 -0
  34. package/dist/esm/components/Collection/Entries/EntryListing.js +143 -0
  35. package/dist/esm/components/Collection/FilterControl.js +33 -0
  36. package/dist/esm/components/Collection/FolderRenameControl.js +403 -0
  37. package/dist/esm/components/Collection/GroupControl.js +33 -0
  38. package/dist/esm/components/Collection/NestedCollection.js +308 -0
  39. package/dist/esm/components/Collection/Sidebar.js +91 -0
  40. package/dist/esm/components/Collection/SortControl.js +59 -0
  41. package/dist/esm/components/Collection/ViewStyleControl.js +38 -0
  42. package/dist/esm/components/Editor/Editor.js +466 -0
  43. package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +395 -0
  44. package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +254 -0
  45. package/dist/esm/components/Editor/EditorControlPane/Widget.js +374 -0
  46. package/dist/esm/components/Editor/EditorInterface.js +386 -0
  47. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +47 -0
  48. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +66 -0
  49. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +288 -0
  50. package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +27 -0
  51. package/dist/esm/components/Editor/EditorToolbar.js +536 -0
  52. package/dist/esm/components/Editor/EntryPathEditor.js +272 -0
  53. package/dist/esm/components/Editor/withWorkflow.js +56 -0
  54. package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +18 -0
  55. package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +20 -0
  56. package/dist/esm/components/EditorWidgets/index.js +4 -0
  57. package/dist/esm/components/MediaLibrary/EmptyMessage.js +22 -0
  58. package/dist/esm/components/MediaLibrary/MediaLibrary.js +446 -0
  59. package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +93 -0
  60. package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +99 -0
  61. package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +198 -0
  62. package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +32 -0
  63. package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +156 -0
  64. package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +51 -0
  65. package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +123 -0
  66. package/dist/esm/components/UI/DragDrop.js +67 -0
  67. package/dist/esm/components/UI/ErrorBoundary.js +173 -0
  68. package/dist/esm/components/UI/FileUploadButton.js +27 -0
  69. package/dist/esm/components/UI/Modal.js +104 -0
  70. package/dist/esm/components/UI/Notifications.js +62 -0
  71. package/dist/esm/components/UI/SettingsDropdown.js +107 -0
  72. package/dist/esm/components/UI/index.js +6 -0
  73. package/dist/esm/components/Workflow/Workflow.js +133 -0
  74. package/dist/esm/components/Workflow/WorkflowCard.js +128 -0
  75. package/dist/esm/components/Workflow/WorkflowList.js +204 -0
  76. package/dist/esm/constants/collectionTypes.js +2 -0
  77. package/dist/esm/constants/collectionViews.js +2 -0
  78. package/dist/esm/constants/commitProps.js +2 -0
  79. package/dist/esm/constants/configSchema.js +644 -0
  80. package/dist/esm/constants/fieldInference.js +57 -0
  81. package/dist/esm/constants/publishModes.js +18 -0
  82. package/dist/esm/constants/validationErrorTypes.js +6 -0
  83. package/dist/esm/formats/formats.js +83 -0
  84. package/dist/esm/formats/frontmatter.js +146 -0
  85. package/dist/esm/formats/helpers.js +12 -0
  86. package/dist/esm/formats/json.js +8 -0
  87. package/dist/esm/formats/toml.js +32 -0
  88. package/dist/esm/formats/yaml.js +51 -0
  89. package/dist/esm/index.js +7 -0
  90. package/dist/esm/integrations/index.js +28 -0
  91. package/dist/esm/integrations/providers/algolia/implementation.js +174 -0
  92. package/dist/esm/integrations/providers/assetStore/implementation.js +165 -0
  93. package/dist/esm/lib/consoleError.js +3 -0
  94. package/dist/esm/lib/formatters.js +191 -0
  95. package/dist/esm/lib/i18n.js +367 -0
  96. package/dist/esm/lib/phrases.js +6 -0
  97. package/dist/esm/lib/polyfill.js +8 -0
  98. package/dist/esm/lib/registry.js +329 -0
  99. package/dist/esm/lib/serializeEntryValues.js +67 -0
  100. package/dist/esm/lib/stega.js +142 -0
  101. package/dist/esm/lib/textHelper.js +9 -0
  102. package/dist/esm/lib/urlHelper.js +111 -0
  103. package/dist/esm/mediaLibrary.js +37 -0
  104. package/dist/esm/reducers/auth.js +27 -0
  105. package/dist/esm/reducers/collections.js +428 -0
  106. package/dist/esm/reducers/combinedReducer.js +8 -0
  107. package/dist/esm/reducers/config.js +29 -0
  108. package/dist/esm/reducers/cursors.js +31 -0
  109. package/dist/esm/reducers/deploys.js +45 -0
  110. package/dist/esm/reducers/editorialWorkflow.js +83 -0
  111. package/dist/esm/reducers/entries.js +568 -0
  112. package/dist/esm/reducers/entryDraft.js +212 -0
  113. package/dist/esm/reducers/globalUI.js +25 -0
  114. package/dist/esm/reducers/index.js +66 -0
  115. package/dist/esm/reducers/integrations.js +53 -0
  116. package/dist/esm/reducers/mediaLibrary.js +252 -0
  117. package/dist/esm/reducers/medias.js +68 -0
  118. package/dist/esm/reducers/notifications.js +23 -0
  119. package/dist/esm/reducers/search.js +92 -0
  120. package/dist/esm/reducers/status.js +30 -0
  121. package/dist/esm/redux/index.js +7 -0
  122. package/dist/esm/redux/middleware/waitUntilAction.js +48 -0
  123. package/dist/esm/routing/history.js +12 -0
  124. package/dist/esm/types/diacritics.d.js +0 -0
  125. package/dist/esm/types/global.d.js +1 -0
  126. package/dist/esm/types/immutable.js +7 -0
  127. package/dist/esm/types/redux.js +14 -0
  128. package/dist/esm/types/tomlify-j0.4.d.js +0 -0
  129. package/dist/esm/valueObjects/AssetProxy.js +44 -0
  130. package/dist/esm/valueObjects/EditorComponent.js +34 -0
  131. package/dist/esm/valueObjects/Entry.js +20 -0
  132. package/index.d.ts +618 -0
  133. package/package.json +106 -0
  134. package/src/__tests__/backend.spec.js +1161 -0
  135. package/src/actions/__tests__/config.spec.js +1009 -0
  136. package/src/actions/__tests__/editorialWorkflow.spec.js +216 -0
  137. package/src/actions/__tests__/entries.spec.js +596 -0
  138. package/src/actions/__tests__/media.spec.ts +171 -0
  139. package/src/actions/__tests__/mediaLibrary.spec.js +327 -0
  140. package/src/actions/__tests__/search.spec.js +209 -0
  141. package/src/actions/auth.ts +127 -0
  142. package/src/actions/collections.ts +18 -0
  143. package/src/actions/config.ts +565 -0
  144. package/src/actions/deploys.ts +104 -0
  145. package/src/actions/editorialWorkflow.ts +567 -0
  146. package/src/actions/entries.ts +1055 -0
  147. package/src/actions/media.ts +139 -0
  148. package/src/actions/mediaLibrary.ts +574 -0
  149. package/src/actions/notifications.ts +36 -0
  150. package/src/actions/search.ts +221 -0
  151. package/src/actions/status.ts +99 -0
  152. package/src/actions/waitUntil.ts +49 -0
  153. package/src/backend.ts +1400 -0
  154. package/src/bootstrap.js +104 -0
  155. package/src/components/App/App.js +286 -0
  156. package/src/components/App/Header.js +266 -0
  157. package/src/components/App/NotFoundPage.js +23 -0
  158. package/src/components/Collection/Collection.js +210 -0
  159. package/src/components/Collection/CollectionControls.js +58 -0
  160. package/src/components/Collection/CollectionSearch.js +243 -0
  161. package/src/components/Collection/CollectionTop.js +81 -0
  162. package/src/components/Collection/ControlButton.js +27 -0
  163. package/src/components/Collection/Entries/Entries.js +82 -0
  164. package/src/components/Collection/Entries/EntriesCollection.js +277 -0
  165. package/src/components/Collection/Entries/EntriesSearch.js +102 -0
  166. package/src/components/Collection/Entries/EntryCard.js +246 -0
  167. package/src/components/Collection/Entries/EntryListing.js +151 -0
  168. package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +163 -0
  169. package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +46 -0
  170. package/src/components/Collection/FilterControl.js +39 -0
  171. package/src/components/Collection/GroupControl.js +39 -0
  172. package/src/components/Collection/NestedCollection.js +330 -0
  173. package/src/components/Collection/Sidebar.js +136 -0
  174. package/src/components/Collection/SortControl.js +68 -0
  175. package/src/components/Collection/ViewStyleControl.js +50 -0
  176. package/src/components/Collection/__tests__/Collection.spec.js +75 -0
  177. package/src/components/Collection/__tests__/NestedCollection.spec.js +445 -0
  178. package/src/components/Collection/__tests__/Sidebar.spec.js +87 -0
  179. package/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap +144 -0
  180. package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +550 -0
  181. package/src/components/Collection/__tests__/__snapshots__/Sidebar.spec.js.snap +312 -0
  182. package/src/components/Editor/Editor.js +497 -0
  183. package/src/components/Editor/EditorControlPane/EditorControl.js +452 -0
  184. package/src/components/Editor/EditorControlPane/EditorControlPane.js +269 -0
  185. package/src/components/Editor/EditorControlPane/Widget.js +384 -0
  186. package/src/components/Editor/EditorInterface.js +444 -0
  187. package/src/components/Editor/EditorPreviewPane/EditorPreview.js +40 -0
  188. package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +74 -0
  189. package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +333 -0
  190. package/src/components/Editor/EditorPreviewPane/PreviewHOC.js +33 -0
  191. package/src/components/Editor/EditorToolbar.js +691 -0
  192. package/src/components/Editor/__tests__/Editor.spec.js +221 -0
  193. package/src/components/Editor/__tests__/EditorToolbar.spec.js +120 -0
  194. package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +45 -0
  195. package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +4233 -0
  196. package/src/components/Editor/withWorkflow.js +61 -0
  197. package/src/components/EditorWidgets/Unknown/UnknownControl.js +17 -0
  198. package/src/components/EditorWidgets/Unknown/UnknownPreview.js +19 -0
  199. package/src/components/EditorWidgets/index.js +5 -0
  200. package/src/components/MediaLibrary/EmptyMessage.js +28 -0
  201. package/src/components/MediaLibrary/MediaLibrary.js +411 -0
  202. package/src/components/MediaLibrary/MediaLibraryButtons.js +136 -0
  203. package/src/components/MediaLibrary/MediaLibraryCard.js +128 -0
  204. package/src/components/MediaLibrary/MediaLibraryCardGrid.js +199 -0
  205. package/src/components/MediaLibrary/MediaLibraryHeader.js +48 -0
  206. package/src/components/MediaLibrary/MediaLibraryModal.js +200 -0
  207. package/src/components/MediaLibrary/MediaLibrarySearch.js +61 -0
  208. package/src/components/MediaLibrary/MediaLibraryTop.js +143 -0
  209. package/src/components/MediaLibrary/__tests__/MediaLibraryButtons.spec.js +45 -0
  210. package/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +49 -0
  211. package/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +264 -0
  212. package/src/components/UI/DragDrop.js +66 -0
  213. package/src/components/UI/ErrorBoundary.js +214 -0
  214. package/src/components/UI/FileUploadButton.js +24 -0
  215. package/src/components/UI/Modal.js +112 -0
  216. package/src/components/UI/Notifications.tsx +83 -0
  217. package/src/components/UI/SettingsDropdown.js +103 -0
  218. package/src/components/UI/__tests__/ErrorBoundary.spec.js +57 -0
  219. package/src/components/UI/index.js +6 -0
  220. package/src/components/Workflow/Workflow.js +169 -0
  221. package/src/components/Workflow/WorkflowCard.js +177 -0
  222. package/src/components/Workflow/WorkflowList.js +272 -0
  223. package/src/constants/__tests__/configSchema.spec.js +611 -0
  224. package/src/constants/collectionTypes.ts +2 -0
  225. package/src/constants/collectionViews.js +2 -0
  226. package/src/constants/commitProps.ts +2 -0
  227. package/src/constants/configSchema.js +441 -0
  228. package/src/constants/fieldInference.tsx +78 -0
  229. package/src/constants/publishModes.ts +22 -0
  230. package/src/constants/validationErrorTypes.js +6 -0
  231. package/src/formats/__tests__/formats.spec.js +87 -0
  232. package/src/formats/__tests__/frontmatter.spec.js +429 -0
  233. package/src/formats/__tests__/toml.spec.js +9 -0
  234. package/src/formats/__tests__/yaml.spec.js +162 -0
  235. package/src/formats/formats.ts +97 -0
  236. package/src/formats/frontmatter.ts +150 -0
  237. package/src/formats/helpers.ts +14 -0
  238. package/src/formats/json.ts +9 -0
  239. package/src/formats/toml.ts +33 -0
  240. package/src/formats/yaml.ts +58 -0
  241. package/src/index.js +8 -0
  242. package/src/integrations/index.js +35 -0
  243. package/src/integrations/providers/algolia/implementation.js +176 -0
  244. package/src/integrations/providers/assetStore/implementation.js +148 -0
  245. package/src/lib/__tests__/formatters.spec.js +751 -0
  246. package/src/lib/__tests__/i18n.spec.js +792 -0
  247. package/src/lib/__tests__/phrases.spec.js +119 -0
  248. package/src/lib/__tests__/registry.spec.js +261 -0
  249. package/src/lib/__tests__/serializeEntryValues.spec.js +22 -0
  250. package/src/lib/__tests__/urlHelper.spec.js +138 -0
  251. package/src/lib/consoleError.js +7 -0
  252. package/src/lib/formatters.ts +286 -0
  253. package/src/lib/i18n.ts +454 -0
  254. package/src/lib/phrases.js +8 -0
  255. package/src/lib/polyfill.js +9 -0
  256. package/src/lib/registry.js +312 -0
  257. package/src/lib/serializeEntryValues.js +75 -0
  258. package/src/lib/stega.ts +145 -0
  259. package/src/lib/textHelper.js +11 -0
  260. package/src/lib/urlHelper.ts +128 -0
  261. package/src/mediaLibrary.ts +51 -0
  262. package/src/reducers/__tests__/auth.spec.ts +38 -0
  263. package/src/reducers/__tests__/collections.spec.js +610 -0
  264. package/src/reducers/__tests__/config.spec.js +38 -0
  265. package/src/reducers/__tests__/entries.spec.js +694 -0
  266. package/src/reducers/__tests__/entryDraft.spec.js +297 -0
  267. package/src/reducers/__tests__/globalUI.js +43 -0
  268. package/src/reducers/__tests__/integrations.spec.ts +76 -0
  269. package/src/reducers/__tests__/mediaLibrary.spec.js +154 -0
  270. package/src/reducers/__tests__/medias.spec.ts +49 -0
  271. package/src/reducers/auth.ts +46 -0
  272. package/src/reducers/collections.ts +535 -0
  273. package/src/reducers/combinedReducer.ts +11 -0
  274. package/src/reducers/config.ts +38 -0
  275. package/src/reducers/cursors.js +36 -0
  276. package/src/reducers/deploys.ts +52 -0
  277. package/src/reducers/editorialWorkflow.ts +163 -0
  278. package/src/reducers/entries.ts +819 -0
  279. package/src/reducers/entryDraft.js +260 -0
  280. package/src/reducers/globalUI.ts +45 -0
  281. package/src/reducers/index.ts +82 -0
  282. package/src/reducers/integrations.ts +59 -0
  283. package/src/reducers/mediaLibrary.ts +296 -0
  284. package/src/reducers/medias.ts +66 -0
  285. package/src/reducers/notifications.ts +52 -0
  286. package/src/reducers/search.ts +111 -0
  287. package/src/reducers/status.ts +40 -0
  288. package/src/redux/index.ts +18 -0
  289. package/src/redux/middleware/waitUntilAction.ts +64 -0
  290. package/src/routing/__tests__/history.spec.ts +49 -0
  291. package/src/routing/history.ts +17 -0
  292. package/src/types/diacritics.d.ts +1 -0
  293. package/src/types/global.d.ts +8 -0
  294. package/src/types/immutable.ts +49 -0
  295. package/src/types/redux.ts +827 -0
  296. package/src/types/tomlify-j0.4.d.ts +13 -0
  297. package/src/valueObjects/AssetProxy.ts +48 -0
  298. package/src/valueObjects/EditorComponent.js +38 -0
  299. package/src/valueObjects/Entry.ts +63 -0
@@ -0,0 +1,296 @@
1
+ import { Map, List } from 'immutable';
2
+ import { v4 as uuid } from 'uuid';
3
+ import { dirname } from 'path';
4
+
5
+ import {
6
+ MEDIA_LIBRARY_OPEN,
7
+ MEDIA_LIBRARY_CLOSE,
8
+ MEDIA_LIBRARY_CREATE,
9
+ MEDIA_INSERT,
10
+ MEDIA_REMOVE_INSERTED,
11
+ MEDIA_LOAD_REQUEST,
12
+ MEDIA_LOAD_SUCCESS,
13
+ MEDIA_LOAD_FAILURE,
14
+ MEDIA_PERSIST_REQUEST,
15
+ MEDIA_PERSIST_SUCCESS,
16
+ MEDIA_PERSIST_FAILURE,
17
+ MEDIA_DELETE_REQUEST,
18
+ MEDIA_DELETE_SUCCESS,
19
+ MEDIA_DELETE_FAILURE,
20
+ MEDIA_DISPLAY_URL_REQUEST,
21
+ MEDIA_DISPLAY_URL_SUCCESS,
22
+ MEDIA_DISPLAY_URL_FAILURE,
23
+ } from '../actions/mediaLibrary';
24
+ import { selectEditingDraft, selectMediaFolder } from './entries';
25
+ import { selectIntegration } from './';
26
+
27
+ import type { MediaLibraryAction } from '../actions/mediaLibrary';
28
+ import type {
29
+ State,
30
+ MediaLibraryInstance,
31
+ MediaFile,
32
+ MediaFileMap,
33
+ DisplayURLState,
34
+ EntryField,
35
+ } from '../types/redux';
36
+
37
+ const defaultState: {
38
+ isVisible: boolean;
39
+ showMediaButton: boolean;
40
+ controlMedia: Map<string, string>;
41
+ displayURLs: Map<string, string>;
42
+ externalLibrary?: MediaLibraryInstance;
43
+ controlID?: string;
44
+ page?: number;
45
+ files?: MediaFile[];
46
+ config: Map<string, unknown>;
47
+ field?: EntryField;
48
+ value?: string | string[];
49
+ replaceIndex?: number | boolean;
50
+ } = {
51
+ isVisible: false,
52
+ showMediaButton: true,
53
+ controlMedia: Map(),
54
+ displayURLs: Map(),
55
+ config: Map(),
56
+ };
57
+
58
+ function mediaLibrary(state = Map(defaultState), action: MediaLibraryAction) {
59
+ switch (action.type) {
60
+ case MEDIA_LIBRARY_CREATE:
61
+ return state.withMutations(map => {
62
+ map.set('externalLibrary', action.payload);
63
+ map.set('showMediaButton', action.payload.enableStandalone());
64
+ });
65
+
66
+ case MEDIA_LIBRARY_OPEN: {
67
+ const { controlID, forImage, privateUpload, config, field, value, replaceIndex } =
68
+ action.payload;
69
+ const libConfig = config || Map();
70
+ const privateUploadChanged = state.get('privateUpload') !== privateUpload;
71
+ if (privateUploadChanged) {
72
+ return Map({
73
+ isVisible: true,
74
+ forImage,
75
+ controlID,
76
+ canInsert: !!controlID,
77
+ privateUpload,
78
+ config: libConfig,
79
+ controlMedia: Map(),
80
+ displayURLs: Map(),
81
+ field,
82
+ value,
83
+ replaceIndex,
84
+ });
85
+ }
86
+ return state.withMutations(map => {
87
+ map.set('isVisible', true);
88
+ map.set('forImage', forImage ?? false);
89
+ map.set('controlID', controlID ?? '');
90
+ map.set('canInsert', !!controlID);
91
+ map.set('privateUpload', privateUpload);
92
+ map.set('config', libConfig);
93
+ map.set('field', field ?? '');
94
+ map.set('value', value == '' && libConfig.get('multiple') ? [] : value ?? '');
95
+ map.set('replaceIndex', replaceIndex ?? false);
96
+ });
97
+ }
98
+
99
+ case MEDIA_LIBRARY_CLOSE:
100
+ return state.set('isVisible', false);
101
+
102
+ case MEDIA_INSERT: {
103
+ const { mediaPath } = action.payload;
104
+ const controlID = state.get('controlID');
105
+ const value = state.get('value');
106
+
107
+ if (!Array.isArray(value)) {
108
+ return state.withMutations(map => {
109
+ map.setIn(['controlMedia', controlID], mediaPath);
110
+ });
111
+ }
112
+
113
+ const replaceIndex = state.get('replaceIndex');
114
+ const mediaArray = Array.isArray(mediaPath) ? mediaPath : [mediaPath];
115
+ const valueArray = value as string[];
116
+ if (typeof replaceIndex == 'number') {
117
+ valueArray[replaceIndex] = mediaArray[0];
118
+ } else {
119
+ valueArray.push(...mediaArray);
120
+ }
121
+
122
+ return state.withMutations(map => {
123
+ map.setIn(['controlMedia', controlID], valueArray);
124
+ });
125
+ }
126
+
127
+ case MEDIA_REMOVE_INSERTED: {
128
+ const controlID = action.payload.controlID;
129
+ return state.setIn(['controlMedia', controlID], '');
130
+ }
131
+
132
+ case MEDIA_LOAD_REQUEST:
133
+ return state.withMutations(map => {
134
+ map.set('isLoading', true);
135
+ map.set('isPaginating', action.payload.page > 1);
136
+ });
137
+
138
+ case MEDIA_LOAD_SUCCESS: {
139
+ const {
140
+ files = [],
141
+ page,
142
+ canPaginate,
143
+ dynamicSearch,
144
+ dynamicSearchQuery,
145
+ privateUpload,
146
+ } = action.payload;
147
+ const privateUploadChanged = state.get('privateUpload') !== privateUpload;
148
+
149
+ if (privateUploadChanged) {
150
+ return state;
151
+ }
152
+
153
+ const filesWithKeys = files.map(file => ({ ...file, key: uuid() }));
154
+ return state.withMutations(map => {
155
+ map.set('isLoading', false);
156
+ map.set('isPaginating', false);
157
+ map.set('page', page ?? 1);
158
+ map.set('hasNextPage', !!(canPaginate && files.length > 0));
159
+ map.set('dynamicSearch', dynamicSearch ?? false);
160
+ map.set('dynamicSearchQuery', dynamicSearchQuery ?? '');
161
+ map.set('dynamicSearchActive', !!dynamicSearchQuery);
162
+ if (page && page > 1) {
163
+ const updatedFiles = (map.get('files') as MediaFile[]).concat(filesWithKeys);
164
+ map.set('files', updatedFiles);
165
+ } else {
166
+ map.set('files', filesWithKeys);
167
+ }
168
+ });
169
+ }
170
+
171
+ case MEDIA_LOAD_FAILURE: {
172
+ const privateUploadChanged = state.get('privateUpload') !== action.payload.privateUpload;
173
+ if (privateUploadChanged) {
174
+ return state;
175
+ }
176
+ return state.set('isLoading', false);
177
+ }
178
+
179
+ case MEDIA_PERSIST_REQUEST:
180
+ return state.set('isPersisting', true);
181
+
182
+ case MEDIA_PERSIST_SUCCESS: {
183
+ const { file, privateUpload } = action.payload;
184
+ const privateUploadChanged = state.get('privateUpload') !== privateUpload;
185
+ if (privateUploadChanged) {
186
+ return state;
187
+ }
188
+ return state.withMutations(map => {
189
+ const fileWithKey = { ...file, key: uuid() };
190
+ const files = map.get('files') as MediaFile[];
191
+ const updatedFiles = [fileWithKey, ...files];
192
+ map.set('files', updatedFiles);
193
+ map.set('isPersisting', false);
194
+ });
195
+ }
196
+
197
+ case MEDIA_PERSIST_FAILURE: {
198
+ const privateUploadChanged = state.get('privateUpload') !== action.payload.privateUpload;
199
+ if (privateUploadChanged) {
200
+ return state;
201
+ }
202
+ return state.set('isPersisting', false);
203
+ }
204
+
205
+ case MEDIA_DELETE_REQUEST:
206
+ return state.set('isDeleting', true);
207
+
208
+ case MEDIA_DELETE_SUCCESS: {
209
+ const { file, privateUpload } = action.payload;
210
+ const { key, id } = file;
211
+ const privateUploadChanged = state.get('privateUpload') !== privateUpload;
212
+ if (privateUploadChanged) {
213
+ return state;
214
+ }
215
+ return state.withMutations(map => {
216
+ const files = map.get('files') as MediaFile[];
217
+ const updatedFiles = files.filter(file => (key ? file.key !== key : file.id !== id));
218
+ map.set('files', updatedFiles);
219
+ map.deleteIn(['displayURLs', id]);
220
+ map.set('isDeleting', false);
221
+ });
222
+ }
223
+
224
+ case MEDIA_DELETE_FAILURE: {
225
+ const privateUploadChanged = state.get('privateUpload') !== action.payload.privateUpload;
226
+ if (privateUploadChanged) {
227
+ return state;
228
+ }
229
+ return state.set('isDeleting', false);
230
+ }
231
+
232
+ case MEDIA_DISPLAY_URL_REQUEST:
233
+ return state.setIn(['displayURLs', action.payload.key, 'isFetching'], true);
234
+
235
+ case MEDIA_DISPLAY_URL_SUCCESS: {
236
+ const displayURLPath = ['displayURLs', action.payload.key];
237
+ return state
238
+ .setIn([...displayURLPath, 'isFetching'], false)
239
+ .setIn([...displayURLPath, 'url'], action.payload.url);
240
+ }
241
+
242
+ case MEDIA_DISPLAY_URL_FAILURE: {
243
+ const displayURLPath = ['displayURLs', action.payload.key];
244
+ return (
245
+ state
246
+ .setIn([...displayURLPath, 'isFetching'], false)
247
+ // make sure that err is set so the CMS won't attempt to load
248
+ // the image again
249
+ .setIn([...displayURLPath, 'err'], action.payload.err || true)
250
+ .deleteIn([...displayURLPath, 'url'])
251
+ );
252
+ }
253
+
254
+ default:
255
+ return state;
256
+ }
257
+ }
258
+
259
+ export function selectMediaFiles(state: State, field?: EntryField) {
260
+ const { mediaLibrary, entryDraft } = state;
261
+ const editingDraft = selectEditingDraft(state.entryDraft);
262
+ const integration = selectIntegration(state, null, 'assetStore');
263
+
264
+ let files;
265
+ if (editingDraft && !integration) {
266
+ const entryFiles = entryDraft
267
+ .getIn(['entry', 'mediaFiles'], List<MediaFileMap>())
268
+ .toJS() as MediaFile[];
269
+ const entry = entryDraft.get('entry');
270
+ const collection = state.collections.get(entry?.get('collection'));
271
+ const mediaFolder = selectMediaFolder(state.config, collection, entry, field);
272
+ files = entryFiles
273
+ .filter(f => dirname(f.path) === mediaFolder)
274
+ .map(file => ({ key: file.id, ...file }));
275
+ } else {
276
+ files = mediaLibrary.get('files') || [];
277
+ }
278
+
279
+ return files;
280
+ }
281
+
282
+ export function selectMediaFileByPath(state: State, path: string) {
283
+ const files = selectMediaFiles(state);
284
+ const file = files.find(file => file.path === path);
285
+ return file;
286
+ }
287
+
288
+ export function selectMediaDisplayURL(state: State, id: string) {
289
+ const displayUrlState = state.mediaLibrary.getIn(
290
+ ['displayURLs', id],
291
+ Map() as unknown as DisplayURLState,
292
+ );
293
+ return displayUrlState;
294
+ }
295
+
296
+ export default mediaLibrary;
@@ -0,0 +1,66 @@
1
+ import { produce } from 'immer';
2
+
3
+ import {
4
+ ADD_ASSETS,
5
+ ADD_ASSET,
6
+ REMOVE_ASSET,
7
+ LOAD_ASSET_REQUEST,
8
+ LOAD_ASSET_SUCCESS,
9
+ LOAD_ASSET_FAILURE,
10
+ } from '../actions/media';
11
+
12
+ import type { MediasAction } from '../actions/media';
13
+ import type AssetProxy from '../valueObjects/AssetProxy';
14
+
15
+ export type Medias = {
16
+ [path: string]: { asset: AssetProxy | undefined; isLoading: boolean; error: Error | null };
17
+ };
18
+
19
+ const defaultState: Medias = {};
20
+
21
+ const medias = produce((state: Medias, action: MediasAction) => {
22
+ switch (action.type) {
23
+ case ADD_ASSETS: {
24
+ const assets = action.payload;
25
+ assets.forEach(asset => {
26
+ state[asset.path] = { asset, isLoading: false, error: null };
27
+ });
28
+ break;
29
+ }
30
+ case ADD_ASSET: {
31
+ const asset = action.payload;
32
+ state[asset.path] = { asset, isLoading: false, error: null };
33
+ break;
34
+ }
35
+ case REMOVE_ASSET: {
36
+ const path = action.payload;
37
+ delete state[path];
38
+ break;
39
+ }
40
+ case LOAD_ASSET_REQUEST: {
41
+ const { path } = action.payload;
42
+ state[path] = state[path] || {};
43
+ state[path].isLoading = true;
44
+ break;
45
+ }
46
+ case LOAD_ASSET_SUCCESS: {
47
+ const { path } = action.payload;
48
+ state[path] = state[path] || {};
49
+ state[path].isLoading = false;
50
+ state[path].error = null;
51
+ break;
52
+ }
53
+ case LOAD_ASSET_FAILURE: {
54
+ const { path, error } = action.payload;
55
+ state[path] = state[path] || {};
56
+ state[path].isLoading = false;
57
+ state[path].error = error;
58
+ }
59
+ }
60
+ }, defaultState);
61
+
62
+ export function selectIsLoadingAsset(state: Medias) {
63
+ return Object.values(state).some(state => state.isLoading);
64
+ }
65
+
66
+ export default medias;
@@ -0,0 +1,52 @@
1
+ import { produce } from 'immer';
2
+ import { v4 as uuid } from 'uuid';
3
+
4
+ import {
5
+ NOTIFICATION_SEND,
6
+ NOTIFICATION_DISMISS,
7
+ NOTIFICATIONS_CLEAR,
8
+ } from '../actions/notifications';
9
+
10
+ import type {
11
+ NotificationsAction,
12
+ NotificationMessage,
13
+ NotificationPayload,
14
+ } from '../actions/notifications';
15
+ import type { TypeOptions } from 'react-toastify';
16
+
17
+ export type Notification = {
18
+ id: string;
19
+ message: string | NotificationMessage;
20
+ dismissAfter?: number;
21
+ type: TypeOptions | undefined;
22
+ };
23
+
24
+ export type NotificationsState = {
25
+ notifications: Notification[];
26
+ };
27
+
28
+ const defaultState: NotificationsState = {
29
+ notifications: [],
30
+ };
31
+
32
+ const notifications = produce((state: NotificationsState, action: NotificationsAction) => {
33
+ switch (action.type) {
34
+ case NOTIFICATIONS_CLEAR:
35
+ state.notifications = [];
36
+ break;
37
+ case NOTIFICATION_DISMISS:
38
+ state.notifications = state.notifications.filter(n => n.id !== action.id);
39
+ break;
40
+ case NOTIFICATION_SEND:
41
+ state.notifications = [
42
+ ...state.notifications,
43
+ {
44
+ id: uuid(),
45
+ ...(action.payload as NotificationPayload),
46
+ },
47
+ ];
48
+ break;
49
+ }
50
+ }, defaultState);
51
+
52
+ export default notifications;
@@ -0,0 +1,111 @@
1
+ import { produce } from 'immer';
2
+
3
+ import {
4
+ QUERY_FAILURE,
5
+ QUERY_REQUEST,
6
+ QUERY_SUCCESS,
7
+ SEARCH_CLEAR,
8
+ SEARCH_ENTRIES_FAILURE,
9
+ SEARCH_ENTRIES_REQUEST,
10
+ SEARCH_ENTRIES_SUCCESS,
11
+ CLEAR_REQUESTS,
12
+ } from '../actions/search';
13
+
14
+ import type { SearchAction } from '../actions/search';
15
+ import type { EntryValue } from '../valueObjects/Entry';
16
+
17
+ export type Search = {
18
+ isFetching: boolean;
19
+ term: string;
20
+ collections: string[];
21
+ page: number;
22
+ entryIds: { collection: string; slug: string }[];
23
+ queryHits: Record<string, EntryValue[]>;
24
+ error: Error | undefined;
25
+ requests: QueryRequest[];
26
+ };
27
+
28
+ type QueryResponse = {
29
+ hits: EntryValue[];
30
+ query: string;
31
+ };
32
+
33
+ export type QueryRequest = {
34
+ id: string;
35
+ expires: Date;
36
+ queryResponse: Promise<QueryResponse>;
37
+ };
38
+
39
+ const defaultState: Search = {
40
+ isFetching: false,
41
+ term: '',
42
+ collections: [],
43
+ page: 0,
44
+ entryIds: [],
45
+ queryHits: {},
46
+ error: undefined,
47
+ requests: [],
48
+ };
49
+
50
+ const search = produce((state: Search, action: SearchAction) => {
51
+ switch (action.type) {
52
+ case SEARCH_CLEAR:
53
+ return defaultState;
54
+
55
+ case SEARCH_ENTRIES_REQUEST: {
56
+ const { page, searchTerm, searchCollections } = action.payload;
57
+ state.isFetching = true;
58
+ state.term = searchTerm;
59
+ state.collections = searchCollections;
60
+ state.page = page;
61
+ break;
62
+ }
63
+
64
+ case SEARCH_ENTRIES_SUCCESS: {
65
+ const { entries, page } = action.payload;
66
+ const entryIds = entries.map(entry => ({ collection: entry.collection, slug: entry.slug }));
67
+ state.isFetching = false;
68
+ state.page = page;
69
+ state.entryIds =
70
+ !page || isNaN(page) || page === 0 ? entryIds : state.entryIds.concat(entryIds);
71
+ break;
72
+ }
73
+
74
+ case SEARCH_ENTRIES_FAILURE: {
75
+ const { error } = action.payload;
76
+ state.isFetching = false;
77
+ state.error = error;
78
+ break;
79
+ }
80
+
81
+ case QUERY_REQUEST: {
82
+ const { searchTerm, request } = action.payload;
83
+ state.isFetching = true;
84
+ state.term = searchTerm;
85
+ if (request) {
86
+ state.requests.push(request);
87
+ }
88
+ break;
89
+ }
90
+
91
+ case CLEAR_REQUESTS: {
92
+ state.requests = state.requests.filter(req => req.expires >= new Date());
93
+ break;
94
+ }
95
+
96
+ case QUERY_SUCCESS: {
97
+ const { namespace, hits } = action.payload;
98
+ state.isFetching = false;
99
+ state.queryHits[namespace] = hits;
100
+ break;
101
+ }
102
+
103
+ case QUERY_FAILURE: {
104
+ const { error } = action.payload;
105
+ state.isFetching = false;
106
+ state.error = error;
107
+ }
108
+ }
109
+ }, defaultState);
110
+
111
+ export default search;
@@ -0,0 +1,40 @@
1
+ import { produce } from 'immer';
2
+
3
+ import { STATUS_REQUEST, STATUS_SUCCESS, STATUS_FAILURE } from '../actions/status';
4
+
5
+ import type { StatusAction } from '../actions/status';
6
+
7
+ export type Status = {
8
+ isFetching: boolean;
9
+ status: {
10
+ auth: { status: boolean };
11
+ api: { status: boolean; statusPage: string };
12
+ };
13
+ error: Error | undefined;
14
+ };
15
+
16
+ const defaultState: Status = {
17
+ isFetching: false,
18
+ status: {
19
+ auth: { status: true },
20
+ api: { status: true, statusPage: '' },
21
+ },
22
+ error: undefined,
23
+ };
24
+
25
+ const status = produce((state: Status, action: StatusAction) => {
26
+ switch (action.type) {
27
+ case STATUS_REQUEST:
28
+ state.isFetching = true;
29
+ break;
30
+ case STATUS_SUCCESS:
31
+ state.isFetching = false;
32
+ state.status = action.payload.status;
33
+ break;
34
+ case STATUS_FAILURE:
35
+ state.isFetching = false;
36
+ state.error = action.payload.error;
37
+ }
38
+ }, defaultState);
39
+
40
+ export default status;
@@ -0,0 +1,18 @@
1
+ import { createStore, applyMiddleware } from 'redux';
2
+ import { composeWithDevTools } from 'redux-devtools-extension';
3
+ import thunkMiddleware from 'redux-thunk';
4
+
5
+ import { waitUntilAction } from './middleware/waitUntilAction';
6
+ import createRootReducer from '../reducers/combinedReducer';
7
+
8
+ import type { ThunkMiddleware } from 'redux-thunk';
9
+ import type { AnyAction } from 'redux';
10
+ import type { State } from '../types/redux';
11
+ import type { Reducer } from 'react';
12
+
13
+ const store = createStore<State | undefined, AnyAction, unknown, unknown>(
14
+ createRootReducer() as unknown as Reducer<State | undefined, AnyAction>,
15
+ composeWithDevTools(applyMiddleware(thunkMiddleware as ThunkMiddleware<State>, waitUntilAction)),
16
+ );
17
+
18
+ export { store };
@@ -0,0 +1,64 @@
1
+ // Based on wait-service by Mozilla:
2
+ // https://github.com/mozilla/gecko-dev/blob/master/devtools/client/shared/redux/middleware/wait-service.js
3
+
4
+ /**
5
+ * A middleware that provides the ability for actions to install a
6
+ * function to be run once when a specific condition is met by an
7
+ * action coming through the system. Think of it as a thunk that
8
+ * blocks until the condition is met.
9
+ */
10
+ import type { Middleware, MiddlewareAPI, Dispatch, AnyAction } from 'redux';
11
+ import type { State } from '../../types/redux';
12
+
13
+ export const WAIT_UNTIL_ACTION = 'WAIT_UNTIL_ACTION';
14
+
15
+ export interface WaitActionArgs {
16
+ predicate: (action: AnyAction) => boolean;
17
+ run: (dispatch: Dispatch, getState: () => State, action: AnyAction) => void;
18
+ }
19
+
20
+ interface WaitAction extends WaitActionArgs {
21
+ type: typeof WAIT_UNTIL_ACTION;
22
+ }
23
+
24
+ // eslint-disable-next-line func-style
25
+ export const waitUntilAction: Middleware<{}, State, Dispatch> = ({
26
+ dispatch,
27
+ getState,
28
+ }: MiddlewareAPI<Dispatch, State>) => {
29
+ let pending: WaitAction[] = [];
30
+
31
+ function checkPending(action: AnyAction): void {
32
+ const readyRequests = [];
33
+ const stillPending = [];
34
+
35
+ // Find the pending requests whose predicates are satisfied with
36
+ // this action. Wait to run the requests until after we update the
37
+ // pending queue because the request handler may synchronously
38
+ // dispatch again and run this service (that use case is
39
+ // completely valid).
40
+ for (const request of pending) {
41
+ if (request.predicate(action)) {
42
+ readyRequests.push(request);
43
+ } else {
44
+ stillPending.push(request);
45
+ }
46
+ }
47
+
48
+ pending = stillPending;
49
+ for (const request of readyRequests) {
50
+ request.run(dispatch, getState, action);
51
+ }
52
+ }
53
+
54
+ return (next: Dispatch<AnyAction>) =>
55
+ (action: AnyAction): null | AnyAction => {
56
+ if (action.type === WAIT_UNTIL_ACTION) {
57
+ pending.push(action as WaitAction);
58
+ return null;
59
+ }
60
+ const result = next(action);
61
+ checkPending(action);
62
+ return result;
63
+ };
64
+ };
@@ -0,0 +1,49 @@
1
+ import { createHashHistory } from 'history';
2
+ import { mocked } from 'jest-mock';
3
+
4
+ import type { History } from 'history';
5
+
6
+ jest.mock('history');
7
+
8
+ const history = { push: jest.fn(), replace: jest.fn() } as unknown as History;
9
+ const mockedCreateHashHistory = mocked(createHashHistory);
10
+ mockedCreateHashHistory.mockReturnValue(history);
11
+
12
+ describe('history', () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+
17
+ describe('navigateToCollection', () => {
18
+ it('should push route', () => {
19
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
20
+ const { navigateToCollection } = require('../history');
21
+
22
+ navigateToCollection('posts');
23
+ expect(history.push).toHaveBeenCalledTimes(1);
24
+ expect(history.push).toHaveBeenCalledWith('/collections/posts');
25
+ });
26
+ });
27
+
28
+ describe('navigateToNewEntry', () => {
29
+ it('should replace route', () => {
30
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
31
+ const { navigateToNewEntry } = require('../history');
32
+
33
+ navigateToNewEntry('posts');
34
+ expect(history.replace).toHaveBeenCalledTimes(1);
35
+ expect(history.replace).toHaveBeenCalledWith('/collections/posts/new');
36
+ });
37
+ });
38
+
39
+ describe('navigateToEntry', () => {
40
+ it('should replace route', () => {
41
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
42
+ const { navigateToEntry } = require('../history');
43
+
44
+ navigateToEntry('posts', 'index');
45
+ expect(history.replace).toHaveBeenCalledTimes(1);
46
+ expect(history.replace).toHaveBeenCalledWith('/collections/posts/entries/index');
47
+ });
48
+ });
49
+ });