Browse Source

Improved login screen

Arnaud Vergnet 3 years ago
parent
commit
f8d148d7ce

+ 6
- 5
locales/en.json View File

186
       "password": "Password",
186
       "password": "Password",
187
       "passwordError": "Please enter a password",
187
       "passwordError": "Please enter a password",
188
       "resetPassword": "Forgot Password",
188
       "resetPassword": "Forgot Password",
189
-      "whyAccountTitle": "Why have an account?",
190
-      "whyAccountSub": "What can you do wth an account",
191
-      "whyAccountParagraph": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!",
192
-      "whyAccountParagraph2": "Logging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!",
193
-      "noAccount": "No Account? Go to the Amicale's building during open hours to create one."
189
+      "mascotDialog": {
190
+        "title": "Why have an account?",
191
+        "message": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!\n\nLogging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!\n\nNo Account? Go to the Amicale's building during open hours to create one.",
192
+        "button": "OK"
193
+      }
194
+
194
     },
195
     },
195
     "profile": {
196
     "profile": {
196
       "title": "Profile",
197
       "title": "Profile",

+ 5
- 5
locales/fr.json View File

186
       "password": "Mot de passe",
186
       "password": "Mot de passe",
187
       "passwordError": "Merci d'entrer un mot de passe",
187
       "passwordError": "Merci d'entrer un mot de passe",
188
       "resetPassword": "Mdp oublié",
188
       "resetPassword": "Mdp oublié",
189
-      "whyAccountTitle": "Un compte ?",
190
-      "whyAccountSub": "Ce qu'un compte t'apporte",
191
-      "whyAccountParagraph": "Un compte Amicale te donne la possibilité de participer à diverses activités sur le campus. tu peux rejoindre des clubs ou même créer le tiens !",
192
-      "whyAccountParagraph2": "Te connecter à ton compte Amicale sur l'appli te permettra de voir tous les clubs en activité, de réserver du matériel, de voter pour les prochaines élections, et plus à venir !",
193
-      "noAccount": "Pas de compte ? Passe à l'Amicale pendant une perm pour en créer un."
189
+      "mascotDialog": {
190
+        "title": "Un compte ?",
191
+        "message": "Un compte Amicale te donne la possibilité de participer à diverses activités sur le campus. tu peux rejoindre des clubs ou même créer le tiens !\n\nTe connecter à ton compte Amicale sur l'appli te permettra de voir tous les clubs en activité, de réserver du matériel, de voter pour les prochaines élections, et plus à venir !\n\nPas de compte ? Passe à l'Amicale pendant une perm pour en créer un.",
192
+        "button": "Dac"
193
+      }
194
     },
194
     },
195
     "profile": {
195
     "profile": {
196
       "title": "Profil",
196
       "title": "Profil",

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

84
             default: '1',
84
             default: '1',
85
             current: '',
85
             current: '',
86
         },
86
         },
87
+        loginShowBanner: {
88
+            key: 'loginShowBanner',
89
+            default: '1',
90
+            current: '',
91
+        },
87
         proxiwashWatchedMachines: {
92
         proxiwashWatchedMachines: {
88
             key: 'proxiwashWatchedMachines',
93
             key: 'proxiwashWatchedMachines',
89
             default: '[]',
94
             default: '[]',

+ 2
- 8
src/navigation/MainNavigator.js View File

90
                     title: i18n.t("screens.game.title"),
90
                     title: i18n.t("screens.game.title"),
91
                 }}
91
                 }}
92
             />
92
             />
93
-            <MainStack.Screen
94
-                name="login"
95
-                component={LoginScreen}
96
-                options={{
97
-                    title: i18n.t('screens.login.title'),
98
-                }}
99
-            />
100
-
93
+            {createScreenCollapsibleStack("login", MainStack, LoginScreen, i18n.t('screens.login.title'),
94
+                true, {headerTintColor: "#fff"}, 'transparent')}
101
             {getWebsiteStack("website", MainStack, WebsiteScreen, "")}
95
             {getWebsiteStack("website", MainStack, WebsiteScreen, "")}
102
 
96
 
103
 
97
 

+ 99
- 66
src/screens/Amicale/LoginScreen.js View File

1
 // @flow
1
 // @flow
2
 
2
 
3
 import * as React from 'react';
3
 import * as React from 'react';
4
-import {Animated, KeyboardAvoidingView, StyleSheet, View} from "react-native";
5
-import {Avatar, Button, Card, HelperText, Paragraph, TextInput, withTheme} from 'react-native-paper';
4
+import {Animated, Dimensions, Image, KeyboardAvoidingView, StyleSheet, View} from "react-native";
5
+import {Button, Card, HelperText, TextInput, withTheme} from 'react-native-paper';
6
 import ConnectionManager from "../../managers/ConnectionManager";
