remove the need for rerender the footer buttons when last slide
This commit is contained in:
parent
c9ae29f010
commit
d9239f3996
2 changed files with 54 additions and 61 deletions
|
|
@ -12,11 +12,9 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
|
|||
|
||||
type Props = {
|
||||
theme: Theme;
|
||||
isLastSlide: boolean;
|
||||
lastSlideIndex: number;
|
||||
nextSlideHandler: () => void;
|
||||
signInHandler: () => void;
|
||||
currentIndex: number;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
};
|
||||
|
||||
|
|
@ -38,9 +36,7 @@ const FooterButtons = ({
|
|||
theme,
|
||||
nextSlideHandler,
|
||||
signInHandler,
|
||||
isLastSlide,
|
||||
lastSlideIndex,
|
||||
currentIndex,
|
||||
scrollX,
|
||||
}: Props) => {
|
||||
const {width} = useWindowDimensions();
|
||||
|
|
@ -49,10 +45,8 @@ const FooterButtons = ({
|
|||
|
||||
// keep in mind penultimate and ultimate slides to run buttons text/opacity/size animations
|
||||
const penultimateSlide = lastSlideIndex - 1;
|
||||
const isPenultimateSlide = currentIndex === (lastSlideIndex - 1);
|
||||
const needToAnimate = isLastSlide || isPenultimateSlide;
|
||||
|
||||
const inputRange = [penultimateSlide * width, lastSlideIndex * width];
|
||||
const inputRange = [(penultimateSlide * width), lastSlideIndex * width];
|
||||
|
||||
// the next button must resize in the last slide to be the 80% wide of the screen with animation
|
||||
const resizeStyle = useAnimatedStyle(() => {
|
||||
|
|
@ -67,15 +61,26 @@ const FooterButtons = ({
|
|||
});
|
||||
|
||||
// use for the opacity of the button text in the penultimate and last slide
|
||||
const opacityTextStyle = useAnimatedStyle(() => {
|
||||
const opacityNextTextStyle = useAnimatedStyle(() => {
|
||||
const interpolatedScale = interpolate(
|
||||
scrollX.value,
|
||||
inputRange,
|
||||
isLastSlide ? [0, 1] : [1, 0], // from last to penultimate it must fade out (from 1 to 0), once it starts getting the penultimate range it must fade in again (from 0 to 1)
|
||||
[penultimateSlide * width, ((penultimateSlide * width) + (width / 2))],
|
||||
[1, 0], // from last to penultimate it must fade out (from 1 to 0), once it starts getting the penultimate range it must fade in again (from 0 to 1)
|
||||
Extrapolate.CLAMP,
|
||||
);
|
||||
|
||||
return {opacity: needToAnimate ? interpolatedScale : 1};
|
||||
return {opacity: interpolatedScale};
|
||||
});
|
||||
|
||||
const opacitySignInTextStyle = useAnimatedStyle(() => {
|
||||
const interpolatedScale = interpolate(
|
||||
scrollX.value,
|
||||
[lastSlideIndex * width, ((penultimateSlide * width) + (width / 2))],
|
||||
[1, 0], // from last to penultimate it must fade out (from 1 to 0), once it starts getting the penultimate range it must fade in again (from 0 to 1)
|
||||
Extrapolate.CLAMP,
|
||||
);
|
||||
|
||||
return {opacity: interpolatedScale};
|
||||
});
|
||||
|
||||
// the sign in button should fade out until dissappear in the last slide
|
||||
|
|
@ -90,8 +95,8 @@ const FooterButtons = ({
|
|||
return {opacity: interpolatedScale};
|
||||
});
|
||||
|
||||
let mainButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row'}, opacityTextStyle]}>
|
||||
const nextButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row', position: 'absolute'}, opacityNextTextStyle]}>
|
||||
<FormattedText
|
||||
id='mobile.onboarding.next'
|
||||
defaultMessage='Next'
|
||||
|
|
@ -104,29 +109,25 @@ const FooterButtons = ({
|
|||
</Animated.View>
|
||||
);
|
||||
|
||||
let mainButtonAction = nextSlideHandler;
|
||||
|
||||
if (isLastSlide) {
|
||||
mainButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row'}, opacityTextStyle]}>
|
||||
<FormattedText
|
||||
id='mobile.onboarding.sign_in_to_get_started'
|
||||
defaultMessage='Sign in to get started'
|
||||
style={buttonTextStyle(theme, 's', 'primary', 'default')}
|
||||
/>
|
||||
</Animated.View>
|
||||
);
|
||||
mainButtonAction = signInHandler;
|
||||
}
|
||||
const signInButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row'}, opacitySignInTextStyle]}>
|
||||
<FormattedText
|
||||
id='mobile.onboarding.sign_in_to_get_started'
|
||||
defaultMessage='Sign in to get started'
|
||||
style={buttonTextStyle(theme, 's', 'primary', 'default')}
|
||||
/>
|
||||
</Animated.View>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{flexDirection: 'column', height: 150, marginTop: 15, width: '100%', marginHorizontal: 10, alignItems: 'center'}}>
|
||||
<AnimatedButton
|
||||
testID='mobile.onboaring.next'
|
||||
onPress={() => mainButtonAction()}
|
||||
onPress={() => nextSlideHandler()}
|
||||
style={[styles.button, buttonBackgroundStyle(theme, 'm', 'primary', 'default'), resizeStyle]}
|
||||
>
|
||||
{mainButtonText}
|
||||
{nextButtonText}
|
||||
{signInButtonText}
|
||||
</AnimatedButton>
|
||||
<AnimatedButton
|
||||
testID='mobile.onboaring.sign_in'
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import React, {useCallback} from 'react';
|
||||
import {View, ListRenderItemInfo, useWindowDimensions, SafeAreaView} from 'react-native';
|
||||
import Animated, {runOnJS, useAnimatedRef, useAnimatedScrollHandler, useSharedValue} from 'react-native-reanimated';
|
||||
import Animated, {useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useSharedValue} from 'react-native-reanimated';
|
||||
|
||||
import {Screens} from '@app/constants';
|
||||
import Background from '@screens/background';
|
||||
import {goToScreen} from '@screens/navigation';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import FooterButtons from './footer_buttons';
|
||||
|
|
@ -26,31 +28,30 @@ const Onboarding = ({
|
|||
}: OnboardingProps) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const styles = getStyleSheet(theme);
|
||||
const [isLastSlide, setIsLastSlide] = useState(false);
|
||||
const slidesData = useSlidesData().slidesData;
|
||||
const lastSlideIndex = slidesData.length - 1;
|
||||
const slidesRef = useAnimatedRef<Animated.ScrollView>();
|
||||
const currentIndex = useSharedValue(0);
|
||||
const scrollX = useSharedValue(0);
|
||||
|
||||
const nextSlide = () => {
|
||||
// const currentIndex = useSharedValue(0);
|
||||
const scrollX = useSharedValue(0);
|
||||
const currentIndex = useDerivedValue(() => Math.round(scrollX.value / width));
|
||||
|
||||
const moveToSlide = useCallback((slideIndexToMove: number) => {
|
||||
slidesRef.current?.scrollTo({x: (slideIndexToMove * width), animated: true});
|
||||
}, [slidesRef.current]);
|
||||
|
||||
const nextSlide = useCallback(() => {
|
||||
const nextSlideIndex = currentIndex.value + 1;
|
||||
if (slidesRef.current && currentIndex.value < lastSlideIndex) {
|
||||
moveToSlide(nextSlideIndex);
|
||||
} else if (slidesRef.current && currentIndex.value === lastSlideIndex) {
|
||||
signInHandler();
|
||||
}
|
||||
};
|
||||
}, [currentIndex.value, slidesRef.current, moveToSlide]);
|
||||
|
||||
const moveToSlide = (slideIndexToMove: number) => {
|
||||
currentIndex.value = slideIndexToMove;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
slidesRef?.current?.scrollTo({x: (slideIndexToMove * width), animated: true});
|
||||
};
|
||||
|
||||
const signInHandler = () => {
|
||||
// temporal validation
|
||||
setIsLastSlide(!isLastSlide);
|
||||
};
|
||||
const signInHandler = useCallback(() => {
|
||||
goToScreen(Screens.SERVER, '', {theme});
|
||||
}, []);
|
||||
|
||||
const renderSlide = useCallback(({item, index}: ListRenderItemInfo<OnboardingItem>) => {
|
||||
return (
|
||||
|
|
@ -64,17 +65,10 @@ const Onboarding = ({
|
|||
);
|
||||
}, []);
|
||||
|
||||
const toogleIsLastSlideValue = (isLast: boolean) => {
|
||||
setIsLastSlide(isLast);
|
||||
};
|
||||
|
||||
const handleScroll = useAnimatedScrollHandler(({contentOffset: {x}}) => {
|
||||
const calculatedIndex = Math.round(x / width);
|
||||
scrollX.value = x;
|
||||
if (calculatedIndex !== currentIndex.value) {
|
||||
currentIndex.value = calculatedIndex;
|
||||
runOnJS(toogleIsLastSlideValue)(calculatedIndex === lastSlideIndex);
|
||||
}
|
||||
const scrollHandler = useAnimatedScrollHandler({
|
||||
onScroll: (event) => {
|
||||
scrollX.value = event.contentOffset.x;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
@ -93,7 +87,7 @@ const Onboarding = ({
|
|||
showsHorizontalScrollIndicator={false}
|
||||
pagingEnabled={true}
|
||||
bounces={false}
|
||||
onScroll={handleScroll}
|
||||
onScroll={scrollHandler}
|
||||
ref={slidesRef}
|
||||
>
|
||||
{slidesData.map((item, index) => {
|
||||
|
|
@ -108,10 +102,8 @@ const Onboarding = ({
|
|||
/>
|
||||
<FooterButtons
|
||||
theme={theme}
|
||||
isLastSlide={isLastSlide}
|
||||
nextSlideHandler={nextSlide}
|
||||
signInHandler={signInHandler}
|
||||
currentIndex={currentIndex.value}
|
||||
scrollX={scrollX}
|
||||
lastSlideIndex={lastSlideIndex}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue