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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // @flow
  2. import * as React from 'react';
  3. import {StyleSheet, View} from "react-native";
  4. import {Button, Text, withTheme} from 'react-native-paper';
  5. import {BarCodeScanner} from "expo-barcode-scanner";
  6. import {Camera} from 'expo-camera';
  7. import URLHandler from "../../utils/URLHandler";
  8. import {Linking} from "expo";
  9. import AlertDialog from "../../components/Dialogs/AlertDialog";
  10. import i18n from 'i18n-js';
  11. import CustomTabBar from "../../components/Tabbar/CustomTabBar";
  12. import LoadingConfirmDialog from "../../components/Dialogs/LoadingConfirmDialog";
  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. requestPermissions = () => Camera.requestPermissionsAsync().then(this.updatePermissionStatus);
  38. updatePermissionStatus = ({status}) => this.setState({hasPermission: status === "granted"});
  39. handleCodeScanned = ({type, data}) => {
  40. if (!URLHandler.isUrlValid(data))
  41. this.showErrorDialog();
  42. else {
  43. this.showOpeningDialog();
  44. Linking.openURL(data);
  45. }
  46. };
  47. getPermissionScreen() {
  48. return <View style={{marginLeft: 10, marginRight: 10}}>
  49. <Text>{i18n.t("scannerScreen.errorPermission")}</Text>
  50. <Button
  51. icon="camera"
  52. mode="contained"
  53. onPress={this.requestPermissions}
  54. style={{
  55. marginTop: 10,
  56. marginLeft: 'auto',
  57. marginRight: 'auto',
  58. }}
  59. >
  60. {i18n.t("scannerScreen.buttonPermission")}
  61. </Button>
  62. </View>
  63. }
  64. getOverlay() {
  65. return (
  66. <View style={{flex: 1}}>
  67. <View style={{flex: 1}}>
  68. <View style={{...overlayBackground, top: 0, height: '10%', width: '80%', left: '10%'}}/>
  69. <View style={{...overlayBackground, left: 0, width: '10%', height: '100%'}}/>
  70. <View style={{...overlayBackground, right: 0, width: '10%', height: '100%'}}/>
  71. <View style={{...overlayBackground, bottom: 0, height: '10%', width: '80%', left: '10%'}}/>
  72. </View>
  73. <View style={styles.overlayTopLeft}>
  74. <View style={{...overlayHorizontalLineStyle, top: 0}}/>
  75. <View style={{...overlayVerticalLineStyle, left: 0}}/>
  76. </View>
  77. <View style={styles.overlayTopRight}>
  78. <View style={{...overlayHorizontalLineStyle, top: 0}}/>
  79. <View style={{...overlayVerticalLineStyle, right: 0}}/>
  80. </View>
  81. <View style={styles.overlayBottomLeft}>
  82. <View style={{...overlayHorizontalLineStyle, bottom: 0}}/>
  83. <View style={{...overlayVerticalLineStyle, left: 0}}/>
  84. </View>
  85. <View style={styles.overlayBottomRight}>
  86. <View style={{...overlayHorizontalLineStyle, bottom: 0}}/>
  87. <View style={{...overlayVerticalLineStyle, right: 0}}/>
  88. </View>
  89. </View>
  90. );
  91. }
  92. showHelpDialog = () => {
  93. this.setState({
  94. dialogVisible: true,
  95. scanned: true,
  96. dialogTitle: i18n.t("scannerScreen.helpTitle"),
  97. dialogMessage: i18n.t("scannerScreen.helpMessage"),
  98. });
  99. };
  100. showOpeningDialog = () => {
  101. this.setState({
  102. loading: true,
  103. scanned: true,
  104. });
  105. };
  106. showErrorDialog() {
  107. this.setState({
  108. dialogVisible: true,
  109. scanned: true,
  110. dialogTitle: i18n.t("scannerScreen.errorTitle"),
  111. dialogMessage: i18n.t("scannerScreen.errorMessage"),
  112. });
  113. }
  114. onDialogDismiss = () => this.setState({
  115. dialogVisible: false,
  116. scanned: false,
  117. });
  118. getScanner() {
  119. return (
  120. <View style={styles.cameraContainer}>
  121. <Camera
  122. onBarCodeScanned={this.state.scanned ? undefined : this.handleCodeScanned}
  123. type={Camera.Constants.Type.back}
  124. barCodeScannerSettings={{
  125. barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
  126. }}
  127. style={StyleSheet.absoluteFill}
  128. ratio={'1:1'}
  129. >
  130. {this.getOverlay()}
  131. </Camera>
  132. </View>
  133. );
  134. }
  135. render() {
  136. return (
  137. <View style={{
  138. ...styles.container,
  139. marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
  140. }}>
  141. {this.state.hasPermission
  142. ? this.getScanner()
  143. : this.getPermissionScreen()
  144. }
  145. <View style={{height: 50}}>
  146. <Button
  147. icon="information"
  148. mode="contained"
  149. onPress={this.showHelpDialog}
  150. style={styles.button}
  151. >
  152. {i18n.t("scannerScreen.helpButton")}
  153. </Button>
  154. </View>
  155. <AlertDialog
  156. visible={this.state.dialogVisible}
  157. onDismiss={this.onDialogDismiss}
  158. title={this.state.dialogTitle}
  159. message={this.state.dialogMessage}
  160. />
  161. <LoadingConfirmDialog
  162. visible={this.state.loading}
  163. titleLoading={i18n.t("general.loading")}
  164. startLoading={true}
  165. />
  166. </View>
  167. );
  168. }
  169. }
  170. const borderOffset = '10%';
  171. const overlayBoxStyle = {
  172. position: 'absolute',
  173. width: 25,
  174. height: 25,
  175. };
  176. const overlayLineStyle = {
  177. position: 'absolute',
  178. backgroundColor: "#fff",
  179. borderRadius: 2,
  180. };
  181. const overlayHorizontalLineStyle = {
  182. ...overlayLineStyle,
  183. width: '100%',
  184. height: 5,
  185. };
  186. const overlayVerticalLineStyle = {
  187. ...overlayLineStyle,
  188. height: '100%',
  189. width: 5,
  190. };
  191. const overlayBackground = {
  192. backgroundColor: "rgba(0,0,0,0.47)",
  193. position: "absolute",
  194. };
  195. const styles = StyleSheet.create({
  196. container: {
  197. flex: 1,
  198. justifyContent: 'center',
  199. },
  200. cameraContainer: {
  201. marginTop: 'auto',
  202. marginBottom: 'auto',
  203. aspectRatio: 1,
  204. width: '100%',
  205. },
  206. button: {
  207. position: 'absolute',
  208. bottom: 5,
  209. width: '80%',
  210. left: '10%'
  211. },
  212. overlayTopLeft: {
  213. ...overlayBoxStyle,
  214. top: borderOffset,
  215. left: borderOffset,
  216. },
  217. overlayTopRight: {
  218. ...overlayBoxStyle,
  219. top: borderOffset,
  220. right: borderOffset,
  221. },
  222. overlayBottomLeft: {
  223. ...overlayBoxStyle,
  224. bottom: borderOffset,
  225. left: borderOffset,
  226. },
  227. overlayBottomRight: {
  228. ...overlayBoxStyle,
  229. bottom: borderOffset,
  230. right: borderOffset,
  231. },
  232. });
  233. export default withTheme(ScannerScreen);