import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress } from "@material-ui/core";
import { WithDialogProps } from "../../store/dialog/with-dialog";
-import { ProjectTreePickerWithSidePanel } from "../../views-components/project-tree-picker/project-tree-picker";
+import { ProjectTreePicker } from "../../views-components/project-tree-picker/project-tree-picker";
import { MOVE_TO_VALIDATION } from "../../validators/move-to/move-to-validator";
export const MoveTo = (props: WithDialogProps<string> & InjectedFormProps<{ name: string }>) =>
type='submit'
onClick={props.handleSubmit}
disabled={props.pristine || props.invalid || props.submitting}>
- {props.submitting
- ? <CircularProgress size={20} />
- : 'Move'}
+ {props.submitting ? <CircularProgress size={20} /> : 'Move'}
</Button>
</DialogActions>
</Dialog>
const Picker = (props: WrappedFieldProps) =>
<div style={{ width: '400px', height: '144px', display: 'flex', flexDirection: 'column' }}>
- <ProjectTreePickerWithSidePanel onChange={projectUuid => props.input.onChange(projectUuid)} />
+ <ProjectTreePicker onChange={projectUuid => props.input.onChange(projectUuid)} />
</div>;
\ No newline at end of file
});
export enum TreeItemStatus {
- INITIAL,
- PENDING,
- LOADED
+ INITIAL = 'initial',
+ PENDING = 'pending',
+ LOADED = 'loaded'
}
export interface TreeItem<T> {
<i onClick={() => this.props.toggleItemOpen(it.id, it.status)}
className={toggableIconContainer}>
<ListItemIcon className={this.getToggableIconClassNames(it.open, it.active)}>
- {it.status !== TreeItemStatus.INITIAL && it.items && it.items.length === 0 ? <span /> : <SidePanelRightArrowIcon />}
+ {it.status === TreeItemStatus.PENDING
+ || (it.status === TreeItemStatus.LOADED && !it.items)
+ || (it.status === TreeItemStatus.LOADED && it.items && it.items.length === 0) ? <span /> : <SidePanelRightArrowIcon />}
</ListItemIcon>
</i>
{this.props.showSelection &&
import { collectionFilesItemActionSet } from './views-components/context-menu/action-sets/collection-files-item-action-set';
import { collectionActionSet } from './views-components/context-menu/action-sets/collection-action-set';
import { collectionResourceActionSet } from './views-components/context-menu/action-sets/collection-resource-action-set';
+import { initPickerProjectTree } from './store/tree-picker/tree-picker-actions';
addMenuActionSet(ContextMenuKind.ROOT_PROJECT, rootProjectActionSet);
addMenuActionSet(ContextMenuKind.PROJECT, projectActionSet);
store.dispatch(initAuth());
store.dispatch(getProjectList(services.authService.getUuid()));
+ store.dispatch(initPickerProjectTree());
const TokenComponent = (props: any) => <ApiToken authService={services.authService} {...props}/>;
const WorkbenchComponent = (props: any) => <Workbench authService={services.authService} {...props}/>;
const columns: DataColumns<any> = [{
name: "Column 1",
render: jest.fn(),
- selected: true
+ selected: true,
+ configurable: false
}];
const state = dataExplorerReducer(undefined,
dataExplorerActions.SET_COLUMNS({ id: "Data explorer", columns }));
name: "Column 1",
render: jest.fn(),
selected: true,
- sortDirection: SortDirection.ASC
+ sortDirection: SortDirection.ASC,
+ configurable: false
}, {
name: "Column 2",
render: jest.fn(),
selected: true,
sortDirection: SortDirection.NONE,
+ configurable: false
}];
const state = dataExplorerReducer({ "Data explorer": { ...initialDataExplorer, columns } },
dataExplorerActions.TOGGLE_SORT({ id: "Data explorer", columnName: "Column 2" }));
name: "Column 1",
render: jest.fn(),
selected: true,
+ configurable: false
}];
const filters: DataTableFilterItem[] = [{
id: "1",
items: [],
data: mockProjectResource({ uuid: "1" }),
- status: 0
+ status: TreeItemStatus.INITIAL
}, {
active: false,
open: false,
// SPDX-License-Identifier: AGPL-3.0
import { default as unionize, ofType, UnionOf } from "unionize";
-import { TreeNode } from "../../models/tree";
+
import { TreePickerNode } from "./tree-picker";
+import { receiveTreePickerData, TreePickerKind } from "../../views-components/project-tree-picker/project-tree-picker";
+import { mockProjectResource } from "../../models/test-utils";
+import { Dispatch } from "redux";
+import { RootState } from "../store";
+import { ServiceRepository } from "../../services/services";
export const treePickerActions = unionize({
- LOAD_TREE_PICKER_NODE: ofType<{ id: string }>(),
- LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreePickerNode> }>(),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string }>(),
- TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ id: string }>()
+ LOAD_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
+ LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreePickerNode>, pickerId: string }>(),
+ TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string, pickerId: string }>(),
+ TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ id: string, pickerId: string }>()
}, {
tag: 'type',
value: 'payload'
});
+export const initPickerProjectTree = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const uuid = services.authService.getUuid();
+
+ dispatch<any>(getPickerTreeProjects(uuid));
+ dispatch<any>(getSharedWithMeProjectsPickerTree(uuid));
+ dispatch<any>(getFavoritesProjectsPickerTree(uuid));
+};
+
+const getPickerTreeProjects = (uuid: string = '') => {
+ return getProjectsPickerTree(uuid, TreePickerKind.PROJECTS);
+};
+
+const getSharedWithMeProjectsPickerTree = (uuid: string = '') => {
+ return getProjectsPickerTree(uuid, TreePickerKind.SHARED_WITH_ME);
+};
+
+const getFavoritesProjectsPickerTree = (uuid: string = '') => {
+ return getProjectsPickerTree(uuid, TreePickerKind.FAVORITES);
+};
+
+const getProjectsPickerTree = (uuid: string, kind: string) => {
+ return receiveTreePickerData(
+ '',
+ [mockProjectResource({ uuid, name: kind })],
+ kind
+ );
+};
+
export type TreePickerAction = UnionOf<typeof treePickerActions>;
describe('TreePickerReducer', () => {
it('LOAD_TREE_PICKER_NODE - initial state', () => {
const tree = createTree<TreePickerNode>();
- const newTree = treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE({ id: '1' }));
- expect(newTree).toEqual(tree);
+ const newState = treePickerReducer({}, treePickerActions.LOAD_TREE_PICKER_NODE({ id: '1', pickerId: "projects" }));
+ expect(newState).toEqual({ 'projects': tree });
});
it('LOAD_TREE_PICKER_NODE', () => {
- const tree = createTree<TreePickerNode>();
const node = createTreePickerNode({ id: '1', value: '1' });
- const [newTree] = [tree]
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE({ id: '1' })));
- expect(getNodeValue('1')(newTree)).toEqual({
+ const [newState] = [{
+ projects: createTree<TreePickerNode>()
+ }]
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE({ id: '1', pickerId: "projects" })));
+
+ expect(getNodeValue('1')(newState.projects)).toEqual({
...createTreePickerNode({ id: '1', value: '1' }),
status: TreeItemStatus.PENDING
});
});
it('LOAD_TREE_PICKER_NODE_SUCCESS - initial state', () => {
- const tree = createTree<TreePickerNode>();
const subNode = createTreePickerNode({ id: '1.1', value: '1.1' });
- const newTree = treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [subNode] }));
- expect(getNodeChildren('')(newTree)).toEqual(['1.1']);
+ const newState = treePickerReducer({}, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [subNode], pickerId: "projects" }));
+ expect(getNodeChildren('')(newState.projects)).toEqual(['1.1']);
});
it('LOAD_TREE_PICKER_NODE_SUCCESS', () => {
- const tree = createTree<TreePickerNode>();
const node = createTreePickerNode({ id: '1', value: '1' });
const subNode = createTreePickerNode({ id: '1.1', value: '1.1' });
- const [newTree] = [tree]
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '1', nodes: [subNode] })));
- expect(getNodeChildren('1')(newTree)).toEqual(['1.1']);
- expect(getNodeValue('1')(newTree)).toEqual({
+ const [newState] = [{
+ projects: createTree<TreePickerNode>()
+ }]
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '1', nodes: [subNode], pickerId: "projects" })));
+ expect(getNodeChildren('1')(newState.projects)).toEqual(['1.1']);
+ expect(getNodeValue('1')(newState.projects)).toEqual({
...createTreePickerNode({ id: '1', value: '1' }),
status: TreeItemStatus.LOADED
});
});
it('TOGGLE_TREE_PICKER_NODE_COLLAPSE - collapsed', () => {
- const tree = createTree<TreePickerNode>();
const node = createTreePickerNode({ id: '1', value: '1' });
- const [newTree] = [tree]
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
- .map(tree => treePickerReducer(tree, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1' })));
- expect(getNodeValue('1')(newTree)).toEqual({
+ const [newState] = [{
+ projects: createTree<TreePickerNode>()
+ }]
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1', pickerId: "projects" })));
+ expect(getNodeValue('1')(newState.projects)).toEqual({
...createTreePickerNode({ id: '1', value: '1' }),
collapsed: false
});
});
it('TOGGLE_TREE_PICKER_NODE_COLLAPSE - expanded', () => {
- const tree = createTree<TreePickerNode>();
const node = createTreePickerNode({ id: '1', value: '1' });
- const [newTree] = [tree]
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
- .map(tree => treePickerReducer(tree, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1' })))
- .map(tree => treePickerReducer(tree, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1' })));
- expect(getNodeValue('1')(newTree)).toEqual({
+ const [newState] = [{
+ projects: createTree<TreePickerNode>()
+ }]
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1', pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id: '1', pickerId: "projects" })));
+ expect(getNodeValue('1')(newState.projects)).toEqual({
...createTreePickerNode({ id: '1', value: '1' }),
collapsed: true
});
});
it('TOGGLE_TREE_PICKER_NODE_SELECT - selected', () => {
- const tree = createTree<TreePickerNode>();
const node = createTreePickerNode({ id: '1', value: '1' });
- const [newTree] = [tree]
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
- .map(tree => treePickerReducer(tree, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '1' })));
- expect(getNodeValue('1')(newTree)).toEqual({
+ const [newState] = [{
+ projects: createTree<TreePickerNode>()
+ }]
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '1', pickerId: "projects" })));
+ expect(getNodeValue('1')(newState.projects)).toEqual({
...createTreePickerNode({ id: '1', value: '1' }),
selected: true
});
});
it('TOGGLE_TREE_PICKER_NODE_SELECT - not selected', () => {
- const tree = createTree<TreePickerNode>();
const node = createTreePickerNode({ id: '1', value: '1' });
- const [newTree] = [tree]
- .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
- .map(tree => treePickerReducer(tree, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '1' })))
- .map(tree => treePickerReducer(tree, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '1' })));
- expect(getNodeValue('1')(newTree)).toEqual({
+ const [newState] = [{
+ projects: createTree<TreePickerNode>()
+ }]
+ .map(state => treePickerReducer(state, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node], pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '1', pickerId: "projects" })))
+ .map(state => treePickerReducer(state, treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '1', pickerId: "projects" })));
+ expect(getNodeValue('1')(newState.projects)).toEqual({
...createTreePickerNode({ id: '1', value: '1' }),
selected: false
});
//
// SPDX-License-Identifier: AGPL-3.0
-import { createTree, setNodeValueWith, TreeNode, setNode, mapTree, mapTreeValues } from "../../models/tree";
+import { createTree, setNodeValueWith, TreeNode, setNode, mapTreeValues, Tree } from "../../models/tree";
import { TreePicker, TreePickerNode } from "./tree-picker";
import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
import { TreeItemStatus } from "../../components/tree/tree";
-
-export const treePickerReducer = (state: TreePicker = createTree(), action: TreePickerAction) =>
+export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
treePickerActions.match(action, {
- LOAD_TREE_PICKER_NODE: ({ id }) =>
- setNodeValueWith(setPending)(id)(state),
- LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes }) => {
- const [newState] = [state]
+ LOAD_TREE_PICKER_NODE: ({ id, pickerId }) => {
+ const picker = state[pickerId] || createTree();
+ const updatedPicker = setNodeValueWith(setPending)(id)(picker);
+ return { ...state, [pickerId]: updatedPicker };
+ },
+ LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes, pickerId }) => {
+ const picker = state[pickerId] || createTree();
+ const [updatedPicker] = [picker]
.map(receiveNodes(nodes)(id))
.map(setNodeValueWith(setLoaded)(id));
- return newState;
+ return { ...state, [pickerId]: updatedPicker };
+ },
+ TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id, pickerId }) => {
+ const picker = state[pickerId] || createTree();
+ const updatedPicker = setNodeValueWith(toggleCollapse)(id)(picker);
+ return { ...state, [pickerId]: updatedPicker };
+ },
+ TOGGLE_TREE_PICKER_NODE_SELECT: ({ id, pickerId }) => {
+ const picker = state[pickerId] || createTree();
+ const updatedPicker = mapTreeValues(toggleSelect(id))(picker);
+ return { ...state, [pickerId]: updatedPicker };
},
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id }) =>
- setNodeValueWith(toggleCollapse)(id)(state),
- TOGGLE_TREE_PICKER_NODE_SELECT: ({ id }) =>
- mapTreeValues(toggleSelect(id))(state),
default: () => state
});
? ({ ...value, selected: !value.selected })
: ({ ...value, selected: false });
-const receiveNodes = (nodes: Array<TreePickerNode>) => (parent: string) => (state: TreePicker) =>
- nodes.reduce((tree, node) =>
+const receiveNodes = (nodes: Array<TreePickerNode>) => (parent: string) => (state: Tree<TreePickerNode>) =>
+ nodes.reduce((tree, node) =>
setNode(
createTreeNode(parent)(node)
)(tree), state);
import { Tree } from "../../models/tree";
import { TreeItemStatus } from "../../components/tree/tree";
-export type TreePicker = Tree<TreePickerNode>;
+export type TreePicker = { [key: string]: Tree<TreePickerNode> };
export interface TreePickerNode {
id: string;
status: TreeItemStatus;
}
-export const createTreePickerNode = (data: {id: string, value: any}) => ({
+export const createTreePickerNode = (data: { id: string, value: any }) => ({
...data,
selected: false,
collapsed: true,
import { withDialog } from "../../store/dialog/with-dialog";
import { dialogActions } from "../../store/dialog/dialog-actions";
import { DialogCollectionCreateWithSelected } from "../dialog-create/dialog-collection-create-selected";
-import { loadProjectTreePickerProjects } from "../project-tree-picker/project-tree-picker";
export const DIALOG_COLLECTION_CREATE_WITH_SELECTED = 'dialogCollectionCreateWithSelected';
export const createCollectionWithSelected = () =>
(dispatch: Dispatch) => {
dispatch(reset(DIALOG_COLLECTION_CREATE_WITH_SELECTED));
- dispatch<any>(loadProjectTreePickerProjects(''));
dispatch(dialogActions.OPEN_DIALOG({ id: DIALOG_COLLECTION_CREATE_WITH_SELECTED, data: {} }));
};
import * as React from "react";
import { InjectedFormProps, Field, WrappedFieldProps } from "redux-form";
-import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress, FormHelperText } from "@material-ui/core";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress } from "@material-ui/core";
import { WithDialogProps } from "../../store/dialog/with-dialog";
import { TextField } from "../../components/text-field/text-field";
import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "../../validators/create-collection/create-collection-validator";
type='submit'
onClick={props.handleSubmit}
disabled={props.pristine || props.invalid || props.submitting}>
- {props.submitting
- ? <CircularProgress size={20} />
- : 'Create a collection'}
+ {props.submitting ? <CircularProgress size={20} /> : 'Create a collection'}
</Button>
</DialogActions>
</Dialog>
import { withDialog } from "../../store/dialog/with-dialog";
import { dialogActions } from "../../store/dialog/dialog-actions";
import { MoveTo } from "../../components/move-to-dialog/move-to-dialog";
-import { loadProjectTreePickerProjects } from "../project-tree-picker/project-tree-picker";
import { reduxForm, startSubmit, stopSubmit } from "redux-form";
export const MOVE_TO_DIALOG = 'moveToDialog';
export const openMoveToDialog = () =>
(dispatch: Dispatch) => {
- dispatch<any>(loadProjectTreePickerProjects(''));
dispatch(dialogActions.OPEN_DIALOG({ id: MOVE_TO_DIALOG, data: {}}));
};
import { Dispatch } from "redux";
import { connect } from "react-redux";
import { Typography } from "@material-ui/core";
-import { TreePicker } from "../tree-picker/tree-picker";
-import { TreeProps, TreeItem, TreeItemStatus } from "../../components/tree/tree";
+import { TreePicker, TreePickerProps } from "../tree-picker/tree-picker";
+import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
import { ProjectResource } from "../../models/project";
import { treePickerActions } from "../../store/tree-picker/tree-picker-actions";
import { ListItemTextIcon } from "../../components/list-item-text-icon/list-item-text-icon";
-import { ProjectIcon } from "../../components/icon/icon";
+import { ProjectIcon, FavoriteIcon, ProjectsIcon, ShareMeIcon } from "../../components/icon/icon";
import { createTreePickerNode } from "../../store/tree-picker/tree-picker";
import { RootState } from "../../store/store";
import { ServiceRepository } from "../../services/services";
import { FilterBuilder } from "../../common/api/filter-builder";
-type ProjectTreePickerProps = Pick<TreeProps<ProjectResource>, 'toggleItemActive' | 'toggleItemOpen'>;
+type ProjectTreePickerProps = Pick<TreePickerProps, 'toggleItemActive' | 'toggleItemOpen'>;
const mapDispatchToProps = (dispatch: Dispatch, props: { onChange: (projectUuid: string) => void }): ProjectTreePickerProps => ({
- toggleItemActive: id => {
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id }));
+ toggleItemActive: (id, status, pickerId) => {
+ getNotSelectedTreePickerKind(pickerId)
+ .forEach(pickerId => dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id: '', pickerId })));
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id, pickerId }));
+
props.onChange(id);
},
- toggleItemOpen: (id, status) => {
- status === TreeItemStatus.INITIAL
- ? dispatch<any>(loadProjectTreePickerProjects(id))
- : dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id }));
+ toggleItemOpen: (id, status, pickerId) => {
+ dispatch<any>(toggleItemOpen(id, status, pickerId));
}
});
+const toggleItemOpen = (id: string, status: TreeItemStatus, pickerId: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ if (status === TreeItemStatus.INITIAL) {
+ if (pickerId === TreePickerKind.PROJECTS) {
+ dispatch<any>(loadProjectTreePickerProjects(id));
+ } else if (pickerId === TreePickerKind.FAVORITES) {
+ dispatch<any>(loadFavoriteTreePickerProjects(id === services.authService.getUuid() ? '' : id));
+ } else {
+ // TODO: load sharedWithMe
+ }
+ } else {
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+ }
+ };
+
+const getNotSelectedTreePickerKind = (pickerId: string) => {
+ return [TreePickerKind.PROJECTS, TreePickerKind.FAVORITES, TreePickerKind.SHARED_WITH_ME].filter(id => id !== pickerId);
+};
+
+export enum TreePickerKind {
+ PROJECTS = 'Projects',
+ SHARED_WITH_ME = 'Shared with me',
+ FAVORITES = 'Favorites'
+}
+
export const ProjectTreePicker = connect(undefined, mapDispatchToProps)((props: ProjectTreePickerProps) =>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Typography variant='caption' style={{ flexShrink: 0 }}>
Select a project
</Typography>
<div style={{ flexGrow: 1, overflow: 'auto' }}>
- <TreePicker {...props} render={renderTreeItem} />
+ <TreePicker {...props} render={renderTreeItem} pickerId={TreePickerKind.PROJECTS} />
+ <TreePicker {...props} render={renderTreeItem} pickerId={TreePickerKind.SHARED_WITH_ME} />
+ <TreePicker {...props} render={renderTreeItem} pickerId={TreePickerKind.FAVORITES} />
</div>
</div>);
-export const ProjectTreePickerWithSidePanel = connect(undefined, mapDispatchToProps)((props: ProjectTreePickerProps) =>
- <div style={{ display: 'flex', flexDirection: 'column' }}>
- <div style={{ flexGrow: 1, overflow: 'auto' }}>
- <TreePicker {...props} render={renderTreeItem} />
- </div>
- </div>
-);
// TODO: move action creator to store directory
export const loadProjectTreePickerProjects = (id: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id }));
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerKind.PROJECTS }));
const ownerUuid = id.length === 0 ? services.authService.getUuid() || '' : id;
const { items } = await services.projectService.list({ filters });
- dispatch<any>(receiveProjectTreePickerData(id, items));
+ dispatch<any>(receiveTreePickerData(id, items, TreePickerKind.PROJECTS));
+ };
+
+export const loadFavoriteTreePickerProjects = (id: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const parentId = services.authService.getUuid() || '';
+
+ if (id === '') {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: parentId, pickerId: TreePickerKind.FAVORITES }));
+ const { items } = await services.favoriteService.list(parentId);
+
+ dispatch<any>(receiveTreePickerData(parentId, items as ProjectResource[], TreePickerKind.FAVORITES));
+ } else {
+ dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId: TreePickerKind.FAVORITES }));
+ const filters = FilterBuilder
+ .create()
+ .addEqual('ownerUuid', id);
+
+ const { items } = await services.projectService.list({ filters });
+
+ dispatch<any>(receiveTreePickerData(id, items, TreePickerKind.FAVORITES));
+ }
+
};
+const getProjectPickerIcon = (item: TreeItem<ProjectResource>) => {
+ switch (item.data.name) {
+ case TreePickerKind.FAVORITES:
+ return FavoriteIcon;
+ case TreePickerKind.PROJECTS:
+ return ProjectsIcon;
+ case TreePickerKind.SHARED_WITH_ME:
+ return ShareMeIcon;
+ default:
+ return ProjectIcon;
+ }
+};
+
const renderTreeItem = (item: TreeItem<ProjectResource>) =>
<ListItemTextIcon
- icon={ProjectIcon}
+ icon={getProjectPickerIcon(item)}
name={item.data.name}
isActive={item.active}
hasMargin={true} />;
+
// TODO: move action creator to store directory
-const receiveProjectTreePickerData = (id: string, projects: ProjectResource[]) =>
+export const receiveTreePickerData = (id: string, projects: ProjectResource[], pickerId: string) =>
(dispatch: Dispatch) => {
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
id,
- nodes: projects.map(project => createTreePickerNode({ id: project.uuid, value: project }))
+ nodes: projects.map(project => createTreePickerNode({ id: project.uuid, value: project })),
+ pickerId,
}));
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id }));
- };
\ No newline at end of file
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+ };
+
+
import CircularProgress from '@material-ui/core/CircularProgress';
import { ProjectTree } from './project-tree';
-import { TreeItem } from '../../components/tree/tree';
+import { TreeItem, TreeItemStatus } from '../../components/tree/tree';
import { ProjectResource } from '../../models/project';
import { mockProjectResource } from '../../models/test-utils';
id: "3",
open: true,
active: true,
- status: 1
+ status: TreeItemStatus.PENDING
};
const wrapper = mount(<ProjectTree
projects={[project]}
id: "3",
open: true,
active: true,
- status: 2,
+ status: TreeItemStatus.LOADED,
items: [
{
data: mockProjectResource(),
id: "3",
open: true,
active: true,
- status: 1
+ status: TreeItemStatus.PENDING
}
]
}
id: "3",
open: false,
active: true,
- status: 1
+ status: TreeItemStatus.PENDING
};
const wrapper = mount(<ProjectTree
projects={[project]}
// SPDX-License-Identifier: AGPL-3.0
import { connect } from "react-redux";
-import { Tree, TreeProps, TreeItem } from "../../components/tree/tree";
+import { Tree, TreeProps, TreeItem, TreeItemStatus } from "../../components/tree/tree";
import { RootState } from "../../store/store";
-import { TreePicker as TTreePicker, TreePickerNode, createTreePickerNode } from "../../store/tree-picker/tree-picker";
-import { getNodeValue, getNodeChildren } from "../../models/tree";
+import { createTreePickerNode, TreePickerNode } from "../../store/tree-picker/tree-picker";
+import { getNodeValue, getNodeChildren, Tree as Ttree, createTree } from "../../models/tree";
+import { Dispatch } from "node_modules/redux";
-const memoizedMapStateToProps = () => {
- let prevState: TTreePicker;
- let prevTree: Array<TreeItem<any>>;
+export interface TreePickerProps {
+ pickerId: string;
+ toggleItemOpen: (id: string, status: TreeItemStatus, pickerId: string) => void;
+ toggleItemActive: (id: string, status: TreeItemStatus, pickerId: string) => void;
+}
- return (state: RootState): Pick<TreeProps<any>, 'items'> => {
- if (prevState !== state.treePicker) {
- prevState = state.treePicker;
- prevTree = getNodeChildren('')(state.treePicker)
- .map(treePickerToTreeItems(state.treePicker));
- }
- return {
- items: prevTree
- };
+const mapStateToProps = (state: RootState, props: TreePickerProps): Pick<TreeProps<any>, 'items'> => {
+ const tree = state.treePicker[props.pickerId] || createTree();
+ return {
+ items: getNodeChildren('')(tree)
+ .map(treePickerToTreeItems(tree))
};
};
-const mapDispatchToProps = (): Pick<TreeProps<any>, 'onContextMenu'> => ({
+const mapDispatchToProps = (dispatch: Dispatch, props: TreePickerProps): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive'> => ({
onContextMenu: () => { return; },
+ toggleItemActive: (id, status) => props.toggleItemActive(id, status, props.pickerId),
+ toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId)
});
-export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree);
+export const TreePicker = connect(mapStateToProps, mapDispatchToProps)(Tree);
-const treePickerToTreeItems = (tree: TTreePicker) =>
+const treePickerToTreeItems = (tree: Ttree<TreePickerNode>) =>
(id: string): TreeItem<any> => {
const node: TreePickerNode = getNodeValue(id)(tree) || createTreePickerNode({ id: '', value: 'InvalidNode' });
const items = getNodeChildren(node.id)(tree)