Skip to content

Commit

Permalink
Store current location/review in Redux (#410)
Browse files Browse the repository at this point in the history
Simplifies EntryWrapper and enables several other improvements:

* [mobile] Add support for about routes in tabs component (fixes #411)
* [mobile] Allow changing settings during "set location position" process (fixed #407)
* [desktop] Highlight on click but load only last location when many selected in quick succession (fixes #412)
* Avoid calling API again to edit the already- loaded location
  • Loading branch information
wbazant authored Jul 3, 2024
1 parent ffb1559 commit 71691e8
Show file tree
Hide file tree
Showing 18 changed files with 340 additions and 259 deletions.
19 changes: 19 additions & 0 deletions src/components/connect/ConnectLocation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'

import { fetchLocationData, setNewLocation } from '../../redux/locationSlice'

const ConnectLocation = ({ locationId }) => {
const dispatch = useDispatch()

useEffect(() => {
if (locationId === 'new') {
dispatch(setNewLocation())
} else {
dispatch(fetchLocationData({ locationId }))
}
}, [dispatch, locationId])
return null
}

export default ConnectLocation
15 changes: 15 additions & 0 deletions src/components/connect/ConnectReview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'

import { fetchReviewData } from '../../redux/reviewSlice'

const ConnectReview = ({reviewId}) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchReviewData(reviewId))
}, [dispatch, reviewId])

return null
}

export default ConnectReview
16 changes: 16 additions & 0 deletions src/components/connect/DisconnectLocation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'

import { clearLocation } from '../../redux/locationSlice'

const DisconnectLocation = () => {
const dispatch = useDispatch()

useEffect(() => {
dispatch(clearLocation())
}, [dispatch])

return null
}

export default DisconnectLocation
21 changes: 21 additions & 0 deletions src/components/connect/connectRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Route } from 'react-router-dom'

import ConnectLocation from './ConnectLocation'
import ConnectReview from './ConnectReview'
import DisconnectLocation from './DisconnectLocation'

const connectRoutes = [
<Route key="connect-location" path="/locations/:locationId">
{({ match }) =>
match && <ConnectLocation locationId={match.params.locationId} />
}
</Route>,
<Route key="connect-review" path="/reviews/:reviewId/edit">
{({ match }) => match && <ConnectReview reviewId={match.params.reviewId} />}
</Route>,
<Route key="disconnect-location" path={['/map', '/list']}>
{({ match }) => match && <DisconnectLocation />}
</Route>,
]

export default connectRoutes
2 changes: 2 additions & 0 deletions src/components/desktop/DesktopLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import styled from 'styled-components/macro'

