mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-06 22:40:26 -05:00
refactor: package list [WIP]
This commit is contained in:
parent
c518b94978
commit
aec337719e
11 changed files with 158 additions and 50 deletions
|
@ -7,6 +7,8 @@ import _ from 'lodash';
|
||||||
import { addScope, addGravatarSupport, deleteProperties, sortByName, parseReadme } from '../../../lib/utils';
|
import { addScope, addGravatarSupport, deleteProperties, sortByName, parseReadme } from '../../../lib/utils';
|
||||||
import { allow } from '../../middleware';
|
import { allow } from '../../middleware';
|
||||||
import { DIST_TAGS, HEADER_TYPE, HEADERS, HTTP_STATUS } from '../../../lib/constants';
|
import { DIST_TAGS, HEADER_TYPE, HEADERS, HTTP_STATUS } from '../../../lib/constants';
|
||||||
|
import { generateGravatarUrl } from '../../../utils/user';
|
||||||
|
import { formatAuthor } from '../../../webui/utils/package';
|
||||||
import logger from '../../../lib/logger';
|
import logger from '../../../lib/logger';
|
||||||
import type { Router } from 'express';
|
import type { Router } from 'express';
|
||||||
import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, $SidebarPackage } from '../../../../types';
|
import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, $SidebarPackage } from '../../../../types';
|
||||||
|
@ -41,12 +43,18 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth,
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processPermissionsPackages(packages) {
|
async function processPermissionsPackages(packages = []) {
|
||||||
const permissions = [];
|
const permissions = [];
|
||||||
for (const pkg of packages) {
|
const packgesCopy = packages.slice();
|
||||||
|
for (const pkg of packgesCopy) {
|
||||||
|
const pkgCopy = { ...pkg };
|
||||||
|
pkgCopy.author = formatAuthor(pkg.author);
|
||||||
try {
|
try {
|
||||||
if (await checkAllow(pkg.name, req.remote_user)) {
|
if (await checkAllow(pkg.name, req.remote_user)) {
|
||||||
permissions.push(pkg);
|
if (config.web) {
|
||||||
|
pkgCopy.author.avatar = generateGravatarUrl(pkgCopy.author.email, config.web.gravatar);
|
||||||
|
}
|
||||||
|
permissions.push(pkgCopy);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.logger.error({ name: pkg.name, error: err }, 'permission process for @{name} has failed: @{error}');
|
logger.logger.error({ name: pkg.name, error: err }, 'permission process for @{name} has failed: @{error}');
|
||||||
|
|
3
src/webui/components/Icon/img/filebinary.svg
Normal file
3
src/webui/components/Icon/img/filebinary.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="filebinary">
|
||||||
|
<path d="M8.5 1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V4.5L8.5 1zM11 14H1V2h7l3 3v9zM5 6.98L3.5 8.5 5 10l-.5 1L2 8.5 4.5 6l.5.98zM7.5 6L10 8.5 7.5 11l-.5-.98L8.5 8.5 7 7l.5-1z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 265 B |
3
src/webui/components/Icon/img/law.svg
Normal file
3
src/webui/components/Icon/img/law.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="law">
|
||||||
|
<path fill-rule="evenodd" d="M7 4c-.83 0-1.5-.67-1.5-1.5S6.17 1 7 1s1.5.67 1.5 1.5S7.83 4 7 4zm7 6c0 1.11-.89 2-2 2h-1c-1.11 0-2-.89-2-2l2-4h-1c-.55 0-1-.45-1-1H8v8c.42 0 1 .45 1 1h1c.42 0 1 .45 1 1H3c0-.55.58-1 1-1h1c0-.55.58-1 1-1h.03L6 5H5c0 .55-.45 1-1 1H3l2 4c0 1.11-.89 2-2 2H2c-1.11 0-2-.89-2-2l2-4H1V5h3c0-.55.45-1 1-1h4c.55 0 1 .45 1 1h3v1h-1l2 4zM2.5 7L1 10h3L2.5 7zM13 10l-1.5-3-1.5 3h3z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 464 B |
6
src/webui/components/Icon/img/version.svg
Normal file
6
src/webui/components/Icon/img/version.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 16" height="16" width="14" id="version">
|
||||||
|
<path fill-rule="evenodd" d="M13 3H7c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1h6c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zm-1 8H8V5h4v6zM4 4h1v1H4v6h1v1H4c-.55 0-1-.45-1-1V5c0-.55.45-1 1-1zM1 5h1v1H1v4h1v1H1c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1z"></path>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 344 B |
|
@ -20,7 +20,10 @@ import spain from './img/spain.svg';
|
||||||
import earth from './img/earth.svg';
|
import earth from './img/earth.svg';
|
||||||
import verdaccio from './img/verdaccio.svg';
|
import verdaccio from './img/verdaccio.svg';
|
||||||
import license from './img/license.svg';
|
import license from './img/license.svg';
|
||||||
|
import law from './img/law.svg';
|
||||||
import time from './img/time.svg';
|
import time from './img/time.svg';
|
||||||
|
import version from './img/version.svg';
|
||||||
|
import filebinary from './img/filebinary.svg';
|
||||||
|
|
||||||
export const Icons: $Shape<IIconsMap> = {
|
export const Icons: $Shape<IIconsMap> = {
|
||||||
// flags
|
// flags
|
||||||
|
@ -35,6 +38,9 @@ export const Icons: $Shape<IIconsMap> = {
|
||||||
verdaccio,
|
verdaccio,
|
||||||
license,
|
license,
|
||||||
time,
|
time,
|
||||||
|
law,
|
||||||
|
version,
|
||||||
|
filebinary,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Icon = ({ className, name, size = 'sm', img = false, pointer = false, ...props }: IProps): Node => {
|
const Icon = ({ className, name, size = 'sm', img = false, pointer = false, ...props }: IProps): Node => {
|
||||||
|
|
|
@ -21,7 +21,7 @@ const getSize = (size: string) => {
|
||||||
default:
|
default:
|
||||||
return `
|
return `
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 16px;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,15 +9,21 @@ import React from 'react';
|
||||||
import type { Element } from 'react';
|
import type { Element } from 'react';
|
||||||
import { spacing } from '../../utils/styles/mixings';
|
import { spacing } from '../../utils/styles/mixings';
|
||||||
|
|
||||||
|
import Grid from '@material-ui/core/Grid';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
||||||
import Avatar2 from '@material-ui/core/Avatar';
|
import Avatar2 from '@material-ui/core/Avatar';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import Divider from '@material-ui/core/Divider';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import BugReport from '@material-ui/icons/BugReport';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
|
import HomeIcon from '@material-ui/icons/Home';
|
||||||
|
import BookmarkBorder from '@material-ui/icons/BookmarkBorder';
|
||||||
|
|
||||||
import Tag from '../Tag';
|
import Tag from '../Tag';
|
||||||
|
import fileSizeSI from '../../utils/file-size';
|
||||||
import { formatDate, formatDateDistance } from '../../utils/package';
|
import { formatDate, formatDateDistance } from '../../utils/package';
|
||||||
|
|
||||||
import { IProps } from './types';
|
import { IProps } from './types';
|
||||||
|
@ -39,7 +45,9 @@ import {
|
||||||
Field,
|
Field,
|
||||||
Content,
|
Content,
|
||||||
Footer,
|
Footer,
|
||||||
|
PackageName,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
import { fontWeight } from '../../utils/styles/sizes';
|
||||||
|
|
||||||
const getInitialsName = (name: string) =>
|
const getInitialsName = (name: string) =>
|
||||||
name
|
name
|
||||||
|
@ -47,36 +55,48 @@ const getInitialsName = (name: string) =>
|
||||||
.reduce((accumulator, currentValue) => accumulator.charAt(0) + currentValue.charAt(0), '')
|
.reduce((accumulator, currentValue) => accumulator.charAt(0) + currentValue.charAt(0), '')
|
||||||
.toUpperCase();
|
.toUpperCase();
|
||||||
|
|
||||||
const Package = ({ name: label, version, time, author: { name, avatar }, description, license, keywords = [] }: IProps): Element<WrapperLink> => {
|
const Package = ({ name: label, version, dist: { unpackedSize } = {}, time, author: { name, avatar }, description, license, keywords = [] }: IProps): Element<WrapperLink> => {
|
||||||
const renderMainInfo = () => (
|
console.log(unpackedSize);
|
||||||
<MainInfo>
|
const renderVersionInfo = () => version && (
|
||||||
<Name>{label}</Name>
|
<OverviewItem>
|
||||||
<Version>{`v${version}`}</Version>
|
<Icon name={'version'} />
|
||||||
</MainInfo>
|
{`v${version}`}
|
||||||
);
|
</OverviewItem>
|
||||||
|
);
|
||||||
const renderAuthorInfo = () => (
|
|
||||||
<Author>
|
const renderAuthorInfo = () => {
|
||||||
<Avatar alt={name} src={avatar}>
|
return (
|
||||||
{!avatar && getInitialsName(name)}
|
<Author>
|
||||||
</Avatar>
|
<Avatar alt={name} src={avatar} style={{ width: '20px', height: '20px' }} />
|
||||||
<Details>
|
<Details>
|
||||||
<Text text={name} weight={'bold'} />
|
<Text text={name} />
|
||||||
</Details>
|
</Details>
|
||||||
</Author>
|
</Author>
|
||||||
|
);
|
||||||
|
<<<<<<< HEAD
|
||||||
|
};
|
||||||
|
=======
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderFileSize = () => unpackedSize && (
|
||||||
|
<OverviewItem>
|
||||||
|
<Icon name={'filebinary'} />
|
||||||
|
{fileSizeSI(unpackedSize)}
|
||||||
|
</OverviewItem>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
>>>>>>> b4b8d6b0... wip
|
||||||
const renderLicenseInfo = () =>
|
const renderLicenseInfo = () =>
|
||||||
license && (
|
license && (
|
||||||
<OverviewItem>
|
<OverviewItem>
|
||||||
<Icon modifiers={spacing('margin', '4px', '5px', '0px', '0px')} name={'license'} pointer={true} />
|
<Icon name={'law'} />
|
||||||
{license}
|
{license}
|
||||||
</OverviewItem>
|
</OverviewItem>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderPublishedInfo = () => (
|
const renderPublishedInfo = () => (
|
||||||
<OverviewItem>
|
<OverviewItem>
|
||||||
<Icon name={'time'} pointer={true} />
|
<Icon name={'time'} />
|
||||||
<Published modifiers={spacing('margin', '0px', '5px', '0px', '0px')}>{`Published on ${formatDate(time)} •`}</Published>
|
<Published modifiers={spacing('margin', '0px', '5px', '0px', '0px')}>{`Published on ${formatDate(time)} •`}</Published>
|
||||||
{`${formatDateDistance(time)} ago`}
|
{`${formatDateDistance(time)} ago`}
|
||||||
</OverviewItem>
|
</OverviewItem>
|
||||||
|
@ -111,26 +131,62 @@ const Package = ({ name: label, version, time, author: { name, avatar }, descrip
|
||||||
// )}
|
// )}
|
||||||
// </WrapperLink>
|
// </WrapperLink>
|
||||||
// );
|
// );
|
||||||
|
const tags = keywords.sort().map((keyword, index) => (
|
||||||
|
<Tag style={{ color: '#485A3E' }} key={index}>
|
||||||
|
{keyword}
|
||||||
|
</Tag>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<List style={{ padding: '12px 0 12px 0'}}>
|
||||||
<ListItem alignItems="flex-start">
|
<ListItem alignItems="flex-start">
|
||||||
<ListItemAvatar>
|
{/* <ListItemAvatar>
|
||||||
<Avatar2 alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
|
<Avatar2 alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
|
||||||
</ListItemAvatar>
|
</ListItemAvatar> */}
|
||||||
<ListItemText
|
<ListItemText component="div" style={{ paddingRight: 0}}
|
||||||
primary={label}
|
primary={
|
||||||
|
<Grid item xs={12} container>
|
||||||
|
<Grid item xs>
|
||||||
|
<PackageName>{label}</PackageName>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs style={{ textAlign: "right" }}>
|
||||||
|
<Tooltip title="Visit homepage" aria-label="Add">
|
||||||
|
<IconButton aria-label="Report" style={{ padding: '6px' }}>
|
||||||
|
<HomeIcon fontSize="small" style={{ fontSize: '16px' }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Open an issue" aria-label="Add">
|
||||||
|
<IconButton aria-label="Report" style={{ padding: '6px' }}>
|
||||||
|
<BugReport fontSize="small" style={{ fontSize: '16px' }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Pin it" aria-label="Add">
|
||||||
|
<IconButton aria-label="Report" style={{ padding: '6px' }}>
|
||||||
|
<BookmarkBorder fontSize="small" style={{ fontSize: '16px' }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
secondary={
|
secondary={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Typography component="span" color="textPrimary">
|
<Typography component="span" style={{ color: '#586069', fontSize: '14px', paddingRight: 0 }}>
|
||||||
{name}
|
{description}
|
||||||
</Typography>
|
</Typography>
|
||||||
{description}
|
{tags.length > 0 && <span style={{ marginTop: '8px', display: 'block' }}>
|
||||||
|
{tags}
|
||||||
|
</span>}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider></Divider>
|
<ListItem alignItems="flex-start">
|
||||||
|
{renderAuthorInfo()}
|
||||||
|
{renderVersionInfo()}
|
||||||
|
{renderPublishedInfo()}
|
||||||
|
{renderFileSize()}
|
||||||
|
{renderLicenseInfo()}
|
||||||
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,8 +52,9 @@ export const OverviewItem = styled.span`
|
||||||
&& {
|
&& {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0 0 5px 0;
|
margin: 0 0 0 16px;
|
||||||
color: ${colors.greyLight};
|
color: #908ba1;
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -83,15 +84,14 @@ export const Version = styled.span`
|
||||||
|
|
||||||
export const Icon = styled(Ico)`
|
export const Icon = styled(Ico)`
|
||||||
&& {
|
&& {
|
||||||
margin: 1px 5px 0 0;
|
margin: 0px 10px 0px 0;
|
||||||
fill: ${colors.greyLight};
|
fill: #908ba1;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Published = styled.span`
|
export const Published = styled.span`
|
||||||
&& {
|
&& {
|
||||||
display: none;
|
color: #908ba1;
|
||||||
color: ${colors.greyLight};
|
|
||||||
${({ modifiers }) => modifiers};
|
${({ modifiers }) => modifiers};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -178,7 +178,7 @@ export const WrapperLink = styled(Link)`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
${OverviewItem} {
|
${OverviewItem} {
|
||||||
margin: 0 0 0 10px;
|
margin: 0 0 0 0;
|
||||||
}
|
}
|
||||||
${Overview} {
|
${Overview} {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -197,3 +197,22 @@ export const WrapperLink = styled(Link)`
|
||||||
`)};
|
`)};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ayush's style
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const PackageName = styled.span`
|
||||||
|
&& {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: #414141;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Divider from '@material-ui/core/Divider';
|
||||||
import Package from '../Package';
|
import Package from '../Package';
|
||||||
import Help from '../Help';
|
import Help from '../Help';
|
||||||
import { formatAuthor, formatLicense } from '../../utils/package';
|
import { formatLicense } from '../../utils/package';
|
||||||
|
|
||||||
import classes from './packageList.scss';
|
import classes from './packageList.scss';
|
||||||
|
|
||||||
|
@ -24,11 +25,14 @@ export default class PackageList extends React.Component {
|
||||||
const { packages } = this.props;
|
const { packages } = this.props;
|
||||||
return (
|
return (
|
||||||
packages.map((pkg, i) => {
|
packages.map((pkg, i) => {
|
||||||
const { name, version, description, time, keywords } = pkg;
|
const { name, version, description, time, keywords, dist } = pkg;
|
||||||
const author = formatAuthor(pkg.author);
|
const author = pkg.author;
|
||||||
const license = formatLicense(pkg.license);
|
const license = formatLicense(pkg.license);
|
||||||
return (
|
return (
|
||||||
<Package key={i} {...{ name, version, author, description, license, time, keywords }} />
|
<React.Fragment key={i}>
|
||||||
|
{i !== 0 && <Divider style={{ margin: 0 }}></Divider>}
|
||||||
|
<Package {...{ name, dist, version, author, description, license, time, keywords }} />
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,10 +11,10 @@ export const Wrapper = styled.span`
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
color: #9f9f9f;
|
color: #485a3e;
|
||||||
background-color: hsla(0, 0%, 51%, 0.1);
|
background-color: #f3f4f2;
|
||||||
padding: 0.22rem 0.4rem;
|
padding: 0.22rem 0.4rem;
|
||||||
margin: 5px 10px 0 0;
|
margin: 8px 8px 0 0;
|
||||||
${ellipsis('300px')};
|
${ellipsis('300px')};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -47,13 +47,17 @@ export function formatAuthor(author) {
|
||||||
let authorDetails = {
|
let authorDetails = {
|
||||||
name: DEFAULT_USER,
|
name: DEFAULT_USER,
|
||||||
email: '',
|
email: '',
|
||||||
avatar: '',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!author) {
|
||||||
|
return authorDetails;
|
||||||
|
}
|
||||||
|
|
||||||
if (isString(author)) {
|
if (isString(author)) {
|
||||||
authorDetails = {
|
authorDetails = {
|
||||||
...authorDetails,
|
...authorDetails,
|
||||||
name: author ? author : authorDetails.name,
|
name: author ? author : authorDetails.name,
|
||||||
|
email: author.email ? author.email : authorDetails.email,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +65,7 @@ export function formatAuthor(author) {
|
||||||
authorDetails = {
|
authorDetails = {
|
||||||
...authorDetails,
|
...authorDetails,
|
||||||
name: author.name ? author.name : authorDetails.name,
|
name: author.name ? author.name : authorDetails.name,
|
||||||
email: author.email ? author.email : authorDetails.email,
|
email: author.email ? author.email: authorDetails.email,
|
||||||
avatar: author.avatar ? author.avatar : authorDetails.avatar,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue