283 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /*
 | |
|  * 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 React, { useState } from 'react';
 | |
| import { Linking, StyleSheet, View } from 'react-native';
 | |
| import {
 | |
|   Avatar,
 | |
|   Button,
 | |
|   Card,
 | |
|   Chip,
 | |
|   Paragraph,
 | |
|   useTheme,
 | |
| } from 'react-native-paper';
 | |
| import i18n from 'i18n-js';
 | |
| import CustomHTML from '../../../components/Overrides/CustomHTML';
 | |
| import { TAB_BAR_HEIGHT } from '../../../components/Tabbar/CustomTabBar';
 | |
| import type { ClubCategoryType, ClubType } from './ClubListScreen';
 | |
| import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
 | |
| import ImageGalleryButton from '../../../components/Media/ImageGalleryButton';
 | |
| import RequestScreen from '../../../components/Screens/RequestScreen';
 | |
| import { useFocusEffect } from '@react-navigation/core';
 | |
| import { useCallback } from 'react';
 | |
| import { useNavigation } from '@react-navigation/native';
 | |
| import { useAuthenticatedRequest } from '../../../context/loginContext';
 | |
| 
 | |
| type Props = {
 | |
|   route: {
 | |
|     params?: {
 | |
|       data?: ClubType;
 | |
|       categories?: Array<ClubCategoryType>;
 | |
|       clubId?: number;
 | |
|     };
 | |
|   };
 | |
| };
 | |
| 
 | |
| type ResponseType = ClubType;
 | |
| 
 | |
| const AMICALE_MAIL = 'clubs@amicale-insat.fr';
 | |
| 
 | |
| const styles = StyleSheet.create({
 | |
|   category: {
 | |
|     marginRight: 5,
 | |
|   },
 | |
|   categoryContainer: {
 | |
|     flexDirection: 'row',
 | |
|     marginTop: 5,
 | |
|   },
 | |
|   card: {
 | |
|     marginTop: 10,
 | |
|   },
 | |
|   icon: {
 | |
|     backgroundColor: 'transparent',
 | |
|   },
 | |
|   emailButton: {
 | |
|     marginLeft: 'auto',
 | |
|   },
 | |
|   scroll: {
 | |
|     paddingLeft: 5,
 | |
|     paddingRight: 5,
 | |
|   },
 | |
|   imageButton: {
 | |
|     width: 300,
 | |
|     height: 300,
 | |
|     marginLeft: 'auto',
 | |
|     marginRight: 'auto',
 | |
|     marginTop: 10,
 | |
|     marginBottom: 10,
 | |
|   },
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * Class defining a club event information page.
 | |
|  * If called with data and categories navigation parameters, will use those to display the data.
 | |
|  * If called with clubId parameter, will fetch the information on the server
 | |
|  */
 | |
