Compare commits

...

31 commits

Author SHA1 Message Date
ac9709d666 Updated translations 2020-04-22 18:54:34 +02:00
f7e66b1251 Reduced splash size 2020-04-22 18:46:54 +02:00
38a5761f23 Removed drawer related files and renamed navigator for more coherence 2020-04-22 18:36:57 +02:00
faa174b8f1 Moved proximo and proxiwash images on the web 2020-04-22 18:34:23 +02:00
030cf7b795 Fixed text alignement 2020-04-22 15:20:10 +02:00
e8fc8b79dc Updated translations 2020-04-22 15:19:00 +02:00
62db99cac7 Reduced image size 2020-04-22 14:58:08 +02:00
87f0c01024 Redirect to service screen index 2020-04-22 12:29:36 +02:00
b5d2f686dd Fixed image alignement 2020-04-22 12:28:35 +02:00
9207d02c2a Improved dashboard amicale item 2020-04-22 12:20:55 +02:00
b31269586b Use card items when viewing the section 2020-04-22 12:08:23 +02:00
fc7754588f Improved login flow 2020-04-22 11:49:22 +02:00
92fce1d425 Fixed login redirection error 2020-04-22 11:35:01 +02:00
0c71a78b22 Enabled collapsing header in login screen 2020-04-22 11:18:43 +02:00
c9b8a6e2ca Added login/logout icon on home 2020-04-22 11:12:24 +02:00
3f945fca7a Added services section title to match current section 2020-04-22 10:49:54 +02:00
49fa8a82e3 Improved readability 2020-04-22 10:47:18 +02:00
524dd5362a Changed sections icons 2020-04-22 10:45:31 +02:00
443f179f1d Changed cards to basic view 2020-04-22 10:42:02 +02:00
8eaa46b900 Added amicale section 2020-04-22 10:14:37 +02:00
926515213d Improved code readability 2020-04-22 09:54:40 +02:00
bbe343da3b Render services in sections 2020-04-22 09:37:31 +02:00
8fca2eac12 Moved tab order 2020-04-22 08:50:47 +02:00
20cff1aeb1 Fixed ref warning 2020-04-21 20:05:21 +02:00
96ed75ac72 Added collapsible header to profile 2020-04-21 20:02:17 +02:00
b151a8ff6f Updated translations and fixed filenames 2020-04-21 19:54:53 +02:00
1f0ada3b24 Improved project structure 2020-04-21 19:48:44 +02:00
78634b0c5d Added more websites 2020-04-21 19:46:33 +02:00
e6835b0d6f Improved dashboard interaction 2020-04-21 19:40:40 +02:00
03c4a43e58 Moved all services in same tab and planning in its own 2020-04-21 19:30:13 +02:00
0b17a35856 Improved selection screens 2020-04-21 15:17:00 +02:00
30 changed files with 839 additions and 839 deletions

6
App.js
View file

