Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions screen2.0/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ const nextConfig = {
fullUrl: true,
},
},

async redirects() {
return [
// SCREEN Redirects (Legacy link handling)
{
source: '/geApp',
destination: '/applets/gene-expression',
permanent: true,
},
{
source: '/index/about',
destination: '/about',
permanent: true,
},
{
source: '/index/cversions',
destination: '/about',
permanent: true,
},
]
},

webpack: (config, { isServer }) => {
if (!isServer) {
Expand Down
173 changes: 20 additions & 153 deletions screen2.0/src/app/_biosampleTables/DownloadButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useState, useEffect } from "react";
import { CircularProgressProps, Box, CircularProgress, Typography, IconButton, Tooltip } from "@mui/material";
import { Close, Download } from "@mui/icons-material";
import { downloadTSV } from "../downloads/utils";
import { useState } from "react";
import { Box, IconButton, Tooltip } from "@mui/material";
import { Download } from "@mui/icons-material";
import { BiosampleData } from "./types";
import Link from "next/link";

export type DownloadButtonProps<T extends boolean> = {
row: BiosampleData<T>
Expand All @@ -22,7 +22,6 @@ const fetchFileSize = async (url: string, setFileSize: React.Dispatch<React.SetS
}
}


/**
*
* @prop row
Expand All @@ -33,33 +32,30 @@ const fetchFileSize = async (url: string, setFileSize: React.Dispatch<React.SetS
* @returns
*/
export const DownloadButton = <T extends boolean>({ row, downloadType }: DownloadButtonProps<T>) => {
const [progress, setProgress] = useState<number>(null) //for progress wheel
const [hover, setHover] = useState<boolean>(false) //for tracking if user is hovering over progress wheel
const [controller, setController] = useState(null); //used to hold an AbortController created in handleDL, which allows aborting download
const [fileSize, setFileSize] = useState<number>(null)

let url: string
let fileName: string
switch (downloadType) {
case "dnase":
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.dnase}-${row.dnase_signal}.txt`
fileName = `${row.dnase}-${row.dnase_signal}.txt`
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.dnase}-${row.dnase_signal}.tsv`
fileName = `${row.dnase}-${row.dnase_signal}.tsv`
break
case "h3k4me3":
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.h3k4me3}-${row.h3k4me3_signal}.txt`
fileName = `${row.h3k4me3}-${row.h3k4me3_signal}.txt`
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.h3k4me3}-${row.h3k4me3_signal}.tsv`
fileName = `${row.h3k4me3}-${row.h3k4me3_signal}.tsv`
break
case "h3k27ac":
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.h3k27ac}-${row.h3k27ac_signal}.txt`
fileName = `${row.h3k27ac}-${row.h3k27ac_signal}.txt`
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.h3k27ac}-${row.h3k27ac_signal}.tsv`
fileName = `${row.h3k27ac}-${row.h3k27ac_signal}.tsv`
break
case "ctcf":
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.ctcf}-${row.ctcf_signal}.txt`
fileName = `${row.ctcf}-${row.ctcf_signal}.txt`
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.ctcf}-${row.ctcf_signal}.tsv`
fileName = `${row.ctcf}-${row.ctcf_signal}.tsv`
break
case "atac":
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.atac}-${row.atac_signal}.txt`
fileName = `${row.atac}-${row.atac_signal}.txt`
url = `https://downloads.wenglab.org/Registry-V4/SCREEN/Signal-Files/${row.atac}-${row.atac_signal}.tsv`
fileName = `${row.atac}-${row.atac_signal}.tsv`
break
case "celltypeccres": {
const signalIDs = [
Expand All @@ -74,153 +70,24 @@ export const DownloadButton = <T extends boolean>({ row, downloadType }: Downloa
}
}

useEffect(() => {
return () => {
// Cleanup: Abort the fetch request if the component is unmounted
if (controller) {
controller.abort();
}
};
}, [controller]);

const handleAbort = () => {
// Trigger the abort signal
if (controller) {
controller.abort();
}
};

function CircularProgressWithLabel(
props: CircularProgressProps & { value: number },
) {
return (
<Box
sx={{ position: 'relative', display: 'inline-flex' }}
>
<CircularProgress variant="determinate" {...props} />
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Typography
variant="caption"
component="div"
color="text.secondary"
>{`${Math.round(props.value)}%`}</Typography>
</Box>
</Box>
);
}

const handleSetHover = (isHovered: boolean) => {
setHover(isHovered)
if (isHovered && !fileSize){
if (isHovered && !fileSize) {
fetchFileSize(url, setFileSize)
}
}

if (row[downloadType] || (downloadType === "celltypeccres" && (row.dnase || row.ctcf || row.h3k27ac || row.h3k4me3))) {


const handleDL = async () => {
// Cleanup previous controller if any
if (controller) {
controller.abort();
}

// Create a new AbortController
const newController = new AbortController();
setController(newController);

// Create a progress callback function
const handleProgress = (progress) => {
setProgress((progress.loaded / progress.total) * 100);
};

try {
const response = await fetch(url, {
signal: newController.signal, // Pass the signal to the fetch request
});

if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}

const reader = response.body!.getReader();
const contentLength = +response.headers.get('Content-Length')!;

let receivedLength = 0;
const chunks = [];

const read = async () => {
const { done, value } = await reader.read();

if (done) {
return;
}

receivedLength += value!.length;
chunks.push(value!);

handleProgress({ loaded: receivedLength, total: contentLength });

// Continue reading the next chunk unless aborted
if (!newController.signal.aborted) {
return read();
}
};

await read();

if (!newController.signal.aborted) {
const dataArray = new Uint8Array(receivedLength);
let position = 0;
for (const chunk of chunks) {
dataArray.set(chunk, position);
position += chunk.length;
}

const dataString = new TextDecoder('utf-8').decode(dataArray);

downloadTSV(dataString, fileName);
}
} catch (error) {
if (error.name !== 'AbortError') {
window.alert('Download failed:' + error);
}
} finally {
setController(null);
setProgress(null);
}
};

return (
<Box
onMouseEnter={() => handleSetHover(true)}
onMouseLeave={() => handleSetHover(false)}
>
{progress ?
hover ?
<IconButton onClick={handleAbort}>
<Close />
</IconButton>
:
<CircularProgressWithLabel value={progress} />
:
<Tooltip title={fileSize ? fileSize && (fileSize / 1000000).toFixed(1) + ' MB' : "Loading file size"}>
<IconButton onClick={handleDL}>
<Download />
</IconButton>
</Tooltip>
}
<Tooltip title={fileSize ? fileSize && (fileSize / 1000000).toFixed(1) + ' MB' : "Loading file size"}>
<IconButton LinkComponent={Link} href={url} download={fileName}>
<Download />
</IconButton>
</Tooltip>
</Box>
)
} else return null
Expand Down
35 changes: 35 additions & 0 deletions screen2.0/src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client'

import { Button, Typography, Stack } from '@mui/material'
import Link from 'next/link'

export default function NotFound() {
return (
<Stack
height="100%"
sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', textAlign: 'center', px: 2 }}
spacing={2}
>
<Typography variant="h1" fontWeight={500} color="primary">
404
</Typography>
<Typography variant="h5" gutterBottom>
Page Not Found
</Typography>
<Typography variant="body1">
Sorry, the page you’re looking for doesn’t exist or has been moved.
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ maxWidth: 500 }}>
If you followed an old link or have this page bookmarked, feel free to contact us and let us know
</Typography>
<Stack direction={"row"} spacing={2}>
<Button component={Link} href="/" variant="outlined" size="large">
Return Home
</Button>
<Button component={Link} href="/about#contact-us" variant="outlined" size="large">
Contact Us
</Button>
</Stack>
</Stack>
)
}
35 changes: 23 additions & 12 deletions screen2.0/src/app/search/_ccredetails/tfintersection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useQuery } from "@apollo/client"
import { TF_INTERSECTION_QUERY, CRE_TF_DCC_QUERY } from "./queries"
import Grid from "@mui/material/Grid2"
import { DataTable, DataTableColumn } from "@weng-lab/psychscreen-ui-components"
import { LoadingMessage } from "../../../common/lib/utility"
import { CreateLink, LoadingMessage } from "../../../common/lib/utility"

