diff --git a/android/app/src/main/res/drawable-hdpi/loader.gif b/android/app/src/main/res/drawable-hdpi/loader.gif deleted file mode 100644 index 9abab46..0000000 Binary files a/android/app/src/main/res/drawable-hdpi/loader.gif and /dev/null differ diff --git a/android/app/src/main/res/drawable-mdpi/loader.gif b/android/app/src/main/res/drawable-mdpi/loader.gif deleted file mode 100644 index 9abab46..0000000 Binary files a/android/app/src/main/res/drawable-mdpi/loader.gif and /dev/null differ diff --git a/android/app/src/main/res/drawable-xhdpi/loader.gif b/android/app/src/main/res/drawable-xhdpi/loader.gif deleted file mode 100644 index 9abab46..0000000 Binary files a/android/app/src/main/res/drawable-xhdpi/loader.gif and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxhdpi/loader.gif b/android/app/src/main/res/drawable-xxhdpi/loader.gif deleted file mode 100644 index 9abab46..0000000 Binary files a/android/app/src/main/res/drawable-xxhdpi/loader.gif and /dev/null differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/loader.gif b/android/app/src/main/res/drawable-xxxhdpi/loader.gif deleted file mode 100644 index 9abab46..0000000 Binary files a/android/app/src/main/res/drawable-xxxhdpi/loader.gif and /dev/null differ diff --git a/android/app/src/main/res/drawable/background_splash.xml b/android/app/src/main/res/drawable/background_splash.xml index 78316f2..f0ce332 100644 --- a/android/app/src/main/res/drawable/background_splash.xml +++ b/android/app/src/main/res/drawable/background_splash.xml @@ -10,25 +10,16 @@ /> - - diff --git a/android/app/src/main/res/drawable/loader.gif b/android/app/src/main/res/drawable/loader.gif new file mode 100644 index 0000000..87f9ac0 Binary files /dev/null and b/android/app/src/main/res/drawable/loader.gif differ diff --git a/android/app/src/main/res/layout/launch_screen.xml b/android/app/src/main/res/layout/launch_screen.xml index 75f3ffc..44af626 100644 --- a/android/app/src/main/res/layout/launch_screen.xml +++ b/android/app/src/main/res/layout/launch_screen.xml @@ -7,14 +7,14 @@ android:layout_height="match_parent" android:layout_gravity="start|end|top|bottom|center|center_horizontal|center_vertical" android:background="@color/dark" - android:fitsSystemWindows="true" + android:statusBarColor="@color/transparent" tools:context=".MainActivity"> { + const forecast = useSelector((state) => state.daily[index]); + console.log('render daily timeline DayCard : ', index); + return ( + +
+ + {index === 0 + ? 'Today' + : index === 1 + ? 'Tomorrow' + : moment(forecast.dt * 1000).format('dddd')} + + + {moment(forecast.dt * 1000).format('D/M')} + + +
+ + + {round(forecast.temp.morn)}° + ~ {round(forecast.feels_like.morn)}° + + + {round(forecast.temp.day)}° + ~ {round(forecast.feels_like.day)}° + + + {round(forecast.temp.eve)}° + ~ {round(forecast.feels_like.eve)}° + + + {round(forecast.temp.night)}° + ~ {round(forecast.feels_like.night)}° + + +
+ ); +}); + +export default ({ dayClicked, onPress }) => { + console.log('render daily timeline : ', dayClicked); + return ( + + + +
+ + + + + + + + {[...Array(8)].map((value, index) => ( + onPress(index)}> + + + ))} + + + ); +}; + +const Container = styled.View` + flex-grow: 1; + flex-direction: row; + justify-content: center; + overflow: hidden; + border-radius: ${RFValue(10)}px; +`; + +const ScrollView = styled.ScrollView.attrs(() => ({ + contentContainerStyle: { + justifyContent: 'space-between', + }, +}))``; + +const column = css` + margin-vertical: ${RFValue(20)}px; + padding-vertical: ${RFValue(20)}px; +`; + +const Column = styled.View` + ${column} + flex: 1; + width: ${RFValue(70)}px; + margin-right: ${RFValue(5)}px; + border-width: ${({ clicked }) => (clicked ? '2px' : '1px')}; + border-color: ${({ clicked, theme }) => + clicked ? theme.colors.primary : theme.colors.border}; + border-radius: ${RFValue(25)}px; + background-color: ${({ theme }) => theme.colors.backgroundAlt}; + /* shadow-opacity: 0.75; + shadow-radius: 5px; + shadow-color: red; + shadow-offset: 0px 0px; + overflow: hidden; */ + elevation: ${({ clicked }) => (clicked ? 4 : 0)}; +`; + +const ColumnFirst = styled.View` + ${column} + width: ${RFValue(40)}px; +`; + +const Text = styled.Text` + text-align: center; + color: ${({ theme }) => theme.colors.text}; + font-size: ${RFValue(17)}px; +`; + +const Header = styled.View` + height: ${RFValue(90)}px; + align-items: center; +`; + +const Content = styled.View` + flex-grow: 1; + justify-content: space-around; + align-items: center; +`; + +const FeatherIcon = styled(Feather).attrs(({ color, theme }) => ({ + size: RFValue(20), + color: color || theme.colors.icon, +}))` + text-align: center; +`; + +const CardHeading = styled(Text)` + font-size: ${RFValue(14)}px; +`; + +const CardSubHeading = styled(Text)` + color: ${({ theme }) => theme.colors.textAlt}; + font-size: ${RFValue(12)}px; +`; + +const WeatherIcon = styled(LottieView)` + width: ${RFValue(40)}px; + height: ${RFValue(40)}px; + padding-top: ${RFValue(5)}px; +`; + +const TimeTempBox = styled.View` + align-items: center; + justify-content: center; +`; + +const Temp = styled(Text)` + font-size: ${RFValue(15)}px; +`; + +const Feels = styled(Text)` + font-size: ${RFValue(11)}px; + color: ${({ theme }) => theme.colors.textAlt}; + padding-right: ${RFValue(9)}px; +`; diff --git a/src/components/Day.jsx b/src/components/Day.jsx index a469fa0..50c0a09 100644 --- a/src/components/Day.jsx +++ b/src/components/Day.jsx @@ -1,32 +1,38 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import styled, { css } from 'styled-components/native'; import { RFValue } from 'react-native-responsive-fontsize'; import LottieView from 'lottie-react-native'; +import moment from 'moment'; +import { useSelector } from 'react-redux'; import { round, weatherIcons } from '../utils'; -export default ({ day, forecast }) => { - return ( - - - - {day} - - - {forecast.weather[0].main} - - {round(forecast.temp.max)}° / {round(forecast.temp.min)}° - - - - ); +export default ({ index }) => { + const forecast = useSelector((state) => state.daily[index]); + return useMemo(() => { + console.log('render day : ', index); + return ( + + + + {moment(forecast.dt * 1000).format('dddd')} + + + {forecast.weather[0].main} + + {round(forecast.temp.max)}° / {round(forecast.temp.min)}° + + + + ); + }, [forecast]); }; const Container = styled.View` @@ -50,6 +56,7 @@ const DayIcon = styled.View` const WeatherIcon = styled(LottieView)` width: ${RFValue(30)}px; height: ${RFValue(30)}px; + elevation: 4; `; const text = css` diff --git a/src/components/DayDetailsBox.jsx b/src/components/DayForecastDetails.jsx similarity index 61% rename from src/components/DayDetailsBox.jsx rename to src/components/DayForecastDetails.jsx index 59aef45..4e33577 100644 --- a/src/components/DayDetailsBox.jsx +++ b/src/components/DayForecastDetails.jsx @@ -1,35 +1,73 @@ -import React, { useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import styled from 'styled-components/native'; import { RFValue } from 'react-native-responsive-fontsize'; import Ionicons from 'react-native-vector-icons/Ionicons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import Fontisto from 'react-native-vector-icons/Fontisto'; +import Feather from 'react-native-vector-icons/Feather'; import { ThemeContext } from 'styled-components'; import { useSelector } from 'react-redux'; +import moment from 'moment'; -const Card = ({ heading, sunHeading, icon }) => { - return ( - - - {heading} - {sunHeading} - - {icon} - - ); +const Card = ({ heading, subHeading, icon }) => { + return useMemo(() => { + console.log('render day forecast Deails : ', heading); + return ( + + + {heading} + {subHeading} + + {icon} + + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [subHeading]); }; -export default ({ forecast }) => { +export default ({ index }) => { + const forecast = useSelector((state) => state.daily[index]); + const timeFormat = useSelector((state) => state.timeFormat); const units = useSelector((state) => state.units); const themeContext = useContext(ThemeContext); const iconSize = RFValue(30); + + const timeString = (sec) => { + if (timeFormat === '24') return moment(sec * 1000).format('HH:mm'); + return moment(sec * 1000).format('h:m A'); + }; + return ( - <> + + + + } + /> + + } + /> + { /> { { /> { { /> { } /> - + ); }; +const Container = styled.View` + margin: ${RFValue(20)}px; + margin-top: 0px; +`; + const BoxRow = styled.View` flex-direction: row; height: ${RFValue(60)}px; diff --git a/src/components/DropDownSettings.jsx b/src/components/DropDownSettings.jsx index 81b93e9..82a967d 100644 --- a/src/components/DropDownSettings.jsx +++ b/src/components/DropDownSettings.jsx @@ -119,7 +119,7 @@ const DropSetting = styled.View` flex-direction: row; justify-content: space-between; align-items: center; - height: ${RFValue(45)}px; + height: ${RFValue(40)}px; margin-bottom: ${RFValue(5)}px; border-radius: 30px; `; diff --git a/src/components/Error.jsx b/src/components/Error.jsx index 11e5313..3f68220 100644 --- a/src/components/Error.jsx +++ b/src/components/Error.jsx @@ -1,14 +1,19 @@ -import React, { useContext } from 'react'; +import React, { useEffect, useContext } from 'react'; import styled from 'styled-components/native'; import { RFValue } from 'react-native-responsive-fontsize'; import { useSelector } from 'react-redux'; import { ThemeContext } from 'styled-components'; +import SplashScreen from 'react-native-splash-screen'; export default ({ getWeather }) => { const error = useSelector((state) => state.error); const themeContext = useContext(ThemeContext); + useEffect(() => { + SplashScreen.hide(); + }, []); + return ( {error} diff --git a/src/components/HourlyTimeline.jsx b/src/components/HourlyTimeline.jsx index 22b9c38..d13f79c 100644 --- a/src/components/HourlyTimeline.jsx +++ b/src/components/HourlyTimeline.jsx @@ -1,88 +1,128 @@ -import React from 'react'; -import { Dimensions } from 'react-native'; +import React, { useEffect, useMemo } from 'react'; import styled from 'styled-components/native'; import { LineChart } from 'react-native-chart-kit'; import { RFValue } from 'react-native-responsive-fontsize'; import { useSelector } from 'react-redux'; import moment from 'moment'; -import { Svg, Text as TextSVG } from 'react-native-svg'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; +import SplashScreen from 'react-native-splash-screen'; -import { round, weatherIcons, rootNavigation } from '../utils'; +import { round, weatherIcons } from '../utils'; export default () => { const timeFormat = useSelector((state) => state.timeFormat); const hourly = useSelector((state) => state.forecast.hourly); + const COLOR = 'blue'; + + useEffect(() => { + SplashScreen.hide(); + }, []); const timeString = (sec) => { if (timeFormat === '24') return moment(sec * 1000).format('HH:mm'); - return moment(sec * 1000).format('hh:mm a'); + return moment(sec * 1000).format('h A'); }; + const chartData = { - labels: hourly.map((item) => timeString(item.dt)), + labels: hourly.map((item, index) => + index === 24 ? 'Tomorrow' : timeString(item.dt) + ), datasets: [ { data: hourly.map((item) => round(item.temp)), - color: (opacity = 0.5) => `rgba(255, 255, 255, ${opacity})`, }, ], legend: [], }; - const chartConfig = { - backgroundColor: '#005aae', - backgroundGradientFrom: '#006ace', - backgroundGradientTo: '#40a1fc', - decimalPlaces: 2, - color: (opacity = 0) => `rgba(255, 255, 255, ${opacity})`, + + const colors = { + blue: { + backgroundColor: '#005aae', + backgroundGradientFrom: '#006ace', + backgroundGradientTo: '#40a1fc', + }, + orange: { + backgroundColor: '#e26a00', + backgroundGradientFrom: '#fb8c00', + backgroundGradientTo: '#ffa726', + fillShadowGradient: '#000', + }, + }; + + const config = { + decimalPlaces: 0, + fillShadowGradientOpacity: 0.12, + color: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`, labelColor: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`, style: {}, propsForDots: { r: '4', strokeWidth: '2', - stroke: '#40a1fc', }, propsForLabels: { fontSize: RFValue(9) }, propsForVerticalLabels: { fontWeight: 'bold', fontSize: RFValue(8) }, }; - return ( - - {/* Hourly forecast */} - - { - return ( - - {indexData}° - - ); - }} - formatYLabel={(label) => round(label)} - style={{}} - /> - - + + const chartConfig = (color) => ({ + ...config, + ...colors[color || COLOR], + propsForDots: { + ...config.propsForDots, + stroke: colors[color || COLOR].backgroundGradientTo, + }, + }); + + const renderDotContent = ({ x, y, index, indexData }) => ( + + {indexData}° + + ); + + return useMemo(() => { + console.log('render hourly timeline'); + return ( + + {/* Hourly forecast */} + + ({ + r: index === 24 ? '6' : '4', + strokeWidth: '2', + stroke: colors['orange'].backgroundGradientTo, + })} + /> + + + ); + }, [hourly]); }; const Container = styled.View` @@ -107,3 +147,18 @@ const HeadingText = styled.Text` padding-top: ${RFValue(5)}px; font-weight: bold; `; + +const DotView = styled.View` + height: ${RFValue(45)}px; + justify-content: space-between; + position: absolute; + top: ${({ y }) => y + RFValue(4)}px; + left: ${({ x }) => x - RFValue(6)}px; +`; + +const DotText = styled.Text` + padding-left: 2px; + color: white; + font-size: ${RFValue(10)}px; + font-weight: bold; +`; diff --git a/src/components/LiveTime.jsx b/src/components/LiveTime.jsx index d935bf2..533b8c4 100644 --- a/src/components/LiveTime.jsx +++ b/src/components/LiveTime.jsx @@ -6,6 +6,7 @@ import { RFValue } from 'react-native-responsive-fontsize'; import { useSelector } from 'react-redux'; export default () => { + const lastFetched = useSelector((state) => state.lastFetched); const timeFormat = useSelector((state) => state.timeFormat); const [currTime, setCurrTime] = useState(moment()); @@ -17,14 +18,25 @@ export default () => { }, []); return ( - + <> + {'Updated ' + moment(lastFetched).fromNow()} + {/* */} + ); }; +const LastFetched = styled.Text` + justify-content: center; + padding: ${RFValue(5)}px; + color: ${({ theme }) => theme.colors.textAlt}; + font-size: ${RFValue(10)}px; + padding-bottom: ${RFValue(20)}px; +`; + const Time = styled.Text` justify-content: center; padding: ${RFValue(5)}px; diff --git a/src/components/Today.jsx b/src/components/Today.jsx index a3bd33e..175926a 100644 --- a/src/components/Today.jsx +++ b/src/components/Today.jsx @@ -1,70 +1,71 @@ -import React, { useEffect, useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import styled from 'styled-components/native'; import { RFValue } from 'react-native-responsive-fontsize'; import LottieView from 'lottie-react-native'; import { useSelector } from 'react-redux'; -import SplashScreen from 'react-native-splash-screen'; + import IconI from 'react-native-vector-icons/Ionicons'; import { ThemeContext } from 'styled-components'; - import LiveTime from './LiveTime'; import { StyledTouchableScale } from './StyledComponents'; import { round, weatherIcons, rootNavigation } from '../utils'; -export default () => { - const weather = useSelector((state) => state.weather); - const forecast = useSelector((state) => state.forecast); +const SettingsIcon = () => { const themeContext = useContext(ThemeContext); - - useEffect(() => { - SplashScreen.hide(); - }, []); - return ( - - - rootNavigation.navigate('DayDetails', { forecast: forecast.daily[0] }) - } - > - - - - - {round(weather.main.temp)}° - - {weather.name} - - Feels Like {round(weather.main.feels_like)}° - - {weather.weather[0].main} - - - rootNavigation.navigate('Settings')} - > - - - + rootNavigation.navigate('Settings')} + > + + ); }; +export default () => { + const weather = useSelector((state) => state.weather); + + return useMemo(() => { + console.log('render today'); + return ( + + rootNavigation.navigate('DayDetails', { index: 0 })} + > + + + + + {round(weather.main.temp)}° + + {weather.name} + + Feels Like {round(weather.main.feels_like)}° + + {weather.weather[0].main} + + + + + ); + }, [weather]); +}; + const Container = styled.View` flex: 1; `; diff --git a/src/components/WeekDays.jsx b/src/components/WeekDays.jsx index 5d086cf..db69e1e 100644 --- a/src/components/WeekDays.jsx +++ b/src/components/WeekDays.jsx @@ -1,56 +1,27 @@ -import React, { useEffect, useState } from 'react'; +import React, { useRef } from 'react'; import styled from 'styled-components/native'; -import moment from 'moment'; import { RFValue } from 'react-native-responsive-fontsize'; -import { useSelector } from 'react-redux'; import { StyledTouchableScale } from './StyledComponents'; import { rootNavigation } from '../utils'; import Day from './Day'; export default () => { - const [today] = useState(moment().format('dddd')); - const [dayNames, setDayNames] = useState(); - - const forecast = useSelector((state) => state.forecast); - - useEffect(() => { - let weekDays = [ - 'Sunday', - 'Monday', - 'Tuesday', - 'Wednesday', - 'Thursday', - 'Friday', - 'Saturday', - ]; - let dayNo = weekDays.indexOf(today); - let day = ''; - while (dayNo >= 0) { - day = weekDays.shift(); - weekDays.push(day); - dayNo--; - } - weekDays = weekDays.slice(0, 5); - setDayNames(weekDays); - }, [today]); + console.log('render weekdays'); return ( - {dayNames && - dayNames.map((day, index) => ( - - rootNavigation.navigate('DayDetails', { - forecast: forecast.daily[index + 1], - }) - } - > - - - ))} + {[...Array(6)].map((value, index) => ( + + rootNavigation.navigate('DayDetails', { index: index + 1 }) + } + > + + + ))} ); }; diff --git a/src/containers/StackNavigation.jsx b/src/containers/StackNavigation.jsx index 0a7b765..6af0661 100644 --- a/src/containers/StackNavigation.jsx +++ b/src/containers/StackNavigation.jsx @@ -50,6 +50,7 @@ export default () => { headerPressColorAndroid: themeContext.colors.buttonPress, headerTitleAlign: 'center', headerBackTitleVisible: false, + headerShown: true, headerTitleStyle: { fontSize: RFValue(18), }, diff --git a/src/pages/DayDetails.jsx b/src/pages/DayDetails.jsx index 1877997..2c11dae 100644 --- a/src/pages/DayDetails.jsx +++ b/src/pages/DayDetails.jsx @@ -1,30 +1,23 @@ -import React, { useContext } from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components/native'; -import { RFValue } from 'react-native-responsive-fontsize'; -import { ThemeContext } from 'styled-components'; -import { useSelector } from 'react-redux'; -import DayDetailsBox from '../components/DayDetailsBox'; +import DailyTimeline from '../components/DailyTimeline'; +import DayForecastDetails from '../components/DayForecastDetails'; export default ({ route }) => { - const units = useSelector((state) => state.units); - const themeContext = useContext(ThemeContext); - const { forecast } = route.params; + const { index } = route.params; + const [dayClicked, setDayClicked] = useState(index); + console.log('render day details '); return ( - - + + ); }; const Container = styled.View` flex: 1; - padding: ${RFValue(10)}px; background-color: ${({ theme }) => theme.colors.background}; `; - -const DailyTimeline = styled.View` - flex-grow: 1; -`; diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index f25fece..0d7dfa6 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,5 +1,5 @@ -import React, { useEffect, useContext } from 'react'; -import { RefreshControl, View, Text, Dimensions } from 'react-native'; +import React, { useEffect, useContext, useLayoutEffect, useMemo } from 'react'; +import { RefreshControl, View, Dimensions } from 'react-native'; import styled from 'styled-components/native'; import { useSelector } from 'react-redux'; @@ -16,53 +16,63 @@ import { useWeather, useStorage } from '../utils'; export default ({ navigation }) => { const screenHeight = Dimensions.get('screen').height; const themeContext = useContext(ThemeContext); - const { error, isLoading, weather, forecast } = useSelector((state) => state); + const isLoading = useSelector((state) => state.isLoading); + const hasData = useSelector((state) => state.hasData); + const error = useSelector((state) => state.error); + const { getWeather } = useWeather(); const fetchStorage = useStorage(); - useEffect(() => { + useLayoutEffect(() => { navigation.setOptions({ headerShown: false }); - getWeather(); + }, []); + + useEffect(() => { fetchStorage(); + getWeather(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (error) { - Toast.show(error, Toast.SHORT); + Toast.show(error, Toast.LONG, Toast.BOTTOM); } }, [error]); - return ( - - {weather && forecast ? ( - - } - > - { + console.log('render home'); + return ( + + {hasData ? ( + + } > - - - - - - ) : ( - error && - )} - - ); + + + + + + + ) : ( + error && + )} + + ); + }, [error, hasData, isLoading, themeContext]); }; const Container = styled.View` diff --git a/src/utils/storage.js b/src/utils/storage.js index 9d8f705..814dcb2 100644 --- a/src/utils/storage.js +++ b/src/utils/storage.js @@ -5,7 +5,7 @@ const getItem = (key) => { try { return AsyncStorage.getItem(key); } catch (e) { - console.log('error getting', key, ': ', e); + console.error('error getting', key, ': ', e); } }; @@ -14,7 +14,7 @@ const getData = async (key) => { const jsonValue = await AsyncStorage.getItem(key); return jsonValue != null ? JSON.parse(jsonValue) : null; } catch (e) { - console.log('error getting', key, ': ', e); + console.error('error getting', key, ': ', e); } }; @@ -22,7 +22,7 @@ const setItem = async (key, value) => { try { await AsyncStorage.setItem(key, value); } catch (e) { - console.log('error setting', key, ': ', e); + console.error('error setting', key, ': ', e); } }; @@ -31,7 +31,7 @@ const setData = async (key, value) => { const jsonValue = JSON.stringify(value); await AsyncStorage.setItem(key, jsonValue); } catch (e) { - console.log('error setting', key, ': ', e); + console.error('error setting', key, ': ', e); } }; @@ -39,7 +39,7 @@ const removeItem = async (key) => { try { await AsyncStorage.removeItem(key); } catch (e) { - console.log('error removing', key, ': ', e); + console.error('error removing', key, ': ', e); } }; @@ -47,7 +47,7 @@ const clearStorage = async () => { try { await AsyncStorage.clear(); } catch (e) { - console.log('error clearAll : ', e); + console.error('error clearAll : ', e); } }; diff --git a/src/utils/store.js b/src/utils/store.js index a05e3f1..529a7c9 100644 --- a/src/utils/store.js +++ b/src/utils/store.js @@ -1,26 +1,39 @@ import { createStore } from 'redux'; +import moment from 'moment'; import { weather, forecast, units, timeFormat, theme } from './storage'; const initialState = { weather: null, forecast: null, error: null, - isLoading: true, + isLoading: false, units: 'metric', timeFormat: '24', theme: 'black', + clickedIndex: 0, + daily: [], + hasData: false, + lastFetched: 0, }; const rootReducer = (state = initialState, action) => { switch (action.type) { case 'storage': { + console.log('storage fetch'); + return { ...state, - weather: action.payload.weather || initialState.weather, - forecast: action.payload.forecast || initialState.forecast, - timeFormat: action.payload.timeFormat || initialState.timeFormat, - units: action.payload.units || initialState.units, - theme: action.payload.theme || initialState.theme, + weather: action.payload.weather || state.weather, + forecast: action.payload.forecast || state.forecast, + timeFormat: action.payload.timeFormat || state.timeFormat, + units: action.payload.units || state.units, + theme: action.payload.theme || state.theme, + daily: action.payload.forecast.daily || state.daily, + hasData: + action.payload.weather && action.payload.forecast + ? true + : state.hasData, + lastFetched: action.payload.weather.dt * 1000 || state.lastFetched, }; } case 'weather': { @@ -28,6 +41,8 @@ const rootReducer = (state = initialState, action) => { return { ...state, weather: action.payload, + hasData: state.forecast ? true : state.hasData, + lastFetched: moment(), }; } case 'onecall': { @@ -35,6 +50,9 @@ const rootReducer = (state = initialState, action) => { return { ...state, forecast: action.payload, + daily: action.payload.daily, + hasData: state.weather ? true : state.hasData, + lastFetched: moment(), }; } case 'error': { diff --git a/src/utils/useLocation.js b/src/utils/useLocation.js index 06a3bd1..670ad77 100644 --- a/src/utils/useLocation.js +++ b/src/utils/useLocation.js @@ -42,7 +42,7 @@ export default () => { Geolocation.getCurrentPosition( // get current location from native GPS service (position) => { - console.log('get location'); + console.log('location fetched'); setIsLoading(false); setData({ latitude: position.coords.latitude, @@ -50,7 +50,7 @@ export default () => { }); }, (err) => { - console.log(err.code, err.message); + console.error(err.code, err.message); setError(err.message); setIsLoading(false); }, diff --git a/src/utils/useWeather.js b/src/utils/useWeather.js index 06038b1..c098fb7 100644 --- a/src/utils/useWeather.js +++ b/src/utils/useWeather.js @@ -105,7 +105,7 @@ export default () => { setForecastLoading(false); } } catch (err) { - console.log('Connection failed'); + console.error('Connection failed'); setError('Connection Failed'); setIsLoading(false); } diff --git a/src/utils/utils.js b/src/utils/utils.js index be1d4dd..7d82276 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -11,22 +11,76 @@ export const ceil = (no) => { }; export const weatherIcons = { - '01d': require('../assets/lotties/01d.json'), - '01n': require('../assets/lotties/01n.json'), - '02d': require('../assets/lotties/02d.json'), - '02n': require('../assets/lotties/02n.json'), - '03d': require('../assets/lotties/03d.json'), - '03n': require('../assets/lotties/03d.json'), - '04d': require('../assets/lotties/03d.json'), - '04n': require('../assets/lotties/03d.json'), - '09d': require('../assets/lotties/09d.json'), - '09n': require('../assets/lotties/09n.json'), - '10d': require('../assets/lotties/09d.json'), - '10n': require('../assets/lotties/09n.json'), - '11d': require('../assets/lotties/11d.json'), - '11n': require('../assets/lotties/11n.json'), - '13d': require('../assets/lotties/13d.json'), - '13n': require('../assets/lotties/13n.json'), - '50d': require('../assets/lotties/50d.json'), - '50n': require('../assets/lotties/50n.json'), + '01d': { + icon: 'weather-sunny', + animation: require('../assets/lotties/01d.json'), + }, + '01n': { + icon: 'weather-night', + animation: require('../assets/lotties/01n.json'), + }, + '02d': { + icon: 'weather-partly-cloudy', + animation: require('../assets/lotties/02d.json'), + }, + '02n': { + icon: 'weather-night-partly-cloudy', + animation: require('../assets/lotties/02n.json'), + }, + '03d': { + icon: 'weather-cloudy', + animation: require('../assets/lotties/03d.json'), + }, + '03n': { + icon: 'weather-cloudy', + animation: require('../assets/lotties/03d.json'), + }, + '04d': { + icon: 'weather-cloudy', + animation: require('../assets/lotties/03d.json'), + }, + '04n': { + icon: 'weather-cloudy', + animation: require('../assets/lotties/03d.json'), + }, + '09d': { + icon: 'weather-rainy', + animation: require('../assets/lotties/09d.json'), + }, + '09n': { + icon: 'weather-rainy', + animation: require('../assets/lotties/09n.json'), + }, + '10d': { + icon: 'weather-pouring', + animation: require('../assets/lotties/09d.json'), + }, + '10n': { + icon: 'weather-pouring', + animation: require('../assets/lotties/09n.json'), + }, + '11d': { + icon: 'weather-lightning-rainy', + animation: require('../assets/lotties/11d.json'), + }, + '11n': { + icon: 'weather-lightning-rainy', + animation: require('../assets/lotties/11n.json'), + }, + '13d': { + icon: 'weather-snowy-heavy', + animation: require('../assets/lotties/13d.json'), + }, + '13n': { + icon: 'weather-snowy-heavy', + animation: require('../assets/lotties/13n.json'), + }, + '50d': { + icon: 'weather-fog', + animation: require('../assets/lotties/50d.json'), + }, + '50n': { + icon: 'weather-fog', + animation: require('../assets/lotties/50n.json'), + }, }; diff --git a/yarn.lock b/yarn.lock index 358e76d..e66097b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1951,6 +1951,11 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== +babel-plugin-transform-remove-console@^6.9.4: + version "6.9.4" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz#b980360c067384e24b357a588d807d3c83527780" + integrity sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A= + babel-preset-current-node-syntax@^0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz#826f1f8e7245ad534714ba001f84f7e906c3b615"