add types and translate to texts

This commit is contained in:
Pablo Velez Vidal 2022-11-03 13:41:16 +01:00
parent 3c64f308f1
commit d921da0e9d
5 changed files with 93 additions and 68 deletions

View file

@ -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>
);
};

View file

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

View file

@ -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>
);

View file

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

View file

@ -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 isnt 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 isnt 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;