add types and translate to texts
This commit is contained in:
parent
3c64f308f1
commit
d921da0e9d
5 changed files with 93 additions and 68 deletions
|
|
@ -15,8 +15,8 @@ type Props = {
|
|||
theme: Theme;
|
||||
isLastSlide: boolean;
|
||||
lastSlideIndex: number;
|
||||
nextSlideHandler: any;
|
||||
signInHandler: any;
|
||||
nextSlideHandler: () => void;
|
||||
signInHandler: () => void;
|
||||
currentIndex: number;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
};
|
||||
|
|
@ -51,11 +51,12 @@ const FooterButtons = ({
|
|||
// keep in mind penultimate and ultimate slides to run buttons animations
|
||||
const isPenultimateSlide = currentIndex === (lastSlideIndex - 1);
|
||||
const needToAnimate = isLastSlide || isPenultimateSlide;
|
||||
const inputRange = [(currentIndex - 1) * width, currentIndex * width, (currentIndex + 1) * width];
|
||||
|
||||
const resizeStyle = useAnimatedStyle(() => {
|
||||
const interpolatedWidth = interpolate(
|
||||
scrollX.value,
|
||||
[(currentIndex - 1) * width, currentIndex * width, (currentIndex + 1) * width],
|
||||
inputRange,
|
||||
[BUTTON_SIZE, isLastSlide ? width * 0.8 : BUTTON_SIZE, width * 0.8],
|
||||
);
|
||||
|
||||
|
|
@ -65,13 +66,23 @@ const FooterButtons = ({
|
|||
const opacityTextStyle = useAnimatedStyle(() => {
|
||||
const interpolatedScale = interpolate(
|
||||
scrollX.value,
|
||||
[(currentIndex - 1) * width, currentIndex * width, (currentIndex + 1) * width],
|
||||
inputRange,
|
||||
[isPenultimateSlide ? 1 : 0, 1, 0],
|
||||
);
|
||||
|
||||
return {opacity: needToAnimate ? interpolatedScale : 1};
|
||||
});
|
||||
|
||||
const opacitySignInButton = useAnimatedStyle(() => {
|
||||
const interpolatedScale = interpolate(
|
||||
scrollX.value,
|
||||
inputRange,
|
||||
[1, (isLastSlide ? 0 : 1), 0],
|
||||
);
|
||||
|
||||
return {opacity: needToAnimate ? interpolatedScale : 1};
|
||||
});
|
||||
|
||||
let mainButtonText = (
|
||||
<Animated.View style={[{flexDirection: 'row'}, opacityTextStyle]}>
|
||||
<FormattedText
|
||||
|
|
@ -110,17 +121,17 @@ const FooterButtons = ({
|
|||
>
|
||||
{mainButtonText}
|
||||
</AnimatedButton>
|
||||
<Button
|
||||
<AnimatedButton
|
||||
testID='mobile.onboaring.sign_in'
|
||||
onPress={() => signInHandler()}
|
||||
containerStyle={[styles.button, buttonBackgroundStyle(theme, 'm', 'link', 'default')]}
|
||||
style={[styles.button, buttonBackgroundStyle(theme, 'm', 'link', 'default'), opacitySignInButton]}
|
||||
>
|
||||
<FormattedText
|
||||
id='mobile.onboarding.sign_in'
|
||||
defaultMessage='Sign in'
|
||||
style={buttonTextStyle(theme, 's', 'primary', 'inverted')}
|
||||
/>
|
||||
</Button>
|
||||
</AnimatedButton>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {View, ListRenderItemInfo, useWindowDimensions, Platform, SafeAreaView} from 'react-native';
|
||||
import Animated, {runOnJS, useAnimatedRef, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
import {View, ListRenderItemInfo, useWindowDimensions, SafeAreaView} from 'react-native';
|
||||
import Animated, {runOnJS, useAnimatedRef, useAnimatedScrollHandler, useSharedValue} from 'react-native-reanimated';
|
||||
|
||||
import Background from '@screens/background';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
|
@ -11,7 +11,7 @@ import {makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import FooterButtons from './footer_buttons';
|
||||
import Paginator from './paginator';
|
||||
import SlideItem from './slide';
|
||||
import slidesData from './slides_data';
|
||||
import useSlidesData, {OnboardingItem} from './slides_data';
|
||||
|
||||
import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
|
|
@ -27,6 +27,7 @@ const Onboarding = ({
|
|||
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);
|
||||
|
|
@ -51,7 +52,7 @@ const Onboarding = ({
|
|||
setIsLastSlide(!isLastSlide);
|
||||
};
|
||||
|
||||
const renderSlide = useCallback(({item, index}: ListRenderItemInfo<any>) => {
|
||||
const renderSlide = useCallback(({item, index}: ListRenderItemInfo<OnboardingItem>) => {
|
||||
return (
|
||||
<SlideItem
|
||||
item={item}
|
||||
|
|
@ -96,7 +97,7 @@ const Onboarding = ({
|
|||
ref={slidesRef}
|
||||
>
|
||||
{slidesData.map((item, index) => {
|
||||
return renderSlide({item, index} as ListRenderItemInfo<any>);
|
||||
return renderSlide({item, index} as ListRenderItemInfo<OnboardingItem>);
|
||||
})}
|
||||
</Animated.ScrollView>
|
||||
<Paginator
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ import Animated, {interpolate, useAnimatedStyle} from 'react-native-reanimated';
|
|||
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import {OnboardingItem} from './slides_data';
|
||||
|
||||
type Props = {
|
||||
data: any;
|
||||
data: OnboardingItem[];
|
||||
theme: Theme;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
moveToSlide: any;
|
||||
moveToSlide: (slideIndexToMove: number) => void;
|
||||
};
|
||||
|
||||
const DOT_SIZE = 16;
|
||||
|
|
@ -44,9 +46,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
width: DOT_SIZE,
|
||||
opacity: 0.15,
|
||||
},
|
||||
button: {
|
||||
marginTop: 5,
|
||||
},
|
||||
}));
|
||||
|
||||
const Paginator = ({
|
||||
|
|
@ -56,34 +55,31 @@ const Paginator = ({
|
|||
moveToSlide,
|
||||
}: Props) => {
|
||||
return (
|
||||
<View style={{flexDirection: 'column', height: 10}}>
|
||||
<View style={{flexDirection: 'row', height: 5}}>
|
||||
{data.map((item: any, i: number) => {
|
||||
return (
|
||||
<Dot
|
||||
item={item}
|
||||
key={item.id}
|
||||
theme={theme}
|
||||
moveToSlide={moveToSlide}
|
||||
index={i}
|
||||
scrollX={scrollX}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
<View style={{flexDirection: 'row', height: 15}}>
|
||||
{data.map((item: OnboardingItem, index: number) => {
|
||||
return (
|
||||
<Dot
|
||||
key={`${item.id}-${index.toString()}`}
|
||||
theme={theme}
|
||||
moveToSlide={moveToSlide}
|
||||
index={index}
|
||||
scrollX={scrollX}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
type DotProps = {
|
||||
item: any;
|
||||
index: number;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
theme: Theme;
|
||||
moveToSlide: any;
|
||||
moveToSlide: (slideIndexToMove: number) => void;
|
||||
};
|
||||
|
||||
const Dot = ({item, index, scrollX, theme, moveToSlide}: DotProps) => {
|
||||
// this has to be extracted as a component since the useAnimatedStyle hook cant be used inside a loop
|
||||
const Dot = ({index, scrollX, theme, moveToSlide}: DotProps) => {
|
||||
const {width} = useWindowDimensions();
|
||||
const styles = getStyleSheet(theme);
|
||||
const inputRange = [(index - 1) * width, index * width, (index + 1) * width];
|
||||
|
|
@ -111,19 +107,15 @@ const Dot = ({item, index, scrollX, theme, moveToSlide}: DotProps) => {
|
|||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => moveToSlide(index)}
|
||||
key={item.id}
|
||||
>
|
||||
<Animated.View
|
||||
style={[styles.fixedDot]}
|
||||
key={'fixed-' + item.id + index.toString()}
|
||||
/>
|
||||
<Animated.View
|
||||
style={[styles.outerDot, outerDotOpacity]}
|
||||
key={'outer-' + item.id + index.toString()}
|
||||
/>
|
||||
<Animated.View
|
||||
style={[styles.dot, dotOpacity]}
|
||||
key={'inner-' + item.id + index.toString()}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
import React from 'react';
|
||||
import {View, useWindowDimensions} from 'react-native';
|
||||
import Animated, {interpolate, useAnimatedStyle} from 'react-native-reanimated';
|
||||
import Animated, {Extrapolate, interpolate, useAnimatedStyle} from 'react-native-reanimated';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import {OnboardingItem} from './slides_data';
|
||||
|
||||
type Props = {
|
||||
item: any;
|
||||
item: OnboardingItem;
|
||||
theme: Theme;
|
||||
scrollX: Animated.SharedValue<number>;
|
||||
index: number;
|
||||
|
|
@ -63,6 +65,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
scrollX.value,
|
||||
inputRange,
|
||||
[width * 2, 0, -width * 2],
|
||||
Extrapolate.CLAMP,
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -77,6 +80,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
scrollX.value,
|
||||
inputRange,
|
||||
[width * 0.6, 0, -width * 0.6],
|
||||
Extrapolate.CLAMP,
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -91,6 +95,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
scrollX.value,
|
||||
inputRange,
|
||||
[width * 0.2, 0, -width * 0.2],
|
||||
Extrapolate.CLAMP,
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -105,6 +110,7 @@ const SlideItem = ({theme, item, scrollX, index}: Props) => {
|
|||
scrollX.value,
|
||||
inputRange,
|
||||
[0.2, 1, 0.2],
|
||||
Extrapolate.CLAMP,
|
||||
);
|
||||
|
||||
return {opacity: opacityInterpolate};
|
||||
|
|
|
|||
|
|
@ -1,30 +1,45 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// pablo - translate each text. turn svgs into react components?
|
||||
export default [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Welcome',
|
||||
description: 'Mattermost is an open source platform for developer collaboration. Secure, flexible, and integrated with your tools.',
|
||||
image: require('./illustrations/chat.svg').default,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Collaborate in real-time',
|
||||
description: 'Persistent channels, direct messaging, and file sharing works seamlessly so you can stay connected, wherever you are.',
|
||||
image: require('./illustrations/team_communication.svg').default,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Start secure audio calls instantly',
|
||||
description: 'When typing isn’t fast enough, switch from channel-based chat to secure audio calls with a single tap.',
|
||||
image: require('./illustrations/calls.svg').default,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Integrate with tools you love',
|
||||
description: 'Go beyond chat with tightly-integratedproduct solutions matched to common development processes.',
|
||||
image: require('./illustrations/integrations.svg').default,
|
||||
},
|
||||
];
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
export type OnboardingItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
}
|
||||
const useSalidesData = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const slidesData: OnboardingItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: intl.formatMessage({id: 'onboarding_screen.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: require('./illustrations/chat.svg').default,
|
||||
},
|
||||
{
|
||||
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: require('./illustrations/team_communication.svg').default,
|
||||
},
|
||||
{
|
||||
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: require('./illustrations/calls.svg').default,
|
||||
},
|
||||
{
|
||||
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: require('./illustrations/integrations.svg').default,
|
||||
},
|
||||
];
|
||||
|
||||
return {slidesData};
|
||||
};
|
||||
|
||||
export default useSalidesData;
|
||||
|
|
|
|||
Loading…
Reference in a new issue