Improve intro slides usage

This commit is contained in:
Arnaud Vergnet 2020-08-14 16:52:18 +02:00
parent f05a1dffb3
commit a84a627fba
7 changed files with 206 additions and 167 deletions

View file

@ -418,19 +418,11 @@
}, },
"updateSlide0": { "updateSlide0": {
"title": "New in this update!", "title": "New in this update!",
"text": "Faster than ever and easier to use!\nThis update includes lots of changes to improve your experience.\nUse the brand new feedback button on the home screen to talk to the developer!" "text": ""
}, },
"updateSlide1": { "updateSlide1": {
"title": "Improved Planex!", "title": "",
"text": "You now have access to new controls, improved display, and you can also mark groups as favorites." "text": ""
},
"updateSlide2": {
"title": "Scanotron 3000!",
"text": "Say hello to Scanotron 3000!\nAvailable from the Qr-Code button on the home screen, it will help you get information about clubs and events around the campus.\n(Useless right now but we have hope for next year)"
},
"updateSlide3": {
"title": "Amicale Account!",
"text": "You can now connect to your Amicale INSAT account from within the app! See all available clubs and more to come!\nClick on the login button from the home screen."
}, },
"aprilFoolsSlide": { "aprilFoolsSlide": {
"title": "New in this update!", "title": "New in this update!",

View file

@ -417,19 +417,11 @@
}, },
"updateSlide0": { "updateSlide0": {
"title": "Nouveau dans cette mise à jour !", "title": "Nouveau dans cette mise à jour !",
"text": "Plus rapide que jamais et plus simple à utiliser !\nCette mise à jour contient de nombreux changements pour améliorer votre expérience.\nUtilisez le tout nouveau bouton de Feedback pour parler directement au développeur!" "text": ""
}, },
"updateSlide1": { "updateSlide1": {
"title": "Planex tout beau !", "title": "",
"text": "Vous avez maintenant accès à de nouveaux contrôles, un affichage amélioré, et vous pouvez marquer des groupes en favoris." "text": ""
},
"updateSlide2": {
"title": "Scanotron 3000 !",
"text": "Dites bonjour à Scanotron 3000 !\nDisponible depuis le bouton Qr-Code sur le menu principal, il vous aidera à avoir des informations sur les clubs et les événements du campus.\n(Inutile tout de suite mais on verra l'année pro)"
},
"updateSlide3": {
"title": "Compte Amicale !",
"text": "Vous pouvez maintenant vous connecter à votre compte Amicale depuis l'appli ! Accédez à la liste des clubs et plus à venir !\nCliquez sur le bouton Se Connecter dans le menu principal."
}, },
"aprilFoolsSlide": { "aprilFoolsSlide": {
"title": "Nouveau dans cette mise à jour !", "title": "Nouveau dans cette mise à jour !",

View file

@ -0,0 +1,41 @@
// @flow
import * as React from 'react';
import {StyleSheet, View} from 'react-native';
import * as Animatable from 'react-native-animatable';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
type PropsType = {
icon: string,
};
const styles = StyleSheet.create({
center: {
marginTop: 'auto',
marginBottom: 'auto',
marginRight: 'auto',
marginLeft: 'auto',
},
});
class IntroIcon extends React.Component<PropsType> {
shouldComponentUpdate(): boolean {
return false;
}
render(): React.Node {
const {icon} = this.props;
return (
<View style={{flex: 1}}>
<Animatable.View
useNativeDriver
style={styles.center}
animation="fadeIn">
<MaterialCommunityIcons name={icon} color="#fff" size={200} />
</Animatable.View>
</View>
);
}
}
export default IntroIcon;

View file

@ -0,0 +1,46 @@
// @flow
import * as React from 'react';
import {StyleSheet, View} from 'react-native';
import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
const styles = StyleSheet.create({
center: {
marginTop: 'auto',
marginBottom: 'auto',
marginRight: 'auto',
marginLeft: 'auto',
},
});
class MascotIntroEnd extends React.Component<null> {
shouldComponentUpdate(): boolean {
return false;
}
render(): React.Node {
return (
<View style={{flex: 1}}>
<Mascot
style={{
...styles.center,
height: '80%',
}}
emotion={MASCOT_STYLE.COOL}
animated
entryAnimation={{
animation: 'slideInDown',
duration: 2000,
}}
loopAnimation={{
animation: 'pulse',
duration: 2000,
iterationCount: 'infinite',
}}
/>
</View>
);
}
}
export default MascotIntroEnd;

View file

@ -0,0 +1,76 @@
// @flow
import * as React from 'react';
import {StyleSheet, View} from 'react-native';
import * as Animatable from 'react-native-animatable';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
const styles = StyleSheet.create({
center: {
marginTop: 'auto',
marginBottom: 'auto',
marginRight: 'auto',
marginLeft: 'auto',
},
});
class MascotIntroWelcome extends React.Component<null> {
shouldComponentUpdate(): boolean {
return false;
}
render(): React.Node {
return (
<View style={{flex: 1}}>
<Mascot
style={{
...styles.center,
height: '80%',
}}
emotion={MASCOT_STYLE.NORMAL}
animated
entryAnimation={{
animation: 'bounceIn',
duration: 2000,
}}
/>
<Animatable.Text
useNativeDriver
animation="fadeInUp"
duration={500}
style={{
color: '#fff',
textAlign: 'center',
fontSize: 25,
}}>
PABLO
</Animatable.Text>
<Animatable.View
useNativeDriver
animation="fadeInUp"
duration={500}
delay={200}
style={{
position: 'absolute',
bottom: 30,
right: '20%',
width: 50,
height: 50,
}}>
<MaterialCommunityIcons
style={{
...styles.center,
transform: [{rotateZ: '70deg'}],
}}
name="undo"
color="#fff"
size={40}
/>
</Animatable.View>
</View>
);
}
}
export default MascotIntroWelcome;

View file

@ -2,7 +2,6 @@
import * as React from 'react'; import * as React from 'react';
import {Platform, StatusBar, StyleSheet, View} from 'react-native'; import {Platform, StatusBar, StyleSheet, View} from 'react-native';
import type {MaterialCommunityIconsGlyphs} from 'react-native-vector-icons/MaterialCommunityIcons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import AppIntroSlider from 'react-native-app-intro-slider'; import AppIntroSlider from 'react-native-app-intro-slider';
@ -12,6 +11,9 @@ import {Card} from 'react-native-paper';
import Update from '../../constants/Update'; import Update from '../../constants/Update';
import ThemeManager from '../../managers/ThemeManager'; import ThemeManager from '../../managers/ThemeManager';
import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot'; import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
import MascotIntroWelcome from '../Intro/MascotIntroWelcome';
import IntroIcon from '../Intro/IconIntro';
import MascotIntroEnd from '../Intro/MascotIntroEnd';
type PropsType = { type PropsType = {
onDone: () => void, onDone: () => void,
@ -23,12 +25,12 @@ type StateType = {
currentSlide: number, currentSlide: number,
}; };
type IntroSlideType = { export type IntroSlideType = {
key: string, key: string,
title: string, title: string,
text: string, text: string,
view: () => React.Node, view: () => React.Node,
mascotStyle: number, mascotStyle?: number,
colors: [string, string], colors: [string, string],
}; };
@ -88,15 +90,14 @@ export default class CustomIntroSlider extends React.Component<
key: '0', // Mascot key: '0', // Mascot
title: i18n.t('intro.slideMain.title'), title: i18n.t('intro.slideMain.title'),
text: i18n.t('intro.slideMain.text'), text: i18n.t('intro.slideMain.text'),
view: this.getWelcomeView, view: (): React.Node => <MascotIntroWelcome />,
mascotStyle: MASCOT_STYLE.NORMAL,
colors: ['#be1522', '#57080e'], colors: ['#be1522', '#57080e'],
}, },
{ {
key: '1', key: '1',
title: i18n.t('intro.slidePlanex.title'), title: i18n.t('intro.slidePlanex.title'),
text: i18n.t('intro.slidePlanex.text'), text: i18n.t('intro.slidePlanex.text'),
view: (): React.Node => CustomIntroSlider.getIconView('calendar-clock'), view: (): React.Node => <IntroIcon icon="calendar-clock" />,
mascotStyle: MASCOT_STYLE.INTELLO, mascotStyle: MASCOT_STYLE.INTELLO,
colors: ['#be1522', '#57080e'], colors: ['#be1522', '#57080e'],
}, },
@ -104,7 +105,7 @@ export default class CustomIntroSlider extends React.Component<
key: '2', key: '2',
title: i18n.t('intro.slideEvents.title'), title: i18n.t('intro.slideEvents.title'),
text: i18n.t('intro.slideEvents.text'), text: i18n.t('intro.slideEvents.text'),
view: (): React.Node => CustomIntroSlider.getIconView('calendar-star'), view: (): React.Node => <IntroIcon icon="calendar-star" />,
mascotStyle: MASCOT_STYLE.HAPPY, mascotStyle: MASCOT_STYLE.HAPPY,
colors: ['#be1522', '#57080e'], colors: ['#be1522', '#57080e'],
}, },
@ -112,8 +113,7 @@ export default class CustomIntroSlider extends React.Component<
key: '3', key: '3',
title: i18n.t('intro.slideServices.title'), title: i18n.t('intro.slideServices.title'),
text: i18n.t('intro.slideServices.text'), text: i18n.t('intro.slideServices.text'),
view: (): React.Node => view: (): React.Node => <IntroIcon icon="view-dashboard-variant" />,
CustomIntroSlider.getIconView('view-dashboard-variant'),
mascotStyle: MASCOT_STYLE.CUTE, mascotStyle: MASCOT_STYLE.CUTE,
colors: ['#be1522', '#57080e'], colors: ['#be1522', '#57080e'],
}, },
@ -121,22 +121,12 @@ export default class CustomIntroSlider extends React.Component<
key: '4', key: '4',
title: i18n.t('intro.slideDone.title'), title: i18n.t('intro.slideDone.title'),
text: i18n.t('intro.slideDone.text'), text: i18n.t('intro.slideDone.text'),
view: (): React.Node => this.getEndView(), view: (): React.Node => <MascotIntroEnd />,
mascotStyle: MASCOT_STYLE.COOL,
colors: ['#9c165b', '#3e042b'], colors: ['#9c165b', '#3e042b'],
}, },
]; ];
// $FlowFixMe
this.updateSlides = []; this.updateSlides = new Update().getUpdateSlides();
for (let i = 0; i < Update.slidesNumber; i += 1) {
this.updateSlides.push({
key: i.toString(),
title: Update.getInstance().titleList[i],
text: Update.getInstance().descriptionList[i],
icon: Update.iconList[i],
colors: Update.colorsList[i],
});
}
this.aprilFoolsSlides = [ this.aprilFoolsSlides = [
{ {
@ -175,7 +165,7 @@ export default class CustomIntroSlider extends React.Component<
<View style={{height: '100%', flex: 1}}> <View style={{height: '100%', flex: 1}}>
<View style={{flex: 1}}>{item.view()}</View> <View style={{flex: 1}}>{item.view()}</View>
<Animatable.View useNativeDriver animation="fadeIn"> <Animatable.View useNativeDriver animation="fadeIn">
{index !== 0 && index !== this.introSlides.length - 1 ? ( {item.mascotStyle != null ? (
<Mascot <Mascot
style={{ style={{
marginLeft: 30, marginLeft: 30,
@ -244,95 +234,6 @@ export default class CustomIntroSlider extends React.Component<
); );
}; };
getEndView = (): React.Node => {
return (
<View style={{flex: 1}}>
<Mascot
style={{
...styles.center,
height: '80%',
}}
emotion={MASCOT_STYLE.COOL}
animated
entryAnimation={{
animation: 'slideInDown',
duration: 2000,
}}
loopAnimation={{
animation: 'pulse',
duration: 2000,
iterationCount: 'infinite',
}}
/>
</View>
);
};
getWelcomeView = (): React.Node => {
return (
<View style={{flex: 1}}>
<Mascot
style={{
...styles.center,
height: '80%',
}}
emotion={MASCOT_STYLE.NORMAL}
animated
entryAnimation={{
animation: 'bounceIn',
duration: 2000,
}}
/>
<Animatable.Text
useNativeDriver
animation="fadeInUp"
duration={500}
style={{
color: '#fff',
textAlign: 'center',
fontSize: 25,
}}>
PABLO
</Animatable.Text>
<Animatable.View
useNativeDriver
animation="fadeInUp"
duration={500}
delay={200}
style={{
position: 'absolute',
bottom: 30,
right: '20%',
width: 50,
height: 50,
}}>
<MaterialCommunityIcons
style={{
...styles.center,
transform: [{rotateZ: '70deg'}],
}}
name="undo"
color="#fff"
size={40}
/>
</Animatable.View>
</View>
);
};
static getIconView(icon: MaterialCommunityIconsGlyphs): React.Node {
return (
<View style={{flex: 1}}>
<Animatable.View
useNativeDriver
style={styles.center}
animation="fadeIn">
<MaterialCommunityIcons name={icon} color="#fff" size={200} />
</Animatable.View>
</View>
);
}
static setStatusBarColor(color: string) { static setStatusBarColor(color: string) {
if (Platform.OS === 'android') StatusBar.setBackgroundColor(color, true); if (Platform.OS === 'android') StatusBar.setBackgroundColor(color, true);
} }

View file

@ -1,6 +1,10 @@
// @flow // @flow
import * as React from 'react';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import type {IntroSlideType} from '../components/Overrides/CustomIntroSlider';
import MascotIntroWelcome from '../components/Intro/MascotIntroWelcome';
import IntroIcon from '../components/Intro/IconIntro';
/** /**
* Singleton used to manage update slides. * Singleton used to manage update slides.
@ -15,46 +19,33 @@ import i18n from 'i18n-js';
*/ */
export default class Update { export default class Update {
// Increment the number to show the update slide // Increment the number to show the update slide
static number = 6; static number = 7;
// Change the number of slides to display updateSlides: Array<IntroSlideType>;
static slidesNumber = 4;
// Change the icons to be displayed on the update slide
static iconList = ['star', 'clock', 'qrcode-scan', 'account'];
static colorsList = [
['#e01928', '#be1522'],
['#7c33ec', '#5e11d1'],
['#337aec', '#114ed1'],
['#e01928', '#be1522'],
];
static instance: Update | null = null;
titleList: Array<string>;
descriptionList: Array<string>;
/** /**
* Init translations * Init translations
*/ */
constructor() { constructor() {
this.titleList = []; this.updateSlides = [
this.descriptionList = []; {
for (let i = 0; i < Update.slidesNumber; i += 1) { key: '0',
this.titleList.push(i18n.t(`intro.updateSlide${i}.title`)); title: i18n.t(`intro.updateSlide0.title`),
this.descriptionList.push(i18n.t(`intro.updateSlide${i}.text`)); text: i18n.t(`intro.updateSlide0.text`),
} view: (): React.Node => <MascotIntroWelcome />,
colors: ['#be1522', '#57080e'],
},
{
key: '1',
title: i18n.t(`intro.updateSlide1.title`),
text: i18n.t(`intro.updateSlide1.text`),
view: (): React.Node => <IntroIcon icon="account-heart-outline" />,
colors: ['#9c165b', '#3e042b'],
},
];
} }
/** getUpdateSlides(): Array<IntroSlideType> {
* Get this class instance or create one if none is found return this.updateSlides;
*
* @returns {Update}
*/
static getInstance(): Update {
if (Update.instance == null) Update.instance = new Update();
return Update.instance;
} }
} }