mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
Generic modal + version check (#111)
This commit is contained in:
committed by
GitHub
parent
b120d6c11e
commit
6169313149
@@ -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",
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
38
app/src/hooks/useAppUpdates.ts
Normal file
38
app/src/hooks/useAppUpdates.ts
Normal 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];
|
||||
};
|
||||
3
app/src/images/icons/modal_close.svg
Normal file
3
app/src/images/icons/modal_close.svg
Normal 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 |
12
app/src/images/logo_inversed.svg
Normal file
12
app/src/images/logo_inversed.svg
Normal 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 |
@@ -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
|
||||
|
||||
72
app/src/screens/Settings/ModalScreen.tsx
Normal file
72
app/src/screens/Settings/ModalScreen.tsx
Normal 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;
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user