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.

HomeScreen.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. // @flow
  2. import * as React from 'react';
  3. import {View} from 'react-native';
  4. import i18n from "i18n-js";
  5. import DashboardItem from "../components/Home/EventDashboardItem";
  6. import WebSectionList from "../components/Lists/WebSectionList";
  7. import {Text, withTheme} from 'react-native-paper';
  8. import FeedItem from "../components/Home/FeedItem";
  9. import SquareDashboardItem from "../components/Home/SmallDashboardItem";
  10. import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardItem";
  11. import {stringToDate} from "../utils/Planning";
  12. import {openBrowser} from "../utils/WebBrowser";
  13. import ActionsDashBoardItem from "../components/Home/ActionsDashboardItem";
  14. import HeaderButton from "../components/Custom/HeaderButton";
  15. import ConnectionManager from "../managers/ConnectionManager";
  16. import {CommonActions} from '@react-navigation/native';
  17. // import DATA from "../dashboard_data.json";
  18. const NAME_AMICALE = 'Amicale INSA Toulouse';
  19. const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/dashboard/dashboard_data.json";
  20. const SECTIONS_ID = [
  21. 'dashboard',
  22. 'news_feed'
  23. ];
  24. const REFRESH_TIME = 1000 * 20; // Refresh every 20 seconds
  25. type Props = {
  26. navigation: Object,
  27. route: Object,
  28. theme: Object,
  29. }
  30. /**
  31. * Class defining the app's home screen
  32. */
  33. class HomeScreen extends React.Component<Props> {
  34. getRenderItem: Function;
  35. createDataset: Function;
  36. colors: Object;
  37. isLoggedIn: boolean | null;
  38. constructor(props) {
  39. super(props);
  40. this.getRenderItem = this.getRenderItem.bind(this);
  41. this.createDataset = this.createDataset.bind(this);
  42. this.colors = props.theme.colors;
  43. this.isLoggedIn = null;
  44. }
  45. /**
  46. * Converts a dateString using Unix Timestamp to a formatted date
  47. *
  48. * @param dateString {string} The Unix Timestamp representation of a date
  49. * @return {string} The formatted output date
  50. */
  51. static getFormattedDate(dateString: string) {
  52. let date = new Date(Number.parseInt(dateString) * 1000);
  53. return date.toLocaleString();
  54. }
  55. componentDidMount() {
  56. this.props.navigation.addListener('focus', this.onScreenFocus);
  57. }
  58. onScreenFocus = () => {
  59. if (this.isLoggedIn !== ConnectionManager.getInstance().isLoggedIn()) {
  60. this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn();
  61. this.props.navigation.setOptions({
  62. headerRight: this.getHeaderButton,
  63. });
  64. }
  65. // TODO if already on home screen
  66. if (this.props.route.params !== undefined) {
  67. if (this.props.route.params.shouldOpen !== undefined && this.props.route.params.shouldOpen) {
  68. this.props.navigation.navigate(this.props.route.params.nextScreen, this.props.route.params.data);
  69. // reset params to prevent infinite loop
  70. this.props.navigation.dispatch(CommonActions.setParams({ shouldOpen: false }));
  71. }
  72. }
  73. };
  74. getHeaderButton = () => {
  75. const screen = this.isLoggedIn
  76. ? "profile"
  77. : "login";
  78. const icon = this.isLoggedIn
  79. ? "account"
  80. : "login";
  81. const onPress = () => this.props.navigation.navigate(screen);
  82. return <HeaderButton
  83. icon={icon}
  84. onPress={onPress}
  85. color={this.isLoggedIn ? undefined : this.colors.primary}
  86. />;
  87. };
  88. onProxiwashClick = () => this.props.navigation.navigate('proxiwash');
  89. onTutorInsaClick = () => openBrowser("https://www.etud.insa-toulouse.fr/~tutorinsa/", this.colors.primary);
  90. onProximoClick = () => this.props.navigation.navigate('proximo');
  91. onMenuClick = () => this.props.navigation.navigate('self-menu');
  92. /**
  93. * Creates the dataset to be used in the FlatList
  94. *
  95. * @param fetchedData
  96. * @return {*}
  97. */
  98. createDataset(fetchedData: Object) {
  99. // fetchedData = DATA;
  100. let newsData = [];
  101. let dashboardData = [];
  102. if (fetchedData['news_feed'] !== undefined)
  103. newsData = fetchedData['news_feed']['data'];
  104. if (fetchedData['dashboard'] !== undefined)
  105. dashboardData = this.generateDashboardDataset(fetchedData['dashboard']);
  106. return [
  107. {
  108. title: '',
  109. data: dashboardData,
  110. id: SECTIONS_ID[0]
  111. },
  112. {
  113. title: i18n.t('homeScreen.newsFeed'),
  114. data: newsData,
  115. id: SECTIONS_ID[1]
  116. }
  117. ];
  118. }
  119. /**
  120. * Generates the dataset associated to the dashboard to be displayed in the FlatList as a section
  121. *
  122. * @param dashboardData
  123. * @return {*}
  124. */
  125. generateDashboardDataset(dashboardData: Object) {
  126. let dataset = [
  127. {
  128. id: 'top',
  129. content: []
  130. },
  131. {
  132. id: 'actions',
  133. content: undefined
  134. },
  135. {
  136. id: 'event',
  137. content: undefined
  138. },
  139. ];
  140. for (let [key, value] of Object.entries(dashboardData)) {
  141. switch (key) {
  142. case 'today_events':
  143. dataset[2]['content'] = value;
  144. break;
  145. case 'available_machines':
  146. dataset[0]['content'][0] = {id: key, data: value};
  147. break;
  148. case 'available_tutorials':
  149. dataset[0]['content'][1] = {id: key, data: value};
  150. break;
  151. case 'proximo_articles':
  152. dataset[0]['content'][2] = {id: key, data: value};
  153. break;
  154. case 'today_menu':
  155. dataset[0]['content'][3] = {id: key, data: value};
  156. break;
  157. }
  158. }
  159. return dataset
  160. }
  161. /**
  162. * Gets a dashboard item
  163. *
  164. * @param item The item to display
  165. * @return {*}
  166. */
  167. getDashboardItem(item: Object) {
  168. let content = item['content'];
  169. if (item['id'] === 'event')
  170. return this.getDashboardEventItem(content);
  171. else if (item['id'] === 'top')
  172. return this.getDashboardTopItem(content);
  173. else if (item['id'] === 'actions')
  174. return this.getActionsDashboardItem();
  175. }
  176. getActionsDashboardItem() {
  177. return <ActionsDashBoardItem {...this.props}/>;
  178. }
  179. /**
  180. * Gets the time limit depending on the current day:
  181. * 17:30 for every day of the week except for thursday 11:30
  182. * 00:00 on weekends
  183. */
  184. getTodayEventTimeLimit() {
  185. let now = new Date();
  186. if (now.getDay() === 4) // Thursday
  187. now.setHours(11, 30, 0);
  188. else if (now.getDay() === 6 || now.getDay() === 0) // Weekend
  189. now.setHours(0, 0, 0);
  190. else
  191. now.setHours(17, 30, 0);
  192. return now;
  193. }
  194. /**
  195. * Gets the duration (in milliseconds) of an event
  196. *
  197. * @param event {Object}
  198. * @return {number} The number of milliseconds
  199. */
  200. getEventDuration(event: Object): number {
  201. let start = stringToDate(event['date_begin']);
  202. let end = stringToDate(event['date_end']);
  203. let duration = 0;
  204. if (start !== undefined && start !== null && end !== undefined && end !== null)
  205. duration = end - start;
  206. return duration;
  207. }
  208. /**
  209. * Gets events starting after the limit
  210. *
  211. * @param events
  212. * @param limit
  213. * @return {Array<Object>}
  214. */
  215. getEventsAfterLimit(events: Object, limit: Date): Array<Object> {
  216. let validEvents = [];
  217. for (let event of events) {
  218. let startDate = stringToDate(event['date_begin']);
  219. if (startDate !== undefined && startDate !== null && startDate >= limit) {
  220. validEvents.push(event);
  221. }
  222. }
  223. return validEvents;
  224. }
  225. /**
  226. * Gets the event with the longest duration in the given array.
  227. * If all events have the same duration, return the first in the array.
  228. *
  229. * @param events
  230. */
  231. getLongestEvent(events: Array<Object>): Object {
  232. let longestEvent = events[0];
  233. let longestTime = 0;
  234. for (let event of events) {
  235. let time = this.getEventDuration(event);
  236. if (time > longestTime) {
  237. longestTime = time;
  238. longestEvent = event;
  239. }
  240. }
  241. return longestEvent;
  242. }
  243. /**
  244. * Gets events that have not yet ended/started
  245. *
  246. * @param events
  247. */
  248. getFutureEvents(events: Array<Object>): Array<Object> {
  249. let validEvents = [];
  250. let now = new Date();
  251. for (let event of events) {
  252. let startDate = stringToDate(event['date_begin']);
  253. let endDate = stringToDate(event['date_end']);
  254. if (startDate !== undefined && startDate !== null) {
  255. if (startDate > now)
  256. validEvents.push(event);
  257. else if (endDate !== undefined && endDate !== null) {
  258. if (endDate > now || endDate < startDate) // Display event if it ends the following day
  259. validEvents.push(event);
  260. }
  261. }
  262. }
  263. return validEvents;
  264. }
  265. /**
  266. * Gets the event to display in the preview
  267. *
  268. * @param events
  269. * @return {Object}
  270. */
  271. getDisplayEvent(events: Array<Object>): Object {
  272. let displayEvent = undefined;
  273. if (events.length > 1) {
  274. let eventsAfterLimit = this.getEventsAfterLimit(events, this.getTodayEventTimeLimit());
  275. if (eventsAfterLimit.length > 0) {
  276. if (eventsAfterLimit.length === 1)
  277. displayEvent = eventsAfterLimit[0];
  278. else
  279. displayEvent = this.getLongestEvent(events);
  280. } else {
  281. displayEvent = this.getLongestEvent(events);
  282. }
  283. } else if (events.length === 1) {
  284. displayEvent = events[0];
  285. }
  286. return displayEvent;
  287. }
  288. /**
  289. * Gets the event render item.
  290. * If a preview is available, it will be rendered inside
  291. *
  292. * @param content
  293. * @return {*}
  294. */
  295. getDashboardEventItem(content: Array<Object>) {
  296. let icon = 'calendar-range';
  297. let title = i18n.t('homeScreen.dashboard.todayEventsTitle');
  298. let subtitle;
  299. let futureEvents = this.getFutureEvents(content);
  300. let isAvailable = futureEvents.length > 0;
  301. if (isAvailable) {
  302. subtitle =
  303. <Text>
  304. <Text style={{fontWeight: "bold"}}>{futureEvents.length}</Text>
  305. <Text>
  306. {
  307. futureEvents.length > 1 ?
  308. i18n.t('homeScreen.dashboard.todayEventsSubtitlePlural') :
  309. i18n.t('homeScreen.dashboard.todayEventsSubtitle')
  310. }
  311. </Text>
  312. </Text>;
  313. } else
  314. subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
  315. let displayEvent = this.getDisplayEvent(futureEvents);
  316. const clickContainerAction = () => this.props.navigation.navigate('planning');
  317. const clickPreviewAction = () => this.props.navigation.navigate('planning-information', {data: displayEvent});
  318. return (
  319. <DashboardItem
  320. {...this.props}
  321. subtitle={subtitle}
  322. icon={icon}
  323. clickAction={clickContainerAction}
  324. title={title}
  325. isAvailable={isAvailable}
  326. >
  327. <PreviewEventDashboardItem
  328. {...this.props}
  329. event={displayEvent}
  330. clickAction={clickPreviewAction}
  331. />
  332. </DashboardItem>
  333. );
  334. }
  335. /**
  336. * Gets a classic dashboard item.
  337. *
  338. * @param content
  339. * @return {*}
  340. */
  341. getDashboardTopItem(content: Array<Object>) {
  342. let proxiwashData = content[0]['data'];
  343. let tutorinsaData = content[1]['data'];
  344. let proximoData = content[2]['data'];
  345. let menuData = content[3]['data'];
  346. return (
  347. <View style={{
  348. flex: 1,
  349. flexDirection: 'row',
  350. justifyContent: 'center',
  351. flexWrap: 'wrap',
  352. margin: 10,
  353. }}>
  354. <SquareDashboardItem
  355. color={this.colors.proxiwashColor}
  356. icon={'washing-machine'}
  357. clickAction={this.onProxiwashClick}
  358. isAvailable={parseInt(proxiwashData['washers']) > 0}
  359. badgeNumber={proxiwashData['washers']}
  360. />
  361. <SquareDashboardItem
  362. color={this.colors.proxiwashColor}
  363. icon={'tumble-dryer'}
  364. clickAction={this.onProxiwashClick}
  365. isAvailable={parseInt(proxiwashData['dryers']) > 0}
  366. badgeNumber={proxiwashData['dryers']}
  367. />
  368. <SquareDashboardItem
  369. color={this.colors.tutorinsaColor}
  370. icon={'school'}
  371. clickAction={this.onTutorInsaClick}
  372. isAvailable={tutorinsaData > 0}
  373. badgeNumber={tutorinsaData}
  374. />
  375. <SquareDashboardItem
  376. color={this.colors.proximoColor}
  377. icon={'shopping'}
  378. clickAction={this.onProximoClick}
  379. isAvailable={parseInt(proximoData) > 0}
  380. badgeNumber={parseInt(proximoData)}
  381. />
  382. <SquareDashboardItem
  383. color={this.colors.menuColor}
  384. icon={'silverware-fork-knife'}
  385. clickAction={this.onMenuClick}
  386. isAvailable={menuData.length > 0}
  387. badgeNumber={0}
  388. />
  389. </View>
  390. );
  391. }
  392. openLink(link: string) {
  393. openBrowser(link, this.colors.primary);
  394. }
  395. /**
  396. * Gets a render item for the given feed object
  397. *
  398. * @param item The feed item to display
  399. * @return {*}
  400. */
  401. getFeedItem(item: Object) {
  402. const onOutLinkPress = this.openLink.bind(this, item.permalink_url);
  403. return (
  404. <FeedItem
  405. title={NAME_AMICALE}
  406. subtitle={HomeScreen.getFormattedDate(item.created_time)}
  407. full_picture={item.full_picture}
  408. message={item.message}
  409. onOutLinkPress={onOutLinkPress}
  410. />
  411. );
  412. }
  413. /**
  414. * Gets a FlatList render item
  415. *
  416. * @param item The item to display
  417. * @param section The current section
  418. * @return {*}
  419. */
  420. getRenderItem({item, section}: Object) {
  421. return (section['id'] === SECTIONS_ID[0] ?
  422. this.getDashboardItem(item) : this.getFeedItem(item));
  423. }
  424. render() {
  425. const nav = this.props.navigation;
  426. return (
  427. <WebSectionList
  428. createDataset={this.createDataset}
  429. navigation={nav}
  430. autoRefreshTime={REFRESH_TIME}
  431. refreshOnFocus={true}
  432. fetchUrl={DATA_URL}
  433. renderItem={this.getRenderItem}/>
  434. );
  435. }
  436. }
  437. export default withTheme(HomeScreen);