Browse Source

refactor planex groups with functionnal components

Arnaud Vergnet 6 months ago
parent
commit
27f7a079b4
2 changed files with 82 additions and 125 deletions
  1. 65
    125
      src/screens/Planex/GroupSelectionScreen.tsx
  2. 17
    0
      src/utils/cacheContext.ts

+ 65
- 125
src/screens/Planex/GroupSelectionScreen.tsx View File

@@ -17,17 +17,18 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-import * as React from 'react';
20
+import React, { useEffect, useLayoutEffect, useState } from 'react';
21 21
 import { Platform } from 'react-native';
22 22
 import i18n from 'i18n-js';
23 23
 import { Searchbar } from 'react-native-paper';
24
-import { StackNavigationProp } from '@react-navigation/stack';
25 24
 import { stringMatchQuery } from '../../utils/Search';
26 25
 import WebSectionList from '../../components/Screens/WebSectionList';
27 26
 import GroupListAccordion from '../../components/Lists/PlanexGroups/GroupListAccordion';
28 27
 import AsyncStorageManager from '../../managers/AsyncStorageManager';
29 28
 import Urls from '../../constants/Urls';
30 29
 import { readData } from '../../utils/WebData';
30
+import { useNavigation } from '@react-navigation/core';
31
+import { useCachedPlanexGroups } from '../../utils/cacheContext';
31 32
 
