+
+export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) =>
+ async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ const { pickerId, includeCollections = false, includeDirectories = false, includeFiles = false } = params;
+ const uuidPrefix = getState().auth.config.uuidPrefix;
+ const publicProjectUuid = `${uuidPrefix}-j7d0g-publicfavorites`;
+
+ const filters = pipe(
+ (fb: FilterBuilder) => includeCollections
+ ? fb.addIsA('head_uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
+ : fb.addIsA('head_uuid', [ResourceKind.PROJECT]),
+ fb => fb
+ .addEqual('link_class', LinkClass.STAR)
+ .addEqual('owner_uuid', publicProjectUuid)
+ .getFilters(),
+ )(new FilterBuilder());
+
+ const { items } = await services.linkService.list({ filters });
+
+ dispatch<any>(receiveTreePickerData<LinkResource>({
+ id: 'Public Favorites',
+ pickerId,
+ 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,
+ status: item.headKind === ResourceKind.PROJECT
+ ? TreeNodeStatus.INITIAL
+ : includeDirectories || includeFiles
+ ? TreeNodeStatus.INITIAL
+ : TreeNodeStatus.LOADED
+ }),
+ }));
+ };
+
+export const receiveTreePickerProjectsData = (id: string, projects: ProjectResource[], pickerId: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
+ id,
+ nodes: projects.map(project => initTreeNode({ id: project.uuid, value: project })),
+ pickerId,
+ }));
+
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+ };
+
+export const loadProjectTreePickerProjects = (id: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.PROJECTS }));
+
+
+ const ownerUuid = id.length === 0 ? getUserUuid(getState()) || '' : id;
+ const { items } = await services.projectService.list(buildParams(ownerUuid));
+
+ dispatch<any>(receiveTreePickerProjectsData(id, items, TreePickerId.PROJECTS));
+ };
+
+export const loadFavoriteTreePickerProjects = (id: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const parentId = getUserUuid(getState()) || '';
+
+ if (id === '') {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: parentId, pickerId: TreePickerId.FAVORITES }));
+ const { items } = await services.favoriteService.list(parentId);
+ dispatch<any>(receiveTreePickerProjectsData(parentId, items as ProjectResource[], TreePickerId.FAVORITES));
+ } else {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.FAVORITES }));
+ const { items } = await services.projectService.list(buildParams(id));
+ dispatch<any>(receiveTreePickerProjectsData(id, items, TreePickerId.FAVORITES));
+ }
+
+ };
+
+export const loadPublicFavoriteTreePickerProjects = (id: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const parentId = getUserUuid(getState()) || '';
+
+ if (id === '') {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: parentId, pickerId: TreePickerId.PUBLIC_FAVORITES }));
+ const { items } = await services.favoriteService.list(parentId);
+ dispatch<any>(receiveTreePickerProjectsData(parentId, items as ProjectResource[], TreePickerId.PUBLIC_FAVORITES));
+ } else {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerId.PUBLIC_FAVORITES }));
+ const { items } = await services.projectService.list(buildParams(id));
+ dispatch<any>(receiveTreePickerProjectsData(id, items, TreePickerId.PUBLIC_FAVORITES));
+ }
+
+ };
+
+const buildParams = (ownerUuid: string) => {
+ return {
+ filters: new FilterBuilder()
+ .addEqual('owner_uuid', ownerUuid)
+ .getFilters(),
+ order: new OrderBuilder<ProjectResource>()
+ .addAsc('name')
+ .getOrder()
+ };
+};
+
+/**
+ * Given a tree picker item, return collection uuid and path
+ * if the item represents a valid target/destination location
+ */
+export type FileOperationLocation = {
+ name: string;
+ uuid: string;
+ pdh?: string;
+ subpath: string;
+}
+export const getFileOperationLocation = (item: ProjectsTreePickerItem) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<FileOperationLocation | undefined> => {
+ if ('kind' in item && item.kind === ResourceKind.COLLECTION) {
+ return {
+ name: item.name,
+ uuid: item.uuid,
+ pdh: item.portableDataHash,
+ subpath: '/',
+ };
+ } else if ('type' in item && item.type === CollectionFileType.DIRECTORY) {
+ const uuid = getCollectionResourceCollectionUuid(item.id);
+ if (uuid) {
+ const collection = getResource<CollectionResource>(uuid)(getState().resources);
+ if (collection) {
+ const itemPath = [item.path, item.name].join('/');
+
+ return {
+ name: item.name,
+ uuid,
+ pdh: collection.portableDataHash,
+ subpath: itemPath,
+ };
+ }
+ }
+ }
+ return undefined;
+ };
+
+/**
+ * Create an expanded tree picker subtree from array of nested projects/collection
+ * First item is assumed to be root and gets empty parent id
+ * Nodes must be sorted from top down to prevent orphaned nodes
+ */
+export const createInitialPickerTree = (sortedAncestors: Array<GroupResource | CollectionResource>, tailUuid: string, initialTree: Tree<GroupResource | CollectionResource>) => {
+ return sortedAncestors
+ .reduce((tree, item, index) => {
+ if (getNode(item.uuid)(tree)) {
+ return tree;
+ } else {
+ return setNode({
+ children: [],
+ id: item.uuid,
+ parent: index === 0 ? '' : item.ownerUuid,
+ value: item,
+ active: false,
+ selected: false,
+ expanded: false,
+ status: item.uuid !== tailUuid ? TreeNodeStatus.LOADED : TreeNodeStatus.INITIAL,
+ })(tree);
+ }
+ }, initialTree);
+};
+
+export const fileOperationLocationToPickerId = (location: FileOperationLocation): string => {
+ let id = location.uuid;
+ if (location.subpath.length && location.subpath !== '/') {
+ id = id + location.subpath;
+ }
+ return id;
+}