From 6f4f6302c06c00e97c7011b6c66b2b848dfbdc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Tue, 14 Nov 2023 14:26:37 +0100 Subject: [PATCH 01/10] chore: adding async storage package --- package-lock.json | 31 +++++++++++++++++++++++++++++++ package.json | 3 ++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 4ae2f3f..427a999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/react-native-fontawesome": "^0.3.0", + "@react-native-async-storage/async-storage": "1.18.2", "@sentry/react-native": "5.5.0", "@testing-library/react-native": "^12.3.1", "axios": "^1.6.0", @@ -4550,6 +4551,17 @@ "react": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.18.2.tgz", + "integrity": "sha512-dM8AfdoeIxlh+zqgr0o5+vCTPQ0Ru1mrPzONZMsr7ufp5h+6WgNxQNza7t0r5qQ6b04AJqTlBNixTWZxqP649Q==", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || 0.60 - 0.72 || 1000.0.0" + } + }, "node_modules/@react-native-community/cli": { "version": "11.3.7", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.7.tgz", @@ -12168,6 +12180,14 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -15076,6 +15096,17 @@ "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", diff --git a/package.json b/package.json index 0e9b1f4..02e8fa7 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "react-native-screens": "~3.22.1", "react-native-svg": "13.9.0", "react-native-web": "~0.19.6", - "sentry-expo": "~7.0.0" + "sentry-expo": "~7.0.0", + "@react-native-async-storage/async-storage": "1.18.2" }, "devDependencies": { "@babel/core": "^7.23.2", From 24df2874d68dde6401f7225f63ff7a8a6b7b7d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Tue, 14 Nov 2023 14:54:42 +0100 Subject: [PATCH 02/10] feat: adding store and retrieve functions --- utils/LocalStorage/readLocal.ts | 17 +++++++++++++++++ utils/LocalStorage/storeLocal.ts | 18 ++++++++++++++++++ utils/index.ts | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 utils/LocalStorage/readLocal.ts create mode 100644 utils/LocalStorage/storeLocal.ts diff --git a/utils/LocalStorage/readLocal.ts b/utils/LocalStorage/readLocal.ts new file mode 100644 index 0000000..514785d --- /dev/null +++ b/utils/LocalStorage/readLocal.ts @@ -0,0 +1,17 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import * as Sentry from '@sentry/react-native'; + +/** + * Function to retrieve data from local storage on device. + * + * @param key Key to retrieve data from local storage. + * @returns Data stored under key. + */ +export const getData = async (key: string) => { + try { + const jsonValue = await AsyncStorage.getItem(key); + return jsonValue != null ? JSON.parse(jsonValue) : null; + } catch (error) { + Sentry.captureException(error); + } +}; diff --git a/utils/LocalStorage/storeLocal.ts b/utils/LocalStorage/storeLocal.ts new file mode 100644 index 0000000..dc33fde --- /dev/null +++ b/utils/LocalStorage/storeLocal.ts @@ -0,0 +1,18 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import * as Sentry from '@sentry/react-native'; + +/** + * + * Function to store data in local storage on device. + * + * @param value Data to store in local storage + * @param key Key to store data under + */ +export const storeData = async (value, key: string) => { + try { + const jsonValue = JSON.stringify(value); + await AsyncStorage.setItem(key, jsonValue); + } catch (error) { + Sentry.captureException(error); + } +}; diff --git a/utils/index.ts b/utils/index.ts index df9c92c..53e6c13 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -1,3 +1,5 @@ export { Ballistics } from './Constants'; +export { getData } from './LocalStorage/readLocal'; +export { storeData } from './LocalStorage/storeLocal'; export { formatNumber } from './helpers/formatNumber'; export { default as useBallisticsParams } from './hooks/useBallisticsParams'; From 9f159003b286811b64e2ecf442b92ca6ffc7f3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Tue, 14 Nov 2023 14:54:59 +0100 Subject: [PATCH 03/10] remove log statement --- app/(tabs)/siktemerker/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/(tabs)/siktemerker/index.tsx b/app/(tabs)/siktemerker/index.tsx index 2f33455..aff8793 100644 --- a/app/(tabs)/siktemerker/index.tsx +++ b/app/(tabs)/siktemerker/index.tsx @@ -24,7 +24,6 @@ export default function Calculate() { body.given_marks = calculatedMarks.given_marks; body.given_distances = calculatedMarks.given_distances; } - console.log('body', body); try { const aimMarkResponse = await calculateBallisticsParams(body); From 4ce28a56e673d70041e44d61463d8f0238ef6e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Tue, 14 Nov 2023 19:39:42 +0100 Subject: [PATCH 04/10] feat: adding local storage --- app/(tabs)/siktemerker/index.tsx | 20 ++++++++--------- utils/LocalStorage/storeLocal.ts | 2 +- .../{readLocal.ts => useLocalStorage.ts} | 22 ++++++++++++++++++- utils/index.ts | 4 ++-- 4 files changed, 33 insertions(+), 15 deletions(-) rename utils/LocalStorage/{readLocal.ts => useLocalStorage.ts} (52%) diff --git a/app/(tabs)/siktemerker/index.tsx b/app/(tabs)/siktemerker/index.tsx index aff8793..67f4e38 100644 --- a/app/(tabs)/siktemerker/index.tsx +++ b/app/(tabs)/siktemerker/index.tsx @@ -1,17 +1,16 @@ import * as Sentry from '@sentry/react-native'; -import { useState } from 'react'; import { Keyboard, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'; import { Button, Input } from '../../../components/common'; -import { AimDistanceMark, CalculatedMarks, MarkValue } from '../../../types'; -import { Ballistics, formatNumber, useBallisticsParams } from '../../../utils/'; +import { AimDistanceMark, MarkValue } from '../../../types'; +import { Ballistics, formatNumber, storeLocalStorage, useBallisticsParams, useLocalStorage } from '../../../utils/'; import MarksTable from './MarksTable'; import { useCalcForm } from './useCalcForm'; export default function Calculate() { - const [calculatedMarks, setCalculatedMarks] = useState(null); const { error, status, calculateBallisticsParams } = useBallisticsParams(); const [{ aimError, aimValue, distanceError, distanceValue }, dispatch] = useCalcForm(); + const { data: ballistics } = useLocalStorage('ballistics'); async function sendMarks(newMark: MarkValue) { const body: AimDistanceMark = { @@ -20,16 +19,15 @@ export default function Calculate() { new_given_distance: newMark.distance, }; - if (calculatedMarks) { - body.given_marks = calculatedMarks.given_marks; - body.given_distances = calculatedMarks.given_distances; + if (ballistics) { + body.given_marks = ballistics.given_marks; + body.given_distances = ballistics.given_distances; } try { const aimMarkResponse = await calculateBallisticsParams(body); if (aimMarkResponse) { - setCalculatedMarks(aimMarkResponse); - // TODO: Store in local storage + await storeLocalStorage(aimMarkResponse, 'ballistics'); } } catch (error) { Sentry.captureException(error); @@ -62,7 +60,7 @@ export default function Calculate() { } async function handleRemoveMark(index: number) { - const newDistances = calculatedMarks.given_distances.filter((distance, i) => i === index); + const newDistances = ballistics.given_distances.filter((distance, i) => i === index); await sendMarks({ aim: 9999, distance: newDistances[0] }); } @@ -114,7 +112,7 @@ export default function Calculate() { Obs, noe gikk galt. Prøv igjen senere. )} - + ); diff --git a/utils/LocalStorage/storeLocal.ts b/utils/LocalStorage/storeLocal.ts index dc33fde..fc01467 100644 --- a/utils/LocalStorage/storeLocal.ts +++ b/utils/LocalStorage/storeLocal.ts @@ -8,7 +8,7 @@ import * as Sentry from '@sentry/react-native'; * @param value Data to store in local storage * @param key Key to store data under */ -export const storeData = async (value, key: string) => { +export const storeLocalStorage = async (value, key: string) => { try { const jsonValue = JSON.stringify(value); await AsyncStorage.setItem(key, jsonValue); diff --git a/utils/LocalStorage/readLocal.ts b/utils/LocalStorage/useLocalStorage.ts similarity index 52% rename from utils/LocalStorage/readLocal.ts rename to utils/LocalStorage/useLocalStorage.ts index 514785d..6a96567 100644 --- a/utils/LocalStorage/readLocal.ts +++ b/utils/LocalStorage/useLocalStorage.ts @@ -1,5 +1,6 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import * as Sentry from '@sentry/react-native'; +import { useEffect, useState } from 'react'; /** * Function to retrieve data from local storage on device. @@ -7,7 +8,7 @@ import * as Sentry from '@sentry/react-native'; * @param key Key to retrieve data from local storage. * @returns Data stored under key. */ -export const getData = async (key: string) => { +const getLocalStorage = async (key: string) => { try { const jsonValue = await AsyncStorage.getItem(key); return jsonValue != null ? JSON.parse(jsonValue) : null; @@ -15,3 +16,22 @@ export const getData = async (key: string) => { Sentry.captureException(error); } }; + +const useLocalStorage = (key: string) => { + const [data, setData] = useState(null); + + useEffect(() => { + const getData = async () => { + const data = await getLocalStorage(key); + setData(data); + }; + getData(); + }, [key]); + + return { + data, + getLocalStorage, + }; +}; + +export default useLocalStorage; diff --git a/utils/index.ts b/utils/index.ts index 53e6c13..b5eb030 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -1,5 +1,5 @@ export { Ballistics } from './Constants'; -export { getData } from './LocalStorage/readLocal'; -export { storeData } from './LocalStorage/storeLocal'; +export { storeLocalStorage } from './LocalStorage/storeLocal'; +export { default as useLocalStorage } from './LocalStorage/useLocalStorage'; export { formatNumber } from './helpers/formatNumber'; export { default as useBallisticsParams } from './hooks/useBallisticsParams'; From 3852ea3a79adcfbdf2287e588688be8130184d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Sun, 19 Nov 2023 14:23:41 +0100 Subject: [PATCH 05/10] fix: adding types to local storage --- app/(tabs)/siktemerker/index.tsx | 6 +++--- utils/LocalStorage/useLocalStorage.ts | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/(tabs)/siktemerker/index.tsx b/app/(tabs)/siktemerker/index.tsx index 67f4e38..3dd6b38 100644 --- a/app/(tabs)/siktemerker/index.tsx +++ b/app/(tabs)/siktemerker/index.tsx @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react-native'; import { Keyboard, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'; import { Button, Input } from '../../../components/common'; -import { AimDistanceMark, MarkValue } from '../../../types'; +import { AimDistanceMark, CalculatedMarks, MarkValue } from '../../../types'; import { Ballistics, formatNumber, storeLocalStorage, useBallisticsParams, useLocalStorage } from '../../../utils/'; import MarksTable from './MarksTable'; import { useCalcForm } from './useCalcForm'; @@ -10,7 +10,7 @@ import { useCalcForm } from './useCalcForm'; export default function Calculate() { const { error, status, calculateBallisticsParams } = useBallisticsParams(); const [{ aimError, aimValue, distanceError, distanceValue }, dispatch] = useCalcForm(); - const { data: ballistics } = useLocalStorage('ballistics'); + const { data: ballistics } = useLocalStorage('ballistics'); async function sendMarks(newMark: MarkValue) { const body: AimDistanceMark = { @@ -60,7 +60,7 @@ export default function Calculate() { } async function handleRemoveMark(index: number) { - const newDistances = ballistics.given_distances.filter((distance, i) => i === index); + const newDistances = ballistics.given_distances.filter((_distance: any, i: number) => i === index); await sendMarks({ aim: 9999, distance: newDistances[0] }); } diff --git a/utils/LocalStorage/useLocalStorage.ts b/utils/LocalStorage/useLocalStorage.ts index 6a96567..908a8f0 100644 --- a/utils/LocalStorage/useLocalStorage.ts +++ b/utils/LocalStorage/useLocalStorage.ts @@ -8,21 +8,22 @@ import { useEffect, useState } from 'react'; * @param key Key to retrieve data from local storage. * @returns Data stored under key. */ -const getLocalStorage = async (key: string) => { +const getLocalStorage = async (key: string): Promise => { try { const jsonValue = await AsyncStorage.getItem(key); return jsonValue != null ? JSON.parse(jsonValue) : null; } catch (error) { Sentry.captureException(error); + return null; } }; -const useLocalStorage = (key: string) => { - const [data, setData] = useState(null); +const useLocalStorage = (key: string) => { + const [data, setData] = useState(null); useEffect(() => { const getData = async () => { - const data = await getLocalStorage(key); + const data = await getLocalStorage(key); setData(data); }; getData(); From f1083955ba4e321b49fe9abb8ce99ba6d972ab0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Sun, 19 Nov 2023 17:12:18 +0100 Subject: [PATCH 06/10] fix: move getLocalState to api dir - remove useLocalStorage hook - use new fetching logic --- app/(tabs)/siktemerker/index.tsx | 18 ++++++++++++++--- ...{useLocalStorage.ts => getLocalStorage.ts} | 20 +------------------ utils/index.ts | 2 +- 3 files changed, 17 insertions(+), 23 deletions(-) rename utils/LocalStorage/{useLocalStorage.ts => getLocalStorage.ts} (58%) diff --git a/app/(tabs)/siktemerker/index.tsx b/app/(tabs)/siktemerker/index.tsx index 3dd6b38..7373ee8 100644 --- a/app/(tabs)/siktemerker/index.tsx +++ b/app/(tabs)/siktemerker/index.tsx @@ -1,16 +1,25 @@ import * as Sentry from '@sentry/react-native'; +import { useEffect, useState } from 'react'; import { Keyboard, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'; import { Button, Input } from '../../../components/common'; import { AimDistanceMark, CalculatedMarks, MarkValue } from '../../../types'; -import { Ballistics, formatNumber, storeLocalStorage, useBallisticsParams, useLocalStorage } from '../../../utils/'; +import { Ballistics, formatNumber, getLocalStorage, storeLocalStorage, useBallisticsParams } from '../../../utils/'; import MarksTable from './MarksTable'; import { useCalcForm } from './useCalcForm'; export default function Calculate() { + const [ballistics, setBallistics] = useState(null); const { error, status, calculateBallisticsParams } = useBallisticsParams(); const [{ aimError, aimValue, distanceError, distanceValue }, dispatch] = useCalcForm(); - const { data: ballistics } = useLocalStorage('ballistics'); + + useEffect(() => { + getLocalStorage('ballistics').then((data) => { + if (data) { + setBallistics(data); + } + }); + }, []); async function sendMarks(newMark: MarkValue) { const body: AimDistanceMark = { @@ -27,7 +36,10 @@ export default function Calculate() { try { const aimMarkResponse = await calculateBallisticsParams(body); if (aimMarkResponse) { - await storeLocalStorage(aimMarkResponse, 'ballistics'); + storeLocalStorage(aimMarkResponse, 'ballistics').then(async () => { + const ballisticsData = await getLocalStorage('ballistics'); + setBallistics(ballisticsData); + }); } } catch (error) { Sentry.captureException(error); diff --git a/utils/LocalStorage/useLocalStorage.ts b/utils/LocalStorage/getLocalStorage.ts similarity index 58% rename from utils/LocalStorage/useLocalStorage.ts rename to utils/LocalStorage/getLocalStorage.ts index 908a8f0..f7b398f 100644 --- a/utils/LocalStorage/useLocalStorage.ts +++ b/utils/LocalStorage/getLocalStorage.ts @@ -1,6 +1,5 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import * as Sentry from '@sentry/react-native'; -import { useEffect, useState } from 'react'; /** * Function to retrieve data from local storage on device. @@ -18,21 +17,4 @@ const getLocalStorage = async (key: string): Promise => { } }; -const useLocalStorage = (key: string) => { - const [data, setData] = useState(null); - - useEffect(() => { - const getData = async () => { - const data = await getLocalStorage(key); - setData(data); - }; - getData(); - }, [key]); - - return { - data, - getLocalStorage, - }; -}; - -export default useLocalStorage; +export default getLocalStorage; diff --git a/utils/index.ts b/utils/index.ts index b5eb030..65e6ff3 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -1,5 +1,5 @@ export { Ballistics } from './Constants'; +export { default as getLocalStorage } from './LocalStorage/getLocalStorage'; export { storeLocalStorage } from './LocalStorage/storeLocal'; -export { default as useLocalStorage } from './LocalStorage/useLocalStorage'; export { formatNumber } from './helpers/formatNumber'; export { default as useBallisticsParams } from './hooks/useBallisticsParams'; From b6ca5be003399a7797c2f434661caa0006b5c99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Haakon=20Helmen=20Rus=C3=A5s?= Date: Sun, 19 Nov 2023 17:35:25 +0100 Subject: [PATCH 07/10] fix: refactor marks form to seperate component --- .../siktemerker/components/MarksForm.tsx | 94 +++++++++++++++++++ .../{ => components}/MarksTable.tsx | 4 +- app/(tabs)/siktemerker/index.tsx | 79 +--------------- 3 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 app/(tabs)/siktemerker/components/MarksForm.tsx rename app/(tabs)/siktemerker/{ => components}/MarksTable.tsx (94%) diff --git a/app/(tabs)/siktemerker/components/MarksForm.tsx b/app/(tabs)/siktemerker/components/MarksForm.tsx new file mode 100644 index 0000000..cc925d5 --- /dev/null +++ b/app/(tabs)/siktemerker/components/MarksForm.tsx @@ -0,0 +1,94 @@ +import React, { FC } from 'react'; +import { Keyboard, StyleSheet, View } from 'react-native'; + +import { Button, Input } from '../../../../components/common'; +import { MarkValue } from '../../../../types'; +import { formatNumber } from '../../../../utils'; +import { useCalcForm } from '../useCalcForm'; + +interface MarksFormProps { + status: string; + sendMarks: (newMark: MarkValue) => Promise; +} + +const MarksForm: FC = ({ sendMarks, status }) => { + const [{ aimError, aimValue, distanceError, distanceValue }, dispatch] = useCalcForm(); + + function handleDistanceChange(value: string) { + dispatch({ type: 'SET_DISTANCE_VALUE', payload: value }); + } + + function handleAimChange(value: string) { + dispatch({ type: 'SET_AIM_VALUE', payload: value }); + } + + async function handleAddMark() { + if (!aimValue) { + dispatch({ type: 'SET_AIM_ERROR', payload: true }); + } + if (!distanceValue) { + dispatch({ type: 'SET_DISTANCE_ERROR', payload: true }); + } + if (aimValue && distanceValue) { + const newEntry: MarkValue = { aim: parseFloat(aimValue), distance: parseFloat(distanceValue) }; + + await sendMarks(newEntry); + dispatch({ type: 'SET_AIM_VALUE', payload: '' }); + dispatch({ type: 'SET_DISTANCE_VALUE', payload: '' }); + Keyboard.dismiss(); + } + } + + return ( + + + dispatch({ type: 'SET_DISTANCE_ERROR', payload: false })} + placeholderText="F.eks. 20" + keyboardType="numeric" + error={distanceError} + errorMessage="Fyll inn avstand" + value={distanceValue} + onChangeText={(value) => handleDistanceChange(formatNumber(value))} + /> + + + dispatch({ type: 'SET_AIM_ERROR', payload: false })} + placeholderText="F.eks. 2.35" + keyboardType="numeric" + value={aimValue} + error={aimError} + errorMessage="Fyll inn siktemerke" + onChangeText={(value) => handleAimChange(formatNumber(value))} + /> + +