Browse Source

Added mascot to every main screen and allow cancel with back button

Arnaud Vergnet 1 year ago
parent
commit
2a9bf5bb6a

+ 25
- 3
src/components/Mascot/MascotPopup.js View File

@@ -4,7 +4,7 @@ import * as React from 'react';
4 4
 import {Avatar, Button, Card, Paragraph, Portal, withTheme} from 'react-native-paper';
5 5
 import Mascot from "./Mascot";
6 6
 import * as Animatable from "react-native-animatable";
7
-import {Dimensions, ScrollView, TouchableWithoutFeedback, View} from "react-native";
7
+import {BackHandler, Dimensions, ScrollView, TouchableWithoutFeedback, View} from "react-native";
8 8
 import type {CustomTheme} from "../../managers/ThemeManager";
9 9
 
10 10
 type Props = {
@@ -62,13 +62,35 @@ class MascotPopup extends React.Component<Props, State> {
62 62
     }
63 63
 
64 64
     shouldComponentUpdate(nextProps: Props): boolean {
65
-        if (nextProps.visible)
65
+        if (nextProps.visible) {
66 66
             this.state.shouldShowDialog = true;
67
-        else if (nextProps.visible !== this.props.visible)
67
+        }else if (nextProps.visible !== this.props.visible) {
68 68
             setTimeout(this.onAnimationEnd, 300);
69
+        }
69 70
         return true;
70 71
     }
71 72
 
73
+    componentDidMount(): * {
74
+        BackHandler.addEventListener(
75
+            'hardwareBackPress',
76
+            this.onBackButtonPressAndroid
77
+        )
78
+    }
79
+
80
+    onBackButtonPressAndroid = () => {
81
+        if (this.state.shouldShowDialog) {
82
+            const cancel = this.props.buttons.cancel;
83
+            const action = this.props.buttons.action;
84
+            if (cancel != null)
85
+                cancel.onPress();
86
+            else
87
+                action.onPress();
88
+            return true;
89
+        } else {
90
+            return false;
91
+        }
92
+    };
93
+
72 94
     getSpeechBubble() {
73 95
         return (
74 96
             <Animatable.View

+ 15
- 5
src/managers/AsyncStorageManager.js View File

@@ -59,8 +59,8 @@ export default class AsyncStorageManager {
59 59
             default: 'home',
60 60
             current: '',
61 61
         },
62
-        homeShowBanner: {
63
-            key: 'homeShowBanner',
62
+        servicesShowBanner: {
63
+            key: 'servicesShowBanner',
64 64
             default: '1',
65 65
             current: '',
66 66
         },
@@ -69,9 +69,14 @@ export default class AsyncStorageManager {
69 69
             default: '1',
70 70
             current: '',
71 71
         },
72
-        proxiwashWatchedMachines: {
73
-            key: 'proxiwashWatchedMachines',
74
-            default: '[]',
72
+        homeShowBanner: {
73
+            key: 'homeShowBanner',
74
+            default: '1',
75
+            current: '',
76
+        },
77
+        eventsShowBanner: {
78
+            key: 'eventsShowBanner',
79
+            default: '1',
75 80
             current: '',
76 81
         },
77 82
         planexShowBanner: {
@@ -79,6 +84,11 @@ export default class AsyncStorageManager {
79 84
             default: '1',
80 85
             current: '',
81 86
         },
87
+        proxiwashWatchedMachines: {
88
+            key: 'proxiwashWatchedMachines',
89
+            default: '[]',
90
+            current: '',
91
+        },
82 92
         showAprilFoolsStart: {
83 93
             key: 'showAprilFoolsStart',
84 94
             default: '1',

+ 62
- 28
src/screens/Planning/PlanningScreen.js View File

@@ -15,6 +15,9 @@ import {
15 15
 import {Avatar, Divider, List} from 'react-native-paper';
16 16
 import CustomAgenda from "../../components/Overrides/CustomAgenda";
17 17
 import {StackNavigationProp} from "@react-navigation/stack";
18
+import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
19
+import MascotPopup from "../../components/Mascot/MascotPopup";
20
+import AsyncStorageManager from "../../managers/AsyncStorageManager";
18 21
 
19 22
 LocaleConfig.locales['fr'] = {
20 23
     monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
@@ -33,6 +36,7 @@ type State = {
33 36
     refreshing: boolean,
34 37
     agendaItems: Object,
35 38
     calendarShowing: boolean,
39
+    mascotDialogVisible: boolean,
36 40
 };
37 41
 
38 42
 const FETCH_URL = "https://www.amicale-insat.fr/api/event/list";
@@ -52,6 +56,7 @@ class PlanningScreen extends React.Component<Props, State> {
52 56
         refreshing: false,
53 57
         agendaItems: {},
54 58
         calendarShowing: false,
59
+        mascotDialogVisible: AsyncStorageManager.getInstance().preferences.eventsShowBanner.current === "1"
55 60
     };
56 61
 
57 62
     currentDate = getDateOnlyString(getCurrentDateString());
@@ -101,6 +106,18 @@ class PlanningScreen extends React.Component<Props, State> {
101 106
     };
102 107
 
103 108
     /**
109
+     * Callback used when closing the banner.
110
+     * This hides the banner and saves to preferences to prevent it from reopening
111
+     */
112
+    onHideMascotDialog = () => {
113
+        this.setState({mascotDialogVisible: false});
114
+        AsyncStorageManager.getInstance().savePref(
115
+            AsyncStorageManager.getInstance().preferences.eventsShowBanner.key,
116
+            '0'
117
+        );
118
+    };
119
+
120
+    /**
104 121
      * Function used to check if a row has changed
105 122
      *
106 123
      * @param r1
@@ -206,34 +223,51 @@ class PlanningScreen extends React.Component<Props, State> {
206 223
 
207 224
     render() {
208 225
         return (
209
-            <CustomAgenda
210
-                {...this.props}
211
-                // the list of items that have to be displayed in agenda. If you want to render item as empty date
212
-                // the value of date key kas to be an empty array []. If there exists no value for date key it is
213
-                // considered that the date in question is not yet loaded
214
-                items={this.state.agendaItems}
215
-                // initially selected day
216
-                selected={this.currentDate}
217
-                // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
218
-                minDate={this.currentDate}
219
-                // Max amount of months allowed to scroll to the past. Default = 50
220
-                pastScrollRange={1}
221
-                // Max amount of months allowed to scroll to the future. Default = 50
222
-                futureScrollRange={AGENDA_MONTH_SPAN}
223
-                // If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
224
-                onRefresh={this.onRefresh}
225
-                // callback that fires when the calendar is opened or closed
226
-                onCalendarToggled={this.onCalendarToggled}
227
-                // Set this true while waiting for new data from a refresh
228
-                refreshing={this.state.refreshing}
229
-                renderItem={this.getRenderItem}
230
-                renderEmptyDate={this.getRenderEmptyDate}
231
-                rowHasChanged={this.rowHasChanged}
232
-                // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
233
-                firstDay={1}
234
-                // ref to this agenda in order to handle back button event
235
-                onRef={this.onAgendaRef}
236
-            />
226
+            <View style={{flex: 1}}>
227
+                <CustomAgenda
228
+                    {...this.props}
229
+                    // the list of items that have to be displayed in agenda. If you want to render item as empty date
230
+                    // the value of date key kas to be an empty array []. If there exists no value for date key it is
231
+                    // considered that the date in question is not yet loaded
232
+                    items={this.state.agendaItems}
233
+                    // initially selected day
234
+                    selected={this.currentDate}
235
+                    // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
236
+                    minDate={this.currentDate}
237
+                    // Max amount of months allowed to scroll to the past. Default = 50
238
+                    pastScrollRange={1}
239
+                    // Max amount of months allowed to scroll to the future. Default = 50
240
+                    futureScrollRange={AGENDA_MONTH_SPAN}
241
+                    // If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
242
+                    onRefresh={this.onRefresh}
243
+                    // callback that fires when the calendar is opened or closed
244
+                    onCalendarToggled={this.onCalendarToggled}
245
+                    // Set this true while waiting for new data from a refresh
246
+                    refreshing={this.state.refreshing}
247
+                    renderItem={this.getRenderItem}
248
+                    renderEmptyDate={this.getRenderEmptyDate}
249
+                    rowHasChanged={this.rowHasChanged}
250
+                    // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
251
+                    firstDay={1}
252
+                    // ref to this agenda in order to handle back button event
253
+                    onRef={this.onAgendaRef}
254
+                />
255
+                <MascotPopup
256
+                    visible={this.state.mascotDialogVisible}
257
+                    title={i18n.t("planningScreen.mascotTitle")}
258
+                    message={i18n.t("planningScreen.mascotMessage")}
259
+                    icon={"calendar-range"}
260
+                    buttons={{
261
+                        action: null,
262
+                        cancel: {
263
+                            message: i18n.t("planningScreen.mascotButton"),
264
+                            icon: "check",
265
+                            onPress: this.onHideMascotDialog,
266
+                        }
267
+                    }}
268
+                    emotion={MASCOT_STYLE.HAPPY}
269
+                />
270
+            </View>
237 271
         );
238 272
     }
239 273
 }

+ 57
- 13
src/screens/Services/ServicesScreen.js View File

@@ -14,6 +14,9 @@ import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHead
14 14
 import ConnectionManager from "../../managers/ConnectionManager";
15 15
 import {StackNavigationProp} from "@react-navigation/stack";
16 16
 import AvailableWebsites from "../../constants/AvailableWebsites";
17
+import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
18
+import MascotPopup from "../../components/Mascot/MascotPopup";
19
+import AsyncStorageManager from "../../managers/AsyncStorageManager";
17 20
 
18 21
 type Props = {
19 22
     navigation: StackNavigationProp,
@@ -21,6 +24,11 @@ type Props = {
21 24
     theme: CustomTheme,
22 25
 }
23 26
 
27
+type State = {
28
+    mascotDialogVisible: boolean,
29
+}
30
+
31
+
24 32
 export type listItem = {
25 33
     title: string,
26 34
     description: string,
@@ -49,7 +57,7 @@ const EMAIL_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Bluemind.
49 57
 const ENT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/ENT.png";
50 58
 const ACCOUNT_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Account.png";
51 59
 
52
-class ServicesScreen extends React.Component<Props> {
60
+class ServicesScreen extends React.Component<Props, State> {
53 61
 
54 62
     amicaleDataset: cardList;
55 63
     studentsDataset: cardList;
@@ -57,6 +65,10 @@ class ServicesScreen extends React.Component<Props> {
57 65
 
58 66
     finalDataset: Array<listItem>
59 67
 
68
+    state = {
69
+        mascotDialogVisible: AsyncStorageManager.getInstance().preferences.servicesShowBanner.current === "1"
70
+    }
71
+
60 72
     constructor(props) {
61 73
         super(props);
62 74
         const nav = props.navigation;
@@ -187,6 +199,19 @@ class ServicesScreen extends React.Component<Props> {
187 199
         });
188 200
     }
189 201
 
202
+
203
+    /**
204
+     * Callback used when closing the banner.
205
+     * This hides the banner and saves to preferences to prevent it from reopening
206
+     */
207
+    onHideMascotDialog = () => {
208
+        this.setState({mascotDialogVisible: false});
209
+        AsyncStorageManager.getInstance().savePref(
210
+            AsyncStorageManager.getInstance().preferences.servicesShowBanner.key,
211
+            '0'
212
+        );
213
+    };
214
+
190 215
     getAboutButton = () =>
191 216
         <MaterialHeaderButtons>
192 217
             <Item title="information" iconName="information" onPress={this.onAboutPress}/>
@@ -271,18 +296,37 @@ class ServicesScreen extends React.Component<Props> {
271 296
 
272 297
     render() {
273 298
         const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
274
-        return <Animated.FlatList
275
-            data={this.finalDataset}
276
-            renderItem={this.renderItem}
277
-            keyExtractor={this.keyExtractor}
278
-            onScroll={onScroll}
279
-            contentContainerStyle={{
280
-                paddingTop: containerPaddingTop,
281
-                paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
282
-            }}
283
-            scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
284
-            ItemSeparatorComponent={() => <Divider/>}
285
-        />
299
+        return (
300
+            <View>
301
+                <Animated.FlatList
302
+                    data={this.finalDataset}
303
+                    renderItem={this.renderItem}
304
+                    keyExtractor={this.keyExtractor}
305
+                    onScroll={onScroll}
306
+                    contentContainerStyle={{
307
+                        paddingTop: containerPaddingTop,
308
+                        paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
309
+                    }}
310
+                    scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
311
+                    ItemSeparatorComponent={() => <Divider/>}
312
+                />
313
+                <MascotPopup
314
+                    visible={this.state.mascotDialogVisible}
315
+                    title={i18n.t("servicesScreen.mascot.title")}
316
+                    message={i18n.t("servicesScreen.mascot.message")}
317
+                    icon={"calendar-range"}
318
+                    buttons={{
319
+                        action: null,
320
+                        cancel: {
321
+                            message: i18n.t("servicesScreen.mascot.button"),
322
+                            icon: "check",
323
+                            onPress: this.onHideMascotDialog,
324
+                        }
325
+                    }}
326
+                    emotion={MASCOT_STYLE.WINK}
327
+                />
328
+            </View>
329
+        );
286 330
     }
287 331
 }
288 332
 

+ 9
- 1
translations/en.json View File

@@ -436,10 +436,18 @@
436 436
       "ent": "See your grades",
437 437
       "insaAccount": "See your information and change your password",
438 438
       "equipment": "Book a BBQ or other equipment"
439
+    },
440
+    "mascot": {
441
+      "title": "So handy!",
442
+      "message": "There a lot of thing you can do with Campus!\n\nHere is a list of every service provided by the app.",
443
+      "button": "Thx buddy"
439 444
     }
440 445
   },
441 446
   "planningScreen": {
442
-    "invalidEvent": "Could not find the event. Please make sure the event you are trying to access is valid."
447
+    "invalidEvent": "Could not find the event. Please make sure the event you are trying to access is valid.",
448
+    "mascotTitle": "Let's party!",
449
+    "mascotMessage": "At the INSA, it's not all about classes!\n\nIf you want to see new people, this page is just for you. Here you will find the list of every event organised by students!",
450
+    "mascotButton": "Thx!"
443 451
   },
444 452
   "feedbackScreen": {
445 453
     "bugs": "Report Bugs",

Loading…
Cancel
Save