Skip to content
Draft
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
56 changes: 56 additions & 0 deletions src/apps/search/map/MapFeaturesContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
import { Feature, FeatureCollection } from '@peripleo/peripleo';
import { Typesense, useCachedHits } from '@performant-software/core-data';
import { useSearchConfig } from '../SearchConfigContext';

interface MapFeaturesContextType {
places: FeatureCollection;
updatePlace(feature: Feature): void;
}

const MapFeaturesContext = createContext<MapFeaturesContextType>(null);

interface Props {
children: ReactNode;
}

export const MapFeaturesContextProvider = ({ children }: Props) => {
const config = useSearchConfig();
const hits = useCachedHits();

// May have to be an empty feature collection instead!
const [places, setPlaces] = useState<FeatureCollection>(null);

/**
* Memo-izes the data to be displayed on the map as a feature collection.
*/
useEffect(() => {
const options = config.map.cluster_radius ? { type: 'Point' } : undefined;
const featureCollection = Typesense.toFeatureCollection(hits, config.map.geometry, options);

// TODO we could make this smarter! If a feature with the given ID already exists,
// we could keep it. (Assuming that the hits would never provide a higher-res version than
// what we already have.)
setPlaces(featureCollection);
}, [hits]);

const updatePlace = (feature: Feature) => {
const updated = places.features.map(f => f.id === feature.id ? feature : f);
setPlaces({ type: 'FeatureCollection', features: updated });
}

return (
<MapFeaturesContext.Provider
value={{
places,
updatePlace
}}
>
{ children }
</MapFeaturesContext.Provider>
)
};

export const useCachedPlaces = () => {
return useContext(MapFeaturesContext);
}
119 changes: 61 additions & 58 deletions src/apps/search/map/MapLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import PanelHistoryContext, { PanelHistoryEntryType } from './PanelHistoryContext';
import GeosearchFilter from '@apps/search/map/GeosearchFilter';
import { useSearchConfig } from '@apps/search/SearchConfigContext';
import { MapFeaturesContextProvider } from './MapFeaturesContext';

const DEFAULT_MAX_ZOOM = 14;

Expand Down Expand Up @@ -111,80 +112,82 @@ const MapLayout = () => {

return (
<PanelHistoryContext.Provider value={{panelHistory, setPanelHistory}}>
<div
className='absolute left-0 right-0 bottom-0 top-[64px]'
>
<MapView />
</div>
<Header
className='h-[64px]'
filters={filters}
onFiltersChange={setFilters}
onTimelineChange={setTimeline}
onViewChange={setView}
timeline={timeline}
view={view}
tableView={config.table}
/>
<div
className='flex grow h-[calc(100vh-160px)]'
>
<MapFeaturesContextProvider>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: no changes in this component, except for wrapping everything in MapFeaturesContextProvider.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the MapSearch component be a more appropriate place for this context than the MapLayout? Or was there a reason for specifically putting this in MapLayout?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also looks like there were come changes to CSS classes?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component that's rendering the selected geometry overlay is the <BasePanel> which, if I saw correctly, is rendered in <SearchRoutes> – which is why <MapLayout> would be the lowest component in the hierarchy this would have to be, I think.

Re CSS classes: I'll double check. But I think that's just an artefact of how git interprets the change of wrapping the whole markup.

<div
className={clsx('flex', { 'grow': view === Views.list && !timeline })}
className='absolute left-0 right-0 bottom-0 top-[64px]'
>
<MapView />
</div>
<Header
className='h-[64px]'
filters={filters}
onFiltersChange={setFilters}
onTimelineChange={setTimeline}
onViewChange={setView}
timeline={timeline}
view={view}
tableView={config.table}
/>
<div
className='flex grow h-[calc(100vh-160px)]'
>
<div
className='flex flex-col'
className={clsx('flex', { 'grow': view === Views.list && !timeline })}
>
<Facets
className='w-[240px] px-6 bg-neutral-100 backdrop-blur-sm shadow-sm overflow-y-auto'
config={config}
open={filters}
<div
className='flex flex-col'
>
<GeosearchFilter />
</Facets>
<Facets
className='w-[240px] px-6 bg-neutral-100 backdrop-blur-sm shadow-sm overflow-y-auto'
config={config}
open={filters}
>
<GeosearchFilter />
</Facets>
</div>
{ view === Views.list && (
<div
className='flex flex-col'
>
<ListView
className='w-[350px]'
/>
</div>
)}
</div>
{ view === Views.list && (
{ view === Views.table && (
<div
className='flex flex-col'
className='flex grow items-end'
>
<ListView
className='w-[350px]'
<TableView
className='h-[350px]'
/>
</div>
)}
{ timeline && (
<div
className='flex grow shrink max-w-full items-end'
>
<TimelineView
className={clsx(
'h-[360px]',
'flex',
'grow',
'shrink',
)}
padding={timelinePadding}
/>
</div>
)}
</div>
{ view === Views.table && (
<div
className='flex grow items-end'
>
<TableView
className='h-[350px]'
/>
</div>
)}
{ timeline && (
<div
className='flex grow shrink max-w-full items-end'
className='flex justify-end'
>
<TimelineView
className={clsx(
'h-[360px]',
'flex',
'grow',
'shrink',
)}
padding={timelinePadding}
<SearchRoutes
className='w-[350px]'
/>
</div>
)}
<div
className='flex justify-end'
>
<SearchRoutes
className='w-[350px]'
/>
</div>
</div>
</MapFeaturesContextProvider>
</PanelHistoryContext.Provider>
);
};
Expand Down
17 changes: 10 additions & 7 deletions src/apps/search/map/MapView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,39 @@ import Tooltip from '@apps/search/map/Tooltip';
import Map from '@components/Map';
import {
SearchResultsLayer,
Typesense as TypesenseUtils,
useCachedHits,
// Typesense as TypesenseUtils,
// useCachedHits,
useGeoSearch,
useSearching
} from '@performant-software/core-data';
import { HoverTooltip, useSelectionValue } from '@peripleo/maplibre';
import { useCurrentRoute, useNavigate } from '@peripleo/peripleo';
import { Feature, FeatureCollection, useCurrentRoute, useNavigate } from '@peripleo/peripleo';
import { parseFeature } from '@utils/search';
import { useContext, useEffect, useMemo } from 'react';
import { useContext, useEffect, useMemo, useState } from 'react';
import _ from 'underscore';
import { useSearchConfig } from '@apps/search/SearchConfigContext';
import { useCachedPlaces } from './MapFeaturesContext';

const MapView = () => {
const config = useSearchConfig();
const { isRefinedWithMap } = useGeoSearch();
const navigate = useNavigate();
const { selected } = useSelectionValue() || {};
const route = useCurrentRoute();
const hits = useCachedHits();
// const hits = useCachedHits();
const places = useCachedPlaces();
const searching = useSearching();

const { boundingBoxOptions, controlsClass } = useContext(MapSearchContext);

/**
* Memo-izes the data to be displayed on the map as a feature collection.
*/
*
const data = useMemo(() => {
const options = config.map.cluster_radius ? { type: 'Point' } : undefined;
return TypesenseUtils.toFeatureCollection(hits, config.map.geometry, options);
}, [hits]);
*/

/**
* If we're on the place detail page or refining results by the map view port, we'll suppress the auto-bounding box
Expand Down Expand Up @@ -79,7 +82,7 @@ const MapView = () => {
>
<SearchResultsLayer
boundingBoxOptions={boundingBoxOptions}
data={data}
data={places}
cluster={!!config.map.cluster_radius}
clusterRadius={config.map.cluster_radius}
fitBoundingBox={fitBoundingBox}
Expand Down
21 changes: 18 additions & 3 deletions src/apps/search/map/panels/BasePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import _ from 'underscore';
import PanelHistoryContext from '@apps/search/map/PanelHistoryContext';
import { useSearchConfig } from '@apps/search/SearchConfigContext';
import { useCachedPlaces } from '../MapFeaturesContext';

interface Props {
className?: string;
Expand All @@ -49,6 +50,7 @@ const BasePanel = (props: Props) => {

const navigate = useNavigate();
const config = useSearchConfig();
const { updatePlace } = useCachedPlaces();
const { t } = useContext(TranslationContext);
const { setSelected } = useSelection();

Expand Down Expand Up @@ -209,7 +211,7 @@ const { data: { people = [] } = {}, loading: peopleLoading } = useLoader(onLoadP

/**
* Memo-izes the geometry.
*/
*
const geometry = useMemo(() => {
if (props.resolveGeometry && item) {
return props.resolveGeometry(item);
Expand All @@ -220,6 +222,19 @@ const { data: { people = [] } = {}, loading: peopleLoading } = useLoader(onLoadP
places.filter((place) => place.place_geometry)
);
}, [item, places, props.resolveGeometry]);
*/

useEffect(() => {
if (props.resolveGeometry && item) {
const highRes = props.resolveGeometry(item);
updatePlace(highRes);
} else if (!_.isEmpty(places.filter((place) => place.place_geometry))) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I didn't figure out what this branch does. Under what conditions will new places come in? I assume this code needs changing... instead of creating one FeatureCollection, we probably need to loop through each place and call updatePlace for each one separately?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For non-place records (people, events, etc), the geometry data is obtained from the related place records (fetched above). For this use case, the record could be linked to multiple geometries, so yes, I believe we'd need to loop through each and update.

For place records, we use the resolveGeometry prop to obtain the geometry data (pulled off the base record). This returns a FeatureCollection and it looks like the updatePlace function expects a Feature?

const place = CoreDataUtils.toFeatureCollection(
places.filter((place) => place.place_geometry)
);
updatePlace(place);
}
}, [item, places, props.resolveGeometry])

/**
* Memo-izes the related media items.
Expand Down Expand Up @@ -450,7 +465,7 @@ const { data: { people = [] } = {}, loading: peopleLoading } = useLoader(onLoadP
/>
)}
</RecordDetailPanel>
{ geometry && (
{/* geometry && (
<LocationMarkers
animate
boundingBoxOptions={boundingBoxOptions}
Expand All @@ -465,7 +480,7 @@ const { data: { people = [] } = {}, loading: peopleLoading } = useLoader(onLoadP
fitBoundingBox={_.get(config.map, 'zoom_to_place', true)}
layerId='current'
/>
)}
) */}
{ manifestUrl && (
<MediaGallery
manifestUrl={manifestUrl}
Expand Down