Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
434e5b1
data: fetch and parse alternate zones
wheninseattle Feb 14, 2025
b153c44
Merge alternateZones with mapLayer - outline next steps
wheninseattle Feb 18, 2025
704d2d6
Add types
wheninseattle Feb 25, 2025
5381a3e
Decouple mapLayer context and alt zones. Filter zones with no observa…
wheninseattle Feb 26, 2025
0aac541
Add hooks to incomplete query block and remove mapLayer feature filter
wheninseattle Mar 4, 2025
4a58064
WIP
wheninseattle Mar 13, 2025
0daa506
Merge branch 'main' into wn-889-add-observation-zones
wheninseattle Apr 1, 2025
0d7f3f2
Improve types
wheninseattle Apr 4, 2025
4021e1a
Add prefetching
wheninseattle Apr 4, 2025
21f132e
Break out functions and add unit tests
wheninseattle Apr 4, 2025
f276c99
Change KML typing to Zod
wheninseattle Apr 4, 2025
94b5e6e
Merge branch 'main' into wn-889-add-observation-zones
wheninseattle Apr 8, 2025
cfa8ebd
Improve type safety and break out functions
wheninseattle Apr 11, 2025
726d1ba
Add tests for KML parsing
wheninseattle Apr 11, 2025
bd6ad1b
Fix dependancies
wheninseattle Apr 11, 2025
6ac7afb
Pick up code review comments
wheninseattle Apr 11, 2025
84358de
Implement prefetching
wheninseattle Apr 17, 2025
8e15125
Address comments
wheninseattle Apr 17, 2025
f1575de
Fix package issues
wheninseattle Apr 17, 2025
8a4c0b2
making ci happy
yuliadub Jul 4, 2025
23c644d
get changes from avy main
yuliadub Jul 4, 2025
67b712b
make prettier happy
yuliadub Jul 4, 2025
8033735
add custom zone name to obs detail view header
yuliadub Aug 11, 2025
02b7643
adjust xml parsing package and fix yarn lock
yuliadub Aug 20, 2025
0acef2c
revert yarn.lock changes
yuliadub Aug 20, 2025
450bd48
remove random yarn files
yuliadub Aug 20, 2025
dabca18
adjust yarn.lock and fix package.json
yuliadub Aug 20, 2025
0dff730
fix tests
yuliadub Aug 20, 2025
ed223ca
fix zod errors
yuliadub Sep 5, 2025
85dbe13
merge main changes in
yuliadub Sep 6, 2025
92cbfdd
add fast-xml-parser back
yuliadub Sep 9, 2025
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
10 changes: 6 additions & 4 deletions components/observations/ObservationDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {NACIcon} from 'components/icons/nac-icons';
import {matchesZone} from 'components/observations/ObservationsFilterForm';
import {AllCapsSm, AllCapsSmBlack, Body, BodyBlack, BodySemibold, bodySize} from 'components/text';
import {HTML} from 'components/text/HTML';
import {useMergedMapLayer} from 'hooks/useAlternateObservationZones';
import {useAvalancheCenterCapabilities} from 'hooks/useAvalancheCenterCapabilities';
import {useMapLayer} from 'hooks/useMapLayer';
import {useNACObservation} from 'hooks/useNACObservation';
Expand Down Expand Up @@ -46,7 +47,7 @@ import {
FormatSnowAvailableForTransport,
FormatWindLoading,
InstabilityDistribution,
MapLayer,
MergedMapLayer,
Observation,
SnowAvailableForTransport,
WindLoading,
Expand Down Expand Up @@ -80,12 +81,13 @@ export const ObservationDetailView: React.FunctionComponent<{
const mapLayer = mapResult.data;
const capabilitiesResult = useAvalancheCenterCapabilities();
const capabilities = capabilitiesResult.data;
const mergedMapLayer = useMergedMapLayer(observation?.center_id?.toUpperCase() as AvalancheCenterID, mapLayer);

if (incompleteQueryState(observationResult, mapResult, capabilitiesResult) || !observation || !mapLayer || !capabilities || !capabilities) {
if (incompleteQueryState(observationResult, mapResult, capabilitiesResult) || !observation || !mergedMapLayer || !capabilities || !capabilities) {
return <QueryState results={[observationResult, mapResult, capabilitiesResult]} />;
}

return <ObservationCard observation={observation} mapLayer={mapLayer} capabilities={capabilities} />;
return <ObservationCard observation={observation} mapLayer={mergedMapLayer} capabilities={capabilities} />;
};

const dataTableFlex = [1, 1];
Expand Down Expand Up @@ -201,7 +203,7 @@ export const withUnits = (value: string | number | null | undefined, units: stri

export const ObservationCard: React.FunctionComponent<{
observation: Observation;
mapLayer: MapLayer;
mapLayer: MergedMapLayer;
capabilities: AllAvalancheCenterCapabilities;
}> = ({observation, mapLayer, capabilities}) => {
const navigation = useNavigation<ObservationsStackNavigationProps>();
Expand Down
9 changes: 5 additions & 4 deletions components/observations/ObservationsFilterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type {Resolver} from 'react-hook-form';
import {FieldErrors, FormProvider, useForm} from 'react-hook-form';
import {KeyboardAvoidingView, Platform, View as RNView, SafeAreaView, ScrollView, TouchableOpacity, findNodeHandle} from 'react-native';
import {colorLookup} from 'theme';
import {MapLayer, ObservationFragment, PartnerType} from 'types/nationalAvalancheCenter';
import {MergedMapLayer, ObservationFragment, PartnerType} from 'types/nationalAvalancheCenter';
import {RequestedTime, requestedTimeToUTCDate} from 'utils/date';
import {z} from 'zod';

Expand Down Expand Up @@ -107,7 +107,7 @@ interface FilterListItem {
label: string;
removeFilter?: (config: ObservationFilterConfig) => ObservationFilterConfig;
}
export const filtersForConfig = (mapLayer: MapLayer, config: ObservationFilterConfig, additionalFilters: Partial<ObservationFilterConfig> | undefined): FilterListItem[] => {
export const filtersForConfig = (mapLayer: MergedMapLayer, config: ObservationFilterConfig, additionalFilters: Partial<ObservationFilterConfig> | undefined): FilterListItem[] => {
if (!config) {
return [];
}
Expand Down Expand Up @@ -171,7 +171,7 @@ export const filtersForConfig = (mapLayer: MapLayer, config: ObservationFilterCo

interface ObservationsFilterFormProps {
requestedTime: RequestedTime;
mapLayer: MapLayer;
mapLayer: MergedMapLayer;
initialFilterConfig: ObservationFilterConfig;
currentFilterConfig: ObservationFilterConfig;
setFilterConfig: React.Dispatch<React.SetStateAction<ObservationFilterConfig>>;
Expand Down Expand Up @@ -324,6 +324,7 @@ export const ObservationsFilterForm: React.FunctionComponent<ObservationsFilterF
</View>
)}
{mapLayer && (
// TODO: Render in two groups with respective labels: <Center_ID> Forecast Zones & Other Regions
Copy link
Copy Markdown
Collaborator

@stevekuznetsov stevekuznetsov Feb 26, 2025

Choose a reason for hiding this comment

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

@rustynwac let's pin down the UX we want for the two sets of zones in the filter modal with an eye to what the websites are doing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@rustynwac @stevekuznetsov did we decide how we want to represent the zones?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@rustynwac let's sync on this

<CheckboxSelectField
name="zones"
items={
Expand Down Expand Up @@ -401,7 +402,7 @@ export const ObservationsFilterForm: React.FunctionComponent<ObservationsFilterF
);
};

export const matchesZone = (mapLayer: MapLayer, lat: number | null | undefined, long: number | null | undefined): string => {
export const matchesZone = (mapLayer: MergedMapLayer, lat: number | null | undefined, long: number | null | undefined): string => {
if (!lat || !long) {
return 'Unknown Zone';
}
Expand Down
25 changes: 20 additions & 5 deletions components/observations/ObservationsListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import {ObservationFilterConfig, ObservationsFilterForm, createDefaultFilterConf
import {usePendingObservations} from 'components/observations/uploader/usePendingObservations';
import {Body, BodyBlack, BodySm, BodySmBlack, BodyXSm, Caption1Semibold, bodySize, bodyXSmSize} from 'components/text';
import {compareDesc, formatDuration, isBefore, parseISO, sub} from 'date-fns';
import {useAlternateObservationZones, useMergedMapLayer} from 'hooks/useAlternateObservationZones';
import {useAvalancheCenterMetadata} from 'hooks/useAvalancheCenterMetadata';
import {useMapLayer} from 'hooks/useMapLayer';
import {useNACObservations} from 'hooks/useNACObservations';
import {useNWACObservations} from 'hooks/useNWACObservations';
import {useRefresh} from 'hooks/useRefresh';
import {useToggle} from 'hooks/useToggle';
import {LoggerContext, LoggerProps} from 'loggerContext';
import {usePostHog} from 'posthog-react-native';

import {
ActivityIndicator,
ColorValue,
Expand Down Expand Up @@ -73,8 +76,13 @@ export const ObservationsListView: React.FunctionComponent<ObservationsListViewP
const originalFilterConfig: ObservationFilterConfig = useMemo(() => createDefaultFilterConfig(additionalFilters), [additionalFilters]);
const [filterConfig, setFilterConfig] = useState<ObservationFilterConfig>(originalFilterConfig);
const [filterModalVisible, {set: setFilterModalVisible, on: showFilterModal, off: hideFilterModal}] = useToggle(false);
const avalancheZoneMetadataResult = useAvalancheCenterMetadata(center_id);
const alternateZonesUrl: string = avalancheZoneMetadataResult.data?.widget_config?.observation_viewer?.alternate_zones || '';
const alternateObservationZonesResult = useAlternateObservationZones(alternateZonesUrl, center_id);

const mapResult = useMapLayer(center_id);
const mapLayer = mapResult.data;
const mergedMapLayer = useMergedMapLayer(center_id, mapLayer);

const postHog = usePostHog();

Expand Down Expand Up @@ -117,6 +125,7 @@ export const ObservationsListView: React.FunctionComponent<ObservationsListViewP
),
[observationsResult],
);

const observations: ObservationFragmentWithPageIndexAndZoneAndSource[] = useMemo(
() =>
flatObservationList
Expand All @@ -138,16 +147,20 @@ export const ObservationsListView: React.FunctionComponent<ObservationsListViewP
// calculate the zone and cache it now
.map(observation => ({
...observation,
zone: mapLayer ? matchesZone(mapLayer, observation.locationPoint.lat, observation.locationPoint.lng) : '',
zone: mergedMapLayer ? matchesZone(mergedMapLayer, observation.locationPoint.lat, observation.locationPoint.lng) : '',
})),
[flatObservationList, mapLayer, displayNWACObservations],
[flatObservationList, mergedMapLayer, displayNWACObservations],
);

const {isRefreshing, refresh} = useRefresh(observationsResult.refetch);
const refreshWrapper = useCallback(() => void refresh(), [refresh]);

// the displayed observations need to match all filters - for instance, if a user chooses a zone *and*
// an observer type, we only show observations that match both of those at the same time
const resolvedFilters = useMemo(() => (mapLayer ? filtersForConfig(mapLayer, filterConfig, additionalFilters) : []), [mapLayer, filterConfig, additionalFilters]);
const resolvedFilters = useMemo(
() => (mergedMapLayer ? filtersForConfig(mergedMapLayer, filterConfig, additionalFilters) : []),
[mergedMapLayer, filterConfig, additionalFilters],
);

// Set a date limit for how far back to look for observations
const [lookBackLimit, setLookBackLimit] = useState<Duration>({years: 1});
Expand Down Expand Up @@ -315,7 +328,9 @@ export const ObservationsListView: React.FunctionComponent<ObservationsListViewP
[filterConfig, setFilterConfig],
);

if (incompleteQueryState(observationsResult, mapResult) || !mapLayer) {
const mergedMapLayerExists = !!mergedMapLayer;

if ((incompleteQueryState(observationsResult, mapResult, avalancheZoneMetadataResult, alternateObservationZonesResult) || !mapLayer, !mergedMapLayerExists)) {
return (
<Center width="100%" height="100%">
<QueryState results={[observationsResult, mapResult]} />
Expand All @@ -331,7 +346,7 @@ export const ObservationsListView: React.FunctionComponent<ObservationsListViewP
<Modal visible={filterModalVisible} onRequestClose={hideFilterModal}>
<ObservationsFilterForm
requestedTime={requestedTime}
mapLayer={mapLayer}
mapLayer={mergedMapLayer}
initialFilterConfig={originalFilterConfig}
currentFilterConfig={filterConfig}
setFilterConfig={setFilterConfig}
Expand Down
Loading
Loading