@firecms/core 3.0.0-canary.11 → 3.0.0-canary.110

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 (269) hide show
  1. package/README.md +1 -1
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +17 -0
  4. package/dist/app/Scaffold.d.ts +30 -0
  5. package/dist/app/index.d.ts +4 -0
  6. package/dist/app/useApp.d.ts +16 -0
  7. package/dist/components/CircularProgressCenter.d.ts +1 -1
  8. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  9. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -12
  10. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  12. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  13. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  14. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  15. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  16. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  17. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +2 -2
  18. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -2
  19. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  20. package/dist/components/EntityPreview.d.ts +5 -4
  21. package/dist/components/ErrorView.d.ts +1 -1
  22. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  23. package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
  24. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  25. package/dist/components/ReferenceWidget.d.ts +3 -1
  26. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  27. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  28. package/dist/components/VirtualTable/VirtualTableProps.d.ts +15 -12
  29. package/dist/components/VirtualTable/types.d.ts +3 -3
  30. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  31. package/dist/components/common/index.d.ts +1 -0
  32. package/dist/components/common/table_height.d.ts +5 -0
  33. package/dist/components/common/types.d.ts +4 -6
  34. package/dist/components/common/useColumnsIds.d.ts +2 -1
  35. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  36. package/dist/components/index.d.ts +3 -1
  37. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +6 -9
  38. package/dist/core/DefaultDrawer.d.ts +19 -0
  39. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  40. package/dist/core/EntityEditView.d.ts +18 -4
  41. package/dist/core/FireCMS.d.ts +1 -1
  42. package/dist/core/NavigationRoutes.d.ts +3 -3
  43. package/dist/core/index.d.ts +3 -4
  44. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  45. package/dist/form/components/ErrorFocus.d.ts +1 -1
  46. package/dist/form/components/FieldHelperText.d.ts +3 -3
  47. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  48. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  49. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  50. package/dist/form/components/index.d.ts +1 -0
  51. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  52. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  53. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  54. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  55. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  56. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  57. package/dist/form/field_bindings/MultiSelectBinding.d.ts +1 -1
  58. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  59. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  60. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  61. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -5
  62. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  63. package/dist/form/index.d.ts +2 -4
  64. package/dist/hooks/data/save.d.ts +1 -2
  65. package/dist/hooks/index.d.ts +1 -0
  66. package/dist/hooks/useBuildNavigationController.d.ts +1 -2
  67. package/dist/hooks/useProjectLog.d.ts +2 -2
  68. package/dist/hooks/useValidateAuthenticator.d.ts +4 -8
  69. package/dist/index.d.ts +1 -0
  70. package/dist/index.es.js +15627 -12442
  71. package/dist/index.es.js.map +1 -1
  72. package/dist/index.umd.js +19866 -587
  73. package/dist/index.umd.js.map +1 -1
  74. package/dist/internal/useBuildDataSource.d.ts +1 -16
  75. package/dist/preview/PropertyPreviewProps.d.ts +6 -1
  76. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  77. package/dist/preview/components/ReferencePreview.d.ts +2 -1
  78. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  79. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  80. package/dist/types/auth.d.ts +3 -3
  81. package/dist/types/collections.d.ts +39 -5
  82. package/dist/types/datasource.d.ts +45 -31
  83. package/dist/types/entities.d.ts +6 -1
  84. package/dist/types/entity_actions.d.ts +14 -0
  85. package/dist/types/fields.d.ts +45 -39
  86. package/dist/types/index.d.ts +0 -1
  87. package/dist/types/navigation.d.ts +6 -5
  88. package/dist/types/plugins.d.ts +14 -4
  89. package/dist/types/properties.d.ts +32 -19
  90. package/dist/types/storage.d.ts +11 -3
  91. package/dist/util/entities.d.ts +1 -1
  92. package/dist/util/icon_list.d.ts +5 -1
  93. package/dist/util/icon_synonyms.d.ts +1 -97
  94. package/dist/util/icons.d.ts +2 -2
  95. package/dist/util/index.d.ts +1 -0
  96. package/dist/util/navigation_utils.d.ts +2 -2
  97. package/dist/util/objects.d.ts +1 -1
  98. package/dist/util/plurals.d.ts +0 -2
  99. package/dist/util/resolutions.d.ts +14 -13
  100. package/dist/util/storage.d.ts +23 -2
  101. package/dist/util/useStorageUploadController.d.ts +1 -1
  102. package/package.json +128 -119
  103. package/src/app/AppBar.tsx +18 -0
  104. package/src/app/Drawer.tsx +25 -0
  105. package/src/app/Scaffold.tsx +245 -0
  106. package/src/app/index.ts +4 -0
  107. package/src/app/useApp.tsx +32 -0
  108. package/src/components/ArrayContainer.tsx +9 -7
  109. package/src/components/CircularProgressCenter.tsx +1 -1
  110. package/src/components/ClearFilterSortButton.tsx +41 -0
  111. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +12 -19
  112. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +21 -20
  113. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
  114. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +41 -35
  115. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  116. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +22 -12
  117. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +14 -6
  118. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  119. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +17 -13
  120. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +19 -7
  121. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +7 -10
  122. package/src/components/EntityCollectionView/EntityCollectionView.tsx +75 -51
  123. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  124. package/src/components/EntityCollectionView/useSelectionController.tsx +19 -7
  125. package/src/components/EntityPreview.tsx +21 -15
  126. package/src/components/EntityView.tsx +5 -5
  127. package/src/components/ErrorView.tsx +1 -1
  128. package/src/components/HomePage/DefaultHomePage.tsx +24 -15
  129. package/src/components/HomePage/NavigationCard.tsx +3 -3
  130. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  131. package/src/components/HomePage/SmallNavigationCard.tsx +5 -6
  132. package/src/components/PropertyIdCopyTooltip.tsx +48 -0
  133. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +2 -5
  134. package/src/components/ReferenceWidget.tsx +22 -12
  135. package/src/components/SearchIconsView.tsx +11 -7
  136. package/src/components/SelectableTable/SelectableTable.tsx +7 -7
  137. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  138. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  139. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
  140. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  141. package/src/components/VirtualTable/VirtualTable.tsx +70 -37
  142. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -1
  143. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  144. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  145. package/src/components/VirtualTable/VirtualTableProps.tsx +18 -14
  146. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  147. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  148. package/src/components/VirtualTable/types.tsx +2 -3
  149. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
  150. package/src/components/common/index.ts +1 -0
  151. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  152. package/src/components/common/types.tsx +4 -6
  153. package/src/components/common/useColumnsIds.tsx +23 -2
  154. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
  155. package/src/components/common/useTableSearchHelper.ts +53 -12
  156. package/src/components/index.tsx +3 -1
  157. package/src/contexts/DialogsProvider.tsx +2 -2
  158. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +52 -37
  159. package/src/core/DefaultDrawer.tsx +177 -0
  160. package/src/core/DrawerNavigationItem.tsx +64 -0
  161. package/src/core/EntityEditView.tsx +702 -137
  162. package/src/core/EntitySidePanel.tsx +1 -2
  163. package/src/core/FireCMS.tsx +39 -44
  164. package/src/core/NavigationRoutes.tsx +7 -8
  165. package/src/core/SideDialogs.tsx +4 -0
  166. package/src/core/field_configs.tsx +4 -5
  167. package/src/core/index.tsx +3 -4
  168. package/src/form/PropertyFieldBinding.tsx +53 -21
  169. package/src/form/components/CustomIdField.tsx +4 -2
  170. package/src/form/components/FieldHelperText.tsx +4 -4
  171. package/src/form/components/LabelWithIcon.tsx +27 -19
  172. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  173. package/src/form/components/StorageItemPreview.tsx +21 -11
  174. package/src/form/components/StorageUploadProgress.tsx +3 -4
  175. package/src/form/components/index.tsx +1 -0
  176. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +15 -11
  177. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +26 -21
  178. package/src/form/field_bindings/BlockFieldBinding.tsx +13 -12
  179. package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -14
  180. package/src/form/field_bindings/KeyValueFieldBinding.tsx +52 -46
  181. package/src/form/field_bindings/MapFieldBinding.tsx +15 -15
  182. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +138 -0
  183. package/src/form/field_bindings/MultiSelectBinding.tsx +9 -5
  184. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +11 -16
  185. package/src/form/field_bindings/ReferenceFieldBinding.tsx +37 -27
  186. package/src/form/field_bindings/RepeatFieldBinding.tsx +20 -16
  187. package/src/form/field_bindings/SelectFieldBinding.tsx +47 -41
  188. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +31 -52
  189. package/src/form/field_bindings/SwitchFieldBinding.tsx +9 -5
  190. package/src/form/field_bindings/TextFieldBinding.tsx +26 -22
  191. package/src/form/index.tsx +6 -6
  192. package/src/form/validation.ts +1 -17
  193. package/src/hooks/data/save.ts +6 -1
  194. package/src/hooks/data/useDataSource.tsx +1 -1
  195. package/src/hooks/index.tsx +1 -0
  196. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  197. package/src/hooks/useBuildModeController.tsx +11 -5
  198. package/src/hooks/useBuildNavigationController.tsx +86 -29
  199. package/src/hooks/useProjectLog.tsx +19 -8
  200. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  201. package/src/hooks/useValidateAuthenticator.tsx +21 -41
  202. package/src/index.ts +1 -0
  203. package/src/internal/useBuildDataSource.ts +84 -62
  204. package/src/internal/useBuildSideEntityController.tsx +90 -21
  205. package/src/internal/useUnsavedChangesDialog.tsx +3 -1
  206. package/src/preview/PropertyPreview.tsx +10 -17
  207. package/src/preview/PropertyPreviewProps.tsx +7 -1
  208. package/src/preview/components/BooleanPreview.tsx +2 -2
  209. package/src/preview/components/EnumValuesChip.tsx +2 -2
  210. package/src/preview/components/ImagePreview.tsx +24 -34
  211. package/src/preview/components/ReferencePreview.tsx +23 -23
  212. package/src/preview/components/StorageThumbnail.tsx +5 -1
  213. package/src/preview/components/UrlComponentPreview.tsx +44 -11
  214. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  215. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +3 -2
  216. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +1 -2
  217. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  218. package/src/preview/property_previews/ArrayOneOfPreview.tsx +3 -4
  219. package/src/preview/property_previews/ArrayPropertyPreview.tsx +3 -4
  220. package/src/preview/property_previews/MapPropertyPreview.tsx +6 -6
  221. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
  222. package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
  223. package/src/preview/util.ts +2 -2
  224. package/src/types/auth.tsx +3 -3
  225. package/src/types/collections.ts +48 -7
  226. package/src/types/customization_controller.tsx +0 -1
  227. package/src/types/datasource.ts +53 -39
  228. package/src/types/entities.ts +11 -1
  229. package/src/types/entity_actions.tsx +16 -3
  230. package/src/types/fields.tsx +49 -43
  231. package/src/types/index.ts +0 -1
  232. package/src/types/navigation.ts +7 -7
  233. package/src/types/plugins.tsx +16 -6
  234. package/src/types/properties.ts +37 -20
  235. package/src/types/storage.ts +12 -3
  236. package/src/util/entities.ts +5 -4
  237. package/src/util/enums.ts +1 -1
  238. package/src/util/icon_list.ts +15 -10
  239. package/src/util/icon_synonyms.ts +3 -99
  240. package/src/util/index.ts +1 -0
  241. package/src/util/navigation_utils.ts +6 -6
  242. package/src/util/objects.ts +25 -28
  243. package/src/util/permissions.ts +1 -0
  244. package/src/util/plurals.ts +0 -2
  245. package/src/util/references.ts +3 -0
  246. package/src/util/resolutions.ts +44 -39
  247. package/src/util/storage.ts +76 -22
  248. package/src/util/strings.ts +2 -2
  249. package/src/util/useStorageUploadController.tsx +22 -4
  250. package/src/util/useTraceUpdate.tsx +1 -0
  251. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  252. package/dist/components/VirtualTable/common.d.ts +0 -2
  253. package/dist/core/Drawer.d.ts +0 -23
  254. package/dist/core/Scaffold.d.ts +0 -55
  255. package/dist/core/SideEntityView.d.ts +0 -7
  256. package/dist/form/EntityForm.d.ts +0 -77
  257. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  258. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  259. package/dist/internal/useLocaleConfig.d.ts +0 -1
  260. package/dist/types/appcheck.d.ts +0 -26
  261. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -28
  262. package/src/core/Drawer.tsx +0 -191
  263. package/src/core/Scaffold.tsx +0 -281
  264. package/src/core/SideEntityView.tsx +0 -38
  265. package/src/form/EntityForm.tsx +0 -728
  266. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
  267. package/src/internal/useBuildCustomizationController.tsx +0 -5
  268. package/src/internal/useLocaleConfig.tsx +0 -18
  269. package/src/types/appcheck.ts +0 -29
