1
Fork 0

Add gallery reducer for timeline view as well

This commit is contained in:
viktorstrate 2021-05-02 17:28:11 +02:00
parent a069eff0b2
commit c992fe8933
No known key found for this signature in database
GPG Key ID: 3F855605109C1E8A
11 changed files with 528 additions and 295 deletions

View File

@ -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: {

View File

@ -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 = (

View File

@ -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>
)

View File

@ -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',
},
},
})
}

View File

@ -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()
}

View File

@ -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>

View File

@ -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>
)

View File

@ -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}
/>
)}
</>

View File

@ -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}
/>
))

View File

@ -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>

View File

@ -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 }, '')
}