@webiny/app-aco 0.0.0-unstable.7be00a75a9 → 0.0.0-unstable.7c2def32a7
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/app.js +5 -1
- package/app.js.map +1 -1
- package/components/Actions/DeleteFolder/DeleteFolder.js +1 -1
- package/components/Actions/DeleteFolder/DeleteFolder.js.map +1 -1
- package/components/AdvancedSearch/AdvancedSearch.d.ts +2 -2
- package/components/AdvancedSearch/AdvancedSearch.js +1 -1
- package/components/AdvancedSearch/AdvancedSearch.js.map +1 -1
- package/components/AdvancedSearch/QueryBuilderDrawer/QueryBuilder/components/OperationSelector.js +2 -2
- package/components/AdvancedSearch/QueryBuilderDrawer/QueryBuilder/components/OperationSelector.js.map +1 -1
- package/components/AdvancedSearch/QueryBuilderDrawer/QueryBuilderDrawer.d.ts +2 -2
- package/components/AdvancedSearch/SelectedFilter/SelectedFilter.js +1 -1
- package/components/AdvancedSearch/SelectedFilter/SelectedFilter.js.map +1 -1
- package/components/AdvancedSearch/domain/Filter.d.ts +1 -1
- package/components/AdvancedSearch/domain/FilterRepositoryFactory.d.ts +2 -2
- package/components/AdvancedSearch/domain/FilterRepositoryFactory.js.map +1 -1
- package/components/AdvancedSearch/gateways/FiltersGraphQLGateway.d.ts +2 -2
- package/components/AdvancedSearch/gateways/FiltersGraphQLGateway.js +9 -10
- package/components/AdvancedSearch/gateways/FiltersGraphQLGateway.js.map +1 -1
- package/components/AdvancedSearch/gateways/filters.gql.d.ts +5 -5
- package/components/AdvancedSearch/gateways/filters.gql.js +5 -6
- package/components/AdvancedSearch/gateways/filters.gql.js.map +1 -1
- package/components/AdvancedSearch/useFilterRepository.js +7 -2
- package/components/AdvancedSearch/useFilterRepository.js.map +1 -1
- package/components/FolderTree/List/List.js +14 -6
- package/components/FolderTree/List/List.js.map +1 -1
- package/components/Table/cells/CellAuthor.d.ts +2 -0
- package/components/Table/cells/CellAuthor.js +10 -0
- package/components/Table/cells/CellAuthor.js.map +1 -0
- package/components/Table/cells/CellCreated.d.ts +2 -0
- package/components/Table/cells/CellCreated.js +13 -0
- package/components/Table/cells/CellCreated.js.map +1 -0
- package/components/Table/cells/CellModified.d.ts +2 -0
- package/components/Table/cells/CellModified.js +13 -0
- package/components/Table/cells/CellModified.js.map +1 -0
- package/components/Table/cells/index.d.ts +3 -0
- package/components/Table/cells/index.js +3 -0
- package/components/Table/components/Table/TableInner.d.ts +2 -2
- package/components/Table/createTableData.d.ts +7 -5
- package/components/Table/createTableData.js +22 -7
- package/components/Table/createTableData.js.map +1 -1
- package/components/Table/index.d.ts +2 -2
- package/components/Table/index.js +2 -2
- package/components/index.d.ts +0 -1
- package/components/index.js +0 -1
- package/config/AcoConfig.d.ts +8 -12
- package/config/advanced-search/FieldRenderer.d.ts +3 -1
- package/config/folder/Action.d.ts +1 -1
- package/config/folder/index.d.ts +3 -2
- package/config/index.d.ts +0 -1
- package/config/index.js +0 -1
- package/config/record/Action.d.ts +1 -1
- package/config/record/index.d.ts +1 -1
- package/config/table/Column.d.ts +8 -39
- package/config/table/Column.js +4 -67
- package/config/table/Column.js.map +1 -1
- package/config/table/Sorting.d.ts +2 -11
- package/config/table/Sorting.js +1 -28
- package/config/table/index.d.ts +5 -9
- package/contexts/index.d.ts +0 -3
- package/contexts/index.js +0 -3
- package/dialogs/DialogSetPermissions/UsersTeamsSelection/ListItemMeta.js +5 -0
- package/dialogs/DialogSetPermissions/UsersTeamsSelection/ListItemMeta.js.map +1 -1
- package/dialogs/DialogSetPermissions/graphql.d.ts +1 -1
- package/dialogs/DialogSetPermissions/graphql.js +1 -2
- package/dialogs/DialogSetPermissions/graphql.js.map +1 -1
- package/dialogs/useConfirmMoveFolderDialog.d.ts +1 -0
- package/dialogs/useConfirmMoveFolderDialog.js +3 -2
- package/dialogs/useConfirmMoveFolderDialog.js.map +1 -1
- package/dialogs/useCreateDialog.js +5 -9
- package/dialogs/useCreateDialog.js.map +1 -1
- package/dialogs/useEditDialog.js +2 -2
- package/dialogs/useEditDialog.js.map +1 -1
- package/dialogs/useSetPermissionsDialog.js +16 -4
- package/dialogs/useSetPermissionsDialog.js.map +1 -1
- package/domain/folder/Folder.d.ts +2 -1
- package/domain/folder/Folder.js.map +1 -1
- package/features/folders/cache/FoldersCacheFactory.d.ts +3 -1
- package/features/folders/cache/FoldersCacheFactory.js +5 -0
- package/features/folders/cache/FoldersCacheFactory.js.map +1 -1
- package/features/folders/cache/updateDescendantPermissions.d.ts +12 -0
- package/features/folders/cache/updateDescendantPermissions.js +27 -0
- package/features/folders/cache/updateDescendantPermissions.js.map +1 -0
- package/features/folders/createFolder/CreateFolderGqlGateway.d.ts +3 -3
- package/features/folders/createFolder/CreateFolderGqlGateway.js +15 -16
- package/features/folders/createFolder/CreateFolderGqlGateway.js.map +1 -1
- package/features/folders/deleteFolder/DeleteFolderGqlGateway.d.ts +3 -3
- package/features/folders/deleteFolder/DeleteFolderGqlGateway.js +5 -6
- package/features/folders/deleteFolder/DeleteFolderGqlGateway.js.map +1 -1
- package/features/folders/folderEvents/AddCreatedFolderToCache.d.ts +22 -0
- package/features/folders/folderEvents/AddCreatedFolderToCache.js +24 -0
- package/features/folders/folderEvents/AddCreatedFolderToCache.js.map +1 -0
- package/features/folders/folderEvents/FolderCreatedEvent.d.ts +14 -0
- package/features/folders/folderEvents/FolderCreatedEvent.js +13 -0
- package/features/folders/folderEvents/FolderCreatedEvent.js.map +1 -0
- package/features/folders/folderEvents/FolderDeletedEvent.d.ts +9 -0
- package/features/folders/folderEvents/FolderDeletedEvent.js +13 -0
- package/features/folders/folderEvents/FolderDeletedEvent.js.map +1 -0
- package/features/folders/folderEvents/FolderEvents.test.d.ts +1 -0
- package/features/folders/folderEvents/FolderEvents.test.js +238 -0
- package/features/folders/folderEvents/FolderEvents.test.js.map +1 -0
- package/features/folders/folderEvents/FolderUpdatedEvent.d.ts +12 -0
- package/features/folders/folderEvents/FolderUpdatedEvent.js +13 -0
- package/features/folders/folderEvents/FolderUpdatedEvent.js.map +1 -0
- package/features/folders/folderEvents/FolderWebsocketMessages.d.ts +15 -0
- package/features/folders/folderEvents/FolderWebsocketMessages.js +48 -0
- package/features/folders/folderEvents/FolderWebsocketMessages.js.map +1 -0
- package/features/folders/folderEvents/RefreshUpdatedFolderInCache.d.ts +19 -0
- package/features/folders/folderEvents/RefreshUpdatedFolderInCache.js +27 -0
- package/features/folders/folderEvents/RefreshUpdatedFolderInCache.js.map +1 -0
- package/features/folders/folderEvents/RemoveDeletedFolderFromCache.d.ts +13 -0
- package/features/folders/folderEvents/RemoveDeletedFolderFromCache.js +15 -0
- package/features/folders/folderEvents/RemoveDeletedFolderFromCache.js.map +1 -0
- package/features/folders/folderEvents/abstractions.d.ts +28 -0
- package/features/folders/folderEvents/abstractions.js +7 -0
- package/features/folders/folderEvents/abstractions.js.map +1 -0
- package/features/folders/folderEvents/feature.d.ts +9 -0
- package/features/folders/folderEvents/feature.js +21 -0
- package/features/folders/folderEvents/feature.js.map +1 -0
- package/features/folders/folderEvents/index.d.ts +5 -0
- package/features/folders/folderEvents/index.js +5 -0
- package/features/folders/folderModelProvider/FolderModelProvider.js +30 -31
- package/features/folders/folderModelProvider/FolderModelProvider.js.map +1 -1
- package/features/folders/folderModelProvider/GetFolderModelGqlGateway.d.ts +3 -3
- package/features/folders/folderModelProvider/GetFolderModelGqlGateway.js +5 -7
- package/features/folders/folderModelProvider/GetFolderModelGqlGateway.js.map +1 -1
- package/features/folders/getFolder/GetFolderGqlGateway.d.ts +3 -3
- package/features/folders/getFolder/GetFolderGqlGateway.js +5 -7
- package/features/folders/getFolder/GetFolderGqlGateway.js.map +1 -1
- package/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCase.d.ts +4 -3
- package/features/folders/getFolderExtensionsFields/useFolderExtensionsFields.d.ts +4 -3
- package/features/folders/getFolderLevelPermission/GetFolderLevelPermission.test.js +12 -16
- package/features/folders/getFolderLevelPermission/GetFolderLevelPermission.test.js.map +1 -1
- package/features/folders/getFolderLevelPermission/decorators/GetFolderLevelPermissionWithFlpDecorator.d.ts +3 -3
- package/features/folders/getFolderLevelPermission/decorators/GetFolderLevelPermissionWithFlpDecorator.js +5 -5
- package/features/folders/getFolderLevelPermission/decorators/GetFolderLevelPermissionWithFlpDecorator.js.map +1 -1
- package/features/folders/listFolders/ListFoldersGqlGateway.d.ts +1 -1
- package/features/folders/listFolders/ListFoldersGqlGateway.js +1 -2
- package/features/folders/listFolders/ListFoldersGqlGateway.js.map +1 -1
- package/features/folders/listFolders/useListFolders.d.ts +1 -1
- package/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.d.ts +3 -3
- package/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.js +5 -7
- package/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.js.map +1 -1
- package/features/folders/listFoldersByParentIds/useListFoldersByParentIds.d.ts +2 -2
- package/features/folders/loadFolderHierarchy/useLoadFolderHierarchy.d.ts +2 -2
- package/features/folders/updateFolder/UpdateFolderGqlGateway.d.ts +3 -3
- package/features/folders/updateFolder/UpdateFolderGqlGateway.js +6 -7
- package/features/folders/updateFolder/UpdateFolderGqlGateway.js.map +1 -1
- package/features/folders/updateFolder/decorators/RepositoryWithPermissionsChange.d.ts +1 -4
- package/features/folders/updateFolder/decorators/RepositoryWithPermissionsChange.js +3 -30
- package/features/folders/updateFolder/decorators/RepositoryWithPermissionsChange.js.map +1 -1
- package/features/folders/updateFolder/decorators/{UseCaseWithoutInheritedPermissions.d.ts → UseCaseWithoutReadOnlyPermissions.d.ts} +2 -2
- package/features/folders/updateFolder/decorators/{UseCaseWithoutInheritedPermissions.js → UseCaseWithoutReadOnlyPermissions.js} +6 -6
- package/features/folders/updateFolder/decorators/UseCaseWithoutReadOnlyPermissions.js.map +1 -0
- package/features/folders/updateFolder/feature.js +2 -2
- package/features/folders/updateFolder/feature.js.map +1 -1
- package/hooks/index.d.ts +0 -6
- package/hooks/index.js +0 -6
- package/index.d.ts +3 -2
- package/index.js +2 -2
- package/package.json +23 -29
- package/presentation/folderTree/FolderTree.js +13 -5
- package/presentation/folderTree/FolderTree.js.map +1 -1
- package/presentation/folderTree/FolderTreePresenter.d.ts +5 -1
- package/presentation/folderTree/FolderTreePresenter.js +49 -31
- package/presentation/folderTree/FolderTreePresenter.js.map +1 -1
- package/presentation/folderTree/FolderTreePresenter.test.js +111 -7
- package/presentation/folderTree/FolderTreePresenter.test.js.map +1 -1
- package/table.types.d.ts +2 -9
- package/types.d.ts +5 -26
- package/types.js.map +1 -1
- package/components/Extensions/Extensions.d.ts +0 -2
- package/components/Extensions/Extensions.js +0 -30
- package/components/Extensions/Extensions.js.map +0 -1
- package/components/Extensions/index.d.ts +0 -1
- package/components/Extensions/index.js +0 -1
- package/config/folder/createFolderFieldDecoratorFactory.d.ts +0 -13
- package/config/folder/createFolderFieldDecoratorFactory.js +0 -17
- package/config/folder/createFolderFieldDecoratorFactory.js.map +0 -1
- package/config/table/Sorting.js.map +0 -1
- package/contexts/acoList.d.ts +0 -49
- package/contexts/acoList.js +0 -303
- package/contexts/acoList.js.map +0 -1
- package/contexts/app.d.ts +0 -33
- package/contexts/app.js +0 -47
- package/contexts/app.js.map +0 -1
- package/contexts/folders.d.ts +0 -12
- package/contexts/folders.js +0 -32
- package/contexts/folders.js.map +0 -1
- package/contexts/navigateFolder.d.ts +0 -17
- package/contexts/navigateFolder.js +0 -54
- package/contexts/navigateFolder.js.map +0 -1
- package/contexts/records.d.ts +0 -31
- package/contexts/records.js +0 -316
- package/contexts/records.js.map +0 -1
- package/exports/admin/aco.d.ts +0 -2
- package/exports/admin/aco.js +0 -2
- package/features/folders/updateFolder/decorators/UseCaseWithoutInheritedPermissions.js.map +0 -1
- package/graphql/app.gql.d.ts +0 -17
- package/graphql/app.gql.js +0 -22
- package/graphql/app.gql.js.map +0 -1
- package/graphql/records/common.d.ts +0 -4
- package/graphql/records/common.js +0 -57
- package/graphql/records/common.js.map +0 -1
- package/graphql/records/getRecord.d.ts +0 -2
- package/graphql/records/getRecord.js +0 -5
- package/graphql/records/getRecord.js.map +0 -1
- package/graphql/records/listRecords.d.ts +0 -2
- package/graphql/records/listRecords.js +0 -26
- package/graphql/records/listRecords.js.map +0 -1
- package/graphql/records/moveRecord.d.ts +0 -2
- package/graphql/records/moveRecord.js +0 -16
- package/graphql/records/moveRecord.js.map +0 -1
- package/graphql/records/types.d.ts +0 -108
- package/graphql/records/types.js +0 -0
- package/graphql/records.gql.d.ts +0 -3
- package/graphql/records.gql.js +0 -3
- package/handlers.d.ts +0 -23
- package/handlers.js +0 -22
- package/handlers.js.map +0 -1
- package/hooks/useAcoApp.d.ts +0 -2
- package/hooks/useAcoApp.js +0 -16
- package/hooks/useAcoApp.js.map +0 -1
- package/hooks/useAcoList.d.ts +0 -3
- package/hooks/useAcoList.js +0 -10
- package/hooks/useAcoList.js.map +0 -1
- package/hooks/useFolderModel.d.ts +0 -2
- package/hooks/useFolderModel.js +0 -16
- package/hooks/useFolderModel.js.map +0 -1
- package/hooks/useFoldersType.d.ts +0 -1
- package/hooks/useFoldersType.js +0 -12
- package/hooks/useFoldersType.js.map +0 -1
- package/hooks/useNavigateFolder.d.ts +0 -2
- package/hooks/useNavigateFolder.js +0 -10
- package/hooks/useNavigateFolder.js.map +0 -1
- package/hooks/useRecords.d.ts +0 -62
- package/hooks/useRecords.js +0 -64
- package/hooks/useRecords.js.map +0 -1
- package/hooks/useTags.d.ts +0 -14
- package/hooks/useTags.js +0 -27
- package/hooks/useTags.js.map +0 -1
- package/sorting.d.ts +0 -9
- package/sorting.js +0 -31
- package/sorting.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { makeAutoObservable, reaction, runInAction } from "mobx";
|
|
2
|
-
import
|
|
2
|
+
import { StringFormatter } from "@webiny/app-admin/features/stringFormatter/abstractions.js";
|
|
3
3
|
import { FolderTreePresenter } from "./abstractions.js";
|
|
4
4
|
import { ROOT_FOLDER } from "../../constants.js";
|
|
5
5
|
import { FoldersCache, FoldersContext } from "../../features/folders/abstractions.js";
|
|
@@ -13,7 +13,7 @@ import { GetFolderLevelPermissionUseCase } from "../../features/folders/getFolde
|
|
|
13
13
|
import { FormModelFactory } from "@webiny/app-admin/features/formModel/abstractions.js";
|
|
14
14
|
import { FolderDtoMapper } from "../../domain/folder/FolderDtoMapper.js";
|
|
15
15
|
class FolderTreePresenterImpl {
|
|
16
|
-
constructor(foldersContext, foldersCache, listFoldersUseCase, listFoldersByParentIdsUseCase, createFolderUseCase, updateFolderUseCase, deleteFolderUseCase, getFolderAncestorsUseCase, getFolderLevelPermissionUseCase, formModelFactory){
|
|
16
|
+
constructor(foldersContext, foldersCache, listFoldersUseCase, listFoldersByParentIdsUseCase, createFolderUseCase, updateFolderUseCase, deleteFolderUseCase, getFolderAncestorsUseCase, getFolderLevelPermissionUseCase, formModelFactory, stringFormatter){
|
|
17
17
|
this.foldersContext = foldersContext;
|
|
18
18
|
this.foldersCache = foldersCache;
|
|
19
19
|
this.listFoldersUseCase = listFoldersUseCase;
|
|
@@ -24,6 +24,7 @@ class FolderTreePresenterImpl {
|
|
|
24
24
|
this.getFolderAncestorsUseCase = getFolderAncestorsUseCase;
|
|
25
25
|
this.getFolderLevelPermissionUseCase = getFolderLevelPermissionUseCase;
|
|
26
26
|
this.formModelFactory = formModelFactory;
|
|
27
|
+
this.stringFormatter = stringFormatter;
|
|
27
28
|
this.currentFolderId = null;
|
|
28
29
|
this.loading = false;
|
|
29
30
|
this._loadingNodeIds = [];
|
|
@@ -33,14 +34,32 @@ class FolderTreePresenterImpl {
|
|
|
33
34
|
};
|
|
34
35
|
this.callbacks = new Set();
|
|
35
36
|
this.disposed = false;
|
|
37
|
+
this.previousFolders = [];
|
|
36
38
|
makeAutoObservable(this, {
|
|
37
|
-
callbacks: false
|
|
39
|
+
callbacks: false,
|
|
40
|
+
previousFolders: false
|
|
38
41
|
}, {
|
|
39
42
|
autoBind: true
|
|
40
43
|
});
|
|
41
44
|
reaction(()=>this.currentFolderId, (folderId)=>{
|
|
42
45
|
if (!this.disposed) this.callbacks.forEach((cb)=>cb(folderId));
|
|
43
46
|
});
|
|
47
|
+
reaction(()=>({
|
|
48
|
+
folders: this.foldersCache.getItems(),
|
|
49
|
+
currentFolderId: this.currentFolderId
|
|
50
|
+
}), ({ folders, currentFolderId })=>{
|
|
51
|
+
if (null !== currentFolderId) {
|
|
52
|
+
const stillExists = folders.some((f)=>f.id === currentFolderId);
|
|
53
|
+
const isTransientReloadClear = 0 === folders.length && this.loading;
|
|
54
|
+
if (!stillExists && !isTransientReloadClear) {
|
|
55
|
+
const resolvedId = this.resolveValidFolderId(currentFolderId, this.previousFolders);
|
|
56
|
+
this.selectFolder(resolvedId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.previousFolders = folders;
|
|
60
|
+
}, {
|
|
61
|
+
fireImmediately: true
|
|
62
|
+
});
|
|
44
63
|
this.loadFolders();
|
|
45
64
|
}
|
|
46
65
|
get vm() {
|
|
@@ -66,9 +85,10 @@ class FolderTreePresenterImpl {
|
|
|
66
85
|
};
|
|
67
86
|
}
|
|
68
87
|
selectFolder(folderId) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
88
|
+
const normalized = folderId === ROOT_FOLDER ? null : folderId;
|
|
89
|
+
this.currentFolderId = normalized;
|
|
90
|
+
if (normalized) this.loadChildFolders([
|
|
91
|
+
normalized
|
|
72
92
|
]);
|
|
73
93
|
}
|
|
74
94
|
onFolderChange(callback) {
|
|
@@ -114,15 +134,10 @@ class FolderTreePresenterImpl {
|
|
|
114
134
|
createFolder(parentFolderId) {
|
|
115
135
|
const form = this.formModelFactory.create({
|
|
116
136
|
fields: (fields)=>({
|
|
117
|
-
title: fields.text().label("Title").required("Title is required"),
|
|
118
|
-
slug: fields.text().label("Slug").required("Slug is required").computedUntilDirty((
|
|
119
|
-
const title =
|
|
120
|
-
return slugify(String(title ?? "")
|
|
121
|
-
replacement: "-",
|
|
122
|
-
lower: true,
|
|
123
|
-
remove: /[*#\?<>_\{\}\[\]+~.()'"!:;@]/g,
|
|
124
|
-
trim: false
|
|
125
|
-
});
|
|
137
|
+
title: fields.text().label("Title").required("Title is required").autoFocus(),
|
|
138
|
+
slug: fields.text().label("Slug").required("Slug is required").computedUntilDirty(({ form })=>{
|
|
139
|
+
const title = form.field("title").getValue();
|
|
140
|
+
return this.stringFormatter.slugify(String(title ?? ""));
|
|
126
141
|
}),
|
|
127
142
|
parentId: fields.text().label("Parent folder").renderer("folderTree")
|
|
128
143
|
}),
|
|
@@ -148,15 +163,10 @@ class FolderTreePresenterImpl {
|
|
|
148
163
|
if (!folder) return;
|
|
149
164
|
const form = this.formModelFactory.create({
|
|
150
165
|
fields: (fields)=>({
|
|
151
|
-
title: fields.text().label("Name").required("Name is required"),
|
|
152
|
-
slug: fields.text().label("Slug").required("Slug is required").computedUntilDirty((
|
|
153
|
-
const title =
|
|
154
|
-
return slugify(String(title ?? "")
|
|
155
|
-
replacement: "-",
|
|
156
|
-
lower: true,
|
|
157
|
-
remove: /[*#\?<>_\{\}\[\]+~.()'"!:;@]/g,
|
|
158
|
-
trim: false
|
|
159
|
-
});
|
|
166
|
+
title: fields.text().label("Name").required("Name is required").autoFocus(),
|
|
167
|
+
slug: fields.text().label("Slug").required("Slug is required").computedUntilDirty(({ form })=>{
|
|
168
|
+
const title = form.field("title").getValue();
|
|
169
|
+
return this.stringFormatter.slugify(String(title ?? ""));
|
|
160
170
|
})
|
|
161
171
|
}),
|
|
162
172
|
layout: (layout)=>[
|
|
@@ -199,7 +209,6 @@ class FolderTreePresenterImpl {
|
|
|
199
209
|
permissions: folder?.permissions ?? []
|
|
200
210
|
});
|
|
201
211
|
}
|
|
202
|
-
await this.loadFolders();
|
|
203
212
|
runInAction(()=>{
|
|
204
213
|
this.operation = {
|
|
205
214
|
active: false,
|
|
@@ -209,22 +218,18 @@ class FolderTreePresenterImpl {
|
|
|
209
218
|
return true;
|
|
210
219
|
}
|
|
211
220
|
async deleteFolder(folderId) {
|
|
212
|
-
const folders = this.foldersCache.getItems();
|
|
213
|
-
const folder = folders.find((f)=>f.id === folderId);
|
|
214
|
-
const parentId = folder?.parentId ?? null;
|
|
215
221
|
this.operation = {
|
|
216
222
|
active: true,
|
|
217
223
|
mode: "delete",
|
|
218
224
|
folderId
|
|
219
225
|
};
|
|
220
226
|
await this.deleteFolderUseCase.execute(folderId);
|
|
221
|
-
await this.loadFolders();
|
|
222
227
|
runInAction(()=>{
|
|
228
|
+
this.foldersCache.removeItems((f)=>f.id === folderId);
|
|
223
229
|
this.operation = {
|
|
224
230
|
active: false,
|
|
225
231
|
mode: null
|
|
226
232
|
};
|
|
227
|
-
this.currentFolderId = parentId;
|
|
228
233
|
});
|
|
229
234
|
}
|
|
230
235
|
cancelOperation() {
|
|
@@ -245,6 +250,18 @@ class FolderTreePresenterImpl {
|
|
|
245
250
|
});
|
|
246
251
|
}
|
|
247
252
|
}
|
|
253
|
+
resolveValidFolderId(folderId, previousFolders) {
|
|
254
|
+
let candidateId = folderId;
|
|
255
|
+
const visited = new Set();
|
|
256
|
+
while(null !== candidateId){
|
|
257
|
+
if (visited.has(candidateId)) break;
|
|
258
|
+
visited.add(candidateId);
|
|
259
|
+
if (this.foldersCache.getItem((f)=>f.id === candidateId)) return candidateId;
|
|
260
|
+
const prev = previousFolders.find((f)=>f.id === candidateId);
|
|
261
|
+
candidateId = prev?.parentId ?? null;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
248
265
|
buildTree() {
|
|
249
266
|
const folders = this.foldersCache.getItems();
|
|
250
267
|
return this.buildTreeFromFolders(folders);
|
|
@@ -295,7 +312,8 @@ const FolderTreePresenter_FolderTreePresenter = FolderTreePresenter.createImplem
|
|
|
295
312
|
DeleteFolderUseCase,
|
|
296
313
|
GetFolderAncestorsUseCase,
|
|
297
314
|
GetFolderLevelPermissionUseCase,
|
|
298
|
-
FormModelFactory
|
|
315
|
+
FormModelFactory,
|
|
316
|
+
StringFormatter
|
|
299
317
|
]
|
|
300
318
|
});
|
|
301
319
|
export { FolderTreePresenter_FolderTreePresenter as FolderTreePresenter };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/folderTree/FolderTreePresenter.js","sources":["../../../src/presentation/folderTree/FolderTreePresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction } from \"mobx\";\nimport slugify from \"slugify\";\nimport {\n FolderTreePresenter as Abstraction,\n type IFolderTreeNode,\n type IFolderTreeViewModel,\n type IFolderOperationState\n} from \"./abstractions.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FoldersCache, FoldersContext } from \"~/features/folders/abstractions.js\";\nimport { ListFoldersUseCase } from \"~/features/folders/listFolders/abstractions.js\";\nimport { ListFoldersByParentIdsUseCase } from \"~/features/folders/listFoldersByParentIds/abstractions.js\";\nimport { CreateFolderUseCase } from \"~/features/folders/createFolder/abstractions.js\";\nimport { UpdateFolderUseCase } from \"~/features/folders/updateFolder/abstractions.js\";\nimport { DeleteFolderUseCase } from \"~/features/folders/deleteFolder/abstractions.js\";\nimport { GetFolderAncestorsUseCase } from \"~/features/folders/getFolderAncestors/abstractions.js\";\nimport { GetFolderLevelPermissionUseCase } from \"~/features/folders/getFolderLevelPermission/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { Folder } from \"~/domain/folder/Folder.js\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport { FolderDtoMapper } from \"~/domain/folder/FolderDtoMapper.js\";\n\ntype FolderChangeCallback = (folderId: string | null) => void;\n\nclass FolderTreePresenterImpl implements Abstraction.Interface {\n private currentFolderId: string | null = null;\n private loading = false;\n private _loadingNodeIds: string[] = [];\n private operation: IFolderOperationState = { active: false, mode: null };\n private callbacks: Set<FolderChangeCallback> = new Set();\n private disposed = false;\n\n constructor(\n private foldersContext: FoldersContext.Interface,\n private foldersCache: FoldersCache.Interface,\n private listFoldersUseCase: ListFoldersUseCase.Interface,\n private listFoldersByParentIdsUseCase: ListFoldersByParentIdsUseCase.Interface,\n private createFolderUseCase: CreateFolderUseCase.Interface,\n private updateFolderUseCase: UpdateFolderUseCase.Interface,\n private deleteFolderUseCase: DeleteFolderUseCase.Interface,\n private getFolderAncestorsUseCase: GetFolderAncestorsUseCase.Interface,\n private getFolderLevelPermissionUseCase: GetFolderLevelPermissionUseCase.Interface,\n private formModelFactory: FormModelFactory.Interface\n ) {\n makeAutoObservable<FolderTreePresenterImpl, \"callbacks\">(\n this,\n { callbacks: false },\n { autoBind: true }\n );\n\n reaction(\n () => this.currentFolderId,\n folderId => {\n if (!this.disposed) {\n this.callbacks.forEach(cb => cb(folderId));\n }\n }\n );\n\n void this.loadFolders();\n }\n\n get vm(): IFolderTreeViewModel {\n const tree = this.buildTree();\n const allFolders = this.foldersCache.getItems();\n const currentNode = this.findNode(this.currentFolderId, tree);\n const currentFolder = this.findFolder(this.currentFolderId);\n const childNodes = currentNode\n ? currentNode.children\n : tree.filter(n => n.id !== ROOT_FOLDER);\n return {\n folders: allFolders.map(f => FolderDtoMapper.toDTO(f)),\n tree,\n currentFolderId: this.currentFolderId,\n currentFolder: currentFolder ? FolderDtoMapper.toDTO(currentFolder) : null,\n isRootFolder: this.currentFolderId === null,\n currentFolderTitle: currentNode?.name ?? \"All Files\",\n childFolders: childNodes\n .map(n => {\n const folder = allFolders.find(f => f.id === n.id);\n return folder ? FolderDtoMapper.toDTO(folder) : null;\n })\n .filter((f): f is FolderDto => f !== null),\n loading: this.loading,\n loadingNodeIds: this._loadingNodeIds,\n operation: this.operation\n };\n }\n\n selectFolder(folderId: string | null): void {\n this.currentFolderId = folderId;\n if (folderId) {\n void this.loadChildFolders([folderId]);\n }\n }\n\n onFolderChange(callback: FolderChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n async loadChildFolders(parentIds: string[]): Promise<void> {\n const fetchableIds = parentIds.filter(id => id !== ROOT_FOLDER && id !== \"0\");\n if (fetchableIds.length === 0) {\n return;\n }\n\n runInAction(() => {\n this._loadingNodeIds = fetchableIds;\n });\n\n try {\n await this.listFoldersByParentIdsUseCase.execute(fetchableIds);\n } finally {\n runInAction(() => {\n this._loadingNodeIds = [];\n });\n }\n }\n\n async moveFolder(folderId: string, targetParentId: string | null): Promise<void> {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: folder.title,\n slug: folder.slug,\n type: this.foldersContext.type,\n parentId: targetParentId,\n permissions: folder.permissions\n });\n\n await this.loadFolders();\n }\n\n canManageStructure(folderId: string): boolean {\n return this.getFolderLevelPermissionUseCase.execute(folderId, \"canManageStructure\");\n }\n\n getAncestorIds(folderId: string): string[] {\n return this.getFolderAncestorsUseCase.execute(folderId).map(f => f.id);\n }\n\n createFolder(parentFolderId?: string): void {\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Title\").required(\"Title is required\"),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(f => {\n const title = f.field(\"title\").getValue();\n return slugify(String(title ?? \"\"), {\n replacement: \"-\",\n lower: true,\n remove: /[*#\\?<>_\\{\\}\\[\\]+~.()'\"!:;@]/g,\n trim: false\n });\n }),\n parentId: fields\n .text()\n .label(\"Parent folder\")\n .renderer(\"folderTree\" as any)\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\"), layout.row(\"parentId\")]\n });\n\n form.setData({ parentId: parentFolderId ?? null });\n\n this.operation = {\n active: true,\n mode: \"create\",\n parentFolderId,\n form\n };\n }\n\n editFolder(folderId: string): void {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Name\").required(\"Name is required\"),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(f => {\n const title = f.field(\"title\").getValue();\n return slugify(String(title ?? \"\"), {\n replacement: \"-\",\n lower: true,\n remove: /[*#\\?<>_\\{\\}\\[\\]+~.()'\"!:;@]/g,\n trim: false\n });\n })\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\")]\n });\n\n form.setData({ title: folder.title, slug: folder.slug });\n\n this.operation = {\n active: true,\n mode: \"edit\",\n folderId,\n form\n };\n }\n\n async submitOperation(): Promise<boolean> {\n const { mode, form, parentFolderId, folderId } = this.operation;\n if (!form || !mode) {\n return false;\n }\n\n const data = await form.submit<{ title: string; slug: string; parentId?: string | null }>();\n if (!data) {\n return false;\n }\n\n if (mode === \"create\") {\n await this.createFolderUseCase.execute({\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: data.parentId ?? parentFolderId ?? null,\n permissions: []\n });\n } else if (mode === \"edit\" && folderId) {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: folder?.parentId ?? null,\n permissions: folder?.permissions ?? []\n });\n }\n\n await this.loadFolders();\n\n runInAction(() => {\n this.operation = { active: false, mode: null };\n });\n\n return true;\n }\n\n async deleteFolder(folderId: string): Promise<void> {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n const parentId = folder?.parentId ?? null;\n\n this.operation = {\n active: true,\n mode: \"delete\",\n folderId\n };\n\n await this.deleteFolderUseCase.execute(folderId);\n await this.loadFolders();\n\n runInAction(() => {\n this.operation = { active: false, mode: null };\n this.currentFolderId = parentId;\n });\n }\n\n cancelOperation(): void {\n this.operation = { active: false, mode: null };\n }\n\n private async loadFolders(): Promise<void> {\n runInAction(() => {\n this.loading = true;\n });\n\n try {\n await this.listFoldersUseCase.execute();\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n private buildTree(): IFolderTreeNode[] {\n const folders = this.foldersCache.getItems();\n return this.buildTreeFromFolders(folders);\n }\n\n private buildTreeFromFolders(folders: Folder[]): IFolderTreeNode[] {\n const nodeMap = new Map<string, IFolderTreeNode>();\n\n for (const folder of folders) {\n nodeMap.set(folder.id, {\n id: folder.id,\n name: folder.title,\n slug: folder.slug,\n parentId: folder.parentId,\n children: [],\n hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false,\n canManagePermissions: folder.canManagePermissions ?? false,\n canManageStructure: folder.canManageStructure ?? true\n });\n }\n\n const roots: IFolderTreeNode[] = [];\n for (const folder of folders) {\n const node = nodeMap.get(folder.id)!;\n if (folder.parentId && nodeMap.has(folder.parentId)) {\n nodeMap.get(folder.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n }\n\n private findFolder(folderId: string | null): Folder | null {\n if (!folderId) {\n return null;\n }\n\n return this.foldersCache.getItem(f => f.id === folderId) ?? null;\n }\n\n private findNode(folderId: string | null, nodes: IFolderTreeNode[]): IFolderTreeNode | null {\n if (folderId === null) {\n return null;\n }\n\n for (const node of nodes) {\n if (node.id === folderId) {\n return node;\n }\n const found = this.findNode(folderId, node.children);\n if (found) {\n return found;\n }\n }\n\n return null;\n }\n}\n\nexport const FolderTreePresenter = Abstraction.createImplementation({\n implementation: FolderTreePresenterImpl,\n dependencies: [\n FoldersContext,\n FoldersCache,\n ListFoldersUseCase,\n ListFoldersByParentIdsUseCase,\n CreateFolderUseCase,\n UpdateFolderUseCase,\n DeleteFolderUseCase,\n GetFolderAncestorsUseCase,\n GetFolderLevelPermissionUseCase,\n FormModelFactory\n ]\n});\n"],"names":["FolderTreePresenterImpl","foldersContext","foldersCache","listFoldersUseCase","listFoldersByParentIdsUseCase","createFolderUseCase","updateFolderUseCase","deleteFolderUseCase","getFolderAncestorsUseCase","getFolderLevelPermissionUseCase","formModelFactory","Set","makeAutoObservable","reaction","folderId","cb","tree","allFolders","currentNode","currentFolder","childNodes","n","ROOT_FOLDER","f","FolderDtoMapper","folder","callback","parentIds","fetchableIds","id","runInAction","targetParentId","folders","parentFolderId","form","fields","title","slugify","String","layout","mode","data","parentId","nodeMap","Map","roots","node","nodes","found","FolderTreePresenter","Abstraction","FoldersContext","FoldersCache","ListFoldersUseCase","ListFoldersByParentIdsUseCase","CreateFolderUseCase","UpdateFolderUseCase","DeleteFolderUseCase","GetFolderAncestorsUseCase","GetFolderLevelPermissionUseCase","FormModelFactory"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/folderTree/FolderTreePresenter.js","sources":["../../../src/presentation/folderTree/FolderTreePresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction } from \"mobx\";\nimport { StringFormatter } from \"@webiny/app-admin/features/stringFormatter/abstractions.js\";\nimport {\n FolderTreePresenter as Abstraction,\n type IFolderTreeNode,\n type IFolderTreeViewModel,\n type IFolderOperationState\n} from \"./abstractions.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FoldersCache, FoldersContext } from \"~/features/folders/abstractions.js\";\nimport { ListFoldersUseCase } from \"~/features/folders/listFolders/abstractions.js\";\nimport { ListFoldersByParentIdsUseCase } from \"~/features/folders/listFoldersByParentIds/abstractions.js\";\nimport { CreateFolderUseCase } from \"~/features/folders/createFolder/abstractions.js\";\nimport { UpdateFolderUseCase } from \"~/features/folders/updateFolder/abstractions.js\";\nimport { DeleteFolderUseCase } from \"~/features/folders/deleteFolder/abstractions.js\";\nimport { GetFolderAncestorsUseCase } from \"~/features/folders/getFolderAncestors/abstractions.js\";\nimport { GetFolderLevelPermissionUseCase } from \"~/features/folders/getFolderLevelPermission/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { Folder } from \"~/domain/folder/Folder.js\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport { FolderDtoMapper } from \"~/domain/folder/FolderDtoMapper.js\";\n\ntype FolderChangeCallback = (folderId: string | null) => void;\n\nclass FolderTreePresenterImpl implements Abstraction.Interface {\n private currentFolderId: string | null = null;\n private loading = false;\n private _loadingNodeIds: string[] = [];\n private operation: IFolderOperationState = { active: false, mode: null };\n private callbacks: Set<FolderChangeCallback> = new Set();\n private disposed = false;\n\n private previousFolders: Folder[] = [];\n\n constructor(\n private foldersContext: FoldersContext.Interface,\n private foldersCache: FoldersCache.Interface,\n private listFoldersUseCase: ListFoldersUseCase.Interface,\n private listFoldersByParentIdsUseCase: ListFoldersByParentIdsUseCase.Interface,\n private createFolderUseCase: CreateFolderUseCase.Interface,\n private updateFolderUseCase: UpdateFolderUseCase.Interface,\n private deleteFolderUseCase: DeleteFolderUseCase.Interface,\n private getFolderAncestorsUseCase: GetFolderAncestorsUseCase.Interface,\n private getFolderLevelPermissionUseCase: GetFolderLevelPermissionUseCase.Interface,\n private formModelFactory: FormModelFactory.Interface,\n private stringFormatter: StringFormatter.Interface\n ) {\n makeAutoObservable<FolderTreePresenterImpl, \"callbacks\" | \"previousFolders\">(\n this,\n { callbacks: false, previousFolders: false },\n { autoBind: true }\n );\n\n reaction(\n () => this.currentFolderId,\n folderId => {\n if (!this.disposed) {\n this.callbacks.forEach(cb => cb(folderId));\n }\n }\n );\n\n reaction(\n () => ({\n folders: this.foldersCache.getItems(),\n currentFolderId: this.currentFolderId\n }),\n ({ folders, currentFolderId }) => {\n if (currentFolderId !== null) {\n const stillExists = folders.some(f => f.id === currentFolderId);\n // Skip when folders is empty *and* a reload is in flight — the cache is\n // cleared before being repopulated, so this is a transient state, not a\n // real \"all folders were removed\" event.\n const isTransientReloadClear = folders.length === 0 && this.loading;\n if (!stillExists && !isTransientReloadClear) {\n const resolvedId = this.resolveValidFolderId(\n currentFolderId,\n this.previousFolders\n );\n this.selectFolder(resolvedId);\n }\n }\n\n this.previousFolders = folders;\n },\n { fireImmediately: true }\n );\n\n void this.loadFolders();\n }\n\n get vm(): IFolderTreeViewModel {\n const tree = this.buildTree();\n const allFolders = this.foldersCache.getItems();\n const currentNode = this.findNode(this.currentFolderId, tree);\n const currentFolder = this.findFolder(this.currentFolderId);\n const childNodes = currentNode\n ? currentNode.children\n : tree.filter(n => n.id !== ROOT_FOLDER);\n return {\n folders: allFolders.map(f => FolderDtoMapper.toDTO(f)),\n tree,\n currentFolderId: this.currentFolderId,\n currentFolder: currentFolder ? FolderDtoMapper.toDTO(currentFolder) : null,\n isRootFolder: this.currentFolderId === null,\n currentFolderTitle: currentNode?.name ?? \"All Files\",\n childFolders: childNodes\n .map(n => {\n const folder = allFolders.find(f => f.id === n.id);\n return folder ? FolderDtoMapper.toDTO(folder) : null;\n })\n .filter((f): f is FolderDto => f !== null),\n loading: this.loading,\n loadingNodeIds: this._loadingNodeIds,\n operation: this.operation\n };\n }\n\n selectFolder(folderId: string | null): void {\n const normalized = folderId === ROOT_FOLDER ? null : folderId;\n this.currentFolderId = normalized;\n if (normalized) {\n void this.loadChildFolders([normalized]);\n }\n }\n\n onFolderChange(callback: FolderChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n async loadChildFolders(parentIds: string[]): Promise<void> {\n const fetchableIds = parentIds.filter(id => id !== ROOT_FOLDER && id !== \"0\");\n if (fetchableIds.length === 0) {\n return;\n }\n\n runInAction(() => {\n this._loadingNodeIds = fetchableIds;\n });\n\n try {\n await this.listFoldersByParentIdsUseCase.execute(fetchableIds);\n } finally {\n runInAction(() => {\n this._loadingNodeIds = [];\n });\n }\n }\n\n async moveFolder(folderId: string, targetParentId: string | null): Promise<void> {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: folder.title,\n slug: folder.slug,\n type: this.foldersContext.type,\n parentId: targetParentId,\n permissions: folder.permissions\n });\n\n await this.loadFolders();\n }\n\n canManageStructure(folderId: string): boolean {\n return this.getFolderLevelPermissionUseCase.execute(folderId, \"canManageStructure\");\n }\n\n getAncestorIds(folderId: string): string[] {\n return this.getFolderAncestorsUseCase.execute(folderId).map(f => f.id);\n }\n\n createFolder(parentFolderId?: string): void {\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Title\").required(\"Title is required\").autoFocus(),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(({ form }) => {\n const title = form.field(\"title\").getValue();\n return this.stringFormatter.slugify(String(title ?? \"\"));\n }),\n parentId: fields\n .text()\n .label(\"Parent folder\")\n .renderer(\"folderTree\" as any)\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\"), layout.row(\"parentId\")]\n });\n\n form.setData({ parentId: parentFolderId ?? null });\n\n this.operation = {\n active: true,\n mode: \"create\",\n parentFolderId,\n form\n };\n }\n\n editFolder(folderId: string): void {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Name\").required(\"Name is required\").autoFocus(),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(({ form }) => {\n const title = form.field(\"title\").getValue();\n return this.stringFormatter.slugify(String(title ?? \"\"));\n })\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\")]\n });\n\n form.setData({ title: folder.title, slug: folder.slug });\n\n this.operation = {\n active: true,\n mode: \"edit\",\n folderId,\n form\n };\n }\n\n async submitOperation(): Promise<boolean> {\n const { mode, form, parentFolderId, folderId } = this.operation;\n if (!form || !mode) {\n return false;\n }\n\n const data = await form.submit<{ title: string; slug: string; parentId?: string | null }>();\n if (!data) {\n return false;\n }\n\n if (mode === \"create\") {\n await this.createFolderUseCase.execute({\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: data.parentId ?? parentFolderId ?? null,\n permissions: []\n });\n } else if (mode === \"edit\" && folderId) {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: folder?.parentId ?? null,\n permissions: folder?.permissions ?? []\n });\n }\n\n runInAction(() => {\n this.operation = { active: false, mode: null };\n });\n\n return true;\n }\n\n async deleteFolder(folderId: string): Promise<void> {\n this.operation = {\n active: true,\n mode: \"delete\",\n folderId\n };\n\n await this.deleteFolderUseCase.execute(folderId);\n\n runInAction(() => {\n this.foldersCache.removeItems(f => f.id === folderId);\n this.operation = { active: false, mode: null };\n });\n }\n\n cancelOperation(): void {\n this.operation = { active: false, mode: null };\n }\n\n private async loadFolders(): Promise<void> {\n runInAction(() => {\n this.loading = true;\n });\n\n try {\n await this.listFoldersUseCase.execute();\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n private resolveValidFolderId(\n folderId: string | null,\n previousFolders: Folder[]\n ): string | null {\n let candidateId = folderId;\n const visited = new Set<string>();\n\n while (candidateId !== null) {\n if (visited.has(candidateId)) {\n return null;\n }\n visited.add(candidateId);\n\n if (this.foldersCache.getItem(f => f.id === candidateId)) {\n return candidateId;\n }\n\n const prev = previousFolders.find(f => f.id === candidateId);\n candidateId = prev?.parentId ?? null;\n }\n\n return null;\n }\n\n private buildTree(): IFolderTreeNode[] {\n const folders = this.foldersCache.getItems();\n return this.buildTreeFromFolders(folders);\n }\n\n private buildTreeFromFolders(folders: Folder[]): IFolderTreeNode[] {\n const nodeMap = new Map<string, IFolderTreeNode>();\n\n for (const folder of folders) {\n nodeMap.set(folder.id, {\n id: folder.id,\n name: folder.title,\n slug: folder.slug,\n parentId: folder.parentId,\n children: [],\n hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false,\n canManagePermissions: folder.canManagePermissions ?? false,\n canManageStructure: folder.canManageStructure ?? true\n });\n }\n\n const roots: IFolderTreeNode[] = [];\n for (const folder of folders) {\n const node = nodeMap.get(folder.id)!;\n if (folder.parentId && nodeMap.has(folder.parentId)) {\n nodeMap.get(folder.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n }\n\n private findFolder(folderId: string | null): Folder | null {\n if (!folderId) {\n return null;\n }\n\n return this.foldersCache.getItem(f => f.id === folderId) ?? null;\n }\n\n private findNode(folderId: string | null, nodes: IFolderTreeNode[]): IFolderTreeNode | null {\n if (folderId === null) {\n return null;\n }\n\n for (const node of nodes) {\n if (node.id === folderId) {\n return node;\n }\n const found = this.findNode(folderId, node.children);\n if (found) {\n return found;\n }\n }\n\n return null;\n }\n}\n\nexport const FolderTreePresenter = Abstraction.createImplementation({\n implementation: FolderTreePresenterImpl,\n dependencies: [\n FoldersContext,\n FoldersCache,\n ListFoldersUseCase,\n ListFoldersByParentIdsUseCase,\n CreateFolderUseCase,\n UpdateFolderUseCase,\n DeleteFolderUseCase,\n GetFolderAncestorsUseCase,\n GetFolderLevelPermissionUseCase,\n FormModelFactory,\n StringFormatter\n ]\n});\n"],"names":["FolderTreePresenterImpl","foldersContext","foldersCache","listFoldersUseCase","listFoldersByParentIdsUseCase","createFolderUseCase","updateFolderUseCase","deleteFolderUseCase","getFolderAncestorsUseCase","getFolderLevelPermissionUseCase","formModelFactory","stringFormatter","Set","makeAutoObservable","reaction","folderId","cb","folders","currentFolderId","stillExists","f","isTransientReloadClear","resolvedId","tree","allFolders","currentNode","currentFolder","childNodes","n","ROOT_FOLDER","FolderDtoMapper","folder","normalized","callback","parentIds","fetchableIds","id","runInAction","targetParentId","parentFolderId","form","fields","title","String","layout","mode","data","previousFolders","candidateId","visited","prev","nodeMap","Map","roots","node","nodes","found","FolderTreePresenter","Abstraction","FoldersContext","FoldersCache","ListFoldersUseCase","ListFoldersByParentIdsUseCase","CreateFolderUseCase","UpdateFolderUseCase","DeleteFolderUseCase","GetFolderAncestorsUseCase","GetFolderLevelPermissionUseCase","FormModelFactory","StringFormatter"],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ import { ListFoldersByParentIdsUseCase } from "../../features/folders/listFolder
|
|
|
11
11
|
import { GetFolderAncestorsUseCase } from "../../features/folders/getFolderAncestors/abstractions.js";
|
|
12
12
|
import { GetFolderLevelPermissionUseCase } from "../../features/folders/getFolderLevelPermission/abstractions.js";
|
|
13
13
|
import { FormModelFactory } from "@webiny/app-admin/features/formModel/abstractions.js";
|
|
14
|
+
import { StringFormatter } from "@webiny/app-admin/features/stringFormatter/abstractions.js";
|
|
14
15
|
import { ListCache } from "../../features/folders/cache/index.js";
|
|
15
16
|
import { Folder } from "../../domain/folder/Folder.js";
|
|
16
17
|
const TYPE = "FmFile";
|
|
@@ -91,6 +92,7 @@ function createMockFormModel() {
|
|
|
91
92
|
}),
|
|
92
93
|
reset: vi.fn(),
|
|
93
94
|
validate: vi.fn(async ()=>true),
|
|
95
|
+
setErrors: vi.fn(),
|
|
94
96
|
submit: vi.fn(async ()=>data),
|
|
95
97
|
evaluateRules: vi.fn(()=>({
|
|
96
98
|
visible: true,
|
|
@@ -107,6 +109,7 @@ function createMockFormModel() {
|
|
|
107
109
|
isDirty: false,
|
|
108
110
|
isValid: null,
|
|
109
111
|
errors: [],
|
|
112
|
+
hasErrors: false,
|
|
110
113
|
submitCount: 0,
|
|
111
114
|
focusField: vi.fn(),
|
|
112
115
|
getData: vi.fn(()=>({})),
|
|
@@ -114,7 +117,8 @@ function createMockFormModel() {
|
|
|
114
117
|
},
|
|
115
118
|
getFieldBuilders: vi.fn(()=>[]),
|
|
116
119
|
resolveChildLayout: vi.fn(()=>[]),
|
|
117
|
-
registry: {}
|
|
120
|
+
registry: {},
|
|
121
|
+
traverse: vi.fn()
|
|
118
122
|
};
|
|
119
123
|
return form;
|
|
120
124
|
}
|
|
@@ -170,6 +174,9 @@ function createTestPresenter(folders = []) {
|
|
|
170
174
|
execute: vi.fn().mockReturnValue(true)
|
|
171
175
|
});
|
|
172
176
|
container.registerInstance(FormModelFactory, formModelFactory);
|
|
177
|
+
container.registerInstance(StringFormatter, {
|
|
178
|
+
slugify: (value)=>value
|
|
179
|
+
});
|
|
173
180
|
FolderTreePresenterFeature.register(container);
|
|
174
181
|
const presenter = container.resolve(FolderTreePresenter);
|
|
175
182
|
return {
|
|
@@ -400,7 +407,7 @@ describe("FolderTreePresenter", ()=>{
|
|
|
400
407
|
presenter.createFolder("root-1");
|
|
401
408
|
await presenter.submitOperation();
|
|
402
409
|
expect(createFolderUseCase.execute).toHaveBeenCalledTimes(1);
|
|
403
|
-
expect(listFolders.execute).toHaveBeenCalledTimes(
|
|
410
|
+
expect(listFolders.execute).toHaveBeenCalledTimes(1);
|
|
404
411
|
});
|
|
405
412
|
});
|
|
406
413
|
describe("editFolder", ()=>{
|
|
@@ -467,40 +474,137 @@ describe("FolderTreePresenter", ()=>{
|
|
|
467
474
|
expect(presenter.vm.operation.active).toBe(false);
|
|
468
475
|
expect(presenter.vm.operation.mode).toBeNull();
|
|
469
476
|
});
|
|
470
|
-
it("should navigate to parent folder after
|
|
477
|
+
it("should navigate to parent folder after deleting the currently open folder", async ()=>{
|
|
471
478
|
const { presenter } = createTestPresenter(createFlatFolders());
|
|
472
479
|
await vi.waitFor(()=>{
|
|
473
480
|
expect(presenter.vm.loading).toBe(false);
|
|
474
481
|
});
|
|
482
|
+
presenter.selectFolder("child-1");
|
|
475
483
|
await presenter.deleteFolder("child-1");
|
|
476
484
|
expect(presenter.vm.currentFolderId).toBe("root-1");
|
|
477
485
|
});
|
|
478
|
-
it("should navigate to root (null) when deleting
|
|
486
|
+
it("should navigate to root (null) when deleting the currently open root folder", async ()=>{
|
|
479
487
|
const { presenter } = createTestPresenter(createFlatFolders());
|
|
480
488
|
await vi.waitFor(()=>{
|
|
481
489
|
expect(presenter.vm.loading).toBe(false);
|
|
482
490
|
});
|
|
491
|
+
presenter.selectFolder("root-1");
|
|
483
492
|
await presenter.deleteFolder("root-1");
|
|
484
493
|
expect(presenter.vm.currentFolderId).toBeNull();
|
|
485
494
|
});
|
|
486
|
-
it("should
|
|
495
|
+
it("should not change currentFolderId when deleting a folder that isn't open", async ()=>{
|
|
496
|
+
const { presenter } = createTestPresenter(createFlatFolders());
|
|
497
|
+
await vi.waitFor(()=>{
|
|
498
|
+
expect(presenter.vm.loading).toBe(false);
|
|
499
|
+
});
|
|
500
|
+
presenter.selectFolder("root-2");
|
|
501
|
+
await presenter.deleteFolder("child-1");
|
|
502
|
+
expect(presenter.vm.currentFolderId).toBe("root-2");
|
|
503
|
+
});
|
|
504
|
+
it("should remove the deleted folder from the cache without refetching from the API", async ()=>{
|
|
487
505
|
const { presenter, listFolders } = createTestPresenter(createFlatFolders());
|
|
488
506
|
await vi.waitFor(()=>{
|
|
489
507
|
expect(presenter.vm.loading).toBe(false);
|
|
490
508
|
});
|
|
491
509
|
await presenter.deleteFolder("child-1");
|
|
492
|
-
expect(listFolders.execute).toHaveBeenCalledTimes(
|
|
510
|
+
expect(listFolders.execute).toHaveBeenCalledTimes(1);
|
|
511
|
+
expect(presenter.vm.folders.find((f)=>"child-1" === f.id)).toBeUndefined();
|
|
493
512
|
});
|
|
494
|
-
it("should fire onFolderChange callback when navigating to parent after
|
|
513
|
+
it("should fire onFolderChange callback when navigating to parent after deleting the open folder", async ()=>{
|
|
495
514
|
const { presenter } = createTestPresenter(createFlatFolders());
|
|
496
515
|
await vi.waitFor(()=>{
|
|
497
516
|
expect(presenter.vm.loading).toBe(false);
|
|
498
517
|
});
|
|
518
|
+
presenter.selectFolder("child-1");
|
|
499
519
|
const callback = vi.fn();
|
|
500
520
|
presenter.onFolderChange(callback);
|
|
501
521
|
await presenter.deleteFolder("child-1");
|
|
502
522
|
expect(callback).toHaveBeenCalledWith("root-1");
|
|
503
523
|
});
|
|
524
|
+
it("should resolve the label for the parent folder after deleting the currently open folder", async ()=>{
|
|
525
|
+
const { presenter } = createTestPresenter(createFlatFolders());
|
|
526
|
+
await vi.waitFor(()=>{
|
|
527
|
+
expect(presenter.vm.loading).toBe(false);
|
|
528
|
+
});
|
|
529
|
+
presenter.selectFolder("grandchild-1");
|
|
530
|
+
await presenter.deleteFolder("grandchild-1");
|
|
531
|
+
expect(presenter.vm.currentFolderId).toBe("child-1");
|
|
532
|
+
expect(presenter.vm.currentFolderTitle).toBe("Invoices");
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
describe("stale currentFolderId validation", ()=>{
|
|
536
|
+
it("should fall back to root when the initial currentFolderId no longer exists", async ()=>{
|
|
537
|
+
const { presenter } = createTestPresenter(createFlatFolders());
|
|
538
|
+
presenter.selectFolder("does-not-exist");
|
|
539
|
+
await vi.waitFor(()=>{
|
|
540
|
+
expect(presenter.vm.loading).toBe(false);
|
|
541
|
+
});
|
|
542
|
+
expect(presenter.vm.currentFolderId).toBeNull();
|
|
543
|
+
});
|
|
544
|
+
it("should fall back to the nearest existing ancestor when a deeply nested folder was deleted in another session", async ()=>{
|
|
545
|
+
const { presenter, foldersCache, listFolders, updateFolderUseCase } = createTestPresenter(createFlatFolders());
|
|
546
|
+
await vi.waitFor(()=>{
|
|
547
|
+
expect(presenter.vm.loading).toBe(false);
|
|
548
|
+
});
|
|
549
|
+
presenter.selectFolder("grandchild-1");
|
|
550
|
+
expect(presenter.vm.currentFolderId).toBe("grandchild-1");
|
|
551
|
+
listFolders.execute.mockImplementationOnce(async ()=>{
|
|
552
|
+
foldersCache.removeItems((f)=>"grandchild-1" === f.id || "child-1" === f.id);
|
|
553
|
+
});
|
|
554
|
+
await presenter.moveFolder("root-2", "root-1");
|
|
555
|
+
expect(updateFolderUseCase.execute).toHaveBeenCalledTimes(1);
|
|
556
|
+
expect(presenter.vm.currentFolderId).toBe("root-1");
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
describe("external cache deletion (e.g. useDeleteFolder bypassing presenter)", ()=>{
|
|
560
|
+
it("should redirect to parent when the current folder is removed from cache externally", async ()=>{
|
|
561
|
+
const { presenter, foldersCache } = createTestPresenter(createFlatFolders());
|
|
562
|
+
await vi.waitFor(()=>{
|
|
563
|
+
expect(presenter.vm.loading).toBe(false);
|
|
564
|
+
});
|
|
565
|
+
presenter.selectFolder("child-1");
|
|
566
|
+
expect(presenter.vm.currentFolderId).toBe("child-1");
|
|
567
|
+
foldersCache.removeItems((f)=>"child-1" === f.id);
|
|
568
|
+
expect(presenter.vm.currentFolderId).toBe("root-1");
|
|
569
|
+
});
|
|
570
|
+
it("should fire onFolderChange callback on external cache deletion", async ()=>{
|
|
571
|
+
const { presenter, foldersCache } = createTestPresenter(createFlatFolders());
|
|
572
|
+
await vi.waitFor(()=>{
|
|
573
|
+
expect(presenter.vm.loading).toBe(false);
|
|
574
|
+
});
|
|
575
|
+
presenter.selectFolder("child-1");
|
|
576
|
+
const callback = vi.fn();
|
|
577
|
+
presenter.onFolderChange(callback);
|
|
578
|
+
foldersCache.removeItems((f)=>"child-1" === f.id);
|
|
579
|
+
expect(callback).toHaveBeenCalledWith("root-1");
|
|
580
|
+
});
|
|
581
|
+
it("should redirect to root when a root-level folder is removed externally", async ()=>{
|
|
582
|
+
const { presenter, foldersCache } = createTestPresenter(createFlatFolders());
|
|
583
|
+
await vi.waitFor(()=>{
|
|
584
|
+
expect(presenter.vm.loading).toBe(false);
|
|
585
|
+
});
|
|
586
|
+
presenter.selectFolder("root-1");
|
|
587
|
+
foldersCache.removeItems((f)=>"root-1" === f.id);
|
|
588
|
+
expect(presenter.vm.currentFolderId).toBeNull();
|
|
589
|
+
});
|
|
590
|
+
it("should redirect to root when the last remaining folder is removed externally", async ()=>{
|
|
591
|
+
const singleFolder = [
|
|
592
|
+
createFolder({
|
|
593
|
+
id: "root-1",
|
|
594
|
+
title: "Documents",
|
|
595
|
+
slug: "documents",
|
|
596
|
+
parentId: null
|
|
597
|
+
})
|
|
598
|
+
];
|
|
599
|
+
const { presenter, foldersCache } = createTestPresenter(singleFolder);
|
|
600
|
+
await vi.waitFor(()=>{
|
|
601
|
+
expect(presenter.vm.loading).toBe(false);
|
|
602
|
+
});
|
|
603
|
+
presenter.selectFolder("root-1");
|
|
604
|
+
expect(presenter.vm.currentFolderId).toBe("root-1");
|
|
605
|
+
foldersCache.removeItems((f)=>"root-1" === f.id);
|
|
606
|
+
expect(presenter.vm.currentFolderId).toBeNull();
|
|
607
|
+
});
|
|
504
608
|
});
|
|
505
609
|
describe("operation state", ()=>{
|
|
506
610
|
it("should start with inactive operation state", async ()=>{
|