32 33
 export type PlanexGroupType = {
33 34
   name: string;
@@ -40,14 +41,7 @@ export type PlanexGroupCategoryType = {
40 41
   content: Array<PlanexGroupType>;
41 42
 };
42 43
 
43
-type PropsType = {
44
-  navigation: StackNavigationProp<any>;
45
-};
46
-
47
-type StateType = {
48
-  currentSearchString: string;
49
-  favoriteGroups: Array<PlanexGroupType>;
50
-};
44
+export type PlanexGroupsType = { [key: string]: PlanexGroupCategoryType };
51 45
 
52 46
 function sortName(
53 47
   a: PlanexGroupType | PlanexGroupCategoryType,
@@ -62,46 +56,34 @@ function sortName(
62 56
   return 0;
63 57
 }
64 58
 
65
-/**
66
- * Class defining planex group selection screen.
67
- */
68
-class GroupSelectionScreen extends React.Component<PropsType, StateType> {
69
-  constructor(props: PropsType) {
70
-    super(props);
71
-    this.state = {
72
-      currentSearchString: '',
73
-      favoriteGroups: AsyncStorageManager.getObject(
74
-        AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key
75
-      ),
76
-    };
77
-  }
59
+function GroupSelectionScreen() {
60
+  const navigation = useNavigation();
61
+  const { groups, setGroups } = useCachedPlanexGroups();
62
+  const [currentSearchString, setCurrentSearchString] = useState('');
63
+  const [favoriteGroups, setFavoriteGroups] = useState<Array<PlanexGroupType>>(
64
+    AsyncStorageManager.getObject(
65
+      AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key
66
+    )
67
+  );
78 68
 
79
-  /**
80
-   * Creates the header content
81
-   */
82
-  componentDidMount() {
83
-    const { navigation } = this.props;
69
+  useLayoutEffect(() => {
84 70
     navigation.setOptions({
85
-      headerTitle: this.getSearchBar,
71
+      headerTitle: getSearchBar,
86 72
       headerBackTitleVisible: false,
87 73
       headerTitleContainerStyle:
88 74
         Platform.OS === 'ios'
89 75
           ? { marginHorizontal: 0, width: '70%' }
90 76
           : { marginHorizontal: 0, right: 50, left: 50 },
91 77
     });
92
-  }
78
+    // eslint-disable-next-line react-hooks/exhaustive-deps
79
+  }, [navigation]);
93 80
 
94
-  /**
95
-   * Gets the header search bar
96
-   *
97
-   * @return {*}
98
-   */
99
-  getSearchBar = () => {
81
+  const getSearchBar = () => {
100 82
     return (
101 83
       // @ts-ignore
102 84
       <Searchbar
103 85
         placeholder={i18n.t('screens.proximo.search')}
104
-        onChangeText={this.onSearchStringChange}
86
+        onChangeText={setCurrentSearchString}
105 87
       />
106 88
     );
107 89
   };
@@ -112,18 +94,17 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
112 94
    * @param item The article to render
113 95
    * @return {*}
114 96
    */
115
-  getRenderItem = ({ item }: { item: PlanexGroupCategoryType }) => {
116
-    const { currentSearchString, favoriteGroups } = this.state;
97
+  const getRenderItem = ({ item }: { item: PlanexGroupCategoryType }) => {
117 98
     if (
118
-      this.shouldDisplayAccordion(item) ||
99
+      shouldDisplayAccordion(item) ||
119 100
       (item.id === 0 && item.content.length === 0)
120 101
     ) {
121 102
       return (
122 103
         <GroupListAccordion
123 104
           item={item}
124 105
           favorites={[...favoriteGroups]}
125
-          onGroupPress={this.onListItemPress}
126
-          onFavoritePress={this.onListFavoritePress}
106
+          onGroupPress={onListItemPress}
107
+          onFavoritePress={onListFavoritePress}
127 108
           currentSearchString={currentSearchString}
128 109
         />
129 110
       );
@@ -137,7 +118,7 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
137 118
    * @param fetchedData
138 119
    * @return {*}
139 120
    * */
140
-  createDataset = (
121
+  const createDataset = (
141 122
     fetchedData:
142 123
       | {
143 124
           [key: string]: PlanexGroupCategoryType;
@@ -147,28 +128,18 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
147 128
     return [
148 129
       {
149 130
         title: '',
150
-        data: this.generateData(fetchedData),
131
+        data: generateData(fetchedData),
151 132
       },
152 133
     ];
153 134
   };
154 135
 
155 136
   /**
156
-   * Callback used when the search changes
157
-   *
158
-   * @param str The new search string
159
-   */
160
-  onSearchStringChange = (str: string) => {
161
-    this.setState({ currentSearchString: str });
162
-  };
163
-
164
-  /**
165 137
    * Callback used when clicking an article in the list.
166 138
    * It opens the modal to show detailed information about the article
167 139
    *
168 140
    * @param item The article pressed
169 141
    */
170
-  onListItemPress = (item: PlanexGroupType) => {
171
-    const { navigation } = this.props;
142
+  const onListItemPress = (item: PlanexGroupType) => {
172 143
     navigation.navigate('planex', {
173 144
       screen: 'index',
174 145
       params: { group: item },
@@ -180,8 +151,8 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
180 151
    *
181 152
    * @param item The item to add/remove from favorites
182 153
    */
183
-  onListFavoritePress = (item: PlanexGroupType) => {
184
-    this.updateGroupFavorites(item);
154
+  const onListFavoritePress = (item: PlanexGroupType) => {
155
+    updateGroupFavorites(item);
185 156
   };
186 157
 
187 158
   /**
@@ -190,16 +161,15 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
190 161
    * @param group The group to check
191 162
    * @returns {boolean}
192 163
    */
193
-  isGroupInFavorites(group: PlanexGroupType): boolean {
164
+  const isGroupInFavorites = (group: PlanexGroupType): boolean => {
194 165
     let isFav = false;
195
-    const { favoriteGroups } = this.state;
196 166
     favoriteGroups.forEach((favGroup: PlanexGroupType) => {
197 167
       if (group.id === favGroup.id) {
198 168
         isFav = true;
199 169
       }
200 170
     });
201 171
     return isFav;
202
-  }
172
+  };
203 173
 
204 174
   /**
205 175
    * Adds or removes the given group to the favorites list, depending on whether it is already in it or not.
@@ -207,13 +177,13 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
207 177
    *
208 178
    * @param group The group to add/remove to favorites
209 179
    */
210
-  updateGroupFavorites(group: PlanexGroupType) {
211
-    if (this.isGroupInFavorites(group)) {
212
-      this.removeGroupFromFavorites(group);
180
+  const updateGroupFavorites = (group: PlanexGroupType) => {
181
+    if (isGroupInFavorites(group)) {
182
+      removeGroupFromFavorites(group);
213 183
     } else {
214
-      this.addGroupToFavorites(group);
184
+      addGroupToFavorites(group);
215 185
     }
216
-  }
186
+  };
217 187
 
218 188
   /**
219 189
    * Checks whether to display the given group category, depending on user search query
@@ -221,8 +191,7 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
221 191
    * @param item The group category
222 192
    * @returns {boolean}
223 193
    */
224
-  shouldDisplayAccordion(item: PlanexGroupCategoryType): boolean {
225
-    const { currentSearchString } = this.state;
194
+  const shouldDisplayAccordion = (item: PlanexGroupCategoryType): boolean => {
226 195
     let shouldDisplay = false;
227 196
     for (let i = 0; i < item.content.length; i += 1) {
228 197
       if (stringMatchQuery(item.content[i].name, currentSearchString)) {
@@ -231,7 +200,7 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
231 200
       }
232 201
     }
233 202
     return shouldDisplay;
234
-  }
203
+  };
235 204
 
236 205
   /**
237 206
    * Generates the dataset to be used in the FlatList.
@@ -240,14 +209,9 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
240 209
    * @param fetchedData The raw data fetched from the server
241 210
    * @returns {[]}
242 211
    */
243
-  generateData(
244
-    fetchedData:
245
-      | {
246
-          [key: string]: PlanexGroupCategoryType;
247
-        }
248
-      | undefined
249
-  ): Array<PlanexGroupCategoryType> {
250
-    const { favoriteGroups } = this.state;
212
+  const generateData = (
213
+    fetchedData: PlanexGroupsType | undefined
214
+  ): Array<PlanexGroupCategoryType> => {
251 215
     const data: Array<PlanexGroupCategoryType> = [];
252 216
     if (fetchedData) {
253 217
       Object.values(fetchedData).forEach(
@@ -263,68 +227,44 @@ class GroupSelectionScreen extends React.Component<PropsType, StateType> {
263 227
       });
264 228
     }
265 229
     return data;
266
-  }
230
+  };
267 231
 
268 232
   /**
269 233
    * Removes the given group from the favorites
270 234
    *
271 235
    * @param group The group to remove from the array
272 236
    */
273
-  removeGroupFromFavorites(group: PlanexGroupType) {
274
-    this.setState((prevState: StateType): {
275
-      favoriteGroups: Array<PlanexGroupType>;
276
-    } => {
277
-      const { favoriteGroups } = prevState;
278
-      for (let i = 0; i < favoriteGroups.length; i += 1) {
279
-        if (group.id === favoriteGroups[i].id) {
280
-          favoriteGroups.splice(i, 1);
281
-          break;
282
-        }
283
-      }
284
-      AsyncStorageManager.set(
285
-        AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key,
286
-        favoriteGroups
287
-      );
288
-      return { favoriteGroups };
289
-    });
290
-  }
237
+  const removeGroupFromFavorites = (group: PlanexGroupType) => {
238
+    setFavoriteGroups(favoriteGroups.filter((g) => g.id !== group.id));
239
+  };
240
+
241
+  useEffect(() => {
242
+    AsyncStorageManager.set(
243
+      AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key,
244
+      favoriteGroups
245
+    );
246
+  }, [favoriteGroups]);
291 247
 
292 248
   /**
293 249
    * Adds the given group to favorites
294 250
    *
295 251
    * @param group The group to add to the array
296 252
    */
297
-  addGroupToFavorites(group: PlanexGroupType) {
298
-    this.setState((prevState: StateType): {
299
-      favoriteGroups: Array<PlanexGroupType>;
300
-    } => {
301
-      const { favoriteGroups } = prevState;
302
-      favoriteGroups.push(group);
303
-      favoriteGroups.sort(sortName);
304
-      AsyncStorageManager.set(
305
-        AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key,
306
-        favoriteGroups
307
-      );
308
-      return { favoriteGroups };
309
-    });
310
-  }
253
+  const addGroupToFavorites = (group: PlanexGroupType) => {
254
+    setFavoriteGroups([...favoriteGroups, group].sort(sortName));
255
+  };
311 256
 
312
-  render() {
313
-    const { state } = this;
314
-    return (
315
-      <WebSectionList
316
-        request={() =>
317
-          readData<{ [key: string]: PlanexGroupCategoryType }>(
318
-            Urls.planex.groups
319
-          )
320
-        }
321
-        createDataset={this.createDataset}
322
-        refreshOnFocus={true}
323
-        renderItem={this.getRenderItem}
324
-        updateData={state.currentSearchString + state.favoriteGroups.length}
325
-      />
326
-    );
327
-  }
257
+  return (
258
+    <WebSectionList
259
+      request={() => readData<PlanexGroupsType>(Urls.planex.groups)}
260
+      createDataset={createDataset}
261
+      refreshOnFocus={true}
262
+      renderItem={getRenderItem}
263
+      updateData={currentSearchString + favoriteGroups.length}
264
+      cache={groups}
265
+      onCacheUpdate={setGroups}
266
+    />
267
+  );
328 268
 }
329 269
 
330 270
 export default GroupSelectionScreen;

+ 17
- 0
src/utils/cacheContext.ts View File

@@ -1,4 +1,5 @@
1 1
 import React, { useContext } from 'react';
2
+import { PlanexGroupsType } from '../screens/Planex/GroupSelectionScreen';
2 3
 import { ArticlesType } from '../screens/Services/Proximo/ProximoListScreen';
3 4
 import { CategoriesType } from '../screens/Services/Proximo/ProximoMainScreen';
4 5
 
@@ -7,6 +8,9 @@ export type CacheType = {
7 8
     articles?: ArticlesType;
8 9
     categories?: CategoriesType;
9 10
   };
11
+  planex?: {
12
+    groups?: PlanexGroupsType;
13
+  };
10 14
 };
11 15
 
12 16
 export type CacheContextType = {
@@ -52,3 +56,16 @@ export function useCachedProximoArticles() {
52 56
   };
53 57
   return { articles, setArticles };
54 58
 }
59
+
60
+export function useCachedPlanexGroups() {
61
+  const { cache, setCache } = useCache();
62
+  const groups = cache?.planex?.groups;
63
+  const setGroups = (newGroups: PlanexGroupsType) => {
64
+    setCache({
65
+      planex: {
66
+        groups: newGroups,
67
+      },
68
+    });
69
+  };
70
+  return { groups, setGroups };
71
+}

Loading…
Cancel
Save