import aboutRoutes from '../about/aboutRoutes'
import authRoutes from '../auth/authRoutes'
import connectRoutes from '../connect/connectRoutes'
import MapPage from '../map/MapPage'
import Header from './Header'
import SidePaneSwitch from './SidePaneSwitch'
Expand Down Expand Up @@ -52,6 +53,7 @@ const DesktopLayout = () => (
{aboutRoutes}
{authRoutes}
<Route>
{connectRoutes}
<WindowSize>
{({ width: vw }) => (
<StyledSplit
Expand Down
39 changes: 7 additions & 32 deletions src/components/entry/EntryWrapper.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,25 @@
import { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Redirect, useParams } from 'react-router-dom'
import { toast } from 'react-toastify'
import { useSelector } from 'react-redux'

import { updateEntryLocation } from '../../redux/mapSlice'
import { getLocationById } from '../../utils/api'
import Entry from './Entry'
import EntryMobile from './EntryMobile'
import EntryOverview from './EntryOverview'
import EntryReviews from './EntryReviews'

const EntryWrapper = ({ desktop }) => {
const locationData = useSelector((state) => state.map.location)
const dispatch = useDispatch()
const { location: locationData, isLoading } = useSelector(
(state) => state.location,
)

const [reviews, setReviews] = useState()
const [isError, setIsError] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
const [lightboxIndex, setLightboxIndex] = useState([0, 0])
const { locationId } = useParams()

useEffect(() => {
async function fetchEntryData() {
setIsLoading(true)

try {
const locationData = await getLocationById(locationId, 'reviews')

dispatch(updateEntryLocation(locationData))
setReviews(locationData.reviews)
setIsLoading(false)
} catch {
toast.error(`Location #${locationId} not found`, {
autoClose: 5000,
})
setIsError(true)
}
if (locationData) {
setReviews(locationData.reviews)
}

fetchEntryData()
}, [locationId, dispatch])
}, [locationData, setReviews])

const addSubmittedReview = (submittedReview) => {
setReviews((reviews) => [...reviews, submittedReview])
Expand All @@ -60,10 +39,6 @@ const EntryWrapper = ({ desktop }) => {

const EntryComponent = desktop ? Entry : EntryMobile

if (isError) {
return <Redirect to="/map" />
}

return (
<EntryComponent
isLightboxOpen={isLightboxOpen}
Expand Down
35 changes: 7 additions & 28 deletions src/components/form/EditLocation.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
import { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { toast } from 'react-toastify'
import { useSelector } from 'react-redux'

import { getLocationById } from '../../utils/api'
import { useAppHistory } from '../../utils/useAppHistory'
import { Page } from '../entry/Entry'
import { LocationForm, locationToForm } from './LocationForm'

export const EditLocationForm = (props) => {
const { locationId } = useParams()
const history = useAppHistory()
const [location, setLocation] = useState(null)
const { location, isLoading } = useSelector((state) => state.location)

useEffect(() => {
const loadFormData = async () => {
try {
const location = await getLocationById(locationId)
setLocation(location)
} catch (error) {
toast.error(`Location #${locationId} not found`)
}
}

loadFormData()
}, [locationId])

const handleSubmit = () => {
if (location) {
history.push(`/locations/${location.id}`)
}
}

return (
return isLoading ? (
<div>Loading...</div>
) : (
location && (
<LocationForm
initialValues={locationToForm(location)}
editingId={locationId}
onSubmit={handleSubmit}
editingId={location.id}
onSubmit={() => history.push(`/locations/${location.id}`)}
{...props}
/>
)
Expand Down
43 changes: 7 additions & 36 deletions src/components/form/EditReview.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,21 @@
import { useEffect, useState } from 'react'
import { useDispatch } from 'react-redux'
import { useParams } from 'react-router-dom'
import { toast } from 'react-toastify'
import { useSelector } from 'react-redux'

import { rememberLocationIdForReviewId } from '../../redux/miscSlice'
import { getReviewById } from '../../utils/api'
import { useAppHistory } from '../../utils/useAppHistory'
import { Page } from '../entry/Entry'
import { ReviewForm, reviewToForm } from './ReviewForm'

export const EditReviewForm = (props) => {
const { reviewId } = useParams()
const history = useAppHistory()
const dispatch = useDispatch()
const [review, setReview] = useState(null)
const { review, isLoading } = useSelector((state) => state.review)

useEffect(() => {
const loadFormData = async () => {
try {
const review = await getReviewById(reviewId)
dispatch(
rememberLocationIdForReviewId({
reviewId,
locationId: review.location_id,
}),
)
setReview(review)
} catch (error) {
toast.error(`Review #${reviewId} not found`)
}
}

loadFormData()
}, [reviewId, dispatch])

const afterSubmit = () => {
if (review) {
history.push(`/locations/${review.location_id}`)
}
}
return (
return isLoading ? (
<div>Loading...</div>
) : (
review && (
<ReviewForm
initialValues={{ review: reviewToForm(review) }}
editingId={reviewId}
onSubmit={afterSubmit}
editingId={review.id}
onSubmit={() => history.push(`/locations/${review.location_id}`)}
{...props}
/>
)
Expand Down
37 changes: 6 additions & 31 deletions src/components/map/MapPage.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useRouteMatch } from 'react-router-dom'
import styled from 'styled-components/macro'

import {
clusterClick,
restoreOldView,
zoomIn,
zoomInAndSave,
} from '../../redux/mapSlice'
import { clusterClick, zoomIn, zoomInAndSave } from '../../redux/mapSlice'
import { useTypesById } from '../../redux/useTypesById'
import { getAllLocations, viewChangeAndFetch } from '../../redux/viewChange'
import { bootstrapURLKeys } from '../../utils/bootstrapURLKeys'
Expand All @@ -28,30 +22,13 @@ const BottomLeftLoadingIndicator = styled(LoadingIndicator)`

const MapPage = ({ isDesktop }) => {
const history = useAppHistory()
const locationRouteMatch = useRouteMatch({
path: ['/locations/:locationId/:nextSegment', '/locations/:locationId'],
})
const reviewRouteMatch = useRouteMatch({
path: '/reviews/:reviewId/edit',
})
const locationIdsByReviewId = useSelector(
(state) => state.misc.locationIdsByReviewId,
)
let locationId, isAddingLocation, isViewingLocation
if (locationRouteMatch) {
locationId = parseInt(locationRouteMatch.params.locationId)
isAddingLocation = locationRouteMatch.params.locationId === 'new'
isViewingLocation =
locationRouteMatch.params.nextSegment?.indexOf('@') === 0
} else if (reviewRouteMatch) {
const reviewId = parseInt(reviewRouteMatch.params.reviewId)
locationId = locationIdsByReviewId[reviewId]
isAddingLocation = false
isViewingLocation = true
}
const dispatch = useDispatch()

const { locationId } = useSelector((state) => state.location)
const isAddingLocation = locationId === 'new'
const isViewingLocation = locationId !== null && locationId !== 'new'

const { getCommonName } = useTypesById()
const dispatch = useDispatch()
const settings = useSelector((state) => state.settings)
const allLocations = useSelector(getAllLocations)
const isLoading = useSelector((state) => state.map.isLoading)
Expand All @@ -66,8 +43,6 @@ const MapPage = ({ isDesktop }) => {
useEffect(() => {
if (isAddingLocation) {
dispatch(zoomInAndSave())
} else {
dispatch(restoreOldView())
}
}, [dispatch, isAddingLocation])

Expand Down
22 changes: 6 additions & 16 deletions src/components/mobile/MobileLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { matchPath, Route, Switch, useLocation } from 'react-router-dom'

import { setStreetView } from '../../redux/mapSlice'
import { useAppHistory } from '../../utils/useAppHistory'
import useRoutedTabs from '../../utils/useRoutedTabs'
import aboutRoutes from '../about/aboutRoutes'
import AccountPage from '../auth/AccountPage'
import authRoutes from '../auth/authRoutes'
import connectRoutes from '../connect/connectRoutes'
import EntryWrapper from '../entry/EntryWrapper'
import { EditLocationForm } from '../form/EditLocation'
import { EditReviewForm } from '../form/EditReview'
Expand All @@ -16,9 +16,9 @@ import { ReviewForm } from '../form/ReviewForm'
import MapPage from '../map/MapPage'
import SettingsPage from '../settings/SettingsPage'
import { zIndex } from '../ui/GlobalStyle'
import { PageTabs, Tab, TabList, TabPanels } from '../ui/PageTabs'
import { PageTabs, TabList, TabPanels } from '../ui/PageTabs'
import ListPage from './ListPage'
import { DEFAULT_TAB, useTabs } from './tabs'
import Tabs from './Tabs'
import TopBarSwitch from './TopBarSwitch'

const shouldDisplayMapPage = (pathname) => {
Expand All @@ -44,21 +44,10 @@ const shouldDisplayMapPage = (pathname) => {
}
const MobileLayout = () => {
const history = useAppHistory()
const tabs = useTabs()
const dispatch = useDispatch()
const streetView = useSelector((state) => state.map.streetView)
const { pathname } = useLocation()
const [tabIndex, handleTabChange] = useRoutedTabs(
tabs.map(({ paths }) => paths),
DEFAULT_TAB,
)

const tabList = tabs.map(({ paths, icon, label }) => (
<Tab key={paths[0]}>
{icon}
{label}
</Tab>
))
const { tabIndex, handleTabChange, tabContent } = Tabs()

if (
['/list', '/settings', '/users/edit'].some((path) =>
Expand All @@ -84,6 +73,7 @@ const MobileLayout = () => {
>
<MapPage />
</div>
{connectRoutes}
<TabPanels>
<TopBarSwitch />
<Switch>
Expand Down Expand Up @@ -151,7 +141,7 @@ const MobileLayout = () => {
bottom: 0,
}}
>
{tabList}
{tabContent}
</TabList>
</Route>
</Switch>
Expand Down
Loading

0 comments on commit 71691e8

Please sign in to comment.