@@ -29,7 +29,7 @@ import { getParentReferencesFromPath } from "../util/parent_references_from_path
29
29
  const DEFAULT_BASE_PATH = "/";
30
30
  const DEFAULT_COLLECTION_PATH = "/c";
31
31
 
32
- type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
32
+ export type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
33
33
  basePath?: string,
34
34
  baseCollectionPath?: string,
35
35
  authController: AuthController<UserType>;
@@ -130,6 +130,30 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
130
130
  .filter(Boolean) as TopNavigationEntry[]
131
131
  ];
132
132
 
133
+ // Sort by group, entries with group "Admin" will go last, and second to last will be the group "Views"
134
+ navigationEntries = navigationEntries.sort((a, b) => {
135
+ if (a.group !== "Views" && a.group !== "Admin" && (b.group === "Views" || b.group === "Admin")) {
136
+ return -1;
137
+ }
138
+ if (b.group !== "Views" && b.group !== "Admin" && (a.group === "Views" || a.group === "Admin")) {
139
+ return 1;
140
+ }
141
+ if (a.group === "Admin" && b.group !== "Admin") {
142
+ return 1;
143
+ }
144
+ if (a.group !== "Admin" && b.group === "Admin") {
145
+ return -1;
146
+ }
147
+ if (a.group === "Views" && b.group !== "Views") {
148
+ return -1;
149
+ }
150
+ if (a.group !== "Views" && b.group === "Views") {
151
+ return 1;
152
+ }
153
+ return 0;
154
+
155
+ });
156
+
133
157
  if (viewsOrder) {
134
158
  navigationEntries = navigationEntries.sort((a, b) => {
135
159
  const aIndex = viewsOrder.indexOf(a.path);
@@ -163,24 +187,38 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
163
187
  if (authController.initialLoading)
164
188
  return;
165
189
 
190
+ console.debug("Refreshing navigation");
191
+
166
192
  try {
167
- console.debug("Refreshing navigation")
193
+
168
194
  const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
169
195
  resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, injectCollections),
170
196
  resolveCMSViews(viewsProp, authController, dataSourceDelegate),
171
197
  resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
172
198
  ]
173
199
  );
174
- if (
175
- !equal(collectionsRef.current, resolvedCollections) ||
176
- !equal(viewsRef.current, resolvedViews) ||
177
- !equal(adminViewsRef.current, resolvedAdminViews) ||
178
- !equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder))
179
- ) {
200
+
201
+ let shouldUpdateTopLevelNav = false;
202
+ if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
203
+ collectionsRef.current = resolvedCollections;
204
+ shouldUpdateTopLevelNav = true;
205
+ }
206
+ if (collectionsRef.current === undefined) {
180
207
  collectionsRef.current = resolvedCollections;
208
+ shouldUpdateTopLevelNav = true;
209
+ }
210
+ if (!equal(viewsRef.current, resolvedViews)) {
181
211
  viewsRef.current = resolvedViews;
212
+ shouldUpdateTopLevelNav = true;
213
+ }
214
+ if (!equal(adminViewsRef.current, resolvedAdminViews)) {
182
215
  adminViewsRef.current = resolvedAdminViews;
183
- setTopLevelNavigation(computeTopNavigation(resolvedCollections ?? [], resolvedViews, resolvedAdminViews, viewsOrder));
216
+ shouldUpdateTopLevelNav = true;
217
+ }
218
+
219
+ const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
220
+ if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
221
+ setTopLevelNavigation(computedTopLevelNav);
184
222
  }
