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.

ProxiwashScreen.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // @flow
  2. import * as React from 'react';
  3. import {Alert, AsyncStorage, View} from 'react-native';
  4. import {Body, Card, CardItem, Left, Right, Text} from 'native-base';
  5. import ThemeManager from '../utils/ThemeManager';
  6. import i18n from "i18n-js";
  7. import CustomMaterialIcon from "../components/CustomMaterialIcon";
  8. import FetchedDataSectionList from "../components/FetchedDataSectionList";
  9. import NotificationsManager from "../utils/NotificationsManager";
  10. import PlatformTouchable from "react-native-platform-touchable";
  11. const DATA_URL = "https://etud.insa-toulouse.fr/~vergnet/appli-amicale/dataProxiwash.json";
  12. const WATCHED_MACHINES_PREFKEY = "proxiwash.watchedMachines";
  13. let reminderNotifTime = 5;
  14. const MACHINE_STATES = {
  15. TERMINE: "0",
  16. DISPONIBLE: "1",
  17. FONCTIONNE: "2",
  18. HS: "3",
  19. ERREUR: "4"
  20. };
  21. let stateStrings = {};
  22. let modalStateStrings = {};
  23. let stateIcons = {};
  24. let stateColors = {};
  25. /**
  26. * Class defining the app's proxiwash screen. This screen shows information about washing machines and
  27. * dryers, taken from a scrapper reading proxiwash website
  28. */
  29. export default class ProxiwashScreen extends FetchedDataSectionList {
  30. state = {
  31. refreshing: false,
  32. firstLoading: true,
  33. fetchedData: {},
  34. machinesWatched: [],
  35. };
  36. /**
  37. * Creates machine state parameters using current theme and translations
  38. */
  39. constructor() {
  40. super(DATA_URL);
  41. let colors = ThemeManager.getInstance().getCurrentThemeVariables();
  42. stateColors[MACHINE_STATES.TERMINE] = colors.proxiwashFinishedColor;
  43. stateColors[MACHINE_STATES.DISPONIBLE] = colors.proxiwashReadyColor;
  44. stateColors[MACHINE_STATES.FONCTIONNE] = colors.proxiwashRunningColor;
  45. stateColors[MACHINE_STATES.HS] = colors.proxiwashBrokenColor;
  46. stateColors[MACHINE_STATES.ERREUR] = colors.proxiwashErrorColor;
  47. stateStrings[MACHINE_STATES.TERMINE] = i18n.t('proxiwashScreen.states.finished');
  48. stateStrings[MACHINE_STATES.DISPONIBLE] = i18n.t('proxiwashScreen.states.ready');
  49. stateStrings[MACHINE_STATES.FONCTIONNE] = i18n.t('proxiwashScreen.states.running');
  50. stateStrings[MACHINE_STATES.HS] = i18n.t('proxiwashScreen.states.broken');
  51. stateStrings[MACHINE_STATES.ERREUR] = i18n.t('proxiwashScreen.states.error');
  52. modalStateStrings[MACHINE_STATES.TERMINE] = i18n.t('proxiwashScreen.modal.finished');
  53. modalStateStrings[MACHINE_STATES.DISPONIBLE] = i18n.t('proxiwashScreen.modal.ready');
  54. modalStateStrings[MACHINE_STATES.FONCTIONNE] = i18n.t('proxiwashScreen.modal.running');
  55. modalStateStrings[MACHINE_STATES.HS] = i18n.t('proxiwashScreen.modal.broken');
  56. modalStateStrings[MACHINE_STATES.ERREUR] = i18n.t('proxiwashScreen.modal.error');
  57. stateIcons[MACHINE_STATES.TERMINE] = 'check-circle';
  58. stateIcons[MACHINE_STATES.DISPONIBLE] = 'radiobox-blank';
  59. stateIcons[MACHINE_STATES.FONCTIONNE] = 'progress-check';
  60. stateIcons[MACHINE_STATES.HS] = 'alert-octagram-outline';
  61. stateIcons[MACHINE_STATES.ERREUR] = 'alert';
  62. }
  63. getHeaderTranslation() {
  64. return i18n.t("screens.proxiwash");
  65. }
  66. getUpdateToastTranslations() {
  67. return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")];
  68. }
  69. getKeyExtractor(item: Object) {
  70. return item !== undefined ? item.number : undefined;
  71. }
  72. /**
  73. * Get which machines have notifications enabled before loading the screen
  74. *
  75. * @returns {Promise<void>}
  76. */
  77. async componentWillMount() {
  78. let dataString = await AsyncStorage.getItem(WATCHED_MACHINES_PREFKEY);
  79. if (dataString === null)
  80. dataString = '[]';
  81. this.setState({
  82. machinesWatched: JSON.parse(dataString)
  83. });
  84. }
  85. /**
  86. * Get the time remaining based on start/end time and done percent
  87. *
  88. * @param startString The string representing the start time. Format: hh:mm
  89. * @param endString The string representing the end time. Format: hh:mm
  90. * @param percentDone The percentage done
  91. * @returns {number} How many minutes are remaining for this machine
  92. */
  93. static getRemainingTime(startString: string, endString: string, percentDone: string): number {
  94. let startArray = startString.split(':');
  95. let endArray = endString.split(':');
  96. let startDate = new Date();
  97. startDate.setHours(parseInt(startArray[0]), parseInt(startArray[1]), 0, 0);
  98. let endDate = new Date();
  99. endDate.setHours(parseInt(endArray[0]), parseInt(endArray[1]), 0, 0);
  100. // Convert milliseconds into minutes
  101. let time: string = (((100 - parseFloat(percentDone)) / 100) * (endDate - startDate) / (60 * 1000)).toFixed(0);
  102. return parseInt(time);
  103. }
  104. /**
  105. * Setup notifications for the machine with the given ID.
  106. * One notification will be sent at the end of the program.
  107. * Another will be send a few minutes before the end, based on the value of reminderNotifTime
  108. *
  109. * @param machineId The machine's ID
  110. * @param remainingTime The time remaining for this machine
  111. * @returns {Promise<void>}
  112. */
  113. async setupNotifications(machineId: string, remainingTime: number) {
  114. if (!this.isMachineWatched(machineId)) {
  115. let endNotifID = await NotificationsManager.scheduleNotification(
  116. i18n.t('proxiwashScreen.notifications.machineFinishedTitle'),
  117. i18n.t('proxiwashScreen.notifications.machineFinishedBody', {number: machineId}),
  118. new Date().getTime() + remainingTime * (60 * 1000) // Convert back to milliseconds
  119. );
  120. let reminderNotifID = undefined;
  121. let val = await AsyncStorage.getItem('proxiwashNotifKey');
  122. if (val === null)
  123. val = "5";
  124. if (val !== "never")
  125. reminderNotifTime = parseInt(val);
  126. else
  127. reminderNotifTime = -1;
  128. console.log(reminderNotifTime);
  129. if (remainingTime > reminderNotifTime && reminderNotifTime > 0) {
  130. reminderNotifID = await NotificationsManager.scheduleNotification(
  131. i18n.t('proxiwashScreen.notifications.machineRunningTitle', {time: reminderNotifTime}),
  132. i18n.t('proxiwashScreen.notifications.machineRunningBody', {number: machineId}),
  133. new Date().getTime() + (remainingTime - reminderNotifTime) * (60 * 1000) // Convert back to milliseconds
  134. );
  135. }
  136. let data = this.state.machinesWatched;
  137. data.push({machineNumber: machineId, endNotifID: endNotifID, reminderNotifID: reminderNotifID});
  138. this.setState({machinesWatched: data});
  139. AsyncStorage.setItem(WATCHED_MACHINES_PREFKEY, JSON.stringify(data));
  140. } else
  141. this.disableNotification(machineId);
  142. }
  143. /**
  144. * Stop scheduled notifications for the machine of the given ID.
  145. * This will also remove the notification if it was already shown.
  146. *
  147. * @param machineId The machine's ID
  148. */
  149. disableNotification(machineId: string) {
  150. let data: Object = this.state.machinesWatched;
  151. if (data.length > 0) {
  152. let elem = this.state.machinesWatched.find(function (elem) {
  153. return elem.machineNumber === machineId
  154. });
  155. let arrayIndex = data.indexOf(elem);
  156. NotificationsManager.cancelScheduledNotification(data[arrayIndex].endNotifID);
  157. if (data[arrayIndex].reminderNotifID !== undefined)
  158. NotificationsManager.cancelScheduledNotification(data[arrayIndex].reminderNotifID);
  159. data.splice(arrayIndex, 1);
  160. this.setState({machinesWatched: data});
  161. AsyncStorage.setItem(WATCHED_MACHINES_PREFKEY, JSON.stringify(data));
  162. }
  163. }
  164. /**
  165. * Checks whether the machine of the given ID has scheduled notifications
  166. *
  167. * @param machineID The machine's ID
  168. * @returns {boolean}
  169. */
  170. isMachineWatched(machineID: string) {
  171. return this.state.machinesWatched.find(function (elem) {
  172. return elem.machineNumber === machineID
  173. }) !== undefined;
  174. }
  175. createDataset(fetchedData: Object) {
  176. return [
  177. {
  178. title: i18n.t('proxiwashScreen.dryers'),
  179. icon: 'tumble-dryer',
  180. data: fetchedData.dryers === undefined ? [] : fetchedData.dryers,
  181. extraData: super.state
  182. },
  183. {
  184. title: i18n.t('proxiwashScreen.washers'),
  185. icon: 'washing-machine',
  186. data: fetchedData.washers === undefined ? [] : fetchedData.washers,
  187. extraData: super.state
  188. },
  189. ];
  190. }
  191. hasTabs(): boolean {
  192. return true;
  193. }
  194. showAlert(title : string, item : Object, remainingTime: number) {
  195. let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}];
  196. let message = modalStateStrings[MACHINE_STATES[item.state]];
  197. if (MACHINE_STATES[item.state] === MACHINE_STATES.FONCTIONNE) {
  198. buttons = [
  199. {
  200. text: this.isMachineWatched(item.number) ?
  201. i18n.t("proxiwashScreen.modal.disableNotifications") :
  202. i18n.t("proxiwashScreen.modal.enableNotifications"),
  203. onPress: () => this.setupNotifications(item.number, remainingTime)
  204. },
  205. {
  206. text: i18n.t("proxiwashScreen.modal.cancel")
  207. }
  208. ];
  209. message = i18n.t('proxiwashScreen.modal.running',
  210. {
  211. start: item.startTime,
  212. end: item.endTime,
  213. remaining: remainingTime
  214. });
  215. }
  216. Alert.alert(
  217. title,
  218. message,
  219. buttons
  220. );
  221. }
  222. /**
  223. * Get list item to be rendered
  224. *
  225. * @param item The object containing the item's FetchedData
  226. * @param section The object describing the current SectionList section
  227. * @param data The full FetchedData used by the SectionList
  228. * @returns {React.Node}
  229. */
  230. getRenderItem(item: Object, section: Object, data: Object) {
  231. let isMachineRunning = MACHINE_STATES[item.state] === MACHINE_STATES.FONCTIONNE;
  232. let machineName = (section.title === i18n.t('proxiwashScreen.dryers') ? i18n.t('proxiwashScreen.dryer') : i18n.t('proxiwashScreen.washer')) + ' n°' + item.number;
  233. let remainingTime = 0;
  234. if (isMachineRunning)
  235. remainingTime = ProxiwashScreen.getRemainingTime(item.startTime, item.endTime, item.donePercent);
  236. return (
  237. <Card style={{
  238. flex: 0,
  239. height: 64
  240. }}>
  241. <CardItem
  242. style={{
  243. backgroundColor: stateColors[MACHINE_STATES[item.state]],
  244. paddingRight: 0,
  245. paddingLeft: 0,
  246. height: '100%',
  247. }}
  248. >
  249. <View style={{
  250. height: 64,
  251. position: 'absolute',
  252. right: 0,
  253. width: item.donePercent !== '' ? (100 - parseInt(item.donePercent)).toString() + '%' : 0,
  254. backgroundColor: ThemeManager.getInstance().getCurrentThemeVariables().containerBgColor
  255. }}/>
  256. <PlatformTouchable
  257. onPress={() => this.showAlert(machineName, item, remainingTime)}
  258. style={{
  259. height: 64,
  260. position: 'absolute',
  261. right: 0,
  262. width: '100%'
  263. }}
  264. >
  265. <View/>
  266. </PlatformTouchable>
  267. <Left style={{marginLeft: 10}}>
  268. <CustomMaterialIcon
  269. icon={section.title === i18n.t('proxiwashScreen.dryers') ? 'tumble-dryer' : 'washing-machine'}
  270. fontSize={30}
  271. />
  272. <Body>
  273. <Text>
  274. {machineName + ' '}
  275. {this.isMachineWatched(item.number) ?
  276. <CustomMaterialIcon
  277. icon='bell-ring'
  278. color={ThemeManager.getInstance().getCurrentThemeVariables().brandPrimary}
  279. fontSize={20}
  280. /> : ''}
  281. </Text>
  282. <Text note>
  283. {isMachineRunning ? item.startTime + '/' + item.endTime : ''}
  284. </Text>
  285. </Body>
  286. </Left>
  287. <Right style={{marginRight: 10}}>
  288. <Text style={MACHINE_STATES[item.state] === MACHINE_STATES.TERMINE ?
  289. {fontWeight: 'bold'} : {}}
  290. >
  291. {stateStrings[MACHINE_STATES[item.state]]}
  292. </Text>
  293. <CustomMaterialIcon icon={stateIcons[MACHINE_STATES[item.state]]}
  294. fontSize={25}
  295. />
  296. </Right>
  297. </CardItem>
  298. </Card>);
  299. }
  300. }