From 867a4d0dc776773933dbc635fae5ee666fd97b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Mon, 25 Sep 2023 16:12:04 +0200 Subject: [PATCH 1/4] Add picture sequence navigation buttons --- .../components/views/picture/PictureView.tsx | 103 +++++++++++++----- .../overlay/PictureNavigationButtons.tsx | 35 +++++- .../src/hooks/presentation-channel.hook.ts | 15 +-- 3 files changed, 115 insertions(+), 38 deletions(-) diff --git a/projects/bp-gallery/src/components/views/picture/PictureView.tsx b/projects/bp-gallery/src/components/views/picture/PictureView.tsx index 61070d559..dd8d2ad4f 100644 --- a/projects/bp-gallery/src/components/views/picture/PictureView.tsx +++ b/projects/bp-gallery/src/components/views/picture/PictureView.tsx @@ -33,6 +33,8 @@ export interface PictureViewContextFields { navigatePicture?: (target: PictureNavigationTarget) => void; hasNext?: boolean; hasPrevious?: boolean; + hasNextInSequence?: boolean; + hasPreviousInSequence?: boolean; sideBarOpen?: boolean; noDistractionMode?: boolean; setSideBarOpen?: Dispatch>; @@ -45,6 +47,11 @@ export const PictureViewContext = createContext({ img: // Used for the sidebar (in px) --> same as in shared.scss const MOBILE_BREAKPOINT = 750; +export type PictureIds = { + pictureInSiblingsId: string; + pictureInSequenceId: string; +}; + const PictureView = ({ initialPictureId, siblingIds, @@ -60,7 +67,9 @@ const PictureView = ({ const containerRef = useRef(null); - const [pictureId, setPictureId] = useState(initialPictureId); + const [pictureInSiblingsId, setPictureInSiblingsId] = useState(initialPictureId); + const [pictureInSequenceId, setPictureInSequenceId] = useState(initialPictureId); + const [sideBarOpen, setSideBarOpen] = useState(false); useEffect(() => { @@ -70,10 +79,14 @@ const PictureView = ({ }, []); useEffect(() => { - if (fetchMore && siblingIds && siblingIds.indexOf(pictureId) === siblingIds.length - 1) { - fetchMore(pictureId); + if ( + fetchMore && + siblingIds && + siblingIds.indexOf(pictureInSiblingsId) === siblingIds.length - 1 + ) { + fetchMore(pictureInSiblingsId); } - }, [fetchMore, siblingIds, pictureId]); + }, [fetchMore, siblingIds, pictureInSiblingsId]); const search = window.location.search; const [sessionId, isPresentationMode] = useMemo((): [string, boolean] => { @@ -85,39 +98,71 @@ const PictureView = ({ ]; }, [search]); - const [hasPrevious, hasNext] = useMemo(() => { - return [ - Boolean(getPreviousPictureId(pictureId, siblingIds)), - Boolean(getNextPictureId(pictureId, siblingIds)), - ]; - }, [pictureId, siblingIds]); - // Api connection - usePrefetchPictureHook(pictureId, siblingIds); + usePrefetchPictureHook(pictureInSiblingsId, siblingIds); - const { data, loading, error } = useGetPictureInfoQuery({ variables: { pictureId } }); + const { data, loading, error } = useGetPictureInfoQuery({ + variables: { pictureId: pictureInSequenceId }, + }); const picture: FlatPicture | undefined = useSimplifiedQueryResponseData(data)?.picture; const pictureLink = asUploadPath(picture?.media); + const pictureSequenceIds = useMemo( + () => picture?.picture_sequence?.pictures?.map(picture => picture.id), + [picture] + ); - const onNavigateMessage = useCallback((pictureId: string) => { - replaceHistoryWithoutRouter(`/picture/${pictureId}${window.location.search}`); - setPictureId(pictureId); - }, []); + const [hasPrevious, hasNext, hasPreviousInSequence, hasNextInSequence] = useMemo(() => { + return [ + Boolean(getPreviousPictureId(pictureInSiblingsId, siblingIds)), + Boolean(getNextPictureId(pictureInSiblingsId, siblingIds)), + Boolean(getPreviousPictureId(pictureInSequenceId, pictureSequenceIds)), + Boolean(getNextPictureId(pictureInSequenceId, pictureSequenceIds)), + ]; + }, [pictureInSequenceId, pictureInSiblingsId, pictureSequenceIds, siblingIds]); + + const onNavigateMessage = useCallback( + ({ pictureInSiblingsId, pictureInSequenceId }: PictureIds) => { + replaceHistoryWithoutRouter(`/picture/${pictureInSequenceId}${window.location.search}`); + setPictureInSiblingsId(pictureInSiblingsId); + setPictureInSequenceId(pictureInSequenceId); + }, + [] + ); const navigateToPicture = usePresentationChannel(sessionId, onNavigateMessage); // Call the previous or next picture const navigatePicture = useCallback( (target: PictureNavigationTarget) => { - const targetId = - target === PictureNavigationTarget.NEXT - ? getNextPictureId(pictureId, siblingIds) - : getPreviousPictureId(pictureId, siblingIds); - if (targetId) { - navigateToPicture(targetId); + const targetIds: Partial = { + pictureInSequenceId, + pictureInSiblingsId, + }; + switch (target) { + case PictureNavigationTarget.NEXT: + targetIds.pictureInSiblingsId = getNextPictureId(pictureInSiblingsId, siblingIds); + break; + case PictureNavigationTarget.PREVIOUS: + targetIds.pictureInSiblingsId = getPreviousPictureId(pictureInSiblingsId, siblingIds); + break; + case PictureNavigationTarget.NEXT_IN_SEQUENCE: + targetIds.pictureInSequenceId = getNextPictureId(pictureInSequenceId, pictureSequenceIds); + break; + case PictureNavigationTarget.PREVIOUS_IN_SEQUENCE: + targetIds.pictureInSequenceId = getPreviousPictureId( + pictureInSequenceId, + pictureSequenceIds + ); + break; + } + if (targetIds.pictureInSiblingsId && targetIds.pictureInSequenceId) { + if (targetIds.pictureInSiblingsId !== pictureInSiblingsId) { + targetIds.pictureInSequenceId = targetIds.pictureInSiblingsId; + } + navigateToPicture(targetIds as PictureIds); } }, - [pictureId, siblingIds, navigateToPicture] + [pictureInSequenceId, pictureInSiblingsId, siblingIds, pictureSequenceIds, navigateToPicture] ); const [img, setImg] = useState(null); @@ -131,6 +176,8 @@ const PictureView = ({ navigatePicture, hasNext, hasPrevious, + hasNextInSequence, + hasPreviousInSequence, sideBarOpen, noDistractionMode, setSideBarOpen, @@ -144,20 +191,20 @@ const PictureView = ({ const unblock = history.block(() => { setSideBarOpen(false); if (onBack) { - onBack(pictureId); + onBack(pictureInSiblingsId); } }); return () => { unblock(); }; - }, [history, pictureId, onBack]); + }, [history, pictureInSiblingsId, onBack]); - const onImageContextMenu = useBlockImageContextMenuByPictureId(pictureId); + const onImageContextMenu = useBlockImageContextMenuByPictureId(pictureInSequenceId); return (
- +
diff --git a/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx b/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx index 7ceddec59..fd20a0408 100644 --- a/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx +++ b/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx @@ -1,4 +1,4 @@ -import { ChevronLeft, ChevronRight } from '@mui/icons-material'; +import { ChevronLeft, ChevronRight, Filter } from '@mui/icons-material'; import { IconButton } from '@mui/material'; import { useContext, useEffect } from 'react'; import { PictureViewContext } from '../PictureView'; @@ -7,10 +7,13 @@ import { useNoDistractionModeStyle } from '../helpers/no-distraction-mode-style' export enum PictureNavigationTarget { NEXT, PREVIOUS, + NEXT_IN_SEQUENCE, + PREVIOUS_IN_SEQUENCE, } const PictureNavigationButtons = () => { - const { navigatePicture, hasNext, hasPrevious } = useContext(PictureViewContext); + const { navigatePicture, hasNext, hasPrevious, hasNextInSequence, hasPreviousInSequence } = + useContext(PictureViewContext); useEffect(() => { const navigateKeyboardAction = (event: KeyboardEvent) => { @@ -29,7 +32,7 @@ const PictureNavigationButtons = () => { const noDistractionModeStyle = useNoDistractionModeStyle(); return ( -
+
{ > +
+ navigatePicture(PictureNavigationTarget.PREVIOUS_IN_SEQUENCE) + : undefined + } + size='large' + > + + + + navigatePicture(PictureNavigationTarget.NEXT_IN_SEQUENCE) + : undefined + } + size='large' + > + + + +
); }; diff --git a/projects/bp-gallery/src/hooks/presentation-channel.hook.ts b/projects/bp-gallery/src/hooks/presentation-channel.hook.ts index bf83fc35d..5b97e63f5 100644 --- a/projects/bp-gallery/src/hooks/presentation-channel.hook.ts +++ b/projects/bp-gallery/src/hooks/presentation-channel.hook.ts @@ -1,14 +1,17 @@ import { useCallback, useEffect, useRef } from 'react'; +import { PictureIds } from '../components/views/picture/PictureView'; import { FallbackChannel, channelFactory } from '../helpers/channel-helpers'; -const usePresentationChannel = (id: string, onNavigate: (pictureId: string) => void) => { +const usePresentationChannel = (id: string, onNavigate: (ids: PictureIds) => void) => { const producer = useRef(null); useEffect(() => { const consumer = channelFactory(id); producer.current = channelFactory(id); - consumer.onmessage = (event: MessageEvent<{ pictureId: string }>) => { - onNavigate(event.data.pictureId); + consumer.onmessage = ( + event: MessageEvent<{ pictureInSiblingsId: string; pictureInSequenceId: string }> + ) => { + onNavigate(event.data); }; return () => { @@ -19,10 +22,8 @@ const usePresentationChannel = (id: string, onNavigate: (pictureId: string) => v }, [id, onNavigate]); return useCallback( - (targetId: string) => { - producer.current?.postMessage({ - pictureId: targetId, - }); + (ids: PictureIds) => { + producer.current?.postMessage(ids); }, [producer] ); From 5a19a8f2a8a622014a9204656344354f487290f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Mon, 25 Sep 2023 16:13:06 +0200 Subject: [PATCH 2/4] Prefetch picture sequence neighbors --- .../components/views/picture/PictureView.tsx | 9 ++-- .../bp-gallery/src/hooks/prefetch.hook.ts | 46 ++++++++++++++++--- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/projects/bp-gallery/src/components/views/picture/PictureView.tsx b/projects/bp-gallery/src/components/views/picture/PictureView.tsx index dd8d2ad4f..034470cbf 100644 --- a/projects/bp-gallery/src/components/views/picture/PictureView.tsx +++ b/projects/bp-gallery/src/components/views/picture/PictureView.tsx @@ -98,9 +98,6 @@ const PictureView = ({ ]; }, [search]); - // Api connection - usePrefetchPictureHook(pictureInSiblingsId, siblingIds); - const { data, loading, error } = useGetPictureInfoQuery({ variables: { pictureId: pictureInSequenceId }, }); @@ -111,6 +108,12 @@ const PictureView = ({ [picture] ); + usePrefetchPictureHook( + { pictureInSiblingsId, pictureInSequenceId }, + siblingIds, + pictureSequenceIds + ); + const [hasPrevious, hasNext, hasPreviousInSequence, hasNextInSequence] = useMemo(() => { return [ Boolean(getPreviousPictureId(pictureInSiblingsId, siblingIds)), diff --git a/projects/bp-gallery/src/hooks/prefetch.hook.ts b/projects/bp-gallery/src/hooks/prefetch.hook.ts index 0f41f04f5..d2e75f93e 100644 --- a/projects/bp-gallery/src/hooks/prefetch.hook.ts +++ b/projects/bp-gallery/src/hooks/prefetch.hook.ts @@ -1,17 +1,24 @@ import { useEffect } from 'react'; -import { useGetPictureInfoLazyQuery } from '../graphql/APIConnector'; +import { PictureIds } from '../components/views/picture/PictureView'; import { getNextPictureId, getPreviousPictureId, } from '../components/views/picture/helpers/next-prev-picture'; +import { useGetPictureInfoLazyQuery } from '../graphql/APIConnector'; -const usePrefetchPictureHook = (id: string, siblings?: string[]) => { +const usePrefetchPictureHook = ( + { pictureInSiblingsId, pictureInSequenceId }: PictureIds, + siblings?: string[], + pictureSequenceIds?: string[] +) => { const [previousQuery] = useGetPictureInfoLazyQuery(); const [nextQuery] = useGetPictureInfoLazyQuery(); + const [previousInSequenceQuery] = useGetPictureInfoLazyQuery(); + const [nextInSequenceQuery] = useGetPictureInfoLazyQuery(); useEffect(() => { - if (siblings?.includes(id)) { - const previousId = getPreviousPictureId(id, siblings); + if (siblings?.includes(pictureInSiblingsId)) { + const previousId = getPreviousPictureId(pictureInSiblingsId, siblings); if (previousId) { previousQuery({ variables: { @@ -19,7 +26,7 @@ const usePrefetchPictureHook = (id: string, siblings?: string[]) => { }, }); } - const nextId = getNextPictureId(id, siblings); + const nextId = getNextPictureId(pictureInSiblingsId, siblings); if (nextId) { nextQuery({ variables: { @@ -28,7 +35,34 @@ const usePrefetchPictureHook = (id: string, siblings?: string[]) => { }); } } - }, [id, siblings, previousQuery, nextQuery]); + if (pictureSequenceIds?.includes(pictureInSequenceId)) { + const previousId = getPreviousPictureId(pictureInSequenceId, pictureSequenceIds); + if (previousId) { + previousInSequenceQuery({ + variables: { + pictureId: previousId, + }, + }); + } + const nextId = getNextPictureId(pictureInSequenceId, pictureSequenceIds); + if (nextId) { + nextInSequenceQuery({ + variables: { + pictureId: nextId, + }, + }); + } + } + }, [ + pictureInSiblingsId, + siblings, + previousQuery, + nextQuery, + pictureSequenceIds, + pictureInSequenceId, + previousInSequenceQuery, + nextInSequenceQuery, + ]); }; export default usePrefetchPictureHook; From 3f2411a3befcf8371d4e54676527d093cae63ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Sat, 16 Dec 2023 19:58:36 +0100 Subject: [PATCH 3/4] Fix ambiguous selector in test --- projects/bp-gallery/cypress/e2e/picture-grid.cy.ts | 4 ++-- .../views/picture/overlay/PictureNavigationButtons.tsx | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/projects/bp-gallery/cypress/e2e/picture-grid.cy.ts b/projects/bp-gallery/cypress/e2e/picture-grid.cy.ts index 95844c4c0..a5d1e642f 100644 --- a/projects/bp-gallery/cypress/e2e/picture-grid.cy.ts +++ b/projects/bp-gallery/cypress/e2e/picture-grid.cy.ts @@ -17,8 +17,8 @@ describe('picture grid', () => { cy.visit('/archives/1'); cy.get('.overview-container .picture-preview').first().click(); cy.url().should('contain', '/picture/'); - cy.get('.picture-navigation-buttons [data-testid="ChevronRightIcon"]').click(); - cy.get('.picture-navigation-buttons [data-testid="ChevronRightIcon"]').click(); + cy.get('.picture-navigation-buttons [data-testid="next"]').click(); + cy.get('.picture-navigation-buttons [data-testid="next"]').click(); cy.contains('Zurück').click(); urlIs('/archives/1'); }); diff --git a/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx b/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx index fd20a0408..f112f3056 100644 --- a/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx +++ b/projects/bp-gallery/src/components/views/picture/overlay/PictureNavigationButtons.tsx @@ -39,6 +39,7 @@ const PictureNavigationButtons = () => { navigatePicture ? () => navigatePicture(PictureNavigationTarget.PREVIOUS) : undefined } size='large' + data-testid='previous' > @@ -46,6 +47,7 @@ const PictureNavigationButtons = () => { style={{ visibility: hasNext ? 'visible' : 'hidden' }} onClick={navigatePicture ? () => navigatePicture(PictureNavigationTarget.NEXT) : undefined} size='large' + data-testid='next' > @@ -58,6 +60,7 @@ const PictureNavigationButtons = () => { : undefined } size='large' + data-testid='previous-in-sequence' > @@ -70,6 +73,7 @@ const PictureNavigationButtons = () => { : undefined } size='large' + data-testid='next-in-sequence' > From ce1125c333a1c5a1721154e9f15dc7cda32b354a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20D=C3=B6rbandt?= Date: Thu, 22 Aug 2024 15:45:30 +0200 Subject: [PATCH 4/4] Small refactorings --- .../src/components/views/picture/PictureView.tsx | 9 +++++---- projects/bp-gallery/src/hooks/prefetch.hook.ts | 12 ++++++------ .../src/hooks/presentation-channel.hook.ts | 4 +--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/projects/bp-gallery/src/components/views/picture/PictureView.tsx b/projects/bp-gallery/src/components/views/picture/PictureView.tsx index 034470cbf..26bfb7e8a 100644 --- a/projects/bp-gallery/src/components/views/picture/PictureView.tsx +++ b/projects/bp-gallery/src/components/views/picture/PictureView.tsx @@ -114,14 +114,15 @@ const PictureView = ({ pictureSequenceIds ); - const [hasPrevious, hasNext, hasPreviousInSequence, hasNextInSequence] = useMemo(() => { - return [ + const [hasPrevious, hasNext, hasPreviousInSequence, hasNextInSequence] = useMemo( + () => [ Boolean(getPreviousPictureId(pictureInSiblingsId, siblingIds)), Boolean(getNextPictureId(pictureInSiblingsId, siblingIds)), Boolean(getPreviousPictureId(pictureInSequenceId, pictureSequenceIds)), Boolean(getNextPictureId(pictureInSequenceId, pictureSequenceIds)), - ]; - }, [pictureInSequenceId, pictureInSiblingsId, pictureSequenceIds, siblingIds]); + ], + [pictureInSequenceId, pictureInSiblingsId, pictureSequenceIds, siblingIds] + ); const onNavigateMessage = useCallback( ({ pictureInSiblingsId, pictureInSequenceId }: PictureIds) => { diff --git a/projects/bp-gallery/src/hooks/prefetch.hook.ts b/projects/bp-gallery/src/hooks/prefetch.hook.ts index d2e75f93e..4af4b61f3 100644 --- a/projects/bp-gallery/src/hooks/prefetch.hook.ts +++ b/projects/bp-gallery/src/hooks/prefetch.hook.ts @@ -11,8 +11,8 @@ const usePrefetchPictureHook = ( siblings?: string[], pictureSequenceIds?: string[] ) => { - const [previousQuery] = useGetPictureInfoLazyQuery(); - const [nextQuery] = useGetPictureInfoLazyQuery(); + const [previousInSiblingsQuery] = useGetPictureInfoLazyQuery(); + const [nextInSiblingsQuery] = useGetPictureInfoLazyQuery(); const [previousInSequenceQuery] = useGetPictureInfoLazyQuery(); const [nextInSequenceQuery] = useGetPictureInfoLazyQuery(); @@ -20,7 +20,7 @@ const usePrefetchPictureHook = ( if (siblings?.includes(pictureInSiblingsId)) { const previousId = getPreviousPictureId(pictureInSiblingsId, siblings); if (previousId) { - previousQuery({ + previousInSiblingsQuery({ variables: { pictureId: previousId, }, @@ -28,7 +28,7 @@ const usePrefetchPictureHook = ( } const nextId = getNextPictureId(pictureInSiblingsId, siblings); if (nextId) { - nextQuery({ + nextInSiblingsQuery({ variables: { pictureId: nextId, }, @@ -56,8 +56,8 @@ const usePrefetchPictureHook = ( }, [ pictureInSiblingsId, siblings, - previousQuery, - nextQuery, + previousInSiblingsQuery, + nextInSiblingsQuery, pictureSequenceIds, pictureInSequenceId, previousInSequenceQuery, diff --git a/projects/bp-gallery/src/hooks/presentation-channel.hook.ts b/projects/bp-gallery/src/hooks/presentation-channel.hook.ts index 5b97e63f5..654a937c9 100644 --- a/projects/bp-gallery/src/hooks/presentation-channel.hook.ts +++ b/projects/bp-gallery/src/hooks/presentation-channel.hook.ts @@ -8,9 +8,7 @@ const usePresentationChannel = (id: string, onNavigate: (ids: PictureIds) => voi useEffect(() => { const consumer = channelFactory(id); producer.current = channelFactory(id); - consumer.onmessage = ( - event: MessageEvent<{ pictureInSiblingsId: string; pictureInSequenceId: string }> - ) => { + consumer.onmessage = (event: MessageEvent) => { onNavigate(event.data); };