Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
34b1cb4
MOB-1346: state updates
sepeterson Aug 4, 2026
31081e2
MOB-1346: no permissions request in ExploreV2 location search
sepeterson Aug 4, 2026
0be516d
MOB-1346: not by me in context
sepeterson Aug 4, 2026
f763af3
MOB-1346: advanced search subsection UI
sepeterson Aug 4, 2026
97018db
MOB-1346: header in AdvancedSearch
sepeterson Aug 4, 2026
2f7e41e
MOB-1346: subcomponent tests
sepeterson Aug 4, 2026
9d77efd
MOB-1346: advanced search ui
sepeterson Aug 7, 2026
8e1bda1
merge main
sepeterson Aug 12, 2026
09109e9
MOB-1346: search button is gray until changes are applied
sepeterson Aug 12, 2026
2b2adb7
MOB-1346: seed advanced search from plain context
sepeterson Aug 12, 2026
ddce5d7
MOB-1346: support map area advanced search
sepeterson Aug 12, 2026
efc3cca
MOB-1346: support initial project and unobserved
sepeterson Aug 13, 2026
edf1dc5
Merge branch 'main' into mob-1346-screen
sepeterson Aug 13, 2026
733908f
MOB-1346: switch iconic taxa to rely on general taxon subject handlin…
sepeterson Aug 13, 2026
b0a5974
MOB-1346: share some sharable logic
sepeterson Aug 13, 2026
9bd213d
MOB-1346: tests fixed
sepeterson Aug 13, 2026
a9048d8
Merge branch 'main' into mob-1346-screen
sepeterson Aug 14, 2026
7a08399
MOB-1346: missing newlines
sepeterson Aug 17, 2026
0dc74f9
MOB-1346: drop non-taxon subjects in draftFromV2State
sepeterson Aug 17, 2026
0d54746
MOB-1346: observations_count cleanup
sepeterson Aug 17, 2026
ac1d837
MOB-1346: reset disabled wiring
sepeterson Aug 17, 2026
b0c567a
MOB-1346: discard sheet
sepeterson Aug 17, 2026
41d7722
MOB-1346: visual adjustments
sepeterson Aug 17, 2026
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
23 changes: 3 additions & 20 deletions src/components/Explore/ExploreV2/components/ExploreV2Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useNavigation } from "@react-navigation/native";
import { THUMBNAIL_CLASS } from "appConstants/classNames";
import locationLabel from "components/Explore/ExploreV2/helpers/locationLabel";
import {
Body1,
Body3,
Expand All @@ -14,11 +15,8 @@ import DisplayTaxonName from "components/SharedComponents/DisplayTaxonName";
import { Image, Pressable, View } from "components/styledComponents";
import type { TFunction } from "i18next";
import type { ExploreStackScreenProps } from "navigation/types";
import type { ExploreV2LocationState, ExploreV2Subject } from "providers/ExploreV2Context";
import {
EXPLORE_V2_PLACE_MODE,
useExploreV2,
} from "providers/ExploreV2Context";
import type { ExploreV2Subject } from "providers/ExploreV2Context";
import { useExploreV2 } from "providers/ExploreV2Context";
import React from "react";
import { useCurrentUser, useTranslation } from "sharedHooks";
import colors from "styles/tailwindColors";
Expand All @@ -41,21 +39,6 @@ function subjectLabel( subject: ExploreV2Subject | null, t: TFunction ): string
}
}

function locationLabel( location: ExploreV2LocationState, t: TFunction ): string {
switch ( location.placeMode ) {
case EXPLORE_V2_PLACE_MODE.WORLDWIDE:
return t( "Worldwide" );
case EXPLORE_V2_PLACE_MODE.NEARBY:
return t( "Nearby" );
case EXPLORE_V2_PLACE_MODE.PLACE:
return location.place.display_name || "";
case EXPLORE_V2_PLACE_MODE.MAP_AREA:
return t( "Map-Area" );
default:
return "";
}
}

