Skip to content

Commit

Permalink
Revert "Merge branch 'v2.17.1' into v2.17.0a"
Browse files Browse the repository at this point in the history
This reverts commit 10582c9, reversing
changes made to c0719d6.
  • Loading branch information
jtklein committed Jan 8, 2025
1 parent 10582c9 commit d324caa
Show file tree
Hide file tree
Showing 100 changed files with 1,883 additions and 1,032 deletions.
9 changes: 4 additions & 5 deletions .detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ module.exports = {
config: "e2e/jest.config.js",
},
jest: {
setupTimeout: 900000,
teardownTimeout: 900000,
setupTimeout: 120000,
},
},
apps: {
Expand All @@ -28,16 +27,16 @@ module.exports = {
simulator: {
type: "ios.simulator",
device: {
type: "iPhone 15 Pro",
type: "iPhone 14 Plus",
},
},
},
configurations: {
"ios.debug": {
"ios.sim.debug": {
device: "simulator",
app: "ios.debug",
},
"ios.release": {
"ios.sim.release": {
device: "simulator",
app: "ios.release",
},
Expand Down
3 changes: 3 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ labels: ''
assignees: ''

---
**Is this an AR camera feature?**
If so, please head to [react-native-inat-camera](https://github.com/inaturalist/react-native-inat-camera) library and fill out a feature request there.

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
2. Build locally to a device or simulator by running `npm run ios` or `npm run android`

## Manual Linking
Most third-party libraries use autolinking as of [React Native 0.60.0](https://facebook.github.io/react-native/blog/2019/07/03/version-60#native-modules-are-now-autolinked). Any exceptions are listed in the `react-native.config.js` file.
Most third-party libraries use autolinking as of [React Native 0.60.0](https://facebook.github.io/react-native/blog/2019/07/03/version-60#native-modules-are-now-autolinked). Any exceptions are listed in the `react-native.config.js` file. Currently, [react-native-inat-camera](https://github.com/inaturalist/react-native-inat-camera) on Android is manually linked.

## Tests
We currently have three kinds of tests:
Expand Down
2 changes: 2 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ android {
}

dependencies {
implementation project(':react-native-inat-camera')

// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation("com.facebook.react:flipper-integration")
Expand Down
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- the following permissions replaces READ_EXTERNAL_STORAGE in Android 13 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

<application
android:name="org.inaturalist.seek.MainApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.flipper.ReactNativeFlipper
import com.facebook.soloader.SoLoader

import org.inaturalist.inatcamera.nativecamera.INatCameraViewPackage

class MainApplication : Application(), ReactApplication {

override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
add(INatCameraViewPackage())
}

override fun getJSMainModuleName(): String = "index"
Expand Down
2 changes: 2 additions & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
rootProject.name = 'Seek'
include ':@react-native-camera-roll_camera-roll'
project(':@react-native-camera-roll_camera-roll').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-camera-roll/camera-roll/android')
include ':react-native-inat-camera'
project(':react-native-inat-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-inat-camera/android')
include ':react-native-check-app-install'
project(':react-native-check-app-install').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-check-app-install/android')
include ':RNSendIntentModule', ':app'
Expand Down
3 changes: 1 addition & 2 deletions assets/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ const icons: Icons = {
iNat_valueprop_bullet_2: require( "./icons/icon-cv-green.webp" ),
iNat_valueprop_bullet_3: require( "./icons/icon-obs-green.webp" ),
iNat_valueprop_bullet_4: require( "./icons/icon-person-green.webp" ),
noProfilePhoto: require( "./icons/img-inatlogin-nophoto.webp" ),
gallery: require( "./icons/icon-gallery.webp" )
noProfilePhoto: require( "./icons/img-inatlogin-nophoto.webp" )
};

export default icons;
Binary file removed assets/icons/icon-gallery.webp
Binary file not shown.
Binary file removed assets/icons/[email protected]
Binary file not shown.
Binary file removed assets/icons/[email protected]
Binary file not shown.
2 changes: 1 addition & 1 deletion components/About/AboutScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const AboutScreen = (): Node => {
<Image source={logos.iNat} />
<View style={viewStyles.marginSmall} />
<StyledText style={[baseTextStyles.bodyBold, textStyles.boldText]}>{i18n.t( "about.seek_designed_by" )}</StyledText>
<StyledText style={[baseTextStyles.body, textStyles.text]}>{i18n.t( "about.inat_team_credits_5" )}</StyledText>
<StyledText style={[baseTextStyles.body, textStyles.text]}>{i18n.t( "about.inat_team_credits_4" )}</StyledText>
<View style={viewStyles.marginSmall} />
<StyledText style={[baseTextStyles.body, textStyles.text]}>{i18n.t( "about.support_from" )}</StyledText>
<View style={viewStyles.block} />
Expand Down
214 changes: 214 additions & 0 deletions components/Camera/ARCamera/ARCamera.e2e-mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// @flow

import React, {
useReducer,
useEffect,
useRef,
useCallback,
useContext
} from "react";
import {
Image,
TouchableOpacity,
View,
Platform
} from "react-native";
import { CameraRoll } from "@react-native-camera-roll/camera-roll";
import { useNavigation } from "@react-navigation/native";
import type { Node } from "react";
import RNFS from "react-native-fs";

import i18n from "../../../i18n";
import icons from "../../../assets/icons";
import {
showCameraSaveFailureAlert
} from "../../../utility/cameraHelpers";
import { checkCameraPermissions } from "../../../utility/androidHelpers.android";
import { createTimestamp } from "../../../utility/dateHelpers";
import { fetchImageLocationOrErrorCode } from "../../../utility/resultsHelpers";
import { useObservation } from "../../Providers/ObservationProvider";
import { UserContext } from "../../UserContext";

const useVisionCamera = Platform.OS === "android";

const ARCamera = ( ): Node => {
const navigation = useNavigation( );
const camera = useRef<any>( null );
const { setObservation, observation } = useObservation();

// determines whether or not to fetch untruncated coords or precise coords for posting to iNat
const { login } = useContext( UserContext );

// eslint-disable-next-line no-shadow
const [state, dispatch] = useReducer( ( state, action ) => {
switch ( action.type ) {
case "RESET_RANKS":
return { ...state, ranks: {} };
case "SET_RANKS":
return { ...state, ranks: action.ranks };
case "PHOTO_TAKEN":
return { ...state, pictureTaken: true };
case "RESET_STATE":
return {
...state,
pictureTaken: false,
error: null,
ranks: {}
};
case "FILTER_TAXON":
return {
...state,
pictureTaken: false,
error: null,
ranks: {}
};
case "ERROR":
return { ...state, error: action.error };
default:
throw new Error( );
}
}, {
ranks: {},
error: null,
pictureTaken: false
} );

const {
error,
pictureTaken
} = state;

const updateError = useCallback( ( err, errEvent?: string ) => {
console.log( "updateError" );
// don't update error on first camera load
if ( err === null && error === null ) {
return;
}
dispatch( { type: "ERROR", error: err, errorEvent: errEvent } );
}, [error] );

const navigateToResults = useCallback( async ( uri, predictions ) => {
const userImage = {
time: createTimestamp( ), // add current time to AR camera photos
uri,
predictions
};

// AR camera photos don't come with a location
// especially when user has location permissions off
// this is also needed for ancestor screen, species nearby
const { image, errorCode } = await fetchImageLocationOrErrorCode( userImage, login );
image.errorCode = errorCode;
image.arCamera = true;
setObservation( { image } );
}, [setObservation, login] );

useEffect( ( ) => {
if ( observation && observation.taxon && observation.image.arCamera && pictureTaken ) {
navigation.navigate( "Drawer", {
screen: "Match"
} );
}
}, [observation, navigation, pictureTaken] );

const handleCameraRollSaveError = useCallback( async ( uri, predictions, e ) => {
// react-native-cameraroll does not yet have granular detail about read vs. write permissions
// but there's a pull request for it as of March 2021

await showCameraSaveFailureAlert( e, uri );
navigateToResults( uri, predictions );
}, [navigateToResults] );

const savePhoto = useCallback( async ( photo: { uri: string, predictions: Array<Object> } ) => {
console.log( "savePhoto" );
console.log( "photo.uri", photo.uri );
CameraRoll.save( photo.uri, { type: "photo", album: "Seek" } )
.then( uri => navigateToResults( uri, photo.predictions ) )
.catch( e => handleCameraRollSaveError( photo.uri, photo.predictions, e ) );
}, [handleCameraRollSaveError, navigateToResults] );

const resetState = ( ) => dispatch( { type: "RESET_STATE" } );

const requestAndroidPermissions = useCallback( ( ) => {
if ( Platform.OS === "android" ) {
checkCameraPermissions( ).then( ( result ) => {
if ( result === "permissions" ) {
updateError( "permissions" );
}
updateError( null );
} ).catch( e => console.log( e, "couldn't get camera permissions" ) );
}
}, [updateError] );

useEffect( ( ) => {
navigation.addListener( "focus", ( ) => {
setObservation( null );
// reset when camera loads, not when leaving page, for quicker transition
resetState( );
requestAndroidPermissions( );
} );
}, [navigation, requestAndroidPermissions, setObservation] );

const takePicture = useCallback( async () => {
dispatch( { type: "PHOTO_TAKEN" } );

if ( Platform.OS === "ios" ) {
CameraRoll.getPhotos( {
first: 20,
assetType: "Photos"
} )
.then( async ( r ) => {
console.log( "r.edges", r.edges );
const testPhoto = r.edges[r.edges.length - 1].node.image;
console.log( "testPhoto", testPhoto );
let oldUri = testPhoto.uri;
if ( testPhoto.uri.includes( "ph://" ) ) {
let id = testPhoto.uri.replace( "ph://", "" );
id = id.substring( 0, id.indexOf( "/" ) );
oldUri = `assets-library://asset/asset.jpg?id=${id}&ext=jpg`;
console.log( `Converted file uri to ${oldUri}` );
}
const encodedUri = encodeURI( oldUri );
const destPath = `${RNFS.TemporaryDirectoryPath}temp.jpg`;
const newUri = await RNFS.copyAssetsFileIOS( encodedUri, destPath, 0, 0 );
console.log( "newUri", newUri );
const photo = { uri: newUri, predictions: [] };
if ( typeof photo !== "object" ) {
updateError( "photoError", photo );
} else {
savePhoto( photo );
}
} )
.catch( ( err ) => {
updateError( "take", err );
} );
} else if ( Platform.OS === "android" ) {
if ( camera.current ) {
if ( useVisionCamera ) {
// TODO: mock vision camera with frame processor takePhoto() method
} else {
// TODO: mock react-native-inat-camera takePictureAsync() method
}
}
}
}, [
savePhoto,
updateError
] );

return (
<View style={{ flex: 1, padding: 40, justifyContent: "center", alignItems: "center" }}>
<TouchableOpacity
accessibilityLabel={i18n.t( "accessibility.take_photo" )}
accessible
testID="takePhotoButton"
onPress={takePicture}
disabled={pictureTaken}
>
<Image source={icons.arCameraButton} />
</TouchableOpacity>
</View>
);
};

export default ARCamera;
6 changes: 1 addition & 5 deletions components/Camera/ARCamera/ARCamera.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ import {
rotatePhotoPatch,
rotationTempPhotoPatch
} from "../../../utility/visionCameraPatches";
import {
checkCameraPermissions,
checkSavePermissions
} from "../../../utility/androidHelpers.android";
import { checkCameraPermissions, checkSavePermissions } from "../../../utility/androidHelpers.android";
import { savePostingSuccess } from "../../../utility/loginHelpers";
import { dirModel, dirTaxonomy } from "../../../utility/dirStorage";
import { createTimestamp } from "../../../utility/dateHelpers";
Expand Down Expand Up @@ -460,7 +457,6 @@ const ARCamera = ( ): Node => {
takePicture={takePicture}
cameraLoaded={cameraLoaded.value}
filterByTaxonId={filterByTaxonId}
setIsActive={setIsActive}
/>
)
}
Expand Down
20 changes: 13 additions & 7 deletions components/Camera/ARCamera/ARCameraHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,27 @@ interface Prediction {
}

interface Props {
ranks: {
[key: string]: {
taxon_id: number;
name: string;
}[];
};
prediction: Prediction;
}

const ARCameraHeader = ( { prediction }: Props ) => {
const ARCameraHeader = ( { ranks, prediction }: Props ) => {
const { isLandscape } = useAppOrientation( );
const rankToRender = prediction?.rank || null;
const rankToRender = ranks ? Object.keys( ranks )[0] || null : prediction?.rank || null;
const [commonName, setCommonName] = useState<string | void | null>( null );
const settings = useFetchUserSettings( );
const scientificNames = settings?.scientificNames;
const showScientificName = scientificNames || !commonName;

let id: number | null = null;
let id = null;

if ( rankToRender && !scientificNames ) {
id = prediction?.taxon_id;
id = ranks ? ranks[rankToRender][0].taxon_id : prediction?.taxon_id;
} else {
id = null;
}
Expand Down Expand Up @@ -101,11 +107,11 @@ const ARCameraHeader = ( { prediction }: Props ) => {
return null;
};

const scientificName = prediction?.name;
const scientificName = ranks ? ranks[rankToRender][0].name : prediction?.name;
return (
<View style={viewStyles.header}>
{( prediction && rankToRender ) && (
<View testID="headerPrediction" style={setTaxonomicRankBubbleColor( )}>
{( ( ranks || prediction ) && rankToRender ) && (
<View style={setTaxonomicRankBubbleColor( )}>
<View style={viewStyles.greenButton}>
<GreenRectangle
text={i18n.t( rankDict[rankToRender] )}
Expand Down
Loading

0 comments on commit d324caa

Please sign in to comment.