Application Android et IOS pour l'amicale des élèves
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CustomIntroSlider.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. // @flow
  2. import * as React from 'react';
  3. import {Platform, StatusBar, StyleSheet, View} from 'react-native';
  4. import type {MaterialCommunityIconsGlyphs} from 'react-native-vector-icons/MaterialCommunityIcons';
  5. import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
  6. import i18n from 'i18n-js';
  7. import AppIntroSlider from 'react-native-app-intro-slider';
  8. import LinearGradient from 'react-native-linear-gradient';
  9. import * as Animatable from 'react-native-animatable';
  10. import {Card} from 'react-native-paper';
  11. import Update from '../../constants/Update';
  12. import ThemeManager from '../../managers/ThemeManager';
  13. import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot';
  14. type PropsType = {
  15. onDone: () => void,
  16. isUpdate: boolean,
  17. isAprilFools: boolean,
  18. };
  19. type StateType = {
  20. currentSlide: number,
  21. };
  22. type IntroSlideType = {
  23. key: string,
  24. title: string,
  25. text: string,
  26. view: () => React.Node,
  27. mascotStyle: number,
  28. colors: [string, string],
  29. };
  30. const styles = StyleSheet.create({
  31. mainContent: {
  32. paddingBottom: 100,
  33. },
  34. text: {
  35. color: 'rgba(255, 255, 255, 0.8)',
  36. backgroundColor: 'transparent',
  37. textAlign: 'center',
  38. paddingHorizontal: 16,
  39. },
  40. title: {
  41. fontSize: 22,
  42. color: 'white',
  43. backgroundColor: 'transparent',
  44. textAlign: 'center',
  45. marginBottom: 16,
  46. },
  47. center: {
  48. marginTop: 'auto',
  49. marginBottom: 'auto',
  50. marginRight: 'auto',
  51. marginLeft: 'auto',
  52. },
  53. });
  54. /**
  55. * Class used to create intro slides
  56. */
  57. export default class CustomIntroSlider extends React.Component<
  58. PropsType,
  59. StateType,
  60. > {
  61. sliderRef: {current: null | AppIntroSlider};
  62. introSlides: Array<IntroSlideType>;
  63. updateSlides: Array<IntroSlideType>;
  64. aprilFoolsSlides: Array<IntroSlideType>;
  65. currentSlides: Array<IntroSlideType>;
  66. /**
  67. * Generates intro slides
  68. */
  69. constructor() {
  70. super();
  71. this.state = {
  72. currentSlide: 0,
  73. };
  74. this.sliderRef = React.createRef();
  75. this.introSlides = [
  76. {
  77. key: '0', // Mascot
  78. title: i18n.t('intro.slideMain.title'),
  79. text: i18n.t('intro.slideMain.text'),
  80. view: this.getWelcomeView,
  81. mascotStyle: MASCOT_STYLE.NORMAL,
  82. colors: ['#be1522', '#57080e'],
  83. },
  84. {
  85. key: '1',
  86. title: i18n.t('intro.slidePlanex.title'),
  87. text: i18n.t('intro.slidePlanex.text'),
  88. view: (): React.Node => CustomIntroSlider.getIconView('calendar-clock'),
  89. mascotStyle: MASCOT_STYLE.INTELLO,
  90. colors: ['#be1522', '#57080e'],
  91. },
  92. {
  93. key: '2',
  94. title: i18n.t('intro.slideEvents.title'),
  95. text: i18n.t('intro.slideEvents.text'),
  96. view: (): React.Node => CustomIntroSlider.getIconView('calendar-star'),
  97. mascotStyle: MASCOT_STYLE.HAPPY,
  98. colors: ['#be1522', '#57080e'],
  99. },
  100. {
  101. key: '3',
  102. title: i18n.t('intro.slideServices.title'),
  103. text: i18n.t('intro.slideServices.text'),
  104. view: (): React.Node =>
  105. CustomIntroSlider.getIconView('view-dashboard-variant'),
  106. mascotStyle: MASCOT_STYLE.CUTE,
  107. colors: ['#be1522', '#57080e'],
  108. },
  109. {
  110. key: '4',
  111. title: i18n.t('intro.slideDone.title'),
  112. text: i18n.t('intro.slideDone.text'),
  113. view: (): React.Node => this.getEndView(),
  114. mascotStyle: MASCOT_STYLE.COOL,
  115. colors: ['#9c165b', '#3e042b'],
  116. },
  117. ];
  118. // $FlowFixMe
  119. this.updateSlides = [];
  120. for (let i = 0; i < Update.slidesNumber; i += 1) {
  121. this.updateSlides.push({
  122. key: i.toString(),
  123. title: Update.getInstance().titleList[i],
  124. text: Update.getInstance().descriptionList[i],
  125. icon: Update.iconList[i],
  126. colors: Update.colorsList[i],
  127. });
  128. }
  129. this.aprilFoolsSlides = [
  130. {
  131. key: '1',
  132. title: i18n.t('intro.aprilFoolsSlide.title'),
  133. text: i18n.t('intro.aprilFoolsSlide.text'),
  134. view: (): React.Node => <View />,
  135. mascotStyle: MASCOT_STYLE.NORMAL,
  136. colors: ['#e01928', '#be1522'],
  137. },
  138. ];
  139. }
  140. /**
  141. * Render item to be used for the intro introSlides
  142. *
  143. * @param item The item to be displayed
  144. * @param dimensions Dimensions of the item
  145. */
  146. getIntroRenderItem = ({
  147. item,
  148. dimensions,
  149. }: {
  150. item: IntroSlideType,
  151. dimensions: {width: number, height: number},
  152. }): React.Node => {
  153. const {state} = this;
  154. const index = parseInt(item.key, 10);
  155. return (
  156. <LinearGradient
  157. style={[styles.mainContent, dimensions]}
  158. colors={item.colors}
  159. start={{x: 0, y: 0.1}}
  160. end={{x: 0.1, y: 1}}>
  161. {state.currentSlide === index ? (
  162. <View style={{height: '100%', flex: 1}}>
  163. <View style={{flex: 1}}>{item.view()}</View>
  164. <Animatable.View useNativeDriver animation="fadeIn">
  165. {index !== 0 && index !== this.introSlides.length - 1 ? (
  166. <Mascot
  167. style={{
  168. marginLeft: 30,
  169. marginBottom: 0,
  170. width: 100,
  171. marginTop: -30,
  172. }}
  173. emotion={item.mascotStyle}
  174. animated
  175. entryAnimation={{
  176. animation: 'slideInLeft',
  177. duration: 500,
  178. }}
  179. loopAnimation={{
  180. animation: 'pulse',
  181. iterationCount: 'infinite',
  182. duration: 2000,
  183. }}
  184. />
  185. ) : null}
  186. <View
  187. style={{
  188. marginLeft: 50,
  189. width: 0,
  190. height: 0,
  191. borderLeftWidth: 20,
  192. borderRightWidth: 0,
  193. borderBottomWidth: 20,
  194. borderStyle: 'solid',
  195. backgroundColor: 'transparent',
  196. borderLeftColor: 'transparent',
  197. borderRightColor: 'transparent',
  198. borderBottomColor: 'rgba(0,0,0,0.60)',
  199. }}
  200. />
  201. <Card
  202. style={{
  203. backgroundColor: 'rgba(0,0,0,0.38)',
  204. marginHorizontal: 20,
  205. borderColor: 'rgba(0,0,0,0.60)',
  206. borderWidth: 4,
  207. borderRadius: 10,
  208. elevation: 0,
  209. }}>
  210. <Card.Content>
  211. <Animatable.Text
  212. useNativeDriver
  213. animation="fadeIn"
  214. delay={100}
  215. style={styles.title}>
  216. {item.title}
  217. </Animatable.Text>
  218. <Animatable.Text
  219. useNativeDriver
  220. animation="fadeIn"
  221. delay={200}
  222. style={styles.text}>
  223. {item.text}
  224. </Animatable.Text>
  225. </Card.Content>
  226. </Card>
  227. </Animatable.View>
  228. </View>
  229. ) : null}
  230. </LinearGradient>
  231. );
  232. };
  233. getEndView = (): React.Node => {
  234. return (
  235. <View style={{flex: 1}}>
  236. <Mascot
  237. style={{
  238. ...styles.center,
  239. height: '80%',
  240. }}
  241. emotion={MASCOT_STYLE.COOL}
  242. animated
  243. entryAnimation={{
  244. animation: 'slideInDown',
  245. duration: 2000,
  246. }}
  247. loopAnimation={{
  248. animation: 'pulse',
  249. duration: 2000,
  250. iterationCount: 'infinite',
  251. }}
  252. />
  253. </View>
  254. );
  255. };
  256. getWelcomeView = (): React.Node => {
  257. return (
  258. <View style={{flex: 1}}>
  259. <Mascot
  260. style={{
  261. ...styles.center,
  262. height: '80%',
  263. }}
  264. emotion={MASCOT_STYLE.NORMAL}
  265. animated
  266. entryAnimation={{
  267. animation: 'bounceIn',
  268. duration: 2000,
  269. }}
  270. />
  271. <Animatable.Text
  272. useNativeDriver
  273. animation="fadeInUp"
  274. duration={500}
  275. style={{
  276. color: '#fff',
  277. textAlign: 'center',
  278. fontSize: 25,
  279. }}>
  280. PABLO
  281. </Animatable.Text>
  282. <Animatable.View
  283. useNativeDriver
  284. animation="fadeInUp"
  285. duration={500}
  286. delay={200}
  287. style={{
  288. position: 'absolute',
  289. bottom: 30,
  290. right: '20%',
  291. width: 50,
  292. height: 50,
  293. }}>
  294. <MaterialCommunityIcons
  295. style={{
  296. ...styles.center,
  297. transform: [{rotateZ: '70deg'}],
  298. }}
  299. name="undo"
  300. color="#fff"
  301. size={40}
  302. />
  303. </Animatable.View>
  304. </View>
  305. );
  306. };
  307. static getIconView(icon: MaterialCommunityIconsGlyphs): React.Node {
  308. return (
  309. <View style={{flex: 1}}>
  310. <Animatable.View
  311. useNativeDriver
  312. style={styles.center}
  313. animation="fadeIn">
  314. <MaterialCommunityIcons name={icon} color="#fff" size={200} />
  315. </Animatable.View>
  316. </View>
  317. );
  318. }
  319. static setStatusBarColor(color: string) {
  320. if (Platform.OS === 'android') StatusBar.setBackgroundColor(color, true);
  321. }
  322. onSlideChange = (index: number) => {
  323. CustomIntroSlider.setStatusBarColor(this.currentSlides[index].colors[0]);
  324. this.setState({currentSlide: index});
  325. };
  326. onSkip = () => {
  327. CustomIntroSlider.setStatusBarColor(
  328. this.currentSlides[this.currentSlides.length - 1].colors[0],
  329. );
  330. if (this.sliderRef.current != null)
  331. this.sliderRef.current.goToSlide(this.currentSlides.length - 1);
  332. };
  333. onDone = () => {
  334. const {props} = this;
  335. CustomIntroSlider.setStatusBarColor(
  336. ThemeManager.getCurrentTheme().colors.surface,
  337. );
  338. props.onDone();
  339. };
  340. getRenderNextButton = (): React.Node => {
  341. return (
  342. <Animatable.View
  343. useNativeDriver
  344. animation="fadeIn"
  345. style={{
  346. borderRadius: 25,
  347. padding: 5,
  348. backgroundColor: 'rgba(0,0,0,0.2)',
  349. }}>
  350. <MaterialCommunityIcons name="arrow-right" color="#fff" size={40} />
  351. </Animatable.View>
  352. );
  353. };
  354. getRenderDoneButton = (): React.Node => {
  355. return (
  356. <Animatable.View
  357. useNativeDriver
  358. animation="bounceIn"
  359. style={{
  360. borderRadius: 25,
  361. padding: 5,
  362. backgroundColor: 'rgb(190,21,34)',
  363. }}>
  364. <MaterialCommunityIcons name="check" color="#fff" size={40} />
  365. </Animatable.View>
  366. );
  367. };
  368. render(): React.Node {
  369. const {props, state} = this;
  370. this.currentSlides = this.introSlides;
  371. if (props.isUpdate) this.currentSlides = this.updateSlides;
  372. else if (props.isAprilFools) this.currentSlides = this.aprilFoolsSlides;
  373. CustomIntroSlider.setStatusBarColor(this.currentSlides[0].colors[0]);
  374. return (
  375. <AppIntroSlider
  376. ref={this.sliderRef}
  377. data={this.currentSlides}
  378. extraData={state.currentSlide}
  379. renderItem={this.getIntroRenderItem}
  380. renderNextButton={this.getRenderNextButton}
  381. renderDoneButton={this.getRenderDoneButton}
  382. onDone={this.onDone}
  383. onSlideChange={this.onSlideChange}
  384. onSkip={this.onSkip}
  385. />
  386. );
  387. }
  388. }