forked from vergnet/application-amicale
		
	Update Amicale and related components to use TypeScript
This commit is contained in:
		
							parent
							
								
									18ec6e0a59
								
							
						
					
					
						commit
						f95635136e
					
				
					 17 changed files with 400 additions and 476 deletions
				
			
		|  | @ -17,37 +17,34 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {StackNavigationProp} from '@react-navigation/stack'; | import {StackNavigationProp} from '@react-navigation/stack'; | ||||||
| import ConnectionManager from '../../managers/ConnectionManager'; | import ConnectionManager from '../../managers/ConnectionManager'; | ||||||
| import type {ApiGenericDataType} from '../../utils/WebData'; |  | ||||||
| import {ERROR_TYPE} from '../../utils/WebData'; | import {ERROR_TYPE} from '../../utils/WebData'; | ||||||
| import ErrorView from '../Screens/ErrorView'; | import ErrorView from '../Screens/ErrorView'; | ||||||
| import BasicLoadingScreen from '../Screens/BasicLoadingScreen'; | import BasicLoadingScreen from '../Screens/BasicLoadingScreen'; | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType<T> = { | ||||||
|   navigation: StackNavigationProp, |   navigation: StackNavigationProp<any>; | ||||||
|   requests: Array<{ |   requests: Array<{ | ||||||
|     link: string, |     link: string; | ||||||
|     params: {...}, |     params: object; | ||||||
|     mandatory: boolean, |     mandatory: boolean; | ||||||
|   }>, |   }>; | ||||||
|   renderFunction: (Array<ApiGenericDataType | null>) => React.Node, |   renderFunction: (data: Array<T | null>) => React.ReactNode; | ||||||
|   errorViewOverride?: Array<{ |   errorViewOverride?: Array<{ | ||||||
|     errorCode: number, |     errorCode: number; | ||||||
|     message: string, |     message: string; | ||||||
|     icon: string, |     icon: string; | ||||||
|     showRetryButton: boolean, |     showRetryButton: boolean; | ||||||
|   }> | null, |   }> | null; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type StateType = { | type StateType = { | ||||||
|   loading: boolean, |   loading: boolean; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class AuthenticatedScreen extends React.Component<PropsType, StateType> { | class AuthenticatedScreen<T> extends React.Component<PropsType<T>, StateType> { | ||||||
|   static defaultProps = { |   static defaultProps = { | ||||||
|     errorViewOverride: null, |     errorViewOverride: null, | ||||||
|   }; |   }; | ||||||
|  | @ -58,13 +55,14 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> { | ||||||
| 
 | 
 | ||||||
|   errors: Array<number>; |   errors: Array<number>; | ||||||
| 
 | 
 | ||||||
|   fetchedData: Array<ApiGenericDataType | null>; |   fetchedData: Array<T | null>; | ||||||
| 
 | 
 | ||||||
|   constructor(props: PropsType) { |   constructor(props: PropsType<T>) { | ||||||
|     super(props); |     super(props); | ||||||
|     this.state = { |     this.state = { | ||||||
|       loading: true, |       loading: true, | ||||||
|     }; |     }; | ||||||
|  |     this.currentUserToken = null; | ||||||
|     this.connectionManager = ConnectionManager.getInstance(); |     this.connectionManager = ConnectionManager.getInstance(); | ||||||
|     props.navigation.addListener('focus', this.onScreenFocus); |     props.navigation.addListener('focus', this.onScreenFocus); | ||||||
|     this.fetchedData = new Array(props.requests.length); |     this.fetchedData = new Array(props.requests.length); | ||||||
|  | @ -91,20 +89,20 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> { | ||||||
|    * @param index The index for the data |    * @param index The index for the data | ||||||
|    * @param error The error code received |    * @param error The error code received | ||||||
|    */ |    */ | ||||||
|   onRequestFinished( |   onRequestFinished(data: T | null, index: number, error?: number) { | ||||||
|     data: ApiGenericDataType | null, |  | ||||||
|     index: number, |  | ||||||
|     error?: number, |  | ||||||
|   ) { |  | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|     if (index >= 0 && index < props.requests.length) { |     if (index >= 0 && index < props.requests.length) { | ||||||
|       this.fetchedData[index] = data; |       this.fetchedData[index] = data; | ||||||
|       this.errors[index] = error != null ? error : ERROR_TYPE.SUCCESS; |       this.errors[index] = error != null ? error : ERROR_TYPE.SUCCESS; | ||||||
|     } |     } | ||||||
|     // Token expired, logout user
 |     // Token expired, logout user
 | ||||||
|     if (error === ERROR_TYPE.BAD_TOKEN) this.connectionManager.disconnect(); |     if (error === ERROR_TYPE.BAD_TOKEN) { | ||||||
|  |       this.connectionManager.disconnect(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (this.allRequestsFinished()) this.setState({loading: false}); |     if (this.allRequestsFinished()) { | ||||||
|  |       this.setState({loading: false}); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|  | @ -132,7 +130,7 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> { | ||||||
|    * |    * | ||||||
|    * @return {*} |    * @return {*} | ||||||
|    */ |    */ | ||||||
|   getErrorRender(): React.Node { |   getErrorRender() { | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|     const errorCode = this.getError(); |     const errorCode = this.getError(); | ||||||
|     let shouldOverride = false; |     let shouldOverride = false; | ||||||
|  | @ -169,18 +167,18 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> { | ||||||
|    */ |    */ | ||||||
|   fetchData = () => { |   fetchData = () => { | ||||||
|     const {state, props} = this; |     const {state, props} = this; | ||||||
|     if (!state.loading) this.setState({loading: true}); |     if (!state.loading) { | ||||||
|  |       this.setState({loading: true}); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (this.connectionManager.isLoggedIn()) { |     if (this.connectionManager.isLoggedIn()) { | ||||||
|       for (let i = 0; i < props.requests.length; i += 1) { |       for (let i = 0; i < props.requests.length; i += 1) { | ||||||
|         this.connectionManager |         this.connectionManager | ||||||
|           .authenticatedRequest( |           .authenticatedRequest<T>( | ||||||
|             props.requests[i].link, |             props.requests[i].link, | ||||||
|             props.requests[i].params, |             props.requests[i].params, | ||||||
|           ) |           ) | ||||||
|           .then((response: ApiGenericDataType): void => |           .then((response: T): void => this.onRequestFinished(response, i)) | ||||||
|             this.onRequestFinished(response, i), |  | ||||||
|           ) |  | ||||||
|           .catch((error: number): void => |           .catch((error: number): void => | ||||||
|             this.onRequestFinished(null, i, error), |             this.onRequestFinished(null, i, error), | ||||||
|           ); |           ); | ||||||
|  | @ -200,7 +198,9 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> { | ||||||
|   allRequestsFinished(): boolean { |   allRequestsFinished(): boolean { | ||||||
|     let finished = true; |     let finished = true; | ||||||
|     this.errors.forEach((error: number | null) => { |     this.errors.forEach((error: number | null) => { | ||||||
|       if (error == null) finished = false; |       if (error == null) { | ||||||
|  |         finished = false; | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|     return finished; |     return finished; | ||||||
|   } |   } | ||||||
|  | @ -212,11 +212,14 @@ class AuthenticatedScreen extends React.Component<PropsType, StateType> { | ||||||
|     this.fetchData(); |     this.fetchData(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render(): React.Node { |   render() { | ||||||
|     const {state, props} = this; |     const {state, props} = this; | ||||||
|     if (state.loading) return <BasicLoadingScreen />; |     if (state.loading) { | ||||||
|     if (this.getError() === ERROR_TYPE.SUCCESS) |       return <BasicLoadingScreen />; | ||||||
|  |     } | ||||||
|  |     if (this.getError() === ERROR_TYPE.SUCCESS) { | ||||||
|       return props.renderFunction(this.fetchedData); |       return props.renderFunction(this.fetchedData); | ||||||
|  |     } | ||||||
|     return this.getErrorRender(); |     return this.getErrorRender(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -17,28 +17,25 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import i18n from 'i18n-js'; | import i18n from 'i18n-js'; | ||||||
| import {StackNavigationProp} from '@react-navigation/stack'; |  | ||||||
| import LoadingConfirmDialog from '../Dialogs/LoadingConfirmDialog'; | import LoadingConfirmDialog from '../Dialogs/LoadingConfirmDialog'; | ||||||
| import ConnectionManager from '../../managers/ConnectionManager'; | import ConnectionManager from '../../managers/ConnectionManager'; | ||||||
|  | import {useNavigation} from '@react-navigation/native'; | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType = { | ||||||
|   navigation: StackNavigationProp, |   visible: boolean; | ||||||
|   visible: boolean, |   onDismiss: () => void; | ||||||
|   onDismiss: () => void, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class LogoutDialog extends React.PureComponent<PropsType> { | function LogoutDialog(props: PropsType) { | ||||||
|   onClickAccept = async (): Promise<void> => { |   const navigation = useNavigation(); | ||||||
|     const {props} = this; |   const onClickAccept = async (): Promise<void> => { | ||||||
|     return new Promise((resolve: () => void) => { |     return new Promise((resolve: () => void) => { | ||||||
|       ConnectionManager.getInstance() |       ConnectionManager.getInstance() | ||||||
|         .disconnect() |         .disconnect() | ||||||
|         .then(() => { |         .then(() => { | ||||||
|           props.navigation.reset({ |           navigation.reset({ | ||||||
|             index: 0, |             index: 0, | ||||||
|             routes: [{name: 'main'}], |             routes: [{name: 'main'}], | ||||||
|           }); |           }); | ||||||
|  | @ -48,19 +45,16 @@ class LogoutDialog extends React.PureComponent<PropsType> { | ||||||
|     }); |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|   return ( |   return ( | ||||||
|     <LoadingConfirmDialog |     <LoadingConfirmDialog | ||||||
|       visible={props.visible} |       visible={props.visible} | ||||||
|       onDismiss={props.onDismiss} |       onDismiss={props.onDismiss} | ||||||
|         onAccept={this.onClickAccept} |       onAccept={onClickAccept} | ||||||
|       title={i18n.t('dialog.disconnect.title')} |       title={i18n.t('dialog.disconnect.title')} | ||||||
|       titleLoading={i18n.t('dialog.disconnect.titleLoading')} |       titleLoading={i18n.t('dialog.disconnect.titleLoading')} | ||||||
|       message={i18n.t('dialog.disconnect.message')} |       message={i18n.t('dialog.disconnect.message')} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export default LogoutDialog; | export default LogoutDialog; | ||||||
|  | @ -17,25 +17,13 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 | import React from 'react'; | ||||||
| 
 |  | ||||||
| import * as React from 'react'; |  | ||||||
| import {View} from 'react-native'; | import {View} from 'react-native'; | ||||||
| import {Headline, withTheme} from 'react-native-paper'; | import {Headline, useTheme} from 'react-native-paper'; | ||||||
| import i18n from 'i18n-js'; | import i18n from 'i18n-js'; | ||||||
| import type {CustomThemeType} from '../../../managers/ThemeManager'; |  | ||||||
| 
 | 
 | ||||||
| type PropsType = { | function VoteNotAvailable() { | ||||||
|   theme: CustomThemeType, |   const theme = useTheme(); | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class VoteNotAvailable extends React.Component<PropsType> { |  | ||||||
|   shouldComponentUpdate(): boolean { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|   return ( |   return ( | ||||||
|     <View |     <View | ||||||
|       style={{ |       style={{ | ||||||
|  | @ -45,7 +33,7 @@ class VoteNotAvailable extends React.Component<PropsType> { | ||||||
|       }}> |       }}> | ||||||
|       <Headline |       <Headline | ||||||
|         style={{ |         style={{ | ||||||
|             color: props.theme.colors.textDisabled, |           color: theme.colors.textDisabled, | ||||||
|           textAlign: 'center', |           textAlign: 'center', | ||||||
|         }}> |         }}> | ||||||
|         {i18n.t('screens.vote.noVote')} |         {i18n.t('screens.vote.noVote')} | ||||||
|  | @ -53,6 +41,5 @@ class VoteNotAvailable extends React.Component<PropsType> { | ||||||
|     </View> |     </View> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export default withTheme(VoteNotAvailable); | export default VoteNotAvailable; | ||||||
|  | @ -17,8 +17,6 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import { | import { | ||||||
|   Avatar, |   Avatar, | ||||||
|  | @ -31,16 +29,11 @@ import { | ||||||
| import {FlatList, StyleSheet} from 'react-native'; | import {FlatList, StyleSheet} from 'react-native'; | ||||||
| import i18n from 'i18n-js'; | import i18n from 'i18n-js'; | ||||||
| import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen'; | import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen'; | ||||||
| import type {CustomThemeType} from '../../../managers/ThemeManager'; |  | ||||||
| import type { |  | ||||||
|   CardTitleIconPropsType, |  | ||||||
|   ListIconPropsType, |  | ||||||
| } from '../../../constants/PaperStyles'; |  | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType = { | ||||||
|   teams: Array<VoteTeamType>, |   teams: Array<VoteTeamType>; | ||||||
|   dateEnd: string, |   dateEnd: string; | ||||||
|   theme: CustomThemeType, |   theme: ReactNativePaper.Theme; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||||||
|  | @ -58,10 +51,10 @@ class VoteResults extends React.Component<PropsType> { | ||||||
|   winnerIds: Array<number>; |   winnerIds: Array<number>; | ||||||
| 
 | 
 | ||||||
|   constructor(props: PropsType) { |   constructor(props: PropsType) { | ||||||
|     super(); |     super(props); | ||||||
|     props.teams.sort(this.sortByVotes); |     props.teams.sort(this.sortByVotes); | ||||||
|     this.getTotalVotes(props.teams); |     this.totalVotes = this.getTotalVotes(props.teams); | ||||||
|     this.getWinnerIds(props.teams); |     this.winnerIds = this.getWinnerIds(props.teams); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   shouldComponentUpdate(): boolean { |   shouldComponentUpdate(): boolean { | ||||||
|  | @ -69,26 +62,31 @@ class VoteResults extends React.Component<PropsType> { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getTotalVotes(teams: Array<VoteTeamType>) { |   getTotalVotes(teams: Array<VoteTeamType>) { | ||||||
|     this.totalVotes = 0; |     let totalVotes = 0; | ||||||
|     for (let i = 0; i < teams.length; i += 1) { |     for (let i = 0; i < teams.length; i += 1) { | ||||||
|       this.totalVotes += teams[i].votes; |       totalVotes += teams[i].votes; | ||||||
|     } |     } | ||||||
|  |     return totalVotes; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getWinnerIds(teams: Array<VoteTeamType>) { |   getWinnerIds(teams: Array<VoteTeamType>) { | ||||||
|     const max = teams[0].votes; |     const max = teams[0].votes; | ||||||
|     this.winnerIds = []; |     let winnerIds = []; | ||||||
|     for (let i = 0; i < teams.length; i += 1) { |     for (let i = 0; i < teams.length; i += 1) { | ||||||
|       if (teams[i].votes === max) this.winnerIds.push(teams[i].id); |       if (teams[i].votes === max) { | ||||||
|       else break; |         winnerIds.push(teams[i].id); | ||||||
|  |       } else { | ||||||
|  |         break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     return winnerIds; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   sortByVotes = (a: VoteTeamType, b: VoteTeamType): number => b.votes - a.votes; |   sortByVotes = (a: VoteTeamType, b: VoteTeamType): number => b.votes - a.votes; | ||||||
| 
 | 
 | ||||||
|   voteKeyExtractor = (item: VoteTeamType): string => item.id.toString(); |   voteKeyExtractor = (item: VoteTeamType): string => item.id.toString(); | ||||||
| 
 | 
 | ||||||
|   resultRenderItem = ({item}: {item: VoteTeamType}): React.Node => { |   resultRenderItem = ({item}: {item: VoteTeamType}) => { | ||||||
|     const isWinner = this.winnerIds.indexOf(item.id) !== -1; |     const isWinner = this.winnerIds.indexOf(item.id) !== -1; | ||||||
|     const isDraw = this.winnerIds.length > 1; |     const isDraw = this.winnerIds.length > 1; | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|  | @ -101,7 +99,7 @@ class VoteResults extends React.Component<PropsType> { | ||||||
|         <List.Item |         <List.Item | ||||||
|           title={item.name} |           title={item.name} | ||||||
|           description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`} |           description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`} | ||||||
|           left={(iconProps: ListIconPropsType): React.Node => |           left={(iconProps) => | ||||||
|             isWinner ? ( |             isWinner ? ( | ||||||
|               <List.Icon |               <List.Icon | ||||||
|                 style={iconProps.style} |                 style={iconProps.style} | ||||||
|  | @ -125,7 +123,7 @@ class VoteResults extends React.Component<PropsType> { | ||||||
|     ); |     ); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render(): React.Node { |   render() { | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|     return ( |     return ( | ||||||
|       <Card style={styles.card}> |       <Card style={styles.card}> | ||||||
|  | @ -134,15 +132,14 @@ class VoteResults extends React.Component<PropsType> { | ||||||
|           subtitle={`${i18n.t('screens.vote.results.subtitle')} ${ |           subtitle={`${i18n.t('screens.vote.results.subtitle')} ${ | ||||||
|             props.dateEnd |             props.dateEnd | ||||||
|           }`}
 |           }`}
 | ||||||
|           left={(iconProps: CardTitleIconPropsType): React.Node => ( |           left={(iconProps) => ( | ||||||
|             <Avatar.Icon size={iconProps.size} icon="podium-gold" /> |             <Avatar.Icon size={iconProps.size} icon="podium-gold" /> | ||||||
|           )} |           )} | ||||||
|         /> |         /> | ||||||
|         <Card.Content> |         <Card.Content> | ||||||
|           <Subheading>{`${i18n.t('screens.vote.results.totalVotes')} ${ |           <Subheading> | ||||||
|             this.totalVotes |             {`${i18n.t('screens.vote.results.totalVotes')} ${this.totalVotes}`} | ||||||
|           }`}</Subheading>
 |           </Subheading> | ||||||
|           {/* $FlowFixMe */} |  | ||||||
|           <FlatList |           <FlatList | ||||||
|             data={props.teams} |             data={props.teams} | ||||||
|             keyExtractor={this.voteKeyExtractor} |             keyExtractor={this.voteKeyExtractor} | ||||||
|  | @ -17,8 +17,6 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {Avatar, Button, Card, RadioButton} from 'react-native-paper'; | import {Avatar, Button, Card, RadioButton} from 'react-native-paper'; | ||||||
| import {FlatList, StyleSheet, View} from 'react-native'; | import {FlatList, StyleSheet, View} from 'react-native'; | ||||||
|  | @ -27,19 +25,18 @@ import ConnectionManager from '../../../managers/ConnectionManager'; | ||||||
| import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog'; | import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog'; | ||||||
| import ErrorDialog from '../../Dialogs/ErrorDialog'; | import ErrorDialog from '../../Dialogs/ErrorDialog'; | ||||||
| import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen'; | import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen'; | ||||||
| import type {CardTitleIconPropsType} from '../../../constants/PaperStyles'; |  | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType = { | ||||||
|   teams: Array<VoteTeamType>, |   teams: Array<VoteTeamType>; | ||||||
|   onVoteSuccess: () => void, |   onVoteSuccess: () => void; | ||||||
|   onVoteError: () => void, |   onVoteError: () => void; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type StateType = { | type StateType = { | ||||||
|   selectedTeam: string, |   selectedTeam: string; | ||||||
|   voteDialogVisible: boolean, |   voteDialogVisible: boolean; | ||||||
|   errorDialogVisible: boolean, |   errorDialogVisible: boolean; | ||||||
|   currentError: number, |   currentError: number; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||||||
|  | @ -53,10 +50,10 @@ const styles = StyleSheet.create({ | ||||||
| 
 | 
 | ||||||
| export default class VoteSelect extends React.PureComponent< | export default class VoteSelect extends React.PureComponent< | ||||||
|   PropsType, |   PropsType, | ||||||
|   StateType, |   StateType | ||||||
| > { | > { | ||||||
|   constructor() { |   constructor(props: PropsType) { | ||||||
|     super(); |     super(props); | ||||||
|     this.state = { |     this.state = { | ||||||
|       selectedTeam: 'none', |       selectedTeam: 'none', | ||||||
|       voteDialogVisible: false, |       voteDialogVisible: false, | ||||||
|  | @ -70,7 +67,7 @@ export default class VoteSelect extends React.PureComponent< | ||||||
| 
 | 
 | ||||||
|   voteKeyExtractor = (item: VoteTeamType): string => item.id.toString(); |   voteKeyExtractor = (item: VoteTeamType): string => item.id.toString(); | ||||||
| 
 | 
 | ||||||
|   voteRenderItem = ({item}: {item: VoteTeamType}): React.Node => ( |   voteRenderItem = ({item}: {item: VoteTeamType}) => ( | ||||||
|     <RadioButton.Item label={item.name} value={item.id.toString()} /> |     <RadioButton.Item label={item.name} value={item.id.toString()} /> | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  | @ -111,7 +108,7 @@ export default class VoteSelect extends React.PureComponent< | ||||||
|     props.onVoteError(); |     props.onVoteError(); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render(): React.Node { |   render() { | ||||||
|     const {state, props} = this; |     const {state, props} = this; | ||||||
|     return ( |     return ( | ||||||
|       <View> |       <View> | ||||||
|  | @ -119,7 +116,7 @@ export default class VoteSelect extends React.PureComponent< | ||||||
|           <Card.Title |           <Card.Title | ||||||
|             title={i18n.t('screens.vote.select.title')} |             title={i18n.t('screens.vote.select.title')} | ||||||
|             subtitle={i18n.t('screens.vote.select.subtitle')} |             subtitle={i18n.t('screens.vote.select.subtitle')} | ||||||
|             left={(iconProps: CardTitleIconPropsType): React.Node => ( |             left={(iconProps) => ( | ||||||
|               <Avatar.Icon size={iconProps.size} icon="alert-decagram" /> |               <Avatar.Icon size={iconProps.size} icon="alert-decagram" /> | ||||||
|             )} |             )} | ||||||
|           /> |           /> | ||||||
|  | @ -127,7 +124,6 @@ export default class VoteSelect extends React.PureComponent< | ||||||
|             <RadioButton.Group |             <RadioButton.Group | ||||||
|               onValueChange={this.onVoteSelectionChange} |               onValueChange={this.onVoteSelectionChange} | ||||||
|               value={state.selectedTeam}> |               value={state.selectedTeam}> | ||||||
|               {/* $FlowFixMe */} |  | ||||||
|               <FlatList |               <FlatList | ||||||
|                 data={props.teams} |                 data={props.teams} | ||||||
|                 keyExtractor={this.voteKeyExtractor} |                 keyExtractor={this.voteKeyExtractor} | ||||||
|  | @ -17,16 +17,13 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {Avatar, Card, Paragraph} from 'react-native-paper'; | import {Avatar, Card, Paragraph} from 'react-native-paper'; | ||||||
| import {StyleSheet} from 'react-native'; | import {StyleSheet} from 'react-native'; | ||||||
| import i18n from 'i18n-js'; | import i18n from 'i18n-js'; | ||||||
| import type {CardTitleIconPropsType} from '../../../constants/PaperStyles'; |  | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType = { | ||||||
|   startDate: string, |   startDate: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||||||
|  | @ -38,21 +35,13 @@ const styles = StyleSheet.create({ | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default class VoteTease extends React.Component<PropsType> { | export default function VoteTease(props: PropsType) { | ||||||
|   shouldComponentUpdate(): boolean { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|   return ( |   return ( | ||||||
|     <Card style={styles.card}> |     <Card style={styles.card}> | ||||||
|       <Card.Title |       <Card.Title | ||||||
|         title={i18n.t('screens.vote.tease.title')} |         title={i18n.t('screens.vote.tease.title')} | ||||||
|         subtitle={i18n.t('screens.vote.tease.subtitle')} |         subtitle={i18n.t('screens.vote.tease.subtitle')} | ||||||
|           left={(iconProps: CardTitleIconPropsType): React.Node => ( |         left={(iconProps) => <Avatar.Icon size={iconProps.size} icon="vote" />} | ||||||
|             <Avatar.Icon size={iconProps.size} icon="vote" /> |  | ||||||
|           )} |  | ||||||
|       /> |       /> | ||||||
|       <Card.Content> |       <Card.Content> | ||||||
|         <Paragraph> |         <Paragraph> | ||||||
|  | @ -62,4 +51,3 @@ export default class VoteTease extends React.Component<PropsType> { | ||||||
|     </Card> |     </Card> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| } |  | ||||||
|  | @ -1,93 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright (c) 2019 - 2020 Arnaud Vergnet. |  | ||||||
|  * |  | ||||||
|  * This file is part of Campus INSAT. |  | ||||||
|  * |  | ||||||
|  * Campus INSAT is free software: you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  * the Free Software Foundation, either version 3 of the License, or |  | ||||||
|  * (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * Campus INSAT is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; |  | ||||||
| import {Avatar, Card, Paragraph, withTheme} from 'react-native-paper'; |  | ||||||
| import {StyleSheet} from 'react-native'; |  | ||||||
| import i18n from 'i18n-js'; |  | ||||||
| import type {CustomThemeType} from '../../../managers/ThemeManager'; |  | ||||||
| import type {CardTitleIconPropsType} from '../../../constants/PaperStyles'; |  | ||||||
| 
 |  | ||||||
| type PropsType = { |  | ||||||
|   startDate: string | null, |  | ||||||
|   justVoted: boolean, |  | ||||||
|   hasVoted: boolean, |  | ||||||
|   isVoteRunning: boolean, |  | ||||||
|   theme: CustomThemeType, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const styles = StyleSheet.create({ |  | ||||||
|   card: { |  | ||||||
|     margin: 10, |  | ||||||
|   }, |  | ||||||
|   icon: { |  | ||||||
|     backgroundColor: 'transparent', |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| class VoteWait extends React.Component<PropsType> { |  | ||||||
|   shouldComponentUpdate(): boolean { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|     const {startDate} = props; |  | ||||||
|     return ( |  | ||||||
|       <Card style={styles.card}> |  | ||||||
|         <Card.Title |  | ||||||
|           title={ |  | ||||||
|             props.isVoteRunning |  | ||||||
|               ? i18n.t('screens.vote.wait.titleSubmitted') |  | ||||||
|               : i18n.t('screens.vote.wait.titleEnded') |  | ||||||
|           } |  | ||||||
|           subtitle={i18n.t('screens.vote.wait.subtitle')} |  | ||||||
|           left={(iconProps: CardTitleIconPropsType): React.Node => ( |  | ||||||
|             <Avatar.Icon size={iconProps.size} icon="progress-check" /> |  | ||||||
|           )} |  | ||||||
|         /> |  | ||||||
|         <Card.Content> |  | ||||||
|           {props.justVoted ? ( |  | ||||||
|             <Paragraph style={{color: props.theme.colors.success}}> |  | ||||||
|               {i18n.t('screens.vote.wait.messageSubmitted')} |  | ||||||
|             </Paragraph> |  | ||||||
|           ) : null} |  | ||||||
|           {props.hasVoted ? ( |  | ||||||
|             <Paragraph style={{color: props.theme.colors.success}}> |  | ||||||
|               {i18n.t('screens.vote.wait.messageVoted')} |  | ||||||
|             </Paragraph> |  | ||||||
|           ) : null} |  | ||||||
|           {startDate != null ? ( |  | ||||||
|             <Paragraph> |  | ||||||
|               {`${i18n.t('screens.vote.wait.messageDate')} ${startDate}`} |  | ||||||
|             </Paragraph> |  | ||||||
|           ) : ( |  | ||||||
|             <Paragraph> |  | ||||||
|               {i18n.t('screens.vote.wait.messageDateUndefined')} |  | ||||||
|             </Paragraph> |  | ||||||
|           )} |  | ||||||
|         </Card.Content> |  | ||||||
|       </Card> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default withTheme(VoteWait); |  | ||||||
							
								
								
									
										80
									
								
								src/components/Amicale/Vote/VoteWait.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/components/Amicale/Vote/VoteWait.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2019 - 2020 Arnaud Vergnet. | ||||||
|  |  * | ||||||
|  |  * This file is part of Campus INSAT. | ||||||
|  |  * | ||||||
|  |  * Campus INSAT is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * Campus INSAT is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | import * as React from 'react'; | ||||||
|  | import {Avatar, Card, Paragraph, useTheme} from 'react-native-paper'; | ||||||
|  | import {StyleSheet} from 'react-native'; | ||||||
|  | import i18n from 'i18n-js'; | ||||||
|  | 
 | ||||||
|  | type PropsType = { | ||||||
|  |   startDate: string | null; | ||||||
|  |   justVoted: boolean; | ||||||
|  |   hasVoted: boolean; | ||||||
|  |   isVoteRunning: boolean; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const styles = StyleSheet.create({ | ||||||
|  |   card: { | ||||||
|  |     margin: 10, | ||||||
|  |   }, | ||||||
|  |   icon: { | ||||||
|  |     backgroundColor: 'transparent', | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export default function VoteWait(props: PropsType) { | ||||||
|  |   const theme = useTheme(); | ||||||
|  |   const {startDate} = props; | ||||||
|  |   return ( | ||||||
|  |     <Card style={styles.card}> | ||||||
|  |       <Card.Title | ||||||
|  |         title={ | ||||||
|  |           props.isVoteRunning | ||||||
|  |             ? i18n.t('screens.vote.wait.titleSubmitted') | ||||||
|  |             : i18n.t('screens.vote.wait.titleEnded') | ||||||
|  |         } | ||||||
|  |         subtitle={i18n.t('screens.vote.wait.subtitle')} | ||||||
|  |         left={(iconProps) => ( | ||||||
|  |           <Avatar.Icon size={iconProps.size} icon="progress-check" /> | ||||||
|  |         )} | ||||||
|  |       /> | ||||||
|  |       <Card.Content> | ||||||
|  |         {props.justVoted ? ( | ||||||
|  |           <Paragraph style={{color: theme.colors.success}}> | ||||||
|  |             {i18n.t('screens.vote.wait.messageSubmitted')} | ||||||
|  |           </Paragraph> | ||||||
|  |         ) : null} | ||||||
|  |         {props.hasVoted ? ( | ||||||
|  |           <Paragraph style={{color: theme.colors.success}}> | ||||||
|  |             {i18n.t('screens.vote.wait.messageVoted')} | ||||||
|  |           </Paragraph> | ||||||
|  |         ) : null} | ||||||
|  |         {startDate != null ? ( | ||||||
|  |           <Paragraph> | ||||||
|  |             {`${i18n.t('screens.vote.wait.messageDate')} ${startDate}`} | ||||||
|  |           </Paragraph> | ||||||
|  |         ) : ( | ||||||
|  |           <Paragraph> | ||||||
|  |             {i18n.t('screens.vote.wait.messageDateUndefined')} | ||||||
|  |           </Paragraph> | ||||||
|  |         )} | ||||||
|  |       </Card.Content> | ||||||
|  |     </Card> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | @ -1,78 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright (c) 2019 - 2020 Arnaud Vergnet. |  | ||||||
|  * |  | ||||||
|  * This file is part of Campus INSAT. |  | ||||||
|  * |  | ||||||
|  * Campus INSAT is free software: you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  * the Free Software Foundation, either version 3 of the License, or |  | ||||||
|  * (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * Campus INSAT is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; |  | ||||||
| import {Collapsible} from 'react-navigation-collapsible'; |  | ||||||
| import withCollapsible from '../../utils/withCollapsible'; |  | ||||||
| import CustomTabBar from '../Tabbar/CustomTabBar'; |  | ||||||
| 
 |  | ||||||
| export type CollapsibleComponentPropsType = { |  | ||||||
|   children?: React.Node, |  | ||||||
|   hasTab?: boolean, |  | ||||||
|   onScroll?: (event: SyntheticEvent<EventTarget>) => void, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| type PropsType = { |  | ||||||
|   ...CollapsibleComponentPropsType, |  | ||||||
|   collapsibleStack: Collapsible, |  | ||||||
|   // eslint-disable-next-line flowtype/no-weak-types
 |  | ||||||
|   component: any, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class CollapsibleComponent extends React.Component<PropsType> { |  | ||||||
|   static defaultProps = { |  | ||||||
|     children: null, |  | ||||||
|     hasTab: false, |  | ||||||
|     onScroll: null, |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   onScroll = (event: SyntheticEvent<EventTarget>) => { |  | ||||||
|     const {props} = this; |  | ||||||
|     if (props.onScroll) props.onScroll(event); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|     const Comp = props.component; |  | ||||||
|     const { |  | ||||||
|       containerPaddingTop, |  | ||||||
|       scrollIndicatorInsetTop, |  | ||||||
|       onScrollWithListener, |  | ||||||
|     } = props.collapsibleStack; |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|       <Comp |  | ||||||
|         // eslint-disable-next-line react/jsx-props-no-spreading
 |  | ||||||
|         {...props} |  | ||||||
|         onScroll={onScrollWithListener(this.onScroll)} |  | ||||||
|         contentContainerStyle={{ |  | ||||||
|           paddingTop: containerPaddingTop, |  | ||||||
|           paddingBottom: props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0, |  | ||||||
|           minHeight: '100%', |  | ||||||
|         }} |  | ||||||
|         scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}> |  | ||||||
|         {props.children} |  | ||||||
|       </Comp> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default withCollapsible(CollapsibleComponent); |  | ||||||
							
								
								
									
										63
									
								
								src/components/Collapsible/CollapsibleComponent.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/components/Collapsible/CollapsibleComponent.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2019 - 2020 Arnaud Vergnet. | ||||||
|  |  * | ||||||
|  |  * This file is part of Campus INSAT. | ||||||
|  |  * | ||||||
|  |  * Campus INSAT is free software: you can redistribute it and/or modify | ||||||
|  |  *  it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * Campus INSAT is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | import * as React from 'react'; | ||||||
|  | import {useCollapsibleStack} from 'react-navigation-collapsible'; | ||||||
|  | import CustomTabBar from '../Tabbar/CustomTabBar'; | ||||||
|  | import {NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; | ||||||
|  | 
 | ||||||
|  | export interface CollapsibleComponentPropsType { | ||||||
|  |   children?: React.ReactNode; | ||||||
|  |   hasTab?: boolean; | ||||||
|  |   onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface PropsType extends CollapsibleComponentPropsType { | ||||||
|  |   component: React.ComponentType<any>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function CollapsibleComponent(props: PropsType) { | ||||||
|  |   const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => { | ||||||
|  |     if (props.onScroll) { | ||||||
|  |       props.onScroll(event); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   const Comp = props.component; | ||||||
|  |   const { | ||||||
|  |     containerPaddingTop, | ||||||
|  |     scrollIndicatorInsetTop, | ||||||
|  |     onScrollWithListener, | ||||||
|  |   } = useCollapsibleStack(); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Comp | ||||||
|  |       {...props} | ||||||
|  |       onScroll={onScrollWithListener(onScroll)} | ||||||
|  |       contentContainerStyle={{ | ||||||
|  |         paddingTop: containerPaddingTop, | ||||||
|  |         paddingBottom: props.hasTab ? CustomTabBar.TAB_BAR_HEIGHT : 0, | ||||||
|  |         minHeight: '100%', | ||||||
|  |       }} | ||||||
|  |       scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}> | ||||||
|  |       {props.children} | ||||||
|  |     </Comp> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default CollapsibleComponent; | ||||||
|  | @ -17,29 +17,19 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {Animated} from 'react-native'; | import {Animated, FlatListProps} from 'react-native'; | ||||||
| import type {CollapsibleComponentPropsType} from './CollapsibleComponent'; | import type {CollapsibleComponentPropsType} from './CollapsibleComponent'; | ||||||
| import CollapsibleComponent from './CollapsibleComponent'; | import CollapsibleComponent from './CollapsibleComponent'; | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type Props<T> = FlatListProps<T> & CollapsibleComponentPropsType; | ||||||
|   ...CollapsibleComponentPropsType, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| // eslint-disable-next-line react/prefer-stateless-function
 | function CollapsibleFlatList<T>(props: Props<T>) { | ||||||
| class CollapsibleFlatList extends React.Component<PropsType> { |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|   return ( |   return ( | ||||||
|       <CollapsibleComponent // eslint-disable-next-line react/jsx-props-no-spreading
 |     <CollapsibleComponent {...props} component={Animated.FlatList}> | ||||||
|         {...props} |  | ||||||
|         component={Animated.FlatList}> |  | ||||||
|       {props.children} |       {props.children} | ||||||
|     </CollapsibleComponent> |     </CollapsibleComponent> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export default CollapsibleFlatList; | export default CollapsibleFlatList; | ||||||
|  | @ -17,29 +17,19 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {Animated} from 'react-native'; | import {Animated, ScrollViewProps} from 'react-native'; | ||||||
| import type {CollapsibleComponentPropsType} from './CollapsibleComponent'; | import type {CollapsibleComponentPropsType} from './CollapsibleComponent'; | ||||||
| import CollapsibleComponent from './CollapsibleComponent'; | import CollapsibleComponent from './CollapsibleComponent'; | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type Props = ScrollViewProps & CollapsibleComponentPropsType; | ||||||
|   ...CollapsibleComponentPropsType, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| // eslint-disable-next-line react/prefer-stateless-function
 | function CollapsibleScrollView(props: Props) { | ||||||
| class CollapsibleScrollView extends React.Component<PropsType> { |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|   return ( |   return ( | ||||||
|       <CollapsibleComponent // eslint-disable-next-line react/jsx-props-no-spreading
 |     <CollapsibleComponent {...props} component={Animated.ScrollView}> | ||||||
|         {...props} |  | ||||||
|         component={Animated.ScrollView}> |  | ||||||
|       {props.children} |       {props.children} | ||||||
|     </CollapsibleComponent> |     </CollapsibleComponent> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export default CollapsibleScrollView; | export default CollapsibleScrollView; | ||||||
|  | @ -17,29 +17,19 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {Animated} from 'react-native'; | import {Animated, SectionListProps} from 'react-native'; | ||||||
| import type {CollapsibleComponentPropsType} from './CollapsibleComponent'; | import type {CollapsibleComponentPropsType} from './CollapsibleComponent'; | ||||||
| import CollapsibleComponent from './CollapsibleComponent'; | import CollapsibleComponent from './CollapsibleComponent'; | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type Props<T> = SectionListProps<T> & CollapsibleComponentPropsType; | ||||||
|   ...CollapsibleComponentPropsType, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| // eslint-disable-next-line react/prefer-stateless-function
 | function CollapsibleSectionList<T>(props: Props<T>) { | ||||||
| class CollapsibleSectionList extends React.Component<PropsType> { |  | ||||||
|   render(): React.Node { |  | ||||||
|     const {props} = this; |  | ||||||
|   return ( |   return ( | ||||||
|       <CollapsibleComponent // eslint-disable-next-line react/jsx-props-no-spreading
 |     <CollapsibleComponent {...props} component={Animated.SectionList}> | ||||||
|         {...props} |  | ||||||
|         component={Animated.SectionList}> |  | ||||||
|       {props.children} |       {props.children} | ||||||
|     </CollapsibleComponent> |     </CollapsibleComponent> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export default CollapsibleSectionList; | export default CollapsibleSectionList; | ||||||
|  | @ -21,8 +21,11 @@ | ||||||
| 
 | 
 | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {View} from 'react-native'; | import {View} from 'react-native'; | ||||||
| import {ActivityIndicator, withTheme} from 'react-native-paper'; | import {ActivityIndicator, useTheme} from 'react-native-paper'; | ||||||
| import type {CustomThemeType} from '../../managers/ThemeManager'; | 
 | ||||||
|  | type Props = { | ||||||
|  |   isAbsolute?: boolean; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Component used to display a header button |  * Component used to display a header button | ||||||
|  | @ -30,29 +33,21 @@ import type {CustomThemeType} from '../../managers/ThemeManager'; | ||||||
|  * @param props Props to pass to the component |  * @param props Props to pass to the component | ||||||
|  * @return {*} |  * @return {*} | ||||||
|  */ |  */ | ||||||
| function BasicLoadingScreen(props: { | export default function BasicLoadingScreen(props: Props) { | ||||||
|   theme: CustomThemeType, |   const theme = useTheme(); | ||||||
|   isAbsolute: boolean, |   const {isAbsolute} = props; | ||||||
| }): React.Node { |  | ||||||
|   const {theme, isAbsolute} = props; |  | ||||||
|   const {colors} = theme; |  | ||||||
|   let position; |  | ||||||
|   if (isAbsolute != null && isAbsolute) position = 'absolute'; |  | ||||||
| 
 |  | ||||||
|   return ( |   return ( | ||||||
|     <View |     <View | ||||||
|       style={{ |       style={{ | ||||||
|         backgroundColor: colors.background, |         backgroundColor: theme.colors.background, | ||||||
|         position, |         position: isAbsolute ? 'absolute' : 'relative', | ||||||
|         top: 0, |         top: 0, | ||||||
|         right: 0, |         right: 0, | ||||||
|         width: '100%', |         width: '100%', | ||||||
|         height: '100%', |         height: '100%', | ||||||
|         justifyContent: 'center', |         justifyContent: 'center', | ||||||
|       }}> |       }}> | ||||||
|       <ActivityIndicator animating size="large" color={colors.primary} /> |       <ActivityIndicator animating size="large" color={theme.colors.primary} /> | ||||||
|     </View> |     </View> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export default withTheme(BasicLoadingScreen); |  | ||||||
|  | @ -17,8 +17,6 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {Button, Subheading, withTheme} from 'react-native-paper'; | import {Button, Subheading, withTheme} from 'react-native-paper'; | ||||||
| import {StyleSheet, View} from 'react-native'; | import {StyleSheet, View} from 'react-native'; | ||||||
|  | @ -27,17 +25,16 @@ import i18n from 'i18n-js'; | ||||||
| import * as Animatable from 'react-native-animatable'; | import * as Animatable from 'react-native-animatable'; | ||||||
| import {StackNavigationProp} from '@react-navigation/stack'; | import {StackNavigationProp} from '@react-navigation/stack'; | ||||||
| import {ERROR_TYPE} from '../../utils/WebData'; | import {ERROR_TYPE} from '../../utils/WebData'; | ||||||
| import type {CustomThemeType} from '../../managers/ThemeManager'; |  | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType = { | ||||||
|   navigation: StackNavigationProp, |   navigation?: StackNavigationProp<any>; | ||||||
|   theme: CustomThemeType, |   theme: ReactNativePaper.Theme; | ||||||
|   route: {name: string}, |   route?: {name: string}; | ||||||
|   onRefresh?: () => void, |   onRefresh?: () => void; | ||||||
|   errorCode?: number, |   errorCode?: number; | ||||||
|   icon?: string, |   icon?: string; | ||||||
|   message?: string, |   message?: string; | ||||||
|   showRetryButton?: boolean, |   showRetryButton?: boolean; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||||||
|  | @ -82,9 +79,11 @@ class ErrorView extends React.PureComponent<PropsType> { | ||||||
|   constructor(props: PropsType) { |   constructor(props: PropsType) { | ||||||
|     super(props); |     super(props); | ||||||
|     this.icon = ''; |     this.icon = ''; | ||||||
|  |     this.showLoginButton = false; | ||||||
|  |     this.message = ''; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getRetryButton(): React.Node { |   getRetryButton() { | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|     return ( |     return ( | ||||||
|       <Button |       <Button | ||||||
|  | @ -97,7 +96,7 @@ class ErrorView extends React.PureComponent<PropsType> { | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getLoginButton(): React.Node { |   getLoginButton() { | ||||||
|     return ( |     return ( | ||||||
|       <Button |       <Button | ||||||
|         mode="contained" |         mode="contained" | ||||||
|  | @ -111,10 +110,12 @@ class ErrorView extends React.PureComponent<PropsType> { | ||||||
| 
 | 
 | ||||||
|   goToLogin = () => { |   goToLogin = () => { | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|  |     if (props.navigation) { | ||||||
|       props.navigation.navigate('login', { |       props.navigation.navigate('login', { | ||||||
|         screen: 'login', |         screen: 'login', | ||||||
|       params: {nextScreen: props.route.name}, |         params: {nextScreen: props.route ? props.route.name : undefined}, | ||||||
|       }); |       }); | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   generateMessage() { |   generateMessage() { | ||||||
|  | @ -169,13 +170,17 @@ class ErrorView extends React.PureComponent<PropsType> { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render(): React.Node { |   render() { | ||||||
|     const {props} = this; |     const {props} = this; | ||||||
|     this.generateMessage(); |     this.generateMessage(); | ||||||
|     let button; |     let button; | ||||||
|     if (this.showLoginButton) button = this.getLoginButton(); |     if (this.showLoginButton) { | ||||||
|     else if (props.showRetryButton) button = this.getRetryButton(); |       button = this.getLoginButton(); | ||||||
|     else button = null; |     } else if (props.showRetryButton) { | ||||||
|  |       button = this.getRetryButton(); | ||||||
|  |     } else { | ||||||
|  |       button = null; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Animatable.View |       <Animatable.View | ||||||
|  | @ -17,8 +17,6 @@ | ||||||
|  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 |  * along with Campus INSAT.  If not, see <https://www.gnu.org/licenses/>.
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| // @flow
 |  | ||||||
| 
 |  | ||||||
| import * as React from 'react'; | import * as React from 'react'; | ||||||
| import {RefreshControl, View} from 'react-native'; | import {RefreshControl, View} from 'react-native'; | ||||||
| import {StackNavigationProp} from '@react-navigation/stack'; | import {StackNavigationProp} from '@react-navigation/stack'; | ||||||
|  | @ -35,31 +33,30 @@ import MascotPopup from '../../components/Mascot/MascotPopup'; | ||||||
| import AsyncStorageManager from '../../managers/AsyncStorageManager'; | import AsyncStorageManager from '../../managers/AsyncStorageManager'; | ||||||
| import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable'; | import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable'; | ||||||
| import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList'; | import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList'; | ||||||
| import type {ApiGenericDataType} from '../../utils/WebData'; |  | ||||||
| 
 | 
 | ||||||
| export type VoteTeamType = { | export type VoteTeamType = { | ||||||
|   id: number, |   id: number; | ||||||
|   name: string, |   name: string; | ||||||
|   votes: number, |   votes: number; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type TeamResponseType = { | type TeamResponseType = { | ||||||
|   has_voted: boolean, |   has_voted: boolean; | ||||||
|   teams: Array<VoteTeamType>, |   teams: Array<VoteTeamType>; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type VoteDatesStringType = { | type VoteDatesStringType = { | ||||||
|   date_begin: string, |   date_begin: string; | ||||||
|   date_end: string, |   date_end: string; | ||||||
|   date_result_begin: string, |   date_result_begin: string; | ||||||
|   date_result_end: string, |   date_result_end: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type VoteDatesObjectType = { | type VoteDatesObjectType = { | ||||||
|   date_begin: Date, |   date_begin: Date; | ||||||
|   date_end: Date, |   date_end: Date; | ||||||
|   date_result_begin: Date, |   date_result_begin: Date; | ||||||
|   date_result_end: Date, |   date_result_end: Date; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // const FAKE_DATE = {
 | // const FAKE_DATE = {
 | ||||||
|  | @ -113,12 +110,12 @@ type VoteDatesObjectType = { | ||||||
| const MIN_REFRESH_TIME = 5 * 1000; | const MIN_REFRESH_TIME = 5 * 1000; | ||||||
| 
 | 
 | ||||||
| type PropsType = { | type PropsType = { | ||||||
|   navigation: StackNavigationProp, |   navigation: StackNavigationProp<any>; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type StateType = { | type StateType = { | ||||||
|   hasVoted: boolean, |   hasVoted: boolean; | ||||||
|   mascotDialogVisible: boolean, |   mascotDialogVisible: boolean; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -139,10 +136,13 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
| 
 | 
 | ||||||
|   lastRefresh: Date | null; |   lastRefresh: Date | null; | ||||||
| 
 | 
 | ||||||
|   authRef: {current: null | AuthenticatedScreen}; |   authRef: {current: null | AuthenticatedScreen<any>}; | ||||||
| 
 | 
 | ||||||
|   constructor() { |   constructor(props: PropsType) { | ||||||
|     super(); |     super(props); | ||||||
|  |     this.teams = []; | ||||||
|  |     this.datesString = null; | ||||||
|  |     this.dates = null; | ||||||
|     this.state = { |     this.state = { | ||||||
|       hasVoted: false, |       hasVoted: false, | ||||||
|       mascotDialogVisible: AsyncStorageManager.getBool( |       mascotDialogVisible: AsyncStorageManager.getBool( | ||||||
|  | @ -174,8 +174,8 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|     return dateString; |     return dateString; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getMainRenderItem = ({item}: {item: {key: string}}): React.Node => { |   getMainRenderItem = ({item}: {item: {key: string}}) => { | ||||||
|     if (item.key === 'info') |     if (item.key === 'info') { | ||||||
|       return ( |       return ( | ||||||
|         <View> |         <View> | ||||||
|           <Button |           <Button | ||||||
|  | @ -191,21 +191,24 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|           </Button> |           </Button> | ||||||
|         </View> |         </View> | ||||||
|       ); |       ); | ||||||
|  |     } | ||||||
|     return this.getContent(); |     return this.getContent(); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   getScreen = (data: Array<ApiGenericDataType | null>): React.Node => { |   getScreen = (data: Array<TeamResponseType | VoteDatesStringType | null>) => { | ||||||
|     const {state} = this; |     const {state} = this; | ||||||
|     // data[0] = FAKE_TEAMS2;
 |     // data[0] = FAKE_TEAMS2;
 | ||||||
|     // data[1] = FAKE_DATE;
 |     // data[1] = FAKE_DATE;
 | ||||||
|     this.lastRefresh = new Date(); |     this.lastRefresh = new Date(); | ||||||
| 
 | 
 | ||||||
|     const teams: TeamResponseType | null = data[0]; |     const teams = data[0] as TeamResponseType | null; | ||||||
|     const dateStrings: VoteDatesStringType | null = data[1]; |     const dateStrings = data[1] as VoteDatesStringType | null; | ||||||
| 
 | 
 | ||||||
|     if (dateStrings != null && dateStrings.date_begin == null) |     if (dateStrings != null && dateStrings.date_begin == null) { | ||||||
|       this.datesString = null; |       this.datesString = null; | ||||||
|     else this.datesString = dateStrings; |     } else { | ||||||
|  |       this.datesString = dateStrings; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (teams != null) { |     if (teams != null) { | ||||||
|       this.teams = teams.teams; |       this.teams = teams.teams; | ||||||
|  | @ -225,13 +228,20 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|     ); |     ); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   getContent(): React.Node { |   getContent() { | ||||||
|     const {state} = this; |     const {state} = this; | ||||||
|     if (!this.isVoteStarted()) return this.getTeaseVoteCard(); |     if (!this.isVoteStarted()) { | ||||||
|     if (this.isVoteRunning() && !this.hasVoted && !state.hasVoted) |       return this.getTeaseVoteCard(); | ||||||
|  |     } | ||||||
|  |     if (this.isVoteRunning() && !this.hasVoted && !state.hasVoted) { | ||||||
|       return this.getVoteCard(); |       return this.getVoteCard(); | ||||||
|     if (!this.isResultStarted()) return this.getWaitVoteCard(); |     } | ||||||
|     if (this.isResultRunning()) return this.getVoteResultCard(); |     if (!this.isResultStarted()) { | ||||||
|  |       return this.getWaitVoteCard(); | ||||||
|  |     } | ||||||
|  |     if (this.isResultRunning()) { | ||||||
|  |       return this.getVoteResultCard(); | ||||||
|  |     } | ||||||
|     return <VoteNotAvailable />; |     return <VoteNotAvailable />; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -240,7 +250,7 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|   /** |   /** | ||||||
|    * The user has not voted yet, and the votes are open |    * The user has not voted yet, and the votes are open | ||||||
|    */ |    */ | ||||||
|   getVoteCard(): React.Node { |   getVoteCard() { | ||||||
|     return ( |     return ( | ||||||
|       <VoteSelect |       <VoteSelect | ||||||
|         teams={this.teams} |         teams={this.teams} | ||||||
|  | @ -253,8 +263,8 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|   /** |   /** | ||||||
|    * Votes have ended, results can be displayed |    * Votes have ended, results can be displayed | ||||||
|    */ |    */ | ||||||
|   getVoteResultCard(): React.Node { |   getVoteResultCard() { | ||||||
|     if (this.dates != null && this.datesString != null) |     if (this.dates != null && this.datesString != null) { | ||||||
|       return ( |       return ( | ||||||
|         <VoteResults |         <VoteResults | ||||||
|           teams={this.teams} |           teams={this.teams} | ||||||
|  | @ -264,14 +274,15 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|           )} |           )} | ||||||
|         /> |         /> | ||||||
|       ); |       ); | ||||||
|  |     } | ||||||
|     return <VoteNotAvailable />; |     return <VoteNotAvailable />; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Vote will open shortly |    * Vote will open shortly | ||||||
|    */ |    */ | ||||||
|   getTeaseVoteCard(): React.Node { |   getTeaseVoteCard() { | ||||||
|     if (this.dates != null && this.datesString != null) |     if (this.dates != null && this.datesString != null) { | ||||||
|       return ( |       return ( | ||||||
|         <VoteTease |         <VoteTease | ||||||
|           startDate={this.getDateString( |           startDate={this.getDateString( | ||||||
|  | @ -280,24 +291,26 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|           )} |           )} | ||||||
|         /> |         /> | ||||||
|       ); |       ); | ||||||
|  |     } | ||||||
|     return <VoteNotAvailable />; |     return <VoteNotAvailable />; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * Votes have ended, or user has voted waiting for results |    * Votes have ended, or user has voted waiting for results | ||||||
|    */ |    */ | ||||||
|   getWaitVoteCard(): React.Node { |   getWaitVoteCard() { | ||||||
|     const {state} = this; |     const {state} = this; | ||||||
|     let startDate = null; |     let startDate = null; | ||||||
|     if ( |     if ( | ||||||
|       this.dates != null && |       this.dates != null && | ||||||
|       this.datesString != null && |       this.datesString != null && | ||||||
|       this.dates.date_result_begin != null |       this.dates.date_result_begin != null | ||||||
|     ) |     ) { | ||||||
|       startDate = this.getDateString( |       startDate = this.getDateString( | ||||||
|         this.dates.date_result_begin, |         this.dates.date_result_begin, | ||||||
|         this.datesString.date_result_begin, |         this.datesString.date_result_begin, | ||||||
|       ); |       ); | ||||||
|  |     } | ||||||
|     return ( |     return ( | ||||||
|       <VoteWait |       <VoteWait | ||||||
|         startDate={startDate} |         startDate={startDate} | ||||||
|  | @ -314,12 +327,15 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|   reloadData = () => { |   reloadData = () => { | ||||||
|     let canRefresh; |     let canRefresh; | ||||||
|     const {lastRefresh} = this; |     const {lastRefresh} = this; | ||||||
|     if (lastRefresh != null) |     if (lastRefresh != null) { | ||||||
|       canRefresh = |       canRefresh = | ||||||
|         new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME; |         new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME; | ||||||
|     else canRefresh = true; |     } else { | ||||||
|     if (canRefresh && this.authRef.current != null) |       canRefresh = true; | ||||||
|  |     } | ||||||
|  |     if (canRefresh && this.authRef.current != null) { | ||||||
|       this.authRef.current.reload(); |       this.authRef.current.reload(); | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   showMascotDialog = () => { |   showMascotDialog = () => { | ||||||
|  | @ -380,8 +396,12 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|           date_result_begin: dateResultBegin, |           date_result_begin: dateResultBegin, | ||||||
|           date_result_end: dateResultEnd, |           date_result_end: dateResultEnd, | ||||||
|         }; |         }; | ||||||
|       } else this.dates = null; |       } else { | ||||||
|     } else this.dates = null; |         this.dates = null; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       this.dates = null; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|  | @ -391,11 +411,11 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|    * |    * | ||||||
|    * @returns {*} |    * @returns {*} | ||||||
|    */ |    */ | ||||||
|   render(): React.Node { |   render() { | ||||||
|     const {props, state} = this; |     const {props, state} = this; | ||||||
|     return ( |     return ( | ||||||
|       <View style={{flex: 1}}> |       <View style={{flex: 1}}> | ||||||
|         <AuthenticatedScreen |         <AuthenticatedScreen<TeamResponseType | VoteDatesStringType> | ||||||
|           navigation={props.navigation} |           navigation={props.navigation} | ||||||
|           ref={this.authRef} |           ref={this.authRef} | ||||||
|           requests={[ |           requests={[ | ||||||
|  | @ -418,7 +438,6 @@ export default class VoteScreen extends React.Component<PropsType, StateType> { | ||||||
|           message={i18n.t('screens.vote.mascotDialog.message')} |           message={i18n.t('screens.vote.mascotDialog.message')} | ||||||
|           icon="vote" |           icon="vote" | ||||||
|           buttons={{ |           buttons={{ | ||||||
|             action: null, |  | ||||||
|             cancel: { |             cancel: { | ||||||
|               message: i18n.t('screens.vote.mascotDialog.button'), |               message: i18n.t('screens.vote.mascotDialog.button'), | ||||||
|               icon: 'check', |               icon: 'check', | ||||||
|  | @ -35,8 +35,6 @@ export type ApiDataLoginType = { | ||||||
|   token: string; |   token: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type ApiGenericDataType = {[key: string]: any}; |  | ||||||
| 
 |  | ||||||
| type ApiResponseType<T> = { | type ApiResponseType<T> = { | ||||||
|   error: number; |   error: number; | ||||||
|   data: T; |   data: T; | ||||||
|  | @ -70,7 +68,7 @@ export function isApiResponseValid<T>(response: ApiResponseType<T>): boolean { | ||||||
|  * @param path The API path from the API endpoint |  * @param path The API path from the API endpoint | ||||||
|  * @param method The HTTP method to use (GET or POST) |  * @param method The HTTP method to use (GET or POST) | ||||||
|  * @param params The params to use for this request |  * @param params The params to use for this request | ||||||
|  * @returns {Promise<ApiGenericDataType>} |  * @returns {Promise<T>} | ||||||
|  */ |  */ | ||||||
| export async function apiRequest<T>( | export async function apiRequest<T>( | ||||||
|   path: string, |   path: string, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue