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.

ScannerScreen.js 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // @flow
  2. import * as React from 'react';
  3. import {Linking, Platform, StyleSheet, View} from "react-native";
  4. import {Button, Text, withTheme} from 'react-native-paper';
  5. import {RNCamera} from 'react-native-camera';
  6. import {BarcodeMask} from '@nartc/react-native-barcode-mask';
  7. import URLHandler from "../../utils/URLHandler";
  8. import AlertDialog from "../../components/Dialogs/AlertDialog";
  9. import i18n from 'i18n-js';
  10. import CustomTabBar from "../../components/Tabbar/CustomTabBar";
  11. import LoadingConfirmDialog from "../../components/Dialogs/LoadingConfirmDialog";
  12. import {PERMISSIONS, request, RESULTS} from 'react-native-permissions';
  13. type Props = {};
  14. type State = {
  15. hasPermission: boolean,
  16. scanned: boolean,
  17. dialogVisible: boolean,
  18. dialogTitle: string,
  19. dialogMessage: string,
  20. loading: boolean,
  21. };
  22. class ScannerScreen extends React.Component<Props, State> {
  23. state = {
  24. hasPermission: false,
  25. scanned: false,
  26. dialogVisible: false,
  27. dialogTitle: "",
  28. dialogMessage: "",
  29. loading: false,
  30. };
  31. constructor() {
  32. super();
  33. }
  34. componentDidMount() {
  35. this.requestPermissions();
  36. }
  37. /**
  38. * Requests permission to use the camera
  39. */
  40. requestPermissions = () => {
  41. if (Platform.OS === 'android')
  42. request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus)
  43. else
  44. request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus)
  45. };
  46. /**
  47. * Updates the state permission status
  48. *
  49. * @param result
  50. */
  51. updatePermissionStatus = (result) => this.setState({hasPermission: result === RESULTS.GRANTED});
  52. /**
  53. * Opens scanned link if it is a valid app link or shows and error dialog
  54. *
  55. * @param type The barcode type
  56. * @param data The scanned value
  57. */
  58. handleCodeScanned = ({type, data}) => {
  59. if (!URLHandler.isUrlValid(data))
  60. this.showErrorDialog();
  61. else {
  62. this.showOpeningDialog();
  63. Linking.openURL(data);
  64. }
  65. };
  66. /**
  67. * Gets a view asking user for permission to use the camera
  68. *
  69. * @returns {*}
  70. */
  71. getPermissionScreen() {
  72. return <View style={{marginLeft: 10, marginRight: 10}}>
  73. <Text>{i18n.t("screens.scanner.permissions.error")}</Text>
  74. <Button
  75. icon="camera"
  76. mode="contained"
  77. onPress={this.requestPermissions}
  78. style={{
  79. marginTop: 10,
  80. marginLeft: 'auto',
  81. marginRight: 'auto',
  82. }}
  83. >
  84. {i18n.t("screens.scanner.permissions.button")}
  85. </Button>
  86. </View>
  87. }
  88. /**
  89. * Shows a dialog indicating how to use the scanner
  90. */
  91. showHelpDialog = () => {
  92. this.setState({
  93. dialogVisible: true,
  94. scanned: true,
  95. dialogTitle: i18n.t("screens.scanner.help.title"),
  96. dialogMessage: i18n.t("screens.scanner.help.message"),
  97. });
  98. };
  99. /**
  100. * Shows a loading dialog
  101. */
  102. showOpeningDialog = () => {
  103. this.setState({
  104. loading: true,
  105. scanned: true,
  106. });
  107. };
  108. /**
  109. * Shows a dialog indicating the user the scanned code was invalid
  110. */
  111. showErrorDialog() {
  112. this.setState({
  113. dialogVisible: true,
  114. scanned: true,
  115. dialogTitle: i18n.t("screens.scanner.error.title"),
  116. dialogMessage: i18n.t("screens.scanner.error.message"),
  117. });
  118. }
  119. /**
  120. * Hide any dialog
  121. */
  122. onDialogDismiss = () => this.setState({
  123. dialogVisible: false,
  124. scanned: false,
  125. });
  126. /**
  127. * Gets a view with the scanner.
  128. * This scanner uses the back camera, can only scan qr codes and has a square mask on the center.
  129. * The mask is only for design purposes as a code is scanned as soon as it enters the camera view
  130. *
  131. * @returns {*}
  132. */
  133. getScanner() {
  134. return (
  135. <RNCamera
  136. onBarCodeRead={this.state.scanned ? undefined : this.handleCodeScanned}
  137. type={RNCamera.Constants.Type.back}
  138. barCodeScannerSettings={{
  139. barCodeTypes: [RNCamera.Constants.BarCodeType.qr],
  140. }}
  141. style={StyleSheet.absoluteFill}
  142. captureAudio={false}
  143. >
  144. <BarcodeMask
  145. backgroundColor={"#000"}
  146. maskOpacity={0.5}
  147. animatedLineThickness={1}
  148. animationDuration={1000}
  149. width={250}
  150. height={250}
  151. />
  152. </RNCamera>
  153. );
  154. }
  155. render() {
  156. return (
  157. <View style={{
  158. ...styles.container,
  159. marginBottom: CustomTabBar.TAB_BAR_HEIGHT
  160. }}>
  161. {this.state.hasPermission
  162. ? this.getScanner()
  163. : this.getPermissionScreen()
  164. }
  165. <Button
  166. icon="information"
  167. mode="contained"
  168. onPress={this.showHelpDialog}
  169. style={styles.button}
  170. >
  171. {i18n.t("screens.scanner.help.button")}
  172. </Button>
  173. <AlertDialog
  174. visible={this.state.dialogVisible}
  175. onDismiss={this.onDialogDismiss}
  176. title={this.state.dialogTitle}
  177. message={this.state.dialogMessage}
  178. />
  179. <LoadingConfirmDialog
  180. visible={this.state.loading}
  181. titleLoading={i18n.t("general.loading")}
  182. startLoading={true}
  183. />
  184. </View>
  185. );
  186. }
  187. }
  188. const styles = StyleSheet.create({
  189. container: {
  190. flex: 1,
  191. justifyContent: 'center',
  192. },
  193. button: {
  194. position: 'absolute',
  195. bottom: 20,
  196. width: '80%',
  197. left: '10%'
  198. },
  199. });
  200. export default withTheme(ScannerScreen);