diff --git a/package.json b/package.json
index 7ac7f4f..f9853b6 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"react": "17.0.1",
"react-i18next": "^11.8.4",
"react-native": "0.63.4",
+ "react-native-app-intro-slider": "^4.0.4",
"react-native-circular-progress": "^1.3.7",
"react-native-dotenv": "^2.5.3",
"react-native-dropdown-picker": "^5.1.21",
diff --git a/src/images/mockPhone.svg b/src/images/mockPhone.svg
new file mode 100644
index 0000000..b775456
--- /dev/null
+++ b/src/images/mockPhone.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/images/mockPhoneWithGeo.svg b/src/images/mockPhoneWithGeo.svg
new file mode 100644
index 0000000..3446cd6
--- /dev/null
+++ b/src/images/mockPhoneWithGeo.svg
@@ -0,0 +1,51 @@
+
diff --git a/src/images/onboardingBottom.png b/src/images/onboardingBottom.png
new file mode 100644
index 0000000..6ae4982
Binary files /dev/null and b/src/images/onboardingBottom.png differ
diff --git a/src/images/onboardingHighBottom.png b/src/images/onboardingHighBottom.png
new file mode 100644
index 0000000..e52bffa
Binary files /dev/null and b/src/images/onboardingHighBottom.png differ
diff --git a/src/images/onboardingHighTop.png b/src/images/onboardingHighTop.png
new file mode 100644
index 0000000..410419c
Binary files /dev/null and b/src/images/onboardingHighTop.png differ
diff --git a/src/images/onboardingTop.png b/src/images/onboardingTop.png
new file mode 100644
index 0000000..bee1042
Binary files /dev/null and b/src/images/onboardingTop.png differ
diff --git a/src/navigation/mainTabs.tsx b/src/navigation/mainTabs.tsx
index 7afe40b..6b11b8d 100644
--- a/src/navigation/mainTabs.tsx
+++ b/src/navigation/mainTabs.tsx
@@ -11,6 +11,7 @@ import Account from '../images/navigation/account.svg';
import Quests from '../images/navigation/quests.svg';
import TabBar from '../components/TabBar';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import { useAuthContext } from '../contexts/AuthProvider';
/**
* Type with params of screens and their props in BottomTabNavigator
@@ -36,6 +37,7 @@ const Icon = styled.View<{color: string}>`
* Functional component for implementing navigation between screens
*/
export default function MainTabsNavigation(): React.ReactElement {
+ const authContext = useAuthContext();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
@@ -76,6 +78,7 @@ export default function MainTabsNavigation(): React.ReactElement {
{
return ;
diff --git a/src/navigation/profileStack.tsx b/src/navigation/profileStack.tsx
index 4d73a0b..1fef4c0 100644
--- a/src/navigation/profileStack.tsx
+++ b/src/navigation/profileStack.tsx
@@ -15,6 +15,7 @@ import FriendAddingScreenWithSuspense from '../screens/FriendsAdding';
import ChangeUsernameScreen from '../screens/ChangeUsername';
import ChangePasswordScreen from '../screens/ChangePassword';
import AchievementsScreenWithSuspense from '../screens/Achievements';
+import OnboardingSlider from '../onboarding/OnboardingSlider';
/**
* Type with params of screens and their props in ProfileStackScreen
@@ -94,6 +95,11 @@ export type ProfileStackParamList = {
* FriendAdding screen props
*/
FriendAdding: undefined;
+
+ /**
+ * Onboarding stack props
+ */
+ Onboarding: undefined;
};
const ProfileStack = createStackNavigator();
@@ -105,8 +111,13 @@ export default function ProfileStackNavigation(): React.ReactElement {
const authContext = useAuthContext();
return (
-
- {authContext.state.isFirstRegistration && }
+
+ {authContext.state.isFirstRegistration &&
+ <>
+
+
+ >
+ }
diff --git a/src/onboarding/OnboardingSlider.tsx b/src/onboarding/OnboardingSlider.tsx
new file mode 100644
index 0000000..4762d11
--- /dev/null
+++ b/src/onboarding/OnboardingSlider.tsx
@@ -0,0 +1,108 @@
+import React from 'react';
+import AppIntroSlider from 'react-native-app-intro-slider';
+import MainInfo from './screens/MainInfo';
+import AboutQuests from './screens/AboutQuests';
+import AboutGeolocation from './screens/AboutGeolocation';
+import QuestPassing from './screens/QuestPassing';
+import AboutModalize from './screens/AboutModalize';
+import Colors from '../styles/colors';
+import styled from 'styled-components/native';
+import { StyledFonts } from '../styles/textStyles';
+import { useAuthContext } from '../contexts/AuthProvider';
+import Back from '../images/back.svg';
+
+const data = [
+ {
+ index: 1,
+ component: ,
+ },
+ {
+ index: 2,
+ component: ,
+ },
+ {
+ index: 3,
+ component: ,
+ },
+ {
+ index: 4,
+ component: ,
+ },
+ {
+ index: 5,
+ component: ,
+ },
+];
+
+type Item = typeof data[0];
+
+const dot = {
+ backgroundColor: 'rgba(104, 198, 223, 0.3)',
+ width: 15,
+ height: 15,
+ borderRadius: 8,
+};
+
+const activeDot = {
+ backgroundColor: Colors.Blue,
+ width: 15,
+ height: 15,
+ borderRadius: 8,
+};
+
+const DoneButton = styled.TouchableOpacity`
+ padding: 13px;
+ border-radius: 30px;
+ flex-direction: row;
+ align-items: center;
+`;
+
+const ButtonText = styled.Text`
+ ${StyledFonts.uiWebMedium};
+ font-size: 16px;
+ color: ${Colors.DarkBlue};
+`;
+
+const NextArrow = styled(Back)`
+ color: ${Colors.DarkBlue};
+ transform: rotate(180deg);
+ margin-left: 10px;
+`;
+
+/**
+ *
+ */
+export default function OnboardingSlider(): React.ReactElement {
+ const authContext = useAuthContext();
+
+ const _renderItem = ({ item }: {item: Item}): React.ReactElement => {
+ return (
+ <>
+ {item.component}
+ >
+ );
+ };
+
+ const _renderDoneButton = (): React.ReactElement => {
+ return (
+ authContext.actions.setFirstRegistrationFalse()}>
+ Done
+
+
+ );
+ };
+
+ const _keyExtractor = (item: Item): string => item.index.toString();
+
+ return (
+
+ );
+}
diff --git a/src/onboarding/components/OnboardingBody.tsx b/src/onboarding/components/OnboardingBody.tsx
new file mode 100644
index 0000000..f566049
--- /dev/null
+++ b/src/onboarding/components/OnboardingBody.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import styled from 'styled-components/native';
+import Colors from '../../styles/colors';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import { Dimensions } from 'react-native';
+
+const Body = styled.View<{bottomOffset: number}>`
+ background-color: ${Colors.Background};
+ flex: 1;
+ padding: ${props => 60 + props.bottomOffset}px 15px;
+ justify-content: space-between;
+`;
+
+const PatternView = styled.View<{pos: 'top' | 'bottom'}>`
+ position: absolute;
+ ${props => props.pos === 'top' ? 'top: 0;' : 'bottom: 0;'}
+ width: ${Dimensions.get('screen').width}px;
+ aspect-ratio: ${375 / 218};
+`;
+
+const Pattern = styled.Image`
+ width: 100%;
+ height: 100%;
+`;
+
+interface OnboardingBodyProps {
+ /**
+ * If it is first screen of onboarding
+ */
+ isFirstScreen?: boolean,
+
+ /**
+ * Children
+ */
+ children: React.ReactElement[] | React.ReactElement,
+}
+
+/**
+ * Wrapper for onboarding screens
+ *
+ * @param props - props for component rendering
+ */
+export default function OnboardingBody({ isFirstScreen, children }: OnboardingBodyProps): React.ReactElement {
+ const insets = useSafeAreaInsets();
+
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/src/onboarding/components/QuestType.tsx b/src/onboarding/components/QuestType.tsx
new file mode 100644
index 0000000..e3e5963
--- /dev/null
+++ b/src/onboarding/components/QuestType.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import styled from 'styled-components/native';
+import Colors from '../../styles/colors';
+import { StyledFonts } from '../../styles/textStyles';
+
+const Row = styled.View`
+ flex-direction: row;
+ align-self: stretch;
+ align-items: center;
+`;
+
+const IconView = styled.View`
+ background-color: ${Colors.Blue};
+ width: 60px;
+ aspect-ratio: 1;
+ border-radius: 30px;
+ align-items: center;
+ justify-content: center;
+ margin-right: 15px;
+`;
+
+const TextView = styled.View`
+ flex: 1;
+`;
+
+const Title = styled.Text`
+ ${StyledFonts.uiWebMedium};
+ font-size: 22px;
+ line-height: 22px;
+ color: ${Colors.Black};
+`;
+
+const Description = styled.Text`
+ ${StyledFonts.uiWebRegular};
+ font-size: 16px;
+ line-height: 19px;
+ color: ${Colors.Black};
+`;
+
+interface QuestTypeProps {
+ /**
+ * Icon of current type
+ */
+ icon: React.ReactElement,
+
+ /**
+ * Name of current type
+ */
+ title: string,
+
+ /**
+ * Information about current type
+ */
+ description: string,
+}
+
+/**
+ * Block with information about specific type of quests
+ *
+ * @param props - props for component rendering
+ */
+export default function QuestType({ icon, title, description }: QuestTypeProps): React.ReactElement {
+ return (
+
+ {icon}
+
+ {title}
+ {description}
+
+
+ );
+}
diff --git a/src/onboarding/components/ScreenInfo.tsx b/src/onboarding/components/ScreenInfo.tsx
new file mode 100644
index 0000000..c3b76af
--- /dev/null
+++ b/src/onboarding/components/ScreenInfo.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { StyledFonts } from '../../styles/textStyles';
+import Colors from '../../styles/colors';
+import styled from 'styled-components/native';
+import { View } from 'react-native';
+
+const Title = styled.Text`
+ ${StyledFonts.roboto};
+ font-size: 28px;
+ line-height: 28px;
+ color: ${Colors.Black};
+ margin: 20px 0 15px;
+ align-self: stretch;
+`;
+
+const Description = styled.Text`
+ ${StyledFonts.uiWebRegular};
+ font-size: 22px;
+ line-height: 22px;
+ color: ${Colors.Black};
+ align-self: stretch;
+`;
+
+interface ScreenInfoProps {
+ /**
+ * Title
+ */
+ title: string,
+
+ /**
+ * Information
+ */
+ description: string,
+}
+
+/**
+ * Block with main information on screen
+ *
+ * @param props - props for component rendering
+ */
+export default function ScreenInfo({ title, description }: ScreenInfoProps): React.ReactElement {
+ return (
+
+ {title}
+ {description}
+
+ );
+}
diff --git a/src/onboarding/screens/AboutGeolocation.tsx b/src/onboarding/screens/AboutGeolocation.tsx
new file mode 100644
index 0000000..eb74f40
--- /dev/null
+++ b/src/onboarding/screens/AboutGeolocation.tsx
@@ -0,0 +1,31 @@
+import React, { useState } from 'react';
+import OnboardingBody from '../components/OnboardingBody';
+import MockPhone from '../../images/mockPhoneWithGeo.svg';
+import { Dimensions } from 'react-native';
+import ScreenInfo from '../components/ScreenInfo';
+import styled from 'styled-components/native';
+
+const MockPhoneView = styled.View`
+ flex: 1;
+ max-height: ${Dimensions.get('screen'). width * 400 / 375 + 50}px;
+ margin-bottom: 20px;
+ align-items: center;
+ justify-content: flex-end;
+`;
+
+/**
+ * Screen with information about geolocation
+ */
+export default function AboutGeolocation(): React.ReactElement {
+ const mockPhoneWidth = Dimensions.get('screen').width;
+ const [mockPhoneHeight, setMockPhoneHeight] = useState(mockPhoneWidth * 400 / 375);
+
+ return (
+
+ event.nativeEvent.layout.height > 0 && setMockPhoneHeight(Math.min(mockPhoneHeight, event.nativeEvent.layout.height))}>
+
+
+
+
+ );
+}
diff --git a/src/onboarding/screens/AboutModalize.tsx b/src/onboarding/screens/AboutModalize.tsx
new file mode 100644
index 0000000..73eb6c1
--- /dev/null
+++ b/src/onboarding/screens/AboutModalize.tsx
@@ -0,0 +1,131 @@
+import React, { useState } from 'react';
+import OnboardingBody from '../components/OnboardingBody';
+import MockPhone from '../../images/mockPhone.svg';
+import ScreenInfo from '../components/ScreenInfo';
+import styled from 'styled-components/native';
+import { Dimensions, View } from 'react-native';
+import Colors from '../../styles/colors';
+import LinearGradient from 'react-native-linear-gradient';
+import TextBlock from '../../components/questBlocks/Text/TextBlock';
+import { PageBlock } from '../../types/questData';
+import { TargetLocationProvider } from '../../contexts/TargetLocationContext';
+import { AudioAccompanimentProvider } from '../../contexts/AudioAccompanimentContext';
+
+const pageBlock: PageBlock = {
+ 'type': 'page',
+ 'data': [
+ {
+ 'type': 'header',
+ 'data': {
+ 'level': 2,
+ 'text': 'Есенщина',
+ },
+ },
+ {
+ 'type': 'paragraph',
+ 'data': {
+ 'text': 'Причина образования слова — противостояние Есенина и Маяковского. Маяковский глубоко осуждал Есенина, в частности за его меланхоличные мысли:',
+ },
+ },
+ {
+ 'type': 'quote',
+ 'data': {
+ 'caption': 'Выступление на диспуте «Упадочное настроение среди молодежи (Есенщина)», 5 марта 1927 г., публикация Ф. Н. Пицкельэ ',
+ 'text': '«По вопросу об есенинщине я глубоко убежден, конечно, что Есенин сам по себе не так страшен и не мог бы быть так страшен, как есенинщина. Конечно, есенинщина производное от Есенина, потому что многие идеализируют в этом отношении Есенина, а он не имеет никакого отношения к этому. Но на множество процентов это результат дальнейших популяризаторов, дальнейших пропагандистов Есенина. Нельзя же все-таки скрывать такой факт, что выступавшие товарищи, и т. Бухарин в своих заметках выступали не только против есенинщины, а против Есенина, против Есенина самого, как он есть.»',
+ },
+ },
+ ],
+};
+
+const MockPhoneView = styled.View`
+ flex: 1;
+ max-height: ${Dimensions.get('screen'). width * 400 / 375 + 50}px;
+ margin-bottom: 20px;
+ align-items: center;
+ justify-content: flex-end;
+`;
+
+const GradientView = styled.View`
+ height: 147%;
+ width: 100%;
+ position: absolute;
+ align-self: center;
+ z-index: 999;
+ elevation: ${999};
+`;
+
+const Gradient = styled(LinearGradient)`
+ flex: 1;
+`;
+
+const ModalizeContainer = styled.View<{width: number, height: number}>`
+ background-color: ${Colors.White};
+ height: ${props => props.height}px;
+ width: ${props => props.width}px;
+ position: absolute;
+ bottom: 0;
+ align-self: center;
+ border-radius: 10px;
+ elevation: ${16};
+ box-shadow: 0 8px 10px rgba(0, 0, 0, 0.2);
+`;
+
+const ModalizeView = styled.View`
+ flex: 1;
+ padding: 12px 15px;
+ overflow: hidden;
+`;
+
+const Handle = styled.View`
+ background-color: rgba(85, 85, 107, 0.1);
+ width: 50px;
+ height: 5px;
+ border-radius: 3px;
+ align-self: center;
+ margin-bottom: 20px;
+`;
+
+/**
+ *
+ */
+export default function AboutModalize(): React.ReactElement {
+ const [mockPhoneHeight, setMockPhoneHeight] = useState(Dimensions.get('screen').width * 400 / 375);
+ const mockPhoneWidth = mockPhoneHeight * 375 / 400;
+ const modalizeWidth = mockPhoneWidth * 305 / 375;
+ const modalizeHeight = mockPhoneHeight * 325 / 400;
+
+ return (
+
+
+
+ event.nativeEvent.layout.height > 0 && setMockPhoneHeight(Math.min(mockPhoneHeight, event.nativeEvent.layout.height))}>
+
+
+
+
+
+ console.log()}/>
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/onboarding/screens/AboutQuests.tsx b/src/onboarding/screens/AboutQuests.tsx
new file mode 100644
index 0000000..4db697f
--- /dev/null
+++ b/src/onboarding/screens/AboutQuests.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import OnboardingBody from '../components/OnboardingBody';
+import Human from '../../images/human.svg';
+import Route from '../../images/route.svg';
+import Book from '../../images/book.svg';
+import Puzzle from '../../images/puzzle.svg';
+import QuestType from '../components/QuestType';
+import ScreenInfo from '../components/ScreenInfo';
+import styled from 'styled-components/native';
+import { Dimensions } from 'react-native';
+
+const Container = styled.View`
+ flex: 1;
+ max-height: ${Dimensions.get('screen'). width * 400 / 375 + 50}px;
+ margin-bottom: 40px;
+ justify-content: flex-end;
+`;
+
+const QuestTypesView = styled.View`
+ flex: 1;
+ max-height: 400px;
+ justify-content: space-between;
+`;
+
+/**
+ * Screen with information about quests
+ */
+export default function AboutQuests(): React.ReactElement {
+ return (
+
+
+
+ } title={'Квесты'} description={'Прогулки по городу с заданиями и интерактивными действиями'}/>
+ } title={'Маршруты'} description={'Подборка связанных между собой локаций по одной теме'}/>
+ } title={'Истории'} description={'Интерактивные новеллы. Выходить из дома необязательно :)'}/>
+ } title={'Тесты'} description={'Проверь свое знание истории и культуры города'}/>
+
+
+
+
+ );
+}
diff --git a/src/onboarding/screens/MainInfo.tsx b/src/onboarding/screens/MainInfo.tsx
new file mode 100644
index 0000000..9d42293
--- /dev/null
+++ b/src/onboarding/screens/MainInfo.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import OnboardingBody from '../components/OnboardingBody';
+import LogoIcon from '../../images/fullLogo.svg';
+import styled from 'styled-components/native';
+import { StyledFonts } from '../../styles/textStyles';
+import Colors from '../../styles/colors';
+
+const Container = styled.View`
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+`;
+
+const Logo = styled(LogoIcon)`
+ margin-bottom: 25px;
+`;
+
+const DefaultText = styled.Text`
+ ${StyledFonts.uiWebMedium};
+ line-height: 22px;
+ font-size: 22px;
+ color: ${Colors.Black};
+ text-align: center;
+`;
+
+const Delimiter = styled.View`
+ background-color: ${Colors.Blue};
+ height: 1px;
+ margin: 25px 75px;
+ align-self: stretch;
+`;
+
+/**
+ * First screen of onboarding
+ */
+export default function MainInfo(): React.ReactElement {
+ return (
+
+
+
+ Que.St: квесты по Петербургу
+
+ Приложение, посвещенное изучению истории и культруы Санкт-Петербурга!
+
+
+ );
+}
diff --git a/src/onboarding/screens/QuestPassing.tsx b/src/onboarding/screens/QuestPassing.tsx
new file mode 100644
index 0000000..d058781
--- /dev/null
+++ b/src/onboarding/screens/QuestPassing.tsx
@@ -0,0 +1,67 @@
+import React, { useState } from 'react';
+import OnboardingBody from '../components/OnboardingBody';
+import MockPhone from '../../images/mockPhone.svg';
+import ScreenInfo from '../components/ScreenInfo';
+import { Dimensions, Platform, View } from 'react-native';
+import styled from 'styled-components/native';
+import Colors from '../../styles/colors';
+import Alarm from '../../images/alarm.svg';
+import { StyledFonts } from '../../styles/textStyles';
+
+const MockPhoneView = styled.View`
+ flex: 1;
+ max-height: ${Dimensions.get('screen'). width * 400 / 375 + 50}px;
+ margin-bottom: 20px;
+ align-items: center;
+ justify-content: flex-end;
+`;
+
+const TaskView = styled.View<{width: number, topOffset: number}>`
+ min-height: 64px;
+ width: ${props => props.width}px;
+ position: absolute;
+ top: ${props => props.topOffset}px;
+ align-self: center;
+ background-color: ${Colors.White};
+ ${Platform.OS === 'ios' && 'border: rgba(85, 85, 107, 0.15) 1px;'}
+ border-radius: 10px;
+ padding: 15px 20px;
+ flex-direction: row;
+ align-items: center;
+ elevation: ${16};
+ box-shadow: 0 8px 10px rgba(0, 0, 0, 0.2);
+`;
+
+const TaskText = styled.Text`
+ ${StyledFonts.uiWebRegular};
+ flex: 1;
+ margin-left: 15px;
+ font-size: 14px;
+ line-height: 16px;
+ color: ${Colors.Black};
+`;
+
+/**
+ * Screen with information about passing the quest
+ */
+export default function QuestPassing(): React.ReactElement {
+ const [mockPhoneHeight, setMockPhoneHeight] = useState(Dimensions.get('screen').width * 400 / 375);
+ const mockPhoneWidth = mockPhoneHeight * 375 / 400;
+ const taskViewWidth = mockPhoneWidth * 320 / 375;
+ const taskViewTopOffset = mockPhoneHeight / 10;
+
+ return (
+
+ event.nativeEvent.layout.height > 0 && setMockPhoneHeight(Math.min(mockPhoneHeight, event.nativeEvent.layout.height))}>
+
+
+
+
+ Добраться до квартиры Бриков, где жила Лиля Брик
+
+
+
+
+
+ );
+}
diff --git a/src/screens/ChangeUsername.tsx b/src/screens/ChangeUsername.tsx
index 5fb0755..af6b0c4 100644
--- a/src/screens/ChangeUsername.tsx
+++ b/src/screens/ChangeUsername.tsx
@@ -12,7 +12,13 @@ import { graphql } from 'react-relay';
import { useMutation } from 'react-relay/hooks';
import Tip from '../images/tip.svg';
import { ChangeUsernameMutation } from './__generated__/ChangeUsernameMutation.graphql';
-import { useAuthContext } from '../contexts/AuthProvider';
+import { ProfileStackParamList } from '../navigation/profileStack';
+import { StackScreenProps } from '@react-navigation/stack';
+
+/**
+ * Type with props of screen 'ChangeUsername' in ProfileStackScreen
+ */
+type Props = StackScreenProps;
/**
* Styles for login view
@@ -65,10 +71,11 @@ const StyledButton = styled(Button)`
/**
* Screen with changing username
+ *
+ * @param props - props for component rendering
*/
-export default function ChangeUsernameScreen(): ReactElement {
+export default function ChangeUsernameScreen({ navigation }: Props): ReactElement {
const { t } = useTranslation();
- const authContext = useAuthContext();
const [username, setUsername] = useState('');
const [ updateUsername ] = useMutation(
@@ -105,14 +112,16 @@ export default function ChangeUsernameScreen(): ReactElement {
/>
updateUsername({
- variables: { username },
- onError: error => Alert.alert(t([`errors.${error.source.errors[0].extensions.code}`, 'errors.unspecific'])),
- onCompleted: () => {
- Alert.alert(t('signUp.successful'));
- authContext.actions.setFirstRegistrationFalse();
- },
- })}
+ onPress={() => {
+ updateUsername({
+ variables: { username },
+ onError: error => Alert.alert(t([`errors.${error.source.errors[0].extensions.code}`, 'errors.unspecific'])),
+ onCompleted: () => {
+ Alert.alert(t('signUp.successful'));
+ navigation.navigate('Onboarding');
+ },
+ });
+ }}
/>
);
diff --git a/yarn.lock b/yarn.lock
index 55e3428..e1b4ea0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7683,6 +7683,11 @@ react-is@^16.12.0, react-is@^16.13.0, react-is@^16.7.0, react-is@^16.8.1, react-
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
+react-native-app-intro-slider@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/react-native-app-intro-slider/-/react-native-app-intro-slider-4.0.4.tgz#fa5cda7057db62c448ac975ffd2ba0cff94cc8d8"
+ integrity sha512-Zkjaol6X3BbZkHUpVDj2LjdidpS6rCgKi0fx80xgGKa0pHxBRd4swWTv2bHnnvu5k1/HXwYk0mY2TbK+2jHl5w==
+
react-native-circular-progress@^1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/react-native-circular-progress/-/react-native-circular-progress-1.3.7.tgz#cc430fbc612bd01134a8fc9667be107591ae6959"