diff --git a/dev-client/src/hooks/useElevationData.ts b/dev-client/src/model/elevation/elevationFunctions.ts similarity index 62% rename from dev-client/src/hooks/useElevationData.ts rename to dev-client/src/model/elevation/elevationFunctions.ts index 4654e9c66..bd5b19dbe 100644 --- a/dev-client/src/hooks/useElevationData.ts +++ b/dev-client/src/model/elevation/elevationFunctions.ts @@ -15,21 +15,10 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -import {useMemo, useState} from 'react'; - import {Coords} from 'terraso-client-shared/types'; -import {getElevation} from 'terraso-mobile-client/services'; - -export const useElevationData = (coords: Coords): number => { - const [siteElevationValue, setSiteElevationValue] = useState(0); - - useMemo(async () => { - const elevation = await getElevation(coords.latitude, coords.longitude); - if (elevation !== undefined) { - setSiteElevationValue(elevation); - } - }, [coords]); +import {ElevationKey} from 'terraso-mobile-client/model/elevation/elevationTypes'; - return siteElevationValue; +export const elevationKey = (coords: Coords): ElevationKey => { + return `(${coords.longitude.toFixed(5)}, ${coords.latitude.toFixed(5)})` as ElevationKey; }; diff --git a/dev-client/src/model/elevation/elevationHooks.ts b/dev-client/src/model/elevation/elevationHooks.ts new file mode 100644 index 000000000..88f54a105 --- /dev/null +++ b/dev-client/src/model/elevation/elevationHooks.ts @@ -0,0 +1,37 @@ +/* + * Copyright © 2024 Technology Matters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import {useEffect} from 'react'; + +import {Coords} from 'terraso-client-shared/types'; + +import {selectElevation} from 'terraso-mobile-client/model/elevation/elevationSelectors'; +import {fetchElevation} from 'terraso-mobile-client/model/elevation/elevationSlice'; +import {useDispatch, useSelector} from 'terraso-mobile-client/store'; + +export const useElevationData = (coords: Coords): number | undefined => { + const dispatch = useDispatch(); + const elevation = useSelector(selectElevation(coords)); + + useEffect(() => { + if (!elevation) { + dispatch(fetchElevation(coords)); + } + }, [dispatch, elevation, coords]); + + return elevation?.value; +}; diff --git a/dev-client/src/model/elevation/elevationSelectors.ts b/dev-client/src/model/elevation/elevationSelectors.ts new file mode 100644 index 000000000..ad20fb509 --- /dev/null +++ b/dev-client/src/model/elevation/elevationSelectors.ts @@ -0,0 +1,24 @@ +/* + * Copyright © 2024 Technology Matters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import {Coords} from 'terraso-client-shared/types'; + +import {elevationKey} from 'terraso-mobile-client/model/elevation/elevationFunctions'; +import {AppState} from 'terraso-mobile-client/store'; + +export const selectElevation = (coords: Coords) => (state: AppState) => + state.elevation.elevation[elevationKey(coords)]; diff --git a/dev-client/src/services.ts b/dev-client/src/model/elevation/elevationService.ts similarity index 100% rename from dev-client/src/services.ts rename to dev-client/src/model/elevation/elevationService.ts diff --git a/dev-client/src/model/elevation/elevationSlice.ts b/dev-client/src/model/elevation/elevationSlice.ts new file mode 100644 index 000000000..d0f7b3dab --- /dev/null +++ b/dev-client/src/model/elevation/elevationSlice.ts @@ -0,0 +1,65 @@ +/* + * Copyright © 2024 Technology Matters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import {createAsyncThunk, createSlice} from '@reduxjs/toolkit'; + +import {Coords} from 'terraso-client-shared/types'; + +import {elevationKey} from 'terraso-mobile-client/model/elevation/elevationFunctions'; +import {getElevation} from 'terraso-mobile-client/model/elevation/elevationService'; +import { + ElevationKey, + ElevationRecord, +} from 'terraso-mobile-client/model/elevation/elevationTypes'; + +export type ElevationState = { + elevation: Record; +}; + +const initialState: ElevationState = { + elevation: {}, +}; + +const {reducer} = createSlice({ + name: 'elevation', + initialState, + reducers: {}, + extraReducers(builder) { + builder + .addCase(fetchElevation.pending, (state, action) => { + state.elevation[elevationKey(action.meta.arg)] = {fetching: true}; + }) + .addCase(fetchElevation.rejected, (state, action) => { + state.elevation[elevationKey(action.meta.arg)] = {fetching: false}; + }) + .addCase(fetchElevation.fulfilled, (state, action) => { + state.elevation[elevationKey(action.meta.arg)] = { + fetching: false, + value: action.payload, + }; + }); + }, +}); + +const fetchElevation = createAsyncThunk( + 'elevation/fetchElevation', + async (coords: Coords) => { + return getElevation(coords.latitude, coords.longitude); + }, +); + +export {fetchElevation, reducer}; diff --git a/dev-client/src/model/elevation/elevationTypes.ts b/dev-client/src/model/elevation/elevationTypes.ts new file mode 100644 index 000000000..5ab94b3ca --- /dev/null +++ b/dev-client/src/model/elevation/elevationTypes.ts @@ -0,0 +1,22 @@ +/* + * Copyright © 2024 Technology Matters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +export type ElevationKey = `(${number}, ${number})`; +export type ElevationRecord = { + value?: number; + fetching: boolean; +}; diff --git a/dev-client/src/screens/HomeScreen/components/TemporaryLocationCallout.tsx b/dev-client/src/screens/HomeScreen/components/TemporaryLocationCallout.tsx index f1066f146..cb8e3b2cd 100644 --- a/dev-client/src/screens/HomeScreen/components/TemporaryLocationCallout.tsx +++ b/dev-client/src/screens/HomeScreen/components/TemporaryLocationCallout.tsx @@ -40,7 +40,7 @@ import { } from 'terraso-mobile-client/components/NativeBaseAdapters'; import {SoilIdStatusDisplay} from 'terraso-mobile-client/components/SoilIdStatusDisplay'; import {renderElevation} from 'terraso-mobile-client/components/util/site'; -import {useElevationData} from 'terraso-mobile-client/hooks/useElevationData'; +import {useElevationData} from 'terraso-mobile-client/model/elevation/elevationHooks'; import {getTopMatch} from 'terraso-mobile-client/model/soilId/soilIdRanking'; import {useNavigation} from 'terraso-mobile-client/navigation/hooks/useNavigation'; import {CalloutDetail} from 'terraso-mobile-client/screens/HomeScreen/components/CalloutDetail'; diff --git a/dev-client/src/store/index.ts b/dev-client/src/store/index.ts index 5e203b37b..9707cfa5d 100644 --- a/dev-client/src/store/index.ts +++ b/dev-client/src/store/index.ts @@ -28,10 +28,15 @@ import createStoreFactory, { StateFromStoreFactory, } from 'terraso-client-shared/store/store'; +import {reducer as elevationReducer} from 'terraso-mobile-client/model/elevation/elevationSlice'; import {reducer as mapReducer} from 'terraso-mobile-client/model/map/mapSlice'; import {reducer as preferencesReducer} from 'terraso-mobile-client/model/preferences/preferencesSlice'; -const reducers = {map: mapReducer, preferences: preferencesReducer}; +const reducers = { + map: mapReducer, + preferences: preferencesReducer, + elevation: elevationReducer, +}; export type AppState = StateFromStoreFactory & StateFromReducersMapObject;