Skip to content

Commit

Permalink
Show redirect link to validator if bioformats2raw (#240)
Browse files Browse the repository at this point in the history
* Show redirect link to validator if bioformats2raw

* lint fixes
  • Loading branch information
will-moore authored Feb 24, 2025
1 parent 2327cae commit aec3e45
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 3 deletions.
21 changes: 19 additions & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Link, Typography } from "@material-ui/core";
import { ThemeProvider, makeStyles } from "@material-ui/styles";
import { Provider, atom } from "jotai";
import { useAtomValue, useSetAtom } from "jotai";
Expand All @@ -7,7 +8,14 @@ import ReactDOM from "react-dom/client";
import Menu from "./components/Menu";
import Viewer from "./components/Viewer";
import "./codecs/register";
import { type ImageLayerConfig, type ViewState, addImageAtom, atomWithEffect, sourceErrorAtom } from "./state";
import {
type ImageLayerConfig,
type ViewState,
addImageAtom,
atomWithEffect,
redirectObjAtom,
sourceErrorAtom,
} from "./state";
import theme from "./theme";
import { defer, typedEmitter } from "./utils";

Expand Down Expand Up @@ -53,6 +61,7 @@ export function createViewer(element: HTMLElement, options: { menuOpen?: boolean

function App() {
const sourceError = useAtomValue(sourceErrorAtom);
const redirectObj = useAtomValue(redirectObjAtom);
const addImage = useSetAtom(addImageAtom);
const setViewState = useSetAtom(viewStateAtom);
React.useImperativeHandle(
Expand All @@ -73,7 +82,7 @@ export function createViewer(element: HTMLElement, options: { menuOpen?: boolean
const classes = useStyles();
return (
<>
{sourceError === null && (
{sourceError === null && redirectObj === null && (
<>
<Menu open={options.menuOpen ?? true} />
<Viewer viewStateAtom={viewStateAtom} />
Expand All @@ -84,6 +93,14 @@ export function createViewer(element: HTMLElement, options: { menuOpen?: boolean
<p>{`Error: server replied with "${sourceError}" when loading the resource`}</p>
</div>
)}
{redirectObj !== null && (
<div className={classes.errorContainer}>
<Typography variant="h5">
{redirectObj.message}
<Link href={redirectObj.url}> {redirectObj.url} </Link>
</Typography>
</div>
)}
</>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export async function createSourceData(config: ImageLayerConfig): Promise<Source
}
}

if (utils.isBioformats2rawlayout(attrs)) {
let toUrl = `${utils.OME_VALIDATOR_URL}?source=${config.source}`;
throw new utils.RedirectError("Please open in ome-ngff-validator", toUrl);
}
utils.assert(utils.isMultiscales(attrs), "Group is missing multiscales specification.");
data = await utils.loadMultiscales(node, attrs.multiscales);
if (attrs.multiscales[0].axes) {
Expand Down
14 changes: 13 additions & 1 deletion src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Readable } from "@zarrita/storage";
import type { ZarrPixelSource } from "./ZarrPixelSource";
import type { default as GridLayer, GridLayerProps, GridLoader } from "./gridLayer";
import { initLayerStateFromSource } from "./io";
import type { RedirectError } from "./utils";

export interface ViewState {
zoom: number;
Expand Down Expand Up @@ -130,6 +131,12 @@ export type ControllerProps<T = object> = {

export const sourceErrorAtom = atom<string | null>(null);

export interface Redirect {
url: string;
message: string;
}
export const redirectObjAtom = atom<Redirect | null>(null);

export const sourceInfoAtom = atom<WithId<SourceData>[]>([]);

export const addImageAtom = atom(null, async (get, set, config: ImageLayerConfig) => {
Expand All @@ -143,7 +150,12 @@ export const addImageAtom = atom(null, async (get, set, config: ImageLayerConfig
}
set(sourceInfoAtom, [...prevSourceInfo, { id, ...sourceData }]);
} catch (err) {
set(sourceErrorAtom, (err as Error).message);
if ((err as Error).name === "RedirectError") {
let redirect = err as RedirectError;
set(redirectObjAtom, { message: redirect.message, url: redirect.url });
} else {
set(sourceErrorAtom, (err as Error).message);
}
}
});

Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ declare namespace Ome {
axes?: string[] | Axis[];
}

interface Bioformats2rawlayout {
"bioformats2raw.layout": 3;
}

interface Acquisition {
id: number;
name?: string;
Expand Down
22 changes: 22 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const COLORS = {
export const MAGENTA_GREEN = [COLORS.magenta, COLORS.green];
export const RGB = [COLORS.red, COLORS.green, COLORS.blue];
export const CYMRGB = Object.values(COLORS).slice(0, -2);
export const OME_VALIDATOR_URL = "https://ome.github.io/ome-ngff-validator/";

async function normalizeStore(source: string | Readable): Promise<zarr.Location<Readable>> {
if (typeof source === "string") {
Expand Down Expand Up @@ -380,6 +381,23 @@ export class AssertionError extends Error {
}
}

/**
* Error thrown when we want to redirect.
*/
export class RedirectError extends Error {
url: string;

/**
* @param message The error message.
* @param url The url to redirect to.
*/
constructor(message: string, url: string) {
super(message);
this.name = "RedirectError";
this.url = url;
}
}

/**
* Make an assertion. An error is thrown if `expr` does not have truthy value.
*
Expand Down Expand Up @@ -425,3 +443,7 @@ export function isOmeroMultiscales(
export function isMultiscales(attrs: zarr.Attributes): attrs is { multiscales: Ome.Multiscale[] } {
return "multiscales" in attrs;
}

export function isBioformats2rawlayout(attrs: zarr.Attributes): attrs is { multiscales: Ome.Bioformats2rawlayout } {
return "bioformats2raw.layout" in attrs;
}

0 comments on commit aec3e45

Please sign in to comment.