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
10 changes: 7 additions & 3 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,15 @@ const BaseApp: React.FunctionComponent<{
filter: (url: string) => url.includes('/observations/'), // Only handle observation links
config: {
screens: {
Observations: {
MainStack: {
path: 'observations/#/view/observations',
screens: {
observationsList: '',
observation: ':id',
bottomTabs: {
screens: {
Observations: '',
},
},
observationModal: ':id',
},
},
},
Expand Down
35 changes: 18 additions & 17 deletions components/observations/ObservationDetailView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {useCallback, useMemo} from 'react';
import {Image, ScrollView, StyleSheet} from 'react-native';
import {Image, ScrollView} from 'react-native';

import Ionicons from '@expo/vector-icons/Ionicons';
import {useFocusEffect, useNavigation} from '@react-navigation/native';
Expand Down Expand Up @@ -46,7 +46,6 @@ import {
FormatSnowAvailableForTransport,
FormatWindLoading,
InstabilityDistribution,
MapLayerFeature,
Observation,
Position,
SnowAvailableForTransport,
Expand All @@ -56,6 +55,7 @@ import {
} from 'types/nationalAvalancheCenter';
import {observationDateToLocalShortDateString, utcDateToLocalShortDateString} from 'utils/date';

// TODO: Remove NWACObs issue:1152
export const NWACObservationDetailView: React.FunctionComponent<{
id: string;
}> = ({id}) => {
Expand All @@ -66,13 +66,11 @@ export const NWACObservationDetailView: React.FunctionComponent<{
const capabilitiesResult = useAvalancheCenterCapabilities();
const capabilities = capabilitiesResult.data;

const mapFeatures = useMemo(() => mapFeaturesForCenter(mapLayer, observation?.center_id.toUpperCase() as AvalancheCenterID), [mapLayer, observation]);

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

return <ObservationCard observation={observation} mapLayerFeatures={mapFeatures} capabilities={capabilities} />;
return <ObservationCard observation={observation} capabilities={capabilities} />;
};

export const ObservationDetailView: React.FunctionComponent<{
Expand All @@ -86,12 +84,23 @@ export const ObservationDetailView: React.FunctionComponent<{
const capabilities = capabilitiesResult.data;

const mapFeatures = useMemo(() => mapFeaturesForCenter(mapLayer, observation?.center_id.toUpperCase() as AvalancheCenterID), [mapLayer, observation?.center_id]);
const navigation = useNavigation<MainStackNavigationProps>();
const zone_name = useMemo(
() => observation?.location_point?.lat && observation?.location_point?.lng && matchesZone(mapFeatures ?? [], observation.location_point?.lat, observation.location_point?.lng),
[observation, mapFeatures],
);

React.useEffect(() => {
if (zone_name) {
navigation.setOptions({title: `${zone_name} Observation`});
}
}, [navigation, zone_name]);

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

return <ObservationCard observation={observation} mapLayerFeatures={mapFeatures} capabilities={capabilities} />;
return <ObservationCard observation={observation} capabilities={capabilities} />;
};

const dataTableFlex = [1, 1];
Expand Down Expand Up @@ -177,18 +186,10 @@ export const withUnits = (value: string | number | null | undefined, units: stri

export const ObservationCard: React.FunctionComponent<{
observation: Observation;
mapLayerFeatures: MapLayerFeature[];
capabilities: AllAvalancheCenterCapabilities;
}> = ({observation, mapLayerFeatures, capabilities}) => {
const navigation = useNavigation<MainStackNavigationProps>();
}> = ({observation, capabilities}) => {
const {avalanches_observed, avalanches_triggered, avalanches_caught} = observation.instability;
const zone_name =
observation.location_point?.lat && observation.location_point?.lng && matchesZone(mapLayerFeatures, observation.location_point?.lat, observation.location_point?.lng);
React.useEffect(() => {
if (zone_name) {
navigation.setOptions({title: `${zone_name} Observation`});
}
}, [navigation, zone_name]);

const postHog = usePostHog();

const recordAnalytics = useCallback(() => {
Expand All @@ -208,7 +209,7 @@ export const ObservationCard: React.FunctionComponent<{
const initialCameraBounds: CameraBounds = {ne: nePosition, sw: swPosition};

return (
<View style={{...StyleSheet.absoluteFillObject, backgroundColor: 'white'}}>
<View style={{flex: 1, backgroundColor: 'white'}}>
<VStack space={8} backgroundColor="white" style={{height: '100%', width: '100%'}}>
<ScrollView style={{height: '100%', width: '100%'}}>
<VStack space={8} backgroundColor={colorLookup('primary.background')} paddingBottom={insets.bottom}>
Expand Down
76 changes: 76 additions & 0 deletions components/observations/ObservationDetailViewModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Ionicons from '@expo/vector-icons/Ionicons';
import {useNavigation} from '@react-navigation/native';
import {AvalancheCenterLogo} from 'components/AvalancheCenterLogo';
import {incompleteQueryState, QueryState} from 'components/content/QueryState';
import {HStack, View} from 'components/core';
import {ObservationCard} from 'components/observations/ObservationDetailView';
import {matchesZone} from 'components/observations/ObservationsFilterForm';
import {Title3Black} from 'components/text';
import {useAllMapLayers} from 'hooks/useAllMapLayers';
import {useAvalancheCenterCapabilities} from 'hooks/useAvalancheCenterCapabilities';
import {useNACObservation} from 'hooks/useNACObservation';
import React, {useCallback, useMemo} from 'react';
import {MainStackNavigationProps} from 'routes';
import {colorLookup} from 'theme';
import {AvalancheCenterID, mapFeaturesForCenter} from 'types/nationalAvalancheCenter';

export const ObservationDetailModalView: React.FunctionComponent<{
id: string;
}> = ({id}) => {
const observationResult = useNACObservation(id);
const observation = observationResult.data;
const mapResult = useAllMapLayers();
const mapLayer = mapResult.data;
const capabilitiesResult = useAvalancheCenterCapabilities();
const capabilities = capabilitiesResult.data;

const mapFeatures = useMemo(() => mapFeaturesForCenter(mapLayer, observation?.center_id.toUpperCase() as AvalancheCenterID), [mapLayer, observation?.center_id]);
const navigation = useNavigation<MainStackNavigationProps>();
const zone_name = useMemo(
() => observation?.location_point?.lat && observation?.location_point?.lng && matchesZone(mapFeatures ?? [], observation.location_point?.lat, observation.location_point?.lng),
[observation, mapFeatures],
);

const closeModal = useCallback(() => navigation.goBack(), [navigation]);

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

return (
<View flex={1}>
<ObsDetailModalHeader title={`${zone_name} Observation`} centerId={observation.center_id} onClosePressed={closeModal} />
<ObservationCard observation={observation} capabilities={capabilities} />
</View>
);
};

interface ObsDetailModalHeaderProps {
title: string;
centerId: AvalancheCenterID;
onClosePressed: () => void;
}

const ObsDetailModalHeader: React.FunctionComponent<ObsDetailModalHeaderProps> = ({title, centerId, onClosePressed}) => {
return (
<View style={{width: '100%', backgroundColor: colorLookup('white'), paddingVertical: 8, justifyContent: 'center', alignContent: 'center'}}>
<HStack justifyContent="space-between" space={8} px={16}>
<Ionicons.Button
size={24}
color={colorLookup('primary')}
name="close"
backgroundColor={colorLookup('white')}
iconStyle={{marginLeft: 0, marginRight: 0}}
style={{textAlign: 'center', borderColor: 'transparent', borderWidth: 1}}
onPress={onClosePressed}
/>

<Title3Black textAlign="center" style={{flex: 1, borderColor: 'transparent', borderWidth: 1, color: colorLookup('text')}}>
{title}
</Title3Black>

<AvalancheCenterLogo style={{height: 32, width: 32, resizeMode: 'contain', flex: 0, flexGrow: 0, marginTop: 4}} avalancheCenterId={centerId} />
</HStack>
</View>
);
};
29 changes: 25 additions & 4 deletions components/screens/navigation/MainStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {NavigationHeader} from 'components/content/NavigationHeader';
import {View} from 'components/core';
import {FeatureFlagsDebuggerScreen} from 'components/FeatureFlagsDebugger';
import {NWACObservationDetailView, ObservationDetailView} from 'components/observations/ObservationDetailView';
import {ObservationDetailModalView} from 'components/observations/ObservationDetailViewModal';
import {ObservationForm} from 'components/observations/ObservationForm';
import {ObservationsPortal} from 'components/observations/ObservationsPortal';
import {ForecastScreen} from 'components/screens/ForecastScreen';
Expand Down Expand Up @@ -78,6 +79,16 @@ export const MainStackNavigator: React.FunctionComponent<{
title: 'Observation',
}}
/>

<MainStack.Screen
name="observationModal"
component={ObservationDetailModal}
options={{
presentation: 'pageSheet',
headerShown: false,
}}
/>

<MainStack.Screen
name="nwacObservation"
component={NWACObservationDetailScreen}
Expand Down Expand Up @@ -149,7 +160,7 @@ const ObservationSubmitScreen = () => {
return <ObservationForm key={`${center_id}-observationForm`} center_id={center_id} />;
};

export const ObservationDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'observation'>) => {
const ObservationDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'observation'>) => {
const {id} = route.params;
return (
<View style={styles.fullScreen}>
Expand All @@ -158,7 +169,17 @@ export const ObservationDetailScreen = ({route}: NativeStackScreenProps<MainStac
);
};

export const NWACObservationDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'nwacObservation'>) => {
const ObservationDetailModal = ({route}: NativeStackScreenProps<MainStackParamList, 'observationModal'>) => {
const {id} = route.params;

return (
<View style={styles.fullScreen}>
<ObservationDetailModalView id={id} />
</View>
);
};

const NWACObservationDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'nwacObservation'>) => {
const {id} = route.params;
return (
<View style={styles.fullScreen}>
Expand All @@ -169,7 +190,7 @@ export const NWACObservationDetailScreen = ({route}: NativeStackScreenProps<Main

// MARK: Weather Station Screens

export const WeatherStationsDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'stationsDetail'>) => {
const WeatherStationsDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'stationsDetail'>) => {
const {preferences} = usePreferences();
const center_id = preferences.center;
return (
Expand All @@ -182,7 +203,7 @@ export const WeatherStationsDetailScreen = ({route}: NativeStackScreenProps<Main
);
};

export const WeatherStationDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'stationDetail'>) => {
const WeatherStationDetailScreen = ({route}: NativeStackScreenProps<MainStackParamList, 'stationDetail'>) => {
const {preferences} = usePreferences();
const center_id = preferences.center;
return (
Expand Down
3 changes: 3 additions & 0 deletions routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export type MainStackParamList = {
observation: {
id: string;
};
observationModal: {
id: string;
};
nwacObservation: {
id: string;
};
Expand Down