X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/a5e78803ff259ce6467ecbc286ee970f1bfda4aa..db1c206c8403ed2b874625f1d1afc7af85dde25c:/src/store/tree-picker/tree-picker-actions.ts diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts index 49c1d60a..b7710494 100644 --- a/src/store/tree-picker/tree-picker-actions.ts +++ b/src/store/tree-picker/tree-picker-actions.ts @@ -2,31 +2,33 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { unionize, ofType, UnionOf } from "~/common/unionize"; -import { TreeNode, initTreeNode, getNodeDescendants, TreeNodeStatus, getNode, TreePickerId, Tree } from '~/models/tree'; -import { createCollectionFilesTree } from "~/models/collection-file"; +import { unionize, ofType, UnionOf } from "common/unionize"; +import { TreeNode, initTreeNode, getNodeDescendants, TreeNodeStatus, getNode, TreePickerId, Tree } from 'models/tree'; +import { createCollectionFilesTree } from "models/collection-file"; import { Dispatch } from 'redux'; -import { RootState } from '~/store/store'; -import { getUserUuid } from "~/common/getuser"; -import { ServiceRepository } from '~/services/services'; -import { FilterBuilder } from '~/services/api/filter-builder'; +import { RootState } from 'store/store'; +import { getUserUuid } from "common/getuser"; +import { ServiceRepository } from 'services/services'; +import { FilterBuilder } from 'services/api/filter-builder'; import { pipe, values } from 'lodash/fp'; -import { ResourceKind } from '~/models/resource'; -import { GroupContentsResource } from '~/services/groups-service/groups-service'; +import { ResourceKind } from 'models/resource'; +import { GroupContentsResource } from 'services/groups-service/groups-service'; import { getTreePicker, TreePicker } from './tree-picker'; -import { ProjectsTreePickerItem } from '~/views-components/projects-tree-picker/generic-projects-tree-picker'; -import { OrderBuilder } from '~/services/api/order-builder'; -import { ProjectResource } from '~/models/project'; +import { ProjectsTreePickerItem } from './tree-picker-middleware'; +import { OrderBuilder } from 'services/api/order-builder'; +import { ProjectResource } from 'models/project'; import { mapTree } from '../../models/tree'; -import { LinkResource, LinkClass } from "~/models/link"; -import { mapTreeValues } from "~/models/tree"; -import { sortFilesTree } from "~/services/collection-service/collection-service-files-response"; +import { LinkResource, LinkClass } from "models/link"; +import { mapTreeValues } from "models/tree"; +import { sortFilesTree } from "services/collection-service/collection-service-files-response"; +import { GroupClass, GroupResource } from "models/group"; export const treePickerActions = unionize({ LOAD_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(), LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array>, pickerId: string }>(), APPEND_TREE_PICKER_NODE_SUBTREE: ofType<{ id: string, subtree: Tree, pickerId: string }>(), TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string, pickerId: string }>(), + EXPAND_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(), ACTIVATE_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string, relatedTreePickers?: string[] }>(), DEACTIVATE_TREE_PICKER_NODE: ofType<{ pickerId: string }>(), TOGGLE_TREE_PICKER_NODE_SELECTION: ofType<{ id: string, pickerId: string }>(), @@ -38,6 +40,22 @@ export const treePickerActions = unionize({ export type TreePickerAction = UnionOf; +export interface LoadProjectParams { + includeCollections?: boolean; + includeFiles?: boolean; + includeFilterGroups?: boolean; + loadShared?: boolean; + options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; }; +} + +export const treePickerSearchActions = unionize({ + SET_TREE_PICKER_PROJECT_SEARCH: ofType<{ pickerId: string, projectSearchValue: string }>(), + SET_TREE_PICKER_COLLECTION_FILTER: ofType<{ pickerId: string, collectionFilterValue: string }>(), + SET_TREE_PICKER_LOAD_PARAMS: ofType<{ pickerId: string, params: LoadProjectParams }>(), +}); + +export type TreePickerSearchAction = UnionOf; + export const getProjectsTreePickerIds = (pickerId: string) => ({ home: `${pickerId}_home`, shared: `${pickerId}_shared`, @@ -92,44 +110,89 @@ export const receiveTreePickerData = (params: ReceiveTreePickerDataParams) nodes: data.map(item => initTreeNode(extractNodeData(item))), pickerId, })); - dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId })); + dispatch(treePickerActions.EXPAND_TREE_PICKER_NODE({ id, pickerId })); }; -interface LoadProjectParams { +interface LoadProjectParamsWithId extends LoadProjectParams { id: string; pickerId: string; includeCollections?: boolean; includeFiles?: boolean; + includeFilterGroups?: boolean; loadShared?: boolean; + options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; }; } -export const loadProject = (params: LoadProjectParams) => - async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => { - const { id, pickerId, includeCollections = false, includeFiles = false, loadShared = false } = params; + +export const loadProject = (params: LoadProjectParamsWithId) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options } = params; dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId })); - const filters = pipe( + let filterB = pipe( (fb: FilterBuilder) => includeCollections ? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION]) : fb.addIsA('uuid', [ResourceKind.PROJECT]), - fb => fb.getFilters(), + fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]), )(new FilterBuilder()); - const { items } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined }); + const state = getState(); + + if (state.treePickerSearch.collectionFilterValues[pickerId]) { + filterB = filterB.addILike('collections.name', state.treePickerSearch.collectionFilterValues[pickerId]); + } + + const filters = filterB.getFilters(); + + const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 }); + + if (itemsAvailable > 1000) { + items.push({ + uuid: "more-items-available", + kind: ResourceKind.WORKFLOW, + name: "*** Not all items were loaded (limit 1000 items) ***", + description: "", + definition: "", + ownerUuid: "", + createdAt: "", + modifiedByClientUuid: "", + modifiedByUserUuid: "", + modifiedAt: "", + href: "", + etag: "" + }); + } dispatch(receiveTreePickerData({ id, pickerId, - data: items, - extractNodeData: item => ({ - id: item.uuid, - value: item, - status: item.kind === ResourceKind.PROJECT - ? TreeNodeStatus.INITIAL - : includeFiles - ? TreeNodeStatus.INITIAL - : TreeNodeStatus.LOADED + data: items.filter((item) => { + if (!includeFilterGroups && (item as GroupResource).groupClass && (item as GroupResource).groupClass === GroupClass.FILTER) { + return false; + } + + if (options && options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as ProjectResource).frozenByUuid) { + return false; + } + + return true; }), + extractNodeData: item => ( + item.uuid === "more-items-available" ? + { + id: item.uuid, + value: item, + status: TreeNodeStatus.LOADED + } + : { + id: item.uuid, + value: item, + status: item.kind === ResourceKind.PROJECT + ? TreeNodeStatus.INITIAL + : includeFiles + ? TreeNodeStatus.INITIAL + : TreeNodeStatus.LOADED + }), })); }; @@ -142,7 +205,6 @@ export const loadCollection = (id: string, pickerId: string) => const node = getNode(id)(picker); if (node && 'kind' in node.value && node.value.kind === ResourceKind.COLLECTION) { - const files = await services.collectionService.files(node.value.portableDataHash); const tree = createCollectionFilesTree(files); const sorted = sortFilesTree(tree); @@ -177,11 +239,11 @@ export const initUserProject = (pickerId: string) => })); } }; -export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false) => +export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const uuid = getUserUuid(getState()); if (uuid) { - dispatch(loadProject({ id: uuid, pickerId, includeCollections, includeFiles })); + dispatch(loadProject({ id: uuid, pickerId, includeCollections, includeFiles, options })); } }; @@ -234,9 +296,11 @@ interface LoadFavoritesProjectParams { pickerId: string; includeCollections?: boolean; includeFiles?: boolean; + options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }; } -export const loadFavoritesProject = (params: LoadFavoritesProjectParams) => +export const loadFavoritesProject = (params: LoadFavoritesProjectParams, + options: { showOnlyOwned: boolean, showOnlyWritable: boolean } = { showOnlyOwned: true, showOnlyWritable: false }) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const { pickerId, includeCollections = false, includeFiles = false } = params; const uuid = getUserUuid(getState()); @@ -248,12 +312,22 @@ export const loadFavoritesProject = (params: LoadFavoritesProjectParams) => fb => fb.getFilters(), )(new FilterBuilder()); - const { items } = await services.favoriteService.list(uuid, { filters }); + const { items } = await services.favoriteService.list(uuid, { filters }, options.showOnlyOwned); dispatch(receiveTreePickerData({ id: 'Favorites', pickerId, - data: items, + data: items.filter((item) => { + if (options.showOnlyWritable && (item as GroupResource).writableBy && (item as GroupResource).writableBy.indexOf(uuid) === -1) { + return false; + } + + if (options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as ProjectResource).frozenByUuid) { + return false; + } + + return true; + }), extractNodeData: item => ({ id: item.uuid, value: item, @@ -271,8 +345,7 @@ export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) = async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const { pickerId, includeCollections = false, includeFiles = false } = params; const uuidPrefix = getState().auth.config.uuidPrefix; - const systemUuid = `${uuidPrefix}-tpzed-000000000000000`; - const allusersUuid = `${uuidPrefix}-j7d0g-fffffffffffffff`; + const publicProjectUuid = `${uuidPrefix}-j7d0g-publicfavorites`; const filters = pipe( (fb: FilterBuilder) => includeCollections @@ -280,8 +353,7 @@ export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) = : fb.addIsA('head_uuid', [ResourceKind.PROJECT]), fb => fb .addEqual('link_class', LinkClass.STAR) - .addIn('owner_uuid', [systemUuid, allusersUuid]) - .addEqual('tail_uuid', allusersUuid) + .addEqual('owner_uuid', publicProjectUuid) .getFilters(), )(new FilterBuilder()); @@ -290,7 +362,13 @@ export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) = dispatch(receiveTreePickerData({ id: 'Public Favorites', pickerId, - data: items, + data: items.filter(item => { + if (params.options && params.options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as any).frozenByUuid) { + return false; + } + + return true; + }), extractNodeData: item => ({ id: item.headUuid, value: item,