185
223
  } catch (e) {
186
224
  console.error(e);
@@ -205,9 +243,8 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
205
243
  refreshNavigation();
206
244
  }, [refreshNavigation]);
207
245
 
208
- const getCollection = useCallback(<EC extends EntityCollection>(
246
+ const getCollection = useCallback((
209
247
  idOrPath: string,
210
- entityId?: string,
211
248
  includeUserOverride = false
212
249
  ): EC | undefined => {
213
250
  const collections = collectionsRef.current;
@@ -217,8 +254,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
217
254
  const baseCollection = getCollectionByPathOrId(removeInitialAndTrailingSlashes(idOrPath), collections);
218
255
 
219
256
  const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(idOrPath) : undefined;
220
-
221
- const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride) : undefined;
257
+ const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride ?? {}) : undefined;
222
258
 
223
259
  let result: Partial<EntityCollection> | undefined = overriddenCollection;
224
260
 
@@ -238,17 +274,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
238
274
 
239
275
  return { ...overriddenCollection, ...result } as EC;
240
276
 
241
- }, [
242
- basePath,
243
- baseCollectionPath,
244
- ]);
277
+ }, [userConfigPersistence]);
245
278
 
246
279
  const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
247
280
 
248
281
  const collections = collectionsRef.current;
