Skip to content
12 changes: 12 additions & 0 deletions src/api/nexus/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ export async function updateJsonFileByUrl(
);
}

export function fetchResourceByIdRaw(
id: string,
session: Session,
options?: ComposeUrlParams,
headerExtraOptions?: Record<string, string> | null
) {
const url = composeUrl('resource', id, options);
return fetch(url, {
headers: createHeaders(session.accessToken, headerExtraOptions),
});
}

export function fetchResourceById<T>(
id: string,
session: Session,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,8 @@ import { useAtomValue } from 'jotai';

import { meModelResourceAtom } from '@/state/virtual-lab/build/me-model';
import { PDFViewerContainer } from '@/components/explore-section/common/pdf/PDFViewerContainer';
import { AnalysisFileType } from '@/components/explore-section/common/pdf/types';
import { composeUrl, ensureArray } from '@/util/nexus';
import { FileDistribution } from '@/types/explore-section/delta-properties';

const categoryMap: Record<string, string> = {
traces: AnalysisFileType.Traces,
scores: AnalysisFileType.Scores,
parameters_distribution: AnalysisFileType.Distribution,
thumbnail: AnalysisFileType.Thumbnail,
currentscape: AnalysisFileType.Currentscape,
};
import { ensureArray } from '@/util/nexus';

export default function AnalysisPreview() {
const meModelResource = useAtomValue(meModelResourceAtom);
Expand All @@ -26,27 +17,7 @@ export default function AnalysisPreview() {
);
}

const distributions = ensureArray(image).map((i) => {
const encodingFormat = i.about?.includes('thumbnail') ? 'image/png' : 'application/pdf';

const category = i.about?.split('/').at(-1);
const name = category ? categoryMap[category] : 'unknown.pdf';

const [org, project] = i['@id'].split('/').slice(-3, -1);

return {
...i,
'@type': 'DataDownload',
name,
contentSize: {
unitCode: 'bytes',
value: 0,
},
contentUrl: composeUrl('file', i['@id'], { org, project }),
encodingFormat,
atLocation: {} as FileDistribution['atLocation'],
} satisfies FileDistribution;
});

return <PDFViewerContainer distributions={distributions} />;
return (
<PDFViewerContainer distributions={ensureArray(image) as { '@id': string; about: string }[]} />
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Analysis() {

return (
<div className="-mt-7 border border-primary-8 p-6">
<PDFViewerContainer distributions={ensureArray(detail.data.distribution)} />
<PDFViewerContainer distributions={ensureArray(detail.data.image)} />
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export default function Launcher({ analysis }: { analysis?: Analysis }) {
<RunningAnalysis createdAt={analysisStarted} />
</div>
)}
{/* @ts-ignore */}
{analysisPDFUrl && <PDFViewer url={analysisPDFUrl} />}
</>
);
Expand Down
70 changes: 42 additions & 28 deletions src/components/explore-section/common/pdf/PDFViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
'use client';

import Image from 'next/image';
import { Fragment, useMemo, useState, useEffect } from 'react';
import { Fragment, useState, useEffect, useMemo } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';
import { Divider, Skeleton, Empty } from 'antd';
import { AnalysisType } from './types';
import { AnalysisType, typeLabel } from './types';
import { getSession } from '@/authFetch';
import { createHeaders, classNames } from '@/util/utils';
import { classNames } from '@/util/utils';
import { useAccessToken } from '@/hooks/useAccessToken';
import { fetchResourceByIdRaw } from '@/api/nexus';
import { nexus } from '@/config';
import { composeUrl } from '@/util/nexus';
import styles from './styles.module.css';

pdfjs.GlobalWorkerOptions.workerSrc = new URL(
Expand All @@ -16,42 +19,51 @@ pdfjs.GlobalWorkerOptions.workerSrc = new URL(
).toString();

type Props = {
url: string;
type?: string;
distribution: Distribution;
};

const options = {
standardFontDataUrl: '/standard_fonts/',
};

export default function PDFViewer({ url, type }: Props) {
type Distribution = { '@id': string; about: string };

export default function PDFViewer({ distribution }: Props) {
const [totalPages, setNumPages] = useState<number>();
const token = useAccessToken();

const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => {
setNumPages(numPages);
};

const parts = distribution.about.split('/');
const type = parts.at(-1) as AnalysisType;

const id = distribution['@id'];

const pdfFile = useMemo(
() => ({
url,
url: composeUrl('resource', id, {
org: nexus.org,
project: nexus.project,
}),
httpHeaders: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}),
[url, token]
[token, id]
);

return (
<div className="mt-4 flex flex-col items-center">
{type && (
<h2 className="mb-6 w-full bg-neutral-1 p-3 text-center text-2xl font-bold capitalize text-primary-8">
{type}
<h2 className="mb-6 w-full bg-neutral-1 p-3 text-center text-2xl font-bold text-primary-8">
{typeLabel(type)}
</h2>
)}
{type === AnalysisType.Thumbnail ? (
<ImageViewer contentUrl={url} />
{type === 'thumbnail' ? (
<ImageViewer contentUrl={distribution['@id']} />
) : (
<Document
options={options}
Expand All @@ -60,7 +72,7 @@ export default function PDFViewer({ url, type }: Props) {
className={classNames('w-full', styles.pdf)}
>
{Array.from(new Array(totalPages), (el, index) => (
<Fragment key={url}>
<Fragment key={distribution['@id']}>
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
Expand All @@ -81,29 +93,31 @@ export default function PDFViewer({ url, type }: Props) {
}

function ImageViewer({ contentUrl }: { contentUrl: string }) {
const [thumbnail, setImage] = useState<string | null>(null);
const [thumbnail, setThumbnail] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(false);

useEffect(() => {
(async () => {
const fetch = async () => {
const session = await getSession();
if (!session) {
return null;
}
setLoading(true);
fetch(contentUrl, {
method: 'GET',
headers: createHeaders(session.accessToken),
})
.then((response) => {
return response.blob();
})
.then((blob) => {
setImage(URL.createObjectURL(blob));
setLoading(false);
})
.catch(() => setLoading(false));
})();

const res = await fetchResourceByIdRaw(contentUrl, session, {
org: nexus.org,
project: nexus.project,
});

const blob = await res.blob();
setThumbnail(URL.createObjectURL(blob));
};

try {
fetch();
} finally {
setLoading(false);
}
}, [contentUrl]);

if (thumbnail) {
Expand Down
Loading