Compare commits

...

16 commits

19 changed files with 411 additions and 239 deletions

22
App.js
View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {Platform, StatusBar, YellowBox} from 'react-native';
import {Platform, StatusBar, View, YellowBox} from 'react-native';
import LocaleManager from './src/managers/LocaleManager';
import AsyncStorageManager from "./src/managers/AsyncStorageManager";
import CustomIntroSlider from "./src/components/Overrides/CustomIntroSlider";
@ -20,7 +20,7 @@ import {setSafeBounceHeight} from "react-navigation-collapsible";
import {enableScreens} from 'react-native-screens';
// Native optimizations https://reactnavigation.org/docs/react-native-screens
enableScreens();
enableScreens(true);
YellowBox.ignoreWarnings([ // collapsible headers cause this warning, just ignore as it is not an issue
@ -71,6 +71,7 @@ export default class App extends React.Component<Props, State> {
this.loadAssetsAsync().then(() => {
this.onLoadFinished();
});
// console.log(Linking.makeUrl('path/into/app', { hello: 'world', goodbye: 'now' }))
}
/**
@ -120,7 +121,8 @@ export default class App extends React.Component<Props, State> {
} else {
StatusBar.setBarStyle('dark-content', true);
}
StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface, true);
if (Platform.OS === "android")
StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface, true);
}
/**
@ -190,12 +192,14 @@ export default class App extends React.Component<Props, State> {
} else {
return (
<PaperProvider theme={this.state.currentTheme}>
<NavigationContainer theme={this.state.currentTheme} ref={this.navigatorRef}>
<MainNavigator
defaultHomeRoute={this.defaultHomeRoute}
defaultHomeData={this.defaultHomeData}
/>
</NavigationContainer>
<View style={{backgroundColor: ThemeManager.getCurrentTheme().colors.background, flex: 1}}>
<NavigationContainer theme={this.state.currentTheme} ref={this.navigatorRef}>
<MainNavigator
defaultHomeRoute={this.defaultHomeRoute}
defaultHomeData={this.defaultHomeData}
/>
</NavigationContainer>
</View>
</PaperProvider>
);
}

View file

@ -15,6 +15,12 @@ type Props = {
mandatory: boolean
}>,
renderFunction: (Array<{ [key: string]: any } | null>) => React.Node,
errorViewOverride?: Array<{
errorCode: number,
message: string,
icon: string,
showRetryButton: boolean
}>,
}
type State = {
@ -158,13 +164,38 @@ class AuthenticatedScreen extends React.Component<Props, State> {
* @return {*}
*/
getErrorRender() {
return (
<ErrorView
{...this.props}
errorCode={this.getError()}
onRefresh={this.fetchData}
/>
);
const errorCode = this.getError();
let shouldOverride = false;
let override = null;
const overrideList = this.props.errorViewOverride;
if (overrideList != null) {
for (let i = 0; i < overrideList.length; i++) {
if (overrideList[i].errorCode === errorCode) {
shouldOverride = true;
override = overrideList[i];
break;
}
}
}
if (shouldOverride && override != null) {
return (
<ErrorView
{...this.props}
icon={override.icon}
message={override.message}
showRetryButton={override.showRetryButton}
/>
);
} else {
return (
<ErrorView
{...this.props}
errorCode={errorCode}
onRefresh={this.fetchData}
/>
);
}
}
/**

View file

@ -11,6 +11,7 @@ type Props = {
title: string,
titleLoading: string,
message: string,
startLoading: boolean,
}
type State = {
@ -19,8 +20,16 @@ type State = {
class LoadingConfirmDialog extends React.PureComponent<Props, State> {
static defaultProps = {
title: '',
message: '',
onDismiss: () => {},
onAccept: () => {return Promise.resolve()},
startLoading: false,
}
state = {
loading: false,
loading: this.props.startLoading,
};
/**

View file

@ -65,6 +65,17 @@ class CustomTabBar extends React.Component<Props, State> {
}
}
onItemLongPress(route: Object) {
const event = this.props.navigation.emit({
type: 'tabLongPress',
target: route.key,
canPreventDefault: true,
});
if (route.name === "home" && !event.defaultPrevented) {
this.props.navigation.navigate('tetris');
}
}
tabBarIcon = (route, focused) => {
let icon = TAB_ICONS[route.name];
icon = focused ? icon : icon + ('-outline');
@ -93,12 +104,8 @@ class CustomTabBar extends React.Component<Props, State> {
const onPress = () => this.onItemPress(route, state.index, index);
const onLongPress = () => {
this.props.navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
const onLongPress = () => this.onItemLongPress(route);
if (isFocused) {
const stackState = route.state;
const stackRoute = route.state ? stackState.routes[stackState.index] : undefined;

View file

@ -1,9 +1,10 @@
// @flow
import * as React from 'react';
import {Image, View} from "react-native";
import {FAB, withTheme} from 'react-native-paper';
import {Image, Platform, View} from "react-native";
import {FAB, TouchableRipple, withTheme} from 'react-native-paper';
import * as Animatable from "react-native-animatable";
import CustomTabBar from "./CustomTabBar";
type Props = {
focused: boolean,
@ -65,23 +66,38 @@ class TabHomeIcon extends React.Component<Props> {
render(): React$Node {
const props = this.props;
return (
<View style={{
flex: 1,
justifyContent: 'center',
}}>
<AnimatedFAB
duration={200}
easing={"ease-out"}
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
useNativeDriver
<View
style={{
flex: 1,
justifyContent: 'center',
}}>
<TouchableRipple
onPress={props.onPress}
onLongPress={props.onLongPress}
icon={this.iconRender}
borderless={true}
rippleColor={Platform.OS === 'android' ? this.props.theme.colors.primary : 'transparent'}
style={{
marginLeft: 'auto',
marginRight: 'auto'
}}/>
position: 'absolute',
bottom: 0,
left: 0,
width: '100%',
height: CustomTabBar.TAB_BAR_HEIGHT + 30,
marginBottom: -15,
}}
>
<AnimatedFAB
duration={200}
easing={"ease-out"}
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
icon={this.iconRender}
style={{
marginTop: 15,
marginLeft: 'auto',
marginRight: 'auto'
}}/>
</TouchableRipple>
</View>
);
}

View file

@ -69,10 +69,11 @@ class TabIcon extends React.Component<Props> {
<TouchableRipple
onPress={props.onPress}
onLongPress={props.onLongPress}
borderless={true}
rippleColor={this.props.theme.colors.primary}
style={{
flex: 1,
justifyContent: 'center',
alignItem: 'center',
}}
>
<View>

View file

@ -9,13 +9,38 @@ import {createStackNavigator, TransitionPresets} from "@react-navigation/stack";
import i18n from "i18n-js";
import TabNavigator from "./TabNavigator";
import TetrisScreen from "../screens/Tetris/TetrisScreen";
import VoteScreen from "../screens/Amicale/VoteScreen";
import LoginScreen from "../screens/Amicale/LoginScreen";
import {Platform} from "react-native";
import AvailableRoomScreen from "../screens/Websites/AvailableRoomScreen";
import BibScreen from "../screens/Websites/BibScreen";
import SelfMenuScreen from "../screens/Services/SelfMenuScreen";
import ProximoMainScreen from "../screens/Proximo/ProximoMainScreen";
import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
import {AmicaleWebsiteScreen} from "../screens/Websites/AmicaleWebsiteScreen";
import {ElusEtudiantsWebsiteScreen} from "../screens/Websites/ElusEtudiantsWebsiteScreen";
import {WiketudWebsiteScreen} from "../screens/Websites/WiketudWebsiteScreen";
import {TutorInsaWebsiteScreen} from "../screens/Websites/TutorInsaWebsiteScreen";
import {ENTWebsiteScreen} from "../screens/Websites/ENTWebsiteScreen";
import {BlueMindWebsiteScreen} from "../screens/Websites/BlueMindWebsiteScreen";
import ProfileScreen from "../screens/Amicale/ProfileScreen";
import ClubListScreen from "../screens/Amicale/Clubs/ClubListScreen";
import ClubAboutScreen from "../screens/Amicale/Clubs/ClubAboutScreen";
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
import {createScreenCollapsibleStack, getWebsiteStack} from "../utils/CollapsibleUtils";
const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS;
const screenTransition = TransitionPresets.SlideFromRightIOS;
const defaultScreenOptions = {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.SlideFromRightIOS,
...screenTransition,
};
const MainStack = createStackNavigator();
function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
@ -30,6 +55,7 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
component={props.createTabNavigator}
options={{
headerShown: false,
title: i18n.t('screens.home'),
}}
/>
<MainStack.Screen
@ -67,6 +93,71 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
title: i18n.t("game.title"),
}}
/>
<MainStack.Screen
name="login"
component={LoginScreen}
options={{
title: i18n.t('screens.login'),
}}
/>
{/* INSA */}
{getWebsiteStack("available-rooms", MainStack, AvailableRoomScreen, i18n.t('screens.availableRooms'))}
{getWebsiteStack("bib", MainStack, BibScreen, i18n.t('screens.bib'))}
{createScreenCollapsibleStack("self-menu", MainStack, SelfMenuScreen, i18n.t('screens.menuSelf'))}
{/* STUDENTS */}
{createScreenCollapsibleStack("proximo", MainStack, ProximoMainScreen, i18n.t('screens.proximo'))}
{createScreenCollapsibleStack(
"proximo-list",
MainStack,
ProximoListScreen,
i18n.t('screens.proximoArticles'),
true,
{...screenTransition},
)}
<MainStack.Screen
name="proximo-about"
component={ProximoAboutScreen}
options={{
title: i18n.t('screens.proximo'),
...modalTransition,
}}
/>
{getWebsiteStack("amicale-website", MainStack, AmicaleWebsiteScreen, i18n.t('screens.amicaleWebsite'))}
{getWebsiteStack("elus-etudiants", MainStack, ElusEtudiantsWebsiteScreen, "Élus Étudiants")}
{getWebsiteStack("wiketud", MainStack, WiketudWebsiteScreen, "Wiketud")}
{getWebsiteStack("tutorinsa", MainStack, TutorInsaWebsiteScreen, "Tutor'INSA")}
{getWebsiteStack("ent", MainStack, ENTWebsiteScreen, i18n.t('screens.ent'))}
{getWebsiteStack("bluemind", MainStack, BlueMindWebsiteScreen, i18n.t('screens.bluemind'))}
{/* AMICALE */}
{createScreenCollapsibleStack("profile", MainStack, ProfileScreen, i18n.t('screens.profile'))}
{createScreenCollapsibleStack("club-list", MainStack, ClubListScreen, i18n.t('clubs.clubList'))}
<MainStack.Screen
name="club-information"
component={ClubDisplayScreen}
options={{
title: i18n.t('screens.clubDisplayScreen'),
...modalTransition,
}}
/>
<MainStack.Screen
name="club-about"
component={ClubAboutScreen}
options={{
title: i18n.t('screens.clubsAbout'),
...modalTransition,
}}
/>
<MainStack.Screen
name="vote"
component={VoteScreen}
options={{
title: i18n.t('screens.vote'),
}}
/>
</MainStack.Navigator>
);
}

