@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.
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityPreview.d.ts +3 -2
- package/dist/components/FireCMSAppBar.d.ts +4 -2
- package/dist/components/ReferenceWidget.d.ts +3 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -6
- package/dist/components/VirtualTable/types.d.ts +3 -3
- package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
- package/dist/components/common/index.d.ts +1 -0
- package/dist/components/common/table_height.d.ts +5 -0
- package/dist/components/common/types.d.ts +4 -6
- package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/core/Drawer.d.ts +5 -12
- package/dist/core/DrawerNavigationItem.d.ts +9 -0
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/core/Scaffold.d.ts +8 -12
- package/dist/core/index.d.ts +3 -4
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/hooks/data/save.d.ts +1 -2
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useValidateAuthenticator.d.ts +2 -6
- package/dist/index.es.js +10087 -9914
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +1 -16
- package/dist/preview/components/ReferencePreview.d.ts +2 -1
- package/dist/types/collections.d.ts +13 -2
- package/dist/types/datasource.d.ts +2 -5
- package/dist/types/entities.d.ts +5 -1
- package/dist/types/entity_actions.d.ts +14 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/types/navigation.d.ts +2 -2
- package/dist/types/plugins.d.ts +3 -1
- package/dist/types/properties.d.ts +10 -2
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icon_synonyms.d.ts +1 -97
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/objects.d.ts +1 -1
- package/dist/util/resolutions.d.ts +13 -13
- package/package.json +139 -119
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +19 -18
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +38 -34
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -1
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +2 -2
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -4
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +47 -25
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityPreview.tsx +9 -7
- package/src/components/EntityView.tsx +5 -5
- package/src/components/FireCMSAppBar.tsx +32 -15
- package/src/components/HomePage/DefaultHomePage.tsx +1 -1
- package/src/components/HomePage/NavigationCard.tsx +2 -2
- package/src/components/HomePage/SmallNavigationCard.tsx +5 -5
- package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +1 -0
- package/src/components/ReferenceWidget.tsx +21 -11
- package/src/components/SearchIconsView.tsx +5 -5
- package/src/components/SelectableTable/SelectableTable.tsx +5 -3
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +36 -29
- package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -6
- package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
- package/src/components/VirtualTable/types.tsx +2 -3
- package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
- package/src/components/common/index.ts +1 -0
- package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
- package/src/components/common/types.tsx +4 -6
- package/src/components/common/useColumnsIds.tsx +10 -2
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
- package/src/components/index.tsx +1 -0
- package/src/core/Drawer.tsx +16 -68
- package/src/core/DrawerNavigationItem.tsx +62 -0
- package/src/core/EntityEditView.tsx +7 -8
- package/src/core/EntitySidePanel.tsx +1 -1
- package/src/core/FireCMS.tsx +37 -42
- package/src/core/NavigationRoutes.tsx +3 -4
- package/src/core/Scaffold.tsx +80 -66
- package/src/core/field_configs.tsx +2 -3
- package/src/core/index.tsx +3 -4
- package/src/form/EntityForm.tsx +29 -21
- package/src/form/PropertyFieldBinding.tsx +0 -2
- package/src/form/components/StorageItemPreview.tsx +2 -2
- package/src/form/components/StorageUploadProgress.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -4
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +15 -13
- package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -4
- package/src/hooks/data/save.ts +3 -1
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
- package/src/hooks/useBuildModeController.tsx +11 -5
- package/src/hooks/useBuildNavigationController.tsx +77 -21
- package/src/hooks/useProjectLog.tsx +3 -3
- package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
- package/src/hooks/useValidateAuthenticator.tsx +17 -37
- package/src/internal/useBuildDataSource.ts +42 -47
- package/src/internal/useBuildSideEntityController.tsx +86 -22
- package/src/preview/PropertyPreview.tsx +4 -14
- package/src/preview/components/BooleanPreview.tsx +2 -2
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +21 -22
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
- package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
- package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
- package/src/types/collections.ts +19 -3
- package/src/types/datasource.ts +7 -4
- package/src/types/entities.ts +9 -1
- package/src/types/entity_actions.tsx +16 -3
- package/src/types/index.ts +0 -1
- package/src/types/navigation.ts +2 -3
- package/src/types/plugins.tsx +4 -3
- package/src/types/properties.ts +15 -4
- package/src/types/storage.ts +1 -1
- package/src/util/entities.ts +1 -1
- package/src/util/enums.ts +1 -1
- package/src/util/icon_list.ts +2 -2
- package/src/util/icon_synonyms.ts +3 -99
- package/src/util/navigation_utils.ts +6 -6
- package/src/util/objects.ts +8 -21
- package/src/util/resolutions.ts +32 -31
- package/dist/components/VirtualTable/common.d.ts +0 -2
- package/dist/core/SideEntityView.d.ts +0 -7
- package/dist/internal/useBuildCustomizationController.d.ts +0 -2
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/dist/types/appcheck.d.ts +0 -26
- package/src/core/SideEntityView.tsx +0 -38
- package/src/internal/useBuildCustomizationController.tsx +0 -5
- package/src/internal/useLocaleConfig.tsx +0 -18
- 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
|
-
|
|
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={
|
|
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={
|
|
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={
|
|
271
|
+
className={cls(focusedMixin, "rounded-md")}
|
|
272
272
|
style={{
|
|
273
273
|
...provided.draggableProps.style
|
|
274
274
|
}}
|
package/src/hooks/data/save.ts
CHANGED
|
@@ -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>({
|
package/src/hooks/index.tsx
CHANGED
|
@@ -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
|
|
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
|
-
}, [
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 =
|
|
12
|
-
|
|
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
|
-
}, [
|
|
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 (!
|
|
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 (
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
|
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 {
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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,
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
export function cmsToDelegateModel(data: any,
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
): any {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
+
// }
|