spacedrive/interface/hooks/useCallbackToWatchResize.ts
Vítor Vasconcellos 30e7c9d709
[ENG-528] QuickPreview isn't correctly handling errors for video/audio playback (#815)
* Centralize the file preview logic in `Thumb.tsx`

* Fix useEffect

* Fix Inspector thumb keeping internal state from previous selected files
 - Change video border to follow video aspect-ratio, just like Finder

* Restore memo to Thumb
 - Only add borders to video thumb, not the video itself

* Simplify and improve Thumb logic
 - Add A internal Thumbnail component
 - Rename main component to FileThumb to match mobile naming
 - Move getIcon utility function to assets/icons

* Add new `useCallbackToWatchResize` hook
 - Replace `ResizeObserver` with new resize hook in `Thumb`
 - Simplify and improve `useIsDark` hook by replacing `react-responsive` with direct usage of WebAPI `matchMedia`
 - Fix `Thumb` src not updating correctly
 - Fix video extension incorrectly showing when size <= 80 in `Thumb`

* Fix `Inspector` not updating thumb type
 - Remove superfluous `newThumb` from `getExplorerItemData`

* Remove superfluous `ThumSize`

* Forgot a `?`

* Fix incorrect className check in `Thumb`
 - Updated changed files to use the hooks root import

* Format

* Fix PDF preview compleatly breaking the application
 - Allow Linux to access both the spacedrive:// custom protocol and the workaround webserver
 - On Linux only use the webserver for audio and video, which don't work when requested through a custom protocol
 - Configure tauri IPC to allow API access to the spacedrive://localhost domain, to avoid PDF previews from breaking the security scope and rendering the application unusable
 - Configure CSP to allow the pdf plugin custom protocol used by webkit
 - Fix race condition between Thumb error handler and thumbType useEffect, by using replacing it with a useLayoutEffect
 - Improve Thumb's error handling
2023-05-18 03:31:15 +00:00

78 lines
2.1 KiB
TypeScript

import { DependencyList, Dispatch, RefObject, SetStateAction, useCallback, useEffect } from 'react';
export type ResizeRect = Readonly<Omit<DOMRectReadOnly, 'toJSON'>>;
type Cb = (rect: ResizeRect) => void;
const defaultRect: ResizeRect = {
y: 0,
x: 0,
top: 0,
left: 0,
right: 0,
width: 0,
height: 0,
bottom: 0
};
const observedElementsCb = new WeakMap<Element, Set<Cb>>();
// Why use a single ResizeObserver instead of one per component?
// https://github.com/WICG/resize-observer/issues/59
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const elem = entry.target;
const cbs = observedElementsCb.get(elem);
if (cbs) {
// TODO: contentRect is included in the spec for web compat reasons, and may be deprecated one day
// Find a way to reconstruct contentRect from the other properties
// Do not use elem.getBoundingClientRect() as it is very CPU expensive
for (const cb of cbs) cb(entry.contentRect);
} else {
resizeObserver.unobserve(elem);
}
}
});
export function useCallbackToWatchResize(
callback: Cb,
deps: [RefObject<Element>, ...React.DependencyList]
): void;
export function useCallbackToWatchResize(
callback: Cb,
deps: React.DependencyList,
_ref: RefObject<Element>
): void;
export function useCallbackToWatchResize(
callback: Cb,
deps: DependencyList,
_ref?: RefObject<Element>
) {
const ref = _ref ?? (deps[0] as RefObject<Element> | undefined);
if (ref == null) throw new Error('Element not provided');
// Disable lint warning because this hook is a wrapper for useCallback
// eslint-disable-next-line react-hooks/exhaustive-deps
const onResize = useCallback(callback, deps);
useEffect(() => {
const elem = ref.current;
if (elem == null) {
onResize(defaultRect);
return;
}
const setStates =
observedElementsCb.get(elem) ?? new Set<Dispatch<SetStateAction<ResizeRect>>>();
observedElementsCb.set(elem, setStates);
setStates.add(onResize);
resizeObserver.observe(elem);
return () => {
resizeObserver.unobserve(elem);
setStates.delete(onResize);
if (setStates.size === 0) observedElementsCb.delete(elem);
};
}, [ref, onResize]);
}