View file

@ -7,9 +7,6 @@ import PlanningScreen from '../screens/Planning/PlanningScreen';
import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen';
import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen';
import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen';
import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
import PlanexScreen from '../screens/Planex/PlanexScreen';
import AsyncStorageManager from "../managers/AsyncStorageManager";
import {useTheme} from 'react-native-paper';
@ -21,23 +18,10 @@ import FeedItemScreen from "../screens/Home/FeedItemScreen";
import {createCollapsibleStack} from "react-navigation-collapsible";
import GroupSelectionScreen from "../screens/Planex/GroupSelectionScreen";
import CustomTabBar from "../components/Tabbar/CustomTabBar";
import SelfMenuScreen from "../screens/Services/SelfMenuScreen";
import AvailableRoomScreen from "../screens/Websites/AvailableRoomScreen";
import BibScreen from "../screens/Websites/BibScreen";
import {AmicaleWebsiteScreen} from "../screens/Websites/AmicaleWebsiteScreen";
import {ElusEtudiantsWebsiteScreen} from "../screens/Websites/ElusEtudiantsWebsiteScreen";
import {WiketudWebsiteScreen} from "../screens/Websites/WiketudWebsiteScreen";
import {TutorInsaWebsiteScreen} from "../screens/Websites/TutorInsaWebsiteScreen";
import {ENTWebsiteScreen} from "../screens/Websites/ENTWebsiteScreen";
import {BlueMindWebsiteScreen} from "../screens/Websites/BlueMindWebsiteScreen";
import LoginScreen from "../screens/Amicale/LoginScreen";
import ProfileScreen from "../screens/Amicale/ProfileScreen";
import ClubListScreen from "../screens/Amicale/Clubs/ClubListScreen";
import ClubAboutScreen from "../screens/Amicale/Clubs/ClubAboutScreen";
import VoteScreen from "../screens/Amicale/VoteScreen";
import AmicaleContactScreen from "../screens/Amicale/AmicaleContactScreen";
import WebsitesHomeScreen from "../screens/Services/ServicesScreen";
import ServicesSectionScreen from "../screens/Services/ServicesSectionScreen";
import AmicaleContactScreen from "../screens/Amicale/AmicaleContactScreen";
import {createScreenCollapsibleStack, getWebsiteStack} from "../utils/CollapsibleUtils";
const defaultScreenOptions = {
gestureEnabled: true,
@ -47,41 +31,6 @@ const defaultScreenOptions = {
const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS;
const screenTransition = Platform.OS === 'ios' ? TransitionPresets.SlideFromRightIOS : TransitionPresets.ScaleFromCenterAndroid;
function createScreenCollapsibleStack(
name: string,
Stack: any,
component: any,
title: string,
useNativeDriver?: boolean,
options?: { [key: string]: any }) {
const {colors} = useTheme();
const screenOptions = options != null ? options : {};
return createCollapsibleStack(
<Stack.Screen
name={name}
component={component}
options={{
title: title,
headerStyle: {
backgroundColor: colors.surface,
},
...screenOptions,
}}
/>,
{
collapsedColor: 'transparent',
useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
}
)
}
function getWebsiteStack(name: string, Stack: any, component: any, title: string) {
return createScreenCollapsibleStack(name, Stack, component, title, false);
}
const ServicesStack = createStackNavigator();
function ServicesStackComponent() {
@ -93,64 +42,7 @@ function ServicesStackComponent() {
>
{createScreenCollapsibleStack("index", ServicesStack, WebsitesHomeScreen, i18n.t('screens.services'))}
{createScreenCollapsibleStack("services-section", ServicesStack, ServicesSectionScreen, "SECTION")}
{/* INSA */}
{getWebsiteStack("available-rooms", ServicesStack, AvailableRoomScreen, i18n.t('screens.availableRooms'))}
{getWebsiteStack("bib", ServicesStack, BibScreen, i18n.t('screens.bib'))}
{createScreenCollapsibleStack("self-menu", ServicesStack, SelfMenuScreen, i18n.t('screens.menuSelf'))}
{/* STUDENTS */}
{createScreenCollapsibleStack("proximo", ServicesStack, ProximoMainScreen, i18n.t('screens.proximo'))}
{createScreenCollapsibleStack(
"proximo-list",
ServicesStack,
ProximoListScreen,
i18n.t('screens.proximoArticles'),
true,
{...screenTransition},
)}
<ServicesStack.Screen
name="proximo-about"
component={ProximoAboutScreen}
options={{
title: i18n.t('screens.proximo'),
...modalTransition,
}}
/>
{getWebsiteStack("amicale-website", ServicesStack, AmicaleWebsiteScreen, i18n.t('screens.amicaleWebsite'))}
{getWebsiteStack("elus-etudiants", ServicesStack, ElusEtudiantsWebsiteScreen, "Élus Étudiants")}
{getWebsiteStack("wiketud", ServicesStack, WiketudWebsiteScreen, "Wiketud")}
{getWebsiteStack("tutorinsa", ServicesStack, TutorInsaWebsiteScreen, "Tutor'INSA")}
{getWebsiteStack("ent", ServicesStack, ENTWebsiteScreen, i18n.t('screens.ent'))}
{getWebsiteStack("bluemind", ServicesStack, BlueMindWebsiteScreen, i18n.t('screens.bluemind'))}
{/* AMICALE */}
{createScreenCollapsibleStack("login", ServicesStack, LoginScreen, i18n.t('screens.login'))}
{createScreenCollapsibleStack("profile", ServicesStack, ProfileScreen, i18n.t('screens.profile'))}
{createScreenCollapsibleStack("club-list", ServicesStack, ClubListScreen, i18n.t('clubs.clubList'))}
<ServicesStack.Screen
name="club-about"
component={ClubAboutScreen}
options={{
title: i18n.t('screens.clubsAbout'),
...modalTransition,
}}
/>
<ServicesStack.Screen
name="vote"
component={VoteScreen}
options={{
title: i18n.t('screens.vote'),
}}
/>
<ServicesStack.Screen
name="amicale-contact"
component={AmicaleContactScreen}
options={{
title: i18n.t('screens.amicaleAbout'),
}}
/>
{createScreenCollapsibleStack("amicale-contact", ServicesStack, AmicaleContactScreen, i18n.t('screens.amicaleAbout'), true, {...modalTransition})}
</ServicesStack.Navigator>
);
}
@ -231,7 +123,7 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
initialParams={params}
/>,
{
collapsedColor: 'transparent',
collapsedColor: colors.surface,
useNativeDriver: true,
}
)}
@ -267,8 +159,6 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
...modalTransition,
}}
/>
{createScreenCollapsibleStack("self-menu", HomeStack, SelfMenuScreen, i18n.t('screens.menuSelf'), true, {...modalTransition})}
{createScreenCollapsibleStack("login", HomeStack, LoginScreen, i18n.t('screens.login'))}
</HomeStack.Navigator>
);
}