282
+ if (collections === undefined)
283
+ throw Error("getCollectionFromPaths: Collections have not been initialised yet");
249
284
  let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
250
- if (!currentCollections)
251
- throw Error("Collections have not been initialised yet");
252
285
 
253
286
  for (let i = 0; i < pathSegments.length; i++) {
254
287
  const pathSegment = pathSegments[i];
@@ -267,9 +300,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
267
300
  const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
268
301
 
269
302
  const collections = collectionsRef.current;
303
+ if (collections === undefined)
304
+ throw Error("getCollectionFromIds: Collections have not been initialised yet");
270
305
  let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
271
- if (!currentCollections)
272
- throw Error("Collections have not been initialised yet");
273
306
 
274
307
  for (let i = 0; i < ids.length; i++) {
275
308
  const id = ids[i];
@@ -305,9 +338,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
305
338
  []);
306
339
 
307
340
  const resolveAliasesFrom = useCallback((path: string): string => {
308
- const collections = collectionsRef.current;
309
- if (!collections)
310
- throw Error("Collections have not been initialised yet");
341
+ const collections = collectionsRef.current ?? [];
311
342
  return resolveCollectionPathIds(path, collections);
312
343
  }, []);
313
344
 
@@ -410,7 +441,7 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
410
441
  collectionPermissions: PermissionsBuilder | undefined,
411
442
  authController: AuthController,
412
443
  dataSource: DataSourceDelegate,
413
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
444
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
414
445
  let resolvedCollections: EntityCollection[] = [];
415
446
  if (typeof collections === "function") {
416
447
  resolvedCollections = await collections({
@@ -422,14 +453,14 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
422
453
  resolvedCollections = collections;
423
454
  }
424
455
 
425
- resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
426
-
427
- resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
428
-
429
456
  if (injectCollections) {
430
457
  resolvedCollections = injectCollections(resolvedCollections ?? []);
431
458
  }
432
459
 
460
+ resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
461
+
462
+ resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
463
+
433
464
  return resolvedCollections;
434
465
  }
435
466
 
@@ -454,3 +485,29 @@ function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
454
485
  }
455
486
  return trimmed ?? "Views";
456
487
  }
