Create a hook to fetch a particular user's observations; add basic styling to obs cards

This commit is contained in:
Amanda Bullington
2021-09-30 15:14:03 -07:00
parent a839f7867c
commit 180da57723
10 changed files with 256 additions and 15 deletions

View File

@@ -0,0 +1,45 @@
// @flow strict-local
import React from "react";
import { Pressable, Text, View, Image } from "react-native";
import type { Node } from "react";
import { viewStyles, textStyles } from "../../styles/observations/obsCard";
type Props = {
item: {
uuid: string,
userPhoto: string,
commonName: string,
location: string,
timeObservedAt: string,
identifications: number,
comments: number,
qualityGrade: string
}
}
const ObsCard = ( { item }: Props ): Node => {
const handlePress = ( ) => console.log( "obs card was pressed" );
return (
<Pressable
onPress={handlePress}
style={viewStyles.row}
>
<Image source={{ uri: item.userPhoto }} style={viewStyles.imageBackground} />
<View style={viewStyles.obsDetailsColumn}>
<Text style={textStyles.text}>{item.commonName}</Text>
<Text style={textStyles.text}>{item.location}</Text>
<Text style={textStyles.text}>{item.timeObservedAt}</Text>
</View>
<View>
<Text style={textStyles.text}>{item.identifications}</Text>
<Text style={textStyles.text}>{item.comments}</Text>
<Text style={textStyles.text}>{item.qualityGrade}</Text>
</View>
</Pressable>
);
};
export default ObsCard;

View File

@@ -0,0 +1,27 @@
// @flow strict-local
import React from "react";
import { FlatList } from "react-native";
import type { Node } from "react";
import ObsCard from "./ObsCard";
import viewStyles from "../../styles/observations/observationsList";
import useFetchObservations from "../hooks/fetchObservations";
const ObservationsList = ( ): Node => {
const observations = useFetchObservations( );
const extractKey = item => item.uuid;
const renderItem = ( { item } ) => <ObsCard item={item} />;
return (
<FlatList
contentContainerStyle={viewStyles.background}
data={observations}
keyExtractor={extractKey}
renderItem={renderItem}
/>
);
};
export default ObservationsList;

View File

@@ -0,0 +1,40 @@
// @flow strict-local
import { useState, useEffect } from "react";
import inatjs from "inaturalistjs";
const useFetchObservations = ( ): any => {
const [observations, setObservations] = useState( [] );
useEffect( ( ) => {
const fetchObservations = async ( ) => {
try {
const testUser = "albulltest";
const params = { user_login: testUser };
const response = await inatjs.observations.search( params );
const userObservations = response.results;
const onlyNecessaryObsDetails = userObservations.map( ( obs => {
return {
uuid: obs.uuid,
userPhoto: obs.taxon.default_photo.square_url,
commonName: obs.taxon.preferred_common_name || obs.taxon.name,
location: obs.place_guess || obs.location,
timeObservedAt: obs.time_observed_at,
identifications: obs.identifications_count,
comments: obs.comments.length,
qualityGrade: obs.quality_grade
};
} ) );
setObservations( onlyNecessaryObsDetails );
} catch ( e ) {
console.log( e, JSON.stringify( e ), "couldn't fetch observations" );
}
};
fetchObservations( );
}, [] );
return observations;
};
export default useFetchObservations;

View File

@@ -3,7 +3,7 @@
*/
import {AppRegistry} from "react-native";
import App from "./App";
import ObsList from "./components/Observations/ObservationsList";
import {name as appName} from "./app.json";
AppRegistry.registerComponent( appName, () => App );
AppRegistry.registerComponent( appName, () => ObsList );

View File

