animate onboarding screen appear from screen to onboarding by clicking back button
This commit is contained in:
parent
fa7a605c8f
commit
0aabcb9918
3 changed files with 64 additions and 34 deletions
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {View, ListRenderItemInfo, useWindowDimensions, SafeAreaView, ScrollView, StyleSheet} from 'react-native';
|
||||
import Animated, {useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useSharedValue} from 'react-native-reanimated';
|
||||
import React, {useCallback, useEffect} from 'react';
|
||||
import {View, ListRenderItemInfo, useWindowDimensions, SafeAreaView, ScrollView, StyleSheet, Platform} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import Animated, {useAnimatedRef, useAnimatedScrollHandler, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {storeOnboardingViewedValue} from '@actions/app/global';
|
||||
import {Screens} from '@app/constants';
|
||||
|
|
@ -36,16 +37,22 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
const AnimatedSafeArea = Animated.createAnimatedComponent(SafeAreaView);
|
||||
|
||||
const Onboarding = ({
|
||||
theme,
|
||||
}: OnboardingProps) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const {slidesData} = useSlidesData();
|
||||
const lastSlideIndex = slidesData.length - 1;
|
||||
const LAST_SLIDE_INDEX = slidesData.length - 1;
|
||||
const dimensions = useWindowDimensions();
|
||||
const slidesRef = useAnimatedRef<ScrollView>();
|
||||
|
||||
const scrollX = useSharedValue(0);
|
||||
|
||||
// used to smothly animate the whole onboarding screen during the appear event scenario (from server screen back to onboarding screen)
|
||||
const translateX = useSharedValue(dimensions.width);
|
||||
|
||||
const currentIndex = useDerivedValue(() => Math.round(scrollX.value / width));
|
||||
|
||||
const moveToSlide = useCallback((slideIndexToMove: number) => {
|
||||
|
|
@ -57,9 +64,9 @@ const Onboarding = ({
|
|||
|
||||
const nextSlide = useCallback(() => {
|
||||
const nextSlideIndex = currentIndex.value + 1;
|
||||
if (slidesRef.current && currentIndex.value < lastSlideIndex) {
|
||||
if (slidesRef.current && currentIndex.value < LAST_SLIDE_INDEX) {
|
||||
moveToSlide(nextSlideIndex);
|
||||
} else if (slidesRef.current && currentIndex.value === lastSlideIndex) {
|
||||
} else if (slidesRef.current && currentIndex.value === LAST_SLIDE_INDEX) {
|
||||
signInHandler();
|
||||
}
|
||||
}, [currentIndex.value, slidesRef.current, moveToSlide]);
|
||||
|
|
@ -68,7 +75,7 @@ const Onboarding = ({
|
|||
// mark the onboarding as already viewed
|
||||
storeOnboardingViewedValue();
|
||||
|
||||
goToScreen(Screens.SERVER, '', {theme}, loginAnimationOptions());
|
||||
goToScreen(Screens.SERVER, '', {animated: true, theme}, loginAnimationOptions());
|
||||
}, []);
|
||||
|
||||
const renderSlide = useCallback(({item, index}: ListRenderItemInfo<OnboardingItem>) => {
|
||||
|
|
@ -79,6 +86,7 @@ const Onboarding = ({
|
|||
scrollX={scrollX}
|
||||
index={index}
|
||||
key={`key-${index.toString()}`}
|
||||
lastSlideIndex={LAST_SLIDE_INDEX}
|
||||
/>
|
||||
);
|
||||
}, [theme]);
|
||||
|
|
@ -89,15 +97,36 @@ const Onboarding = ({
|
|||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const listener = {
|
||||
componentDidAppear: () => {
|
||||
translateX.value = 0;
|
||||
},
|
||||
componentDidDisappear: () => {
|
||||
translateX.value = -dimensions.width;
|
||||
},
|
||||
};
|
||||
const unsubscribe = Navigation.events().registerComponentListener(listener, Screens.ONBOARDING);
|
||||
|
||||
return () => unsubscribe.remove();
|
||||
}, [dimensions]);
|
||||
|
||||
const transform = useAnimatedStyle(() => {
|
||||
const duration = Platform.OS === 'android' ? 250 : 350;
|
||||
return {
|
||||
transform: [{translateX: withTiming(translateX.value, {duration})}],
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.onBoardingContainer}
|
||||
testID='onboarding.screen'
|
||||
>
|
||||
<Background theme={theme}/>
|
||||
<SafeAreaView
|
||||
<AnimatedSafeArea
|
||||
style={[styles.scrollContainer, transform]}
|
||||
key={'onboarding_content'}
|
||||
style={styles.scrollContainer}
|
||||
>
|
||||
<Animated.ScrollView
|
||||
scrollEventThrottle={16}
|
||||
|
|
@ -123,9 +152,9 @@ const Onboarding = ({
|
|||
nextSlideHandler={nextSlide}
|
||||
signInHandler={signInHandler}
|
||||
scrollX={scrollX}
|
||||
lastSlideIndex={lastSlideIndex}
|
||||
lastSlideIndex={LAST_SLIDE_INDEX}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
</AnimatedSafeArea>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ type Props = {
|
|||
theme: Theme;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
index: number;
|
||||
lastSlideIndex: number;
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
|
|
@ -57,10 +58,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
}));
|
||||
|
||||
const FIRST_SLIDE = 0;
|
||||
const LAST_SLIDE = 3;
|
||||
const FIRST_LOAD_ELEMENTS_POSITION = 400;
|
||||
|
||||
const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
||||
const SlideItem = ({theme, item, scrollX, index, lastSlideIndex}: Props) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
|
|
@ -69,9 +68,9 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
*/
|
||||
const [firstLoad, setFirstLoad] = useState(true);
|
||||
|
||||
const initialImagePosition = useSharedValue(FIRST_LOAD_ELEMENTS_POSITION);
|
||||
const initialTitlePosition = useSharedValue(FIRST_LOAD_ELEMENTS_POSITION);
|
||||
const initialDescriptionPosition = useSharedValue(FIRST_LOAD_ELEMENTS_POSITION);
|
||||
const initialImagePosition = useSharedValue(width);
|
||||
const initialTitlePosition = useSharedValue(width);
|
||||
const initialDescriptionPosition = useSharedValue(width);
|
||||
|
||||
const initialElementsOpacity = useSharedValue(0);
|
||||
|
||||
|
|
@ -189,7 +188,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
style={[
|
||||
styles.title,
|
||||
(index === FIRST_SLIDE ? styles.fontFirstTitle : styles.fontTitle),
|
||||
(index === LAST_SLIDE ? styles.widthLastSlide : undefined),
|
||||
(index === lastSlideIndex ? styles.widthLastSlide : undefined),
|
||||
translateTitle,
|
||||
opacity,
|
||||
translateFirstTitleOnLoad,
|
||||
|
|
@ -204,7 +203,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
translateDescription,
|
||||
opacity,
|
||||
(index === FIRST_SLIDE && firstLoad ? styles.firstSlideInitialPosition : undefined),
|
||||
(index === LAST_SLIDE ? styles.widthLastSlide : undefined),
|
||||
(index === lastSlideIndex ? styles.widthLastSlide : undefined),
|
||||
translateFirstDescriptionOnLoad,
|
||||
]}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import ServerHeader from './header';
|
|||
import type {DeepLinkWithData, LaunchProps} from '@typings/launch';
|
||||
|
||||
interface ServerProps extends LaunchProps {
|
||||
animated?: boolean;
|
||||
closeButtonId?: string;
|
||||
componentId: string;
|
||||
theme: Theme;
|
||||
|
|
@ -48,9 +49,24 @@ const defaultServerUrlMessage = {
|
|||
defaultMessage: 'Please enter a valid server URL',
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
appInfo: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.56),
|
||||
},
|
||||
flex: {
|
||||
flex: 1,
|
||||
},
|
||||
scrollContainer: {
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}));
|
||||
|
||||
const AnimatedSafeArea = Animated.createAnimatedComponent(SafeAreaView);
|
||||
|
||||
const Server = ({
|
||||
animated,
|
||||
closeButtonId,
|
||||
componentId,
|
||||
displayName: defaultDisplayName,
|
||||
|
|
@ -63,7 +79,7 @@ const Server = ({
|
|||
const intl = useIntl();
|
||||
const managedConfig = useManagedConfig<ManagedConfig>();
|
||||
const dimensions = useWindowDimensions();
|
||||
const translateX = useSharedValue(0);
|
||||
const translateX = useSharedValue(animated ? dimensions.width : 0);
|
||||
const keyboardAwareRef = useRef<KeyboardAwareScrollView>(null);
|
||||
const [connecting, setConnecting] = useState(false);
|
||||
const [displayName, setDisplayName] = useState<string>('');
|
||||
|
|
@ -362,18 +378,4 @@ const Server = ({
|
|||
);
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
appInfo: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.56),
|
||||
},
|
||||
flex: {
|
||||
flex: 1,
|
||||
},
|
||||
scrollContainer: {
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}));
|
||||
|
||||
export default Server;
|
||||
|
|
|
|||
Loading…
Reference in a new issue