const SubjectThumbnail = ( { subject }: { subject: ExploreV2Subject } ) => {
switch ( subject.type ) {
case "taxon": {
Expand Down
186 changes: 186 additions & 0 deletions src/components/Explore/ExploreV2/helpers/advancedSearchOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import type { TFunction } from "i18next";
import {
DATE_OBSERVED,
DATE_UPLOADED,
ESTABLISHMENT_MEAN,
MEDIA,
PHOTO_LICENSE,
REVIEWED,
TAXONOMIC_RANK,
WILD_STATUS,
} from "providers/ExploreContext";
import { OBSERVATIONS_SORT } from "sharedHelpers/observationsSort";

export interface FilterOption<ValueT> {
label: string;
value: ValueT;
}
const option = <ValueT extends string | number>(
value: ValueT,
label: string,
): [ValueT, FilterOption<ValueT>] => [value, { label, value }];

// `label` / `text` are the two lines of each row in the sort sheet; `labelCaps`
// is the single-line, all-caps version in the dropdown button
export const getSortByValues = ( t: TFunction ) => ( {

@sepeterson sepeterson Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This file is mostly copied from ExploreV1's FilterModal.tsx

[OBSERVATIONS_SORT.DATE_UPLOADED_NEWEST]: {
label: t( "Date-uploaded" ),
labelCaps: t( "DATE-UPLOADED-NEWEST" ),
text: t( "Newest-to-oldest" ),
value: OBSERVATIONS_SORT.DATE_UPLOADED_NEWEST,
},
[OBSERVATIONS_SORT.DATE_UPLOADED_OLDEST]: {
label: t( "Date-uploaded" ),
labelCaps: t( "DATE-UPLOADED-OLDEST" ),
text: t( "Oldest-to-newest" ),
value: OBSERVATIONS_SORT.DATE_UPLOADED_OLDEST,
},
[OBSERVATIONS_SORT.DATE_OBSERVED_NEWEST]: {
label: t( "Date-observed" ),
labelCaps: t( "DATE-OBSERVED-NEWEST" ),
text: t( "Newest-to-oldest" ),
value: OBSERVATIONS_SORT.DATE_OBSERVED_NEWEST,
},
[OBSERVATIONS_SORT.DATE_OBSERVED_OLDEST]: {
label: t( "Date-observed" ),
labelCaps: t( "DATE-OBSERVED-OLDEST" ),
text: t( "Oldest-to-newest" ),
value: OBSERVATIONS_SORT.DATE_OBSERVED_OLDEST,
},
} );

export const getTaxonomicRankValues = ( t: TFunction ) => Object.fromEntries( [
option( TAXONOMIC_RANK.none, t( "NONE--ranks" ) ),
option( TAXONOMIC_RANK.kingdom, t( "Ranks-KINGDOM" ) ),
option( TAXONOMIC_RANK.phylum, t( "Ranks-PHYLUM" ) ),
option( TAXONOMIC_RANK.subphylum, t( "Ranks-SUBPHYLUM" ) ),
option( TAXONOMIC_RANK.superclass, t( "Ranks-SUPERCLASS" ) ),
option( TAXONOMIC_RANK.class, t( "Ranks-CLASS" ) ),
option( TAXONOMIC_RANK.subclass, t( "Ranks-SUBCLASS" ) ),
option( TAXONOMIC_RANK.infraclass, t( "Ranks-INFRACLASS" ) ),
option( TAXONOMIC_RANK.subterclass, t( "Ranks-SUBTERCLASS" ) ),
option( TAXONOMIC_RANK.superorder, t( "Ranks-SUPERORDER" ) ),
option( TAXONOMIC_RANK.order, t( "Ranks-ORDER" ) ),
option( TAXONOMIC_RANK.suborder, t( "Ranks-SUBORDER" ) ),
option( TAXONOMIC_RANK.infraorder, t( "Ranks-INFRAORDER" ) ),
option( TAXONOMIC_RANK.parvorder, t( "Ranks-PARVORDER" ) ),
option( TAXONOMIC_RANK.zoosection, t( "Ranks-ZOOSECTION" ) ),
option( TAXONOMIC_RANK.zoosubsection, t( "Ranks-ZOOSUBSECTION" ) ),
option( TAXONOMIC_RANK.superfamily, t( "Ranks-SUPERFAMILY" ) ),
option( TAXONOMIC_RANK.epifamily, t( "Ranks-EPIFAMILY" ) ),
option( TAXONOMIC_RANK.family, t( "Ranks-FAMILY" ) ),
option( TAXONOMIC_RANK.subfamily, t( "Ranks-SUBFAMILY" ) ),
option( TAXONOMIC_RANK.supertribe, t( "Ranks-SUPERTRIBE" ) ),
option( TAXONOMIC_RANK.tribe, t( "Ranks-TRIBE" ) ),
option( TAXONOMIC_RANK.subtribe, t( "Ranks-SUBTRIBE" ) ),
option( TAXONOMIC_RANK.genus, t( "Ranks-GENUS" ) ),
option( TAXONOMIC_RANK.genushybrid, t( "Ranks-GENUSHYBRID" ) ),
option( TAXONOMIC_RANK.subgenus, t( "Ranks-SUBGENUS" ) ),
option( TAXONOMIC_RANK.section, t( "Ranks-SECTION" ) ),
option( TAXONOMIC_RANK.subsection, t( "Ranks-SUBSECTION" ) ),
option( TAXONOMIC_RANK.complex, t( "Ranks-COMPLEX" ) ),
option( TAXONOMIC_RANK.species, t( "Ranks-SPECIES" ) ),
option( TAXONOMIC_RANK.hybrid, t( "Ranks-HYBRID" ) ),
option( TAXONOMIC_RANK.subspecies, t( "Ranks-SUBSPECIES" ) ),
option( TAXONOMIC_RANK.variety, t( "Ranks-VARIETY" ) ),
option( TAXONOMIC_RANK.form, t( "Ranks-FORM" ) ),
option( TAXONOMIC_RANK.infrahybrid, t( "Ranks-INFRAHYBRID" ) ),
] );

export const getDateObservedValues = ( t: TFunction ) => ( {
[DATE_OBSERVED.ALL]: {
label: t( "All" ),
labelCaps: t( "ALL" ),
value: DATE_OBSERVED.ALL,
},
[DATE_OBSERVED.EXACT_DATE]: {
label: t( "Exact-Date" ),
labelCaps: t( "EXACT-DATE" ),
text: t( "Filter-by-observed-on-date" ),
value: DATE_OBSERVED.EXACT_DATE,
},
[DATE_OBSERVED.DATE_RANGE]: {
label: t( "Date-Range" ),
labelCaps: t( "DATE-RANGE" ),
text: t( "Filter-by-observed-between-dates" ),
value: DATE_OBSERVED.DATE_RANGE,
},
[DATE_OBSERVED.MONTHS]: {
label: t( "Months" ),
labelCaps: t( "MONTHS" ),
text: t( "Filter-by-observed-during-months" ),
value: DATE_OBSERVED.MONTHS,
},
} );

export const getDateUploadedValues = ( t: TFunction ) => ( {
[DATE_UPLOADED.ALL]: {
label: t( "All" ),
labelCaps: t( "ALL" ),
value: DATE_UPLOADED.ALL,
},
[DATE_UPLOADED.EXACT_DATE]: {
label: t( "Exact-Date" ),
labelCaps: t( "EXACT-DATE" ),
text: t( "Filter-by-uploaded-on-date" ),
value: DATE_UPLOADED.EXACT_DATE,
},
[DATE_UPLOADED.DATE_RANGE]: {
label: t( "Date-Range" ),
labelCaps: t( "DATE-RANGE" ),
text: t( "Filter-by-uploaded-between-dates" ),
value: DATE_UPLOADED.DATE_RANGE,
},
} );

export const getMonthValues = ( t: TFunction ) => Object.fromEntries( [
option( 1, t( "January" ) ),
option( 2, t( "February" ) ),
option( 3, t( "March" ) ),
option( 4, t( "April" ) ),
option( 5, t( "May" ) ),
option( 6, t( "June" ) ),
option( 7, t( "July" ) ),
option( 8, t( "August" ) ),
option( 9, t( "September" ) ),
option( 10, t( "October" ) ),
option( 11, t( "November" ) ),
option( 12, t( "December" ) ),
] );

export const getMediaValues = ( t: TFunction ) => Object.fromEntries( [
option( MEDIA.ALL, t( "All" ) ),
option( MEDIA.PHOTOS, t( "Photos" ) ),
option( MEDIA.SOUNDS, t( "Sounds" ) ),
option( MEDIA.NONE, t( "No-Media" ) ),
] );

export const getEstablishmentValues = ( t: TFunction ) => Object.fromEntries( [
option( ESTABLISHMENT_MEAN.ANY, t( "Any--establishment-means" ) ),
option( ESTABLISHMENT_MEAN.INTRODUCED, t( "Introduced" ) ),
option( ESTABLISHMENT_MEAN.NATIVE, t( "Native" ) ),
option( ESTABLISHMENT_MEAN.ENDEMIC, t( "Endemic" ) ),
] );

export const getWildValues = ( t: TFunction ) => Object.fromEntries( [
option( WILD_STATUS.ALL, t( "All" ) ),
option( WILD_STATUS.WILD, t( "Wild" ) ),
option( WILD_STATUS.CAPTIVE, t( "Captive-Cultivated" ) ),
] );

export const getReviewedValues = ( t: TFunction ) => Object.fromEntries( [
option( REVIEWED.ALL, t( "All-observations" ) ),
option( REVIEWED.REVIEWED, t( "Reviewed-observations-only" ) ),
option( REVIEWED.UNREVIEWED, t( "Unreviewed-observations-only" ) ),
] );

export const getPhotoLicenseValues = ( t: TFunction ) => Object.fromEntries( [
option( PHOTO_LICENSE.ALL, t( "ALL" ) ),
option( PHOTO_LICENSE.CC0, t( "CC0" ) ),
option( PHOTO_LICENSE.CCBY, t( "CC-BY" ) ),
option( PHOTO_LICENSE.CCBYNC, t( "CC-BY-NC" ) ),
option( PHOTO_LICENSE.CCBYSA, t( "CC-BY-SA" ) ),
option( PHOTO_LICENSE.CCBYND, t( "CC-BY-ND" ) ),
option( PHOTO_LICENSE.CCBYNCSA, t( "CC-BY-NC-SA" ) ),
option( PHOTO_LICENSE.CCBYNCND, t( "CC-BY-NC-ND" ) ),
] );
Loading
Loading