Merge branch 'dev'

This commit is contained in:
keplyx 2020-02-24 10:08:36 +01:00
commit e3e5dac314
32 changed files with 539 additions and 343 deletions

13
App.js
View file

@ -31,9 +31,16 @@ export default class App extends React.Component<Props, State> {
currentTheme: null, currentTheme: null,
}; };
onIntroDone: Function;
loadAssetsAsync: Function;
onLoadFinished: Function;
constructor(props: Object) { constructor(props: Object) {
super(props); super(props);
LocaleManager.initTranslations(); LocaleManager.initTranslations();
this.onIntroDone = this.onIntroDone.bind(this);
this.loadAssetsAsync = this.loadAssetsAsync.bind(this);
this.onLoadFinished = this.onLoadFinished.bind(this);
} }
/** /**
@ -102,14 +109,14 @@ export default class App extends React.Component<Props, State> {
if (this.state.isLoading) { if (this.state.isLoading) {
return ( return (
<AppLoading <AppLoading
startAsync={() => this.loadAssetsAsync()} startAsync={this.loadAssetsAsync}
onFinish={() => this.onLoadFinished()} onFinish={this.onLoadFinished}
onError={console.warn} onError={console.warn}
/> />
); );
} }
if (this.state.showIntro || this.state.showUpdate) { if (this.state.showIntro || this.state.showUpdate) {
return <CustomIntroSlider onDone={() => this.onIntroDone()} return <CustomIntroSlider onDone={this.onIntroDone}
isUpdate={this.state.showUpdate && !this.state.showIntro}/>; isUpdate={this.state.showUpdate && !this.state.showIntro}/>;
} else { } else {
const AppNavigator = createAppContainerWithInitialRoute(AsyncStorageManager.getInstance().preferences.defaultStartScreen.current); const AppNavigator = createAppContainerWithInitialRoute(AsyncStorageManager.getInstance().preferences.defaultStartScreen.current);

View file

@ -13,7 +13,7 @@ Ce dépot contient les sources de cette application, modifiable par les étudian
Vous voulez influencer le développement ? C'est très simple ! Vous voulez influencer le développement ? C'est très simple !
Pas besoin de connaissance, il est possible d'aider simplement en proposant des améliorations ou en rapportant des bugs par mail (vergnet@etud.insa-toulouse.fr) ou sur [cette page](https://git.srv-falcon.etud.insa-toulouse.fr/vergnet/application-amicale/issues), en vous connectant avec vos login INSA. Pas besoin de connaissance, il est possible d'aider simplement en proposant des améliorations ou en rapportant des bugs par mail (vergnet@etud.insa-toulouse.fr) ou sur [cette page](https://git.etud.insa-toulouse.fr/vergnet/application-amicale/issues), en vous connectant avec vos login INSA.
Si vous avez assez de connaissances et vous souhaitez proposer des modifications dans le code, installez l'application sur votre machine, réalisez votre modification et créez une 'pull request'. Si vous avez assez de connaissances et vous souhaitez proposer des modifications dans le code, installez l'application sur votre machine, réalisez votre modification et créez une 'pull request'.

View file

@ -10,7 +10,7 @@
"android", "android",
"web" "web"
], ],
"version": "1.5.0", "version": "1.5.1",
"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": 13, "versionCode": 14,
"icon": "./assets/android.icon.png", "icon": "./assets/android.icon.png",
"adaptiveIcon": { "adaptiveIcon": {
"foregroundImage": "./assets/android.adaptive-icon.png", "foregroundImage": "./assets/android.adaptive-icon.png",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

View file

@ -30,7 +30,6 @@ type State = {
export default class BaseContainer extends React.Component<Props, State> { export default class BaseContainer extends React.Component<Props, State> {
static defaultProps = { static defaultProps = {
headerRightButton: <View/>, headerRightButton: <View/>,
hasTabs: false, hasTabs: false,
@ -46,40 +45,61 @@ export default class BaseContainer extends React.Component<Props, State> {
isHeaderVisible: true, isHeaderVisible: true,
}; };
toggle() { onDrawerPress: Function;
onWillFocus: Function;
onWillBlur: Function;
onChangeOrientation: Function;
constructor() {
super();
this.onDrawerPress = this.onDrawerPress.bind(this);
this.onWillFocus = this.onWillFocus.bind(this);
this.onWillBlur = this.onWillBlur.bind(this);
this.onChangeOrientation = this.onChangeOrientation.bind(this);
}
onDrawerPress() {
this.props.navigation.toggleDrawer(); this.props.navigation.toggleDrawer();
} }
onWillFocus() {
if (this.props.enableRotation) {
ScreenOrientation.unlockAsync();
ScreenOrientation.addOrientationChangeListener(this.onChangeOrientation);
}
}
onWillBlur() {
if (this.props.enableRotation)
ScreenOrientation.lockAsync(ScreenOrientation.Orientation.PORTRAIT);
}
onChangeOrientation(OrientationChangeEvent) {
if (this.props.hideHeaderOnLandscape) {
let isLandscape = OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE ||
OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE_LEFT ||
OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE_RIGHT;
this.setState({isHeaderVisible: !isLandscape});
const setParamsAction = NavigationActions.setParams({
params: {showTabBar: !isLandscape},
key: this.props.navigation.state.key,
});
this.props.navigation.dispatch(setParamsAction);
StatusBar.setHidden(isLandscape);
}
}
/** /**
* Register for blur event to close side menu on screen change * Register for blur event to close side menu on screen change
*/ */
componentDidMount() { componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener( this.willFocusSubscription = this.props.navigation.addListener(
'willFocus', 'willFocus',
() => { this.onWillFocus
if (this.props.enableRotation) { );
ScreenOrientation.unlockAsync();
ScreenOrientation.addOrientationChangeListener((OrientationChangeEvent) => {
if (this.props.hideHeaderOnLandscape) {
let isLandscape = OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE ||
OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE_LEFT ||
OrientationChangeEvent.orientationInfo.orientation === ScreenOrientation.Orientation.LANDSCAPE_RIGHT;
this.setState({isHeaderVisible: !isLandscape});
const setParamsAction = NavigationActions.setParams({
params: {showTabBar: !isLandscape},
key: this.props.navigation.state.key,
});
this.props.navigation.dispatch(setParamsAction);
StatusBar.setHidden(isLandscape);
}
});
}
});
this.willBlurSubscription = this.props.navigation.addListener( this.willBlurSubscription = this.props.navigation.addListener(
'willBlur', 'willBlur',
() => { this.onWillBlur
if (this.props.enableRotation)
ScreenOrientation.lockAsync(ScreenOrientation.Orientation.PORTRAIT);
}
); );
} }
@ -93,7 +113,9 @@ export default class BaseContainer extends React.Component<Props, State> {
this.willFocusSubscription.remove(); this.willFocusSubscription.remove();
} }
getMainContainer() {
render() {
// console.log("rendering BaseContainer");
return ( return (
<Container> <Container>
{this.state.isHeaderVisible ? {this.state.isHeaderVisible ?
@ -104,7 +126,7 @@ export default class BaseContainer extends React.Component<Props, State> {
leftButton={ leftButton={
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => this.toggle()}> onPress={this.onDrawerPress}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="menu"/> icon="menu"/>
@ -118,9 +140,4 @@ export default class BaseContainer extends React.Component<Props, State> {
</Container> </Container>
); );
} }
render() {
return (this.getMainContainer());
}
} }

View file

@ -32,11 +32,10 @@ type Props = {
* @prop navigation {Object} The navigation object from react navigation * @prop navigation {Object} The navigation object from react navigation
*/ */
export default class CustomHeader extends React.Component<Props> { export default class CustomHeader extends React.Component<Props> {
static defaultProps = { static defaultProps = {
hasBackButton: false, hasBackButton: false,
hasSearchField: false, hasSearchField: false,
searchCallback: () => null, searchCallback: null,
shouldFocusSearchBar: false, shouldFocusSearchBar: false,
title: '', title: '',
subtitle: '', subtitle: '',
@ -45,10 +44,28 @@ export default class CustomHeader extends React.Component<Props> {
hasTabs: false, hasTabs: false,
}; };
onPressBack: Function;
constructor() {
super();
this.onPressBack = this.onPressBack.bind(this);
}
shouldComponentUpdate(nextProps: Props): boolean {
return nextProps.title !== this.props.title ||
nextProps.subtitle !== this.props.subtitle ||
nextProps.hasBackButton !== this.props.hasBackButton ||
nextProps.hasSearchField !== this.props.hasSearchField ||
nextProps.shouldFocusSearchBar !== this.props.shouldFocusSearchBar ||
nextProps.hasTabs !== this.props.hasTabs ||
nextProps.rightButton !== this.props.rightButton ||
nextProps.leftButton !== this.props.leftButton;
}
componentDidMount() { componentDidMount() {
if (this.refs.searchInput !== undefined && this.refs.searchInput._root !== undefined && this.props.shouldFocusSearchBar) { if (this.refs.searchInput !== undefined && this.refs.searchInput._root !== undefined && this.props.shouldFocusSearchBar) {
// does not work if called to early for some reason... // does not work if called too early for some reason...
setTimeout(() => this.refs.searchInput._root.focus(), 500); setTimeout(this.refs.searchInput._root.focus, 500);
} }
} }
@ -67,7 +84,7 @@ export default class CustomHeader extends React.Component<Props> {
ref="searchInput" ref="searchInput"
placeholder={i18n.t('proximoScreen.search')} placeholder={i18n.t('proximoScreen.search')}
placeholderTextColor={ThemeManager.getCurrentThemeVariables().toolbarPlaceholderColor} placeholderTextColor={ThemeManager.getCurrentThemeVariables().toolbarPlaceholderColor}
onChangeText={(text) => this.props.searchCallback(text)}/> onChangeText={this.props.searchCallback}/>
</Item> </Item>
</Body> </Body>
); );
@ -87,17 +104,20 @@ export default class CustomHeader extends React.Component<Props> {
} }
onPressBack() {
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
}
render() { render() {
// console.log("rendering CustomHeader");
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 ?
if (this.props.hasBackButton) if (this.props.hasBackButton)
button = button =
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => { onPress={this.onPressBack}>
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={Platform.OS === 'ios' ? 'chevron-left' : "arrow-left"}/> icon={Platform.OS === 'ios' ? 'chevron-left' : "arrow-left"}/>

View file

@ -117,7 +117,7 @@ export default class CustomIntroSlider extends React.Component<Props> {
* @param item * @param item
* @param dimensions * @param dimensions
*/ */
static getIntroRenderItem(item: Object, dimensions: Object) { static getIntroRenderItem({item, dimensions}: Object) {
return ( return (
<LinearGradient <LinearGradient
@ -143,9 +143,9 @@ export default class CustomIntroSlider extends React.Component<Props> {
render() { render() {
return ( return (
<AppIntroSlider <AppIntroSlider
renderItem={({item, dimensions}) => CustomIntroSlider.getIntroRenderItem(item, dimensions)} renderItem={CustomIntroSlider.getIntroRenderItem}
slides={this.props.isUpdate ? this.updateSlides : this.introSlides} slides={this.props.isUpdate ? this.updateSlides : this.introSlides}
onDone={() => this.props.onDone()} onDone={this.props.onDone}
bottomButton bottomButton
showSkipButton showSkipButton
skipLabel={i18n.t('intro.buttons.skip')} skipLabel={i18n.t('intro.buttons.skip')}

View file

@ -30,7 +30,16 @@ export default class CustomMaterialIcon extends React.Component<Props> {
width: 30, width: 30,
}; };
shouldComponentUpdate(nextProps: Props): boolean {
return nextProps.icon !== this.props.icon ||
nextProps.active !== this.props.active ||
nextProps.width !== this.props.width ||
nextProps.fontSize !== this.props.fontSize ||
nextProps.color !== this.props.color;
}
render() { render() {
// console.log("rendering icon " + this.props.icon);
return ( return (
<Icon <Icon
active active

View file

@ -25,13 +25,18 @@ type Props = {
} }
export default class DashboardItem extends React.Component<Props> { export default class DashboardItem extends React.Component<Props> {
static defaultProps = { static defaultProps = {
isSquare: false, isSquare: false,
isSquareLeft: true, isSquareLeft: true,
displayEvent: undefined, displayEvent: undefined,
}; };
shouldComponentUpdate(nextProps: Props): boolean {
return nextProps.isAvailable !== this.props.isAvailable ||
nextProps.subtitle !== this.props.subtitle;
}
/** /**
* Convert the date string given by in the event list json to a date object * Convert the date string given by in the event list json to a date object
* @param dateString * @param dateString
@ -203,6 +208,7 @@ export default class DashboardItem extends React.Component<Props> {
render() { render() {
// console.log("rendering DashboardItem " + this.props.title);
let marginRight = 10; let marginRight = 10;
if (this.props.isSquare) { if (this.props.isSquare) {
if (this.props.isSquareLeft) if (this.props.isSquareLeft)

View file

@ -25,7 +25,6 @@ type State = {
* Used by inheriting from it and redefining getters. * Used by inheriting from it and redefining getters.
*/ */
export default class FetchedDataSectionList extends React.Component<Props, State> { export default class FetchedDataSectionList extends React.Component<Props, State> {
webDataManager: WebDataManager; webDataManager: WebDataManager;
willFocusSubscription: function; willFocusSubscription: function;
@ -42,12 +41,29 @@ export default class FetchedDataSectionList extends React.Component<Props, State
machinesWatched: [], machinesWatched: [],
}; };
onRefresh: Function;
onFetchSuccess: Function;
onFetchError: Function;
renderSectionHeaderEmpty: Function;
renderSectionHeaderNotEmpty: Function;
renderItemEmpty: Function;
renderItemNotEmpty: Function;
constructor(fetchUrl: string, refreshTime: number) { constructor(fetchUrl: string, refreshTime: number) {
super(); super();
this.webDataManager = new WebDataManager(fetchUrl); this.webDataManager = new WebDataManager(fetchUrl);
this.refreshTime = refreshTime; this.refreshTime = refreshTime;
// creating references to functions used in render()
this.onRefresh = this.onRefresh.bind(this);
this.onFetchSuccess = this.onFetchSuccess.bind(this);
this.onFetchError = this.onFetchError.bind(this);
this.renderSectionHeaderEmpty = this.renderSectionHeader.bind(this, true);
this.renderSectionHeaderNotEmpty = this.renderSectionHeader.bind(this, false);
this.renderItemEmpty = this.renderItem.bind(this, true);
this.renderItemNotEmpty = this.renderItem.bind(this, false);
} }
/** /**
* Get the translation for the header in the current language * Get the translation for the header in the current language
* @return {string} * @return {string}
@ -74,26 +90,18 @@ export default class FetchedDataSectionList extends React.Component<Props, State
*/ */
componentDidMount() { componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener( this.willFocusSubscription = this.props.navigation.addListener(
'willFocus', 'willFocus', this.onScreenFocus.bind(this));
() => {
this.onScreenFocus();
}
);
this.willBlurSubscription = this.props.navigation.addListener( this.willBlurSubscription = this.props.navigation.addListener(
'willBlur', 'willBlur', this.onScreenBlur.bind(this));
() => {
this.onScreenBlur();
}
);
} }
/** /**
* Refresh data when focusing the screen and setup a refresh interval if asked to * Refresh data when focusing the screen and setup a refresh interval if asked to
*/ */
onScreenFocus() { onScreenFocus() {
this._onRefresh(); this.onRefresh();
if (this.refreshTime > 0) if (this.refreshTime > 0)
this.refreshInterval = setInterval(() => this._onRefresh(), this.refreshTime) this.refreshInterval = setInterval(this.onRefresh.bind(this), this.refreshTime)
} }
/** /**
@ -113,11 +121,29 @@ export default class FetchedDataSectionList extends React.Component<Props, State
this.willFocusSubscription.remove(); this.willFocusSubscription.remove();
} }
onFetchSuccess(fetchedData: Object) {
this.setState({
fetchedData: fetchedData,
refreshing: false,
firstLoading: false
});
this.lastRefresh = new Date();
}
onFetchError() {
this.setState({
fetchedData: {},
refreshing: false,
firstLoading: false
});
this.webDataManager.showUpdateToast(this.getUpdateToastTranslations()[0], this.getUpdateToastTranslations()[1]);
}
/** /**
* Refresh data and show a toast if any error occurred * Refresh data and show a toast if any error occurred
* @private * @private
*/ */
_onRefresh = () => { onRefresh() {
let canRefresh; let canRefresh;
if (this.lastRefresh !== undefined) if (this.lastRefresh !== undefined)
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh; canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh;
@ -127,25 +153,10 @@ export default class FetchedDataSectionList extends React.Component<Props, State
if (canRefresh) { if (canRefresh) {
this.setState({refreshing: true}); this.setState({refreshing: true});
this.webDataManager.readData() this.webDataManager.readData()
.then((fetchedData) => { .then(this.onFetchSuccess)
this.setState({ .catch(this.onFetchError);
fetchedData: fetchedData,
refreshing: false,
firstLoading: false
});
this.lastRefresh = new Date();
})
.catch(() => {
this.setState({
fetchedData: {},
refreshing: false,
firstLoading: false
});
this.webDataManager.showUpdateToast(this.getUpdateToastTranslations()[0], this.getUpdateToastTranslations()[1]);
});
} }
}
};
/** /**
* Get the render item to be used for display in the list. * Get the render item to be used for display in the list.
@ -153,10 +164,9 @@ export default class FetchedDataSectionList extends React.Component<Props, State
* *
* @param item * @param item
* @param section * @param section
* @param data
* @return {*} * @return {*}
*/ */
getRenderItem(item: Object, section: Object, data: Object) { getRenderItem(item: Object, section: Object) {
return <View/>; return <View/>;
} }
@ -222,6 +232,11 @@ export default class FetchedDataSectionList extends React.Component<Props, State
return []; return [];
} }
datasetKeyExtractor(item: Object) {
return item.text
}
/** /**
* Create the dataset when no fetched data is available. * Create the dataset when no fetched data is available.
* No need to be overridden, has good defaults. * No need to be overridden, has good defaults.
@ -243,7 +258,7 @@ export default class FetchedDataSectionList extends React.Component<Props, State
'access-point-network-off' 'access-point-network-off'
} }
], ],
keyExtractor: (item: Object) => item.text, keyExtractor: this.datasetKeyExtractor,
} }
]; ];
} }
@ -275,6 +290,19 @@ export default class FetchedDataSectionList extends React.Component<Props, State
return true; return true;
} }
renderSectionHeader(isEmpty: boolean, {section: {title}} : Object) {
return isEmpty ?
<View/> :
this.getRenderSectionHeader(title)
}
renderItem(isEmpty: boolean, {item, section}: Object) {
return isEmpty ?
this.getEmptyRenderItem(item.text, item.isSpinner, item.icon) :
this.getRenderItem(item, section)
}
/** /**
* Get the section list render using the generated dataset * Get the section list render using the generated dataset
* *
@ -292,19 +320,11 @@ export default class FetchedDataSectionList extends React.Component<Props, State
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={this.state.refreshing} refreshing={this.state.refreshing}
onRefresh={this._onRefresh} onRefresh={this.onRefresh}
/> />
} }
renderSectionHeader={({section: {title}}) => renderSectionHeader={isEmpty ? this.renderSectionHeaderEmpty : this.renderSectionHeaderNotEmpty}
isEmpty ? renderItem={isEmpty ? this.renderItemEmpty : this.renderItemNotEmpty}
<View/> :
this.getRenderSectionHeader(title)
}
renderItem={({item, section}) =>
isEmpty ?
this.getEmptyRenderItem(item.text, item.isSpinner, item.icon) :
this.getRenderItem(item, section, dataset)
}
style={{minHeight: 300, width: '100%'}} style={{minHeight: 300, width: '100%'}}
contentContainerStyle={ contentContainerStyle={
isEmpty ? isEmpty ?
@ -351,11 +371,12 @@ export default class FetchedDataSectionList extends React.Component<Props, State
} }
render() { render() {
const nav = this.props.navigation; // console.log("rendering FetchedDataSectionList");
const dataset = this.createDataset(this.state.fetchedData); const dataset = this.createDataset(this.state.fetchedData);
return ( return (
<BaseContainer <BaseContainer
navigation={nav} headerTitle={this.getHeaderTranslation()} navigation={this.props.navigation}
headerTitle={this.getHeaderTranslation()}
headerRightButton={this.getRightButton()} headerRightButton={this.getRightButton()}
hasTabs={this.hasTabs()} hasTabs={this.hasTabs()}
hasBackButton={this.hasBackButton()} hasBackButton={this.hasBackButton()}
@ -375,5 +396,4 @@ export default class FetchedDataSectionList extends React.Component<Props, State
</BaseContainer> </BaseContainer>
); );
} }
} }

View file

@ -30,6 +30,8 @@ export default class SideBar extends React.Component<Props, State> {
active: 'Home', active: 'Home',
}; };
getRenderItem: Function;
/** /**
* Generate the datasets * Generate the datasets
* *
@ -38,7 +40,6 @@ export default class SideBar extends React.Component<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
// Dataset used to render the drawer // Dataset used to render the drawer
// If the link field is defined, clicking on the item will open the link
this.dataSet = [ this.dataSet = [
{ {
name: i18n.t('sidenav.divider1'), name: i18n.t('sidenav.divider1'),
@ -103,21 +104,34 @@ export default class SideBar extends React.Component<Props, State> {
icon: "information", icon: "information",
}, },
]; ];
this.getRenderItem = this.getRenderItem.bind(this);
} }
getRenderItem(item: Object) { shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
return nextState.active !== this.state.active;
}
onListItemPress(route: string) {
this.props.navigation.navigate(route);
}
listKeyExtractor(item: Object) {
return item.route;
}
getRenderItem({item}: Object) {
const onListItemPress = this.onListItemPress.bind(this, item.route);
if (item.icon !== undefined) { if (item.icon !== undefined) {
return ( return (
<ListItem <ListItem
button button
noBorder noBorder
selected={this.state.active === item.route} selected={this.state.active === item.route}
onPress={() => { onPress={onListItemPress}
if (item.link !== undefined)
Linking.openURL(item.link).catch((err) => console.error('Error opening link', err));
else
this.navigateToScreen(item.route);
}}
> >
<Left> <Left>
<CustomMaterialIcon <CustomMaterialIcon
@ -155,15 +169,8 @@ export default class SideBar extends React.Component<Props, State> {
} }
/**
* Navigate to the selected route
* @param route {string} The route name to navigate to
*/
navigateToScreen(route: string) {
this.props.navigation.navigate(route);
};
render() { render() {
// console.log("rendering SideBar");
return ( return (
<Container style={{ <Container style={{
backgroundColor: ThemeManager.getCurrentThemeVariables().sideMenuBgColor, backgroundColor: ThemeManager.getCurrentThemeVariables().sideMenuBgColor,
@ -172,8 +179,8 @@ export default class SideBar extends React.Component<Props, State> {
<FlatList <FlatList
data={this.dataSet} data={this.dataSet}
extraData={this.state} extraData={this.state}
keyExtractor={(item) => item.route} keyExtractor={this.listKeyExtractor}
renderItem={({item}) => this.getRenderItem(item)} renderItem={this.getRenderItem}
/> />
</Container> </Container>
); );

View file

@ -35,6 +35,21 @@ export default class WebViewScreen extends React.Component<Props> {
}; };
webviewArray: Array<WebView> = []; webviewArray: Array<WebView> = [];
onRefreshClicked: Function;
onWebviewRef: Function;
onGoBackWebview: Function;
onGoForwardWebview: Function;
onOpenWebLink: Function;
constructor() {
super();
this.onRefreshClicked = this.onRefreshClicked.bind(this);
this.onWebviewRef = this.onWebviewRef.bind(this);
this.onGoBackWebview = this.onGoBackWebview.bind(this);
this.onGoForwardWebview = this.onGoForwardWebview.bind(this);
this.onOpenWebLink = this.onOpenWebLink.bind(this);
}
openWebLink(url: string) { openWebLink(url: string) {
Linking.openURL(url).catch((err) => console.error('Error opening link', err)); Linking.openURL(url).catch((err) => console.error('Error opening link', err));
} }
@ -43,7 +58,7 @@ export default class WebViewScreen extends React.Component<Props> {
return ( return (
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => clickAction()}> onPress={clickAction}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon={icon}/> icon={icon}/>
@ -54,36 +69,62 @@ export default class WebViewScreen extends React.Component<Props> {
getRefreshButton() { getRefreshButton() {
return ( return (
<View style={{flexDirection: 'row'}}> <View style={{flexDirection: 'row'}}>
{this.getHeaderButton(() => this.refreshWebview(), 'refresh')} {this.getHeaderButton(this.onRefreshClicked, 'refresh')}
</View> </View>
); );
}; };
refreshWebview() { onRefreshClicked() {
for (let view of this.webviewArray) { for (let view of this.webviewArray) {
if (view !== null) if (view !== null)
view.reload(); view.reload();
} }
} }
goBackWebview() { onGoBackWebview() {
for (let view of this.webviewArray) { for (let view of this.webviewArray) {
if (view !== null) if (view !== null)
view.goBack(); view.goBack();
} }
} }
goForwardWebview() { onGoForwardWebview() {
for (let view of this.webviewArray) { for (let view of this.webviewArray) {
if (view !== null) if (view !== null)
view.goForward(); view.goForward();
} }
} }
onOpenWebLink() {
this.openWebLink(this.props.data[0]['url'])
}
onWebviewRef(ref: WebView) {
this.webviewArray.push(ref)
}
getRenderLoading() {
return (
<View style={{
backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor,
position: 'absolute',
top: 0,
right: 0,
width: '100%',
height: '100%',
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}}>
<Spinner/>
</View>
);
}
getWebview(obj: Object) { getWebview(obj: Object) {
return ( return (
<WebView <WebView
ref={ref => (this.webviewArray.push(ref))} ref={this.onWebviewRef}
source={{uri: obj['url']}} source={{uri: obj['url']}}
style={{ style={{
width: '100%', width: '100%',
@ -92,21 +133,7 @@ export default class WebViewScreen extends React.Component<Props> {
startInLoadingState={true} startInLoadingState={true}
injectedJavaScript={obj['customJS']} injectedJavaScript={obj['customJS']}
javaScriptEnabled={true} javaScriptEnabled={true}
renderLoading={() => renderLoading={this.getRenderLoading}
<View style={{
backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor,
position: 'absolute',
top: 0,
right: 0,
width: '100%',
height: '100%',
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}}>
<Spinner/>
</View>
}
/> />
); );
} }
@ -133,6 +160,7 @@ export default class WebViewScreen extends React.Component<Props> {
} }
render() { render() {
// console.log("rendering WebViewScreen");
const nav = this.props.navigation; const nav = this.props.navigation;
this.webviewArray = []; this.webviewArray = [];
return ( return (
@ -166,7 +194,7 @@ export default class WebViewScreen extends React.Component<Props> {
<Left style={{ <Left style={{
paddingLeft: 6, paddingLeft: 6,
}}> }}>
{this.getHeaderButton(() => this.openWebLink(this.props.data[0]['url']), 'open-in-new')} {this.getHeaderButton(this.onOpenWebLink, 'open-in-new')}
</Left> </Left>
<Body/> <Body/>
<Right style={{ <Right style={{
@ -179,8 +207,8 @@ export default class WebViewScreen extends React.Component<Props> {
marginRight: 0, marginRight: 0,
marginLeft: 'auto' marginLeft: 'auto'
}}> }}>
{this.getHeaderButton(() => this.goBackWebview(), 'chevron-left')} {this.getHeaderButton(this.onGoBackWebview, 'chevron-left')}
{this.getHeaderButton(() => this.goForwardWebview(), 'chevron-right')} {this.getHeaderButton(this.onGoForwardWebview, 'chevron-right')}
</View> </View>
</Right> </Right>
</Footer> : <View/>} </Footer> : <View/>}

View file

@ -15,7 +15,7 @@ import ThemeManager from "../../utils/ThemeManager";
const links = { const links = {
appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148', appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
playstore: 'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application', playstore: 'https://play.google.com/store/apps/details?id=fr.amicaleinsat.application',
git: 'https://git.srv-falcon.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md', git: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/README.md',
bugsMail: 'mailto:vergnet@etud.insa-toulouse.fr?' + bugsMail: 'mailto:vergnet@etud.insa-toulouse.fr?' +
'subject=' + 'subject=' +
'[BUG] Application Amicale INSA Toulouse' + '[BUG] Application Amicale INSA Toulouse' +
@ -25,9 +25,9 @@ const links = {
'Nature du problème :\n\n\n' + 'Nature du problème :\n\n\n' +
'Étapes pour reproduire ce pb :\n\n\n\n' + 'Étapes pour reproduire ce pb :\n\n\n\n' +
'Stp corrige le pb, bien cordialement.', 'Stp corrige le pb, bien cordialement.',
bugsGit: 'https://git.srv-falcon.etud.insa-toulouse.fr/vergnet/application-amicale/issues', bugsGit: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/issues',
changelog: 'https://git.srv-falcon.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md', changelog: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/Changelog.md',
license: 'https://git.srv-falcon.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE', license: 'https://git.etud.insa-toulouse.fr/vergnet/application-amicale/src/branch/master/LICENSE',
authorMail: "mailto:vergnet@etud.insa-toulouse.fr?" + authorMail: "mailto:vergnet@etud.insa-toulouse.fr?" +
"subject=" + "subject=" +
"Application Amicale INSA Toulouse" + "Application Amicale INSA Toulouse" +
@ -187,9 +187,14 @@ export default class AboutScreen extends React.Component<Props, State> {
}, },
]; ];
getCardItem: Function;
getMainCard: Function;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.modalRef = React.createRef(); this.modalRef = React.createRef();
this.getCardItem = this.getCardItem.bind(this);
this.getMainCard = this.getMainCard.bind(this);
} }
getAppCard() { getAppCard() {
@ -197,7 +202,7 @@ export default class AboutScreen extends React.Component<Props, State> {
<Card> <Card>
<CardItem> <CardItem>
<Left> <Left>
<Thumbnail square source={require('../../assets/icon.png')}/> <Thumbnail square source={require('../../assets/android.icon.png')}/>
<Body> <Body>
<H1>{appJson.expo.name}</H1> <H1>{appJson.expo.name}</H1>
<Text note> <Text note>
@ -210,10 +215,8 @@ export default class AboutScreen extends React.Component<Props, State> {
data={this.appData} data={this.appData}
extraData={this.state} extraData={this.state}
keyExtractor={(item) => item.icon} keyExtractor={(item) => item.icon}
listKey={(item) => "app"} listKey={"app"}
renderItem={({item}) => renderItem={this.getCardItem}
this.getCardItem(item.onPressCallback, item.icon, item.text, item.showChevron, item.showOnlyDebug)
}
/> />
</Card> </Card>
); );
@ -241,10 +244,8 @@ export default class AboutScreen extends React.Component<Props, State> {
data={this.authorData} data={this.authorData}
extraData={this.state} extraData={this.state}
keyExtractor={(item) => item.icon} keyExtractor={(item) => item.icon}
listKey={(item) => "team1"} listKey={"team1"}
renderItem={({item}) => renderItem={this.getCardItem}
this.getCardItem(item.onPressCallback, item.icon, item.text, item.showChevron, item.showOnlyDebug)
}
/> />
<CardItem header> <CardItem header>
<Text>{i18n.t('aboutScreen.additionalDev')}</Text> <Text>{i18n.t('aboutScreen.additionalDev')}</Text>
@ -253,10 +254,8 @@ export default class AboutScreen extends React.Component<Props, State> {
data={this.additionalDevData} data={this.additionalDevData}
extraData={this.state} extraData={this.state}
keyExtractor={(item) => item.icon} keyExtractor={(item) => item.icon}
listKey={(item) => "team2"} listKey={"team2"}
renderItem={({item}) => renderItem={this.getCardItem}
this.getCardItem(item.onPressCallback, item.icon, item.text, item.showChevron, item.showOnlyDebug)
}
/> />
</Card> </Card>
); );
@ -272,10 +271,8 @@ export default class AboutScreen extends React.Component<Props, State> {
data={this.technoData} data={this.technoData}
extraData={this.state} extraData={this.state}
keyExtractor={(item) => item.icon} keyExtractor={(item) => item.icon}
listKey={(item) => "techno"} listKey={"techno"}
renderItem={({item}) => renderItem={this.getCardItem}
this.getCardItem(item.onPressCallback, item.icon, item.text, item.showChevron, item.showOnlyDebug)
}
/> />
</Card> </Card>
); );
@ -284,24 +281,19 @@ export default class AboutScreen extends React.Component<Props, State> {
/** /**
* Get a clickable card item to be rendered inside a card. * Get a clickable card item to be rendered inside a card.
* *
* @param onPressCallback The callback to use when the item is clicked
* @param icon The icon name to use from MaterialCommunityIcons
* @param text The text to show
* @param showChevron Whether to show a chevron indicating this button will change screen
* @param showOnlyInDebug Should we show te current item only in debug mode?
* @returns {React.Node} * @returns {React.Node}
*/ */
getCardItem(onPressCallback: Function, icon: string, text: string, showChevron: boolean, showOnlyInDebug: boolean) { getCardItem({item}: Object) {
let shouldShow = !showOnlyInDebug || (showOnlyInDebug && this.state.isDebugUnlocked); let shouldShow = !item.showOnlyInDebug || (item.showOnlyInDebug && this.state.isDebugUnlocked);
if (shouldShow) { if (shouldShow) {
return ( return (
<CardItem button <CardItem button
onPress={onPressCallback}> onPress={item.onPressCallback}>
<Left> <Left>
<CustomMaterialIcon icon={icon}/> <CustomMaterialIcon icon={item.icon}/>
<Text>{text}</Text> <Text>{item.text}</Text>
</Left> </Left>
{showChevron ? {item.showChevron ?
<Right> <Right>
<CustomMaterialIcon icon="chevron-right" <CustomMaterialIcon icon="chevron-right"
fontSize={20}/> fontSize={20}/>
@ -331,6 +323,8 @@ export default class AboutScreen extends React.Component<Props, State> {
} }
getBugReportModal() { getBugReportModal() {
const onPressMail = openWebLink.bind(this, links.bugsMail);
const onPressGit = openWebLink.bind(this, links.bugsGit);
return ( return (
<Modalize ref={this.modalRef} <Modalize ref={this.modalRef}
adjustToContentHeight adjustToContentHeight
@ -349,7 +343,7 @@ export default class AboutScreen extends React.Component<Props, State> {
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto', marginRight: 'auto',
}} }}
onPress={() => openWebLink(links.bugsMail)}> onPress={onPressMail}>
<CustomMaterialIcon <CustomMaterialIcon
icon={'email'} icon={'email'}
color={'#fff'}/> color={'#fff'}/>
@ -361,7 +355,7 @@ export default class AboutScreen extends React.Component<Props, State> {
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto', marginRight: 'auto',
}} }}
onPress={() => openWebLink(links.bugsGit)}> onPress={onPressGit}>
<CustomMaterialIcon <CustomMaterialIcon
icon={'git'} icon={'git'}
color={'#fff'}/> color={'#fff'}/>
@ -378,7 +372,7 @@ export default class AboutScreen extends React.Component<Props, State> {
} }
} }
getMainCard(item: Object) { getMainCard({item}: Object) {
switch (item.id) { switch (item.id) {
case 'app': case 'app':
return this.getAppCard(); return this.getAppCard();
@ -401,9 +395,7 @@ export default class AboutScreen extends React.Component<Props, State> {
data={this.dataOrder} data={this.dataOrder}
extraData={this.state} extraData={this.state}
keyExtractor={(item) => item.id} keyExtractor={(item) => item.id}
renderItem={({item}) => renderItem={this.getMainCard}
this.getMainCard(item)
}
/> />
</Container> </Container>
); );

View file

@ -14,7 +14,7 @@ import DashboardItem from "../components/DashboardItem";
const ICON_AMICALE = require('../assets/amicale.png'); const ICON_AMICALE = require('../assets/amicale.png');
const NAME_AMICALE = 'Amicale INSA Toulouse'; const NAME_AMICALE = 'Amicale INSA Toulouse';
const DATA_URL = "https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/dashboard/dashboard_data.json"; const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/dashboard/dashboard_data.json";
const SECTIONS_ID = [ const SECTIONS_ID = [
'dashboard', 'dashboard',
@ -38,8 +38,33 @@ function openWebLink(link) {
*/ */
export default class HomeScreen extends FetchedDataSectionList { export default class HomeScreen extends FetchedDataSectionList {
onProxiwashClick: Function;
onTutorInsaClick: Function;
onMenuClick: Function;
onProximoClick: Function;
constructor() { constructor() {
super(DATA_URL, REFRESH_TIME); super(DATA_URL, REFRESH_TIME);
this.onProxiwashClick = this.onProxiwashClick.bind(this);
this.onTutorInsaClick = this.onTutorInsaClick.bind(this);
this.onMenuClick = this.onMenuClick.bind(this);
this.onProximoClick = this.onProximoClick.bind(this);
}
onProxiwashClick() {
this.props.navigation.navigate('Proxiwash');
}
onTutorInsaClick() {
this.props.navigation.navigate('TutorInsaScreen');
}
onProximoClick() {
this.props.navigation.navigate('Proximo');
}
onMenuClick() {
this.props.navigation.navigate('SelfMenuScreen');
} }
/** /**
@ -289,6 +314,14 @@ export default class HomeScreen extends FetchedDataSectionList {
} }
clickAction(isAvailable: boolean, displayEvent: Object) {
if (isAvailable)
this.props.navigation.navigate('PlanningDisplayScreen', {data: displayEvent});
else
this.props.navigation.navigate('PlanningScreen');
};
getDashboardEventItem(content: Array<Object>) { getDashboardEventItem(content: Array<Object>) {
let icon = 'calendar-range'; let icon = 'calendar-range';
let color = ThemeManager.getCurrentThemeVariables().planningColor; let color = ThemeManager.getCurrentThemeVariables().planningColor;
@ -310,12 +343,6 @@ export default class HomeScreen extends FetchedDataSectionList {
</Text>; </Text>;
} else } else
subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA'); subtitle = i18n.t('homeScreen.dashboard.todayEventsSubtitleNA');
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);
@ -324,7 +351,7 @@ export default class HomeScreen extends FetchedDataSectionList {
subtitle={subtitle} subtitle={subtitle}
color={color} color={color}
icon={icon} icon={icon}
clickAction={() => clickAction()} clickAction={this.clickAction.bind(this, isAvailable, displayEvent)}
title={title} title={title}
isAvailable={isAvailable} isAvailable={isAvailable}
displayEvent={displayEvent} displayEvent={displayEvent}
@ -355,7 +382,6 @@ export default class HomeScreen extends FetchedDataSectionList {
</Text>; </Text>;
} else } else
proximoSubtitle = i18n.t('homeScreen.dashboard.proximoSubtitleNA'); proximoSubtitle = i18n.t('homeScreen.dashboard.proximoSubtitleNA');
let proximoClickAction = () => this.props.navigation.navigate('Proximo');
let menuIcon = 'silverware-fork-knife'; let menuIcon = 'silverware-fork-knife';
@ -367,7 +393,6 @@ export default class HomeScreen extends FetchedDataSectionList {
menuSubtitle = i18n.t('homeScreen.dashboard.menuSubtitle'); menuSubtitle = i18n.t('homeScreen.dashboard.menuSubtitle');
} else } else
menuSubtitle = i18n.t('homeScreen.dashboard.menuSubtitleNA'); menuSubtitle = i18n.t('homeScreen.dashboard.menuSubtitleNA');
let menuClickAction = () => this.props.navigation.navigate('SelfMenuScreen');
return ( return (
<View style={{ <View style={{
flexDirection: 'row', flexDirection: 'row',
@ -379,7 +404,7 @@ export default class HomeScreen extends FetchedDataSectionList {
subtitle={menuSubtitle} subtitle={menuSubtitle}
color={menuColor} color={menuColor}
icon={menuIcon} icon={menuIcon}
clickAction={() => menuClickAction()} clickAction={this.onMenuClick}
title={menuTitle} title={menuTitle}
isAvailable={isMenuAvailable} isAvailable={isMenuAvailable}
isSquareLeft={true}/> isSquareLeft={true}/>
@ -388,13 +413,14 @@ export default class HomeScreen extends FetchedDataSectionList {
subtitle={proximoSubtitle} subtitle={proximoSubtitle}
color={proximoColor} color={proximoColor}
icon={proximoIcon} icon={proximoIcon}
clickAction={() => proximoClickAction()} clickAction={this.onProximoClick}
title={proximoTitle} title={proximoTitle}
isAvailable={isProximoAvailable}/> isAvailable={isProximoAvailable}/>
</View> </View>
); );
} }
getDashboardMiddleItem(content: Array<Object>) { getDashboardMiddleItem(content: Array<Object>) {
let proxiwashData = content[0]['data']; let proxiwashData = content[0]['data'];
let tutorinsaData = content[1]['data']; let tutorinsaData = content[1]['data'];
@ -449,7 +475,6 @@ export default class HomeScreen extends FetchedDataSectionList {
</Text>; </Text>;
} else } else
proxiwashSubtitle = i18n.t('homeScreen.dashboard.proxiwashSubtitleNA'); proxiwashSubtitle = i18n.t('homeScreen.dashboard.proxiwashSubtitleNA');
let proxiwashClickAction = () => this.props.navigation.navigate('Proxiwash');
let tutorinsaIcon = 'school'; let tutorinsaIcon = 'school';
let tutorinsaColor = ThemeManager.getCurrentThemeVariables().tutorinsaColor; let tutorinsaColor = ThemeManager.getCurrentThemeVariables().tutorinsaColor;
@ -470,7 +495,6 @@ export default class HomeScreen extends FetchedDataSectionList {
</Text>; </Text>;
} else } else
tutorinsaSubtitle = i18n.t('homeScreen.dashboard.tutorinsaSubtitleNA'); tutorinsaSubtitle = i18n.t('homeScreen.dashboard.tutorinsaSubtitleNA');
let tutorinsaClickAction = () => this.props.navigation.navigate('TutorInsaScreen');
return ( return (
<View style={{ <View style={{
@ -483,7 +507,7 @@ export default class HomeScreen extends FetchedDataSectionList {
subtitle={proxiwashSubtitle} subtitle={proxiwashSubtitle}
color={proxiwashColor} color={proxiwashColor}
icon={proxiwashIcon} icon={proxiwashIcon}
clickAction={() => proxiwashClickAction()} clickAction={this.onProxiwashClick}
title={proxiwashTitle} title={proxiwashTitle}
isAvailable={proxiwashIsAvailable} isAvailable={proxiwashIsAvailable}
isSquareLeft={true}/> isSquareLeft={true}/>
@ -492,7 +516,7 @@ export default class HomeScreen extends FetchedDataSectionList {
subtitle={tutorinsaSubtitle} subtitle={tutorinsaSubtitle}
color={tutorinsaColor} color={tutorinsaColor}
icon={tutorinsaIcon} icon={tutorinsaIcon}
clickAction={() => tutorinsaClickAction()} clickAction={this.onTutorInsaClick}
title={tutorinsaTitle} title={tutorinsaTitle}
isAvailable={tutorinsaIsAvailable}/> isAvailable={tutorinsaIsAvailable}/>
</View> </View>
@ -500,7 +524,7 @@ export default class HomeScreen extends FetchedDataSectionList {
} }
getRenderItem(item: Object, section: Object, data: Object) { getRenderItem(item: Object, section: Object) {
return ( return (
section['id'] === SECTIONS_ID[0] ? this.getDashboardItem(item) : section['id'] === SECTIONS_ID[0] ? this.getDashboardItem(item) :
<Card style={{ <Card style={{
@ -525,7 +549,7 @@ export default class HomeScreen extends FetchedDataSectionList {
}}> }}>
<Body> <Body>
{item.full_picture !== '' && item.full_picture !== undefined ? {item.full_picture !== '' && item.full_picture !== undefined ?
<TouchableOpacity onPress={() => openWebLink(item.full_picture)} <TouchableOpacity onPress={openWebLink.bind(null, item.full_picture)}
style={{width: '100%', height: 250, marginBottom: 5}}> style={{width: '100%', height: 250, marginBottom: 5}}>
<Image source={{uri: item.full_picture}} <Image source={{uri: item.full_picture}}
style={{flex: 1, resizeMode: "contain"}} style={{flex: 1, resizeMode: "contain"}}
@ -547,7 +571,7 @@ export default class HomeScreen extends FetchedDataSectionList {
}}> }}>
<Left> <Left>
<Button transparent <Button transparent
onPress={() => openWebLink(item.permalink_url)}> onPress={openWebLink.bind(null, item.permalink_url)}>
<CustomMaterialIcon <CustomMaterialIcon
icon="facebook" icon="facebook"
color="#57aeff" color="#57aeff"

View file

@ -8,13 +8,12 @@ import ThemeManager from "../utils/ThemeManager";
import HTML from "react-native-render-html"; import HTML from "react-native-render-html";
import {Linking} from "expo"; import {Linking} from "expo";
import PlanningEventManager from '../utils/PlanningEventManager'; import PlanningEventManager from '../utils/PlanningEventManager';
import i18n from 'i18n-js';
type Props = { type Props = {
navigation: Object, navigation: Object,
}; };
function openWebLink(link) { function openWebLink(event, link) {
Linking.openURL(link).catch((err) => console.error('Error opening link', err)); Linking.openURL(link).catch((err) => console.error('Error opening link', err));
} }
@ -22,8 +21,8 @@ function openWebLink(link) {
* Class defining an about screen. This screen shows the user information about the app and it's author. * 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> { export default class PlanningDisplayScreen extends React.Component<Props> {
render() { render() {
// console.log("rendering planningDisplayScreen");
const nav = this.props.navigation; const nav = this.props.navigation;
const displayData = nav.getParam('data', []); const displayData = nav.getParam('data', []);
return ( return (
@ -60,7 +59,7 @@ export default class PlanningDisplayScreen extends React.Component<Props> {
}, },
div: {color: ThemeManager.getCurrentThemeVariables().textColor} div: {color: ThemeManager.getCurrentThemeVariables().textColor}
}} }}
onLinkPress={(event, link) => openWebLink(link)}/> onLinkPress={openWebLink}/>
: <View/>} : <View/>}
</Content> </Content>
</Container> </Container>

View file

@ -5,7 +5,6 @@ import {BackHandler, Image} from 'react-native';
import {H3, Text, View} 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 BaseContainer from "../components/BaseContainer"; import BaseContainer from "../components/BaseContainer";
import {Agenda, LocaleConfig} from 'react-native-calendars'; import {Agenda, LocaleConfig} from 'react-native-calendars';
import Touchable from 'react-native-platform-touchable'; import Touchable from 'react-native-platform-touchable';
@ -35,14 +34,6 @@ const FETCH_URL = "https://amicale-insat.fr/event/json/list";
const AGENDA_MONTH_SPAN = 6; const AGENDA_MONTH_SPAN = 6;
/**
* Opens a link in the device's browser
* @param link The link to open
*/
function openWebLink(link) {
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
}
/** /**
* Class defining the app's planning screen * Class defining the app's planning screen
*/ */
@ -56,12 +47,21 @@ export default class PlanningScreen extends React.Component<Props, State> {
didFocusSubscription: Function; didFocusSubscription: Function;
willBlurSubscription: Function; willBlurSubscription: Function;
state = { state = {
refreshing: false, refreshing: false,
agendaItems: {}, agendaItems: {},
calendarShowing: false, calendarShowing: false,
}; };
onRefresh: Function;
onCalendarToggled: Function;
getRenderItem: Function;
getRenderEmptyDate: Function;
onAgendaRef: Function;
onCalendarToggled: Function;
onBackButtonPressAndroid: Function;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.webDataManager = new WebDataManager(FETCH_URL); this.webDataManager = new WebDataManager(FETCH_URL);
@ -76,10 +76,25 @@ export default class PlanningScreen extends React.Component<Props, State> {
if (i18n.currentLocale().startsWith("fr")) { if (i18n.currentLocale().startsWith("fr")) {
LocaleConfig.defaultLocale = 'fr'; LocaleConfig.defaultLocale = 'fr';
} }
// Create references for functions required in the render function
this.onRefresh = this.onRefresh.bind(this);
this.onCalendarToggled = this.onCalendarToggled.bind(this);
this.getRenderItem = this.getRenderItem.bind(this);
this.getRenderEmptyDate = this.getRenderEmptyDate.bind(this);
this.onAgendaRef = this.onAgendaRef.bind(this);
this.onCalendarToggled = this.onCalendarToggled.bind(this);
this.onBackButtonPressAndroid = this.onBackButtonPressAndroid.bind(this);
}
shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
return nextState.refreshing === false && this.state.refreshing === true ||
nextState.agendaItems !== this.state.agendaItems ||
nextState.calendarShowing !== this.state.calendarShowing;
} }
componentDidMount() { componentDidMount() {
this._onRefresh(); this.onRefresh();
this.willBlurSubscription = this.props.navigation.addListener( this.willBlurSubscription = this.props.navigation.addListener(
'willBlur', 'willBlur',
() => () =>
@ -90,7 +105,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
); );
} }
onBackButtonPressAndroid = () => { onBackButtonPressAndroid() {
if (this.state.calendarShowing) { if (this.state.calendarShowing) {
this.agendaRef.chooseDay(this.agendaRef.state.selectedDay); this.agendaRef.chooseDay(this.agendaRef.state.selectedDay);
return true; return true;
@ -126,10 +141,6 @@ export default class PlanningScreen extends React.Component<Props, State> {
} }
getRenderItem(item: Object) { getRenderItem(item: Object) {
let navData = {
data: item
};
const nav = this.props.navigation;
return ( return (
<Touchable <Touchable
style={{ style={{
@ -138,7 +149,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
marginRight: 10, marginRight: 10,
marginTop: 17, marginTop: 17,
}} }}
onPress={() => nav.navigate('PlanningDisplayScreen', navData)}> onPress={() => this.props.navigation.navigate('PlanningDisplayScreen', {data: item})}>
<View style={{ <View style={{
padding: 10, padding: 10,
flex: 1, flex: 1,
@ -200,7 +211,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
* Refresh data and show a toast if any error occurred * Refresh data and show a toast if any error occurred
* @private * @private
*/ */
_onRefresh = () => { onRefresh = () => {
let canRefresh; let canRefresh;
if (this.lastRefresh !== undefined) if (this.lastRefresh !== undefined)
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh; canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh;
@ -221,7 +232,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
this.setState({ this.setState({
refreshing: false, refreshing: false,
}); });
console.log(err); // console.log(err);
}); });
} }
}; };
@ -252,38 +263,46 @@ export default class PlanningScreen extends React.Component<Props, State> {
} }
} }
onAgendaRef(ref: Agenda) {
this.agendaRef = ref;
}
onCalendarToggled(isCalendarOpened: boolean) {
this.setState({calendarShowing: isCalendarOpened});
}
currentDate = this.getCurrentDate();
render() { render() {
const nav = this.props.navigation; // console.log("rendering PlanningScreen");
return ( return (
<BaseContainer navigation={nav} headerTitle={i18n.t('screens.planning')}> <BaseContainer navigation={this.props.navigation} headerTitle={i18n.t('screens.planning')}>
<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
// considered that the date in question is not yet loaded // considered that the date in question is not yet loaded
items={this.state.agendaItems} items={this.state.agendaItems}
// initially selected day // initially selected day
selected={this.getCurrentDate()} selected={this.currentDate}
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
minDate={this.getCurrentDate()} minDate={this.currentDate}
// Max amount of months allowed to scroll to the past. Default = 50 // Max amount of months allowed to scroll to the past. Default = 50
pastScrollRange={1} pastScrollRange={1}
// Max amount of months allowed to scroll to the future. Default = 50 // Max amount of months allowed to scroll to the future. Default = 50
futureScrollRange={AGENDA_MONTH_SPAN} futureScrollRange={AGENDA_MONTH_SPAN}
// If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly. // If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
onRefresh={() => this._onRefresh()} onRefresh={this.onRefresh}
// callback that fires when the calendar is opened or closed // callback that fires when the calendar is opened or closed
onCalendarToggled={(calendarOpened) => { onCalendarToggled={this.onCalendarToggled}
this.setState({calendarShowing: calendarOpened})
}}
// Set this true while waiting for new data from a refresh // Set this true while waiting for new data from a refresh
refreshing={this.state.refreshing} refreshing={this.state.refreshing}
renderItem={(item) => this.getRenderItem(item)} renderItem={this.getRenderItem}
renderEmptyDate={() => this.getRenderEmptyDate()} renderEmptyDate={this.getRenderEmptyDate}
rowHasChanged={() => this.rowHasChanged()} rowHasChanged={this.rowHasChanged}
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday. // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
firstDay={1} firstDay={1}
// ref to this agenda in order to handle back button event // ref to this agenda in order to handle back button event
ref={(ref) => this.agendaRef = ref} ref={this.onAgendaRef}
// agenda theme // agenda theme
theme={{ theme={{
backgroundColor: ThemeManager.getCurrentThemeVariables().agendaBackgroundColor, backgroundColor: ThemeManager.getCurrentThemeVariables().agendaBackgroundColor,

View file

@ -1,7 +1,7 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Image, Linking, View} from 'react-native'; import {Image, View} from 'react-native';
import {Card, CardItem, Container, Content, H2, Left, Text} from 'native-base'; import {Card, CardItem, Container, Content, H2, Left, Text} from 'native-base';
import CustomHeader from "../../components/CustomHeader"; import CustomHeader from "../../components/CustomHeader";
import i18n from "i18n-js"; import i18n from "i18n-js";

View file

@ -70,12 +70,28 @@ export default class ProximoListScreen extends React.Component<Props, State> {
sortNameIcon: '', sortNameIcon: '',
modalCurrentDisplayItem: {}, modalCurrentDisplayItem: {},
}; };
_menu: Menu; sortMenuRef: Menu;
onMenuRef: Function;
onSearchStringChange: Function;
onSelectSortModeName: Function;
onSelectSortModePrice: Function;
onSortMenuPress: Function;
renderItem: Function;
onListItemPress: Function;
constructor(props: any) { constructor(props: any) {
super(props); super(props);
this.modalRef = React.createRef(); this.modalRef = React.createRef();
this.originalData = this.navData['data']; this.originalData = this.navData['data'];
this.onMenuRef = this.onMenuRef.bind(this);
this.onSearchStringChange = this.onSearchStringChange.bind(this);
this.onSelectSortModeName = this.onSelectSortModeName.bind(this);
this.onSelectSortModePrice = this.onSelectSortModePrice.bind(this);
this.onSortMenuPress = this.onSortMenuPress.bind(this);
this.renderItem = this.renderItem.bind(this);
this.onListItemPress = this.onListItemPress.bind(this);
} }
/** /**
@ -83,8 +99,8 @@ export default class ProximoListScreen extends React.Component<Props, State> {
* *
* @param ref The menu reference * @param ref The menu reference
*/ */
setMenuRef = (ref: Menu) => { onMenuRef(ref: Menu) {
this._menu = ref; this.sortMenuRef = ref;
}; };
/** /**
@ -131,7 +147,7 @@ export default class ProximoListScreen extends React.Component<Props, State> {
break; break;
} }
this.setupSortIcons(mode, isReverse); this.setupSortIcons(mode, isReverse);
this._menu.hide(); this.sortMenuRef.hide();
} }
/** /**
@ -214,7 +230,7 @@ export default class ProximoListScreen extends React.Component<Props, State> {
return filteredData; return filteredData;
} }
search(str: string) { onSearchStringChange(str: string) {
this.setState({ this.setState({
currentlyDisplayedData: this.filterData(str) currentlyDisplayedData: this.filterData(str)
}) })
@ -251,7 +267,7 @@ export default class ProximoListScreen extends React.Component<Props, State> {
); );
} }
showItemDetails(item: Object) { onListItemPress(item: Object) {
this.setState({ this.setState({
modalCurrentDisplayItem: item modalCurrentDisplayItem: item
}); });
@ -260,16 +276,27 @@ export default class ProximoListScreen extends React.Component<Props, State> {
} }
} }
onSelectSortModeName() {
this.sortModeSelected(sortMode.name);
}
onSelectSortModePrice() {
this.sortModeSelected(sortMode.price);
}
onSortMenuPress() {
this.sortMenuRef.show();
}
getSortMenu() { getSortMenu() {
return ( return (
<Menu <Menu
ref={this.setMenuRef} ref={this.onMenuRef}
button={ button={
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => onPress={this.onSortMenuPress}>
this._menu.show()
}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon={'sort'}/> icon={'sort'}/>
@ -277,12 +304,12 @@ export default class ProximoListScreen extends React.Component<Props, State> {
} }
> >
<MenuItem <MenuItem
onPress={() => this.sortModeSelected(sortMode.name)}> onPress={this.onSelectSortModeName}>
{this.state.sortNameIcon} {this.state.sortNameIcon}
{i18n.t('proximoScreen.sortName')} {i18n.t('proximoScreen.sortName')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onPress={() => this.sortModeSelected(sortMode.price)}> onPress={this.onSelectSortModePrice}>
{this.state.sortPriceIcon} {this.state.sortPriceIcon}
{i18n.t('proximoScreen.sortPrice')} {i18n.t('proximoScreen.sortPrice')}
</MenuItem> </MenuItem>
@ -290,7 +317,39 @@ export default class ProximoListScreen extends React.Component<Props, State> {
); );
} }
renderItem({item}: Object) {
return (<ListItem
thumbnail
onPress={this.onListItemPress}
>
<Left>
<Thumbnail square source={{uri: item.image}}/>
</Left>
<Body>
<Text style={{marginLeft: 20}}>
{item.name}
</Text>
<Text note style={{
marginLeft: 20,
color: this.getStockColor(parseInt(item.quantity))
}}>
{item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
</Text>
</Body>
<Right>
<Text style={{fontWeight: "bold"}}>
{item.price}
</Text>
</Right>
</ListItem>);
}
keyExtractor(item: Object) {
return item.name + item.code;
}
render() { render() {
// console.log("rendering ProximoListScreen");
const nav = this.props.navigation; const nav = this.props.navigation;
return ( return (
<Container> <Container>
@ -303,7 +362,7 @@ export default class ProximoListScreen extends React.Component<Props, State> {
hasBackButton={true} hasBackButton={true}
navigation={nav} navigation={nav}
hasSearchField={true} hasSearchField={true}
searchCallback={(text) => this.search(text)} searchCallback={this.onSearchStringChange}
shouldFocusSearchBar={this.shouldFocusSearchBar} shouldFocusSearchBar={this.shouldFocusSearchBar}
rightButton={this.getSortMenu()} rightButton={this.getSortMenu()}
/> />
@ -311,35 +370,9 @@ export default class ProximoListScreen extends React.Component<Props, State> {
<FlatList <FlatList
data={this.state.currentlyDisplayedData} data={this.state.currentlyDisplayedData}
extraData={this.state.currentlyDisplayedData} extraData={this.state.currentlyDisplayedData}
keyExtractor={(item) => item.name + item.code} keyExtractor={this.keyExtractor}
style={{minHeight: 300, width: '100%'}} style={{minHeight: 300, width: '100%'}}
renderItem={({item}) => renderItem={this.renderItem}
<ListItem
thumbnail
onPress={() => {
this.showItemDetails(item);
}}
>
<Left>
<Thumbnail square source={{uri: item.image}}/>
</Left>
<Body>
<Text style={{marginLeft: 20}}>
{item.name}
</Text>
<Text note style={{
marginLeft: 20,
color: this.getStockColor(parseInt(item.quantity))
}}>
{item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
</Text>
</Body>
<Right>
<Text style={{fontWeight: "bold"}}>
{item.price}
</Text>
</Right>
</ListItem>}
/> />
</Container> </Container>
); );

View file

@ -9,7 +9,7 @@ import FetchedDataSectionList from "../../components/FetchedDataSectionList";
import ThemeManager from "../../utils/ThemeManager"; import ThemeManager from "../../utils/ThemeManager";
import Touchable from "react-native-platform-touchable"; import Touchable from "react-native-platform-touchable";
const DATA_URL = "https://srv-falcon.etud.insa-toulouse.fr/~proximo/data/stock-v2.json"; const DATA_URL = "https://etud.insa-toulouse.fr/~proximo/data/stock-v2.json";
/** /**
@ -18,12 +18,31 @@ const DATA_URL = "https://srv-falcon.etud.insa-toulouse.fr/~proximo/data/stock-v
*/ */
export default class ProximoMainScreen extends FetchedDataSectionList { export default class ProximoMainScreen extends FetchedDataSectionList {
onPressSearchBtn: Function;
onPressAboutBtn: Function;
constructor() { constructor() {
super(DATA_URL, 0); super(DATA_URL, 0);
this.onPressSearchBtn = this.onPressSearchBtn.bind(this);
this.onPressAboutBtn = this.onPressAboutBtn.bind(this);
} }
static sortFinalData(a: Object, b: Object) { static sortFinalData(a: Object, b: Object) {
return a.type.id - b.type.id; let str1 = a.type.name.toLowerCase();
let str2 = b.type.name.toLowerCase();
// Make 'All' category with id -1 stick to the top
if (a.type.id === -1)
return -1;
if (b.type.id === -1)
return 1;
// Sort others by name ascending
if (str1 < str2)
return -1;
if (str1 > str2)
return 1;
return 0;
} }
getHeaderTranslation() { getHeaderTranslation() {
@ -63,7 +82,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
let articles = fetchedData.articles; let articles = fetchedData.articles;
finalData.push({ finalData.push({
type: { type: {
id: "0", id: -1,
name: i18n.t('proximoScreen.all'), name: i18n.t('proximoScreen.all'),
icon: 'star' icon: 'star'
}, },
@ -100,7 +119,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
return availableArticles; return availableArticles;
} }
getRightButton() { onPressSearchBtn() {
let searchScreenData = { let searchScreenData = {
shouldFocusSearchBar: true, shouldFocusSearchBar: true,
data: { data: {
@ -113,8 +132,14 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
this.getAvailableArticles(this.state.fetchedData.articles, undefined) : [] this.getAvailableArticles(this.state.fetchedData.articles, undefined) : []
}, },
}; };
this.props.navigation.navigate('ProximoListScreen', searchScreenData);
}
onPressAboutBtn() {
this.props.navigation.navigate('ProximoAboutScreen');
}
getRightButton() {
return ( return (
<View <View
style={{ style={{
@ -122,14 +147,14 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
}}> }}>
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => this.props.navigation.navigate('ProximoListScreen', searchScreenData)}> onPress={this.onPressSearchBtn}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="magnify"/> icon="magnify"/>
</Touchable> </Touchable>
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => this.props.navigation.navigate('ProximoAboutScreen')}> onPress={this.onPressAboutBtn}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="information"/> icon="information"/>
@ -138,19 +163,18 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
); );
} }
getRenderItem(item: Object, section: Object, data: Object) { getRenderItem(item: Object, section: Object) {
let dataToSend = { let dataToSend = {
shouldFocusSearchBar: false, shouldFocusSearchBar: false,
data: item, data: item,
}; };
const onPress = this.props.navigation.navigate.bind(this, 'ProximoListScreen', dataToSend);
if (item.data.length > 0) { if (item.data.length > 0) {
return ( return (
<ListItem <ListItem
button button
thumbnail thumbnail
onPress={() => { onPress={onPress}
this.props.navigation.navigate('ProximoListScreen', dataToSend);
}}
> >
<Left> <Left>
<CustomMaterialIcon <CustomMaterialIcon

View file

@ -13,7 +13,7 @@ import Touchable from "react-native-platform-touchable";
import AsyncStorageManager from "../../utils/AsyncStorageManager"; import AsyncStorageManager from "../../utils/AsyncStorageManager";
import * as Expo from "expo"; import * as Expo from "expo";
const DATA_URL = "https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json"; const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/washinsa/washinsa.json";
const MACHINE_STATES = { const MACHINE_STATES = {
"TERMINE": "0", "TERMINE": "0",
@ -36,6 +36,8 @@ const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
*/ */
export default class ProxiwashScreen extends FetchedDataSectionList { export default class ProxiwashScreen extends FetchedDataSectionList {
onAboutPress: Function;
/** /**
* Creates machine state parameters using current theme and translations * Creates machine state parameters using current theme and translations
*/ */
@ -75,6 +77,8 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
machinesWatched: [], machinesWatched: [],
}; };
this.setMinTimeRefresh(30); this.setMinTimeRefresh(30);
this.onAboutPress = this.onAboutPress.bind(this);
} }
/** /**
@ -82,7 +86,6 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
*/ */
componentDidMount() { componentDidMount() {
super.componentDidMount(); super.componentDidMount();
if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') { if (AsyncStorageManager.getInstance().preferences.expoToken.current !== '') {
// Get latest watchlist from server // Get latest watchlist from server
NotificationsManager.getMachineNotificationWatchlist((fetchedList) => { NotificationsManager.getMachineNotificationWatchlist((fetchedList) => {
@ -241,13 +244,14 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
showAlert(title: string, item: Object, isDryer: boolean) { showAlert(title: string, item: Object, isDryer: boolean) {
let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}]; let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}];
let message = modalStateStrings[MACHINE_STATES[item.state]]; let message = modalStateStrings[MACHINE_STATES[item.state]];
const onPress = this.setupNotifications.bind(this, item.number);
if (MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"]) { if (MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"]) {
buttons = [ buttons = [
{ {
text: this.isMachineWatched(item.number) ? text: this.isMachineWatched(item.number) ?
i18n.t("proxiwashScreen.modal.disableNotifications") : i18n.t("proxiwashScreen.modal.disableNotifications") :
i18n.t("proxiwashScreen.modal.enableNotifications"), i18n.t("proxiwashScreen.modal.enableNotifications"),
onPress: () => this.setupNotifications(item.number) onPress: onPress
}, },
{ {
text: i18n.t("proxiwashScreen.modal.cancel") text: i18n.t("proxiwashScreen.modal.cancel")
@ -272,11 +276,15 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
); );
} }
onAboutPress() {
this.props.navigation.navigate('ProxiwashAboutScreen');
}
getRightButton(): * { getRightButton(): * {
return ( return (
<Touchable <Touchable
style={{padding: 6}} style={{padding: 6}}
onPress={() => this.props.navigation.navigate('ProxiwashAboutScreen')}> onPress={this.onAboutPress}>
<CustomMaterialIcon <CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"} color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="information"/> icon="information"/>
@ -289,13 +297,13 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
* *
* @param item The object containing the item's FetchedData * @param item The object containing the item's FetchedData
* @param section The object describing the current SectionList section * @param section The object describing the current SectionList section
* @param data The full FetchedData used by the SectionList
* @returns {React.Node} * @returns {React.Node}
*/ */
getRenderItem(item: Object, section: Object, data: Object) { getRenderItem(item: Object, section: Object) {
let isMachineRunning = MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"]; let isMachineRunning = MACHINE_STATES[item.state] === MACHINE_STATES["EN COURS"];
let machineName = (section.title === i18n.t('proxiwashScreen.dryers') ? i18n.t('proxiwashScreen.dryer') : i18n.t('proxiwashScreen.washer')) + ' n°' + item.number; let machineName = (section.title === i18n.t('proxiwashScreen.dryers') ? i18n.t('proxiwashScreen.dryer') : i18n.t('proxiwashScreen.washer')) + ' n°' + item.number;
let isDryer = section.title === i18n.t('proxiwashScreen.dryers'); let isDryer = section.title === i18n.t('proxiwashScreen.dryers');
const onPress = this.showAlert.bind(this, machineName, item, isDryer);
return ( return (
<Card style={{ <Card style={{
flex: 0, flex: 0,
@ -320,7 +328,7 @@ export default class ProxiwashScreen extends FetchedDataSectionList {
backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor
}}/> }}/>
<PlatformTouchable <PlatformTouchable
onPress={() => this.showAlert(machineName, item, isDryer)} onPress={onPress}
style={{ style={{
height: 64, height: 64,
position: 'absolute', position: 'absolute',

View file

@ -7,7 +7,7 @@ import ThemeManager from "../utils/ThemeManager";
import i18n from "i18n-js"; import i18n from "i18n-js";
import FetchedDataSectionList from "../components/FetchedDataSectionList"; import FetchedDataSectionList from "../components/FetchedDataSectionList";
const DATA_URL = "https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json"; const DATA_URL = "https://etud.insa-toulouse.fr/~amicale_app/menu/menu_data.json";
/** /**
* Class defining the app's menu screen. * Class defining the app's menu screen.
@ -119,7 +119,7 @@ export default class SelfMenuScreen extends FetchedDataSectionList {
); );
} }
getRenderItem(item: Object, section: Object, data: Object) { getRenderItem(item: Object, section: Object) {
return ( return (
<Card style={{ <Card style={{
flex: 0, flex: 0,

View file

@ -43,6 +43,17 @@ export default class SettingsScreen extends React.Component<Props, State> {
startScreenPickerSelected: AsyncStorageManager.getInstance().preferences.defaultStartScreen.current, startScreenPickerSelected: AsyncStorageManager.getInstance().preferences.defaultStartScreen.current,
}; };
onProxiwashNotifPickerValueChange: Function;
onStartScreenPickerValueChange: Function;
onToggleNightMode: Function;
constructor() {
super();
this.onProxiwashNotifPickerValueChange = this.onProxiwashNotifPickerValueChange.bind(this);
this.onStartScreenPickerValueChange = this.onStartScreenPickerValueChange.bind(this);
this.onToggleNightMode = this.onToggleNightMode.bind(this);
}
/** /**
* Get a list item using the specified control * Get a list item using the specified control
* *
@ -118,7 +129,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
mode="dropdown" mode="dropdown"
style={{width: 120}} style={{width: 120}}
selectedValue={this.state.proxiwashNotifPickerSelected} selectedValue={this.state.proxiwashNotifPickerSelected}
onValueChange={(value) => this.onProxiwashNotifPickerValueChange(value)} onValueChange={this.onProxiwashNotifPickerValueChange}
> >
<Picker.Item label={i18n.t('settingsScreen.proxiwashNotifReminderPicker.never')} value="never"/> <Picker.Item label={i18n.t('settingsScreen.proxiwashNotifReminderPicker.never')} value="never"/>
<Picker.Item label={i18n.t('settingsScreen.proxiwashNotifReminderPicker.5')} value="5"/> <Picker.Item label={i18n.t('settingsScreen.proxiwashNotifReminderPicker.5')} value="5"/>
@ -141,7 +152,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
mode="dropdown" mode="dropdown"
style={{width: 120}} style={{width: 120}}
selectedValue={this.state.startScreenPickerSelected} selectedValue={this.state.startScreenPickerSelected}
onValueChange={(value) => this.onStartScreenPickerValueChange(value)} onValueChange={this.onStartScreenPickerValueChange}
> >
<Picker.Item label={i18n.t('screens.home')} value="Home"/> <Picker.Item label={i18n.t('screens.home')} value="Home"/>
<Picker.Item label={i18n.t('screens.planning')} value="Planning"/> <Picker.Item label={i18n.t('screens.planning')} value="Planning"/>
@ -155,7 +166,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
/** /**
* Toggle night mode and save it to preferences * Toggle night mode and save it to preferences
*/ */
toggleNightMode() { onToggleNightMode() {
ThemeManager.getInstance().setNightMode(!this.state.nightMode); ThemeManager.getInstance().setNightMode(!this.state.nightMode);
this.setState({nightMode: !this.state.nightMode}); this.setState({nightMode: !this.state.nightMode});
this.resetStack(); this.resetStack();
@ -203,7 +214,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
<Right> <Right>
<CheckBox <CheckBox
checked={this.state.nightMode} checked={this.state.nightMode}
onPress={() => this.toggleNightMode()} onPress={onPressCallback}
style={{marginRight: 20}}/> style={{marginRight: 20}}/>
</Right> </Right>
</ListItem> </ListItem>
@ -221,7 +232,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
<Text>{i18n.t('settingsScreen.generalCard')}</Text> <Text>{i18n.t('settingsScreen.generalCard')}</Text>
</CardItem> </CardItem>
<List> <List>
{this.getToggleItem(() => this.toggleNightMode(), 'theme-light-dark', i18n.t('settingsScreen.nightMode'), i18n.t('settingsScreen.nightModeSub'))} {this.getToggleItem(this.onToggleNightMode, 'theme-light-dark', i18n.t('settingsScreen.nightMode'), i18n.t('settingsScreen.nightModeSub'))}
{SettingsScreen.getGeneralItem(this.getStartScreenPicker(), 'power', i18n.t('settingsScreen.startScreen'), i18n.t('settingsScreen.startScreenSub'))} {SettingsScreen.getGeneralItem(this.getStartScreenPicker(), 'power', i18n.t('settingsScreen.startScreen'), i18n.t('settingsScreen.startScreenSub'))}
</List> </List>
</Card> </Card>

View file

@ -8,7 +8,7 @@ type Props = {
} }
const URL = 'https://www.etud.insa-toulouse.fr/~amicale'; const URL = 'https://amicale-insat.fr/';
/** /**
* Class defining the app's planex screen. * Class defining the app's planex screen.

View file

@ -12,8 +12,8 @@ type Props = {
const ROOM_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 PC_URL = 'http://planex.insa-toulouse.fr/sallesInfo.php';
const BIB_URL = 'https://bibbox.insa-toulouse.fr/'; 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_GENERAL = 'https://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'; const CUSTOM_CSS_Bib = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/rooms/customBibMobile.css';
/** /**
* Class defining the app's planex screen. * Class defining the app's planex screen.

View file

@ -11,7 +11,7 @@ type Props = {
const URL = 'https://etud-mel.insa-toulouse.fr/webmail/'; const URL = 'https://etud-mel.insa-toulouse.fr/webmail/';
const CUSTOM_CSS_GENERAL = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/bluemind/customMobile.css'; const CUSTOM_CSS_GENERAL = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/bluemind/customMobile.css';
/** /**

View file

@ -8,7 +8,7 @@ type Props = {
} }
const URL = 'https://srv-falcon.etud.insa-toulouse.fr/~eeinsat/'; const URL = 'https://etud.insa-toulouse.fr/~eeinsat/';
/** /**
* Class defining the app's planex screen. * Class defining the app's planex screen.

View file

@ -11,12 +11,12 @@ type Props = {
const URL = 'https://ent.insa-toulouse.fr/'; const URL = 'https://ent.insa-toulouse.fr/';
const CUSTOM_CSS_GENERAL = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/ent/customMobile.css'; const CUSTOM_CSS_GENERAL = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/ent/customMobile.css';
// let stylesheet = document.createElement('link'); // let stylesheet = document.createElement('link');
// stylesheet.type = 'text/css'; // stylesheet.type = 'text/css';
// stylesheet.rel = 'stylesheet'; // stylesheet.rel = 'stylesheet';
// stylesheet.href = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/ent/customMobile.css'; // stylesheet.href = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/ent/customMobile.css';
// let mobileSpec = document.createElement('meta'); // let mobileSpec = document.createElement('meta');
// mobileSpec.name = 'viewport'; // mobileSpec.name = 'viewport';
// mobileSpec.content = 'width=device-width, initial-scale=1.0'; // mobileSpec.content = 'width=device-width, initial-scale=1.0';

View file

@ -11,8 +11,8 @@ type Props = {
const PLANEX_URL = 'http://planex.insa-toulouse.fr/'; const PLANEX_URL = 'http://planex.insa-toulouse.fr/';
const CUSTOM_CSS_GENERAL = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customMobile3.css'; const CUSTOM_CSS_GENERAL = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customMobile3.css';
const CUSTOM_CSS_NIGHTMODE = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customDark2.css'; const CUSTOM_CSS_NIGHTMODE = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customDark2.css';
// // JS + JQuery functions used to remove alpha from events. Copy paste in browser console for quick testing // // JS + JQuery functions used to remove alpha from events. Copy paste in browser console for quick testing
// // Remove alpha from given Jquery node // // Remove alpha from given Jquery node

View file

@ -8,7 +8,7 @@ type Props = {
} }
const URL = 'https://www.etud.insa-toulouse.fr/wiketud'; const URL = 'https://wiki.etud.insa-toulouse.fr/';
/** /**
* Class defining the app's planex screen. * Class defining the app's planex screen.

View file

@ -6,7 +6,7 @@ import AsyncStorageManager from "./AsyncStorageManager";
import LocaleManager from "./LocaleManager"; import LocaleManager from "./LocaleManager";
import passwords from "../passwords"; import passwords from "../passwords";
const EXPO_TOKEN_SERVER = 'https://srv-falcon.etud.insa-toulouse.fr/~amicale_app/expo_notifications/save_token.php'; const EXPO_TOKEN_SERVER = 'https://etud.insa-toulouse.fr/~amicale_app/expo_notifications/save_token.php';
/** /**
* Static class used to manage notifications sent to the user * Static class used to manage notifications sent to the user
@ -123,18 +123,8 @@ export default class NotificationsManager {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}), }),
body: JSON.stringify(data) // <-- Post parameters body: JSON.stringify(data) // <-- Post parameters
}) });
.then((response) => response.json())
.then((responseJson) => {
callback(responseJson);
})
.catch((error) => {
console.log(error);
});
} else {
console.log('Expo token not available');
} }
} }
/** /**
@ -161,16 +151,7 @@ export default class NotificationsManager {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}), }),
body: JSON.stringify(data) // <-- Post parameters body: JSON.stringify(data) // <-- Post parameters
}) });
.then((response) => response.text())
.then((responseText) => {
console.log(responseText);
})
.catch((error) => {
console.log(error);
});
} else {
console.log('Expo token not available');
} }
} }
@ -194,16 +175,7 @@ export default class NotificationsManager {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}), }),
body: JSON.stringify(data) // <-- Post parameters body: JSON.stringify(data) // <-- Post parameters
}) });
.then((response) => response.text())
.then((responseText) => {
console.log(responseText);
})
.catch((error) => {
console.log(error);
});
} else {
console.log('Expo token not available');
} }
} }
} }

View file

@ -25,8 +25,8 @@ export default class WebDataManager {
let response = await fetch(this.FETCH_URL); let response = await fetch(this.FETCH_URL);
fetchedData = await response.json(); fetchedData = await response.json();
} catch (error) { } catch (error) {
console.log('Could not read FetchedData from server'); // console.log('Could not read FetchedData from server');
console.log(error); // console.log(error);
throw new Error('Could not read FetchedData from server'); throw new Error('Could not read FetchedData from server');
} }
this.lastDataFetched = fetchedData; this.lastDataFetched = fetchedData;