diff --git a/package-lock.json b/package-lock.json index bf369e3..3ef1bf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15651,22 +15651,6 @@ "dev": true, "license": "ISC" }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "extraneous": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/src/NMRiumWrapper.tsx b/src/NMRiumWrapper.tsx index 9bb85d7..f21ed91 100644 --- a/src/NMRiumWrapper.tsx +++ b/src/NMRiumWrapper.tsx @@ -5,6 +5,7 @@ import { useCallback, useEffect, useRef } from 'react'; import { RootLayout } from 'react-science/ui'; import { LoadingIndicator } from './Loadingindicator.js'; +import { loadSpectraFromSource } from './data-source/loadSpectraFromSource.js'; import events from './events/event.js'; import { useLoadSpectra } from './hooks/useLoadSpectra.js'; import { usePreferences } from './hooks/usePreferences.js'; @@ -20,8 +21,13 @@ const containerStyle: CSSProperties = { export default function NMRiumWrapper() { const { allowedOrigins, isFetchAllowedOriginsPending } = useWhiteList(); const nmriumRef = useRef(null); - const { workspace, preferences, defaultEmptyMessage, customWorkspaces } = - usePreferences(); + const { + workspace, + preferences, + defaultEmptyMessage, + customWorkspaces, + spectraSource, + } = usePreferences(); const { load: loadSpectra, data, isLoading, setActiveTab } = useLoadSpectra(); @@ -33,6 +39,25 @@ export default function NMRiumWrapper() { events.trigger('data-change', { state, source }); }, []); + useEffect(() => { + if (!spectraSource) return; + + const { source, id } = spectraSource; + + async function loadFromSource() { + try { + const nmrium = await loadSpectraFromSource(source, id); + void loadSpectra({ nmrium }); + } catch (error) { + events.trigger('error', error as Error); + // eslint-disable-next-line no-console + console.error(error); + } + } + + void loadFromSource(); + }, [spectraSource, loadSpectra]); + useEffect(() => { const clearActionListener = events.on( 'action-request', diff --git a/src/config/nmrxiv/index.ts b/src/config/nmrxiv/index.ts new file mode 100644 index 0000000..a723cec --- /dev/null +++ b/src/config/nmrxiv/index.ts @@ -0,0 +1,10 @@ +import development from './nmrxiv.development.json' with { type: 'json' }; +import production from './nmrxiv.production.json' with { type: 'json' }; + +interface NmrXivConfig { + baseURL: string; +} + +export const nmrXivConfig: NmrXivConfig = import.meta.env.DEV + ? development + : production; diff --git a/src/config/nmrxiv/nmrxiv.development.json b/src/config/nmrxiv/nmrxiv.development.json new file mode 100644 index 0000000..d2493bf --- /dev/null +++ b/src/config/nmrxiv/nmrxiv.development.json @@ -0,0 +1,3 @@ +{ + "baseURL": "https://dev.nmrxiv.org/api/v1/samples" +} \ No newline at end of file diff --git a/src/config/nmrxiv/nmrxiv.production.json b/src/config/nmrxiv/nmrxiv.production.json new file mode 100644 index 0000000..a0f9518 --- /dev/null +++ b/src/config/nmrxiv/nmrxiv.production.json @@ -0,0 +1,3 @@ +{ + "baseURL": "https://nmrxiv.org/api/v1/samples" +} \ No newline at end of file diff --git a/src/data-source/loadSpectraFromSource.ts b/src/data-source/loadSpectraFromSource.ts new file mode 100644 index 0000000..403c090 --- /dev/null +++ b/src/data-source/loadSpectraFromSource.ts @@ -0,0 +1,20 @@ +import { loadNmrXivSpectraById } from './nmrxiv.js'; + +export type SpectraSource = 'nmrxiv'; + +type SpectraSourceLoader = (id: string) => Promise; + +const spectraSourceLoaders: Record = { + nmrxiv: loadNmrXivSpectraById, +}; + +export async function loadSpectraFromSource( + source: string, + id: string, +): Promise { + const loader = spectraSourceLoaders[source]; + if (!loader) { + throw new Error(`Unknown spectra source: "${source}"`); + } + return loader(id); +} diff --git a/src/data-source/nmrxiv.ts b/src/data-source/nmrxiv.ts new file mode 100644 index 0000000..034f000 --- /dev/null +++ b/src/data-source/nmrxiv.ts @@ -0,0 +1,17 @@ +import { nmrXivConfig } from '../config/nmrxiv/index.js'; + +function getNmrXivSpectraJsonURL(id: string): string { + return `${nmrXivConfig.baseURL}/${id}/nmriumInfo`; +} + +export async function loadNmrXivSpectraById(id: string): Promise { + const response = await fetch(getNmrXivSpectraJsonURL(id)); + + if (!response.ok) { + throw new Error( + `Failed to fetch nmrXiv spectra "${id}" (status ${response.status})`, + ); + } + + return response.json(); +} diff --git a/src/hooks/usePreferences.ts b/src/hooks/usePreferences.ts index 567aebc..9d7280b 100644 --- a/src/hooks/usePreferences.ts +++ b/src/hooks/usePreferences.ts @@ -3,7 +3,9 @@ import type { WorkspacePreferences, } from '@zakodium/nmrium-core'; import type { NMRiumWorkspace } from 'nmrium'; +import { useMemo } from 'react'; +import { parseSpectraSource } from '../utilities/parseSpectraSource.js'; import type { WorkspaceOptions } from '../workspaces/integration.js'; import { getIntegrationWorkspace } from '../workspaces/integration.js'; import { getNmrXivWorkspace } from '../workspaces/nmrxiv.js'; @@ -58,11 +60,18 @@ export function usePreferences() { parameters.get('hidePanelOnLoad')?.toLowerCase() === 'true'; } + const rawSpectra = parameters.get('spectra'); + const spectraSource = useMemo( + () => parseSpectraSource(rawSpectra), + [rawSpectra], + ); + return { preferences, workspace, defaultEmptyMessage, customWorkspaces, + spectraSource, }; } diff --git a/src/utilities/parseSpectraSource.ts b/src/utilities/parseSpectraSource.ts new file mode 100644 index 0000000..274bea7 --- /dev/null +++ b/src/utilities/parseSpectraSource.ts @@ -0,0 +1,25 @@ +export interface SpectraSource { + source: string; + id: string; +} + +/** + * Parses a `spectra` query parameter in the format `:` and returns an object with `source` and `id` properties. + * + * Example: + * `nmrxiv:S2173` => { source: 'nmrxiv', id: 'S2173' } + */ +export function parseSpectraSource( + value: string | null, +): SpectraSource | undefined { + if (!value) return undefined; + + const separatorIndex = value.indexOf(':'); + if (separatorIndex === -1) return undefined; + + const source = value.slice(0, separatorIndex).trim(); + const id = value.slice(separatorIndex + 1).trim(); + if (!source || !id) return undefined; + + return { source, id }; +}