-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow extensions to replace splash page
The splash page can now be replaced by providing a compatable react component.
- Loading branch information
1 parent
27ef50d
commit c73f590
Showing
7 changed files
with
219 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
import React from "react"; | ||
import { connect } from "react-redux"; | ||
import Title from "../framework/title"; | ||
import NavBar from "../navBar"; | ||
import Flex from "../../components/framework/flex"; | ||
import { logos } from "./logos"; | ||
import { CenterContent } from "./centerContent"; | ||
import { changePage } from "../../actions/navigation"; | ||
import DefaultSplashContent from "./splash"; | ||
import { hasExtension, getExtension } from "../../util/extensions"; | ||
import ErrorBoundary from "../../util/errorBoundry"; | ||
import { fetchJSON } from "../../util/serverInteraction"; | ||
import { charonAPIAddress, controlsHiddenWidth } from "../../util/globals"; | ||
import { changePage } from "../../actions/navigation"; | ||
|
||
const SplashContent = hasExtension("splashComponent") ? | ||
getExtension("splashComponent") : | ||
DefaultSplashContent; | ||
/* TODO: check that when compiling DefaultSplashContent isn't included if extension is defined */ | ||
|
||
|
||
@connect((state) => ({ | ||
errorMessage: state.general.errorMessage, | ||
|
@@ -28,116 +32,20 @@ class Splash extends React.Component { | |
console.warn(err.message); | ||
}); | ||
} | ||
formatDataset(fields) { | ||
let path = fields.join("/"); | ||
if (this.state.source !== "live") { | ||
path = this.state.source + "/" + path; | ||
} | ||
return ( | ||
<li key={path}> | ||
<div | ||
style={{color: "#5097BA", textDecoration: "none", cursor: "pointer", fontWeight: "400", fontSize: "94%"}} | ||
onClick={() => this.props.dispatch(changePage({path, push: true}))} | ||
> | ||
{path} | ||
</div> | ||
</li> | ||
); | ||
} | ||
listAvailable() { | ||
if (!this.state.source) return null; | ||
if (!this.state.available) { | ||
if (this.state.source === "live" || this.state.source === "staging") { | ||
return ( | ||
<CenterContent> | ||
<div style={{fontSize: "18px"}}> | ||
{`No available ${this.state.source} datasets. Try "/local/" for local datasets.`} | ||
</div> | ||
</CenterContent> | ||
); | ||
} | ||
return null; | ||
} | ||
|
||
let listJSX; | ||
/* make two columns for wide screens */ | ||
if (this.props.browserDimensions.width > 1000) { | ||
const secondColumnStart = Math.ceil(this.state.available.length / 2); | ||
listJSX = ( | ||
<div style={{display: "flex", flexWrap: "wrap"}}> | ||
<div style={{flex: "1 50%", minWidth: "0"}}> | ||
<ul> | ||
{this.state.available.slice(0, secondColumnStart).map((data) => this.formatDataset(data))} | ||
</ul> | ||
</div> | ||
<div style={{flex: "1 50%", minWidth: "0"}}> | ||
<ul> | ||
{this.state.available.slice(secondColumnStart).map((data) => this.formatDataset(data))} | ||
</ul> | ||
</div> | ||
</div> | ||
); | ||
} else { | ||
listJSX = ( | ||
<ul style={{marginLeft: "-22px"}}> | ||
{this.state.available.map((data) => this.formatDataset(data))} | ||
</ul> | ||
); | ||
} | ||
return ( | ||
<CenterContent> | ||
<div> | ||
<div style={{fontSize: "26px"}}> | ||
{`Available ${this.state.narratives ? "Narratives" : "Datasets"} for source ${this.state.source}`} | ||
</div> | ||
{listJSX} | ||
</div> | ||
</CenterContent> | ||
); | ||
} | ||
render() { | ||
const isMobile = this.props.browserDimensions.width < controlsHiddenWidth; | ||
return ( | ||
<div> | ||
<NavBar minified={isMobile}/> | ||
|
||
<div className="static container"> | ||
<Flex justifyContent="center"> | ||
<Title/> | ||
</Flex> | ||
<div className="row"> | ||
<h1 style={{textAlign: "center", marginTop: "-10px", fontSize: "29px"}}> Real-time tracking of virus evolution </h1> | ||
</div> | ||
{/* First: either display the error message or the intro-paragraph */} | ||
{this.props.errorMessage || this.state.errorMessage ? ( | ||
<CenterContent> | ||
<div> | ||
<p style={{color: "rgb(222, 60, 38)", fontWeight: 600, fontSize: "24px"}}> | ||
{"😱 404, or an error has occured 😱"} | ||
</p> | ||
<p style={{color: "rgb(222, 60, 38)", fontWeight: 400, fontSize: "18px"}}> | ||
{`Details: ${this.props.errorMessage || this.state.errorMessage}`} | ||
</p> | ||
<p style={{fontSize: "16px"}}> | ||
{"If this keeps happening, or you believe this is a bug, please "} | ||
<a href={"mailto:[email protected]"}>{"get in contact with us."}</a> | ||
</p> | ||
</div> | ||
</CenterContent> | ||
) : ( | ||
<p style={{maxWidth: 600, marginTop: 0, marginRight: "auto", marginBottom: 20, marginLeft: "auto", textAlign: "center", fontSize: 16, fontWeight: 300, lineHeight: 1.42857143}}> | ||
Nextstrain is an open-source project to harness the scientific and public health potential of pathogen genome data. We provide a continually-updated view of publicly available data with powerful analytics and visualizations showing pathogen evolution and epidemic spread. Our goal is to aid epidemiological understanding and improve outbreak response. | ||
</p> | ||
)} | ||
{/* Secondly, list the available datasets / narratives */} | ||
{this.listAvailable()} | ||
{/* Finally, the footer (logos) */} | ||
<CenterContent> | ||
{logos} | ||
</CenterContent> | ||
|
||
</div> | ||
</div> | ||
<ErrorBoundary> | ||
<SplashContent | ||
isMobile={this.props.browserDimensions.width < controlsHiddenWidth} | ||
source={this.state.source} | ||
available={this.state.available} | ||
narratives={this.state.narratives} | ||
browserDimensions={this.props.browserDimensions} | ||
dispatch={this.props.dispatch} | ||
errorMessage={this.props.errorMessage || this.state.errorMessage} | ||
changePage={changePage} | ||
/> | ||
</ErrorBoundary> | ||
); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import React from "react"; | ||
import Title from "../framework/title"; | ||
import NavBar from "../navBar"; | ||
import Flex from "../../components/framework/flex"; | ||
import { logos } from "./logos"; | ||
import { CenterContent } from "./centerContent"; | ||
|
||
|
||
const formatDataset = (source, fields, dispatch, changePage) => { | ||
let path = fields.join("/"); | ||
if (source !== "live") { | ||
path = source + "/" + path; | ||
} | ||
return ( | ||
<li key={path}> | ||
<div | ||
style={{color: "#5097BA", textDecoration: "none", cursor: "pointer", fontWeight: "400", fontSize: "94%"}} | ||
onClick={() => dispatch(changePage({path, push: true}))} | ||
> | ||
{path} | ||
</div> | ||
</li> | ||
); | ||
}; | ||
|
||
const listAvailable = (source, available, narratives, browserDimensions, dispatch, changePage) => { | ||
if (!source) return null; | ||
if (!available) { | ||
if (source === "live" || source === "staging") { | ||
return ( | ||
<CenterContent> | ||
<div style={{fontSize: "18px"}}> | ||
{`No available ${source} datasets. Try "/local/" for local datasets.`} | ||
</div> | ||
</CenterContent> | ||
); | ||
} | ||
return null; | ||
} | ||
|
||
|
||
let listJSX; | ||
/* make two columns for wide screens */ | ||
if (browserDimensions.width > 1000) { | ||
const secondColumnStart = Math.ceil(available.length / 2); | ||
listJSX = ( | ||
<div style={{display: "flex", flexWrap: "wrap"}}> | ||
<div style={{flex: "1 50%", minWidth: "0"}}> | ||
<ul> | ||
{available.slice(0, secondColumnStart).map((data) => formatDataset(source, data, dispatch, changePage))} | ||
</ul> | ||
</div> | ||
<div style={{flex: "1 50%", minWidth: "0"}}> | ||
<ul> | ||
{available.slice(secondColumnStart).map((data) => formatDataset(source, data, dispatch, changePage))} | ||
</ul> | ||
</div> | ||
</div> | ||
); | ||
} else { | ||
listJSX = ( | ||
<ul style={{marginLeft: "-22px"}}> | ||
{available.map((data) => formatDataset(source, data, dispatch, changePage))} | ||
</ul> | ||
); | ||
} | ||
return ( | ||
<CenterContent> | ||
<div> | ||
<div style={{fontSize: "26px"}}> | ||
{`Available ${narratives ? "Narratives" : "Datasets"} for source ${source}`} | ||
</div> | ||
{listJSX} | ||
</div> | ||
</CenterContent> | ||
); | ||
}; | ||
|
||
|
||
const SplashContent = ({isMobile, source, available, narratives, browserDimensions, dispatch, errorMessage, changePage}) => ( | ||
<div> | ||
<NavBar minified={isMobile}/> | ||
|
||
<div className="static container"> | ||
<Flex justifyContent="center"> | ||
<Title/> | ||
</Flex> | ||
<div className="row"> | ||
<h1 style={{textAlign: "center", marginTop: "-10px", fontSize: "29px"}}> Real-time tracking of virus evolution </h1> | ||
</div> | ||
{/* First: either display the error message or the intro-paragraph */} | ||
{errorMessage ? ( | ||
<CenterContent> | ||
<div> | ||
<p style={{color: "rgb(222, 60, 38)", fontWeight: 600, fontSize: "24px"}}> | ||
{"😱 404, or an error has occured 😱"} | ||
</p> | ||
<p style={{color: "rgb(222, 60, 38)", fontWeight: 400, fontSize: "18px"}}> | ||
{`Details: ${errorMessage}`} | ||
</p> | ||
<p style={{fontSize: "16px"}}> | ||
{"If this keeps happening, or you believe this is a bug, please "} | ||
<a href={"mailto:[email protected]"}>{"get in contact with us."}</a> | ||
</p> | ||
</div> | ||
</CenterContent> | ||
) : ( | ||
<p style={{maxWidth: 600, marginTop: 0, marginRight: "auto", marginBottom: 20, marginLeft: "auto", textAlign: "center", fontSize: 16, fontWeight: 300, lineHeight: 1.42857143}}> | ||
Nextstrain is an open-source project to harness the scientific and public health potential of pathogen genome data. We provide a continually-updated view of publicly available data with powerful analytics and visualizations showing pathogen evolution and epidemic spread. Our goal is to aid epidemiological understanding and improve outbreak response. | ||
</p> | ||
)} | ||
{/* Secondly, list the available datasets / narratives */} | ||
{listAvailable(source, available, narratives, browserDimensions, dispatch, changePage)} | ||
{/* Finally, the footer (logos) */} | ||
<CenterContent> | ||
{logos} | ||
</CenterContent> | ||
|
||
</div> | ||
</div> | ||
); | ||
|
||
export default SplashContent; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from "react"; | ||
|
||
class ErrorBoundary extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { hasError: false }; | ||
} | ||
|
||
static getDerivedStateFromError(error) { | ||
// Update state so the next render will show the fallback UI. | ||
return { hasError: true }; | ||
} | ||
|
||
componentDidCatch(error, info) { | ||
// You can also log the error to an error reporting service | ||
console.error(error); | ||
console.error(info); | ||
} | ||
|
||
render() { | ||
if (this.state.hasError) { | ||
// You can render any custom fallback UI | ||
return (<h1>Something went wrong.</h1>); | ||
} | ||
|
||
return this.props.children; | ||
} | ||
} | ||
|
||
export default ErrorBoundary; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,31 @@ | ||
|
||
|
||
/* set up time -- only runs once no matter how many components import this */ | ||
|
||
const registry = (() => { | ||
console.log("EXTENSTIONS.jS"); | ||
if (!process.env.EXTENSION_DATA) { | ||
console.log("no EXTENSION_DATA found") | ||
console.log("no EXTENSION_DATA found"); | ||
return {}; | ||
} | ||
const extensionJson = JSON.parse(process.env.EXTENSION_DATA); | ||
console.log("extensionJson", extensionJson); | ||
return extensionJson; | ||
const extensions = JSON.parse(process.env.EXTENSION_DATA); | ||
|
||
Object.keys(extensions).forEach((key) => { | ||
if (key.endsWith("Component")) { | ||
console.log("loading component", key); | ||
/* "@extensions" is a webpack alias */ | ||
extensions[key] = require(`@extensions/${extensions[key]}`).default; // eslint-disable-line | ||
} | ||
}); | ||
console.log("extensions", extensions); | ||
return extensions; | ||
})(); | ||
|
||
|
||
export const getExtension = (what) => { | ||
console.log("trying to get:", what) | ||
return "something"; | ||
} | ||
if (registry[what]) { | ||
return registry[what]; | ||
} | ||
console.error("Requested non-existing extension", what); | ||
return false; | ||
}; | ||
|
||
export const hasExtension = (what) => { | ||
return Object.keys(registry).includes(what); | ||
}; |
Oops, something went wrong.