@@ -114,7 +114,6 @@
AA97B3DCA18D747D9E7F0358 /* Pods-iNaturalistReactNative-iNaturalistReactNativeTests.debug.xcconfig */,
ACD831F90866E862C17F1AD1 /* Pods-iNaturalistReactNative-iNaturalistReactNativeTests.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@@ -263,7 +262,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
shellScript = "export NODE_BINARY=/Users/amanda/.nvm/versions/node/v12.13.0/bin/node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
010B3221A1D8DF0722FE9DE2 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
@@ -485,6 +484,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = N5J7L4P93Z;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = iNaturalistReactNative/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -511,6 +511,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = N5J7L4P93Z;
INFOPLIST_FILE = iNaturalistReactNative/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

94
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "inaturalistreactnative",
"version": "0.0.1",
"dependencies": {
"inaturalistjs": "github:inaturalist/inaturalistjs",
"react": "17.0.2",
"react-native": "0.65.1"
},
@@ -25,6 +26,7 @@
"eslint-plugin-react": "^7.25.1",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react-native": "^3.11.0",
"flow-bin": "^0.149.0",
"jest": "^26.6.3",
"metro-react-native-babel-preset": "^0.66.0",
"react-native-codegen": "^0.0.7",
@@ -4376,8 +4378,7 @@
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"node_modules/atob": {
"version": "2.1.2",
@@ -5128,7 +5129,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
@@ -5305,6 +5305,28 @@
"node": ">=4"
}
},
"node_modules/cross-fetch": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.5.tgz",
"integrity": "sha512-xqYAhQb4NhCJSRym03dwxpP1bYXpK3y7UN83Bo2WFi3x1Zmzn0SL/6xGoPr+gpt4WmNrgCCX3HPysvOwFOW36w==",
"dependencies": {
"node-fetch": "2.6.1",
"whatwg-fetch": "2.0.4"
}
},
"node_modules/cross-fetch/node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/cross-fetch/node_modules/whatwg-fetch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -5450,7 +5472,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
@@ -6843,6 +6864,18 @@
"integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==",
"dev": true
},
"node_modules/flow-bin": {
"version": "0.149.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.149.0.tgz",
"integrity": "sha512-32hM6iKSInPCUuooS23SJ4c5Up0Tt9ozrXEE6urEpTDJU0z/vQblnCBRt3QZaEEDzSKOu2QZAU6K7fbShOOHaQ==",
"dev": true,
"bin": {
"flow": "cli.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/flow-parser": {
"version": "0.121.0",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz",
@@ -6864,7 +6897,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -7399,6 +7431,15 @@
"node": ">=0.8.19"
}
},
"node_modules/inaturalistjs": {
"version": "1.13.2",
"resolved": "git+ssh://git@github.com/inaturalist/inaturalistjs.git#b60365d9b49c68f77e684b0d79c49933a88117cb",
"license": "MIT",
"dependencies": {
"cross-fetch": "^2.2.3",
"form-data": "^3.0.0"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -17139,8 +17180,7 @@
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"atob": {
"version": "2.1.2",
@@ -17716,7 +17756,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
@@ -17870,6 +17909,27 @@
}
}
},
"cross-fetch": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.5.tgz",
"integrity": "sha512-xqYAhQb4NhCJSRym03dwxpP1bYXpK3y7UN83Bo2WFi3x1Zmzn0SL/6xGoPr+gpt4WmNrgCCX3HPysvOwFOW36w==",
"requires": {
"node-fetch": "2.6.1",
"whatwg-fetch": "2.0.4"
},
"dependencies": {
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"whatwg-fetch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
}
}
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -17984,8 +18044,7 @@
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"denodeify": {
"version": "1.2.1",
@@ -19032,6 +19091,12 @@
"integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==",
"dev": true
},
"flow-bin": {
"version": "0.149.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.149.0.tgz",
"integrity": "sha512-32hM6iKSInPCUuooS23SJ4c5Up0Tt9ozrXEE6urEpTDJU0z/vQblnCBRt3QZaEEDzSKOu2QZAU6K7fbShOOHaQ==",
"dev": true
},
"flow-parser": {
"version": "0.121.0",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.121.0.tgz",
@@ -19047,7 +19112,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -19435,6 +19499,14 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"inaturalistjs": {
"version": "git+ssh://git@github.com/inaturalist/inaturalistjs.git#b60365d9b49c68f77e684b0d79c49933a88117cb",
"from": "inaturalistjs@github:inaturalist/inaturalistjs",
"requires": {
"cross-fetch": "^2.2.3",
"form-data": "^3.0.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",

View File

@@ -10,6 +10,7 @@
"lint": "eslint ."
},
"dependencies": {
"inaturalistjs": "github:inaturalist/inaturalistjs",
"react": "17.0.2",
"react-native": "0.65.1"
},
@@ -27,6 +28,7 @@
"eslint-plugin-react": "^7.25.1",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react-native": "^3.11.0",
"flow-bin": "^0.149.0",
"jest": "^26.6.3",
"metro-react-native-babel-preset": "^0.66.0",
"react-native-codegen": "^0.0.7",

6
styles/global.js Normal file
View File

@@ -0,0 +1,6 @@
// @flow strict-local
export const colors = {
white: "#ffffff",
black: "#000000"
};

View File

@@ -0,0 +1,33 @@
// @flow strict-local
import { StyleSheet } from "react-native";
import type { ViewStyleProp, TextStyleProp } from "react-native/Libraries/StyleSheet/StyleSheet";
import { colors } from "../global";
const viewStyles: { [string]: ViewStyleProp } = StyleSheet.create( {
imageBackground: {
width: 75,
height: 75,
borderRadius: 10,
backgroundColor: colors.black,
marginHorizontal: 20
},
obsDetailsColumn: {
width: 200
},
row: {
flexDirection: "row",
flexWrap: "nowrap",
marginVertical: 10
}
} );
const textStyles: { [string]: TextStyleProp } = StyleSheet.create( {
text: { }
} );
export {
viewStyles,
textStyles
};

View File

@@ -0,0 +1,15 @@
// @flow strict-local
import { StyleSheet } from "react-native";
import { colors } from "../global";
import type { ViewStyleProp } from "react-native/Libraries/StyleSheet/StyleSheet";
const viewStyles: { [string]: ViewStyleProp } = StyleSheet.create( {
background: {
backgroundColor: colors.white,
flex: 1
}
} );
export default viewStyles;