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,
};
onIntroDone: Function;
loadAssetsAsync: Function;
onLoadFinished: Function;
constructor(props: Object) {
super(props);
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) {
return (
<AppLoading
startAsync={() => this.loadAssetsAsync()}
onFinish={() => this.onLoadFinished()}
startAsync={this.loadAssetsAsync}
onFinish={this.onLoadFinished}
onError={console.warn}
/>
);
}
if (this.state.showIntro || this.state.showUpdate) {
return <CustomIntroSlider onDone={() => this.onIntroDone()}
return <CustomIntroSlider onDone={this.onIntroDone}
isUpdate={this.state.showUpdate && !this.state.showIntro}/>;
} else {
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 !
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'.

View file

@ -10,7 +10,7 @@
"android",
"web"
],
"version": "1.5.0",
"version": "1.5.1",
"orientation": "portrait",
"primaryColor": "#be1522",
"icon": "./assets/android.icon.png",
@ -36,7 +36,7 @@
},
"android": {
"package": "fr.amicaleinsat.application",
"versionCode": 13,
"versionCode": 14,
"icon": "./assets/android.icon.png",
"adaptiveIcon": {
"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> {
static defaultProps = {
headerRightButton: <View/>,
hasTabs: false,
@ -46,40 +45,61 @@ export default class BaseContainer extends React.Component<Props, State> {
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();
}
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
*/
componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
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.onWillFocus
);
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
() => {
if (this.props.enableRotation)
ScreenOrientation.lockAsync(ScreenOrientation.Orientation.PORTRAIT);
}
this.onWillBlur
);
}
@ -93,7 +113,9 @@ export default class BaseContainer extends React.Component<Props, State> {
this.willFocusSubscription.remove();
}
getMainContainer() {
render() {
// console.log("rendering BaseContainer");
return (
<Container>
{this.state.isHeaderVisible ?
@ -104,7 +126,7 @@ export default class BaseContainer extends React.Component<Props, State> {
leftButton={
<Touchable
style={{padding: 6}}
onPress={() => this.toggle()}>
onPress={this.onDrawerPress}>
<CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="menu"/>
@ -118,9 +140,4 @@ export default class BaseContainer extends React.Component<Props, State> {
</Container>
);
}
render() {
return (this.getMainContainer());
}
}

View file

@ -32,11 +32,10 @@ type Props = {
* @prop navigation {Object} The navigation object from react navigation
*/
export default class CustomHeader extends React.Component<Props> {
static defaultProps = {
hasBackButton: false,
hasSearchField: false,
searchCallback: () => null,
searchCallback: null,
shouldFocusSearchBar: false,
title: '',
subtitle: '',
@ -45,10 +44,28 @@ export default class CustomHeader extends React.Component<Props> {
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() {
if (this.refs.searchInput !== undefined && this.refs.searchInput._root !== undefined && this.props.shouldFocusSearchBar) {
// does not work if called to early for some reason...
setTimeout(() => this.refs.searchInput._root.focus(), 500);
// does not work if called too early for some reason...
setTimeout(this.refs.searchInput._root.focus, 500);
}
}
@ -67,7 +84,7 @@ export default class CustomHeader extends React.Component<Props> {
ref="searchInput"
placeholder={i18n.t('proximoScreen.search')}
placeholderTextColor={ThemeManager.getCurrentThemeVariables().toolbarPlaceholderColor}
onChangeText={(text) => this.props.searchCallback(text)}/>
onChangeText={this.props.searchCallback}/>
</Item>
</Body>
);
@ -87,17 +104,20 @@ export default class CustomHeader extends React.Component<Props> {
}
onPressBack() {
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
}
render() {
// console.log("rendering CustomHeader");
let button;
// Does the app have a back button or a burger menu ?
if (this.props.hasBackButton)
button =
<Touchable
style={{padding: 6}}
onPress={() => {
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
}}>
onPress={this.onPressBack}>
<CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
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 dimensions
*/
static getIntroRenderItem(item: Object, dimensions: Object) {
static getIntroRenderItem({item, dimensions}: Object) {
return (
<LinearGradient
@ -143,9 +143,9 @@ export default class CustomIntroSlider extends React.Component<Props> {
render() {
return (
<AppIntroSlider
renderItem={({item, dimensions}) => CustomIntroSlider.getIntroRenderItem(item, dimensions)}
renderItem={CustomIntroSlider.getIntroRenderItem}
slides={this.props.isUpdate ? this.updateSlides : this.introSlides}
onDone={() => this.props.onDone()}
onDone={this.props.onDone}
bottomButton
showSkipButton
skipLabel={i18n.t('intro.buttons.skip')}

View file

@ -30,7 +30,16 @@ export default class CustomMaterialIcon extends React.Component<Props> {
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() {
// console.log("rendering icon " + this.props.icon);
return (
<Icon
active

View file

@ -25,13 +25,18 @@ type Props = {
}
export default class DashboardItem extends React.Component<Props> {
static defaultProps = {
isSquare: false,
isSquareLeft: true,
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
* @param dateString
@ -203,6 +208,7 @@ export default class DashboardItem extends React.Component<Props> {
render() {
// console.log("rendering DashboardItem " + this.props.title);
let marginRight = 10;
if (this.props.isSquare) {
if (this.props.isSquareLeft)

View file

@ -25,7 +25,6 @@ type State = {
* Used by inheriting from it and redefining getters.
*/
export default class FetchedDataSectionList extends React.Component<Props, State> {
webDataManager: WebDataManager;
willFocusSubscription: function;
@ -42,12 +41,29 @@ export default class FetchedDataSectionList extends React.Component<Props, State
machinesWatched: [],
};
onRefresh: Function;
onFetchSuccess: Function;
onFetchError: Function;
renderSectionHeaderEmpty: Function;
renderSectionHeaderNotEmpty: Function;
renderItemEmpty: Function;
renderItemNotEmpty: Function;
constructor(fetchUrl: string, refreshTime: number) {
super();
this.webDataManager = new WebDataManager(fetchUrl);
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
* @return {string}
@ -74,26 +90,18 @@ export default class FetchedDataSectionList extends React.Component<Props, State
*/
componentDidMount() {
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this.onScreenFocus();
}
);
'willFocus', this.onScreenFocus.bind(this));
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
() => {
this.onScreenBlur();
}
);
'willBlur', this.onScreenBlur.bind(this));
}
/**
* Refresh data when focusing the screen and setup a refresh interval if asked to
*/
onScreenFocus() {
this._onRefresh();
this.onRefresh();
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();
}
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
* @private
*/
_onRefresh = () => {
onRefresh() {
let canRefresh;
if (this.lastRefresh !== undefined)
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) {
this.setState({refreshing: true});
this.webDataManager.readData()
.then((fetchedData) => {
this.setState({
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]);
});
.then(this.onFetchSuccess)
.catch(this.onFetchError);
}
};
}
/**
* 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 section
* @param data
* @return {*}
*/
getRenderItem(item: Object, section: Object, data: Object) {
getRenderItem(item: Object, section: Object) {
return <View/>;
}
@ -222,6 +232,11 @@ export default class FetchedDataSectionList extends React.Component<Props, State
return [];
}
datasetKeyExtractor(item: Object) {
return item.text
}
/**
* Create the dataset when no fetched data is available.
* 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'
}
],
keyExtractor: (item: Object) => item.text,
keyExtractor: this.datasetKeyExtractor,
}
];
}
@ -275,6 +290,19 @@ export default class FetchedDataSectionList extends React.Component<Props, State
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
*
@ -292,19 +320,11 @@ export default class FetchedDataSectionList extends React.Component<Props, State
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}
onRefresh={this.onRefresh}
/>
}
renderSectionHeader={({section: {title}}) =>
isEmpty ?
<View/> :
this.getRenderSectionHeader(title)
}
renderItem={({item, section}) =>
isEmpty ?
this.getEmptyRenderItem(item.text, item.isSpinner, item.icon) :
this.getRenderItem(item, section, dataset)
}
renderSectionHeader={isEmpty ? this.renderSectionHeaderEmpty : this.renderSectionHeaderNotEmpty}
renderItem={isEmpty ? this.renderItemEmpty : this.renderItemNotEmpty}
style={{minHeight: 300, width: '100%'}}
contentContainerStyle={
isEmpty ?
@ -351,11 +371,12 @@ export default class FetchedDataSectionList extends React.Component<Props, State
}
render() {
const nav = this.props.navigation;
// console.log("rendering FetchedDataSectionList");
const dataset = this.createDataset(this.state.fetchedData);
return (
<BaseContainer
navigation={nav} headerTitle={this.getHeaderTranslation()}
navigation={this.props.navigation}
headerTitle={this.getHeaderTranslation()}
headerRightButton={this.getRightButton()}
hasTabs={this.hasTabs()}
hasBackButton={this.hasBackButton()}
@ -375,5 +396,4 @@ export default class FetchedDataSectionList extends React.Component<Props, State
</BaseContainer>
);
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -8,13 +8,12 @@ import ThemeManager from "../utils/ThemeManager";
import HTML from "react-native-render-html";
import {Linking} from "expo";
import PlanningEventManager from '../utils/PlanningEventManager';
import i18n from 'i18n-js';
type Props = {
navigation: Object,
};
function openWebLink(link) {
function openWebLink(event, link) {
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.
*/
export default class PlanningDisplayScreen extends React.Component<Props> {
render() {
// console.log("rendering planningDisplayScreen");
const nav = this.props.navigation;
const displayData = nav.getParam('data', []);
return (
@ -60,7 +59,7 @@ export default class PlanningDisplayScreen extends React.Component<Props> {
},
div: {color: ThemeManager.getCurrentThemeVariables().textColor}
}}
onLinkPress={(event, link) => openWebLink(link)}/>
onLinkPress={openWebLink}/>
: <View/>}
</Content>
</Container>

View file

@ -5,7 +5,6 @@ import {BackHandler, Image} from 'react-native';
import {H3, Text, View} from 'native-base';
import i18n from "i18n-js";
import ThemeManager from "../utils/ThemeManager";
import {Linking} from "expo";
import BaseContainer from "../components/BaseContainer";
import {Agenda, LocaleConfig} from 'react-native-calendars';
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;
/**
* 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
*/
@ -56,12 +47,21 @@ export default class PlanningScreen extends React.Component<Props, State> {
didFocusSubscription: Function;
willBlurSubscription: Function;
state = {
refreshing: false,
agendaItems: {},
calendarShowing: false,
};
onRefresh: Function;
onCalendarToggled: Function;
getRenderItem: Function;
getRenderEmptyDate: Function;
onAgendaRef: Function;
onCalendarToggled: Function;
onBackButtonPressAndroid: Function;
constructor(props: any) {
super(props);
this.webDataManager = new WebDataManager(FETCH_URL);
@ -76,10 +76,25 @@ export default class PlanningScreen extends React.Component<Props, State> {
if (i18n.currentLocale().startsWith("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() {
this._onRefresh();
this.onRefresh();
this.willBlurSubscription = this.props.navigation.addListener(
'willBlur',
() =>
@ -90,7 +105,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
);
}
onBackButtonPressAndroid = () => {
onBackButtonPressAndroid() {
if (this.state.calendarShowing) {
this.agendaRef.chooseDay(this.agendaRef.state.selectedDay);
return true;
@ -126,10 +141,6 @@ export default class PlanningScreen extends React.Component<Props, State> {
}
getRenderItem(item: Object) {
let navData = {
data: item
};
const nav = this.props.navigation;
return (
<Touchable
style={{
@ -138,7 +149,7 @@ export default class PlanningScreen extends React.Component<Props, State> {
marginRight: 10,
marginTop: 17,
}}
onPress={() => nav.navigate('PlanningDisplayScreen', navData)}>
onPress={() => this.props.navigation.navigate('PlanningDisplayScreen', {data: item})}>
<View style={{
padding: 10,
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
* @private
*/
_onRefresh = () => {
onRefresh = () => {
let canRefresh;
if (this.lastRefresh !== undefined)
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({
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() {
const nav = this.props.navigation;
// console.log("rendering PlanningScreen");
return (
<BaseContainer navigation={nav} headerTitle={i18n.t('screens.planning')}>
<BaseContainer navigation={this.props.navigation} headerTitle={i18n.t('screens.planning')}>
<Agenda
// 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
// considered that the date in question is not yet loaded
items={this.state.agendaItems}
// initially selected day
selected={this.getCurrentDate()}
selected={this.currentDate}
// 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
pastScrollRange={1}
// Max amount of months allowed to scroll to the future. Default = 50
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.
onRefresh={() => this._onRefresh()}
onRefresh={this.onRefresh}
// callback that fires when the calendar is opened or closed
onCalendarToggled={(calendarOpened) => {
this.setState({calendarShowing: calendarOpened})
}}
onCalendarToggled={this.onCalendarToggled}
// Set this true while waiting for new data from a refresh
refreshing={this.state.refreshing}
renderItem={(item) => this.getRenderItem(item)}
renderEmptyDate={() => this.getRenderEmptyDate()}
rowHasChanged={() => this.rowHasChanged()}
renderItem={this.getRenderItem}
renderEmptyDate={this.getRenderEmptyDate}
rowHasChanged={this.rowHasChanged}
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
firstDay={1}
// ref to this agenda in order to handle back button event
ref={(ref) => this.agendaRef = ref}
ref={this.onAgendaRef}
// agenda theme
theme={{
backgroundColor: ThemeManager.getCurrentThemeVariables().agendaBackgroundColor,

View file

@ -1,7 +1,7 @@
// @flow
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 CustomHeader from "../../components/CustomHeader";
import i18n from "i18n-js";

View file

@ -70,12 +70,28 @@ export default class ProximoListScreen extends React.Component<Props, State> {
sortNameIcon: '',
modalCurrentDisplayItem: {},
};
_menu: Menu;
sortMenuRef: Menu;
onMenuRef: Function;
onSearchStringChange: Function;
onSelectSortModeName: Function;
onSelectSortModePrice: Function;
onSortMenuPress: Function;
renderItem: Function;
onListItemPress: Function;
constructor(props: any) {
super(props);
this.modalRef = React.createRef();
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
*/
setMenuRef = (ref: Menu) => {
this._menu = ref;
onMenuRef(ref: Menu) {
this.sortMenuRef = ref;
};
/**
@ -131,7 +147,7 @@ export default class ProximoListScreen extends React.Component<Props, State> {
break;
}
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;
}
search(str: string) {
onSearchStringChange(str: string) {
this.setState({
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({
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() {
return (
<Menu
ref={this.setMenuRef}
ref={this.onMenuRef}
button={
<Touchable
style={{padding: 6}}
onPress={() =>
this._menu.show()
}>
onPress={this.onSortMenuPress}>
<CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon={'sort'}/>
@ -277,12 +304,12 @@ export default class ProximoListScreen extends React.Component<Props, State> {
}
>
<MenuItem
onPress={() => this.sortModeSelected(sortMode.name)}>
onPress={this.onSelectSortModeName}>
{this.state.sortNameIcon}
{i18n.t('proximoScreen.sortName')}
</MenuItem>
<MenuItem
onPress={() => this.sortModeSelected(sortMode.price)}>
onPress={this.onSelectSortModePrice}>
{this.state.sortPriceIcon}
{i18n.t('proximoScreen.sortPrice')}
</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() {
// console.log("rendering ProximoListScreen");
const nav = this.props.navigation;
return (
<Container>
@ -303,7 +362,7 @@ export default class ProximoListScreen extends React.Component<Props, State> {
hasBackButton={true}
navigation={nav}
hasSearchField={true}
searchCallback={(text) => this.search(text)}
searchCallback={this.onSearchStringChange}
shouldFocusSearchBar={this.shouldFocusSearchBar}
rightButton={this.getSortMenu()}
/>
@ -311,35 +370,9 @@ export default class ProximoListScreen extends React.Component<Props, State> {
<FlatList
data={this.state.currentlyDisplayedData}
extraData={this.state.currentlyDisplayedData}
keyExtractor={(item) => item.name + item.code}
keyExtractor={this.keyExtractor}
style={{minHeight: 300, width: '100%'}}
renderItem={({item}) =>
<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>}
renderItem={this.renderItem}
/>
</Container>
);

View file

@ -9,7 +9,7 @@ import FetchedDataSectionList from "../../components/FetchedDataSectionList";
import ThemeManager from "../../utils/ThemeManager";
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 {
onPressSearchBtn: Function;
onPressAboutBtn: Function;
constructor() {
super(DATA_URL, 0);
this.onPressSearchBtn = this.onPressSearchBtn.bind(this);
this.onPressAboutBtn = this.onPressAboutBtn.bind(this);
}
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() {
@ -63,7 +82,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
let articles = fetchedData.articles;
finalData.push({
type: {
id: "0",
id: -1,
name: i18n.t('proximoScreen.all'),
icon: 'star'
},
@ -100,7 +119,7 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
return availableArticles;
}
getRightButton() {
onPressSearchBtn() {
let searchScreenData = {
shouldFocusSearchBar: true,
data: {
@ -113,8 +132,14 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
this.getAvailableArticles(this.state.fetchedData.articles, undefined) : []
},
};
this.props.navigation.navigate('ProximoListScreen', searchScreenData);
}
onPressAboutBtn() {
this.props.navigation.navigate('ProximoAboutScreen');
}
getRightButton() {
return (
<View
style={{
@ -122,14 +147,14 @@ export default class ProximoMainScreen extends FetchedDataSectionList {
}}>
<Touchable
style={{padding: 6}}
onPress={() => this.props.navigation.navigate('ProximoListScreen', searchScreenData)}>
onPress={this.onPressSearchBtn}>
<CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
icon="magnify"/>
</Touchable>
<Touchable
style={{padding: 6}}
onPress={() => this.props.navigation.navigate('ProximoAboutScreen')}>
onPress={this.onPressAboutBtn}>
<CustomMaterialIcon
color={Platform.OS === 'ios' ? ThemeManager.getCurrentThemeVariables().brandPrimary : "#fff"}
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 = {
shouldFocusSearchBar: false,
data: item,
};
const onPress = this.props.navigation.navigate.bind(this, 'ProximoListScreen', dataToSend);
if (item.data.length > 0) {
return (
<ListItem
button
thumbnail
onPress={() => {
this.props.navigation.navigate('ProximoListScreen', dataToSend);
}}
onPress={onPress}
>
<Left>
<CustomMaterialIcon

View file

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

View file

@ -7,7 +7,7 @@ import ThemeManager from "../utils/ThemeManager";
import i18n from "i18n-js";
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.
@ -119,7 +119,7 @@ export default class SelfMenuScreen extends FetchedDataSectionList {
);
}
getRenderItem(item: Object, section: Object, data: Object) {
getRenderItem(item: Object, section: Object) {
return (
<Card style={{
flex: 0,

View file

@ -43,6 +43,17 @@ export default class SettingsScreen extends React.Component<Props, State> {
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
*
@ -118,7 +129,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
mode="dropdown"
style={{width: 120}}
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.5')} value="5"/>
@ -141,7 +152,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
mode="dropdown"
style={{width: 120}}
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.planning')} value="Planning"/>
@ -155,7 +166,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
/**
* Toggle night mode and save it to preferences
*/
toggleNightMode() {
onToggleNightMode() {
ThemeManager.getInstance().setNightMode(!this.state.nightMode);
this.setState({nightMode: !this.state.nightMode});
this.resetStack();
@ -203,7 +214,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
<Right>
<CheckBox
checked={this.state.nightMode}
onPress={() => this.toggleNightMode()}
onPress={onPressCallback}
style={{marginRight: 20}}/>
</Right>
</ListItem>
@ -221,7 +232,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
<Text>{i18n.t('settingsScreen.generalCard')}</Text>
</CardItem>
<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'))}
</List>
</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.

View file

@ -12,8 +12,8 @@ type Props = {
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';
const CUSTOM_CSS_GENERAL = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/rooms/customMobile.css';
const CUSTOM_CSS_Bib = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/rooms/customBibMobile.css';
/**
* 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 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.

View file

@ -11,12 +11,12 @@ type Props = {
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');
// stylesheet.type = 'text/css';
// 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');
// mobileSpec.name = 'viewport';
// 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 CUSTOM_CSS_GENERAL = 'https://srv-falcon.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_GENERAL = 'https://etud.insa-toulouse.fr/~amicale_app/custom_css/planex/customMobile3.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
// // 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.

View file

@ -6,7 +6,7 @@ import AsyncStorageManager from "./AsyncStorageManager";
import LocaleManager from "./LocaleManager";
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
@ -123,18 +123,8 @@ export default class NotificationsManager {
'Content-Type': 'application/json',
}),
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',
}),
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',
}),
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);
fetchedData = await response.json();
} catch (error) {
console.log('Could not read FetchedData from server');
console.log(error);
// console.log('Could not read FetchedData from server');
// console.log(error);
throw new Error('Could not read FetchedData from server');
}
this.lastDataFetched = fetchedData;