Browse Source

Improved doc

Arnaud Vergnet 3 years ago
parent
commit
401c7d85ef

+ 30
- 1
src/utils/AutoHideHandler.js View File

@@ -4,6 +4,9 @@ import * as React from 'react';
4 4
 
5 5
 const speedOffset = 5;
6 6
 
7
+/**
8
+ * Class used to detect when to show or hide a component based on scrolling
9
+ */
7 10
 export default class AutoHideHandler {
8 11
 
9 12
     lastOffset: number;
@@ -16,16 +19,42 @@ export default class AutoHideHandler {
16 19
         this.isHidden = startHidden;
17 20
     }
18 21
 
22
+    /**
23
+     * Adds a listener to the hide event
24
+     *
25
+     * @param listener
26
+     */
19 27
     addListener(listener: Function) {
20 28
         this.listeners.push(listener);
21 29
     }
22 30
 
31
+    /**
32
+     * Notifies every listener whether they should hide or show.
33
+     *
34
+     * @param shouldHide
35
+     */
23 36
     notifyListeners(shouldHide: boolean) {
24 37
         for (let i = 0; i < this.listeners.length; i++) {
25 38
             this.listeners[i](shouldHide);
26 39
         }
27 40
     }
28 41
 
42
+    /**
43
+     * Callback to be used on the onScroll animated component event.
44
+     *
45
+     * Detects if the current speed exceeds a threshold and notifies listeners to hide or show.
46
+     *
47
+     * The hide even is triggered when the user scrolls down, and the show event on scroll up.
48
+     * This does not take into account the speed when the y coordinate is negative, to prevent hiding on over scroll.
49
+     * (When scrolling up and hitting the top on ios for example)
50
+     *
51
+     * //TODO Known issue:
52
+     * When refreshing a list with the pull down gesture on ios,
53
+     * this can trigger the hide event as it scrolls down the list to show the refresh indicator.
54
+     * Android shows the refresh indicator on top of the list so this is not an issue.
55
+     *
56
+     * @param nativeEvent The scroll event generated by the animated component onScroll prop
57
+     */
29 58
     onScroll({nativeEvent}: Object) {
30 59
         const speed = nativeEvent.contentOffset.y < 0 ? 0 : this.lastOffset - nativeEvent.contentOffset.y;
31 60
         if (speed < -speedOffset && !this.isHidden) { // Go down
@@ -38,4 +67,4 @@ export default class AutoHideHandler {
38 67
         this.lastOffset = nativeEvent.contentOffset.y;
39 68
     }
40 69
 
41
-}
70
+}

+ 28
- 1
src/utils/CollapsibleUtils.js View File

@@ -5,6 +5,21 @@ import {useTheme} from "react-native-paper";
5 5
 import {createCollapsibleStack} from "react-navigation-collapsible";
6 6
 import StackNavigator, {StackNavigationOptions} from "@react-navigation/stack";
7 7
 
8
+/**
9
+ * Creates a navigation stack with the collapsible library, allowing the header to collapse on scroll.
10
+ *
11
+ * Please use the getWebsiteStack function if your screen uses a webview as their main component as it needs special parameters.
12
+ *
13
+ * @param name The screen name in the navigation stack
14
+ * @param Stack The stack component
15
+ * @param component The screen component
16
+ * @param title The screen title shown in the header (needs to be translated)
17
+ * @param useNativeDriver Whether to use the native driver for animations.
18
+ * Set to false if the screen uses a webview as this component does not support native driver.
19
+ * In all other cases, set it to true for increase performance.
20
+ * @param options Screen options to use, or null if no options are necessary.
21
+ * @returns {JSX.Element}
22
+ */
8 23
 export function createScreenCollapsibleStack(
9 24
     name: string,
10 25
     Stack: StackNavigator,
@@ -33,6 +48,18 @@ export function createScreenCollapsibleStack(
33 48
     )
34 49
 }
35 50
 
51
+/**
52
+ * Creates a navigation stack with the collapsible library, allowing the header to collapse on scroll.
53
+ *
54
+ * This is a preset for screens using a webview as their main component, as it uses special parameters to work.
55
+ * (aka a dirty workaround)
56
+ *
57
+ * @param name
58
+ * @param Stack
59
+ * @param component
60
+ * @param title
61
+ * @returns {JSX.Element}
62
+ */
36 63
 export function getWebsiteStack(name: string, Stack: any, component: any, title: string) {
37 64
     return createScreenCollapsibleStack(name, Stack, component, title, false);
38
-}
65
+}

