Compare commits
16 commits
4c4f968e25
...
7162978503
| Author | SHA1 | Date | |
|---|---|---|---|
| 7162978503 | |||
| b7e1d56ff6 | |||
| f617dba1e6 | |||
| 0267ff70ce | |||
| 2fc2db39c7 | |||
| f8363353c3 | |||
| 0410499593 | |||
| 43a3d64deb | |||
| c4337b13cb | |||
| b7d6c98025 | |||
| 033bb388fd | |||
| 56effeaaf9 | |||
| b83e748d29 | |||
| 65ba27ea26 | |||
| 3a3cf200f5 | |||
| 3bac6d6662 |
19 changed files with 411 additions and 239 deletions
8
App.js
8
App.js
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
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 LocaleManager from './src/managers/LocaleManager';
|
||||||
import AsyncStorageManager from "./src/managers/AsyncStorageManager";
|
import AsyncStorageManager from "./src/managers/AsyncStorageManager";
|
||||||
import CustomIntroSlider from "./src/components/Overrides/CustomIntroSlider";
|
import CustomIntroSlider from "./src/components/Overrides/CustomIntroSlider";
|
||||||
|
|
@ -20,7 +20,7 @@ import {setSafeBounceHeight} from "react-navigation-collapsible";
|
||||||
import {enableScreens} from 'react-native-screens';
|
import {enableScreens} from 'react-native-screens';
|
||||||
|
|
||||||
// Native optimizations https://reactnavigation.org/docs/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
|
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.loadAssetsAsync().then(() => {
|
||||||
this.onLoadFinished();
|
this.onLoadFinished();
|
||||||
});
|
});
|
||||||
|
// console.log(Linking.makeUrl('path/into/app', { hello: 'world', goodbye: 'now' }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,6 +121,7 @@ export default class App extends React.Component<Props, State> {
|
||||||
} else {
|
} else {
|
||||||
StatusBar.setBarStyle('dark-content', true);
|
StatusBar.setBarStyle('dark-content', true);
|
||||||
}
|
}
|
||||||
|
if (Platform.OS === "android")
|
||||||
StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface, true);
|
StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,12 +192,14 @@ export default class App extends React.Component<Props, State> {
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<PaperProvider theme={this.state.currentTheme}>
|
<PaperProvider theme={this.state.currentTheme}>
|
||||||
|
<View style={{backgroundColor: ThemeManager.getCurrentTheme().colors.background, flex: 1}}>
|
||||||
<NavigationContainer theme={this.state.currentTheme} ref={this.navigatorRef}>
|
<NavigationContainer theme={this.state.currentTheme} ref={this.navigatorRef}>
|
||||||
<MainNavigator
|
<MainNavigator
|
||||||
defaultHomeRoute={this.defaultHomeRoute}
|
defaultHomeRoute={this.defaultHomeRoute}
|
||||||
defaultHomeData={this.defaultHomeData}
|
defaultHomeData={this.defaultHomeData}
|
||||||
/>
|
/>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
</View>
|
||||||
</PaperProvider>
|
</PaperProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ type Props = {
|
||||||
mandatory: boolean
|
mandatory: boolean
|
||||||
}>,
|
}>,
|
||||||
renderFunction: (Array<{ [key: string]: any } | null>) => React.Node,
|
renderFunction: (Array<{ [key: string]: any } | null>) => React.Node,
|
||||||
|
errorViewOverride?: Array<{
|
||||||
|
errorCode: number,
|
||||||
|
message: string,
|
||||||
|
icon: string,
|
||||||
|
showRetryButton: boolean
|
||||||
|
}>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
|
@ -158,15 +164,40 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getErrorRender() {
|
getErrorRender() {
|
||||||
|
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 (
|
return (
|
||||||
<ErrorView
|
<ErrorView
|
||||||
{...this.props}
|
{...this.props}
|
||||||
errorCode={this.getError()}
|
icon={override.icon}
|
||||||
|
message={override.message}
|
||||||
|
showRetryButton={override.showRetryButton}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<ErrorView
|
||||||
|
{...this.props}
|
||||||
|
errorCode={errorCode}
|
||||||
onRefresh={this.fetchData}
|
onRefresh={this.fetchData}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the data, to be called using ref by parent components
|
* Reloads the data, to be called using ref by parent components
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ type Props = {
|
||||||
title: string,
|
title: string,
|
||||||
titleLoading: string,
|
titleLoading: string,
|
||||||
message: string,
|
message: string,
|
||||||
|
startLoading: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
|
@ -19,8 +20,16 @@ type State = {
|
||||||
|
|
||||||
class LoadingConfirmDialog extends React.PureComponent<Props, State> {
|
class LoadingConfirmDialog extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
onDismiss: () => {},
|
||||||
|
onAccept: () => {return Promise.resolve()},
|
||||||
|
startLoading: false,
|
||||||
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
loading: false,
|
loading: this.props.startLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
tabBarIcon = (route, focused) => {
|
||||||
let icon = TAB_ICONS[route.name];
|
let icon = TAB_ICONS[route.name];
|
||||||
icon = focused ? icon : icon + ('-outline');
|
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 onPress = () => this.onItemPress(route, state.index, index);
|
||||||
|
|
||||||
const onLongPress = () => {
|
const onLongPress = () => this.onItemLongPress(route);
|
||||||
this.props.navigation.emit({
|
|
||||||
type: 'tabLongPress',
|
|
||||||
target: route.key,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (isFocused) {
|
if (isFocused) {
|
||||||
const stackState = route.state;
|
const stackState = route.state;
|
||||||
const stackRoute = route.state ? stackState.routes[stackState.index] : undefined;
|
const stackRoute = route.state ? stackState.routes[stackState.index] : undefined;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Image, View} from "react-native";
|
import {Image, Platform, View} from "react-native";
|
||||||
import {FAB, withTheme} from 'react-native-paper';
|
import {FAB, TouchableRipple, withTheme} from 'react-native-paper';
|
||||||
import * as Animatable from "react-native-animatable";
|
import * as Animatable from "react-native-animatable";
|
||||||
|
import CustomTabBar from "./CustomTabBar";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
focused: boolean,
|
focused: boolean,
|
||||||
|
|
@ -65,23 +66,38 @@ class TabHomeIcon extends React.Component<Props> {
|
||||||
render(): React$Node {
|
render(): React$Node {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<View
|
||||||
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}>
|
}}>
|
||||||
|
<TouchableRipple
|
||||||
|
onPress={props.onPress}
|
||||||
|
onLongPress={props.onLongPress}
|
||||||
|
borderless={true}
|
||||||
|
rippleColor={Platform.OS === 'android' ? this.props.theme.colors.primary : 'transparent'}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: CustomTabBar.TAB_BAR_HEIGHT + 30,
|
||||||
|
marginBottom: -15,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AnimatedFAB
|
<AnimatedFAB
|
||||||
duration={200}
|
duration={200}
|
||||||
easing={"ease-out"}
|
easing={"ease-out"}
|
||||||
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
|
animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
|
||||||
useNativeDriver
|
|
||||||
onPress={props.onPress}
|
|
||||||
onLongPress={props.onLongPress}
|
|
||||||
icon={this.iconRender}
|
icon={this.iconRender}
|
||||||
style={{
|
style={{
|
||||||
|
marginTop: 15,
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
marginRight: 'auto'
|
marginRight: 'auto'
|
||||||
}}/>
|
}}/>
|
||||||
|
</TouchableRipple>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,11 @@ class TabIcon extends React.Component<Props> {
|
||||||
<TouchableRipple
|
<TouchableRipple
|
||||||
onPress={props.onPress}
|
onPress={props.onPress}
|
||||||
onLongPress={props.onLongPress}
|
onLongPress={props.onLongPress}
|
||||||
|
borderless={true}
|
||||||
|
rippleColor={this.props.theme.colors.primary}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItem: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View>
|
<View>
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,38 @@ import {createStackNavigator, TransitionPresets} from "@react-navigation/stack";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import TabNavigator from "./TabNavigator";
|
import TabNavigator from "./TabNavigator";
|
||||||
import TetrisScreen from "../screens/Tetris/TetrisScreen";
|
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 = {
|
const defaultScreenOptions = {
|
||||||
gestureEnabled: true,
|
gestureEnabled: true,
|
||||||
cardOverlayEnabled: true,
|
cardOverlayEnabled: true,
|
||||||
...TransitionPresets.SlideFromRightIOS,
|
...screenTransition,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const MainStack = createStackNavigator();
|
const MainStack = createStackNavigator();
|
||||||
|
|
||||||
function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
|
function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
|
||||||
|
|
@ -30,6 +55,7 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
|
||||||
component={props.createTabNavigator}
|
component={props.createTabNavigator}
|
||||||
options={{
|
options={{
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
|
title: i18n.t('screens.home'),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<MainStack.Screen
|
<MainStack.Screen
|
||||||
|
|
@ -67,6 +93,71 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
|
||||||
title: i18n.t("game.title"),
|
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>
|
</MainStack.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ import PlanningScreen from '../screens/Planning/PlanningScreen';
|
||||||
import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
|
import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
|
||||||
import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen';
|
import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen';
|
||||||
import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen';
|
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 PlanexScreen from '../screens/Planex/PlanexScreen';
|
||||||
import AsyncStorageManager from "../managers/AsyncStorageManager";
|
import AsyncStorageManager from "../managers/AsyncStorageManager";
|
||||||
import {useTheme} from 'react-native-paper';
|
import {useTheme} from 'react-native-paper';
|
||||||
|
|
@ -21,23 +18,10 @@ import FeedItemScreen from "../screens/Home/FeedItemScreen";
|
||||||
import {createCollapsibleStack} from "react-navigation-collapsible";
|
import {createCollapsibleStack} from "react-navigation-collapsible";
|
||||||
import GroupSelectionScreen from "../screens/Planex/GroupSelectionScreen";
|
import GroupSelectionScreen from "../screens/Planex/GroupSelectionScreen";
|
||||||
import CustomTabBar from "../components/Tabbar/CustomTabBar";
|
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 WebsitesHomeScreen from "../screens/Services/ServicesScreen";
|
||||||
import ServicesSectionScreen from "../screens/Services/ServicesSectionScreen";
|
import ServicesSectionScreen from "../screens/Services/ServicesSectionScreen";
|
||||||
|
import AmicaleContactScreen from "../screens/Amicale/AmicaleContactScreen";
|
||||||
|
import {createScreenCollapsibleStack, getWebsiteStack} from "../utils/CollapsibleUtils";
|
||||||
|
|
||||||
const defaultScreenOptions = {
|
const defaultScreenOptions = {
|
||||||
gestureEnabled: true,
|
gestureEnabled: true,
|
||||||
|
|
@ -47,41 +31,6 @@ const defaultScreenOptions = {
|
||||||
|
|
||||||
const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS;
|
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();
|
const ServicesStack = createStackNavigator();
|
||||||
|
|
||||||
function ServicesStackComponent() {
|
function ServicesStackComponent() {
|
||||||
|
|
@ -93,64 +42,7 @@ function ServicesStackComponent() {
|
||||||
>
|
>
|
||||||
{createScreenCollapsibleStack("index", ServicesStack, WebsitesHomeScreen, i18n.t('screens.services'))}
|
{createScreenCollapsibleStack("index", ServicesStack, WebsitesHomeScreen, i18n.t('screens.services'))}
|
||||||
{createScreenCollapsibleStack("services-section", ServicesStack, ServicesSectionScreen, "SECTION")}
|
{createScreenCollapsibleStack("services-section", ServicesStack, ServicesSectionScreen, "SECTION")}
|
||||||
|
{createScreenCollapsibleStack("amicale-contact", ServicesStack, AmicaleContactScreen, i18n.t('screens.amicaleAbout'), true, {...modalTransition})}
|
||||||
{/* 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'),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ServicesStack.Navigator>
|
</ServicesStack.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +123,7 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
|
||||||
initialParams={params}
|
initialParams={params}
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
collapsedColor: 'transparent',
|
collapsedColor: colors.surface,
|
||||||
useNativeDriver: true,
|
useNativeDriver: true,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
@ -267,8 +159,6 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
|
||||||
...modalTransition,
|
...modalTransition,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{createScreenCollapsibleStack("self-menu", HomeStack, SelfMenuScreen, i18n.t('screens.menuSelf'), true, {...modalTransition})}
|
|
||||||
{createScreenCollapsibleStack("login", HomeStack, LoginScreen, i18n.t('screens.login'))}
|
|
||||||
</HomeStack.Navigator>
|
</HomeStack.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
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 {Card, List, Text, withTheme} from 'react-native-paper';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {Linking} from "expo";
|
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 = {};
|
type State = {};
|
||||||
|
|
||||||
|
|
@ -123,14 +128,22 @@ class AmicaleContactScreen extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
|
||||||
return (
|
return (
|
||||||
//$FlowFixMe
|
<Animated.FlatList
|
||||||
<FlatList
|
|
||||||
data={[{key: "1"}]}
|
data={[{key: "1"}]}
|
||||||
renderItem={this.getScreen}
|
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));
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,36 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {ScrollView, View} from 'react-native';
|
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 ImageModal from 'react-native-image-modal';
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
|
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
|
||||||
import CustomHTML from "../../../components/Overrides/CustomHTML";
|
import CustomHTML from "../../../components/Overrides/CustomHTML";
|
||||||
import CustomTabBar from "../../../components/Tabbar/CustomTabBar";
|
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 = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: StackNavigationProp,
|
||||||
route: Object
|
route: {
|
||||||
|
params?: {
|
||||||
|
data?: club,
|
||||||
|
categories?: Array<category>,
|
||||||
|
clubId?: number,
|
||||||
|
}, ...
|
||||||
|
},
|
||||||
|
theme: CustomTheme
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
imageModalVisible: boolean,
|
imageModalVisible: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AMICALE_MAIL = "clubs@amicale-insat.fr";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a club event information page.
|
* 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 data and categories navigation parameters, will use those to display the data.
|
||||||
|
|
@ -25,34 +39,32 @@ type State = {
|
||||||
*/
|
*/
|
||||||
class ClubDisplayScreen extends React.Component<Props, State> {
|
class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
|
|
||||||
displayData: Object;
|
displayData: club | null;
|
||||||
categories: Object | null;
|
categories: Array<category> | null;
|
||||||
clubId: number;
|
clubId: number;
|
||||||
|
|
||||||
shouldFetchData: boolean;
|
shouldFetchData: boolean;
|
||||||
|
|
||||||
colors: Object;
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
imageModalVisible: false,
|
imageModalVisible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.colors = props.theme.colors;
|
if (this.props.route.params != null) {
|
||||||
|
if (this.props.route.params.data != null && this.props.route.params.categories != null) {
|
||||||
if (this.props.route.params.data !== undefined && this.props.route.params.categories !== undefined) {
|
|
||||||
this.displayData = this.props.route.params.data;
|
this.displayData = this.props.route.params.data;
|
||||||
this.categories = this.props.route.params.categories;
|
this.categories = this.props.route.params.categories;
|
||||||
this.clubId = this.props.route.params.data.id;
|
this.clubId = this.props.route.params.data.id;
|
||||||
this.shouldFetchData = false;
|
this.shouldFetchData = false;
|
||||||
} else {
|
} else if (this.props.route.params.clubId != null) {
|
||||||
this.displayData = null;
|
this.displayData = null;
|
||||||
this.categories = null;
|
this.categories = null;
|
||||||
this.clubId = this.props.route.params.clubId;
|
this.clubId = this.props.route.params.clubId;
|
||||||
this.shouldFetchData = true;
|
this.shouldFetchData = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getCategoryName(id: number) {
|
getCategoryName(id: number) {
|
||||||
if (this.categories !== null) {
|
if (this.categories !== null) {
|
||||||
|
|
@ -64,7 +76,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
getCategoriesRender(categories: Array<number | null>) {
|
getCategoriesRender(categories: [number, number]) {
|
||||||
if (this.categories === null)
|
if (this.categories === null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
@ -84,7 +96,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>;
|
return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>;
|
||||||
}
|
}
|
||||||
|
|
||||||
getManagersRender(resp: Array<string>) {
|
getManagersRender(resp: Array<string>, email: string | null) {
|
||||||
let final = [];
|
let final = [];
|
||||||
for (let i = 0; i < resp.length; i++) {
|
for (let i = 0; i < resp.length; i++) {
|
||||||
final.push(<Paragraph key={i.toString()}>{resp[i]}</Paragraph>)
|
final.push(<Paragraph key={i.toString()}>{resp[i]}</Paragraph>)
|
||||||
|
|
@ -98,24 +110,46 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
left={(props) => <Avatar.Icon
|
left={(props) => <Avatar.Icon
|
||||||
{...props}
|
{...props}
|
||||||
style={{backgroundColor: 'transparent'}}
|
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"/>}
|
icon="account-tie"/>}
|
||||||
/>
|
/>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
{final}
|
{final}
|
||||||
|
{this.getEmailButton(email, hasManagers)}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</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) {
|
updateHeaderTitle(data: Object) {
|
||||||
this.props.navigation.setOptions({title: data.name})
|
this.props.navigation.setOptions({title: data.name})
|
||||||
}
|
}
|
||||||
|
|
||||||
getScreen = (response: Array<Object>) => {
|
getScreen = (response: Array<Object>) => {
|
||||||
let data = response[0];
|
let data: club = response[0];
|
||||||
this.updateHeaderTitle(data);
|
this.updateHeaderTitle(data);
|
||||||
|
if (data != null) {
|
||||||
return (
|
return (
|
||||||
<ScrollView style={{paddingLeft: 5, paddingRight: 5}}>
|
<ScrollView style={{paddingLeft: 5, paddingRight: 5}}>
|
||||||
{this.getCategoriesRender(data.category)}
|
{this.getCategoriesRender(data.category)}
|
||||||
|
|
@ -128,7 +162,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
}}>
|
}}>
|
||||||
<ImageModal
|
<ImageModal
|
||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
imageBackgroundColor={this.colors.background}
|
imageBackgroundColor={this.props.theme.colors.background}
|
||||||
style={{
|
style={{
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 300,
|
height: 300,
|
||||||
|
|
@ -145,9 +179,12 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
<CustomHTML html={data.description}/>
|
<CustomHTML html={data.description}/>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
: <View/>}
|
: <View/>}
|
||||||
{this.getManagersRender(data.responsibles)}
|
{this.getManagersRender(data.responsibles, data.email)}
|
||||||
</ScrollView>
|
q </ScrollView>
|
||||||
);
|
);
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -162,6 +199,14 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
renderFunction={this.getScreen}
|
renderFunction={this.getScreen}
|
||||||
|
errorViewOverride={[
|
||||||
|
{
|
||||||
|
errorCode: ERROR_TYPE.BAD_INPUT,
|
||||||
|
message: i18n.t("clubs.invalidClub"),
|
||||||
|
icon: "account-question",
|
||||||
|
showRetryButton: false
|
||||||
|
}
|
||||||
|
]}
|
||||||
/>;
|
/>;
|
||||||
else
|
else
|
||||||
return this.getScreen([this.displayData]);
|
return this.getScreen([this.displayData]);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export type club = {
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
logo: string,
|
logo: string,
|
||||||
email:string,
|
email: string | null,
|
||||||
category: [number, number],
|
category: [number, number],
|
||||||
responsibles: Array<string>,
|
responsibles: Array<string>,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -192,11 +192,11 @@ class HomeScreen extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onProximoClick = () => {
|
onProximoClick = () => {
|
||||||
this.props.navigation.navigate('services', {screen: "index"});
|
this.props.navigation.navigate("proximo");
|
||||||
};
|
};
|
||||||
|
|
||||||
onTutorInsaClick = () => {
|
onTutorInsaClick = () => {
|
||||||
this.props.navigation.navigate('services', {screen: "index"});
|
this.props.navigation.navigate("tutorinsa");
|
||||||
};
|
};
|
||||||
|
|
||||||
onMenuClick = () => {
|
onMenuClick = () => {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {Linking} from "expo";
|
||||||
import AlertDialog from "../../components/Dialogs/AlertDialog";
|
import AlertDialog from "../../components/Dialogs/AlertDialog";
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
|
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
|
||||||
|
import LoadingConfirmDialog from "../../components/Dialogs/LoadingConfirmDialog";
|
||||||
|
|
||||||
type Props = {};
|
type Props = {};
|
||||||
type State = {
|
type State = {
|
||||||
|
|
@ -18,6 +19,7 @@ type State = {
|
||||||
dialogVisible: boolean,
|
dialogVisible: boolean,
|
||||||
dialogTitle: string,
|
dialogTitle: string,
|
||||||
dialogMessage: string,
|
dialogMessage: string,
|
||||||
|
loading: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScannerScreen extends React.Component<Props, State> {
|
class ScannerScreen extends React.Component<Props, State> {
|
||||||
|
|
@ -28,6 +30,7 @@ class ScannerScreen extends React.Component<Props, State> {
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
dialogTitle: "",
|
dialogTitle: "",
|
||||||
dialogMessage: "",
|
dialogMessage: "",
|
||||||
|
loading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -46,7 +49,7 @@ class ScannerScreen extends React.Component<Props, State> {
|
||||||
if (!URLHandler.isUrlValid(data))
|
if (!URLHandler.isUrlValid(data))
|
||||||
this.showErrorDialog();
|
this.showErrorDialog();
|
||||||
else {
|
else {
|
||||||
this.setState({scanned: true});
|
this.showOpeningDialog();
|
||||||
Linking.openURL(data);
|
Linking.openURL(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -108,6 +111,13 @@ class ScannerScreen extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
showOpeningDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
loading: true,
|
||||||
|
scanned: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
showErrorDialog() {
|
showErrorDialog() {
|
||||||
this.setState({
|
this.setState({
|
||||||
dialogVisible: true,
|
dialogVisible: true,
|
||||||
|
|
@ -166,6 +176,11 @@ class ScannerScreen extends React.Component<Props, State> {
|
||||||
title={this.state.dialogTitle}
|
title={this.state.dialogTitle}
|
||||||
message={this.state.dialogMessage}
|
message={this.state.dialogMessage}
|
||||||
/>
|
/>
|
||||||
|
<LoadingConfirmDialog
|
||||||
|
visible={this.state.loading}
|
||||||
|
titleLoading={i18n.t("general.loading")}
|
||||||
|
startLoading={true}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ import {Card, withTheme} from 'react-native-paper';
|
||||||
import DateManager from "../../managers/DateManager";
|
import DateManager from "../../managers/DateManager";
|
||||||
import ImageModal from 'react-native-image-modal';
|
import ImageModal from 'react-native-image-modal';
|
||||||
import BasicLoadingScreen from "../../components/Screens/BasicLoadingScreen";
|
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 ErrorView from "../../components/Screens/ErrorView";
|
||||||
import CustomHTML from "../../components/Overrides/CustomHTML";
|
import CustomHTML from "../../components/Overrides/CustomHTML";
|
||||||
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
|
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
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() {
|
render() {
|
||||||
if (this.state.loading)
|
if (this.state.loading)
|
||||||
return <BasicLoadingScreen/>;
|
return <BasicLoadingScreen/>;
|
||||||
else if (this.errorCode === 0)
|
else if (this.errorCode === 0)
|
||||||
return this.getContent();
|
return this.getContent();
|
||||||
else
|
else
|
||||||
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
|
return this.getErrorView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {Avatar, Button, Card, Divider, List, Title, TouchableRipple, withTheme}
|
||||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
import type {CustomTheme} from "../../managers/ThemeManager";
|
||||||
import ConnectionManager from "../../managers/ConnectionManager";
|
import ConnectionManager from "../../managers/ConnectionManager";
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
@ -67,12 +68,6 @@ class ServicesScreen extends React.Component<Props, State> {
|
||||||
image: AMICALE_IMAGE,
|
image: AMICALE_IMAGE,
|
||||||
onPress: () => nav.navigate("profile"),
|
onPress: () => nav.navigate("profile"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: i18n.t('screens.amicaleAbout'),
|
|
||||||
subtitle: "CONTACT",
|
|
||||||
image: AMICALE_IMAGE,
|
|
||||||
onPress: () => nav.navigate("amicale-contact"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: i18n.t('screens.vote'),
|
title: i18n.t('screens.vote'),
|
||||||
subtitle: "ELECTIONS",
|
subtitle: "ELECTIONS",
|
||||||
|
|
@ -174,9 +169,18 @@ class ServicesScreen extends React.Component<Props, State> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.navigation.addListener('focus', this.onFocus);
|
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 = () => {
|
onFocus = () => {
|
||||||
this.handleNavigationParams();
|
this.handleNavigationParams();
|
||||||
this.setState({isLoggedIn: ConnectionManager.getInstance().isLoggedIn()})
|
this.setState({isLoggedIn: ConnectionManager.getInstance().isLoggedIn()})
|
||||||
|
|
|
||||||
38
src/utils/CollapsibleUtils.js
Normal file
38
src/utils/CollapsibleUtils.js
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ export default class URLHandler {
|
||||||
static EVENT_INFO_URL_PATH = "event";
|
static EVENT_INFO_URL_PATH = "event";
|
||||||
|
|
||||||
static CLUB_INFO_ROUTE = "club-information";
|
static CLUB_INFO_ROUTE = "club-information";
|
||||||
static EVENT_INFO_ROUTE = "home-planning-information";
|
static EVENT_INFO_ROUTE = "planning-information";
|
||||||
|
|
||||||
onInitialURLParsed: Function;
|
onInitialURLParsed: Function;
|
||||||
onDetectURL: Function;
|
onDetectURL: Function;
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,6 @@
|
||||||
"vote": "Elections",
|
"vote": "Elections",
|
||||||
"scanner": "Scanotron 3000"
|
"scanner": "Scanotron 3000"
|
||||||
},
|
},
|
||||||
"sidenav": {
|
|
||||||
"divider1": "Student websites",
|
|
||||||
"divider2": "Services",
|
|
||||||
"divider3": "Personalisation",
|
|
||||||
"divider4": "Amicale"
|
|
||||||
},
|
|
||||||
"intro": {
|
"intro": {
|
||||||
"slide1": {
|
"slide1": {
|
||||||
"title": "Welcome to CAMPUS",
|
"title": "Welcome to CAMPUS",
|
||||||
|
|
@ -261,6 +255,9 @@
|
||||||
"managersUnavailable": "This club has no one :(",
|
"managersUnavailable": "This club has no one :(",
|
||||||
"categories": "Categories",
|
"categories": "Categories",
|
||||||
"categoriesFilterMessage": "Click on a category to filter the list",
|
"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": {
|
"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!",
|
"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 ?",
|
"title": "A question ?",
|
||||||
|
|
@ -388,5 +385,8 @@
|
||||||
"students": "Student services",
|
"students": "Student services",
|
||||||
"insa": "INSA services",
|
"insa": "INSA services",
|
||||||
"notLoggedIn": "Not logged in"
|
"notLoggedIn": "Not logged in"
|
||||||
|
},
|
||||||
|
"planningScreen": {
|
||||||
|
"invalidEvent": "Could not find the event. Please make sure the event you are trying to access is valid."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,6 @@
|
||||||
"vote": "Élections",
|
"vote": "Élections",
|
||||||
"scanner": "Scanotron 3000"
|
"scanner": "Scanotron 3000"
|
||||||
},
|
},
|
||||||
"sidenav": {
|
|
||||||
"divider1": "Sites étudiants",
|
|
||||||
"divider2": "Services",
|
|
||||||
"divider3": "Personnalisation",
|
|
||||||
"divider4": "Amicale"
|
|
||||||
},
|
|
||||||
"intro": {
|
"intro": {
|
||||||
"slide1": {
|
"slide1": {
|
||||||
"title": "Bienvenue sur CAMPUS",
|
"title": "Bienvenue sur CAMPUS",
|
||||||
|
|
@ -261,6 +255,9 @@
|
||||||
"managersUnavailable": "Ce club est tout seul :(",
|
"managersUnavailable": "Ce club est tout seul :(",
|
||||||
"categories": "Catégories",
|
"categories": "Catégories",
|
||||||
"categoriesFilterMessage": "Cliquez sur une catégorie pour filtrer la liste",
|
"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": {
|
"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 !",
|
"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 ?",
|
"title": "Une question ?",
|
||||||
|
|
@ -388,5 +385,8 @@
|
||||||
"students": "Services étudiants",
|
"students": "Services étudiants",
|
||||||
"insa": "Services de l'INSA",
|
"insa": "Services de l'INSA",
|
||||||
"notLoggedIn": "Non connecté"
|
"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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue