forked from vergnet/application-amicale
		
	Improved doc and typing and removed unused file
This commit is contained in:
		
							parent
							
								
									869a8e5ec0
								
							
						
					
					
						commit
						b66e50eaf8
					
				
					 15 changed files with 427 additions and 228 deletions
				
			
		|  | @ -4,6 +4,7 @@ import * as React from 'react'; | ||||||
| import {FlatList} from "react-native"; | import {FlatList} from "react-native"; | ||||||
| import packageJson from '../../../package'; | import packageJson from '../../../package'; | ||||||
| import {List} from 'react-native-paper'; | import {List} from 'react-native-paper'; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
| 
 | 
 | ||||||
| type listItem = { | type listItem = { | ||||||
|     name: string, |     name: string, | ||||||
|  | @ -16,7 +17,7 @@ type listItem = { | ||||||
|  * @param object The raw json |  * @param object The raw json | ||||||
|  * @return {Array<listItem>} |  * @return {Array<listItem>} | ||||||
|  */ |  */ | ||||||
| function generateListFromObject(object: { [string]: string }): Array<listItem> { | function generateListFromObject(object: { [key: string]: string }): Array<listItem> { | ||||||
|     let list = []; |     let list = []; | ||||||
|     let keys = Object.keys(object); |     let keys = Object.keys(object); | ||||||
|     let values = Object.values(object); |     let values = Object.values(object); | ||||||
|  | @ -28,8 +29,7 @@ function generateListFromObject(object: { [string]: string }): Array<listItem> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object, |     navigation: StackNavigationProp, | ||||||
|     route: Object |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const LIST_ITEM_HEIGHT = 64; | const LIST_ITEM_HEIGHT = 64; | ||||||
|  | @ -39,23 +39,23 @@ const LIST_ITEM_HEIGHT = 64; | ||||||
|  */ |  */ | ||||||
| export default class AboutDependenciesScreen extends React.Component<Props> { | export default class AboutDependenciesScreen extends React.Component<Props> { | ||||||
| 
 | 
 | ||||||
|     data: Array<Object>; |     data: Array<listItem>; | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|         super(); |         super(); | ||||||
|         this.data = generateListFromObject(packageJson.dependencies); |         this.data = generateListFromObject(packageJson.dependencies); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     keyExtractor = (item: Object) => item.name; |     keyExtractor = (item: listItem) => item.name; | ||||||
| 
 | 
 | ||||||
|     renderItem = ({item}: Object) => |     renderItem = ({item}: { item: listItem }) => | ||||||
|         <List.Item |         <List.Item | ||||||
|             title={item.name} |             title={item.name} | ||||||
|             description={item.version.replace('^', '').replace('~', '')} |             description={item.version.replace('^', '').replace('~', '')} | ||||||
|             style={{height: LIST_ITEM_HEIGHT}} |             style={{height: LIST_ITEM_HEIGHT}} | ||||||
|         />; |         />; | ||||||
| 
 | 
 | ||||||
|     itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); |     itemLayout = (data: any, index: number) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); | ||||||
| 
 | 
 | ||||||
|     render() { |     render() { | ||||||
|         return ( |         return ( | ||||||
|  |  | ||||||
|  | @ -5,6 +5,14 @@ import {FlatList, Linking, Platform, View} from 'react-native'; | ||||||
| import i18n from "i18n-js"; | import i18n from "i18n-js"; | ||||||
| import {Avatar, Card, List, Title, withTheme} from 'react-native-paper'; | import {Avatar, Card, List, Title, withTheme} from 'react-native-paper'; | ||||||
| import packageJson from "../../../package.json"; | import packageJson from "../../../package.json"; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
|  | 
 | ||||||
|  | type ListItem = { | ||||||
|  |     onPressCallback: () => void, | ||||||
|  |     icon: string, | ||||||
|  |     text: string, | ||||||
|  |     showChevron: boolean | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| const links = { | const links = { | ||||||
|     appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148', |     appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148', | ||||||
|  | @ -29,7 +37,7 @@ const links = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object, |     navigation: StackNavigationProp, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -48,7 +56,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|     /** |     /** | ||||||
|      * Data to be displayed in the app card |      * Data to be displayed in the app card | ||||||
|      */ |      */ | ||||||
|     appData: Array<Object> = [ |     appData = [ | ||||||
|         { |         { | ||||||
|             onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore), |             onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore), | ||||||
|             icon: Platform.OS === "ios" ? 'apple' : 'google-play', |             icon: Platform.OS === "ios" ? 'apple' : 'google-play', | ||||||
|  | @ -83,7 +91,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|     /** |     /** | ||||||
|      * Data to be displayed in the author card |      * Data to be displayed in the author card | ||||||
|      */ |      */ | ||||||
|     authorData: Array<Object> = [ |     authorData = [ | ||||||
|         { |         { | ||||||
|             onPressCallback: () => openWebLink(links.meme), |             onPressCallback: () => openWebLink(links.meme), | ||||||
|             icon: 'account-circle', |             icon: 'account-circle', | ||||||
|  | @ -106,7 +114,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|     /** |     /** | ||||||
|      * Data to be displayed in the additional developer card |      * Data to be displayed in the additional developer card | ||||||
|      */ |      */ | ||||||
|     additionalDevData: Array<Object> = [ |     additionalDevData = [ | ||||||
|         { |         { | ||||||
|             onPressCallback: () => console.log('Meme this'), |             onPressCallback: () => console.log('Meme this'), | ||||||
|             icon: 'account', |             icon: 'account', | ||||||
|  | @ -129,7 +137,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|     /** |     /** | ||||||
|      * Data to be displayed in the technologies card |      * Data to be displayed in the technologies card | ||||||
|      */ |      */ | ||||||
|     technoData: Array<Object> = [ |     technoData = [ | ||||||
|         { |         { | ||||||
|             onPressCallback: () => openWebLink(links.react), |             onPressCallback: () => openWebLink(links.react), | ||||||
|             icon: 'react', |             icon: 'react', | ||||||
|  | @ -146,7 +154,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|     /** |     /** | ||||||
|      * Order of information cards |      * Order of information cards | ||||||
|      */ |      */ | ||||||
|     dataOrder: Array<Object> = [ |     dataOrder = [ | ||||||
|         { |         { | ||||||
|             id: 'app', |             id: 'app', | ||||||
|         }, |         }, | ||||||
|  | @ -158,16 +166,9 @@ class AboutScreen extends React.Component<Props> { | ||||||
|         }, |         }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     colors: Object; |  | ||||||
| 
 |  | ||||||
|     constructor(props) { |  | ||||||
|         super(props); |  | ||||||
|         this.colors = props.theme.colors; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Gets the app icon |      * Gets the app icon | ||||||
|  |      * | ||||||
|      * @param props |      * @param props | ||||||
|      * @return {*} |      * @return {*} | ||||||
|      */ |      */ | ||||||
|  | @ -187,7 +188,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|      * @param item The item to extract the key from |      * @param item The item to extract the key from | ||||||
|      * @return {string} The extracted key |      * @return {string} The extracted key | ||||||
|      */ |      */ | ||||||
|     keyExtractor(item: Object): string { |     keyExtractor(item: ListItem): string { | ||||||
|         return item.icon; |         return item.icon; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -271,7 +272,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|      * @param props |      * @param props | ||||||
|      * @return {*} |      * @return {*} | ||||||
|      */ |      */ | ||||||
|     getChevronIcon(props: Object) { |     getChevronIcon(props) { | ||||||
|         return ( |         return ( | ||||||
|             <List.Icon {...props} icon={'chevron-right'}/> |             <List.Icon {...props} icon={'chevron-right'}/> | ||||||
|         ); |         ); | ||||||
|  | @ -284,18 +285,18 @@ class AboutScreen extends React.Component<Props> { | ||||||
|      * @param props |      * @param props | ||||||
|      * @return {*} |      * @return {*} | ||||||
|      */ |      */ | ||||||
|     getItemIcon(item: Object, props: Object) { |     getItemIcon(item: ListItem, props) { | ||||||
|         return ( |         return ( | ||||||
|             <List.Icon {...props} icon={item.icon}/> |             <List.Icon {...props} icon={item.icon}/> | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Get a clickable card item to be rendered inside a card. |      * Gets a clickable card item to be rendered inside a card. | ||||||
|      * |      * | ||||||
|      * @returns {*} |      * @returns {*} | ||||||
|      */ |      */ | ||||||
|     getCardItem = ({item}: Object) => { |     getCardItem = ({item}: { item: ListItem }) => { | ||||||
|         const getItemIcon = this.getItemIcon.bind(this, item); |         const getItemIcon = this.getItemIcon.bind(this, item); | ||||||
|         if (item.showChevron) { |         if (item.showChevron) { | ||||||
|             return ( |             return ( | ||||||
|  | @ -323,7 +324,7 @@ class AboutScreen extends React.Component<Props> { | ||||||
|      * @param item The item to show |      * @param item The item to show | ||||||
|      * @return {*} |      * @return {*} | ||||||
|      */ |      */ | ||||||
|     getMainCard = ({item}: Object) => { |     getMainCard = ({item}: { item: { id: string } }) => { | ||||||
|         switch (item.id) { |         switch (item.id) { | ||||||
|             case 'app': |             case 'app': | ||||||
|                 return this.getAppCard(); |                 return this.getAppCard(); | ||||||
|  |  | ||||||
|  | @ -5,14 +5,24 @@ import {FlatList, View} from "react-native"; | ||||||
| import AsyncStorageManager from "../../managers/AsyncStorageManager"; | import AsyncStorageManager from "../../managers/AsyncStorageManager"; | ||||||
| import CustomModal from "../../components/Overrides/CustomModal"; | import CustomModal from "../../components/Overrides/CustomModal"; | ||||||
| import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper'; | import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper'; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
|  | import {Modalize} from "react-native-modalize"; | ||||||
|  | import type {CustomTheme} from "../../managers/ThemeManager"; | ||||||
|  | 
 | ||||||
|  | type PreferenceItem = { | ||||||
|  |     key: string, | ||||||
|  |     default: string, | ||||||
|  |     current: string, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object, |     navigation: StackNavigationProp, | ||||||
|  |     theme: CustomTheme | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type State = { | type State = { | ||||||
|     modalCurrentDisplayItem: Object, |     modalCurrentDisplayItem: PreferenceItem, | ||||||
|     currentPreferences: Array<Object>, |     currentPreferences: Array<PreferenceItem>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -21,20 +31,20 @@ type State = { | ||||||
|  */ |  */ | ||||||
| class DebugScreen extends React.Component<Props, State> { | class DebugScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     modalRef: Object; |     modalRef: Modalize; | ||||||
|     modalInputValue = ''; |     modalInputValue: string; | ||||||
| 
 |  | ||||||
|     onModalRef: Function; |  | ||||||
| 
 |  | ||||||
|     colors: Object; |  | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Copies user preferences to state for easier manipulation | ||||||
|  |      * | ||||||
|  |      * @param props | ||||||
|  |      */ | ||||||
|     constructor(props) { |     constructor(props) { | ||||||
|         super(props); |         super(props); | ||||||
|         this.onModalRef = this.onModalRef.bind(this); |         this.modalInputValue = ""; | ||||||
|         this.colors = props.theme.colors; |  | ||||||
|         let copy = {...AsyncStorageManager.getInstance().preferences}; |         let copy = {...AsyncStorageManager.getInstance().preferences}; | ||||||
|         let currentPreferences = []; |         let currentPreferences : Array<PreferenceItem> = []; | ||||||
|         Object.values(copy).map((object) => { |         Object.values(copy).map((object: any) => { | ||||||
|             currentPreferences.push(object); |             currentPreferences.push(object); | ||||||
|         }); |         }); | ||||||
|         this.state = { |         this.state = { | ||||||
|  | @ -44,10 +54,11 @@ class DebugScreen extends React.Component<Props, State> { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Show the edit modal |      * Shows the edit modal | ||||||
|  |      * | ||||||
|      * @param item |      * @param item | ||||||
|      */ |      */ | ||||||
|     showEditModal(item: Object) { |     showEditModal(item: PreferenceItem) { | ||||||
|         this.setState({ |         this.setState({ | ||||||
|             modalCurrentDisplayItem: item |             modalCurrentDisplayItem: item | ||||||
|         }); |         }); | ||||||
|  | @ -81,14 +92,14 @@ class DebugScreen extends React.Component<Props, State> { | ||||||
|                     <Button |                     <Button | ||||||
|                         mode="contained" |                         mode="contained" | ||||||
|                         dark={true} |                         dark={true} | ||||||
|                         color={this.colors.success} |                         color={this.props.theme.colors.success} | ||||||
|                         onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.modalInputValue)}> |                         onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.modalInputValue)}> | ||||||
|                         Save new value |                         Save new value | ||||||
|                     </Button> |                     </Button> | ||||||
|                     <Button |                     <Button | ||||||
|                         mode="contained" |                         mode="contained" | ||||||
|                         dark={true} |                         dark={true} | ||||||
|                         color={this.colors.danger} |                         color={this.props.theme.colors.danger} | ||||||
|                         onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.state.modalCurrentDisplayItem.default)}> |                         onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.state.modalCurrentDisplayItem.default)}> | ||||||
|                         Reset to default |                         Reset to default | ||||||
|                     </Button> |                     </Button> | ||||||
|  | @ -98,6 +109,12 @@ class DebugScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Finds the index of the given key in the preferences array | ||||||
|  |      * | ||||||
|  |      * @param key THe key to find the index of | ||||||
|  |      * @returns {number} | ||||||
|  |      */ | ||||||
|     findIndexOfKey(key: string) { |     findIndexOfKey(key: string) { | ||||||
|         let index = -1; |         let index = -1; | ||||||
|         for (let i = 0; i < this.state.currentPreferences.length; i++) { |         for (let i = 0; i < this.state.currentPreferences.length; i++) { | ||||||
|  | @ -130,11 +147,11 @@ class DebugScreen extends React.Component<Props, State> { | ||||||
|      * |      * | ||||||
|      * @param ref |      * @param ref | ||||||
|      */ |      */ | ||||||
|     onModalRef(ref: Object) { |     onModalRef = (ref: Modalize) => { | ||||||
|         this.modalRef = ref; |         this.modalRef = ref; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     renderItem = ({item}: Object) => { |     renderItem = ({item}: {item: PreferenceItem}) => { | ||||||
|         return ( |         return ( | ||||||
|             <List.Item |             <List.Item | ||||||
|                 title={item.key} |                 title={item.key} | ||||||
|  |  | ||||||
|  | @ -12,14 +12,18 @@ type Props = { | ||||||
|     collapsibleStack: Collapsible |     collapsibleStack: Collapsible | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type State = {}; | type DatasetItem = { | ||||||
|  |     name: string, | ||||||
|  |     email: string, | ||||||
|  |     icon: string, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Class defining a planning event information page. |  * Class defining a planning event information page. | ||||||
|  */ |  */ | ||||||
| class AmicaleContactScreen extends React.Component<Props, State> { | class AmicaleContactScreen extends React.Component<Props> { | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  |     // Dataset containing information about contacts
 | ||||||
|     CONTACT_DATASET = [ |     CONTACT_DATASET = [ | ||||||
|         { |         { | ||||||
|             name: i18n.t("amicaleAbout.roles.interSchools"), |             name: i18n.t("amicaleAbout.roles.interSchools"), | ||||||
|  | @ -68,18 +72,11 @@ class AmicaleContactScreen extends React.Component<Props, State> { | ||||||
|         }, |         }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     colors: Object; |     keyExtractor = (item: DatasetItem) => item.email; | ||||||
| 
 | 
 | ||||||
|     constructor(props) { |     getChevronIcon = (props) => <List.Icon {...props} icon={'chevron-right'}/>; | ||||||
|         super(props); |  | ||||||
|         this.colors = props.theme.colors; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     keyExtractor = (item: Object) => item.email; |     renderItem = ({item}: { item: DatasetItem }) => { | ||||||
| 
 |  | ||||||
|     getChevronIcon = (props: Object) => <List.Icon {...props} icon={'chevron-right'}/>; |  | ||||||
| 
 |  | ||||||
|     renderItem = ({item}: Object) => { |  | ||||||
|         const onPress = () => Linking.openURL('mailto:' + item.email); |         const onPress = () => Linking.openURL('mailto:' + item.email); | ||||||
|         return <List.Item |         return <List.Item | ||||||
|             title={item.name} |             title={item.name} | ||||||
|  |  | ||||||
|  | @ -1,85 +0,0 @@ | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; |  | ||||||
| import {ScrollView, StyleSheet} from "react-native"; |  | ||||||
| import {Button, withTheme} from 'react-native-paper'; |  | ||||||
| 
 |  | ||||||
| type Props = { |  | ||||||
|     navigation: Object, |  | ||||||
|     route: Object, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type State = {} |  | ||||||
| 
 |  | ||||||
| class AmicaleHomeScreen extends React.Component<Props, State> { |  | ||||||
| 
 |  | ||||||
|     state = {}; |  | ||||||
| 
 |  | ||||||
|     colors: Object; |  | ||||||
| 
 |  | ||||||
|     constructor(props) { |  | ||||||
|         super(props); |  | ||||||
| 
 |  | ||||||
|         this.colors = props.theme.colors; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     render() { |  | ||||||
|         const nav = this.props.navigation; |  | ||||||
|         return ( |  | ||||||
|             <ScrollView> |  | ||||||
|                 <Button |  | ||||||
|                     icon={"login"} |  | ||||||
|                     onPress={() => nav.navigate("login")} |  | ||||||
|                 > |  | ||||||
|                     LOGIN |  | ||||||
|                 </Button> |  | ||||||
|                 <Button |  | ||||||
|                     icon={"information"} |  | ||||||
|                     onPress={() => nav.navigate("amicale-contact")} |  | ||||||
|                 > |  | ||||||
|                     INFO |  | ||||||
|                 </Button> |  | ||||||
|                 <Button |  | ||||||
|                     icon={"information"} |  | ||||||
|                     onPress={() => nav.navigate("club-list")} |  | ||||||
|                 > |  | ||||||
|                     CLUBS |  | ||||||
|                 </Button> |  | ||||||
|                 <Button |  | ||||||
|                     icon={"information"} |  | ||||||
|                     onPress={() => nav.navigate("profile")} |  | ||||||
|                 > |  | ||||||
|                     PROFILE |  | ||||||
|                 </Button> |  | ||||||
|                 <Button |  | ||||||
|                     icon={"information"} |  | ||||||
|                     onPress={() => nav.navigate("vote")} |  | ||||||
|                 > |  | ||||||
|                     VOTE |  | ||||||
|                 </Button> |  | ||||||
|             </ScrollView> |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const styles = StyleSheet.create({ |  | ||||||
|     container: { |  | ||||||
|         flex: 1, |  | ||||||
|         flexDirection: 'column', |  | ||||||
|         justifyContent: 'center', |  | ||||||
|     }, |  | ||||||
|     card: { |  | ||||||
|         margin: 10, |  | ||||||
|     }, |  | ||||||
|     header: { |  | ||||||
|         fontSize: 36, |  | ||||||
|         marginBottom: 48 |  | ||||||
|     }, |  | ||||||
|     textInput: {}, |  | ||||||
|     btnContainer: { |  | ||||||
|         marginTop: 5, |  | ||||||
|         marginBottom: 10, |  | ||||||
|     } |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| export default withTheme(AmicaleHomeScreen); |  | ||||||
|  | @ -6,25 +6,11 @@ import {Card, List, Text, withTheme} from 'react-native-paper'; | ||||||
| import i18n from 'i18n-js'; | import i18n from 'i18n-js'; | ||||||
| import Autolink from "react-native-autolink"; | import Autolink from "react-native-autolink"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = {}; | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| type State = { |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| const CONTACT_LINK = 'clubs@amicale-insat.fr'; | const CONTACT_LINK = 'clubs@amicale-insat.fr'; | ||||||
| 
 | 
 | ||||||
| /** | class ClubAboutScreen extends React.Component<Props> { | ||||||
|  * Class defining a planning event information page. |  | ||||||
|  */ |  | ||||||
| class ClubAboutScreen extends React.Component<Props, State> { |  | ||||||
| 
 |  | ||||||
|     colors: Object; |  | ||||||
| 
 |  | ||||||
|     constructor(props) { |  | ||||||
|         super(props); |  | ||||||
|         this.colors = props.theme.colors; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     render() { |     render() { | ||||||
|         return ( |         return ( | ||||||
|  |  | ||||||
|  | @ -65,6 +65,12 @@ class ClubDisplayScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the name of the category with the given ID | ||||||
|  |      * | ||||||
|  |      * @param id The category's ID | ||||||
|  |      * @returns {string|*} | ||||||
|  |      */ | ||||||
|     getCategoryName(id: number) { |     getCategoryName(id: number) { | ||||||
|         if (this.categories !== null) { |         if (this.categories !== null) { | ||||||
|             for (let i = 0; i < this.categories.length; i++) { |             for (let i = 0; i < this.categories.length; i++) { | ||||||
|  | @ -75,6 +81,12 @@ class ClubDisplayScreen extends React.Component<Props, State> { | ||||||
|         return ""; |         return ""; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the view for rendering categories | ||||||
|  |      * | ||||||
|  |      * @param categories The categories to display (max 2) | ||||||
|  |      * @returns {null|*} | ||||||
|  |      */ | ||||||
|     getCategoriesRender(categories: [number, number]) { |     getCategoriesRender(categories: [number, number]) { | ||||||
|         if (this.categories === null) |         if (this.categories === null) | ||||||
|             return null; |             return null; | ||||||
|  | @ -95,12 +107,19 @@ class ClubDisplayScreen extends React.Component<Props, State> { | ||||||
|         return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>; |         return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getManagersRender(resp: Array<string>, email: string | null) { |     /** | ||||||
|         let final = []; |      * Gets the view for rendering club managers if any | ||||||
|         for (let i = 0; i < resp.length; i++) { |      * | ||||||
|             final.push(<Paragraph key={i.toString()}>{resp[i]}</Paragraph>) |      * @param managers The list of manager names | ||||||
|  |      * @param email The club contact email | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|  |     getManagersRender(managers: Array<string>, email: string | null) { | ||||||
|  |         let managersListView = []; | ||||||
|  |         for (let i = 0; i < managers.length; i++) { | ||||||
|  |             managersListView.push(<Paragraph key={i.toString()}>{managers[i]}</Paragraph>) | ||||||
|         } |         } | ||||||
|         const hasManagers = resp.length > 0; |         const hasManagers = managers.length > 0; | ||||||
|         return ( |         return ( | ||||||
|             <Card style={{marginTop: 10, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}> |             <Card style={{marginTop: 10, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}> | ||||||
|                 <Card.Title |                 <Card.Title | ||||||
|  | @ -113,13 +132,20 @@ class ClubDisplayScreen extends React.Component<Props, State> { | ||||||
|                         icon="account-tie"/>} |                         icon="account-tie"/>} | ||||||
|                 /> |                 /> | ||||||
|                 <Card.Content> |                 <Card.Content> | ||||||
|                     {final} |                     {managersListView} | ||||||
|                     {this.getEmailButton(email, hasManagers)} |                     {this.getEmailButton(email, hasManagers)} | ||||||
|                 </Card.Content> |                 </Card.Content> | ||||||
|             </Card> |             </Card> | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the email button to contact the club, or the amicale if the club does not have any managers | ||||||
|  |      * | ||||||
|  |      * @param email The club contact email | ||||||
|  |      * @param hasManagers True if the club has managers | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getEmailButton(email: string | null, hasManagers: boolean) { |     getEmailButton(email: string | null, hasManagers: boolean) { | ||||||
|         const destinationEmail = email != null && hasManagers |         const destinationEmail = email != null && hasManagers | ||||||
|             ? email |             ? email | ||||||
|  | @ -141,13 +167,21 @@ class ClubDisplayScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     updateHeaderTitle(data: Object) { |     /** | ||||||
|  |      * Updates the header title to match the given club | ||||||
|  |      * | ||||||
|  |      * @param data The club data | ||||||
|  |      */ | ||||||
|  |     updateHeaderTitle(data: club) { | ||||||
|         this.props.navigation.setOptions({title: data.name}) |         this.props.navigation.setOptions({title: data.name}) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getScreen = (response: Array<Object>) => { |     getScreen = (response: Array<{ [key: string]: any } | null>) => { | ||||||
|         let data: club = response[0]; |         let data: club | null = null; | ||||||
|         this.updateHeaderTitle(data); |         if (response[0] != null) { | ||||||
|  |             data = response[0]; | ||||||
|  |             this.updateHeaderTitle(data); | ||||||
|  |         } | ||||||
|         if (data != null) { |         if (data != null) { | ||||||
|             return ( |             return ( | ||||||
|                 <ScrollView style={{paddingLeft: 5, paddingRight: 5}}> |                 <ScrollView style={{paddingLeft: 5, paddingRight: 5}}> | ||||||
|  | @ -184,7 +218,6 @@ class ClubDisplayScreen extends React.Component<Props, State> { | ||||||
|             ); |             ); | ||||||
|         } else |         } else | ||||||
|             return null; |             return null; | ||||||
| 
 |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     render() { |     render() { | ||||||
|  |  | ||||||
|  | @ -131,6 +131,15 @@ class ClubListScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     onChipSelect = (id: number) => this.updateFilteredData(null, id); |     onChipSelect = (id: number) => this.updateFilteredData(null, id); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Updates the search string and category filter, saving them to the State. | ||||||
|  |      * | ||||||
|  |      * If the given category is already in the filter, it removes it. | ||||||
|  |      * Otherwise it adds it to the filter. | ||||||
|  |      * | ||||||
|  |      * @param filterStr The new filter string to use | ||||||
|  |      * @param categoryId The category to add/remove from the filter | ||||||
|  |      */ | ||||||
|     updateFilteredData(filterStr: string | null, categoryId: number | null) { |     updateFilteredData(filterStr: string | null, categoryId: number | null) { | ||||||
|         let newCategoriesState = [...this.state.currentlySelectedCategories]; |         let newCategoriesState = [...this.state.currentlySelectedCategories]; | ||||||
|         let newStrState = this.state.currentSearchString; |         let newStrState = this.state.currentSearchString; | ||||||
|  | @ -150,6 +159,11 @@ class ClubListScreen extends React.Component<Props, State> { | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the list header, with controls to change the categories filter | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getListHeader() { |     getListHeader() { | ||||||
|         return <ClubListHeader |         return <ClubListHeader | ||||||
|             categories={this.categories} |             categories={this.categories} | ||||||
|  | @ -158,6 +172,12 @@ class ClubListScreen extends React.Component<Props, State> { | ||||||
|         />; |         />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the category object of the given ID | ||||||
|  |      * | ||||||
|  |      * @param id The ID of the category to find | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getCategoryOfId = (id: number) => { |     getCategoryOfId = (id: number) => { | ||||||
|         for (let i = 0; i < this.categories.length; i++) { |         for (let i = 0; i < this.categories.length; i++) { | ||||||
|             if (id === this.categories[i].id) |             if (id === this.categories[i].id) | ||||||
|  | @ -165,6 +185,12 @@ class ClubListScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Checks if the given item should be rendered according to current name and category filters | ||||||
|  |      * | ||||||
|  |      * @param item The club to check | ||||||
|  |      * @returns {boolean} | ||||||
|  |      */ | ||||||
|     shouldRenderItem(item: club) { |     shouldRenderItem(item: club) { | ||||||
|         let shouldRender = this.state.currentlySelectedCategories.length === 0 |         let shouldRender = this.state.currentlySelectedCategories.length === 0 | ||||||
|             || isItemInCategoryFilter(this.state.currentlySelectedCategories, item.category); |             || isItemInCategoryFilter(this.state.currentlySelectedCategories, item.category); | ||||||
|  |  | ||||||
|  | @ -11,10 +11,11 @@ import {Collapsible} from "react-navigation-collapsible"; | ||||||
| import CustomTabBar from "../../components/Tabbar/CustomTabBar"; | import CustomTabBar from "../../components/Tabbar/CustomTabBar"; | ||||||
| import type {CustomTheme} from "../../managers/ThemeManager"; | import type {CustomTheme} from "../../managers/ThemeManager"; | ||||||
| import AsyncStorageManager from "../../managers/AsyncStorageManager"; | import AsyncStorageManager from "../../managers/AsyncStorageManager"; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object, |     navigation: StackNavigationProp, | ||||||
|     route: Object, |     route: { params: { nextScreen: string } }, | ||||||
|     collapsibleStack: Collapsible, |     collapsibleStack: Collapsible, | ||||||
|     theme: CustomTheme |     theme: CustomTheme | ||||||
| } | } | ||||||
|  | @ -47,9 +48,9 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         dialogError: 0, |         dialogError: 0, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     onEmailChange: Function; |     onEmailChange: (value: string) => null; | ||||||
|     onPasswordChange: Function; |     onPasswordChange: (value: string) => null; | ||||||
|     passwordInputRef: Object; |     passwordInputRef: { current: null | TextInput }; | ||||||
| 
 | 
 | ||||||
|     nextScreen: string | null; |     nextScreen: string | null; | ||||||
| 
 | 
 | ||||||
|  | @ -64,7 +65,10 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         this.handleNavigationParams(); |         this.handleNavigationParams(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     handleNavigationParams () { |     /** | ||||||
|  |      * Saves the screen to navigate to after a successful login if one was provided in navigation parameters | ||||||
|  |      */ | ||||||
|  |     handleNavigationParams() { | ||||||
|         if (this.props.route.params != null) { |         if (this.props.route.params != null) { | ||||||
|             if (this.props.route.params.nextScreen != null) |             if (this.props.route.params.nextScreen != null) | ||||||
|                 this.nextScreen = this.props.route.params.nextScreen; |                 this.nextScreen = this.props.route.params.nextScreen; | ||||||
|  | @ -73,6 +77,11 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Shows an error dialog with the corresponding login error | ||||||
|  |      * | ||||||
|  |      * @param error The error given by the login request | ||||||
|  |      */ | ||||||
|     showErrorDialog = (error: number) => |     showErrorDialog = (error: number) => | ||||||
|         this.setState({ |         this.setState({ | ||||||
|             dialogVisible: true, |             dialogVisible: true, | ||||||
|  | @ -81,6 +90,10 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     hideErrorDialog = () => this.setState({dialogVisible: false}); |     hideErrorDialog = () => this.setState({dialogVisible: false}); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Navigates to the screen specified in navigation parameters or simply go back tha stack. | ||||||
|  |      * Saves in user preferences to not show the login banner again. | ||||||
|  |      */ | ||||||
|     handleSuccess = () => { |     handleSuccess = () => { | ||||||
|         // Do not show the login banner again
 |         // Do not show the login banner again
 | ||||||
|         AsyncStorageManager.getInstance().savePref( |         AsyncStorageManager.getInstance().savePref( | ||||||
|  | @ -93,32 +106,75 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|             this.props.navigation.replace(this.nextScreen); |             this.props.navigation.replace(this.nextScreen); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Navigates to the Amicale website screen with the reset password link as navigation parameters | ||||||
|  |      */ | ||||||
|     onResetPasswordClick = () => this.props.navigation.navigate('amicale-website', {path: RESET_PASSWORD_PATH}); |     onResetPasswordClick = () => this.props.navigation.navigate('amicale-website', {path: RESET_PASSWORD_PATH}); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * The user has unfocused the input, his email is ready to be validated | ||||||
|  |      */ | ||||||
|     validateEmail = () => this.setState({isEmailValidated: true}); |     validateEmail = () => this.setState({isEmailValidated: true}); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Checks if the entered email is valid (matches the regex) | ||||||
|  |      * | ||||||
|  |      * @returns {boolean} | ||||||
|  |      */ | ||||||
|     isEmailValid() { |     isEmailValid() { | ||||||
|         return emailRegex.test(this.state.email); |         return emailRegex.test(this.state.email); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Checks if we should tell the user his email is invalid. | ||||||
|  |      * We should only show this if his email is invalid and has been checked when un-focusing the input | ||||||
|  |      * | ||||||
|  |      * @returns {boolean|boolean} | ||||||
|  |      */ | ||||||
|     shouldShowEmailError() { |     shouldShowEmailError() { | ||||||
|         return this.state.isEmailValidated && !this.isEmailValid(); |         return this.state.isEmailValidated && !this.isEmailValid(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * The user has unfocused the input, his password is ready to be validated | ||||||
|  |      */ | ||||||
|     validatePassword = () => this.setState({isPasswordValidated: true}); |     validatePassword = () => this.setState({isPasswordValidated: true}); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Checks if the user has entered a password | ||||||
|  |      * | ||||||
|  |      * @returns {boolean} | ||||||
|  |      */ | ||||||
|     isPasswordValid() { |     isPasswordValid() { | ||||||
|         return this.state.password !== ''; |         return this.state.password !== ''; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Checks if we should tell the user his password is invalid. | ||||||
|  |      * We should only show this if his password is invalid and has been checked when un-focusing the input | ||||||
|  |      * | ||||||
|  |      * @returns {boolean|boolean} | ||||||
|  |      */ | ||||||
|     shouldShowPasswordError() { |     shouldShowPasswordError() { | ||||||
|         return this.state.isPasswordValidated && !this.isPasswordValid(); |         return this.state.isPasswordValidated && !this.isPasswordValid(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * If the email and password are valid, and we are not loading a request, then the login button can be enabled | ||||||
|  |      * | ||||||
|  |      * @returns {boolean} | ||||||
|  |      */ | ||||||
|     shouldEnableLogin() { |     shouldEnableLogin() { | ||||||
|         return this.isEmailValid() && this.isPasswordValid() && !this.state.loading; |         return this.isEmailValid() && this.isPasswordValid() && !this.state.loading; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Called when the user input changes in the email or password field. | ||||||
|  |      * This saves the new value in the State and disabled input validation (to prevent errors to show while typing) | ||||||
|  |      * | ||||||
|  |      * @param isEmail True if the field is the email field | ||||||
|  |      * @param value The new field value | ||||||
|  |      */ | ||||||
|     onInputChange(isEmail: boolean, value: string) { |     onInputChange(isEmail: boolean, value: string) { | ||||||
|         if (isEmail) { |         if (isEmail) { | ||||||
|             this.setState({ |             this.setState({ | ||||||
|  | @ -133,8 +189,23 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     onEmailSubmit = () => this.passwordInputRef.focus(); |     /** | ||||||
|  |      * Focuses the password field when the email field is done | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|  |     onEmailSubmit = () => { | ||||||
|  |         if (this.passwordInputRef.current != null) | ||||||
|  |             this.passwordInputRef.current.focus(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Called when the user clicks on login or finishes to type his password. | ||||||
|  |      * | ||||||
|  |      * Checks if we should allow the user to login, | ||||||
|  |      * then makes the login request and enters a loading state until the request finishes | ||||||
|  |      * | ||||||
|  |      */ | ||||||
|     onSubmit = () => { |     onSubmit = () => { | ||||||
|         if (this.shouldEnableLogin()) { |         if (this.shouldEnableLogin()) { | ||||||
|             this.setState({loading: true}); |             this.setState({loading: true}); | ||||||
|  | @ -147,6 +218,11 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the form input | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getFormInput() { |     getFormInput() { | ||||||
|         return ( |         return ( | ||||||
|             <View> |             <View> | ||||||
|  | @ -173,9 +249,7 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|                     {i18n.t("loginScreen.emailError")} |                     {i18n.t("loginScreen.emailError")} | ||||||
|                 </HelperText> |                 </HelperText> | ||||||
|                 <TextInput |                 <TextInput | ||||||
|                     ref={(ref) => { |                     ref={this.passwordInputRef} | ||||||
|                         this.passwordInputRef = ref; |  | ||||||
|                     }} |  | ||||||
|                     label={i18n.t("loginScreen.password")} |                     label={i18n.t("loginScreen.password")} | ||||||
|                     mode='outlined' |                     mode='outlined' | ||||||
|                     value={this.state.password} |                     value={this.state.password} | ||||||
|  | @ -201,6 +275,10 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the card containing the input form | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getMainCard() { |     getMainCard() { | ||||||
|         return ( |         return ( | ||||||
|             <Card style={styles.card}> |             <Card style={styles.card}> | ||||||
|  | @ -239,6 +317,11 @@ class LoginScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the card containing the information about the Amicale account | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getSecondaryCard() { |     getSecondaryCard() { | ||||||
|         return ( |         return ( | ||||||
|             <Card style={styles.card}> |             <Card style={styles.card}> | ||||||
|  |  | ||||||
|  | @ -12,10 +12,12 @@ import {Collapsible} from "react-navigation-collapsible"; | ||||||
| import {withCollapsible} from "../../utils/withCollapsible"; | import {withCollapsible} from "../../utils/withCollapsible"; | ||||||
| import type {cardList} from "../../components/Lists/CardList/CardList"; | import type {cardList} from "../../components/Lists/CardList/CardList"; | ||||||
| import CardList from "../../components/Lists/CardList/CardList"; | import CardList from "../../components/Lists/CardList/CardList"; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
|  | import type {CustomTheme} from "../../managers/ThemeManager"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object, |     navigation: StackNavigationProp, | ||||||
|     theme: Object, |     theme: CustomTheme, | ||||||
|     collapsibleStack: Collapsible, |     collapsibleStack: Collapsible, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -23,6 +25,23 @@ type State = { | ||||||
|     dialogVisible: boolean, |     dialogVisible: boolean, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type ProfileData = { | ||||||
|  |     first_name: string, | ||||||
|  |     last_name: string, | ||||||
|  |     email: string, | ||||||
|  |     birthday: string, | ||||||
|  |     phone: string, | ||||||
|  |     branch: string, | ||||||
|  |     link: string, | ||||||
|  |     validity: boolean, | ||||||
|  |     clubs: Array<Club>, | ||||||
|  | } | ||||||
|  | type Club = { | ||||||
|  |     id: number, | ||||||
|  |     name: string, | ||||||
|  |     is_manager: boolean, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png"; | const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png"; | ||||||
| const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png"; | const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png"; | ||||||
| 
 | 
 | ||||||
|  | @ -34,9 +53,9 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|         dialogVisible: false, |         dialogVisible: false, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     data: Object; |     data: ProfileData; | ||||||
| 
 | 
 | ||||||
|     flatListData: Array<Object>; |     flatListData: Array<{ id: string }>; | ||||||
|     amicaleDataset: cardList; |     amicaleDataset: cardList; | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|  | @ -79,12 +98,25 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     hideDisconnectDialog = () => this.setState({dialogVisible: false}); |     hideDisconnectDialog = () => this.setState({dialogVisible: false}); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the logout header button | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getHeaderButton = () => <MaterialHeaderButtons> |     getHeaderButton = () => <MaterialHeaderButtons> | ||||||
|         <Item title="logout" iconName="logout" onPress={this.showDisconnectDialog}/> |         <Item title="logout" iconName="logout" onPress={this.showDisconnectDialog}/> | ||||||
|     </MaterialHeaderButtons>; |     </MaterialHeaderButtons>; | ||||||
| 
 | 
 | ||||||
|     getScreen = (data: Object) => { |     /** | ||||||
|         this.data = data[0]; |      * Gets the main screen component with the fetched data | ||||||
|  |      * | ||||||
|  |      * @param data The data fetched from the server | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|  |     getScreen = (data: Array<{ [key: string]: any } | null>) => { | ||||||
|  |         if (data[0] != null) { | ||||||
|  |             this.data = data[0]; | ||||||
|  |         } | ||||||
|         const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; |         const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; | ||||||
|         return ( |         return ( | ||||||
|             <View style={{flex: 1}}> |             <View style={{flex: 1}}> | ||||||
|  | @ -109,7 +141,7 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|         ) |         ) | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     getRenderItem = ({item}: Object) => { |     getRenderItem = ({item}: { item: { id: string } }) => { | ||||||
|         switch (item.id) { |         switch (item.id) { | ||||||
|             case '0': |             case '0': | ||||||
|                 return this.getWelcomeCard(); |                 return this.getWelcomeCard(); | ||||||
|  | @ -122,6 +154,11 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the list of services available with the Amicale account | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getServicesList() { |     getServicesList() { | ||||||
|         return ( |         return ( | ||||||
|             <CardList |             <CardList | ||||||
|  | @ -131,12 +168,17 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets a card welcoming the user to his account | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getWelcomeCard() { |     getWelcomeCard() { | ||||||
|         return ( |         return ( | ||||||
|             <Card style={styles.card}> |             <Card style={styles.card}> | ||||||
|                 <Card.Title |                 <Card.Title | ||||||
|                     title={i18n.t("profileScreen.welcomeTitle", {name: this.data.first_name})} |                     title={i18n.t("profileScreen.welcomeTitle", {name: this.data.first_name})} | ||||||
|                     left={(props) => <Avatar.Image |                     left={() => <Avatar.Image | ||||||
|                         size={64} |                         size={64} | ||||||
|                         source={ICON_AMICALE} |                         source={ICON_AMICALE} | ||||||
|                         style={{backgroundColor: 'transparent',}} |                         style={{backgroundColor: 'transparent',}} | ||||||
|  | @ -340,7 +382,7 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|      * @param item The club to render |      * @param item The club to render | ||||||
|      * @return {*} |      * @return {*} | ||||||
|      */ |      */ | ||||||
|     clubListItem = ({item}: Object) => { |     clubListItem = ({item}: { item: Club }) => { | ||||||
|         const onPress = () => this.openClubDetailsScreen(item.id); |         const onPress = () => this.openClubDetailsScreen(item.id); | ||||||
|         let description = i18n.t("profileScreen.isMember"); |         let description = i18n.t("profileScreen.isMember"); | ||||||
|         let icon = (props) => <List.Icon {...props} icon="chevron-right"/>; |         let icon = (props) => <List.Icon {...props} icon="chevron-right"/>; | ||||||
|  | @ -356,9 +398,9 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|         />; |         />; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     clubKeyExtractor = (item: Object) => item.name; |     clubKeyExtractor = (item: Club) => item.name; | ||||||
| 
 | 
 | ||||||
|     sortClubList = (a: Object, b: Object) => a.is_manager ? -1 : 1; |     sortClubList = (a: Club, b: Club) => a.is_manager ? -1 : 1; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Renders the list of clubs the user is part of |      * Renders the list of clubs the user is part of | ||||||
|  | @ -366,7 +408,7 @@ class ProfileScreen extends React.Component<Props, State> { | ||||||
|      * @param list The club list |      * @param list The club list | ||||||
|      * @return {*} |      * @return {*} | ||||||
|      */ |      */ | ||||||
|     getClubList(list: Array<Object>) { |     getClubList(list: Array<Club>) { | ||||||
|         list.sort(this.sortClubList); |         list.sort(this.sortClubList); | ||||||
|         return ( |         return ( | ||||||
|             //$FlowFixMe
 |             //$FlowFixMe
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import VoteTease from "../../components/Amicale/Vote/VoteTease"; | ||||||
| import VoteSelect from "../../components/Amicale/Vote/VoteSelect"; | import VoteSelect from "../../components/Amicale/Vote/VoteSelect"; | ||||||
| import VoteResults from "../../components/Amicale/Vote/VoteResults"; | import VoteResults from "../../components/Amicale/Vote/VoteResults"; | ||||||
| import VoteWait from "../../components/Amicale/Vote/VoteWait"; | import VoteWait from "../../components/Amicale/Vote/VoteWait"; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
| 
 | 
 | ||||||
| export type team = { | export type team = { | ||||||
|     id: number, |     id: number, | ||||||
|  | @ -86,13 +87,16 @@ type objectVoteDates = { | ||||||
| const MIN_REFRESH_TIME = 5 * 1000; | const MIN_REFRESH_TIME = 5 * 1000; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object |     navigation: StackNavigationProp | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type State = { | type State = { | ||||||
|     hasVoted: boolean, |     hasVoted: boolean, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Screen displaying vote information and controls | ||||||
|  |  */ | ||||||
| export default class VoteScreen extends React.Component<Props, State> { | export default class VoteScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     state = { |     state = { | ||||||
|  | @ -107,7 +111,7 @@ export default class VoteScreen extends React.Component<Props, State> { | ||||||
|     today: Date; |     today: Date; | ||||||
| 
 | 
 | ||||||
|     mainFlatListData: Array<{ key: string }>; |     mainFlatListData: Array<{ key: string }>; | ||||||
|     lastRefresh: Date; |     lastRefresh: Date | null; | ||||||
| 
 | 
 | ||||||
|     authRef: { current: null | AuthenticatedScreen }; |     authRef: { current: null | AuthenticatedScreen }; | ||||||
| 
 | 
 | ||||||
|  | @ -116,22 +120,30 @@ export default class VoteScreen extends React.Component<Props, State> { | ||||||
|         this.hasVoted = false; |         this.hasVoted = false; | ||||||
|         this.today = new Date(); |         this.today = new Date(); | ||||||
|         this.authRef = React.createRef(); |         this.authRef = React.createRef(); | ||||||
|  |         this.lastRefresh = null; | ||||||
|         this.mainFlatListData = [ |         this.mainFlatListData = [ | ||||||
|             {key: 'main'}, |             {key: 'main'}, | ||||||
|             {key: 'info'}, |             {key: 'info'}, | ||||||
|         ] |         ] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Reloads vote data if last refresh delta is smaller than the minimum refresh time | ||||||
|  |      */ | ||||||
|     reloadData = () => { |     reloadData = () => { | ||||||
|         let canRefresh; |         let canRefresh; | ||||||
|         if (this.lastRefresh !== undefined) |         const lastRefresh = this.lastRefresh; | ||||||
|             canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME; |         if (lastRefresh != null) | ||||||
|  |             canRefresh = (new Date().getTime() - lastRefresh.getTime()) > MIN_REFRESH_TIME; | ||||||
|         else |         else | ||||||
|             canRefresh = true; |             canRefresh = true; | ||||||
|         if (canRefresh && this.authRef.current != null) |         if (canRefresh && this.authRef.current != null) | ||||||
|             this.authRef.current.reload() |             this.authRef.current.reload() | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Generates the objects containing string and Date representations of key vote dates | ||||||
|  |      */ | ||||||
|     generateDateObject() { |     generateDateObject() { | ||||||
|         const strings = this.datesString; |         const strings = this.datesString; | ||||||
|         if (strings != null) { |         if (strings != null) { | ||||||
|  | @ -152,6 +164,16 @@ export default class VoteScreen extends React.Component<Props, State> { | ||||||
|             this.dates = null; |             this.dates = null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the string representation of the given date. | ||||||
|  |      * | ||||||
|  |      * If the given date is the same day as today, only return the tile. | ||||||
|  |      * Otherwise, return the full date. | ||||||
|  |      * | ||||||
|  |      * @param date The Date object representation of the wanted date | ||||||
|  |      * @param dateString The string representation of the wanted date | ||||||
|  |      * @returns {string} | ||||||
|  |      */ | ||||||
|     getDateString(date: Date, dateString: string): string { |     getDateString(date: Date, dateString: string): string { | ||||||
|         if (this.today.getDate() === date.getDate()) { |         if (this.today.getDate() === date.getDate()) { | ||||||
|             const str = getTimeOnlyString(dateString); |             const str = getTimeOnlyString(dateString); | ||||||
|  | @ -176,7 +198,7 @@ export default class VoteScreen extends React.Component<Props, State> { | ||||||
|         return this.dates != null && this.today > this.dates.date_result_begin; |         return this.dates != null && this.today > this.dates.date_result_begin; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     mainRenderItem = ({item}: Object) => { |     mainRenderItem = ({item}: { item: { key: string } }) => { | ||||||
|         if (item.key === 'info') |         if (item.key === 'info') | ||||||
|             return <VoteTitle/>; |             return <VoteTitle/>; | ||||||
|         else if (item.key === 'main' && this.dates != null) |         else if (item.key === 'main' && this.dates != null) | ||||||
|  | @ -190,8 +212,8 @@ export default class VoteScreen extends React.Component<Props, State> { | ||||||
|         // data[1] = FAKE_DATE;
 |         // data[1] = FAKE_DATE;
 | ||||||
|         this.lastRefresh = new Date(); |         this.lastRefresh = new Date(); | ||||||
| 
 | 
 | ||||||
|         const teams : teamResponse | null = data[0]; |         const teams: teamResponse | null = data[0]; | ||||||
|         const dateStrings : stringVoteDates | null = data[1]; |         const dateStrings: stringVoteDates | null = data[1]; | ||||||
| 
 | 
 | ||||||
|         if (dateStrings != null && dateStrings.date_begin == null) |         if (dateStrings != null && dateStrings.date_begin == null) | ||||||
|             this.datesString = null; |             this.datesString = null; | ||||||
|  | @ -282,6 +304,13 @@ export default class VoteScreen extends React.Component<Props, State> { | ||||||
|                          isVoteRunning={this.isVoteRunning()}/>; |                          isVoteRunning={this.isVoteRunning()}/>; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Renders the authenticated screen. | ||||||
|  |      * | ||||||
|  |      * Teams and dates are not mandatory to allow showing the information box even if api requests fail | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     render() { |     render() { | ||||||
|         return ( |         return ( | ||||||
|             <AuthenticatedScreen |             <AuthenticatedScreen | ||||||
|  |  | ||||||
|  | @ -7,29 +7,29 @@ import ImageModal from 'react-native-image-modal'; | ||||||
| import Autolink from "react-native-autolink"; | import Autolink from "react-native-autolink"; | ||||||
| import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton"; | import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton"; | ||||||
| import CustomTabBar from "../../components/Tabbar/CustomTabBar"; | import CustomTabBar from "../../components/Tabbar/CustomTabBar"; | ||||||
|  | import {StackNavigationProp} from "@react-navigation/stack"; | ||||||
|  | import type {feedItem} from "./HomeScreen"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|     navigation: Object, |     navigation: StackNavigationProp, | ||||||
|     route: Object |     route: { params: { data: feedItem, date: string } } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const ICON_AMICALE = require('../../../assets/amicale.png'); | const ICON_AMICALE = require('../../../assets/amicale.png'); | ||||||
| const NAME_AMICALE = 'Amicale INSA Toulouse'; | const NAME_AMICALE = 'Amicale INSA Toulouse'; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Class defining a planning event information page. |  * Class defining a feed item page. | ||||||
|  */ |  */ | ||||||
| class FeedItemScreen extends React.Component<Props> { | class FeedItemScreen extends React.Component<Props> { | ||||||
| 
 | 
 | ||||||
|     displayData: Object; |     displayData: feedItem; | ||||||
|     date: string; |     date: string; | ||||||
| 
 | 
 | ||||||
|     colors: Object; |  | ||||||
| 
 |  | ||||||
|     constructor(props) { |     constructor(props) { | ||||||
|         super(props); |         super(props); | ||||||
|         this.colors = props.theme.colors; |         this.displayData = props.route.params.data; | ||||||
|         this.displayData = this.props.route.params.data; |         this.date = props.route.params.date; | ||||||
|         this.date = this.props.route.params.date; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     componentDidMount() { |     componentDidMount() { | ||||||
|  | @ -38,16 +38,29 @@ class FeedItemScreen extends React.Component<Props> { | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Opens the feed item out link in browser or compatible app | ||||||
|  |      */ | ||||||
|     onOutLinkPress = () => { |     onOutLinkPress = () => { | ||||||
|         Linking.openURL(this.displayData.permalink_url); |         Linking.openURL(this.displayData.permalink_url); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the out link header button | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getHeaderButton = () => { |     getHeaderButton = () => { | ||||||
|         return <MaterialHeaderButtons> |         return <MaterialHeaderButtons> | ||||||
|             <Item title="main" iconName={'facebook'} color={"#2e88fe"} onPress={this.onOutLinkPress}/> |             <Item title="main" iconName={'facebook'} color={"#2e88fe"} onPress={this.onOutLinkPress}/> | ||||||
|         </MaterialHeaderButtons>; |         </MaterialHeaderButtons>; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the Amicale INSA avatar | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getAvatar() { |     getAvatar() { | ||||||
|         return ( |         return ( | ||||||
|             <Avatar.Image size={48} source={ICON_AMICALE} |             <Avatar.Image size={48} source={ICON_AMICALE} | ||||||
|  | @ -55,8 +68,8 @@ class FeedItemScreen extends React.Component<Props> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getContent() { |     render() { | ||||||
|         const hasImage = this.displayData.full_picture !== '' && this.displayData.full_picture !== undefined; |         const hasImage = this.displayData.full_picture !== '' && this.displayData.full_picture != null; | ||||||
|         return ( |         return ( | ||||||
|             <ScrollView style={{margin: 5,}}> |             <ScrollView style={{margin: 5,}}> | ||||||
|                 <Card.Title |                 <Card.Title | ||||||
|  | @ -89,10 +102,6 @@ class FeedItemScreen extends React.Component<Props> { | ||||||
|             </ScrollView> |             </ScrollView> | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     render() { |  | ||||||
|         return this.getContent(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default withTheme(FeedItemScreen); | export default withTheme(FeedItemScreen); | ||||||
|  |  | ||||||
|  | @ -111,8 +111,6 @@ type State = { | ||||||
|  */ |  */ | ||||||
| class HomeScreen extends React.Component<Props, State> { | class HomeScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     colors: Object; |  | ||||||
| 
 |  | ||||||
|     isLoggedIn: boolean | null; |     isLoggedIn: boolean | null; | ||||||
| 
 | 
 | ||||||
|     fabRef: { current: null | AnimatedFAB }; |     fabRef: { current: null | AnimatedFAB }; | ||||||
|  | @ -125,7 +123,6 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     constructor(props) { |     constructor(props) { | ||||||
|         super(props); |         super(props); | ||||||
|         this.colors = props.theme.colors; |  | ||||||
|         this.fabRef = React.createRef(); |         this.fabRef = React.createRef(); | ||||||
|         this.currentNewFeed = []; |         this.currentNewFeed = []; | ||||||
|         this.isLoggedIn = null; |         this.isLoggedIn = null; | ||||||
|  | @ -155,6 +152,9 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Updates login state and navigation parameters on screen focus | ||||||
|  |      */ | ||||||
|     onScreenFocus = () => { |     onScreenFocus = () => { | ||||||
|         if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) { |         if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) { | ||||||
|             this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn(); |             this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn(); | ||||||
|  | @ -169,6 +169,9 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|         this.handleNavigationParams(); |         this.handleNavigationParams(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Navigates to the a new screen if navigation parameters specify one | ||||||
|  |      */ | ||||||
|     handleNavigationParams = () => { |     handleNavigationParams = () => { | ||||||
|         if (this.props.route.params != null) { |         if (this.props.route.params != null) { | ||||||
|             if (this.props.route.params.nextScreen != null) { |             if (this.props.route.params.nextScreen != null) { | ||||||
|  | @ -179,6 +182,11 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets header buttons based on login state | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getHeaderButton = () => { |     getHeaderButton = () => { | ||||||
|         let onPressLog = () => this.props.navigation.navigate("login", {nextScreen: "profile"}); |         let onPressLog = () => this.props.navigation.navigate("login", {nextScreen: "profile"}); | ||||||
|         let logIcon = "login"; |         let logIcon = "login"; | ||||||
|  | @ -262,7 +270,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|                         id: 'washers', |                         id: 'washers', | ||||||
|                         data: dashboardData == null ? 0 : dashboardData.available_machines.washers, |                         data: dashboardData == null ? 0 : dashboardData.available_machines.washers, | ||||||
|                         icon: 'washing-machine', |                         icon: 'washing-machine', | ||||||
|                         color: this.colors.proxiwashColor, |                         color: this.props.theme.colors.proxiwashColor, | ||||||
|                         onPress: this.onProxiwashClick, |                         onPress: this.onProxiwashClick, | ||||||
|                         isAvailable: dashboardData == null ? false : dashboardData.available_machines.washers > 0 |                         isAvailable: dashboardData == null ? false : dashboardData.available_machines.washers > 0 | ||||||
|                     }, |                     }, | ||||||
|  | @ -270,7 +278,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|                         id: 'dryers', |                         id: 'dryers', | ||||||
|                         data: dashboardData == null ? 0 : dashboardData.available_machines.dryers, |                         data: dashboardData == null ? 0 : dashboardData.available_machines.dryers, | ||||||
|                         icon: 'tumble-dryer', |                         icon: 'tumble-dryer', | ||||||
|                         color: this.colors.proxiwashColor, |                         color: this.props.theme.colors.proxiwashColor, | ||||||
|                         onPress: this.onProxiwashClick, |                         onPress: this.onProxiwashClick, | ||||||
|                         isAvailable: dashboardData == null ? false : dashboardData.available_machines.dryers > 0 |                         isAvailable: dashboardData == null ? false : dashboardData.available_machines.dryers > 0 | ||||||
|                     }, |                     }, | ||||||
|  | @ -278,7 +286,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|                         id: 'available_tutorials', |                         id: 'available_tutorials', | ||||||
|                         data: dashboardData == null ? 0 : dashboardData.available_tutorials, |                         data: dashboardData == null ? 0 : dashboardData.available_tutorials, | ||||||
|                         icon: 'school', |                         icon: 'school', | ||||||
|                         color: this.colors.tutorinsaColor, |                         color: this.props.theme.colors.tutorinsaColor, | ||||||
|                         onPress: this.onTutorInsaClick, |                         onPress: this.onTutorInsaClick, | ||||||
|                         isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0 |                         isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0 | ||||||
|                     }, |                     }, | ||||||
|  | @ -286,7 +294,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|                         id: 'proximo_articles', |                         id: 'proximo_articles', | ||||||
|                         data: dashboardData == null ? 0 : dashboardData.proximo_articles, |                         data: dashboardData == null ? 0 : dashboardData.proximo_articles, | ||||||
|                         icon: 'shopping', |                         icon: 'shopping', | ||||||
|                         color: this.colors.proximoColor, |                         color: this.props.theme.colors.proximoColor, | ||||||
|                         onPress: this.onProximoClick, |                         onPress: this.onProximoClick, | ||||||
|                         isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0 |                         isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0 | ||||||
|                     }, |                     }, | ||||||
|  | @ -294,7 +302,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|                         id: 'today_menu', |                         id: 'today_menu', | ||||||
|                         data: dashboardData == null ? [] : dashboardData.today_menu, |                         data: dashboardData == null ? [] : dashboardData.today_menu, | ||||||
|                         icon: 'silverware-fork-knife', |                         icon: 'silverware-fork-knife', | ||||||
|                         color: this.colors.menuColor, |                         color: this.props.theme.colors.menuColor, | ||||||
|                         onPress: this.onMenuClick, |                         onPress: this.onMenuClick, | ||||||
|                         isAvailable: dashboardData == null ? false : dashboardData.today_menu.length > 0 |                         isAvailable: dashboardData == null ? false : dashboardData.today_menu.length > 0 | ||||||
|                     }, |                     }, | ||||||
|  | @ -324,6 +332,11 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|             return this.getDashboardActions(); |             return this.getDashboardActions(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets a dashboard item with action buttons | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getDashboardActions() { |     getDashboardActions() { | ||||||
|         return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>; |         return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>; | ||||||
|     } |     } | ||||||
|  | @ -446,7 +459,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|     onEventContainerClick = () => this.props.navigation.navigate('planning'); |     onEventContainerClick = () => this.props.navigation.navigate('planning'); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Gets the event render item. |      * Gets the event dashboard render item. | ||||||
|      * If a preview is available, it will be rendered inside |      * If a preview is available, it will be rendered inside | ||||||
|      * |      * | ||||||
|      * @param content |      * @param content | ||||||
|  | @ -473,6 +486,12 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets a dashboard shortcut item | ||||||
|  |      * | ||||||
|  |      * @param item | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     dashboardRowRenderItem = ({item}: { item: dashboardSmallItem }) => { |     dashboardRowRenderItem = ({item}: { item: dashboardSmallItem }) => { | ||||||
|         return ( |         return ( | ||||||
|             <SquareDashboardItem |             <SquareDashboardItem | ||||||
|  | @ -486,7 +505,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Gets a classic dashboard item. |      * Gets a dashboard item with a row of shortcut buttons. | ||||||
|      * |      * | ||||||
|      * @param content |      * @param content | ||||||
|      * @return {*} |      * @return {*} | ||||||
|  | @ -553,7 +572,7 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Callback used when closing the banner. |      * Callback used when closing the banner. | ||||||
|      * This hides the banner and saves to preferences to prevent it from reopening |      * This hides the banner and saves to preferences to prevent it from reopening. | ||||||
|      */ |      */ | ||||||
|     onHideBanner = () => { |     onHideBanner = () => { | ||||||
|         this.setState({bannerVisible: false}); |         this.setState({bannerVisible: false}); | ||||||
|  | @ -563,6 +582,10 @@ class HomeScreen extends React.Component<Props, State> { | ||||||
|         ); |         ); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Callback when pressing the login button on the banner. | ||||||
|  |      * This hides the banner and takes the user to the login page. | ||||||
|  |      */ | ||||||
|     onLoginBanner = () => { |     onLoginBanner = () => { | ||||||
|         this.onHideBanner(); |         this.onHideBanner(); | ||||||
|         this.props.navigation.navigate("login", {nextScreen: "profile"}); |         this.props.navigation.navigate("login", {nextScreen: "profile"}); | ||||||
|  |  | ||||||
|  | @ -41,6 +41,9 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|         this.requestPermissions(); |         this.requestPermissions(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Requests permission to use the camera | ||||||
|  |      */ | ||||||
|     requestPermissions = () => { |     requestPermissions = () => { | ||||||
|         if (Platform.OS === 'android') |         if (Platform.OS === 'android') | ||||||
|             request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus) |             request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus) | ||||||
|  | @ -48,8 +51,19 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|             request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus) |             request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus) | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Updates the state permission status | ||||||
|  |      * | ||||||
|  |      * @param result | ||||||
|  |      */ | ||||||
|     updatePermissionStatus = (result) => this.setState({hasPermission: result === RESULTS.GRANTED}); |     updatePermissionStatus = (result) => this.setState({hasPermission: result === RESULTS.GRANTED}); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Opens scanned link if it is a valid app link or shows and error dialog | ||||||
|  |      * | ||||||
|  |      * @param type The barcode type | ||||||
|  |      * @param data The scanned value | ||||||
|  |      */ | ||||||
|     handleCodeScanned = ({type, data}) => { |     handleCodeScanned = ({type, data}) => { | ||||||
|         if (!URLHandler.isUrlValid(data)) |         if (!URLHandler.isUrlValid(data)) | ||||||
|             this.showErrorDialog(); |             this.showErrorDialog(); | ||||||
|  | @ -59,6 +73,11 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets a view asking user for permission to use the camera | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getPermissionScreen() { |     getPermissionScreen() { | ||||||
|         return <View style={{marginLeft: 10, marginRight: 10}}> |         return <View style={{marginLeft: 10, marginRight: 10}}> | ||||||
|             <Text>{i18n.t("scannerScreen.errorPermission")}</Text> |             <Text>{i18n.t("scannerScreen.errorPermission")}</Text> | ||||||
|  | @ -77,6 +96,9 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|         </View> |         </View> | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Shows a dialog indicating how to use the scanner | ||||||
|  |      */ | ||||||
|     showHelpDialog = () => { |     showHelpDialog = () => { | ||||||
|         this.setState({ |         this.setState({ | ||||||
|             dialogVisible: true, |             dialogVisible: true, | ||||||
|  | @ -86,6 +108,9 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|         }); |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Shows a loading dialog | ||||||
|  |      */ | ||||||
|     showOpeningDialog = () => { |     showOpeningDialog = () => { | ||||||
|         this.setState({ |         this.setState({ | ||||||
|             loading: true, |             loading: true, | ||||||
|  | @ -93,6 +118,9 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|         }); |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Shows a dialog indicating the user the scanned code was invalid | ||||||
|  |      */ | ||||||
|     showErrorDialog() { |     showErrorDialog() { | ||||||
|         this.setState({ |         this.setState({ | ||||||
|             dialogVisible: true, |             dialogVisible: true, | ||||||
|  | @ -102,11 +130,21 @@ class ScannerScreen extends React.Component<Props, State> { | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Hide any dialog | ||||||
|  |      */ | ||||||
|     onDialogDismiss = () => this.setState({ |     onDialogDismiss = () => this.setState({ | ||||||
|         dialogVisible: false, |         dialogVisible: false, | ||||||
|         scanned: false, |         scanned: false, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets a view with the scanner. | ||||||
|  |      * This scanner uses the back camera, can only scan qr codes and has a square mask on the center. | ||||||
|  |      * The mask is only for design purposes as a code is scanned as soon as it enters the camera view | ||||||
|  |      * | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|     getScanner() { |     getScanner() { | ||||||
|         return ( |         return ( | ||||||
|             <RNCamera |             <RNCamera | ||||||
|  |  | ||||||
|  | @ -32,10 +32,10 @@ export function stringMatchQuery(str: string, query: string) { | ||||||
|  * Checks if the given arrays have an item in common |  * Checks if the given arrays have an item in common | ||||||
|  * |  * | ||||||
|  * @param filter The filter array |  * @param filter The filter array | ||||||
|  * @param categories The item's categories array |  * @param categories The item's categories tuple | ||||||
|  * @returns {boolean} True if at least one entry is in both arrays |  * @returns {boolean} True if at least one entry is in both arrays | ||||||
|  */ |  */ | ||||||
| export function isItemInCategoryFilter(filter: Array<string>, categories: Array<string>) { | export function isItemInCategoryFilter(filter: Array<number>, categories: [number, number]) { | ||||||
|     for (const category of categories) { |     for (const category of categories) { | ||||||
|         if (filter.indexOf(category) !== -1) |         if (filter.indexOf(category) !== -1) | ||||||
|             return true; |             return true; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue