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

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