Generic modal + version check (#111)

This commit is contained in:
Leszek Stachowski
2025-02-14 16:46:32 +01:00
committed by GitHub
parent b120d6c11e
commit 6169313149
8 changed files with 168 additions and 1 deletions

View File

@@ -65,9 +65,11 @@
"react": "^18.3.1",
"react-native": "0.75.4",
"react-native-biometrics": "^3.0.1",
"react-native-check-version": "^1.3.0",
"react-native-cloud-storage": "^2.2.2",
"react-native-crypto": "^2.2.0",
"react-native-date-picker": "https://github.com/norman-kapschefsky/react-native-date-picker#07b13884e392f386611248bab0f2f9b1093b2f35",
"react-native-device-info": "^14.0.4",
"react-native-dialog": "^9.3.0",
"react-native-dotenv": "^3.4.11",
"react-native-fs": "^2.20.0",

View File

@@ -30,6 +30,7 @@ import ProveScreen from './screens/ProveFlow/ProveScreen';
import QRCodeViewFinderScreen from './screens/ProveFlow/ViewFinder';
import CloudBackupScreen from './screens/Settings/CloudBackupScreen';
import DevSettingsScreen from './screens/Settings/DevSettingsScreen';
import ModalScreen from './screens/Settings/ModalScreen';
import PassportDataInfoScreen from './screens/Settings/PassportDataInfoScreen';
import ShowRecoveryPhraseScreen from './screens/Settings/ShowRecoveryPhraseScreen';
import SettingsScreen from './screens/SettingsScreen';
@@ -67,6 +68,14 @@ const AppNavigation = createNativeStackNavigator({
headerShown: false,
},
},
Modal: {
screen: ModalScreen,
options: {
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
},
},
/**
* SCAN PASSPORT FLOW
*/

View File

@@ -0,0 +1,38 @@
import { useState } from 'react';
import { Linking } from 'react-native';
import { checkVersion } from 'react-native-check-version';
import { useNavigation } from '@react-navigation/native';
export const useAppUpdates = (): [boolean, () => void, boolean] => {
const navigation = useNavigation();
const [newVersionUrl, setNewVersionUrl] = useState<string | null>(null);
const [isModalDismissed, setIsModalDismissed] = useState(false);
checkVersion().then(version => {
if (version.needsUpdate) {
setNewVersionUrl(version.url);
}
});
const showAppUpdateModal = () => {
navigation.navigate('Modal', {
titleText: 'New Version Available',
bodyText:
"We've improved performance, fixed bugs, and added new features. Update now to install the latest version of Self.",
buttonText: 'Update and restart',
onButtonPress: () => {
if (newVersionUrl !== null) {
Linking.openURL(
newVersionUrl, // TODO or use: `Platform.OS === 'ios' ? appStoreUrl : playStoreUrl`
);
}
},
onModalDismiss: () => {
setIsModalDismissed(true);
},
});
};
return [newVersionUrl !== null, showAppUpdateModal, isModalDismissed];
};

View File

@@ -0,0 +1,3 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.6797 24.7617C11.0078 24.7617 9.43359 24.4414 7.95703 23.8008C6.48047 23.168 5.17969 22.2891 4.05469 21.1641C2.92969 20.0391 2.04688 18.7422 1.40625 17.2734C0.773438 15.7969 0.457031 14.2188 0.457031 12.5391C0.457031 10.8672 0.773438 9.29688 1.40625 7.82812C2.04688 6.35156 2.92578 5.05078 4.04297 3.92578C5.16797 2.80078 6.46875 1.92188 7.94531 1.28906C9.42188 0.648437 10.9961 0.328125 12.668 0.328125C14.3398 0.328125 15.9141 0.648437 17.3906 1.28906C18.875 1.92188 20.1758 2.80078 21.293 3.92578C22.418 5.05078 23.3008 6.35156 23.9414 7.82812C24.582 9.29688 24.9023 10.8672 24.9023 12.5391C24.9023 14.2188 24.582 15.7969 23.9414 17.2734C23.3008 18.7422 22.418 20.0391 21.293 21.1641C20.1758 22.2891 18.8789 23.168 17.4023 23.8008C15.9258 24.4414 14.3516 24.7617 12.6797 24.7617ZM8.89453 17.5312C9.25391 17.5312 9.55469 17.4141 9.79688 17.1797L12.6914 14.2734L15.5977 17.1797C15.8242 17.4141 16.1133 17.5312 16.4648 17.5312C16.8086 17.5312 17.0977 17.4141 17.332 17.1797C17.5664 16.9453 17.6836 16.6562 17.6836 16.3125C17.6836 15.9766 17.5625 15.6953 17.3203 15.4688L14.4023 12.5508L17.332 9.63281C17.5664 9.39062 17.6836 9.10938 17.6836 8.78906C17.6836 8.44531 17.5664 8.16016 17.332 7.93359C17.1055 7.69922 16.8242 7.58203 16.4883 7.58203C16.1445 7.58203 15.8555 7.69922 15.6211 7.93359L12.6914 10.8516L9.77344 7.94531C9.53125 7.71094 9.23828 7.59375 8.89453 7.59375C8.55859 7.59375 8.27344 7.71094 8.03906 7.94531C7.8125 8.17188 7.69922 8.45703 7.69922 8.80078C7.69922 9.12109 7.81641 9.39844 8.05078 9.63281L10.9805 12.5508L8.05078 15.4805C7.81641 15.7148 7.69922 15.9922 7.69922 16.3125C7.69922 16.6562 7.8125 16.9453 8.03906 17.1797C8.27344 17.4141 8.55859 17.5312 8.89453 17.5312Z" fill="#D4D4D4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,12 @@
<svg width="34" height="35" viewBox="0 0 34 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4532_4109)">
<path d="M17.0047 13.5474H17C14.8171 13.5474 13.0475 15.317 13.0475 17.4999V17.5046C13.0475 19.6875 14.8171 21.4571 17 21.4571H17.0047C19.1876 21.4571 20.9572 19.6875 20.9572 17.5046V17.4999C20.9572 15.317 19.1876 13.5474 17.0047 13.5474Z" fill="#00FFB6"/>
<path d="M9.24611 13.8403C9.24611 11.4933 11.1492 9.59028 13.4961 9.59028H21.7033L30.7936 0.5H8.13167L0 8.63167V22.0097H9.24611V13.8356V13.8403Z" fill="black"/>
<path d="M24.7539 12.957V20.8479C24.7539 23.1948 22.8509 25.0979 20.5039 25.0979H12.6131L3.20642 34.5045H25.8684L34 26.3729V12.9618H24.7539V12.957Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_4532_4109">
<rect width="34" height="34" fill="white" transform="translate(0 0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 860 B

View File

@@ -1,11 +1,12 @@
import React from 'react';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { usePreventRemove } from '@react-navigation/native';
import { useFocusEffect, usePreventRemove } from '@react-navigation/native';
import { Button, YStack, styled } from 'tamagui';
import { BodyText } from '../components/typography/BodyText';
import { Caption } from '../components/typography/Caption';
import { useAppUpdates } from '../hooks/useAppUpdates';
import useHapticNavigation from '../hooks/useHapticNavigation';
import SelfCard from '../images/card-style-1.svg';
import ScanIcon from '../images/icons/qr_scan.svg';
@@ -25,6 +26,15 @@ const ScanButton = styled(Button, {
});
const HomeScreen: React.FC = () => {
const [isNewVersionAvailable, showAppUpdateModal, isModalDismissed] =
useAppUpdates();
useFocusEffect(() => {
if (isNewVersionAvailable && !isModalDismissed) {
showAppUpdateModal();
}
});
const onCaptionPress = useHapticNavigation('ConfirmBelongingScreen');
const onScanButtonPress = useHapticNavigation('QRCodeViewFinder');
// Prevents back navigation

View File

@@ -0,0 +1,72 @@
import React from 'react';
import { StaticScreenProps } from '@react-navigation/native';
import { View, XStack, YStack, styled } from 'tamagui';
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
import Description from '../../components/typography/Description';
import { Title } from '../../components/typography/Title';
import useHapticNavigation from '../../hooks/useHapticNavigation';
import ModalClose from '../../images/icons/modal_close.svg';
import LogoInversed from '../../images/logo_inversed.svg';
import { white } from '../../utils/colors';
const ModalBackDrop = styled(View, {
display: 'flex',
alignItems: 'center',
// TODO cannot use filter(blur), so increased opacity
backgroundColor: '#000000BB',
alignContent: 'center',
alignSelf: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
});
const ModalDescription = styled(Description, {
textAlign: 'left',
});
interface ModalScreenProps
extends StaticScreenProps<{
titleText: string;
bodyText: string;
buttonText: string;
onButtonPress: () => void;
onModalDismiss: () => void;
}> {}
const ModalScreen: React.FC<ModalScreenProps> = ({ route: { params } }) => {
const navigateBack = useHapticNavigation('Home', { action: 'cancel' });
const onButtonPressed = () => {
params?.onButtonPress();
navigateBack();
};
return (
<ModalBackDrop>
<View backgroundColor={white} padding={20} borderRadius={10} mx={8}>
<YStack gap={40}>
<XStack alignItems="center" justifyContent="space-between">
<LogoInversed />
<ModalClose
onPress={() => {
navigateBack();
params?.onModalDismiss();
}}
/>
</XStack>
<YStack gap={20}>
<Title textAlign="left">{params?.titleText}</Title>
<ModalDescription>{params?.bodyText}</ModalDescription>
</YStack>
<PrimaryButton onPress={onButtonPressed}>
{params?.buttonText}
</PrimaryButton>
</YStack>
</View>
</ModalBackDrop>
);
};
export default ModalScreen;

View File

@@ -12474,9 +12474,11 @@ __metadata:
react-native: "npm:0.75.4"
react-native-biometrics: "npm:^3.0.1"
react-native-bundle-visualizer: "npm:^3.1.3"
react-native-check-version: "npm:^1.3.0"
react-native-cloud-storage: "npm:^2.2.2"
react-native-crypto: "npm:^2.2.0"
react-native-date-picker: "https://github.com/norman-kapschefsky/react-native-date-picker#07b13884e392f386611248bab0f2f9b1093b2f35"
react-native-device-info: "npm:^14.0.4"
react-native-dialog: "npm:^9.3.0"
react-native-dotenv: "npm:^3.4.11"
react-native-fs: "npm:^2.20.0"
@@ -13123,6 +13125,16 @@ __metadata:
languageName: node
linkType: hard
"react-native-check-version@npm:^1.3.0":
version: 1.3.0
resolution: "react-native-check-version@npm:1.3.0"
peerDependencies:
react-native: ">=0.40.0"
react-native-device-info: ">=0.20.0"
checksum: 10c0/6a4216841ae0d59cb1fd952107960b29867423838f8bbfbd9a49a53ed50e240e6e3892408865009188a9c7f3b56c2eec730147b0f1374ae0f15ee6c2979c7deb
languageName: node
linkType: hard
"react-native-cloud-storage@npm:^2.2.2":
version: 2.2.2
resolution: "react-native-cloud-storage@npm:2.2.2"
@@ -13167,6 +13179,15 @@ __metadata:
languageName: node
linkType: hard
"react-native-device-info@npm:^14.0.4":
version: 14.0.4
resolution: "react-native-device-info@npm:14.0.4"
peerDependencies:
react-native: "*"
checksum: 10c0/3d89519b4e9f9f130b352f5243282f8b807aba0f55ccc0e1dc9f15b285215949751491ef2f11f1984a55f0f5e0ae76cd16620d2cdfcc118e2ca5f39d300d5524
languageName: node
linkType: hard
"react-native-dialog@npm:^9.3.0":
version: 9.3.0
resolution: "react-native-dialog@npm:9.3.0"