Skip to content

Commit

Permalink
Feature/local storage (#75)
Browse files Browse the repository at this point in the history
* chore: adding async  storage package

* feat: adding store and retrieve functions

* remove log statement

* feat: adding local storage

* fix: adding types to local storage

* fix: move getLocalState to api dir
- remove useLocalStorage hook
- use new fetching logic

* fix: refactor marks form to seperate component

* fix: typo corrected by wife

* styles: general style changes

* styles: better table styling
  • Loading branch information
haakonhelmenrusas authored Nov 19, 2023
1 parent 4076e80 commit 64c4d83
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 101 deletions.
93 changes: 93 additions & 0 deletions app/(tabs)/siktemerker/components/MarksForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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<void>;
}

const MarksForm: FC<MarksFormProps> = ({ 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 (
<View style={styles.form}>
<View>
<Input
textAlign="center"
maxLength={100}
label="Avstand"
onBlur={() => dispatch({ type: 'SET_DISTANCE_ERROR', payload: false })}
placeholderText="F.eks. 20"
keyboardType="numeric"
error={distanceError}
errorMessage="Fyll inn"
value={distanceValue}
onChangeText={(value) => handleDistanceChange(formatNumber(value))}
/>
</View>
<View>
<Input
textAlign="center"
maxLength={15}
label="Merke"
onBlur={() => dispatch({ type: 'SET_AIM_ERROR', payload: false })}
placeholderText="F.eks. 2.3"
keyboardType="numeric"
value={aimValue}
error={aimError}
errorMessage="Fyll inn"
onChangeText={(value) => handleAimChange(formatNumber(value))}
/>
</View>
<Button
type="filled"
width={100}
loading={status === 'pending'}
buttonStyle={{ marginLeft: 'auto', marginTop: 16 }}
onPress={handleAddMark}
label="Beregn"
/>
</View>
);
};

const styles = StyleSheet.create({
form: {
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-end',
marginBottom: 32,
},
});

export default MarksForm;
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { faTrash } from '@fortawesome/free-solid-svg-icons/faTrash';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { StyleSheet, Text, View } from 'react-native';
import Button from '../../../components/common/Button';
import { CalculatedMarks } from '../../../types';
import Button from '../../../../components/common/Button';
import { CalculatedMarks } from '../../../../types';

interface CalculationTableProps {
ballistics: CalculatedMarks | null;
Expand All @@ -13,10 +13,10 @@ export default function MarksTable({ ballistics, removeMark }: CalculationTableP
const renderBallisticTable = () => {
if (ballistics) {
return ballistics.given_distances.map((distance, index) => (
<View style={styles.tr} key={index}>
<Text>{distance.toFixed(1)}m</Text>
<Text>{ballistics.given_marks[index].toFixed(2)}</Text>
<Text>{ballistics.calculated_marks[index].toFixed(2)}</Text>
<View style={[styles.tr, index % 2 === 0 ? styles.evenRow : styles.oddRow]} key={index}>
<Text style={styles.trData}>{distance.toFixed(1)}m</Text>
<Text style={styles.trData}>{ballistics.given_marks[index].toFixed(2)}</Text>
<Text style={styles.trData}>{ballistics.calculated_marks[index].toFixed(2)}</Text>
<Button
icon={<FontAwesomeIcon icon={faTrash} color="red" />}
label="Fjern"
Expand All @@ -32,11 +32,11 @@ export default function MarksTable({ ballistics, removeMark }: CalculationTableP

return (
<View>
<View style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }}>
<Text style={styles.thead}>Avstand</Text>
<Text style={styles.thead}>Merke</Text>
<Text style={styles.thead}>Beregnet</Text>
<Text style={styles.thead}></Text>
<View style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<Text style={[styles.thead, styles.heading]}>Avstand</Text>
<Text style={[styles.thead, styles.heading]}>Merke</Text>
<Text style={[styles.thead, styles.heading]}>Beregnet</Text>
<Text style={styles.theadEnd}></Text>
</View>
{renderBallisticTable()}
</View>
Expand All @@ -49,13 +49,31 @@ const styles = StyleSheet.create({
height: 48,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
justifyContent: 'space-between',
},
trData: {
flex: 1,
textAlign: 'center',
},
evenRow: {
backgroundColor: '#F8F8F8',
},
oddRow: {
backgroundColor: '#FFF',
},
thead: {
fontWeight: 'bold',
fontSize: 16,
marginTop: 8,
},
theadEnd: {
width: '20%',
},
heading: {
flex: 1,
textAlign: 'center',
marginBottom: 8,
},
info: {
marginTop: 8,
fontSize: 16,
Expand Down
110 changes: 24 additions & 86 deletions app/(tabs)/siktemerker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import * as Sentry from '@sentry/react-native';
import { useState } from 'react';
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, useBallisticsParams } from '../../../utils/';
import MarksTable from './MarksTable';
import { useCalcForm } from './useCalcForm';
import { Ballistics, getLocalStorage, storeLocalStorage, useBallisticsParams } from '../../../utils/';
import MarksForm from './components/MarksForm';
import MarksTable from './components/MarksTable';

export default function Calculate() {
const [calculatedMarks, setCalculatedMarks] = useState<CalculatedMarks>(null);
const [ballistics, setBallistics] = useState<CalculatedMarks | null>(null);
const { error, status, calculateBallisticsParams } = useBallisticsParams();
const [{ aimError, aimValue, distanceError, distanceValue }, dispatch] = useCalcForm();

useEffect(() => {
getLocalStorage<CalculatedMarks>('ballistics').then((data) => {
if (data) {
setBallistics(data);
}
});
}, []);

async function sendMarks(newMark: MarkValue) {
const body: AimDistanceMark = {
Expand All @@ -20,50 +26,26 @@ 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;
}
console.log('body', body);

try {
const aimMarkResponse = await calculateBallisticsParams(body);
if (aimMarkResponse) {
setCalculatedMarks(aimMarkResponse);
// TODO: Store in local storage
storeLocalStorage(aimMarkResponse, 'ballistics').then(async () => {
const ballisticsData = await getLocalStorage<CalculatedMarks>('ballistics');
setBallistics(ballisticsData);
});
}
} catch (error) {
Sentry.captureException(error);
}
}

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();
}
}

async function handleRemoveMark(index: number) {
const newDistances = calculatedMarks.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] });
}
Expand All @@ -72,50 +54,13 @@ export default function Calculate() {
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<View style={{ flex: 1 }}>
<Text style={styles.title}>Siktemerker</Text>
<View style={styles.form}>
<View>
<Input
textAlign="right"
maxLength={100}
label="Avstand"
onBlur={() => 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))}
/>
</View>
<View>
<Input
textAlign="right"
maxLength={15}
label="Merke"
onBlur={() => 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))}
/>
</View>
<Button
type="filled"
width={100}
loading={status === 'pending'}
buttonStyle={{ marginLeft: 'auto', marginTop: 16 }}
onPress={handleAddMark}
label="Beregn"
/>
</View>
<MarksForm sendMarks={sendMarks} status={status} />
{error && (
<>
<View style={{ marginBottom: 8, padding: 8 }}>Obs, noe gikk galt. Prøv igjen senere.</View>
<View style={{ marginBottom: 8, padding: 8 }}>Oisann, noe gikk galt. Prøv igjen senere.</View>
</>
)}
<MarksTable ballistics={calculatedMarks} removeMark={handleRemoveMark} />
<MarksTable ballistics={ballistics} removeMark={handleRemoveMark} />
</View>
</TouchableWithoutFeedback>
);
Expand All @@ -127,11 +72,4 @@ const styles = StyleSheet.create({
marginBottom: 16,
marginTop: 16,
},
form: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
});
4 changes: 2 additions & 2 deletions components/common/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const Input = ({ label, error, errorMessage, placeholderText, ...props }: InputP
const textInputColor = editableTextInputColor;

const styles = StyleSheet.create({
container: { marginBottom: 16, height: 54 },
container: { marginBottom: 16, height: 54, marginRight: 8 },
label: { color: textInputColor, fontWeight: '500', fontSize: 16, marginBottom: 4 },
input: {
backgroundColor: '#FFF',
Expand All @@ -24,7 +24,7 @@ const Input = ({ label, error, errorMessage, placeholderText, ...props }: InputP
width: '100%',
borderColor: textInputColor,
borderWidth: 1,
borderRadius: 4,
borderRadius: 12,
},
});

Expand Down
Loading

0 comments on commit 64c4d83

Please sign in to comment.