Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions app/components/packages/details/PackageDependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';

import styles from './styles';

Expand Down Expand Up @@ -35,12 +36,10 @@ const PackageDependencies = ({ classes, dependencies }) => {
{dependency.name}
</Typography>
}
secondary={
<Typography color="textSecondary" variant="body2">
{dependency.version}
</Typography>
}
/>
<ListItemSecondaryAction>
{dependency.version}
</ListItemSecondaryAction>
</ListItem>
))}
</List>
Expand Down
2 changes: 1 addition & 1 deletion app/components/packages/details/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const styles = (theme) => ({
maxHeight: 425,
},
listItem: {
padding: theme.spacing(1) / 2,
padding: theme.spacing(1),
margin: 0,
},
cardHeader: {
Expand Down
4 changes: 1 addition & 3 deletions app/components/packages/item/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ const styles = (theme) => ({
color: darken(grayColor, 0.5),
},
group: {
fontSize: 14,
color: theme.palette.common.grayColor,
[theme.breakpoints.down('md')]: {
fontSize: 14,
},
},
statusMissing: {
color: theme.palette.secondary.main,
Expand Down
7 changes: 5 additions & 2 deletions app/components/sidebar/Container.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ const AppSidebar = ({ classes, className }) => {
return showDialog(dialogHandler, dialogOptions);
}, [mode, directory, dispatch]);

const dedupe = useCallback(() => {
const onDedupe = useCallback(() => {
const dialogOptions = {
title: 'Confirmation',
type: 'question',
Expand Down Expand Up @@ -214,7 +214,10 @@ const AppSidebar = ({ classes, className }) => {
npmEnv={npmEnv}
fromSearch={fromSearch}
installPackagesFromJson={installPackagesFromJson}
dedupe={dedupe}
onDedupe={onDedupe}
onCacheClean={() => {}}
onCacheVerify={() => {}}
onShrinkWrap={() => {}}
cache={cache}
/>
<Log log={commandLog} />
Expand Down
13 changes: 7 additions & 6 deletions app/components/sidebar/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const Sidebar = ({
projectInfo,
npmEnv,
installPackagesFromJson,
dedupe,
onDedupe,
cache,
}) => {
return (
Expand All @@ -36,15 +36,16 @@ const Sidebar = ({
mode={mode}
npmEnv={npmEnv}
/>
<ActionsTab
mode={mode}
onInstallPackagesFromJson={installPackagesFromJson}
onDedupe={onDedupe}
/>
<HistoryTab
directories={history}
onHistoryClick={loadDirectory}
loading={loading}
/>
<ActionsTab
mode={mode}
onInstallPackagesFromJson={installPackagesFromJson}
/>
</Tabs>
</>
);
Expand All @@ -56,7 +57,7 @@ Sidebar.propTypes = {
history: arrayOf(object),
loadDirectory: func.isRequired,
installPackagesFromJson: func.isRequired,
dedupe: func.isRequired,
onDedupe: func.isRequired,
cache: func.isRequired,
projectInfo: objectOf(string).isRequired,
updatedAt: string,
Expand Down
7 changes: 5 additions & 2 deletions app/components/sidebar/tabs/Tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,21 @@ const SidebarTabs = ({ classes, className, children }) => {
root: classes.tabLabel,
}}
icon={<PackagesIcon />}
label="Packages"
/>
<Tab
classes={{
root: classes.tabLabel,
}}
icon={<HistoryIcon />}
icon={<ActionsIcon />}
label="Actions"
/>
<Tab
classes={{
root: classes.tabLabel,
}}
icon={<ActionsIcon />}
icon={<HistoryIcon />}
label="History"
/>
</Tabs>
</AppBar>
Expand Down
173 changes: 121 additions & 52 deletions app/components/sidebar/tabs/actions/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,68 +10,137 @@ import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import ArrowRightIcon from '@material-ui/icons/ArrowRightAlt';
import InstallIcon from '@material-ui/icons/ArchiveOutlined';
import CacheCleanIcon from '@material-ui/icons/DeleteSweepOutlined';
import CacheCleanVerify from '@material-ui/icons/VerticalAlignBottom';
import ShrinkWrapIcon from '@material-ui/icons/CenterFocusWeakOutlined';
import DedupeIcon from '@material-ui/icons/CenterFocusWeakOutlined';
import Typography from '@material-ui/core/Typography';
import { iMessage } from 'commons/utils';

import styles from './styles';

const ActionsTab = ({ classes, mode, onInstallPackagesFromJson }) => (
<>
<div className={classes.header}>
<Typography className={classes.title} color="textSecondary">
{iMessage('title', 'actions')}
</Typography>
<Divider />
</div>
<div className={classes.content}>
<List dense>
<ListItem className={classes.listItem}>
<ListItemText
primary={
<div className={classes.flexWrapper}>
<InstallIcon />
<Typography className={classes.label}>
{iMessage('action', 'npmInstall')}
</Typography>
</div>
}
secondary={
<Typography className={classes.secondaryText}>
{iMessage('info', 'npmInstallInfo')}
</Typography>
}
/>
<ListItemSecondaryAction>
<Tooltip
title={
mode === 'global'
? iMessage('info', 'notGlobalModeAvailable')
: iMessage('info', 'npmInstallRun')
}
const ActionItem = ({
classes,
mode,
label,
title,
details,
handler,
icon,
}) => {
return (
<ListItem className={classes.listItem}>
<ListItemText
primary={
<div className={classes.flexWrapper}>
{icon}
<Typography className={classes.label}>{label}</Typography>
</div>
}
secondary={
<Typography className={classes.secondaryText}>{details}</Typography>
}
/>
<ListItemSecondaryAction>
<Tooltip title={title}>
<div>
<IconButton
aria-label="action-install"
disabled={mode === 'global'}
onClick={handler}
disableRipple
>
<div>
<IconButton
aria-label="action-install"
disabled={mode === 'global'}
onClick={onInstallPackagesFromJson}
disableRipple
>
<ArrowRightIcon
color={mode === 'global' ? 'inherit' : 'primary'}
/>
</IconButton>
</div>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
</List>
</div>
</>
);
<ArrowRightIcon />
</IconButton>
</div>
</Tooltip>
</ListItemSecondaryAction>
</ListItem>
);
};

ActionItem.propTypes = {
classes: PropTypes.objectOf(PropTypes.string).isRequired,
mode: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
details: PropTypes.string.isRequired,
handler: PropTypes.func.isRequired,
};

const WithStylesActionItem = withStyles(styles)(ActionItem);

const ActionsTab = ({
classes,
mode,
onInstallPackagesFromJson,
onCacheClean,
onCacheVerify,
onShrinkWrap,
onDedupe,
}) => {
const actions = [
{
label: 'npm install',
details: 'Install packages from package.json',
title: 'Run npm install',
handler: onInstallPackagesFromJson,
icon: <InstallIcon color="secondary" />,
},
{
label: 'npm cache clean',
details: 'Delete all data out of the cache folder.',
title: 'Run npm cache clean',
handler: onCacheClean,
icon: <CacheCleanIcon color="secondary" />,
},
{
label: 'npm cache verify',
details: 'Verify the contents of the cache folder',
title: 'Run npm cache verify',
handler: onCacheVerify,
icon: <CacheCleanVerify color="secondary" />,
},
{
label: 'npm shrinkwrap',
details:
'Repurposes package-lock.json into a publishable npm-shrinkwrap.json',
title: 'Run npm shrinkwrap',
handler: onShrinkWrap,
icon: <ShrinkWrapIcon color="secondary" />,
},
{
label: 'npm dedupe',
details: 'Mode dependencies further up the tree',
title: 'Run npm dedupe',
handler: onDedupe,
icon: <DedupeIcon color="secondary" />,
},
];

return (
<>
<div className={classes.header}>
<Typography className={classes.title} color="textSecondary">
{iMessage('title', 'actions')}
</Typography>
<Divider />
</div>
<div className={classes.content}>
<List dense>
{actions.map((action) => (
<WithStylesActionItem {...action} mode={mode} />
))}
</List>
</div>
</>
);
};

ActionsTab.propTypes = {
classes: PropTypes.objectOf(PropTypes.string).isRequired,
onInstallPackagesFromJson: PropTypes.func.isRequired,
onCacheClean: PropTypes.func.isRequired,
mode: PropTypes.string.isRequired,
};

Expand Down
13 changes: 10 additions & 3 deletions app/models/notifications/epics.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { of } from 'rxjs';
import { mergeMap, catchError, withLatestFrom } from 'rxjs/operators';
import { map, mergeMap, catchError, withLatestFrom } from 'rxjs/operators';
import { v1 as uuidv1 } from 'uuid';
import semver from 'semver';
import { combineEpics, ofType } from 'redux-observable';
Expand All @@ -13,7 +13,7 @@ const parseNotificationEpic = (action$, state$) =>
action$.pipe(
ofType(parseNotification.type),
withLatestFrom(state$),
mergeMap((values) => {
map((values) => {
const [notification, state] = values;
const {
notifications: { notifications: stateNotifications },
Expand All @@ -25,7 +25,14 @@ const parseNotificationEpic = (action$, state$) =>
const [reason, details] = notificationText.split(':');
const isExtraneous = reason === 'extraneous';
const detailsArr = details.trim().split(',');
const [requiredDetails, requiredByName] = detailsArr;

return {
isExtraneous,
details: detailsArr,
};
}),
mergeMap(({ details, isExtraneous }) => {
const [requiredDetails, requiredByName] = details;
const [requiredName, requiredVersion] = requiredDetails.split('@');
const isValidVersion = semver.valid(requiredVersion);

Expand Down