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.

AboutScreen.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. // @flow
  2. import * as React from 'react';
  3. import {FlatList, Linking, Platform, Image, View} from 'react-native';
  4. import i18n from 'i18n-js';
  5. import {Avatar, Card, List, withTheme} from 'react-native-paper';
  6. import {StackNavigationProp} from '@react-navigation/stack';
  7. import {Modalize} from "react-native-modalize";
  8. import packageJson from '../../../package.json';
  9. import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
  10. import APP_LOGO from '../../../assets/android.icon.round.png';
  11. import type {
  12. CardTitleIconPropsType,
  13. ListIconPropsType,
  14. } from '../../constants/PaperStyles';
  15. import OptionsDialog from "../../components/Dialogs/OptionsDialog";
  16. import type {OptionsDialogButtonType} from "../../components/Dialogs/OptionsDialog";
  17. type ListItemType = {
  18. onPressCallback: () => void,
  19. icon: string,
  20. text: string,
  21. showChevron: boolean,
  22. };
  23. type AthorsItemType = {
  24. name: string,
  25. message: string,
  26. btnTrool: OptionsDialogButtonType,
  27. btnLinkedin: OptionsDialogButtonType,
  28. btnMail: OptionsDialogButtonType,
  29. };
  30. const links = {
  31. appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
  32. playstore:
  33. 'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
  34. git:
  35. 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
  36. changelog:
  37. 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
  38. license:
  39. 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
  40. arnaudMail:
  41. 'mailto:vergnet@etud.insa-toulouse.fr?' +
  42. 'subject=' +
  43. 'Application Amicale INSA Toulouse' +
  44. '&body=' +
  45. 'Coucou !\n\n',
  46. arnaudLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
  47. yohanMail:
  48. 'mailto:ysimard@etud.insa-toulouse.fr?' +
  49. 'subject=' +
  50. 'Application Amicale INSA Toulouse' +
  51. '&body=' +
  52. 'Coucou !\n\n',
  53. yohanLinkedin: 'https://www.linkedin.com/in/yohan-simard',
  54. react: 'https://facebook.github.io/react-native/',
  55. meme: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
  56. };
  57. type PropsType = {
  58. navigation: StackNavigationProp,
  59. };
  60. /**
  61. * Opens a link in the device's browser
  62. * @param link The link to open
  63. */
  64. function openWebLink(link: string) {
  65. Linking.openURL(link);
  66. }
  67. /**
  68. * Class defining an about screen. This screen shows the user information about the app and it's author.
  69. */
  70. class AboutScreen extends React.Component<PropsType> {
  71. modalRef: Modalize | null;
  72. /**
  73. * Data team
  74. */
  75. teamUsers = {
  76. arnaud: {
  77. name: 'Arnaud VERGNET',
  78. message: 'C vrément tro 1 bg !!',
  79. icon: 'account-circle',
  80. btnTrool: {
  81. title: 'SWAG',
  82. onPress: () => {
  83. openWebLink(links.meme);
  84. },
  85. },
  86. btnLinkedin: {
  87. title: 'Linkedin',
  88. onPress: () => {
  89. openWebLink(links.arnaudMail);
  90. },
  91. },
  92. btnMail: {
  93. title: i18n.t('screens.about.authorMail'),
  94. onPress: () => {
  95. openWebLink(links.arnaudLinkedin);
  96. },
  97. },
  98. },
  99. yohan: {
  100. name: 'Yohan Simard',
  101. message: 'Correction de quelques bugs',
  102. icon: 'account-circle',
  103. btnTrool: null,
  104. btnLinkedin: {
  105. title: 'Linkedin',
  106. onPress: () => {
  107. openWebLink(links.yohanLinkedin);
  108. },
  109. },
  110. btnMail: {
  111. title: i18n.t('screens.about.authorMail'),
  112. onPress: () => {
  113. openWebLink(links.yohanMail);
  114. },
  115. },
  116. },
  117. };
  118. /**
  119. * Data thanks
  120. */
  121. thanksUsers = {
  122. beranger: {
  123. name: 'Béranger Quintana Y Arciosana',
  124. message: 'Étudiant en AE (2020) et Président de l’Amicale au moment de la création et du lancement du projet. L’application, c’était son idée. Il a beaucoup aidé pour trouver des bugs, de nouvelles fonctionnalités et faire de la com.',
  125. icon: 'account-circle',
  126. btnTrool: null,
  127. btnLinkedin: null,
  128. btnMail: null,
  129. },
  130. celine: {
  131. name: 'Céline Tassin',
  132. message: 'Étudiante en GPE (2020). Sans elle, tout serait moins mignon. Elle a aidé pour écrire le texte, faire de la com, et aussi à créer la mascotte 🦊.',
  133. icon: 'account-circle',
  134. btnTrool: null,
  135. btnLinkedin: null,
  136. btnMail: null,
  137. },
  138. damien: {
  139. name: 'Damien Molina',
  140. message: 'Étudiant en IR (2020) et créateur de la dernière version du site de l’Amicale. Grâce à son aide, intégrer les services de l’Amicale à l’application a été très simple.',
  141. icon: 'account-circle',
  142. btnTrool: null,
  143. btnLinkedin: null,
  144. btnMail: null,
  145. },
  146. titouan: {
  147. name: 'Titouan Labourdette',
  148. message: 'Étudiant en AE (2020) et Président de l’Amicale au moment de la création et du lancement du projet. L’application, c’était son idée. Il a beaucoup aidé pour trouver des bugs, de nouvelles fonctionnalités et faire de la com.',
  149. icon: 'account-circle',
  150. btnTrool: null,
  151. btnLinkedin: null,
  152. btnMail: null,
  153. },
  154. theo: {
  155. name: 'Théo Tami',
  156. message: 'Étudiant en IR (2020). Il a beaucoup aidé pour trouver des bugs et proposer des nouvelles fonctionnalités.',
  157. icon: 'account-circle',
  158. btnTrool: null,
  159. btnLinkedin: null,
  160. btnMail: null,
  161. },
  162. };
  163. /**
  164. * Data to be displayed in the app card
  165. */
  166. appData = [
  167. {
  168. onPressCallback: () => {
  169. openWebLink(Platform.OS === 'ios' ? links.appstore : links.playstore);
  170. },
  171. icon: Platform.OS === 'ios' ? 'apple' : 'google-play',
  172. text:
  173. Platform.OS === 'ios'
  174. ? i18n.t('screens.about.appstore')
  175. : i18n.t('screens.about.playstore'),
  176. showChevron: true,
  177. },
  178. {
  179. onPressCallback: () => {
  180. const {navigation} = this.props;
  181. navigation.navigate('feedback');
  182. },
  183. icon: 'bug',
  184. text: i18n.t('screens.feedback.homeButtonTitle'),
  185. showChevron: true,
  186. },
  187. {
  188. onPressCallback: () => {
  189. openWebLink(links.git);
  190. },
  191. icon: 'git',
  192. text: 'Git',
  193. showChevron: true,
  194. },
  195. {
  196. onPressCallback: () => {
  197. openWebLink(links.changelog);
  198. },
  199. icon: 'refresh',
  200. text: i18n.t('screens.about.changelog'),
  201. showChevron: true,
  202. },
  203. {
  204. onPressCallback: () => {
  205. openWebLink(links.license);
  206. },
  207. icon: 'file-document',
  208. text: i18n.t('screens.about.license'),
  209. showChevron: true,
  210. },
  211. ];
  212. /**
  213. * Data to be displayed in the additional developer card
  214. */
  215. teamData = [
  216. {
  217. onPressCallback: () => {
  218. this.onListItemPress(this.teamUsers.arnaud);
  219. },
  220. icon: this.teamUsers.arnaud.icon,
  221. text: this.teamUsers.arnaud.name,
  222. showChevron: false,
  223. },
  224. {
  225. onPressCallback: () => {
  226. this.onListItemPress(this.teamUsers.yohan);
  227. },
  228. icon: this.teamUsers.yohan.icon,
  229. text: this.teamUsers.yohan.name,
  230. showChevron: false,
  231. },
  232. ];
  233. /**
  234. * Data to be displayed in the thanks card
  235. */
  236. thanksData = [
  237. {
  238. onPressCallback: () => {
  239. this.onListItemPress(this.thanksUsers.beranger);
  240. },
  241. icon: this.thanksUsers.beranger.icon,
  242. text: this.thanksUsers.beranger.name,
  243. showChevron: false,
  244. },
  245. {
  246. onPressCallback: () => {
  247. this.onListItemPress(this.thanksUsers.celine);
  248. },
  249. icon: this.thanksUsers.celine.icon,
  250. text: this.thanksUsers.celine.name,
  251. showChevron: false,
  252. },
  253. {
  254. onPressCallback: () => {
  255. this.onListItemPress(this.thanksUsers.damien);
  256. },
  257. icon: this.thanksUsers.damien.icon,
  258. text: this.thanksUsers.damien.name,
  259. showChevron: false,
  260. },
  261. {
  262. onPressCallback: () => {
  263. this.onListItemPress(this.thanksUsers.titouan);
  264. },
  265. icon: this.thanksUsers.titouan.icon,
  266. text: this.thanksUsers.titouan.name,
  267. showChevron: false,
  268. },
  269. {
  270. onPressCallback: () => {
  271. this.onListItemPress(this.thanksUsers.theo);
  272. },
  273. icon: this.thanksUsers.theo.icon,
  274. text: this.thanksUsers.theo.name,
  275. showChevron: false,
  276. },
  277. ];
  278. /**
  279. * Data to be displayed in the technologies card
  280. */
  281. technoData = [
  282. {
  283. onPressCallback: () => {
  284. openWebLink(links.react);
  285. },
  286. icon: 'react',
  287. text: i18n.t('screens.about.reactNative'),
  288. showChevron: true,
  289. },
  290. {
  291. onPressCallback: () => {
  292. const {navigation} = this.props;
  293. navigation.navigate('dependencies');
  294. },
  295. icon: 'developer-board',
  296. text: i18n.t('screens.about.libs'),
  297. showChevron: true,
  298. },
  299. ];
  300. /**
  301. * Order of information cards
  302. */
  303. dataOrder = [
  304. {
  305. id: 'app',
  306. },
  307. {
  308. id: 'team',
  309. },
  310. {
  311. id: 'thanks',
  312. },
  313. {
  314. id: 'techno',
  315. },
  316. ];
  317. constructor(props: PropsType) {
  318. super(props);
  319. this.state = {
  320. dialogVisible: false,
  321. dialogTitle: '',
  322. dialogMessage: '',
  323. dialogButtons: [],
  324. onDialogDismiss: () => {
  325. this.setState({dialogVisible: false});
  326. },
  327. };
  328. }
  329. /**
  330. * Callback used when clicking an article in the list.
  331. * It opens the modal to show detailed information about the article
  332. *
  333. * @param user TODO
  334. */
  335. onListItemPress(user: AthorsItemType) {
  336. const dialogBtn: Array<OptionsDialogButtonType> = [
  337. {
  338. title: 'OK',
  339. onPress: () => {
  340. this.onDialogDismiss();
  341. },
  342. }
  343. ];
  344. if(user.btnMail != null) {
  345. dialogBtn.push(user.btnMail);
  346. }
  347. if(user.btnLinkedin != null) {
  348. dialogBtn.push(user.btnLinkedin);
  349. }
  350. if(user.btnTrool != null) {
  351. dialogBtn.push(user.btnTrool);
  352. }
  353. this.setState({
  354. dialogVisible: true,
  355. dialogTitle: user.name,
  356. dialogMessage: user.message,
  357. dialogButtons: dialogBtn,
  358. });
  359. }
  360. /**
  361. * Gets the app card showing information and links about the app.
  362. *
  363. * @return {*}
  364. */
  365. getAppCard(): React.Node {
  366. return (
  367. <Card style={{marginBottom: 10}}>
  368. <Card.Title
  369. title="Campus"
  370. subtitle={packageJson.version}
  371. left={(iconProps: CardTitleIconPropsType): React.Node => (
  372. <Image
  373. size={iconProps.size}
  374. source={APP_LOGO}
  375. style={{width: iconProps.size, height: iconProps.size}}
  376. />
  377. )}
  378. />
  379. <Card.Content>
  380. <FlatList
  381. data={this.appData}
  382. keyExtractor={this.keyExtractor}
  383. renderItem={this.getCardItem}
  384. />
  385. </Card.Content>
  386. </Card>
  387. );
  388. }
  389. /**
  390. * Gets the team card showing information and links about the team
  391. *
  392. * @return {*}
  393. */
  394. getTeamCard(): React.Node {
  395. return (
  396. <Card style={{marginBottom: 10}}>
  397. <Card.Title
  398. title={i18n.t('screens.about.team')}
  399. left={(iconProps: CardTitleIconPropsType): React.Node => (
  400. <Avatar.Icon size={iconProps.size} icon="account-multiple" />
  401. )}
  402. />
  403. <Card.Content>
  404. <FlatList
  405. data={this.teamData}
  406. keyExtractor={this.keyExtractor}
  407. renderItem={this.getCardItem}
  408. />
  409. </Card.Content>
  410. </Card>
  411. );
  412. }
  413. /**
  414. * Gets the thanks card showing information and links about the team TODO
  415. *
  416. * @return {*}
  417. */
  418. getThanksCard(): React.Node {
  419. return (
  420. <Card style={{marginBottom: 10}}>
  421. <Card.Title
  422. title={i18n.t('screens.about.thanks')}
  423. left={(iconProps: CardTitleIconPropsType): React.Node => (
  424. <Avatar.Icon size={iconProps.size} icon="hand-heart" />
  425. )}
  426. />
  427. <Card.Content>
  428. <FlatList
  429. data={this.thanksData}
  430. keyExtractor={this.keyExtractor}
  431. renderItem={this.getCardItem}
  432. />
  433. </Card.Content>
  434. </Card>
  435. );
  436. }
  437. /**
  438. * Gets the techno card showing information and links about the technologies used in the app
  439. *
  440. * @return {*}
  441. */
  442. getTechnoCard(): React.Node {
  443. return (
  444. <Card style={{marginBottom: 10}}>
  445. <Card.Title
  446. title={i18n.t('screens.about.technologies')}
  447. left={(iconProps: CardTitleIconPropsType): React.Node => (
  448. <Avatar.Icon size={iconProps.size} icon="build" />
  449. )}
  450. />
  451. <Card.Content>
  452. <FlatList
  453. data={this.technoData}
  454. keyExtractor={this.keyExtractor}
  455. renderItem={this.getCardItem}
  456. />
  457. </Card.Content>
  458. </Card>
  459. );
  460. }
  461. /**
  462. * Gets a chevron icon
  463. *
  464. * @param props
  465. * @return {*}
  466. */
  467. static getChevronIcon(props: ListIconPropsType): React.Node {
  468. return (
  469. <List.Icon color={props.color} style={props.style} icon="chevron-right" />
  470. );
  471. }
  472. /**
  473. * Gets a custom list item icon
  474. *
  475. * @param item The item to show the icon for
  476. * @param props
  477. * @return {*}
  478. */
  479. static getItemIcon(item: ListItemType, props: ListIconPropsType): React.Node {
  480. return (
  481. <List.Icon color={props.color} style={props.style} icon={item.icon} />
  482. );
  483. }
  484. /**
  485. * Gets a clickable card item to be rendered inside a card.
  486. *
  487. * @returns {*}
  488. */
  489. getCardItem = ({item}: {item: ListItemType}): React.Node => {
  490. const getItemIcon = (props: ListIconPropsType): React.Node =>
  491. AboutScreen.getItemIcon(item, props);
  492. if (item.showChevron) {
  493. return (
  494. <List.Item
  495. title={item.text}
  496. left={getItemIcon}
  497. right={AboutScreen.getChevronIcon}
  498. onPress={item.onPressCallback}
  499. />
  500. );
  501. }
  502. return (
  503. <List.Item
  504. title={item.text}
  505. left={getItemIcon}
  506. onPress={item.onPressCallback}
  507. />
  508. );
  509. };
  510. /**
  511. * Gets a card, depending on the given item's id
  512. *
  513. * @param item The item to show
  514. * @return {*}
  515. */
  516. getMainCard = ({item}: {item: {id: string}}): React.Node => {
  517. switch (item.id) {
  518. case 'app':
  519. return this.getAppCard();
  520. case 'team':
  521. return this.getTeamCard();
  522. case 'thanks':
  523. return this.getThanksCard();
  524. case 'techno':
  525. return this.getTechnoCard();
  526. default:
  527. return null;
  528. }
  529. };
  530. /**
  531. * Extracts a key from the given item
  532. *
  533. * @param item The item to extract the key from
  534. * @return {string} The extracted key
  535. */
  536. keyExtractor = (item: ListItemType): string => item.icon;
  537. render(): React.Node {
  538. const {state} = this;
  539. return (
  540. <View
  541. style={{
  542. height: '100%',
  543. }}>
  544. <CollapsibleFlatList
  545. style={{padding: 5}}
  546. data={this.dataOrder}
  547. renderItem={this.getMainCard}
  548. />
  549. <OptionsDialog
  550. visible={state.dialogVisible}
  551. title={state.dialogTitle}
  552. message={state.dialogMessage}
  553. buttons={state.dialogButtons}
  554. onDismiss={state.onDialogDismiss}
  555. />
  556. </View>
  557. );
  558. }
  559. }
  560. export default withTheme(AboutScreen);