diff --git a/src/PageManager.tsx b/src/PageManager.tsx index 95655293..0ec7cd5e 100644 --- a/src/PageManager.tsx +++ b/src/PageManager.tsx @@ -1435,16 +1435,37 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { const onPopState = (e: PopStateEvent) => { if (ignorePopStateRef.current) { + logger.info('[LightboxTrace][PageManager] popstate ignored', { + reason: 'ignorePopStateRef', + pathname: window.location.pathname, + state: e.state + }) ignorePopStateRef.current = false return } + logger.info('[LightboxTrace][PageManager] popstate received', { + pathname: window.location.pathname, + state: e.state, + secondaryStackLength: secondaryStackRef.current.length, + drawerOpen, + drawerNoteId, + panelMode, + isSmallScreen + }) + // If the side panel has frames, this popstate is almost certainly stack navigation — do not let // modalManager steal it (history.forward + return), which leaves the URL changed and the panel stale. if (secondaryStackRef.current.length === 0) { const closeModal = modalManager.pop() + logger.info('[LightboxTrace][PageManager] modalManager.pop result', { + closeModal, + pathname: window.location.pathname, + state: e.state + }) if (closeModal) { ignorePopStateRef.current = true + logger.info('[LightboxTrace][PageManager] modal popped, forcing history.forward') window.history.forward() return } diff --git a/src/components/ImageWithLightbox/index.tsx b/src/components/ImageWithLightbox/index.tsx index 556db32f..a977e37b 100644 --- a/src/components/ImageWithLightbox/index.tsx +++ b/src/components/ImageWithLightbox/index.tsx @@ -1,9 +1,10 @@ import { randomString } from '@/lib/random' import { cn } from '@/lib/utils' +import logger from '@/lib/logger' import { useContentPolicy } from '@/providers/ContentPolicyProvider' import modalManager from '@/services/modal-manager.service' import { TImetaInfo } from '@/types' -import { useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' import Lightbox from 'yet-another-react-lightbox' @@ -28,15 +29,59 @@ export default function ImageWithLightbox({ const { autoLoadMedia } = useContentPolicy() const [display, setDisplay] = useState(autoLoadMedia) const [index, setIndex] = useState(-1) + + const logLightboxEvent = useCallback((stage: string, details?: Record) => { + logger.info('[LightboxTrace]', { + stage, + id, + imageUrl: image.url, + index, + pathname: window.location.pathname, + search: window.location.search, + hash: window.location.hash, + ...details + }) + }, [id, image.url, index]) + useEffect(() => { if (index >= 0) { + logLightboxEvent('modal-register') modalManager.register(id, () => { + logLightboxEvent('modal-callback-close') setIndex(-1) }) } else { + logLightboxEvent('modal-unregister') modalManager.unregister(id) } - }, [index]) + }, [id, index, logLightboxEvent]) + + useEffect(() => { + if (index < 0) return + + const onCaptureKeydown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + logLightboxEvent('escape-keydown-capture', { + defaultPrevented: event.defaultPrevented, + eventPhase: event.eventPhase + }) + } + } + const onPopState = (event: PopStateEvent) => { + logLightboxEvent('window-popstate-while-open', { + hasState: !!event.state, + state: event.state + }) + } + + window.addEventListener('keydown', onCaptureKeydown, true) + window.addEventListener('popstate', onPopState) + + return () => { + window.removeEventListener('keydown', onCaptureKeydown, true) + window.removeEventListener('popstate', onPopState) + } + }, [index, logLightboxEvent]) if (!display) { return ( @@ -53,8 +98,12 @@ export default function ImageWithLightbox({ } const handlePhotoClick = (event: React.MouseEvent) => { + logLightboxEvent('thumbnail-click', { + defaultPreventedBefore: event.defaultPrevented + }) event.stopPropagation() event.preventDefault() + logLightboxEvent('set-open-index') setIndex(0) } @@ -74,10 +123,22 @@ export default function ImageWithLightbox({ createPortal(
e.stopPropagation()} - onPointerDown={(e) => e.stopPropagation()} - onMouseDown={(e) => e.stopPropagation()} - onTouchStart={(e) => e.stopPropagation()} + onClick={(e) => { + logLightboxEvent('overlay-click', { target: (e.target as HTMLElement)?.tagName }) + e.stopPropagation() + }} + onPointerDown={(e) => { + logLightboxEvent('overlay-pointerdown', { target: (e.target as HTMLElement)?.tagName }) + e.stopPropagation() + }} + onMouseDown={(e) => { + logLightboxEvent('overlay-mousedown', { target: (e.target as HTMLElement)?.tagName }) + e.stopPropagation() + }} + onTouchStart={(e) => { + logLightboxEvent('overlay-touchstart', { target: (e.target as HTMLElement)?.tagName }) + e.stopPropagation() + }} > = 0} - close={() => setIndex(-1)} + close={() => { + logLightboxEvent('lightbox-close-callback') + setIndex(-1) + }} controller={{ closeOnBackdropClick: false, closeOnPullUp: true, diff --git a/src/components/NoteDrawer/index.tsx b/src/components/NoteDrawer/index.tsx index e144da5d..a70c381b 100644 --- a/src/components/NoteDrawer/index.tsx +++ b/src/components/NoteDrawer/index.tsx @@ -3,6 +3,7 @@ import { Sheet, SheetContent } from '@/components/ui/sheet' import NotePage from '@/pages/secondary/NotePage' import { useSecondaryPage } from '@/PageManager' import type { Event } from 'nostr-tools' +import logger from '@/lib/logger' interface NoteDrawerProps { open: boolean @@ -44,7 +45,18 @@ export default function NoteDrawer({ open, onOpenChange, noteId, initialEvent }: if (!displayNoteId) return null return ( - + { + logger.info('[LightboxTrace][NoteDrawer] onOpenChange', { + currentOpen: open, + nextOpen, + noteId: displayNoteId + }) + onOpenChange(nextOpen) + }} + registerWithModalManager={false} + >
m.id !== id) + logger.info('[LightboxTrace][ModalManager] unregister', { + id, + modalCount: this.modals.length + }) } pop() { const modal = this.modals.pop() - if (!modal) return false + if (!modal) { + logger.info('[LightboxTrace][ModalManager] pop noop', { modalCount: this.modals.length }) + return false + } modal.cb() + logger.info('[LightboxTrace][ModalManager] pop close', { + id: modal.id, + modalCount: this.modals.length + }) return true } }