Compare commits
8 commits
1ede8f4e9a
...
8723765e43
| Author | SHA1 | Date | |
|---|---|---|---|
| 8723765e43 | |||
| ab554cae94 | |||
| ba893495e1 | |||
| 7829b893c5 | |||
| 557dd000ae | |||
| 2d26f2c395 | |||
| cacfb2862c | |||
| 17016b6452 |
24 changed files with 314 additions and 333 deletions
5
App.js
5
App.js
|
|
@ -14,6 +14,7 @@ import {initExpoToken} from "./utils/Notifications";
|
|||
import {Provider as PaperProvider} from 'react-native-paper';
|
||||
import AprilFoolsManager from "./managers/AprilFoolsManager";
|
||||
import Update from "./constants/Update";
|
||||
import ConnectionManager from "./managers/ConnectionManager";
|
||||
|
||||
type Props = {};
|
||||
|
||||
|
|
@ -91,6 +92,10 @@ export default class App extends React.Component<Props, State> {
|
|||
await AsyncStorageManager.getInstance().loadPreferences();
|
||||
ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme);
|
||||
await initExpoToken();
|
||||
try {
|
||||
await ConnectionManager.getInstance().recoverLogin();
|
||||
} catch (e) {}
|
||||
|
||||
this.onLoadFinished();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,20 +11,19 @@ afterEach(() => {
|
|||
});
|
||||
|
||||
test('isLoggedIn yes', () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.resolve(true);
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return 'token';
|
||||
});
|
||||
return expect(c.isLoggedIn()).resolves.toBe(true);
|
||||
return expect(c.isLoggedIn()).toBe(true);
|
||||
});
|
||||
|
||||
test('isLoggedIn no', () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.reject(false);
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return null;
|
||||
});
|
||||
return expect(c.isLoggedIn()).rejects.toBe(false);
|
||||
return expect(c.isLoggedIn()).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
test('recoverLogin error crypto', () => {
|
||||
jest.spyOn(SecureStore, 'getItemAsync').mockImplementationOnce(() => {
|
||||
return Promise.reject();
|
||||
|
|
@ -111,7 +110,12 @@ test("isConnectionResponseValid", () => {
|
|||
state: false,
|
||||
};
|
||||
expect(c.isConnectionResponseValid(json)).toBeTrue();
|
||||
|
||||
json = {
|
||||
state: false,
|
||||
message: 'Adresse mail ou mot de passe incorrect',
|
||||
token: ''
|
||||
};
|
||||
expect(c.isConnectionResponseValid(json)).toBeTrue();
|
||||
json = {
|
||||
state: true,
|
||||
message: 'Connexion confirmée',
|
||||
|
|
@ -143,7 +147,6 @@ test("isConnectionResponseValid", () => {
|
|||
expect(c.isConnectionResponseValid(json)).toBeFalse();
|
||||
});
|
||||
|
||||
|
||||
test("connect bad credentials", () => {
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
|
|
@ -178,6 +181,25 @@ test("connect good credentials", () => {
|
|||
return expect(c.connect('email', 'password')).resolves.toBeTruthy();
|
||||
});
|
||||
|
||||
test("connect good credentials no consent", () => {
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
json: () => {
|
||||
return {
|
||||
state: false,
|
||||
message: 'pas de consent',
|
||||
token: '',
|
||||
data: {
|
||||
consent: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
});
|
||||
return expect(c.connect('email', 'password'))
|
||||
.rejects.toBe(ERROR_TYPE.NO_CONSENT);
|
||||
});
|
||||
|
||||
test("connect good credentials, fail save token", () => {
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
|
|
@ -221,8 +243,8 @@ test("connect bogus response 1", () => {
|
|||
|
||||
|
||||
test("authenticatedRequest success", () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.resolve('token');
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return 'token';
|
||||
});
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
|
|
@ -236,8 +258,8 @@ test("authenticatedRequest success", () => {
|
|||
});
|
||||
|
||||
test("authenticatedRequest error wrong token", () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.resolve('token');
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return 'token';
|
||||
});
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
|
|
@ -251,8 +273,8 @@ test("authenticatedRequest error wrong token", () => {
|
|||
});
|
||||
|
||||
test("authenticatedRequest error bogus response", () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.resolve('token');
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return 'token';
|
||||
});
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
|
|
@ -266,8 +288,8 @@ test("authenticatedRequest error bogus response", () => {
|
|||
});
|
||||
|
||||
test("authenticatedRequest connection error", () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.resolve('token');
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return 'token';
|
||||
});
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.reject()
|
||||
|
|
@ -277,8 +299,8 @@ test("authenticatedRequest connection error", () => {
|
|||
});
|
||||
|
||||
test("authenticatedRequest error no token", () => {
|
||||
jest.spyOn(ConnectionManager.prototype, 'recoverLogin').mockImplementationOnce(() => {
|
||||
return Promise.reject(false);
|
||||
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
|
||||
return null;
|
||||
});
|
||||
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
|
||||
return Promise.resolve({
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
|||
loading: true,
|
||||
};
|
||||
|
||||
currentUserToken: string;
|
||||
currentUserToken: string | null;
|
||||
connectionManager: ConnectionManager;
|
||||
errorCode: number;
|
||||
data: Object;
|
||||
|
|
@ -35,8 +35,6 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
|||
this.colors = props.theme.colors;
|
||||
this.connectionManager = ConnectionManager.getInstance();
|
||||
this.props.navigation.addListener('focus', this.onScreenFocus.bind(this));
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
onScreenFocus() {
|
||||
|
|
@ -47,26 +45,24 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
|||
fetchData = () => {
|
||||
if (!this.state.loading)
|
||||
this.setState({loading: true});
|
||||
this.connectionManager.isLoggedIn()
|
||||
.then(() => {
|
||||
this.connectionManager.authenticatedRequest(this.props.link)
|
||||
.then((data) => {
|
||||
this.onFinishedLoading(data, -1);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.onFinishedLoading(undefined, error);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.onFinishedLoading(undefined, ERROR_TYPE.BAD_CREDENTIALS);
|
||||
});
|
||||
if (this.connectionManager.isLoggedIn()) {
|
||||
this.connectionManager.authenticatedRequest(this.props.link)
|
||||
.then((data) => {
|
||||
this.onFinishedLoading(data, -1);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.onFinishedLoading(undefined, error);
|
||||
});
|
||||
} else {
|
||||
this.onFinishedLoading(undefined, ERROR_TYPE.BAD_CREDENTIALS);
|
||||
}
|
||||
};
|
||||
|
||||
onFinishedLoading(data: Object, error: number) {
|
||||
this.data = data;
|
||||
this.currentUserToken = data !== undefined
|
||||
? this.connectionManager.getToken()
|
||||
: '';
|
||||
: null;
|
||||
this.errorCode = error;
|
||||
this.setState({loading: false});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function HeaderButton(props) {
|
|||
<IconButton
|
||||
icon={props.icon}
|
||||
size={26}
|
||||
color={colors.text}
|
||||
color={props.color !== undefined ? props.color : colors.text}
|
||||
onPress={props.onPress}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import * as React from 'react';
|
||||
import {Button, Card, withTheme} from 'react-native-paper';
|
||||
import {StyleSheet} from "react-native";
|
||||
import i18n from 'i18n-js';
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
|
|
@ -20,6 +21,8 @@ class ActionsDashBoardItem extends React.PureComponent<Props> {
|
|||
|
||||
openDrawer = () => this.props.navigation.openDrawer();
|
||||
|
||||
gotToSettings = () => this.props.navigation.navigate("SettingsScreen");
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Card style={{
|
||||
|
|
@ -31,10 +34,17 @@ class ActionsDashBoardItem extends React.PureComponent<Props> {
|
|||
icon="information"
|
||||
mode="contained"
|
||||
onPress={this.openDrawer}
|
||||
style={styles.button}
|
||||
style={styles.servicesButton}
|
||||
>
|
||||
PLUS DE SERVICES
|
||||
{i18n.t("homeScreen.servicesButton")}
|
||||
</Button>
|
||||
<Button
|
||||
icon="settings"
|
||||
mode="contained"
|
||||
onPress={this.gotToSettings}
|
||||
style={styles.settingsButton}
|
||||
compact
|
||||
/>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
);
|
||||
|
|
@ -58,8 +68,12 @@ const styles = StyleSheet.create({
|
|||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
button: {
|
||||
servicesButton: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 5,
|
||||
},
|
||||
settingsButton: {
|
||||
marginLeft: 5,
|
||||
marginRight: 'auto',
|
||||
}
|
||||
});
|
||||
|
|
|
|||
40
components/Lists/ProximoListItem.js
Normal file
40
components/Lists/ProximoListItem.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, List, Text, withTheme} from 'react-native-paper';
|
||||
import i18n from "i18n-js";
|
||||
|
||||
type Props = {
|
||||
onPress: Function,
|
||||
color: string,
|
||||
item: Object,
|
||||
}
|
||||
|
||||
class ProximoListItem extends React.PureComponent<Props> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.colors = props.theme.colors;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<List.Item
|
||||
title={this.props.item.name}
|
||||
description={this.props.item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
|
||||
descriptionStyle={{color: this.props.color}}
|
||||
onPress={this.props.onPress}
|
||||
left={() => <Avatar.Image style={{backgroundColor: 'transparent'}} size={64}
|
||||
source={{uri: this.props.item.image}}/>}
|
||||
right={() =>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
{this.props.item.price}€
|
||||
</Text>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(ProximoListItem);
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import {FlatList} from "react-native";
|
||||
|
||||
type Props = {
|
||||
data: Array<Object>,
|
||||
keyExtractor: Function,
|
||||
renderItem: Function,
|
||||
updateData: number,
|
||||
}
|
||||
|
||||
/**
|
||||
* FlatList implementing PureComponent for increased performance.
|
||||
*
|
||||
* This is a pure component, meaning it will only update if a shallow comparison of state and props is different.
|
||||
* To force the component to update, change the value of updateData.
|
||||
*/
|
||||
export default class PureFlatList extends React.PureComponent<Props> {
|
||||
|
||||
static defaultProps = {
|
||||
updateData: null,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<FlatList
|
||||
data={this.props.data}
|
||||
keyExtractor={this.props.keyExtractor}
|
||||
style={{minHeight: 300, width: '100%'}}
|
||||
renderItem={this.props.renderItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -57,8 +57,6 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
|||
onFetchSuccess: Function;
|
||||
onFetchError: Function;
|
||||
getEmptySectionHeader: Function;
|
||||
showSnackBar: Function;
|
||||
hideSnackBar: Function;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -67,8 +65,6 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
|||
this.onFetchSuccess = this.onFetchSuccess.bind(this);
|
||||
this.onFetchError = this.onFetchError.bind(this);
|
||||
this.getEmptySectionHeader = this.getEmptySectionHeader.bind(this);
|
||||
this.showSnackBar = this.showSnackBar.bind(this);
|
||||
this.hideSnackBar = this.hideSnackBar.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -159,16 +155,12 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
|||
/**
|
||||
* Shows the error popup
|
||||
*/
|
||||
showSnackBar() {
|
||||
this.setState({snackbarVisible: true})
|
||||
}
|
||||
showSnackBar = () => this.setState({snackbarVisible: true});
|
||||
|
||||
/**
|
||||
* Hides the error popup
|
||||
*/
|
||||
hideSnackBar() {
|
||||
this.setState({snackbarVisible: false})
|
||||
}
|
||||
hideSnackBar = () => this.setState({snackbarVisible: false});
|
||||
|
||||
render() {
|
||||
let dataset = [];
|
||||
|
|
@ -177,20 +169,10 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
|||
const shouldRenderHeader = this.props.renderSectionHeader !== null;
|
||||
return (
|
||||
<View>
|
||||
<Snackbar
|
||||
visible={this.state.snackbarVisible}
|
||||
onDismiss={this.hideSnackBar}
|
||||
action={{
|
||||
label: 'OK',
|
||||
onPress: this.hideSnackBar,
|
||||
}}
|
||||
duration={4000}
|
||||
>
|
||||
{i18n.t("homeScreen.listUpdateFail")}
|
||||
</Snackbar>
|
||||
{/*$FlowFixMe*/}
|
||||
<SectionList
|
||||
sections={dataset}
|
||||
extraData={this.props.updateData}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={this.state.refreshing}
|
||||
|
|
@ -212,6 +194,17 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
|||
onRefresh={this.onRefresh}/>
|
||||
}
|
||||
/>
|
||||
<Snackbar
|
||||
visible={this.state.snackbarVisible}
|
||||
onDismiss={this.hideSnackBar}
|
||||
action={{
|
||||
label: 'OK',
|
||||
onPress: () => {},
|
||||
}}
|
||||
duration={4000}
|
||||
>
|
||||
{i18n.t("homeScreen.listUpdateFail")}
|
||||
</Snackbar>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ type Props = {
|
|||
};
|
||||
|
||||
type State = {
|
||||
active: string,
|
||||
isLoggedIn: boolean,
|
||||
dialogVisible: boolean,
|
||||
};
|
||||
|
|
@ -154,18 +153,16 @@ class SideBar extends React.PureComponent<Props, State> {
|
|||
];
|
||||
this.getRenderItem = this.getRenderItem.bind(this);
|
||||
this.colors = props.theme.colors;
|
||||
ConnectionManager.getInstance().setLoginCallback((value) => this.onLoginStateChange(value));
|
||||
ConnectionManager.getInstance().addLoginStateListener((value) => this.onLoginStateChange(value));
|
||||
this.state = {
|
||||
active: 'Home',
|
||||
isLoggedIn: false,
|
||||
isLoggedIn: ConnectionManager.getInstance().isLoggedIn(),
|
||||
dialogVisible: false,
|
||||
};
|
||||
ConnectionManager.getInstance().isLoggedIn().then(data => undefined).catch(error => undefined);
|
||||
}
|
||||
|
||||
showDisconnectDialog = () => this.setState({ dialogVisible: true });
|
||||
showDisconnectDialog = () => this.setState({dialogVisible: true});
|
||||
|
||||
hideDisconnectDialog = () => this.setState({ dialogVisible: false });
|
||||
hideDisconnectDialog = () => this.setState({dialogVisible: false});
|
||||
|
||||
|
||||
onLoginStateChange(isLoggedIn: boolean) {
|
||||
|
|
@ -239,9 +236,10 @@ class SideBar extends React.PureComponent<Props, State> {
|
|||
style={styles.drawerCover}
|
||||
/>
|
||||
</TouchableRipple>
|
||||
{/*$FlowFixMe*/}
|
||||
<FlatList
|
||||
data={this.dataSet}
|
||||
extraData={this.state}
|
||||
extraData={this.state.isLoggedIn}
|
||||
keyExtractor={this.listKeyExtractor}
|
||||
renderItem={this.getRenderItem}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -16,9 +16,14 @@ export default class ConnectionManager {
|
|||
static instance: ConnectionManager | null = null;
|
||||
|
||||
#email: string;
|
||||
#token: string;
|
||||
#token: string | null;
|
||||
|
||||
loginCallback: Function;
|
||||
listeners: Array<Function>;
|
||||
|
||||
constructor() {
|
||||
this.#token = null;
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this class instance or create one if none is found
|
||||
|
|
@ -35,20 +40,24 @@ export default class ConnectionManager {
|
|||
}
|
||||
|
||||
onLoginStateChange(newState: boolean) {
|
||||
this.loginCallback(newState);
|
||||
for (let i = 0; i < this.listeners.length; i++) {
|
||||
if (this.listeners[i] !== undefined)
|
||||
this.listeners[i](newState);
|
||||
}
|
||||
}
|
||||
|
||||
setLoginCallback(callback: Function) {
|
||||
this.loginCallback = callback;
|
||||
addLoginStateListener(listener: Function) {
|
||||
this.listeners.push(listener);
|
||||
}
|
||||
|
||||
async recoverLogin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.#token !== undefined)
|
||||
resolve(this.#token);
|
||||
if (this.getToken() !== null)
|
||||
resolve(this.getToken());
|
||||
else {
|
||||
SecureStore.getItemAsync('token')
|
||||
.then((token) => {
|
||||
this.#token = token;
|
||||
if (token !== null) {
|
||||
this.onLoginStateChange(true);
|
||||
resolve(token);
|
||||
|
|
@ -62,16 +71,8 @@ export default class ConnectionManager {
|
|||
});
|
||||
}
|
||||
|
||||
async isLoggedIn() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.recoverLogin()
|
||||
.then(() => {
|
||||
resolve(true);
|
||||
})
|
||||
.catch(() => {
|
||||
reject(false);
|
||||
})
|
||||
});
|
||||
isLoggedIn() {
|
||||
return this.getToken() !== null;
|
||||
}
|
||||
|
||||
async saveLogin(email: string, token: string) {
|
||||
|
|
@ -93,6 +94,7 @@ export default class ConnectionManager {
|
|||
return new Promise((resolve, reject) => {
|
||||
SecureStore.deleteItemAsync('token')
|
||||
.then(() => {
|
||||
this.#token = null;
|
||||
this.onLoginStateChange(false);
|
||||
resolve(true);
|
||||
})
|
||||
|
|
@ -126,7 +128,9 @@ export default class ConnectionManager {
|
|||
.catch(() => {
|
||||
reject(ERROR_TYPE.SAVE_TOKEN);
|
||||
});
|
||||
} else if (data.data.consent !== undefined && !data.data.consent)
|
||||
} else if (data.data !== undefined
|
||||
&& data.data.consent !== undefined
|
||||
&& !data.data.consent)
|
||||
reject(ERROR_TYPE.NO_CONSENT);
|
||||
else
|
||||
reject(ERROR_TYPE.BAD_CREDENTIALS);
|
||||
|
|
@ -166,32 +170,29 @@ export default class ConnectionManager {
|
|||
|
||||
async authenticatedRequest(url: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.recoverLogin()
|
||||
.then(token => {
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: JSON.stringify({token: token})
|
||||
}).then(async (response) => response.json())
|
||||
.then((data) => {
|
||||
if (this.isRequestResponseValid(data)) {
|
||||
if (data.state)
|
||||
resolve(data.data);
|
||||
else
|
||||
reject(ERROR_TYPE.BAD_CREDENTIALS);
|
||||
} else
|
||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
||||
})
|
||||
.catch(() => {
|
||||
if (this.getToken() !== null) {
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: JSON.stringify({token: this.getToken()})
|
||||
}).then(async (response) => response.json())
|
||||
.then((data) => {
|
||||
if (this.isRequestResponseValid(data)) {
|
||||
if (data.state)
|
||||
resolve(data.data);
|
||||
else
|
||||
reject(ERROR_TYPE.BAD_CREDENTIALS);
|
||||
} else
|
||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
reject(ERROR_TYPE.NO_TOKEN);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
||||
});
|
||||
} else
|
||||
reject(ERROR_TYPE.NO_TOKEN);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -244,9 +244,7 @@ class AboutScreen extends React.Component<Props, State> {
|
|||
<Card.Content>
|
||||
<FlatList
|
||||
data={this.appData}
|
||||
extraData={this.state}
|
||||
keyExtractor={this.keyExtractor}
|
||||
listKey={"app"}
|
||||
renderItem={this.getCardItem}
|
||||
/>
|
||||
</Card.Content>
|
||||
|
|
@ -269,17 +267,15 @@ class AboutScreen extends React.Component<Props, State> {
|
|||
<Title>{i18n.t('aboutScreen.author')}</Title>
|
||||
<FlatList
|
||||
data={this.authorData}
|
||||
extraData={this.state}
|
||||
keyExtractor={this.keyExtractor}
|
||||
listKey={"team1"}
|
||||
listKey={"1"}
|
||||
renderItem={this.getCardItem}
|
||||
/>
|
||||
<Title>{i18n.t('aboutScreen.additionalDev')}</Title>
|
||||
<FlatList
|
||||
data={this.additionalDevData}
|
||||
extraData={this.state}
|
||||
keyExtractor={this.keyExtractor}
|
||||
listKey={"team2"}
|
||||
listKey={"2"}
|
||||
renderItem={this.getCardItem}
|
||||
/>
|
||||
</Card.Content>
|
||||
|
|
@ -299,9 +295,7 @@ class AboutScreen extends React.Component<Props, State> {
|
|||
<Title>{i18n.t('aboutScreen.technologies')}</Title>
|
||||
<FlatList
|
||||
data={this.technoData}
|
||||
extraData={this.state}
|
||||
keyExtractor={this.keyExtractor}
|
||||
listKey={"techno"}
|
||||
renderItem={this.getCardItem}
|
||||
/>
|
||||
</Card.Content>
|
||||
|
|
@ -404,28 +398,24 @@ class AboutScreen extends React.Component<Props, State> {
|
|||
<Button
|
||||
icon="email"
|
||||
mode="contained"
|
||||
dark={true}
|
||||
color={this.colors.primary}
|
||||
style={{
|
||||
marginTop: 20,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
onPress={this.onPressMail}>
|
||||
<Text>{i18n.t('aboutScreen.bugsMail')}</Text>
|
||||
{i18n.t('aboutScreen.bugsMail')}
|
||||
</Button>
|
||||
<Button
|
||||
icon="git"
|
||||
mode="contained"
|
||||
dark={true}
|
||||
color={this.colors.primary}
|
||||
style={{
|
||||
marginTop: 20,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}
|
||||
onPress={this.onPressGit}>
|
||||
<Text>{i18n.t('aboutScreen.bugsGit')}</Text>
|
||||
{i18n.t('aboutScreen.bugsGit')}
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -476,8 +466,6 @@ class AboutScreen extends React.Component<Props, State> {
|
|||
<FlatList
|
||||
style={{padding: 5}}
|
||||
data={this.dataOrder}
|
||||
extraData={this.state}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={this.getMainCard}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ const emailRegex = /^.+@.+\..+$/;
|
|||
class LoginScreen extends React.Component<Props, State> {
|
||||
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
email: 'vergnet@etud.insa-toulouse.fr',
|
||||
password: '3D514ùdsqg',
|
||||
isEmailValidated: false,
|
||||
isPasswordValidated: false,
|
||||
loading: false,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardI
|
|||
import {stringToDate} from "../utils/Planning";
|
||||
import {openBrowser} from "../utils/WebBrowser";
|
||||
import ActionsDashBoardItem from "../components/Home/ActionsDashboardItem";
|
||||
import HeaderButton from "../components/Custom/HeaderButton";
|
||||
import ConnectionManager from "../managers/ConnectionManager";
|
||||
// import DATA from "../dashboard_data.json";
|
||||
|
||||
|
||||
|
|
@ -30,15 +32,10 @@ type Props = {
|
|||
theme: Object,
|
||||
}
|
||||
|
||||
type State = {
|
||||
imageModalVisible: boolean,
|
||||
imageList: Array<Object>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Class defining the app's home screen
|
||||
*/
|
||||
class HomeScreen extends React.Component<Props, State> {
|
||||
class HomeScreen extends React.Component<Props> {
|
||||
|
||||
onProxiwashClick: Function;
|
||||
onTutorInsaClick: Function;
|
||||
|
|
@ -49,10 +46,7 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
|
||||
colors: Object;
|
||||
|
||||
state = {
|
||||
imageModalVisible: false,
|
||||
imageList: [],
|
||||
};
|
||||
isLoggedIn: boolean | null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -63,6 +57,8 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
this.getRenderItem = this.getRenderItem.bind(this);
|
||||
this.createDataset = this.createDataset.bind(this);
|
||||
this.colors = props.theme.colors;
|
||||
|
||||
this.isLoggedIn = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,6 +72,35 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.navigation.addListener('focus', this.onScreenFocus);
|
||||
}
|
||||
|
||||
onScreenFocus = () => {
|
||||
if (this.isLoggedIn !== ConnectionManager.getInstance().isLoggedIn()) {
|
||||
this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn();
|
||||
this.props.navigation.setOptions({
|
||||
headerRight: this.getHeaderButton,
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
getHeaderButton = () => {
|
||||
const screen = this.isLoggedIn
|
||||
? "ProfileScreen"
|
||||
: "LoginScreen";
|
||||
const icon = this.isLoggedIn
|
||||
? "account"
|
||||
: "login";
|
||||
const onPress = () => this.props.navigation.navigate(screen);
|
||||
return <HeaderButton
|
||||
icon={icon}
|
||||
onPress={onPress}
|
||||
color={this.isLoggedIn ? undefined : this.colors.primary}
|
||||
/>;
|
||||
};
|
||||
|
||||
onProxiwashClick() {
|
||||
this.props.navigation.navigate('Proxiwash');
|
||||
}
|
||||
|
|
@ -92,16 +117,6 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
this.props.navigation.navigate('SelfMenuScreen');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a key for the given item
|
||||
*
|
||||
* @param item The item to extract the key from
|
||||
* @return {*} The extracted key
|
||||
*/
|
||||
getKeyExtractor(item: Object) {
|
||||
return item !== undefined ? item.id : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the dataset to be used in the FlatList
|
||||
*
|
||||
|
|
@ -120,15 +135,11 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
{
|
||||
title: '',
|
||||
data: dashboardData,
|
||||
extraData: super.state,
|
||||
keyExtractor: this.getKeyExtractor,
|
||||
id: SECTIONS_ID[0]
|
||||
},
|
||||
{
|
||||
title: i18n.t('homeScreen.newsFeed'),
|
||||
data: newsData,
|
||||
extraData: super.state,
|
||||
keyExtractor: this.getKeyExtractor,
|
||||
id: SECTIONS_ID[1]
|
||||
}
|
||||
];
|
||||
|
|
@ -426,18 +437,6 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
openBrowser(link, this.colors.primary);
|
||||
}
|
||||
|
||||
showImageModal(imageList) {
|
||||
this.setState({
|
||||
imageModalVisible: true,
|
||||
imageList: imageList,
|
||||
});
|
||||
};
|
||||
|
||||
hideImageModal = () => {
|
||||
this.setState({imageModalVisible: false});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a render item for the given feed object
|
||||
*
|
||||
|
|
@ -472,16 +471,13 @@ class HomeScreen extends React.Component<Props, State> {
|
|||
render() {
|
||||
const nav = this.props.navigation;
|
||||
return (
|
||||
<View>
|
||||
<WebSectionList
|
||||
createDataset={this.createDataset}
|
||||
navigation={nav}
|
||||
autoRefreshTime={REFRESH_TIME}
|
||||
refreshOnFocus={true}
|
||||
fetchUrl={DATA_URL}
|
||||
renderItem={this.getRenderItem}/>
|
||||
</View>
|
||||
|
||||
<WebSectionList
|
||||
createDataset={this.createDataset}
|
||||
navigation={nav}
|
||||
autoRefreshTime={REFRESH_TIME}
|
||||
refreshOnFocus={true}
|
||||
fetchUrl={DATA_URL}
|
||||
renderItem={this.getRenderItem}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Image, Platform, ScrollView, View} from "react-native";
|
||||
import {FlatList, Image, Platform, ScrollView, View} from "react-native";
|
||||
import i18n from "i18n-js";
|
||||
import CustomModal from "../../components/Custom/CustomModal";
|
||||
import {Avatar, IconButton, List, RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
|
||||
import PureFlatList from "../../components/Lists/PureFlatList";
|
||||
import {IconButton, RadioButton, Searchbar, Subheading, Text, Title, withTheme} from "react-native-paper";
|
||||
import {stringMatchQuery} from "../../utils/Search";
|
||||
import ProximoListItem from "../../components/Lists/ProximoListItem";
|
||||
|
||||
function sortPrice(a, b) {
|
||||
return a.price - b.price;
|
||||
|
|
@ -39,7 +40,7 @@ type Props = {
|
|||
type State = {
|
||||
currentSortMode: number,
|
||||
modalCurrentDisplayItem: React.Node,
|
||||
currentlyDisplayedData: Array<Object>,
|
||||
currentSearchString: string,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -48,30 +49,21 @@ type State = {
|
|||
class ProximoListScreen extends React.Component<Props, State> {
|
||||
|
||||
modalRef: Object;
|
||||
originalData: Array<Object>;
|
||||
listData: Array<Object>;
|
||||
shouldFocusSearchBar: boolean;
|
||||
|
||||
onSearchStringChange: Function;
|
||||
onSortMenuPress: Function;
|
||||
renderItem: Function;
|
||||
onModalRef: Function;
|
||||
|
||||
colors: Object;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.originalData = this.props.route.params['data']['data'];
|
||||
this.listData = this.props.route.params['data']['data'];
|
||||
this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar'];
|
||||
this.state = {
|
||||
currentlyDisplayedData: this.originalData.sort(sortName),
|
||||
currentSearchString: '',
|
||||
currentSortMode: 3,
|
||||
modalCurrentDisplayItem: null,
|
||||
};
|
||||
|
||||
this.onSearchStringChange = this.onSearchStringChange.bind(this);
|
||||
this.onSortMenuPress = this.onSortMenuPress.bind(this);
|
||||
this.renderItem = this.renderItem.bind(this);
|
||||
this.onModalRef = this.onModalRef.bind(this);
|
||||
this.colors = props.theme.colors;
|
||||
}
|
||||
|
||||
|
|
@ -80,11 +72,9 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
* Creates the header content
|
||||
*/
|
||||
componentDidMount() {
|
||||
const button = this.getSortMenuButton.bind(this);
|
||||
const title = this.getSearchBar.bind(this);
|
||||
this.props.navigation.setOptions({
|
||||
headerRight: button,
|
||||
headerTitle: title,
|
||||
headerRight: this.getSortMenuButton,
|
||||
headerTitle: this.getSearchBar,
|
||||
headerBackTitleVisible: false,
|
||||
headerTitleContainerStyle: Platform.OS === 'ios' ?
|
||||
{marginHorizontal: 0, width: '70%'} :
|
||||
|
|
@ -97,21 +87,21 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getSearchBar() {
|
||||
getSearchBar = () => {
|
||||
return (
|
||||
<Searchbar
|
||||
placeholder={i18n.t('proximoScreen.search')}
|
||||
onChangeText={this.onSearchStringChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the sort menu header button
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getSortMenuButton() {
|
||||
getSortMenuButton = () => {
|
||||
return (
|
||||
<IconButton
|
||||
icon="sort"
|
||||
|
|
@ -120,20 +110,20 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
onPress={this.onSortMenuPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback used when clicking on the sort menu button.
|
||||
* It will open the modal to show a sort selection
|
||||
*/
|
||||
onSortMenuPress() {
|
||||
onSortMenuPress = () => {
|
||||
this.setState({
|
||||
modalCurrentDisplayItem: this.getModalSortMenu()
|
||||
});
|
||||
if (this.modalRef) {
|
||||
this.modalRef.open();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current sort mode.
|
||||
|
|
@ -144,19 +134,18 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
this.setState({
|
||||
currentSortMode: mode,
|
||||
});
|
||||
let data = this.state.currentlyDisplayedData;
|
||||
switch (mode) {
|
||||
case 1:
|
||||
data.sort(sortPrice);
|
||||
this.listData.sort(sortPrice);
|
||||
break;
|
||||
case 2:
|
||||
data.sort(sortPriceReverse);
|
||||
this.listData.sort(sortPriceReverse);
|
||||
break;
|
||||
case 3:
|
||||
data.sort(sortName);
|
||||
this.listData.sort(sortName);
|
||||
break;
|
||||
case 4:
|
||||
data.sort(sortNameReverse);
|
||||
this.listData.sort(sortNameReverse);
|
||||
break;
|
||||
}
|
||||
if (this.modalRef && mode !== this.state.currentSortMode) {
|
||||
|
|
@ -181,46 +170,14 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the given string to improve search performance
|
||||
*
|
||||
* @param str The string to sanitize
|
||||
* @return {string} The sanitized string
|
||||
*/
|
||||
sanitizeString(str: string): string {
|
||||
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only articles whose name contains the given string.
|
||||
* Case and accents insensitive.
|
||||
*
|
||||
* @param str The string used to filter article names
|
||||
* @returns {[]}
|
||||
*/
|
||||
filterData(str: string) {
|
||||
let filteredData = [];
|
||||
const testStr = this.sanitizeString(str);
|
||||
const articles = this.originalData;
|
||||
for (const article of articles) {
|
||||
const name = this.sanitizeString(article.name);
|
||||
if (name.includes(testStr)) {
|
||||
filteredData.push(article)
|
||||
}
|
||||
}
|
||||
return filteredData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used when the search changes
|
||||
*
|
||||
* @param str The new search string
|
||||
*/
|
||||
onSearchStringChange(str: string) {
|
||||
this.setState({
|
||||
currentlyDisplayedData: this.filterData(str)
|
||||
})
|
||||
}
|
||||
onSearchStringChange = (str: string) => {
|
||||
this.setState({currentSearchString: str})
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the modal content depending on the given article
|
||||
|
|
@ -333,23 +290,20 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
* @param item The article to render
|
||||
* @return {*}
|
||||
*/
|
||||
renderItem({item}: Object) {
|
||||
const onPress = this.onListItemPress.bind(this, item);
|
||||
return (
|
||||
<List.Item
|
||||
title={item.name}
|
||||
description={item.quantity + ' ' + i18n.t('proximoScreen.inStock')}
|
||||
descriptionStyle={{color: this.getStockColor(parseInt(item.quantity))}}
|
||||
onPress={onPress}
|
||||
left={() => <Avatar.Image style={{backgroundColor: 'transparent'}} size={64}
|
||||
source={{uri: item.image}}/>}
|
||||
right={() =>
|
||||
<Text style={{fontWeight: "bold"}}>
|
||||
{item.price}€
|
||||
</Text>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
renderItem = ({item}: Object) => {
|
||||
if (stringMatchQuery(item.name, this.state.currentSearchString)) {
|
||||
const onPress = this.onListItemPress.bind(this, item);
|
||||
const color = this.getStockColor(parseInt(item.quantity));
|
||||
return (
|
||||
<ProximoListItem
|
||||
item={item}
|
||||
onPress={onPress}
|
||||
color={color}
|
||||
/>
|
||||
);
|
||||
} else
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts a key for the given article
|
||||
|
|
@ -366,9 +320,9 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @param ref
|
||||
*/
|
||||
onModalRef(ref: Object) {
|
||||
onModalRef = (ref: Object) => {
|
||||
this.modalRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
|
@ -378,11 +332,12 @@ class ProximoListScreen extends React.Component<Props, State> {
|
|||
<CustomModal onRef={this.onModalRef}>
|
||||
{this.state.modalCurrentDisplayItem}
|
||||
</CustomModal>
|
||||
<PureFlatList
|
||||
data={this.state.currentlyDisplayedData}
|
||||
{/*$FlowFixMe*/}
|
||||
<FlatList
|
||||
data={this.listData}
|
||||
extraData={this.state.currentSearchString + this.state.currentSortMode}
|
||||
keyExtractor={this.keyExtractor}
|
||||
renderItem={this.renderItem}
|
||||
updateData={this.state.currentSortMode}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -142,7 +142,6 @@ class ProximoMainScreen extends React.Component<Props, State> {
|
|||
{
|
||||
title: '',
|
||||
data: this.generateData(fetchedData),
|
||||
extraData: this.state,
|
||||
keyExtractor: this.getKeyExtractor
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -272,14 +272,12 @@ class ProxiwashScreen extends React.Component<Props, State> {
|
|||
title: i18n.t('proxiwashScreen.dryers'),
|
||||
icon: 'tumble-dryer',
|
||||
data: data.dryers === undefined ? [] : data.dryers,
|
||||
extraData: this.state,
|
||||
keyExtractor: this.getKeyExtractor
|
||||
},
|
||||
{
|
||||
title: i18n.t('proxiwashScreen.washers'),
|
||||
icon: 'washing-machine',
|
||||
data: data.washers === undefined ? [] : data.washers,
|
||||
extraData: this.state,
|
||||
keyExtractor: this.getKeyExtractor
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ class SelfMenuScreen extends React.Component<Props> {
|
|||
{
|
||||
title: '',
|
||||
data: [],
|
||||
extraData: super.state,
|
||||
keyExtractor: this.getKeyExtractor
|
||||
}
|
||||
];
|
||||
|
|
@ -69,7 +68,6 @@ class SelfMenuScreen extends React.Component<Props> {
|
|||
{
|
||||
title: DateManager.getInstance().getTranslatedDate(fetchedData[i].date),
|
||||
data: fetchedData[i].meal[0].foodcategory,
|
||||
extraData: super.state,
|
||||
keyExtractor: this.getKeyExtractor,
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import ScoreManager from "./ScoreManager";
|
|||
import type {coordinates} from './Shapes/BaseShape';
|
||||
|
||||
|
||||
export type cell = {color: string, isEmpty: boolean};
|
||||
export type cell = {color: string, isEmpty: boolean, key: string};
|
||||
export type grid = Array<Array<cell>>;
|
||||
|
||||
/**
|
||||
|
|
@ -50,6 +50,7 @@ export default class GridManager {
|
|||
line.push({
|
||||
color: this.#colors.tetrisBackground,
|
||||
isEmpty: true,
|
||||
key: col.toString(),
|
||||
});
|
||||
}
|
||||
return line;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export default class Piece {
|
|||
grid[coord[i].y][coord[i].x] = {
|
||||
color: this.#colors.tetrisBackground,
|
||||
isEmpty: true,
|
||||
key: grid[coord[i].y][coord[i].x].key
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -73,6 +74,7 @@ export default class Piece {
|
|||
grid[coord[i].y][coord[i].x] = {
|
||||
color: this.#currentShape.getColor(),
|
||||
isEmpty: false,
|
||||
key: grid[coord[i].y][coord[i].x].key
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ import {View} from 'react-native';
|
|||
import {withTheme} from 'react-native-paper';
|
||||
|
||||
type Props = {
|
||||
color: string,
|
||||
isEmpty: boolean,
|
||||
id: string,
|
||||
item: Object
|
||||
}
|
||||
|
||||
class Cell extends React.PureComponent<Props> {
|
||||
|
|
@ -20,17 +18,19 @@ class Cell extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const item = this.props.item;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: this.props.isEmpty ? 'transparent' : this.props.color,
|
||||
borderColor: this.props.isEmpty ? 'transparent' : this.colors.tetrisBorder,
|
||||
backgroundColor: item.isEmpty ? 'transparent' : item.color,
|
||||
borderColor: item.isEmpty ? 'transparent' : this.colors.tetrisBorder,
|
||||
borderStyle: 'solid',
|
||||
borderRadius: 2,
|
||||
borderWidth: 1,
|
||||
aspectRatio: 1,
|
||||
}}
|
||||
key={item.key}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ type Props = {
|
|||
backgroundColor: string,
|
||||
height: number,
|
||||
width: number,
|
||||
containerMaxHeight: number|string,
|
||||
containerMaxWidth: number|string,
|
||||
containerMaxHeight: number | string,
|
||||
containerMaxWidth: number | string,
|
||||
}
|
||||
|
||||
class Grid extends React.Component<Props>{
|
||||
class Grid extends React.Component<Props> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
|
|
@ -25,22 +25,24 @@ class Grid extends React.Component<Props>{
|
|||
}
|
||||
|
||||
getRow(rowNumber: number) {
|
||||
let cells = [];
|
||||
for (let i = 0; i < this.props.width; i++) {
|
||||
let cell = this.props.grid[rowNumber][i];
|
||||
let key = rowNumber + ':' + i;
|
||||
cells.push(<Cell color={cell.color} isEmpty={cell.isEmpty} id={key}/>);
|
||||
}
|
||||
return(
|
||||
<View style={{
|
||||
flexDirection: 'row',
|
||||
backgroundColor: this.props.backgroundColor,
|
||||
}}>
|
||||
let cells = this.props.grid[rowNumber].map(this.getCellRender);
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
backgroundColor: this.props.backgroundColor,
|
||||
}}
|
||||
key={rowNumber.toString()}
|
||||
>
|
||||
{cells}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
getCellRender = (item: Object) => {
|
||||
return <Cell item={item} key={item.key}/>;
|
||||
};
|
||||
|
||||
getGrid() {
|
||||
let rows = [];
|
||||
for (let i = 0; i < this.props.height; i++) {
|
||||
|
|
@ -55,7 +57,7 @@ class Grid extends React.Component<Props>{
|
|||
flexDirection: 'column',
|
||||
maxWidth: this.props.containerMaxWidth,
|
||||
maxHeight: this.props.containerMaxHeight,
|
||||
aspectRatio: this.props.width/this.props.height,
|
||||
aspectRatio: this.props.width / this.props.height,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}}>
|
||||
|
|
|
|||
|
|
@ -22,19 +22,24 @@ class Preview extends React.PureComponent<Props> {
|
|||
let grids = [];
|
||||
for (let i = 0; i < this.props.next.length; i++) {
|
||||
grids.push(
|
||||
<Grid
|
||||
width={this.props.next[i][0].length}
|
||||
height={this.props.next[i].length}
|
||||
grid={this.props.next[i]}
|
||||
containerMaxHeight={50}
|
||||
containerMaxWidth={50}
|
||||
backgroundColor={'transparent'}
|
||||
/>
|
||||
this.getGridRender(this.props.next[i], i)
|
||||
);
|
||||
}
|
||||
return grids;
|
||||
}
|
||||
|
||||
getGridRender(item: Object, index: number) {
|
||||
return <Grid
|
||||
width={item[0].length}
|
||||
height={item.length}
|
||||
grid={item}
|
||||
containerMaxHeight={50}
|
||||
containerMaxWidth={50}
|
||||
backgroundColor={'transparent'}
|
||||
key={index.toString()}
|
||||
/>;
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.props.next.length > 0) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
"homeScreen": {
|
||||
"listUpdated": "List updated!",
|
||||
"listUpdateFail": "Error while updating list",
|
||||
"servicesButton": "More services",
|
||||
"newsFeed": "Campus News",
|
||||
"dashboard": {
|
||||
"seeMore": "Click to see more",
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
"homeScreen": {
|
||||
"listUpdated": "List mise à jour!",
|
||||
"listUpdateFail": "Erreur lors de la mise à jour de la liste",
|
||||
"servicesButton": "Plus de services",
|
||||
"newsFeed": "Nouvelles du campus",
|
||||
"dashboard": {
|
||||
"seeMore": "Cliquez pour plus d'infos",
|
||||
|
|
@ -159,7 +160,7 @@
|
|||
"loading": "Chargement...",
|
||||
"description": "C'est le service de laverie proposé par promologis pour les résidences INSA (On t'en voudra pas si tu loges pas sur le campus et que tu fais ta machine ici). Le local situé au pied du R2 avec ses 3 sèche-linges et 9 machines est ouvert 7J/7 24h/24 ! Ici tu peux vérifier leur disponibilité ! Tu peux amener ta lessive, la prendre sur place ou encore mieux l'acheter au Proximo (moins chère qu'à la laverie directement). Tu peux payer par CB ou espèces.",
|
||||
"informationTab": "Informations",
|
||||
"paymentTab" : "Paiement",
|
||||
"paymentTab": "Paiement",
|
||||
"tariffs": "Tarifs",
|
||||
"washersTariff": "3€ la machine + 0.80€ avec la lessive.",
|
||||
"dryersTariff": "0.35€ pour 5min de sèche linge.",
|
||||
|
|
@ -186,7 +187,6 @@
|
|||
"error": "Il y a eu une erreur et il est impossible de récupérer les informations de cette machine. Veuillez nous excuser pour le gène occasionnée.",
|
||||
"notificationErrorTitle": "Erreur",
|
||||
"notificationErrorDescription": "Impossible de créer les notifications. Merci de vérifier que vous avez activé les notifications puis redémarrez l'appli."
|
||||
|
||||
},
|
||||
"states": {
|
||||
"finished": "TERMINÉ",
|
||||
|
|
|
|||
Loading…
Reference in a new issue