Files
spacedrive/interface/components/PDFViewer.tsx
Vítor Vasconcellos dea7785857 [ENG-651] Fix PDF rendering breaking app on macOS (#854)
* Fix macOS PDF rendering
 - Fix app crashing due to PDF rendering receiving empty URLs
 - Attempt fix PDF rendering empty PDFs due to it not supporting range requests

* Fix dumb change from `data` to `src` in `<object>`
 - Fix QuickPreview not closing with space bar
 - Fix double-click simultaneously renaming and opening file
 - Minor improvements to QuickPreview header
 - Fix Button inside Button react error in QuickPreview
 - Don't render thumb without a valid source

* ExternalObject events must not influence the link state
 - More macOS PDF range changes

* Use `<iframe>` instead of `<embed>` or `<object>` to load pdf in macOS
 - Revert removing range support for macOS pdf type
 - Rename `ExternalObject` to `PDFViewer`
 - Fix `AddLocationDialog` sometimes requiring multiple confimations on first load

* `Accept-Ranges: none` Header response as it breaks linux video playback
 - Extract location id from from `ExplorerItemData`, to allow rendering original versions on `Overview`

* Format

* cargo fmt
2023-05-25 06:34:18 +00:00

66 lines
2.0 KiB
TypeScript

import { memo, useLayoutEffect, useMemo } from 'react';
import { useOperatingSystem } from '~/hooks';
export interface PDFViewerProps {
src: string;
onLoad?: (event: HTMLElementEventMap['load']) => void;
onError?: (event: HTMLElementEventMap['error']) => void;
className?: string;
crossOrigin?: React.ComponentProps<'link'>['crossOrigin'];
}
export const PDFViewer = memo(
({ src, onLoad, onError, className, crossOrigin }: PDFViewerProps) => {
const os = useOperatingSystem(true);
// Ignore empty urls
const href = !src || src === '#' ? null : src;
// Use link preload as a hack to get access to an onLoad and onError events for the object tag
// as well as to normalize the URL
const link = useMemo(() => {
if (href == null) return null;
const link = document.createElement('link');
link.as = 'fetch';
link.rel = 'preload';
if (crossOrigin) link.crossOrigin = crossOrigin;
link.href = href;
link.addEventListener('load', () => link.remove());
link.addEventListener('error', () => link.remove());
return link;
}, [crossOrigin, href]);
// The useLayoutEffect is used to ensure that the event listeners are added before the object is loaded
// The useLayoutEffect declaration order is important here
useLayoutEffect(() => {
if (!link) return;
if (onLoad) link.addEventListener('load', onLoad);
if (onError) link.addEventListener('error', onError);
return () => {
if (onLoad) link.removeEventListener('load', onLoad);
if (onError) link.removeEventListener('error', onError);
};
}, [link, onLoad, onError]);
useLayoutEffect(() => {
if (!link) return;
document.head.appendChild(link);
return () => link.remove();
}, [link]);
// Use link to normalize URL
return link ? (
os === 'macOS' ? (
// FIX-ME: Using <embed> isn't working in macOS for some reason
<iframe src={link.href} style={{ objectFit: 'unset' }} className={className} />
) : (
<embed src={link.href} type="application/pdf" className={className} />
)
) : null;
}
);