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.

ProfileScreen.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. // @flow
  2. import * as React from 'react';
  3. import {Animated, FlatList, StyleSheet, View} from "react-native";
  4. import {Avatar, Button, Card, Divider, List, Paragraph, withTheme} from 'react-native-paper';
  5. import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
  6. import i18n from 'i18n-js';
  7. import LogoutDialog from "../../components/Amicale/LogoutDialog";
  8. import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
  9. import CustomTabBar from "../../components/Tabbar/CustomTabBar";
  10. import {Collapsible} from "react-navigation-collapsible";
  11. import {withCollapsible} from "../../utils/withCollapsible";
  12. import type {cardList} from "../../components/Lists/CardList/CardList";
  13. import CardList from "../../components/Lists/CardList/CardList";
  14. type Props = {
  15. navigation: Object,
  16. theme: Object,
  17. collapsibleStack: Collapsible,
  18. }
  19. type State = {
  20. dialogVisible: boolean,
  21. }
  22. const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png";
  23. const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png";
  24. const ICON_AMICALE = require('../../../assets/amicale.png');
  25. class ProfileScreen extends React.Component<Props, State> {
  26. state = {
  27. dialogVisible: false,
  28. };
  29. data: Object;
  30. flatListData: Array<Object>;
  31. amicaleDataset: cardList;
  32. constructor() {
  33. super();
  34. this.flatListData = [
  35. {id: '0'},
  36. {id: '1'},
  37. {id: '2'},
  38. {id: '3'},
  39. ]
  40. this.amicaleDataset = [
  41. {
  42. title: i18n.t('screens.clubsAbout'),
  43. subtitle: i18n.t('servicesScreen.descriptions.clubs'),
  44. image: CLUBS_IMAGE,
  45. onPress: () => this.props.navigation.navigate("club-list"),
  46. },
  47. {
  48. title: i18n.t('screens.vote'),
  49. subtitle: i18n.t('servicesScreen.descriptions.vote'),
  50. image: VOTE_IMAGE,
  51. onPress: () => this.props.navigation.navigate("vote"),
  52. },
  53. {
  54. title: i18n.t('screens.amicaleWebsite'),
  55. subtitle: i18n.t('servicesScreen.descriptions.amicaleWebsite'),
  56. image: ICON_AMICALE,
  57. onPress: () => this.props.navigation.navigate("amicale-website"),
  58. },
  59. ];
  60. }
  61. componentDidMount() {
  62. this.props.navigation.setOptions({
  63. headerRight: this.getHeaderButton,
  64. });
  65. }
  66. showDisconnectDialog = () => this.setState({dialogVisible: true});
  67. hideDisconnectDialog = () => this.setState({dialogVisible: false});
  68. getHeaderButton = () => <MaterialHeaderButtons>
  69. <Item title="logout" iconName="logout" onPress={this.showDisconnectDialog}/>
  70. </MaterialHeaderButtons>;
  71. getScreen = (data: Object) => {
  72. this.data = data[0];
  73. const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
  74. return (
  75. <View style={{flex: 1}}>
  76. <Animated.FlatList
  77. renderItem={this.getRenderItem}
  78. data={this.flatListData}
  79. // Animations
  80. onScroll={onScroll}
  81. contentContainerStyle={{
  82. paddingTop: containerPaddingTop,
  83. paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
  84. minHeight: '100%'
  85. }}
  86. scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
  87. />
  88. <LogoutDialog
  89. {...this.props}
  90. visible={this.state.dialogVisible}
  91. onDismiss={this.hideDisconnectDialog}
  92. />
  93. </View>
  94. )
  95. };
  96. getRenderItem = ({item}: Object) => {
  97. switch (item.id) {
  98. case '0':
  99. return this.getWelcomeCard();
  100. case '1':
  101. return this.getPersonalCard();
  102. case '2':
  103. return this.getClubCard();
  104. default:
  105. return this.getMembershipCar();
  106. }
  107. };
  108. getServicesList() {
  109. return (
  110. <CardList
  111. dataset={this.amicaleDataset}
  112. isHorizontal={true}
  113. />
  114. );
  115. }
  116. getWelcomeCard() {
  117. return (
  118. <Card style={styles.card}>
  119. <Card.Title
  120. title={i18n.t("profileScreen.welcomeTitle", {name: this.data.first_name})}
  121. left={(props) => <Avatar.Image
  122. size={64}
  123. source={ICON_AMICALE}
  124. style={{backgroundColor: 'transparent',}}
  125. />}
  126. titleStyle={{marginLeft: 10}}
  127. />
  128. <Card.Content>
  129. <Divider/>
  130. <Paragraph>
  131. {i18n.t("profileScreen.welcomeDescription")}
  132. </Paragraph>
  133. {this.getServicesList()}
  134. <Paragraph>
  135. {i18n.t("profileScreen.welcomeFeedback")}
  136. </Paragraph>
  137. <Divider/>
  138. <Card.Actions>
  139. <Button
  140. icon="bug"
  141. mode="contained"
  142. onPress={() => this.props.navigation.navigate('feedback')}
  143. style={styles.editButton}>
  144. {i18n.t("feedbackScreen.homeButtonTitle")}
  145. </Button>
  146. </Card.Actions>
  147. </Card.Content>
  148. </Card>
  149. );
  150. }
  151. /**
  152. * Checks if the given field is available
  153. *
  154. * @param field The field to check
  155. * @return {boolean}
  156. */
  157. isFieldAvailable(field: ?string) {
  158. return field !== null;
  159. }
  160. /**
  161. * Gets the given field value.
  162. * If the field does not have a value, returns a placeholder text
  163. *
  164. * @param field The field to get the value from
  165. * @return {*}
  166. */
  167. getFieldValue(field: ?string) {
  168. return this.isFieldAvailable(field)
  169. ? field
  170. : i18n.t("profileScreen.noData");
  171. }
  172. /**
  173. * Gets a list item showing personal information
  174. *
  175. * @param field The field to display
  176. * @param icon The icon to use
  177. * @return {*}
  178. */
  179. getPersonalListItem(field: ?string, icon: string) {
  180. let title = this.isFieldAvailable(field) ? this.getFieldValue(field) : ':(';
  181. let subtitle = this.isFieldAvailable(field) ? '' : this.getFieldValue(field);
  182. return (
  183. <List.Item
  184. title={title}
  185. description={subtitle}
  186. left={props => <List.Icon
  187. {...props}
  188. icon={icon}
  189. color={this.isFieldAvailable(field) ? undefined : this.props.theme.colors.textDisabled}
  190. />}
  191. />
  192. );
  193. }
  194. /**
  195. * Gets a card containing user personal information
  196. *
  197. * @return {*}
  198. */
  199. getPersonalCard() {
  200. return (
  201. <Card style={styles.card}>
  202. <Card.Title
  203. title={this.data.first_name + ' ' + this.data.last_name}
  204. subtitle={this.data.email}
  205. left={(props) => <Avatar.Icon
  206. {...props}
  207. icon="account"
  208. color={this.props.theme.colors.primary}
  209. style={styles.icon}
  210. />}
  211. />
  212. <Card.Content>
  213. <Divider/>
  214. <List.Section>
  215. <List.Subheader>{i18n.t("profileScreen.personalInformation")}</List.Subheader>
  216. {this.getPersonalListItem(this.data.birthday, "cake-variant")}
  217. {this.getPersonalListItem(this.data.phone, "phone")}
  218. {this.getPersonalListItem(this.data.email, "email")}
  219. {this.getPersonalListItem(this.data.branch, "school")}
  220. </List.Section>
  221. <Divider/>
  222. <Card.Actions>
  223. <Button
  224. icon="account-edit"
  225. mode="contained"
  226. onPress={() => this.props.navigation.navigate('amicale-website', {path: this.data.link})}
  227. style={styles.editButton}>
  228. {i18n.t("profileScreen.editInformation")}
  229. </Button>
  230. </Card.Actions>
  231. </Card.Content>
  232. </Card>
  233. );
  234. }
  235. /**
  236. * Gets a cars containing clubs the user is part of
  237. *
  238. * @return {*}
  239. */
  240. getClubCard() {
  241. return (
  242. <Card style={styles.card}>
  243. <Card.Title
  244. title={i18n.t("profileScreen.clubs")}
  245. subtitle={i18n.t("profileScreen.clubsSubtitle")}
  246. left={(props) => <Avatar.Icon
  247. {...props}
  248. icon="account-group"
  249. color={this.props.theme.colors.primary}
  250. style={styles.icon}
  251. />}
  252. />
  253. <Card.Content>
  254. <Divider/>
  255. {this.getClubList(this.data.clubs)}
  256. </Card.Content>
  257. </Card>
  258. );
  259. }
  260. /**
  261. * Gets a card showing if the user has payed his membership
  262. *
  263. * @return {*}
  264. */
  265. getMembershipCar() {
  266. return (
  267. <Card style={styles.card}>
  268. <Card.Title
  269. title={i18n.t("profileScreen.membership")}
  270. subtitle={i18n.t("profileScreen.membershipSubtitle")}
  271. left={(props) => <Avatar.Icon
  272. {...props}
  273. icon="credit-card"
  274. color={this.props.theme.colors.primary}
  275. style={styles.icon}
  276. />}
  277. />
  278. <Card.Content>
  279. <List.Section>
  280. {this.getMembershipItem(this.data.validity)}
  281. </List.Section>
  282. </Card.Content>
  283. </Card>
  284. );
  285. }
  286. /**
  287. * Gets the item showing if the user has payed his membership
  288. *
  289. * @return {*}
  290. */
  291. getMembershipItem(state: boolean) {
  292. return (
  293. <List.Item
  294. title={state ? i18n.t("profileScreen.membershipPayed") : i18n.t("profileScreen.membershipNotPayed")}
  295. left={props => <List.Icon
  296. {...props}
  297. color={state ? this.props.theme.colors.success : this.props.theme.colors.danger}
  298. icon={state ? 'check' : 'close'}
  299. />}
  300. />
  301. );
  302. }
  303. /**
  304. * Opens the club details screen for the club of given ID
  305. * @param id The club's id to open
  306. */
  307. openClubDetailsScreen(id: number) {
  308. this.props.navigation.navigate("club-information", {clubId: id});
  309. }
  310. /**
  311. * Gets a list item for the club list
  312. *
  313. * @param item The club to render
  314. * @return {*}
  315. */
  316. clubListItem = ({item}: Object) => {
  317. const onPress = () => this.openClubDetailsScreen(item.id);
  318. let description = i18n.t("profileScreen.isMember");
  319. let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
  320. if (item.is_manager) {
  321. description = i18n.t("profileScreen.isManager");
  322. icon = (props) => <List.Icon {...props} icon="star" color={this.props.theme.colors.primary}/>;
  323. }
  324. return <List.Item
  325. title={item.name}
  326. description={description}
  327. left={icon}
  328. onPress={onPress}
  329. />;
  330. };
  331. clubKeyExtractor = (item: Object) => item.name;
  332. sortClubList = (a: Object, b: Object) => a.is_manager ? -1 : 1;
  333. /**
  334. * Renders the list of clubs the user is part of
  335. *
  336. * @param list The club list
  337. * @return {*}
  338. */
  339. getClubList(list: Array<Object>) {
  340. list.sort(this.sortClubList);
  341. return (
  342. //$FlowFixMe
  343. <FlatList
  344. renderItem={this.clubListItem}
  345. keyExtractor={this.clubKeyExtractor}
  346. data={list}
  347. />
  348. );
  349. }
  350. render() {
  351. return (
  352. <AuthenticatedScreen
  353. {...this.props}
  354. requests={[
  355. {
  356. link: 'user/profile',
  357. params: {},
  358. mandatory: true,
  359. }
  360. ]}
  361. renderFunction={this.getScreen}
  362. />
  363. );
  364. }
  365. }
  366. const styles = StyleSheet.create({
  367. card: {
  368. margin: 10,
  369. },
  370. icon: {
  371. backgroundColor: 'transparent'
  372. },
  373. editButton: {
  374. marginLeft: 'auto'
  375. }
  376. });
  377. export default withCollapsible(withTheme(ProfileScreen));