@ -9,7 +9,7 @@ import {AppLoading} from 'expo';
import type {CustomTheme} from "./src/managers/ThemeManager";
import ThemeManager from './src/managers/ThemeManager';
import {NavigationContainer} from '@react-navigation/native';
import DrawerNavigator from './src/navigation/DrawerNavigator';
import MainNavigator from './src/navigation/MainNavigator';
import {initExpoToken} from "./src/utils/Notifications";
import {Provider as PaperProvider} from 'react-native-paper';
import AprilFoolsManager from "./src/managers/AprilFoolsManager";
@ -156,7 +156,7 @@ export default class App extends React.Component<Props, State> {
*/
onLoadFinished() {
// Only show intro if this is the first time starting the app
this.createDrawerNavigator = () => <DrawerNavigator
this.createDrawerNavigator = () => <MainNavigator
defaultHomeRoute={this.defaultHomeRoute}
defaultHomeData={this.defaultHomeData}
/>;
@ -191,7 +191,7 @@ export default class App extends React.Component<Props, State> {
return (
<PaperProvider theme={this.state.currentTheme}>
<NavigationContainer theme={this.state.currentTheme} ref={this.navigatorRef}>
<DrawerNavigator
<MainNavigator
defaultHomeRoute={this.defaultHomeRoute}
defaultHomeData={this.defaultHomeData}
/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 147 KiB

View file

@ -5,36 +5,48 @@ import {Avatar, Card, List, withTheme} from 'react-native-paper';
import {StyleSheet} from "react-native";
import {DrawerNavigationProp} from "@react-navigation/drawer";
import type {CustomTheme} from "../../managers/ThemeManager";
import i18n from 'i18n-js';
const ICON_AMICALE = require("../../../assets/amicale.png");
type Props = {
navigation: DrawerNavigationProp,
theme: CustomTheme,
isLoggedIn: boolean,
}
class ActionsDashBoardItem extends React.Component<Props> {
shouldComponentUpdate(nextProps: Props): boolean {
return (nextProps.theme.dark !== this.props.theme.dark)
|| (nextProps.isLoggedIn !== this.props.isLoggedIn);
}
render() {
const isLoggedIn = this.props.isLoggedIn;
return (
<Card style={{
...styles.card,
borderColor: this.props.theme.colors.primary,
}}>
<List.Item
title={"AMICALE"}
description={"VOTRE COMPTE"}
title={i18n.t("homeScreen.dashboard.amicaleTitle")}
description={isLoggedIn
? i18n.t("homeScreen.dashboard.amicaleConnected")
: i18n.t("homeScreen.dashboard.amicaleConnect")}
left={props => <Avatar.Image
{...props}
size={40}
source={ICON_AMICALE}
style={styles.avatar}/>}
right={props => <List.Icon {...props} icon="chevron-right"/>}
onPress={() => this.props.navigation.navigate("amicale-home")}
right={props => <List.Icon {...props} icon={isLoggedIn
? "chevron-right"
: "login"}/>}
onPress={isLoggedIn
? () => this.props.navigation.navigate("services", {
screen: 'index'
})
: () => this.props.navigation.navigate("login")}
style={styles.list}
/>
</Card>
@ -51,12 +63,14 @@ const styles = StyleSheet.create({
borderWidth: 1,
},
avatar: {
backgroundColor: 'transparent'
backgroundColor: 'transparent',
marginTop: 'auto',
marginBottom: 'auto',
},
list: {
// height: 50,
paddingTop:0,
paddingBottom:0,
paddingTop: 0,
paddingBottom: 0,
}
});

View file

@ -16,7 +16,6 @@ type Props = {
};
const AnimatableBadge = Animatable.createAnimatableComponent(Badge);
const AnimatableIconButton = Animatable.createAnimatableComponent(IconButton);
/**
* Component used to render a small dashboard item
@ -34,7 +33,7 @@ class SmallDashboardItem extends React.Component<Props> {
const colors = props.theme.colors;
return (
<View>
<AnimatableIconButton
<IconButton
icon={props.icon}
color={
props.isAvailable

View file

@ -0,0 +1,63 @@
// @flow
import * as React from 'react';
import {Animated} from "react-native";
import ImageListItem from "./ImageListItem";
import CardListItem from "./CardListItem";
type Props = {
dataset: Array<cardItem>,
isHorizontal: boolean,
}
export type cardItem = {
title: string,
subtitle: string,
image: string | number,
onPress: () => void,
};
export type cardList = Array<cardItem>;
export default class CardList extends React.Component<Props> {
static defaultProps = {
isHorizontal: false,
}
renderItem = ({item}: { item: cardItem }) => {
if (this.props.isHorizontal)
return <ImageListItem item={item} key={item.title}/>;
else
return <CardListItem item={item} key={item.title}/>;
};
keyExtractor = (item: cardItem) => item.title;
render() {
let containerStyle;
if (this.props.isHorizontal) {
containerStyle = {
...this.props.contentContainerStyle,
height: 150,
justifyContent: 'space-around',
};
} else {
containerStyle = {
...this.props.contentContainerStyle,
}
}
return (
<Animated.FlatList
{...this.props}
data={this.props.dataset}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
numColumns={this.props.isHorizontal ? undefined : 2}
horizontal={this.props.isHorizontal}
contentContainerStyle={containerStyle}
/>
);
}
}

View file

@ -0,0 +1,44 @@
// @flow
import * as React from 'react';
import {Caption, Card, Paragraph} from 'react-native-paper';
import type {cardItem} from "./CardList";
type Props = {
item: cardItem,
}
export default class CardListItem extends React.Component<Props> {
shouldComponentUpdate() {
return false;
}
render() {
const props = this.props;
const item = props.item;
const source = typeof item.image === "number"
? item.image
: {uri: item.image};
return (
<Card
style={{
width: '40%',
margin: 5,
marginLeft: 'auto',
marginRight: 'auto',
}}
onPress={item.onPress}
>
<Card.Cover
style={{height: 80}}
source={source}
/>
<Card.Content>
<Paragraph>{item.title}</Paragraph>
<Caption>{item.subtitle}</Caption>
</Card.Content>
</Card>
);
}
}

View file

@ -0,0 +1,53 @@
// @flow
import * as React from 'react';
import {Text, TouchableRipple} from 'react-native-paper';
import {Image, View} from 'react-native';
import type {cardItem} from "./CardList";
type Props = {
item: cardItem,
}
export default class ImageListItem extends React.Component<Props> {
shouldComponentUpdate() {
return false;
}
render() {
const props = this.props;
const item = props.item;
const source = typeof item.image === "number"
? item.image
: {uri: item.image};
return (
<TouchableRipple
style={{
width: 100,
height: 150,
margin: 5,
}}
onPress={item.onPress}
>
<View>
<Image
style={{
width: 80,
height: 80,
marginLeft: 'auto',
marginRight: 'auto',
}}
source={source}
/>
<Text style={{
marginTop: 5,
marginLeft: 'auto',
marginRight: 'auto',
textAlign: 'center'
}}>{item.title}</Text>
</View>
</TouchableRipple>
);
}
}

View file

@ -6,15 +6,13 @@ import {HeaderButton, HeaderButtons} from 'react-navigation-header-buttons';
import {withTheme} from "react-native-paper";
import * as Touchable from "react-native/Libraries/Components/Touchable/TouchableNativeFeedback.android";
const MaterialHeaderButton = (props: Object) => (
<HeaderButton
const MaterialHeaderButton = (props: Object) => <HeaderButton
IconComponent={MaterialCommunityIcons}
iconSize={26}
color={props.theme.colors.text}
color={props.color != null ? props.color : props.theme.colors.text}
background={Touchable.Ripple(props.theme.colors.ripple, true)}
{...props}
/>
);
/>;
const MaterialHeaderButtons = (props: Object) => {
return (

View file

@ -1,148 +0,0 @@
// @flow
import * as React from 'react';
import {FlatList} from "react-native";
import {Drawer, withTheme} from 'react-native-paper';
import {Linking} from "expo";
import AnimatedAccordion from "../Animations/AnimatedAccordion";
import {StackActions} from '@react-navigation/native';
type Props = {
navigation: Object,
startOpen: boolean,
isLoggedIn: boolean,
sectionName: string,
activeRoute: string,
listKey: string,
listData: Array<Object>,
}
const LIST_ITEM_HEIGHT = 48;
class SideBarSection extends React.PureComponent<Props> {
colors: Object;
accordionRef: {current: null | AnimatedAccordion};
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.accordionRef = React.createRef();
}
/**
* Searches if the current route is contained in the given list data.
* If this is the case and the list is collapsed, we should expand this list.
*
* @return boolean
*/
shouldExpandList() {
for (let i = 0; i < this.props.listData.length; i++) {
if (this.props.listData[i].route === this.props.activeRoute) {
return true;
}
}
return false;
}
/**
* Callback when a drawer item is pressed.
* It will either navigate to the associated screen, or open the browser to the associated link
*
* @param item The item pressed
*/
onListItemPress(item: Object) {
if (item.link !== undefined)
Linking.openURL(item.link);
else if (item.action !== undefined)
item.action();
else if (this.props.activeRoute === "main")
this.props.navigation.navigate(item.route);
else {
this.props.navigation.dispatch(
StackActions.replace(item.route)
);
this.props.navigation.closeDrawer();
}
}
/**
* Key extractor for list items
*
* @param item The item to extract the key from
* @return {string} The extracted key
*/
listKeyExtractor = (item: Object) => item.route;
shouldHideItem(item: Object) {
const onlyWhenLoggedOut = item.onlyWhenLoggedOut !== undefined && item.onlyWhenLoggedOut === true;
const onlyWhenLoggedIn = item.onlyWhenLoggedIn !== undefined && item.onlyWhenLoggedIn === true;
return (onlyWhenLoggedIn && !this.props.isLoggedIn || onlyWhenLoggedOut && this.props.isLoggedIn);
}
/**
* Gets the render item for the given list item
*
* @param item The item to render
* @return {*}
*/
getRenderItem = ({item}: Object) => {
const onListItemPress = this.onListItemPress.bind(this, item);
if (this.shouldHideItem(item))
return null;
return (
<Drawer.Item
label={item.name}
active={this.props.activeRoute === item.route}
icon={item.icon}
onPress={onListItemPress}
style={{
height: LIST_ITEM_HEIGHT,
justifyContent: 'center',
}}
/>
);
};
shouldRenderAccordion() {
let itemsToRender = 0;
for (let i = 0; i < this.props.listData.length; i++) {
if (!this.shouldHideItem(this.props.listData[i]))
itemsToRender += 1;
}
return itemsToRender > 1;
}
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
getFlatList() {
return (
// $FlowFixMe
<FlatList
data={this.props.listData}
extraData={this.props.isLoggedIn.toString() + this.props.activeRoute}
renderItem={this.getRenderItem}
keyExtractor={this.listKeyExtractor}
listKey={this.props.listKey}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
getItemLayout={this.itemLayout}
/>
);
}
render() {
if (this.shouldRenderAccordion()) {
return (
<AnimatedAccordion
title={this.props.sectionName}
keepOpen={this.shouldExpandList()}
>
{this.getFlatList()}
</AnimatedAccordion>
);
} else
return this.getFlatList();
}
}
export default withTheme(SideBarSection);

View file

@ -1,267 +0,0 @@
// @flow
import * as React from 'react';
import {Dimensions, FlatList, Image, StyleSheet, View,} from 'react-native';
import i18n from "i18n-js";
import {TouchableRipple} from "react-native-paper";
import ConnectionManager from "../../managers/ConnectionManager";
import LogoutDialog from "../Amicale/LogoutDialog";
import SideBarSection from "./SideBarSection";
import {DrawerNavigationProp} from "@react-navigation/drawer";
const deviceWidth = Dimensions.get("window").width;
type Props = {
navigation: DrawerNavigationProp,
state: {[key: string] : any},
theme?: Object,
};
type State = {
isLoggedIn: boolean,
dialogVisible: boolean,
};
/**
* Component used to render the drawer menu content
*/
class SideBar extends React.Component<Props, State> {
dataSet: Array<Object>;
activeRoute: string;
/**
* Generate the dataset
*
* @param props
*/
constructor(props: Props) {
super(props);
this.activeRoute = 'main';
// Dataset used to render the drawer
const mainData = [
{
name: i18n.t('screens.home'),
route: "main",
icon: "home",
},
];
const amicaleData = [
{
name: i18n.t('screens.login'),
route: "login",
icon: "login",
onlyWhenLoggedOut: true,
shouldEmphasis: true,
},
{
name: i18n.t('screens.amicaleAbout'),
route: "amicale-contact",
icon: "information",
},
{
name: i18n.t('screens.profile'),
route: "profile",
icon: "account",
onlyWhenLoggedIn: true,
},
{
name: i18n.t('clubs.clubList'),
route: "club-list",
icon: "account-group",
onlyWhenLoggedIn: true,
},
{
name: i18n.t('screens.vote'),
route: "vote",
icon: "vote",
onlyWhenLoggedIn: true,
},
{
name: i18n.t('screens.logout'),
route: 'disconnect',
action: this.showDisconnectDialog,
icon: "logout",
onlyWhenLoggedIn: true,
},
];
const servicesData = [
{
name: i18n.t('screens.menuSelf'),
route: "self-menu",
icon: "silverware-fork-knife",
},
{
name: i18n.t('screens.availableRooms'),
route: "available-rooms",
icon: "calendar-check",
},
{
name: i18n.t('screens.bib'),
route: "bib",
icon: "book",
},
{
name: i18n.t('screens.bluemind'),
route: "bluemind",
link: "https://etud-mel.insa-toulouse.fr/webmail/",
icon: "email",
},
{
name: i18n.t('screens.ent'),
route: "ent",
link: "https://ent.insa-toulouse.fr/",
icon: "notebook",
},
];
const websitesData = [
{
name: "Amicale",
route: "amicale-website",
icon: "alpha-a-box",
},
{
name: "Élus Étudiants",
route: "elus-etudiants",
icon: "alpha-e-box",
},
{
name: "Wiketud",
route: "wiketud",
icon: "wikipedia",
},
{
name: "Tutor'INSA",
route: "tutorinsa",
icon: "school",
},
];
const othersData = [
{
name: i18n.t('screens.settings'),
route: "settings",
icon: "settings",
},
{
name: i18n.t('screens.about'),
route: "about",
icon: "information",
},
];
this.dataSet = [
{
key: '1',
name: i18n.t('screens.home'),
startOpen: true, // App always starts on Main
data: mainData
},
{
key: '2',
name: i18n.t('sidenav.divider4'),
startOpen: false, // TODO set by user preferences
data: amicaleData
},
{
key: '3',
name: i18n.t('sidenav.divider2'),
startOpen: false,
data: servicesData
},
{
key: '4',
name: i18n.t('sidenav.divider1'),
startOpen: false,
data: websitesData
},
{
key: '5',
name: i18n.t('sidenav.divider3'),
startOpen: false,
data: othersData
},
];
ConnectionManager.getInstance().addLoginStateListener(this.onLoginStateChange);
this.state = {
isLoggedIn: ConnectionManager.getInstance().isLoggedIn(),
dialogVisible: false,
};
}
shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
const nextNavigationState = nextProps.state.routes[0].state;
const nextRoute = nextNavigationState.routes[nextNavigationState.index].name;
let currentRoute = "main";
const currentNavigationState = this.props.state.routes[0].state;
if (currentNavigationState != null) {
currentRoute = currentNavigationState.routes[currentNavigationState.index].name;
}
this.activeRoute = nextRoute;
return (nextState !== this.state)
|| (nextRoute !== currentRoute);
}
showDisconnectDialog = () => this.setState({dialogVisible: true});
hideDisconnectDialog = () => this.setState({dialogVisible: false});
onLoginStateChange = (isLoggedIn: boolean) => this.setState({isLoggedIn: isLoggedIn});
/**
* Gets the render item for the given list item
*
* @param item The item to render
* @return {*}
*/
getRenderItem = ({item}: Object) => {
return <SideBarSection
{...this.props}
listKey={item.key}
activeRoute={this.activeRoute}
isLoggedIn={this.state.isLoggedIn}
sectionName={item.name}
startOpen={item.startOpen}
listData={item.data}
/>
};
render() {
return (
<View style={{height: '100%'}}>
<TouchableRipple
onPress={() => this.props.navigation.navigate("tetris")}
>
<Image
source={require("../../../assets/drawer-cover.png")}
style={styles.drawerCover}
/>
</TouchableRipple>
{/*$FlowFixMe*/}
<FlatList
data={this.dataSet}
extraData={this.state.isLoggedIn.toString() + this.activeRoute}
renderItem={this.getRenderItem}
/>
<LogoutDialog
{...this.props}
visible={this.state.dialogVisible}
onDismiss={this.hideDisconnectDialog}
/>
</View>
);
}
}
const styles = StyleSheet.create({
drawerCover: {
height: deviceWidth / 3,
width: 2 * deviceWidth / 3,
position: "relative",
marginBottom: 10,
marginTop: 20
},
});
export default SideBar;

View file

@ -19,8 +19,8 @@ type State = {
const TAB_ICONS = {
proxiwash: 'tshirt-crew',
students: 'account-circle',
insa: 'book',
services: 'account-circle',
planning: 'calendar-range',
planex: 'clock',
};

View file

@ -7,7 +7,8 @@ import AboutDependenciesScreen from '../screens/About/AboutDependenciesScreen';
import DebugScreen from '../screens/About/DebugScreen';
import {createStackNavigator, TransitionPresets} from "@react-navigation/stack";
import i18n from "i18n-js";
import TabNavigator from "./MainTabNavigator";
import TabNavigator from "./TabNavigator";
import TetrisScreen from "../screens/Tetris/TetrisScreen";
const defaultScreenOptions = {
gestureEnabled: true,
@ -59,6 +60,13 @@ function MainStackComponent(props: { createTabNavigator: () => React.Node }) {
title: i18n.t('aboutScreen.debug')
}}
/>
<MainStack.Screen
name="tetris"
component={TetrisScreen}
options={{
title: i18n.t("game.title"),
}}
/>
</MainStack.Navigator>
);
}
@ -68,7 +76,7 @@ type Props = {
defaultHomeData: { [key: string]: any }
}
export default class DrawerNavigator extends React.Component<Props> {
export default class MainNavigator extends React.Component<Props> {
createTabNavigator: () => React.Node;

View file

@ -21,23 +21,23 @@ 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/Other/SelfMenuScreen";
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 TetrisScreen from "../screens/Tetris/TetrisScreen";
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 AmicaleHomeScreen from "../screens/Amicale/AmicaleHomeScreen";
import WebsitesHomeScreen from "../screens/Websites/WebsitesHomeScreen";
import InsaHomeScreen from "../screens/Insa/InsaHomeScreen";
import WebsitesHomeScreen from "../screens/Services/ServicesScreen";
import ServicesSectionScreen from "../screens/Services/ServicesSectionScreen";
const defaultScreenOptions = {
gestureEnabled: true,
@ -77,41 +77,39 @@ function createScreenCollapsibleStack(
)
}
function getWebsiteStack(name: string, Stack: any, component: any, title: string) {
function getWebsiteStack(name: string, Stack: any, component: any, title: string) {
return createScreenCollapsibleStack(name, Stack, component, title, false);
}
const StudentsStack = createStackNavigator();
const ServicesStack = createStackNavigator();
function StudentsStackComponent() {
function ServicesStackComponent() {
return (
<StudentsStack.Navigator
<ServicesStack.Navigator
initialRouteName="index"
headerMode={"screen"}
screenOptions={defaultScreenOptions}
>
<StudentsStack.Screen
name="index"
component={WebsitesHomeScreen}
options={{
title: "WEBSITES HOME",
}}
/>
{getWebsiteStack("amicale-website", StudentsStack, AmicaleWebsiteScreen, "Amicale")}
{getWebsiteStack("elus-etudiants", StudentsStack, ElusEtudiantsWebsiteScreen, "Élus Étudiants")}
{getWebsiteStack("wiketud", StudentsStack, WiketudWebsiteScreen, "Wiketud")}
{getWebsiteStack("tutorinsa", StudentsStack, TutorInsaWebsiteScreen, "Tutor'INSA")}
{createScreenCollapsibleStack("proximo", StudentsStack, ProximoMainScreen, "Proximo")}
{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",
StudentsStack,
ServicesStack,
ProximoListScreen,
i18n.t('screens.proximoArticles'),
true,
{...screenTransition},
)}
<StudentsStack.Screen
<ServicesStack.Screen
name="proximo-about"
component={ProximoAboutScreen}
options={{
@ -119,22 +117,41 @@ function StudentsStackComponent() {
...modalTransition,
}}
/>
<StudentsStack.Screen
name="planning"
component={PlanningScreen}
{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.planning'),
}}
/>
<StudentsStack.Screen
name="planning-information"
component={PlanningDisplayScreen}
options={{
title: i18n.t('screens.planningDisplayScreen'),
title: i18n.t('screens.clubsAbout'),
...modalTransition,
}}
/>
</StudentsStack.Navigator>
<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>
);
}
@ -160,26 +177,31 @@ function ProxiwashStackComponent() {
);
}
const InsaStack = createStackNavigator();
const PlanningStack = createStackNavigator();
function InsaStackComponent() {
function PlanningStackComponent() {
return (
<InsaStack.Navigator
<PlanningStack.Navigator
initialRouteName="index"
headerMode={"screen"}
screenOptions={defaultScreenOptions}
>
<InsaStack.Screen
name="index"
component={InsaHomeScreen}
<PlanningStack.Screen
name="planning"
component={PlanningScreen}
options={{
title: "INSA HOME",
title: i18n.t('screens.planning'),
}}
/>
{getWebsiteStack("available-rooms", InsaStack, AvailableRoomScreen, i18n.t('screens.availableRooms'))}
{getWebsiteStack("bib", InsaStack, BibScreen, i18n.t('screens.bib'))}
{createScreenCollapsibleStack("self-menu", InsaStack, SelfMenuScreen, i18n.t('screens.menuSelf'))}
</InsaStack.Navigator>
<PlanningStack.Screen
name="planning-information"
component={PlanningDisplayScreen}
options={{
title: i18n.t('screens.planningDisplayScreen'),
...modalTransition,
}}
/>
</PlanningStack.Navigator>
);
}
@ -213,14 +235,6 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
useNativeDriver: true,
}
)}
<HomeStack.Screen
name="feed-information"
component={FeedItemScreen}
options={{
title: i18n.t('screens.feedDisplayScreen'),
...modalTransition,
}}
/>
<HomeStack.Screen
name="scanner"
component={ScannerScreen}
@ -229,36 +243,6 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
...modalTransition,
}}
/>
<HomeStack.Screen
name="home-planning-information"
component={PlanningDisplayScreen}
options={{
title: i18n.t('screens.planningDisplayScreen'),
...modalTransition,
}}
/>
<HomeStack.Screen
name="tetris"
component={TetrisScreen}
options={{
title: i18n.t("game.title"),
}}
/>
<HomeStack.Screen
name="login"
component={LoginScreen}
options={{
title: i18n.t('screens.login'),
}}
/>
<HomeStack.Screen
name="profile"
component={ProfileScreen}
options={{
title: i18n.t('screens.profile'),
}}
/>
{createScreenCollapsibleStack("club-list", HomeStack, ClubListScreen, i18n.t('clubs.clubList'))}
<HomeStack.Screen
name="club-information"
component={ClubDisplayScreen}
@ -268,34 +252,23 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
}}
/>
<HomeStack.Screen
name="club-about"
component={ClubAboutScreen}
name="feed-information"
component={FeedItemScreen}
options={{
title: i18n.t('screens.clubsAbout'),
title: i18n.t('screens.feedDisplayScreen'),
...modalTransition,
}}
/>
<HomeStack.Screen
name="vote"
component={VoteScreen}
name="planning-information"
component={PlanningDisplayScreen}
options={{
title: i18n.t('screens.vote'),
}}
/>
<HomeStack.Screen
name="amicale-contact"
component={AmicaleContactScreen}
options={{
title: i18n.t('screens.amicaleAbout'),
}}
/>
<HomeStack.Screen
name="amicale-home"
component={AmicaleHomeScreen}
options={{
title: "AMICALE HOME",
title: i18n.t('screens.planningDisplayScreen'),
...modalTransition,
}}
/>
{createScreenCollapsibleStack("self-menu", HomeStack, SelfMenuScreen, i18n.t('screens.menuSelf'), true, {...modalTransition})}
{createScreenCollapsibleStack("login", HomeStack, LoginScreen, i18n.t('screens.login'))}
</HomeStack.Navigator>
);
}
@ -348,27 +321,26 @@ export default class TabNavigator extends React.Component<Props> {
initialRouteName={this.defaultRoute}
tabBar={props => <CustomTabBar {...props} />}
>
<Tab.Screen
name="services"
option
component={ServicesStackComponent}
options={{title: i18n.t('screens.services')}}
/>
<Tab.Screen
name="proxiwash"
component={ProxiwashStackComponent}
options={{title: i18n.t('screens.proxiwash')}}
/>
<Tab.Screen
name="students"
option
component={StudentsStackComponent}
options={{title: "ETUDIANTS"}}
/>
<Tab.Screen
name="home"
component={this.createHomeStackComponent}
options={{title: i18n.t('screens.home')}}
/>
<Tab.Screen
name="insa"
component={InsaStackComponent}
options={{title: "INSA"}}
name="planning"
component={PlanningStackComponent}
options={{title: i18n.t('screens.planning')}}
/>
<Tab.Screen

View file

@ -1,16 +1,21 @@
// @flow
import * as React from 'react';
import {KeyboardAvoidingView, ScrollView, StyleSheet, View} from "react-native";
import {Animated, KeyboardAvoidingView, StyleSheet, View} from "react-native";
import {Avatar, Button, Card, HelperText, Paragraph, TextInput, withTheme} from 'react-native-paper';
import ConnectionManager from "../../managers/ConnectionManager";
import i18n from 'i18n-js';
import ErrorDialog from "../../components/Dialogs/ErrorDialog";
import {CommonActions} from "@react-navigation/native";
import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import type {CustomTheme} from "../../managers/ThemeManager";
type Props = {
navigation: Object,
route: Object,
collapsibleStack: Collapsible,
theme: CustomTheme
}
type State = {
@ -41,32 +46,17 @@ class LoginScreen extends React.Component<Props, State> {
dialogError: 0,
};
colors: Object;
onEmailChange: Function;
onPasswordChange: Function;
passwordInputRef: Object;
nextScreen: string;
constructor(props) {
super(props);
this.onEmailChange = this.onInputChange.bind(this, true);
this.onPasswordChange = this.onInputChange.bind(this, false);
this.colors = props.theme.colors;
this.props.navigation.addListener('focus', this.onScreenFocus);
}
onScreenFocus = () => {
if (this.props.route.params !== undefined && this.props.route.params.nextScreen !== undefined) {
this.nextScreen = this.props.route.params.nextScreen;
this.props.navigation.dispatch(CommonActions.setParams({nextScreen: 'profile'}));
} else
this.nextScreen = 'profile';
};
showErrorDialog = (error: number) =>
this.setState({
dialogVisible: true,
@ -75,7 +65,7 @@ class LoginScreen extends React.Component<Props, State> {
hideErrorDialog = () => this.setState({dialogVisible: false});
handleSuccess = () => this.props.navigation.replace(this.nextScreen);
handleSuccess = () => this.props.navigation.goBack();
onResetPasswordClick = () => this.props.navigation.navigate('amicale-website', {
screen: 'amicale-website',
@ -235,7 +225,7 @@ class LoginScreen extends React.Component<Props, State> {
left={(props) => <Avatar.Icon
{...props}
icon={"help"}
color={this.colors.primary}
color={this.props.theme.colors.primary}
style={{backgroundColor: 'transparent'}}/>}
/>
<Card.Content>
@ -248,6 +238,7 @@ class LoginScreen extends React.Component<Props, State> {
}
render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return (
<KeyboardAvoidingView
behavior={"height"}
@ -256,7 +247,14 @@ class LoginScreen extends React.Component<Props, State> {
enabled
keyboardVerticalOffset={100}
>
<ScrollView>
<Animated.ScrollView
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
>
<View>
{this.getMainCard()}
{this.getSecondaryCard()}
@ -266,7 +264,7 @@ class LoginScreen extends React.Component<Props, State> {
onDismiss={this.hideErrorDialog}
errorCode={this.state.dialogError}
/>
</ScrollView>
</Animated.ScrollView>
</KeyboardAvoidingView>
);
}
@ -292,4 +290,4 @@ const styles = StyleSheet.create({
}
});
export default withTheme(LoginScreen);
export default withCollapsible(withTheme(LoginScreen));

View file

@ -1,16 +1,20 @@
// @flow
import * as React from 'react';
import {FlatList, StyleSheet, View} from "react-native";
import {Animated, FlatList, StyleSheet, View} from "react-native";
import {Avatar, Button, Card, Divider, List, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
import i18n from 'i18n-js';
import LogoutDialog from "../../components/Amicale/LogoutDialog";
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {Collapsible} from "react-navigation-collapsible";
import {withCollapsible} from "../../utils/withCollapsible";
type Props = {
navigation: Object,
theme: Object,
collapsibleStack: Collapsible,
}
type State = {
@ -52,12 +56,20 @@ class ProfileScreen extends React.Component<Props, State> {
getScreen = (data: Object) => {
this.data = data[0];
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return (
<View>
{/*$FlowFixMe*/}
<FlatList
<View style={{flex: 1}}>
<Animated.FlatList
renderItem={this.getRenderItem}
data={this.flatListData}
// Animations
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT,
minHeight: '100%'
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/>
<LogoutDialog
{...this.props}
@ -323,4 +335,4 @@ const styles = StyleSheet.create({
});
export default withTheme(ProfileScreen);
export default withCollapsible(withTheme(ProfileScreen));

View file

@ -17,6 +17,9 @@ import AnimatedFAB from "../../components/Animations/AnimatedFAB";
import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../managers/ThemeManager";
import {View} from "react-native-animatable";
import {HiddenItem} from "react-navigation-header-buttons";
import ConnectionManager from "../../managers/ConnectionManager";
import LogoutDialog from "../../components/Amicale/LogoutDialog";
// import DATA from "../dashboard_data.json";
@ -95,21 +98,32 @@ type Props = {
theme: CustomTheme,
}
type State = {
dialogVisible: boolean,
}
/**
* Class defining the app's home screen
*/
class HomeScreen extends React.Component<Props> {
class HomeScreen extends React.Component<Props, State> {
colors: Object;
isLoggedIn: boolean | null;
fabRef: { current: null | AnimatedFAB };
currentNewFeed: Array<feedItem>;
state = {
dialogVisible: false,
}
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.fabRef = React.createRef();
this.currentNewFeed = [];
this.isLoggedIn = null;
}
/**
@ -130,9 +144,12 @@ class HomeScreen extends React.Component<Props> {
}
onScreenFocus = () => {
this.props.navigation.setOptions({
headerRight: this.getHeaderButton,
});
if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) {
this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn();
this.props.navigation.setOptions({
headerRight: this.getHeaderButton,
});
}
// handle link open when home is not focused or created
this.handleNavigationParams();
};
@ -148,25 +165,43 @@ class HomeScreen extends React.Component<Props> {
};
getHeaderButton = () => {
let onPressLog = () => this.props.navigation.navigate("login");
let logIcon = "login";
let logColor = this.props.theme.colors.primary;
if (this.isLoggedIn) {
onPressLog = () => this.showDisconnectDialog();
logIcon = "logout";
logColor = this.props.theme.colors.text;
}
const onPressSettings = () => this.props.navigation.navigate("settings");
const onPressAbout = () => this.props.navigation.navigate("about");
return <MaterialHeaderButtons>
<Item title="settings" iconName={"settings"} onPress={onPressSettings}/>
<Item title="information" iconName={"information"} onPress={onPressAbout}/>
<Item title="log" iconName={logIcon} color={logColor} onPress={onPressLog}/>
<HiddenItem title={i18n.t("screens.settings")} iconName={"settings"} onPress={onPressSettings}/>
<HiddenItem title={i18n.t("screens.about")} iconName={"information"} onPress={onPressAbout}/>
</MaterialHeaderButtons>;
};
showDisconnectDialog = () => this.setState({dialogVisible: true});
hideDisconnectDialog = () => this.setState({dialogVisible: false});
onProxiwashClick = () => {
this.props.navigation.navigate("proxiwash");
};
onProximoClick = () => {
this.props.navigation.navigate("proximo");
this.props.navigation.navigate('services', {screen: "index"});
};
onTutorInsaClick = () => this.props.navigation.navigate('tutorinsa');
onTutorInsaClick = () => {
this.props.navigation.navigate('services', {screen: "index"});
};
onMenuClick = () => this.props.navigation.navigate('self-menu');
onMenuClick = () => {
this.props.navigation.navigate('self-menu');
};
/**
* Creates the dataset to be used in the FlatList
@ -244,7 +279,7 @@ class HomeScreen extends React.Component<Props> {
},
{
id: 'today_menu',
data: dashboardData == null ? 0 : dashboardData.today_menu,
data: dashboardData == null ? [] : dashboardData.today_menu,
icon: 'silverware-fork-knife',
color: this.colors.menuColor,
onPress: this.onMenuClick,
@ -277,7 +312,7 @@ class HomeScreen extends React.Component<Props> {
}
getDashboardActions() {
return <ActionsDashBoardItem {...this.props}/>;
return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>;
}
/**
@ -407,8 +442,11 @@ class HomeScreen extends React.Component<Props> {
getDashboardEvent(content: Array<event>) {
let futureEvents = this.getFutureEvents(content);
let displayEvent = this.getDisplayEvent(futureEvents);
const clickPreviewAction = () =>
this.props.navigation.navigate('home-planning-information', {data: displayEvent});
// const clickPreviewAction = () =>
// this.props.navigation.navigate('students', {
// screen: 'planning-information',
// params: {data: displayEvent}
// });
return (
<DashboardItem
eventNumber={futureEvents.length}
@ -416,7 +454,7 @@ class HomeScreen extends React.Component<Props> {
>
<PreviewEventDashboardItem
event={displayEvent != null ? displayEvent : undefined}
clickAction={clickPreviewAction}
clickAction={this.onEventContainerClick}
/>
</DashboardItem>
);
@ -522,6 +560,11 @@ class HomeScreen extends React.Component<Props> {
icon="qrcode-scan"
onPress={this.openScanner}
/>
<LogoutDialog
{...this.props}
visible={this.state.dialogVisible}
onDismiss={this.hideDisconnectDialog}
/>
</View>
);
}

View file

@ -1,85 +0,0 @@
// @flow
import * as React from 'react';
import {ScrollView, StyleSheet} from "react-native";
import {Button, withTheme} from 'react-native-paper';
type Props = {
navigation: Object,
route: Object,
}
type State = {}
class InsaHomeScreen extends React.Component<Props, State> {
state = {};
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
render() {
const nav = this.props.navigation;
return (
<ScrollView>
<Button
icon={"information"}
onPress={() => nav.navigate("self-menu")}
>
RU
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("available-rooms")}
>
AVAILABLE ROOMS
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("bib")}
>
BIB
</Button>
<Button// TODO create webview
icon={"information"}
onPress={() => nav.navigate("self-menu")}
>
EMAIL
</Button>
<Button// TODO create webview
icon={"information"}
onPress={() => nav.navigate("self-menu")}
>
ENT
</Button>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
},
card: {
margin: 10,
},
header: {
fontSize: 36,
marginBottom: 48
},
textInput: {},
btnContainer: {
marginTop: 5,
marginBottom: 10,
}
});
export default withTheme(InsaHomeScreen);

View file

@ -10,6 +10,8 @@ type Props = {
navigation: Object,
};
const LOGO = "https://etud.insa-toulouse.fr/~amicale_app/images/proximo-logo.png";
/**
* Class defining the proximo about screen.
*/
@ -27,9 +29,8 @@ export default class ProximoAboutScreen extends React.Component<Props> {
alignItems: 'center'
}}>
<Image
source={require('../../../assets/proximo-logo.png')}
style={{flex: 1, resizeMode: "contain"}}
resizeMode="contain"/>
source={{uri: LOGO}}
style={{height: '100%', width: '100%', resizeMode: "contain"}}/>
</View>
<Text>{i18n.t('proximoScreen.description')}</Text>
<Card style={{margin: 5}}>

View file

@ -10,6 +10,8 @@ type Props = {
navigation: Object,
};
const LOGO = "https://etud.insa-toulouse.fr/~amicale_app/images/proxiwash-logo.png";
/**
* Class defining the proxiwash about screen.
*/
@ -27,9 +29,8 @@ export default class ProxiwashAboutScreen extends React.Component<Props> {
alignItems: 'center'
}}>
<Image
source={require('../../../assets/proxiwash-logo.png')}
style={{flex: 1, resizeMode: "contain"}}
resizeMode="contain"/>
source={{uri: LOGO}}
style={{height: '100%', width: '100%', resizeMode: "contain"}}/>
</View>
<Text>{i18n.t('proxiwashScreen.description')}</Text>
<Card style={{margin: 5}}>

View file

@ -0,0 +1,291 @@
// @flow
import * as React from 'react';
import type {cardList} from "../../components/Lists/CardList/CardList";
import CardList from "../../components/Lists/CardList/CardList";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import {CommonActions} from "@react-navigation/native";
import {Animated, View} from "react-native";
import {Avatar, Button, Card, Divider, List, Title, TouchableRipple, withTheme} from "react-native-paper";
import type {CustomTheme} from "../../managers/ThemeManager";
import ConnectionManager from "../../managers/ConnectionManager";
import i18n from 'i18n-js';
type Props = {
navigation: Object,
route: Object,
collapsibleStack: Collapsible,
theme: CustomTheme,
}
const BIB_IMAGE = "https://scontent-cdg2-1.xx.fbcdn.net/v/t1.0-9/50695561_2124263197597162_2325349608210825216_n.jpg?_nc_cat=109&_nc_sid=8bfeb9&_nc_ohc=tmcV6FWO7_kAX9vfWHU&_nc_ht=scontent-cdg2-1.xx&oh=3b81c76e46b49f7c3a033ea3b07ec212&oe=5EC59B4D";
const RU_IMAGE = "https://scontent-cdg2-1.xx.fbcdn.net/v/t1.0-9/47123773_2041883702501779_5289372776166064128_o.jpg?_nc_cat=100&_nc_sid=cdbe9c&_nc_ohc=dpuBGlIIy_EAX8CyC0l&_nc_ht=scontent-cdg2-1.xx&oh=5c5bb4f0c7f12b554246f7c9b620a5f3&oe=5EC4DB31";
const ROOM_IMAGE = "https://scontent-cdt1-1.xx.fbcdn.net/v/t1.0-9/47041013_2043521689004647_316124496522117120_n.jpg?_nc_cat=103&_nc_sid=8bfeb9&_nc_ohc=bIp8OVJvvSEAX8mKnDZ&_nc_ht=scontent-cdt1-1.xx&oh=b4fef72a645804a849ad30e9e20fca12&oe=5EC29309";
const EMAIL_IMAGE = "https://etud-mel.insa-toulouse.fr/webmail/images/logo-bluemind.png";
const ENT_IMAGE = "https://ent.insa-toulouse.fr/media/org/jasig/portal/layout/tab-column/xhtml-theme/insa/institutional/LogoInsa.png";
const PROXIMO_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/proximo-logo.png"
const WIKETUD_LINK = "https://wiki.etud.insa-toulouse.fr/resources/assets/wiketud.png?ff051";
const AMICALE_IMAGE = require("../../../assets/amicale.png");
const EE_IMAGE = "https://etud.insa-toulouse.fr/~eeinsat/wp-content/uploads/2019/09/logo-blanc.png";
const TUTORINSA_IMAGE = "https://www.etud.insa-toulouse.fr/~tutorinsa/public/images/logo-gray.png";
export type listItem = {
title: string,
description: string,
image: string | number,
shouldLogin: boolean,
content: cardList,
}
type State = {
isLoggedIn: boolean,
}
class ServicesScreen extends React.Component<Props, State> {
amicaleDataset: cardList;
studentsDataset: cardList;
insaDataset: cardList;
finalDataset: Array<listItem>
constructor(props) {
super(props);
const nav = props.navigation;
this.amicaleDataset = [
{
title: i18n.t('screens.clubsAbout'),
subtitle: "CLUB LIST",
image: AMICALE_IMAGE,
onPress: () => nav.navigate("club-list"),
},
{
title: i18n.t('screens.profile'),
subtitle: "PROFIL",
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",
image: AMICALE_IMAGE,
onPress: () => nav.navigate("vote"),
},
];
this.studentsDataset = [
{
title: i18n.t('screens.proximo'),
subtitle: "proximo",
image: PROXIMO_IMAGE,
onPress: () => nav.navigate("proximo"),
},
{
title: i18n.t('screens.amicaleWebsite'),
subtitle: "AMICALE",
image: AMICALE_IMAGE,
onPress: () => nav.navigate("amicale-website"),
},
{
title: "Wiketud",
subtitle: "wiketud",
image: WIKETUD_LINK,
onPress: () => nav.navigate("wiketud"),
},
{
title: "Élus Étudiants",
subtitle: "ELUS ETUDIANTS",
image: EE_IMAGE,
onPress: () => nav.navigate("elus-etudiants"),
},
{
title: "Tutor'INSA",
subtitle: "TUTOR INSA",
image: TUTORINSA_IMAGE,
onPress: () => nav.navigate("tutorinsa"),
},
];
this.insaDataset = [
{
title: i18n.t('screens.menuSelf'),
subtitle: "the ru",
image: RU_IMAGE,
onPress: () => nav.navigate("self-menu"),
},
{
title: i18n.t('screens.availableRooms'),
subtitle: "ROOMS",
image: ROOM_IMAGE,
onPress: () => nav.navigate("available-rooms"),
},
{
title: i18n.t('screens.bib'),
subtitle: "BIB",
image: BIB_IMAGE,
onPress: () => nav.navigate("bib"),
},
{
title: i18n.t('screens.bluemind'),
subtitle: "EMAIL",
image: EMAIL_IMAGE,
onPress: () => nav.navigate("bluemind"),
},
{
title: i18n.t('screens.ent'),
subtitle: "ENT",
image: ENT_IMAGE,
onPress: () => nav.navigate("ent"),
},
];
this.finalDataset = [
{
title: i18n.t("servicesScreen.amicale"),
description: "LOGIN",
image: AMICALE_IMAGE,
shouldLogin: true,
content: this.amicaleDataset
},
{
title: i18n.t("servicesScreen.students"),
description: "SERVICES OFFERED BY STUDENTS",
image: 'account-group',
shouldLogin: false,
content: this.studentsDataset
},
{
title: i18n.t("servicesScreen.insa"),
description: "SERVICES OFFERED BY INSA",
image: 'school',
shouldLogin: false,
content: this.insaDataset
},
];
this.state = {
isLoggedIn: ConnectionManager.getInstance().isLoggedIn()
}
}
componentDidMount() {
this.props.navigation.addListener('focus', this.onFocus);
}
onFocus = () => {
this.handleNavigationParams();
this.setState({isLoggedIn: ConnectionManager.getInstance().isLoggedIn()})
}
handleNavigationParams() {
if (this.props.route.params != null) {
if (this.props.route.params.nextScreen != null) {
this.props.navigation.navigate(this.props.route.params.nextScreen);
// reset params to prevent infinite loop
this.props.navigation.dispatch(CommonActions.setParams({nextScreen: null}));
}
}
};
getAvatar(props, source: string | number) {
if (typeof source === "number")
return <Avatar.Image
{...props}
size={48}
source={AMICALE_IMAGE}
style={{backgroundColor: 'transparent'}}
/>
else
return <Avatar.Icon
{...props}
size={48}
icon={source}
color={this.props.theme.colors.primary}
style={{backgroundColor: 'transparent'}}
/>
}
getLoginMessage() {
return (
<View>
<Title style={{
marginLeft: 'auto',
marginRight: 'auto',
}}>
{i18n.t("servicesScreen.notLoggedIn")}
</Title>
<Button
icon="login"
mode="contained"
onPress={() => this.props.navigation.navigate("login")}
style={{
marginLeft: 'auto',
marginRight: 'auto',
}}>
{i18n.t("screens.login")}
</Button>
</View>
)
}
renderItem = ({item}: { item: listItem }) => {
const shouldShowLogin = !this.state.isLoggedIn && item.shouldLogin;
return (
<TouchableRipple
style={{
margin: 5,
marginBottom: 20,
}}
onPress={shouldShowLogin
? undefined
: () => this.props.navigation.navigate("services-section", {data: item})}
>
<View>
<Card.Title
title={item.title}
left={(props) => this.getAvatar(props, item.image)}
right={shouldShowLogin
? undefined
: (props) => <List.Icon {...props} icon="chevron-right"/>}
/>
{
shouldShowLogin
? this.getLoginMessage()
: <CardList
dataset={item.content}
isHorizontal={true}
/>
}
</View>
</TouchableRipple>
);
};
keyExtractor = (item: listItem) => {
return item.title;
}
render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return <Animated.FlatList
data={this.finalDataset}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
ItemSeparatorComponent={() => <Divider/>}
/>
}
}
export default withCollapsible(withTheme(ServicesScreen));

View file

@ -0,0 +1,76 @@
// @flow
import * as React from 'react';
import CardList from "../../components/Lists/CardList/CardList";
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {withCollapsible} from "../../utils/withCollapsible";
import {Collapsible} from "react-navigation-collapsible";
import {CommonActions} from "@react-navigation/native";
import ConnectionManager from "../../managers/ConnectionManager";
import type {listItem} from "./ServicesScreen";
import ErrorView from "../../components/Screens/ErrorView";
import {ERROR_TYPE} from "../../utils/WebData";
type Props = {
navigation: Object,
route: Object,
collapsibleStack: Collapsible,
}
type State = {
isLoggedIn: boolean,
}
class ServicesSectionScreen extends React.Component<Props, State> {
finalDataset: listItem;
constructor(props) {
super(props);
this.handleNavigationParams();
this.state = {
isLoggedIn: ConnectionManager.getInstance().isLoggedIn(),
}
}
componentDidMount() {
this.props.navigation.addListener('focus', this.onFocus);
}
onFocus = () => {
this.setState({isLoggedIn: ConnectionManager.getInstance().isLoggedIn()})
}
handleNavigationParams() {
if (this.props.route.params != null) {
if (this.props.route.params.data != null) {
this.finalDataset = this.props.route.params.data;
// reset params to prevent infinite loop
this.props.navigation.dispatch(CommonActions.setParams({data: null}));
this.props.navigation.setOptions({
headerTitle: this.finalDataset.title,
});
}
}
}
render() {
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
if (!this.state.isLoggedIn && this.finalDataset.shouldLogin)
return <ErrorView {...this.props} errorCode={ERROR_TYPE.BAD_TOKEN}/>;
else
return <CardList
dataset={this.finalDataset.content}
isHorizontal={false}
onScroll={onScroll}
contentContainerStyle={{
paddingTop: containerPaddingTop,
paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
}}
scrollIndicatorInsets={{top: scrollIndicatorInsetTop}}
/>
}
}
export default withCollapsible(ServicesSectionScreen);

View file

@ -0,0 +1,18 @@
// @flow
import * as React from 'react';
import WebViewScreen from "../../components/Screens/WebViewScreen";
const URL = 'https://etud-mel.insa-toulouse.fr/webmail/';
/**
* Class defining the app's available rooms screen.
* This screen uses a webview to render the page
*/
export const BlueMindWebsiteScreen = (props: Object) => {
return (
<WebViewScreen
{...props}
url={URL}/>
);
};

View file

@ -0,0 +1,18 @@
// @flow
import * as React from 'react';
import WebViewScreen from "../../components/Screens/WebViewScreen";
const URL = 'https://ent.insa-toulouse.fr/';
/**
* Class defining the app's available rooms screen.
* This screen uses a webview to render the page
*/
export const ENTWebsiteScreen = (props: Object) => {
return (
<WebViewScreen
{...props}
url={URL}/>
);
};

View file

@ -1,91 +0,0 @@
// @flow
import * as React from 'react';
import {ScrollView, StyleSheet} from "react-native";
import {Button, withTheme} from 'react-native-paper';
type Props = {
navigation: Object,
route: Object,
}
type State = {}
class WebsitesHomeScreen extends React.Component<Props, State> {
state = {};
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
render() {
const nav = this.props.navigation;
return (
<ScrollView>
<Button
icon={"information"}
onPress={() => nav.navigate("amicale-website")}
>
AMICALE
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("elus-etudiants")}
>
ELUS ETUDIANTS
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("tutorinsa")}
>
TUTOR INSA
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("wiketud")}
>
WIKETUD
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("proximo")}
>
PROXIMO
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("planning")}
>
PLANNING
</Button>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
},
card: {
margin: 10,
},
header: {
fontSize: 36,
marginBottom: 48
},
textInput: {},
btnContainer: {
marginTop: 5,
marginBottom: 10,
}
});
export default withTheme(WebsitesHomeScreen);

View file

@ -1,13 +1,15 @@
{
"screens": {
"home": "Home",
"planning": "Planning",
"planning": "Events",
"planningDisplayScreen": "Event details",
"clubDisplayScreen": "Club details",
"feedDisplayScreen": "Details",
"clubsAbout": "Clubs",
"amicaleAbout": "The Amicale",
"amicaleAbout": "Contact",
"amicaleWebsite": "Amicale's website",
"proxiwash": "Proxiwash",
"services": "Services",
"proximo": "Proximo",
"proximoArticles": "Articles",
"menuSelf": "RU Menu",
@ -95,28 +97,11 @@
"todayEventsSubtitleNA": "No events today",
"todayEventsSubtitle": " event coming today",
"todayEventsSubtitlePlural": " events coming today",
"proximoTitle": "Proximo",
"proximoSubtitleNA": "No articles available",
"proximoSubtitle": " article available",
"proximoSubtitlePlural": " articles available",
"tutorinsaSubtitleNA": "No tutorial available",
"tutorinsaSubtitle": " tutorial available",
"tutorinsaSubtitlePlural": " tutorials available",
"proxiwashTitle": "Available machines",
"proxiwashSubtitleNA": "No machines available",
"proxiwashSubtitle1": " dryer",
"proxiwashSubtitle1Plural": " dryers",
"proxiwashSubtitle2": " washer",
"proxiwashSubtitle2Plural": " washers",
"menuTitle": "Today's menu",
"menuSubtitleNA": "No menu available",
"menuSubtitle": "Click here to see the menu"
"amicaleTitle": "The Amicale",
"amicaleConnect": "Login",
"amicaleConnected": "See available services"
}
},
"planningScreen": {
"wipTitle": "WORK IN PROGRESS",
"wipSubtitle": "Soon, every event at the INSA Toulouse in one place !"
},
"aboutScreen": {
"appstore": "See on the Appstore",
"playstore": "See on the Playstore",
@ -293,7 +278,7 @@
"animation": "Animation",
"clubs": "Clubs",
"event": "Events",
"tech" : "Technique",
"tech": "Technique",
"communication": "Communication",
"intraSchools": "Alumni / IAT",
"publicRelations": "Public Relations"
@ -311,28 +296,28 @@
"tease": {
"title": "Elections incoming",
"subtitle": "Be ready to vote!",
"message" : "Vote start:"
"message": "Vote start:"
},
"wait": {
"titleSubmitted": "Vote submitted!",
"titleEnded": "Votes closed",
"subtitle" : "Waiting for results...",
"messageSubmitted" : "Vote submitted successfully.",
"messageVoted" : "Thank you for your participation.",
"messageDate" : "Results available:",
"messageDateUndefined" : "Results will be available shortly"
"subtitle": "Waiting for results...",
"messageSubmitted": "Vote submitted successfully.",
"messageVoted": "Thank you for your participation.",
"messageDate": "Results available:",
"messageDateUndefined": "Results will be available shortly"
},
"results": {
"title": "Results",
"subtitle": "Available until:",
"totalVotes" : "Total votes:",
"votes" : "votes"
"totalVotes": "Total votes:",
"votes": "votes"
},
"title": {
"title": "The Elections",
"subtitle": "Why your vote is important",
"paragraph1" : "The Amicale's elections is the right moment for you to choose the next team, which will handle different projects on the campus, help organizing your favorite events, animate the campus life during the whole year, and relay your ideas to the administration, so that your campus life is the most enjoyable possible!\nYour turn to make a change!\uD83D\uDE09",
"paragraph2" : "Note: If there is only one list, it is still important to vote to show your support, so that the administration knows the current list is supported by students. It is always a plus when taking difficult decisions! \uD83D\uDE09"
"paragraph1": "The Amicale's elections is the right moment for you to choose the next team, which will handle different projects on the campus, help organizing your favorite events, animate the campus life during the whole year, and relay your ideas to the administration, so that your campus life is the most enjoyable possible!\nYour turn to make a change!\uD83D\uDE09",
"paragraph2": "Note: If there is only one list, it is still important to vote to show your support, so that the administration knows the current list is supported by students. It is always a plus when taking difficult decisions! \uD83D\uDE09"
}
},
"dialog": {
@ -397,5 +382,11 @@
"time": "Time: ",
"exit": "leave Game"
}
},
"servicesScreen": {
"amicale": "The Amicale",
"students": "Student services",
"insa": "INSA services",
"notLoggedIn": "Not logged in"
}
}

View file

@ -1,13 +1,15 @@
{
"screens": {
"home": "Accueil",
"planning": "Planning",
"planning": "Événements",
"planningDisplayScreen": "Détails",
"clubDisplayScreen": "Détails",
"feedDisplayScreen": "Détails",
"clubsAbout": "Les Clubs",
"amicaleAbout": "L' Amicale",
"amicaleAbout": "Contact",
"amicaleWebsite": "Site de l'Amicale",
"proxiwash": "Proxiwash",
"services": "Services",
"proximo": "Proximo",
"proximoArticles": "Articles",
"menuSelf": "Menu du RU",
@ -95,28 +97,11 @@
"todayEventsSubtitleNA": "Pas d'événement",
"todayEventsSubtitle": " événement aujourd'hui",
"todayEventsSubtitlePlural": " événements aujourd'hui",
"proximoTitle": "Proximo",
"proximoSubtitleNA": "pas d'article en vente",
"proximoSubtitle": " article disponible",
"proximoSubtitlePlural": " articles disponibles",
"tutorinsaSubtitleNA": "Aucun tutorat disponible",
"tutorinsaSubtitle": " tutorat disponible",
"tutorinsaSubtitlePlural": " tutorats disponibles",
"proxiwashTitle": "Machines disponibles",
"proxiwashSubtitleNA": "Pas de machine disponible",
"proxiwashSubtitle1": " sèche-linge",
"proxiwashSubtitle1Plural": " sèche-linges",
"proxiwashSubtitle2": " lave-linge",
"proxiwashSubtitle2Plural": " lave-linges",
"menuTitle": "Menu d'aujourd'hui",
"menuSubtitleNA": "Pas de menu disponible",
"menuSubtitle": "Cliquez ici pour voir le menu"
"amicaleTitle": "L'Amicale",
"amicaleConnect": "Se connecter",
"amicaleConnected": "Voir les services disponibles"
}
},
"planningScreen": {
"wipTitle": "WORK IN PROGRESS",
"wipSubtitle": "Bientôt, tous les évènements de l'INSA Toulouse en un seul endroit !"
},
"aboutScreen": {
"appstore": "Voir sur l'Appstore",
"playstore": "Voir sur le Playstore",
@ -293,7 +278,7 @@
"animation": "Animation",
"clubs": "Clubs",
"event": "Événements",
"tech" : "Technique",
"tech": "Technique",
"communication": "Communication",
"intraSchools": "Alumni / IAT",
"publicRelations": "Relations Publiques"
@ -311,28 +296,28 @@
"tease": {
"title": "Les élections arrivent",
"subtitle": "Préparez vous à voter !",
"message" : "Début des votes :"
"message": "Début des votes :"
},
"wait": {
"titleSubmitted": "Vote envoyé !",
"titleEnded": "Votes fermés",
"subtitle" : "Attente des résultats...",
"messageSubmitted" : "Votre vote a bien été envoyé.",
"messageVoted" : "Merci pour votre participation.",
"messageDate" : "Disponibilité des résultats :",
"messageDateUndefined" : "les résultats seront disponibles sous peu."
"subtitle": "Attente des résultats...",
"messageSubmitted": "Votre vote a bien été envoyé.",
"messageVoted": "Merci pour votre participation.",
"messageDate": "Disponibilité des résultats :",
"messageDateUndefined": "les résultats seront disponibles sous peu."
},
"results": {
"title": "Résultats",
"subtitle": "Disponibles jusqu'à :",
"totalVotes" : "Nombre total de votes :",
"votes" : "votes"
"totalVotes": "Nombre total de votes :",
"votes": "votes"
},
"title": {
"title": "Les Élections",
"subtitle": "Pourquoi votre vote est important",
"paragraph1" : "Les élections de l'amicale, c'est le moment pour vous de choisir la prochaine équipe qui portera les différents projets du campus, qui soutiendra les organisations de vos événements favoris, qui vous proposera des animations tout au long de l'année, et qui poussera vos idées à ladministration pour que la vie de campus soit des plus riches !\nAlors à vous de jouer ! \uD83D\uDE09",
"paragraph2" : "NB : Si par cas il n'y a qu'une liste qui se présente, il est important que tout le monde vote, afin qui la liste puisse montrer à ladministration que les INSAiens la soutiennent ! Ça compte toujours pour les décisions difficiles ! \uD83D\uDE09"
"paragraph1": "Les élections de l'amicale, c'est le moment pour vous de choisir la prochaine équipe qui portera les différents projets du campus, qui soutiendra les organisations de vos événements favoris, qui vous proposera des animations tout au long de l'année, et qui poussera vos idées à ladministration pour que la vie de campus soit des plus riches !\nAlors à vous de jouer ! \uD83D\uDE09",
"paragraph2": "NB : Si par cas il n'y a qu'une liste qui se présente, il est important que tout le monde vote, afin qui la liste puisse montrer à ladministration que les INSAiens la soutiennent ! Ça compte toujours pour les décisions difficiles ! \uD83D\uDE09"
}
},
"dialog": {
@ -397,5 +382,11 @@
"time": "Temps: ",
"exit": "Quitter"
}
},
"servicesScreen": {
"amicale": "L'Amicale",
"students": "Services étudiants",
"insa": "Services de l'INSA",
"notLoggedIn": "Non connecté"
}
}