Browse Source

bug-fix swishing to close second panel

imwald
Silberengel 3 weeks ago
parent
commit
63f557c566
  1. 54
      src/PageManager.tsx
  2. 12
      src/layouts/SecondaryPageLayout/index.tsx

54
src/PageManager.tsx

@ -7,11 +7,7 @@ import { RefreshButton } from '@/components/RefreshButton'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import logger from '@/lib/logger' import logger from '@/lib/logger'
import { import { useMobileSwipeBackOnElement } from '@/lib/mobile-swipe-back'
MOBILE_SWIPE_BACK_EDGE_PX,
tryMobileSwipeBackFromGesture,
useMobileSwipeBackOnElement
} from '@/lib/mobile-swipe-back'
import { preventRadixSheetCloseForPortaledOverlay } from '@/lib/sheet-dismiss-guard' import { preventRadixSheetCloseForPortaledOverlay } from '@/lib/sheet-dismiss-guard'
import { ChevronLeft } from 'lucide-react' import { ChevronLeft } from 'lucide-react'
import { NavigationService } from '@/services/navigation.service' import { NavigationService } from '@/services/navigation.service'
@ -2132,6 +2128,9 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
restorePrimaryTabAfterSecondaryClose() restorePrimaryTabAfterSecondaryClose()
} }
let lastPopSecondaryPageAt = 0
const POP_SECONDARY_PAGE_DEBOUNCE_MS = 400
/** Pop one secondary frame in React state before history.back (popstate can no-op when indices match). */ /** Pop one secondary frame in React state before history.back (popstate can no-op when indices match). */
const popOneSecondaryStackFrame = () => { const popOneSecondaryStackFrame = () => {
const pre = secondaryStackRef.current const pre = secondaryStackRef.current
@ -2153,6 +2152,10 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
/** UI-first back: sync stack / drawer immediately, then align browser history. */ /** UI-first back: sync stack / drawer immediately, then align browser history. */
const popSecondaryPage = () => { const popSecondaryPage = () => {
const now = Date.now()
if (now - lastPopSecondaryPageAt < POP_SECONDARY_PAGE_DEBOUNCE_MS) return
lastPopSecondaryPageAt = now
navigationCounterRef.current += 1 navigationCounterRef.current += 1
if (primaryNoteView) { if (primaryNoteView) {
setPrimaryNoteView(null) setPrimaryNoteView(null)
@ -2168,12 +2171,9 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
ignorePopStateRef.current = true ignorePopStateRef.current = true
window.history.back() window.history.back()
} else { } else {
// replaceState in hardClose already points at the primary URL — do not history.back()
// afterward or the browser returns to the note entry and popstate/sync reopens the panel.
hardCloseSecondaryPanel() hardCloseSecondaryPanel()
const pathOnly = window.location.pathname.split('?')[0].split('#')[0]
if (!isPrimaryOnlyPathname(pathOnly)) {
ignorePopStateRef.current = true
window.history.back()
}
} }
return return
} }
@ -2230,40 +2230,6 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
enabled: mobileSecondaryPanelOpen enabled: mobileSecondaryPanelOpen
}) })
const mobileSecondaryOpen = isSmallScreen && (drawerOpen || secondaryStack.length > 0)
useEffect(() => {
if (!mobileSecondaryOpen) return
let grab: { x: number; y: number; pointerId: number } | null = null
const onPointerDown = (e: PointerEvent) => {
if ((e.button !== 0 && e.button !== -1) || e.clientX > MOBILE_SWIPE_BACK_EDGE_PX) return
grab = { x: e.clientX, y: e.clientY, pointerId: e.pointerId }
}
const onPointerUp = (e: PointerEvent) => {
if (!grab || grab.pointerId !== e.pointerId) return
tryMobileSwipeBackFromGesture(grab, e.clientX, e.clientY, e.pointerId, () =>
popSecondaryPageRef.current()
)
grab = null
}
const onPointerCancel = () => {
grab = null
}
const capture = { capture: true } as const
document.addEventListener('pointerdown', onPointerDown, capture)
document.addEventListener('pointerup', onPointerUp, capture)
document.addEventListener('pointercancel', onPointerCancel, capture)
return () => {
document.removeEventListener('pointerdown', onPointerDown, capture)
document.removeEventListener('pointerup', onPointerUp, capture)
document.removeEventListener('pointercancel', onPointerCancel, capture)
}
}, [mobileSecondaryOpen])
useEffect(() => { useEffect(() => {
const shouldBeOpen = const shouldBeOpen =
panelMode === 'single' && panelMode === 'single' &&

12
src/layouts/SecondaryPageLayout/index.tsx

@ -8,13 +8,12 @@ import {
isRadixDialogOpen, isRadixDialogOpen,
shouldIgnoreKeyboardShortcutEvent shouldIgnoreKeyboardShortcutEvent
} from '@/lib/keyboard-shortcuts' } from '@/lib/keyboard-shortcuts'
import { useMobileSwipeBackOnElement } from '@/lib/mobile-swipe-back'
import { useSecondaryPage } from '@/PageManager' import { useSecondaryPage } from '@/PageManager'
import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider' import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { ChevronLeft } from 'lucide-react' import { ChevronLeft } from 'lucide-react'
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react' import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
const SecondaryPageLayout = forwardRef( const SecondaryPageLayout = forwardRef(
@ -42,13 +41,7 @@ const SecondaryPageLayout = forwardRef(
) => { ) => {
const scrollAreaRef = useRef<HTMLDivElement>(null) const scrollAreaRef = useRef<HTMLDivElement>(null)
const { isSmallScreen } = useScreenSize() const { isSmallScreen } = useScreenSize()
const { currentIndex, pop } = useSecondaryPage() const { currentIndex } = useSecondaryPage()
const [mobileSwipeRoot, setMobileSwipeRoot] = useState<HTMLElement | null>(null)
const mobileSwipeActive =
isSmallScreen && (index === undefined || currentIndex === index)
useMobileSwipeBackOnElement(mobileSwipeActive ? mobileSwipeRoot : null, pop, {
enabled: mobileSwipeActive
})
const shouldRenderTitlebar = const shouldRenderTitlebar =
titlebar != null || (title != null && title !== '') || !hideBackButton titlebar != null || (title != null && title !== '') || !hideBackButton
@ -97,7 +90,6 @@ const SecondaryPageLayout = forwardRef(
return ( return (
<DeepBrowsingProvider active={currentIndex === index}> <DeepBrowsingProvider active={currentIndex === index}>
<div <div
ref={setMobileSwipeRoot}
className="flex min-h-0 min-w-0 flex-1 flex-col touch-pan-y" className="flex min-h-0 min-w-0 flex-1 flex-col touch-pan-y"
style={{ style={{
paddingBottom: 'calc(env(safe-area-inset-bottom) + 3rem)' paddingBottom: 'calc(env(safe-area-inset-bottom) + 3rem)'

Loading…
Cancel
Save