Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fixes and Plugin Versions content #231

Merged
merged 14 commits into from
Aug 6, 2021
29 changes: 25 additions & 4 deletions src/components/Plugin/Plugin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ export class Plugin extends Component {
async componentDidMount() {
let { pluginData } = this.state;

if (!pluginData) {
if (!pluginData)
pluginData = await this.fetchPluginData();
}
else
this.fetchPluginVersions(pluginData.name)

this.setState({ pluginData, loading: false });
if (this.isLoggedIn()) {
Expand All @@ -65,9 +66,13 @@ export class Plugin extends Component {

onStarClicked = () => {
if (this.isLoggedIn()) {
return this.isFavorite() ? this.unfavPlugin() : this.favPlugin();
if (this.isFavorite())
this.unfavPlugin();
else
this.favPlugin();
}
return Promise.resolve();
else
this.showNotifications(new Error('You need to be logged in!'))
zrthxn marked this conversation as resolved.
Show resolved Hide resolved
}

favPlugin = async () => {
Expand Down Expand Up @@ -132,6 +137,22 @@ export class Plugin extends Component {
}
}

async fetchPluginVersions(name) {
try {
const versions = await this.client.getPlugins({ limit: 10e6, name });
return this.setState((prevState) => ({
pluginData: {
...prevState.pluginData,
versions: versions.data,
url: versions.url,
}
}));
} catch (e) {
this.showNotifications(new HttpApiCallError(e));
return e
}
}

async fetchIsPluginStarred({ name }) {
try {
const response = await this.client.getPluginStars({ plugin_name: name });
Expand Down
264 changes: 143 additions & 121 deletions src/components/Plugin/components/PluginBody/PluginBody.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Required for setting README html

import React, { useState, useEffect, useCallback } from 'react';
import { Link } from 'react-router-dom';
import {
Grid,
GridItem,
Expand All @@ -13,6 +12,7 @@ import {
Popover,
ClipboardCopy,
Button,
ExpandableSection,
} from '@patternfly/react-core';
import { DownloadIcon, UserAltIcon } from '@patternfly/react-icons';
import PropTypes from 'prop-types';
Expand All @@ -22,8 +22,8 @@ import { sanitize } from 'dompurify';
import './PluginBody.css';
import ErrorNotification from '../../../Notification';
import HttpApiCallError from '../../../../errors/HttpApiCallError';
// eslint-disable-next-line import/named
import { GithubAPIRepoError, GithubAPIProfileError, GithubAPIReadmeError } from '../../../../errors/GithubError';
import { removeEmail } from '../../../../utils/common';

const PluginBody = ({ pluginData }) => {
const [activeTab, setActiveTab] = useState(1);
Expand Down Expand Up @@ -81,7 +81,37 @@ const PluginBody = ({ pluginData }) => {
fetchRepo();
}, [fetchReadme, fetchRepoData, pluginData.public_repo, showNotifications]);

const versionString = (v) => `Version ${v}${v === pluginData.version ? ' (This)' : ''}`;
const InstallButton = () => {
if (pluginData.version)
return <>
<ClipboardCopy isReadOnly>
{pluginData.url}
</ClipboardCopy>
</>


if (pluginData.versions)
return <>
<p><b>Version { pluginData.versions[0].version }</b></p>
<ClipboardCopy isReadOnly>
{pluginData.url + pluginData.versions[0].id}
zrthxn marked this conversation as resolved.
Show resolved Hide resolved
</ClipboardCopy>
<br />
<ExpandableSection toggleText="More Versions">
zrthxn marked this conversation as resolved.
Show resolved Hide resolved
{
pluginData.versions.slice(1).map((version) =>(
<div key={version.version}>
<a href={`/p/${version.id}`}>
Version {version.version}
</a>
</div>
))
}
</ExpandableSection>
</>

return <p>Loading</p>
}

return (
<>
Expand All @@ -101,132 +131,119 @@ const PluginBody = ({ pluginData }) => {

<article>
<Card id="plugin-body">
<Tabs activeKey={activeTab} onSelect={handleTabClick}>
<Tab eventKey={1} title={<TabTitleText>Overview</TabTitleText>}>
<Grid hasGutter>
<GridItem md={8} sm={12}>
<Grid hasGutter>
<GridItem md={8} sm={12}>
<Tabs activeKey={activeTab} onSelect={handleTabClick}>
<Tab eventKey={1} title={<TabTitleText>Overview</TabTitleText>}>
<div style={{ color: 'gray', margin: '1em 0' }}>README</div>
{ readme ? <div dangerouslySetInnerHTML={{ __html: readme }} /> : null }
</GridItem>
<GridItem md={4} sm={12}>
<div className="plugin-body-side-col">
<div className="plugin-body-detail-section">
<h4>Install</h4>
<p>Click to install this plugin to your ChRIS Server.</p>
<br />
<Popover
position="bottom"
headerContent={<b>Install to your ChRIS server</b>}
bodyContent={() => (
<div>
<p>
Copy and Paste the URL below into your ChRIS Admin Dashboard
to install this plugin.
</p>
<br />
<ClipboardCopy isReadOnly>
{
pluginData.url ? pluginData.url
: `${process.env.REACT_APP_STORE_URL}plugins/${pluginData.id}/`
}
</ClipboardCopy>
</div>
)}
>
<Button isBlock style={{ fontSize: '1.125em' }}>
<DownloadIcon />
{' '}
Install to ChRIS
</Button>
</Popover>
</div>
<div className="plugin-body-detail-section">
<h4>Repository</h4>
<a href={pluginData.public_repo}>
{pluginData.public_repo}
</a>
</div>
<div className="plugin-body-detail-section">
<h4>Contributors</h4>
{
pluginData.authors.map((author) => (
<a key={author} href={`#${author}`}>
<p>
<UserAltIcon />
{' '}
{author}
</p>
</a>
))
}

<br />
<a className="pf-m-link" href={`${pluginData.public_repo}/graphs/contributors`}>
View all contributors
</a>
</div>
<div className="plugin-body-detail-section">
<h4>Plugin ID</h4>
{pluginData.id}
</div>
<div className="plugin-body-detail-section">
<h4>License</h4>
{ repoData ? repoData.license.name : pluginData.license }
</div>
<div className="plugin-body-detail-section">
<h4>Content Type</h4>
{pluginData.type}
</div>
<div className="plugin-body-detail-section">
<h4>Date added</h4>
{(new Date(pluginData.creation_date)).toDateString()}
</div>
</div>
</GridItem>
</Grid>
</Tab>

<Tab eventKey={2} title={<TabTitleText>Parameters</TabTitleText>}>
<Grid hasGutter className="plugin-body-main">
<GridItem sm={12}>Parameters content</GridItem>
</Grid>
</Tab>

<Tab eventKey={3} title={<TabTitleText>Versions</TabTitleText>}>
<Grid hasGutter className="plugin-body-main">
<GridItem sm={12}>
{
pluginData.versions !== undefined ? (
<>
<h2>Versions of this plugin</h2>
</Tab>

<Tab eventKey={2} title={<TabTitleText>Parameters</TabTitleText>}>
<Grid hasGutter className="plugin-body-main">
<GridItem sm={12}>Parameters content</GridItem>
</Grid>
</Tab>

{
pluginData.versions &&
<Tab eventKey={3} title={<TabTitleText>Versions</TabTitleText>}>
<Grid hasGutter className="plugin-body-main">
<GridItem sm={12}>
{
Object.keys(pluginData.versions).length > 1
? Object.keys(pluginData.versions).map((version) => (
<div key={version}>
<Link
href={`/plugin/${pluginData.name}/${version}`}
to={`/plugin/${pluginData.name}/${version}`}
>
{versionString(version)}
</Link>
</div>
)) : (
pluginData.versions ? (
<>
<h2>Versions of this plugin</h2>
{ pluginData.versions.map((version) => (
<a key={version} href={`/p/${version.id}`}>
<b>Version { version.version }</b>
</a>
))}
</>
) : (
<div>
{versionString(Object.keys(pluginData.versions).toString())}
<p>This is the only version of this plugin.</p>
</div>
)
}
</>
) : (
}
</GridItem>
</Grid>
</Tab>
}
</Tabs>
</GridItem>

<GridItem md={4} sm={12}>
<div className="plugin-body-side-col">
<div className="plugin-body-detail-section">
<h4>Install</h4>
<p>Click to install this plugin to your ChRIS Server.</p>
<br />
<Popover
position="bottom"
maxWidth="30rem"
headerContent={<b>Install to your ChRIS server</b>}
bodyContent={() => (
<div>
<p>This is the only version of this plugin.</p>
<p>
Copy and Paste the URL below into your ChRIS Admin Dashboard
to install this plugin.
</p>
<br />
<InstallButton/>
</div>
)
)}
>
<Button isBlock style={{ fontSize: '1.125em' }}>
<DownloadIcon />
{' '}
Install to ChRIS
</Button>
</Popover>
</div>
<div className="plugin-body-detail-section">
<h4>Repository</h4>
<a href={pluginData.public_repo}>
{pluginData.public_repo}
</a>
</div>

<div className="plugin-body-detail-section">
<h4>Contributors</h4>
{
Array.isArray(pluginData.authors) ?
pluginData.authors.map((author) => (
<a key={author} href={`#${author}`}>
<p><UserAltIcon /> {author}</p>
</a>
))
:
<a key={pluginData.authors} href={`#${pluginData.authors}`}>
<p><UserAltIcon /> {removeEmail(pluginData.authors)}</p>
</a>
}
</GridItem>
</Grid>
</Tab>
</Tabs>

<br />
<a className="pf-m-link" href={`${pluginData.public_repo}/graphs/contributors`}>
View all contributors
</a>
</div>

<div className="plugin-body-detail-section">
<h4>License</h4>
{ repoData ? repoData.license.name : pluginData.license }
</div>
<div className="plugin-body-detail-section">
<h4>Content Type</h4>
{pluginData.type}
</div>
<div className="plugin-body-detail-section">
<h4>Date added</h4>
{(new Date(pluginData.creation_date)).toDateString()}
</div>
</div>
</GridItem>
</Grid>
</Card>
</article>
</>
Expand All @@ -242,7 +259,12 @@ PluginBody.propTypes = {
public_repo: PropTypes.string,
type: PropTypes.string,
authorURL: PropTypes.string,
authors: PropTypes.arrayOf(PropTypes.string),
authors: PropTypes.oneOfType([
PropTypes.string, PropTypes.arrayOf(PropTypes.string)
]),
versions: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.any), PropTypes.any
])
}).isRequired,
};

Expand Down
13 changes: 1 addition & 12 deletions src/components/Plugins/Plugins.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,12 @@ import NotFound from '../NotFound/NotFound';
import ChrisStore from '../../store/ChrisStore';
import HttpApiCallError from '../../errors/HttpApiCallError';
import ErrorNotification from '../Notification';
import { removeEmail } from '../../utils/common';

import './Plugins.css';

const CATEGORIES = ['FreeSurfer', 'MRI', 'Segmentation'];

// Remove email from author
export const removeEmail = (authors) => {
const emailRegex = /(<|\().+?@.{2,}?\..{2,}?(>|\))/g;
// Match '<' or '(' at the beginning and end
// Match <string>@<host>.<tld> inside brackets
if (!Array.isArray(authors))
// eslint-disable-next-line no-param-reassign
authors = [ authors ]

return authors.map((author) => author.replace(emailRegex, "").trim());
}

/**
* A page showing a list of ChRIS plugins, according to the search
* specified in the URI's query string.
Expand Down
Loading