diff --git a/app/screens/onboarding/footer_buttons.tsx b/app/screens/onboarding/footer_buttons.tsx index 2b381be04..1c89ee5bf 100644 --- a/app/screens/onboarding/footer_buttons.tsx +++ b/app/screens/onboarding/footer_buttons.tsx @@ -37,16 +37,16 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ }, footerButtonsContainer: { flexDirection: 'column', - height: 150, - marginTop: 15, + height: 120, + marginTop: 25, + marginBottom: 15, width: '100%', - marginHorizontal: 10, alignItems: 'center', }, })); const AnimatedButton = Animated.createAnimatedComponent(Pressable); -const BUTTON_SIZE = 80; +const BUTTON_SIZE = 100; const FooterButtons = ({ theme, @@ -115,7 +115,7 @@ const FooterButtons = ({ ); @@ -139,7 +139,7 @@ const FooterButtons = ({ {nextButtonText} {signInButtonText} @@ -147,12 +147,12 @@ const FooterButtons = ({ diff --git a/app/screens/onboarding/index.tsx b/app/screens/onboarding/index.tsx index 44e011da9..830cd1f6a 100644 --- a/app/screens/onboarding/index.tsx +++ b/app/screens/onboarding/index.tsx @@ -1,9 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback, useEffect, useRef} from 'react'; -import {View, ListRenderItemInfo, useWindowDimensions, SafeAreaView, ScrollView, Animated as AnimatedRN, StyleSheet} from 'react-native'; -import Animated, {Easing, useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useSharedValue} from 'react-native-reanimated'; +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 {storeOnboardingViewedValue} from '@actions/app/global'; import {Screens} from '@app/constants'; @@ -45,27 +45,14 @@ const Onboarding = ({ const slidesRef = useAnimatedRef(); const scrollX = useSharedValue(0); - const scrollAnimation = useRef(new AnimatedRN.Value(0)); const currentIndex = useDerivedValue(() => Math.round(scrollX.value / width)); - useEffect(() => { - scrollAnimation.current?.addListener((animation) => { - slidesRef.current?.scrollTo({ - x: animation.value, - animated: false, - }); - }); - return () => scrollAnimation.current.removeAllListeners(); - }, []); - const moveToSlide = useCallback((slideIndexToMove: number) => { - AnimatedRN.timing(scrollAnimation.current, { - toValue: (slideIndexToMove * width), - duration: Math.abs(currentIndex.value - slideIndexToMove) * 200, - useNativeDriver: true, - easing: Easing.linear, - }).start(); + slidesRef.current?.scrollTo({ + x: (slideIndexToMove * width), + animated: true, + }); }, [currentIndex.value]); const nextSlide = useCallback(() => { diff --git a/app/screens/onboarding/paginator.tsx b/app/screens/onboarding/paginator.tsx index ca1aa8297..69a9ef906 100644 --- a/app/screens/onboarding/paginator.tsx +++ b/app/screens/onboarding/paginator.tsx @@ -47,6 +47,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ paginatorContainer: { flexDirection: 'row', height: 15, + justifyContent: 'space-between', + width: 120, }, })); @@ -59,7 +61,7 @@ const Paginator = ({ const styles = getStyleSheet(theme); return ( - {[...Array(dataLength)].map((item: number, index: number) => { + {[...Array(dataLength)].map((_, index: number) => { return ( { +const Dot = ({ + index, + scrollX, + theme, + moveToSlide, +}: DotProps) => { const {width} = useWindowDimensions(); const styles = getStyleSheet(theme); const inputRange = [(index - 1) * width, index * width, (index + 1) * width]; @@ -108,7 +115,10 @@ const Dot = ({index, scrollX, theme, moveToSlide}: DotProps) => { }); return ( - moveToSlide(index)}> + moveToSlide(index)} + hitSlop={{top: 8, left: 8, right: 8, bottom: 8}} + > diff --git a/app/screens/onboarding/slide.tsx b/app/screens/onboarding/slide.tsx index 4bc19ea2c..020fec814 100644 --- a/app/screens/onboarding/slide.tsx +++ b/app/screens/onboarding/slide.tsx @@ -17,20 +17,26 @@ type Props = { }; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ + image: { + justifyContent: 'center', + maxHeight: 180, + }, title: { - fontWeight: '600', - marginBottom: 5, height: 100, color: theme.centerChannelColor, textAlign: 'center', - fontFamily: 'Metropolis-SemiBold', paddingHorizontal: 20, }, fontTitle: { - fontSize: 40, + marginTop: 32, + ...typography('Heading', 1000, 'SemiBold'), }, fontFirstTitle: { - fontSize: 66, + ...typography('Heading', 1200, 'SemiBold'), + paddingTop: 40, + }, + widthLastSlide: { + paddingHorizontal: 50, }, firstSlideInitialPosition: { left: 200, @@ -39,17 +45,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ description: { textAlign: 'center', paddingHorizontal: 20, - fontFamily: 'Open Sans', height: 80, color: changeOpacity(theme.centerChannelColor, 0.64), ...typography('Body', 200, 'Regular'), }, - image: { - justifyContent: 'center', - height: 60, - maxHeight: 180, - width: 60, - }, itemContainer: { flex: 1, alignItems: 'center', @@ -58,6 +57,7 @@ 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) => { @@ -77,11 +77,11 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => { useEffect(() => { if (index === FIRST_SLIDE) { - initialImagePosition.value = withTiming(0, {duration: 1000}); - initialTitlePosition.value = withTiming(0, {duration: 1250}); - initialDescriptionPosition.value = withTiming(0, {duration: 1500}); + initialImagePosition.value = withTiming(0, {duration: 500}); + initialTitlePosition.value = withTiming(0, {duration: 700}); + initialDescriptionPosition.value = withTiming(0, {duration: 900}); - initialElementsOpacity.value = withTiming(1, {duration: 1500}); + initialElementsOpacity.value = withTiming(1, {duration: 900}); setFirstLoad(false); } }, []); @@ -175,6 +175,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => { { style={[ styles.title, (index === FIRST_SLIDE ? styles.fontFirstTitle : styles.fontTitle), + (index === LAST_SLIDE ? styles.widthLastSlide : undefined), translateTitle, opacity, translateFirstTitleOnLoad, @@ -202,6 +204,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => { translateDescription, opacity, (index === FIRST_SLIDE && firstLoad ? styles.firstSlideInitialPosition : undefined), + (index === LAST_SLIDE ? styles.widthLastSlide : undefined), translateFirstDescriptionOnLoad, ]} > diff --git a/app/screens/onboarding/slides_data.tsx b/app/screens/onboarding/slides_data.tsx index 467fa2887..b5d2eed8f 100644 --- a/app/screens/onboarding/slides_data.tsx +++ b/app/screens/onboarding/slides_data.tsx @@ -15,9 +15,10 @@ import TeamCommunicationSvg from './illustrations/team_communication'; const styles = StyleSheet.create({ image: { justifyContent: 'center', - height: 60, - maxHeight: 180, - width: 60, + height: 200, + }, + lastSlideImage: { + height: 250, }, }); @@ -26,7 +27,7 @@ const useSalidesData = () => { const callsSvg = (); const chatSvg = (); const teamCommunicationSvg = (); - const integrationsSvg = (); + const integrationsSvg = (); const slidesData: OnboardingItem[] = [ { @@ -46,7 +47,7 @@ const useSalidesData = () => { }, { title: intl.formatMessage({id: 'onboarding.integrations', defaultMessage: 'Integrate with tools you love'}), - description: intl.formatMessage({id: 'onboarding.integrations_description', defaultMessage: 'Go beyond chat with tightly-integratedproduct solutions matched to common development processes.'}), + description: intl.formatMessage({id: 'onboarding.integrations_description', defaultMessage: 'Go beyond chat with tightly-integrated product solutions matched to common development processes.'}), image: integrationsSvg, }, ]; diff --git a/app/utils/typography.ts b/app/utils/typography.ts index 812bf5f35..50ecbf763 100644 --- a/app/utils/typography.ts +++ b/app/utils/typography.ts @@ -6,7 +6,7 @@ import {StyleSheet, TextStyle} from 'react-native'; // type FontFamilies = 'OpenSans' | 'Metropolis'; type FontTypes = 'Heading' | 'Body'; type FontStyles = 'SemiBold' | 'Regular' | 'Light'; -type FontSizes = 25 | 50 | 75 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 1000; +type FontSizes = 25 | 50 | 75 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 1000 | 1200; const fontFamily = StyleSheet.create({ OpenSans: { @@ -30,6 +30,11 @@ const fontStyle = StyleSheet.create({ }); const fontSize = StyleSheet.create({ + 1200: { + fontSize: 66, + lineHeight: 48, + letterSpacing: -0.02, + }, 1000: { fontSize: 40, lineHeight: 48, diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 5be3baffa..562926c8a 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -656,7 +656,7 @@ "onboarding.calls": "Start secure audio calls instantly", "onboarding.calls_description": "When typing isn’t fast enough, switch from channel-based chat to secure audio calls with a single tap.", "onboarding.integrations": "Integrate with tools you love", - "onboarding.integrations_description": "Go beyond chat with tightly-integratedproduct solutions matched to common development processes.", + "onboarding.integrations_description": "Go beyond chat with tightly-integrated product solutions matched to common development processes.", "onboarding.realtime_collaboration": "Collaborate in real-time", "onboarding.realtime_collaboration_description": "Persistent channels, direct messaging, and file sharing works seamlessly so you can stay connected, wherever you are.", "onboarding.welcome": "Welcome",