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 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. // @flow
  2. import * as React from 'react';
  3. import {Alert, Platform, 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. import Touchable from "react-native-platform-touchable";
  12. import AsyncStorageManager from "../../utils/AsyncStorageManager";
  13. import * as Expo from "expo";
  14. const DATA_URL = "https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json";
  15. const MACHINE_STATES = {
  16. "TERMINE": "0",
  17. "DISPONIBLE": "1",
  18. "EN COURS": "2",
  19. "HS": "3",
  20. "ERREUR": "4"
  21. };
  22. let stateStrings = {};
  23. let modalStateStrings = {};
  24. let stateIcons = {};
  25. let stateColors = {};
  26. const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
  27. /**
  28. * Class defining the app's proxiwash screen. This screen shows information about washing machines and
  29. * dryers, taken from a scrapper reading proxiwash website
  30. */
  31. export default class ProxiwashScreen extends FetchedDataSectionList {
  32. /**
  33. * Creates machine state parameters using current theme and translations
  34. */
  35. constructor() {
  36. super(DATA_URL, REFRESH_TIME);
  37. let colors = ThemeManager.getCurrentThemeVariables();
  38. stateColors[MACHINE_STATES.TERMINE] = colors.proxiwashFinishedColor;
  39. stateColors[MACHINE_STATES.DISPONIBLE] = colors.proxiwashReadyColor;
  40. stateColors[MACHINE_STATES["EN COURS"]] = colors.proxiwashRunningColor;
  41. stateColors[MACHINE_STATES.HS] = colors.proxiwashBrokenColor;
  42. stateColors[MACHINE_STATES.ERREUR] = colors.proxiwashErrorColor;
  43. stateStrings[MACHINE_STATES.TERMINE] = i18n.t('proxiwashScreen.states.finished');
  44. stateStrings[MACHINE_STATES.DISPONIBLE] = i18n.t('proxiwashScreen.states.ready');
  45. stateStrings[MACHINE_STATES["EN COURS"]] = i18n.t('proxiwashScreen.states.running');
  46. stateStrings[MACHINE_STATES.HS] = i18n.t('proxiwashScreen.states.broken');
  47. stateStrings[MACHINE_STATES.ERREUR] = i18n.t('proxiwashScreen.states.error');
  48. modalStateStrings[MACHINE_STATES.TERMINE] = i18n.t('proxiwashScreen.modal.finished');
  49. modalStateStrings[MACHINE_STATES.DISPONIBLE] = i18n.t('proxiwashScreen.modal.ready');
  50. modalStateStrings[MACHINE_STATES["EN COURS"]] = i18n.t('proxiwashScreen.modal.running');
  51. modalStateStrings[MACHINE_STATES.HS] = i18n.t('proxiwashScreen.modal.broken');
  52. modalStateStrings[MACHINE_STATES.ERREUR] = i18n.t('proxiwashScreen.modal.error');
  53. stateIcons[MACHINE_STATES.TERMINE] = 'check-circle';
  54. stateIcons[MACHINE_STATES.DISPONIBLE] = 'radiobox-blank';
  55. stateIcons[MACHINE_STATES["EN COURS"]] = 'progress-check';
  56. stateIcons[MACHINE_STATES.HS] = 'alert-octagram-outline';
  57. stateIcons[MACHINE_STATES.ERREUR] = 'alert';
  58. // let dataString = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.current;
  59. this.state = {
  60. refreshing: false,
  61. firstLoading: true,
  62. fetchedData: {},
  63. // machinesWatched: JSON.parse(dataString),
  64. machinesWatched: [],
  65. };
  66. this.setMinTimeRefresh(30);
  67. }
  68. /**
  69. * Setup notification channel for android and add listeners to detect notifications fired
  70. */
  71. componentDidMount() {
  72. super.componentDidMount();
  73. if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') {
  74. // Get latest watchlist from server
  75. NotificationsManager.getMachineNotificationWatchlist((fetchedList) => {
  76. this.setState({machinesWatched: fetchedList})
  77. });
  78. // Get updated watchlist after received notification
  79. Expo.Notifications.addListener((notification) => {
  80. NotificationsManager.getMachineNotificationWatchlist((fetchedList) => {
  81. this.setState({machinesWatched: fetchedList})
  82. });
  83. });
  84. if (Platform.OS === 'android') {
  85. Expo.Notifications.createChannelAndroidAsync('reminders', {
  86. name: 'Reminders',
  87. priority: 'max',
  88. vibrate: [0, 250, 250, 250],
  89. });
  90. }
  91. }
  92. }
  93. getHeaderTranslation() {
  94. return i18n.t("screens.proxiwash");
  95. }
  96. getUpdateToastTranslations() {
  97. return [i18n.t("proxiwashScreen.listUpdated"), i18n.t("proxiwashScreen.listUpdateFail")];
  98. }
  99. getDryersKeyExtractor(item: Object) {
  100. return item !== undefined ? "dryer" + item.number : undefined;
  101. }
  102. getWashersKeyExtractor(item: Object) {
  103. return item !== undefined ? "washer" + item.number : undefined;
  104. }
  105. /**
  106. * Setup notifications for the machine with the given ID.
  107. * One notification will be sent at the end of the program.
  108. * Another will be send a few minutes before the end, based on the value of reminderNotifTime
  109. *
  110. * @param machineId The machine's ID
  111. * @returns {Promise<void>}
  112. */
  113. setupNotifications(machineId: string) {
  114. if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') {
  115. if (!this.isMachineWatched(machineId)) {
  116. NotificationsManager.setupMachineNotification(machineId, true);
  117. this.saveNotificationToState(machineId);
  118. } else
  119. this.disableNotification(machineId);
  120. } else {
  121. this.showNotificationsDisabledWarning();
  122. }
  123. }
  124. showNotificationsDisabledWarning() {
  125. Alert.alert(
  126. i18n.t("proxiwashScreen.modal.notificationErrorTitle"),
  127. i18n.t("proxiwashScreen.modal.notificationErrorDescription"),
  128. );
  129. }
  130. /**
  131. * Stop scheduled notifications for the machine of the given ID.
  132. * This will also remove the notification if it was already shown.
  133. *
  134. * @param machineId The machine's ID
  135. */
  136. disableNotification(machineId: string) {
  137. let data = this.state.machinesWatched;
  138. if (data.length > 0) {
  139. let arrayIndex = data.indexOf(machineId);
  140. if (arrayIndex !== -1) {
  141. NotificationsManager.setupMachineNotification(machineId, false);
  142. this.removeNotificationFroState(arrayIndex);
  143. }
  144. }
  145. }
  146. /**
  147. * Add the given notifications associated to a machine ID to the watchlist, and save the array to the preferences
  148. *
  149. * @param machineId
  150. */
  151. saveNotificationToState(machineId: string) {
  152. let data = this.state.machinesWatched;
  153. data.push(machineId);
  154. this.updateNotificationState(data);
  155. }
  156. /**
  157. * remove the given index from the watchlist array and save it to preferences
  158. *
  159. * @param index
  160. */
  161. removeNotificationFroState(index: number) {
  162. let data = this.state.machinesWatched;
  163. data.splice(index, 1);
  164. this.updateNotificationState(data);
  165. }
  166. /**
  167. * Set the given data as the watchlist and save it to preferences
  168. *
  169. * @param data
  170. */
  171. updateNotificationState(data: Array<Object>) {
  172. this.setState({machinesWatched: data});
  173. // let prefKey = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.key;
  174. // AsyncStorageManager.getInstance().savePref(prefKey, JSON.stringify(data));
  175. }
  176. /**
  177. * Checks whether the machine of the given ID has scheduled notifications
  178. *
  179. * @param machineID The machine's ID
  180. * @returns {boolean}
  181. */
  182. isMachineWatched(machineID: string) {
  183. return this.state.machinesWatched.indexOf(machineID) !== -1;
  184. }
  185. createDataset(fetchedData: Object) {
  186. return [
  187. {
  188. title: i18n.t('proxiwashScreen.washers'),
  189. icon: 'washing-machine',
  190. data: fetchedData.washers === undefined ? [] : fetchedData.washers,
  191. extraData: super.state,
  192. keyExtractor: this.getWashersKeyExtractor
  193. },
  194. {
  195. title: i18n.t('proxiwashScreen.dryers'),
  196. icon: 'tumble-dryer',
  197. data: fetchedData.dryers === undefined ? [] : fetchedData.dryers,
  198. extraData: super.state,
  199. keyExtractor: this.getDryersKeyExtractor
  200. },
  201. ];
  202. }
  203. hasTabs(): boolean {
  204. return true;
  205. }
  206. /**
  207. * Show an alert fo a machine, allowing to enable/disable notifications if running
  208. *
  209. * @param title
  210. * @param item
  211. * @param isDryer
  212. */
  213. showAlert(title: string, item: Object, isDryer: boolean) {
  214. let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}];
  215. let message = modalStateStrings[MACHINE_STATES[item.state]];
  216. if (MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"]) {
  217. buttons = [
  218. {
  219. text: this.isMachineWatched(item.number) ?
  220. i18n.t("proxiwashScreen.modal.disableNotifications") :
  221. i18n.t("proxiwashScreen.modal.enableNotifications"),
  222. onPress: () => this.setupNotifications(item.number)
  223. },
  224. {
  225. text: i18n.t("proxiwashScreen.modal.cancel")
  226. }
  227. ];
  228. message = i18n.t('proxiwashScreen.modal.running',
  229. {
  230. start: item.startTime,
  231. end: item.endTime,
  232. remaining: item.remainingTime
  233. });
  234. } else if (MACHINE_STATES[item.state] === MACHINE_STATES.DISPONIBLE) {
  235. if (isDryer)
  236. message += '\n' + i18n.t('proxiwashScreen.dryersTariff');
  237. else
  238. message += '\n' + i18n.t('proxiwashScreen.washersTariff');
  239. }
  240. Alert.alert(
  241. title,
  242. message,
  243. buttons
  244. );
  245. }
  246. getRightButton(): * {
  247. return (
  248. <Touchable
  249. style={{padding: 6}}
  250. onPress={() => this.props.navigation.navigate('ProxiwashAboutScreen')}>
  251. <CustomMaterialIcon
  252. color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
  253. icon="information"/>
  254. </Touchable>
  255. );
  256. }
  257. /**
  258. * Get list item to be rendered
  259. *
  260. * @param item The object containing the item's FetchedData
  261. * @param section The object describing the current SectionList section
  262. * @param data The full FetchedData used by the SectionList
  263. * @returns {React.Node}
  264. */
  265. getRenderItem(item: Object, section: Object, data: Object) {
  266. let isMachineRunning = MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"];
  267. let machineName = (section.title === i18n.t('proxiwashScreen.dryers') ? i18n.t('proxiwashScreen.dryer') : i18n.t('proxiwashScreen.washer')) + ' n°' + item.number;
  268. let isDryer = section.title === i18n.t('proxiwashScreen.dryers');
  269. return (
  270. <Card style={{
  271. flex: 0,
  272. height: 64,
  273. marginLeft: 10,
  274. marginRight: 10
  275. }}>
  276. <CardItem
  277. style={{
  278. backgroundColor: stateColors[MACHINE_STATES[item.state]],
  279. paddingRight: 0,
  280. paddingLeft: 0,
  281. height: '100%',
  282. }}
  283. >
  284. <View style={{
  285. height: 64,
  286. position: 'absolute',
  287. right: 0,
  288. width: item.donePercent !== '' ? (100 - parseInt(item.donePercent)).toString() + '%' : 0,
  289. backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor
  290. }}/>
  291. <PlatformTouchable
  292. onPress={() => this.showAlert(machineName, item, isDryer)}
  293. style={{
  294. height: 64,
  295. position: 'absolute',
  296. zIndex: 10, // Make sure the button is above the text
  297. right: 0,
  298. width: '100%'
  299. }}
  300. >
  301. <View/>
  302. </PlatformTouchable>
  303. <Left style={{marginLeft: 10}}>
  304. <CustomMaterialIcon
  305. icon={isDryer ? 'tumble-dryer' : 'washing-machine'}
  306. fontSize={30}
  307. />
  308. <Body>
  309. <Text>
  310. {machineName + ' '}
  311. {this.isMachineWatched(item.number) ?
  312. <CustomMaterialIcon
  313. icon='bell-ring'
  314. color={ThemeManager.getCurrentThemeVariables().brandPrimary}
  315. fontSize={20}
  316. /> : ''}
  317. </Text>
  318. <Text note>
  319. {isMachineRunning ? item.startTime + '/' + item.endTime : ''}
  320. </Text>
  321. </Body>
  322. </Left>
  323. <Right style={{marginRight: 10}}>
  324. <Text style={MACHINE_STATES[item.state] === MACHINE_STATES.TERMINE ?
  325. {fontWeight: 'bold'} : {}}
  326. >
  327. {stateStrings[MACHINE_STATES[item.state]]}
  328. </Text>
  329. <CustomMaterialIcon icon={stateIcons[MACHINE_STATES[item.state]]}
  330. fontSize={25}
  331. />
  332. </Right>
  333. </CardItem>
  334. </Card>);
  335. }
  336. }