Merge branch 'dev'

This commit is contained in:
keplyx 2020-02-05 10:54:52 +01:00
commit 08f98caf30
19 changed files with 364 additions and 313 deletions

11
App.js
View file

@ -1,10 +1,9 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {StatusBar, Platform} from 'react-native'; import {Platform, StatusBar} from 'react-native';
import {Root, StyleProvider} from 'native-base'; import {Root, StyleProvider} from 'native-base';
import {createAppContainerWithInitialRoute} from './navigation/AppNavigator'; import {createAppContainerWithInitialRoute} from './navigation/AppNavigator';
import ThemeManager from './utils/ThemeManager';
import LocaleManager from './utils/LocaleManager'; import LocaleManager from './utils/LocaleManager';
import * as Font from 'expo-font'; import * as Font from 'expo-font';
import {clearThemeCache} from 'native-base-shoutem-theme'; import {clearThemeCache} from 'native-base-shoutem-theme';
@ -12,6 +11,7 @@ import AsyncStorageManager from "./utils/AsyncStorageManager";
import CustomIntroSlider from "./components/CustomIntroSlider"; import CustomIntroSlider from "./components/CustomIntroSlider";
import {AppLoading} from 'expo'; import {AppLoading} from 'expo';
import NotificationsManager from "./utils/NotificationsManager"; import NotificationsManager from "./utils/NotificationsManager";
import ThemeManager from './utils/ThemeManager';
type Props = {}; type Props = {};
@ -49,12 +49,9 @@ export default class App extends React.Component<Props, State> {
setupStatusBar() { setupStatusBar() {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
console.log(ThemeManager.getNightMode());
if (ThemeManager.getNightMode()) { if (ThemeManager.getNightMode()) {
console.log('setting light mode');
StatusBar.setBarStyle('light-content', true); StatusBar.setBarStyle('light-content', true);
} else { } else {
console.log('setting dark mode');
StatusBar.setBarStyle('dark-content', true); StatusBar.setBarStyle('dark-content', true);
} }
} }
@ -73,7 +70,6 @@ export default class App extends React.Component<Props, State> {
} }
async loadAssetsAsync() { async loadAssetsAsync() {
console.log('Starting loading assets...');
// Wait for custom fonts to be loaded before showing the app // Wait for custom fonts to be loaded before showing the app
await Font.loadAsync({ await Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'), 'Roboto': require('native-base/Fonts/Roboto.ttf'),
@ -83,18 +79,15 @@ export default class App extends React.Component<Props, State> {
await AsyncStorageManager.getInstance().loadPreferences(); await AsyncStorageManager.getInstance().loadPreferences();
ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme()); ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme());
await NotificationsManager.initExpoToken(); await NotificationsManager.initExpoToken();
// console.log(AsyncStorageManager.getInstance().preferences.expoToken.current);
} }
onLoadFinished() { onLoadFinished() {
// Only show intro if this is the first time starting the app // Only show intro if this is the first time starting the app
console.log('Finished loading');
this.setState({ this.setState({
isLoading: false, isLoading: false,
currentTheme: ThemeManager.getCurrentTheme(), currentTheme: ThemeManager.getCurrentTheme(),
showIntro: AsyncStorageManager.getInstance().preferences.showIntro.current === '1', showIntro: AsyncStorageManager.getInstance().preferences.showIntro.current === '1',
showUpdate: AsyncStorageManager.getInstance().preferences.showUpdate5.current === '1' showUpdate: AsyncStorageManager.getInstance().preferences.showUpdate5.current === '1'
// showIntro: true
}); });
// Status bar goes dark if set too fast // Status bar goes dark if set too fast
setTimeout(this.setupStatusBar, setTimeout(this.setupStatusBar,

View file

@ -2,6 +2,15 @@
Pensez à garder l'appli à jour pour profiter des dernières fonctionnalités ! Pensez à garder l'appli à jour pour profiter des dernières fonctionnalités !
- **v1.5.0** - _05/02/2020_
- Amélioration des performances de l'application
- Amélioration du menu gauche
- Ajout d'animations au changement d'écran
- Affichage de l'événement de l'accueil directement au clic, au lieu de juste amener sur la liste
- _Notes de développement :_
- Passage de React Navigation 3 à 4
- Mise à jour d'autres librairies
- **v1.4.0** - _01/02/2020_ - **v1.4.0** - _01/02/2020_
- Correction d'un crash lors du rafraichissement de planex - Correction d'un crash lors du rafraichissement de planex
- Correction de bugs divers - Correction de bugs divers

View file

@ -10,7 +10,7 @@
"android", "android",
"web" "web"
], ],
"version": "1.4.0", "version": "1.5.0",
"orientation": "portrait", "orientation": "portrait",
"primaryColor": "#be1522", "primaryColor": "#be1522",
"icon": "./assets/android.icon.png", "icon": "./assets/android.icon.png",
@ -36,7 +36,7 @@
}, },
"android": { "android": {
"package": "fr.amicaleinsat.application", "package": "fr.amicaleinsat.application",
"versionCode": 12, "versionCode": 13,
"icon": "./assets/android.icon.png", "icon": "./assets/android.icon.png",
"adaptiveIcon": { "adaptiveIcon": {
"foregroundImage": "./assets/android.adaptive-icon.png", "foregroundImage": "./assets/android.adaptive-icon.png",

View file

@ -2,5 +2,10 @@ module.exports = function(api) {
api.cache(true); api.cache(true);
return { return {
presets: ['babel-preset-expo'], presets: ['babel-preset-expo'],
env: {
production: {
plugins: ['react-native-paper/babel'],
},
},
}; };
}; };

View file

@ -3,7 +3,6 @@
import * as React from 'react'; import * as React from 'react';
import {Container} from "native-base"; import {Container} from "native-base";
import CustomHeader from "./CustomHeader"; import CustomHeader from "./CustomHeader";
import CustomSideMenu from "./CustomSideMenu";
import CustomMaterialIcon from "./CustomMaterialIcon"; import CustomMaterialIcon from "./CustomMaterialIcon";
import {Platform, StatusBar, View} from "react-native"; import {Platform, StatusBar, View} from "react-native";
import ThemeManager from "../utils/ThemeManager"; import ThemeManager from "../utils/ThemeManager";
@ -15,6 +14,7 @@ import {NavigationActions} from "react-navigation";
type Props = { type Props = {
navigation: Object, navigation: Object,
headerTitle: string, headerTitle: string,
headerSubtitle: string,
headerRightButton: React.Node, headerRightButton: React.Node,
children: React.Node, children: React.Node,
hasTabs: boolean, hasTabs: boolean,
@ -25,7 +25,6 @@ type Props = {
} }
type State = { type State = {
isOpen: boolean,
isHeaderVisible: boolean isHeaderVisible: boolean
} }
@ -39,24 +38,17 @@ export default class BaseContainer extends React.Component<Props, State> {
hasSideMenu: true, hasSideMenu: true,
enableRotation: false, enableRotation: false,
hideHeaderOnLandscape: false, hideHeaderOnLandscape: false,
headerSubtitle: '',
}; };
willBlurSubscription: function; willBlurSubscription: function;
willFocusSubscription: function; willFocusSubscription: function;
state = { state = {
isOpen: false,
isHeaderVisible: true, isHeaderVisible: true,
}; };
toggle() { toggle() {
this.setState({ this.props.navigation.toggleDrawer();
isOpen: !this.state.isOpen,
});
} }
updateMenuState(isOpen: boolean) {
this.setState({isOpen});
}
/** /**
* Register for blur event to close side menu on screen change * Register for blur event to close side menu on screen change
*/ */
@ -87,7 +79,6 @@ export default class BaseContainer extends React.Component<Props, State> {
() => { () => {
if (this.props.enableRotation) if (this.props.enableRotation)
ScreenOrientation.lockAsync(ScreenOrientation.Orientation.PORTRAIT); ScreenOrientation.lockAsync(ScreenOrientation.Orientation.PORTRAIT);
this.setState({isOpen: false});
} }
); );
} }
@ -107,7 +98,9 @@ export default class BaseContainer extends React.Component<Props, State> {
<Container> <Container>
{this.state.isHeaderVisible ? {this.state.isHeaderVisible ?
<CustomHeader <CustomHeader
navigation={this.props.navigation} title={this.props.headerTitle} navigation={this.props.navigation}
title={this.props.headerTitle}
subtitle={this.props.headerSubtitle}
leftButton={ leftButton={
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
@ -128,20 +121,6 @@ export default class BaseContainer extends React.Component<Props, State> {
render() { render() {
return ( return (this.getMainContainer());
<View style={{
backgroundColor: ThemeManager.getCurrentThemeVariables().sideMenuBgColor,
width: '100%',
height: '100%'
}}>
{this.props.hasSideMenu ?
<CustomSideMenu
navigation={this.props.navigation} isOpen={this.state.isOpen}
onChange={(isOpen) => this.updateMenuState(isOpen)}>
{this.getMainContainer()}
</CustomSideMenu> :
this.getMainContainer()}
</View>
);
} }
} }

View file

@ -1,13 +1,14 @@
// @flow // @flow
import * as React from "react"; import * as React from "react";
import {Body, Header, Input, Item, Left, Right, Title} from "native-base"; import {Body, Header, Input, Item, Left, Right, Subtitle, Title} from "native-base";
import {Platform, StyleSheet, View} from "react-native"; import {Platform, StyleSheet, View} from "react-native";
import {getStatusBarHeight} from "react-native-status-bar-height"; import {getStatusBarHeight} from "react-native-status-bar-height";
import Touchable from 'react-native-platform-touchable'; import Touchable from 'react-native-platform-touchable';
import ThemeManager from "../utils/ThemeManager"; import ThemeManager from "../utils/ThemeManager";
import CustomMaterialIcon from "./CustomMaterialIcon"; import CustomMaterialIcon from "./CustomMaterialIcon";
import i18n from "i18n-js"; import i18n from "i18n-js";
import {NavigationActions} from 'react-navigation';
type Props = { type Props = {
hasBackButton: boolean, hasBackButton: boolean,
@ -17,6 +18,7 @@ type Props = {
leftButton: React.Node, leftButton: React.Node,
rightButton: React.Node, rightButton: React.Node,
title: string, title: string,
subtitle: string,
navigation: Object, navigation: Object,
hasTabs: boolean, hasTabs: boolean,
}; };
@ -37,6 +39,7 @@ export default class CustomHeader extends React.Component<Props> {
searchCallback: () => null, searchCallback: () => null,
shouldFocusSearchBar: false, shouldFocusSearchBar: false,
title: '', title: '',
subtitle: '',
leftButton: <View/>, leftButton: <View/>,
rightButton: <View/>, rightButton: <View/>,
hasTabs: false, hasTabs: false,
@ -51,6 +54,7 @@ export default class CustomHeader extends React.Component<Props> {
getSearchBar() { getSearchBar() {
return ( return (
<Body>
<Item <Item
style={{ style={{
width: '100%', width: '100%',
@ -65,9 +69,24 @@ export default class CustomHeader extends React.Component<Props> {
placeholderTextColor={ThemeManager.getCurrentThemeVariables().toolbarPlaceholderColor} placeholderTextColor={ThemeManager.getCurrentThemeVariables().toolbarPlaceholderColor}
onChangeText={(text) => this.props.searchCallback(text)}/> onChangeText={(text) => this.props.searchCallback(text)}/>
</Item> </Item>
</Body>
); );
} }
getHeaderTitle() {
return (
<Body>
<Title style={{
color: ThemeManager.getCurrentThemeVariables().toolbarTextColor
}}>
{this.props.title}
</Title>
{this.props.subtitle !== '' ? <Subtitle>{this.props.subtitle}</Subtitle> : null}
</Body>
);
}
render() { render() {
let button; let button;
// Does the app have a back button or a burger menu ? // Does the app have a back button or a burger menu ?
@ -75,10 +94,13 @@ export default class CustomHeader extends React.Component<Props> {
button = button =
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => this.props.navigation.goBack()}> onPress={() => {
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
}}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="arrow-left"/> icon={Platform.OS === 'ios' ? 'chevron-left' : "arrow-left"}/>
</Touchable>; </Touchable>;
else else
button = this.props.leftButton; button = this.props.leftButton;
@ -89,14 +111,9 @@ export default class CustomHeader extends React.Component<Props> {
<Left style={{flex: 0}}> <Left style={{flex: 0}}>
{button} {button}
</Left> </Left>
<Body>
{this.props.hasSearchField ? {this.props.hasSearchField ?
this.getSearchBar() : this.getSearchBar() :
<Title style={{ this.getHeaderTitle()}
paddingLeft: 10,
color: ThemeManager.getCurrentThemeVariables().toolbarTextColor
}}>{this.props.title}</Title>}
</Body>
<Right style={{flex: this.props.hasSearchField ? 0 : 1}}> <Right style={{flex: this.props.hasSearchField ? 0 : 1}}>
{this.props.rightButton} {this.props.rightButton}
</Right> </Right>
@ -104,7 +121,6 @@ export default class CustomHeader extends React.Component<Props> {
} }
}; };
// Fix header in status bar on Android // Fix header in status bar on Android
const styles = StyleSheet.create({ const styles = StyleSheet.create({
header: { header: {

View file

@ -1,48 +0,0 @@
// @flow
import * as React from 'react';
import SideMenu from "react-native-side-menu";
import SideBar from "./Sidebar";
import {View} from "react-native";
type Props = {
navigation: Object,
children: React.Node,
isOpen: boolean,
onChange: Function,
}
type State = {
shouldShowMenu: boolean, // Prevent menu from showing in transitions between tabs
}
export default class CustomSideMenu extends React.Component<Props, State> {
state = {
shouldShowMenu: this.props.isOpen,
};
// Stop the side menu from being shown while tab transition is playing
// => Hide the menu when behind the actual screen
onMenuMove(percent: number) {
if (percent <= 0)
this.setState({shouldShowMenu: false});
else if (this.state.shouldShowMenu === false)
this.setState({shouldShowMenu: true});
}
render() {
return (
<SideMenu menu={
this.state.shouldShowMenu ?
<SideBar navigation={this.props.navigation}/>
: <View/>}
isOpen={this.props.isOpen}
onChange={this.props.onChange}
onSliding={(percent) => this.onMenuMove(percent)}>
{this.props.children}
</SideMenu>
);
}
}

View file

@ -1,56 +1,14 @@
// @flow // @flow
import {createAppContainer, createStackNavigator} from 'react-navigation'; import {createAppContainer} from 'react-navigation';
import {createMaterialBottomTabNavigatorWithInitialRoute} from './MainTabNavigator'; import {createDrawerNavigatorWithInitialRoute} from './DrawerNavigator';
import SettingsScreen from '../screens/SettingsScreen';
import AboutScreen from '../screens/About/AboutScreen';
import ProximoListScreen from '../screens/Proximo/ProximoListScreen';
import AboutDependenciesScreen from '../screens/About/AboutDependenciesScreen';
import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen';
import ProximoAboutScreen from '../screens/Proximo/ProximoAboutScreen';
import SelfMenuScreen from '../screens/SelfMenuScreen';
import TutorInsaScreen from "../screens/Websites/TutorInsaScreen";
import AmicaleScreen from "../screens/Websites/AmicaleScreen";
import WiketudScreen from "../screens/Websites/WiketudScreen";
import ElusEtudScreen from "../screens/Websites/ElusEtudScreen";
import BlueMindScreen from "../screens/Websites/BlueMindScreen";
import EntScreen from "../screens/Websites/EntScreen";
import AvailableRoomScreen from "../screens/Websites/AvailableRoomScreen";
import DebugScreen from '../screens/DebugScreen';
import {fromRight} from "react-navigation-transitions";
/** /**
* Create a stack navigator using the drawer to handle navigation between screens * Create a stack navigator using the drawer to handle navigation between screens
*/ */
function createAppContainerWithInitialRoute(initialRoute: string) { function createAppContainerWithInitialRoute(initialRoute: string) {
return createAppContainer( return createAppContainer(createDrawerNavigatorWithInitialRoute(initialRoute));
createStackNavigator({
Main: createMaterialBottomTabNavigatorWithInitialRoute(initialRoute),
// Drawer: MainDrawerNavigator,
ProximoListScreen: {screen: ProximoListScreen},
SettingsScreen: {screen: SettingsScreen},
AboutScreen: {screen: AboutScreen},
AboutDependenciesScreen: {screen: AboutDependenciesScreen},
SelfMenuScreen: {screen: SelfMenuScreen},
TutorInsaScreen: {screen: TutorInsaScreen},
AmicaleScreen: {screen: AmicaleScreen},
WiketudScreen: {screen: WiketudScreen},
ElusEtudScreen: {screen: ElusEtudScreen},
BlueMindScreen: {screen: BlueMindScreen},
EntScreen: {screen: EntScreen},
AvailableRoomScreen: {screen: AvailableRoomScreen},
ProxiwashAboutScreen: {screen: ProxiwashAboutScreen},
ProximoAboutScreen: {screen: ProximoAboutScreen},
DebugScreen: {screen: DebugScreen},
},
{
initialRouteName: "Main",
mode: 'card',
headerMode: "none",
transitionConfig: () => fromRight(),
})
);
} }
export {createAppContainerWithInitialRoute}; export {createAppContainerWithInitialRoute};

View file

@ -0,0 +1,79 @@
// @flow
import { createDrawerNavigator } from 'react-navigation-drawer';
import {createMaterialBottomTabNavigatorWithInitialRoute} from './MainTabNavigator';
import SettingsScreen from '../screens/SettingsScreen';
import AboutScreen from '../screens/About/AboutScreen';
import AboutDependenciesScreen from '../screens/About/AboutDependenciesScreen';
import SelfMenuScreen from '../screens/SelfMenuScreen';
import TutorInsaScreen from "../screens/Websites/TutorInsaScreen";
import AmicaleScreen from "../screens/Websites/AmicaleScreen";
import WiketudScreen from "../screens/Websites/WiketudScreen";
import ElusEtudScreen from "../screens/Websites/ElusEtudScreen";
import BlueMindScreen from "../screens/Websites/BlueMindScreen";
import EntScreen from "../screens/Websites/EntScreen";
import AvailableRoomScreen from "../screens/Websites/AvailableRoomScreen";
import DebugScreen from '../screens/DebugScreen';
import Sidebar from "../components/Sidebar";
import {createStackNavigator, TransitionPresets} from "react-navigation-stack";
const AboutStack = createStackNavigator({
AboutScreen: {screen: AboutScreen},
AboutDependenciesScreen: {screen: AboutDependenciesScreen},
DebugScreen: {screen: DebugScreen},
},
{
initialRouteName: "AboutScreen",
mode: 'card',
headerMode: "none",
defaultNavigationOptions: {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.SlideFromRightIOS,
},
});
// Create a stack to use animations
function createDrawerStackWithInitialRoute(initialRoute: string) {
return createStackNavigator({
Main: createMaterialBottomTabNavigatorWithInitialRoute(initialRoute),
SettingsScreen: {screen: SettingsScreen},
AboutScreen: AboutStack,
SelfMenuScreen: {screen: SelfMenuScreen},
TutorInsaScreen: {screen: TutorInsaScreen},
AmicaleScreen: {screen: AmicaleScreen},
WiketudScreen: {screen: WiketudScreen},
ElusEtudScreen: {screen: ElusEtudScreen},
BlueMindScreen: {screen: BlueMindScreen},
EntScreen: {screen: EntScreen},
AvailableRoomScreen: {screen: AvailableRoomScreen},
},
{
initialRouteName: "Main",
mode: 'card',
headerMode: "none",
defaultNavigationOptions: {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.SlideFromRightIOS,
},
});
}
/**
* Creates the drawer navigation stack
*/
function createDrawerNavigatorWithInitialRoute(initialRoute: string) {
return createDrawerNavigator({
Main: createDrawerStackWithInitialRoute(initialRoute),
}, {
contentComponent: Sidebar,
initialRouteName: 'Main',
backBehavior: 'initialRoute',
drawerType: 'front',
useNativeAnimations: true,
});
}
export {createDrawerNavigatorWithInitialRoute};

View file

@ -1,10 +1,15 @@
import * as React from 'react'; import * as React from 'react';
import {createStackNavigator, TransitionPresets} from 'react-navigation-stack';
import {createMaterialBottomTabNavigator} from "react-navigation-material-bottom-tabs"; import {createMaterialBottomTabNavigator} from "react-navigation-material-bottom-tabs";
import HomeScreen from '../screens/HomeScreen'; import HomeScreen from '../screens/HomeScreen';
import PlanningScreen from '../screens/PlanningScreen'; import PlanningScreen from '../screens/PlanningScreen';
import PlanningDisplayScreen from '../screens/PlanningDisplayScreen';
import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen'; import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen';
import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen';
import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen'; import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen';
import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
import PlanexScreen from '../screens/Websites/PlanexScreen'; import PlanexScreen from '../screens/Websites/PlanexScreen';
import CustomMaterialIcon from "../components/CustomMaterialIcon"; import CustomMaterialIcon from "../components/CustomMaterialIcon";
import ThemeManager from "../utils/ThemeManager"; import ThemeManager from "../utils/ThemeManager";
@ -17,12 +22,78 @@ const TAB_ICONS = {
Planex: 'timetable', Planex: 'timetable',
}; };
const ProximoStack = createStackNavigator({
ProximoMainScreen: {screen: ProximoMainScreen},
ProximoListScreen: {screen: ProximoListScreen},
ProximoAboutScreen: {
screen: ProximoAboutScreen,
navigationOptions: () => ({
...TransitionPresets.ModalSlideFromBottomIOS,
}),
},
},
{
initialRouteName: "ProximoMainScreen",
mode: 'card',
headerMode: "none",
defaultNavigationOptions: {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.SlideFromRightIOS,
},
});
const ProxiwashStack = createStackNavigator({
ProxiwashScreen: {screen: ProxiwashScreen},
ProxiwashAboutScreen: {screen: ProxiwashAboutScreen},
},
{
initialRouteName: "ProxiwashScreen",
mode: 'card',
headerMode: "none",
defaultNavigationOptions: {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.ModalSlideFromBottomIOS,
},
});
const PlanningStack = createStackNavigator({
PlanningScreen: {screen: PlanningScreen},
PlanningDisplayScreen: {screen: PlanningDisplayScreen},
},
{
initialRouteName: "PlanningScreen",
mode: 'card',
headerMode: "none",
defaultNavigationOptions: {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.ModalSlideFromBottomIOS,
},
});
const HomeStack = createStackNavigator({
HomeScreen: {screen: HomeScreen},
PlanningDisplayScreen: {screen: PlanningDisplayScreen},
},
{
initialRouteName: "HomeScreen",
mode: 'card',
headerMode: "none",
defaultNavigationOptions: {
gestureEnabled: true,
cardOverlayEnabled: true,
...TransitionPresets.ModalSlideFromBottomIOS,
},
});
function createMaterialBottomTabNavigatorWithInitialRoute(initialRoute: string) { function createMaterialBottomTabNavigatorWithInitialRoute(initialRoute: string) {
return createMaterialBottomTabNavigator({ return createMaterialBottomTabNavigator({
Home: {screen: HomeScreen}, Home: HomeStack,
Planning: {screen: PlanningScreen,}, Planning: PlanningStack,
Proxiwash: {screen: ProxiwashScreen,}, Proxiwash: ProxiwashStack,
Proximo: {screen: ProximoMainScreen,}, Proximo: ProximoStack,
Planex: { Planex: {
screen: PlanexScreen, screen: PlanexScreen,
navigationOptions: ({navigation}) => { navigationOptions: ({navigation}) => {

View file

@ -8,6 +8,7 @@
"eject": "expo eject" "eject": "expo eject"
}, },
"dependencies": { "dependencies": {
"@react-native-community/masked-view": "0.1.5",
"expo": "^36.0.0", "expo": "^36.0.0",
"expo-font": "~8.0.0", "expo-font": "~8.0.0",
"expo-linear-gradient": "~8.0.0", "expo-linear-gradient": "~8.0.0",
@ -21,19 +22,22 @@
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz", "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.1.tar.gz",
"react-native-app-intro-slider": "^3.0.0", "react-native-app-intro-slider": "^3.0.0",
"react-native-autolink": "^1.8.1", "react-native-autolink": "^1.8.1",
"react-native-calendars": "^1.212.0", "react-native-calendars": "^1.260.0",
"react-native-gesture-handler": "~1.5.0", "react-native-gesture-handler": "~1.5.0",
"react-native-material-menu": "^0.6.7", "react-native-material-menu": "^1.0.0",
"react-native-modalize": "^1.3.6", "react-native-modalize": "^1.3.6",
"react-native-paper": "^3.5.1", "react-native-paper": "^3.5.1",
"react-native-platform-touchable": "^1.1.1", "react-native-platform-touchable": "^1.1.1",
"react-native-reanimated": "~1.4.0",
"react-native-render-html": "^4.1.2", "react-native-render-html": "^4.1.2",
"react-native-safe-area-context": "0.6.0",
"react-native-screens": "2.0.0-alpha.12", "react-native-screens": "2.0.0-alpha.12",
"react-native-side-menu": "^1.1.3",
"react-native-status-bar-height": "^2.3.1", "react-native-status-bar-height": "^2.3.1",
"react-native-webview": "7.4.3", "react-native-webview": "7.4.3",
"react-navigation": "^3.13.0", "react-navigation": "^4.1.0",
"react-navigation-material-bottom-tabs": "^1.1.1", "react-navigation-drawer": "^2.3.3",
"react-navigation-material-bottom-tabs": "^2.1.5",
"react-navigation-stack": "^2.1.0",
"react-navigation-transitions": "^1.0.12" "react-navigation-transitions": "^1.0.12"
}, },
"devDependencies": { "devDependencies": {

View file

@ -319,14 +319,12 @@ export default class AboutScreen extends React.Component<Props, State> {
tryUnlockDebugMode() { tryUnlockDebugMode() {
this.debugTapCounter = this.debugTapCounter + 1; this.debugTapCounter = this.debugTapCounter + 1;
console.log(this.debugTapCounter);
if (this.debugTapCounter >= 4) { if (this.debugTapCounter >= 4) {
this.unlockDebugMode(); this.unlockDebugMode();
} }
} }
unlockDebugMode() { unlockDebugMode() {
console.log('unlocked');
this.setState({isDebugUnlocked: true}); this.setState({isDebugUnlocked: true});
let key = AsyncStorageManager.getInstance().preferences.debugUnlocked.key; let key = AsyncStorageManager.getInstance().preferences.debugUnlocked.key;
AsyncStorageManager.getInstance().savePref(key, '1'); AsyncStorageManager.getInstance().savePref(key, '1');

View file

@ -83,7 +83,6 @@ export default class DebugScreen extends React.Component<Props, State> {
alertCurrentExpoToken() { alertCurrentExpoToken() {
let token = AsyncStorageManager.getInstance().preferences.expoToken.current; let token = AsyncStorageManager.getInstance().preferences.expoToken.current;
console.log(token);
Alert.alert( Alert.alert(
'Expo Token', 'Expo Token',
token, token,

View file

@ -310,7 +310,12 @@ export default class HomeScreen extends FetchedDataSectionList {
</Text>; </Text>;
} else } else
subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA'); subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
let clickAction = () => this.props.navigation.navigate('Planning'); let clickAction = () => {
if (isAvailable)
this.props.navigation.navigate('PlanningDisplayScreen', {data: displayEvent});
else
this.props.navigation.navigate('PlanningScreen');
};
let displayEvent = this.getDisplayEvent(futureEvents); let displayEvent = this.getDisplayEvent(futureEvents);

View file

@ -0,0 +1,69 @@
// @flow
import * as React from 'react';
import {Image} from 'react-native';
import {Container, Content, H1, H3, View} from 'native-base';
import CustomHeader from "../components/CustomHeader";
import ThemeManager from "../utils/ThemeManager";
import HTML from "react-native-render-html";
import {Linking} from "expo";
import PlanningEventManager from '../utils/PlanningEventManager';
import i18n from 'i18n-js';
type Props = {
navigation: Object,
};
function openWebLink(link) {
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
}
/**
* Class defining an about screen. This screen shows the user information about the app and it's author.
*/
export default class PlanningDisplayScreen extends React.Component<Props> {
render() {
const nav = this.props.navigation;
const displayData = nav.getParam('data', []);
return (
<Container>
<CustomHeader
navigation={nav}
title={displayData.title}
subtitle={PlanningEventManager.getFormattedTime(displayData)}
hasBackButton={true}/>
<Content padder>
<H1>
{displayData.title}
</H1>
<H3 style={{
marginTop: 10,
color: ThemeManager.getCurrentThemeVariables().listNoteColor
}}>
{PlanningEventManager.getFormattedTime(displayData)}
</H3>
{displayData.logo !== null ?
<View style={{width: '100%', height: 300, marginTop: 20, marginBottom: 20}}>
<Image style={{flex: 1, resizeMode: "contain"}}
source={{uri: displayData.logo}}/>
</View>
: <View/>}
{displayData.description !== null ?
// Surround description with div to allow text styling if the description is not html
<HTML html={"<div>" + displayData.description + "</div>"}
tagsStyles={{
p: {
color: ThemeManager.getCurrentThemeVariables().textColor,
fontSize: ThemeManager.getCurrentThemeVariables().fontSizeBase
},
div: {color: ThemeManager.getCurrentThemeVariables().textColor}
}}
onLinkPress={(event, link) => openWebLink(link)}/>
: <View/>}
</Content>
</Container>
);
}
}

View file

@ -1,18 +1,16 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {BackHandler, Image, View} from 'react-native'; import {BackHandler, Image} from 'react-native';
import {Button, Content, H1, H3, Text} from 'native-base'; import {H3, Text, View} from 'native-base';
import i18n from "i18n-js"; import i18n from "i18n-js";
import ThemeManager from "../utils/ThemeManager"; import ThemeManager from "../utils/ThemeManager";
import {Linking} from "expo"; import {Linking} from "expo";
import BaseContainer from "../components/BaseContainer"; import BaseContainer from "../components/BaseContainer";
import {Agenda, LocaleConfig} from 'react-native-calendars'; import {Agenda, LocaleConfig} from 'react-native-calendars';
import HTML from 'react-native-render-html';
import Touchable from 'react-native-platform-touchable'; import Touchable from 'react-native-platform-touchable';
import {Modalize} from 'react-native-modalize';
import WebDataManager from "../utils/WebDataManager"; import WebDataManager from "../utils/WebDataManager";
import CustomMaterialIcon from "../components/CustomMaterialIcon"; import PlanningEventManager from '../utils/PlanningEventManager';
LocaleConfig.locales['fr'] = { LocaleConfig.locales['fr'] = {
monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'], monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
@ -28,7 +26,6 @@ type Props = {
} }
type State = { type State = {
modalCurrentDisplayItem: Object,
refreshing: boolean, refreshing: boolean,
agendaItems: Object, agendaItems: Object,
calendarShowing: boolean, calendarShowing: boolean,
@ -51,7 +48,6 @@ function openWebLink(link) {
*/ */
export default class PlanningScreen extends React.Component<Props, State> { export default class PlanningScreen extends React.Component<Props, State> {
modalRef: Modalize;
agendaRef: Agenda; agendaRef: Agenda;
webDataManager: WebDataManager; webDataManager: WebDataManager;
@ -61,7 +57,6 @@ export default class PlanningScreen extends React.Component<Props, State> {
didFocusSubscription: Function; didFocusSubscription: Function;
willBlurSubscription: Function; willBlurSubscription: Function;
state = { state = {
modalCurrentDisplayItem: {},
refreshing: false, refreshing: false,
agendaItems: {}, agendaItems: {},
calendarShowing: false, calendarShowing: false,
@ -69,7 +64,6 @@ export default class PlanningScreen extends React.Component<Props, State> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.modalRef = React.createRef();
this.webDataManager = new WebDataManager(FETCH_URL); this.webDataManager = new WebDataManager(FETCH_URL);
this.didFocusSubscription = props.navigation.addListener( this.didFocusSubscription = props.navigation.addListener(
'didFocus', 'didFocus',
@ -131,72 +125,11 @@ export default class PlanningScreen extends React.Component<Props, State> {
return daysOfYear; return daysOfYear;
} }
getModalHeader() {
return (
<View style={{marginBottom: 0}}>
<Button
onPress={() => this.modalRef.current.close()}
style={{
marginTop: 50,
marginLeft: 'auto',
}}
transparent>
<CustomMaterialIcon icon={'close'}/>
</Button>
</View>
);
}
getModalContent() {
return (
<View style={{
flex: 1,
padding: 20
}}>
<H1>
{this.state.modalCurrentDisplayItem.title}
</H1>
<H3 style={{
marginTop: 10,
color: ThemeManager.getCurrentThemeVariables().listNoteColor
}}>
{this.getFormattedTime(this.state.modalCurrentDisplayItem)}
</H3>
<Content>
{this.state.modalCurrentDisplayItem.logo !== null ?
<View style={{width: '100%', height: 200, marginTop: 20, marginBottom: 20}}>
<Image style={{flex: 1, resizeMode: "contain"}}
source={{uri: this.state.modalCurrentDisplayItem.logo}}/>
</View>
: <View/>}
{this.state.modalCurrentDisplayItem.description !== null ?
// Surround description with div to allow text styling if the description is not html
<HTML html={"<div>" + this.state.modalCurrentDisplayItem.description + "</div>"}
tagsStyles={{
p: {
color: ThemeManager.getCurrentThemeVariables().textColor,
fontSize: ThemeManager.getCurrentThemeVariables().fontSizeBase
},
div: {color: ThemeManager.getCurrentThemeVariables().textColor}
}}
onLinkPress={(event, link) => openWebLink(link)}/>
: <View/>}
</Content>
</View>
);
}
showItemDetails(item: Object) {
this.setState({
modalCurrentDisplayItem: item,
});
if (this.modalRef.current) {
this.modalRef.current.open();
}
}
getRenderItem(item: Object) { getRenderItem(item: Object) {
let navData = {
data: item
};
const nav = this.props.navigation;
return ( return (
<Touchable <Touchable
style={{ style={{
@ -205,7 +138,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
marginRight: 10, marginRight: 10,
marginTop: 17, marginTop: 17,
}} }}
onPress={() => this.showItemDetails(item)}> onPress={() => nav.navigate('PlanningDisplayScreen', navData)}>
<View style={{ <View style={{
padding: 10, padding: 10,
flex: 1, flex: 1,
@ -219,7 +152,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
marginTop: 5, marginTop: 5,
marginBottom: 10 marginBottom: 10
}}> }}>
{this.getFormattedTime(item)} {PlanningEventManager.getFormattedTime(item)}
</Text> </Text>
<H3 style={{marginBottom: 10}}>{item.title}</H3> <H3 style={{marginBottom: 10}}>{item.title}</H3>
</View> </View>
@ -296,8 +229,8 @@ export default class PlanningScreen extends React.Component<Props, State> {
generateEventAgenda(eventList: Array<Object>) { generateEventAgenda(eventList: Array<Object>) {
let agendaItems = this.generateEmptyCalendar(); let agendaItems = this.generateEmptyCalendar();
for (let i = 0; i < eventList.length; i++) { for (let i = 0; i < eventList.length; i++) {
if (agendaItems[this.getEventStartDate(eventList[i])] !== undefined) { if (agendaItems[PlanningEventManager.getEventStartDate(eventList[i])] !== undefined) {
this.pushEventInOrder(agendaItems, eventList[i], this.getEventStartDate(eventList[i])); this.pushEventInOrder(agendaItems, eventList[i], PlanningEventManager.getEventStartDate(eventList[i]));
} }
} }
this.setState({agendaItems: agendaItems}) this.setState({agendaItems: agendaItems})
@ -308,7 +241,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
agendaItems[startDate].push(event); agendaItems[startDate].push(event);
else { else {
for (let i = 0; i < agendaItems[startDate].length; i++) { for (let i = 0; i < agendaItems[startDate].length; i++) {
if (this.isEventBefore(event, agendaItems[startDate][i])) { if (PlanningEventManager.isEventBefore(event, agendaItems[startDate][i])) {
agendaItems[startDate].splice(i, 0, event); agendaItems[startDate].splice(i, 0, event);
break; break;
} else if (i === agendaItems[startDate].length - 1) { } else if (i === agendaItems[startDate].length - 1) {
@ -319,65 +252,10 @@ export default class PlanningScreen extends React.Component<Props, State> {
} }
} }
isEventBefore(event1: Object, event2: Object) {
let date1 = new Date();
let date2 = new Date();
let timeArray = this.getEventStartTime(event1).split(":");
date1.setHours(parseInt(timeArray[0]), parseInt(timeArray[1]));
timeArray = this.getEventStartTime(event2).split(":");
date2.setHours(parseInt(timeArray[0]), parseInt(timeArray[1]));
return date1 < date2;
}
getEventStartDate(event: Object) {
return event.date_begin.split(" ")[0];
}
getEventStartTime(event: Object) {
if (event !== undefined && Object.keys(event).length > 0 && event.date_begin !== null)
return this.formatTime(event.date_begin.split(" ")[1]);
else
return "";
}
getEventEndTime(event: Object) {
if (event !== undefined && Object.keys(event).length > 0 && event.date_end !== null)
return this.formatTime(event.date_end.split(" ")[1]);
else
return "";
}
getFormattedTime(event: Object) {
if (this.getEventEndTime(event) !== "")
return this.getEventStartTime(event) + " - " + this.getEventEndTime(event);
else
return this.getEventStartTime(event);
}
formatTime(time: string) {
let array = time.split(':');
return array[0] + ':' + array[1];
}
onModalClosed() {
this.setState({
modalCurrentDisplayItem: {},
});
}
render() { render() {
const nav = this.props.navigation; const nav = this.props.navigation;
return ( return (
<BaseContainer navigation={nav} headerTitle={i18n.t('screens.planning')}> <BaseContainer navigation={nav} headerTitle={i18n.t('screens.planning')}>
<Modalize ref={this.modalRef}
modalStyle={{
backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor,
}}
// adjustToContentHeight // Breaks when displaying full screen, half, then full again
HeaderComponent={() => this.getModalHeader()}
onClosed={() => this.onModalClosed()}>
{this.getModalContent()}
</Modalize>
<Agenda <Agenda
// the list of items that have to be displayed in agenda. If you want to render item as empty date // the list of items that have to be displayed in agenda. If you want to render item as empty date
// the value of date key kas to be an empty array []. If there exists no value for date key it is // the value of date key kas to be an empty array []. If there exists no value for date key it is
@ -431,14 +309,6 @@ export default class PlanningScreen extends React.Component<Props, State> {
agendaDayNumColor: ThemeManager.getCurrentThemeVariables().listNoteColor, agendaDayNumColor: ThemeManager.getCurrentThemeVariables().listNoteColor,
agendaTodayColor: ThemeManager.getCurrentThemeVariables().brandPrimary, agendaTodayColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
agendaKnobColor: ThemeManager.getCurrentThemeVariables().brandPrimary, agendaKnobColor: ThemeManager.getCurrentThemeVariables().brandPrimary,
// Fix for days hiding behind knob
'stylesheet.calendar.header': {
week: {
marginTop: 0,
flexDirection: 'row',
justifyContent: 'space-between'
}
}
}} }}
/> />
</BaseContainer> </BaseContainer>

View file

@ -2,6 +2,7 @@
"screens": { "screens": {
"home": "Home", "home": "Home",
"planning": "Planning", "planning": "Planning",
"planningDisplayScreen": "Event Details",
"proxiwash": "Proxiwash", "proxiwash": "Proxiwash",
"proximo": "Proximo", "proximo": "Proximo",
"menuSelf": "RU Menu", "menuSelf": "RU Menu",

View file

@ -2,6 +2,7 @@
"screens": { "screens": {
"home": "Accueil", "home": "Accueil",
"planning": "Planning", "planning": "Planning",
"planningDisplayScreen": "Détails",
"proxiwash": "Proxiwash", "proxiwash": "Proxiwash",
"proximo": "Proximo", "proximo": "Proximo",
"menuSelf": "Menu du RU", "menuSelf": "Menu du RU",

View file

@ -0,0 +1,42 @@
export default class PlanningEventManager {
static isEventBefore(event1: Object, event2: Object) {
let date1 = new Date();
let date2 = new Date();
let timeArray = this.getEventStartTime(event1).split(":");
date1.setHours(parseInt(timeArray[0]), parseInt(timeArray[1]));
timeArray = this.getEventStartTime(event2).split(":");
date2.setHours(parseInt(timeArray[0]), parseInt(timeArray[1]));
return date1 < date2;
}
static getEventStartDate(event: Object) {
return event.date_begin.split(" ")[0];
}
static getEventStartTime(event: Object) {
if (event !== undefined && Object.keys(event).length > 0 && event.date_begin !== null)
return this.formatTime(event.date_begin.split(" ")[1]);
else
return "";
}
static getEventEndTime(event: Object) {
if (event !== undefined && Object.keys(event).length > 0 && event.date_end !== null)
return this.formatTime(event.date_end.split(" ")[1]);
else
return "";
}
static getFormattedTime(event: Object) {
if (this.getEventEndTime(event) !== "")
return this.getEventStartTime(event) + " - " + this.getEventEndTime(event);
else
return this.getEventStartTime(event);
}
static formatTime(time: string) {
let array = time.split(':');
return array[0] + ':' + array[1];
}
}