Skip to content

Commit

Permalink
Fix onboarding screen UI and performance
Browse files Browse the repository at this point in the history
  • Loading branch information
wyne committed Apr 20, 2024
1 parent e3f6ec1 commit 533fa84
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 128 deletions.
2 changes: 1 addition & 1 deletion src/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const Navigation = () => {
/>
<Stack.Screen name="Onboarding" component={OnboardingScreen}
options={{
presentation: 'formSheet',
presentation: 'modal',
orientation: 'portrait',
title: 'Onboarding',
headerShown: false,
Expand Down
2 changes: 1 addition & 1 deletion src/components/AppInfo/RotatingIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const RotatingIcon: React.FunctionComponent = ({ }) => {

await analytics().logEvent('app_icon');
}}>
<Animated.View style={[animatedStyles]} entering={PinwheelIn.delay(0).duration(2000).easing(Easing.elastic(1))}>
<Animated.View style={[animatedStyles]}>
<Image source={require('../../../assets/icon.png')}
contentFit='contain'
style={{
Expand Down
144 changes: 144 additions & 0 deletions src/components/Onboarding/OnboardingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React from 'react';

import {
ImageURISource,
Animated as RNAnimated,
StyleSheet,
Text,
View
} from 'react-native';
import { Button } from 'react-native-elements';
import Animated from 'react-native-reanimated';
import Video from 'react-native-video';

import { OnboardingScreenItem } from './Onboarding';

interface Props {
active?: boolean;
closeOnboarding: () => void;
index: number;
isLast: boolean;
item: OnboardingScreenItem;
width: number;
}

const OnboardingPage: React.FC<Props> = React.memo(({
active = false,
closeOnboarding,
isLast,
item,
width,
}) => {
let media;
switch (item.media.type) {
case 'image':
media = (
<RNAnimated.Image source={item.media.source as ImageURISource}
style={{
width: item.media.width || '100%',
height: item.media.height || '100%',
borderRadius: item.media.borderRadius || 0,
resizeMode: 'contain',
}} />
);
break;
case 'video':
media = (
<View style={{
backgroundColor: 'black',
padding: 5,
width: item.media.width || '100%',
height: item.media.height || '100%',
borderRadius: 10,
}}>
<Video
source={item.media.source as number}
paused={!active}
repeat={true}
resizeMode='contain'
style={{
width: '100%',
height: '100%',
}}
/>
</View>
);
break;
}

return (
<View style={[styles.itemContainer, { width: width }]} >
<Animated.View style={[styles.titleContainer]}>
<Text style={[styles.title]}>{item.title}</Text>
</Animated.View>

<Animated.View style={[styles.imageContainer]}>
{media}
</Animated.View>

<Animated.View style={[styles.descriptionContainer]}>
<Text style={[styles.description, { color: item.color }]}>
{item.description}
</Text>
<View style={{ alignContent: 'center' }}>
{isLast &&
<Button
title="Get Started"
titleStyle={{ color: 'black' }}
onPress={closeOnboarding}
buttonStyle={[styles.finishButton]}
type='outline'
/>
}
</View>
</Animated.View>
</View>
);
});

export default OnboardingPage;

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
width: '100%',
},
itemContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'space-around',
},
titleContainer: {
height: '15%',
justifyContent: 'flex-end',
},
title: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
},
imageContainer: {
height: '40%',
justifyContent: 'center',
alignItems: 'center',
width: '80%',
padding: 20,
},
descriptionContainer: {
height: '25%',
justifyContent: 'flex-start',
padding: 20,
},
description: {
fontSize: 25,
textAlign: 'center',
},
finishButton: {
borderColor: 'black',
borderRadius: 20,
padding: 10,
margin: 15,
},
});
146 changes: 20 additions & 126 deletions src/screens/OnboardingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,28 @@ import React from 'react';
import { RouteProp } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import {
Dimensions,
ImageURISource,
Animated as RNAnimated,
StyleSheet,
Text,
View,
ViewToken,
ViewToken
} from 'react-native';
import { ExpandingDot } from 'react-native-animated-pagination-dots';
import { Button } from 'react-native-elements';
import Animated, { FadeIn } from 'react-native-reanimated';
import Video from 'react-native-video';
import { SemVer } from 'semver';

