Skip to content

Commit

Permalink
🐛 Fix image reader navigation (tablet) (#329)
Browse files Browse the repository at this point in the history
* 🐛 Fix image reader navigation (tablet)

Resolves (hopefully) #328

* Account for zoom in swipe handler
  • Loading branch information
aaronleopold authored Apr 28, 2024
1 parent 61e6fb4 commit abb00de
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { mediaQueryKeys } from '@stump/api'
import { queryClient } from '@stump/client'
import type { Media } from '@stump/types'
import clsx from 'clsx'
import React, { memo, useEffect } from 'react'
import React, { memo, useEffect, useMemo } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useSwipeable } from 'react-swipeable'
import { useMediaMatch, useWindowSize } from 'rooks'

import { useDetectZoom } from '@/hooks/useDetectZoom'
import { useReaderStore } from '@/stores'

export type PagedReaderProps = {
Expand Down Expand Up @@ -32,6 +35,21 @@ function PagedReader({ currentPage, media, onPageChange, getPageUrl }: PagedRead
setShowToolBar: state.setShowToolBar,
showToolBar: state.showToolBar,
}))
const { innerWidth } = useWindowSize()
const { isZoomed } = useDetectZoom()

const isMobile = useMediaMatch('(max-width: 768px)')
const [imageWidth, setImageWidth] = React.useState<number | null>(null)
/**
* If the image width is >= 80% of the screen width, we want to fix the side navigation
*/
const fixSideNavigation = useMemo(() => {
if (imageWidth && innerWidth) {
return imageWidth >= innerWidth * 0.8
} else {
return isMobile
}
}, [imageWidth, innerWidth, isMobile])

/**
* This effect is responsible for updating the current page ref when the current page changes. This was
Expand Down Expand Up @@ -94,20 +112,46 @@ function PagedReader({ currentPage, media, onPageChange, getPageUrl }: PagedRead
}
})

const swipeHandlers = useSwipeable({
delta: 150,
onSwipedLeft: () => handlePageChange(currentPage + 1),
onSwipedRight: () => handlePageChange(currentPage - 1),
preventScrollOnSwipe: true,
})
const swipeEnabled = useMemo(
() => !isZoomed && !showToolBar && isMobile,
[isZoomed, showToolBar, isMobile],
)

return (
<div className="relative flex h-full w-full items-center justify-center">
<SideBarControl position="left" onClick={() => handlePageChange(currentPage - 1)} />
<div
className="relative flex h-full w-full items-center justify-center"
{...(swipeEnabled ? swipeHandlers : {})}
>
<SideBarControl
fixed={fixSideNavigation}
position="left"
onClick={() => handlePageChange(currentPage - 1)}
/>
{/* TODO: better error handling for the loaded image */}
<img
className="z-30 max-h-full w-full select-none md:w-auto"
src={getPageUrl(currentPage)}
onLoad={(e) => {
const img = e.target as HTMLImageElement
setImageWidth(img.width)
}}
onError={(err) => {
// @ts-expect-error: is oke
err.target.src = '/favicon.png'
}}
onClick={() => setShowToolBar(!showToolBar)}
/>
<SideBarControl position="right" onClick={() => handlePageChange(currentPage + 1)} />
<SideBarControl
fixed={fixSideNavigation}
position="right"
onClick={() => handlePageChange(currentPage + 1)}
/>
</div>
)
}
Expand All @@ -117,20 +161,21 @@ type SideBarControlProps = {
onClick: () => void
/** The position of the sidebar control */
position: 'left' | 'right'
/** Whether the sidebar should be fixed to the screen */
fixed: boolean
}

/**
* A component that renders an invisible div on either the left or right side of the screen that, when
* clicked, will call the onClick callback. This is used in the `PagedReader` component for
* navigating to the next/previous page.
*/
function SideBarControl({ onClick, position }: SideBarControlProps) {
function SideBarControl({ onClick, position, fixed }: SideBarControlProps) {
return (
<div
className={clsx(
'z-50 h-full border border-transparent transition-all duration-300',
'fixed w-[10%] active:border-edge-200 active:bg-background-200 active:bg-opacity-50',
'sm:relative sm:flex sm:w-full sm:flex-shrink',
'z-50 h-full shrink-0 border border-transparent transition-all duration-300 active:border-edge-200 active:bg-background-200 active:bg-opacity-50',
fixed ? 'fixed w-[10%]' : 'relative flex flex-1 flex-grow',
{ 'right-0': position === 'right' },
{ 'left-0': position === 'left' },
)}
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { useDetectZoom } from './useDetectZoom'
export { useLayoutMode } from './useLayoutMode'
export { usePageParam } from './usePageParam'
export { usePreferences } from './usePreferences'
Expand Down
23 changes: 23 additions & 0 deletions packages/browser/src/hooks/useDetectZoom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect, useState } from 'react'

/**
* A hook to detect the zoom level of the browser.
*/
export function useDetectZoom() {
const [zoom, setZoom] = useState<number>()

useEffect(() => {
const handleResize = () => {
setZoom(window.visualViewport?.scale)
}

window.visualViewport?.addEventListener('resize', handleResize)
handleResize()
return () => window.visualViewport?.removeEventListener('resize', handleResize)
}, [])

return {
isZoomed: zoom !== undefined && zoom > 1,
ratio: zoom,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ export default function BookClubNavigation() {
key={tab.to}
underline={false}
className={cx('whitespace-nowrap border-b-2 px-1 py-3 text-sm font-medium', {
'border-brand-500 text-brand-600 dark:text-brand-400': tab.isActive,
'border-transparent text-gray-800 hover:border-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-700 dark:hover:text-gray-200':
!tab.isActive,
'border-brand-500 text-brand-500': tab.isActive,
'border-transparent text-muted hover:border-edge': !tab.isActive,
})}
>
{tab.label}
Expand Down
5 changes: 2 additions & 3 deletions packages/browser/src/scenes/library/LibraryNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ export default function LibraryNavigation() {
underline={false}
onMouseEnter={tab.onHover}
className={cn('whitespace-nowrap border-b-2 px-1 py-3 text-sm font-medium', {
'border-brand-500 text-brand-600 dark:text-brand-400': tab.isActive,
'border-transparent text-gray-800 hover:border-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-700 dark:hover:text-gray-200':
!tab.isActive,
'border-brand-500 text-brand-500': tab.isActive,
'border-transparent text-muted hover:border-edge': !tab.isActive,
})}
>
{tab.label}
Expand Down
5 changes: 2 additions & 3 deletions packages/browser/src/scenes/series/SeriesNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ export default function SeriesNavigation() {
className={cn(
'whitespace-nowrap border-b-2 px-1 py-3 text-sm font-medium',
{
'border-brand-500 text-brand-600 dark:text-brand-400': tab.isActive,
'border-transparent text-gray-800 hover:border-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-700 dark:hover:text-gray-200':
!tab.isActive,
'border-brand-500 text-brand-500': tab.isActive,
'border-transparent text-muted hover:border-edge': !tab.isActive,
},
// {
// 'pointer-events-none !text-opacity-40': tab.disabled,
Expand Down

0 comments on commit abb00de

Please sign in to comment.