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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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/Overrides/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('debug'),
  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('dependencies'),
  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. keyExtractor={this.keyExtractor}
  234. renderItem={this.getCardItem}
  235. />
  236. </Card.Content>
  237. </Card>
  238. );
  239. }
  240. /**
  241. * Gets the team card showing information and links about the team
  242. *
  243. * @return {*}
  244. */
  245. getTeamCard() {
  246. return (
  247. <Card style={{marginBottom: 10}}>
  248. <Card.Title
  249. title={i18n.t('aboutScreen.team')}
  250. left={(props) => <Avatar.Icon {...props} icon={'account-multiple'}/>}/>
  251. <Card.Content>
  252. <Title>{i18n.t('aboutScreen.author')}</Title>
  253. <FlatList
  254. data={this.authorData}
  255. keyExtractor={this.keyExtractor}
  256. listKey={"1"}
  257. renderItem={this.getCardItem}
  258. />
  259. <Title>{i18n.t('aboutScreen.additionalDev')}</Title>
  260. <FlatList
  261. data={this.additionalDevData}
  262. keyExtractor={this.keyExtractor}
  263. listKey={"2"}
  264. renderItem={this.getCardItem}
  265. />
  266. </Card.Content>
  267. </Card>
  268. );
  269. }
  270. /**
  271. * Gets the techno card showing information and links about the technologies used in the app
  272. *
  273. * @return {*}
  274. */
  275. getTechnoCard() {
  276. return (
  277. <Card style={{marginBottom: 10}}>
  278. <Card.Content>
  279. <Title>{i18n.t('aboutScreen.technologies')}</Title>
  280. <FlatList
  281. data={this.technoData}
  282. keyExtractor={this.keyExtractor}
  283. renderItem={this.getCardItem}
  284. />
  285. </Card.Content>
  286. </Card>
  287. );
  288. }
  289. /**
  290. * Gets a chevron icon
  291. *
  292. * @param props
  293. * @return {*}
  294. */
  295. getChevronIcon(props: Object) {
  296. return (
  297. <List.Icon {...props} icon={'chevron-right'}/>
  298. );
  299. }
  300. /**
  301. * Gets a custom list item icon
  302. *
  303. * @param item The item to show the icon for
  304. * @param props
  305. * @return {*}
  306. */
  307. getItemIcon(item: Object, props: Object) {
  308. return (
  309. <List.Icon {...props} icon={item.icon}/>
  310. );
  311. }
  312. /**
  313. * Get a clickable card item to be rendered inside a card.
  314. *
  315. * @returns {*}
  316. */
  317. getCardItem({item}: Object) {
  318. let shouldShow = item === undefined
  319. || !item.showOnlyInDebug
  320. || (item.showOnlyInDebug && this.state.isDebugUnlocked);
  321. const getItemIcon = this.getItemIcon.bind(this, item);
  322. if (shouldShow) {
  323. if (item.showChevron) {
  324. return (
  325. <List.Item
  326. title={item.text}
  327. left={getItemIcon}
  328. right={this.getChevronIcon}
  329. onPress={item.onPressCallback}
  330. />
  331. );
  332. } else {
  333. return (
  334. <List.Item
  335. title={item.text}
  336. left={getItemIcon}
  337. onPress={item.onPressCallback}
  338. />
  339. );
  340. }
  341. } else
  342. return null;
  343. }
  344. /**
  345. * Tries to unlock debug mode
  346. */
  347. tryUnlockDebugMode() {
  348. this.debugTapCounter = this.debugTapCounter + 1;
  349. if (this.debugTapCounter >= 4) {
  350. this.unlockDebugMode();
  351. }
  352. }
  353. /**
  354. * Unlocks debug mode
  355. */
  356. unlockDebugMode() {
  357. this.setState({isDebugUnlocked: true});
  358. let key = AsyncStorageManager.getInstance().preferences.debugUnlocked.key;
  359. AsyncStorageManager.getInstance().savePref(key, '1');
  360. }
  361. /**
  362. * Gets the bug report modal's content
  363. *
  364. * @return {*}
  365. */
  366. getBugReportModal() {
  367. return (
  368. <View style={{
  369. flex: 1,
  370. padding: 20
  371. }}>
  372. <Title>{i18n.t('aboutScreen.bugs')}</Title>
  373. <Text>
  374. {i18n.t('aboutScreen.bugsDescription')}
  375. </Text>
  376. <Button
  377. icon="email"
  378. mode="contained"
  379. style={{
  380. marginTop: 20,
  381. marginLeft: 'auto',
  382. marginRight: 'auto',
  383. }}
  384. onPress={this.onPressMail}>
  385. {i18n.t('aboutScreen.bugsMail')}
  386. </Button>
  387. <Button
  388. icon="git"
  389. mode="contained"
  390. style={{
  391. marginTop: 20,
  392. marginLeft: 'auto',
  393. marginRight: 'auto',
  394. }}
  395. onPress={this.onPressGit}>
  396. {i18n.t('aboutScreen.bugsGit')}
  397. </Button>
  398. </View>
  399. );
  400. }
  401. /**
  402. * opens the bug report modal
  403. */
  404. openBugReportModal() {
  405. if (this.modalRef) {
  406. this.modalRef.open();
  407. }
  408. }
  409. /**
  410. * Gets a card, depending on the given item's id
  411. *
  412. * @param item The item to show
  413. * @return {*}
  414. */
  415. getMainCard({item}: Object) {
  416. switch (item.id) {
  417. case 'app':
  418. return this.getAppCard();
  419. case 'team':
  420. return this.getTeamCard();
  421. case 'techno':
  422. return this.getTechnoCard();
  423. }
  424. return <View/>;
  425. }
  426. /**
  427. * Callback used when receiving the modal ref
  428. *
  429. * @param ref
  430. */
  431. onModalRef(ref: Object) {
  432. this.modalRef = ref;
  433. }
  434. render() {
  435. return (
  436. <View style={{padding: 5}}>
  437. <CustomModal onRef={this.onModalRef}>
  438. {this.getBugReportModal()}
  439. </CustomModal>
  440. <FlatList
  441. style={{padding: 5}}
  442. data={this.dataOrder}
  443. renderItem={this.getMainCard}
  444. />
  445. </View>
  446. );
  447. }
  448. }
  449. export default withTheme(AboutScreen);