import { getOnboardingScreens, OnboardingScreenItem } from '../components/Onboarding/Onboarding';
import OnboardingPage from '../components/Onboarding/OnboardingPage';
import SkipButton from '../components/Onboarding/SkipButton';
import { RootStackParamList } from '../Navigation';

const { width } = Dimensions.get('screen');

type OnboardingScreenItemProps = {
item: OnboardingScreenItem;
index: number;
};

export interface Props {
navigation: NativeStackNavigationProp<RootStackParamList, 'Onboarding' | 'Tutorial'>;
route: RouteProp<RootStackParamList, 'Onboarding' | 'Tutorial'>;
}

const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route }) => {
const [width, setWidth] = React.useState(0);

const {
onboarding = false,
version = new SemVer('0.0.0')
Expand Down Expand Up @@ -64,77 +55,12 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })

const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 });

const renderItem = React.useCallback(({ item, index }: OnboardingScreenItemProps) => {

let media;
switch (item.media.type) {
case 'image':
media = (
<RNAnimated.Image source={item.media.source as ImageURISource}
style={{
width: item.media.width || '100%',
height: item.media.height || '100%',
borderRadius: item.media.borderRadius || 0,
resizeMode: 'contain',
}} />
);
break;
case 'video':
media = (
<View style={{
backgroundColor: 'black',
padding: 5,
width: item.media.width || '100%',
height: item.media.height || '100%',
borderRadius: 10,
}}>
<Video
source={item.media.source as number}
paused={index != activeIndex}
repeat={true}
resizeMode='contain'
style={{
width: '100%',
height: '100%',
}}
/>
</View>
);
break;
}

return (
<View style={[styles.itemContainer]}>
<Animated.View style={[styles.titleContainer]}>
<Text style={[styles.title]}>{item.title}</Text>
</Animated.View>

<Animated.View style={[styles.imageContainer]}>
{media}
</Animated.View>

<Animated.View style={[styles.descriptionContainer]}>
<Text style={[styles.description, { color: item.color }]}>
{item.description}
</Text>
<View style={{ alignContent: 'center' }}>
{index === onboardingScreens.length - 1 &&
<Button
title="Get Started"
titleStyle={{ color: 'black' }}
onPress={closeOnboarding}
buttonStyle={[styles.finishButton]}
type='outline'
/>
}
</View>
</Animated.View>
</View>
);
}, [activeIndex]);

return (
<Animated.View style={[styles.container]} entering={FadeIn}>
<Animated.View style={[styles.container]} entering={FadeIn}
onLayout={event => {
const { width } = event.nativeEvent.layout;
setWidth(width);
}}>
<View style={[StyleSheet.absoluteFillObject]}>
{onboardingScreens.map((item, index) => {
const inputRange = [
Expand Down Expand Up @@ -164,7 +90,16 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
data={onboardingScreens}
renderItem={renderItem}
renderItem={({ item, index }) =>
<OnboardingPage
closeOnboarding={closeOnboarding}
index={index}
item={item}
width={width}
active={index === activeIndex}
isLast={index === onboardingScreens.length - 1}
/>
}
keyExtractor={keyExtractor}
showsHorizontalScrollIndicator={false}
pagingEnabled
Expand Down Expand Up @@ -203,53 +138,12 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
);
};

const borders = false;

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
itemContainer: {
flex: 1,
width: width,
alignItems: 'center',
justifyContent: 'space-around',
},
titleContainer: {
height: '15%',
justifyContent: 'flex-end',
borderWidth: borders ? 1 : 0,
},
title: {
fontSize: 30,
fontWeight: 'bold',
textAlign: 'center',
},
imageContainer: {
height: '40%',
justifyContent: 'center',
alignItems: 'center',
width: '80%',
padding: 20,
borderWidth: borders ? 1 : 0,
},
descriptionContainer: {
height: '25%',
justifyContent: 'flex-start',
padding: 20,
borderWidth: borders ? 1 : 0,
},
description: {
fontSize: 25,
textAlign: 'center',
},
finishButton: {
borderColor: 'black',
borderRadius: 20,
padding: 10,
margin: 15,
width: '100%',
},
});

Expand Down

0 comments on commit 533fa84

Please sign in to comment.