type TFBindData = {
name: string;
Expand Down Expand Up @@ -75,7 +75,7 @@ export const TfIntersection: React.FC<{ assembly: string; coordinates: { chromos
totalmap[x.target.name] = x.counts.total
})

let tableData: TFBindData[] =
const tableData: TFBindData[] =
data &&
Object.keys(peakmap).map((k) => {
return {
Expand All @@ -91,9 +91,13 @@ export const TfIntersection: React.FC<{ assembly: string; coordinates: { chromos
header: "Factor",
value: (row) => row.name,
render: (row) => (
<Link href={`https://www.factorbook.org/tf/human/${row.name}/function`} rel="noopener noreferrer" target="_blank">
<button>{row.name}</button>
</Link>
<CreateLink
linkPrefix={`https://www.factorbook.org/tf/human/${row.name}/function`}
label={row.name}
showExternalIcon
underline="hover"
onClick={(e) => e.stopPropagation()}
/>
),
},
{
Expand Down Expand Up @@ -154,13 +158,20 @@ export const TfIntersection: React.FC<{ assembly: string; coordinates: { chromos
header: "experiment/file",
value: (row) => row.expID,
render: (row) => (
<Link
href={`https://www.encodeproject.org/experiments/${row.expID.split("/")[0]}/`}
rel="noopener noreferrer"
target="_blank"
>
<button>{row.expID}</button>
</Link>
<CreateLink
linkPrefix={`https://www.encodeproject.org/experiments/${row.expID.split("/")[0]}/`}
label={row.expID}
showExternalIcon
underline="hover"
onClick={(e) => e.stopPropagation()}
/>
// <Link
// href={`https://www.encodeproject.org/experiments/${row.expID.split("/")[0]}/`}
// rel="noopener noreferrer"
// target="_blank"
// >
// <button>{row.expID}</button>
// </Link>
),
},
]}
Expand Down
2 changes: 2 additions & 0 deletions screen2.0/src/app/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ export default function Search({ searchParams }: { searchParams: { [key: string]

//No human or mouse genes have "chr" followed by a number, so safe to check this way
if (/chr\d+/.test(encodeInput)) searchType = "region"
//handle rs1 edge case in case user searches in lowercase
else if (encodeInput.toLowerCase() === "rs1") searchType = "gene"
//check for "rs" followed by number. Genes RS1 and Rs1 exist, but lowercase r is differentiator
else if (/rs\d+/.test(encodeInput)) searchType = "snp"
//check for beginning of cCRE accession. No Genes start with these
Expand Down
3 changes: 2 additions & 1 deletion screen2.0/src/common/lib/utility.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ export const CreateLink: React.FC<{
linkArg?: string,
label: string,
showExternalIcon?: boolean,
onClick?: React.MouseEventHandler<HTMLAnchorElement>
variant?: OverridableStringUnion<Variant | 'inherit', TypographyPropsVariantOverrides>, textColor?: string, underline?: "none" | "always" | "hover" }> = (props) => {
const link = props.linkPrefix + (props.linkArg ?? "")
return (
<Link variant={props.variant} href={link} rel="noopener noreferrer" target="_blank" color={props.textColor} underline={props.underline}>
<Link variant={props.variant} href={link} rel="noopener noreferrer" target="_blank" color={props.textColor} underline={props.underline} onClick={props.onClick}>
{props.label}
{props.showExternalIcon && <Launch sx={{ display: "inline-flex", verticalAlign: "middle", ml: 0.5 }} color="inherit" fontSize="inherit" />}
</Link>
Expand Down