remove the need for rerender the footer buttons when last slide

This commit is contained in:
Pablo Velez Vidal 2022-11-08 16:39:50 +01:00
parent c9ae29f010
commit d9239f3996
2 changed files with 54 additions and 61 deletions

View file

@ -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'

View file

@ -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}
/>