Browse Source

Improved login screen

Arnaud Vergnet 1 year ago
parent
commit
f8d148d7ce

+ 6
- 5
locales/en.json View File

@@ -186,11 +186,12 @@
186 186
       "password": "Password",
187 187
       "passwordError": "Please enter a password",
188 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 196
     "profile": {
196 197
       "title": "Profile",

+ 5
- 5
locales/fr.json View File

@@ -186,11 +186,11 @@
186 186
       "password": "Mot de passe",
187 187
       "passwordError": "Merci d'entrer un mot de passe",
188 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 195
     "profile": {
196 196
       "title": "Profil",

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

@@ -84,6 +84,11 @@ export default class AsyncStorageManager {
84 84
             default: '1',
85 85
             current: '',
86 86
         },
87
+        loginShowBanner: {
88
+            key: 'loginShowBanner',
89
+            default: '1',
90
+            current: '',
91
+        },
87 92
         proxiwashWatchedMachines: {
88 93
             key: 'proxiwashWatchedMachines',
89 94
             default: '[]',

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

@@ -90,14 +90,8 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
90 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 95
             {getWebsiteStack("website", MainStack, WebsiteScreen, "")}
102 96
 
103 97
 

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

@@ -1,18 +1,20 @@
1 1
 // @flow
2 2
 
3 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 6
 import ConnectionManager from "../../managers/ConnectionManager";
7 7
 import i18n from 'i18n-js';
8 8
 import ErrorDialog from "../../components/Dialogs/ErrorDialog";
9 9
 import {withCollapsible} from "../../utils/withCollapsible";
10 10
 import {Collapsible} from "react-navigation-collapsible";
11
-import CustomTabBar from "../../components/Tabbar/CustomTabBar";
12 11
 import type {CustomTheme} from "../../managers/ThemeManager";
13 12
 import AsyncStorageManager from "../../managers/AsyncStorageManager";
14 13
 import {StackNavigationProp} from "@react-navigation/stack";
15 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 19
 type Props = {
18 20
     navigation: StackNavigationProp,
@@ -29,6 +31,7 @@ type State = {
29 31
     loading: boolean,
30 32
     dialogVisible: boolean,
31 33
     dialogError: number,
34
+    mascotDialogVisible: boolean,
32 35
 }
33 36
 
34 37
 const ICON_AMICALE = require('../../../assets/amicale.png');
@@ -47,11 +50,13 @@ class LoginScreen extends React.Component<Props, State> {
47 50
         loading: false,
48 51
         dialogVisible: false,
49 52
         dialogError: 0,
53
+        mascotDialogVisible: AsyncStorageManager.getInstance().preferences.loginShowBanner.current === "1"
50 54
     };
51 55
 
52 56
     onEmailChange: (value: string) => null;
53 57
     onPasswordChange: (value: string) => null;
54 58
     passwordInputRef: { current: null | TextInput };
59
+    windowHeight: number;
55 60
 
56 61
     nextScreen: string | null;
57 62
 
@@ -61,6 +66,7 @@ class LoginScreen extends React.Component<Props, State> {
61 66
         this.onEmailChange = this.onInputChange.bind(this, true);
62 67
         this.onPasswordChange = this.onInputChange.bind(this, false);
63 68
         this.props.navigation.addListener('focus', this.onScreenFocus);
69
+        this.windowHeight = Dimensions.get('window').height;
64 70
     }
65 71
 
66 72
     onScreenFocus = () => {
@@ -79,6 +85,18 @@ class LoginScreen extends React.Component<Props, State> {
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 101
      * Shows an error dialog with the corresponding login error
84 102
      *
@@ -111,7 +129,11 @@ class LoginScreen extends React.Component<Props, State> {
111 129
     /**
112 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 139
      * The user has unfocused the input, his email is ready to be validated
@@ -283,18 +305,31 @@ class LoginScreen extends React.Component<Props, State> {
283 305
      */
284 306
     getMainCard() {
285 307
         return (
286
-            <Card style={styles.card}>
308
+            <View style={styles.card}>
287 309
                 <Card.Title
288 310
                     title={i18n.t("screens.login.title")}
311
+                    titleStyle={{color: "#fff"}}
289 312
                     subtitle={i18n.t("screens.login.subtitle")}
290
-                    left={(props) => <Avatar.Image
313
+                    subtitleStyle={{color: "#fff"}}
314
+                    left={(props) => <Image
291 315
                         {...props}
292 316
                         source={ICON_AMICALE}
293
-                        style={{backgroundColor: 'transparent'}}/>}
317
+                        style={{
318
+                            width: props.size,
319
+                            height: props.size,
320
+                        }}/>}
294 321
                 />
295 322
                 <Card.Content>
296 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 333
                         <Button
299 334
                             icon="send"
300 335
                             mode="contained"
@@ -304,76 +339,75 @@ class LoginScreen extends React.Component<Props, State> {
304 339
                             style={{marginLeft: 'auto'}}>
305 340
                             {i18n.t("screens.login.title")}
306 341
                         </Button>
342
+
307 343
                     </Card.Actions>
308 344
                     <Card.Actions>
309 345
                         <Button
310 346
                             icon="help-circle"
311 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 354
                         </Button>
316 355
                     </Card.Actions>
317 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 361
     render() {
349 362
         const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
350 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,11 +415,10 @@ class LoginScreen extends React.Component<Props, State> {
381 415
 const styles = StyleSheet.create({
382 416
     container: {
383 417
         flex: 1,
384
-        flexDirection: 'column',
385
-        justifyContent: 'center',
386 418
     },
387 419
     card: {
388
-        margin: 10,
420
+        marginTop: 'auto',
421
+        marginBottom: 'auto',
389 422
     },
390 423
     header: {
391 424
         fontSize: 36,

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

@@ -18,6 +18,7 @@ import StackNavigator, {StackNavigationOptions} from "@react-navigation/stack";
18 18
  * Set to false if the screen uses a webview as this component does not support native driver.
19 19
  * In all other cases, set it to true for increase performance.
20 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 22
  * @returns {JSX.Element}
22 23
  */
23 24
 export function createScreenCollapsibleStack(
@@ -26,7 +27,8 @@ export function createScreenCollapsibleStack(
26 27
     component: React.Node,
27 28
     title: string,
28 29
     useNativeDriver?: boolean,
29
-    options?: StackNavigationOptions) {
30
+    options?: StackNavigationOptions,
31
+    headerColor?: string) {
30 32
     const {colors} = useTheme();
31 33
     const screenOptions = options != null ? options : {};
32 34
     return createCollapsibleStack(
@@ -36,13 +38,13 @@ export function createScreenCollapsibleStack(
36 38
             options={{
37 39
                 title: title,
38 40
                 headerStyle: {
39
-                    backgroundColor: colors.surface,
41
+                    backgroundColor: headerColor!=null ? headerColor :colors.surface,
40 42
                 },
41 43
                 ...screenOptions,
42 44
             }}
43 45
         />,
44 46
         {
45
-            collapsedColor: colors.surface,
47
+            collapsedColor: headerColor!=null ? headerColor :colors.surface,
46 48
             useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
47 49
         }
48 50
     )

Loading…
Cancel
Save