Application Android et IOS pour l'amicale des élèves https://play.google.com/store/apps/details?id=fr.amicaleinsat.application
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 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // @flow
  2. import * as React from 'react';
  3. import {FlatList, Linking, Platform, View} from 'react-native';
  4. import i18n from "i18n-js";
  5. import appJson from '../../app';
  6. import AsyncStorageManager from "../../managers/AsyncStorageManager";
  7. import CustomModal from "../../components/CustomModal";
  8. import {Avatar, Button, Card, List, Text, Title, withTheme} from 'react-native-paper';
  9. const links = {
  10. appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
  11. playstore: 'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
  12. git: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
  13. bugsMail: 'mailto:vergnet@etud.insa-toulouse.fr?' +
  14. 'subject=' +
  15. '[BUG] Application Amicale INSA Toulouse' +
  16. '&body=' +
  17. 'Coucou Arnaud ça bug c\'est nul,\n\n' +
  18. 'Informations sur ton système si tu sais (iOS ou Android, modèle du tel, version):\n\n\n' +
  19. 'Nature du problème :\n\n\n' +
  20. 'Étapes pour reproduire ce pb :\n\n\n\n' +
  21. 'Stp corrige le pb, bien cordialement.',
  22. bugsGit: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/issues',
  23. changelog: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
  24. license: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
  25. authorMail: "mailto:vergnet@etud.insa-toulouse.fr?" +
  26. "subject=" +
  27. "Application Amicale INSA Toulouse" +
  28. "&body=" +
  29. "Coucou !\n\n",
  30. authorLinkedin: 'https://www.linkedin.com/in/arnaud-vergnet-434ba5179/',
  31. yohanMail: "mailto:ysimard@etud.insa-toulouse.fr?" +
  32. "subject=" +
  33. "Application Amicale INSA Toulouse" +
  34. "&body=" +
  35. "Coucou !\n\n",
  36. yohanLinkedin: 'https://www.linkedin.com/in/yohan-simard',
  37. react: 'https://facebook.github.io/react-native/',
  38. };
  39. type Props = {
  40. navigation: Object,
  41. };
  42. type State = {
  43. isDebugUnlocked: boolean,
  44. };
  45. /**
  46. * Opens a link in the device's browser
  47. * @param link The link to open
  48. */
  49. function openWebLink(link) {
  50. Linking.openURL(link).catch((err) => console.error('Error opening link', err));
  51. }
  52. /**
  53. * Class defining an about screen. This screen shows the user information about the app and it's author.
  54. */
  55. class AboutScreen extends React.Component<Props, State> {
  56. debugTapCounter = 0;
  57. modalRef: Object;
  58. state = {
  59. isDebugUnlocked: AsyncStorageManager.getInstance().preferences.debugUnlocked.current === '1'
  60. };
  61. /**
  62. * Data to be displayed in the app card
  63. */
  64. appData: Array<Object> = [
  65. {
  66. onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore),
  67. icon: Platform.OS === "ios" ? 'apple' : 'google-play',
  68. text: Platform.OS === "ios" ? i18n.t('aboutScreen.appstore') : i18n.t('aboutScreen.playstore'),
  69. showChevron: true
  70. },
  71. {
  72. onPressCallback: () => this.openBugReportModal(),
  73. icon: 'bug',
  74. text: i18n.t('aboutScreen.bugs'),
  75. showChevron: true
  76. },
  77. {
  78. onPressCallback: () => openWebLink(links.git),
  79. icon: 'git',
  80. text: 'Git',
  81. showChevron: true
  82. },
  83. {
  84. onPressCallback: () => openWebLink(links.changelog),
  85. icon: 'refresh',
  86. text: i18n.t('aboutScreen.changelog'),
  87. showChevron: true
  88. },
  89. {
  90. onPressCallback: () => openWebLink(links.license),
  91. icon: 'file-document',
  92. text: i18n.t('aboutScreen.license'),
  93. showChevron: true
  94. },
  95. {
  96. onPressCallback: () => this.props.navigation.navigate('DebugScreen'),
  97. icon: 'bug-check',
  98. text: i18n.t('aboutScreen.debug'),
  99. showChevron: true,
  100. showOnlyInDebug: true
  101. },
  102. ];
  103. /**
  104. * Data to be displayed in the author card
  105. */
  106. authorData: Array<Object> = [
  107. {
  108. onPressCallback: () => this.tryUnlockDebugMode(),
  109. icon: 'account-circle',
  110. text: 'Arnaud VERGNET',
  111. showChevron: false
  112. },
  113. {
  114. onPressCallback: () => openWebLink(links.authorMail),
  115. icon: 'email',
  116. text: i18n.t('aboutScreen.authorMail'),
  117. showChevron: true
  118. },
  119. {
  120. onPressCallback: () => openWebLink(links.authorLinkedin),
  121. icon: 'linkedin',
  122. text: 'Linkedin',
  123. showChevron: true
  124. },
  125. ];
  126. /**
  127. * Data to be displayed in the additional developer card
  128. */
  129. additionalDevData: Array<Object> = [
  130. {
  131. onPressCallback: () => console.log('Meme this'),
  132. icon: 'account',
  133. text: 'Yohan SIMARD',
  134. showChevron: false
  135. },
  136. {
  137. onPressCallback: () => openWebLink(links.yohanMail),
  138. icon: 'email',
  139. text: i18n.t('aboutScreen.authorMail'),
  140. showChevron: true
  141. },
  142. {
  143. onPressCallback: () => openWebLink(links.yohanLinkedin),
  144. icon: 'linkedin',
  145. text: 'Linkedin',
  146. showChevron: true
  147. },
  148. ];
  149. /**
  150. * Data to be displayed in the technologies card
  151. */
  152. technoData: Array<Object> = [
  153. {
  154. onPressCallback: () => openWebLink(links.react),
  155. icon: 'react',
  156. text: i18n.t('aboutScreen.reactNative'),
  157. showChevron: true
  158. },
  159. {
  160. onPressCallback: () => this.props.navigation.navigate('AboutDependenciesScreen'),
  161. icon: 'developer-board',
  162. text: i18n.t('aboutScreen.libs'),
  163. showChevron: true
  164. },
  165. ];
  166. /**
  167. * Order of information cards
  168. */
  169. dataOrder: Array<Object> = [
  170. {
  171. id: 'app',
  172. },
  173. {
  174. id: 'team',
  175. },
  176. {
  177. id: 'techno',
  178. },
  179. ];
  180. getCardItem: Function;
  181. getMainCard: Function;
  182. onModalRef: Function;
  183. onPressMail: Function;
  184. onPressGit: Function;
  185. colors: Object;
  186. constructor(props) {
  187. super(props);
  188. this.getCardItem = this.getCardItem.bind(this);
  189. this.getMainCard = this.getMainCard.bind(this);
  190. this.onModalRef = this.onModalRef.bind(this);
  191. this.onPressMail = openWebLink.bind(this, links.bugsMail);
  192. this.onPressGit = openWebLink.bind(this, links.bugsGit);
  193. this.colors = props.theme.colors;
  194. }
  195. /**
  196. * Gets the app icon
  197. * @param props
  198. * @return {*}
  199. */
  200. getAppIcon(props) {
  201. return (
  202. <Avatar.Image
  203. {...props}
  204. source={require('../../assets/android.icon.png')}
  205. style={{backgroundColor: 'transparent'}}
  206. />
  207. );
  208. }
  209. /**
  210. * Extracts a key from the given item
  211. *
  212. * @param item The item to extract the key from
  213. * @return {string} The extracted key
  214. */
  215. keyExtractor(item: Object): string {
  216. return item.icon;
  217. }
  218. /**
  219. * Gets the app card showing information and links about the app.
  220. *
  221. * @return {*}
  222. */
  223. getAppCard() {
  224. return (
  225. <Card style={{marginBottom: 10}}>
  226. <Card.Title
  227. title={appJson.expo.name}
  228. subtitle={appJson.expo.version}
  229. left={this.getAppIcon}/>
  230. <Card.Content>
  231. <FlatList
  232. data={this.appData}
  233. extraData={this.state}
  234. keyExtractor={this.keyExtractor}
  235. listKey={"app"}
  236. renderItem={this.getCardItem}
  237. />
  238. </Card.Content>
  239. </Card>
  240. );
  241. }
  242. /**
  243. * Gets the team card showing information and links about the team
  244. *
  245. * @return {*}
  246. */
  247. getTeamCard() {
  248. return (
  249. <Card style={{marginBottom: 10}}>
  250. <Card.Title
  251. title={i18n.t('aboutScreen.team')}
  252. left={(props) => <Avatar.Icon {...props} icon={'account-multiple'}/>}/>
  253. <Card.Content>
  254. <Title>{i18n.t('aboutScreen.author')}</Title>
  255. <FlatList
  256. data={this.authorData}
  257. extraData={this.state}
  258. keyExtractor={this.keyExtractor}
  259. listKey={"team1"}
  260. renderItem={this.getCardItem}
  261. />
  262. <Title>{i18n.t('aboutScreen.additionalDev')}</Title>
  263. <FlatList
  264. data={this.additionalDevData}
  265. extraData={this.state}
  266. keyExtractor={this.keyExtractor}
  267. listKey={"team2"}
  268. renderItem={this.getCardItem}
  269. />
  270. </Card.Content>
  271. </Card>
  272. );
  273. }
  274. /**
  275. * Gets the techno card showing information and links about the technologies used in the app
  276. *
  277. * @return {*}
  278. */
  279. getTechnoCard() {
  280. return (
  281. <Card style={{marginBottom: 10}}>
  282. <Card.Content>
  283. <Title>{i18n.t('aboutScreen.technologies')}</Title>
  284. <FlatList
  285. data={this.technoData}
  286. extraData={this.state}
  287. keyExtractor={this.keyExtractor}
  288. listKey={"techno"}
  289. renderItem={this.getCardItem}
  290. />
  291. </Card.Content>
  292. </Card>
  293. );
  294. }
  295. /**
  296. * Gets a chevron icon
  297. *
  298. * @param props
  299. * @return {*}
  300. */
  301. getChevronIcon(props: Object) {
  302. return (
  303. <List.Icon {...props} icon={'chevron-right'}/>
  304. );
  305. }
  306. /**
  307. * Gets a custom list item icon
  308. *
  309. * @param item The item to show the icon for
  310. * @param props
  311. * @return {*}
  312. */
  313. getItemIcon(item: Object, props: Object) {
  314. return (
  315. <List.Icon {...props} icon={item.icon}/>
  316. );
  317. }
  318. /**
  319. * Get a clickable card item to be rendered inside a card.
  320. *
  321. * @returns {*}
  322. */
  323. getCardItem({item}: Object) {
  324. let shouldShow = item === undefined
  325. || !item.showOnlyInDebug
  326. || (item.showOnlyInDebug && this.state.isDebugUnlocked);
  327. const getItemIcon = this.getItemIcon.bind(this, item);
  328. if (shouldShow) {
  329. if (item.showChevron) {
  330. return (
  331. <List.Item
  332. title={item.text}
  333. left={getItemIcon}
  334. right={this.getChevronIcon}
  335. onPress={item.onPressCallback}
  336. />
  337. );
  338. } else {
  339. return (
  340. <List.Item
  341. title={item.text}
  342. left={getItemIcon}
  343. onPress={item.onPressCallback}
  344. />
  345. );
  346. }
  347. } else
  348. return null;
  349. }
  350. /**
  351. * Tries to unlock debug mode
  352. */
  353. tryUnlockDebugMode() {
  354. this.debugTapCounter = this.debugTapCounter + 1;
  355. if (this.debugTapCounter >= 4) {
  356. this.unlockDebugMode();
  357. }
  358. }
  359. /**
  360. * Unlocks debug mode
  361. */
  362. unlockDebugMode() {
  363. this.setState({isDebugUnlocked: true});
  364. let key = AsyncStorageManager.getInstance().preferences.debugUnlocked.key;
  365. AsyncStorageManager.getInstance().savePref(key, '1');
  366. }
  367. /**
  368. * Gets the bug report modal's content
  369. *
  370. * @return {*}
  371. */
  372. getBugReportModal() {
  373. return (
  374. <View style={{
  375. flex: 1,
  376. padding: 20
  377. }}>
  378. <Title>{i18n.t('aboutScreen.bugs')}</Title>
  379. <Text>
  380. {i18n.t('aboutScreen.bugsDescription')}
  381. </Text>
  382. <Button
  383. icon="email"
  384. mode="contained"
  385. dark={true}
  386. color={this.colors.primary}
  387. style={{
  388. marginTop: 20,
  389. marginLeft: 'auto',
  390. marginRight: 'auto',
  391. }}
  392. onPress={this.onPressMail}>
  393. <Text>{i18n.t('aboutScreen.bugsMail')}</Text>
  394. </Button>
  395. <Button
  396. icon="git"
  397. mode="contained"
  398. dark={true}
  399. color={this.colors.primary}
  400. style={{
  401. marginTop: 20,
  402. marginLeft: 'auto',
  403. marginRight: 'auto',
  404. }}
  405. onPress={this.onPressGit}>
  406. <Text>{i18n.t('aboutScreen.bugsGit')}</Text>
  407. </Button>
  408. </View>
  409. );
  410. }
  411. /**
  412. * opens the bug report modal
  413. */
  414. openBugReportModal() {
  415. if (this.modalRef) {
  416. this.modalRef.open();
  417. }
  418. }
  419. /**
  420. * Gets a card, depending on the given item's id
  421. *
  422. * @param item The item to show
  423. * @return {*}
  424. */
  425. getMainCard({item}: Object) {
  426. switch (item.id) {
  427. case 'app':
  428. return this.getAppCard();
  429. case 'team':
  430. return this.getTeamCard();
  431. case 'techno':
  432. return this.getTechnoCard();
  433. }
  434. return <View/>;
  435. }
  436. /**
  437. * Callback used when receiving the modal ref
  438. *
  439. * @param ref
  440. */
  441. onModalRef(ref: Object) {
  442. this.modalRef = ref;
  443. }
  444. render() {
  445. return (
  446. <View style={{padding: 5}}>
  447. <CustomModal onRef={this.onModalRef}>
  448. {this.getBugReportModal()}
  449. </CustomModal>
  450. <FlatList
  451. style={{padding: 5}}
  452. data={this.dataOrder}
  453. extraData={this.state}
  454. keyExtractor={(item) => item.id}
  455. renderItem={this.getMainCard}
  456. />
  457. </View>
  458. );
  459. }
  460. }
  461. export default withTheme(AboutScreen);