Compare commits

...

6 commits

Author SHA1 Message Date
keplyx
bec015bce9 Updated version + fixed planex css cache not updating 2019-11-08 14:36:53 +01:00
keplyx
c9425ed6a2 Added update intro text 2019-11-08 12:42:30 +01:00
keplyx
e4a002c8dc Show text to tell user to rotate the screen 2019-11-08 12:27:15 +01:00
keplyx
bb0ff390a9 Hide webview header and tabs on landscape 2019-11-08 03:56:29 +01:00
keplyx
1da41c1fd1 Added tabbed webview for available rooms 2019-11-08 02:37:38 +01:00
keplyx
8b132ccc91 Fixed chevron not showing in about screen 2019-11-07 18:59:52 +01:00
15 changed files with 281 additions and 78 deletions

4
App.js
View file

@ -72,7 +72,7 @@ export default class App extends React.Component<Props, State> {
showUpdate: false,
});
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showIntro.key, '0');
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showUpdate3.key, '0');
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.showUpdate4.key, '0');
}
async loadAssetsAsync() {
@ -95,7 +95,7 @@ export default class App extends React.Component<Props, State> {
isLoading: false,
currentTheme: ThemeManager.getCurrentTheme(),
showIntro: AsyncStorageManager.getInstance().preferences.showIntro.current === '1',
showUpdate: AsyncStorageManager.getInstance().preferences.showUpdate3.current === '1'
showUpdate: AsyncStorageManager.getInstance().preferences.showUpdate4.current === '1'
// showIntro: true
});
// Status bar goes dark if set too fast

View file

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

View file

@ -18,6 +18,7 @@ type Props = {
hasTabs: boolean,
hasBackButton: boolean,
hasSideMenu: boolean,
isHeaderVisible: boolean
}
type State = {
@ -34,6 +35,7 @@ export default class BaseContainer extends React.Component<Props, State> {
hasTabs: false,
hasBackButton: false,
hasSideMenu: true,
isHeaderVisible: true,
};
@ -95,6 +97,7 @@ export default class BaseContainer extends React.Component<Props, State> {
render() {
if (this.props.isHeaderVisible) {
return (
<View style={{
backgroundColor: ThemeManager.getCurrentThemeVariables().sideMenuBgColor,
@ -110,5 +113,17 @@ export default class BaseContainer extends React.Component<Props, State> {
this.getMainContainer()}
</View>
);
} else {
return (
<View style={{
backgroundColor: ThemeManager.getCurrentThemeVariables().sideMenuBgColor,
width: '100%',
height: '100%'
}}>
{this.props.children}
</View>
);
}
}
}

View file

@ -105,7 +105,7 @@ export default class CustomIntroSlider extends React.Component<Props> {
key: '1',
title: i18n.t('intro.updateSlide.title'),
text: i18n.t('intro.updateSlide.text'),
icon: 'view-dashboard',
icon: 'school',
colors: ['#e01928', '#be1522'],
},
]

View file

@ -2,39 +2,93 @@
import * as React from 'react';
import {Linking, Platform, View} from 'react-native';
import {Spinner, Footer, Right, Left, Body} from 'native-base';
import {Spinner, Footer, Right, Left, Body, Tab, TabHeading, Text, Tabs} from 'native-base';
import WebView from "react-native-webview";
import Touchable from "react-native-platform-touchable";
import CustomMaterialIcon from "../components/CustomMaterialIcon";
import ThemeManager from "../utils/ThemeManager";
import BaseContainer from "../components/BaseContainer";
import {ScreenOrientation} from 'expo';
import {NavigationActions} from 'react-navigation';
type Props = {
navigation: Object,
data: Array<{
url: string,
customInjectedJS: string,
icon: string,
name: string,
customJS: string
}>,
headerTitle: string,
hasHeaderBackButton: boolean,
hasSideMenu: boolean,
hasFooter: boolean,
}
type State = {
isLandscape: boolean,
}
/**
* Class defining a webview screen.
*/
export default class WebViewScreen extends React.Component<Props> {
export default class WebViewScreen extends React.Component<Props, State> {
static defaultProps = {
customInjectedJS: '',
hasBackButton: false,
hasSideMenu: true,
hasFooter: true,
};
webview: WebView;
state = {
isLandscape: false,
};
openWebLink() {
Linking.openURL(this.props.url).catch((err) => console.error('Error opening link', err));
webviewArray: Array<WebView> = [];
willFocusSubscription: function;
willBlurSubscription: function;
/**
* Register for blur event to close side menu on screen change
*/
componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
payload => {
ScreenOrientation.unlockAsync();
ScreenOrientation.addOrientationChangeListener((OrientationChangeEvent) => {
let isLandscape = OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE ||
OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE_LEFT ||
OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE_RIGHT;
this.setState({isLandscape: isLandscape});
const setParamsAction = NavigationActions.setParams({
params: {showTabBar: !isLandscape},
key: this.props.navigation.state.key,
});
this.props.navigation.dispatch(setParamsAction);
});
}
);
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
payload => {
ScreenOrientation.lockAsync(ScreenOrientation.Orientation.PORTRAIT);
}
);
}
/**
* Unregister from event when un-mounting components
*/
componentWillUnmount() {
if (this.willBlurSubscription !== undefined)
this.willBlurSubscription.remove();
if (this.willFocusSubscription !== undefined)
this.willFocusSubscription.remove();
}
openWebLink(url: string) {
Linking.openURL(url).catch((err) => console.error('Error opening link', err));
}
getHeaderButton(clickAction: Function, icon: string) {
@ -58,35 +112,34 @@ export default class WebViewScreen extends React.Component<Props> {
};
refreshWebview() {
this.webview.reload();
for (let view of this.webviewArray) {
view.reload();
}
}
goBackWebview() {
this.webview.goBack();
for (let view of this.webviewArray) {
view.goBack();
}
}
goForwardWebview() {
this.webview.goForward();
for (let view of this.webviewArray) {
view.goForward();
}
}
render() {
const nav = this.props.navigation;
getWebview(obj: Object) {
return (
<BaseContainer
navigation={nav}
headerTitle={this.props.headerTitle}
headerRightButton={this.getRefreshButton()}
hasBackButton={this.props.hasHeaderBackButton}
hasSideMenu={this.props.hasSideMenu}>
<WebView
ref={ref => (this.webview = ref)}
source={{uri: this.props.url}}
ref={ref => (this.webviewArray.push(ref))}
source={{uri: obj['url']}}
style={{
width: '100%',
height: '100%',
}}
startInLoadingState={true}
injectedJavaScript={this.props.customInjectedJS}
injectedJavaScript={obj['customJS']}
javaScriptEnabled={true}
renderLoading={() =>
<View style={{
@ -104,12 +157,56 @@ export default class WebViewScreen extends React.Component<Props> {
</View>
}
/>
{this.props.hasFooter ?
);
}
getTabbedWebview() {
let tabbedView = [];
for (let i = 0; i < this.props.data.length; i++) {
tabbedView.push(
<Tab heading={
<TabHeading>
<CustomMaterialIcon
icon={this.props.data[i]['icon']}
color={ThemeManager.getCurrentThemeVariables().tabIconColor}
fontSize={20}
/>
<Text>{this.props.data[i]['name']}</Text>
</TabHeading>}
key={this.props.data[i]['url']}
style={{backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor}}>
{this.getWebview(this.props.data[i])}
</Tab>);
}
return tabbedView;
}
render() {
const nav = this.props.navigation;
return (
<BaseContainer
navigation={nav}
headerTitle={this.props.headerTitle}
headerRightButton={this.getRefreshButton()}
hasBackButton={this.props.hasHeaderBackButton}
hasSideMenu={this.props.hasSideMenu}
isHeaderVisible={!this.state.isLandscape}>
{this.props.data.length === 1 ?
this.getWebview(this.props.data[0]) :
<Tabs
tabContainerStyle={{
elevation: 0, // Fix for android shadow
}}
locked={true}
>
{this.getTabbedWebview()}
</Tabs>}
{this.props.hasFooter && this.props.data.length === 1 ?
<Footer>
<Left style={{
paddingLeft: 6,
}}>
{this.getHeaderButton(() => this.openWebLink(), 'open-in-new')}
{this.getHeaderButton(() => this.openWebLink(this.props.data[0]['url']), 'open-in-new')}
</Left>
<Body/>
<Right style={{

View file

@ -24,14 +24,22 @@ function createMaterialBottomTabNavigatorWithInitialRoute(initialRoute: string)
Planning: {screen: PlanningScreen,},
Proxiwash: {screen: ProxiwashScreen,},
Proximo: {screen: ProximoMainScreen,},
Planex: {screen: PlanexScreen},
Planex: {
screen: PlanexScreen,
navigationOptions: ({ navigation }) => {
const showTabBar = navigation.state && navigation.state.params ? navigation.state.params.showTabBar : true;
return {
tabBarVisible: showTabBar,
};
},},
}, {
defaultNavigationOptions: ({navigation}) => ({
tabBarIcon: ({focused, horizontal, tintColor}) => {
let icon = TAB_ICONS[navigation.state.routeName];
return <CustomMaterialIcon icon={icon} color={tintColor}/>;
}
},
tabBarVisible: true,
}),
order: ['Proximo', 'Planning', 'Home', 'Proxiwash', 'Planex'],
initialRouteName: initialRoute,

View file

@ -152,7 +152,7 @@ export default class AboutScreen extends React.Component<Props, State> {
onPressCallback: () => openWebLink(links.react),
icon: 'react',
text: i18n.t('aboutScreen.reactNative'),
showChevron: false
showChevron: true
},
{
onPressCallback: () => this.props.navigation.navigate('AboutDependenciesScreen', {data: packageJson.dependencies}),

View file

@ -22,7 +22,14 @@ export default class AmicaleScreen extends React.Component<Props> {
return (
<WebViewScreen
navigation={nav}
url={URL}
data={[
{
url: URL,
icon: '',
name: '',
customJS: ''
},
]}
headerTitle={'Amicale'}
hasHeaderBackButton={true}
hasSideMenu={false}/>

View file

@ -9,7 +9,11 @@ type Props = {
}
const URL = 'http://planex.insa-toulouse.fr/salles.php';
const ROOM_URL = 'http://planex.insa-toulouse.fr/salles.php';
const PC_URL = 'http://planex.insa-toulouse.fr/sallesInfo.php';
const BIB_URL = 'https://bibbox.insa-toulouse.fr/';
const CUSTOM_CSS_GENERAL = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/rooms/customMobile.css';
const CUSTOM_CSS_Bib = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/rooms/customBibMobile.css';
/**
* Class defining the app's planex screen.
@ -18,10 +22,20 @@ const URL = 'http://planex.insa-toulouse.fr/salles.php';
export default class AvailableRoomScreen extends React.Component<Props> {
customInjectedJS: string;
customBibInjectedJS: string;
constructor() {
super();
this.customInjectedJS = '';
this.customInjectedJS =
'document.querySelector(\'head\').innerHTML += \'<meta name="viewport" content="width=device-width, initial-scale=1.0">\';' +
'document.querySelector(\'head\').innerHTML += \'<link rel="stylesheet" href="' + CUSTOM_CSS_GENERAL + '" type="text/css"/>\';' +
'let header = $(".table tbody tr:first");' +
'$("table").prepend("<thead></thead>");true;' + // Fix for crash on ios
'$("thead").append(header);true;';
this.customBibInjectedJS =
'document.querySelector(\'head\').innerHTML += \'<meta name="viewport" content="width=device-width, initial-scale=1.0">\';' +
'document.querySelector(\'head\').innerHTML += \'<link rel="stylesheet" href="' + CUSTOM_CSS_Bib + '" type="text/css"/>\';';
}
render() {
@ -29,11 +43,31 @@ export default class AvailableRoomScreen extends React.Component<Props> {
return (
<WebViewScreen
navigation={nav}
url={URL}
data={[
{
url: ROOM_URL,
icon: 'file-document-outline',
name: i18n.t('availableRoomScreen.normalRoom'),
customJS: this.customInjectedJS
},
{
url: PC_URL,
icon: 'monitor',
name: i18n.t('availableRoomScreen.computerRoom'),
customJS: this.customInjectedJS
},
{
url: BIB_URL,
icon: 'book',
name: i18n.t('availableRoomScreen.bibRoom'),
customJS: this.customBibInjectedJS
},
]}
customInjectedJS={this.customInjectedJS}
headerTitle={i18n.t('screens.availableRooms')}
hasHeaderBackButton={true}
hasSideMenu={false}/>
hasSideMenu={false}
hasFooter={false}/>
);
}
}

View file

@ -3,6 +3,7 @@
import * as React from 'react';
import ThemeManager from "../utils/ThemeManager";
import WebViewScreen from "../components/WebViewScreen";
import i18n from "i18n-js";
type Props = {
navigation: Object,
@ -11,8 +12,8 @@ type Props = {
const PLANEX_URL = 'http://planex.insa-toulouse.fr/';
const CUSTOM_CSS_GENERAL = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customMobile.css';
const CUSTOM_CSS_NIGHTMODE = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customDark.css';
const CUSTOM_CSS_GENERAL = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customMobile2.css';
const CUSTOM_CSS_NIGHTMODE = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customDark2.css';
/**
* Class defining the app's planex screen.
@ -26,7 +27,9 @@ export default class PlanexScreen extends React.Component<Props> {
super();
this.customInjectedJS =
'document.querySelector(\'head\').innerHTML += \'<meta name="viewport" content="width=device-width, initial-scale=1.0">\';' +
'document.querySelector(\'head\').innerHTML += \'<link rel="stylesheet" href="' + CUSTOM_CSS_GENERAL + '" type="text/css"/>\';';
'document.querySelector(\'head\').innerHTML += \'<link rel="stylesheet" href="' + CUSTOM_CSS_GENERAL + '" type="text/css"/>\';' +
'$(".fc-toolbar .fc-center").append(\'<p id="rotateToLandscape">' + i18n.t("planexScreen.rotateToLandscape") + '</p>\');' +
'$(".fc-toolbar .fc-center").append(\'<p id="rotateToPortrait">' + i18n.t("planexScreen.rotateToPortrait") + '</p>\');';
if (ThemeManager.getNightMode())
this.customInjectedJS += 'document.querySelector(\'head\').innerHTML += \'<link rel="stylesheet" href="' + CUSTOM_CSS_NIGHTMODE + '" type="text/css"/>\';';
}
@ -36,7 +39,14 @@ export default class PlanexScreen extends React.Component<Props> {
return (
<WebViewScreen
navigation={nav}
url={PLANEX_URL}
data={[
{
url: PLANEX_URL,
icon: '',
name: '',
customJS: this.customInjectedJS
},
]}
customInjectedJS={this.customInjectedJS}
headerTitle={'Planex'}
hasHeaderBackButton={false}

View file

@ -22,7 +22,14 @@ export default class TutorInsaScreen extends React.Component<Props> {
return (
<WebViewScreen
navigation={nav}
url={URL}
data={[
{
url: URL,
icon: '',
name: '',
customJS: ''
},
]}
headerTitle={'Tutor\'INSA'}
hasHeaderBackButton={true}
hasSideMenu={false}/>

View file

@ -22,7 +22,14 @@ export default class WiketudScreen extends React.Component<Props> {
return (
<WebViewScreen
navigation={nav}
url={URL}
data={[
{
url: URL,
icon: '',
name: '',
customJS: ''
},
]}
headerTitle={'Wiketud'}
hasHeaderBackButton={true}
hasSideMenu={false}/>

View file

@ -41,7 +41,7 @@
},
"updateSlide": {
"title": "New in this update!",
"text": "Say hello to the brand new dashboard!\nGet a quick look at the most important information right from the home screen!"
"text": "Get ready for your exams with Tutor'INSA! Book a Bib'Box or find an empty room to study, available in the side menu."
},
"buttons": {
"next": "Next",
@ -182,6 +182,15 @@
"machineRunningBody": "The machine n°{{number}} is still running"
}
},
"planexScreen": {
"rotateToLandscape": "Turn your screen to see the whole week",
"rotateToPortrait": "Turn your screen to see only 2 days"
},
"availableRoomScreen": {
"normalRoom": "Work",
"computerRoom": "Computer",
"bibRoom": "Bib'Box"
},
"general": {
"loading": "Loading...",
"networkError": "Unable to contact servers. Make sure you are connected to Internet."

View file

@ -41,7 +41,7 @@
},
"updateSlide": {
"title": "Nouveau dans cette mise à jour !",
"text": "Dites bonjour à la toute nouvelle Dashboard !\nAccédez rapidement aux informations les plus importantes depuis l'écran d'accueil !"
"text": "Préparez vos exams avec Tutor'INSA! Réservez une Bib'Box ou trouvez une salle libre pour travailler, disponible dans le menu à gauche. "
},
"buttons": {
"next": "Suivant",
@ -184,6 +184,15 @@
"machineRunningBody": "La machine n°{{number}} n'est pas encore terminée"
}
},
"planexScreen": {
"rotateToLandscape": "Tournez votre téléphone pour voir la semaine entière",
"rotateToPortrait": "Tournez votre téléphone pour voir seulement 2 jours"
},
"availableRoomScreen": {
"normalRoom": "Travail",
"computerRoom": "Ordi",
"bibRoom": "Bib'Box"
},
"general": {
"loading": "Chargement...",
"networkError": "Impossible de contacter les serveurs. Assurez vous d'être connecté à internet."

View file

@ -29,8 +29,8 @@ export default class AsyncStorageManager {
default: '1',
current: '',
},
showUpdate3: {
key: 'showUpdate3',
showUpdate4: {
key: 'showUpdate4',
default: '1',
current: '',
},