Refactor App Update Checker

This commit is contained in:
Rajarshee Chatterjee
2023-08-17 22:24:09 +05:30
parent be2cca88af
commit 28b65e35d2
8 changed files with 100 additions and 98 deletions
+3 -1
View File
@@ -12,13 +12,14 @@ import { Provider as PaperProvider } from 'react-native-paper';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
import * as Notifications from 'expo-notifications';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import { createDatabase } from '@database/db';
import { persistor, store } from '@redux/store';
import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary';
import NewUpdateDialog from '@components/NewUpdateDialog';
import Main from './src/navigators/Main';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
// Initialize trackers
import './src/services/Trackers/index';
@@ -49,6 +50,7 @@ const App = () => {
<SafeAreaProvider>
<PaperProvider>
<BottomSheetModalProvider>
<NewUpdateDialog />
<StatusBar translucent={true} backgroundColor="transparent" />
<Main />
</BottomSheetModalProvider>
+31 -27
View File
@@ -1,52 +1,57 @@
import React, { useState } from 'react';
import { StyleSheet, View, Text, Dimensions } from 'react-native';
import React, { useEffect } from 'react';
import { ScrollView, StyleSheet, View, Text, Dimensions } from 'react-native';
import { Portal, Modal } from 'react-native-paper';
import * as Linking from 'expo-linking';
import { ScrollView } from 'react-native-gesture-handler';
import Button from './Button/Button';
import { Button } from '@components';
import { useBoolean, useReleaseChecker, useTheme } from '@hooks';
import { getString } from '@strings/translations';
import { useTheme } from '@hooks/useTheme';
interface NewUpdateDialogProps {
newVersion: {
tag_name: string;
body: string;
downloadUrl: string;
};
}
const NewUpdateDialog: React.FC<NewUpdateDialogProps> = ({ newVersion }) => {
const [newUpdateDialog, showNewUpdateDialog] = useState(true);
const NewUpdateDialog: React.FC = () => {
const theme = useTheme();
const newRelease = useReleaseChecker();
const {
value: isVisible,
setFalse: hideModal,
setValue: setModalVisible,
} = useBoolean();
useEffect(() => {
setModalVisible(Boolean(newRelease));
}, [newRelease]);
const modalHeight = Dimensions.get('window').height / 2;
if (!newRelease) {
return null;
}
return (
<Portal>
<Modal
visible={newUpdateDialog}
onDismiss={() => showNewUpdateDialog(false)}
visible={isVisible}
onDismiss={hideModal}
contentContainerStyle={[
styles.containerStyle,
{ backgroundColor: theme.overlay3 },
]}
>
<Text style={[styles.modalHeader, { color: theme.onSurface }]}>
{`${getString('common.newUpdateAvailable')} ${newVersion.tag_name}`}
{`${getString('common.newUpdateAvailable')} ${newRelease?.tagName}`}
</Text>
<ScrollView style={{ height: modalHeight }}>
{newVersion.body}
<Text style={{ color: theme.onSurface }}>
{newRelease?.changelog}
</Text>
</ScrollView>
<View style={styles.buttonCtn}>
<Button
title={getString('common.cancel')}
onPress={() => showNewUpdateDialog(false)}
/>
<Button title={getString('common.cancel')} onPress={hideModal} />
<Button
title={getString('common.install')}
onPress={() => Linking.openURL(newVersion.downloadUrl)}
onPress={() => Linking.openURL(newRelease?.downloadUrl)}
/>
</View>
</Modal>
@@ -67,7 +72,6 @@ const styles = StyleSheet.create({
fontSize: 20,
marginBottom: 16,
},
body: {},
buttonCtn: {
flexDirection: 'row',
marginTop: 16,
-57
View File
@@ -1,57 +0,0 @@
import { useState, useEffect } from 'react';
import { appVersion } from '../utils/versionUtils';
export const useGithubUpdateChecker = () => {
const latestReleaseUrl =
'https://api.github.com/repos/rajarsheechatterjee/lnreader/releases/latest';
const [checking, setChecking] = useState(true);
const [latestRelease, setLatestRelease] = useState();
const checkForRelease = async () => {
let res = await fetch(latestReleaseUrl);
res = await res.json();
const release = {
tag_name: res.tag_name,
body: res.body,
downloadUrl: res.assets[0].browser_download_url,
};
setLatestRelease(release);
setChecking(false);
};
const isNewVersion = versionTag => {
const regex = /[^\\d.]/;
const currentVersion = appVersion;
const newVersion = versionTag.replace(regex, '');
const newSemVer = newVersion.split('.');
const oldSemVer = currentVersion.split('.');
return oldSemVer?.some((version, index) => {
if (newSemVer[index] > version) {
return true;
}
});
};
useEffect(() => {
checkForRelease();
// .catch(e => {
// showToast(`Could not connect to github:\n${`${e}`.split(':')[1].trim()}`);
// // console.error(e);
// });
}, []);
if (!checking) {
const data = {
latestRelease,
isNewVersion: isNewVersion(latestRelease.tag_name),
};
return data;
}
};
+1
View File
@@ -10,3 +10,4 @@ export { useTheme } from './useTheme';
export { default as useCategories } from './useCategories';
export { useCategorySettings, useLibrarySettings } from './useSettings';
export { usePreviousRouteName } from './usePreviousRouteName';
export { useReleaseChecker } from './useReleaseChecker';
-7
View File
@@ -12,12 +12,6 @@ const useNovel = () => {
return novel;
};
const useChapter = () => {
const chapter = useSelector(state => state.chapterReducer);
return chapter;
};
const useFindNovel = novelId => {
let novel = useSelector(state => state.preferenceReducer.novelSettings);
novel = novel[novelId];
@@ -90,7 +84,6 @@ export {
usePreferences,
useContinueReading,
useNovel,
useChapter,
useSavedSettings,
usePosition,
};
+65
View File
@@ -0,0 +1,65 @@
import { useState, useEffect } from 'react';
import dayjs from 'dayjs';
import { useMMKVNumber } from 'react-native-mmkv';
import { appVersion } from '../utils/versionUtils';
import { showToast } from './showToast';
const LATEST_RELEASE_URL =
'https://api.github.com/repos/LNReader/lnreader/releases/latest';
export interface NewRelease {
tagName: string;
changelog: string;
downloadUrl: string;
}
const isNewVersion = (versionTag: string) => {
const regex = /[^\\d.]/;
const currentVersion = appVersion;
const newVersion = versionTag.replace(regex, '');
const newSemVer = newVersion.split('.');
const oldSemVer = currentVersion.split('.');
return oldSemVer?.some((version, index) => {
if (newSemVer[index] > version) {
return true;
}
});
};
export const useReleaseChecker = () => {
const [lastChecked, setLastChecked] = useMMKVNumber('last_app_update_check');
const [newRelease, setNewRelease] = useState<NewRelease>();
const getNewRelease = async () => {
try {
if (lastChecked && dayjs(lastChecked).diff(undefined, 'hours') < 24) {
return;
}
const newReleaseRes = await fetch(LATEST_RELEASE_URL);
const newReleaseJSON = await newReleaseRes.json();
if (isNewVersion(newReleaseJSON.tag_name)) {
setNewRelease({
changelog: newReleaseJSON.body,
downloadUrl: newReleaseJSON.assets[0].browser_download_url,
tagName: newReleaseJSON.tag_name,
});
}
setLastChecked(+Date.now());
} catch (error: any) {
showToast(error.message);
}
};
useEffect(() => {
getNewRelease();
}, []);
return newRelease;
};
@@ -1,5 +1,4 @@
import React from 'react';
import Color from 'color';
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
import Library from '../screens/library/LibraryScreen';
-5
View File
@@ -5,7 +5,6 @@ import { createStackNavigator } from '@react-navigation/stack';
import { setBarColor } from '../theme/utils/setBarColor';
import { useTheme } from '../hooks/useTheme';
import { useGithubUpdateChecker } from '../hooks/githubUpdateChecker';
/**
* Navigators
@@ -26,7 +25,6 @@ import MigrateNovel from '../screens/browse/migration/MigrationNovels';
import MalTopNovels from '../screens/browse/discover/MalTopNovels';
import AniListTopNovels from '../screens/browse/discover/AniListTopNovels';
import NewUpdateDialog from '../components/NewUpdateDialog';
import BrowseSettings from '../screens/browse/BrowseSettings';
import { useAppDispatch } from '@redux/hooks';
import { updateLibraryAction } from '@redux/updates/updates.actions';
@@ -56,11 +54,8 @@ const MainNavigator = () => {
}
}, []);
const { isNewVersion, latestRelease } = useGithubUpdateChecker() || {};
return (
<NavigationContainer theme={{ colors: theme }}>
{isNewVersion && <NewUpdateDialog newVersion={latestRelease} />}
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="BottomNavigator" component={BottomNavigator} />
<Stack.Screen name="Novel" component={Novel} />