6
 import ConnectionManager from "../../managers/ConnectionManager";
7
 import i18n from 'i18n-js';
7
 import i18n from 'i18n-js';
8
 import ErrorDialog from "../../components/Dialogs/ErrorDialog";
8
 import ErrorDialog from "../../components/Dialogs/ErrorDialog";
9
 import {withCollapsible} from "../../utils/withCollapsible";
9
 import {withCollapsible} from "../../utils/withCollapsible";
10
 import {Collapsible} from "react-navigation-collapsible";
10
 import {Collapsible} from "react-navigation-collapsible";
11
-import CustomTabBar from "../../components/Tabbar/CustomTabBar";
12
 import type {CustomTheme} from "../../managers/ThemeManager";
11
 import type {CustomTheme} from "../../managers/ThemeManager";
13
 import AsyncStorageManager from "../../managers/AsyncStorageManager";
12
 import AsyncStorageManager from "../../managers/AsyncStorageManager";
14
 import {StackNavigationProp} from "@react-navigation/stack";
13
 import {StackNavigationProp} from "@react-navigation/stack";
15
 import AvailableWebsites from "../../constants/AvailableWebsites";
14
 import AvailableWebsites from "../../constants/AvailableWebsites";
15
+import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
16
+import MascotPopup from "../../components/Mascot/MascotPopup";
17
+import LinearGradient from "react-native-linear-gradient";
16
 
18
 
17
 type Props = {
19
 type Props = {
18
     navigation: StackNavigationProp,
20
     navigation: StackNavigationProp,
29
     loading: boolean,
31
     loading: boolean,
30
     dialogVisible: boolean,
32
     dialogVisible: boolean,
31
     dialogError: number,
33
     dialogError: number,
34
+    mascotDialogVisible: boolean,
32
 }
35
 }
33
 
36
 
34
 const ICON_AMICALE = require('../../../assets/amicale.png');
37
 const ICON_AMICALE = require('../../../assets/amicale.png');
47
         loading: false,
50
         loading: false,
48
         dialogVisible: false,
51
         dialogVisible: false,
49
         dialogError: 0,
52
         dialogError: 0,
53
+        mascotDialogVisible: AsyncStorageManager.getInstance().preferences.loginShowBanner.current === "1"
50
     };
54
     };
51
 
55
 
52
     onEmailChange: (value: string) => null;
56
     onEmailChange: (value: string) => null;
53
     onPasswordChange: (value: string) => null;
57
     onPasswordChange: (value: string) => null;
54
     passwordInputRef: { current: null | TextInput };
58
     passwordInputRef: { current: null | TextInput };
59
+    windowHeight: number;
55
 
60
 
56
     nextScreen: string | null;
61
     nextScreen: string | null;
57
 
62
 
61
         this.onEmailChange = this.onInputChange.bind(this, true);
66
         this.onEmailChange = this.onInputChange.bind(this, true);
62
         this.onPasswordChange = this.onInputChange.bind(this, false);
67
         this.onPasswordChange = this.onInputChange.bind(this, false);
63
         this.props.navigation.addListener('focus', this.onScreenFocus);
68
         this.props.navigation.addListener('focus', this.onScreenFocus);
69
+        this.windowHeight = Dimensions.get('window').height;
64
     }
70
     }
65
 
71
 
66
     onScreenFocus = () => {
72
     onScreenFocus = () => {
79
         }
85
         }
80
     }
86
     }
81
 
87
 
88
+    hideMascotDialog = () => {
89
+        AsyncStorageManager.getInstance().savePref(
90
+            AsyncStorageManager.getInstance().preferences.loginShowBanner.key,
91
+            '0'
92
+        );
93
+        this.setState({mascotDialogVisible: false})
94
+    };
95
+
96
+    showMascotDialog = () => {
97
+        this.setState({mascotDialogVisible: true})
98
+    };
99
+
82
     /**
100
     /**
83
      * Shows an error dialog with the corresponding login error
101
      * Shows an error dialog with the corresponding login error
84
      *
102
      *
111
     /**
129
     /**
112
      * Navigates to the Amicale website screen with the reset password link as navigation parameters
130
      * Navigates to the Amicale website screen with the reset password link as navigation parameters
113
      */
131
      */
114
-    onResetPasswordClick = () => this.props.navigation.navigate("website", {host: AvailableWebsites.websites.AMICALE, path: RESET_PASSWORD_PATH, title: i18n.t('screens.websites.amicale')});
132
+    onResetPasswordClick = () => this.props.navigation.navigate("website", {
133
+        host: AvailableWebsites.websites.AMICALE,
134
+        path: RESET_PASSWORD_PATH,
135
+        title: i18n.t('screens.websites.amicale')
136
+    });
115
 
