Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/local storage #75

Merged
merged 10 commits into from
Nov 19, 2023
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
Loading