application-amicale/src/components/Overrides/CustomIntroSlider.tsx
Arnaud Vergnet 00f9428972 Use context to handle preferences
This is not tested, expect crashes
2021-05-18 11:36:15 +02:00

341 lines
9.1 KiB
TypeScript

/*
* Copyright (c) 2019 - 2020 Arnaud Vergnet.
*
* This file is part of Campus INSAT.
*
* Campus INSAT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Campus INSAT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
*/
import * as React from 'react';
import {
ListRenderItemInfo,
Platform,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import i18n from 'i18n-js';
import AppIntroSlider from 'react-native-app-intro-slider';
import LinearGradient from 'react-native-linear-gradient';
import * as Animatable from 'react-native-animatable';
import { Card } from 'react-native-paper';
import Update from '../../constants/Update';
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
import MascotIntroWelcome from '../Intro/MascotIntroWelcome';
import IntroIcon from '../Intro/IconIntro';
import MascotIntroEnd from '../Intro/MascotIntroEnd';
import GENERAL_STYLES from '../../constants/Styles';
type PropsType = {
onDone: () => void;
isUpdate: boolean;
isAprilFools: boolean;
};
type StateType = {
currentSlide: number;
};
export type IntroSlideType = {
key: string;
title: string;
text: string;
view: () => React.ReactNode;
mascotStyle?: number;
colors: [string, string];
};
const styles = StyleSheet.create({
mainContent: {
flex: 1,
paddingBottom: 100,
},
text: {
color: 'rgba(255, 255, 255, 0.8)',
backgroundColor: 'transparent',
textAlign: 'center',
paddingHorizontal: 16,
},
title: {
fontSize: 22,
color: 'white',
backgroundColor: 'transparent',
textAlign: 'center',
marginBottom: 16,
},
mascot: {
marginLeft: 30,
marginBottom: 0,
width: 100,
marginTop: -30,
},
speechArrow: {
marginLeft: 50,
width: 0,
height: 0,
borderLeftWidth: 20,
borderRightWidth: 0,
borderBottomWidth: 20,
borderStyle: 'solid',
backgroundColor: 'transparent',
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
borderBottomColor: 'rgba(0,0,0,0.60)',
},
card: {
backgroundColor: 'rgba(0,0,0,0.38)',
marginHorizontal: 20,
borderColor: 'rgba(0,0,0,0.60)',
borderWidth: 4,
borderRadius: 10,
elevation: 0,
},
nextButtonContainer: {
borderRadius: 25,
padding: 5,
backgroundColor: 'rgba(0,0,0,0.2)',
},
doneButtonContainer: {
borderRadius: 25,
padding: 5,
backgroundColor: 'rgb(190,21,34)',
},
});
/**
* Class used to create intro slides
*/
export default class CustomIntroSlider extends React.Component<
PropsType,
StateType
> {
sliderRef: { current: null | AppIntroSlider };
introSlides: Array<IntroSlideType>;
updateSlides: Array<IntroSlideType>;
aprilFoolsSlides: Array<IntroSlideType>;
currentSlides: Array<IntroSlideType>;
/**
* Generates intro slides
*/
constructor(props: PropsType) {
super(props);
this.currentSlides = [];
this.state = {
currentSlide: 0,
};
this.sliderRef = React.createRef();
this.introSlides = [
{
key: '0', // Mascot
title: i18n.t('intro.slideMain.title'),
text: i18n.t('intro.slideMain.text'),
view: () => <MascotIntroWelcome />,
colors: ['#be1522', '#57080e'],
},
{
key: '1',
title: i18n.t('intro.slidePlanex.title'),
text: i18n.t('intro.slidePlanex.text'),
view: () => <IntroIcon icon="calendar-clock" />,
mascotStyle: MASCOT_STYLE.INTELLO,
colors: ['#be1522', '#57080e'],
},
{
key: '2',
title: i18n.t('intro.slideEvents.title'),
text: i18n.t('intro.slideEvents.text'),
view: () => <IntroIcon icon="calendar-star" />,
mascotStyle: MASCOT_STYLE.HAPPY,
colors: ['#be1522', '#57080e'],
},
{
key: '3',
title: i18n.t('intro.slideServices.title'),
text: i18n.t('intro.slideServices.text'),
view: () => <IntroIcon icon="view-dashboard-variant" />,
mascotStyle: MASCOT_STYLE.CUTE,
colors: ['#be1522', '#57080e'],
},
{
key: '4',
title: i18n.t('intro.slideDone.title'),
text: i18n.t('intro.slideDone.text'),
view: () => <MascotIntroEnd />,
colors: ['#9c165b', '#3e042b'],
},
];
this.updateSlides = new Update().getUpdateSlides();
this.aprilFoolsSlides = [
{
key: '1',
title: i18n.t('intro.aprilFoolsSlide.title'),
text: i18n.t('intro.aprilFoolsSlide.text'),
view: () => <View />,
mascotStyle: MASCOT_STYLE.NORMAL,
colors: ['#e01928', '#be1522'],
},
];
}
/**
* Render item to be used for the intro introSlides
*
* @param data
*/
getIntroRenderItem = (
data:
| (ListRenderItemInfo<IntroSlideType> & {
dimensions: { width: number; height: number };
})
| ListRenderItemInfo<IntroSlideType>
) => {
const item = data.item;
const { state } = this;
const index = parseInt(item.key, 10);
return (
<LinearGradient
style={[styles.mainContent]}
colors={item.colors}
start={{ x: 0, y: 0.1 }}
end={{ x: 0.1, y: 1 }}
>
{state.currentSlide === index ? (
<View style={GENERAL_STYLES.flex}>
<View style={GENERAL_STYLES.flex}>{item.view()}</View>
<Animatable.View useNativeDriver animation="fadeIn">
{item.mascotStyle != null ? (
<Mascot
style={styles.mascot}
emotion={item.mascotStyle}
animated
entryAnimation={{
animation: 'slideInLeft',
duration: 500,
}}
loopAnimation={{
animation: 'pulse',
iterationCount: 'infinite',
duration: 2000,
}}
/>
) : null}
<View style={styles.speechArrow} />
<Card style={styles.card}>
<Card.Content>
<Animatable.Text
useNativeDriver
animation="fadeIn"
delay={100}
style={styles.title}
>
{item.title}
</Animatable.Text>
<Animatable.Text
useNativeDriver
animation="fadeIn"
delay={200}
style={styles.text}
>
{item.text}
</Animatable.Text>
</Card.Content>
</Card>
</Animatable.View>
</View>
) : null}
</LinearGradient>
);
};
static setStatusBarColor(color: string) {
if (Platform.OS === 'android') {
StatusBar.setBackgroundColor(color, true);
}
}
onSlideChange = (index: number) => {
CustomIntroSlider.setStatusBarColor(this.currentSlides[index].colors[0]);
this.setState({ currentSlide: index });
};
onSkip = () => {
CustomIntroSlider.setStatusBarColor(
this.currentSlides[this.currentSlides.length - 1].colors[0]
);
if (this.sliderRef.current != null) {
this.sliderRef.current.goToSlide(this.currentSlides.length - 1);
}
};
onDone = () => {
const { props } = this;
props.onDone();
};
getRenderNextButton = () => {
return (
<Animatable.View
useNativeDriver
animation="fadeIn"
style={styles.nextButtonContainer}
>
<MaterialCommunityIcons name="arrow-right" color="#fff" size={40} />
</Animatable.View>
);
};
getRenderDoneButton = () => {
return (
<Animatable.View
useNativeDriver
animation="bounceIn"
style={styles.doneButtonContainer}
>
<MaterialCommunityIcons name="check" color="#fff" size={40} />
</Animatable.View>
);
};
render() {
const { props, state } = this;
this.currentSlides = this.introSlides;
if (props.isUpdate) {
this.currentSlides = this.updateSlides;
} else if (props.isAprilFools) {
this.currentSlides = this.aprilFoolsSlides;
}
CustomIntroSlider.setStatusBarColor(this.currentSlides[0].colors[0]);
return (
<AppIntroSlider
ref={this.sliderRef}
data={this.currentSlides}
extraData={state.currentSlide}
renderItem={this.getIntroRenderItem}
renderNextButton={this.getRenderNextButton}
renderDoneButton={this.getRenderDoneButton}
onDone={this.onDone}
onSlideChange={this.onSlideChange}
onSkip={this.onSkip}
/>
);
}
}