Skip to content

Commit 5a6988c

Browse files
Implement proper in-app update notifications
1 parent 7a33eb5 commit 5a6988c

File tree

14 files changed

+265
-68
lines changed

14 files changed

+265
-68
lines changed

licenses.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
"licenseUrl": "https://github.com/onubo/react-native-logs/raw/master/LICENSE",
155155
"parents": "opendtu-react-native"
156156
},
157-
"[email protected].0-alpha.2": {
157+
158158
"licenses": "MIT",
159159
"repository": "https://github.com/iamacup/react-native-markdown-display",
160160
"licenseUrl": "https://github.com/iamacup/react-native-markdown-display/raw/master/LICENSE",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"react-native-get-random-values": "^1.9.0",
5353
"react-native-linear-gradient": "^2.8.3",
5454
"react-native-logs": "^5.0.1",
55-
"react-native-markdown-display": "^7.0.0-alpha.2",
55+
"react-native-markdown-display": "^7.0.2",
5656
"react-native-paper": "^5.11.2",
5757
"react-native-paper-dates": "^0.20.4",
5858
"react-native-reanimated": "^3.5.4",

src/App.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { SafeAreaProvider } from 'react-native-safe-area-context';
1414
import SplashScreen from 'react-native-splash-screen';
1515
import { Provider as ReduxProvider } from 'react-redux';
1616

17+
import EnableAppUpdatesModal from '@/components/modals/EnableAppUpdatesModal';
18+
1719
import ApiProvider from '@/api/ApiHandler';
1820
import DatabaseProvider from '@/database';
1921
import GithubProvider from '@/github';
@@ -231,6 +233,10 @@ const _App: FC = () => {
231233
});
232234
}, [i18n.language, t]);
233235

236+
const showEnableAppUpdatesModal = useAppSelector(
237+
state => state.settings.enableAppUpdates === null,
238+
);
239+
234240
if (!i18nLanguageMatchesSettings) {
235241
return null;
236242
}
@@ -245,6 +251,13 @@ const _App: FC = () => {
245251
<NavigationContainer theme={navigationTheme}>
246252
<NavigationStack />
247253
</NavigationContainer>
254+
<EnableAppUpdatesModal
255+
visible={showEnableAppUpdatesModal}
256+
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
257+
onDismiss={() => {}}
258+
dismissable={false}
259+
dismissableBackButton={false}
260+
/>
248261
</PaperProvider>
249262
);
250263
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { FC } from 'react';
2+
import { useCallback } from 'react';
3+
import { useTranslation } from 'react-i18next';
4+
import { Box } from 'react-native-flex-layout';
5+
import type { ModalProps } from 'react-native-paper';
6+
import { Button, Portal, Text } from 'react-native-paper';
7+
8+
import { setEnableAppUpdates } from '@/slices/settings';
9+
10+
import BaseModal from '@/components/BaseModal';
11+
12+
import { useAppDispatch } from '@/store';
13+
14+
const EnableAppUpdatesModal: FC<Omit<ModalProps, 'children'>> = props => {
15+
const { onDismiss } = props;
16+
const { t } = useTranslation();
17+
const dispatch = useAppDispatch();
18+
19+
const handleAbort = useCallback(() => {
20+
onDismiss?.();
21+
}, [onDismiss]);
22+
23+
const handleEnable = useCallback(() => {
24+
console.log('handleEnable');
25+
dispatch(setEnableAppUpdates({ enable: true }));
26+
handleAbort();
27+
}, [dispatch, handleAbort]);
28+
29+
const handleDisable = useCallback(() => {
30+
dispatch(setEnableAppUpdates({ enable: false }));
31+
handleAbort();
32+
}, [dispatch, handleAbort]);
33+
34+
return (
35+
<Portal>
36+
<BaseModal {...props}>
37+
<Box p={16}>
38+
<Box mb={8}>
39+
<Text variant="bodyLarge">
40+
{t('settings.doYouWantToEnableAppUpdates')}
41+
</Text>
42+
</Box>
43+
</Box>
44+
<Box
45+
style={{
46+
flexDirection: 'row',
47+
justifyContent: 'flex-end',
48+
alignItems: 'center',
49+
padding: 8,
50+
}}
51+
>
52+
<Button
53+
style={{ marginRight: 8 }}
54+
onPress={handleEnable}
55+
mode="contained"
56+
>
57+
{t('enable')}
58+
</Button>
59+
<Button onPress={handleDisable} mode="text">
60+
{t('disable')}
61+
</Button>
62+
</Box>
63+
</BaseModal>
64+
</Portal>
65+
);
66+
};
67+
68+
export default EnableAppUpdatesModal;

