@firecms/core 3.0.0-beta.5 → 3.0.0-beta.7

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 (163) hide show
  1. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  2. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  3. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  5. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  6. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  7. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  8. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  9. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  10. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  11. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
  12. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  13. package/dist/components/EntityPreview.d.ts +3 -2
  14. package/dist/components/FireCMSAppBar.d.ts +4 -2
  15. package/dist/components/ReferenceWidget.d.ts +3 -1
  16. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  17. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  18. package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -6
  19. package/dist/components/VirtualTable/types.d.ts +3 -3
  20. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  21. package/dist/components/common/index.d.ts +1 -0
  22. package/dist/components/common/table_height.d.ts +5 -0
  23. package/dist/components/common/types.d.ts +4 -6
  24. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  25. package/dist/components/index.d.ts +1 -0
  26. package/dist/core/Drawer.d.ts +5 -12
  27. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  28. package/dist/core/NavigationRoutes.d.ts +1 -1
  29. package/dist/core/Scaffold.d.ts +8 -12
  30. package/dist/core/index.d.ts +3 -4
  31. package/dist/form/components/ErrorFocus.d.ts +1 -1
  32. package/dist/hooks/data/save.d.ts +1 -2
  33. package/dist/hooks/index.d.ts +1 -0
  34. package/dist/hooks/useValidateAuthenticator.d.ts +2 -6
  35. package/dist/index.es.js +10087 -9914
  36. package/dist/index.es.js.map +1 -1
  37. package/dist/index.umd.js +5 -5
  38. package/dist/index.umd.js.map +1 -1
  39. package/dist/internal/useBuildDataSource.d.ts +1 -16
  40. package/dist/preview/components/ReferencePreview.d.ts +2 -1
  41. package/dist/types/collections.d.ts +13 -2
  42. package/dist/types/datasource.d.ts +2 -5
  43. package/dist/types/entities.d.ts +5 -1
  44. package/dist/types/entity_actions.d.ts +14 -0
  45. package/dist/types/index.d.ts +0 -1
  46. package/dist/types/navigation.d.ts +2 -2
  47. package/dist/types/plugins.d.ts +3 -1
  48. package/dist/types/properties.d.ts +10 -2
  49. package/dist/util/entities.d.ts +1 -1
  50. package/dist/util/icon_synonyms.d.ts +1 -97
  51. package/dist/util/navigation_utils.d.ts +2 -2
  52. package/dist/util/objects.d.ts +1 -1
  53. package/dist/util/resolutions.d.ts +13 -13
  54. package/package.json +139 -119
  55. package/src/components/ClearFilterSortButton.tsx +41 -0
  56. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
  57. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +19 -18
  58. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
  59. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +38 -34
  60. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  61. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -1
  62. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +2 -2
  63. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  64. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  65. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
  66. package/src/components/EntityCollectionView/EntityCollectionView.tsx +47 -25
  67. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  68. package/src/components/EntityPreview.tsx +9 -7
  69. package/src/components/EntityView.tsx +5 -5
  70. package/src/components/FireCMSAppBar.tsx +32 -15
  71. package/src/components/HomePage/DefaultHomePage.tsx +1 -1
  72. package/src/components/HomePage/NavigationCard.tsx +2 -2
  73. package/src/components/HomePage/SmallNavigationCard.tsx +5 -5
  74. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  75. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +1 -0
  76. package/src/components/ReferenceWidget.tsx +21 -11
  77. package/src/components/SearchIconsView.tsx +5 -5
  78. package/src/components/SelectableTable/SelectableTable.tsx +5 -3
  79. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  80. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  81. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
  82. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  83. package/src/components/VirtualTable/VirtualTable.tsx +36 -29
  84. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  85. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  86. package/src/components/VirtualTable/VirtualTableProps.tsx +1 -6
  87. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  88. package/src/components/VirtualTable/types.tsx +2 -3
  89. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
  90. package/src/components/common/index.ts +1 -0
  91. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  92. package/src/components/common/types.tsx +4 -6
  93. package/src/components/common/useColumnsIds.tsx +10 -2
  94. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
  95. package/src/components/index.tsx +1 -0
  96. package/src/core/Drawer.tsx +16 -68
  97. package/src/core/DrawerNavigationItem.tsx +62 -0
  98. package/src/core/EntityEditView.tsx +7 -8
  99. package/src/core/EntitySidePanel.tsx +1 -1
  100. package/src/core/FireCMS.tsx +37 -42
  101. package/src/core/NavigationRoutes.tsx +3 -4
  102. package/src/core/Scaffold.tsx +80 -66
  103. package/src/core/field_configs.tsx +2 -3
  104. package/src/core/index.tsx +3 -4
  105. package/src/form/EntityForm.tsx +29 -21
  106. package/src/form/PropertyFieldBinding.tsx +0 -2
  107. package/src/form/components/StorageItemPreview.tsx +2 -2
  108. package/src/form/components/StorageUploadProgress.tsx +3 -3
  109. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
  110. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  111. package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -4
  112. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  113. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
  114. package/src/form/field_bindings/ReferenceFieldBinding.tsx +15 -13
  115. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  116. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -4
  117. package/src/hooks/data/save.ts +3 -1
  118. package/src/hooks/index.tsx +1 -0
  119. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  120. package/src/hooks/useBuildModeController.tsx +11 -5
  121. package/src/hooks/useBuildNavigationController.tsx +77 -21
  122. package/src/hooks/useProjectLog.tsx +3 -3
  123. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  124. package/src/hooks/useValidateAuthenticator.tsx +17 -37
  125. package/src/internal/useBuildDataSource.ts +42 -47
  126. package/src/internal/useBuildSideEntityController.tsx +86 -22
  127. package/src/preview/PropertyPreview.tsx +4 -14
  128. package/src/preview/components/BooleanPreview.tsx +2 -2
  129. package/src/preview/components/EnumValuesChip.tsx +1 -1
  130. package/src/preview/components/ReferencePreview.tsx +21 -22
  131. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  132. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  133. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  134. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  135. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  136. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  137. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  138. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  139. package/src/types/collections.ts +19 -3
  140. package/src/types/datasource.ts +7 -4
  141. package/src/types/entities.ts +9 -1
  142. package/src/types/entity_actions.tsx +16 -3
  143. package/src/types/index.ts +0 -1
  144. package/src/types/navigation.ts +2 -3
  145. package/src/types/plugins.tsx +4 -3
  146. package/src/types/properties.ts +15 -4
  147. package/src/types/storage.ts +1 -1
  148. package/src/util/entities.ts +1 -1
  149. package/src/util/enums.ts +1 -1
  150. package/src/util/icon_list.ts +2 -2
  151. package/src/util/icon_synonyms.ts +3 -99
  152. package/src/util/navigation_utils.ts +6 -6
  153. package/src/util/objects.ts +8 -21
  154. package/src/util/resolutions.ts +32 -31
  155. package/dist/components/VirtualTable/common.d.ts +0 -2
  156. package/dist/core/SideEntityView.d.ts +0 -7
  157. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  158. package/dist/internal/useLocaleConfig.d.ts +0 -1
  159. package/dist/types/appcheck.d.ts +0 -26
  160. package/src/core/SideEntityView.tsx +0 -38
  161. package/src/internal/useBuildCustomizationController.tsx +0 -5
  162. package/src/internal/useLocaleConfig.tsx +0 -18
  163. package/src/types/appcheck.ts +0 -29
