mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Fix lightbox fullscreen issues (#4149)
* Improve lightbox context hook * Prevent fullscreen drop while loading * Fix close not working from fullscreen
This commit is contained in:
@@ -319,12 +319,12 @@ export const LightboxComponent: React.FC<IProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const close = useCallback(() => {
|
const close = useCallback(() => {
|
||||||
if (!isFullscreen) {
|
if (isFullscreen) document.exitFullscreen();
|
||||||
hide();
|
|
||||||
document.body.style.overflow = "auto";
|
hide();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
document.body.style.overflow = "auto";
|
||||||
(Mousetrap as any).unpause();
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} else document.exitFullscreen();
|
(Mousetrap as any).unpause();
|
||||||
}, [isFullscreen, hide]);
|
}, [isFullscreen, hide]);
|
||||||
|
|
||||||
const handleClose = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClose = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
@@ -685,64 +685,269 @@ export const LightboxComponent: React.FC<IProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderBody() {
|
||||||
|
if (images.length === 0 || isLoading || isSwitchingPage) {
|
||||||
|
return <LoadingIndicator />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentImage: ILightboxImage | undefined = images[currentIndex];
|
||||||
|
|
||||||
|
function setRating(v: number | null) {
|
||||||
|
if (currentImage?.id) {
|
||||||
|
updateImage({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
id: currentImage.id,
|
||||||
|
rating100: v,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onIncrementClick() {
|
||||||
|
if (currentImage?.id === undefined) return;
|
||||||
|
try {
|
||||||
|
await mutateImageIncrementO(currentImage.id);
|
||||||
|
} catch (e) {
|
||||||
|
Toast.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDecrementClick() {
|
||||||
|
if (currentImage?.id === undefined) return;
|
||||||
|
try {
|
||||||
|
await mutateImageDecrementO(currentImage.id);
|
||||||
|
} catch (e) {
|
||||||
|
Toast.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onResetClick() {
|
||||||
|
if (currentImage?.id === undefined) return;
|
||||||
|
try {
|
||||||
|
await mutateImageResetO(currentImage?.id);
|
||||||
|
} catch (e) {
|
||||||
|
Toast.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageHeader =
|
||||||
|
page && pages
|
||||||
|
? intl.formatMessage(
|
||||||
|
{ id: "dialogs.lightbox.page_header" },
|
||||||
|
{ page, total: pages }
|
||||||
|
)
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={CLASSNAME_HEADER}>
|
||||||
|
<div className={CLASSNAME_LEFT_SPACER}>{renderChapterMenu()}</div>
|
||||||
|
<div className={CLASSNAME_INDICATOR}>
|
||||||
|
<span>
|
||||||
|
{chapterHeader()} {pageHeader}
|
||||||
|
</span>
|
||||||
|
{images.length > 1 ? (
|
||||||
|
<b ref={indicatorRef}>{`${currentIndex + 1} / ${
|
||||||
|
images.length
|
||||||
|
}`}</b>
|
||||||
|
) : undefined}
|
||||||
|
</div>
|
||||||
|
<div className={CLASSNAME_RIGHT}>
|
||||||
|
<div className={CLASSNAME_OPTIONS}>
|
||||||
|
<div className={CLASSNAME_OPTIONS_ICON}>
|
||||||
|
<Button
|
||||||
|
ref={overlayTarget}
|
||||||
|
variant="link"
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: "dialogs.lightbox.options",
|
||||||
|
})}
|
||||||
|
onClick={() => setShowOptions(!showOptions)}
|
||||||
|
>
|
||||||
|
<Icon icon={faCog} />
|
||||||
|
</Button>
|
||||||
|
<Overlay
|
||||||
|
target={overlayTarget.current}
|
||||||
|
show={showOptions}
|
||||||
|
placement="bottom"
|
||||||
|
container={containerRef}
|
||||||
|
rootClose
|
||||||
|
onHide={() => setShowOptions(false)}
|
||||||
|
>
|
||||||
|
{({ placement, arrowProps, show: _show, ...props }) => (
|
||||||
|
<div
|
||||||
|
className="popover"
|
||||||
|
{...props}
|
||||||
|
style={{ ...props.style }}
|
||||||
|
>
|
||||||
|
<Popover.Title>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "dialogs.lightbox.options",
|
||||||
|
})}
|
||||||
|
</Popover.Title>
|
||||||
|
<Popover.Content>{renderOptionsForm()}</Popover.Content>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Overlay>
|
||||||
|
</div>
|
||||||
|
<InputGroup className={CLASSNAME_OPTIONS_INLINE}>
|
||||||
|
{renderOptionsForm()}
|
||||||
|
</InputGroup>
|
||||||
|
</div>
|
||||||
|
{slideshowEnabled && (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={toggleSlideshow}
|
||||||
|
title="Toggle Slideshow"
|
||||||
|
>
|
||||||
|
<Icon icon={slideshowInterval !== null ? faPause : faPlay} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{zoom !== 1 && (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => {
|
||||||
|
setResetPosition(!resetPosition);
|
||||||
|
setZoom(1);
|
||||||
|
}}
|
||||||
|
title="Reset zoom"
|
||||||
|
>
|
||||||
|
<Icon icon={faSearchMinus} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{document.fullscreenEnabled && (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={toggleFullscreen}
|
||||||
|
title="Toggle Fullscreen"
|
||||||
|
>
|
||||||
|
<Icon icon={faExpand} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => close()}
|
||||||
|
title="Close Lightbox"
|
||||||
|
>
|
||||||
|
<Icon icon={faTimes} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={CLASSNAME_DISPLAY}>
|
||||||
|
{allowNavigation && (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={handleLeft}
|
||||||
|
className={`${CLASSNAME_NAVBUTTON} d-none d-lg-block`}
|
||||||
|
>
|
||||||
|
<Icon icon={faChevronLeft} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cx(CLASSNAME_CAROUSEL, {
|
||||||
|
[CLASSNAME_INSTANT]: instantTransition,
|
||||||
|
})}
|
||||||
|
style={{ left: `${currentIndex * -100}vw` }}
|
||||||
|
ref={carouselRef}
|
||||||
|
>
|
||||||
|
{images.map((image, i) => (
|
||||||
|
<div className={`${CLASSNAME_IMAGE}`} key={image.paths.image}>
|
||||||
|
{i >= currentIndex - 1 && i <= currentIndex + 1 ? (
|
||||||
|
<LightboxImage
|
||||||
|
src={image.paths.image ?? ""}
|
||||||
|
displayMode={displayMode}
|
||||||
|
scaleUp={lightboxSettings?.scaleUp ?? false}
|
||||||
|
scrollMode={
|
||||||
|
lightboxSettings?.scrollMode ??
|
||||||
|
GQL.ImageLightboxScrollMode.Zoom
|
||||||
|
}
|
||||||
|
resetPosition={resetPosition}
|
||||||
|
zoom={i === currentIndex ? zoom : 1}
|
||||||
|
scrollAttemptsBeforeChange={scrollAttemptsBeforeChange}
|
||||||
|
firstScroll={firstScroll}
|
||||||
|
inScrollGroup={inScrollGroup}
|
||||||
|
current={i === currentIndex}
|
||||||
|
alignBottom={movingLeft}
|
||||||
|
setZoom={updateZoom}
|
||||||
|
debouncedScrollReset={debouncedScrollReset}
|
||||||
|
onLeft={handleLeft}
|
||||||
|
onRight={handleRight}
|
||||||
|
isVideo={isVideo(image.visual_files?.[0] ?? {})}
|
||||||
|
/>
|
||||||
|
) : undefined}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{allowNavigation && (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={handleRight}
|
||||||
|
className={`${CLASSNAME_NAVBUTTON} d-none d-lg-block`}
|
||||||
|
>
|
||||||
|
<Icon icon={faChevronRight} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showNavigation && !isFullscreen && images.length > 1 && (
|
||||||
|
<div className={CLASSNAME_NAV} style={navOffset} ref={navRef}>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => setIndex(images.length - 1)}
|
||||||
|
className={CLASSNAME_NAVBUTTON}
|
||||||
|
>
|
||||||
|
<Icon icon={faArrowLeft} className="mr-4" />
|
||||||
|
</Button>
|
||||||
|
{navItems}
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => setIndex(0)}
|
||||||
|
className={CLASSNAME_NAVBUTTON}
|
||||||
|
>
|
||||||
|
<Icon icon={faArrowRight} className="ml-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={CLASSNAME_FOOTER}>
|
||||||
|
<div className={CLASSNAME_FOOTER_LEFT}>
|
||||||
|
{currentImage?.id !== undefined && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<OCounterButton
|
||||||
|
onDecrement={onDecrementClick}
|
||||||
|
onIncrement={onIncrementClick}
|
||||||
|
onReset={onResetClick}
|
||||||
|
value={currentImage?.o_counter ?? 0}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<RatingSystem
|
||||||
|
value={currentImage?.rating100 ?? undefined}
|
||||||
|
onSetRating={(v) => {
|
||||||
|
setRating(v ?? null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{currentImage?.title && (
|
||||||
|
<Link to={`/images/${currentImage.id}`} onClick={() => close()}>
|
||||||
|
{currentImage.title ?? ""}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isVisible) {
|
if (!isVisible) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (images.length === 0 || isLoading || isSwitchingPage) {
|
|
||||||
return <LoadingIndicator />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentImage: ILightboxImage | undefined = images[currentIndex];
|
|
||||||
|
|
||||||
function setRating(v: number | null) {
|
|
||||||
if (currentImage?.id) {
|
|
||||||
updateImage({
|
|
||||||
variables: {
|
|
||||||
input: {
|
|
||||||
id: currentImage.id,
|
|
||||||
rating100: v,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onIncrementClick() {
|
|
||||||
if (currentImage?.id === undefined) return;
|
|
||||||
try {
|
|
||||||
await mutateImageIncrementO(currentImage.id);
|
|
||||||
} catch (e) {
|
|
||||||
Toast.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onDecrementClick() {
|
|
||||||
if (currentImage?.id === undefined) return;
|
|
||||||
try {
|
|
||||||
await mutateImageDecrementO(currentImage.id);
|
|
||||||
} catch (e) {
|
|
||||||
Toast.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onResetClick() {
|
|
||||||
if (currentImage?.id === undefined) return;
|
|
||||||
try {
|
|
||||||
await mutateImageResetO(currentImage?.id);
|
|
||||||
} catch (e) {
|
|
||||||
Toast.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageHeader =
|
|
||||||
page && pages
|
|
||||||
? intl.formatMessage(
|
|
||||||
{ id: "dialogs.lightbox.page_header" },
|
|
||||||
{ page, total: pages }
|
|
||||||
)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={CLASSNAME}
|
className={CLASSNAME}
|
||||||
@@ -750,198 +955,7 @@ export const LightboxComponent: React.FC<IProps> = ({
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
>
|
>
|
||||||
<div className={CLASSNAME_HEADER}>
|
{renderBody()}
|
||||||
<div className={CLASSNAME_LEFT_SPACER}>{renderChapterMenu()}</div>
|
|
||||||
<div className={CLASSNAME_INDICATOR}>
|
|
||||||
<span>
|
|
||||||
{chapterHeader()} {pageHeader}
|
|
||||||
</span>
|
|
||||||
{images.length > 1 ? (
|
|
||||||
<b ref={indicatorRef}>{`${currentIndex + 1} / ${images.length}`}</b>
|
|
||||||
) : undefined}
|
|
||||||
</div>
|
|
||||||
<div className={CLASSNAME_RIGHT}>
|
|
||||||
<div className={CLASSNAME_OPTIONS}>
|
|
||||||
<div className={CLASSNAME_OPTIONS_ICON}>
|
|
||||||
<Button
|
|
||||||
ref={overlayTarget}
|
|
||||||
variant="link"
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id: "dialogs.lightbox.options",
|
|
||||||
})}
|
|
||||||
onClick={() => setShowOptions(!showOptions)}
|
|
||||||
>
|
|
||||||
<Icon icon={faCog} />
|
|
||||||
</Button>
|
|
||||||
<Overlay
|
|
||||||
target={overlayTarget.current}
|
|
||||||
show={showOptions}
|
|
||||||
placement="bottom"
|
|
||||||
container={containerRef}
|
|
||||||
rootClose
|
|
||||||
onHide={() => setShowOptions(false)}
|
|
||||||
>
|
|
||||||
{({ placement, arrowProps, show: _show, ...props }) => (
|
|
||||||
<div
|
|
||||||
className="popover"
|
|
||||||
{...props}
|
|
||||||
style={{ ...props.style }}
|
|
||||||
>
|
|
||||||
<Popover.Title>
|
|
||||||
{intl.formatMessage({
|
|
||||||
id: "dialogs.lightbox.options",
|
|
||||||
})}
|
|
||||||
</Popover.Title>
|
|
||||||
<Popover.Content>{renderOptionsForm()}</Popover.Content>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Overlay>
|
|
||||||
</div>
|
|
||||||
<InputGroup className={CLASSNAME_OPTIONS_INLINE}>
|
|
||||||
{renderOptionsForm()}
|
|
||||||
</InputGroup>
|
|
||||||
</div>
|
|
||||||
{slideshowEnabled && (
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={toggleSlideshow}
|
|
||||||
title="Toggle Slideshow"
|
|
||||||
>
|
|
||||||
<Icon icon={slideshowInterval !== null ? faPause : faPlay} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{zoom !== 1 && (
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={() => {
|
|
||||||
setResetPosition(!resetPosition);
|
|
||||||
setZoom(1);
|
|
||||||
}}
|
|
||||||
title="Reset zoom"
|
|
||||||
>
|
|
||||||
<Icon icon={faSearchMinus} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{document.fullscreenEnabled && (
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={toggleFullscreen}
|
|
||||||
title="Toggle Fullscreen"
|
|
||||||
>
|
|
||||||
<Icon icon={faExpand} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button variant="link" onClick={() => close()} title="Close Lightbox">
|
|
||||||
<Icon icon={faTimes} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={CLASSNAME_DISPLAY}>
|
|
||||||
{allowNavigation && (
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={handleLeft}
|
|
||||||
className={`${CLASSNAME_NAVBUTTON} d-none d-lg-block`}
|
|
||||||
>
|
|
||||||
<Icon icon={faChevronLeft} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={cx(CLASSNAME_CAROUSEL, {
|
|
||||||
[CLASSNAME_INSTANT]: instantTransition,
|
|
||||||
})}
|
|
||||||
style={{ left: `${currentIndex * -100}vw` }}
|
|
||||||
ref={carouselRef}
|
|
||||||
>
|
|
||||||
{images.map((image, i) => (
|
|
||||||
<div className={`${CLASSNAME_IMAGE}`} key={image.paths.image}>
|
|
||||||
{i >= currentIndex - 1 && i <= currentIndex + 1 ? (
|
|
||||||
<LightboxImage
|
|
||||||
src={image.paths.image ?? ""}
|
|
||||||
displayMode={displayMode}
|
|
||||||
scaleUp={lightboxSettings?.scaleUp ?? false}
|
|
||||||
scrollMode={
|
|
||||||
lightboxSettings?.scrollMode ??
|
|
||||||
GQL.ImageLightboxScrollMode.Zoom
|
|
||||||
}
|
|
||||||
resetPosition={resetPosition}
|
|
||||||
zoom={i === currentIndex ? zoom : 1}
|
|
||||||
scrollAttemptsBeforeChange={scrollAttemptsBeforeChange}
|
|
||||||
firstScroll={firstScroll}
|
|
||||||
inScrollGroup={inScrollGroup}
|
|
||||||
current={i === currentIndex}
|
|
||||||
alignBottom={movingLeft}
|
|
||||||
setZoom={updateZoom}
|
|
||||||
debouncedScrollReset={debouncedScrollReset}
|
|
||||||
onLeft={handleLeft}
|
|
||||||
onRight={handleRight}
|
|
||||||
isVideo={isVideo(image.visual_files?.[0] ?? {})}
|
|
||||||
/>
|
|
||||||
) : undefined}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{allowNavigation && (
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={handleRight}
|
|
||||||
className={`${CLASSNAME_NAVBUTTON} d-none d-lg-block`}
|
|
||||||
>
|
|
||||||
<Icon icon={faChevronRight} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{showNavigation && !isFullscreen && images.length > 1 && (
|
|
||||||
<div className={CLASSNAME_NAV} style={navOffset} ref={navRef}>
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={() => setIndex(images.length - 1)}
|
|
||||||
className={CLASSNAME_NAVBUTTON}
|
|
||||||
>
|
|
||||||
<Icon icon={faArrowLeft} className="mr-4" />
|
|
||||||
</Button>
|
|
||||||
{navItems}
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={() => setIndex(0)}
|
|
||||||
className={CLASSNAME_NAVBUTTON}
|
|
||||||
>
|
|
||||||
<Icon icon={faArrowRight} className="ml-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={CLASSNAME_FOOTER}>
|
|
||||||
<div className={CLASSNAME_FOOTER_LEFT}>
|
|
||||||
{currentImage?.id !== undefined && (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<OCounterButton
|
|
||||||
onDecrement={onDecrementClick}
|
|
||||||
onIncrement={onIncrementClick}
|
|
||||||
onReset={onResetClick}
|
|
||||||
value={currentImage?.o_counter ?? 0}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<RatingSystem
|
|
||||||
value={currentImage?.rating100 ?? undefined}
|
|
||||||
onSetRating={(v) => {
|
|
||||||
setRating(v ?? null);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{currentImage?.title && (
|
|
||||||
<Link to={`/images/${currentImage.id}`} onClick={() => close()}>
|
|
||||||
{currentImage.title ?? ""}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,12 +19,21 @@ export interface IState {
|
|||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
interface IContext {
|
interface IContext {
|
||||||
|
lightboxState: IState;
|
||||||
setLightboxState: (state: Partial<IState>) => void;
|
setLightboxState: (state: Partial<IState>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LightboxContext = React.createContext<IContext>({
|
export const LightboxContext = React.createContext<IContext | null>(null);
|
||||||
setLightboxState: () => {},
|
|
||||||
});
|
export function useLightboxContext() {
|
||||||
|
const context = React.useContext(LightboxContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useLightboxContext must be used within a LightboxProvider"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
export const LightboxProvider: React.FC = ({ children }) => {
|
export const LightboxProvider: React.FC = ({ children }) => {
|
||||||
const [lightboxState, setLightboxState] = useState<IState>({
|
const [lightboxState, setLightboxState] = useState<IState>({
|
||||||
@@ -53,7 +62,9 @@ export const LightboxProvider: React.FC = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LightboxContext.Provider value={{ setLightboxState: setPartialState }}>
|
<LightboxContext.Provider
|
||||||
|
value={{ lightboxState, setLightboxState: setPartialState }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
{lightboxState.isVisible && (
|
{lightboxState.isVisible && (
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { LightboxContext, IState } from "./context";
|
import { IState, useLightboxContext } from "./context";
|
||||||
import { IChapter } from "./types";
|
import { IChapter } from "./types";
|
||||||
|
|
||||||
export const useLightbox = (
|
export const useLightbox = (
|
||||||
state: Partial<Omit<IState, "isVisible">>,
|
state: Partial<Omit<IState, "isVisible">>,
|
||||||
chapters: IChapter[] = []
|
chapters: IChapter[] = []
|
||||||
) => {
|
) => {
|
||||||
const { setLightboxState } = useContext(LightboxContext);
|
const { setLightboxState } = useLightboxContext();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLightboxState({
|
setLightboxState({
|
||||||
@@ -50,7 +50,7 @@ export const useLightbox = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useGalleryLightbox = (id: string, chapters: IChapter[] = []) => {
|
export const useGalleryLightbox = (id: string, chapters: IChapter[] = []) => {
|
||||||
const { setLightboxState } = useContext(LightboxContext);
|
const { setLightboxState } = useLightboxContext();
|
||||||
|
|
||||||
const pageSize = 40;
|
const pageSize = 40;
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user