Browse Source

Improved home screen handling

Arnaud Vergnet 1 year ago
parent
commit
22d5f61fc5
4 changed files with 130 additions and 133 deletions
  1. 3
    1
      locales/en.json
  2. 3
    1
      locales/fr.json
  3. 15
    20
      src/components/Screens/WebSectionList.js
  4. 109
    111
      src/screens/Home/HomeScreen.js

+ 3
- 1
locales/en.json View File

@@ -95,8 +95,10 @@
95 95
     },
96 96
     "home": {
97 97
       "title": "Campus",
98
-      "feedTitle": "Amicale's News",
98
+      "feedTitle": "Campus News",
99 99
       "feed": "Details",
100
+      "feedLoading": "Loading News",
101
+      "feedError": "Failed to load news",
100 102
       "dashboard": {
101 103
         "seeMore": "Click to see more",
102 104
         "todayEventsTitle": "Today's events",

+ 3
- 1
locales/fr.json View File

@@ -95,8 +95,10 @@
95 95
     },
96 96
     "home": {
97 97
       "title": "Campus",
98
-      "feedTitle": "News de l'Amicale",
98
+      "feedTitle": "News du Campus",
99 99
       "feed": "Détails",
100
+      "feedLoading": "Chargement des news",
101
+      "feedError": "Erreur de chargement des news",
100 102
       "dashboard": {
101 103
         "seeMore": "Clique pour plus d'infos",
102 104
         "todayEventsTitle": "Événements aujourd'hui",

+ 15
- 20
src/components/Screens/WebSectionList.js View File

@@ -11,21 +11,23 @@ import {withCollapsible} from "../../utils/withCollapsible";
11 11
 import * as Animatable from 'react-native-animatable';
12 12
 import CustomTabBar from "../Tabbar/CustomTabBar";
13 13
 import {Collapsible} from "react-navigation-collapsible";
14
+import {StackNavigationProp} from "@react-navigation/stack";
14 15
 
15 16
 type Props = {
16
-    navigation: { [key: string]: any },
17
+    navigation: StackNavigationProp,
17 18
     fetchUrl: string,
18 19
     autoRefreshTime: number,
19 20
     refreshOnFocus: boolean,
20 21
     renderItem: (data: { [key: string]: any }) => React.Node,
21
-    createDataset: (data: { [key: string]: any }) => Array<Object>,
22
+    createDataset: (data: { [key: string]: any } | null, isLoading?: boolean) => Array<Object>,
22 23
     onScroll: (event: SyntheticEvent<EventTarget>) => void,
23 24
     collapsibleStack: Collapsible,
24 25
 
25 26
     showError: boolean,
26 27
     itemHeight?: number,
27 28
     updateData?: number,
28
-    renderSectionHeader?: (data: { [key: string]: any }) => React.Node,
29
+    renderListHeaderComponent?: (data: { [key: string]: any } | null) => React.Node,
30
+    renderSectionHeader?: (data: { section: { [key: string]: any } }, isLoading?: boolean) => React.Node,
29 31
     stickyHeader?: boolean,
30 32
 }
31 33
 
@@ -53,7 +55,6 @@ class WebSectionList extends React.PureComponent<Props, State> {
53 55
         showError: true,
54 56
     };
55 57
 
56
-    scrollRef: { current: null | Animated.SectionList };
57 58
     refreshInterval: IntervalID;
58 59
     lastRefresh: Date | null;
59 60
 
@@ -69,31 +70,26 @@ class WebSectionList extends React.PureComponent<Props, State> {
69 70
      * Allows to detect when the screen is focused
70 71
      */
71 72
     componentDidMount() {
72
-        const onScreenFocus = this.onScreenFocus.bind(this);
73
-        const onScreenBlur = this.onScreenBlur.bind(this);
74
-        this.props.navigation.addListener('focus', onScreenFocus);
75
-        this.props.navigation.addListener('blur', onScreenBlur);
76
-        this.scrollRef = React.createRef();
77
-        this.onRefresh();
73
+        this.props.navigation.addListener('focus', this.onScreenFocus);
74
+        this.props.navigation.addListener('blur', this.onScreenBlur);
78 75
         this.lastRefresh = null;
76
+        this.onRefresh();
79 77
     }
80 78
 
81 79
     /**
82 80
      * Refreshes data when focusing the screen and setup a refresh interval if asked to
83 81
      */
84
-    onScreenFocus() {
82
+    onScreenFocus = () => {
85 83
         if (this.props.refreshOnFocus && this.lastRefresh)
86 84
             this.onRefresh();
87 85
         if (this.props.autoRefreshTime > 0)
88 86
             this.refreshInterval = setInterval(this.onRefresh, this.props.autoRefreshTime)
89
-        // if (this.scrollRef.current) // Reset scroll to top
90
-        //     this.scrollRef.current.getNode().scrollToLocation({animated:false, itemIndex:0, sectionIndex:0});
91 87
     }
92 88
 
93 89
     /**
94 90
      * Removes any interval on un-focus
95 91
      */
96
-    onScreenBlur() {
92
+    onScreenBlur = () => {
97 93
         clearInterval(this.refreshInterval);
98 94
     }
99 95
 
@@ -173,7 +169,7 @@ class WebSectionList extends React.PureComponent<Props, State> {
173 169
                     duration={500}
174 170
                     useNativeDriver
175 171
                 >
176
-                    {this.props.renderSectionHeader(data)}
172
+                    {this.props.renderSectionHeader(data, this.state.refreshing)}
177 173
                 </Animatable.View>
178 174
             );
179 175
         } else
@@ -205,16 +201,12 @@ class WebSectionList extends React.PureComponent<Props, State> {
205 201
     render() {
206 202
         let dataset = [];
207 203
         if (this.state.fetchedData != null || (this.state.fetchedData == null && !this.props.showError)) {
208
-            if (this.state.fetchedData == null)
209
-                dataset = this.props.createDataset({});
210
-            else
211
-                dataset = this.props.createDataset(this.state.fetchedData);
204
+            dataset = this.props.createDataset(this.state.fetchedData, this.state.refreshing);
212 205
         }
213 206
         const {containerPaddingTop, scrollIndicatorInsetTop, onScrollWithListener} = this.props.collapsibleStack;
214 207
         return (
215 208
             <View>
216 209
                 <Animated.SectionList
217
-                    ref={this.scrollRef}
218 210
                     sections={dataset}
219 211
                     extraData={this.props.updateData}
220 212
                     refreshControl={
@@ -228,6 +220,9 @@ class WebSectionList extends React.PureComponent<Props, State> {
228 220
                     renderItem={this.renderItem}
229 221
                     stickySectionHeadersEnabled={this.props.stickyHeader}
230 222
                     style={{minHeight: '100%'}}
223
+                    ListHeaderComponent={this.props.renderListHeaderComponent != null
224
+                        ? this.props.renderListHeaderComponent(this.state.fetchedData)
225
+                        : null}
231 226
                     ListEmptyComponent={this.state.refreshing
232 227
                         ? <BasicLoadingScreen/>
233 228
                         : <ErrorView

+ 109
- 111
src/screens/Home/HomeScreen.js View File

@@ -5,7 +5,7 @@ import {FlatList} from 'react-native';
5 5
 import i18n from "i18n-js";
6 6
 import DashboardItem from "../../components/Home/EventDashboardItem";
7 7
 import WebSectionList from "../../components/Screens/WebSectionList";
8
-import {Headline, withTheme} from 'react-native-paper';
8
+import {ActivityIndicator, Headline, withTheme} from 'react-native-paper';
9 9
 import FeedItem from "../../components/Home/FeedItem";
10 10
 import SmallDashboardItem from "../../components/Home/SmallDashboardItem";
11 11
 import PreviewEventDashboardItem from "../../components/Home/PreviewEventDashboardItem";
@@ -16,6 +16,7 @@ import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHead
16 16
 import AnimatedFAB from "../../components/Animations/AnimatedFAB";
17 17
 import {StackNavigationProp} from "@react-navigation/stack";
18 18
 import type {CustomTheme} from "../../managers/ThemeManager";
19
+import * as Animatable from "react-native-animatable";
19 20
 import {View} from "react-native-animatable";
20 21
 import ConnectionManager from "../../managers/ConnectionManager";
21 22
 import LogoutDialog from "../../components/Amicale/LogoutDialog";
@@ -23,6 +24,8 @@ import AsyncStorageManager from "../../managers/AsyncStorageManager";
23 24
 import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
24 25
 import MascotPopup from "../../components/Mascot/MascotPopup";
25 26
 import DashboardManager from "../../managers/DashboardManager";
27
+import type {ServiceItem} from "../../managers/ServicesManager";
28
+import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
26 29
 // import DATA from "../dashboard_data.json";
27 30
 
28 31
 
@@ -61,11 +64,6 @@ export type fullDashboard = {
61 64
     available_tutorials: number,
62 65
 }
63 66
 
64
-type dashboardItem = {
65
-    id: string,
66
-    content: Array<{ [key: string]: any }>
67
-};
68
-
69 67
 export type event = {
70 68
     id: number,
71 69
     title: string,
@@ -78,12 +76,6 @@ export type event = {
78 76
     url: string,
79 77
 }
80 78
 
81
-type listSection = {
82
-    title: string,
83
-    data: Array<dashboardItem> | Array<feedItem>,
84
-    id: string
85
-};
86
-
87 79
 type Props = {
88 80
     navigation: StackNavigationProp,
89 81
     route: { params: any, ... },
@@ -203,85 +195,42 @@ class HomeScreen extends React.Component<Props, State> {
203 195
 
204 196
     hideDisconnectDialog = () => this.setState({dialogVisible: false});
205 197
 
198
+    openScanner = () => this.props.navigation.navigate("scanner");
199
+
206 200
     /**
207 201
      * Creates the dataset to be used in the FlatList
208 202
      *
209 203
      * @param fetchedData
204
+     * @param isLoading
210 205
      * @return {*}
211 206
      */
212
-    createDataset = (fetchedData: rawDashboard) => {
207
+    createDataset = (fetchedData: rawDashboard | null, isLoading: boolean) => {
213 208
         // fetchedData = DATA;
214
-        let dashboardData;
215
-        if (fetchedData.news_feed != null)
216
-            this.currentNewFeed = fetchedData.news_feed.data;
217
-        if (fetchedData.dashboard != null)
218
-            this.currentDashboard = fetchedData.dashboard;
219
-
220
-        if (fetchedData.dashboard != null)
221
-            dashboardData = this.generateDashboardDataset(fetchedData.dashboard);
209
+        if (fetchedData != null) {
210
+            if (fetchedData.news_feed != null)
211
+                this.currentNewFeed = fetchedData.news_feed.data;
212
+            if (fetchedData.dashboard != null)
213
+                this.currentDashboard = fetchedData.dashboard;
214
+        }
215
+        if (this.currentNewFeed.length > 0)
216
+            return [
217
+                {
218
+                    title: i18n.t("screens.home.feedTitle"),
219
+                    data: this.currentNewFeed,
220
+                    id: SECTIONS_ID[1]
221
+                }
222
+            ];
222 223
         else
223
-            dashboardData = this.generateDashboardDataset(null);
224
-        return [
225
-            {
226
-                title: '',
227
-                data: dashboardData,
228
-                id: SECTIONS_ID[0]
229
-            },
230
-            {
231
-                title: i18n.t("screens.home.feedTitle"),
232
-                data: this.currentNewFeed,
233
-                id: SECTIONS_ID[1]
234
-            }
235
-        ];
224
+            return [
225
+                {
226
+                    title: isLoading ? i18n.t("screens.home.feedLoading") : i18n.t("screens.home.feedError"),
227
+                    data: [],
228
+                    id: SECTIONS_ID[1]
229
+                }
230
+            ];
236 231
     };
237 232
 
238 233
     /**
239
-     * Generates the dataset associated to the dashboard to be displayed in the FlatList as a section
240
-     *
241
-     * @param dashboardData
242
-     * @return {Array<dashboardItem>}
243
-     */
244
-    generateDashboardDataset(dashboardData: fullDashboard | null): Array<dashboardItem> {
245
-        return [
246
-            {id: 'actions', content: []},
247
-            {
248
-                id: 'top',
249
-                content: this.dashboardManager.getCurrentDashboard(),
250
-            },
251
-            {
252
-                id: 'event',
253
-                content: dashboardData == null ? [] : dashboardData.today_events
254
-            },
255
-
256
-        ];
257
-    }
258
-
259
-    /**
260
-     * Gets a dashboard item
261
-     *
262
-     * @param item The item to display
263
-     * @return {*}
264
-     */
265
-    getDashboardItem(item: dashboardItem) {
266
-        let content = item.content;
267
-        if (item.id === 'event')
268
-            return this.getDashboardEvent(content);
269
-        else if (item.id === 'top')
270
-            return this.getDashboardRow(content);
271
-        else
272
-            return this.getDashboardActions();
273
-    }
274
-
275
-    /**
276
-     * Gets a dashboard item with action buttons
277
-     *
278
-     * @returns {*}
279
-     */
280
-    getDashboardActions() {
281
-        return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>;
282
-    }
283
-
284
-    /**
285 234
      * Gets the time limit depending on the current day:
286 235
      * 17:30 for every day of the week except for thursday 11:30
287 236
      * 00:00 on weekends
@@ -427,22 +376,13 @@ class HomeScreen extends React.Component<Props, State> {
427 376
     }
428 377
 
429 378
     /**
430
-     * Gets a dashboard shortcut item
379
+     * Gets a dashboard item with action buttons
431 380
      *
432
-     * @param item
433 381
      * @returns {*}
434 382
      */
435
-    dashboardRowRenderItem = ({item}: { item: DashboardItem }) => {
436
-        return (
437
-            <SmallDashboardItem
438
-                image={item.image}
439
-                onPress={item.onPress}
440
-                badgeCount={this.currentDashboard != null && item.badgeFunction != null
441
-                    ? item.badgeFunction(this.currentDashboard)
442
-                    : null}
443
-            />
444
-        );
445
-    };
383
+    getDashboardActions() {
384
+        return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>;
385
+    }
446 386
 
447 387
     /**
448 388
      * Gets a dashboard item with a row of shortcut buttons.
@@ -450,7 +390,7 @@ class HomeScreen extends React.Component<Props, State> {
450 390
      * @param content
451 391
      * @return {*}
452 392
      */
453
-    getDashboardRow(content: Array<DashboardItem>) {
393
+    getDashboardRow(content: Array<ServiceItem>) {
454 394
         return (
455 395
             //$FlowFixMe
456 396
             <FlatList
@@ -467,6 +407,24 @@ class HomeScreen extends React.Component<Props, State> {
467 407
     }
468 408
 
469 409
     /**
410
+     * Gets a dashboard shortcut item
411
+     *
412
+     * @param item
413
+     * @returns {*}
414
+     */
415
+    dashboardRowRenderItem = ({item}: { item: ServiceItem }) => {
416
+        return (
417
+            <SmallDashboardItem
418
+                image={item.image}
419
+                onPress={item.onPress}
420
+                badgeCount={this.currentDashboard != null && item.badgeFunction != null
421
+                    ? item.badgeFunction(this.currentDashboard)
422
+                    : null}
423
+            />
424
+        );
425
+    };
426
+
427
+    /**
470 428
      * Gets a render item for the given feed object
471 429
      *
472 430
      * @param item The feed item to display
@@ -491,28 +449,15 @@ class HomeScreen extends React.Component<Props, State> {
491 449
      * @param section The current section
492 450
      * @return {*}
493 451
      */
494
-    getRenderItem = ({item, section}: {
495
-        item: { [key: string]: any },
496
-        section: listSection
497
-    }) => {
498
-        if (section.id === SECTIONS_ID[0]) {
499
-            const data: dashboardItem = item;
500
-            return this.getDashboardItem(data);
501
-        } else {
502
-            const data: feedItem = item;
503
-            return this.getFeedItem(data);
504
-        }
505
-    };
506
-
507
-    openScanner = () => this.props.navigation.navigate("scanner");
452
+    getRenderItem = ({item}: { item: feedItem, }) => this.getFeedItem(item);
508 453
 
509 454
     onScroll = (event: SyntheticEvent<EventTarget>) => {
510 455
         if (this.fabRef.current != null)
511 456
             this.fabRef.current.onScroll(event);
512 457
     };
513 458
 
514
-    renderSectionHeader = (data: { [key: string]: any }) => {
515
-        if (data.section.title !== "")
459
+    renderSectionHeader = (data: { section: { [key: string]: any } }, isLoading: boolean) => {
460
+        if (data.section.data.length > 0)
516 461
             return (
517 462
                 <Headline style={{
518 463
                     textAlign: "center",
@@ -523,7 +468,59 @@ class HomeScreen extends React.Component<Props, State> {
523 468
                 </Headline>
524 469
             )
525 470
         else
526
-            return null;
471
+            return (
472
+                <View>
473
+                    <Headline style={{
474
+                        textAlign: "center",
475
+                        marginTop: 50,
476
+                        marginBottom: 10,
477
+                        marginLeft: 20,
478
+                        marginRight: 20,
479
+                        color: this.props.theme.colors.textDisabled
480
+                    }}>
481
+                        {data.section.title}
482
+                    </Headline>
483
+                    {isLoading
484
+                        ? <ActivityIndicator
485
+                            style={{
486
+                                marginTop: 10
487
+                            }}
488
+                        />
489
+                        : <MaterialCommunityIcons
490
+                            name={"access-point-network-off"}
491
+                            size={100}
492
+                            color={this.props.theme.colors.textDisabled}
493
+                            style={{
494
+                                marginLeft: "auto",
495
+                                marginRight: "auto",
496
+                            }}
497
+                        />}
498
+
499
+                </View>
500
+            );
501
+    }
502
+
503
+    getListHeader = (fetchedData: rawDashboard) => {
504
+        let dashboard = null;
505
+        if (fetchedData != null) {
506
+            dashboard = fetchedData.dashboard;
507
+        }
508
+
509
+        return (
510
+            <Animatable.View
511
+                animation={"fadeInDown"}
512
+                duration={500}
513
+                useNativeDriver={true}
514
+            >
515
+                {this.getDashboardActions()}
516
+                {this.getDashboardRow(this.dashboardManager.getCurrentDashboard())}
517
+                {this.getDashboardEvent(
518
+                    dashboard == null
519
+                        ? []
520
+                        : dashboard.today_events
521
+                )}
522
+            </Animatable.View>
523
+        );
527 524
     }
528 525
 
529 526
     /**
@@ -556,6 +553,7 @@ class HomeScreen extends React.Component<Props, State> {
556 553
                         onScroll={this.onScroll}
557 554
                         showError={false}
558 555
                         renderSectionHeader={this.renderSectionHeader}
556
+                        renderListHeaderComponent={this.getListHeader}
559 557
                     />
560 558
                 </View>
561 559
                 <MascotPopup

Loading…
Cancel
Save