diff --git a/.github/workflows/e2e_android.yml b/.github/workflows/e2e_android.yml index eea994f2c..59f3fd4e2 100644 --- a/.github/workflows/e2e_android.yml +++ b/.github/workflows/e2e_android.yml @@ -13,7 +13,22 @@ concurrency: cancel-in-progress: true jobs: + checksecret: + name: check for secrets presence + runs-on: ubuntu-latest + outputs: + is_SECRETS_PRESENT_set: ${{ steps.checksecret_job.outputs.is_SECRETS_PRESENT_set }} + steps: + - name: Check whether secrets are present in the action runner + id: checksecret_job + env: + SECRETS_PRESENT: ${{ secrets.OAUTH_CLIENT_SECRET }} + run: | + echo "is_SECRETS_PRESENT_set: ${{ env.SECRETS_PRESENT != '' }}" + echo "::set-output name=is_SECRETS_PRESENT_set::${{ env.SECRETS_PRESENT != '' }}" build: + needs: checksecret + if: needs.checksecret.outputs.is_SECRETS_PRESENT_set == 'true' # 4-core Ubunutu GitHub Larger Runner # https://docs.github.com/en/enterprise-cloud@latest/billing/reference/actions-runner-pricing#x64-powered-larger-runners runs-on: ubuntu-24.04-m diff --git a/.github/workflows/e2e_ios.yml b/.github/workflows/e2e_ios.yml index eea480673..2bc0b5dd2 100644 --- a/.github/workflows/e2e_ios.yml +++ b/.github/workflows/e2e_ios.yml @@ -15,12 +15,12 @@ concurrency: jobs: checksecret: - name: check for oauth client + name: check for secrets presence runs-on: macos-26 outputs: is_SECRETS_PRESENT_set: ${{ steps.checksecret_job.outputs.is_SECRETS_PRESENT_set }} steps: - - name: Check whether unity activation requests should be done + - name: Check whether secrets are present in the action runner id: checksecret_job env: SECRETS_PRESENT: ${{ secrets.OAUTH_CLIENT_SECRET }} diff --git a/android/app/src/main/assets/fonts/INatIcon.ttf b/android/app/src/main/assets/fonts/INatIcon.ttf index 68c4f0506..5f4b7ad3f 100644 Binary files a/android/app/src/main/assets/fonts/INatIcon.ttf and b/android/app/src/main/assets/fonts/INatIcon.ttf differ diff --git a/android/link-assets-manifest.json b/android/link-assets-manifest.json index 34a4c6db9..39e30b83d 100644 --- a/android/link-assets-manifest.json +++ b/android/link-assets-manifest.json @@ -3,7 +3,7 @@ "data": [ { "path": "assets/fonts/INatIcon.ttf", - "sha1": "89aa6f39308fa5bad4edbdd509983df51f06f30b" + "sha1": "35ca4fba9e8a1542c0ee4a06d4b5035627d92cbb" }, { "path": "assets/fonts/Lato-Bold.ttf", diff --git a/ios/iNaturalistReactNative.xcodeproj/project.pbxproj b/ios/iNaturalistReactNative.xcodeproj/project.pbxproj index 149c6398d..328e220c0 100644 --- a/ios/iNaturalistReactNative.xcodeproj/project.pbxproj +++ b/ios/iNaturalistReactNative.xcodeproj/project.pbxproj @@ -31,9 +31,9 @@ 8F346E4A2CF6912700CED7B4 /* geomodel.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = 8F346E492CF6912700CED7B4 /* geomodel.mlmodel */; }; 8FE171A22E97F0780003E759 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8FE171A12E97F0780003E759 /* GoogleService-Info.plist */; }; 8FF73F732F69C8CE007CF4F0 /* AutoContinueShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF73F722F69C8CE007CF4F0 /* AutoContinueShareViewController.swift */; }; - 99CC9E26994B44F7B52A1B5D /* INatIcon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8B9F9C9225124178BD456CA4 /* INatIcon.ttf */; }; AE4DC81B3A87484CB3FD6750 /* Lato-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4B0AEEF6CA584BCF9880EB35 /* Lato-Regular.ttf */; }; E5DFC1C6FBFA45739CE91C69 /* Lato-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 69DF855D92EA4ADFB73B47F1 /* Lato-MediumItalic.ttf */; }; + D224B9770DAC4D9098E09BFF /* INatIcon.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 54EEE7CC3E4645E4B9CE1AA8 /* INatIcon.ttf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -92,7 +92,6 @@ 8B65ED3A29F575FE0054CCEF /* ShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ShareViewController.swift"; sourceTree = ""; }; 8B65ED3C29F576D00054CCEF /* iNaturalistReactNative-ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "iNaturalistReactNative-ShareExtension.entitlements"; sourceTree = ""; }; 8B8BAD0429F54EB300CE5C9F /* iNaturalistReactNative.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = iNaturalistReactNative.entitlements; path = iNaturalistReactNative/iNaturalistReactNative.entitlements; sourceTree = ""; }; - 8B9F9C9225124178BD456CA4 /* INatIcon.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = INatIcon.ttf; path = ../assets/fonts/INatIcon.ttf; sourceTree = ""; }; 8C2D97D72EED451C887998A8 /* Lato-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Lato-BoldItalic.ttf"; path = "../assets/fonts/Lato-BoldItalic.ttf"; sourceTree = ""; }; 8F1AC6762BC1B610002F994B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 8F346E492CF6912700CED7B4 /* geomodel.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = geomodel.mlmodel; sourceTree = ""; }; @@ -100,6 +99,7 @@ 8FF73F722F69C8CE007CF4F0 /* AutoContinueShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AutoContinueShareViewController.swift; path = "../node_modules/react-native-share-menu/ios/AutoContinueShareViewController.swift"; sourceTree = SOURCE_ROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; F15C1390617A309CE0A194B2 /* Pods_iNaturalistReactNative_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iNaturalistReactNative_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 54EEE7CC3E4645E4B9CE1AA8 /* INatIcon.ttf */ = {isa = PBXFileReference; name = "INatIcon.ttf"; path = "../assets/fonts/INatIcon.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -229,7 +229,7 @@ 3A9BAF07FCF24F668E2EF5AB /* Lato-Medium.ttf */, 69DF855D92EA4ADFB73B47F1 /* Lato-MediumItalic.ttf */, 4B0AEEF6CA584BCF9880EB35 /* Lato-Regular.ttf */, - 8B9F9C9225124178BD456CA4 /* INatIcon.ttf */, + 54EEE7CC3E4645E4B9CE1AA8 /* INatIcon.ttf */, ); name = Resources; sourceTree = ""; @@ -338,7 +338,7 @@ E5DFC1C6FBFA45739CE91C69 /* Lato-MediumItalic.ttf in Resources */, AE4DC81B3A87484CB3FD6750 /* Lato-Regular.ttf in Resources */, 716F7BDCD8B943479083CFAC /* Settings.bundle in Resources */, - 99CC9E26994B44F7B52A1B5D /* INatIcon.ttf in Resources */, + D224B9770DAC4D9098E09BFF /* INatIcon.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/link-assets-manifest.json b/ios/link-assets-manifest.json index 34a4c6db9..39e30b83d 100644 --- a/ios/link-assets-manifest.json +++ b/ios/link-assets-manifest.json @@ -3,7 +3,7 @@ "data": [ { "path": "assets/fonts/INatIcon.ttf", - "sha1": "89aa6f39308fa5bad4edbdd509983df51f06f30b" + "sha1": "35ca4fba9e8a1542c0ee4a06d4b5035627d92cbb" }, { "path": "assets/fonts/Lato-Bold.ttf", diff --git a/package-lock.json b/package-lock.json index 504082295..82b969b4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23153,9 +23153,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz", + "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==", "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", diff --git a/src/api/posts.ts b/src/api/posts.ts index 713d036c2..84ecf505a 100644 --- a/src/api/posts.ts +++ b/src/api/posts.ts @@ -1,27 +1,34 @@ import type { ErrorWithResponse, INatApiError } from "api/error"; import handleError from "api/error"; +import type { + ApiDefaultResult, ApiGetByIdParams, ApiOpts, ApiParams, ApiResponse, +} from "api/types"; import inatjs from "inaturalistjs"; -const fetchUserPosts = async ( - params: Record = {}, - opts: Record = {}, -): Promise | null | ErrorWithResponse | INatApiError> => { +const fetchBlogPosts = async ( + params: ApiParams = {}, + opts: ApiOpts = {}, +): Promise | null | ErrorWithResponse | INatApiError> => { try { - return await inatjs.posts.for_user( params, opts ); + const response = await inatjs.posts.for_user( params, opts ); + if ( !response ) { return null; } + return response; } catch ( e ) { return handleError( e as ErrorWithResponse, - { context: { functionName: "fetchUserPosts", opts } }, + { context: { functionName: "fetchBlogPosts", opts } }, ); } }; -const fetchProjectPosts = async ( - params: Record = {}, - opts: Record = {}, -): Promise | null | ErrorWithResponse | INatApiError> => { +const fetchProjectPosts = async ( + params: ApiGetByIdParams, + opts: ApiOpts = {}, +): Promise | null | ErrorWithResponse | INatApiError> => { try { - return await inatjs.projects.posts( params, opts ); + const response = await inatjs.projects.posts( params, opts ); + if ( !response ) { return null; } + return response; } catch ( e ) { return handleError( e as ErrorWithResponse, @@ -30,7 +37,24 @@ const fetchProjectPosts = async ( } }; +const fetchUserPosts = async ( + params: ApiGetByIdParams, + opts: ApiOpts = {}, +): Promise | null | ErrorWithResponse | INatApiError> => { + try { + const response = await inatjs.users.posts( params, opts ); + if ( !response ) { return null; } + return response; + } catch ( e ) { + return handleError( + e as ErrorWithResponse, + { context: { functionName: "fetchUserPosts", opts } }, + ); + } +}; + export { + fetchBlogPosts, fetchProjectPosts, fetchUserPosts, }; diff --git a/src/api/types.d.ts b/src/api/types.d.ts index 94e388fef..77404e9e5 100644 --- a/src/api/types.d.ts +++ b/src/api/types.d.ts @@ -14,6 +14,10 @@ export interface ApiParams { ttl?: number; } +export interface ApiGetByIdParams extends ApiParams { + id: number; +} + export interface ApiPlace { id?: number; name?: string; @@ -21,7 +25,7 @@ export interface ApiPlace { place_type?: number | null; } -export interface ApiPostForProject { +export interface ApiPost { body: string; id: number; published_at: string; @@ -29,7 +33,7 @@ export interface ApiPostForProject { } // When using POST_FOR_USER_FIELDS -export interface ApiPostForUser extends ApiPostForProject { +export interface ApiPostForUser extends ApiPost { parent: { id: number; icon_url: string | null; @@ -293,4 +297,5 @@ export interface ApiObservationsSearchParams extends ApiParams { typeof ORDER_BY_UPDATED_AT | typeof ORDER_BY_VOTES; return_bounds?: boolean; + unobserved_by_user_id?: number; } diff --git a/src/api/usersTyped.ts b/src/api/usersTyped.ts index aa0cde902..fb93b7936 100644 --- a/src/api/usersTyped.ts +++ b/src/api/usersTyped.ts @@ -1,17 +1,12 @@ +import type { ErrorWithResponse, INatApiError } from "api/error"; +import handleError from "api/error"; +import type { + ApiDefaultResult, ApiGetByIdParams, ApiOpts, ApiResponse, +} from "api/types"; import inatjs from "inaturalistjs"; -import type { ErrorWithResponse, INatApiError } from "./error"; -import handleError from "./error"; -import type { - ApiDefaultResult, ApiOpts, ApiParams, ApiResponse, -} from "./types"; - -interface UsersProjectsParams extends ApiParams { - id: number; -} - const fetchUserProjects = async ( - params: UsersProjectsParams, + params: ApiGetByIdParams, opts: ApiOpts = {}, ): Promise | null | ErrorWithResponse | INatApiError> => { try { diff --git a/src/components/AddToProjects/AddToProjects.tsx b/src/components/AddToProjects/AddToProjects.tsx index 3ace94731..1ba2bc718 100644 --- a/src/components/AddToProjects/AddToProjects.tsx +++ b/src/components/AddToProjects/AddToProjects.tsx @@ -16,7 +16,9 @@ import React, { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import type { ListRenderItem } from "react-native"; import Project from "realmModels/Project"; -import type { RealmProject, RealmProjectObservation } from "realmModels/types"; +import type { RealmProject } from "realmModels/types"; +import validateProjectFieldsForObservation from "sharedHelpers/validateProjectFieldsForObservation"; +import type { ObservationFlowSlice } from "stores/createObservationFlowSlice"; import useStore from "stores/useStore"; import { getShadow } from "styles/global"; import colors from "styles/tailwindColors"; @@ -46,9 +48,10 @@ const AddToProjects = ( ) => { }, [], ); - const projectObservations = useStore( - state => state.currentObservation?.projectObservations, + const currentObservation = useStore( + ( state: ObservationFlowSlice ) => state.currentObservation, ); + const [selectedProjectIds, setSelectedProjectIds] = useState( () => new Set( ) ); const joinedProjects = useMemo( @@ -56,6 +59,13 @@ const AddToProjects = ( ) => { [joinedProjectsCollection], ); + const validationResult = useMemo( + () => { + const selectedProjects = joinedProjects.filter( jp => selectedProjectIds.has( jp.id ) ); + return validateProjectFieldsForObservation( currentObservation, selectedProjects ); + }, + [currentObservation, joinedProjects, selectedProjectIds], + ); const listHeaderComponent = useMemo( ( ) => ( @@ -116,13 +126,11 @@ const AddToProjects = ( ) => { const onSave = ( ) => { navigation.goBack( ); }; - const disabled = false; const renderExpanded = useCallback( - ( item: RealmProject ) => ( + ( item: RealmProject, projectValid: boolean ) => ( - {/* TODO: MOB-1499 this will be based on the result of a validation function */} - {Math.random() > 0.5 + {!projectValid ? ( { error.obsFieldId === pof.obsField?.id, + )} /> ) )} ), - [t], + [validationResult, t], ); const renderRightIcon = useCallback( - ( item: RealmProject, isSelected: boolean ) => { - // Logic if all required fields have been filled out will live in zustand - if ( - projectObservations?.some( - ( po: RealmProjectObservation ) => po.projectId === item.id, - ) - ) { - return ( - - ); - } + ( isSelected: boolean, projectValid: boolean ) => { if ( isSelected ) { + if ( projectValid ) { + return ( + + ); + } return ( { } return ; }, - [projectObservations], + [], ); const renderProject: ListRenderItem = useCallback( ( { item } ) => { const isSelected = selectedProjectIds.has( item.id ); const canExpand = item.projectObservationFields.length > 0; + const projectValid = !validationResult.errors.some( + error => error.projectId === item?.id, + ); return ( @@ -205,13 +212,13 @@ const AddToProjects = ( ) => { - {renderRightIcon( item, isSelected )} + {renderRightIcon( isSelected, projectValid )} - {canExpand && isSelected && renderExpanded( item )} + {canExpand && isSelected && renderExpanded( item, projectValid )} ); }, - [renderExpanded, renderRightIcon, selectedProjectIds, toggleProject], + [renderExpanded, renderRightIcon, selectedProjectIds, toggleProject, validationResult], ); return ( @@ -237,7 +244,7 @@ const AddToProjects = ( ) => { text={t( "SAVE" )} onPress={onSave} level="neutral" - disabled={disabled} + disabled={!validationResult.valid} /> diff --git a/src/components/Explore/ExploreContainer.js b/src/components/Explore/ExploreContainer.js index 0f932a3af..a898eb005 100644 --- a/src/components/Explore/ExploreContainer.js +++ b/src/components/Explore/ExploreContainer.js @@ -12,13 +12,11 @@ import { } from "providers/ExploreContext"; import type { Node } from "react"; import React, { useCallback, useEffect, useState } from "react"; -import { useCurrentUser, useFeatureFlag } from "sharedHooks"; +import { useCurrentUser } from "sharedHooks"; import useLocationPermission from "sharedHooks/useLocationPermission"; -import { FeatureFlag } from "stores/createFeatureFlagSlice"; import useStore from "stores/useStore"; import Explore from "./Explore"; -import ExploreV2 from "./ExploreV2"; import mapParamsToAPI from "./helpers/mapParamsToAPI"; import useExploreHeaderCount from "./hooks/useExploreHeaderCount"; import useParams from "./hooks/useParams"; @@ -26,7 +24,6 @@ import useParams from "./hooks/useParams"; const ExploreContainerWithContext = ( ): Node => { const navigation = useNavigation( ); const { isConnected } = useNetInfo( ); - const exploreV2Enabled = useFeatureFlag( FeatureFlag.ExploreV2Enabled ); const exploreView = useStore( state => state.exploreView ); const setExploreView = useStore( state => state.setExploreView ); @@ -137,48 +134,32 @@ const ExploreContainerWithContext = ( ): Node => { return ( <> - {!exploreV2Enabled - ? ( - dispatch( { type: EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN } ) - } - isConnected={isConnected} - isFetchingHeaderCount={isFetchingHeaderCount} - openFiltersModal={openFiltersModal} - queryParams={queryParams} - showFiltersModal={showFiltersModal} - updateTaxon={taxon => dispatch( { type: EXPLORE_ACTION.CHANGE_TAXON, taxon } )} - updateLocation={updateLocation} - updateUser={updateUser} - updateProject={updateProject} - placeMode={state.placeMode} - hasLocationPermissions={hasLocationPermissions} - renderLocationPermissionsGate={renderPermissionsGate} - requestLocationPermissions={requestLocationPermissions} - startFetching={startFetching} - /> - ) - : ( - - )} + dispatch( { type: EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN } ) + } + isConnected={isConnected} + isFetchingHeaderCount={isFetchingHeaderCount} + openFiltersModal={openFiltersModal} + queryParams={queryParams} + showFiltersModal={showFiltersModal} + updateTaxon={taxon => dispatch( { type: EXPLORE_ACTION.CHANGE_TAXON, taxon } )} + updateLocation={updateLocation} + updateUser={updateUser} + updateProject={updateProject} + placeMode={state.placeMode} + hasLocationPermissions={hasLocationPermissions} + renderLocationPermissionsGate={renderPermissionsGate} + requestLocationPermissions={requestLocationPermissions} + startFetching={startFetching} + /> {renderPermissionsGate( { onPermissionGranted: startFetching, } ) } diff --git a/src/components/Explore/ExploreFiltersContainer.tsx b/src/components/Explore/ExploreFiltersContainer.tsx deleted file mode 100644 index 36449523a..000000000 --- a/src/components/Explore/ExploreFiltersContainer.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import type { ApiProject } from "api/types"; -import { - ExploreProvider, -} from "providers/ExploreContext"; -import React from "react"; - -import FilterModalV2 from "./Modals/FilterModalV2"; - -const ExploreFiltersContainerWithContext = () => { - const navigation = useNavigation(); - - const closeModal = () => { - navigation.goBack(); - }; - - const filterByIconicTaxonUnknown = () => { - console.log( " Not implemented in ExploreV2 yet" ); - }; - - const updateTaxon = ( - taxon: { - name: string; - } | null, - ) => { - console.log( " Not implemented in ExploreV2 yet", taxon ); - }; - - const updateUser = ( - user: { - login: string; - } | null, - ) => { - console.log( " Not implemented in ExploreV2 yet", user ); - }; - - const updateProject = ( project: ApiProject ) => { - console.log( " Not implemented in ExploreV2 yet", project ); - }; - - return ( - - ); -}; - -const ExploreFiltersContainer = () => ( - - - -); - -export default ExploreFiltersContainer; diff --git a/src/components/Explore/ExploreSearchContainer.tsx b/src/components/Explore/ExploreSearchContainer.tsx deleted file mode 100644 index 9dfaa6023..000000000 --- a/src/components/Explore/ExploreSearchContainer.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { useNavigation, useRoute } from "@react-navigation/native"; -import type { ApiPlace, ApiProject, ApiTaxon } from "api/types"; -import { View } from "components/styledComponents"; -import type { TabStackScreenProps } from "navigation/types"; -import { - ExploreProvider, -} from "providers/ExploreContext"; -import React from "react"; -import type { RealmTaxon } from "realmModels/types"; -import { useLocationPermission } from "sharedHooks"; - -import ExploreLocationSearch from "./SearchScreens/ExploreLocationSearch"; -import ExploreProjectSearch from "./SearchScreens/ExploreProjectSearch"; -import ExploreTaxonSearch from "./SearchScreens/ExploreTaxonSearch"; -import ExploreUserSearch from "./SearchScreens/ExploreUserSearch"; - -const ExploreSearchContainerWithContext = () => { - const navigation = useNavigation["navigation"]>(); - const { params } = useRoute["route"]>(); - - const { - hasPermissions, - renderPermissionsGate, - requestPermissions, - } = useLocationPermission( ); - - const initialSearchMode = params?.initialSearchMode || "none"; - - const closeModal = () => { - navigation.goBack(); - }; - - const updateTaxon = ( - taxon: { - name: string; - } | null, - ) => { - console.log( "Not implemented in ExploreV2 yet.", taxon ); - }; - - const updateLocation = ( location: "worldwide" | ApiPlace ) => { - console.log( "Not implemented in ExploreV2 yet.", location ); - }; - - const updateUser = ( user: null | { login: string } ) => { - console.log( "Not implemented in ExploreV2 yet.", user ); - }; - const updateProject = ( project: null | ApiProject ) => { - console.log( "Not implemented in ExploreV2 yet.", project ); - }; - - if ( initialSearchMode === "taxon" ) { - return ( - { - navigation.push( "TaxonDetails", { id: taxon.id } ); - }} - updateTaxon={updateTaxon} - /> - ); - } - - if ( initialSearchMode === "location" ) { - return ( - - ); - } - - if ( initialSearchMode === "users" ) { - return ( - - ); - } - - if ( initialSearchMode === "projects" ) { - return ( - - ); - } - - return ( - - {renderPermissionsGate( {} )} - - ); -}; - -const ExploreSearchContainer = () => ( - - - -); - -export default ExploreSearchContainer; diff --git a/src/components/Explore/ExploreV2.tsx b/src/components/Explore/ExploreV2.tsx deleted file mode 100644 index faa7a844f..000000000 --- a/src/components/Explore/ExploreV2.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import { refresh } from "@react-native-community/netinfo"; -import { useNavigation } from "@react-navigation/native"; -import classnames from "classnames"; -import { - Body2, - Button, - INatIconButton, - OfflineNotice, - ViewWrapper, -} from "components/SharedComponents"; -import { Pressable, View } from "components/styledComponents"; -import { PLACE_MODE } from "providers/ExploreContext"; -import React from "react"; -import { Alert } from "react-native"; -import { - useDebugMode, - useStoredLayout, - useTranslation, -} from "sharedHooks"; -import type { RenderLocationPermissionsGateFunction } from "sharedHooks/useLocationPermission"; -import { getShadow } from "styles/global"; - -import IdentifiersView from "./IdentifiersView"; -import ObservationsView from "./ObservationsView"; -import ObservationsViewBar from "./ObservationsViewBar"; -import ObserversView from "./ObserversView"; -import SpeciesView from "./SpeciesView"; - -const DROP_SHADOW = getShadow( { - offsetHeight: 4, - elevation: 6, -} ); - -enum EXPLORE_VIEW { - OBSERVATIONS = "observations", - IDENTIFIERS = "identifiers", - OBSERVERS = "observers", - SPECIES = "species" -} - -enum EXPLORE_OBSERVATIONS_LAYOUT { - GRID = "grid", - LIST = "list", - MAP = "map" -} - -interface Props { - canFetch?: boolean; - currentExploreView: EXPLORE_VIEW; - handleUpdateCount: ( exploreView: EXPLORE_VIEW, totalResults: number ) => void; - hasLocationPermissions?: boolean; - isConnected: boolean; - placeMode: string; - queryParams: object; - renderLocationPermissionsGate: RenderLocationPermissionsGateFunction; - requestLocationPermissions: ( ) => void; -} - -const ExploreV2 = ( { - canFetch, - currentExploreView, - handleUpdateCount, - hasLocationPermissions, - isConnected, - placeMode, - queryParams, - renderLocationPermissionsGate, - requestLocationPermissions, -}: Props ) => { - const navigation = useNavigation(); - const { t } = useTranslation( ); - const { layout, writeLayoutToStorage } = useStoredLayout( "exploreObservationsLayout" ) as { - layout: EXPLORE_OBSERVATIONS_LAYOUT | null; - writeLayoutToStorage: ( newValue: EXPLORE_OBSERVATIONS_LAYOUT ) => void; - }; - const { isDebug } = useDebugMode( ); - - const renderMainContent = ( ) => { - if ( isConnected === false ) { - return ( - refresh()} - /> - ); - } - // hasLocationPermissions === undefined means we haven't checked for location permissions yet - if ( placeMode === PLACE_MODE.NEARBY && hasLocationPermissions === false ) { - return ( - - - {t( "To-view-nearby-organisms-please-enable-location" )} - -