From afe628ea42a6c64118f6fb4d7d96b7a5070ce1c5 Mon Sep 17 00:00:00 2001
From: Theo Sanderson
Date: Sat, 26 Oct 2024 15:28:11 +0100
Subject: [PATCH] Move from subdomains for special hosted builds to just URLs
(#627)
---
taxonium_website/src/App.jsx | 179 ++++++++++++--------------------
taxonium_website/src/trees.json | 36 +++++++
taxonium_website/vercel.json | 16 +++
3 files changed, 118 insertions(+), 113 deletions(-)
create mode 100644 taxonium_website/src/trees.json
create mode 100644 taxonium_website/vercel.json
diff --git a/taxonium_website/src/App.jsx b/taxonium_website/src/App.jsx
index a283b052..bfd15c49 100644
--- a/taxonium_website/src/App.jsx
+++ b/taxonium_website/src/App.jsx
@@ -1,60 +1,77 @@
import React, { useState, Suspense, useRef, useEffect } from "react";
import AboutOverlay from "./components/AboutOverlay";
import TaxoniumBit from "./components/TaxoniumBit";
-
import { CgListTree } from "react-icons/cg";
import { BsInfoSquare } from "react-icons/bs";
import { FaGithub } from "react-icons/fa";
+import { HiOutlineBookOpen } from "react-icons/hi";
import useQueryAsState from "./hooks/useQueryAsState";
import classNames from "classnames";
import { useInputHelper } from "./hooks/useInputHelper";
-
import InputSupplier from "./components/InputSupplier";
+import treeConfig from "./trees.json";
-import { HiOutlineBookOpen } from "react-icons/hi";
-
-const default_query = {};
+// Hardcoded list of paths to show in the showcase
+const SHOWCASE_PATHS = [
+ "sars-cov-2/public",
+ "taxonomy/visual",
+ "taxonomy/full",
+ "mpox/public",
+];
-default_query.backend = null;
-if (window.location.hostname.includes("viridian.taxonium.org")) {
- default_query.backend = "https://viridian-api.cov2tree.org";
-}
-if (window.location.hostname.includes("cov2tree.org")) {
- default_query.backend = "https://api.cov2tree.org";
-}
+function checkLegacyHostname() {
+ const currentHostname = window.location.hostname;
-if (window.location.hostname.includes("mpx.taxonium.org")) {
- default_query.protoUrl = "https://mpx-tree.vercel.app/mpx.jsonl.gz";
- default_query.configUrl = "https://mpx-tree.vercel.app/config.json";
-}
+ // Look through all configurations for matching legacy hostnames
+ for (const [path, config] of Object.entries(treeConfig)) {
+ if (
+ config.legacyHostnames &&
+ config.legacyHostnames.includes(currentHostname)
+ ) {
+ // If we find a match, redirect to the new path
+ // Preserve any query parameters
+ const newPath = `${window.location.protocol}//${window.location.host}/${path}${window.location.search}`;
-if (window.location.hostname.includes("taxonomy.taxonium.org")) {
- default_query.treeUrl =
- "https://cov2tree.nyc3.digitaloceanspaces.com/ncbi/tree.nwk.gz";
+ // Only redirect if we're not already on the correct path
+ if (!window.location.pathname.startsWith(`/${path}`)) {
+ window.location.href = newPath;
+ return true;
+ }
+ }
+ }
- default_query.metaUrl =
- "https://cov2tree.nyc3.digitaloceanspaces.com/ncbi/metadata.tsv.gz";
+ return false;
+}
- default_query.configUrl =
- "https://cov2tree.nyc3.digitaloceanspaces.com/ncbi/config.json";
+function getConfigFromPath() {
+ // First check for legacy hostname redirects
+ if (checkLegacyHostname()) {
+ return null; // Return null as we're about to redirect
+ }
- default_query.ladderizeTree = "true";
-}
+ // Remove leading slash and get full path
+ const path = window.location.pathname.substring(1);
-if (window.location.hostname.includes("visualtreeoflife.taxonium.org")) {
- default_query.protoUrl =
- "https://cov2tree.nyc3.digitaloceanspaces.com/wikidata/out.jsonl.gz";
+ // Return the configuration for this path, if it exists
+ return treeConfig[path] || null;
}
function App() {
useEffect(() => {
import("taxonium-component");
}, []);
- const [uploadedData, setUploadedData] = useState(null);
- // check if .epicov.org is in the URL
+ const pathConfig = getConfigFromPath();
+ const default_query = pathConfig || {};
+ const [uploadedData, setUploadedData] = useState(null);
const [query, updateQuery] = useQueryAsState(default_query);
+ const [title, setTitle] = useState(null);
+ const [beingDragged, setBeingDragged] = useState(false);
+ const [aboutEnabled, setAboutEnabled] = useState(false);
+ const [overlayContent, setOverlayContent] = useState(null);
+
+ const dragTimeout = useRef(null);
const inputHelper = useInputHelper({
setUploadedData,
@@ -62,29 +79,20 @@ function App() {
query,
uploadedData,
});
- const [title, setTitle] = useState(null);
- const [beingDragged, setBeingDragged] = useState(false);
-
- const dragTimeout = useRef(null);
function onDrop(ev) {
console.log("File(s) dropped");
setBeingDragged(false);
-
- // Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
if (ev.dataTransfer.items) {
- // Use DataTransferItemList interface to access the file(s)
for (var i = 0; i < ev.dataTransfer.items.length; i++) {
- // If dropped items aren't files, reject them
if (ev.dataTransfer.items[i].kind === "file") {
var file = ev.dataTransfer.items[i].getAsFile();
inputHelper.readFile(file);
}
}
} else {
- // Use DataTransfer interface to access the file(s)
inputHelper.readFile(ev.dataTransfer.files[0]);
}
}
@@ -97,18 +105,14 @@ function App() {
ev.preventDefault();
return;
}
- console.log("File(s) in drop zone");
setBeingDragged(true);
if (dragTimeout.current) {
clearTimeout(dragTimeout.current);
}
-
- // Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
}
function onDragLeave(ev) {
- //debounce:
if (dragTimeout.current) {
clearTimeout(dragTimeout.current);
}
@@ -117,62 +121,19 @@ function App() {
}, 500);
}
- const [aboutEnabled, setAboutEnabled] = useState(false);
-
- const protoUrl = query.protoUrl;
- if (protoUrl && protoUrl.includes(".pb")) {
- const url_parts = window.location.href.split("?", 2);
- if (url_parts[1]) {
- console.log(url_parts, "parts");
- // V1 format
- if (!window.redirecting) {
- window.alert(
- "It looks like you are trying to load a Taxonium V1 proto. We will now redirect you to the V1 site. "
- );
- }
- window.redirecting = 1;
- // split url into before question mark and after
-
- window.location.href =
- "https://cov2tree-git-v1-theosanderson.vercel.app/?" + url_parts[1];
- } else {
- if (!window.redirecting) {
- window.alert(
- "It looks like you are trying to load a Taxonium V1 proto. We will now redirect you to the V1 site. "
- );
- }
- window.redirecting = 1;
- window.location.href =
- "https://cov2tree-git-v1-theosanderson.vercel.app/?protoUrl=" +
- protoUrl;
+ // Generate showcase items from hardcoded list
+ const showCase = SHOWCASE_PATHS.map((path) => {
+ const config = treeConfig[path];
+ if (!config) {
+ console.warn(`No configuration found for showcase path: ${path}`);
+ return null;
}
- }
- const [overlayContent, setOverlayContent] = useState(null);
- // does the window location contain epicov anywhere
- const isGisaid = window.location.toString().includes("epicov.org");
-
- const showCase = [
- {
- title: "SARS-CoV-2",
- url: "/?backend=https://api.cov2tree.org",
- desc: "All seven million public sequences of SARS-CoV-2 from the INSDC databases",
- },
- {
- title: "Wikidata visual tree of life",
- url: "/?configUrl=https%3A%2F%2Fcov2tree.nyc3.digitaloceanspaces.com%2Fncbi%2Fconfig_special2.json&protoUrl=https%3A%2F%2Fcov2tree.nyc3.cdn.digitaloceanspaces.com%2Fncbi%2Fspecial_filtered.jsonl.gz&xType=x_dist",
- desc: "The tree of life, showing species from Wikidata with images. Links to Wikipedia.",
- },
- {
- title: "NCBI Taxonomy (full)",
- url: "https://taxonomy.taxonium.org",
- desc: "Full 2.2M NCBI Taxonomy of species",
- },
- {
- title: "Mpox",
- url: "https://mpx.taxonium.org",
- desc: "Mpox sequences from GenBank",
- },
- ];
+ return {
+ title: config.title,
+ url: `/${path}`,
+ desc: config.description,
+ };
+ }).filter(Boolean); // Remove any null entries from missing configs
return (
<>
@@ -195,7 +156,7 @@ function App() {
className={classNames(
"from-gray-500 to-gray-600 bg-gradient-to-bl shadow-md",
"flex justify-between items-center px-4 flex-shrink-0",
- isGisaid ? "h-11" : "h-16"
+ "h-16"
)}
>
-
+
@@ -287,7 +243,6 @@ function App() {
- {/* Horizontal separator and text "or load an existing tree:"*/}
@@ -295,7 +250,6 @@ function App() {
- {/* Showcases */}
{showCase.map((item, i) => (
@@ -311,14 +265,13 @@ function App() {
))}
- {/* documentation link, centered with react-icons*/}
diff --git a/taxonium_website/src/trees.json b/taxonium_website/src/trees.json
new file mode 100644
index 00000000..431021bd
--- /dev/null
+++ b/taxonium_website/src/trees.json
@@ -0,0 +1,36 @@
+{
+ "sars-cov-2/public": {
+ "backend": "https://api.cov2tree.org",
+ "title": "SARS-CoV-2",
+ "description": "All seven million public sequences of SARS-CoV-2 from the INSDC databases",
+ "legacyHostnames": ["cov2tree.org"]
+ },
+ "sars-cov-2/viridian": {
+ "backend": "https://viridian-api.cov2tree.org",
+ "title": "SARS-CoV-2 Viridian",
+ "description": "SARS-CoV-2 sequences processed through Viridian pipeline",
+ "legacyHostnames": ["viridian.taxonium.org"]
+ },
+ "mpox/public": {
+ "protoUrl": "https://mpx-tree.vercel.app/mpx.jsonl.gz",
+ "configUrl": "https://mpx-tree.vercel.app/config.json",
+ "title": "Mpox",
+ "description": "Mpox sequences from GenBank",
+ "legacyHostnames": ["mpx.taxonium.org"]
+ },
+ "taxonomy/full": {
+ "treeUrl": "https://cov2tree.nyc3.digitaloceanspaces.com/ncbi/tree.nwk.gz",
+ "metaUrl": "https://cov2tree.nyc3.digitaloceanspaces.com/ncbi/metadata.tsv.gz",
+ "configUrl": "https://cov2tree.nyc3.digitaloceanspaces.com/ncbi/config.json",
+ "ladderizeTree": true,
+ "title": "NCBI Taxonomy (full)",
+ "description": "Full 2.2M NCBI Taxonomy of species",
+ "legacyHostnames": ["taxonomy.taxonium.org"]
+ },
+ "taxonomy/visual": {
+ "protoUrl": "https://cov2tree.nyc3.digitaloceanspaces.com/wikidata/out.jsonl.gz",
+ "title": "Wikidata visual tree of life",
+ "description": "The tree of life, showing species from Wikidata with images. Links to Wikipedia.",
+ "legacyHostnames": ["visualtreeoflife.taxonium.org"]
+ }
+}
diff --git a/taxonium_website/vercel.json b/taxonium_website/vercel.json
new file mode 100644
index 00000000..5b1bb42c
--- /dev/null
+++ b/taxonium_website/vercel.json
@@ -0,0 +1,16 @@
+{
+ "routes": [
+ {
+ "src": "/assets/(.*)",
+ "dest": "/assets/$1"
+ },
+ {
+ "src": "/(.*)",
+ "dest": "/index.html"
+ },
+ {
+ "src": "/favicon.ico",
+ "dest": "/favicon.ico"
+ }
+ ]
+}