Product Hunt Mobile Theme with Redux in React Native Part II

DeVladinci
Product Hunt
Published in
4 min readApr 17, 2018

--

In a previous post, we added theming to the app. The next step is adding automatic switching based on sunrise/sunset for the current location.

Since automatic switching of the theme is not a mainstream feature, we’ll disable it by default. If the user switches it on, we’re going to request for location permissions (iOS Only). Once we’ve been granted permissions from the user, we’ll make a network request to Sunset-Sunrise API.

When I’ve started working on this feature, I wanted to change the theme immediately when sunset or sunrise occurs. That wouldn’t be a good UX experience because the user can be in the middle of the reading comments for example, and this sudden change can be frustrating and unexpected.

Instead, the theme only changes when the user switches on automatic night mode or opens the app.

1. UI

We already have the UI for dark/white theme, so the only addition here is going to be one switch.

2. Actions

In the previous post, the action was quite simple and didn’t have any logic. This, unfortunately, is no longer the case.

We’re going to need three separate actions:

  1. Toggle automatic theming on/off
  2. Get sunset/sunrise time
  3. Get dark/light theme depending on the time

Separating actions should make things simpler: we’ll call each action when the app starts or toggled from settings.

The first is very similar to the one we’ve already added in the previous post.

After enabling/disabling the feature, we should either get sunset/sunrise time or clean up already saved time. We will update toggleAutomaticTheme after implementing getSunsetSunriseTime , updateTheme and reset actions.

Get sunrise sunset time action

This action is quite complex, so let’s chop it to pieces and see what is going on. First, we’re checking if the automatic theme is enabled – if not, the execution just stops there.

// utils.jsexport function isAutomaticEnabled(state: any): boolean {
return state[constants.MODULE].automatic;
}

Day length changes over the year depending on your location, so we need to pull actual data from the API. In order to prevent spamming the API, we’ll check to see if we’ve pulled the data for the current day:

// utils.jsexport function isFetchedToday(state: any): boolean {
const themeState = state[constants.MODULE];
if (!themeState.sunrise) return false; return moment(themeState.sunrise).isSame(moment(), "day");
}

If we’ve already fetched today, just update the theme.

Finally, it is a time to get location and sunset/sunrise time. Before implementing the action, we need to make a couple of changes to be able to retrieve current user location. Since we need location only on foreground for iOS we do need to make any changes, NSLocationWhenInUserUsageDescription is in Info.plist already. For Android in AndroidManifest.xml add the following permission request:

<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />

Now we can get the location. If it’s successful, we’re going to call the API.

navigator.geolocation.getCurrentPosition(position => {}, error => {});

In case there is an error — maybe user didn’t allow to get his access, or something else went wrong — we disable automatic theme. But, if we successfully retrieve current position we’re going to call Sunset-Sunrise API .

update theme

Update theme action is simple compared to getSunriseSunset. Most important thing there is getting a theme for the current time. If it’s during the daytime, we’ll switch to the lighter them. Otherwise, dark theme.

The last one is reset action. It’s not a mandatory one, but I like to keep state clean also dispatching the action will notify the UI about the change.

Finally, we can update toggleAutomaticTheme if the value is ON dispatch getSunriseSunsetTime if OFF just reset to the initial state.

3. Reducer

Handling reset and toggle automatic in the reducer are quite straightforward.

Handling sunset/sunrise time is more interesting since the API returns time in GMT. We need to get user’s timezone and get the actual time based on the timezone.

4. Handling app state and theme

What about next time when the user opens the app? We should check what type of theme should be active. Handling the cold start is easy we’re going to dispatch action in componentDidMount().

But what about changing state from background to foreground? We should add a listener for state change and when trigger change if the previous state is inactive or background and next one is active.

Conclusion

I underestimated the complexity of the feature. Initially, I thought it would take an hour or so. 🤦‍♂

it was a day work and a lot of edge cases. Yes, I do think it is a convenient feature and find it very handy.

Useful links:

Moment Range — https://github.com/rotaready/moment-range

Redux — https://redux.js.org/

Redux-Persist — https://github.com/rt2zz/redux-persist

React Native Device Info — https://github.com/rebeccahughes/react-native-device-info

--

--