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.

SettingsScreen.tsx 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. * Copyright (c) 2019 - 2020 Arnaud Vergnet.
  3. *
  4. * This file is part of Campus INSAT.
  5. *
  6. * Campus INSAT is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Campus INSAT is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import * as React from 'react';
  20. import {View} from 'react-native';
  21. import i18n from 'i18n-js';
  22. import {
  23. RadioButton,
  24. Card,
  25. List,
  26. Switch,
  27. ToggleButton,
  28. withTheme,
  29. } from 'react-native-paper';
  30. import {Appearance} from 'react-native-appearance';
  31. import {StackNavigationProp} from '@react-navigation/stack';
  32. import ThemeManager from '../../../managers/ThemeManager';
  33. import AsyncStorageManager from '../../../managers/AsyncStorageManager';
  34. import CustomSlider from '../../../components/Overrides/CustomSlider';
  35. import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
  36. type PropsType = {
  37. navigation: StackNavigationProp<any>;
  38. theme: ReactNativePaper.Theme;
  39. };
  40. type StateType = {
  41. nightMode: boolean;
  42. nightModeFollowSystem: boolean;
  43. startScreenPickerSelected: string;
  44. selectedWash: string;
  45. isDebugUnlocked: boolean;
  46. };
  47. /**
  48. * Class defining the Settings screen. This screen shows controls to modify app preferences.
  49. */
  50. class SettingsScreen extends React.Component<PropsType, StateType> {
  51. savedNotificationReminder: number;
  52. /**
  53. * Loads user preferences into state
  54. */
  55. constructor(props: PropsType) {
  56. super(props);
  57. const notifReminder = AsyncStorageManager.getString(
  58. AsyncStorageManager.PREFERENCES.proxiwashNotifications.key,
  59. );
  60. this.savedNotificationReminder = parseInt(notifReminder, 10);
  61. if (Number.isNaN(this.savedNotificationReminder)) {
  62. this.savedNotificationReminder = 0;
  63. }
  64. this.state = {
  65. nightMode: ThemeManager.getNightMode(),
  66. nightModeFollowSystem:
  67. AsyncStorageManager.getBool(
  68. AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
  69. ) && Appearance.getColorScheme() !== 'no-preference',
  70. startScreenPickerSelected: AsyncStorageManager.getString(
  71. AsyncStorageManager.PREFERENCES.defaultStartScreen.key,
  72. ),
  73. selectedWash: AsyncStorageManager.getString(
  74. AsyncStorageManager.PREFERENCES.selectedWash.key,
  75. ),
  76. isDebugUnlocked: AsyncStorageManager.getBool(
  77. AsyncStorageManager.PREFERENCES.debugUnlocked.key,
  78. ),
  79. };
  80. }
  81. /**
  82. * Saves the value for the proxiwash reminder notification time
  83. *
  84. * @param value The value to store
  85. */
  86. onProxiwashNotifPickerValueChange = (value: number) => {
  87. AsyncStorageManager.set(
  88. AsyncStorageManager.PREFERENCES.proxiwashNotifications.key,
  89. value,
  90. );
  91. };
  92. /**
  93. * Saves the value for the proxiwash reminder notification time
  94. *
  95. * @param value The value to store
  96. */
  97. onStartScreenPickerValueChange = (value: string) => {
  98. if (value != null) {
  99. this.setState({startScreenPickerSelected: value});
  100. AsyncStorageManager.set(
  101. AsyncStorageManager.PREFERENCES.defaultStartScreen.key,
  102. value,
  103. );
  104. }
  105. };
  106. /**
  107. * Returns a picker allowing the user to select the proxiwash reminder notification time
  108. *
  109. * @returns {React.Node}
  110. */
  111. getProxiwashNotifPicker() {
  112. const {theme} = this.props;
  113. return (
  114. <CustomSlider
  115. style={{flex: 1, marginHorizontal: 10, height: 50}}
  116. minimumValue={0}
  117. maximumValue={10}
  118. step={1}
  119. value={this.savedNotificationReminder}
  120. onValueChange={this.onProxiwashNotifPickerValueChange}
  121. thumbTintColor={theme.colors.primary}
  122. minimumTrackTintColor={theme.colors.primary}
  123. />
  124. );
  125. }
  126. /**
  127. * Returns a radio picker allowing the user to select the proxiwash
  128. *
  129. * @returns {React.Node}
  130. */
  131. getProxiwashChangePicker() {
  132. const {selectedWash} = this.state;
  133. return (
  134. <RadioButton.Group
  135. onValueChange={this.onSelectWashValueChange}
  136. value={selectedWash}>
  137. <RadioButton.Item
  138. label={i18n.t('screens.proxiwash.washinsa.title')}
  139. value="washinsa"
  140. />
  141. <RadioButton.Item
  142. label={i18n.t('screens.proxiwash.tripodeB.title')}
  143. value="tripodeB"
  144. />
  145. </RadioButton.Group>
  146. );
  147. }
  148. /**
  149. * Returns a picker allowing the user to select the start screen
  150. *
  151. * @returns {React.Node}
  152. */
  153. getStartScreenPicker() {
  154. const {startScreenPickerSelected} = this.state;
  155. return (
  156. <ToggleButton.Row
  157. onValueChange={this.onStartScreenPickerValueChange}
  158. value={startScreenPickerSelected}
  159. style={{marginLeft: 'auto', marginRight: 'auto'}}>
  160. <ToggleButton icon="account-circle" value="services" />
  161. <ToggleButton icon="tshirt-crew" value="proxiwash" />
  162. <ToggleButton icon="triangle" value="home" />
  163. <ToggleButton icon="calendar-range" value="planning" />
  164. <ToggleButton icon="clock" value="planex" />
  165. </ToggleButton.Row>
  166. );
  167. }
  168. /**
  169. * Toggles night mode and saves it to preferences
  170. */
  171. onToggleNightMode = () => {
  172. const {nightMode} = this.state;
  173. ThemeManager.getInstance().setNightMode(!nightMode);
  174. this.setState({nightMode: !nightMode});
  175. };
  176. onToggleNightModeFollowSystem = () => {
  177. const {nightModeFollowSystem} = this.state;
  178. const value = !nightModeFollowSystem;
  179. this.setState({nightModeFollowSystem: value});
  180. AsyncStorageManager.set(
  181. AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
  182. value,
  183. );
  184. if (value) {
  185. const nightMode = Appearance.getColorScheme() === 'dark';
  186. ThemeManager.getInstance().setNightMode(nightMode);
  187. this.setState({nightMode});
  188. }
  189. };
  190. /**
  191. * Gets a list item using a checkbox control
  192. *
  193. * @param onPressCallback The callback when the checkbox state changes
  194. * @param icon The icon name to display on the list item
  195. * @param title The text to display as this list item title
  196. * @param subtitle The text to display as this list item subtitle
  197. * @param state The current state of the switch
  198. * @returns {React.Node}
  199. */
  200. static getToggleItem(
  201. onPressCallback: () => void,
  202. icon: string,
  203. title: string,
  204. subtitle: string,
  205. state: boolean,
  206. ) {
  207. return (
  208. <List.Item
  209. title={title}
  210. description={subtitle}
  211. left={(props) => (
  212. <List.Icon color={props.color} style={props.style} icon={icon} />
  213. )}
  214. right={() => <Switch value={state} onValueChange={onPressCallback} />}
  215. />
  216. );
  217. }
  218. getNavigateItem(
  219. route: string,
  220. icon: string,
  221. title: string,
  222. subtitle: string,
  223. onLongPress?: () => void,
  224. ) {
  225. const {navigation} = this.props;
  226. return (
  227. <List.Item
  228. title={title}
  229. description={subtitle}
  230. onPress={() => {
  231. navigation.navigate(route);
  232. }}
  233. left={(props) => (
  234. <List.Icon color={props.color} style={props.style} icon={icon} />
  235. )}
  236. right={(props) => (
  237. <List.Icon
  238. color={props.color}
  239. style={props.style}
  240. icon="chevron-right"
  241. />
  242. )}
  243. onLongPress={onLongPress}
  244. />
  245. );
  246. }
  247. /**
  248. * Saves the value for the proxiwash selected wash
  249. *
  250. * @param value The value to store
  251. */
  252. onSelectWashValueChange = (value: string) => {
  253. if (value != null) {
  254. this.setState({selectedWash: value});
  255. AsyncStorageManager.set(
  256. AsyncStorageManager.PREFERENCES.selectedWash.key,
  257. value,
  258. );
  259. }
  260. };
  261. /**
  262. * Unlocks debug mode and saves its state to user preferences
  263. */
  264. unlockDebugMode = () => {
  265. this.setState({isDebugUnlocked: true});
  266. AsyncStorageManager.set(
  267. AsyncStorageManager.PREFERENCES.debugUnlocked.key,
  268. true,
  269. );
  270. };
  271. render() {
  272. const {nightModeFollowSystem, nightMode, isDebugUnlocked} = this.state;
  273. return (
  274. <CollapsibleScrollView>
  275. <Card style={{margin: 5}}>
  276. <Card.Title title={i18n.t('screens.settings.generalCard')} />
  277. <List.Section>
  278. {Appearance.getColorScheme() !== 'no-preference'
  279. ? SettingsScreen.getToggleItem(
  280. this.onToggleNightModeFollowSystem,
  281. 'theme-light-dark',
  282. i18n.t('screens.settings.nightModeAuto'),
  283. i18n.t('screens.settings.nightModeAutoSub'),
  284. nightModeFollowSystem,
  285. )
  286. : null}
  287. {Appearance.getColorScheme() === 'no-preference' ||
  288. !nightModeFollowSystem
  289. ? SettingsScreen.getToggleItem(
  290. this.onToggleNightMode,
  291. 'theme-light-dark',
  292. i18n.t('screens.settings.nightMode'),
  293. nightMode
  294. ? i18n.t('screens.settings.nightModeSubOn')
  295. : i18n.t('screens.settings.nightModeSubOff'),
  296. nightMode,
  297. )
  298. : null}
  299. <List.Item
  300. title={i18n.t('screens.settings.startScreen')}
  301. description={i18n.t('screens.settings.startScreenSub')}
  302. left={(props) => (
  303. <List.Icon
  304. color={props.color}
  305. style={props.style}
  306. icon="power"
  307. />
  308. )}
  309. />
  310. {this.getStartScreenPicker()}
  311. {this.getNavigateItem(
  312. 'dashboard-edit',
  313. 'view-dashboard',
  314. i18n.t('screens.settings.dashboard'),
  315. i18n.t('screens.settings.dashboardSub'),
  316. )}
  317. </List.Section>
  318. </Card>
  319. <Card style={{margin: 5}}>
  320. <Card.Title title="Proxiwash" />
  321. <List.Section>
  322. <List.Item
  323. title={i18n.t('screens.settings.proxiwashNotifReminder')}
  324. description={i18n.t('screens.settings.proxiwashNotifReminderSub')}
  325. left={(props) => (
  326. <List.Icon
  327. color={props.color}
  328. style={props.style}
  329. icon="washing-machine"
  330. />
  331. )}
  332. />
  333. <View style={{marginLeft: 30}}>
  334. {this.getProxiwashNotifPicker()}
  335. </View>
  336. <List.Item
  337. title={i18n.t('screens.settings.proxiwashChangeWash')}
  338. description={i18n.t('screens.settings.proxiwashChangeWashSub')}
  339. left={(props) => (
  340. <List.Icon
  341. color={props.color}
  342. style={props.style}
  343. icon="washing-machine"
  344. />
  345. )}
  346. />
  347. <View style={{marginLeft: 30}}>
  348. {this.getProxiwashChangePicker()}
  349. </View>
  350. </List.Section>
  351. </Card>
  352. <Card style={{margin: 5}}>
  353. <Card.Title title={i18n.t('screens.settings.information')} />
  354. <List.Section>
  355. {isDebugUnlocked
  356. ? this.getNavigateItem(
  357. 'debug',
  358. 'bug-check',
  359. i18n.t('screens.debug.title'),
  360. '',
  361. )
  362. : null}
  363. {this.getNavigateItem(
  364. 'about',
  365. 'information',
  366. i18n.t('screens.about.title'),
  367. i18n.t('screens.about.buttonDesc'),
  368. this.unlockDebugMode,
  369. )}
  370. {this.getNavigateItem(
  371. 'feedback',
  372. 'comment-quote',
  373. i18n.t('screens.feedback.homeButtonTitle'),
  374. i18n.t('screens.feedback.homeButtonSubtitle'),
  375. )}
  376. </List.Section>
  377. </Card>
  378. </CollapsibleScrollView>
  379. );
  380. }
  381. }
  382. export default withTheme(SettingsScreen);