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.

AuthenticatedScreen.js 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // @flow
  2. import * as React from 'react';
  3. import ConnectionManager from "../../managers/ConnectionManager";
  4. import {ERROR_TYPE} from "../../utils/WebData";
  5. import ErrorView from "../Screens/ErrorView";
  6. import BasicLoadingScreen from "../Screens/BasicLoadingScreen";
  7. import {StackNavigationProp} from "@react-navigation/stack";
  8. type Props = {
  9. navigation: StackNavigationProp,
  10. requests: Array<{
  11. link: string,
  12. params: Object,
  13. mandatory: boolean
  14. }>,
  15. renderFunction: (Array<{ [key: string]: any } | null>) => React.Node,
  16. errorViewOverride?: Array<{
  17. errorCode: number,
  18. message: string,
  19. icon: string,
  20. showRetryButton: boolean
  21. }>,
  22. }
  23. type State = {
  24. loading: boolean,
  25. }
  26. class AuthenticatedScreen extends React.Component<Props, State> {
  27. state = {
  28. loading: true,
  29. };
  30. currentUserToken: string | null;
  31. connectionManager: ConnectionManager;
  32. errors: Array<number>;
  33. fetchedData: Array<{ [key: string]: any } | null>;
  34. constructor(props: Object) {
  35. super(props);
  36. this.connectionManager = ConnectionManager.getInstance();
  37. this.props.navigation.addListener('focus', this.onScreenFocus);
  38. this.fetchedData = new Array(this.props.requests.length);
  39. this.errors = new Array(this.props.requests.length);
  40. }
  41. /**
  42. * Refreshes screen if user changed
  43. */
  44. onScreenFocus = () => {
  45. if (this.currentUserToken !== this.connectionManager.getToken()) {
  46. this.currentUserToken = this.connectionManager.getToken();
  47. this.fetchData();
  48. }
  49. };
  50. /**
  51. * Fetches the data from the server.
  52. *
  53. * If the user is not logged in errorCode is set to BAD_TOKEN and all requests fail.
  54. *
  55. * If the user is logged in, send all requests.
  56. */
  57. fetchData = () => {
  58. if (!this.state.loading)
  59. this.setState({loading: true});
  60. if (this.connectionManager.isLoggedIn()) {
  61. for (let i = 0; i < this.props.requests.length; i++) {
  62. this.connectionManager.authenticatedRequest(
  63. this.props.requests[i].link,
  64. this.props.requests[i].params)
  65. .then((data) => {
  66. this.onRequestFinished(data, i, -1);
  67. })
  68. .catch((error) => {
  69. this.onRequestFinished(null, i, error);
  70. });
  71. }
  72. } else {
  73. for (let i = 0; i < this.props.requests.length; i++) {
  74. this.onRequestFinished(null, i, ERROR_TYPE.BAD_TOKEN);
  75. }
  76. }
  77. };
  78. /**
  79. * Callback used when a request finishes, successfully or not.
  80. * Saves data and error code.
  81. * If the token is invalid, logout the user and open the login screen.
  82. * If the last request was received, stop the loading screen.
  83. *
  84. * @param data The data fetched from the server
  85. * @param index The index for the data
  86. * @param error The error code received
  87. */
  88. onRequestFinished(data: { [key: string]: any } | null, index: number, error: number) {
  89. if (index >= 0 && index < this.props.requests.length) {
  90. this.fetchedData[index] = data;
  91. this.errors[index] = error;
  92. }
  93. if (error === ERROR_TYPE.BAD_TOKEN) // Token expired, logout user
  94. this.connectionManager.disconnect();
  95. if (this.allRequestsFinished())
  96. this.setState({loading: false});
  97. }
  98. /**
  99. * Checks if all requests finished processing
  100. *
  101. * @return {boolean} True if all finished
  102. */
  103. allRequestsFinished() {
  104. let finished = true;
  105. for (let i = 0; i < this.fetchedData.length; i++) {
  106. if (this.fetchedData[i] === undefined) {
  107. finished = false;
  108. break;
  109. }
  110. }
  111. return finished;
  112. }
  113. /**
  114. * Checks if all requests have finished successfully.
  115. * This will return false only if a mandatory request failed.
  116. * All non-mandatory requests can fail without impacting the return value.
  117. *
  118. * @return {boolean} True if all finished successfully
  119. */
  120. allRequestsValid() {
  121. let valid = true;
  122. for (let i = 0; i < this.fetchedData.length; i++) {
  123. if (this.fetchedData[i] === null && this.props.requests[i].mandatory) {
  124. valid = false;
  125. break;
  126. }
  127. }
  128. return valid;
  129. }
  130. /**
  131. * Gets the error to render.
  132. * Non-mandatory requests are ignored.
  133. *
  134. *
  135. * @return {number} The error code or ERROR_TYPE.SUCCESS if no error was found
  136. */
  137. getError() {
  138. for (let i = 0; i < this.errors.length; i++) {
  139. if (this.errors[i] !== 0 && this.props.requests[i].mandatory) {
  140. return this.errors[i];
  141. }
  142. }
  143. return ERROR_TYPE.SUCCESS;
  144. }
  145. /**
  146. * Gets the error view to display in case of error
  147. *
  148. * @return {*}
  149. */
  150. getErrorRender() {
  151. const errorCode = this.getError();
  152. let shouldOverride = false;
  153. let override = null;
  154. const overrideList = this.props.errorViewOverride;
  155. if (overrideList != null) {
  156. for (let i = 0; i < overrideList.length; i++) {
  157. if (overrideList[i].errorCode === errorCode) {
  158. shouldOverride = true;
  159. override = overrideList[i];
  160. break;
  161. }
  162. }
  163. }
  164. if (shouldOverride && override != null) {
  165. return (
  166. <ErrorView
  167. {...this.props}
  168. icon={override.icon}
  169. message={override.message}
  170. showRetryButton={override.showRetryButton}
  171. />
  172. );
  173. } else {
  174. return (
  175. <ErrorView
  176. {...this.props}
  177. errorCode={errorCode}
  178. onRefresh={this.fetchData}
  179. />
  180. );
  181. }
  182. }
  183. /**
  184. * Reloads the data, to be called using ref by parent components
  185. */
  186. reload() {
  187. this.fetchData();
  188. }
  189. render() {
  190. return (
  191. this.state.loading
  192. ? <BasicLoadingScreen/>
  193. : (this.allRequestsValid()
  194. ? this.props.renderFunction(this.fetchedData)
  195. : this.getErrorRender())
  196. );
  197. }
  198. }
  199. export default AuthenticatedScreen;