forked from Ivasoft/mattermost-mobile
pr feedback, reorganize styles, move types to correct folder, remove unnecesary values
This commit is contained in:
@@ -5,8 +5,8 @@ import React from 'react';
|
||||
import {Pressable, useWindowDimensions, View} from 'react-native';
|
||||
import Animated, {Extrapolate, interpolate, useAnimatedStyle} from 'react-native-reanimated';
|
||||
|
||||
import CompassIcon from '@app/components/compass_icon';
|
||||
import FormattedText from '@app/components/formatted_text';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
@@ -28,9 +28,25 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
marginLeft: 5,
|
||||
marginTop: 2,
|
||||
},
|
||||
nextButtonText: {
|
||||
flexDirection: 'row',
|
||||
position: 'absolute',
|
||||
},
|
||||
signInButtonText: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
footerButtonsContainer: {
|
||||
flexDirection: 'column',
|
||||
height: 150,
|
||||
marginTop: 15,
|
||||
width: '100%',
|
||||
marginHorizontal: 10,
|
||||
alignItems: 'center',
|
||||
},
|
||||
}));
|
||||
|
||||
const AnimatedButton = Animated.createAnimatedComponent(Pressable);
|
||||
const BUTTON_SIZE = 80;
|
||||
|
||||
const FooterButtons = ({
|
||||
theme,
|
||||
@@ -41,7 +57,6 @@ const FooterButtons = ({
|
||||
}: Props) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const styles = getStyleSheet(theme);
|
||||
const BUTTON_SIZE = 80;
|
||||
|
||||
// keep in mind penultimate and ultimate slides to run buttons text/opacity/size animations
|
||||
const penultimateSlide = lastSlideIndex - 1;
|
||||
@@ -96,7 +111,7 @@ const FooterButtons = ({
|
||||
});
|
||||
|
||||
const nextButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row', position: 'absolute'}, opacityNextTextStyle]}>
|
||||
<Animated.View style={[styles.nextButtonText, opacityNextTextStyle]}>
|
||||
<FormattedText
|
||||
id='mobile.onboarding.next'
|
||||
defaultMessage='Next'
|
||||
@@ -110,7 +125,7 @@ const FooterButtons = ({
|
||||
);
|
||||
|
||||
const signInButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row'}, opacitySignInTextStyle]}>
|
||||
<Animated.View style={[styles.signInButtonText, opacitySignInTextStyle]}>
|
||||
<FormattedText
|
||||
id='mobile.onboarding.sign_in_to_get_started'
|
||||
defaultMessage='Sign in to get started'
|
||||
@@ -120,10 +135,10 @@ const FooterButtons = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{flexDirection: 'column', height: 150, marginTop: 15, width: '100%', marginHorizontal: 10, alignItems: 'center'}}>
|
||||
<View style={styles.footerButtonsContainer}>
|
||||
<AnimatedButton
|
||||
testID='mobile.onboaring.next'
|
||||
onPress={() => nextSlideHandler()}
|
||||
onPress={nextSlideHandler}
|
||||
style={[styles.button, buttonBackgroundStyle(theme, 'm', 'primary', 'default'), resizeStyle]}
|
||||
>
|
||||
{nextButtonText}
|
||||
@@ -131,7 +146,7 @@ const FooterButtons = ({
|
||||
</AnimatedButton>
|
||||
<AnimatedButton
|
||||
testID='mobile.onboaring.sign_in'
|
||||
onPress={() => signInHandler()}
|
||||
onPress={signInHandler}
|
||||
style={[styles.button, buttonBackgroundStyle(theme, 'm', 'link', 'default'), opacitySignInButton]}
|
||||
>
|
||||
<FormattedText
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,19 +2,19 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useEffect, useRef} from 'react';
|
||||
import {View, ListRenderItemInfo, useWindowDimensions, SafeAreaView, ScrollView, Animated as AnimatedRN} from 'react-native';
|
||||
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 {storeOnboardingViewedValue} from '@actions/app/global';
|
||||
import {Screens} from '@app/constants';
|
||||
import Background from '@screens/background';
|
||||
import {goToScreen, loginAnimationOptions} from '@screens/navigation';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {OnboardingItem} from '@typings/screens/onboarding';
|
||||
|
||||
import FooterButtons from './footer_buttons';
|
||||
import Paginator from './paginator';
|
||||
import SlideItem from './slide';
|
||||
import useSlidesData, {OnboardingItem} from './slides_data';
|
||||
import useSlidesData from './slides_data';
|
||||
|
||||
import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
@@ -22,9 +22,7 @@ interface OnboardingProps extends LaunchProps {
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const AnimatedSafeArea = Animated.createAnimatedComponent(SafeAreaView);
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme(() => ({
|
||||
const styles = StyleSheet.create({
|
||||
onBoardingContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
@@ -34,17 +32,15 @@ const getStyleSheet = makeStyleSheetFromTheme(() => ({
|
||||
scrollContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
const Onboarding = ({
|
||||
theme,
|
||||
}: OnboardingProps) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const styles = getStyleSheet(theme);
|
||||
const slidesData = useSlidesData().slidesData;
|
||||
const {slidesData} = useSlidesData();
|
||||
const lastSlideIndex = slidesData.length - 1;
|
||||
const slidesRef = useAnimatedRef<ScrollView>();
|
||||
|
||||
@@ -63,14 +59,14 @@ const Onboarding = ({
|
||||
return () => scrollAnimation.current.removeAllListeners();
|
||||
}, []);
|
||||
|
||||
const moveToSlide = (slideIndexToMove: number) => {
|
||||
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();
|
||||
};
|
||||
}, [currentIndex.value]);
|
||||
|
||||
const nextSlide = useCallback(() => {
|
||||
const nextSlideIndex = currentIndex.value + 1;
|
||||
@@ -95,7 +91,7 @@ const Onboarding = ({
|
||||
theme={theme}
|
||||
scrollX={scrollX}
|
||||
index={index}
|
||||
key={item.id}
|
||||
key={`key-${index.toString()}`}
|
||||
/>
|
||||
);
|
||||
}, []);
|
||||
@@ -112,12 +108,11 @@ const Onboarding = ({
|
||||
testID='onboarding.screen'
|
||||
>
|
||||
<Background theme={theme}/>
|
||||
<AnimatedSafeArea
|
||||
<SafeAreaView
|
||||
key={'onboarding_content'}
|
||||
style={[styles.scrollContainer]}
|
||||
style={styles.scrollContainer}
|
||||
>
|
||||
<Animated.ScrollView
|
||||
scrollEventThrottle={16}
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
pagingEnabled={true}
|
||||
@@ -130,7 +125,7 @@ const Onboarding = ({
|
||||
})}
|
||||
</Animated.ScrollView>
|
||||
<Paginator
|
||||
data={slidesData}
|
||||
dataLength={slidesData.length}
|
||||
theme={theme}
|
||||
scrollX={scrollX}
|
||||
moveToSlide={moveToSlide}
|
||||
@@ -142,7 +137,7 @@ const Onboarding = ({
|
||||
scrollX={scrollX}
|
||||
lastSlideIndex={lastSlideIndex}
|
||||
/>
|
||||
</AnimatedSafeArea>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,10 +7,8 @@ import Animated, {interpolate, useAnimatedStyle} from 'react-native-reanimated';
|
||||
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import {OnboardingItem} from './slides_data';
|
||||
|
||||
type Props = {
|
||||
data: OnboardingItem[];
|
||||
dataLength: number;
|
||||
theme: Theme;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
moveToSlide: (slideIndexToMove: number) => void;
|
||||
@@ -46,20 +44,25 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
width: DOT_SIZE,
|
||||
opacity: 0.15,
|
||||
},
|
||||
paginatorContainer: {
|
||||
flexDirection: 'row',
|
||||
height: 15,
|
||||
},
|
||||
}));
|
||||
|
||||
const Paginator = ({
|
||||
theme,
|
||||
data,
|
||||
dataLength,
|
||||
scrollX,
|
||||
moveToSlide,
|
||||
}: Props) => {
|
||||
const styles = getStyleSheet(theme);
|
||||
return (
|
||||
<View style={{flexDirection: 'row', height: 15}}>
|
||||
{data.map((item: OnboardingItem, index: number) => {
|
||||
<View style={styles.paginatorContainer}>
|
||||
{[...Array(dataLength)].map((item: number, index: number) => {
|
||||
return (
|
||||
<Dot
|
||||
key={`${item.id}-${index.toString()}`}
|
||||
key={`key-${index.toString()}`}
|
||||
theme={theme}
|
||||
moveToSlide={moveToSlide}
|
||||
index={index}
|
||||
@@ -105,18 +108,10 @@ const Dot = ({index, scrollX, theme, moveToSlide}: DotProps) => {
|
||||
});
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => moveToSlide(index)}
|
||||
>
|
||||
<Animated.View
|
||||
style={[styles.fixedDot]}
|
||||
/>
|
||||
<Animated.View
|
||||
style={[styles.outerDot, outerDotOpacity]}
|
||||
/>
|
||||
<Animated.View
|
||||
style={[styles.dot, dotOpacity]}
|
||||
/>
|
||||
<TouchableOpacity onPress={() => moveToSlide(index)}>
|
||||
<Animated.View style={styles.fixedDot}/>
|
||||
<Animated.View style={[styles.outerDot, outerDotOpacity]}/>
|
||||
<Animated.View style={[styles.dot, dotOpacity]}/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,11 +5,10 @@ import React, {useEffect, useState} from 'react';
|
||||
import {View, useWindowDimensions} from 'react-native';
|
||||
import Animated, {Extrapolate, interpolate, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import {OnboardingItem} from '@typings/screens/onboarding';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import {OnboardingItem} from './slides_data';
|
||||
|
||||
type Props = {
|
||||
item: OnboardingItem;
|
||||
theme: Theme;
|
||||
@@ -24,6 +23,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
height: 100,
|
||||
color: theme.centerChannelColor,
|
||||
textAlign: 'center',
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
fontTitle: {
|
||||
fontSize: 40,
|
||||
@@ -36,10 +37,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
opacity: 0,
|
||||
},
|
||||
description: {
|
||||
fontWeight: '400',
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 20,
|
||||
fontFamily: 'Open Sans',
|
||||
height: 80,
|
||||
color: changeOpacity(theme.centerChannelColor, 0.64),
|
||||
...typography('Body', 200, 'Regular'),
|
||||
@@ -57,15 +57,16 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const FIRST_SLIDE = 0;
|
||||
const FIRST_LOAD_ELEMENTS_POSITION = 400;
|
||||
|
||||
const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const styles = getStyleSheet(theme);
|
||||
const FIRST_SLIDE = 0;
|
||||
|
||||
/**
|
||||
* Code used to animate the first image load
|
||||
*/
|
||||
const FIRST_LOAD_ELEMENTS_POSITION = 400;
|
||||
const [firstLoad, setFirstLoad] = useState(true);
|
||||
|
||||
const initialImagePosition = useSharedValue(FIRST_LOAD_ELEMENTS_POSITION);
|
||||
@@ -182,7 +183,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
||||
>
|
||||
{item.image}
|
||||
</Animated.View>
|
||||
<View style={{flex: 0.3}}>
|
||||
<View>
|
||||
<Animated.Text
|
||||
style={[
|
||||
styles.title,
|
||||
@@ -211,4 +212,4 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SlideItem;
|
||||
export default React.memo(SlideItem);
|
||||
|
||||
@@ -5,18 +5,13 @@ import React from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {StyleSheet} from 'react-native';
|
||||
|
||||
import {OnboardingItem} from '@typings/screens/onboarding';
|
||||
|
||||
import CallsSvg from './illustrations/calls';
|
||||
import ChatSvg from './illustrations/chat';
|
||||
import IntegrationsSvg from './illustrations/integrations';
|
||||
import TeamCommunicationSvg from './illustrations/team_communication';
|
||||
|
||||
export type OnboardingItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
image: React.ReactElement;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
image: {
|
||||
justifyContent: 'center',
|
||||
@@ -35,25 +30,21 @@ const useSalidesData = () => {
|
||||
|
||||
const slidesData: OnboardingItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: intl.formatMessage({id: 'onboarding_screen.welcome', defaultMessage: 'Welcome'}),
|
||||
title: intl.formatMessage({id: 'onboarding.welcome', defaultMessage: 'Welcome'}),
|
||||
description: intl.formatMessage({id: 'onboaring.welcome_description', defaultMessage: 'Mattermost is an open source platform for developer collaboration. Secure, flexible, and integrated with your tools.'}),
|
||||
image: chatSvg,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: intl.formatMessage({id: 'onboarding.realtime_collaboration', defaultMessage: 'Collaborate in real-time'}),
|
||||
description: intl.formatMessage({id: 'onboarding.realtime_collaboration_description', defaultMessage: 'Persistent channels, direct messaging, and file sharing works seamlessly so you can stay connected, wherever you are.'}),
|
||||
image: teamCommunicationSvg,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: intl.formatMessage({id: 'onboarding.calls', defaultMessage: 'Start secure audio calls instantly'}),
|
||||
description: intl.formatMessage({id: 'onboarding.calls_description', defaultMessage: 'When typing isn’t fast enough, switch from channel-based chat to secure audio calls with a single tap.'}),
|
||||
image: callsSvg,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
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.'}),
|
||||
image: integrationsSvg,
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"*": ["./*", "node_modules/*"],
|
||||
}
|
||||
},
|
||||
"include": ["app/**/*", "share_extensionn/**/*", "test/**/*", "types/**/*"],
|
||||
"include": ["app/**/*", "share_extension/**/*", "test/**/*", "types/**/*"],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"build",
|
||||
|
||||
7
types/screens/onboarding.ts
Normal file
7
types/screens/onboarding.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
export type OnboardingItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
image: React.ReactElement;
|
||||
};
|
||||
Reference in New Issue
Block a user