137
 
116
     /**
138
     /**
117
      * The user has unfocused the input, his email is ready to be validated
139
      * The user has unfocused the input, his email is ready to be validated
283
      */
305
      */
284
     getMainCard() {
306
     getMainCard() {
285
         return (
307
         return (
286
-            <Card style={styles.card}>
308
+            <View style={styles.card}>
287
                 <Card.Title
309
                 <Card.Title
288
                     title={i18n.t("screens.login.title")}
310
                     title={i18n.t("screens.login.title")}
311
+                    titleStyle={{color: "#fff"}}
289
                     subtitle={i18n.t("screens.login.subtitle")}
312
                     subtitle={i18n.t("screens.login.subtitle")}
290
-                    left={(props) => <Avatar.Image
313
+                    subtitleStyle={{color: "#fff"}}
314
+                    left={(props) => <Image
291
                         {...props}
315
                         {...props}
292
                         source={ICON_AMICALE}
316
                         source={ICON_AMICALE}
293
-                        style={{backgroundColor: 'transparent'}}/>}
317
+                        style={{
318
+                            width: props.size,
319
+                            height: props.size,
320
+                        }}/>}
294
                 />
321
                 />
295
                 <Card.Content>
322
                 <Card.Content>
296
                     {this.getFormInput()}
323
                     {this.getFormInput()}
297
-                    <Card.Actions>
324
+                    <Card.Actions style={{flexWrap: "wrap"}}>
325
+                        <Button
326
+                        icon="lock-question"
327
+                        mode="contained"
328
+                        onPress={this.onResetPasswordClick}
329
+                        color={this.props.theme.colors.warning}
330
+                        style={{marginRight: 'auto', marginBottom: 20}}>
331
+                        {i18n.t("screens.login.resetPassword")}
332
+                    </Button>
298
                         <Button
333
                         <Button
299
                             icon="send"
334
                             icon="send"
300
                             mode="contained"
335
                             mode="contained"
304
                             style={{marginLeft: 'auto'}}>
339
                             style={{marginLeft: 'auto'}}>
305
                             {i18n.t("screens.login.title")}
340
                             {i18n.t("screens.login.title")}
306
                         </Button>
341
                         </Button>
342
+
307
                     </Card.Actions>
343
                     </Card.Actions>
308
                     <Card.Actions>
344
                     <Card.Actions>
309
                         <Button
345
                         <Button
310
                             icon="help-circle"
346
                             icon="help-circle"
311
                             mode="contained"
347
                             mode="contained"
312
-                            onPress={this.onResetPasswordClick}
313
-                            style={{marginLeft: 'auto'}}>
314
-                            {i18n.t("screens.login.resetPassword")}
348
+                            onPress={this.showMascotDialog}
349
+                            style={{
350
+                                marginLeft: 'auto',
351
+                                marginRight: 'auto',
352
+                            }}>
353
+                            {i18n.t("screens.login.mascotDialog.title")}
315
                         </Button>
354
                         </Button>
316
                     </Card.Actions>
355
                     </Card.Actions>
317
                 </Card.Content>
356
                 </Card.Content>
318
-            </Card>
319
-        );
320
-    }
321
-
322
-    /**
323
-     * Gets the card containing the information about the Amicale account
324
-     *
325
-     * @returns {*}
326
-     */
327
-    getSecondaryCard() {
328
-        return (
329
-            <Card style={styles.card}>
330
-                <Card.Title
331
-                    title={i18n.t("screens.login.whyAccountTitle")}
332
-                    subtitle={i18n.t("screens.login.whyAccountSub")}
333
-                    left={(props) => <Avatar.Icon
334
-                        {...props}
335
-                        icon={"help"}
336
-                        color={this.props.theme.colors.primary}
337
-                        style={{backgroundColor: 'transparent'}}/>}
338
-                />
339
-                <Card.Content>
340
-                    <Paragraph>{i18n.t("screens.login.whyAccountParagraph")}</Paragraph>
341
-                    <Paragraph>{i18n.t("screens.login.whyAccountParagraph2")}</Paragraph>
342
-                    <Paragraph>{i18n.t("screens.login.noAccount")}</Paragraph>
343
-                </Card.Content>
344
-            </Card>
357
+            </View>
345
         );
358
         );
346
     }
359
     }
347
 
360
 
