Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,299 changes: 1,227 additions & 72 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"react-intersection-observer": "^10.0.2",
"react-leaflet": "^4.2.1",
"react-leaflet-cluster": "^2.1.0",
"react-markdown": "^10.1.0",
"react-redux": "~7.2.3",
"react-router": "^5.3.4",
"react-router-dom": "^5.3.4",
Expand Down
2 changes: 1 addition & 1 deletion src/components/banner-info-card/BannerInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
createExternalNavigationUri,
getExternalLinkAttributes,
} from '../../features/utils'
import { Agent } from '../agent/Agent'
import { Agent } from '../agent'
import { Distance } from '../distance/Distance'
import { Duration } from '../duration/Duration'
import IfUserLoggedIn from '../login/if-user-logged-in'
Expand Down
7 changes: 7 additions & 0 deletions src/features/banner/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,10 @@ export const changeBannerSettingsAction =
throw Error('Error while changing banner settings')
}
}

export const resetBrowsedBannersAction =
() => (dispatch: Dispatch<BannerActionTypes>) => {
dispatch({
type: RESET_BROWSED_BANNERS,
})
}
3 changes: 2 additions & 1 deletion src/features/banner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
editBannerAction,
deleteBannerAction,
changeBannerSettingsAction,
resetBrowsedBannersAction,
} from './actions'
import {
Banner,
Expand Down Expand Up @@ -75,9 +76,9 @@ export {
resetMapBannersAction as resetMapBanners,
loadMapBannersAction as loadMapBanners,
removePendingBannerAction as removePendingBanner,
editBannerAction as editBanner,
deleteBannerAction as deleteBanner,
changeBannerSettingsAction as changeBannerSettings,
resetBrowsedBannersAction as resetBrowsedBanners,
}
export type { Banner, BannerType, BannerListType, BannerState, NumDictionary }
export {
Expand Down
143 changes: 91 additions & 52 deletions src/pages/browser/Browser.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React, { Fragment } from 'react'
import React from 'react'
import { connect } from 'react-redux'
import { Link, withRouter, RouteComponentProps } from 'react-router-dom'
import { Helmet } from 'react-helmet'
import { withRouter, RouteComponentProps, Link } from 'react-router-dom'
import { Trans, withTranslation, WithTranslationProps } from 'react-i18next'
import { Helmet } from 'react-helmet'

import { RootState } from '../../storeTypes'
import {
Banner,
getBrowsedBanners,
loadBrowsedBanners as loadBrowsedBannersAction,
getHasMoreBrowsedBanners,
resetBrowsedBanners as resetBrowsedBannersAction,
} from '../../features/banner'
import {
getCountries,
Expand Down Expand Up @@ -43,6 +44,8 @@ import { updateSettingsAction } from '../../features/settings/actions'
import { SettingsState } from '../../features/settings/types'

class Browser extends React.Component<BrowserProps, BrowserState> {
unlisten?: () => void

constructor(props: BrowserProps) {
super(props)
this.state = {
Expand All @@ -55,26 +58,50 @@ class Browser extends React.Component<BrowserProps, BrowserState> {
},
page: 0,
status: 'initial',
scrollRestored: false,
}
}

componentDidMount() {
async componentDidMount() {
this.unlisten = this.props.history.listen((location, action) => {
if (action === 'PUSH') {
const href = location.pathname
if (href.startsWith('/banner/')) {
const scrollable = document.querySelector('.container')
if (scrollable) {
sessionStorage.setItem(
'browseScrollPosition',
(scrollable as HTMLElement).scrollTop.toString()
)
}
} else {
sessionStorage.removeItem('browseScrollPosition')
this.props.resetBrowsedBanners()
this.setState({ scrollRestored: false })
}
}
})

const { fetchBanners, fetchPlace, match } = this.props
const { filter } = this.state
const { placeId } = match.params

const promises: Array<Promise<any>> = []

if (placeId) {
promises.push(fetchPlace(placeId))
await fetchPlace(placeId)
}
promises.push(this.fetchChildren(placeId))
await this.fetchChildren(placeId)

Promise.all(promises)
.then(() => this.setState({ status: 'success' }))
.catch(() => this.setState({ status: 'error' }))
if (this.props.banners.length === 0) {
await fetchBanners(placeId, filter, 0)
}

fetchBanners(placeId, filter, 0)
this.setState({ status: 'success' })
}

componentWillUnmount() {
if (this.unlisten) {
this.unlisten()
}
}

static getDerivedStateFromProps(
Expand All @@ -88,11 +115,40 @@ class Browser extends React.Component<BrowserProps, BrowserState> {
}

componentDidUpdate(prevProps: BrowserProps) {
const { fetchBanners, match } = this.props
const { placeId } = match.params
const { filter } = this.state
if (placeId !== prevProps.match.params.placeId) {
fetchBanners(placeId, filter, 0)
if (!this.state.scrollRestored && this.props.banners.length > 0) {
const scrollPosition = sessionStorage.getItem('browseScrollPosition')
if (scrollPosition) {
setTimeout(() => {
const scrollable = document.querySelector('.container')
if (scrollable) {
scrollable.scrollTo({
top: parseInt(scrollPosition, 10),
behavior: 'smooth',
})
this.setState({ scrollRestored: true })
}
}, 50)
}
}
}

fetchChildren = async (
placeId: string | undefined,
forceLoad: boolean = true
) => {
const {
fetchAdministrativeAreas,
fetchCountries,
getAdministrativeAreas,
countries,
} = this.props

if (placeId) {
if (forceLoad || (getAdministrativeAreas(placeId) ?? []).length === 0) {
await fetchAdministrativeAreas(placeId)
}
} else if (forceLoad || countries.length === 0) {
await fetchCountries()
}
}

Expand All @@ -103,6 +159,7 @@ class Browser extends React.Component<BrowserProps, BrowserState> {
this.setState({
filter,
page: 0,
scrollRestored: false,
})
updateSettings({
defaultOnline: filter.online,
Expand All @@ -125,45 +182,26 @@ class Browser extends React.Component<BrowserProps, BrowserState> {
selectedPlaceId: newPlaceId,
page: 0,
status: 'success',
scrollRestored: false,
})
history.push(`/browse/${newPlaceId || ''}`)
}

// By calling with forceLoad false, we could
// skip loading lists of places if already loaded
// during this session
// We use true for now so that new places are shown when
// banners were added by yourself or someone else while browsing
fetchChildren = async (
placeId: string | undefined,
forceLoad: boolean = true
) => {
const {
fetchAdministrativeAreas,
fetchCountries,
getAdministrativeAreas,
countries,
} = this.props

if (placeId) {
if (forceLoad || (getAdministrativeAreas(placeId) ?? []).length === 0) {
await fetchAdministrativeAreas(placeId)
}
} else if (forceLoad || countries.length === 0) {
await fetchCountries()
}
history.push(`/browse/${newPlaceId || ''}`)
}

onPlaceExpanded = async (place: Place | undefined) => {
this.fetchChildren(place?.id)
}

onLoadMoreBanners = () => {
const { fetchBanners, match } = this.props
const { placeId } = match.params
const { filter, page } = this.state
this.setState({ page: page + 1 })
return fetchBanners(placeId, filter, page + 1)
onLoadMoreBanners = async () => {
if (!this.props.hasMore) return

const nextPage = this.state.page + 1
await this.props.fetchBanners(
this.props.match.params.placeId,
this.state.filter,
nextPage
)
this.setState({ page: nextPage })
}

render() {
Expand Down Expand Up @@ -192,11 +230,9 @@ class Browser extends React.Component<BrowserProps, BrowserState> {
administrativeAreas = getAdministrativeAreas(selectedPlaceId) || []
selectedPlaces = [selectedPlace]

// Get parent path
let currentParentPlace: Place | null = selectedPlace
while (currentParentPlace?.parentPlaceId) {
currentParentPlace = getPlace(currentParentPlace?.parentPlaceId)

if (currentParentPlace) {
selectedPlaces.push(currentParentPlace)
}
Expand Down Expand Up @@ -297,7 +333,7 @@ export type BrowserProps = {
defaultProximityLongitude: number | undefined
banners: Array<Banner>
countries: Array<Place>
hasMore: Boolean
hasMore: boolean
getAdministrativeAreas: (parentPlaceId: string) => Array<Place>
getPlace: (placeID: string) => Place | null
fetchCountries: () => Promise<void>
Expand All @@ -309,6 +345,7 @@ export type BrowserProps = {
page: number
) => Promise<void>
updateSettings: (settings: Partial<SettingsState>) => void
resetBrowsedBanners: () => void
} & RouteComponentProps<{ placeId: string }> &
WithTranslationProps

Expand All @@ -317,6 +354,7 @@ interface BrowserState {
selectedPlaceId?: string
page: number
status: 'initial' | 'success' | 'loading' | 'error'
scrollRestored: boolean
}

const mapStateToProps = (state: RootState) => ({
Expand All @@ -325,7 +363,7 @@ const mapStateToProps = (state: RootState) => ({
getAdministrativeAreas: (parentPlaceId: string) =>
getAdministrativeAreasSelector(state, parentPlaceId),
getPlace: (placeId: string) => getPlaceSelector(state, placeId),
hasMore: getHasMoreBrowsedBanners(state),
hasMore: !!getHasMoreBrowsedBanners(state),
defaultOnline: getDefaultOnline(state),
...getDefaultOrder(state),
})
Expand All @@ -336,6 +374,7 @@ const mapDispatchToProps = {
fetchAdministrativeAreas: loadAdministrativeAreasAction,
fetchPlace: loadPlaceAction,
updateSettings: updateSettingsAction,
resetBrowsedBanners: resetBrowsedBannersAction,
}

export default connect(
Expand Down
Loading