+ 22
- 9
src/utils/Notifications.js View File

@@ -6,10 +6,12 @@ import i18n from "i18n-js";
6 6
 
7 7
 const PushNotification = require("react-native-push-notification");
8 8
 
9
-const reminderIdMultiplicator = 100;
9
+// Used to multiply the normal notification id to create the reminder one. It allows to find it back easily
10
+const reminderIdFactor = 100;
10 11
 
11 12
 /**
12
- * Async function asking permission to send notifications to the user
13
+ * Async function asking permission to send notifications to the user.
14
+ * Used on ios.
13 15
  *
14 16
  * @returns {Promise}
15 17
  */
@@ -32,10 +34,18 @@ export async function askPermissions() {
32 34
     }));
33 35
 }
34 36
 
37
+/**
38
+ * Creates a notification for the given machine id at the given date.
39
+ *
40
+ * This creates 2 notifications. One at the exact date, and one a few minutes before, according to user preference.
41
+ *
42
+ * @param machineID The machine id to schedule notifications for. This is used as id and in the notification string.
43
+ * @param date The date to trigger the notification at
44
+ */
35 45
 function createNotifications(machineID: string, date: Date) {
36 46
     let reminder = parseInt(AsyncStorageManager.getInstance().preferences.proxiwashNotifications.current);
37
-    if (!isNaN(reminder)) {
38
-        let id = reminderIdMultiplicator * parseInt(machineID);
47
+    if (!isNaN(reminder) && reminder > 0) {
48
+        let id = reminderIdFactor * parseInt(machineID);
39 49
         let reminderDate = new Date(date);
40 50
         reminderDate.setMinutes(reminderDate.getMinutes() - reminder);
41 51
         PushNotification.localNotificationSchedule({
@@ -57,11 +67,14 @@ function createNotifications(machineID: string, date: Date) {
57 67
 }
58 68
 
59 69
 /**
60
- * Asks the server to enable/disable notifications for the specified machine
70
+ * Enables or disables notifications for the given machine.
71
+ *
72
+ * The function is async as we need to ask user permissions.
73
+ * If user denies, the promise will be rejected, otherwise it will succeed.
61 74
  *
62
- * @param machineID The machine ID
75
+ * @param machineID The machine ID to setup notifications for
63 76
  * @param isEnabled True to enable notifications, false to disable
64
- * @param endDate
77
+ * @param endDate The trigger date, or null if disabling notifications
65 78
  */
66 79
 export async function setupMachineNotification(machineID: string, isEnabled: boolean, endDate: Date | null) {
67 80
     return new Promise((resolve, reject) => {
@@ -76,9 +89,9 @@ export async function setupMachineNotification(machineID: string, isEnabled: boo
76 89
                 });
77 90
         } else {
78 91
             PushNotification.cancelLocalNotifications({id: machineID});
79
-            let reminderId = reminderIdMultiplicator * parseInt(machineID);
92
+            let reminderId = reminderIdFactor * parseInt(machineID);
80 93
             PushNotification.cancelLocalNotifications({id: reminderId.toString()});
81 94
             resolve();
82 95
         }
83 96
     });
84
-}
97
+}

+ 3
- 2
src/utils/Planning.js View File

@@ -22,7 +22,7 @@ const dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/;
22 22
  * @return {string} The string representation
23 23
  */
24 24
 export function getCurrentDateString(): string {
25
-    return dateToString(new Date(Date.now()));
25
+    return dateToString(new Date(Date.now()), false);
26 26
 }
27 27
 
28 28
 /**
@@ -117,6 +117,7 @@ export function stringToDate(dateString: string): Date | null {
117 117
  * YYYY-MM-DD HH-MM-SS
118 118
  *
119 119
  * @param date The date object to convert
120
+ * @param isUTC Whether to treat the date as UTC
120 121
  * @return {string} The converted string
121 122
  */
122 123
 export function dateToString(date: Date, isUTC: boolean): string {
@@ -198,7 +199,7 @@ export function generateEmptyCalendar(numberOfMonths: number): Object {
198 199
     let daysOfYear = {};
199 200
     for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) {
200 201
         const dateString = getDateOnlyString(
201
-            dateToString(new Date(d)));
202
+            dateToString(new Date(d), false));
202 203
         if (dateString !== null)
203 204
             daysOfYear[dateString] = []
204 205
     }

+ 18
- 2
src/utils/Search.js View File

@@ -1,8 +1,10 @@
1
-
1
+// @flow
2 2
 
3 3
 
4 4
 /**
5
- * Sanitizes the given string to improve search performance
5
+ * Sanitizes the given string to improve search performance.
6
+ *
7
+ * This removes the case, accents, spaces and underscores.
6 8
  *
7 9
  * @param str The string to sanitize
8 10
  * @return {string} The sanitized string
@@ -15,10 +17,24 @@ export function sanitizeString(str: string): string {
15 17
         .replace(/_/g, "");
16 18
 }
17 19
 
20
+/**
21
+ * Checks if the given string matches the query.
22
+ *
23
+ * @param str The string to check
24
+ * @param query The query string used to find a match
25
+ * @returns {boolean}
26
+ */
18 27
 export function stringMatchQuery(str: string, query: string) {
19 28
     return sanitizeString(str).includes(sanitizeString(query));
20 29
 }
21 30
 
31
+/**
32
+ * Checks if the given arrays have an item in common
33
+ *
34
+ * @param filter The filter array
35
+ * @param categories The item's categories array
36
+ * @returns {boolean} True if at least one entry is in both arrays
37
+ */
22 38
 export function isItemInCategoryFilter(filter: Array<string>, categories: Array<string>) {
23 39
     for (const category of categories) {
24 40
         if (filter.indexOf(category) !== -1)

+ 89
- 23
src/utils/URLHandler.js View File

@@ -2,9 +2,12 @@
2 2
 
3 3
 import {Linking} from 'react-native';
4 4
 
5
+/**
6
+ * Class use to handle depp links scanned or clicked.
7
+ */
5 8
 export default class URLHandler {
6 9
 
7
-    static SCHEME = "campus-insat://";
10
+    static SCHEME = "campus-insat://"; // Urls beginning with this string will be opened in the app
8 11
 
9 12
     static CLUB_INFO_URL_PATH = "club";
10 13
     static EVENT_INFO_URL_PATH = "event";
@@ -20,27 +23,12 @@ export default class URLHandler {
20 23
         this.onDetectURL = onDetectURL;
21 24
     }
22 25
 
23
-    listen() {
24
-        Linking.addEventListener('url', this.onUrl);
25
-        Linking.getInitialURL().then(this.onInitialUrl);
26
-    }
27
-
28
-    onUrl = ({url}: { url: string }) => {
29
-        if (url != null) {
30
-            let data = URLHandler.getUrlData(URLHandler.parseUrl(url));
31
-            if (data !== null)
32
-                this.onDetectURL(data);
33
-        }
34
-    };
35
-
36
-    onInitialUrl = (url: ?string) => {
37
-        if (url != null) {
38
-            let data = URLHandler.getUrlData(URLHandler.parseUrl(url));
39
-            if (data !== null)
40
-                this.onInitialURLParsed(data);
41
-        }
42
-    };
43
-
26
+    /**
27
+     * Parses the given url to retrieve the corresponding app path and associated arguments.
28
+     *
29
+     * @param url The url to parse
30
+     * @returns {{path: string, queryParams: {}}}
31
+     */
44 32
     static parseUrl(url: string) {
45 33
         let params = {};
46 34
         let path = "";
@@ -63,7 +51,15 @@ export default class URLHandler {
63 51
         return {path: path, queryParams: params};
64 52
     }
65 53
 
66
-    static getUrlData({path, queryParams}: Object) {
54
+    /**
55
+     * Gets routing data corresponding to the given url.
56
+     * If the url does not match any existing route, null will be returned.
57
+     *
58
+     * @param path Url path
59
+     * @param queryParams Url parameters
60
+     * @returns {null}
61
+     */
62
+    static getUrlData({path, queryParams}: { path: string, queryParams: { [key: string]: string } }) {
67 63
         let data = null;
68 64
         if (path !== null) {
69 65
             if (URLHandler.isClubInformationLink(path))
@@ -74,18 +70,42 @@ export default class URLHandler {
74 70
         return data;
75 71
     }
76 72
 
73
+    /**
74
+     * Checks if the given url is in a valid format
75
+     *
76
+     * @param url The url to check
77
+     * @returns {boolean}
78
+     */
77 79
     static isUrlValid(url: string) {
78 80
         return this.getUrlData(URLHandler.parseUrl(url)) !== null;
79 81
     }
80 82
 
83
+    /**
84
+     * Check if the given path links to the club information screen
85
+     *
86
+     * @param path The url to check
87
+     * @returns {boolean}
88
+     */
81 89
     static isClubInformationLink(path: string) {
82 90
         return path === URLHandler.CLUB_INFO_URL_PATH;
83 91
     }
84 92
 
93
+    /**
94
+     * Check if the given path links to the planning information screen
95
+     *
96
+     * @param path The url to check
97
+     * @returns {boolean}
98
+     */
85 99
     static isPlanningInformationLink(path: string) {
86 100
         return path === URLHandler.EVENT_INFO_URL_PATH;
87 101
     }
88 102
 
103
+    /**
104
+     * Generates data formatted for the club information screen from the url parameters.
105
+     *
106
+     * @param params Url parameters to convert
107
+     * @returns {null|{route: string, data: {clubId: number}}}
108
+     */
89 109
     static generateClubInformationData(params: Object): Object | null {
90 110
         if (params !== undefined && params.id !== undefined) {
91 111
             let id = parseInt(params.id);
@@ -96,6 +116,12 @@ export default class URLHandler {
96 116
         return null;
97 117
     }
98 118
 
119
+    /**
120
+     * Generates data formatted for the planning information screen from the url parameters.
121
+     *
122
+     * @param params Url parameters to convert
123
+     * @returns {null|{route: string, data: {clubId: number}}}
124
+     */
99 125
     static generatePlanningInformationData(params: Object): Object | null {
100 126
         if (params !== undefined && params.id !== undefined) {
101 127
             let id = parseInt(params.id);
@@ -106,4 +132,44 @@ export default class URLHandler {
106 132
         return null;
107 133
     }
108 134
 
135
+    /**
136
+     * Starts listening to events.
137
+     *
138
+     * There are 2 types of event.
139
+     *
140
+     * A classic event, triggered while the app is active.
141
+     * An initial event, called when the app was opened by clicking on a link
142
+     *
143
+     */
144
+    listen() {
145
+        Linking.addEventListener('url', this.onUrl);
146
+        Linking.getInitialURL().then(this.onInitialUrl);
147
+    }
148
+
149
+    /**
150
+     * Gets data from the given url and calls the classic callback with it.
151
+     *
152
+     * @param url The url detected
153
+     */
154
+    onUrl = ({url}: { url: string }) => {
155
+        if (url != null) {
156
+            let data = URLHandler.getUrlData(URLHandler.parseUrl(url));
157
+            if (data !== null)
158
+                this.onDetectURL(data);
159
+        }
160
+    };
161
+
162
+    /**
163
+     * Gets data from the given url and calls the initial callback with it.
164
+     *
165
+     * @param url The url detected
166
+     */
167
+    onInitialUrl = (url: ?string) => {
168
+        if (url != null) {
169
+            let data = URLHandler.getUrlData(URLHandler.parseUrl(url));
170
+            if (data !== null)
171
+                this.onInitialURLParsed(data);
172
+        }
173
+    };
174
+
109 175
 }

+ 25
- 2
src/utils/WebData.js View File

@@ -21,7 +21,18 @@ type response_format = {
21 21
 
22 22
 const API_ENDPOINT = "https://www.amicale-insat.fr/api/";
23 23
 
24
-export async function apiRequest(path: string, method: string, params: ?Object) {
24
+/**
25
+ * Sends a request to the Amicale Website backend
26
+ *
27
+ * In case of failure, the promise will be rejected with the error code.
28
+ * In case of success, the promise will return the data object.
29
+ *
30
+ * @param path The API path from the API endpoint
31
+ * @param method The HTTP method to use (GET or POST)
32
+ * @param params The params to use for this request
33
+ * @returns {Promise<R>}
34
+ */
35
+export async function apiRequest(path: string, method: string, params: ?{ [key: string]: string }) {
25 36
     if (params === undefined || params === null)
26 37
         params = {};
27 38
 
@@ -51,6 +62,14 @@ export async function apiRequest(path: string, method: string, params: ?Object)
51 62
     });
52 63
 }
53 64
 
65
+/**
66
+ * Checks if the given API response is valid.
67
+ *
68
+ * For a request to be valid, it must match the response_format as defined in this file.
69
+ *
70
+ * @param response
71
+ * @returns {boolean}
72
+ */
54 73
 export function isResponseValid(response: response_format) {
55 74
     let valid = response !== undefined
56 75
         && response.error !== undefined
@@ -63,7 +82,11 @@ export function isResponseValid(response: response_format) {
63 82
 }
64 83
 
65 84
 /**
66
- * Read data from FETCH_URL and return it.
85
+ * Reads data from the given url and returns it.
86
+ *
87
+ * Only use this function for non API calls.
88
+ * For Amicale API calls, please use the apiRequest function.
89
+ *
67 90
  * If no data was found, returns an empty object
68 91
  *
69 92
  * @param url The urls to fetch data from

+ 13
- 2
src/utils/withCollapsible.js View File

@@ -1,7 +1,19 @@
1 1
 import React from 'react';
2
-import {StatusBar} from 'react-native';
3 2
 import {useCollapsibleStack} from "react-navigation-collapsible";
4 3
 
4
+/**
5
+ * Function used to manipulate Collapsible Hooks from a class.
6
+ *
7
+ * Usage :
8
+ *
9
+ * export withCollapsible(Component)
10
+ *
11
+ * replacing Component with the one you want to use.
12
+ * This component will then receive the collapsibleStack prop.
13
+ *
14
+ * @param Component The component to use Collapsible with
15
+ * @returns {React.ComponentType<React.ClassAttributes<unknown>>}
16
+ */
5 17
 export const withCollapsible = (Component: any) => {
6 18
     return React.forwardRef((props: any, ref: any) => {
7 19
 
@@ -14,7 +26,6 @@ export const withCollapsible = (Component: any) => {
14 26
             progress,
15 27
             opacity,
16 28
         } = useCollapsibleStack();
17
-        const statusbarHeight = StatusBar.currentHeight != null ? StatusBar.currentHeight : 0;
18 29
         return <Component
19 30
             collapsibleStack={{
20 31
                 onScroll,

Loading…
Cancel
Save