488
+
489
+ function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
490
+ if (a.length !== b.length) {
491
+ return false;
492
+ }
493
+ const aCopy = [...a];
494
+ const bCopy = [...b];
495
+ const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
496
+ const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
497
+ return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
498
+ }
499
+
500
+ function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
501
+ const {
502
+ subcollections: subcollectionsA,
503
+ ...restA
504
+ } = a;
505
+ const {
506
+ subcollections: subcollectionsB,
507
+ ...restB
508
+ } = b;
509
+ if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
510
+ return false;
511
+ }
512
+ return equal(restA, restB);
513
+ }
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- import { AuthController, FireCMSPlugin } from "../types";
2
+ import { AuthController, DataSourceDelegate, FireCMSPlugin } from "../types";
3
3
 
4
4
  export const DEFAULT_SERVER_DEV = "https://api-kdoe6pj3qq-ey.a.run.app";
5
5
  export const DEFAULT_SERVER = "https://api-drplyi3b6q-ey.a.run.app";
@@ -9,17 +9,26 @@ export type AccessResponse = {
9
9
  message?: string;
10
10
  }
11
11
 
12
- async function makeRequest(authController: AuthController, pluginKeys: string | undefined) {
13
- const firebaseToken = await authController.getAuthToken();
12
+ async function makeRequest(authController: AuthController, dataSourceKey: string, pluginKeys: string[] | undefined) {
13
+ let idToken: string | null;
14
+ try {
15
+ idToken = await authController.getAuthToken();
16
+ } catch (e) {
17
+ idToken = null;
18
+ }
14
19
  return fetch(DEFAULT_SERVER + "/access_log",
15
20
  {
16
21
  // mode: "no-cors",
17
22
  method: "POST",
18
23
  headers: {
19
24
  "Content-Type": "application/json",
20
- Authorization: `Basic ${firebaseToken}`,
25
+ Authorization: `Basic ${idToken}`
21
26
  },
22
- body: JSON.stringify({ plugins: pluginKeys })
27
+ body: JSON.stringify({
28
+ email: authController.user?.email ?? null,
29
+ datasource: dataSourceKey,
30
+ plugins: pluginKeys
31
+ })
23
32
  })
24
33
  .then(async (res) => {
25
34
  return res.json();
@@ -27,15 +36,17 @@ async function makeRequest(authController: AuthController, pluginKeys: string |
27
36
  }
28
37
 
29
38
  export function useProjectLog(authController: AuthController,
39
+ dataSourceDelegate: DataSourceDelegate,
30
40
  plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
31
41
  const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
32
42
  const accessedUserRef = useRef<string | null>(null);
33
- const pluginKeys = plugins?.map(plugin => plugin.key).join(",");
43
+ const dataSourceKey = dataSourceDelegate.key;
44
+ const pluginKeys = plugins?.map(plugin => plugin.key);
34
45
  useEffect(() => {
35
46
  if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
36
- makeRequest(authController, pluginKeys).then(setAccessResponse);
47
+ makeRequest(authController, dataSourceKey, pluginKeys).then(setAccessResponse);
37
48
  accessedUserRef.current = authController.user.uid;
38
49
  }
39
- }, [authController]);
50
+ }, [authController, dataSourceKey, pluginKeys]);
40
51
  return accessResponse;
41
52
  }
@@ -80,7 +80,7 @@ export function resolveNavigationFrom<M extends Record<string, any>, UserType ex
80
80
  if (entry.type === "collection") {
81
81
  return Promise.resolve(entry);
82
82
  } else if (entry.type === "entity") {
83
- const collection = navigation.getCollection(entry.path, entry.entityId);
83
+ const collection = navigation.getCollection(entry.path);
84
84
  if (!collection) {
85
85
  throw Error(`No collection defined in the navigation for the entity with path ${entry.path}`);
86
86
  }
@@ -1,43 +1,38 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import equal from "react-fast-compare";
3
3
 
4
- import { AppCheckTokenResult, AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
4
+ import { AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
5
5
 
6
6
  /**
7
7
  * This hook is used internally for validating an authenticator.
8
8
  *
9
9
  * @param authController
10
10
  * @param authentication
11
- * @param getAppCheckToken
12
- * @param appCheckForceRefresh
13
11
  * @param storageSource
14
- * @param dataSource
12
+ * @param dataSourceDelegate
15
13
  */
16
- export function useValidateAuthenticator<UserType extends User = User, Controller extends AuthController<UserType> = AuthController<UserType>>({
17
- disabled,
18
- authController,
19
- authentication,
20
- getAppCheckToken,
21
- appCheckForceRefresh = false,
22
- storageSource,
23
- dataSourceDelegate
24
- }:
25
- {
26
- disabled?: boolean,
27
- authController: Controller,
28
- authentication?: boolean | Authenticator<UserType, Controller>,
29
- getAppCheckToken?: (forceRefresh: boolean) => Promise<AppCheckTokenResult> | undefined,
30
- appCheckForceRefresh?: boolean,
31
- dataSourceDelegate: DataSourceDelegate;
32
- storageSource: StorageSource;
33
- }): {
14
+ export function useValidateAuthenticator<UserType extends User = User, Controller extends AuthController<UserType> = AuthController<UserType>>
15
+ ({
16
+ disabled,
17
+ authController,
18
+ authenticator,
19
+ storageSource,
20
+ dataSourceDelegate
21
+ }:
22
+ {
23
+ disabled?: boolean,
24
+ authController: Controller,
25
+ authenticator?: boolean | Authenticator<UserType, Controller>,
26
+ dataSourceDelegate: DataSourceDelegate;
27
+ storageSource: StorageSource;
28
+ }): {
34
29
  canAccessMainView: boolean,
35
30
  authLoading: boolean,
36
31
  notAllowedError: any,
37
32
  authVerified: boolean,
38
33
  } {
39
34
 
40
- const authenticationEnabled = Boolean(authentication);
35
+ const authenticationEnabled = Boolean(authenticator);
41
36
 
42
37
  const [authLoading, setAuthLoading] = useState<boolean>(authenticationEnabled);
43
38
  const [notAllowedError, setNotAllowedError] = useState<any>(false);
@@ -76,26 +71,11 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
76
71
  }
77
72
 
78
73
  const delegateUser = authController.user;
79
- console.debug("Checking authentication for user", delegateUser);
80
74
 
81
- if (getAppCheckToken) {
82
- try {
83
- if (!await getAppCheckToken(appCheckForceRefresh)) {
84
- setNotAllowedError("App Check failed.");
85
- authController.signOut();
86
- } else {
87
- console.debug("App Check success.");
88
- }
89
- } catch (e: any) {
90
- setNotAllowedError(e.message);
91
- authController.signOut();
92
- }
93
- }
94
-
95
- if (authentication instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
75
+ if (authenticator instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
96
76
  setAuthLoading(true);
97
77
  try {
98
- const allowed = await authentication({
78
+ const allowed = await authenticator({
99
79
  user: delegateUser,
100
80
  authController,
101
81
  dataSourceDelegate,
@@ -120,7 +100,7 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
120
100
  setAuthVerified(true);
121
101
  }
122
102
 
123
- }, [disabled, authController, authentication, getAppCheckToken, appCheckForceRefresh, dataSourceDelegate, storageSource]);
103
+ }, [disabled, authController, authenticator, dataSourceDelegate, storageSource]);
124
104
 
125
105
  useEffect(() => {
126
106
  checkAuthentication();
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./core";
2
+ export * from "./app";
2
3
  export * from "./types";
3
4
  export * from "./form";
4
5
  export * from "./preview";
@@ -5,12 +5,11 @@ import {
5
5
  DeleteEntityProps,
6
6
  Entity,
7
7
  EntityCollection,
8
- EntityReference,
9
8
  EntityValues,
10
9
  FetchCollectionProps,
11
10
  FetchEntityProps,
12
11
  FilterValues,
13
- GeoPoint,
12
+ FireCMSContext,
14
13
  ListenCollectionProps,
15
14
  ListenEntityProps,
16
15
  NavigationController,
@@ -53,13 +52,13 @@ export function useBuildDataSource({
53
52
  */
54
53
  fetchCollection: useCallback(<M extends Record<string, any>>({
55
54
  path,
56
- collection: collectionProp,
55
+ collection,
57
56
  filter,
58
57
  limit,
59
58
  startAfter,
60
59
  searchString,
61
60
  orderBy,
62
- order
61
+ order,
63
62
  }: FetchCollectionProps<M>
64
63
  ): Promise<Entity<M>[]> => {
65
64
  return delegate.fetchCollection<M>({
@@ -69,7 +68,8 @@ export function useBuildDataSource({
69
68
  startAfter,
70
69
  searchString,
71
70
  orderBy,
72
- order
71
+ order,
72
+ collection
73
73
  });
74
74
  }, [delegate]),
75
75
 
@@ -90,7 +90,6 @@ export function useBuildDataSource({
90
90
  * @group Firestore
91
91
  */
92
92
  listenCollection: delegate.listenCollection
93
- // eslint-disable-next-line react-hooks/rules-of-hooks
94
93
  ? useCallback(<M extends Record<string, any>>(
95
94
  {
96
95
  path,
@@ -107,7 +106,7 @@ export function useBuildDataSource({
107
106
  ): () => void => {
108
107
 
109
108
  const collection = collectionProp ?? navigationController.getCollection(path);
110
- const isCollectionGroup = Boolean(collection?.collectionGroup) ?? false;
109
+ const isCollectionGroup = Boolean(collection?.collectionGroup);
111
110
  if (!delegate.listenCollection)
112
111
  throw Error("useBuildDataSource delegate not initialised");
113
112
 
@@ -121,8 +120,7 @@ export function useBuildDataSource({
121
120
  order,
122
121
  onUpdate,
123
122
  onError,
124
- isCollectionGroup,
125
- collection
123
+ collection,
126
124
  });
127
125
  }, [delegate, navigationController.getCollection])
128
126
  : undefined,
@@ -136,11 +134,13 @@ export function useBuildDataSource({
136
134
  */
137
135
  fetchEntity: useCallback(<M extends Record<string, any>>({
138
136
  path,
139
- entityId
137
+ entityId,
138
+ collection
140
139
  }: FetchEntityProps<M>
141
140
  ): Promise<Entity<M> | undefined> => delegate.fetchEntity({
142
141
  path,
143
- entityId
142
+ entityId,
143
+ collection
144
144
  }), [delegate]),
145
145
 
146
146
  /**
@@ -154,7 +154,6 @@ export function useBuildDataSource({
154
154
  * @group Firestore
155
155
  */
156
156
  listenEntity: delegate.listenEntity
157
- // eslint-disable-next-line react-hooks/rules-of-hooks
158
157
  ? useCallback(<M extends Record<string, any>>(
159
158
  {
160
159
  path,
@@ -170,7 +169,8 @@ export function useBuildDataSource({
170
169
  path,
171
170
  entityId,
172
171
  onUpdate,
173
- onError
172
+ onError,
173
+ collection
174
174
  })
175
175
  }, [delegate.listenEntity]) : undefined,
176
176
 
@@ -196,6 +196,15 @@ export function useBuildDataSource({
196
196
 
197
197
  const collection = collectionProp ?? navigationController.getCollection(path);
198
198
 
199
+ console.log("useBuildDatasource save", {
200
+ path,
201
+ entityId,
202
+ values,
203
+ collectionProp,
204
+ collection,
205
+ status
206
+ });
207
+
199
208
  const resolvedCollection = collection
200
209
  ? resolveCollection<M>({
201
210
  collection,
@@ -207,26 +216,24 @@ export function useBuildDataSource({
207
216
 
208
217
  const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties;
209
218
 
210
- const firestoreValues = cmsToDelegateModel(
219
+ const firestoreValues = delegate.cmsToDelegateModel(
211
220
  values,
212
- delegate.buildReference,
213
- delegate.buildGeoPoint,
214
- delegate.buildDate,
215
- delegate.buildDeleteFieldValue
216
221
  );
222
+
217
223
  const updatedFirestoreValues: EntityValues<M> = properties
218
224
  ? updateDateAutoValues(
219
225
  {
220
226
  inputValues: firestoreValues,
221
227
  properties,
222
228
  status,
223
- timestampNowValue: delegate.currentTime(),
229
+ timestampNowValue: delegate.currentTime?.() ?? new Date(),
224
230
  setDateToMidnight: delegate.setDateToMidnight
225
231
  })
226
232
  : firestoreValues;
227
233
 
228
234
  return delegate.saveEntity({
229
235
  path,
236
+ collection,
230
237
  entityId,
231
238
  values: updatedFirestoreValues,
232
239
  status
@@ -267,9 +274,10 @@ export function useBuildDataSource({
267
274
  path: string,
268
275
  name: string,
269
276
  value: any,
270
- entityId?: string
277
+ entityId?: string,
278
+ databaseId?: string
271
279
  ): Promise<boolean> => {
272
- return delegate.checkUniqueField(path, name, value, entityId);
280
+ return delegate.checkUniqueField(path, name, value, entityId, databaseId);
273
281
  }, [delegate.checkUniqueField]),
274
282
 
275
283
  generateEntityId: useCallback((path: string): string => {
@@ -294,16 +302,18 @@ export function useBuildDataSource({
294
302
  filter,
295
303
  orderBy,
296
304
  order,
297
- isCollectionGroup: Boolean(collection.collectionGroup) ?? false
305
+ collection
298
306
  });
299
307
  } : undefined,
300
308
 
301
309
  isFilterCombinationValid: useCallback(({
302
310
  path,
311
+ databaseId,
303
312
  filterValues,
304
313
  sortBy
305
314
  }: {
306
315
  path: string,
316
+ databaseId?: string,
307
317
  filterValues: FilterValues<any>,
308
318
  sortBy?: [string, "asc" | "desc"]
309
319
  }): boolean => {
@@ -312,52 +322,64 @@ export function useBuildDataSource({
312
322
  return delegate.isFilterCombinationValid(
313
323
  {
314
324
  path,
325
+ databaseId,
315
326
  filterValues,
316
327
  sortBy
317
328
  }
318
329
  )
319
- }, [delegate.isFilterCombinationValid])
330
+ }, [delegate.isFilterCombinationValid]),
331
+
332
+ initTextSearch: useCallback(async (props: {
333
+ context: FireCMSContext,
334
+ path: string,
335
+ collection: EntityCollection,
336
+ parentCollectionIds?: string[]
337
+ }): Promise<boolean> => {
338
+ if (!delegate.initTextSearch)
339
+ return false;
340
+ return delegate.initTextSearch(props)
341
+ }, [delegate.initTextSearch]),
320
342
 
321
343
  };
322
344
 
323
345
  }
324
346
 
325
- /**
326
- * Recursive function that converts Firestore data types into CMS or plain
327
- * JS types.
328
- * FireCMS uses Javascript dates internally instead of Firestore timestamps.
329
- * This makes it easier to interact with the rest of the libraries and
330
- * bindings.
331
- * Also, Firestore references are replaced with {@link EntityReference}
332
- * @param data
333
- * @param buildReference
334
- * @param buildGeoPoint
335
- * @param buildDate
336
- * @param buildDelete
337
- * @group Firestore
338
- */
339
- export function cmsToDelegateModel(data: any,
340
- buildReference: (reference: EntityReference) => any,
341
- buildGeoPoint: (geoPoint: GeoPoint) => any,
342
- buildDate: (date: Date) => any,
343
- buildDelete: () => any
344
- ): any {
345
- if (data === undefined) {
346
- return buildDelete();
347
- } else if (data === null) {
348
- return null;
349
- } else if (Array.isArray(data)) {
350
- return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
351
- } else if (data.isEntityReference && data.isEntityReference()) {
352
- return buildReference(data);
353
- } else if (data instanceof GeoPoint) {
354
- return buildGeoPoint(data);
355
- } else if (data instanceof Date) {
356
- return buildDate(data);
357
- } else if (data && typeof data === "object") {
358
- return Object.entries(data)
359
- .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
360
- .reduce((a, b) => ({ ...a, ...b }), {});
361
- }
362
- return data;
363
- }
347
+ // /**
348
+ // * Recursive function that converts Firestore data types into CMS or plain
349
+ // * JS types.
350
+ // * FireCMS uses Javascript dates internally instead of Firestore timestamps.
351
+ // * This makes it easier to interact with the rest of the libraries and
352
+ // * bindings.
353
+ // * Also, Firestore references are replaced with {@link EntityReference}
354
+ // * @param data
355
+ // * @param buildReference
356
+ // * @param buildGeoPoint
357
+ // * @param buildDate
358
+ // * @param buildDelete
359
+ // * @group Firestore
360
+ // */
361
+ // export function cmsToDelegateModel(data: any,
362
+ // buildReference: (reference: EntityReference) => any,
363
+ // buildGeoPoint: (geoPoint: GeoPoint) => any,
364
+ // buildDate: (date: Date) => any,
365
+ // buildDelete: () => any
366
+ // ): any {
367
+ // if (data === undefined) {
368
+ // return buildDelete();
369
+ // } else if (data === null) {
370
+ // return null;
371
+ // } else if (Array.isArray(data)) {
372
+ // return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
373
+ // } else if (data.isEntityReference && data.isEntityReference()) {
374
+ // return buildReference(data);
375
+ // } else if (data instanceof GeoPoint) {
376
+ // return buildGeoPoint(data);
377
+ // } else if (data instanceof Date) {
378
+ // return buildDate(data);
379
+ // } else if (data && typeof data === "object") {
380
+ // return Object.entries(data)
381
+ // .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
382
+ // .reduce((a, b) => ({ ...a, ...b }), {});
383
+ // }
384
+ // return data;
385
+ // }