From 9f5bcca1eb6e7c5a94e5877f997216193a3eab7d Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:35:55 +1000 Subject: [PATCH] Fix lightbox fullscreen issues (#4149) * Improve lightbox context hook * Prevent fullscreen drop while loading * Fix close not working from fullscreen --- ui/v2.5/src/hooks/Lightbox/Lightbox.tsx | 468 ++++++++++++------------ ui/v2.5/src/hooks/Lightbox/context.tsx | 19 +- ui/v2.5/src/hooks/Lightbox/hooks.ts | 8 +- 3 files changed, 260 insertions(+), 235 deletions(-) diff --git a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx index 9388061beb5..5430bede94a 100644 --- a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx +++ b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx @@ -319,12 +319,12 @@ export const LightboxComponent: React.FC = ({ }); const close = useCallback(() => { - if (!isFullscreen) { - hide(); - document.body.style.overflow = "auto"; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (Mousetrap as any).unpause(); - } else document.exitFullscreen(); + if (isFullscreen) document.exitFullscreen(); + + hide(); + document.body.style.overflow = "auto"; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (Mousetrap as any).unpause(); }, [isFullscreen, hide]); const handleClose = (e: React.MouseEvent) => { @@ -685,263 +685,277 @@ export const LightboxComponent: React.FC = ({ ); } - if (!isVisible) { - return <>; - } - - if (images.length === 0 || isLoading || isSwitchingPage) { - return ; - } + function renderBody() { + if (images.length === 0 || isLoading || isSwitchingPage) { + return ; + } - const currentImage: ILightboxImage | undefined = images[currentIndex]; + const currentImage: ILightboxImage | undefined = images[currentIndex]; - function setRating(v: number | null) { - if (currentImage?.id) { - updateImage({ - variables: { - input: { - id: currentImage.id, - rating100: v, + 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 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 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); + 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 } - ) - : ""; + const pageHeader = + page && pages + ? intl.formatMessage( + { id: "dialogs.lightbox.page_header" }, + { page, total: pages } + ) + : ""; - return ( -
-
-
{renderChapterMenu()}
-
- - {chapterHeader()} {pageHeader} - - {images.length > 1 ? ( - {`${currentIndex + 1} / ${images.length}`} - ) : undefined} -
-
-
-
+ return ( + <> +
+
{renderChapterMenu()}
+
+ + {chapterHeader()} {pageHeader} + + {images.length > 1 ? ( + {`${currentIndex + 1} / ${ + images.length + }`} + ) : undefined} +
+
+
+
+ + setShowOptions(false)} + > + {({ placement, arrowProps, show: _show, ...props }) => ( +
+ + {intl.formatMessage({ + id: "dialogs.lightbox.options", + })} + + {renderOptionsForm()} +
+ )} +
+
+ + {renderOptionsForm()} + +
+ {slideshowEnabled && ( - setShowOptions(false)} + )} + {zoom !== 1 && ( +
- - {renderOptionsForm()} - -
- {slideshowEnabled && ( + + + )} + {document.fullscreenEnabled && ( + + )} - )} - {zoom !== 1 && ( +
+
+
+ {allowNavigation && ( )} - {document.fullscreenEnabled && ( + +
+ {images.map((image, i) => ( +
+ {i >= currentIndex - 1 && i <= currentIndex + 1 ? ( + + ) : undefined} +
+ ))} +
+ + {allowNavigation && ( )} -
-
-
- {allowNavigation && ( - + {showNavigation && !isFullscreen && images.length > 1 && ( +
+ + {navItems} + +
)} - -
- {images.map((image, i) => ( -
- {i >= currentIndex - 1 && i <= currentIndex + 1 ? ( - +
+ {currentImage?.id !== undefined && ( + <> +
+ +
+ { + setRating(v ?? null); + }} /> - ) : undefined} -
- ))} + + )} +
+
+ {currentImage?.title && ( + close()}> + {currentImage.title ?? ""} + + )} +
+
+ + ); + } - {allowNavigation && ( - - )} -
- {showNavigation && !isFullscreen && images.length > 1 && ( -
- - {navItems} - -
- )} -
-
- {currentImage?.id !== undefined && ( - <> -
- -
- { - setRating(v ?? null); - }} - /> - - )} -
-
- {currentImage?.title && ( - close()}> - {currentImage.title ?? ""} - - )} -
-
-
+ if (!isVisible) { + return <>; + } + + return ( +
+ {renderBody()}
); }; diff --git a/ui/v2.5/src/hooks/Lightbox/context.tsx b/ui/v2.5/src/hooks/Lightbox/context.tsx index b44a1127d83..8bae11cffe1 100644 --- a/ui/v2.5/src/hooks/Lightbox/context.tsx +++ b/ui/v2.5/src/hooks/Lightbox/context.tsx @@ -19,12 +19,21 @@ export interface IState { onClose?: () => void; } interface IContext { + lightboxState: IState; setLightboxState: (state: Partial) => void; } -export const LightboxContext = React.createContext({ - setLightboxState: () => {}, -}); +export const LightboxContext = React.createContext(null); + +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 }) => { const [lightboxState, setLightboxState] = useState({ @@ -53,7 +62,9 @@ export const LightboxProvider: React.FC = ({ children }) => { }; return ( - + {children} }> {lightboxState.isVisible && ( diff --git a/ui/v2.5/src/hooks/Lightbox/hooks.ts b/ui/v2.5/src/hooks/Lightbox/hooks.ts index 8ec511f52e2..a54de4c3095 100644 --- a/ui/v2.5/src/hooks/Lightbox/hooks.ts +++ b/ui/v2.5/src/hooks/Lightbox/hooks.ts @@ -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 { LightboxContext, IState } from "./context"; +import { IState, useLightboxContext } from "./context"; import { IChapter } from "./types"; export const useLightbox = ( state: Partial>, chapters: IChapter[] = [] ) => { - const { setLightboxState } = useContext(LightboxContext); + const { setLightboxState } = useLightboxContext(); useEffect(() => { setLightboxState({ @@ -50,7 +50,7 @@ export const useLightbox = ( }; export const useGalleryLightbox = (id: string, chapters: IChapter[] = []) => { - const { setLightboxState } = useContext(LightboxContext); + const { setLightboxState } = useLightboxContext(); const pageSize = 40; const [page, setPage] = useState(1);