Skip to content

Commit

Permalink
Onboarding for slide gesture
Browse files Browse the repository at this point in the history
  • Loading branch information
wyne committed Mar 18, 2024
1 parent 2b918c4 commit c163326
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 81 deletions.
6 changes: 3 additions & 3 deletions app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ switch (variant) {
export default {
name: name,
slug: 'scorepad',
version: "2.4.7",
version: "2.5.0",
orientation: "default",
icon: icon,
assetBundlePatterns: [
Expand All @@ -53,7 +53,7 @@ export default {
bundleIdentifier: packageName,
supportsTablet: true,
requireFullScreen: false,
buildNumber: "68",
buildNumber: "69",
infoPlist: {
RCTAsyncStorageExcludeFromBackup: false
},
Expand All @@ -67,7 +67,7 @@ export default {
},
package: packageName,
permissions: [],
versionCode: 68,
versionCode: 69,
googleServicesFile: "./google-services.json",
},
userInterfaceStyle: "dark",
Expand Down
Binary file added assets/onboarding/slide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 2 additions & 5 deletions src/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import OnboardingScreen from '../src/screens/OnboardingScreen';
import SettingsScreen from "../src/screens/SettingsScreen";

import ShareHeader from './components/Headers/ShareHeader';
import { getOnboardingSemVer } from './components/Onboarding/Onboarding';
import ShareScreen from './screens/ShareScreen';

export type OnboardingScreenParamList = {
Expand Down Expand Up @@ -53,11 +54,7 @@ export const Navigation = () => {
console.log(`App Version: ${appVersion}`);
console.log(`Onboarded Version: ${onboardedSemVer}`);

let onboarded = true;

if (onboardedSemVer == null || onboardedSemVer?.compare(new SemVer('2.2.2')) == -1) {
onboarded = false;
}
const onboarded = getOnboardingSemVer(onboardedSemVer) === undefined;

return (
<NavigationContainer theme={MyTheme}>
Expand Down
29 changes: 29 additions & 0 deletions src/components/Onboarding/Onboarding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SemVer } from "semver";

import { getOnboardingSemVer } from "./Onboarding";

describe('onboarding', () => {
it('should return the default if onboarded to 0.0.0', () => {
const applicableVersion = getOnboardingSemVer(new SemVer('0.0.0'));

expect(applicableVersion).toBe('2.2.2');
});

it('should return the default if null', () => {
const applicableVersion = getOnboardingSemVer(null);

expect(applicableVersion).toBe('2.2.2');
});

it('should return the latest applicable screens', () => {
const applicableVersion = getOnboardingSemVer(new SemVer('2.4.0'));

expect(applicableVersion).toBe('2.5.0');
});

it('should not return if caught up', () => {
const applicableVersion = getOnboardingSemVer(new SemVer('2.6.0'));

expect(applicableVersion).toBeUndefined();
});
});
113 changes: 113 additions & 0 deletions src/components/Onboarding/Onboarding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { ImageURISource, } from 'react-native';
import { SemVer, compare } from 'semver';

export type OnboardingScreenItem = {
title: string;
image: ImageURISource;
imageHeight?: number;
imageWidth?: number;
description: string;
backgroundColor: string;
};

type OnboardingScreens = Record<string, OnboardingScreenItem[]>;

const onboardingScreens: OnboardingScreens = {
'2.2.2': [
{
title: "ScorePad\nwith Rounds",
image: require('../../../assets/icon.png'),
imageHeight: 150,
imageWidth: 150,
description: 'Swipe left to begin.',
backgroundColor: '#8ca2b8',
},
{
title: "Add Points",
image: require('../../../assets/onboarding/add.png'),
description: 'Tap the top half of a player’s tile to add points.',
backgroundColor: '#a0c99a',
},
{
title: "Subtract Points",
image: require('../../../assets/onboarding/subtract.png'),
description: 'Tap the bottom half of a player’s tile to subtract points.',
backgroundColor: '#d29898',
},
{
title: "Adjust Point Values",
image: require('../../../assets/onboarding/addend-button.png'),
description: 'Adjust the point value by tapping on the point value selector in the top right.',
backgroundColor: '#9896c5',
},
{
title: "Swipe Gestures",
image: require('../../../assets/onboarding/slide.png'),
description: 'Change gestures from the point settings in the top right.',
backgroundColor: '#94c49e',
},
{
title: "Change Rounds",
image: require('../../../assets/onboarding/rounds.png'),
description: 'Use rounds for score history. \nTap the arrows to cycle rounds.',
backgroundColor: '#c8b780',
},
{
title: "Score History",
image: require('../../../assets/onboarding/sheet.png'),
description: 'Pull up the bottom sheet to view score history and edit the game.',
backgroundColor: '#94c49e',
},
{
title: "That's it!",
image: require('../../../assets/icon.png'),
imageHeight: 150,
imageWidth: 150,
description: 'Return to this tutorial \n at any time.',
backgroundColor: '#8ca2b8',
},
],
'2.5.0': [
{
title: "New: Swipe Gestures",
image: require('../../../assets/onboarding/slide.png'),
description: 'Change gesture methods through the point settings in the top right of a game.',
backgroundColor: '#94c49e',
},
{
title: "That's it!",
image: require('../../../assets/icon.png'),
imageHeight: 150,
imageWidth: 150,
description: 'Return to this tutorial \n at any time.',
backgroundColor: '#8ca2b8',
},
]
};

export const getOnboardingSemVer = (onboardedSemVer: SemVer | null): string | undefined => {
const keys = Object.keys(onboardingScreens)
.sort((a, b) => compare(new SemVer(a), new SemVer(b)));

if (onboardedSemVer === null) {
return keys[0];
}

const v = keys.find(key => {
// Is the onboarded version less than the current key?
const val = compare(onboardedSemVer, new SemVer(key)) === -1;
return val;
});

return v;
};

export const getOnboardingScreens = (onboardedSemVer: SemVer): OnboardingScreenItem[] => {
const applicableVersion = getOnboardingSemVer(onboardedSemVer);

if (!applicableVersion) {
return [];
}

return onboardingScreens[applicableVersion];
};
90 changes: 17 additions & 73 deletions src/screens/OnboardingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,92 +3,31 @@ import React from 'react';
import { RouteProp } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import {
View, StyleSheet,
Dimensions,
Animated as RNAnimated,
StyleSheet,
Text,
View,
ViewToken,
ImageURISource,
} 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 { SafeAreaView } from 'react-native-safe-area-context';
import { parse } from 'semver';

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

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

type OnboardingScreenItem = {
title: string;
image: ImageURISource;
imageHeight?: number;
imageWidth?: number;
description: string;
backgroundColor: string;
};

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

const data: OnboardingScreenItem[] = [
{
title: "ScorePad\nwith Rounds",
image: require('../../assets/icon.png'),
imageHeight: 150,
imageWidth: 150,
description: 'Swipe left to begin.',
backgroundColor: '#8ca2b8',
},
{
title: "Add Points",
image: require('../../assets/onboarding/add.png'),
description: 'Tap the top half of a player’s tile to add points.',
backgroundColor: '#a0c99a',
},
{
title: "Subtract Points",
image: require('../../assets/onboarding/subtract.png'),
description: 'Tap the bottom half of a player’s tile to subtract points.',
backgroundColor: '#d29898',
},
{
title: "Adjust Point Values",
image: require('../../assets/onboarding/addend-button.png'),
description: 'Adjust the point value by tapping on the point value selector in the top right.',
backgroundColor: '#9896c5',
},
// {
// title: "Adjust Point Values",
// image: require('../../assets/onboarding/addend.png'),
// description: 'You can set different values for tap and long press.',
// backgroundColor: '#7370cf',
// },
{
title: "Change Rounds",
image: require('../../assets/onboarding/rounds.png'),
description: 'Use rounds for score history. \nTap the arrows to cycle rounds.',
backgroundColor: '#c8b780',
},
{
title: "Score History",
image: require('../../assets/onboarding/sheet.png'),
description: 'Pull up the bottom sheet to view score history and edit the game.',
backgroundColor: '#94c49e',
},
{
title: "That's it!",
image: require('../../assets/icon.png'),
imageHeight: 150,
imageWidth: 150,
description: 'Return to this tutorial \n at any time.',
backgroundColor: '#8ca2b8',
},
];

export interface Props {
navigation: NativeStackNavigationProp<RootStackParamList, 'Onboarding' | 'Tutorial'>;
route: RouteProp<RootStackParamList, 'Onboarding' | 'Tutorial'>;
Expand All @@ -97,10 +36,15 @@ export interface Props {
const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route }) => {
const { onboarding = false } = route.params;

const onboardedStr = useAppSelector(state => state.settings.onboarded);
const onboardedSemVer = parse(onboardedStr);

const onboardingScreens: OnboardingScreenItem[] = getOnboardingScreens(onboardedSemVer);

const scrollX = React.useRef(new RNAnimated.Value(0)).current;
const keyExtractor = React.useCallback((_: OnboardingScreenItem, index: number) => index.toString(), []);
//Current item index of flatlist

// Current item index of flatlist
const [activeIndex, setActiveIndex] = React.useState<number>(0);
const flatListRef = React.useRef<RNAnimated.FlatList>(null);

Expand Down Expand Up @@ -134,7 +78,7 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
width: item.imageWidth || '100%',
height: item.imageHeight || '100%',
borderRadius: (
index == 0 || index == data.length - 1
index == 0 || index == onboardingScreens.length - 1
) ? 20 : 0,
resizeMode: 'contain',
}} />
Expand All @@ -145,7 +89,7 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
{item.description}
</Text>
<View style={{ alignContent: 'center' }}>
{index === data.length - 1 &&
{index === onboardingScreens.length - 1 &&
<Button
title="Get Started"
titleStyle={{ color: 'black' }}
Expand All @@ -164,7 +108,7 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
<Animated.View style={[styles.container]} entering={FadeIn}>
<SafeAreaView edges={(['top', 'bottom'])} style={onboarding ? { paddingTop: 40 } : {}}>
<View style={[StyleSheet.absoluteFillObject]}>
{data.map((item, index) => {
{onboardingScreens.map((item, index) => {
const inputRange = [
(index - 1) * width,
index * width,
Expand Down Expand Up @@ -192,7 +136,7 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
ref={flatListRef}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
data={data}
data={onboardingScreens}
renderItem={renderItem}
keyExtractor={keyExtractor}
showsHorizontalScrollIndicator={false}
Expand All @@ -209,7 +153,7 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })
/>

<ExpandingDot
data={data}
data={onboardingScreens}
scrollX={scrollX}
expandingDotWidth={30}
dotStyle={{
Expand All @@ -228,7 +172,7 @@ const OnboardingScreen: React.FunctionComponent<Props> = ({ navigation, route })

{onboarding &&
<SkipButton
visible={activeIndex !== data.length - 1}
visible={activeIndex !== onboardingScreens.length - 1}
onPress={closeOnboarding}
/>
}
Expand Down

0 comments on commit c163326

Please sign in to comment.