From cc575fbcc29e2cfc85f2ef270c982b96b6ee6ee8 Mon Sep 17 00:00:00 2001 From: Mayank Kumawat Date: Tue, 9 Jan 2024 19:55:00 +0530 Subject: [PATCH] environment change toggle in settings --- .gitignore | 3 +- app/actions/species.js | 47 +++++++------- app/components/Common/InventoryCard/index.tsx | 7 ++- app/components/Common/Modal/index.js | 15 +++-- app/components/Environment/Environment.tsx | 25 ++++++-- app/components/InventoryOverview/index.tsx | 6 +- app/components/MainScreen/BottomBar.tsx | 3 +- app/components/MainScreen/NavDrawer/index.tsx | 8 ++- .../SelectedPlantLocationSampleTreesCards.tsx | 46 +++++++++----- app/components/MainScreen/index.tsx | 10 ++- app/components/ProfileModal/index.tsx | 8 ++- app/components/Projects/ProjectList.tsx | 18 +++++- .../RegisterSingleTree/SingleTreeOverview.tsx | 6 +- .../Remeasurements/RemeasurementItem.tsx | 7 ++- app/components/SignUp/index.tsx | 7 ++- app/environment.tsx | 26 -------- app/redux/slices/envSlice.tsx | 4 +- app/repositories/migrations/schema20.ts | 4 +- .../RemeasurementFlow/RemeasurementReview.tsx | 62 ++++++++++--------- app/utils/additionalData/functions.ts | 32 ++++++---- app/utils/axiosInstance.js | 2 +- app/utils/updateAndSyncLocalSpecies.ts | 6 +- 22 files changed, 212 insertions(+), 140 deletions(-) delete mode 100644 app/environment.tsx diff --git a/.gitignore b/.gitignore index 4593bc2b5..14aa0d6e3 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,5 @@ yarn-error.log # Temporary files created by Metro to check the health of the file watcher .metro-health-check* # testing -/coverage \ No newline at end of file +/coverage +/environment.tsx \ No newline at end of file diff --git a/app/actions/species.js b/app/actions/species.js index df8856abf..e2461e043 100644 --- a/app/actions/species.js +++ b/app/actions/species.js @@ -11,15 +11,17 @@ import { import { LogTypes } from '../utils/constants'; import { SET_SPECIE, CLEAR_SPECIE } from './Types'; import { APIConfig } from './Config'; +import { store } from '../redux/store'; +import { ENVS } from '../../environment'; -const { protocol, cdnUrl } = APIConfig; +const { protocol } = APIConfig; /** * This function dispatches type SET_SPECIE with payload specie to show specie detail on SpecieInfo screen * It requires the following param * @param {Object} specie - specie which is to be shown or updated */ -export const setSpecie = (specie) => (dispatch) => { +export const setSpecie = specie => dispatch => { dispatch({ type: SET_SPECIE, payload: specie, @@ -29,7 +31,7 @@ export const setSpecie = (specie) => (dispatch) => { /** * This function dispatches type CLEAR_SPECIE to clear the species after navigated back from SpecieInfo screen */ -export const clearSpecie = () => (dispatch) => { +export const clearSpecie = () => dispatch => { dispatch({ type: CLEAR_SPECIE, }); @@ -42,7 +44,7 @@ export const getSpeciesList = () => { return new Promise((resolve, reject) => { // makes an authorized GET request on /species to get the species list. getAuthenticatedRequest('/treemapper/species') - .then((res) => { + .then(res => { const { data, status } = res; // checks if the status code is 200 the resolves the promise with the fetched data if (status === 200) { @@ -57,7 +59,7 @@ export const getSpeciesList = () => { resolve(false); } }) - .catch((err) => { + .catch(err => { // logs the error console.error(`Error at /actions/species/getSpeciesList, ${JSON.stringify(err.response)}`); // logs the error of the failed request in DB @@ -78,11 +80,11 @@ export const getSpeciesList = () => { * @param {object} specieData - contains scientificSpecies as property having scientific specie id and * aliases as property (a name given by user to that scientific specie) */ -export const addUserSpecie = (specieData) => { +export const addUserSpecie = specieData => { return new Promise((resolve, reject) => { // makes an authorized POST request on /species to add a specie of user. postAuthenticatedRequest('/treemapper/species', specieData) - .then((res) => { + .then(res => { const { data, status } = res; // checks if the status code is 200 the resolves the promise with the fetched data @@ -104,7 +106,7 @@ export const addUserSpecie = (specieData) => { resolve(false); } }) - .catch((err) => { + .catch(err => { // logs the error console.error(`Error at /actions/species/addUserSpecie, ${JSON.stringify(err?.response)}`); // logs the error of the failed request in DB @@ -123,11 +125,11 @@ export const addUserSpecie = (specieData) => { * Delete the user specie from the server using the specie id * @param {object} specieId - specie id of user saved species which is use to delete specie from server */ -export const deleteUserSpecie = (specieId) => { +export const deleteUserSpecie = specieId => { return new Promise((resolve, reject) => { // makes an authorized DELETE request on /species to delete a specie of user. deleteAuthenticatedRequest(`/treemapper/species/${specieId}`) - .then((res) => { + .then(res => { const { status } = res; // checks if the status code is 204 then resolves the promise @@ -143,7 +145,7 @@ export const deleteUserSpecie = (specieId) => { resolve(false); } }) - .catch((err) => { + .catch(err => { // logs the error console.error( `Error at /actions/species/deleteUserSpecie, ${JSON.stringify(err?.response)}, ${err}`, @@ -171,7 +173,7 @@ export const updateUserSpecie = ({ const data = { aliases, description, imageFile: image }; // makes an authorized DELETE request on /species to delete a specie of user. putAuthenticatedRequest(`/treemapper/species/${specieId}`, data) - .then((res) => { + .then(res => { const { status } = res; // checks if the status code is 204 then resolves the promise if (status === 200) { @@ -187,7 +189,7 @@ export const updateUserSpecie = ({ resolve(false); } }) - .catch((err) => { + .catch(err => { // logs the error of the failed request in DB dbLog.error({ logType: LogTypes.MANAGE_SPECIES, @@ -207,14 +209,14 @@ export const UpdateSpeciesImage = (image, speciesId, SpecieGuid) => { }; putAuthenticatedRequest(`/treemapper/species/${speciesId}`, body) - .then((res) => { + .then(res => { const { status } = res; if (status === 200) { changeIsUpdatedStatus({ scientificSpecieGuid: SpecieGuid, isUpdated: true }); resolve(true); } }) - .catch((err) => { + .catch(err => { // logs the error of the failed request in DB dbLog.error({ logType: LogTypes.MANAGE_SPECIES, @@ -227,24 +229,25 @@ export const UpdateSpeciesImage = (image, speciesId, SpecieGuid) => { }); }; -export const getBase64ImageFromURL = async (specieImage) => { +export const getBase64ImageFromURL = async specieImage => { return new Promise((resolve, reject) => { - if (cdnUrl) { + const currentEnv = store.getState().envSlice.currentEnv; + if (ENVS[currentEnv].CDN_URL) { RNFS.downloadFile({ - fromUrl: `${protocol}://${cdnUrl}/media/cache/species/default/${specieImage}`, + fromUrl: `${protocol}://${ENVS[currentEnv].CDN_URL}/media/cache/species/default/${specieImage}`, toFile: `${RNFS.DocumentDirectoryPath}/${specieImage}`, - }).promise.then((response) => { + }).promise.then(response => { if (response.statusCode === 200) { RNFS.readFile(`${RNFS.DocumentDirectoryPath}/${specieImage}`, 'base64') - .then((data) => { + .then(data => { resolve(data); - RNFS.unlink(`${RNFS.DocumentDirectoryPath}/${specieImage}`).catch((err) => { + RNFS.unlink(`${RNFS.DocumentDirectoryPath}/${specieImage}`).catch(err => { reject(err); // `unlink` will throw an error, if the item to unlink does not exist console.error(err.message); }); }) - .catch((err) => { + .catch(err => { dbLog.error({ logType: LogTypes.MANAGE_SPECIES, message: 'Error while reading file image', diff --git a/app/components/Common/InventoryCard/index.tsx b/app/components/Common/InventoryCard/index.tsx index 2c77ba2cf..2d99739f9 100644 --- a/app/components/Common/InventoryCard/index.tsx +++ b/app/components/Common/InventoryCard/index.tsx @@ -9,8 +9,10 @@ import { APIConfig } from './../../../actions/Config'; import { cmToInch, meterToFoot, nonISUCountries } from '../../../utils/constants'; import { INCOMPLETE, INCOMPLETE_SAMPLE_TREE, SINGLE } from '../../../utils/inventoryConstants'; import { single_tree_png, placeholder_image, map_img, multiple_tree_png } from '../../../assets'; +import { store } from '../../../redux/store'; +import { ENVS } from '../../../../environment'; -const { protocol, cdnUrl } = APIConfig; +const { protocol } = APIConfig; interface IInventoryCardProps { data?: any; @@ -37,8 +39,9 @@ const InventoryCard = ({ uri: `${imageURIPrefix}${RNFS.DocumentDirectoryPath}/${data.imageURL}`, }); } else if (data.cdnImageUrl) { + const currentEnv = store.getState().envSlice.currentEnv; setImageSource({ - uri: `${protocol}://${cdnUrl}/media/cache/coordinate/thumb/${data.cdnImageUrl}`, + uri: `${protocol}://${ENVS[currentEnv].CDN_URL}/media/cache/coordinate/thumb/${data.cdnImageUrl}`, }); } else if ( activeBtn === true || diff --git a/app/components/Common/Modal/index.js b/app/components/Common/Modal/index.js index a3ef23f0d..e3a997416 100644 --- a/app/components/Common/Modal/index.js +++ b/app/components/Common/Modal/index.js @@ -15,17 +15,20 @@ import { Typography, Colors } from '_styles'; import CountryData from '../../../utils/countryData.json'; import i18next from 'i18next'; import { APIConfig } from '../../../actions/Config'; +import { ENVS } from '../../../../environment'; +import { useSelector } from 'react-redux'; -const { protocol, cdnUrl } = APIConfig; +const { protocol } = APIConfig; export default function index({ visible, openModal, userCountry }) { const [countryData, setCountryData] = useState(null); const [search, setSearch] = useState(null); + const { currentEnv } = useSelector(state => state.envSlice); const renderItem = ({ item }) => { return selectCountry(item)} />; }; - const selectCountry = (title) => { + const selectCountry = title => { userCountry(title); }; const Item = ({ title, onPress }) => ( @@ -34,7 +37,9 @@ export default function index({ visible, openModal, userCountry }) { { - const filteredData = CountryData.filter((el) => + const handleFilter = input => { + const filteredData = CountryData.filter(el => el.countryName.toLowerCase().includes(input.toLowerCase()), ); setCountryData(filteredData); diff --git a/app/components/Environment/Environment.tsx b/app/components/Environment/Environment.tsx index fb9840fa0..e95dae042 100644 --- a/app/components/Environment/Environment.tsx +++ b/app/components/Environment/Environment.tsx @@ -1,23 +1,34 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import FontAwesome5Icon from 'react-native-vector-icons/FontAwesome5'; +import { View, Text, StyleSheet, TouchableOpacity, Dimensions, Alert } from 'react-native'; -import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from 'react-native'; - -import { ENV_TYPE } from '../../environment'; +import { ENV_TYPE } from '../../../environment'; import HeaderV2 from '../Common/Header/HeaderV2'; import { Colors, Typography } from '../../styles'; import { IS_ANDROID, scaleSize } from '../../styles/mixins'; import { SET_APP_ENVIRONMENT } from '../../redux/slices/envSlice'; +import { InventoryContext } from '../../reducers/inventory'; +import { clearAllUploadedInventory } from '../../repositories/inventory'; const { width, height } = Dimensions.get('screen'); const Environment = () => { const { currentEnv } = useSelector(state => state.envSlice); + const { state } = useContext(InventoryContext); const dispatch = useDispatch(); const insects = useSafeAreaInsets(); + const handleChangeEnv = async type => { + if (state.isUploading) { + Alert.alert('Inventory upload is in progress'); + } else { + await clearAllUploadedInventory(); + dispatch(SET_APP_ENVIRONMENT(type)); + } + }; + return ( { dispatch(SET_APP_ENVIRONMENT(ENV_TYPE.STAGING))}> + disabled={state.isUploading} + onPress={() => handleChangeEnv(ENV_TYPE.STAGING)}> { dispatch(SET_APP_ENVIRONMENT(ENV_TYPE.PROD))}> + disabled={state.isUploading} + onPress={() => handleChangeEnv(ENV_TYPE.PROD)}> ; const InventoryOverview = ({ navigation }: any) => { - const { protocol, cdnUrl } = APIConfig; + const { protocol } = APIConfig; + const { currentEnv } = useSelector(state => state.envSlice); + const cdnUrl = ENVS[currentEnv].CDN_URL; const windowHeight = Dimensions.get('window').height; // reference for camera to focus on map diff --git a/app/components/MainScreen/BottomBar.tsx b/app/components/MainScreen/BottomBar.tsx index a9d18dd46..97be5cb7b 100644 --- a/app/components/MainScreen/BottomBar.tsx +++ b/app/components/MainScreen/BottomBar.tsx @@ -1,4 +1,4 @@ -import { Text, View, StyleSheet, TouchableOpacity, Vibration } from 'react-native'; +import { Text, View, StyleSheet, TouchableOpacity } from 'react-native'; import React, { useRef, useState } from 'react'; import { SvgXml } from 'react-native-svg'; import { add_icon, ListIcon, PlotIcon, MapExplore } from '../../assets'; @@ -30,7 +30,6 @@ const BottomBar = ({ state, descriptors, navigation }: IBottomBarProps) => { const _addOptionsRef = useRef(); const onAddPress = () => { - Vibration.vibrate(); setOpen(prev => !prev); }; diff --git a/app/components/MainScreen/NavDrawer/index.tsx b/app/components/MainScreen/NavDrawer/index.tsx index ddce3b413..047b14406 100644 --- a/app/components/MainScreen/NavDrawer/index.tsx +++ b/app/components/MainScreen/NavDrawer/index.tsx @@ -1,6 +1,7 @@ import i18next from 'i18next'; import { useState } from 'react'; import React, { useContext } from 'react'; +import { useSelector } from 'react-redux'; import { useNavigation } from '@react-navigation/native'; import Ionicons from 'react-native-vector-icons/Ionicons'; import FontAwesome from 'react-native-vector-icons/FontAwesome'; @@ -28,8 +29,9 @@ import { Colors, Spacing, Typography } from '../../../styles'; import { InventoryContext } from '../../../reducers/inventory'; import { auth0Login, auth0Logout } from '../../../actions/user'; import { isPlantForThePlanetEmail } from '../../../utils'; +import { ENVS } from '../../../../environment'; -const { protocol, cdnUrl, webAppUrl } = APIConfig; +const { protocol } = APIConfig; const getIcon = screenName => { switch (screenName) { @@ -84,11 +86,15 @@ const state = { const NavDrawer = props => { const { state: userState, dispatch: userDispatch } = useContext(UserContext); const { dispatch } = useContext(InventoryContext); + const { currentEnv } = useSelector(state => state.envSlice); const [loading, setLoading] = useState(false); const navigation = useNavigation(); const insects = useSafeAreaInsets(); + const cdnUrl = ENVS[currentEnv].CDN_URL; + const webAppUrl = ENVS[currentEnv].WEB_APP_URL; + const handleLogout = async () => { const isLogout = await auth0Logout(userDispatch); }; diff --git a/app/components/MainScreen/SelectedPlantLocationSampleTreesCards.tsx b/app/components/MainScreen/SelectedPlantLocationSampleTreesCards.tsx index 5922b6a63..17106a741 100644 --- a/app/components/MainScreen/SelectedPlantLocationSampleTreesCards.tsx +++ b/app/components/MainScreen/SelectedPlantLocationSampleTreesCards.tsx @@ -1,20 +1,30 @@ -import {useNavigation} from '@react-navigation/core'; +import { useNavigation } from '@react-navigation/core'; import i18next from 'i18next'; -import React, {useContext} from 'react'; -import {Dimensions, Image, Platform, StyleSheet, Text, TouchableOpacity, View} from 'react-native'; +import React, { useContext } from 'react'; +import { + Dimensions, + Image, + Platform, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; import Config from 'react-native-config'; import RNFS from 'react-native-fs'; import Carousel from 'react-native-snap-carousel'; -import {APIConfig} from '../../actions/Config'; -import {setRemeasurementId, setSamplePlantLocationIndex} from '../../actions/inventory'; -import {InventoryContext} from '../../reducers/inventory'; -import {Colors, Typography} from '../../styles'; -import {nonISUCountries} from '../../utils/constants'; +import { APIConfig } from '../../actions/Config'; +import { setRemeasurementId, setSamplePlantLocationIndex } from '../../actions/inventory'; +import { InventoryContext } from '../../reducers/inventory'; +import { Colors, Typography } from '../../styles'; +import { nonISUCountries } from '../../utils/constants'; import distanceCalculator from '../../utils/distanceCalculator'; -import {PENDING_DATA_UPLOAD, SYNCED} from '../../utils/inventoryConstants'; -import {getIsDateInRemeasurementRange} from '../../utils/remeasurement'; +import { PENDING_DATA_UPLOAD, SYNCED } from '../../utils/inventoryConstants'; +import { getIsDateInRemeasurementRange } from '../../utils/remeasurement'; import PrimaryButton from '../Common/PrimaryButton'; -const {protocol, cdnUrl} = APIConfig; +import { useSelector } from 'react-redux'; +import { ENVS } from '../../../environment'; +const { protocol } = APIConfig; const IS_ANDROID = Platform.OS === 'android'; @@ -27,7 +37,7 @@ interface ISelectedPlantLocationSampleTreesCardsProps { loadingInventoryData: boolean; } -const {width} = Dimensions.get('window'); +const { width } = Dimensions.get('window'); const itemWidth = width - 25 * 3; const SelectedPlantLocationSampleTreesCards = ({ @@ -39,7 +49,9 @@ const SelectedPlantLocationSampleTreesCards = ({ loadingInventoryData, }: ISelectedPlantLocationSampleTreesCardsProps) => { const navigation = useNavigation(); - const {dispatch} = useContext(InventoryContext); + const { dispatch } = useContext(InventoryContext); + const { currentEnv } = useSelector(state => state.envSlice); + const cdnUrl = ENVS[currentEnv].CDN_URL; const heightUnit = nonISUCountries.includes(countryCode) ? i18next.t('label.select_species_feet') @@ -92,7 +104,7 @@ const SelectedPlantLocationSampleTreesCards = ({ data={singleSelectedPlantLocation?.sampleTrees} itemWidth={itemWidth} sliderWidth={width} - renderItem={({item, index}: any) => { + renderItem={({ item, index }: any) => { let imageSource; // let canRemeasurePlantLocation = false; let isUserDistanceMoreThen100M = true; @@ -142,11 +154,11 @@ const SelectedPlantLocationSampleTreesCards = ({ {imageSource ? : []} {/* textual info of sample tree */} - + HID: {item.hid} {/* species name */} - + {item.specieName} @@ -185,7 +197,7 @@ const SelectedPlantLocationSampleTreesCards = ({ {i18next.t('label.you_are_far_to_remeasure')} diff --git a/app/components/MainScreen/index.tsx b/app/components/MainScreen/index.tsx index b54931e9f..c6a119695 100644 --- a/app/components/MainScreen/index.tsx +++ b/app/components/MainScreen/index.tsx @@ -513,7 +513,7 @@ export default function MainScreen() { - + { + const { currentEnv } = useSelector(state => state.envSlice); + const cdnUrl = ENVS[currentEnv].CDN_URL; + const webAppUrl = ENVS[currentEnv].WEB_APP_URL; + const onPressEdit = () => { openWebView(`${protocol}://${webAppUrl}/login`); }; diff --git a/app/components/Projects/ProjectList.tsx b/app/components/Projects/ProjectList.tsx index 04288c88e..f183336d5 100644 --- a/app/components/Projects/ProjectList.tsx +++ b/app/components/Projects/ProjectList.tsx @@ -1,9 +1,11 @@ import i18next from 'i18next'; import { SvgXml } from 'react-native-svg'; +import { useSelector } from 'react-redux'; import React, { useEffect, useState } from 'react'; import { FlatList, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { LargeButton } from '../Common'; +import { ENVS } from '../../../environment'; import { plant_project } from '../../assets'; import { APIConfig } from '../../actions/Config'; import { Colors, Typography } from '../../styles'; @@ -11,7 +13,7 @@ import openWebView from '../../utils/openWebView'; import { handleFilter } from '../../utils/CountryDataFilter'; import { getAllProjects } from '../../repositories/projects'; -const { protocol, cdnUrl, webAppUrl } = APIConfig; +const { protocol } = APIConfig; interface ProjectListProps { isSelectable: boolean; @@ -24,7 +26,10 @@ export default function ProjectList({ onProjectPress, selectedProjectId, }: ProjectListProps) { + const { currentEnv } = useSelector(state => state.envSlice); const [projects, setProjects] = useState([]); + const cdnUrl = ENVS[currentEnv].CDN_URL; + const webAppUrl = ENVS[currentEnv].WEB_APP_URL; useEffect(() => { getAllProjects().then((projectsData: any) => (projectsData ? setProjects(projectsData) : {})); @@ -69,7 +74,7 @@ export default function ProjectList({ /> ); } - return ; + return ; }; return ( @@ -83,7 +88,14 @@ export default function ProjectList({ ); } -const ProjectItem = ({ item, selectedProjectId }: { item: any; selectedProjectId?: string }) => { +const ProjectItem = ({ + item, + selectedProjectId, + cdnUrl, +}: { + item: any; + selectedProjectId?: string; +}) => { const isProjectSelected = selectedProjectId === item.id; let country: any = handleFilter(item.country); if (country && country.length > 0) { diff --git a/app/components/RegisterSingleTree/SingleTreeOverview.tsx b/app/components/RegisterSingleTree/SingleTreeOverview.tsx index 2690dc41a..a94322eea 100644 --- a/app/components/RegisterSingleTree/SingleTreeOverview.tsx +++ b/app/components/RegisterSingleTree/SingleTreeOverview.tsx @@ -76,8 +76,10 @@ import { getIsDateInRemeasurementRange } from '../../utils/remeasurement'; import { getUserInformation } from '../../repositories/user'; import { measurementValidation } from '../../utils/validations/measurements'; import { UserContext } from '../../reducers/user'; +import { useSelector } from 'react-redux'; +import { ENVS } from '../../../environment'; -const { protocol, cdnUrl } = APIConfig; +const { protocol } = APIConfig; type RootStackParamList = { SingleTreeOverview: { @@ -139,6 +141,8 @@ const SingleTreeOverview = () => { const [plantLocationCoordinates, setPlantLocationCoordinates] = useState<[number, number]>([ 0, 0, ]); + const { currentEnv } = useSelector(state => state.envSlice); + const cdnUrl = ENVS[currentEnv].CDN_URL; const navigation = useNavigation(); const route: SingleTreeOverviewScreenRouteProp = useRoute(); diff --git a/app/components/Remeasurements/RemeasurementItem.tsx b/app/components/Remeasurements/RemeasurementItem.tsx index b5d4cca2d..054ee5cb0 100644 --- a/app/components/Remeasurements/RemeasurementItem.tsx +++ b/app/components/Remeasurements/RemeasurementItem.tsx @@ -14,7 +14,9 @@ import { single_tree_png } from '../../assets'; import { cmToInch, meterToFoot, nonISUCountries } from '../../utils/constants'; import i18next from 'i18next'; import { getUserInformation } from '../../repositories/user'; -const { protocol, cdnUrl } = APIConfig; +import { useSelector } from 'react-redux'; +import { ENVS } from '../../../environment'; +const { protocol } = APIConfig; type Props = { item: any; @@ -28,6 +30,9 @@ const RemeasurementItem = ({ item, hideImage = false, containerStyle = {} }: Pro const [isNonISUCountry, setIsNonISUCountry] = useState(false); const [hid, setHid] = useState(''); + const { currentEnv } = useSelector(state => state.envSlice); + const cdnUrl = ENVS[currentEnv].CDN_URL; + // updates the [imageSource] based on the avaialble image property useEffect(() => { if (item.imageUrl) { diff --git a/app/components/SignUp/index.tsx b/app/components/SignUp/index.tsx index e263ddea1..74d7abc6a 100644 --- a/app/components/SignUp/index.tsx +++ b/app/components/SignUp/index.tsx @@ -26,8 +26,10 @@ import { handleFilter } from '../../utils/CountryDataFilter'; import { Header, Loader, PrimaryButton } from '../Common'; import Modal from '../Common/Modal'; import OutlinedInput from '../Common/OutlinedInput'; +import { useSelector } from 'react-redux'; +import { ENVS } from '../../../environment'; -const { protocol, cdnUrl } = APIConfig; +const { protocol } = APIConfig; // TODO:i18n - if this file is used, please add translations const SignUp = ({ navigation }) => { @@ -52,10 +54,13 @@ const SignUp = ({ navigation }) => { const [country, setCountry] = useState(''); const [modalVisible, setModalVisible] = useState(false); + const { currentEnv } = useSelector(state => state.envSlice); const { state: loadingState, dispatch: loadingDispatch } = useContext(LoadingContext); const { state: userState, dispatch: userDispatch } = useContext(UserContext); const lang = RNLocalize.getLocales()[0]; + const cdnUrl = ENVS[currentEnv].CDN_URL; + const toggleSwitchPublish = () => setIsPrivate(previousState => !previousState); const toggleSwitchContact = () => setGetNews(previousState => !previousState); diff --git a/app/environment.tsx b/app/environment.tsx deleted file mode 100644 index 87cbb28da..000000000 --- a/app/environment.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import Config from 'react-native-config'; - -export enum ENV_TYPE { - STAGING = 'STAGING', - PROD = 'PRODUCTION', -} - -export const ENVS = { - STAGING: { - BUGSNAP_CLIENT_KEY: Config.BUGSNAP_CLIENT_KEY, - AUTH0_DOMAIN: 'accounts.plant-for-the-planet.org', - AUTH0_CLIENT_ID: 'LiEqEef641Pzv8cBGn6i9Jt9jrnyLJEt', - API_ENDPOINT: 'app-staging.plant-for-the-planet.org', - CDN_URL: 'cdn.plant-for-the-planet.org/staging', - WEB_APP_URL: 'dev.pp.eco', - }, - PRODUCTION: { - BUGSNAP_CLIENT_KEY: 'e1b5d94f16186f8c6a2169882998ebda', - AUTH0_DOMAIN: 'accounts.plant-for-the-planet.org', - AUTH0_CLIENT_ID: 'LiEqEef641Pzv8cBGn6i9Jt9jrnyLJEt', - API_ENDPOINT: 'app.plant-for-the-planet.org', - CDN_URL: 'cdn.plant-for-the-planet.org', - WEB_APP_URL: 'www1.plant-for-the-planet.org', - REALM_DISABLE_ANALYTICS: true, - }, -}; diff --git a/app/redux/slices/envSlice.tsx b/app/redux/slices/envSlice.tsx index dc004d29d..1c357195e 100644 --- a/app/redux/slices/envSlice.tsx +++ b/app/redux/slices/envSlice.tsx @@ -1,7 +1,7 @@ import { createSlice } from '@reduxjs/toolkit'; import type { PayloadAction } from '@reduxjs/toolkit'; -import { ENV_TYPE } from '../../environment'; +import { ENV_TYPE } from '../../../environment'; import { modifyUserDetails } from '../../repositories/user'; interface EnvState { @@ -9,7 +9,7 @@ interface EnvState { } const initialState: EnvState = { - currentEnv: ENV_TYPE.STAGING, + currentEnv: ENV_TYPE.PROD, }; export const envSlice = createSlice({ diff --git a/app/repositories/migrations/schema20.ts b/app/repositories/migrations/schema20.ts index 7c007e381..c9b2dd749 100644 --- a/app/repositories/migrations/schema20.ts +++ b/app/repositories/migrations/schema20.ts @@ -1,4 +1,4 @@ -import { ENV_TYPE } from '../../environment'; +import { ENV_TYPE } from '../../../environment'; import { InventoryType } from '../../types/inventory'; // schema version @@ -224,7 +224,7 @@ const User = { expirationTime: 'int?', fetchNecessaryInventoryFlag: { type: 'int', default: InventoryType.NecessaryItems }, fetchGivenMonthsInventoryFlag: 'int?', - appEnvironment: 'string', + appEnvironment: 'string?', }, }; diff --git a/app/screens/RemeasurementFlow/RemeasurementReview.tsx b/app/screens/RemeasurementFlow/RemeasurementReview.tsx index d821fbf84..c0a2aba4e 100644 --- a/app/screens/RemeasurementFlow/RemeasurementReview.tsx +++ b/app/screens/RemeasurementFlow/RemeasurementReview.tsx @@ -1,6 +1,6 @@ -import {CommonActions, useNavigation} from '@react-navigation/native'; +import { CommonActions, useNavigation } from '@react-navigation/native'; import i18next from 'i18next'; -import React, {useContext, useEffect, useState} from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { Dimensions, Image, @@ -15,15 +15,15 @@ import { import RNFS from 'react-native-fs'; import FIcon from 'react-native-vector-icons/Fontisto'; import MIcon from 'react-native-vector-icons/MaterialIcons'; -import {APIConfig} from '../../actions/Config'; -import {setInventoryId, setRemeasurementId} from '../../actions/inventory'; -import {Loader} from '../../components/Common'; +import { APIConfig } from '../../actions/Config'; +import { setInventoryId, setRemeasurementId } from '../../actions/inventory'; +import { Loader } from '../../components/Common'; import AlertModal from '../../components/Common/AlertModal'; import Header from '../../components/Common/Header'; import InputModal from '../../components/Common/InputModal'; import PrimaryButton from '../../components/Common/PrimaryButton'; -import {InventoryContext} from '../../reducers/inventory'; -import {getInventoryByLocationId} from '../../repositories/inventory'; +import { InventoryContext } from '../../reducers/inventory'; +import { getInventoryByLocationId } from '../../repositories/inventory'; import { deletePlantLocationHistory, getPlantLocationHistoryById, @@ -31,9 +31,9 @@ import { updatePlantLocationHistoryEventDate, updatePlantLocationHistoryStatus, } from '../../repositories/plantLocationHistory'; -import {getUserInformation} from '../../repositories/user'; -import {Colors, Typography} from '../../styles'; -import {DBHInMeter, nonISUCountries} from '../../utils/constants'; +import { getUserInformation } from '../../repositories/user'; +import { Colors, Typography } from '../../styles'; +import { DBHInMeter, nonISUCountries } from '../../utils/constants'; import { EDITING, FIX_NEEDED, @@ -42,9 +42,11 @@ import { PENDING_DATA_UPLOAD, SYNCED, } from '../../utils/inventoryConstants'; -import {getConvertedDiameter, getConvertedHeight} from '../../utils/measurements'; -import {measurementValidation} from '../../utils/validations/measurements'; -const {protocol, cdnUrl} = APIConfig; +import { getConvertedDiameter, getConvertedHeight } from '../../utils/measurements'; +import { measurementValidation } from '../../utils/validations/measurements'; +import { useSelector } from 'react-redux'; +import { ENVS } from '../../../environment'; +const { protocol } = APIConfig; type Props = {}; @@ -85,9 +87,11 @@ export default function RemeasurementReview({}: Props) { ]; const navigation = useNavigation(); + const { currentEnv } = useSelector(state => state.envSlice); + const cdnUrl = ENVS[currentEnv].CDN_URL; const { - state: {selectedRemeasurementId}, + state: { selectedRemeasurementId }, dispatch, } = useContext(InventoryContext); @@ -101,7 +105,7 @@ export default function RemeasurementReview({}: Props) { // used to get the data for the selected remeasurement getPlantLocationHistoryById(selectedRemeasurementId).then((plantLocationHistory: any) => { - getInventoryByLocationId({locationId: plantLocationHistory?.parentId}) + getInventoryByLocationId({ locationId: plantLocationHistory?.parentId }) .then(inventory => { console.log(plantLocationHistory, ' plantLocationHistory.samplePlantLocationIndex'); @@ -293,17 +297,17 @@ export default function RemeasurementReview({}: Props) { }; const redirectToParentInventory = () => { - navigation.navigate('InventoryOverview', {navigateToScreen: 'RemeasurementReview'}); + navigation.navigate('InventoryOverview', { navigateToScreen: 'RemeasurementReview' }); }; const handleDeleteInventory = () => { - deletePlantLocationHistory({remeasurementId: selectedRemeasurementId}) + deletePlantLocationHistory({ remeasurementId: selectedRemeasurementId }) .then(() => { setShowDeleteAlert(!showDeleteAlert); navigation.dispatch( CommonActions.reset({ index: 1, - routes: [{name: 'MainScreen'}, {name: 'TreeInventory'}], + routes: [{ name: 'MainScreen' }, { name: 'TreeInventory' }], }), ); }) @@ -333,7 +337,7 @@ export default function RemeasurementReview({}: Props) { } inputType={'number'} setValue={editEnabledFor === 'diameter' ? setEditableDiameter : setEditableHeight} - onSubmitInputField={() => onSubmitInputField({action: editEnabledFor})} + onSubmitInputField={() => onSubmitInputField({ action: editEnabledFor })} /> @@ -345,7 +349,7 @@ export default function RemeasurementReview({}: Props) { marginBottom: 0, }}>
onPressBack()} headingText={i18next.t('label.tree_review_header')} @@ -365,15 +369,15 @@ export default function RemeasurementReview({}: Props) { {/* shows the image */} - {imageUrl ? : []} + {imageUrl ? : []} {/* shows the height and also shows the edit button if editable */} {plantLocationHistory?.status !== 'dead' ? ( - + {i18next.t('label.select_species_height')} { setEditEnabledFor('height'); setEditableHeight(height); @@ -396,11 +400,11 @@ export default function RemeasurementReview({}: Props) { {/* shows the diameter and also shows the edit button if editable */} {plantLocationHistory?.status !== 'dead' ? ( - + {diameterLabel} { setEditEnabledFor('diameter'); setEditableDiameter(diameter); @@ -422,13 +426,13 @@ export default function RemeasurementReview({}: Props) { {plantLocationHistory?.status === 'dead' ? ( <> - + {i18next.t('label.status')} {i18next.t(`label.${plantLocationHistory?.status}`)} - + {i18next.t('label.cause_of_mortality')} {i18next.t(`label.${plantLocationHistory?.statusReason}`)} @@ -443,7 +447,7 @@ export default function RemeasurementReview({}: Props) { {/* shows button only if properties are editable and can be saved */} {isEditable ? ( - + onPressSave()} btnText={i18next.t('label.tree_review_Save')} @@ -470,7 +474,7 @@ export default function RemeasurementReview({}: Props) { secondaryBtnText={i18next.t('label.continue')} onPressSecondaryBtn={() => { setShowIncorrectRatioAlert(false); - onSubmitInputField({action: editEnabledFor, forceContinue: true}); + onSubmitInputField({ action: editEnabledFor, forceContinue: true }); }} /> { return arrayData.sort((a: any, b: any) => { @@ -74,7 +79,7 @@ export const filterFormByTreeAndRegistrationType = ( }; export const getFormattedMetadata = (additionalDetails: any) => { - let formattedDetails: any = {public: {}, private: {}, app: {}}; + let formattedDetails: any = { public: {}, private: {}, app: {} }; if ((additionalDetails || Array.isArray(additionalDetails)) && additionalDetails.length > 0) { for (let detail of additionalDetails) { @@ -249,7 +254,10 @@ interface IGetAppMetadata { } // used to support schema 11 migration -export const appAdditionalDataForAPISchema11 = ({data, isSampleTree = false}: IGetAppMetadata) => { +export const appAdditionalDataForAPISchema11 = ({ + data, + isSampleTree = false, +}: IGetAppMetadata) => { const appAdditionalDetails: any = {}; if (data.treeType === SINGLE || isSampleTree) { @@ -297,8 +305,8 @@ export const appAdditionalDataForAPISchema11 = ({data, isSampleTree = false}: IG return appAdditionalDetails; }; -export const appAdditionalDataForAPI = async ({data, isSampleTree = false}: IGetAppMetadata) => { - let appAdditionalDetails: any = basicAppAdditionalDataForAPI({data, isSampleTree}); +export const appAdditionalDataForAPI = async ({ data, isSampleTree = false }: IGetAppMetadata) => { + let appAdditionalDetails: any = basicAppAdditionalDataForAPI({ data, isSampleTree }); appAdditionalDetails = { ...appAdditionalDetails, @@ -308,7 +316,7 @@ export const appAdditionalDataForAPI = async ({data, isSampleTree = false}: IGet return appAdditionalDetails; }; -export const basicAppAdditionalDataForAPI = ({data, isSampleTree = false}: IGetAppMetadata) => { +export const basicAppAdditionalDataForAPI = ({ data, isSampleTree = false }: IGetAppMetadata) => { let appAdditionalDetails: any = {}; // adding dates to additional details @@ -473,7 +481,7 @@ export const appAdditionalDataForGeoJSON = async ({ return appAdditionalDetails; }; -export const additionalDataForUI = ({data, isSampleTree = false}: IGetAppMetadata) => { +export const additionalDataForUI = ({ data, isSampleTree = false }: IGetAppMetadata) => { const appAdditionalDetails: any[] = []; if (!isSampleTree) { diff --git a/app/utils/axiosInstance.js b/app/utils/axiosInstance.js index 3e670c1c5..c007e4fef 100644 --- a/app/utils/axiosInstance.js +++ b/app/utils/axiosInstance.js @@ -1,6 +1,6 @@ // axiosInstance.js import axios from 'axios'; -import { ENVS } from '../environment'; +import { ENVS } from '../../environment'; import { store } from '../redux/store'; const axiosInstance = axios.create({ diff --git a/app/utils/updateAndSyncLocalSpecies.ts b/app/utils/updateAndSyncLocalSpecies.ts index 292ec3d26..5b45054f5 100644 --- a/app/utils/updateAndSyncLocalSpecies.ts +++ b/app/utils/updateAndSyncLocalSpecies.ts @@ -10,8 +10,12 @@ import shouldUpdateSpeciesSync, { setSpeciesSyncUpdateDate, } from './ScientificSpecies/shouldUpdateSpeciesSync'; import { updateAndSyncLocalSpecies as updateAndSyncLocalSpeciesRepo } from '../repositories/species'; +import { store } from '../redux/store'; +import { ENVS } from '../../environment'; -const { protocol, url } = APIConfig; +const { protocol } = APIConfig; +const { currentEnv } = store.getState().envSlice; +const url = ENVS[currentEnv].API_ENDPOINT; /** * Reads the target path passed as params and parse the file contents to update the species locally in DB.