Skip to content

Commit

Permalink
wrap-up for today
Browse files Browse the repository at this point in the history
  • Loading branch information
Yev Moroz committed Jan 29, 2024
1 parent 2bc3c25 commit f36092e
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 27 deletions.
5 changes: 4 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ Following are consideraitons that can be made for an app that helps with EV char
-- [ ] on a map (via RNMapBox)
- [ ] call maps app to get directions
- [x] start charging for select station
- [x] refresh list on pull-down
- [x] refresh list of charging stations on pull-down
- [ ] user-friendly API handling
- [ ] consider key storage for list of charging stations
- [ ] more testing for utils and components
- [ ] consider react-navigation to better structure moving around

### References
- [Expo](https://expo.dev/)
Expand Down
68 changes: 58 additions & 10 deletions src/features/charging-station/charging-station-details/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Ionicons from '@expo/vector-icons/Ionicons';
import { View, StyleSheet, Text } from 'react-native';
import { View, StyleSheet, Text, Pressable } from 'react-native';

import {
BORDER_RADIUS_S,
Expand All @@ -11,37 +11,81 @@ import {
PAD_XXL,
} from '../../theme/common';
import { Theme, useTheme } from '../../theme/hooks';
import { ChargingStationDataItem } from '../hooks';
import { ChargingStationDataItem, useStartCharging } from '../hooks';

type Props = {
item: ChargingStationDataItem;
isActive: boolean;
onStart: (id: number) => void;
onBack: () => void;
};

export const ChargingStationDetails = (props: Props) => {
const styles = useTheme(themeableStyles);
const [chargingError, startCharging] = useStartCharging(props.item.id, props.onStart);

return (
<View style={styles.listItem}>
<Ionicons style={styles.icon} name="location-sharp" size={32} color={styles.icon.color} />
<View>
<Text style={styles.title}>{props.item.addressInfo.title}</Text>
<Text style={styles.small}>{`${props.item.addressInfo.distance.toFixed(2)} km away`}</Text>
<View style={styles.container}>
<Pressable onPress={props.onBack}>
<View style={styles.row}>
<Ionicons style={styles.icon} name="arrow-back" size={32} color={styles.icon.color} />
<Text style={styles.back}>go back to all</Text>
</View>
</Pressable>
<View style={styles.row}>
<Ionicons style={styles.icon} name="location-sharp" size={32} color={styles.icon.color} />
<View>
<Text style={styles.title}>{props.item.addressInfo.title}</Text>
<Text
style={styles.small}>{`${props.item.addressInfo.distance.toFixed(2)} km away`}</Text>
</View>
</View>
{props.isActive && (
<View style={styles.row}>
<View>
<Text style={styles.title}>Charging...</Text>
</View>
</View>
)}
{!props.isActive && !chargingError && (
<Pressable onPress={startCharging}>
<View style={styles.row}>
<Ionicons style={styles.icon} name="flash" size={32} color={styles.icon.color} />
<View>
<Text style={styles.title}>Start Charging!</Text>
</View>
</View>
</Pressable>
)}
{chargingError && (
<View style={styles.row}>
<Ionicons style={styles.icon} name="warning" size={32} color={styles.icon.color} />
<View>
<Text style={styles.title}>Charging is currently unavailable</Text>
</View>
</View>
)}
</View>
);
};

const themeableStyles = (theme: Theme) =>
StyleSheet.create({
listItem: {
container: {
marginHorizontal: PAD_XXL,
marginVertical: PAD_S,
paddingHorizontal: PAD_S,
paddingVertical: PAD_L,
backgroundColor: theme.colors.PRIMARY,
borderRadius: BORDER_RADIUS_S,
},
row: {
paddingHorizontal: PAD_S,
paddingVertical: PAD_L,
flexDirection: 'row',
},
back: {
alignSelf: 'center',
color: theme.colors.SECONDARY,
},
icon: {
marginRight: PAD_M,
alignSelf: 'center',
Expand All @@ -55,4 +99,8 @@ const themeableStyles = (theme: Theme) =>
small: {
color: theme.colors.SECONDARY,
},
start: {
alignSelf: 'center',
color: theme.colors.SECONDARY,
},
});
20 changes: 14 additions & 6 deletions src/features/charging-station/charging-station-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,29 @@ type Props = {
};

export const ChargingStationList = (props: Props) => {
const [selectedChargingStationId, setSelectedChargingStationId] = useState<number | null>(null);
const [activeChargingStationId, setActiveChargingStationId] = useState<number | null>(null);
const activeChargingStation = useMemo(
() => props.items?.find((item) => item.id === activeChargingStationId),
[activeChargingStationId]
const selectedChargingStation = useMemo(
() => props.items?.find((item) => item.id === selectedChargingStationId),
[selectedChargingStationId]
);

if (activeChargingStation) {
return <ChargingStationDetails item={activeChargingStation} />;
if (selectedChargingStation) {
return (
<ChargingStationDetails
item={selectedChargingStation}
isActive={activeChargingStationId === selectedChargingStationId}
onBack={() => setSelectedChargingStationId(null)}
onStart={setActiveChargingStationId}
/>
);
}

return (
<FlatList
data={props.items}
renderItem={({ item }) => (
<ChargingStationListItem item={item} onPress={setActiveChargingStationId} />
<ChargingStationListItem item={item} onPress={setSelectedChargingStationId} />
)}
onRefresh={props.onRefresh}
refreshing={props.refreshing}
Expand Down
32 changes: 22 additions & 10 deletions src/features/charging-station/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type OptionalApiError = ApiError | null;
type ChargingStationsResponse = ChargingStationDataItem[];
type OptionalChargingStationsResponse = ChargingStationsResponse | null;

/**
* Charging locations are fetched based on given coordinates
* when both latitude and longitude are available
*/
export const useChargingStationsByLocation = (
location: OptionalLocationDetails,
rangeInKm: number = 5
Expand All @@ -42,15 +46,23 @@ export const useChargingStationsByLocation = (
return [result, error, resultNotReady];
};

export const useStartCharging = () => {
const [result, setResult] = useState<OptionalChargingStationsResponse>(null);
const [error, setError] = useState<OptionalApiError>(null);

const onStart = useCallback((chargerId) => {
fetchEvEnergyApi('chargingsession', 'POST', { user: 1, car_id: 1, charger_id: chargerId })
.then(setResult)
.catch(setError);
}, []);
/**
* Charging will start when startCharging callback is triggered
*/
export const useStartCharging = (
chargerId: number,
onChargingStarted: (chargerId: number) => void
): [ApiError | null, () => void] => {
const [chargingError, setChargingError] = useState<ApiError | null>(null);

return [result, error, onStart];
const startCharging = useCallback(() => {
return fetchEvEnergyApi('chargingsession', 'POST', {
user: 1,
car_id: 1,
charger_id: chargerId,
})
.then(() => onChargingStarted(chargerId))
.catch(setChargingError);
}, [chargerId]);
return [chargingError, startCharging];
};

0 comments on commit f36092e

Please sign in to comment.