348
     render() {
361
     render() {
349
         const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
362
         const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
350
         return (
363
         return (
351
-            <KeyboardAvoidingView
352
-                behavior={"height"}
353
-                contentContainerStyle={styles.container}
354
-                style={styles.container}
355
-                enabled
356
-                keyboardVerticalOffset={100}
357
-            >
358
-                <Animated.ScrollView
359
-                    onScroll={onScroll}
360
-                    contentContainerStyle={{
361
-                        paddingTop: containerPaddingTop,
362
-                        paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
363
-                    }}
364
-                    scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
364
+            <LinearGradient
365
+                style={{
366
+                    height: "100%"
367
+                }}
368
+                colors={['#9e0d18', '#530209']}
369
+                start={{x: 0, y: 0.1}}
370
+                end={{x: 0.1, y: 1}}>
371
+                <KeyboardAvoidingView
372
+                    behavior={"height"}
373
+                    contentContainerStyle={styles.container}
374
+                    style={styles.container}
375
+                    enabled
376
+                    keyboardVerticalOffset={100}
365
                 >
377
                 >
366
-                    <View>
367
-                        {this.getMainCard()}
368
-                        {this.getSecondaryCard()}
369
-                    </View>
370
-                    <ErrorDialog
371
-                        visible={this.state.dialogVisible}
372
-                        onDismiss={this.hideErrorDialog}
373
-                        errorCode={this.state.dialogError}
374
-                    />
375
-                </Animated.ScrollView>
376
-            </KeyboardAvoidingView>
378
+                    <Animated.ScrollView
379
+                        onScroll={onScroll}
380
+                        scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
381
+                        style={{flex: 1}}
382
+                    >
383
+                        <View style={{height: this.windowHeight - containerPaddingTop}}>
384
+                            {this.getMainCard()}
385
+                        </View>
386
+
387
+
388
+                        <MascotPopup
389
+                            visible={this.state.mascotDialogVisible}
390
+                            title={i18n.t("screens.login.mascotDialog.title")}
391
+                            message={i18n.t("screens.login.mascotDialog.message")}
392
+                            icon={"help"}
393
+                            buttons={{
394
+                                action: null,
395
+                                cancel: {
396
+                                    message: i18n.t("screens.login.mascotDialog.button"),
397
+                                    icon: "check",
398
+                                    onPress: this.hideMascotDialog,
399
+                                }
400
+                            }}
401
+                            emotion={MASCOT_STYLE.NORMAL}
402
+                        />
403
+                        <ErrorDialog
404
+                            visible={this.state.dialogVisible}
405
+                            onDismiss={this.hideErrorDialog}
406
+                            errorCode={this.state.dialogError}
407
+                        />
408
+                    </Animated.ScrollView>
409
+                </KeyboardAvoidingView>
410
+            </LinearGradient>
377
         );
411
         );
378
     }
412
     }
379
 }
413
 }
381
 const styles = StyleSheet.create({
415
 const styles = StyleSheet.create({
382
     container: {
416
     container: {
383
         flex: 1,
417
         flex: 1,
384
-        flexDirection: 'column',
385
-        justifyContent: 'center',
386
     },
418
     },
387
     card: {
419
     card: {
388
-        margin: 10,
420
+        marginTop: 'auto',
421
+        marginBottom: 'auto',
389
     },
422
     },
390
     header: {
423
     header: {
391
         fontSize: 36,
424
         fontSize: 36,

+ 5
- 3
src/utils/CollapsibleUtils.js View File

18
  * Set to false if the screen uses a webview as this component does not support native driver.
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.
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.
20
  * @param options Screen options to use, or null if no options are necessary.
21
+ * @param headerColor The color of the header. Uses default color if not specified
21
  * @returns {JSX.Element}
22
  * @returns {JSX.Element}
22
  */
23
  */
23
 export function createScreenCollapsibleStack(
24
 export function createScreenCollapsibleStack(
26
     component: React.Node,
27
     component: React.Node,
27
     title: string,
28
     title: string,
28
     useNativeDriver?: boolean,
29
     useNativeDriver?: boolean,
29
-    options?: StackNavigationOptions) {
30
+    options?: StackNavigationOptions,
31
+    headerColor?: string) {
30
     const {colors} = useTheme();
32
     const {colors} = useTheme();
31
     const screenOptions = options != null ? options : {};
33
     const screenOptions = options != null ? options : {};
32
     return createCollapsibleStack(
34
     return createCollapsibleStack(
36
             options={{
38
             options={{
37
                 title: title,
39
                 title: title,
38
                 headerStyle: {
40
                 headerStyle: {
39
-                    backgroundColor: colors.surface,
41
+                    backgroundColor: headerColor!=null ? headerColor :colors.surface,
40
                 },
42
                 },
41
                 ...screenOptions,
43
                 ...screenOptions,
42
             }}
44
             }}
43
         />,
45
         />,
44
         {
46
         {
45
-            collapsedColor: colors.surface,
47
+            collapsedColor: headerColor!=null ? headerColor :colors.surface,
46
             useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
48
             useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
47
         }
49
         }
48
     )
50
     )

Loading…
Cancel
Save