Add gallery reducer for timeline view as well
This commit is contained in:
parent
a069eff0b2
commit
c992fe8933
|
@ -34,6 +34,7 @@ module.exports = {
|
|||
'react/display-name': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import React, { useReducer } from 'react'
|
||||
import React, { useContext, useEffect, 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'
|
||||
import {
|
||||
photoGalleryReducer,
|
||||
urlPresentModeSetupHook,
|
||||
} from '../photoGallery/photoGalleryReducer'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
|
||||
type AlbumGalleryProps = {
|
||||
album?: albumQuery_album
|
||||
|
@ -33,10 +38,7 @@ const AlbumGallery = React.forwardRef(
|
|||
}: AlbumGalleryProps,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) => {
|
||||
// const [imageState, setImageState] = useState<ImageStateType>({
|
||||
// activeImage: -1,
|
||||
// presenting: false,
|
||||
// })
|
||||
const { updateSidebar } = useContext(SidebarContext)
|
||||
|
||||
const [mediaState, dispatchMedia] = useReducer(photoGalleryReducer, {
|
||||
presenting: false,
|
||||
|
@ -44,38 +46,31 @@ const AlbumGallery = React.forwardRef(
|
|||
media: album?.media || [],
|
||||
})
|
||||
|
||||
// const setPresentingWithHistory = (presenting: boolean) => {
|
||||
// setPresenting(presenting)
|
||||
// if (presenting) {
|
||||
// history.pushState({ imageState }, '')
|
||||
// } else {
|
||||
// history.back()
|
||||
// }
|
||||
// }
|
||||
useEffect(() => {
|
||||
dispatchMedia({ type: 'replaceMedia', media: album?.media || [] })
|
||||
}, [album?.media])
|
||||
|
||||
// const updateHistory = (imageState: ImageStateType) => {
|
||||
// history.replaceState({ imageState }, '')
|
||||
// return imageState
|
||||
// }
|
||||
useEffect(() => {
|
||||
if (mediaState.activeIndex != -1) {
|
||||
updateSidebar(
|
||||
<MediaSidebar media={mediaState.media[mediaState.activeIndex]} />
|
||||
)
|
||||
} else {
|
||||
updateSidebar(null)
|
||||
}
|
||||
}, [mediaState.activeIndex])
|
||||
|
||||
// useEffect(() => {
|
||||
// const updateImageState = (event: PopStateEvent) => {
|
||||
// setImageState(event.state.imageState)
|
||||
// }
|
||||
|
||||
// window.addEventListener('popstate', updateImageState)
|
||||
|
||||
// return () => {
|
||||
// window.removeEventListener('popstate', updateImageState)
|
||||
// }
|
||||
// }, [imageState])
|
||||
|
||||
// useEffect(() => {
|
||||
// setActiveImage(-1)
|
||||
// }, [album])
|
||||
urlPresentModeSetupHook({
|
||||
dispatchMedia,
|
||||
openPresentMode: event => {
|
||||
dispatchMedia({
|
||||
type: 'openPresentMode',
|
||||
activeIndex: event.state.activeIndex,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
let subAlbumElement = null
|
||||
|
||||
if (album) {
|
||||
if (album.subAlbums.length > 0) {
|
||||
subAlbumElement = (
|
||||
|
|
|
@ -1,31 +1,20 @@
|
|||
import React, { useContext } from 'react'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Loader } from 'semantic-ui-react'
|
||||
import { MediaThumbnail, PhotoThumbnail } from './MediaThumbnail'
|
||||
import PresentView from './presentView/PresentView'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { PresentMediaProps_Media } from './presentView/PresentMedia'
|
||||
import { sidebarPhoto_media_thumbnail } from '../sidebar/__generated__/sidebarPhoto'
|
||||
import {
|
||||
openPresentModeAction,
|
||||
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
|
||||
}
|
||||
}
|
||||
`
|
||||
toggleFavoriteAction,
|
||||
useMarkFavoriteMutation,
|
||||
} from './photoGalleryMutations'
|
||||
|
||||
const Gallery = styled.div`
|
||||
display: flex;
|
||||
|
@ -68,12 +57,7 @@ const PhotoGallery = ({
|
|||
}: PhotoGalleryProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { updateSidebar } = useContext(SidebarContext)
|
||||
|
||||
const [markFavorite] = useMutation<
|
||||
markMediaFavorite,
|
||||
markMediaFavoriteVariables
|
||||
>(markFavoriteMutation)
|
||||
const [markFavorite] = useMarkFavoriteMutation()
|
||||
|
||||
const { media, activeIndex, presenting } = mediaState
|
||||
|
||||
|
@ -88,33 +72,19 @@ const PhotoGallery = ({
|
|||
media={media}
|
||||
active={active}
|
||||
selectImage={() => {
|
||||
selectImageAction({
|
||||
dispatchMedia({
|
||||
type: 'selectImage',
|
||||
index,
|
||||
mediaState,
|
||||
dispatchMedia,
|
||||
updateSidebar,
|
||||
})
|
||||
}}
|
||||
clickFavorite={() => {
|
||||
markFavorite({
|
||||
variables: {
|
||||
mediaId: media.id,
|
||||
favorite: !media.favorite,
|
||||
},
|
||||
optimisticResponse: {
|
||||
favoriteMedia: {
|
||||
id: media.id,
|
||||
favorite: !media.favorite,
|
||||
__typename: 'Media',
|
||||
},
|
||||
},
|
||||
toggleFavoriteAction({
|
||||
media,
|
||||
markFavorite,
|
||||
})
|
||||
}}
|
||||
clickPresent={() => {
|
||||
dispatchMedia({
|
||||
type: 'setPresenting',
|
||||
presenting: true,
|
||||
})
|
||||
openPresentModeAction({ dispatchMedia, activeIndex: index })
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -135,7 +105,10 @@ const PhotoGallery = ({
|
|||
<PhotoFiller />
|
||||
</Gallery>
|
||||
{presenting && (
|
||||
<PresentView mediaState={mediaState} dispatchMedia={dispatchMedia} />
|
||||
<PresentView
|
||||
activeMedia={mediaState.media[mediaState.activeIndex]}
|
||||
dispatchMedia={dispatchMedia}
|
||||
/>
|
||||
)}
|
||||
</ClearWrap>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { gql, MutationFunction, useMutation } from '@apollo/client'
|
||||
import { PhotoGalleryProps_Media } from './PhotoGallery'
|
||||
import {
|
||||
markMediaFavorite,
|
||||
markMediaFavoriteVariables,
|
||||
} from './__generated__/markMediaFavorite'
|
||||
|
||||
const markFavoriteMutation = gql`
|
||||
mutation markMediaFavorite($mediaId: ID!, $favorite: Boolean!) {
|
||||
favoriteMedia(mediaId: $mediaId, favorite: $favorite) {
|
||||
id
|
||||
favorite
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const useMarkFavoriteMutation = () => {
|
||||
return useMutation<markMediaFavorite, markMediaFavoriteVariables>(
|
||||
markFavoriteMutation
|
||||
)
|
||||
}
|
||||
|
||||
export const toggleFavoriteAction = ({
|
||||
media,
|
||||
markFavorite,
|
||||
}: {
|
||||
media: PhotoGalleryProps_Media
|
||||
markFavorite: MutationFunction<markMediaFavorite, markMediaFavoriteVariables>
|
||||
}) => {
|
||||
markFavorite({
|
||||
variables: {
|
||||
mediaId: media.id,
|
||||
favorite: !media.favorite,
|
||||
},
|
||||
optimisticResponse: {
|
||||
favoriteMedia: {
|
||||
id: media.id,
|
||||
favorite: !media.favorite,
|
||||
__typename: 'Media',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,24 +1,28 @@
|
|||
import React from 'react'
|
||||
import { UpdateSidebarFn } from '../sidebar/Sidebar'
|
||||
import React, { useEffect } from 'react'
|
||||
import { PhotoGalleryProps_Media } from './PhotoGallery'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
|
||||
export type PhotoGalleryState = {
|
||||
export interface PhotoGalleryState {
|
||||
presenting: boolean
|
||||
activeIndex: number
|
||||
media: PhotoGalleryProps_Media[]
|
||||
}
|
||||
|
||||
export type PhotoGalleryAction =
|
||||
export type GalleryAction =
|
||||
| { type: 'nextImage' }
|
||||
| { type: 'previousImage' }
|
||||
| { type: 'setPresenting'; presenting: boolean }
|
||||
| { type: 'closePresentMode' }
|
||||
|
||||
export type PhotoGalleryAction =
|
||||
| GalleryAction
|
||||
| { type: 'openPresentMode'; activeIndex: number }
|
||||
| { type: 'selectImage'; index: number }
|
||||
| { type: 'replaceMedia'; media: PhotoGalleryProps_Media[] }
|
||||
|
||||
export function photoGalleryReducer(
|
||||
state: PhotoGalleryState,
|
||||
action: PhotoGalleryAction
|
||||
): PhotoGalleryState {
|
||||
console.log('photo gallery reducer:', state, action)
|
||||
switch (action.type) {
|
||||
case 'nextImage':
|
||||
return {
|
||||
|
@ -37,10 +41,16 @@ export function photoGalleryReducer(
|
|||
activeIndex: state.activeIndex - 1,
|
||||
}
|
||||
}
|
||||
case 'setPresenting':
|
||||
case 'openPresentMode':
|
||||
return {
|
||||
...state,
|
||||
presenting: action.presenting,
|
||||
presenting: true,
|
||||
activeIndex: action.activeIndex,
|
||||
}
|
||||
case 'closePresentMode':
|
||||
return {
|
||||
...state,
|
||||
presenting: false,
|
||||
}
|
||||
case 'selectImage':
|
||||
return {
|
||||
|
@ -50,25 +60,64 @@ export function photoGalleryReducer(
|
|||
Math.min(state.media.length - 1, action.index)
|
||||
),
|
||||
}
|
||||
case 'replaceMedia':
|
||||
return {
|
||||
...state,
|
||||
media: action.media,
|
||||
activeIndex: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const selectImageAction = ({
|
||||
index,
|
||||
mediaState,
|
||||
export const urlPresentModeSetupHook = ({
|
||||
dispatchMedia,
|
||||
updateSidebar,
|
||||
openPresentMode,
|
||||
}: {
|
||||
index: number
|
||||
mediaState: PhotoGalleryState
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
updateSidebar: UpdateSidebarFn
|
||||
dispatchMedia: React.Dispatch<GalleryAction>
|
||||
openPresentMode: (event: PopStateEvent) => void
|
||||
}) => {
|
||||
updateSidebar(
|
||||
<MediaSidebar media={mediaState.media[mediaState.activeIndex]} />
|
||||
)
|
||||
dispatchMedia({
|
||||
type: 'selectImage',
|
||||
index,
|
||||
})
|
||||
useEffect(() => {
|
||||
const urlChangeListener = (event: PopStateEvent) => {
|
||||
if (event.state.presenting === true) {
|
||||
openPresentMode(event)
|
||||
} else {
|
||||
dispatchMedia({ type: 'closePresentMode' })
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', urlChangeListener)
|
||||
|
||||
history.replaceState({ presenting: false }, '')
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('popstate', urlChangeListener)
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
export const openPresentModeAction = ({
|
||||
dispatchMedia,
|
||||
activeIndex,
|
||||
}: {
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
activeIndex: number
|
||||
}) => {
|
||||
dispatchMedia({
|
||||
type: 'openPresentMode',
|
||||
activeIndex: activeIndex,
|
||||
})
|
||||
|
||||
history.pushState({ presenting: true, activeIndex }, '')
|
||||
}
|
||||
|
||||
export const closePresentModeAction = ({
|
||||
dispatchMedia,
|
||||
}: {
|
||||
dispatchMedia: React.Dispatch<GalleryAction>
|
||||
}) => {
|
||||
dispatchMedia({
|
||||
type: 'closePresentMode',
|
||||
})
|
||||
|
||||
history.back()
|
||||
}
|
||||
|
|
|
@ -1,7 +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 { closePresentModeAction, GalleryAction } from '../photoGalleryReducer'
|
||||
|
||||
import ExitIcon from './icons/Exit'
|
||||
import NextIcon from './icons/Next'
|
||||
|
@ -66,7 +66,7 @@ const NavigationButton = styled(OverlayButton)<{ float: 'left' | 'right' }>`
|
|||
|
||||
type PresentNavigationOverlayProps = {
|
||||
children?: React.ReactChild
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
dispatchMedia: React.Dispatch<GalleryAction>
|
||||
}
|
||||
|
||||
const PresentNavigationOverlay = ({
|
||||
|
@ -113,9 +113,7 @@ const PresentNavigationOverlay = ({
|
|||
</NavigationButton>
|
||||
<ExitButton
|
||||
className={hide ? 'hide' : undefined}
|
||||
onClick={() =>
|
||||
dispatchMedia({ type: 'setPresenting', presenting: false })
|
||||
}
|
||||
onClick={() => closePresentModeAction({ dispatchMedia })}
|
||||
>
|
||||
<ExitIcon />
|
||||
</ExitButton>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import styled, { createGlobalStyle } from 'styled-components'
|
||||
import PresentNavigationOverlay from './PresentNavigationOverlay'
|
||||
import PresentMedia from './PresentMedia'
|
||||
import { PhotoGalleryAction, PhotoGalleryState } from '../photoGalleryReducer'
|
||||
import PresentMedia, { PresentMediaProps_Media } from './PresentMedia'
|
||||
import { closePresentModeAction, GalleryAction } from '../photoGalleryReducer'
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
position: fixed;
|
||||
|
@ -24,14 +24,14 @@ const PreventScroll = createGlobalStyle`
|
|||
type PresentViewProps = {
|
||||
className?: string
|
||||
imageLoaded?(): void
|
||||
mediaState: PhotoGalleryState
|
||||
dispatchMedia: React.Dispatch<PhotoGalleryAction>
|
||||
activeMedia: PresentMediaProps_Media
|
||||
dispatchMedia: React.Dispatch<GalleryAction>
|
||||
}
|
||||
|
||||
const PresentView = ({
|
||||
className,
|
||||
imageLoaded,
|
||||
mediaState,
|
||||
activeMedia,
|
||||
dispatchMedia,
|
||||
}: PresentViewProps) => {
|
||||
useEffect(() => {
|
||||
|
@ -48,7 +48,8 @@ const PresentView = ({
|
|||
|
||||
if (e.key == 'Escape') {
|
||||
e.stopPropagation()
|
||||
dispatchMedia({ type: 'setPresenting', presenting: false })
|
||||
// dispatchMedia({ type: 'setPresenting', presenting: false })
|
||||
closePresentModeAction({ dispatchMedia })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,10 +64,7 @@ const PresentView = ({
|
|||
<StyledContainer {...className}>
|
||||
<PreventScroll />
|
||||
<PresentNavigationOverlay dispatchMedia={dispatchMedia}>
|
||||
<PresentMedia
|
||||
media={mediaState.media[mediaState.activeIndex]}
|
||||
imageLoaded={imageLoaded}
|
||||
/>
|
||||
<PresentMedia media={activeMedia} imageLoaded={imageLoaded} />
|
||||
</PresentNavigationOverlay>
|
||||
</StyledContainer>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useCallback, useRef, useEffect } from 'react'
|
||||
import React, { useRef, useEffect, useReducer, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
import TimelineGroupDate from './TimelineGroupDate'
|
||||
|
@ -16,6 +16,13 @@ import {
|
|||
myTimelineVariables,
|
||||
myTimeline_myTimeline,
|
||||
} from './__generated__/myTimeline'
|
||||
import {
|
||||
getActiveTimelineImage as getActiveTimelineMedia,
|
||||
timelineGalleryReducer,
|
||||
} from './timelineGalleryReducer'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import { urlPresentModeSetupHook } from '../photoGallery/photoGalleryReducer'
|
||||
|
||||
const MY_TIMELINE_QUERY = gql`
|
||||
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
|
||||
|
@ -64,14 +71,14 @@ export type TimelineActiveIndex = {
|
|||
media: number
|
||||
}
|
||||
|
||||
export type TimelineGroup = {
|
||||
date: string
|
||||
groups: myTimeline_myTimeline[]
|
||||
}
|
||||
|
||||
const TimelineGallery = () => {
|
||||
const { t } = useTranslation()
|
||||
const [activeIndex, setActiveIndex] = useState({
|
||||
dateGroup: -1,
|
||||
albumGroup: -1,
|
||||
media: -1,
|
||||
})
|
||||
const [presenting, setPresenting] = useState(false)
|
||||
const { updateSidebar } = useContext(SidebarContext)
|
||||
|
||||
const { getParam, setParam } = useURLParameters()
|
||||
|
||||
|
@ -81,68 +88,15 @@ const TimelineGallery = () => {
|
|||
|
||||
const favoritesNeedsRefresh = useRef(false)
|
||||
|
||||
const nextMedia = useCallback(() => {
|
||||
setActiveIndex(activeIndex => {
|
||||
const albumGroups = dateGroupedAlbums[activeIndex.dateGroup].groups
|
||||
const albumMedia = albumGroups[activeIndex.albumGroup].media
|
||||
|
||||
if (activeIndex.media < albumMedia.length - 1) {
|
||||
return {
|
||||
...activeIndex,
|
||||
media: activeIndex.media + 1,
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.albumGroup < albumGroups.length - 1) {
|
||||
return {
|
||||
...activeIndex,
|
||||
albumGroup: activeIndex.albumGroup + 1,
|
||||
media: 0,
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.dateGroup < dateGroupedAlbums.length - 1) {
|
||||
return {
|
||||
dateGroup: activeIndex.dateGroup + 1,
|
||||
albumGroup: 0,
|
||||
media: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// reached the end
|
||||
return activeIndex
|
||||
})
|
||||
}, [activeIndex])
|
||||
|
||||
const previousMedia = useCallback(() => {
|
||||
setActiveIndex(activeIndex => {
|
||||
if (activeIndex.media > 0) {
|
||||
return {
|
||||
...activeIndex,
|
||||
media: activeIndex.media - 1,
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.albumGroup > 0) {
|
||||
return {
|
||||
...activeIndex,
|
||||
albumGroup: activeIndex.albumGroup - 1,
|
||||
media: 0,
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.dateGroup > 0) {
|
||||
return {
|
||||
dateGroup: activeIndex.dateGroup - 1,
|
||||
albumGroup: 0,
|
||||
media: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// reached the start
|
||||
return activeIndex
|
||||
})
|
||||
}, [activeIndex])
|
||||
const [mediaState, dispatchMedia] = useReducer(timelineGalleryReducer, {
|
||||
presenting: false,
|
||||
timelineGroups: [],
|
||||
activeIndex: {
|
||||
media: -1,
|
||||
album: -1,
|
||||
date: -1,
|
||||
},
|
||||
})
|
||||
|
||||
const { data, error, loading, refetch, fetchMore } = useQuery<
|
||||
myTimeline,
|
||||
|
@ -166,12 +120,36 @@ const TimelineGallery = () => {
|
|||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (favoritesNeedsRefresh) {
|
||||
favoritesNeedsRefresh.current = false
|
||||
refetch({
|
||||
onlyFavorites: onlyFavorites,
|
||||
})
|
||||
dispatchMedia({
|
||||
type: 'replaceTimelineGroups',
|
||||
timeline: data?.myTimeline || [],
|
||||
})
|
||||
}, [data])
|
||||
|
||||
useEffect(() => {
|
||||
const activeMedia = getActiveTimelineMedia({ mediaState })
|
||||
if (activeMedia) {
|
||||
updateSidebar(<MediaSidebar media={activeMedia} />)
|
||||
} else {
|
||||
updateSidebar(null)
|
||||
}
|
||||
}, [mediaState.activeIndex])
|
||||
|
||||
urlPresentModeSetupHook({
|
||||
dispatchMedia,
|
||||
openPresentMode: event => {
|
||||
dispatchMedia({
|
||||
type: 'openPresentMode',
|
||||
activeIndex: event.state.activeIndex,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
favoritesNeedsRefresh.current = false
|
||||
refetch({
|
||||
onlyFavorites: onlyFavorites,
|
||||
})
|
||||
}, [onlyFavorites])
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -186,51 +164,14 @@ const TimelineGallery = () => {
|
|||
return <div>{error.message}</div>
|
||||
}
|
||||
|
||||
type TimelineGroup = {
|
||||
date: string
|
||||
groups: myTimeline_myTimeline[]
|
||||
}
|
||||
|
||||
let timelineGroups = null
|
||||
let dateGroupedAlbums: TimelineGroup[] = []
|
||||
if (data?.myTimeline) {
|
||||
dateGroupedAlbums = data.myTimeline.reduce((acc, val) => {
|
||||
if (acc.length == 0 || acc[acc.length - 1].date != val.date) {
|
||||
acc.push({
|
||||
date: val.date,
|
||||
groups: [val],
|
||||
})
|
||||
} else {
|
||||
acc[acc.length - 1].groups.push(val)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [] as TimelineGroup[])
|
||||
|
||||
timelineGroups = dateGroupedAlbums.map(({ date, groups }, i) => (
|
||||
<TimelineGroupDate
|
||||
key={date}
|
||||
date={date}
|
||||
groups={groups}
|
||||
activeIndex={
|
||||
activeIndex.dateGroup == i
|
||||
? activeIndex
|
||||
: { albumGroup: -1, media: -1 }
|
||||
}
|
||||
setPresenting={setPresenting}
|
||||
onSelectDateGroup={({ media, albumGroup }) => {
|
||||
setActiveIndex({
|
||||
media,
|
||||
albumGroup,
|
||||
dateGroup: i,
|
||||
})
|
||||
}}
|
||||
onFavorite={() => {
|
||||
favoritesNeedsRefresh.current = true
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
const timelineGroups = mediaState.timelineGroups.map((_, i) => (
|
||||
<TimelineGroupDate
|
||||
key={i}
|
||||
groupIndex={i}
|
||||
mediaState={mediaState}
|
||||
dispatchMedia={dispatchMedia}
|
||||
/>
|
||||
))
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -246,17 +187,10 @@ const TimelineGallery = () => {
|
|||
active={!finishedLoadingMore && !loading}
|
||||
text={t('general.loading.paginate.media', 'Loading more media')}
|
||||
/>
|
||||
{presenting && (
|
||||
{mediaState.presenting && (
|
||||
<PresentView
|
||||
media={
|
||||
dateGroupedAlbums &&
|
||||
dateGroupedAlbums[activeIndex.dateGroup].groups[
|
||||
activeIndex.albumGroup
|
||||
].media[activeIndex.media]
|
||||
}
|
||||
nextImage={nextMedia}
|
||||
previousImage={previousMedia}
|
||||
setPresenting={setPresenting}
|
||||
activeMedia={getActiveTimelineMedia({ mediaState })!}
|
||||
dispatchMedia={dispatchMedia}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -2,12 +2,17 @@ import React, { useContext } from 'react'
|
|||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { MediaThumbnail } from '../photoGallery/MediaThumbnail'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
import {
|
||||
toggleFavoriteAction,
|
||||
useMarkFavoriteMutation,
|
||||
} from '../photoGallery/photoGalleryMutations'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import {
|
||||
myTimeline_myTimeline_album,
|
||||
myTimeline_myTimeline_media,
|
||||
} from './__generated__/myTimeline'
|
||||
getActiveTimelineImage,
|
||||
openTimelinePresentMode,
|
||||
TimelineGalleryAction,
|
||||
TimelineGalleryState,
|
||||
} from './timelineGalleryReducer'
|
||||
|
||||
const MediaWrapper = styled.div`
|
||||
display: flex;
|
||||
|
@ -55,38 +60,55 @@ const TotalItemsBubble = styled(Link)`
|
|||
`
|
||||
|
||||
type TimelineGroupAlbumProps = {
|
||||
group: {
|
||||
album: myTimeline_myTimeline_album
|
||||
media: myTimeline_myTimeline_media[]
|
||||
mediaTotal: number
|
||||
}
|
||||
onSelectMedia(index: number): void
|
||||
setPresenting: React.Dispatch<React.SetStateAction<boolean>>
|
||||
activeIndex: number
|
||||
onFavorite(): void
|
||||
dateIndex: number
|
||||
albumIndex: number
|
||||
mediaState: TimelineGalleryState
|
||||
dispatchMedia: React.Dispatch<TimelineGalleryAction>
|
||||
}
|
||||
|
||||
const TimelineGroupAlbum = ({
|
||||
group: { album, media, mediaTotal },
|
||||
onSelectMedia,
|
||||
setPresenting,
|
||||
activeIndex,
|
||||
onFavorite,
|
||||
dateIndex,
|
||||
albumIndex,
|
||||
mediaState,
|
||||
dispatchMedia,
|
||||
}: TimelineGroupAlbumProps) => {
|
||||
const { updateSidebar } = useContext(SidebarContext)
|
||||
const { media, mediaTotal, album } = mediaState.timelineGroups[
|
||||
dateIndex
|
||||
].groups[albumIndex]
|
||||
|
||||
const mediaElms = media.map((media, i) => (
|
||||
const [markFavorite] = useMarkFavoriteMutation()
|
||||
|
||||
const mediaElms = media.map((media, index) => (
|
||||
<MediaThumbnail
|
||||
key={media.id}
|
||||
media={media}
|
||||
onSelectImage={index => {
|
||||
onSelectMedia(index)
|
||||
updateSidebar(<MediaSidebar media={media} />)
|
||||
selectImage={() => {
|
||||
dispatchMedia({
|
||||
type: 'selectImage',
|
||||
index: {
|
||||
album: albumIndex,
|
||||
date: dateIndex,
|
||||
media: index,
|
||||
},
|
||||
})
|
||||
}}
|
||||
setPresenting={setPresenting}
|
||||
onFavorite={onFavorite}
|
||||
index={i}
|
||||
active={activeIndex == i}
|
||||
clickPresent={() => {
|
||||
openTimelinePresentMode({
|
||||
dispatchMedia,
|
||||
activeIndex: {
|
||||
album: albumIndex,
|
||||
date: dateIndex,
|
||||
media: index,
|
||||
},
|
||||
})
|
||||
}}
|
||||
clickFavorite={() => {
|
||||
toggleFavoriteAction({
|
||||
media,
|
||||
markFavorite,
|
||||
})
|
||||
}}
|
||||
active={media.id === getActiveTimelineImage({ mediaState })?.id}
|
||||
/>
|
||||
))
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ import styled from 'styled-components'
|
|||
import { myTimeline_myTimeline } from './__generated__/myTimeline'
|
||||
import { TimelineActiveIndex } from './TimelineGallery'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
TimelineGalleryAction,
|
||||
TimelineGalleryState,
|
||||
} from './timelineGalleryReducer'
|
||||
|
||||
const dateFormatterOptions: Intl.DateTimeFormatOptions = {
|
||||
year: 'numeric',
|
||||
|
@ -26,37 +30,27 @@ const GroupAlbumWrapper = styled.div`
|
|||
`
|
||||
|
||||
type TimelineGroupDateProps = {
|
||||
date: string
|
||||
groups: myTimeline_myTimeline[]
|
||||
onSelectDateGroup(args: { media: number; albumGroup: number }): void
|
||||
activeIndex: TimelineActiveIndex
|
||||
setPresenting: React.Dispatch<React.SetStateAction<boolean>>
|
||||
onFavorite(): void
|
||||
groupIndex: number
|
||||
mediaState: TimelineGalleryState
|
||||
dispatchMedia: React.Dispatch<TimelineGalleryAction>
|
||||
}
|
||||
|
||||
const TimelineGroupDate = ({
|
||||
date,
|
||||
groups,
|
||||
onSelectDateGroup,
|
||||
activeIndex,
|
||||
setPresenting,
|
||||
onFavorite,
|
||||
groupIndex,
|
||||
mediaState,
|
||||
dispatchMedia,
|
||||
}: TimelineGroupDateProps) => {
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const albumGroupElms = groups.map((group, i) => (
|
||||
const group = mediaState.timelineGroups[groupIndex]
|
||||
|
||||
const albumGroupElms = group.groups.map((group, i) => (
|
||||
<TimelineGroupAlbum
|
||||
key={`${group.date}_${group.album.id}`}
|
||||
group={group}
|
||||
onSelectMedia={mediaIndex => {
|
||||
onSelectDateGroup({
|
||||
media: mediaIndex,
|
||||
albumGroup: i,
|
||||
})
|
||||
}}
|
||||
activeIndex={activeIndex.albumGroup == i ? activeIndex.media : -1}
|
||||
setPresenting={setPresenting}
|
||||
onFavorite={onFavorite}
|
||||
dateIndex={groupIndex}
|
||||
albumIndex={i}
|
||||
mediaState={mediaState}
|
||||
dispatchMedia={dispatchMedia}
|
||||
/>
|
||||
))
|
||||
|
||||
|
@ -65,7 +59,7 @@ const TimelineGroupDate = ({
|
|||
dateFormatterOptions
|
||||
)
|
||||
|
||||
const formattedDate = dateFormatter.format(new Date(date))
|
||||
const formattedDate = dateFormatter.format(new Date(group.date))
|
||||
|
||||
return (
|
||||
<GroupDateWrapper>
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
myTimeline_myTimeline,
|
||||
myTimeline_myTimeline_media,
|
||||
} from './__generated__/myTimeline'
|
||||
import { TimelineGroup } from './TimelineGallery'
|
||||
import { UpdateSidebarFn } from '../sidebar/Sidebar'
|
||||
import MediaSidebar from '../sidebar/MediaSidebar'
|
||||
import { GalleryAction } from '../photoGallery/photoGalleryReducer'
|
||||
|
||||
export interface TimelineMediaIndex {
|
||||
date: number
|
||||
album: number
|
||||
media: number
|
||||
}
|
||||
|
||||
export interface TimelineGalleryState {
|
||||
presenting: boolean
|
||||
timelineGroups: TimelineGroup[]
|
||||
activeIndex: TimelineMediaIndex
|
||||
}
|
||||
|
||||
export type TimelineGalleryAction =
|
||||
| GalleryAction
|
||||
| { type: 'replaceTimelineGroups'; timeline: myTimeline_myTimeline[] }
|
||||
| { type: 'selectImage'; index: TimelineMediaIndex }
|
||||
| { type: 'openPresentMode'; activeIndex: TimelineMediaIndex }
|
||||
|
||||
export function timelineGalleryReducer(
|
||||
state: TimelineGalleryState,
|
||||
action: TimelineGalleryAction
|
||||
): TimelineGalleryState {
|
||||
console.log('timeline gallery reducer', state, action)
|
||||
switch (action.type) {
|
||||
case 'replaceTimelineGroups': {
|
||||
const dateGroupedAlbums = action.timeline.reduce((acc, val) => {
|
||||
if (acc.length == 0 || acc[acc.length - 1].date != val.date) {
|
||||
acc.push({
|
||||
date: val.date,
|
||||
groups: [val],
|
||||
})
|
||||
} else {
|
||||
acc[acc.length - 1].groups.push(val)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [] as TimelineGroup[])
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
album: -1,
|
||||
date: -1,
|
||||
media: -1,
|
||||
},
|
||||
timelineGroups: dateGroupedAlbums,
|
||||
}
|
||||
}
|
||||
case 'nextImage': {
|
||||
const { activeIndex, timelineGroups } = state
|
||||
|
||||
const albumGroups = timelineGroups[activeIndex.date].groups
|
||||
const albumMedia = albumGroups[activeIndex.album].media
|
||||
|
||||
if (activeIndex.media < albumMedia.length - 1) {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
...state.activeIndex,
|
||||
media: activeIndex.media + 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.album < albumGroups.length - 1) {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
...state.activeIndex,
|
||||
album: activeIndex.album + 1,
|
||||
media: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.date < timelineGroups.length - 1) {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
date: activeIndex.date + 1,
|
||||
album: 0,
|
||||
media: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// reached the end
|
||||
return state
|
||||
}
|
||||
case 'previousImage': {
|
||||
const { activeIndex } = state
|
||||
|
||||
if (activeIndex.media > 0) {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
...activeIndex,
|
||||
media: activeIndex.media - 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.album > 0) {
|
||||
const albumGroups = state.timelineGroups[activeIndex.date].groups
|
||||
const albumMedia = albumGroups[activeIndex.album - 1].media
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
...activeIndex,
|
||||
album: activeIndex.album - 1,
|
||||
media: albumMedia.length - 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (activeIndex.date > 0) {
|
||||
const albumGroups = state.timelineGroups[activeIndex.date - 1].groups
|
||||
const albumMedia = albumGroups[activeIndex.album - 1].media
|
||||
|
||||
return {
|
||||
...state,
|
||||
activeIndex: {
|
||||
date: activeIndex.date - 1,
|
||||
album: albumGroups.length - 1,
|
||||
media: albumMedia.length - 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// reached the start
|
||||
return state
|
||||
}
|
||||
case 'selectImage': {
|
||||
return {
|
||||
...state,
|
||||
activeIndex: action.index,
|
||||
}
|
||||
}
|
||||
case 'openPresentMode':
|
||||
return {
|
||||
...state,
|
||||
presenting: true,
|
||||
activeIndex: action.activeIndex,
|
||||
}
|
||||
case 'closePresentMode': {
|
||||
return {
|
||||
...state,
|
||||
presenting: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getTimelineImage = ({
|
||||
mediaState,
|
||||
index,
|
||||
}: {
|
||||
mediaState: TimelineGalleryState
|
||||
index: TimelineMediaIndex
|
||||
}): myTimeline_myTimeline_media => {
|
||||
const { date, album, media } = index
|
||||
return mediaState.timelineGroups[date].groups[album].media[media]
|
||||
}
|
||||
|
||||
export const getActiveTimelineImage = ({
|
||||
mediaState,
|
||||
}: {
|
||||
mediaState: TimelineGalleryState
|
||||
}) => {
|
||||
if (
|
||||
Object.values(mediaState.activeIndex).reduce(
|
||||
(acc, next) => next == -1 || acc,
|
||||
false
|
||||
)
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return getTimelineImage({ mediaState, index: mediaState.activeIndex })
|
||||
}
|
||||
|
||||
// export const selectTimelineImageAction = ({
|
||||
// index,
|
||||
// mediaState,
|
||||
// dispatchMedia,
|
||||
// updateSidebar,
|
||||
// }: {
|
||||
// index: TimelineMediaIndex
|
||||
// mediaState: TimelineGalleryState
|
||||
// dispatchMedia: React.Dispatch<TimelineGalleryAction>
|
||||
// updateSidebar: UpdateSidebarFn
|
||||
// }) => {
|
||||
// updateSidebar(
|
||||
// <MediaSidebar media={getTimelineImage({ mediaState, index })} />
|
||||
// )
|
||||
// dispatchMedia({
|
||||
// type: 'selectImage',
|
||||
// index,
|
||||
// })
|
||||
// }
|
||||
|
||||
export const openTimelinePresentMode = ({
|
||||
dispatchMedia,
|
||||
activeIndex,
|
||||
}: {
|
||||
dispatchMedia: React.Dispatch<TimelineGalleryAction>
|
||||
activeIndex: TimelineMediaIndex
|
||||
}) => {
|
||||
dispatchMedia({
|
||||
type: 'openPresentMode',
|
||||
activeIndex,
|
||||
})
|
||||
|
||||
history.pushState({ presenting: true, activeIndex: activeIndex }, '')
|
||||
}
|
Loading…
Reference in New Issue