From 25cb686acebec205fcac3b0cdd17ed08512ef29c Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Thu, 22 Feb 2024 22:53:42 -0600 Subject: [PATCH] Prevent cmd-k key listener interference --- src/app/layout.tsx | 8 ++--- src/components/CommandKClient.tsx | 8 +++-- src/photo/PhotoEscapeHandler.tsx | 19 ++++++---- src/photo/PhotoLinks.tsx | 58 +++++++++++++++++-------------- src/state/AppStateProvider.tsx | 7 +++- src/state/index.ts | 4 ++- 6 files changed, 63 insertions(+), 41 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 95f0a523..a3ed5826 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -94,11 +94,11 @@ export default function RootLayout({ + + + + - - - - ); diff --git a/src/components/CommandKClient.tsx b/src/components/CommandKClient.tsx index d84de599..3a218901 100644 --- a/src/components/CommandKClient.tsx +++ b/src/components/CommandKClient.tsx @@ -40,6 +40,7 @@ export default function CommandKClient({ const { isCommandKOpen: isOpen, setIsCommandKOpen: setIsOpen, + setShouldRespondToKeyboardCommands, } = useAppState(); const isOpenRef = useRef(isOpen); @@ -103,12 +104,15 @@ export default function CommandKClient({ }, [queryLive]); useEffect(() => { - if (!isOpen) { + if (isOpen) { + setShouldRespondToKeyboardCommands?.(false); + } else if (!isOpen) { setQueryLive(''); setQueriedSections([]); setIsLoading(false); + setTimeout(() => setShouldRespondToKeyboardCommands?.(true), 500); } - }, [isOpen]); + }, [isOpen, setShouldRespondToKeyboardCommands]); const sectionTheme: CommandKSection = { heading: 'Theme', diff --git a/src/photo/PhotoEscapeHandler.tsx b/src/photo/PhotoEscapeHandler.tsx index 44aa4579..4ba55918 100644 --- a/src/photo/PhotoEscapeHandler.tsx +++ b/src/photo/PhotoEscapeHandler.tsx @@ -1,6 +1,7 @@ 'use client'; import { getEscapePath } from '@/site/paths'; +import { useAppState } from '@/state'; import { useRouter, usePathname } from 'next/navigation'; import { useEffect } from 'react'; @@ -11,17 +12,21 @@ export default function PhotoEscapeHandler() { const pathname = usePathname(); + const { shouldRespondToKeyboardCommands } = useAppState(); + const escapePath = getEscapePath(pathname); useEffect(() => { - const onKeyUp = (e: KeyboardEvent) => { - if (e.key.toUpperCase() === 'ESCAPE' && escapePath) { - router.push(escapePath, { scroll: false }); + if (shouldRespondToKeyboardCommands) { + const onKeyUp = (e: KeyboardEvent) => { + if (e.key.toUpperCase() === 'ESCAPE' && escapePath) { + router.push(escapePath, { scroll: false }); + }; }; - }; - window.addEventListener(LISTENER_KEYUP, onKeyUp); - return () => window.removeEventListener(LISTENER_KEYUP, onKeyUp); - }, [router, escapePath]); + window.addEventListener(LISTENER_KEYUP, onKeyUp); + return () => window.removeEventListener(LISTENER_KEYUP, onKeyUp); + } + }, [shouldRespondToKeyboardCommands, router, escapePath]); return null; } diff --git a/src/photo/PhotoLinks.tsx b/src/photo/PhotoLinks.tsx index ff5770ad..73c5b892 100644 --- a/src/photo/PhotoLinks.tsx +++ b/src/photo/PhotoLinks.tsx @@ -30,40 +30,46 @@ export default function PhotoLinks({ }) { const router = useRouter(); - const { setNextPhotoAnimation } = useAppState(); + const { + setNextPhotoAnimation, + shouldRespondToKeyboardCommands, + } = useAppState(); const previousPhoto = getPreviousPhoto(photo, photos); const nextPhoto = getNextPhoto(photo, photos); useEffect(() => { - const onKeyUp = (e: KeyboardEvent) => { - switch (e.key.toUpperCase()) { - case 'ARROWLEFT': - case 'J': - if (previousPhoto) { - setNextPhotoAnimation?.(ANIMATION_RIGHT); - router.push( - pathForPhoto(previousPhoto, tag, camera, simulation), - { scroll: false }, - ); - } - break; - case 'ARROWRIGHT': - case 'L': - if (nextPhoto) { - setNextPhotoAnimation?.(ANIMATION_LEFT); - router.push( - pathForPhoto(nextPhoto, tag, camera, simulation), - { scroll: false }, - ); - } - break; + if (shouldRespondToKeyboardCommands) { + const onKeyUp = (e: KeyboardEvent) => { + switch (e.key.toUpperCase()) { + case 'ARROWLEFT': + case 'J': + if (previousPhoto) { + setNextPhotoAnimation?.(ANIMATION_RIGHT); + router.push( + pathForPhoto(previousPhoto, tag, camera, simulation), + { scroll: false }, + ); + } + break; + case 'ARROWRIGHT': + case 'L': + if (nextPhoto) { + setNextPhotoAnimation?.(ANIMATION_LEFT); + router.push( + pathForPhoto(nextPhoto, tag, camera, simulation), + { scroll: false }, + ); + } + break; + }; }; - }; - window.addEventListener(LISTENER_KEYUP, onKeyUp); - return () => window.removeEventListener(LISTENER_KEYUP, onKeyUp); + window.addEventListener(LISTENER_KEYUP, onKeyUp); + return () => window.removeEventListener(LISTENER_KEYUP, onKeyUp); + } }, [ router, + shouldRespondToKeyboardCommands, setNextPhotoAnimation, previousPhoto, nextPhoto, diff --git a/src/state/AppStateProvider.tsx b/src/state/AppStateProvider.tsx index 7001d824..a0d86a92 100644 --- a/src/state/AppStateProvider.tsx +++ b/src/state/AppStateProvider.tsx @@ -17,6 +17,9 @@ export default function AppStateProvider({ const [nextPhotoAnimation, setNextPhotoAnimation] = useState(); + const [shouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands] = + useState(true); + const [isCommandKOpen, setIsCommandKOpen] = useState(false); useEffect(() => { @@ -30,9 +33,11 @@ export default function AppStateProvider({ hasLoaded, setHasLoaded, nextPhotoAnimation, + setNextPhotoAnimation, + shouldRespondToKeyboardCommands, + setShouldRespondToKeyboardCommands, isCommandKOpen, setIsCommandKOpen, - setNextPhotoAnimation, clearNextPhotoAnimation: () => setNextPhotoAnimation?.(undefined), }} > diff --git a/src/state/index.ts b/src/state/index.ts index b91f43af..8c303280 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -6,9 +6,11 @@ export interface AppStateContext { hasLoaded?: boolean setHasLoaded?: Dispatch> nextPhotoAnimation?: AnimationConfig + setNextPhotoAnimation?: Dispatch> + shouldRespondToKeyboardCommands?: boolean + setShouldRespondToKeyboardCommands?: Dispatch> isCommandKOpen?: boolean setIsCommandKOpen?: Dispatch> - setNextPhotoAnimation?: (animation?: AnimationConfig) => void clearNextPhotoAnimation?: () => void }