108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Spin } from 'antd';
|
|
import type { CSSProperties } from 'react';
|
|
import { getAccessToken } from '../auth/oidc';
|
|
|
|
export function useAuthUrl(url: string | null | undefined): string | null {
|
|
const [blobUrl, setBlobUrl] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!url) {
|
|
setBlobUrl(null);
|
|
return;
|
|
}
|
|
let cancelled = false;
|
|
let objectUrl: string | undefined;
|
|
|
|
(async () => {
|
|
try {
|
|
const token = await getAccessToken();
|
|
if (cancelled) return;
|
|
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
if (cancelled || !res.ok) return;
|
|
const blob = await res.blob();
|
|
if (cancelled) return;
|
|
objectUrl = URL.createObjectURL(blob);
|
|
setBlobUrl(objectUrl);
|
|
} catch {
|
|
// image/resource just won't show
|
|
}
|
|
})();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
setBlobUrl(null);
|
|
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
};
|
|
}, [url]);
|
|
|
|
return blobUrl;
|
|
}
|
|
|
|
interface AuthImageProps {
|
|
src: string;
|
|
width?: number | string;
|
|
height?: number | string;
|
|
style?: CSSProperties;
|
|
alt?: string;
|
|
}
|
|
|
|
export function AuthImage({ src, width, height, style, alt = '' }: AuthImageProps) {
|
|
const blobUrl = useAuthUrl(src);
|
|
if (!blobUrl) {
|
|
return (
|
|
<div
|
|
style={{
|
|
width,
|
|
height: height ?? width,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: '#f5f5f5',
|
|
borderRadius: style?.borderRadius ?? 0,
|
|
}}
|
|
>
|
|
<Spin size="small" />
|
|
</div>
|
|
);
|
|
}
|
|
return <img src={blobUrl} width={width} height={height} style={style} alt={alt} />;
|
|
}
|
|
|
|
interface AuthIframeProps {
|
|
src: string;
|
|
style?: CSSProperties;
|
|
title?: string;
|
|
}
|
|
|
|
export function AuthIframe({ src, style, title }: AuthIframeProps) {
|
|
const hashIdx = src.indexOf('#');
|
|
const cleanUrl = hashIdx >= 0 ? src.slice(0, hashIdx) : src;
|
|
const hash = hashIdx >= 0 ? src.slice(hashIdx) : '';
|
|
|
|
const blobUrl = useAuthUrl(cleanUrl);
|
|
|
|
if (!blobUrl) {
|
|
return (
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', ...style }}>
|
|
<Spin />
|
|
</div>
|
|
);
|
|
}
|
|
return <iframe src={`${blobUrl}${hash}`} style={{ border: 'none', ...style }} title={title} />;
|
|
}
|
|
|
|
export async function openAuthUrl(url: string): Promise<void> {
|
|
try {
|
|
const token = await getAccessToken();
|
|
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
if (!res.ok) return;
|
|
const blob = await res.blob();
|
|
const blobUrl = URL.createObjectURL(blob);
|
|
window.open(blobUrl, '_blank');
|
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000);
|
|
} catch {
|
|
// silently fail
|
|
}
|
|
}
|