Rewrite gallery to use reducer
This commit is contained in:
parent
6693cb589c
commit
a069eff0b2
|
@ -16,7 +16,7 @@ docker-compose.yml
|
|||
node_modules/
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
/ui/coverage
|
||||
|
||||
# building
|
||||
.cache/
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
// parser: 'babel-eslint',
|
||||
overrides: [
|
||||
Object.assign(require('eslint-plugin-jest').configs.recommended, {
|
||||
files: ['**/*.test.js'],
|
||||
files: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'],
|
||||
env: { jest: true },
|
||||
plugins: ['jest', 'jest-dom'],
|
||||
rules: Object.assign(
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"@testing-library/jest-dom": "^5.11.10",
|
||||
"@testing-library/react": "^11.2.6",
|
||||
"@types/geojson": "^7946.0.7",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/mapbox-gl": "^2.1.1",
|
||||
"@types/react": "^17.0.3",
|
||||
"@types/react-dom": "^17.0.3",
|
||||
|
@ -2789,9 +2789,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/jest": {
|
||||
"version": "26.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz",
|
||||
"integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==",
|
||||
"version": "26.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz",
|
||||
"integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"jest-diff": "^26.0.0",
|
||||
|
@ -16438,9 +16438,9 @@
|
|||
}
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "26.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz",
|
||||
"integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==",
|
||||
"version": "26.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz",
|
||||
"integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jest-diff": "^26.0.0",
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
"@testing-library/jest-dom": "^5.11.10",
|
||||
"@testing-library/react": "^11.2.6",
|
||||
"@types/geojson": "^7946.0.7",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/mapbox-gl": "^2.1.1",
|
||||
"@types/react": "^17.0.3",
|
||||
"@types/react-dom": "^17.0.3",
|
||||
|
@ -97,6 +97,10 @@
|
|||
"arrowParens": "avoid"
|
||||
},
|
||||
"jest": {
|
||||
"testMatch": [
|
||||
"**/__tests__/**/*.+(ts|tsx|js)",
|
||||
"**/?(*.)+(spec|test).+(ts|tsx|js)"
|
||||
],
|
||||
"transformIgnorePatterns": [
|
||||
"^.+\\.css$"
|
||||
],
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useReducer } from 'react'
|
||||
import AlbumTitle from '../AlbumTitle'
|
||||
import PhotoGallery from '../photoGallery/PhotoGallery'
|
||||
import AlbumBoxes from './AlbumBoxes'
|
||||
import AlbumFilter from '../AlbumFilter'
|
||||
import { albumQuery_album } from '../../Pages/AlbumPage/__generated__/albumQuery'
|
||||
import { OrderDirection } from '../../../__generated__/globalTypes'
|
||||
import { photoGalleryReducer } from '../photoGallery/photoGalleryReducer'
|
||||
|
||||
type AlbumGalleryProps = {
|
||||
album?: albumQuery_album
|
||||
|
@ -29,71 +30,49 @@ const AlbumGallery = React.forwardRef(
|
|||
setOrdering,
|
||||
ordering,
|
||||
onlyFavorites = false,
|
||||
onFavorite,
|
||||
}: AlbumGalleryProps,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) => {
|
||||
type ImageStateType = {
|
||||
activeImage: number
|
||||
presenting: boolean
|
||||
}
|
||||
// const [imageState, setImageState] = useState<ImageStateType>({
|
||||
// activeImage: -1,
|
||||
// presenting: false,
|
||||
// })
|
||||
|
||||
const [imageState, setImageState] = useState<ImageStateType>({
|
||||
activeImage: -1,
|
||||
const [mediaState, dispatchMedia] = useReducer(photoGalleryReducer, {
|
||||
presenting: false,
|
||||
activeIndex: -1,
|
||||
media: album?.media || [],
|
||||
})
|
||||
|
||||
const setPresenting = (presenting: boolean) =>
|
||||
setImageState(state => ({ ...state, presenting }))
|
||||
// const setPresentingWithHistory = (presenting: boolean) => {
|
||||
// setPresenting(presenting)
|
||||
// if (presenting) {
|
||||
// history.pushState({ imageState }, '')
|
||||
// } else {
|
||||
// history.back()
|
||||
// }
|
||||
// }
|
||||
|
||||
const setPresentingWithHistory = (presenting: boolean) => {
|
||||
setPresenting(presenting)
|
||||
if (presenting) {
|
||||
history.pushState({ imageState }, '')
|
||||
} else {
|
||||
history.back()
|
||||
}
|
||||
}
|
||||
// const updateHistory = (imageState: ImageStateType) => {
|
||||
// history.replaceState({ imageState }, '')
|
||||
// return imageState
|
||||
// }
|
||||
|
||||
const updateHistory = (imageState: ImageStateType) => {
|
||||
history.replaceState({ imageState }, '')
|
||||
return imageState
|
||||
}
|
||||
// useEffect(() => {
|
||||
// const updateImageState = (event: PopStateEvent) => {
|
||||
// setImageState(event.state.imageState)
|
||||
// }
|
||||
|
||||
const setActiveImage = (activeImage: number) => {
|
||||
setImageState(state => updateHistory({ ...state, activeImage }))
|
||||
}
|
||||
// window.addEventListener('popstate', updateImageState)
|
||||
|
||||
const nextImage = () => {
|
||||
if (album === undefined) return
|
||||
setActiveImage((imageState.activeImage + 1) % album.media.length)
|
||||
}
|
||||
// return () => {
|
||||
// window.removeEventListener('popstate', updateImageState)
|
||||
// }
|
||||
// }, [imageState])
|
||||
|
||||
const previousImage = () => {
|
||||
if (album === undefined) return
|
||||
|
||||
if (imageState.activeImage <= 0) {
|
||||
setActiveImage(album.media.length - 1)
|
||||
} else {
|
||||
setActiveImage(imageState.activeImage - 1)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const updateImageState = (event: PopStateEvent) => {
|
||||
setImageState(event.state.imageState)
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', updateImageState)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('popstate', updateImageState)
|
||||
}
|
||||
}, [imageState])
|
||||
|
||||
useEffect(() => {
|
||||
setActiveImage(-1)
|
||||
}, [album])
|
||||
// useEffect(() => {
|
||||
// setActiveImage(-1)
|
||||
// }, [album])
|
||||
|
||||
let subAlbumElement = null
|
||||
|
||||
|
@ -135,16 +114,8 @@ const AlbumGallery = React.forwardRef(
|
|||
}
|
||||
<PhotoGallery
|
||||
loading={loading}
|
||||
media={album?.media || []}
|
||||
activeIndex={imageState.activeImage}
|
||||
presenting={imageState.presenting}
|
||||
onSelectImage={index => {
|
||||
setActiveImage(index)
|
||||
}}
|
||||
onFavorite={onFavorite}
|
||||
setPresenting={setPresentingWithHistory}
|
||||
nextImage={nextImage}
|
||||
previousImage={previousImage}
|
||||
mediaState={mediaState}
|
||||
dispatchMedia={dispatchMedia}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,22 +1,8 @@
|
|||
import React, { useCallback, useState } from 'react'
|
||||
import { useMutation, gql } from '@apollo/client'
|
||||
import styled from 'styled-components'
|
||||
import { Icon } from 'semantic-ui-react'
|
||||
import { ProtectedImage } from './ProtectedMedia'
|
||||
import { MediaType } from '../../../__generated__/globalTypes'
|
||||
import {
|
||||
markMediaFavorite,
|
||||
markMediaFavoriteVariables,
|
||||
} from './__generated__/markMediaFavorite'
|
||||
|
||||
const markFavoriteMutation = gql`
|
||||
mutation markMediaFavorite($mediaId: ID!, $favorite: Boolean!) {
|
||||
favoriteMedia(mediaId: $mediaId, favorite: $favorite) {
|
||||
id
|
||||
favorite
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const MediaContainer = styled.div`
|
||||
flex-grow: 1;
|
||||
|
@ -119,26 +105,19 @@ type MediaThumbnailProps = {
|
|||
height: number
|
||||
}
|
||||
}
|
||||
onSelectImage(index: number): void
|
||||
index: number
|
||||
active: boolean
|
||||
setPresenting(presenting: boolean): void
|
||||
onFavorite?(): void
|
||||
selectImage(): void
|
||||
clickPresent(): void
|
||||
clickFavorite(): void
|
||||
}
|
||||
|
||||
export const MediaThumbnail = ({
|
||||
media,
|
||||
onSelectImage,
|
||||
index,
|
||||
active,
|
||||
setPresenting,
|
||||
onFavorite,
|
||||
selectImage,
|
||||
clickPresent,
|
||||
clickFavorite,
|
||||
}: MediaThumbnailProps) => {
|
||||
const [markFavorite] = useMutation<
|
||||
markMediaFavorite,
|
||||
markMediaFavoriteVariables
|
||||
>(markFavoriteMutation)
|
||||
|
||||
let heartIcon = null
|
||||
if (media.favorite !== undefined) {
|
||||
heartIcon = (
|
||||
|
@ -147,21 +126,7 @@ export const MediaThumbnail = ({
|
|||
name={media.favorite ? 'heart' : 'heart outline'}
|
||||
onClick={(event: MouseEvent) => {
|
||||
event.stopPropagation()
|
||||
const favorite = !media.favorite
|
||||
markFavorite({
|
||||
variables: {
|
||||
mediaId: media.id,
|
||||
favorite: favorite,
|
||||
},
|
||||
optimisticResponse: {
|
||||
favoriteMedia: {
|
||||
id: media.id,
|
||||
favorite: favorite,
|
||||
__typename: 'Media',
|
||||
},
|
||||
},
|
||||
})
|
||||
onFavorite && onFavorite()
|
||||
clickFavorite()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -187,7 +152,7 @@ export const MediaThumbnail = ({
|
|||
minWidth: `clamp(124px, ${minWidth}px, 100% - 8px)`,
|
||||
}}
|
||||
onClick={() => {
|
||||
onSelectImage(index)
|
||||
selectImage()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
@ -203,7 +168,7 @@ export const MediaThumbnail = ({
|
|||
<HoverIcon
|
||||
name="expand"
|
||||
onClick={() => {
|
||||
setPresenting(true)
|
||||
clickPresent()
|
||||
}}
|
||||
/>
|
||||
{heartIcon}
|
||||
|
|
|
@ -4,10 +4,28 @@ import { Loader } from 'semantic-ui-react'
|
|||
import { MediaThumbnail, PhotoThumbnail } from './MediaThumbnail'
|
||||
import PresentView from './presentView/PresentView'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { PresentMediaProps_Media } from './presentView/PresentMedia'
|
||||
import { sidebarPhoto_media_thumbnail } from '../sidebar/__generated__/sidebarPhoto'
|
||||
import {
|
||||
PhotoGalleryAction,
|
||||
PhotoGalleryState,
|
||||
selectImageAction,
|
||||
} from './photoGalleryReducer'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import {
|
||||
markMediaFavorite,
|
||||
markMediaFavoriteVariables,
|
||||
} from './__generated__/markMediaFavorite'
|
||||
|
||||
const markFavoriteMutation = gql`
|
||||
mutation markMediaFavorite($mediaId: ID!, $favorite: Boolean!) {
|
||||
favoriteMedia(mediaId: $mediaId, favorite: $favorite) {
|
||||
id
|
||||
favorite
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Gallery = styled.div`
|
||||
display: flex;
|
||||
|
@ -32,37 +50,32 @@ const ClearWrap = styled.div`
|
|||
clear: both;
|
||||
`
|
||||
|
||||
interface PhotoGalleryProps_Media extends PresentMediaProps_Media {
|
||||
export interface PhotoGalleryProps_Media extends PresentMediaProps_Media {
|
||||
thumbnail: sidebarPhoto_media_thumbnail | null
|
||||
favorite?: boolean
|
||||
}
|
||||
|
||||
type PhotoGalleryProps = {
|
||||
loading: boolean
|
||||
media: PhotoGalleryProps_Media[]
|
||||
activeIndex: number
|
||||
presenting: boolean
|
||||
onSelectImage(index: number): void
|
||||
setPresenting(presenting: boolean): void
|
||||
nextImage(): void
|
||||
previousImage(): void
|
||||
onFavorite?(): void
|
||||
mediaState: PhotoGalleryState
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
}
|
||||
|
||||
const PhotoGallery = ({
|
||||
activeIndex = -1,
|
||||
media,
|
||||
mediaState,
|
||||
loading,
|
||||
onSelectImage,
|
||||
presenting,
|
||||
setPresenting,
|
||||
nextImage,
|
||||
previousImage,
|
||||
onFavorite,
|
||||
dispatchMedia,
|
||||
}: PhotoGalleryProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { updateSidebar } = useContext(SidebarContext)
|
||||
|
||||
const activeImage: PhotoGalleryProps_Media | undefined = media[activeIndex]
|
||||
const [markFavorite] = useMutation<
|
||||
markMediaFavorite,
|
||||
markMediaFavoriteVariables
|
||||
>(markFavoriteMutation)
|
||||
|
||||
const { media, activeIndex, presenting } = mediaState
|
||||
|
||||
let photoElements = []
|
||||
if (media) {
|
||||
|
@ -73,14 +86,36 @@ const PhotoGallery = ({
|
|||
<MediaThumbnail
|
||||
key={media.id}
|
||||
media={media}
|
||||
onSelectImage={index => {
|
||||
updateSidebar(<MediaSidebar media={media} />)
|
||||
onSelectImage(index)
|
||||
}}
|
||||
onFavorite={onFavorite}
|
||||
setPresenting={setPresenting}
|
||||
index={index}
|
||||
active={active}
|
||||
selectImage={() => {
|
||||
selectImageAction({
|
||||
index,
|
||||
mediaState,
|
||||
dispatchMedia,
|
||||
updateSidebar,
|
||||
})
|
||||
}}
|
||||
clickFavorite={() => {
|
||||
markFavorite({
|
||||
variables: {
|
||||
mediaId: media.id,
|
||||
favorite: !media.favorite,
|
||||
},
|
||||
optimisticResponse: {
|
||||
favoriteMedia: {
|
||||
id: media.id,
|
||||
favorite: !media.favorite,
|
||||
__typename: 'Media',
|
||||
},
|
||||
},
|
||||
})
|
||||
}}
|
||||
clickPresent={() => {
|
||||
dispatchMedia({
|
||||
type: 'setPresenting',
|
||||
presenting: true,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
@ -100,10 +135,7 @@ const PhotoGallery = ({
|
|||
<PhotoFiller />
|
||||
</Gallery>
|
||||
{presenting && (
|
||||
<PresentView
|
||||
media={activeImage}
|
||||
{...{ nextImage, previousImage, setPresenting }}
|
||||
/>
|
||||
<PresentView mediaState={mediaState} dispatchMedia={dispatchMedia} />
|
||||
)}
|
||||
</ClearWrap>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import { photoGalleryReducer, PhotoGalleryState } from './photoGalleryReducer'
|
||||
import { MediaType } from '../../../__generated__/globalTypes'
|
||||
|
||||
describe('photo gallery reducer', () => {
|
||||
const defaultState: PhotoGalleryState = {
|
||||
presenting: false,
|
||||
activeIndex: 0,
|
||||
media: [
|
||||
{
|
||||
__typename: 'Media',
|
||||
id: '1',
|
||||
highRes: null,
|
||||
thumbnail: null,
|
||||
type: MediaType.Photo,
|
||||
},
|
||||
{
|
||||
__typename: 'Media',
|
||||
id: '2',
|
||||
highRes: null,
|
||||
thumbnail: null,
|
||||
type: MediaType.Photo,
|
||||
},
|
||||
{
|
||||
__typename: 'Media',
|
||||
id: '3',
|
||||
highRes: null,
|
||||
thumbnail: null,
|
||||
type: MediaType.Photo,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
test('next image', () => {
|
||||
expect(photoGalleryReducer(defaultState, { type: 'nextImage' })).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 1,
|
||||
})
|
||||
|
||||
expect(
|
||||
photoGalleryReducer(
|
||||
{ ...defaultState, activeIndex: 2 },
|
||||
{ type: 'nextImage' }
|
||||
)
|
||||
).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 0,
|
||||
})
|
||||
})
|
||||
|
||||
test('previous image', () => {
|
||||
expect(
|
||||
photoGalleryReducer(defaultState, { type: 'previousImage' })
|
||||
).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 2,
|
||||
})
|
||||
|
||||
expect(
|
||||
photoGalleryReducer(
|
||||
{ ...defaultState, activeIndex: 2 },
|
||||
{ type: 'previousImage' }
|
||||
)
|
||||
).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 1,
|
||||
})
|
||||
})
|
||||
|
||||
test('select image', () => {
|
||||
expect(
|
||||
photoGalleryReducer(defaultState, { type: 'selectImage', index: 1 })
|
||||
).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 1,
|
||||
})
|
||||
|
||||
expect(
|
||||
photoGalleryReducer(defaultState, { type: 'selectImage', index: 100 })
|
||||
).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 2,
|
||||
})
|
||||
|
||||
expect(
|
||||
photoGalleryReducer(defaultState, { type: 'selectImage', index: -5 })
|
||||
).toEqual({
|
||||
...defaultState,
|
||||
activeIndex: 0,
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,74 @@
|
|||
import React from 'react'
|
||||
import { UpdateSidebarFn } from '../sidebar/Sidebar'
|
||||
import { PhotoGalleryProps_Media } from './PhotoGallery'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
|
||||
export type PhotoGalleryState = {
|
||||
presenting: boolean
|
||||
activeIndex: number
|
||||
media: PhotoGalleryProps_Media[]
|
||||
}
|
||||
|
||||
export type PhotoGalleryAction =
|
||||
| { type: 'nextImage' }
|
||||
| { type: 'previousImage' }
|
||||
| { type: 'setPresenting'; presenting: boolean }
|
||||
| { type: 'selectImage'; index: number }
|
||||
|
||||
export function photoGalleryReducer(
|
||||
state: PhotoGalleryState,
|
||||
action: PhotoGalleryAction
|
||||
): PhotoGalleryState {
|
||||
switch (action.type) {
|
||||
case 'nextImage':
|
||||
return {
|
||||
...state,
|
||||
activeIndex: (state.activeIndex + 1) % state.media.length,
|
||||
}
|
||||
case 'previousImage':
|
||||
if (state.activeIndex <= 0) {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: state.media.length - 1,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: state.activeIndex - 1,
|
||||
}
|
||||
}
|
||||
case 'setPresenting':
|
||||
return {
|
||||
...state,
|
||||
presenting: action.presenting,
|
||||
}
|
||||
case 'selectImage':
|
||||
return {
|
||||
...state,
|
||||
activeIndex: Math.max(
|
||||
0,
|
||||
Math.min(state.media.length - 1, action.index)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const selectImageAction = ({
|
||||
index,
|
||||
mediaState,
|
||||
dispatchMedia,
|
||||
updateSidebar,
|
||||
}: {
|
||||
index: number
|
||||
mediaState: PhotoGalleryState
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
updateSidebar: UpdateSidebarFn
|
||||
}) => {
|
||||
updateSidebar(
|
||||
<MediaSidebar media={mediaState.media[mediaState.activeIndex]} />
|
||||
)
|
||||
dispatchMedia({
|
||||
type: 'selectImage',
|
||||
index,
|
||||
})
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { debounce, DebouncedFn } from '../../../helpers/utils'
|
||||
import { PhotoGalleryAction } from '../photoGalleryReducer'
|
||||
|
||||
import ExitIcon from './icons/Exit'
|
||||
import NextIcon from './icons/Next'
|
||||
|
@ -65,16 +66,12 @@ const NavigationButton = styled(OverlayButton)<{ float: 'left' | 'right' }>`
|
|||
|
||||
type PresentNavigationOverlayProps = {
|
||||
children?: React.ReactChild
|
||||
nextImage(): void
|
||||
previousImage(): void
|
||||
setPresenting(presenting: boolean): void
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
}
|
||||
|
||||
const PresentNavigationOverlay = ({
|
||||
children,
|
||||
nextImage,
|
||||
previousImage,
|
||||
setPresenting,
|
||||
dispatchMedia,
|
||||
}: PresentNavigationOverlayProps) => {
|
||||
const [hide, setHide] = useState(true)
|
||||
const onMouseMove = useRef<null | DebouncedFn<() => void>>(null)
|
||||
|
@ -103,20 +100,22 @@ const PresentNavigationOverlay = ({
|
|||
<NavigationButton
|
||||
className={hide ? 'hide' : undefined}
|
||||
float="left"
|
||||
onClick={() => previousImage()}
|
||||
onClick={() => dispatchMedia({ type: 'previousImage' })}
|
||||
>
|
||||
<PrevIcon />
|
||||
</NavigationButton>
|
||||
<NavigationButton
|
||||
className={hide ? 'hide' : undefined}
|
||||
float="right"
|
||||
onClick={() => nextImage()}
|
||||
onClick={() => dispatchMedia({ type: 'nextImage' })}
|
||||
>
|
||||
<NextIcon />
|
||||
</NavigationButton>
|
||||
<ExitButton
|
||||
className={hide ? 'hide' : undefined}
|
||||
onClick={() => setPresenting(false)}
|
||||
onClick={() =>
|
||||
dispatchMedia({ type: 'setPresenting', presenting: false })
|
||||
}
|
||||
>
|
||||
<ExitIcon />
|
||||
</ExitButton>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import styled, { createGlobalStyle } from 'styled-components'
|
||||
import PresentNavigationOverlay from './PresentNavigationOverlay'
|
||||
import PresentMedia, { PresentMediaProps_Media } from './PresentMedia'
|
||||
import PresentMedia from './PresentMedia'
|
||||
import { PhotoGalleryAction, PhotoGalleryState } from '../photoGalleryReducer'
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
position: fixed;
|
||||
|
@ -21,37 +22,33 @@ const PreventScroll = createGlobalStyle`
|
|||
`
|
||||
|
||||
type PresentViewProps = {
|
||||
media: PresentMediaProps_Media
|
||||
className?: string
|
||||
imageLoaded?(): void
|
||||
nextImage(): void
|
||||
previousImage(): void
|
||||
setPresenting(presenting: boolean): void
|
||||
mediaState: PhotoGalleryState
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
}
|
||||
|
||||
const PresentView = ({
|
||||
className,
|
||||
media,
|
||||
imageLoaded,
|
||||
nextImage,
|
||||
previousImage,
|
||||
setPresenting,
|
||||
mediaState,
|
||||
dispatchMedia,
|
||||
}: PresentViewProps) => {
|
||||
useEffect(() => {
|
||||
const keyDownEvent = (e: KeyboardEvent) => {
|
||||
if (e.key == 'ArrowRight') {
|
||||
nextImage()
|
||||
e.stopPropagation()
|
||||
dispatchMedia({ type: 'nextImage' })
|
||||
}
|
||||
|
||||
if (e.key == 'ArrowLeft') {
|
||||
previousImage()
|
||||
e.stopPropagation()
|
||||
dispatchMedia({ type: 'previousImage' })
|
||||
}
|
||||
|
||||
if (e.key == 'Escape') {
|
||||
setPresenting(false)
|
||||
e.stopPropagation()
|
||||
dispatchMedia({ type: 'setPresenting', presenting: false })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,10 +62,11 @@ const PresentView = ({
|
|||
return (
|
||||
<StyledContainer {...className}>
|
||||
<PreventScroll />
|
||||
<PresentNavigationOverlay
|
||||
{...{ nextImage, previousImage, setPresenting }}
|
||||
>
|
||||
<PresentMedia media={media} imageLoaded={imageLoaded} />
|
||||
<PresentNavigationOverlay dispatchMedia={dispatchMedia}>
|
||||
<PresentMedia
|
||||
media={mediaState.media[mediaState.activeIndex]}
|
||||
imageLoaded={imageLoaded}
|
||||
/>
|
||||
</PresentNavigationOverlay>
|
||||
</StyledContainer>
|
||||
)
|
||||
|
|
|
@ -52,9 +52,10 @@
|
|||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"./src/@types/"
|
||||
] /* List of folders to include type definitions from. */,
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
"types": ["jest"], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
|
Loading…
Reference in New Issue