MOB-1327: add thumbnail and support place-only visuals

This commit is contained in:
sepeterson
2026-06-16 16:38:56 -05:00
parent 54ecd4d1af
commit 55e2c8b4e1

View File

@@ -1,11 +1,15 @@
import { useNavigation } from "@react-navigation/native";
import {
Body3,
Heading2,
Heading4,
IconicTaxonIcon,
INatIcon,
UserIcon,
} from "components/SharedComponents";
import BackButton from "components/SharedComponents/Buttons/BackButton";
import ContainedSquareButton from "components/SharedComponents/Buttons/ContainedSquareButton";
import { View } from "components/styledComponents";
import { Image, View } from "components/styledComponents";
import type { TFunction } from "i18next";
import type { ExploreStackScreenProps } from "navigation/types";
import type { ExploreV2LocationState, ExploreV2Subject } from "providers/ExploreV2Context";
@@ -17,6 +21,8 @@ import React from "react";
import { useTranslation } from "sharedHooks";
import colors from "styles/tailwindColors";
const THUMBNAIL_CLASS = "w-[62px] h-[62px] rounded-lg";
function subjectLabel( subject: ExploreV2Subject | null, t: TFunction ): string {
if ( !subject ) { return t( "All-organisms" ); }
switch ( subject.type ) {
@@ -44,6 +50,50 @@ function locationLabel( location: ExploreV2LocationState, t: TFunction ): string
}
}
const SubjectThumbnail = ( { subject }: { subject: ExploreV2Subject } ) => {
switch ( subject.type ) {
case "taxon": {
const photo = subject.taxon.default_photo?.url;
return photo
? (
<Image
source={{ uri: photo }}
className={THUMBNAIL_CLASS}
accessibilityIgnoresInvertColors
testID="ExploreV2Header.taxonImage"
/>
)
: (
<IconicTaxonIcon
imageClassName={[THUMBNAIL_CLASS]}
iconicTaxonName={subject.taxon.iconic_taxon_name}
/>
);
}
case "user":
return <UserIcon size={62} uri={subject.user.icon_url} />;
case "project":
return subject.project.icon
? (
<Image
source={{ uri: subject.project.icon }}
className={THUMBNAIL_CLASS}
accessibilityIgnoresInvertColors
testID="ExploreV2Header.projectImage"
/>
)
: (
<View
className={`${THUMBNAIL_CLASS} bg-lightGray items-center justify-center`}
>
<INatIcon name="briefcase" size={28} color={colors.darkGray} />
</View>
);
default:
return null;
}
};
const ExploreV2Header = ( ) => {
const { t } = useTranslation( );
const { state } = useExploreV2( );
@@ -53,35 +103,51 @@ const ExploreV2Header = ( ) => {
const place = locationLabel( state.location, t );
return (
<View className="bg-white p-4 flex-row justify-between items-center">
<View className="flex-1 mr-5">
<Heading4 numberOfLines={1} ellipsizeMode="tail">
{subject}
</Heading4>
{place
<View className="bg-white" testID="ExploreV2Header">
<View className="p-4 flex-row items-center">
<BackButton />
{state.subject
? (
<View className="flex-row items-center pt-2">
<INatIcon name="location" size={15} />
<Body3
maxFontSizeMultiplier={1.5}
className="ml-3"
numberOfLines={1}
ellipsizeMode="tail"
>
{place}
</Body3>
<View className="flex-1 flex-row items-center mr-5">
<SubjectThumbnail subject={state.subject} />
<View className="flex-1 ml-3">
<Heading4 numberOfLines={1} ellipsizeMode="tail">
{subject}
</Heading4>
{place
? (
<View className="flex-row items-center pt-1">
<INatIcon name="location" size={15} />
<Body3
maxFontSizeMultiplier={1.5}
className="ml-2"
numberOfLines={1}
ellipsizeMode="tail"
>
{place}
</Body3>
</View>
)
: null}
</View>
</View>
)
: null}
: (
<View className="flex-1 mr-5">
<Heading2 numberOfLines={1} ellipsizeMode="tail">
{place}
</Heading2>
</View>
)}
<ContainedSquareButton
accessibilityHint={t( "Opens-search-interface" )}
accessibilityLabel={t( "Search" )}
backgroundColor={colors.inatGreen}
icon="magnifying-glass"
onPress={() => navigation.navigate( "UniversalSearch" )}
testID="ExploreV2Header.searchButton"
/>
</View>
<ContainedSquareButton
accessibilityHint={t( "Opens-search-interface" )}
accessibilityLabel={t( "Search" )}
backgroundColor={colors.inatGreen}
icon="magnifying-glass"
onPress={() => navigation.navigate( "UniversalSearch" )}
testID="ExploreV2Header.searchButton"
/>
</View>
);
};