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.

ProxiwashScreen.js 15KB

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