@@ -20,7 +20,7 @@ import { StorageFieldItem, useStorageUploadController } from "../../util/useStor
20
20
  import { StorageUploadProgress } from "../components/StorageUploadProgress";
21
21
  import { StorageItemPreview } from "../components/StorageItemPreview";
22
22
  import {
23
- cn,
23
+ cls,
24
24
  fieldBackgroundDisabledMixin,
25
25
  fieldBackgroundHoverMixin,
26
26
  fieldBackgroundMixin,
@@ -203,7 +203,7 @@ function FileDropComponent({
203
203
  return (
204
204
  <div
205
205
  {...getRootProps()}
206
- className={cn(
206
+ className={cls(
207
207
  fieldBackgroundMixin,
208
208
  disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
209
209
  dropZoneClasses,
@@ -220,7 +220,7 @@ function FileDropComponent({
220
220
  <div
221
221
  {...droppableProvided.droppableProps}
222
222
  ref={droppableProvided.innerRef}
223
- className={cn("flex items-center p-1 no-scrollbar",
223
+ className={cls("flex items-center p-1 no-scrollbar",
224
224
  multipleFilesSupported && internalValue.length ? "overflow-auto" : "",
225
225
  multipleFilesSupported && internalValue.length ? "min-h-[180px]" : "min-h-[250px]"
226
226
  )}
@@ -268,7 +268,7 @@ function FileDropComponent({
268
268
  ref={provided.innerRef}
269
269
  {...provided.draggableProps}
270
270
  {...provided.dragHandleProps}
271
- className={cn(focusedMixin, "rounded-md")}
271
+ className={cls(focusedMixin, "rounded-md")}
272
272
  style={{
273
273
  ...provided.draggableProps.style
274
274
  }}
@@ -17,7 +17,6 @@ import { resolveCollection } from "../../util";
17
17
  export type SaveEntityWithCallbacksProps<M extends Record<string, any>> =
18
18
  SaveEntityProps<M> &
19
19
  {
20
- callbacks?: EntityCallbacks<M>;
21
20
  onSaveSuccess?: (updatedEntity: Entity<M>) => void,
22
21
  onSaveFailure?: (e: Error) => void,
23
22
  onPreSaveHookError?: (e: Error) => void,
@@ -109,6 +108,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
109
108
  updatedValues = values;
110
109
  }
111
110
 
111
+ console.log("Saving entity", entityId, updatedValues);
112
112
  return dataSource.saveEntity({
113
113
  collection,
114
114
  path: resolvedPath,
@@ -117,6 +117,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
117
117
  previousValues,
118
118
  status
119
119
  }).then((entity) => {
120
+ console.log("Entity saved");
120
121
  try {
121
122
  if (callbacks?.onSaveSuccess) {
122
123
  const resolvedCollection = resolveCollection<M>({
@@ -145,6 +146,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, Use
145
146
  onSaveSuccess(entity);
146
147
  })
147
148
  .catch((e) => {
149
+ console.error("!!!", e);
148
150
  if (callbacks?.onSaveFailure) {
149
151
 
150
152
  const resolvedCollection = resolveCollection<M>({
@@ -10,6 +10,7 @@ export * from "./useResolvedNavigationFrom";
10
10
 
11
11
  export * from "./useStorageSource";
12
12
  export * from "./useAuthController";
13
+ export * from "./useDialogsController";
13
14
  export * from "./useSideDialogsController";
14
15
  export * from "./useSideEntityController";
15
16
  export * from "./useFireCMSContext";
@@ -1,10 +1,10 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { PartialEntityCollection, UserConfigurationPersistence } from "../types";
3
3
  import { mergeDeep, stripCollectionPath } from "../util";
4
4
 
5
5
  export function useBuildLocalConfigurationPersistence(): UserConfigurationPersistence {
6
6
 
7
- const [configCache, setConfigCache] = useState<Record<string, PartialEntityCollection>>({});
7
+ const configCache = useRef<Record<string, PartialEntityCollection>>({});
8
8
 
9
9
  const getCollectionFromStorage = useCallback((storageKey: string) => {
10
10
  const item = localStorage.getItem(storageKey);
@@ -13,20 +13,18 @@ export function useBuildLocalConfigurationPersistence(): UserConfigurationPersis
13
13
 
14
14
  const getCollectionConfig = useCallback(<M extends Record<string, any>>(path: string): PartialEntityCollection<M> => {
15
15
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
16
- if (configCache[storageKey]) {
17
- return configCache[storageKey] as PartialEntityCollection<M>;
16
+ if (configCache.current[storageKey]) {
17
+ return configCache.current[storageKey] as PartialEntityCollection<M>;
18
18
  }
19
19
  return getCollectionFromStorage(storageKey);
20
- }, [configCache, getCollectionFromStorage]);
20
+ }, [getCollectionFromStorage]);
21
21
 
22
22
  const onCollectionModified = useCallback(<M extends Record<string, any>>(path: string, data: PartialEntityCollection<M>) => {
23
23
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
24
24
  localStorage.setItem(storageKey, JSON.stringify(data));
25
- setConfigCache((currentCache) => {
26
- const cachedConfig = currentCache[storageKey];
27
- const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
28
- return (mergeDeep(currentCache, newConfig));
29
- });
25
+ const cachedConfig = configCache.current[storageKey];
26
+ const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
27
+ configCache.current[storageKey] = mergeDeep(configCache.current[storageKey], newConfig);
30
28
  }, [getCollectionFromStorage]);
31
29
 
32
30
  const [recentlyVisitedPaths, _setRecentlyVisitedPaths] = useState<string[]>([]);
@@ -8,8 +8,13 @@ import { ModeController } from "./index";
8
8
  */
9
9
  export function useBuildModeController(): ModeController {
10
10
 
11
- const prefersDarkModeQuery = typeof window !== "undefined" &&
12
- window.matchMedia("(prefers-color-scheme: dark)");
11
+ const prefersDarkModeQuery = useCallback((): boolean => {
12
+ if (typeof window === "undefined")
13
+ return false;
14
+ const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
15
+ return mediaQueryList.matches;
16
+ }, []);
17
+
13
18
  const prefersDarkModeStorage: boolean | null = localStorage.getItem("prefers-dark-mode") != null ? localStorage.getItem("prefers-dark-mode") === "true" : null;
14
19
  const prefersDarkMode = prefersDarkModeStorage ?? prefersDarkModeQuery;
15
20
  const [mode, setMode] = useState<"light" | "dark">(prefersDarkMode ? "dark" : "light");
@@ -23,7 +28,7 @@ export function useBuildModeController(): ModeController {
23
28
  const setDarkMode = useCallback(() => {
24
29
  setMode("dark");
25
30
  setDocumentMode("dark");
26
- }, [prefersDarkModeQuery]);
31
+ }, []);
27
32
 
28
33
  const setLightMode = useCallback(() => {
29
34
  setMode("light");
@@ -37,14 +42,15 @@ export function useBuildModeController(): ModeController {
37
42
 
38
43
  const toggleMode = useCallback(() => {
39
44
 
45
+ const prefersDarkModeQueryResult = prefersDarkModeQuery();
40
46
  if (mode === "light") {
41
- if (!prefersDarkModeQuery)
47
+ if (!prefersDarkModeQueryResult)
42
48
  localStorage.setItem("prefers-dark-mode", "true");
43
49
  else
44
50
  localStorage.removeItem("prefers-dark-mode");
45
51
  setDarkMode();
46
52
  } else {
47
- if (prefersDarkModeQuery)
53
+ if (prefersDarkModeQueryResult)
48
54
  localStorage.setItem("prefers-dark-mode", "false");
49
55
  else
50
56
  localStorage.removeItem("prefers-dark-mode");
@@ -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,6 +187,8 @@ 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
193
 
168
194
  const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
@@ -172,16 +198,23 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
172
198
  ]
173
199
  );
174
200
 
175
- if (
176
- !equal(collectionsRef.current, resolvedCollections) ||
177
- !equal(viewsRef.current, resolvedViews) ||
178
- !equal(adminViewsRef.current, resolvedAdminViews) ||
179
- !equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder))
180
- ) {
201
+ let shouldUpdateTopLevelNav = false;
202
+ if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
181
203
  collectionsRef.current = resolvedCollections;
204
+ shouldUpdateTopLevelNav = true;
205
+ }
206
+ if (!equal(viewsRef.current, resolvedViews)) {
182
207
  viewsRef.current = resolvedViews;
208
+ shouldUpdateTopLevelNav = true;
209
+ }
210
+ if (!equal(adminViewsRef.current, resolvedAdminViews)) {
183
211
  adminViewsRef.current = resolvedAdminViews;
184
- setTopLevelNavigation(computeTopNavigation(resolvedCollections ?? [], resolvedViews, resolvedAdminViews, viewsOrder));
212
+ shouldUpdateTopLevelNav = true;
213
+ }
214
+
215
+ const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
216
+ if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
217
+ setTopLevelNavigation(computedTopLevelNav);
185
218
  }
186
219
  } catch (e) {
187
220
  console.error(e);
@@ -208,7 +241,6 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
208
241
 
209
242
  const getCollection = useCallback((
210
243
  idOrPath: string,
211
- entityId?: string,
212
244
  includeUserOverride = false
213
245
  ): EC | undefined => {
214
246
  const collections = collectionsRef.current;
@@ -218,7 +250,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
218
250
  const baseCollection = getCollectionByPathOrId(removeInitialAndTrailingSlashes(idOrPath), collections);
219
251
 
220
252
  const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(idOrPath) : undefined;
221
- const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride) : undefined;
253
+ const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride ?? {}) : undefined;
222
254
 
223
255
  let result: Partial<EntityCollection> | undefined = overriddenCollection;
224
256
 
@@ -243,9 +275,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
243
275
  const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
244
276
 
245
277
  const collections = collectionsRef.current;
278
+ if (collections === undefined)
279
+ throw Error("getCollectionFromPaths: Collections have not been initialised yet");
246
280
  let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
247
- if (!currentCollections)
248
- throw Error("Collections have not been initialised yet");
249
281
 
250
282
  for (let i = 0; i < pathSegments.length; i++) {
251
283
  const pathSegment = pathSegments[i];
@@ -264,9 +296,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
264
296
  const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
265
297
 
266
298
  const collections = collectionsRef.current;
299
+ if (collections === undefined)
300
+ throw Error("getCollectionFromIds: Collections have not been initialised yet");
267
301
  let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
268
- if (!currentCollections)
269
- throw Error("Collections have not been initialised yet");
270
302
 
271
303
  for (let i = 0; i < ids.length; i++) {
272
304
  const id = ids[i];
@@ -302,9 +334,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
302
334
  []);
303
335
 
304
336
  const resolveAliasesFrom = useCallback((path: string): string => {
305
- const collections = collectionsRef.current;
306
- if (!collections)
307
- throw Error("Collections have not been initialised yet");
337
+ const collections = collectionsRef.current ?? [];
308
338
  return resolveCollectionPathIds(path, collections);
309
339
  }, []);
310
340
 
@@ -407,7 +437,7 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
407
437
  collectionPermissions: PermissionsBuilder | undefined,
408
438
  authController: AuthController,
409
439
  dataSource: DataSourceDelegate,
410
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
440
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
411
441
  let resolvedCollections: EntityCollection[] = [];
412
442
  if (typeof collections === "function") {
413
443
  resolvedCollections = await collections({
@@ -419,14 +449,14 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
419
449
  resolvedCollections = collections;
420
450
  }
421
451
 
422
- resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
423
-
424
- resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
425
-
426
452
  if (injectCollections) {
427
453
  resolvedCollections = injectCollections(resolvedCollections ?? []);
428
454
  }
429
455
 
456
+ resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
457
+
458
+ resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
459
+
430
460
  return resolvedCollections;
431
461
  }
432
462
 
@@ -451,3 +481,29 @@ function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
451
481
  }
452
482
  return trimmed ?? "Views";
453
483
  }
484
+
485
+ function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
486
+ if (a.length !== b.length) {
487
+ return false;
488
+ }
489
+ const aCopy = [...a];
490
+ const bCopy = [...b];
491
+ const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
492
+ const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
493
+ return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
494
+ }
495
+
496
+ function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
497
+ const {
498
+ subcollections: subcollectionsA,
499
+ ...restA
500
+ } = a;
501
+ const {
502
+ subcollections: subcollectionsB,
503
+ ...restB
504
+ } = b;
505
+ if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
506
+ return false;
507
+ }
508
+ return equal(restA, restB);
509
+ }
@@ -9,7 +9,7 @@ export type AccessResponse = {
9
9
  message?: string;
10
10
  }
11
11
 
12
- async function makeRequest(authController: AuthController, pluginKeys: string | undefined) {
12
+ async function makeRequest(authController: AuthController, pluginKeys: string[] | undefined) {
13
13
  const firebaseToken = await authController.getAuthToken();
14
14
  return fetch(DEFAULT_SERVER + "/access_log",
15
15
  {
@@ -30,12 +30,12 @@ export function useProjectLog(authController: AuthController,
30
30
  plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
31
31
  const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
32
32
  const accessedUserRef = useRef<string | null>(null);
33
- const pluginKeys = plugins?.map(plugin => plugin.key).join(",");
33
+ const pluginKeys = plugins?.map(plugin => plugin.key);
34
34
  useEffect(() => {
35
35
  if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
36
36
  makeRequest(authController, pluginKeys).then(setAccessResponse);
37
37
  accessedUserRef.current = authController.user.uid;
38
38
  }
39
- }, [authController]);
39
+ }, [authController, pluginKeys]);
40
40
  return accessResponse;
41
41
  }
@@ -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,36 +1,31 @@
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
12
  * @param dataSourceDelegate
15
13
  */
16
- export function useValidateAuthenticator<UserType extends User = User, Controller extends AuthController<UserType> = AuthController<UserType>>({
17
- disabled,
18
- authController,
19
- authenticator,
20
- getAppCheckToken,
21
- appCheckForceRefresh = false,
22
- storageSource,
23
- dataSourceDelegate
24
- }:
25
- {
26
- disabled?: boolean,
27
- authController: Controller,
28
- authenticator?: 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,
@@ -76,21 +71,6 @@ 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
-
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
74
 
95
75
  if (authenticator instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
96
76
  setAuthLoading(true);
@@ -120,7 +100,7 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
120
100
  setAuthVerified(true);
121
101
  }
122
102
 
123
- }, [disabled, authController, authenticator, getAppCheckToken, appCheckForceRefresh, dataSourceDelegate, storageSource]);
103
+ }, [disabled, authController, authenticator, dataSourceDelegate, storageSource]);
124
104
 
125
105
  useEffect(() => {
126
106
  checkAuthentication();
@@ -5,12 +5,10 @@ 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,
14
12
  ListenCollectionProps,
15
13
  ListenEntityProps,
16
14
  NavigationController,
@@ -207,13 +205,10 @@ export function useBuildDataSource({
207
205
 
208
206
  const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties;
209
207
 
210
- const firestoreValues = cmsToDelegateModel(
208
+ const firestoreValues = delegate.cmsToDelegateModel(
211
209
  values,
212
- delegate.buildReference,
213
- delegate.buildGeoPoint,
214
- delegate.buildDate,
215
- delegate.buildDeleteFieldValue
216
210
  );
211
+
217
212
  const updatedFirestoreValues: EntityValues<M> = properties
218
213
  ? updateDateAutoValues(
219
214
  {
@@ -316,48 +311,48 @@ export function useBuildDataSource({
316
311
  sortBy
317
312
  }
318
313
  )
319
- }, [delegate.isFilterCombinationValid])
314
+ }, [delegate.isFilterCombinationValid]),
320
315
 
321
316
  };
322
317
 
323
318
  }
324
319
 
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
- }
320
+ // /**
321
+ // * Recursive function that converts Firestore data types into CMS or plain
322
+ // * JS types.
323
+ // * FireCMS uses Javascript dates internally instead of Firestore timestamps.
324
+ // * This makes it easier to interact with the rest of the libraries and
325
+ // * bindings.
326
+ // * Also, Firestore references are replaced with {@link EntityReference}
327
+ // * @param data
328
+ // * @param buildReference
329
+ // * @param buildGeoPoint
330
+ // * @param buildDate
331
+ // * @param buildDelete
332
+ // * @group Firestore
333
+ // */
334
+ // export function cmsToDelegateModel(data: any,
335
+ // buildReference: (reference: EntityReference) => any,
336
+ // buildGeoPoint: (geoPoint: GeoPoint) => any,
337
+ // buildDate: (date: Date) => any,
338
+ // buildDelete: () => any
339
+ // ): any {
340
+ // if (data === undefined) {
341
+ // return buildDelete();
342
+ // } else if (data === null) {
343
+ // return null;
344
+ // } else if (Array.isArray(data)) {
345
+ // return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
346
+ // } else if (data.isEntityReference && data.isEntityReference()) {
347
+ // return buildReference(data);
348
+ // } else if (data instanceof GeoPoint) {
349
+ // return buildGeoPoint(data);
350
+ // } else if (data instanceof Date) {
351
+ // return buildDate(data);
352
+ // } else if (data && typeof data === "object") {
353
+ // return Object.entries(data)
354
+ // .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
355
+ // .reduce((a, b) => ({ ...a, ...b }), {});
356
+ // }
357
+ // return data;
358
+ // }