mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-06 22:40:26 -05:00
refactor: package sidebar in a single card
This commit is contained in:
parent
11166c0e23
commit
fc24c615b2
13 changed files with 220 additions and 212 deletions
|
@ -1,26 +1,27 @@
|
|||
/* eslint no-unused-vars: 0 */
|
||||
/**
|
||||
* @flow
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
import Card from '@material-ui/core/Card/index';
|
||||
import CardContent from '@material-ui/core/CardContent/index';
|
||||
import CopyToClipBoard from '../CopyToClipBoard';
|
||||
import CardHeader from '@material-ui/core/CardHeader/index';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import CardActions from '@material-ui/core/CardActions';
|
||||
import Typography from "@material-ui/core/Typography/index";
|
||||
import List from '@material-ui/core/List';
|
||||
// import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
|
||||
import { Heading, InstallItem } from './styles';
|
||||
class Authors extends Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{(context) => {
|
||||
{context => {
|
||||
return this.renderAuthor(context);
|
||||
}}
|
||||
</DetailContextConsumer>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
renderAuthor = ({ packageMeta }) => {
|
||||
const { author } = packageMeta.latest;
|
||||
|
@ -30,25 +31,14 @@ class Authors extends Component<any, any> {
|
|||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
{this.renderAvatar(author)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<List subheader={<Heading variant={'subheading'}>{'Author'}</Heading>}>
|
||||
<InstallItem>
|
||||
<Avatar alt={author.name} src={author.avatar} />
|
||||
<ListItemText primary={author.name} />
|
||||
</InstallItem>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
renderAvatar = ({name, email, url, avatar}) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<Avatar aria-label={name} src={avatar} />
|
||||
<Typography color={"textPrimary"} gutterBottom={true} variant={'caption'}>
|
||||
{name}
|
||||
</Typography>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Authors;
|
||||
|
|
23
src/webui/components/Author/styles.js
Normal file
23
src/webui/components/Author/styles.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
|
||||
export const Heading = styled(Typography)`
|
||||
&& {
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
`;
|
||||
|
||||
export const InstallItem = styled(ListItem)`
|
||||
&& {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
`;
|
|
@ -88,10 +88,7 @@ class Dependencies extends Component<any, any> {
|
|||
// $FlowFixMe
|
||||
renderDependencies({ packageMeta }) {
|
||||
const { latest } = packageMeta;
|
||||
// console.log('renderDependencies', latest);
|
||||
const { dependencies, devDependencies, peerDependencies } = latest;
|
||||
// console.log('dependencies', dependencies);
|
||||
// console.log('devDependencies', devDependencies);
|
||||
|
||||
return (
|
||||
<Content>
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import React, {Component} from 'react';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
import Typography from '@material-ui/core/Typography/index';
|
||||
import Grid from '@material-ui/core/Grid/index';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
|
||||
import Install from '../Install';
|
||||
import { Content } from './styles';
|
||||
import Authors from '../Author';
|
||||
import Author from '../Author';
|
||||
import License from '../License';
|
||||
import Repository from '../Repository';
|
||||
import Developers from '../Developers';
|
||||
|
||||
class DetailSidebar extends Component<any, any> {
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
class DetailSidebar extends Component {
|
||||
render() {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
|
@ -20,43 +22,35 @@ class DetailSidebar extends Component<any, any> {
|
|||
);
|
||||
};
|
||||
|
||||
renderSideBar = ({packageMeta, packageName}) => {
|
||||
renderSideBar = ({packageName, packageMeta}) => {
|
||||
return (
|
||||
<Content>
|
||||
<Grid container={true} spacing={24}>
|
||||
<Grid item={true} xs={12}>
|
||||
<>
|
||||
<Card>
|
||||
<CardContent>
|
||||
{this.renderTitle(packageName, packageMeta)}
|
||||
</Grid>
|
||||
<Grid item={true} xs={12}>
|
||||
{this.renderCopyCLI()}
|
||||
</Grid>
|
||||
<Grid item={true} xs={12}>
|
||||
{this.renderSecondLevel(8)}
|
||||
</Grid>
|
||||
<Grid item={true} xs={12}>
|
||||
{this.renderMaintainers()}
|
||||
</Grid>
|
||||
<Grid item={true} xs={12}>
|
||||
{this.renderContributors()}
|
||||
</Grid>
|
||||
<Grid item={true} xs={12}>
|
||||
{this.renderRepository()}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
{this.renderAuthor()}
|
||||
{this.renderMaintainers()}
|
||||
{this.renderContributors()}
|
||||
{this.renderLicense()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderTitle = (packageName, packageMeta) => {
|
||||
return (
|
||||
<>
|
||||
<Typography color={"textPrimary"} gutterBottom={true} variant={'title'}>
|
||||
{packageName}
|
||||
</Typography>
|
||||
<Typography color={"textSecondary"} gutterBottom={true} variant={'body2'}>
|
||||
{packageMeta.latest.description}
|
||||
</Typography>
|
||||
</>
|
||||
<List>
|
||||
<ListItem alignItems={"flex-start"}>
|
||||
<Avatar style={{textTransform: 'capitalize'}}>{packageName[0]}</Avatar>
|
||||
<ListItemText
|
||||
primary={<span style={{textTransform: 'capitalize'}}>{packageName}</span>}
|
||||
secondary={packageMeta.latest.description}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -72,12 +66,8 @@ class DetailSidebar extends Component<any, any> {
|
|||
return <Developers type={'contributors'} />;
|
||||
}
|
||||
|
||||
renderSecondLevel = (spacing = 24) => {
|
||||
return (
|
||||
<Grid container={true} spacing={spacing}>
|
||||
{this.renderAuthor()}
|
||||
</Grid>
|
||||
);
|
||||
renderLicense = () => {
|
||||
return <License />;
|
||||
}
|
||||
|
||||
renderRepository = () => {
|
||||
|
@ -85,18 +75,8 @@ class DetailSidebar extends Component<any, any> {
|
|||
}
|
||||
|
||||
renderAuthor = () => {
|
||||
return (
|
||||
<>
|
||||
<Grid item={true} xs={6}>
|
||||
<Authors />
|
||||
</Grid>
|
||||
<Grid item={true} xs={6}>
|
||||
<License />
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
return <Author />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default DetailSidebar;
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
import colors from '../../utils/styles/colors';
|
||||
|
||||
export const Content = styled.div`
|
||||
&& {
|
||||
padding: 10px;
|
||||
background-color: ${colors.white};
|
||||
}
|
||||
`;
|
|
@ -1,11 +1,12 @@
|
|||
import React, {Component} from 'react';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import Add from '@material-ui/icons/Add';
|
||||
import { Details, Heading, Content, CardContent, Fab } from './styles';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version';
|
||||
|
||||
import { Details, Heading, Content, Fab } from './styles';
|
||||
|
||||
interface Props {
|
||||
type: 'contributors' | 'maintainers'
|
||||
|
@ -17,8 +18,6 @@ class Developers extends Component<Props, any> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { visibleDevs } = this.state;
|
||||
console.log('aqui', visibleDevs);
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{({ packageMeta }) => {
|
||||
|
@ -31,12 +30,15 @@ class Developers extends Component<Props, any> {
|
|||
);
|
||||
};
|
||||
|
||||
handleLoadMore = () => {
|
||||
this.setState((prev) => ({ visibleDevs: prev.visibleDevs + 6 }));
|
||||
}
|
||||
|
||||
renderDevelopers = (developers) => {
|
||||
const { type } = this.props;
|
||||
const { visibleDevs } = this.state;
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<>
|
||||
<Heading variant={'subheading'}>{type}</Heading>
|
||||
<Content>
|
||||
{developers.slice(0, visibleDevs).map(developer => (
|
||||
|
@ -46,11 +48,11 @@ class Developers extends Component<Props, any> {
|
|||
<Fab onClick={this.handleLoadMore} size={'small'}><Add /></Fab>
|
||||
}
|
||||
</Content>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
renderDeveloperDetails = ({ name, avatar }) => {
|
||||
return (
|
||||
<Tooltip title={name}>
|
||||
|
@ -59,10 +61,6 @@ class Developers extends Component<Props, any> {
|
|||
);
|
||||
}
|
||||
|
||||
handleLoadMore = () => {
|
||||
this.setState((prev) => ({ visibleDevs: prev.visibleDevs + 6 }));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@ import Typography from '@material-ui/core/Typography';
|
|||
import { default as MuiFab } from '@material-ui/core/Fab';
|
||||
import colors from '../../utils/styles/colors';
|
||||
|
||||
import { default as MuiCardContent } from '@material-ui/core/CardContent/index';
|
||||
|
||||
export const Details = styled('span')`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -16,7 +14,7 @@ export const Details = styled('span')`
|
|||
`;
|
||||
|
||||
export const Content = styled('div')`
|
||||
margin: -5px;
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
> * {
|
||||
|
@ -24,12 +22,6 @@ export const Content = styled('div')`
|
|||
}
|
||||
`;
|
||||
|
||||
export const CardContent = styled(MuiCardContent)`
|
||||
&& {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Heading = styled(Typography)`
|
||||
&& {
|
||||
font-weight: 700;
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
import Card from '@material-ui/core/Card/index';
|
||||
import CardContent from '@material-ui/core/CardContent/index';
|
||||
import CopyToClipBoard from '../CopyToClipBoard';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import CardActions from '@material-ui/core/CardActions';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
|
||||
class Install extends Component<any, any> {
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
import CopyToClipBoard from '../CopyToClipBoard';
|
||||
|
||||
import { Heading, InstallItem, PackageMangerAvatar } from './styles';
|
||||
|
||||
class Install extends Component {
|
||||
render() {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
|
@ -20,27 +21,33 @@ class Install extends Component<any, any> {
|
|||
|
||||
renderCopyCLI = ({ packageName }) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<CopyToClipBoard text={`npm install ${packageName}`} />
|
||||
<CopyToClipBoard text={`pnpm install ${packageName}`} />
|
||||
<CopyToClipBoard text={`yarn add ${packageName}`} />
|
||||
<CardActions>
|
||||
{this.renderDownloadButton()}
|
||||
</CardActions>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<>
|
||||
<List subheader={<Heading variant={"subheading"}>{'Installation'}</Heading>}>
|
||||
{this.renderListItems(packageName)}
|
||||
</List>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderDownloadButton = () => {
|
||||
renderListItems = (packageName) => {
|
||||
return (
|
||||
<Button color={"primary"} size={'small'} variant={"contained"}>
|
||||
{'Download Tarball'}
|
||||
</Button>
|
||||
<>
|
||||
<InstallItem>
|
||||
<PackageMangerAvatar alt={"npm logo"} src={"https://cldup.com/Rg6WLgqccB.svg"} />
|
||||
<ListItemText primary={<CopyToClipBoard text={`npm install ${packageName}`} />} secondary={'Install using NPM'} />
|
||||
</InstallItem>
|
||||
<InstallItem>
|
||||
<PackageMangerAvatar alt={"yarn logo"} src={"https://raw.githubusercontent.com/yarnpkg/assets/master/yarn-kitten-circle.png"} />
|
||||
<ListItemText primary={<CopyToClipBoard text={`yarn add ${packageName}`} />} secondary={'Install using Yarn'} />
|
||||
</InstallItem>
|
||||
<InstallItem>
|
||||
<PackageMangerAvatar alt={"pnpm logo"} src={"https://pnpm.js.org/img/pnpm-no-name-with-frame.svg"} />
|
||||
<ListItemText primary={<CopyToClipBoard text={`pnpm install ${packageName}`} />} secondary={'Install using PNPM'} />
|
||||
</InstallItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Install;
|
||||
|
|
29
src/webui/components/Install/styles.js
Normal file
29
src/webui/components/Install/styles.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
|
||||
export const Heading = styled(Typography)`
|
||||
&& {
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
`;
|
||||
|
||||
export const InstallItem = styled(ListItem)`
|
||||
&& {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const PackageMangerAvatar = styled(Avatar)`
|
||||
&& {
|
||||
border-radius: 0px;
|
||||
}
|
||||
`;
|
|
@ -1,48 +1,47 @@
|
|||
/* eslint no-unused-vars: 0 */
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import NotesIcon from '@material-ui/icons/Notes';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
import Card from '@material-ui/core/Card/index';
|
||||
import CardContent from '@material-ui/core/CardContent/index';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Notes from '@material-ui/icons/Notes';
|
||||
import Typography from "@material-ui/core/Typography/index";
|
||||
|
||||
class License extends Component<any, any> {
|
||||
import { Heading } from './styles';
|
||||
|
||||
class License extends Component {
|
||||
render() {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{(context) => {
|
||||
return this.renderAuthor(context);
|
||||
return this.renderLicense(context);
|
||||
}}
|
||||
</DetailContextConsumer>
|
||||
);
|
||||
};
|
||||
|
||||
renderAuthor = ({packageMeta}) => {
|
||||
renderLicense = ({packageMeta}) => {
|
||||
const { license } = packageMeta.latest;
|
||||
if (!license) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent style={{ textAling: 'center'}}>
|
||||
{this.renderLicense(license)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<List subheader={<Heading variant={"subheading"}>{'License'}</Heading>}>
|
||||
{this.renderListItems(license)}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
renderLicense = (license) => {
|
||||
renderListItems = (license) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<Notes style={{ fontSize: 38 }} />
|
||||
<Typography color={"textPrimary"} gutterBottom={true} variant={'caption'}>
|
||||
{license}
|
||||
</Typography>
|
||||
</Fragment>
|
||||
<ListItem>
|
||||
<Avatar>
|
||||
<NotesIcon />
|
||||
</Avatar>
|
||||
<ListItemText primary={license} />
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
14
src/webui/components/License/styles.js
Normal file
14
src/webui/components/License/styles.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
export const Heading = styled(Typography)`
|
||||
&& {
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
`;
|
|
@ -1,61 +1,46 @@
|
|||
/* eslint no-unused-vars: 0 */
|
||||
/* eslint react/jsx-max-depth: 0 */
|
||||
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import React, {Component} from 'react';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
|
||||
import { DetailContextConsumer } from '../../pages/version/index';
|
||||
import Card from '@material-ui/core/Card/index';
|
||||
import CardContent from '@material-ui/core/CardContent/index';
|
||||
import Grid from '@material-ui/core/Grid/index';
|
||||
import GitHub from '../../icons/GitHub';
|
||||
import CopyToClipBoard from '../CopyToClipBoard';
|
||||
import BugReport from '@material-ui/icons/BugReport';
|
||||
import CardActions from '@material-ui/core/CardActions/index';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import {GridRepo} from './styles';
|
||||
import Github from '../../icons/GitHub';
|
||||
|
||||
import {Heading} from './styles';
|
||||
|
||||
class Repository extends Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{(context) => {
|
||||
return this.renderAuthor(context);
|
||||
return this.renderRepository(context);
|
||||
}}
|
||||
</DetailContextConsumer>
|
||||
);
|
||||
};
|
||||
|
||||
renderAuthor = ({packageMeta}) => {
|
||||
const { repository, bugs } = packageMeta.latest;
|
||||
renderRepository = ({packageMeta}) => {
|
||||
const { repository } = packageMeta.latest;
|
||||
if (!repository) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { url } = repository;
|
||||
return (
|
||||
<Card>
|
||||
<CardContent style={{ textAling: 'center'}}>
|
||||
<GridRepo container={true} spacing={24}>
|
||||
{this.renderRepository(repository, bugs)}
|
||||
</GridRepo>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button size={"small"}>{'Open Bugs'}</Button>
|
||||
<Button size={"small"}>{'Open Repository'}</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
renderRepository = ({url, type}, bugs) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid item={true} xs={3}>
|
||||
<GitHub style={{ fontSize: 45 }} />
|
||||
</Grid>
|
||||
<Grid item={true} xs={9}>
|
||||
<CopyToClipBoard text={url} />
|
||||
</Grid>
|
||||
</Fragment>
|
||||
<>
|
||||
<List dense={true} subheader={<Heading variant={"subheading"}>{'Repository'}</Heading>}>
|
||||
<ListItem>
|
||||
<Avatar>
|
||||
<Github style={{ fontSize: 45, backgroundColor: '#24292e' }} />
|
||||
</Avatar>
|
||||
<ListItemText primary={<CopyToClipBoard text={<a href={url}>{url}</a>} />} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,14 @@
|
|||
|
||||
import styled from 'react-emotion';
|
||||
import Grid from '@material-ui/core/Grid/index';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
export const Heading = styled(Typography)`
|
||||
&& {
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
`;
|
||||
|
||||
export const GridRepo = styled(Grid)`
|
||||
&& {
|
||||
|
|
Loading…
Reference in a new issue