Browse Source

Update mascot components to use TypeScript

Arnaud Vergnet 1 year ago
parent
commit
172b7e8187

src/components/Mascot/Mascot.js → src/components/Mascot/Mascot.tsx View File

@@ -17,27 +17,27 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import * as Animatable from 'react-native-animatable';
24
-import {Image, TouchableWithoutFeedback, View} from 'react-native';
25
-import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
22
+import {Image, TouchableWithoutFeedback, View, ViewStyle} from 'react-native';
23
+import {AnimatableProperties} from 'react-native-animatable';
26 24
 
27
-export type AnimatableViewRefType = {current: null | Animatable.View};
25
+export type AnimatableViewRefType = {
26
+  current: null | (typeof Animatable.View & View);
27
+};
28 28
 
29 29
 type PropsType = {
30
-  emotion?: number,
31
-  animated?: boolean,
32
-  style?: ViewStyle | null,
33
-  entryAnimation?: Animatable.AnimatableProperties | null,
34
-  loopAnimation?: Animatable.AnimatableProperties | null,
35
-  onPress?: null | ((viewRef: AnimatableViewRefType) => void),
36
-  onLongPress?: null | ((viewRef: AnimatableViewRefType) => void),
30
+  emotion?: MASCOT_STYLE;
31
+  animated?: boolean;
32
+  style?: ViewStyle;
33
+  entryAnimation?: AnimatableProperties<ViewStyle>;
34
+  loopAnimation?: AnimatableProperties<ViewStyle>;
35
+  onPress?: null | ((viewRef: AnimatableViewRefType) => void);
36
+  onLongPress?: null | ((viewRef: AnimatableViewRefType) => void);
37 37
 };
38 38
 
39 39
 type StateType = {
40
-  currentEmotion: number,
40
+  currentEmotion: MASCOT_STYLE;
41 41
 };
42 42
 
43 43
 const MASCOT_IMAGE = require('../../../assets/mascot/mascot.png');
@@ -50,32 +50,32 @@ const MASCOT_EYES_ANGRY = require('../../../assets/mascot/mascot_eyes_angry.png'
50 50
 const MASCOT_GLASSES = require('../../../assets/mascot/mascot_glasses.png');
51 51
 const MASCOT_SUNGLASSES = require('../../../assets/mascot/mascot_sunglasses.png');
52 52
 
53
-export const EYE_STYLE = {
54
-  NORMAL: 0,
55
-  GIRLY: 2,
56
-  CUTE: 3,
57
-  WINK: 4,
58
-  HEART: 5,
59
-  ANGRY: 6,
60
-};
53
+enum EYE_STYLE {
54
+  NORMAL,
55
+  GIRLY,
56
+  CUTE,
57
+  WINK,
58
+  HEART,
59
+  ANGRY,
60
+}
61 61
 
62
-const GLASSES_STYLE = {
63
-  NORMAL: 0,
64
-  COOl: 1,
65
-};
62
+enum GLASSES_STYLE {
63
+  NORMAL,
64
+  COOl,
65
+}
66 66
 
67
-export const MASCOT_STYLE = {
68
-  NORMAL: 0,
69
-  HAPPY: 1,
70
-  GIRLY: 2,
71
-  WINK: 3,
72
-  CUTE: 4,
73
-  INTELLO: 5,
74
-  LOVE: 6,
75
-  COOL: 7,
76
-  ANGRY: 8,
77
-  RANDOM: 999,
78
-};
67
+export enum MASCOT_STYLE {
68
+  NORMAL,
69
+  HAPPY,
70
+  GIRLY,
71
+  WINK,
72
+  CUTE,
73
+  INTELLO,
74
+  LOVE,
75
+  COOL,
76
+  ANGRY,
77
+  RANDOM = 999,
78
+}
79 79
 
80 80
 class Mascot extends React.Component<PropsType, StateType> {
81 81
   static defaultProps = {
@@ -100,9 +100,9 @@ class Mascot extends React.Component<PropsType, StateType> {
100 100
 
101 101
   viewRef: AnimatableViewRefType;
102 102
 
103
-  eyeList: {[key: number]: number | string};
103
+  eyeList: {[key in EYE_STYLE]: number};
104 104
 
105
-  glassesList: {[key: number]: number | string};
105
+  glassesList: {[key in GLASSES_STYLE]: number};
106 106
 
107 107
   onPress: (viewRef: AnimatableViewRefType) => void;
108 108
 
@@ -113,23 +113,25 @@ class Mascot extends React.Component<PropsType, StateType> {
113 113
   constructor(props: PropsType) {
114 114
     super(props);
115 115
     this.viewRef = React.createRef();
116
-    this.eyeList = {};
117
-    this.glassesList = {};
118
-    this.eyeList[EYE_STYLE.NORMAL] = MASCOT_EYES_NORMAL;
119
-    this.eyeList[EYE_STYLE.GIRLY] = MASCOT_EYES_GIRLY;
120
-    this.eyeList[EYE_STYLE.CUTE] = MASCOT_EYES_CUTE;
121
-    this.eyeList[EYE_STYLE.WINK] = MASCOT_EYES_WINK;
122
-    this.eyeList[EYE_STYLE.HEART] = MASCOT_EYES_HEART;
123
-    this.eyeList[EYE_STYLE.ANGRY] = MASCOT_EYES_ANGRY;
124
-
125
-    this.glassesList[GLASSES_STYLE.NORMAL] = MASCOT_GLASSES;
126
-    this.glassesList[GLASSES_STYLE.COOl] = MASCOT_SUNGLASSES;
127
-
128
-    this.initialEmotion =
129
-      props.emotion != null ? props.emotion : Mascot.defaultProps.emotion;
116
+    this.eyeList = {
117
+      [EYE_STYLE.NORMAL]: MASCOT_EYES_NORMAL,
118
+      [EYE_STYLE.GIRLY]: MASCOT_EYES_GIRLY,
119
+      [EYE_STYLE.CUTE]: MASCOT_EYES_CUTE,
120
+      [EYE_STYLE.WINK]: MASCOT_EYES_WINK,
121
+      [EYE_STYLE.HEART]: MASCOT_EYES_HEART,
122
+      [EYE_STYLE.ANGRY]: MASCOT_EYES_ANGRY,
123
+    };
124
+    this.glassesList = {
125
+      [GLASSES_STYLE.NORMAL]: MASCOT_GLASSES,
126
+      [GLASSES_STYLE.COOl]: MASCOT_SUNGLASSES,
127
+    };
128
+    this.initialEmotion = props.emotion
129
+      ? props.emotion
130
+      : Mascot.defaultProps.emotion;
130 131
 
131
-    if (this.initialEmotion === MASCOT_STYLE.RANDOM)
132
+    if (this.initialEmotion === MASCOT_STYLE.RANDOM) {
132 133
       this.initialEmotion = Math.floor(Math.random() * MASCOT_STYLE.ANGRY) + 1;
134
+    }
133 135
 
134 136
     this.state = {
135 137
       currentEmotion: this.initialEmotion,
@@ -138,29 +140,33 @@ class Mascot extends React.Component<PropsType, StateType> {
138 140
     if (props.onPress == null) {
139 141
       this.onPress = (viewRef: AnimatableViewRefType) => {
140 142
         const ref = viewRef.current;
141
-        if (ref != null) {
143
+        if (ref && ref.rubberBand) {
142 144
           this.setState({currentEmotion: MASCOT_STYLE.LOVE});
143 145
           ref.rubberBand(1500).then(() => {
144 146
             this.setState({currentEmotion: this.initialEmotion});
145 147
           });
146 148
         }
147 149
       };
148
-    } else this.onPress = props.onPress;
150
+    } else {
151
+      this.onPress = props.onPress;
152
+    }
149 153
 
150 154
     if (props.onLongPress == null) {
151 155
       this.onLongPress = (viewRef: AnimatableViewRefType) => {
152 156
         const ref = viewRef.current;
153
-        if (ref != null) {
157
+        if (ref && ref.tada) {
154 158
           this.setState({currentEmotion: MASCOT_STYLE.ANGRY});
155 159
           ref.tada(1000).then(() => {
156 160
             this.setState({currentEmotion: this.initialEmotion});
157 161
           });
158 162
         }
159 163
       };
160
-    } else this.onLongPress = props.onLongPress;
164
+    } else {
165
+      this.onLongPress = props.onLongPress;
166
+    }
161 167
   }
162 168
 
163
-  getGlasses(style: number): React.Node {
169
+  getGlasses(style: GLASSES_STYLE) {
164 170
     const glasses = this.glassesList[style];
165 171
     return (
166 172
       <Image
@@ -179,11 +185,7 @@ class Mascot extends React.Component<PropsType, StateType> {
179 185
     );
180 186
   }
181 187
 
182
-  getEye(
183
-    style: number,
184
-    isRight: boolean,
185
-    rotation: string = '0deg',
186
-  ): React.Node {
188
+  getEye(style: EYE_STYLE, isRight: boolean, rotation: string = '0deg') {
187 189
     const eye = this.eyeList[style];
188 190
     return (
189 191
       <Image
@@ -201,7 +203,7 @@ class Mascot extends React.Component<PropsType, StateType> {
201 203
     );
202 204
   }
203 205
 
204
-  getEyes(emotion: number): React.Node {
206
+  getEyes(emotion: MASCOT_STYLE) {
205 207
     const final = [];
206 208
     final.push(
207 209
       <View
@@ -246,7 +248,7 @@ class Mascot extends React.Component<PropsType, StateType> {
246 248
     return final;
247 249
   }
248 250
 
249
-  render(): React.Node {
251
+  render() {
250 252
     const {props, state} = this;
251 253
     const entryAnimation = props.animated ? props.entryAnimation : null;
252 254
     const loopAnimation = props.animated ? props.loopAnimation : null;
@@ -256,7 +258,6 @@ class Mascot extends React.Component<PropsType, StateType> {
256 258
           aspectRatio: 1,
257 259
           ...props.style,
258 260
         }}
259
-        // eslint-disable-next-line react/jsx-props-no-spreading
260 261
         {...entryAnimation}>
261 262
         <TouchableWithoutFeedback
262 263
           onPress={() => {
@@ -266,9 +267,7 @@ class Mascot extends React.Component<PropsType, StateType> {
266 267
             this.onLongPress(this.viewRef);
267 268
           }}>
268 269
           <Animatable.View ref={this.viewRef}>
269
-            <Animatable.View
270
-              // eslint-disable-next-line react/jsx-props-no-spreading
271
-              {...loopAnimation}>
270
+            <Animatable.View {...loopAnimation}>
272 271
               <Image
273 272
                 source={MASCOT_IMAGE}
274 273
                 style={{

src/components/Mascot/MascotPopup.js → src/components/Mascot/MascotPopup.tsx View File

@@ -17,8 +17,6 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
21
-
22 20
 import * as React from 'react';
23 21
 import {
24 22
   Avatar,
@@ -37,48 +35,42 @@ import {
37 35
   View,
38 36
 } from 'react-native';
39 37
 import Mascot from './Mascot';
40
-import type {CustomThemeType} from '../../managers/ThemeManager';
41 38
 import SpeechArrow from './SpeechArrow';
42 39
 import AsyncStorageManager from '../../managers/AsyncStorageManager';
43 40
 
44 41
 type PropsType = {
45
-  theme: CustomThemeType,
46
-  icon: string,
47
-  title: string,
48
-  message: string,
42
+  theme: ReactNativePaper.Theme;
43
+  icon: string;
44
+  title: string;
45
+  message: string;
49 46
   buttons: {
50
-    action: {
51
-      message: string,
52
-      icon: string | null,
53
-      color: string | null,
54
-      onPress?: () => void,
55
-    },
56
-    cancel: {
57
-      message: string,
58
-      icon: string | null,
59
-      color: string | null,
60
-      onPress?: () => void,
61
-    },
62
-  },
63
-  emotion: number,
64
-  visible?: boolean,
65
-  prefKey?: string,
47
+    action?: {
48
+      message: string;
49
+      icon?: string;
50
+      color?: string;
51
+      onPress?: () => void;
52
+    };
53
+    cancel?: {
54
+      message: string;
55
+      icon?: string;
56
+      color?: string;
57
+      onPress?: () => void;
58
+    };
59
+  };
60
+  emotion: number;
61
+  visible?: boolean;
62
+  prefKey?: string;
66 63
 };
67 64
 
68 65
 type StateType = {
69
-  shouldRenderDialog: boolean, // Used to stop rendering after hide animation
70
-  dialogVisible: boolean,
66
+  shouldRenderDialog: boolean; // Used to stop rendering after hide animation
67
+  dialogVisible: boolean;
71 68
 };
72 69
 
73 70
 /**
74 71
  * Component used to display a popup with the mascot.
75 72
  */
76 73
 class MascotPopup extends React.Component<PropsType, StateType> {
77
-  static defaultProps = {
78
-    visible: null,
79
-    prefKey: null,
80
-  };
81
-
82 74
   mascotSize: number;
83 75
 
84 76
   windowWidth: number;
@@ -112,7 +104,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
112 104
     }
113 105
   }
114 106
 
115
-  componentDidMount(): * {
107
+  componentDidMount() {
116 108
     BackHandler.addEventListener(
117 109
       'hardwareBackPress',
118 110
       this.onBackButtonPressAndroid,
@@ -146,14 +138,20 @@ class MascotPopup extends React.Component<PropsType, StateType> {
146 138
     if (state.dialogVisible) {
147 139
       const {cancel} = props.buttons;
148 140
       const {action} = props.buttons;
149
-      if (cancel != null) this.onDismiss(cancel.onPress);
150
-      else this.onDismiss(action.onPress);
141
+      if (cancel) {
142
+        this.onDismiss(cancel.onPress);
143
+      } else if (action) {
144
+        this.onDismiss(action.onPress);
145
+      } else {
146
+        this.onDismiss();
147
+      }
148
+
151 149
       return true;
152 150
     }
153 151
     return false;
154 152
   };
155 153
 
156
-  getSpeechBubble(): React.Node {
154
+  getSpeechBubble() {
157 155
     const {state, props} = this;
158 156
     return (
159 157
       <Animatable.View
@@ -179,7 +177,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
179 177
             title={props.title}
180 178
             left={
181 179
               props.icon != null
182
-                ? (): React.Node => (
180
+                ? () => (
183 181
                     <Avatar.Icon
184 182
                       size={48}
185 183
                       style={{backgroundColor: 'transparent'}}
@@ -187,7 +185,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
187 185
                       icon={props.icon}
188 186
                     />
189 187
                   )
190
-                : null
188
+                : undefined
191 189
             }
192 190
           />
193 191
           <Card.Content
@@ -207,7 +205,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
207 205
     );
208 206
   }
209 207
 
210
-  getMascot(): React.Node {
208
+  getMascot() {
211 209
     const {props, state} = this;
212 210
     return (
213 211
       <Animatable.View
@@ -223,7 +221,7 @@ class MascotPopup extends React.Component<PropsType, StateType> {
223 221
     );
224 222
   }
225 223
 
226
-  getButtons(): React.Node {
224
+  getButtons() {
227 225
     const {props} = this;
228 226
     const {action} = props.buttons;
229 227
     const {cancel} = props.buttons;
@@ -270,12 +268,12 @@ class MascotPopup extends React.Component<PropsType, StateType> {
270 268
     );
271 269
   }
272 270
 
273
-  getBackground(): React.Node {
271
+  getBackground() {
274 272
     const {props, state} = this;
275 273
     return (
276 274
       <TouchableWithoutFeedback
277 275
         onPress={() => {
278
-          this.onDismiss(props.buttons.cancel.onPress);
276
+          this.onDismiss(props.buttons.cancel?.onPress);
279 277
         }}>
280 278
         <Animatable.View
281 279
           style={{
@@ -298,10 +296,12 @@ class MascotPopup extends React.Component<PropsType, StateType> {
298 296
       AsyncStorageManager.set(prefKey, false);
299 297
       this.setState({dialogVisible: false});
300 298
     }
301
-    if (callback != null) callback();
299
+    if (callback != null) {
300
+      callback();
301
+    }
302 302
   };
303 303
 
304
-  render(): React.Node {
304
+  render() {
305 305
     const {shouldRenderDialog} = this.state;
306 306
     if (shouldRenderDialog) {
307 307
       return (

+ 0
- 62
src/components/Mascot/SpeechArrow.js View File

@@ -1,62 +0,0 @@
1
-/*
2
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
3
- *
4
- * This file is part of Campus INSAT.
5
- *
6
- * Campus INSAT is free software: you can redistribute it and/or modify
7
- *  it under the terms of the GNU General Public License as published by
8
- * the Free Software Foundation, either version 3 of the License, or
9
- * (at your option) any later version.
10
- *
11
- * Campus INSAT is distributed in the hope that it will be useful,
12
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
- * GNU General Public License for more details.
15
- *
16
- * You should have received a copy of the GNU General Public License
17
- * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18
- */
19
-
20
-// @flow
21
-
22
-import * as React from 'react';
23
-import {View} from 'react-native';
24
-import type {ViewStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
25
-
26
-type PropsType = {
27
-  style?: ViewStyle | null,
28
-  size: number,
29
-  color: string,
30
-};
31
-
32
-export default class SpeechArrow extends React.Component<PropsType> {
33
-  static defaultProps = {
34
-    style: null,
35
-  };
36
-
37
-  shouldComponentUpdate(): boolean {
38
-    return false;
39
-  }
40
-
41
-  render(): React.Node {
42
-    const {props} = this;
43
-    return (
44
-      <View style={props.style}>
45
-        <View
46
-          style={{
47
-            width: 0,
48
-            height: 0,
49
-            borderLeftWidth: 0,
50
-            borderRightWidth: props.size,
51
-            borderBottomWidth: props.size,
52
-            borderStyle: 'solid',
53
-            backgroundColor: 'transparent',
54
-            borderLeftColor: 'transparent',
55
-            borderRightColor: 'transparent',
56
-            borderBottomColor: props.color,
57
-          }}
58
-        />
59
-      </View>
60
-    );
61
-  }
62
-}

src/constants/PaperStyles.js → src/components/Mascot/SpeechArrow.tsx View File

@@ -17,15 +17,32 @@
17 17
  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
-// @flow
20
+import * as React from 'react';
21
+import {View, ViewStyle} from 'react-native';
21 22
 
22
-import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
23
-
24
-export type ListIconPropsType = {
25
-  color: string,
26
-  style: ViewStyleProp,
23
+type PropsType = {
24
+  style?: ViewStyle;
25
+  size: number;
26
+  color: string;
27 27
 };
28 28
 
29
-export type CardTitleIconPropsType = {
30
-  size: number,
31
-};
29
+export default function SpeechArrow(props: PropsType) {
30
+  return (
31
+    <View style={props.style}>
32
+      <View
33
+        style={{
34
+          width: 0,
35
+          height: 0,
36
+          borderLeftWidth: 0,
37
+          borderRightWidth: props.size,
38
+          borderBottomWidth: props.size,
39
+          borderStyle: 'solid',
40
+          backgroundColor: 'transparent',
41
+          borderLeftColor: 'transparent',
42
+          borderRightColor: 'transparent',
43
+          borderBottomColor: props.color,
44
+        }}
45
+      />
46
+    </View>
47
+  );
48
+}

Loading…
Cancel
Save