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

Display repo and maintainers as byline #821

Merged
merged 10 commits into from
Nov 26, 2019
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ rules:
prefer-destructuring: off
implicit-arrow-linebreak: off
jsx-quotes: off
no-use-before-define: ["error", { "functions": false }]
parserOptions:
ecmaVersion: 6
sourceType: module
Expand Down
3 changes: 3 additions & 0 deletions src/actions/recomputeReduxState.js
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,9 @@ const createMetadataStateFromJSON = (json) => {
if (json.meta.maintainers) {
metadata.maintainers = json.meta.maintainers;
}
if (json.meta.build_url) {
metadata.buildUrl = json.meta.build_url;
}
if (json.meta.genome_annotations) {
metadata.genomeAnnotations = json.meta.genome_annotations;
}
Expand Down
19 changes: 0 additions & 19 deletions src/components/controls/choose-dataset.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import React from "react";
import { connect } from "react-redux";
import { withTheme } from 'styled-components';
import ChooseDatasetSelect from "./choose-dataset-select";
import { SidebarHeader } from "./styles";

const BuildLink = withTheme((props) => {
const niceUrl = props.url.replace(/^(http[s]?:\/\/)/, "");
return (
<div style={{ fontSize: 14, marginTop: 5, marginBottom: 10, color: props.theme.color}}>
<i className="fa fa-clone fa-lg" aria-hidden="true"/>
<span style={{position: "relative", paddingLeft: 10}}/>
<a href={props.url} target="_blank">{niceUrl}</a>
</div>
);
});

// const DroppedFiles = withTheme((props) => {
// /* TODO: this shouldn't be in the auspice src, rather injected as an extension when needed */
// return (
Expand Down Expand Up @@ -47,17 +35,13 @@ class ChooseDataset extends React.Component {
.replace(/\/$/, '')
.split(":")[0];
const displayedDataset = displayedDatasetString.split("/");
let optionForCurrentDataset = {};
const options = [[]];

this.props.available.datasets.forEach((d) => {
const firstField = d.request.split("/")[0];
if (!options[0].includes(firstField)) {
options[0].push(firstField);
}
if (displayedDatasetString === d.request) {
optionForCurrentDataset = d;
}
});


Expand All @@ -78,9 +62,6 @@ class ChooseDataset extends React.Component {
return (
<>
<SidebarHeader>Dataset</SidebarHeader>
{optionForCurrentDataset.buildUrl ? (
<BuildLink url={optionForCurrentDataset.buildUrl}/>
) : null}
{options.map((option, optionIdx) => (
<ChooseDatasetSelect
key={option}
Expand Down
27 changes: 2 additions & 25 deletions src/components/framework/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,23 +364,6 @@ class Footer extends React.Component {
</button>
);
}
hasMaintainers() {
return Object.prototype.hasOwnProperty.call(this.props.metadata, "maintainers")
}
renderMaintainers() {
const renderLink = (m) => (<a href={m.url} target="_blank">{m.name}</a>);
return (
<span style={{textAlign: "center"}}>
{"Build maintained by "}
{this.props.metadata.maintainers.map((m, i) => (
<React.Fragment key={m.name}>
{m.url ? renderLink(m) : m.name}
{i === this.props.metadata.maintainers.length-1 ? "" : i === this.props.metadata.maintainers.length-2 ? " and " : ", "}
</React.Fragment>
))}
</span>
);
}
getCitation() {
return (
<span>
Expand Down Expand Up @@ -411,21 +394,15 @@ class Footer extends React.Component {
);
})}
<Flex style={styles.fineprint}>
{this.hasMaintainers() ? (
<>
{this.renderMaintainers()}
{dot}
</>
) : null}
{this.getUpdated()}
{dot}
{this.downloadDataButton()}
{dot}
{"Auspice v" + version}
</Flex>
<div style={{height: "3px"}}/>
<Flex style={styles.fineprint}>
{this.getCitation()}
{dot}
{"Auspice v" + version}
</Flex>
</div>
</div>
Expand Down
101 changes: 101 additions & 0 deletions src/components/info/byline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from "react";
import { headerFont } from "../../globalStyles";

const styles = {
avatar: {
marginRight: 5,
marginBottom: 2
},
byline: {
fontFamily: headerFont,
fontSize: 15,
marginLeft: 2,
marginTop: 5,
marginBottom: 5,
fontWeight: 500,
color: "#555",
lineHeight: 1.4,
verticalAlign: "middle"
},
bylineWeight: {
fontFamily: headerFont,
fontSize: 15,
fontWeight: 500
}
};

const Byline = ({width, metadata}) => {
return (
<div width={width} style={styles.byline}>
{renderAvatar(metadata)}
{renderBuildInfo(metadata)}
{renderMaintainers(metadata)}
</div>
);
};

function renderAvatar(metadata) {
const repo = metadata.buildUrl;
if (typeof repo === 'string') {
const match = repo.match(/(https?:\/\/)?(www\.)?github.com\/([^/]+)/);
if (match) {
return (
<img style={styles.avatar} alt="avatar" width="28" src={`https://github.com/${match[3]}.png?size=200`}/>
);
}
}
return null;
}

function renderBuildInfo(metadata) {
if (Object.prototype.hasOwnProperty.call(metadata, "buildUrl")) {
const repo = metadata.buildUrl;
if (typeof repo === 'string') {
if (repo.startsWith("https://") || repo.startsWith("http://") || repo.startsWith("www.")) {
return (
<span>
{"Built using "}
<Link url={repo}>
{repo.replace(/^(http[s]?:\/\/)/, "").replace(/^www\./, "")}
</Link>
{". "}
</span>
);
}
}
}
return null;
}

function renderMaintainers(metadata) {
let maintainersArray;
if (Object.prototype.hasOwnProperty.call(metadata, "maintainers")) {
maintainersArray = metadata.maintainers;
if (Array.isArray(maintainersArray) && maintainersArray.length) {
return (
<span>
{"Maintained by "}
{maintainersArray.map((m, i) => (
<React.Fragment key={m.name}>
{m.url ? <Link url={m.url}>{m.name}</Link> : m.name}
{i === maintainersArray.length-1 ? "" : i === maintainersArray.length-2 ? " and " : ", "}
</React.Fragment>
))}
{"."}
</span>
);
}
}
return null;
}

function Link({url, children}) {
return (
<a style={styles.bylineWeight} rel="noopener noreferrer" href={url} target="_blank">
{children}
</a>
);
}


export default Byline;
22 changes: 15 additions & 7 deletions src/components/info/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getVisibleDateRange } from "../../util/treeVisibilityHelpers";
import { numericToCalendar } from "../../util/dateHelpers";
import { months, NODE_VISIBLE } from "../../util/globals";
import { displayFilterValueAsButton } from "../framework/footer";
import Byline from "./byline";

const plurals = {
country: "countries",
Expand Down Expand Up @@ -221,17 +222,25 @@ class Info extends React.Component {
);
}

renderTitle(styles) {
let title = "";
if (this.props.metadata.title) {
title = this.props.metadata.title;
}
return (
<div width={this.props.width} style={styles.title}>
{title}
</div>
);
}

render() {
if (!this.props.metadata || !this.props.nodes || !this.props.visibility) return null;
const styles = this.getStyles(this.props.width);
// const filtersWithValues = Object.keys(this.props.filters).filter((n) => this.props.filters[n].length > 0);
const animating = this.props.animationPlayPauseButton === "Pause";
const showExtended = !animating && !this.props.selectedStrain;
const datesMaxed = this.props.dateMin === this.props.absoluteDateMin && this.props.dateMax === this.props.absoluteDateMax;
let title = "";
if (this.props.metadata.title) {
title = this.props.metadata.title;
}

/* the content is made up of two parts:
(1) the summary - e.g. Showing 4 of 379 sequences, from 1 author, 1 country and 1 region, dated Apr 2016 to Jun 2016.
Expand All @@ -250,9 +259,8 @@ class Info extends React.Component {
return (
<Card center infocard>
<div style={styles.base}>
<div width={this.props.width} style={styles.title}>
{title}
</div>
{this.renderTitle(styles)}
<Byline styles={styles} width={this.props.width} metadata={this.props.metadata}/>
<div width={this.props.width} style={styles.n}>
{animating ? `Animation in progress. ` : null}
{this.props.selectedStrain ? this.selectedStrainButton(this.props.selectedStrain) : null}
Expand Down
27 changes: 27 additions & 0 deletions src/reducers/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,36 @@ const Metadata = (state = {
case types.ADD_COLOR_BYS:
const colorings = Object.assign({}, state.colorings, action.newColorings);
return Object.assign({}, state, {colorings});
case types.SET_AVAILABLE:
if (state.buildUrl) {
return state; // do not use data from getAvailable to overwrite a buildUrl set from a dataset JSON
}
const buildUrl = getBuildUrlFromGetAvailableJson(action.data.datasets);
if (buildUrl) {
return Object.assign({}, state, {buildUrl});
}
return state;
default:
return state;
}
};

function getBuildUrlFromGetAvailableJson(availableData) {
if (!availableData) return undefined;
/* check if the current dataset is present in the getAvailable data
We currently parse the URL (pathname) for the current dataset but this
really should be stored somewhere in redux */
const displayedDatasetString = window.location.pathname
.replace(/^\//, '')
.replace(/\/$/, '')
.split(":")[0];
for (let i=0; i<availableData.length; i++) {
if (availableData[i].request === displayedDatasetString) {
return availableData[i].buildUrl; // may be `undefined`
}
}
return false;
}


export default Metadata;