Skip to content

Commit 88febd6

Browse files
committed
Fix a bug causing the user to lose its saved preferences when internet is off
1 parent 6814bc9 commit 88febd6

10 files changed

Lines changed: 140 additions & 100 deletions

File tree

src/cards/ReposCard.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ function ReposCard({ analyticsTag, label, icon, withAds }) {
8888
}, [selectedDateRange])
8989

9090
const fetchRepos = async () => {
91+
92+
if (!selectedLanguage) {
93+
return []
94+
}
95+
9196
if (!selectedLanguage?.githubValues) {
9297
throw Error(`Github Trending does not support ${selectedLanguage.label}.`)
9398
}
@@ -123,6 +128,7 @@ function ReposCard({ analyticsTag, label, icon, withAds }) {
123128

124129
setCacheCardData({ ...cacheCardData, [cacheKey]: data })
125130
return data
131+
126132
}
127133

128134
function HeaderTitle() {

src/components/ListComponent.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState, useEffect } from "react";
22
import BeatLoader from "react-spinners/BeatLoader";
33
import CarbonAd from './CarbonAd'
4+
import { trackException } from '../utils/Analytics'
45

56
function ListComponent({ fetchData, refresh, renderItem, withAds }) {
67
const [items, setItems] = useState([])
@@ -14,11 +15,10 @@ function ListComponent({ fetchData, refresh, renderItem, withAds }) {
1415
try {
1516
const data = await fetchData()
1617
setItems(data)
17-
}
18-
catch (e) {
18+
} catch (e) {
1919
setError(e)
20-
}
21-
finally {
20+
trackException(e, true)
21+
} finally {
2222
setLoading(false)
2323
}
2424
}
@@ -27,16 +27,16 @@ function ListComponent({ fetchData, refresh, renderItem, withAds }) {
2727
}, [refresh])
2828

2929
if (error) {
30-
return (<p className="errorMsg">{error.message}</p>)
30+
return <p className="errorMsg">{error.message}</p>
3131
}
3232

3333
const renderItems = () => {
34-
if (!items || items.length == 0 ) {
35-
<p className="errorMsg">Could not find any content using the selected languages!</p>
34+
if (!items) {
35+
return
3636
}
3737
return items.map((item, index) => {
3838
let content = [renderItem(item, index)]
39-
if(withAds && index == 0) {
39+
if (withAds && index == 0) {
4040
content.unshift(<CarbonAd key={'carbonAd0'} />)
4141
}
4242
return content
@@ -45,13 +45,13 @@ function ListComponent({ fetchData, refresh, renderItem, withAds }) {
4545

4646
return (
4747
<>
48-
{
49-
loading ?
50-
<div className="cardLoading">
51-
<BeatLoader color={"#A9B2BD"} loading={loading} size={15} className="loading" />
52-
</div> : renderItems()
53-
}
54-
48+
{loading ? (
49+
<div className="cardLoading">
50+
<BeatLoader color={'#A9B2BD'} loading={loading} size={15} className="loading" />
51+
</div>
52+
) : (
53+
renderItems()
54+
)}
5555
</>
5656
)
5757
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { useContext } from 'react'
2+
import { ErrorBoundary } from 'react-error-boundary'
3+
import { trackException } from '../utils/Analytics'
4+
import { AiFillBug } from 'react-icons/ai'
5+
import { WiRefresh } from 'react-icons/wi'
6+
import { APP } from '../Constants'
7+
import '../pages/Page.css'
8+
9+
export default function AppErrorBoundary({ children }) {
10+
function ErrorFallback({ error, resetErrorBoundary }) {
11+
return (
12+
<div className="Page appError">
13+
<AiFillBug size={64} />
14+
<p>Sorry there was a problem loading this page.</p>
15+
<p>{error}</p>
16+
<button onClick={resetErrorBoundary}>
17+
<WiRefresh size={32} className={'buttonIcon'} /> Try again
18+
</button>
19+
</div>
20+
)
21+
}
22+
23+
const errorHandler = (error, info) => {
24+
trackException(error, true)
25+
}
26+
27+
return (
28+
<ErrorBoundary FallbackComponent={ErrorFallback} onError={errorHandler}>
29+
{children}
30+
</ErrorBoundary>
31+
)
32+
}

src/configuration/AppLoadingHOC.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React, { useState } from 'react'
2+
import BeatLoader from 'react-spinners/BeatLoader'
3+
4+
export const AppLoadingHOC = (WrappedComponent) => {
5+
function HOC(props) {
6+
const [isLoading, setIsLoading] = useState(true)
7+
const setLoadingState = (isComponentLoading) => setIsLoading(isComponentLoading)
8+
9+
return (
10+
<>
11+
{isLoading && (
12+
<div className="appLoading">
13+
<BeatLoader color={'#A9B2BD'} loading={true} size={15} />
14+
</div>
15+
)}
16+
<WrappedComponent {...props} setLoading={setLoadingState} isLoading={isLoading} />
17+
</>
18+
)
19+
}
20+
return HOC
21+
}

src/configuration/AppWrapper.js

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,14 @@
11
import React, { useReducer, useContext } from 'react'
2+
import { LS_PREFERENCES_KEY } from '../Constants'
3+
import AppStorage from '../services/localStorage'
4+
import { PreferencesProvider } from '../preferences/PreferencesContext'
25
import { getOSMode } from '../services/os'
36
import AppReducer from '../preferences/AppReducer'
4-
import { PreferencesProvider } from '../preferences/PreferencesContext'
57
import ConfigurationContext from './ConfigurationContext'
6-
import { ErrorBoundary } from 'react-error-boundary'
7-
import { trackException } from '../utils/Analytics'
8-
import { AiFillBug } from 'react-icons/ai'
9-
import { WiRefresh } from 'react-icons/wi'
10-
import { APP, LS_PREFERENCES_KEY } from '../Constants'
11-
import AppStorage from '../services/localStorage'
12-
import '../pages/Page.css'
13-
14-
function ErrorFallback({ error, resetErrorBoundary }) {
15-
return (
16-
<div className="Page appError">
17-
<AiFillBug size={64} />
18-
<p>Sorry there was a problem loading this page.</p>
19-
<p>Please try again or contact the developer at {APP.contactEmail}</p>
20-
<button onClick={resetErrorBoundary}>
21-
<WiRefresh size={32} className={'buttonIcon'} /> Try again
22-
</button>
23-
</div>
24-
)
25-
}
268

279
export default function AppWrapper({ children }) {
2810
const configuration = useContext(ConfigurationContext)
11+
2912
const [state, dispatcher] = useReducer(
3013
AppReducer,
3114
{
@@ -62,15 +45,9 @@ export default function AppWrapper({ children }) {
6245
}
6346
)
6447

65-
const errorHandler = (error, info) => {
66-
trackException(error, true)
67-
}
68-
6948
return (
70-
<ErrorBoundary FallbackComponent={ErrorFallback} onError={errorHandler}>
71-
<PreferencesProvider value={{ ...state, dispatcher: dispatcher }}>
72-
{children}
73-
</PreferencesProvider>
74-
</ErrorBoundary>
49+
<PreferencesProvider value={{ ...state, dispatcher: dispatcher }}>
50+
{children}
51+
</PreferencesProvider>
7552
)
7653
}
Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
1-
import useRemoteConfiguration from './useRemoteConfiguration';
2-
import { ConfigurationProvider } from './ConfigurationContext';
3-
import { LOCAL_CONFIGURATION } from '../Constants';
4-
import BeatLoader from "react-spinners/BeatLoader";
5-
import AppWrapper from './AppWrapper'
1+
import React, { useEffect, useState } from 'react'
2+
import remoteConfigurationApi from '../services/remoteConfiguration'
3+
import { ConfigurationProvider } from './ConfigurationContext'
4+
import { AppLoadingHOC } from './AppLoadingHOC'
5+
import useAsyncError from '../hooks/useAsyncError'
66

7-
export default function ConfigurationWrapper({ children }) {
8-
const [configuration, loadingConfiguration, errorConfiguration] = useRemoteConfiguration()
9-
if (loadingConfiguration) {
10-
return (
11-
<div className="appLoading">
12-
<BeatLoader color={'#A9B2BD'} loading={true} size={15} />
13-
</div>
14-
)
15-
}
7+
export const ConfigurationWrapper = (props) => {
8+
const { setLoading, isLoading } = props
9+
const [configurationData, setConfigurationData] = useState(null)
10+
const throwError = useAsyncError()
1611

17-
const getConfiguration = () => (errorConfiguration ? LOCAL_CONFIGURATION : configuration)
12+
useEffect(() => {
13+
setLoading(true)
14+
const setup = async () => {
15+
// try {
16+
const data = await remoteConfigurationApi.getRemoteConfiguration()
17+
if (!data) {
18+
throwError(
19+
'Could not fetch configuration data, Make sure you are connected to the internet'
20+
)
21+
} else {
22+
setConfigurationData(data)
23+
}
24+
setLoading(false)
25+
}
26+
27+
setup()
28+
}, [])
1829

1930
return (
20-
<ConfigurationProvider value={getConfiguration()}>
21-
<AppWrapper>{children}</AppWrapper>
22-
</ConfigurationProvider>
31+
<>
32+
{configurationData && (
33+
<ConfigurationProvider value={configurationData}>{props.children}</ConfigurationProvider>
34+
)}
35+
</>
2336
)
24-
}
37+
}
38+
39+
export default AppLoadingHOC(ConfigurationWrapper)

src/configuration/useRemoteConfiguration.js

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/hooks/useAsyncError.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React, { useCallback, useState } from 'react'
2+
3+
const useAsyncError = () => {
4+
const [_, setError] = useState()
5+
return useCallback(
6+
(e) => {
7+
setError(() => {
8+
throw e
9+
})
10+
},
11+
[setError]
12+
)
13+
}
14+
export default useAsyncError

src/index.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import ReactDOM from 'react-dom';
33
import 'normalize.css';
44
import './index.css';
55
import App from './App';
6+
import AppWrapper from './configuration/AppWrapper'
7+
import AppErrorBoundary from './configuration/AppErrorBoundary'
68
import ConfigurationWrapper from './configuration/ConfigurationWrapper'
79

810
ReactDOM.render(
911
<React.StrictMode>
10-
<ConfigurationWrapper>
11-
<App />
12-
</ConfigurationWrapper>
12+
<AppErrorBoundary>
13+
<ConfigurationWrapper>
14+
<AppWrapper>
15+
<App />
16+
</AppWrapper>
17+
</ConfigurationWrapper>
18+
</AppErrorBoundary>
1319
</React.StrictMode>,
1420
document.getElementById('root')
15-
);
21+
)

src/services/cachedRequest.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const cachedRequest = async (url) => {
2626
}
2727
} catch (error) {
2828
if (!error.response || error.response.status !== 304) {
29-
throw error
29+
return null //throw error
3030
}
3131
if (!cachedResponse) {
3232
throw 'Network Failed'

0 commit comments

Comments
 (0)