forked from sombochea/verdaccio-ui
fix: added packageMeta type
This commit is contained in:
parent
c0b0189cc6
commit
3c54b116c9
@ -1,4 +1,4 @@
|
|||||||
import React, { Component, ReactNode } from 'react';
|
import React, { Component, ReactNode, ReactElement } from 'react';
|
||||||
|
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
@ -8,18 +8,18 @@ import { DetailContextConsumer } from '../../pages/version/Version';
|
|||||||
import { Heading, AuthorListItem } from './styles';
|
import { Heading, AuthorListItem } from './styles';
|
||||||
import { isEmail } from '../../utils/url';
|
import { isEmail } from '../../utils/url';
|
||||||
|
|
||||||
class Authors extends Component<any, any> {
|
class Authors extends Component {
|
||||||
render() {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
{(context: any) => {
|
{context => {
|
||||||
return context && context.packageMeta && this.renderAuthor(context.packageMeta);
|
return context && context.packageMeta && this.renderAuthor(context.packageMeta);
|
||||||
}}
|
}}
|
||||||
</DetailContextConsumer>
|
</DetailContextConsumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLinkForMail(email: string, avatarComponent: ReactNode, packageName: string, version: string) {
|
public renderLinkForMail(email: string, avatarComponent: ReactNode, packageName: string, version: string): ReactElement<HTMLElement> | ReactNode {
|
||||||
if (!email || isEmail(email) === false) {
|
if (!email || isEmail(email) === false) {
|
||||||
return avatarComponent;
|
return avatarComponent;
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ class Authors extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAuthor = packageMeta => {
|
public renderAuthor = packageMeta => {
|
||||||
const { author, name: packageName, version } = packageMeta.latest;
|
const { author, name: packageName, version } = packageMeta.latest;
|
||||||
|
|
||||||
if (!author) {
|
if (!author) {
|
||||||
|
@ -23,10 +23,10 @@ interface Dist {
|
|||||||
unpackedSize: number;
|
unpackedSize: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
export interface PackageInterface {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
time: string;
|
time?: number | string;
|
||||||
author: Author;
|
author: Author;
|
||||||
description?: string;
|
description?: string;
|
||||||
keywords?: string[];
|
keywords?: string[];
|
||||||
@ -35,6 +35,7 @@ interface Props {
|
|||||||
bugs?: Bugs;
|
bugs?: Bugs;
|
||||||
dist?: Dist;
|
dist?: Dist;
|
||||||
}
|
}
|
||||||
|
// interface Props {} & PackageInterface;
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Author,
|
Author,
|
||||||
@ -56,7 +57,7 @@ import {
|
|||||||
} from './styles';
|
} from './styles';
|
||||||
import { isURL } from '../../utils/url';
|
import { isURL } from '../../utils/url';
|
||||||
|
|
||||||
const Package: React.FC<Props> = ({
|
const Package: React.FC<PackageInterface> = ({
|
||||||
author: { name: authorName, avatar: authorAvatar },
|
author: { name: authorName, avatar: authorAvatar },
|
||||||
bugs,
|
bugs,
|
||||||
description,
|
description,
|
||||||
|
@ -1,25 +1,21 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment, ReactElement } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from '@material-ui/core/Divider';
|
||||||
|
|
||||||
import Package from '../Package';
|
import Package from '../Package';
|
||||||
import Help from '../Help';
|
import Help from '../Help';
|
||||||
import { formatLicense } from '../../utils/package';
|
import { formatLicense } from '../../utils/package';
|
||||||
|
import { PackageInterface } from '../Package/Package';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import classes from './packageList.scss';
|
import classes from './packageList.scss';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
packages: any;
|
packages: PackageInterface[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PackageList extends React.Component<Props, {}> {
|
export default class PackageList extends React.Component<Props, {}> {
|
||||||
static propTypes = {
|
public render(): ReactElement<HTMLElement> {
|
||||||
packages: PropTypes.array,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<div className={'package-list-items'}>
|
<div className={'package-list-items'}>
|
||||||
<div className={classes.pkgContainer}>{this.hasPackages() ? this.renderPackages() : <Help />}</div>
|
<div className={classes.pkgContainer}>{this.hasPackages() ? this.renderPackages() : <Help />}</div>
|
||||||
@ -27,20 +23,19 @@ export default class PackageList extends React.Component<Props, {}> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPackages() {
|
private hasPackages(): boolean {
|
||||||
const { packages } = this.props;
|
const { packages } = this.props;
|
||||||
return packages.length > 0;
|
return packages.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPackages = () => {
|
private renderPackages = () => {
|
||||||
return <>{this.renderList()}</>;
|
return <>{this.renderList()}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderList = () => {
|
private renderList = () => {
|
||||||
const { packages } = this.props;
|
const { packages } = this.props;
|
||||||
return packages.map((pkg, i) => {
|
return packages.map((pkg, i) => {
|
||||||
const { name, version, description, time, keywords, dist, homepage, bugs } = pkg;
|
const { name, version, description, time, keywords, dist, homepage, bugs, author } = pkg;
|
||||||
const author = pkg.author;
|
|
||||||
// TODO: move format license to API side.
|
// TODO: move format license to API side.
|
||||||
const license = formatLicense(pkg.license);
|
const license = formatLicense(pkg.license);
|
||||||
return (
|
return (
|
||||||
|
@ -13,7 +13,7 @@ import { Heading, GithubLink, RepositoryListItem } from './styles';
|
|||||||
import git from './img/git.png';
|
import git from './img/git.png';
|
||||||
import { isURL } from '../../utils/url';
|
import { isURL } from '../../utils/url';
|
||||||
|
|
||||||
class Repository extends Component<any, any> {
|
class Repository extends Component {
|
||||||
public render(): ReactElement<HTMLElement> {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
@ -33,12 +33,7 @@ class Repository extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderRepository = packageMeta => {
|
private renderRepository = packageMeta => {
|
||||||
const {
|
const { repository: { url = null } = {} } = packageMeta.latest;
|
||||||
repository: {
|
|
||||||
// @ts-ignore
|
|
||||||
url,
|
|
||||||
} = {},
|
|
||||||
} = packageMeta.latest;
|
|
||||||
|
|
||||||
if (!url || isURL(url) === false) {
|
if (!url || isURL(url) === false) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -8,7 +8,7 @@ import { formatDateDistance } from '../../utils/package';
|
|||||||
|
|
||||||
import { Heading, Spacer, ListItemText } from './styles';
|
import { Heading, Spacer, ListItemText } from './styles';
|
||||||
|
|
||||||
class UpLinks extends React.PureComponent<any> {
|
class UpLinks extends React.PureComponent<{}> {
|
||||||
public render(): ReactElement<HTMLElement> {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
|
@ -4,7 +4,7 @@ import PackageList from '../../components/PackageList';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isUserLoggedIn: boolean;
|
isUserLoggedIn: boolean;
|
||||||
packages: any[];
|
packages: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Home: React.FC<Props> = ({ packages }) => (
|
const Home: React.FC<Props> = ({ packages }) => (
|
||||||
|
@ -3,31 +3,44 @@ import Grid from '@material-ui/core/Grid';
|
|||||||
import Loading from '../../components/Loading/Loading';
|
import Loading from '../../components/Loading/Loading';
|
||||||
import DetailContainer from '../../components/DetailContainer/DetailContainer';
|
import DetailContainer from '../../components/DetailContainer/DetailContainer';
|
||||||
import DetailSidebar from '../../components/DetailSidebar/DetailSidebar';
|
import DetailSidebar from '../../components/DetailSidebar/DetailSidebar';
|
||||||
import { callDetailPage, DetailPage } from '../../utils/calls';
|
import { callDetailPage } from '../../utils/calls';
|
||||||
import { getRouterPackageName } from '../../utils/package';
|
import { getRouterPackageName } from '../../utils/package';
|
||||||
import NotFound from '../../components/NotFound';
|
import NotFound from '../../components/NotFound';
|
||||||
|
import { PackageMetaInterface } from '../../../types/packageMeta';
|
||||||
|
|
||||||
export interface DetailContextProps {
|
export interface DetailContextProps {
|
||||||
packageMeta: any;
|
packageMeta: PackageMetaInterface;
|
||||||
readMe: any;
|
readMe: string;
|
||||||
packageName: string;
|
packageName: string;
|
||||||
enableLoading: () => void;
|
enableLoading: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DetailContext = React.createContext<DetailContextProps | null>(null);
|
export const DetailContext = React.createContext<Partial<DetailContextProps>>({});
|
||||||
|
|
||||||
export interface VersionPageConsumerProps {
|
export interface VersionPageConsumerProps {
|
||||||
packageMeta: any;
|
packageMeta: PackageMetaInterface;
|
||||||
readMe: any;
|
readMe: string;
|
||||||
packageName: any;
|
packageName: string;
|
||||||
enableLoading: any;
|
enableLoading: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DetailContextProvider: Provider<VersionPageConsumerProps | null> = DetailContext.Provider;
|
export const DetailContextProvider: Provider<Partial<VersionPageConsumerProps>> = DetailContext.Provider;
|
||||||
export const DetailContextConsumer: Consumer<VersionPageConsumerProps | null> = DetailContext.Consumer;
|
export const DetailContextConsumer: Consumer<Partial<VersionPageConsumerProps>> = DetailContext.Consumer;
|
||||||
|
|
||||||
class VersionPage extends Component<any, any> {
|
interface PropsInterface {
|
||||||
constructor(props: any) {
|
match: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateInterface {
|
||||||
|
readMe: string;
|
||||||
|
packageName: string;
|
||||||
|
packageMeta: PackageMetaInterface | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
notFound: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class VersionPage extends Component<PropsInterface, StateInterface> {
|
||||||
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -39,7 +52,7 @@ class VersionPage extends Component<any, any> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDerivedStateFromProps(nextProps: any, prevState: any): any {
|
public static getDerivedStateFromProps(nextProps, prevState): { packageName?: string; isLoading: boolean; notFound?: boolean } | null {
|
||||||
const { match } = nextProps;
|
const { match } = nextProps;
|
||||||
const packageName = getRouterPackageName(match);
|
const packageName = getRouterPackageName(match);
|
||||||
|
|
||||||
@ -65,7 +78,7 @@ class VersionPage extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* eslint no-unused-vars: 0 */
|
/* eslint no-unused-vars: 0 */
|
||||||
public async componentDidUpdate(nextProps: any, prevState: any): Promise<void> {
|
public async componentDidUpdate(nextProps, prevState: StateInterface): Promise<void> {
|
||||||
const { packageName } = this.state;
|
const { packageName } = this.state;
|
||||||
if (packageName !== prevState.packageName) {
|
if (packageName !== prevState.packageName) {
|
||||||
const { readMe, packageMeta } = await callDetailPage(packageName);
|
const { readMe, packageMeta } = await callDetailPage(packageName);
|
||||||
|
@ -12,7 +12,12 @@ const NotFound = asyncComponent(() => import('./components/NotFound'));
|
|||||||
const VersionPackage = asyncComponent(() => import('./pages/version/Version'));
|
const VersionPackage = asyncComponent(() => import('./pages/version/Version'));
|
||||||
const HomePage = asyncComponent(() => import('./pages/home'));
|
const HomePage = asyncComponent(() => import('./pages/home'));
|
||||||
|
|
||||||
class RouterApp extends Component<any, any> {
|
interface RouterAppProps {
|
||||||
|
onLogout: () => void;
|
||||||
|
onToggleLoginModal: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterApp extends Component<RouterAppProps> {
|
||||||
public render(): ReactElement<HTMLDivElement> {
|
public render(): ReactElement<HTMLDivElement> {
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
|
@ -6,9 +6,9 @@ import '../../types';
|
|||||||
* @param {object} response
|
* @param {object} response
|
||||||
* @returns {promise}
|
* @returns {promise}
|
||||||
*/
|
*/
|
||||||
function handleResponseType(response): Promise<any> {
|
function handleResponseType(response: Response): Promise<[boolean, Blob | string]> | Promise<void> {
|
||||||
if (response.headers) {
|
if (response.headers) {
|
||||||
const contentType = response.headers.get('Content-Type');
|
const contentType = response.headers.get('Content-Type') as string;
|
||||||
if (contentType.includes('application/pdf')) {
|
if (contentType.includes('application/pdf')) {
|
||||||
return Promise.all([response.ok, response.blob()]);
|
return Promise.all([response.ok, response.blob()]);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import API from './api';
|
import API from './api';
|
||||||
|
import { PackageMetaInterface } from 'types/packageMeta';
|
||||||
|
|
||||||
export interface DetailPage {
|
export interface DetailPage {
|
||||||
readMe: any;
|
readMe: string;
|
||||||
packageMeta: any;
|
packageMeta: PackageMetaInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function callDetailPage(packageName): Promise<DetailPage> {
|
export async function callDetailPage(packageName): Promise<DetailPage> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* tslint:disable */
|
||||||
export default function fileSizeSI(a?: any, b?: any, c?: any, d?: any, e?: any) {
|
export default function fileSizeSI(a?: any, b?: any, c?: any, d?: any, e?: any) {
|
||||||
return ((b = Math), (c = b.log), (d = 1e3), (e = (c(a) / c(d)) | 0), a / b.pow(d, e)).toFixed(2) + ' ' + (e ? 'kMGTPEZY'[--e] + 'B' : 'Bytes');
|
return ((b = Math), (c = b.log), (d = 1e3), (e = (c(a) / c(d)) | 0), a / b.pow(d, e)).toFixed(2) + ' ' + (e ? 'kMGTPEZY'[--e] + 'B' : 'Bytes');
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,34 @@ import { Base64 } from 'js-base64';
|
|||||||
import API from './api';
|
import API from './api';
|
||||||
import { HEADERS } from '../../lib/constants';
|
import { HEADERS } from '../../lib/constants';
|
||||||
|
|
||||||
export function isTokenExpire(token?: any) {
|
interface PayloadInterface {
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTokenExpire(token?: string): boolean {
|
||||||
if (!isString(token)) {
|
if (!isString(token)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [, payload]: any = token.split('.');
|
const [, payload] = token.split('.');
|
||||||
|
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let exp: number;
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(Base64.decode(payload));
|
exp = JSON.parse(Base64.decode(payload)).exp;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line
|
|
||||||
console.error('Invalid token:', error, token);
|
console.error('Invalid token:', error, token);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payload.exp || !isNumber(payload.exp)) {
|
if (!exp || !isNumber(exp)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Report as expire before (real expire time - 30s)
|
// Report as expire before (real expire time - 30s)
|
||||||
const jsTimestamp = payload.exp * 1000 - 30000;
|
const jsTimestamp = exp * 1000 - 30000;
|
||||||
const expired = Date.now() >= jsTimestamp;
|
const expired = Date.now() >= jsTimestamp;
|
||||||
|
|
||||||
return expired;
|
return expired;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import parseXSS from 'xss';
|
import parseXSS from 'xss';
|
||||||
|
|
||||||
export function preventXSS(text: string) {
|
export function preventXSS(text: string): string {
|
||||||
const encodedText = parseXSS.filterXSS(text);
|
const encodedText = parseXSS.filterXSS(text);
|
||||||
|
|
||||||
return encodedText;
|
return encodedText;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* CSS to represent truncated text with an ellipsis.
|
* CSS to represent truncated text with an ellipsis.
|
||||||
*/
|
*/
|
||||||
export function ellipsis(width: string | number) {
|
export function ellipsis(width: string | number): {} {
|
||||||
return {
|
return {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
maxWidth: width,
|
maxWidth: width,
|
||||||
@ -24,7 +24,7 @@ interface SpacingShortHand<type> {
|
|||||||
|
|
||||||
const positionMap = ['Top', 'Right', 'Bottom', 'Left'];
|
const positionMap = ['Top', 'Right', 'Bottom', 'Left'];
|
||||||
|
|
||||||
export function spacing(property: 'padding' | 'margin', ...values: SpacingShortHand<number | string>[]) {
|
export function spacing(property: 'padding' | 'margin', ...values: SpacingShortHand<number | string>[]): {} {
|
||||||
const [firstValue = 0, secondValue = 0, thirdValue = 0, fourthValue = 0] = values;
|
const [firstValue = 0, secondValue = 0, thirdValue = 0, fourthValue = 0] = values;
|
||||||
const valuesWithDefaults = [firstValue, secondValue, thirdValue, fourthValue];
|
const valuesWithDefaults = [firstValue, secondValue, thirdValue, fourthValue];
|
||||||
let styles = {};
|
let styles = {};
|
||||||
|
7
types/custom.d.ts
vendored
7
types/custom.d.ts
vendored
@ -1,10 +1,7 @@
|
|||||||
// https://stackoverflow.com/questions/44717164/unable-to-import-svg-files-in-typescript
|
// https://stackoverflow.com/questions/44717164/unable-to-import-svg-files-in-typescript
|
||||||
declare module '*.svg' {
|
declare module '*.svg' {
|
||||||
const content: any;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.png' {
|
declare module '*.png';
|
||||||
const content: any;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
6
types/packageMeta.ts
Normal file
6
types/packageMeta.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface PackageMetaInterface {
|
||||||
|
latest: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
_uplinks: {};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user