mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 21:09:05 +00:00
Add reducers for places page
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import imagePopupSrc from './image-popup.svg'
|
||||
import { MediaMarker } from './MapPresentMarker'
|
||||
import { PresentMarker } from './PlacesPage'
|
||||
import { PlacesAction } from './placesReducer'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 56px;
|
||||
@@ -42,21 +41,36 @@ const PointCountCircle = styled.div`
|
||||
`
|
||||
|
||||
type MapClusterMarkerProps = {
|
||||
setPresentMarker: React.Dispatch<React.SetStateAction<PresentMarker | null>>
|
||||
dispatchMarkerMedia: React.Dispatch<PlacesAction>
|
||||
marker: MediaMarker
|
||||
}
|
||||
|
||||
const MapClusterMarker = ({
|
||||
marker,
|
||||
setPresentMarker,
|
||||
dispatchMarkerMedia,
|
||||
}: MapClusterMarkerProps) => {
|
||||
const thumbnail = JSON.parse(marker.thumbnail)
|
||||
|
||||
const presentMedia = () => {
|
||||
setPresentMarker({
|
||||
cluster: !!marker.cluster,
|
||||
id: marker.cluster ? marker.cluster_id : marker.media_id,
|
||||
// presentMarkerClicked({
|
||||
// dispatchMedia: dispatchMarkerMedia,
|
||||
// mediaState: markerMediaState,
|
||||
// marker: {
|
||||
// cluster: !!marker.cluster,
|
||||
// id: marker.cluster ? marker.cluster_id : marker.media_id,
|
||||
// },
|
||||
// })
|
||||
dispatchMarkerMedia({
|
||||
type: 'replacePresentMarker',
|
||||
marker: {
|
||||
cluster: !!marker.cluster,
|
||||
id: marker.cluster ? marker.cluster_id : marker.media_id,
|
||||
},
|
||||
})
|
||||
// setPresentMarker({
|
||||
// cluster: !!marker.cluster,
|
||||
// id: marker.cluster ? marker.cluster_id : marker.media_id,
|
||||
// })
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { gql } from '@apollo/client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useLazyQuery } from '@apollo/client'
|
||||
import PresentView from '../../components/photoGallery/presentView/PresentView'
|
||||
import type mapboxgl from 'mapbox-gl'
|
||||
import { PresentMarker } from './PlacesPage'
|
||||
import {
|
||||
placePageQueryMedia,
|
||||
placePageQueryMediaVariables,
|
||||
} from './__generated__/placePageQueryMedia'
|
||||
import { PlacesAction, PlacesState } from './placesReducer'
|
||||
|
||||
const QUERY_MEDIA = gql`
|
||||
query placePageQueryMedia($mediaIDs: [ID!]!) {
|
||||
@@ -37,7 +42,7 @@ const getMediaFromMarker = (map: mapboxgl.Map, presentMarker: PresentMarker) =>
|
||||
if (cluster) {
|
||||
const mediaSource = map.getSource('media') as mapboxgl.GeoJSONSource
|
||||
|
||||
mediaSource.getClusterLeaves(id, 1000, 0, (error, features) => {
|
||||
mediaSource.getClusterLeaves(id as number, 1000, 0, (error, features) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
@@ -65,72 +70,69 @@ export interface MediaMarker {
|
||||
thumbnail: string
|
||||
cluster: boolean
|
||||
point_count_abbreviated: number
|
||||
cluster_id: number
|
||||
media_id: number
|
||||
cluster_id: string
|
||||
media_id: string
|
||||
}
|
||||
|
||||
type MapPresetMarkerProps = {
|
||||
map: mapboxgl.Map | null
|
||||
presentMarker: PresentMarker | null
|
||||
setPresentMarker: React.Dispatch<React.SetStateAction<PresentMarker | null>>
|
||||
markerMediaState: PlacesState
|
||||
dispatchMarkerMedia: React.Dispatch<PlacesAction>
|
||||
}
|
||||
|
||||
const MapPresentMarker = ({
|
||||
map,
|
||||
presentMarker,
|
||||
setPresentMarker,
|
||||
markerMediaState,
|
||||
dispatchMarkerMedia,
|
||||
}: MapPresetMarkerProps) => {
|
||||
const [mediaMarkers, setMediaMarkers] = useState<MediaMarker[] | null>(null)
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
|
||||
const [loadMedia, { data: loadedMedia }] = useLazyQuery(QUERY_MEDIA)
|
||||
const [loadMedia, { data: loadedMedia }] = useLazyQuery<
|
||||
placePageQueryMedia,
|
||||
placePageQueryMediaVariables
|
||||
>(QUERY_MEDIA)
|
||||
|
||||
useEffect(() => {
|
||||
const presentMarker = markerMediaState.presentMarker
|
||||
if (presentMarker == null || map == null) {
|
||||
setMediaMarkers(null)
|
||||
dispatchMarkerMedia({
|
||||
type: 'closePresentMode',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
getMediaFromMarker(map, presentMarker).then(setMediaMarkers)
|
||||
}, [presentMarker])
|
||||
getMediaFromMarker(map, presentMarker).then(mediaMarkers => {
|
||||
loadMedia({
|
||||
variables: {
|
||||
mediaIDs: mediaMarkers.map(x => x.media_id),
|
||||
},
|
||||
})
|
||||
})
|
||||
}, [markerMediaState.presentMarker])
|
||||
|
||||
useEffect(() => {
|
||||
if (!mediaMarkers) return
|
||||
|
||||
setCurrentIndex(0)
|
||||
loadMedia({
|
||||
variables: {
|
||||
mediaIDs: mediaMarkers.map(x => x.media_id),
|
||||
},
|
||||
const mediaList = loadedMedia?.mediaList || []
|
||||
dispatchMarkerMedia({
|
||||
type: 'replaceMedia',
|
||||
media: mediaList,
|
||||
})
|
||||
}, [mediaMarkers])
|
||||
if (mediaList.length > 0) {
|
||||
dispatchMarkerMedia({
|
||||
type: 'openPresentMode',
|
||||
activeIndex: 0,
|
||||
})
|
||||
}
|
||||
}, [loadedMedia])
|
||||
|
||||
if (
|
||||
presentMarker == null ||
|
||||
map == null ||
|
||||
mediaMarkers == null ||
|
||||
loadedMedia == null
|
||||
) {
|
||||
if (markerMediaState.presenting) {
|
||||
return (
|
||||
<PresentView
|
||||
activeMedia={markerMediaState.media[markerMediaState.activeIndex]}
|
||||
dispatchMedia={dispatchMarkerMedia}
|
||||
disableSaveCloseInHistory={true}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<PresentView
|
||||
media={loadedMedia.mediaList[currentIndex]}
|
||||
nextImage={() => {
|
||||
setCurrentIndex(i => Math.min(mediaMarkers.length - 1, i + 1))
|
||||
}}
|
||||
previousImage={() => {
|
||||
setCurrentIndex(i => Math.max(0, i - 1))
|
||||
}}
|
||||
setPresenting={presenting => {
|
||||
if (!presenting) {
|
||||
setCurrentIndex(0)
|
||||
setPresentMarker(null)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default MapPresentMarker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
import type mapboxgl from 'mapbox-gl'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import React, { useEffect, useReducer, useRef, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
@@ -10,6 +10,8 @@ import MapPresentMarker from './MapPresentMarker'
|
||||
|
||||
// Will be bundled to dist/src/Pages/PlacesPage/PlacesPage.css
|
||||
import 'mapbox-gl/dist/mapbox-gl.css'
|
||||
import { urlPresentModeSetupHook } from '../../components/photoGallery/photoGalleryReducer'
|
||||
import { placesReducer } from './placesReducer'
|
||||
|
||||
const MapWrapper = styled.div`
|
||||
width: 100%;
|
||||
@@ -29,7 +31,7 @@ const MAPBOX_DATA_QUERY = gql`
|
||||
`
|
||||
|
||||
export type PresentMarker = {
|
||||
id: number
|
||||
id: number | string
|
||||
cluster: boolean
|
||||
}
|
||||
|
||||
@@ -37,9 +39,15 @@ const MapPage = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [mapboxLibrary, setMapboxLibrary] = useState<typeof mapboxgl | null>()
|
||||
const [presentMarker, setPresentMarker] = useState<PresentMarker | null>(null)
|
||||
const mapContainer = useRef<HTMLDivElement | null>(null)
|
||||
const map = useRef<mapboxgl.Map | null>(null)
|
||||
// const [presentMarker, setPresentMarker] = useState<PresentMarker | null>(null)
|
||||
|
||||
const [markerMediaState, dispatchMarkerMedia] = useReducer(placesReducer, {
|
||||
presenting: false,
|
||||
activeIndex: -1,
|
||||
media: [],
|
||||
})
|
||||
|
||||
const { data: mapboxData } = useQuery(MAPBOX_DATA_QUERY)
|
||||
|
||||
@@ -100,7 +108,7 @@ const MapPage = () => {
|
||||
const updateMarkers = makeUpdateMarkers({
|
||||
map: map.current,
|
||||
mapboxLibrary,
|
||||
setPresentMarker,
|
||||
dispatchMarkerMedia,
|
||||
})
|
||||
|
||||
map.current.on('move', updateMarkers)
|
||||
@@ -127,6 +135,17 @@ const MapPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
urlPresentModeSetupHook({
|
||||
dispatchMedia: dispatchMarkerMedia,
|
||||
openPresentMode: event => {
|
||||
console.log('OPEN PRESENT MODE MAPS')
|
||||
dispatchMarkerMedia({
|
||||
type: 'openPresentMode',
|
||||
activeIndex: event.state.activeIndex,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Layout title="Places">
|
||||
<Helmet>
|
||||
@@ -137,8 +156,10 @@ const MapPage = () => {
|
||||
</MapWrapper>
|
||||
<MapPresentMarker
|
||||
map={map.current}
|
||||
presentMarker={presentMarker}
|
||||
setPresentMarker={setPresentMarker}
|
||||
markerMediaState={markerMediaState}
|
||||
dispatchMarkerMedia={dispatchMarkerMedia}
|
||||
// presentMarker={presentMarker}
|
||||
// setPresentMarker={setPresentMarker}
|
||||
/>
|
||||
</Layout>
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import MapClusterMarker from './MapClusterMarker'
|
||||
import { MediaMarker } from './MapPresentMarker'
|
||||
import { PresentMarker } from './PlacesPage'
|
||||
import { PlacesAction } from './placesReducer'
|
||||
|
||||
const markers: { [key: string]: mapboxgl.Marker } = {}
|
||||
let markersOnScreen: typeof markers = {}
|
||||
@@ -12,13 +12,14 @@ let markersOnScreen: typeof markers = {}
|
||||
type makeUpdateMarkersArgs = {
|
||||
map: mapboxgl.Map
|
||||
mapboxLibrary: typeof mapboxgl
|
||||
setPresentMarker: React.Dispatch<React.SetStateAction<PresentMarker | null>>
|
||||
dispatchMarkerMedia: React.Dispatch<PlacesAction>
|
||||
// setPresentMarker: React.Dispatch<React.SetStateAction<PresentMarker | null>>
|
||||
}
|
||||
|
||||
export const makeUpdateMarkers = ({
|
||||
map,
|
||||
mapboxLibrary,
|
||||
setPresentMarker,
|
||||
dispatchMarkerMedia,
|
||||
}: makeUpdateMarkersArgs) => () => {
|
||||
const newMarkers: typeof markers = {}
|
||||
const features = map.querySourceFeatures('media')
|
||||
@@ -40,7 +41,9 @@ export const makeUpdateMarkers = ({
|
||||
|
||||
let marker = markers[id]
|
||||
if (!marker) {
|
||||
const el = createClusterPopupElement(props, setPresentMarker)
|
||||
const el = createClusterPopupElement(props, {
|
||||
dispatchMarkerMedia,
|
||||
})
|
||||
marker = markers[id] = new mapboxLibrary.Marker({
|
||||
element: el,
|
||||
}).setLngLat(coords)
|
||||
@@ -58,13 +61,18 @@ export const makeUpdateMarkers = ({
|
||||
|
||||
function createClusterPopupElement(
|
||||
geojsonProps: MediaMarker,
|
||||
setPresentMarker: React.Dispatch<React.SetStateAction<PresentMarker | null>>
|
||||
{
|
||||
dispatchMarkerMedia,
|
||||
}: {
|
||||
dispatchMarkerMedia: React.Dispatch<PlacesAction>
|
||||
}
|
||||
) {
|
||||
// setPresentMarker: React.Dispatch<React.SetStateAction<PresentMarker | null>>
|
||||
const el = document.createElement('div')
|
||||
ReactDOM.render(
|
||||
<MapClusterMarker
|
||||
marker={geojsonProps}
|
||||
setPresentMarker={setPresentMarker}
|
||||
dispatchMarkerMedia={dispatchMarkerMedia}
|
||||
/>,
|
||||
el
|
||||
)
|
||||
|
||||
80
ui/src/Pages/PlacesPage/placesReducer.ts
Normal file
80
ui/src/Pages/PlacesPage/placesReducer.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { PresentMarker } from './PlacesPage'
|
||||
import {
|
||||
PhotoGalleryState,
|
||||
PhotoGalleryAction,
|
||||
photoGalleryReducer,
|
||||
} from './../../components/photoGallery/photoGalleryReducer'
|
||||
|
||||
export interface PlacesState extends PhotoGalleryState {
|
||||
presentMarker?: PresentMarker
|
||||
}
|
||||
|
||||
export type PlacesAction =
|
||||
| PhotoGalleryAction
|
||||
| { type: 'replacePresentMarker'; marker?: PresentMarker }
|
||||
|
||||
export function placesReducer(
|
||||
state: PlacesState,
|
||||
action: PlacesAction
|
||||
): PlacesState {
|
||||
switch (action.type) {
|
||||
case 'replacePresentMarker':
|
||||
if (
|
||||
state.presentMarker &&
|
||||
action.marker &&
|
||||
state.presentMarker.cluster === action.marker.cluster &&
|
||||
state.presentMarker.id === action.marker.id
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
presenting: true,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...state,
|
||||
presentMarker: action.marker,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return photoGalleryReducer(state, action)
|
||||
}
|
||||
}
|
||||
|
||||
// export const presentMarkerClicked = ({
|
||||
// dispatchMedia,
|
||||
// mediaState,
|
||||
// marker,
|
||||
// }: {
|
||||
// dispatchMedia: React.Dispatch<PlacesAction>
|
||||
// mediaState: PlacesState
|
||||
// marker: PresentMarker
|
||||
// }) => {
|
||||
// console.log(
|
||||
// 'present marker clicked',
|
||||
// mediaState,
|
||||
// marker,
|
||||
// !!mediaState.presentMarker,
|
||||
// mediaState.presentMarker?.cluster === marker.cluster,
|
||||
// mediaState.presentMarker?.id === marker.id
|
||||
// )
|
||||
// if (
|
||||
// mediaState.presentMarker &&
|
||||
// mediaState.presentMarker.cluster === marker.cluster &&
|
||||
// mediaState.presentMarker.id === marker.id
|
||||
// ) {
|
||||
// console.log('EQUAL OPEN PRESENT MODE')
|
||||
// history.pushState(
|
||||
// { presenting: true, activeIndex: mediaState.activeIndex },
|
||||
// ''
|
||||
// )
|
||||
// dispatchMedia({
|
||||
// type: 'openPresentMode',
|
||||
// activeIndex: mediaState.activeIndex,
|
||||
// })
|
||||
// } else {
|
||||
// dispatchMedia({
|
||||
// type: 'replacePresentMarker',
|
||||
// marker,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
@@ -65,6 +65,7 @@ export function photoGalleryReducer(
|
||||
...state,
|
||||
media: action.media,
|
||||
activeIndex: -1,
|
||||
presenting: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,11 +67,13 @@ const NavigationButton = styled(OverlayButton)<{ float: 'left' | 'right' }>`
|
||||
type PresentNavigationOverlayProps = {
|
||||
children?: React.ReactChild
|
||||
dispatchMedia: React.Dispatch<GalleryAction>
|
||||
disableSaveCloseInHistory?: boolean
|
||||
}
|
||||
|
||||
const PresentNavigationOverlay = ({
|
||||
children,
|
||||
dispatchMedia,
|
||||
disableSaveCloseInHistory,
|
||||
}: PresentNavigationOverlayProps) => {
|
||||
const [hide, setHide] = useState(true)
|
||||
const onMouseMove = useRef<null | DebouncedFn<() => void>>(null)
|
||||
@@ -113,7 +115,13 @@ const PresentNavigationOverlay = ({
|
||||
</NavigationButton>
|
||||
<ExitButton
|
||||
className={hide ? 'hide' : undefined}
|
||||
onClick={() => closePresentModeAction({ dispatchMedia })}
|
||||
onClick={() => {
|
||||
if (disableSaveCloseInHistory === true) {
|
||||
dispatchMedia({ type: 'closePresentMode' })
|
||||
} else {
|
||||
closePresentModeAction({ dispatchMedia })
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ExitIcon />
|
||||
</ExitButton>
|
||||
|
||||
@@ -26,6 +26,7 @@ type PresentViewProps = {
|
||||
imageLoaded?(): void
|
||||
activeMedia: PresentMediaProps_Media
|
||||
dispatchMedia: React.Dispatch<GalleryAction>
|
||||
disableSaveCloseInHistory?: boolean
|
||||
}
|
||||
|
||||
const PresentView = ({
|
||||
@@ -33,6 +34,7 @@ const PresentView = ({
|
||||
imageLoaded,
|
||||
activeMedia,
|
||||
dispatchMedia,
|
||||
disableSaveCloseInHistory,
|
||||
}: PresentViewProps) => {
|
||||
useEffect(() => {
|
||||
const keyDownEvent = (e: KeyboardEvent) => {
|
||||
@@ -48,8 +50,12 @@ const PresentView = ({
|
||||
|
||||
if (e.key == 'Escape') {
|
||||
e.stopPropagation()
|
||||
// dispatchMedia({ type: 'setPresenting', presenting: false })
|
||||
closePresentModeAction({ dispatchMedia })
|
||||
|
||||
if (disableSaveCloseInHistory === true) {
|
||||
dispatchMedia({ type: 'closePresentMode' })
|
||||
} else {
|
||||
closePresentModeAction({ dispatchMedia })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +69,10 @@ const PresentView = ({
|
||||
return (
|
||||
<StyledContainer {...className}>
|
||||
<PreventScroll />
|
||||
<PresentNavigationOverlay dispatchMedia={dispatchMedia}>
|
||||
<PresentNavigationOverlay
|
||||
dispatchMedia={dispatchMedia}
|
||||
disableSaveCloseInHistory
|
||||
>
|
||||
<PresentMedia media={activeMedia} imageLoaded={imageLoaded} />
|
||||
</PresentNavigationOverlay>
|
||||
</StyledContainer>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useContext } from 'react'
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { MediaThumbnail } from '../photoGallery/MediaThumbnail'
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
toggleFavoriteAction,
|
||||
useMarkFavoriteMutation,
|
||||
} from '../photoGallery/photoGalleryMutations'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import {
|
||||
getActiveTimelineImage,
|
||||
openTimelinePresentMode,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from 'react'
|
||||
import TimelineGroupAlbum from './TimelineGroupAlbum'
|
||||
import styled from 'styled-components'
|
||||
import { myTimeline_myTimeline } from './__generated__/myTimeline'
|
||||
import { TimelineActiveIndex } from './TimelineGallery'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
TimelineGalleryAction,
|
||||
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
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 {
|
||||
@@ -30,7 +28,6 @@ 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) => {
|
||||
|
||||
Reference in New Issue
Block a user