react-native-app-update-helper
Version:
This packages provides a customizable update overlay.
525 lines (524 loc) • 18.8 kB
JavaScript
;
import { useEffect, useState } from 'react';
import { Text, TouchableOpacity, View, StyleSheet, Dimensions, Image, Platform, Modal, Linking } from 'react-native';
import AppUpdatedOverlay from "./components/AppUpdatedOverlay.js";
import { getStoredValue, storeValue } from "./components/store.js";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
const {
width
} = Dimensions.get('window');
const alert = require('./alertImage.png');
const AVAILABLE_UPDATE_STORAGE_KEY = 'o2bY6kS6bTUAFNZD';
const STORED_VERSION_UPDATE_KEY = 'nDUM3KXAwqQDR9KA';
export const AutoUpdateOverlay = ({
currentVersion,
iosStoreLink,
androidStoreLink,
icon = alert,
mainTitle = 'Update Available',
description = 'Please update the application to the latest version.',
buttonTitle = 'Update',
isWhatsNewRequired = true,
whatsNewDescription,
primaryColor = '#11B8B2',
backgroundColor = '#EEEEEE',
titleFontFamily,
descriptionFontFamily,
buttonTitleFontFamily,
titleFontSize = 24,
descriptionFontSize = 14,
buttonTitleFontSize = 14
}) => {
const [autoDetectedVersionNeedsUpdate, setAutoDetectedVersionNeedsUpdate] = useState(false);
const [isUpdateAvailable, setIsUpdateAvailable] = useState(null);
const [isAppUpdated, setIsAppUpdated] = useState(false);
const [whatsNewContent, setWhatsNewContent] = useState('');
const [latestVersion, setLatestVersion] = useState('');
const fetchGooglePlayVersion = async () => {
try {
const googlePlayResponse = await fetch(androidStoreLink);
const googlePlayText = await googlePlayResponse.text();
const googlePlayVersionMatch = googlePlayText.match(/\[\[\["([\d.]+?)"\]\]/);
if (googlePlayVersionMatch && googlePlayVersionMatch[1]) {
const latestVersion = googlePlayVersionMatch[1].trim();
const currentVersionParts = currentVersion.split('.').map(Number);
const latestVersionParts = latestVersion.split('.').map(Number);
setLatestVersion(latestVersion);
for (let i = 0; i < Math.max(currentVersionParts.length, latestVersionParts.length); i++) {
const currentPart = currentVersionParts[i] || 0;
const latestPart = latestVersionParts[i] || 0;
if (currentPart < latestPart) {
if (i == 2) {
const shown = await getStoredValue({
storageTag: AVAILABLE_UPDATE_STORAGE_KEY
});
if (shown == null) {
await storeValue({
storageTag: AVAILABLE_UPDATE_STORAGE_KEY,
valueToStore: 'shown'
});
setIsUpdateAvailable(true);
setAutoDetectedVersionNeedsUpdate(true);
} else {
setAutoDetectedVersionNeedsUpdate(false);
}
} else {
setIsUpdateAvailable(false);
setAutoDetectedVersionNeedsUpdate(true);
}
return;
} else if (currentPart > latestPart) {
setAutoDetectedVersionNeedsUpdate(false);
setIsUpdateAvailable(false);
return;
}
}
setAutoDetectedVersionNeedsUpdate(false);
setIsUpdateAvailable(false);
}
} catch (error) {
console.error('Error fetching Google Play Store version:', error);
}
};
const fetchAppStoreVersion = async () => {
try {
const appStoreResponse = await fetch(iosStoreLink);
const appStoreText = await appStoreResponse.text();
const appStoreVersionMatch = appStoreText.match(/Version\s([\d.]+)/);
if (appStoreVersionMatch && appStoreVersionMatch[1]) {
const latestVersion = appStoreVersionMatch[1].trim();
const currentVersionParts = currentVersion.split('.').map(Number);
const latestVersionParts = latestVersion.split('.').map(Number);
setLatestVersion(latestVersion);
for (let i = 0; i < Math.max(currentVersionParts.length, latestVersionParts.length); i++) {
const currentPart = currentVersionParts[i] || 0;
const latestPart = latestVersionParts[i] || 0;
if (currentPart < latestPart) {
if (i == 2) {
const shown = await getStoredValue({
storageTag: AVAILABLE_UPDATE_STORAGE_KEY
});
if (shown == null) {
await storeValue({
storageTag: AVAILABLE_UPDATE_STORAGE_KEY,
valueToStore: 'shown'
});
setIsUpdateAvailable(true);
setAutoDetectedVersionNeedsUpdate(true);
} else {
setAutoDetectedVersionNeedsUpdate(false);
}
} else {
setIsUpdateAvailable(false);
setAutoDetectedVersionNeedsUpdate(true);
}
return;
} else if (currentPart > latestPart) {
setAutoDetectedVersionNeedsUpdate(false);
setIsUpdateAvailable(false);
return;
}
}
setAutoDetectedVersionNeedsUpdate(false);
setIsUpdateAvailable(false);
}
} catch (error) {
console.error('Error fetching App Store version:', error);
}
};
const fetchWhatsNew = async () => {
if (Platform.OS == 'android') {
const googlePlayResponse = await fetch(androidStoreLink);
const googlePlayText = await googlePlayResponse.text();
const description = googlePlayText.match(/<div itemprop="description">(.*?)<\/div>/s);
if (description && description[1]) {
setWhatsNewContent(description[1]);
setIsAppUpdated(true);
}
} else {
const appStoreResponse = await fetch(iosStoreLink);
const appStoreText = await appStoreResponse.text();
const descriptionSection = appStoreText.match(/<div class="we-truncate we-truncate--multi-line we-truncate--interactive " dir>([\s\S]*?)<\/div>/gi);
if (descriptionSection && descriptionSection[0]) {
const description = descriptionSection[0].match(/<p dir="false" data-test-bidi>([\s\S]*?)<\/p>/i);
if (description && description[1]) {
setWhatsNewContent(description[1]);
setIsAppUpdated(true);
}
}
}
};
const checkIfUserUpdated = async () => {
try {
const storedVersion = await getStoredValue({
storageTag: STORED_VERSION_UPDATE_KEY
});
if (storedVersion !== null) {
if (storedVersion == currentVersion) {
setIsAppUpdated(false);
} else {
await storeValue({
storageTag: STORED_VERSION_UPDATE_KEY,
valueToStore: currentVersion
});
if (whatsNewDescription) {
setWhatsNewContent(whatsNewDescription);
setIsAppUpdated(true);
} else {
fetchWhatsNew();
}
}
} else {
await storeValue({
storageTag: STORED_VERSION_UPDATE_KEY,
valueToStore: currentVersion
});
}
} catch (e) {
console.log(e, 'Error story current version');
}
};
const onDownloadButtonPress = () => {
Linking.openURL(Platform.OS == 'ios' ? iosStoreLink : androidStoreLink);
};
useEffect(() => {
if (Platform.OS == 'ios') {
fetchAppStoreVersion();
} else {
fetchGooglePlayVersion();
}
if (isWhatsNewRequired) {
checkIfUserUpdated();
}
}, [currentVersion, iosStoreLink, androidStoreLink, isWhatsNewRequired]);
return /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(Modal, {
visible: autoDetectedVersionNeedsUpdate,
transparent: true,
animationType: "slide",
onRequestClose: () => {
setAutoDetectedVersionNeedsUpdate(false);
},
children: /*#__PURE__*/_jsx(View, {
style: [styles.centeredView, {
backgroundColor: backgroundColor
}],
children: /*#__PURE__*/_jsxs(View, {
style: styles.modalView,
children: [/*#__PURE__*/_jsx(Image, {
source: icon,
style: styles.icon
}), /*#__PURE__*/_jsx(Text, {
style: [styles.mainTitle, {
fontFamily: titleFontFamily ? titleFontFamily : undefined,
fontSize: titleFontSize
}],
children: mainTitle
}), /*#__PURE__*/_jsx(Text, {
style: [styles.description, {
fontFamily: descriptionFontFamily ? descriptionFontFamily : undefined,
fontSize: descriptionFontSize,
lineHeight: descriptionFontSize + 2
}],
children: description
}), /*#__PURE__*/_jsxs(_Fragment, {
children: [!isUpdateAvailable && /*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, {
backgroundColor: primaryColor
}],
onPress: onDownloadButtonPress,
children: /*#__PURE__*/_jsx(Text, {
style: [styles.buttonTitle, {
fontFamily: buttonTitleFontFamily ? buttonTitleFontFamily : undefined,
fontSize: buttonTitleFontSize
}],
children: buttonTitle
})
}), isUpdateAvailable && /*#__PURE__*/_jsxs(View, {
style: styles.buttonsContainer,
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, {
backgroundColor: primaryColor
}],
onPress: onDownloadButtonPress,
children: /*#__PURE__*/_jsx(Text, {
style: [styles.buttonTitle, {
fontFamily: buttonTitleFontFamily ? buttonTitleFontFamily : undefined,
fontSize: buttonTitleFontSize
}],
children: "Update"
})
}), /*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, styles.dismissButton],
onPress: () => setAutoDetectedVersionNeedsUpdate(false),
children: /*#__PURE__*/_jsx(Text, {
style: [styles.buttonTitle, styles.dismissButtonTitle, {
fontFamily: buttonTitleFontFamily ? buttonTitleFontFamily : undefined,
color: primaryColor,
fontSize: buttonTitleFontSize
}],
children: "Skip for now"
})
})]
})]
})]
})
})
}), isWhatsNewRequired && isAppUpdated ? /*#__PURE__*/_jsx(AppUpdatedOverlay, {
content: whatsNewContent,
version: latestVersion,
primaryColor: primaryColor,
backgroundColor: backgroundColor,
titleFontFamily: titleFontFamily,
descriptionFontFamily: descriptionFontFamily,
buttonTitleFontFamily: buttonTitleFontFamily,
titleFontSize: titleFontSize,
descriptionFontSize: descriptionFontSize,
buttonTitleFontSize: buttonTitleFontSize
}) : /*#__PURE__*/_jsx(_Fragment, {})]
});
};
export const ManualUpdateOverlay = ({
updateAvailable = false,
currentVersion,
iosStoreLink,
androidStoreLink,
icon = alert,
mainTitle = 'Update Available',
description = 'Please update the application to the latest version.',
buttonTitle = 'Update',
primaryColor = '#11B8B2',
backgroundColor = '#EEEEEE',
isWhatsNewRequired = true,
isMandatoryUpdate,
whatsNewDescription,
onDismissButtonPress,
titleFontFamily,
descriptionFontFamily,
buttonTitleFontFamily,
titleFontSize = 24,
descriptionFontSize = 14,
buttonTitleFontSize = 14
}) => {
const [isAppUpdated, setIsAppUpdated] = useState(false);
const [whatsNewContent, setWhatsNewContent] = useState('');
const fetchWhatsNew = async () => {
if (Platform.OS == 'android') {
const googlePlayResponse = await fetch(androidStoreLink);
const googlePlayText = await googlePlayResponse.text();
const description = googlePlayText.match(/<div itemprop="description">(.*?)<\/div>/s);
if (description && description[1]) {
setWhatsNewContent(description[1]);
setIsAppUpdated(true);
}
} else {
const appStoreResponse = await fetch(iosStoreLink);
const appStoreText = await appStoreResponse.text();
const descriptionSection = appStoreText.match(/<div class="we-truncate we-truncate--multi-line we-truncate--interactive " dir>([\s\S]*?)<\/div>/gi);
if (descriptionSection && descriptionSection[0]) {
const description = descriptionSection[0].match(/<p dir="false" data-test-bidi>([\s\S]*?)<\/p>/i);
if (description && description[1]) {
setWhatsNewContent(description[1]);
setIsAppUpdated(true);
}
}
}
};
const checkIfUserUpdated = async () => {
try {
const storedVersion = await getStoredValue({
storageTag: STORED_VERSION_UPDATE_KEY
});
if (storedVersion !== null) {
if (storedVersion == currentVersion) {
setIsAppUpdated(false);
} else {
await storeValue({
storageTag: STORED_VERSION_UPDATE_KEY,
valueToStore: currentVersion
});
if (whatsNewDescription) {
setWhatsNewContent(whatsNewDescription);
setIsAppUpdated(true);
} else {
fetchWhatsNew();
}
}
} else {
await storeValue({
storageTag: STORED_VERSION_UPDATE_KEY,
valueToStore: currentVersion
});
}
} catch (e) {
console.log(e, 'Error story current version');
}
};
const onDownloadButtonPress = () => {
Linking.openURL(Platform.OS == 'ios' ? iosStoreLink : androidStoreLink);
};
useEffect(() => {
if (isWhatsNewRequired) {
checkIfUserUpdated();
}
}, [currentVersion, iosStoreLink, androidStoreLink, whatsNewDescription, isWhatsNewRequired]);
return /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(Modal, {
visible: updateAvailable,
transparent: true,
animationType: "slide",
onRequestClose: () => {
onDismissButtonPress();
},
children: /*#__PURE__*/_jsx(View, {
style: [styles.centeredView, {
backgroundColor: backgroundColor
}],
children: /*#__PURE__*/_jsxs(View, {
style: styles.modalView,
children: [/*#__PURE__*/_jsx(Image, {
source: icon,
style: styles.icon
}), /*#__PURE__*/_jsx(Text, {
style: [styles.mainTitle, {
fontFamily: titleFontFamily ? titleFontFamily : undefined,
fontSize: titleFontSize
}],
children: mainTitle
}), /*#__PURE__*/_jsx(Text, {
style: [styles.description, {
fontFamily: descriptionFontFamily ? descriptionFontFamily : undefined,
fontSize: descriptionFontSize,
lineHeight: descriptionFontSize + 2
}],
children: description
}), isMandatoryUpdate && /*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, {
backgroundColor: primaryColor
}],
onPress: onDownloadButtonPress,
children: /*#__PURE__*/_jsx(Text, {
style: [styles.buttonTitle, {
fontFamily: buttonTitleFontFamily ? buttonTitleFontFamily : undefined,
fontSize: buttonTitleFontSize
}],
children: buttonTitle
})
}), !isMandatoryUpdate && /*#__PURE__*/_jsxs(View, {
style: styles.buttonsContainer,
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, {
backgroundColor: primaryColor
}],
onPress: onDownloadButtonPress,
children: /*#__PURE__*/_jsx(Text, {
style: [styles.buttonTitle, {
fontFamily: buttonTitleFontFamily ? buttonTitleFontFamily : undefined,
fontSize: buttonTitleFontSize
}],
children: "Update"
})
}), /*#__PURE__*/_jsx(TouchableOpacity, {
style: [styles.button, styles.dismissButton],
onPress: onDismissButtonPress,
children: /*#__PURE__*/_jsx(Text, {
style: [styles.buttonTitle, styles.dismissButtonTitle, {
fontFamily: buttonTitleFontFamily ? buttonTitleFontFamily : undefined,
color: primaryColor,
fontSize: buttonTitleFontSize
}],
children: "Skip for now"
})
})]
})]
})
})
}), isWhatsNewRequired && isAppUpdated ? /*#__PURE__*/_jsx(AppUpdatedOverlay, {
content: whatsNewContent,
version: currentVersion,
primaryColor: primaryColor,
backgroundColor: backgroundColor,
titleFontFamily: titleFontFamily,
descriptionFontFamily: descriptionFontFamily,
buttonTitleFontFamily: buttonTitleFontFamily,
titleFontSize: titleFontSize,
descriptionFontSize: descriptionFontSize,
buttonTitleFontSize: buttonTitleFontSize
}) : /*#__PURE__*/_jsx(_Fragment, {})]
});
};
const mainStyles = {
color: {
title: '#14202B',
text: '#9B9B9B',
placeholder: '#a0a0a0',
white: '#FFFFFF'
},
fontSize: {
title: 20,
small: 12
},
borderRadius: {
main: 25,
small: 8,
extraSmall: 4
}
};
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
modalView: {
position: 'absolute',
bottom: 0,
width: width,
backgroundColor: 'white',
padding: 35,
paddingBottom: 20,
alignItems: 'center',
justifyContent: 'center'
},
icon: {
height: 64,
width: 64,
marginBottom: 20
},
mainTitle: {
color: mainStyles.color.title,
fontSize: mainStyles.fontSize.title,
textAlign: 'center',
marginBottom: 20
},
description: {
color: mainStyles.color.text,
fontSize: mainStyles.fontSize.small,
textAlign: 'center',
width: width / 1.2,
lineHeight: 24,
marginBottom: 30
},
button: {
width: width - 40,
paddingVertical: 15,
paddingHorizontal: 20,
borderRadius: mainStyles.borderRadius.extraSmall,
marginBottom: 10
},
dismissButton: {
backgroundColor: 'transparent'
},
buttonTitle: {
color: mainStyles.color.white,
fontSize: mainStyles.fontSize.small,
textAlign: 'center'
},
dismissButtonTitle: {
color: mainStyles.color.text
},
buttonsContainer: {
alignItems: 'center'
}
});
//# sourceMappingURL=index.js.map