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.

PlanningScreen.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // @flow
  2. import * as React from 'react';
  3. import {Content, H1, H2, H3, Text} from 'native-base';
  4. import i18n from "i18n-js";
  5. import {View, Image} from "react-native";
  6. import ThemeManager from "../utils/ThemeManager";
  7. import {Linking} from "expo";
  8. import BaseContainer from "../components/BaseContainer";
  9. import {Agenda} from 'react-native-calendars';
  10. import HTML from 'react-native-render-html';
  11. import Touchable from 'react-native-platform-touchable';
  12. import Modalize from 'react-native-modalize';
  13. import WebDataManager from "../utils/WebDataManager";
  14. type Props = {
  15. navigation: Object,
  16. }
  17. type State = {
  18. modalCurrentDisplayItem: Object,
  19. refreshing: boolean,
  20. agendaItems: Object,
  21. };
  22. const FETCH_URL = "https://amicale-insat.fr/event/json/list";
  23. const AGENDA_MONTH_SPAN = 6;
  24. /**
  25. * Opens a link in the device's browser
  26. * @param link The link to open
  27. */
  28. function openWebLink(link) {
  29. Linking.openURL(link).catch((err) => console.error('Error opening link', err));
  30. }
  31. /**
  32. * Class defining the app's planning screen
  33. */
  34. export default class PlanningScreen extends React.Component<Props, State> {
  35. modalRef: { current: null | Modalize };
  36. webDataManager: WebDataManager;
  37. lastRefresh: Date;
  38. minTimeBetweenRefresh = 60;
  39. constructor(props: any) {
  40. super(props);
  41. this.modalRef = React.createRef();
  42. this.webDataManager = new WebDataManager(FETCH_URL);
  43. }
  44. componentDidMount() {
  45. this._onRefresh();
  46. }
  47. state = {
  48. modalCurrentDisplayItem: {},
  49. refreshing: false,
  50. agendaItems: {}
  51. };
  52. getCurrentDate() {
  53. let today = new Date();
  54. return this.getFormattedDate(today);
  55. }
  56. getFormattedDate(date: Date) {
  57. let dd = String(date.getDate()).padStart(2, '0');
  58. let mm = String(date.getMonth() + 1).padStart(2, '0'); //January is 0!
  59. let yyyy = date.getFullYear();
  60. return yyyy + '-' + mm + '-' + dd;
  61. }
  62. generateEmptyCalendar() {
  63. let end = new Date(new Date().setMonth(new Date().getMonth() + AGENDA_MONTH_SPAN + 1));
  64. let daysOfYear = {};
  65. for (let d = new Date(2019, 8, 1); d <= end; d.setDate(d.getDate() + 1)) {
  66. daysOfYear[this.getFormattedDate(new Date(d))] = []
  67. }
  68. return daysOfYear;
  69. }
  70. getModalContent() {
  71. return (
  72. <View style={{
  73. flex: 1,
  74. padding: 20
  75. }}>
  76. <H1 style={{
  77. marginTop: 20,
  78. }}>
  79. {this.state.modalCurrentDisplayItem.title}
  80. </H1>
  81. <H3 style={{
  82. marginTop: 10,
  83. color: ThemeManager.getCurrentThemeVariables().listNoteColor
  84. }}>
  85. {this.getFormattedTime(this.state.modalCurrentDisplayItem)}
  86. </H3>
  87. <Content>
  88. {this.state.modalCurrentDisplayItem.logo !== null ?
  89. <View style={{width: '100%', height: 200, marginTop: 20, marginBottom: 20}}>
  90. <Image style={{flex: 1, resizeMode: "contain"}}
  91. source={{uri: this.state.modalCurrentDisplayItem.logo}}/>
  92. </View>
  93. : <View/>}
  94. {this.state.modalCurrentDisplayItem.description !== null ?
  95. // Surround description with div to allow text styling if the description is not html
  96. <HTML html={"<div>" + this.state.modalCurrentDisplayItem.description + "</div>"}
  97. tagsStyles={{
  98. p: {color: ThemeManager.getCurrentThemeVariables().textColor},
  99. div: {color: ThemeManager.getCurrentThemeVariables().textColor}
  100. }}/>
  101. : <View/>}
  102. </Content>
  103. </View>
  104. );
  105. }
  106. showItemDetails(item: Object) {
  107. this.setState({
  108. modalCurrentDisplayItem: item
  109. });
  110. if (this.modalRef.current) {
  111. this.modalRef.current.open();
  112. }
  113. }
  114. getRenderItem(item: Object) {
  115. return (
  116. <Touchable
  117. style={{
  118. backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor,
  119. borderRadius: 10,
  120. marginRight: 10,
  121. marginTop: 17,
  122. }}
  123. onPress={() => this.showItemDetails(item)}>
  124. <View style={{
  125. padding: 10,
  126. flex: 1,
  127. flexDirection: 'row'
  128. }}>
  129. <View style={{width: '70%'}}>
  130. <Text style={{
  131. color: ThemeManager.getCurrentThemeVariables().listNoteColor,
  132. marginTop: 5,
  133. marginBottom: 10
  134. }}>
  135. {this.getFormattedTime(item)}
  136. </Text>
  137. <H3 style={{marginBottom: 10}}>{item.title}</H3>
  138. </View>
  139. <View style={{
  140. width: '30%',
  141. height: 80
  142. }}>
  143. {item.logo !== null ?
  144. <Image source={{uri: item.logo}}
  145. style={{
  146. flex: 1,
  147. resizeMode: "contain"
  148. }}/>
  149. : <View/>}
  150. </View>
  151. </View>
  152. </Touchable>
  153. );
  154. }
  155. getRenderEmptyDate() {
  156. return (
  157. <View style={{
  158. padding: 10,
  159. flex: 1,
  160. }}>
  161. <View style={{
  162. width: '100%',
  163. height: 1,
  164. backgroundColor: ThemeManager.getCurrentThemeVariables().agendaEmptyLine,
  165. marginTop: 'auto',
  166. marginBottom: 'auto',
  167. }}/>
  168. </View>
  169. );
  170. }
  171. rowHasChanged(r1: Object, r2: Object) {
  172. if (r1 !== undefined && r2 !== undefined)
  173. return r1.title !== r2.title;
  174. else return !(r1 === undefined && r2 === undefined);
  175. }
  176. /**
  177. * Refresh data and show a toast if any error occurred
  178. * @private
  179. */
  180. _onRefresh = () => {
  181. let canRefresh;
  182. if (this.lastRefresh !== undefined)
  183. canRefresh = (new Date().getTime() - this.lastRefresh.getTime())/1000 > this.minTimeBetweenRefresh;
  184. else
  185. canRefresh = true;
  186. if (canRefresh) {
  187. this.setState({refreshing: true});
  188. this.webDataManager.readData()
  189. .then((fetchedData) => {
  190. this.setState({
  191. refreshing: false,
  192. });
  193. this.generateEventAgenda(fetchedData);
  194. this.lastRefresh = new Date();
  195. })
  196. .catch((err) => {
  197. this.setState({
  198. refreshing: false,
  199. });
  200. console.log(err);
  201. });
  202. }
  203. };
  204. generateEventAgenda(eventList: Array<Object>) {
  205. let agendaItems = this.generateEmptyCalendar();
  206. for (let i = 0; i < eventList.length; i++) {
  207. if (agendaItems[this.getEventStartDate(eventList[i])] !== undefined) {
  208. this.pushEventInOrder(agendaItems, eventList[i], this.getEventStartDate(eventList[i]));
  209. }
  210. }
  211. this.setState({agendaItems: agendaItems})
  212. }
  213. pushEventInOrder(agendaItems: Object, event: Object, startDate: string) {
  214. if (agendaItems[startDate].length === 0)
  215. agendaItems[startDate].push(event);
  216. else {
  217. for (let i = 0; i < agendaItems[startDate].length; i++) {
  218. if (this.isEventBefore(event, agendaItems[startDate][i])) {
  219. agendaItems[startDate].splice(i, 0, event);
  220. break;
  221. } else if (i === agendaItems[startDate].length - 1) {
  222. agendaItems[startDate].push(event);
  223. break;
  224. }
  225. }
  226. }
  227. }
  228. isEventBefore(event1: Object, event2: Object) {
  229. let date1 = new Date();
  230. let date2 = new Date();
  231. let timeArray = this.getEventStartTime(event1).split(":");
  232. date1.setHours(parseInt(timeArray[0]), parseInt(timeArray[1]));
  233. timeArray = this.getEventStartTime(event2).split(":");
  234. date2.setHours(parseInt(timeArray[0]), parseInt(timeArray[1]));
  235. return date1 < date2;
  236. }
  237. getEventStartDate(event: Object) {
  238. return event.date_begin.split(" ")[0];
  239. }
  240. getEventStartTime(event: Object) {
  241. if (event !== undefined && Object.keys(event).length > 0 && event.date_begin !== null)
  242. return this.formatTime(event.date_begin.split(" ")[1]);
  243. else
  244. return "";
  245. }
  246. getEventEndTime(event: Object) {
  247. if (event !== undefined && Object.keys(event).length > 0 && event.date_end !== null)
  248. return this.formatTime(event.date_end.split(" ")[1]);
  249. else
  250. return "";
  251. }
  252. getFormattedTime(event: Object) {
  253. if (this.getEventEndTime(event) !== "")
  254. return this.getEventStartTime(event) + " - " + this.getEventEndTime(event)
  255. else
  256. return this.getEventStartTime(event);
  257. }
  258. formatTime(time: string) {
  259. let array = time.split(':');
  260. return array[0] + ':' + array[1];
  261. }
  262. render() {
  263. const nav = this.props.navigation;
  264. return (
  265. <BaseContainer navigation={nav} headerTitle={i18n.t('screens.planning')}>
  266. <Modalize ref={this.modalRef}
  267. modalStyle={{
  268. backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor,
  269. }}>
  270. {this.getModalContent()}
  271. </Modalize>
  272. <Agenda
  273. // the list of items that have to be displayed in agenda. If you want to render item as empty date
  274. // the value of date key kas to be an empty array []. If there exists no value for date key it is
  275. // considered that the date in question is not yet loaded
  276. items={this.state.agendaItems}
  277. // initially selected day
  278. selected={this.getCurrentDate()}
  279. // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
  280. minDate={"2019-09-01"}
  281. // Max amount of months allowed to scroll to the past. Default = 50
  282. pastScrollRange={1}
  283. // Max amount of months allowed to scroll to the future. Default = 50
  284. futureScrollRange={AGENDA_MONTH_SPAN}
  285. // If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
  286. onRefresh={() => this._onRefresh()}
  287. // Set this true while waiting for new data from a refresh
  288. refreshing={this.state.refreshing}
  289. renderItem={(item) => this.getRenderItem(item)}
  290. renderEmptyDate={() => this.getRenderEmptyDate()}
  291. rowHasChanged={() => this.rowHasChanged()}
  292. // agenda theme
  293. theme={{
  294. backgroundColor: ThemeManager.getCurrentThemeVariables().agendaBackgroundColor,
  295. calendarBackground: ThemeManager.getCurrentThemeVariables().containerBgColor,
  296. textSectionTitleColor: ThemeManager.getCurrentThemeVariables().listNoteColor,
  297. selectedDayBackgroundColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  298. selectedDayTextColor: '#ffffff',
  299. todayTextColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  300. dayTextColor: ThemeManager.getCurrentThemeVariables().textColor,
  301. textDisabledColor: ThemeManager.getCurrentThemeVariables().textDisabledColor,
  302. dotColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  303. selectedDotColor: '#ffffff',
  304. arrowColor: 'orange',
  305. monthTextColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  306. indicatorColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  307. textDayFontWeight: '300',
  308. textMonthFontWeight: 'bold',
  309. textDayHeaderFontWeight: '300',
  310. textDayFontSize: 16,
  311. textMonthFontSize: 16,
  312. textDayHeaderFontSize: 16,
  313. agendaDayTextColor: ThemeManager.getCurrentThemeVariables().listNoteColor,
  314. agendaDayNumColor: ThemeManager.getCurrentThemeVariables().listNoteColor,
  315. agendaTodayColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  316. agendaKnobColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
  317. // Fix for days hiding behind knob
  318. 'stylesheet.calendar.header': {
  319. week: {
  320. marginTop: 0,
  321. flexDirection: 'row',
  322. justifyContent: 'space-between'
  323. }
  324. }
  325. }}
  326. />
  327. </BaseContainer>
  328. );
  329. }
  330. }