| function ClubDisplayScreen(props: Props) {
 | |
|   const navigation = useNavigation();
 | |
|   const theme = useTheme();
 | |
| 
 | |
|   const [displayData, setDisplayData] = useState<ClubType | undefined>();
 | |
|   const [categories, setCategories] =
 | |
|     useState<Array<ClubCategoryType> | undefined>();
 | |
|   const [clubId, setClubId] = useState<number | undefined>();
 | |
| 
 | |
|   useFocusEffect(
 | |
|     useCallback(() => {
 | |
|       if (props.route.params?.data && props.route.params?.categories) {
 | |
|         setDisplayData(props.route.params.data);
 | |
|         setCategories(props.route.params.categories);
 | |
|         setClubId(props.route.params.data.id);
 | |
|       } else {
 | |
|         const id = props.route.params?.clubId;
 | |
|         setClubId(id ? id : 0);
 | |
|       }
 | |
|     }, [props.route.params])
 | |
|   );
 | |
| 
 | |
|   /**
 | |
|    * Gets the name of the category with the given ID
 | |
|    *
 | |
|    * @param id The category's ID
 | |
|    * @returns {string|*}
 | |
|    */
 | |
|   const getCategoryName = (id: number): string => {
 | |
|     let categoryName = '';
 | |
|     if (categories) {
 | |
|       categories.forEach((item: ClubCategoryType) => {
 | |
|         if (id === item.id) {
 | |
|           categoryName = item.name;
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|     return categoryName;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Gets the view for rendering categories
 | |
|    *
 | |
|    * @param categories The categories to display (max 2)
 | |
|    * @returns {null|*}
 | |
|    */
 | |
|   const getCategoriesRender = (c: Array<number | null>) => {
 | |
|     if (!categories) {
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     const final: Array<React.ReactNode> = [];
 | |
|     c.forEach((cat: number | null) => {
 | |
|       if (cat != null) {
 | |
|         final.push(
 | |
|           <Chip style={styles.category} key={cat}>
 | |
|             {getCategoryName(cat)}
 | |
|           </Chip>
 | |
|         );
 | |
|       }
 | |
|     });
 | |
|     return <View style={styles.categoryContainer}>{final}</View>;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Gets the view for rendering club managers if any
 | |
|    *
 | |
|    * @param managers The list of manager names
 | |
|    * @param email The club contact email
 | |
|    * @returns {*}
 | |
|    */
 | |
|   const getManagersRender = (managers: Array<string>, email: string | null) => {
 | |
|     const managersListView: Array<React.ReactNode> = [];
 | |
|     managers.forEach((item: string) => {
 | |
|       managersListView.push(<Paragraph key={item}>{item}</Paragraph>);
 | |
|     });
 | |
|     const hasManagers = managers.length > 0;
 | |
|     return (
 | |
|       <Card
 | |
|         style={{
 | |
|           marginBottom: TAB_BAR_HEIGHT + 20,
 | |
|           ...styles.card,
 | |
|         }}
 | |
|       >
 | |
|         <Card.Title
 | |
|           title={i18n.t('screens.clubs.managers')}
 | |
|           subtitle={
 | |
|             hasManagers
 | |
|               ? i18n.t('screens.clubs.managersSubtitle')
 | |
|               : i18n.t('screens.clubs.managersUnavailable')
 | |
|           }
 | |
|           left={(iconProps) => (
 | |
|             <Avatar.Icon
 | |
|               size={iconProps.size}
 | |
|               style={styles.icon}
 | |
|               color={hasManagers ? theme.colors.success : theme.colors.primary}
 | |
|               icon="account-tie"
 | |
|             />
 | |
|           )}
 | |
|         />
 | |
|         <Card.Content>
 | |
|           {managersListView}
 | |
|           {getEmailButton(email, hasManagers)}
 | |
|         </Card.Content>
 | |
|       </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 {*}
 | |
|    */
 | |
|   const getEmailButton = (email: string | null, hasManagers: boolean) => {
 | |
|     const destinationEmail =
 | |
|       email != null && hasManagers ? email : AMICALE_MAIL;
 | |
|     const text =
 | |
|       email != null && hasManagers
 | |
|         ? i18n.t('screens.clubs.clubContact')
 | |
|         : i18n.t('screens.clubs.amicaleContact');
 | |
|     return (
 | |
|       <Card.Actions>
 | |
|         <Button
 | |
|           icon="email"
 | |
|           mode="contained"
 | |
|           onPress={() => {
 | |
|             Linking.openURL(`mailto:${destinationEmail}`);
 | |
|           }}
 | |
|           style={styles.emailButton}
 | |
|         >
 | |
|           {text}
 | |
|         </Button>
 | |
|       </Card.Actions>
 | |
|     );
 | |
|   };
 | |
| 
 | |
|   const getScreen = (data: ResponseType | undefined) => {
 | |
|     if (data) {
 | |
|       updateHeaderTitle(data);
 | |
|       return (
 | |
|         <CollapsibleScrollView style={styles.scroll} hasTab>
 | |
|           {getCategoriesRender(data.category)}
 | |
|           {data.logo !== null ? (
 | |
|             <ImageGalleryButton
 | |
|               images={[{ url: data.logo }]}
 | |
|               style={styles.imageButton}
 | |
|             />
 | |
|           ) : (
 | |
|             <View />
 | |
|           )}
 | |
| 
 | |
|           {data.description !== null ? (
 | |
|             // Surround description with div to allow text styling if the description is not html
 | |
|             <Card.Content>
 | |
|               <CustomHTML html={data.description} />
 | |
|             </Card.Content>
 | |
|           ) : (
 | |
|             <View />
 | |
|           )}
 | |
|           {getManagersRender(data.responsibles, data.email)}
 | |
|         </CollapsibleScrollView>
 | |
|       );
 | |
|     }
 | |
|     return <View />;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Updates the header title to match the given club
 | |
|    *
 | |
|    * @param data The club data
 | |
|    */
 | |
|   const updateHeaderTitle = (data: ClubType) => {
 | |
|     navigation.setOptions({ title: data.name });
 | |
|   };
 | |
| 
 | |
|   const request = useAuthenticatedRequest<ClubType>('clubs/info', {
 | |
|     id: clubId,
 | |
|   });
 | |
| 
 | |
|   return (
 | |
|     <RequestScreen
 | |
|       request={request}
 | |
|       render={getScreen}
 | |
|       cache={displayData}
 | |
|       onCacheUpdate={setDisplayData}
 | |
|     />
 | |
|   );
 | |
| }
 | |
| 
 | |
| export default ClubDisplayScreen;
 |