insomnia/packages/insomnia-app/app/ui/components/dropdowns/project-dropdown.tsx
Dimitri Mitropoulos 02c65a852d
implements sort order for projects (#3953)
Co-authored-by: Opender Singh <opender94@gmail.com>
2021-09-09 21:42:57 +00:00

123 lines
4.4 KiB
TypeScript

import { Dropdown, DropdownDivider, DropdownItem, SvgIcon, SvgIconProps, Tooltip } from 'insomnia-components';
import { partition } from 'ramda';
import React, { FC, useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { strings } from '../../../common/strings';
import { isDefaultProject, isRemoteProject, Project, projectHasSettings } from '../../../models/project';
import { VCS } from '../../../sync/vcs/vcs';
import { useRemoteProjects } from '../../hooks/project';
import { setActiveProject } from '../../redux/modules/global';
import { createProject } from '../../redux/modules/project';
import { selectActiveProject, selectProjects } from '../../redux/selectors';
import { showModal } from '../modals';
import ProjectSettingsModal from '../modals/project-settings-modal';
import { svgPlacementHack, tooltipIconPlacementHack } from './dropdown-placement-hacks';
const Checkmark = styled(SvgIcon)({
...svgPlacementHack,
'& svg': {
fill: 'var(--color-surprise)',
},
});
const StyledSvgIcon = styled(SvgIcon)({
...svgPlacementHack,
'& svg': {
fill: 'var(--hl)',
},
});
const StyledTooltip = styled(Tooltip)({
...tooltipIconPlacementHack,
});
const TooltipIcon = ({ message, icon }: { message: string; icon: SvgIconProps['icon'] }) => (
<StyledTooltip message={message}>
<StyledSvgIcon icon={icon} />
</StyledTooltip>
);
const spinner = <i className="fa fa-spin fa-refresh" />;
const home = <TooltipIcon message={`${strings.defaultProject.singular} ${strings.project.singular} (Always ${strings.localProject.singular})`} icon="home" />;
const remoteProject = <TooltipIcon message={`${strings.remoteProject.singular} ${strings.project.singular}`} icon="globe" />;
const localProject = <TooltipIcon message={`${strings.localProject.singular} ${strings.project.singular}`} icon="laptop" />;
interface Props {
vcs?: VCS;
}
const ProjectDropdownItem: FC<{
project: Project;
isActive: boolean;
setActive: (projectId: string) => void;
}> = ({ isActive, project, setActive }) => {
const { _id, name } = project;
const isDefault = isDefaultProject(project);
const isRemote = isRemoteProject(project);
return (
<DropdownItem
key={_id}
icon={isDefault ? home : isRemote ? remoteProject : localProject}
right={isActive && <Checkmark icon="checkmark" />}
value={_id}
onClick={setActive}
>
{name}
</DropdownItem>
);
};
ProjectDropdownItem.displayName = DropdownItem.name; // This is required because the Dropdown component will otherwise silently disregard this component.
export const ProjectDropdown: FC<Props> = ({ vcs }) => {
const { loading, refresh } = useRemoteProjects(vcs);
const projects = useSelector(selectProjects);
const activeProject = useSelector(selectActiveProject);
const dispatch = useDispatch();
const setActive = useCallback((projectId: string) => dispatch(setActiveProject(projectId)), [dispatch]);
const createNew = useCallback(() => dispatch(createProject()), [dispatch]);
const showSettings = useCallback(() => showModal(ProjectSettingsModal), []);
// dropdown button
const button = useMemo(() => (
<button type="button" className="row" title={activeProject.name}>
{activeProject.name}
<i className="fa fa-caret-down space-left" />
</button>
), [activeProject]);
const renderProject = useCallback((project: Project) => (
<ProjectDropdownItem
key={project._id}
isActive={project._id === activeProject._id}
setActive={setActive}
project={project}
/>
), [setActive, activeProject._id]);
const [defaultProjects, userProjects] = partition(isDefaultProject, projects);
return (
<Dropdown renderButton={button} onOpen={refresh}>
{defaultProjects.map(renderProject)}
<DropdownDivider>All {strings.project.plural.toLowerCase()}{' '}{loading && spinner}</DropdownDivider>
{userProjects.map(renderProject)}
{projectHasSettings(activeProject) && <>
<DropdownDivider />
<DropdownItem icon={<StyledSvgIcon icon="gear" />} onClick={showSettings}>
{strings.project.singular} Settings
</DropdownItem>
</>}
<DropdownDivider />
<DropdownItem icon={<StyledSvgIcon icon="plus" />} onClick={createNew}>
Create new {strings.project.singular.toLowerCase()}
</DropdownItem>
</Dropdown>
);
};