src/github/FetchHandler.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {
1111

1212
import ago from '@/utils/ago';
1313

14-
import { AppGithubBaseConfig, OpenDTUGithubBaseConfig, useGithub } from '@/github/index';
14+
import {
15+
AppGithubBaseConfig,
16+
OpenDTUGithubBaseConfig,
17+
useGithub,
18+
} from '@/github/index';
1519
import { useAppDispatch, useAppSelector } from '@/store';
1620

1721
const FetchHandler: FC = () => {
@@ -36,6 +40,10 @@ const FetchHandler: FC = () => {
3640
: true,
3741
);
3842

43+
const enableAppUpdates = useAppSelector(
44+
state => !!state.settings.enableAppUpdates,
45+
);
46+
3947
const githubApi = useGithub();
4048

4149
useEffect(() => {
@@ -67,7 +75,7 @@ const FetchHandler: FC = () => {
6775
console.log('SKIP allReleasesRefetchOk');
6876
}
6977

70-
if (latestAppReleaseRefetchOk) {
78+
if (latestAppReleaseRefetchOk && enableAppUpdates) {
7179
const appRelease = await githubApi.request(
7280
'GET /repos/{owner}/{repo}/releases/latest',
7381
AppGithubBaseConfig,
@@ -90,6 +98,7 @@ const FetchHandler: FC = () => {
9098
latestReleaseRefetchOk,
9199
allReleasesRefetchOk,
92100
latestAppReleaseRefetchOk,
101+
enableAppUpdates,
93102
]);
94103

95104
return null;

src/hooks/useHasNewAppVersion.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ import { useMemo } from 'react';
55

66
import { useAppSelector } from '@/store';
77

8-
const useHasNewAppVersion = () => {
8+
const useHasNewAppVersion = (options?: { usedForIndicatorOnly: boolean }) => {
9+
const { usedForIndicatorOnly = false } = options ?? {};
10+
911
const appRelease = useAppSelector(
1012
state => state.github.latestAppRelease?.data,
1113
);
1214

15+
const showIndicator = useAppSelector(
16+
state => !!state.settings.enableAppUpdates,
17+
);
18+
1319
return useMemo(() => {
1420
if (!appRelease) return [false, null] as const;
1521

@@ -19,8 +25,11 @@ const useHasNewAppVersion = () => {
1925
'>',
2026
);
2127

22-
return [newAppVersionAvailable, appRelease] as const;
23-
}, [appRelease]);
28+
return [
29+
!showIndicator && usedForIndicatorOnly ? false : newAppVersionAvailable,
30+
appRelease,
31+
] as const;
32+
}, [appRelease, showIndicator, usedForIndicatorOnly]);
2433
};
2534

2635
export default useHasNewAppVersion;

src/slices/settings.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
UpdateDtuHostnameAction,
1919
UpdateDtuSerialNumberAction,
2020
SetLanguageAction,
21+
EnableAppUpdatesAction,
2122
} from '@/types/settings';
2223

2324
const initialState: SettingsState = {
@@ -26,6 +27,7 @@ const initialState: SettingsState = {
2627
dtuConfigs: [],
2728
selectedDtuConfig: null,
2829
databaseConfigs: [],
30+
enableAppUpdates: null,
2931
};
3032

3133
const log = logger.createLogger();
@@ -204,6 +206,9 @@ const settingsSlice = createSlice({
204206
? null
205207
: action.payload.databaseUuid;
206208
},
209+
setEnableAppUpdates: (state, action: EnableAppUpdatesAction) => {
210+
state.enableAppUpdates = action.payload.enable;
211+
},
207212
},
208213
});
209214

@@ -225,6 +230,7 @@ export const {
225230
removeDatabaseConfig,
226231
updateDatabaseConfig,
227232
updateDTUDatabaseUuid,
233+
setEnableAppUpdates,
228234
} = settingsSlice.actions;
229235

230236
export const { reducer: SettingsReducer } = settingsSlice;

src/types/settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface SettingsState {
2828
// app specific
2929
appTheme: 'light' | 'dark' | 'system';
3030
language: 'en' | 'de';
31+
enableAppUpdates: boolean | null;
3132

3233
// opendtu
3334
dtuConfigs: OpenDTUConfig[];
@@ -100,3 +101,7 @@ export type UpdateDTUDatabaseUuidAction = PayloadAction<{
100101
index: Index;
101102
databaseUuid: string | 'none';
102103
}>;
104+
105+
export type EnableAppUpdatesAction = PayloadAction<{
106+
enable: boolean;
107+
}>;

src/views/navigation/NavigationStack.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import AboutSettingsScreen from '@/views/navigation/screens/AboutSettingsScreen'
1313
import ConfigureGraphsScreen from '@/views/navigation/screens/ConfigureGraphsScreen';
1414
import DeviceListScreen from '@/views/navigation/screens/DeviceListScreen';
1515
import DeviceSettingsScreen from '@/views/navigation/screens/DeviceSettingsScreen';
16+
import LicensesScreen from '@/views/navigation/screens/LicensesScreen';
1617
import MainScreen from '@/views/navigation/screens/MainScreen';
1718
import ManageDatabasesScreen from '@/views/navigation/screens/ManageDatabasesScreen';
1819
import MqttInformationScreen from '@/views/navigation/screens/MqttInformationScreen';
@@ -113,6 +114,11 @@ const NavigationStack: FC = () => {
113114
component={MqttInformationScreen}
114115
options={{ headerBackVisible: true }}
115116
/>
117+
<Stack.Screen
118+
name="LicensesScreen"
119+
component={LicensesScreen}
120+
options={{ headerBackVisible: true }}
121+
/>
116122
</Stack.Navigator>
117123
);
118124
};

src/views/navigation/NavigationTabs.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ const BottomNavigation: FC = () => {
2424
const { t } = useTranslation();
2525
const [index, setIndex] = useState<number>(0);
2626

27-
const [hasNewAppVersion] = useHasNewAppVersion();
27+
const [hasNewAppVersion] = useHasNewAppVersion({
28+
usedForIndicatorOnly: true,
29+
});
2830

2931
const routes = useMemo<BaseRoutes>(
3032
() => [

0 commit comments

Comments
 (0)