View file

@ -1,12 +1,17 @@
// @flow
import * as React from 'react';
import {FlatList, Image, View} from 'react-native';
import {Animated, FlatList, Image, View} from 'react-native';
import {Card, List, Text, withTheme} from 'react-native-paper';
import i18n from 'i18n-js';
import {Linking} from "expo";
import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {withCollapsible} from "../../utils/withCollapsible";
type Props = {};
type Props = {
collapsibleStack: Collapsible
};
type State = {};
@ -123,14 +128,22 @@ class AmicaleContactScreen extends React.Component<Props, State> {
};
render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return (
//$FlowFixMe
<FlatList
<Animated.FlatList
data={[{key: "1"}]}
renderItem={this.getScreen}
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/>
);
}
}
export default withTheme(AmicaleContactScreen);
export default withCollapsible(withTheme(AmicaleContactScreen));

View file

@ -2,22 +2,36 @@
import * as React from 'react';
import {ScrollView, View} from 'react-native';
import {Avatar, Card, Chip, Paragraph, withTheme} from 'react-native-paper';
import {Avatar, Button, Card, Chip, Paragraph, withTheme} from 'react-native-paper';
import ImageModal from 'react-native-image-modal';
import i18n from "i18n-js";
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import CustomHTML from "../../../components/Overrides/CustomHTML";
import CustomTabBar from "../../../components/Tabbar/CustomTabBar";
import type {category, club} from "./ClubListScreen";
import type {CustomTheme} from "../../../managers/ThemeManager";
import {StackNavigationProp} from "@react-navigation/stack";
import {Linking} from "expo";
import {ERROR_TYPE} from "../../../utils/WebData";
type Props = {
navigation: Object,
route: Object
navigation: StackNavigationProp,
route: {
params?: {
data?: club,
categories?: Array<category>,
clubId?: number,
}, ...
},
theme: CustomTheme
};
type State = {
imageModalVisible: boolean,
};
const AMICALE_MAIL = "clubs@amicale-insat.fr";
/**
* Class defining a club event information page.
* If called with data and categories navigation parameters, will use those to display the data.
@ -25,32 +39,30 @@ type State = {
*/
class ClubDisplayScreen extends React.Component<Props, State> {
displayData: Object;
categories: Object | null;
displayData: club | null;
categories: Array<category> | null;
clubId: number;
shouldFetchData: boolean;
colors: Object;
state = {
imageModalVisible: false,
};
constructor(props) {
super(props);
this.colors = props.theme.colors;
if (this.props.route.params.data !== undefined && this.props.route.params.categories !== undefined) {
this.displayData = this.props.route.params.data;
this.categories = this.props.route.params.categories;
this.clubId = this.props.route.params.data.id;
this.shouldFetchData = false;
} else {
this.displayData = null;
this.categories = null;
this.clubId = this.props.route.params.clubId;
this.shouldFetchData = true;
if (this.props.route.params != null) {
if (this.props.route.params.data != null && this.props.route.params.categories != null) {
this.displayData = this.props.route.params.data;
this.categories = this.props.route.params.categories;
this.clubId = this.props.route.params.data.id;
this.shouldFetchData = false;
} else if (this.props.route.params.clubId != null) {
this.displayData = null;
this.categories = null;
this.clubId = this.props.route.params.clubId;
this.shouldFetchData = true;
}
}
}
@ -64,7 +76,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
return "";
}
getCategoriesRender(categories: Array<number | null>) {
getCategoriesRender(categories: [number, number]) {
if (this.categories === null)
return null;
@ -84,7 +96,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>;
}
getManagersRender(resp: Array<string>) {
getManagersRender(resp: Array<string>, email: string | null) {
let final = [];
for (let i = 0; i < resp.length; i++) {
final.push(<Paragraph key={i.toString()}>{resp[i]}</Paragraph>)
@ -98,56 +110,81 @@ class ClubDisplayScreen extends React.Component<Props, State> {
left={(props) => <Avatar.Icon
{...props}
style={{backgroundColor: 'transparent'}}
color={hasManagers ? this.colors.success : this.colors.primary}
color={hasManagers ? this.props.theme.colors.success : this.props.theme.colors.primary}
icon="account-tie"/>}
/>
<Card.Content>
{final}
{this.getEmailButton(email, hasManagers)}
</Card.Content>
</Card>
);
}
getEmailButton(email: string | null, hasManagers: boolean) {
const destinationEmail = email != null && hasManagers
? email
: AMICALE_MAIL;
const text = email != null && hasManagers
? i18n.t("clubs.clubContact")
: i18n.t("clubs.amicaleContact");
return (
<Card.Actions>
<Button
icon="email"
mode="contained"
onPress={() => Linking.openURL('mailto:' + destinationEmail)}
style={{marginLeft: 'auto'}}
>
{text}
</Button>
</Card.Actions>
);
}
updateHeaderTitle(data: Object) {
this.props.navigation.setOptions({title: data.name})
}
getScreen = (response: Array<Object>) => {
let data = response[0];
let data: club = response[0];
this.updateHeaderTitle(data);
if (data != null) {
return (
<ScrollView style={{paddingLeft: 5, paddingRight: 5}}>
{this.getCategoriesRender(data.category)}
{data.logo !== null ?
<View style={{
marginLeft: 'auto',
marginRight: 'auto',
marginTop: 10,
marginBottom: 10,
}}>
<ImageModal
resizeMode="contain"
imageBackgroundColor={this.props.theme.colors.background}
style={{
width: 300,
height: 300,
}}
source={{
uri: data.logo,
}}
/></View>
: <View/>}
return (
<ScrollView style={{paddingLeft: 5, paddingRight: 5}}>
{this.getCategoriesRender(data.category)}
{data.logo !== null ?
<View style={{
marginLeft: 'auto',
marginRight: 'auto',
marginTop: 10,
marginBottom: 10,
}}>
<ImageModal
resizeMode="contain"
imageBackgroundColor={this.colors.background}
style={{
width: 300,
height: 300,
}}
source={{
uri: data.logo,
}}
/></View>
: <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/>}
{this.getManagersRender(data.responsibles, data.email)}
q </ScrollView>
);
} else
return null;
{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/>}
{this.getManagersRender(data.responsibles)}
</ScrollView>
);
};
render() {
@ -162,6 +199,14 @@ class ClubDisplayScreen extends React.Component<Props, State> {
}
]}
renderFunction={this.getScreen}
errorViewOverride={[
{
errorCode: ERROR_TYPE.BAD_INPUT,
message: i18n.t("clubs.invalidClub"),
icon: "account-question",
showRetryButton: false
}
]}
/>;
else
return this.getScreen([this.displayData]);

View file

@ -24,7 +24,7 @@ export type club = {
name: string,
description: string,
logo: string,
email:string,
email: string | null,
category: [number, number],
responsibles: Array<string>,
};

View file

@ -192,11 +192,11 @@ class HomeScreen extends React.Component<Props, State> {
};
onProximoClick = () => {
this.props.navigation.navigate('services', {screen: "index"});
this.props.navigation.navigate("proximo");
};
onTutorInsaClick = () => {
this.props.navigation.navigate('services', {screen: "index"});
this.props.navigation.navigate("tutorinsa");
};
onMenuClick = () => {

View file

@ -10,6 +10,7 @@ import {Linking} from "expo";
import AlertDialog from "../../components/Dialogs/AlertDialog";
import i18n from 'i18n-js';
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import LoadingConfirmDialog from "../../components/Dialogs/LoadingConfirmDialog";
type Props = {};
type State = {
@ -18,6 +19,7 @@ type State = {
dialogVisible: boolean,
dialogTitle: string,
dialogMessage: string,
loading: boolean,
};
class ScannerScreen extends React.Component<Props, State> {
@ -28,6 +30,7 @@ class ScannerScreen extends React.Component<Props, State> {
dialogVisible: false,
dialogTitle: "",
dialogMessage: "",
loading: false,
};
constructor() {
@ -46,7 +49,7 @@ class ScannerScreen extends React.Component<Props, State> {
if (!URLHandler.isUrlValid(data))
this.showErrorDialog();
else {
this.setState({scanned: true});
this.showOpeningDialog();
Linking.openURL(data);
}
};
@ -108,6 +111,13 @@ class ScannerScreen extends React.Component<Props, State> {
});
};
showOpeningDialog = () => {
this.setState({
loading: true,
scanned: true,
});
};
showErrorDialog() {
this.setState({
dialogVisible: true,
@ -166,6 +176,11 @@ class ScannerScreen extends React.Component<Props, State> {
title={this.state.dialogTitle}
message={this.state.dialogMessage}
/>
<LoadingConfirmDialog
visible={this.state.loading}
titleLoading={i18n.t("general.loading")}
startLoading={true}
/>
</View>
);
}

View file

@ -7,10 +7,11 @@ import {Card, withTheme} from 'react-native-paper';
import DateManager from "../../managers/DateManager";
import ImageModal from 'react-native-image-modal';
import BasicLoadingScreen from "../../components/Screens/BasicLoadingScreen";
import {apiRequest} from "../../utils/WebData";
import {apiRequest, ERROR_TYPE} from "../../utils/WebData";
import ErrorView from "../../components/Screens/ErrorView";
import CustomHTML from "../../components/Overrides/CustomHTML";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import i18n from 'i18n-js';
type Props = {
navigation: Object,
@ -113,13 +114,20 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
);
}
getErrorView() {
if (this.errorCode === ERROR_TYPE.BAD_INPUT)
return <ErrorView {...this.props} showRetryButton={false} message={i18n.t("planningScreen.invalidEvent")} icon={"calendar-remove"}/>;
else
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
}
render() {
if (this.state.loading)
return <BasicLoadingScreen/>;
else if (this.errorCode === 0)
return this.getContent();
else
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
return this.getErrorView();
}
}

View file

@ -12,6 +12,7 @@ import {Avatar, Button, Card, Divider, List, Title, TouchableRipple, withTheme}
import type {CustomTheme} from "../../managers/ThemeManager";
import ConnectionManager from "../../managers/ConnectionManager";
import i18n from 'i18n-js';
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
type Props = {
navigation: Object,
@ -67,12 +68,6 @@ class ServicesScreen extends React.Component<Props, State> {
image: AMICALE_IMAGE,
onPress: () => nav.navigate("profile"),
},
{
title: i18n.t('screens.amicaleAbout'),
subtitle: "CONTACT",
image: AMICALE_IMAGE,
onPress: () => nav.navigate("amicale-contact"),
},
{
title: i18n.t('screens.vote'),
subtitle: "ELECTIONS",
@ -174,9 +169,18 @@ class ServicesScreen extends React.Component<Props, State> {
componentDidMount() {
this.props.navigation.addListener('focus', this.onFocus);
this.props.navigation.setOptions({
headerRight: this.getAboutButton,
});
}
getAboutButton = () =>
<MaterialHeaderButtons>
<Item title="information" iconName="information" onPress={this.onAboutPress}/>
</MaterialHeaderButtons>;
onAboutPress = () => this.props.navigation.navigate('amicale-contact');
onFocus = () => {
this.handleNavigationParams();
this.setState({isLoggedIn: ConnectionManager.getInstance().isLoggedIn()})

View file

@ -0,0 +1,38 @@
// @flow
import * as React from 'react';
import {useTheme} from "react-native-paper";
import {createCollapsibleStack} from "react-navigation-collapsible";
import StackNavigator, {StackNavigationOptions} from "@react-navigation/stack";
export function createScreenCollapsibleStack(
name: string,
Stack: StackNavigator,
component: React.Node,
title: string,
useNativeDriver?: boolean,
options?: StackNavigationOptions) {
const {colors} = useTheme();
const screenOptions = options != null ? options : {};
return createCollapsibleStack(
<Stack.Screen
name={name}
component={component}
options={{
title: title,
headerStyle: {
backgroundColor: colors.surface,
},
...screenOptions,
}}
/>,
{
collapsedColor: colors.surface,
useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview
}
)
}
export function getWebsiteStack(name: string, Stack: any, component: any, title: string) {
return createScreenCollapsibleStack(name, Stack, component, title, false);
}

View file

@ -8,7 +8,7 @@ export default class URLHandler {
static EVENT_INFO_URL_PATH = "event";
static CLUB_INFO_ROUTE = "club-information";
static EVENT_INFO_ROUTE = "home-planning-information";
static EVENT_INFO_ROUTE = "planning-information";
onInitialURLParsed: Function;
onDetectURL: Function;

View file

@ -26,12 +26,6 @@
"vote": "Elections",
"scanner": "Scanotron 3000"
},
"sidenav": {
"divider1": "Student websites",
"divider2": "Services",
"divider3": "Personalisation",
"divider4": "Amicale"
},
"intro": {
"slide1": {
"title": "Welcome to CAMPUS",
@ -261,6 +255,9 @@
"managersUnavailable": "This club has no one :(",
"categories": "Categories",
"categoriesFilterMessage": "Click on a category to filter the list",
"clubContact": "Contact the club",
"amicaleContact": "Contact the Amicale",
"invalidClub": "Could not find the club. Please make sure the club you are trying to access is valid.",
"about": {
"text": "The clubs, making the campus live, with more than sixty clubs offering various activities! From the philosophy club to the PABI (Production Artisanale de Bière Insaienne), without forgetting the multiple music and dance clubs, you will surely find an activity that suits you!",
"title": "A question ?",
@ -388,5 +385,8 @@
"students": "Student services",
"insa": "INSA services",
"notLoggedIn": "Not logged in"
},
"planningScreen": {
"invalidEvent": "Could not find the event. Please make sure the event you are trying to access is valid."
}
}

View file

@ -26,12 +26,6 @@
"vote": "Élections",
"scanner": "Scanotron 3000"
},
"sidenav": {
"divider1": "Sites étudiants",
"divider2": "Services",
"divider3": "Personnalisation",
"divider4": "Amicale"
},
"intro": {
"slide1": {
"title": "Bienvenue sur CAMPUS",
@ -261,6 +255,9 @@
"managersUnavailable": "Ce club est tout seul :(",
"categories": "Catégories",
"categoriesFilterMessage": "Cliquez sur une catégorie pour filtrer la liste",
"clubContact": "Contacter le club",
"amicaleContact": "Contacter l'Amicale",
"invalidClub": "Impossible de trouver le club. Merci de vérifier que le club que vous voulez voir est valide.",
"about": {
"text": "Les clubs, c'est ce qui fait vivre le campus au quotidien, plus d'une soixantaine de clubs qui proposent des activités diverses et variées ! Du club Philosophie au PABI (Production Artisanale de Bière Insaienne), en passant par les multiples clubs de musique et de danse, vous trouverez forcément une activité qui vous permettra de vous épanouir sur le campus !",
"title": "Une question ?",
@ -388,5 +385,8 @@
"students": "Services étudiants",
"insa": "Services de l'INSA",
"notLoggedIn": "Non connecté"
},
"planningScreen": {
"invalidEvent": "Impossible de trouver l'événement. Merci de vérifier que l'événement que vous voulez voir est valide."
}
}