Compare commits

..

No commits in common. "98168b560bed874f4251ac5e5aa40908829d3bb6" and "869a8e5ec0d8a8043445bfe6bb1acf2983472895" have entirely different histories.

15 changed files with 228 additions and 428 deletions

View file

@ -4,7 +4,6 @@ import * as React from 'react';
import {FlatList} from "react-native"; import {FlatList} from "react-native";
import packageJson from '../../../package'; import packageJson from '../../../package';
import {List} from 'react-native-paper'; import {List} from 'react-native-paper';
import {StackNavigationProp} from "@react-navigation/stack";
type listItem = { type listItem = {
name: string, name: string,
@ -17,7 +16,7 @@ type listItem = {
* @param object The raw json * @param object The raw json
* @return {Array<listItem>} * @return {Array<listItem>}
*/ */
function generateListFromObject(object: { [key: string]: string }): Array<listItem> { function generateListFromObject(object: { [string]: string }): Array<listItem> {
let list = []; let list = [];
let keys = Object.keys(object); let keys = Object.keys(object);
let values = Object.values(object); let values = Object.values(object);
@ -29,7 +28,8 @@ function generateListFromObject(object: { [key: string]: string }): Array<listIt
} }
type Props = { type Props = {
navigation: StackNavigationProp, navigation: Object,
route: Object
} }
const LIST_ITEM_HEIGHT = 64; const LIST_ITEM_HEIGHT = 64;
@ -39,23 +39,23 @@ const LIST_ITEM_HEIGHT = 64;
*/ */
export default class AboutDependenciesScreen extends React.Component<Props> { export default class AboutDependenciesScreen extends React.Component<Props> {
data: Array<listItem>; data: Array<Object>;
constructor() { constructor() {
super(); super();
this.data = generateListFromObject(packageJson.dependencies); this.data = generateListFromObject(packageJson.dependencies);
} }
keyExtractor = (item: listItem) => item.name; keyExtractor = (item: Object) => item.name;
renderItem = ({item}: { item: listItem }) => renderItem = ({item}: Object) =>
<List.Item <List.Item
title={item.name} title={item.name}
description={item.version.replace('^', '').replace('~', '')} description={item.version.replace('^', '').replace('~', '')}
style={{height: LIST_ITEM_HEIGHT}} style={{height: LIST_ITEM_HEIGHT}}
/>; />;
itemLayout = (data: any, index: number) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index}); itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
render() { render() {
return ( return (

View file

@ -5,14 +5,6 @@ import {FlatList, Linking, Platform, View} from 'react-native';
import i18n from "i18n-js"; import i18n from "i18n-js";
import {Avatar, Card, List, Title, withTheme} from 'react-native-paper'; import {Avatar, Card, List, Title, withTheme} from 'react-native-paper';
import packageJson from "../../../package.json"; import packageJson from "../../../package.json";
import {StackNavigationProp} from "@react-navigation/stack";
type ListItem = {
onPressCallback: () => void,
icon: string,
text: string,
showChevron: boolean
};
const links = { const links = {
appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148', appstore: 'https://apps.apple.com/us/app/campus-amicale-insat/id1477722148',
@ -37,7 +29,7 @@ const links = {
}; };
type Props = { type Props = {
navigation: StackNavigationProp, navigation: Object,
}; };
/** /**
@ -56,7 +48,7 @@ class AboutScreen extends React.Component<Props> {
/** /**
* Data to be displayed in the app card * Data to be displayed in the app card
*/ */
appData = [ appData: Array<Object> = [
{ {
onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore), onPressCallback: () => openWebLink(Platform.OS === "ios" ? links.appstore : links.playstore),
icon: Platform.OS === "ios" ? 'apple' : 'google-play', icon: Platform.OS === "ios" ? 'apple' : 'google-play',
@ -91,7 +83,7 @@ class AboutScreen extends React.Component<Props> {
/** /**
* Data to be displayed in the author card * Data to be displayed in the author card
*/ */
authorData = [ authorData: Array<Object> = [
{ {
onPressCallback: () => openWebLink(links.meme), onPressCallback: () => openWebLink(links.meme),
icon: 'account-circle', icon: 'account-circle',
@ -114,7 +106,7 @@ class AboutScreen extends React.Component<Props> {
/** /**
* Data to be displayed in the additional developer card * Data to be displayed in the additional developer card
*/ */
additionalDevData = [ additionalDevData: Array<Object> = [
{ {
onPressCallback: () => console.log('Meme this'), onPressCallback: () => console.log('Meme this'),
icon: 'account', icon: 'account',
@ -137,7 +129,7 @@ class AboutScreen extends React.Component<Props> {
/** /**
* Data to be displayed in the technologies card * Data to be displayed in the technologies card
*/ */
technoData = [ technoData: Array<Object> = [
{ {
onPressCallback: () => openWebLink(links.react), onPressCallback: () => openWebLink(links.react),
icon: 'react', icon: 'react',
@ -154,7 +146,7 @@ class AboutScreen extends React.Component<Props> {
/** /**
* Order of information cards * Order of information cards
*/ */
dataOrder = [ dataOrder: Array<Object> = [
{ {
id: 'app', id: 'app',
}, },
@ -166,9 +158,16 @@ class AboutScreen extends React.Component<Props> {
}, },
]; ];
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
/** /**
* Gets the app icon * Gets the app icon
*
* @param props * @param props
* @return {*} * @return {*}
*/ */
@ -188,7 +187,7 @@ class AboutScreen extends React.Component<Props> {
* @param item The item to extract the key from * @param item The item to extract the key from
* @return {string} The extracted key * @return {string} The extracted key
*/ */
keyExtractor(item: ListItem): string { keyExtractor(item: Object): string {
return item.icon; return item.icon;
} }
@ -272,7 +271,7 @@ class AboutScreen extends React.Component<Props> {
* @param props * @param props
* @return {*} * @return {*}
*/ */
getChevronIcon(props) { getChevronIcon(props: Object) {
return ( return (
<List.Icon {...props} icon={'chevron-right'}/> <List.Icon {...props} icon={'chevron-right'}/>
); );
@ -285,18 +284,18 @@ class AboutScreen extends React.Component<Props> {
* @param props * @param props
* @return {*} * @return {*}
*/ */
getItemIcon(item: ListItem, props) { getItemIcon(item: Object, props: Object) {
return ( return (
<List.Icon {...props} icon={item.icon}/> <List.Icon {...props} icon={item.icon}/>
); );
} }
/** /**
* Gets a clickable card item to be rendered inside a card. * Get a clickable card item to be rendered inside a card.
* *
* @returns {*} * @returns {*}
*/ */
getCardItem = ({item}: { item: ListItem }) => { getCardItem = ({item}: Object) => {
const getItemIcon = this.getItemIcon.bind(this, item); const getItemIcon = this.getItemIcon.bind(this, item);
if (item.showChevron) { if (item.showChevron) {
return ( return (
@ -324,7 +323,7 @@ class AboutScreen extends React.Component<Props> {
* @param item The item to show * @param item The item to show
* @return {*} * @return {*}
*/ */
getMainCard = ({item}: { item: { id: string } }) => { getMainCard = ({item}: Object) => {
switch (item.id) { switch (item.id) {
case 'app': case 'app':
return this.getAppCard(); return this.getAppCard();

View file

@ -5,24 +5,14 @@ import {FlatList, View} from "react-native";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import CustomModal from "../../components/Overrides/CustomModal"; import CustomModal from "../../components/Overrides/CustomModal";
import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper'; import {Button, List, Subheading, TextInput, Title, withTheme} from 'react-native-paper';
import {StackNavigationProp} from "@react-navigation/stack";
import {Modalize} from "react-native-modalize";
import type {CustomTheme} from "../../managers/ThemeManager";
type PreferenceItem = {
key: string,
default: string,
current: string,
}
type Props = { type Props = {
navigation: StackNavigationProp, navigation: Object,
theme: CustomTheme
}; };
type State = { type State = {
modalCurrentDisplayItem: PreferenceItem, modalCurrentDisplayItem: Object,
currentPreferences: Array<PreferenceItem>, currentPreferences: Array<Object>,
} }
/** /**
@ -31,20 +21,20 @@ type State = {
*/ */
class DebugScreen extends React.Component<Props, State> { class DebugScreen extends React.Component<Props, State> {
modalRef: Modalize; modalRef: Object;
modalInputValue: string; modalInputValue = '';
onModalRef: Function;
colors: Object;
/**
* Copies user preferences to state for easier manipulation
*
* @param props
*/
constructor(props) { constructor(props) {
super(props); super(props);
this.modalInputValue = ""; this.onModalRef = this.onModalRef.bind(this);
this.colors = props.theme.colors;
let copy = {...AsyncStorageManager.getInstance().preferences}; let copy = {...AsyncStorageManager.getInstance().preferences};
let currentPreferences : Array<PreferenceItem> = []; let currentPreferences = [];
Object.values(copy).map((object: any) => { Object.values(copy).map((object) => {
currentPreferences.push(object); currentPreferences.push(object);
}); });
this.state = { this.state = {
@ -54,11 +44,10 @@ class DebugScreen extends React.Component<Props, State> {
} }
/** /**
* Shows the edit modal * Show the edit modal
*
* @param item * @param item
*/ */
showEditModal(item: PreferenceItem) { showEditModal(item: Object) {
this.setState({ this.setState({
modalCurrentDisplayItem: item modalCurrentDisplayItem: item
}); });
@ -92,14 +81,14 @@ class DebugScreen extends React.Component<Props, State> {
<Button <Button
mode="contained" mode="contained"
dark={true} dark={true}
color={this.props.theme.colors.success} color={this.colors.success}
onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.modalInputValue)}> onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.modalInputValue)}>
Save new value Save new value
</Button> </Button>
<Button <Button
mode="contained" mode="contained"
dark={true} dark={true}
color={this.props.theme.colors.danger} color={this.colors.danger}
onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.state.modalCurrentDisplayItem.default)}> onPress={() => this.saveNewPrefs(this.state.modalCurrentDisplayItem.key, this.state.modalCurrentDisplayItem.default)}>
Reset to default Reset to default
</Button> </Button>
@ -109,12 +98,6 @@ class DebugScreen extends React.Component<Props, State> {
); );
} }
/**
* Finds the index of the given key in the preferences array
*
* @param key THe key to find the index of
* @returns {number}
*/
findIndexOfKey(key: string) { findIndexOfKey(key: string) {
let index = -1; let index = -1;
for (let i = 0; i < this.state.currentPreferences.length; i++) { for (let i = 0; i < this.state.currentPreferences.length; i++) {
@ -147,11 +130,11 @@ class DebugScreen extends React.Component<Props, State> {
* *
* @param ref * @param ref
*/ */
onModalRef = (ref: Modalize) => { onModalRef(ref: Object) {
this.modalRef = ref; this.modalRef = ref;
} }
renderItem = ({item}: {item: PreferenceItem}) => { renderItem = ({item}: Object) => {
return ( return (
<List.Item <List.Item
title={item.key} title={item.key}

View file

@ -12,18 +12,14 @@ type Props = {
collapsibleStack: Collapsible collapsibleStack: Collapsible
}; };
type DatasetItem = { type State = {};
name: string,
email: string,
icon: string,
}
/** /**
* Class defining a planning event information page. * Class defining a planning event information page.
*/ */
class AmicaleContactScreen extends React.Component<Props> { class AmicaleContactScreen extends React.Component<Props, State> {
// Dataset containing information about contacts
CONTACT_DATASET = [ CONTACT_DATASET = [
{ {
name: i18n.t("amicaleAbout.roles.interSchools"), name: i18n.t("amicaleAbout.roles.interSchools"),
@ -72,11 +68,18 @@ class AmicaleContactScreen extends React.Component<Props> {
}, },
]; ];
keyExtractor = (item: DatasetItem) => item.email; colors: Object;
getChevronIcon = (props) => <List.Icon {...props} icon={'chevron-right'}/>; constructor(props) {
super(props);
this.colors = props.theme.colors;
}
renderItem = ({item}: { item: DatasetItem }) => { keyExtractor = (item: Object) => item.email;
getChevronIcon = (props: Object) => <List.Icon {...props} icon={'chevron-right'}/>;
renderItem = ({item}: Object) => {
const onPress = () => Linking.openURL('mailto:' + item.email); const onPress = () => Linking.openURL('mailto:' + item.email);
return <List.Item return <List.Item
title={item.name} title={item.name}

View file

@ -0,0 +1,85 @@
// @flow
import * as React from 'react';
import {ScrollView, StyleSheet} from "react-native";
import {Button, withTheme} from 'react-native-paper';
type Props = {
navigation: Object,
route: Object,
}
type State = {}
class AmicaleHomeScreen extends React.Component<Props, State> {
state = {};
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
render() {
const nav = this.props.navigation;
return (
<ScrollView>
<Button
icon={"login"}
onPress={() => nav.navigate("login")}
>
LOGIN
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("amicale-contact")}
>
INFO
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("club-list")}
>
CLUBS
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("profile")}
>
PROFILE
</Button>
<Button
icon={"information"}
onPress={() => nav.navigate("vote")}
>
VOTE
</Button>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
},
card: {
margin: 10,
},
header: {
fontSize: 36,
marginBottom: 48
},
textInput: {},
btnContainer: {
marginTop: 5,
marginBottom: 10,
}
});
export default withTheme(AmicaleHomeScreen);

View file

@ -6,11 +6,25 @@ import {Card, List, Text, withTheme} from 'react-native-paper';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import Autolink from "react-native-autolink"; import Autolink from "react-native-autolink";
type Props = {}; type Props = {
};
type State = {
};
const CONTACT_LINK = 'clubs@amicale-insat.fr'; const CONTACT_LINK = 'clubs@amicale-insat.fr';
class ClubAboutScreen extends React.Component<Props> { /**
* Class defining a planning event information page.
*/
class ClubAboutScreen extends React.Component<Props, State> {
colors: Object;
constructor(props) {
super(props);
this.colors = props.theme.colors;
}
render() { render() {
return ( return (

View file

@ -65,12 +65,6 @@ class ClubDisplayScreen extends React.Component<Props, State> {
} }
} }
/**
* Gets the name of the category with the given ID
*
* @param id The category's ID
* @returns {string|*}
*/
getCategoryName(id: number) { getCategoryName(id: number) {
if (this.categories !== null) { if (this.categories !== null) {
for (let i = 0; i < this.categories.length; i++) { for (let i = 0; i < this.categories.length; i++) {
@ -81,12 +75,6 @@ class ClubDisplayScreen extends React.Component<Props, State> {
return ""; return "";
} }
/**
* Gets the view for rendering categories
*
* @param categories The categories to display (max 2)
* @returns {null|*}
*/
getCategoriesRender(categories: [number, number]) { getCategoriesRender(categories: [number, number]) {
if (this.categories === null) if (this.categories === null)
return null; return null;
@ -107,19 +95,12 @@ class ClubDisplayScreen extends React.Component<Props, State> {
return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>; return <View style={{flexDirection: 'row', marginTop: 5}}>{final}</View>;
} }
/** getManagersRender(resp: Array<string>, email: string | null) {
* Gets the view for rendering club managers if any let final = [];
* for (let i = 0; i < resp.length; i++) {
* @param managers The list of manager names final.push(<Paragraph key={i.toString()}>{resp[i]}</Paragraph>)
* @param email The club contact email
* @returns {*}
*/
getManagersRender(managers: Array<string>, email: string | null) {
let managersListView = [];
for (let i = 0; i < managers.length; i++) {
managersListView.push(<Paragraph key={i.toString()}>{managers[i]}</Paragraph>)
} }
const hasManagers = managers.length > 0; const hasManagers = resp.length > 0;
return ( return (
<Card style={{marginTop: 10, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}> <Card style={{marginTop: 10, marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
<Card.Title <Card.Title
@ -132,20 +113,13 @@ class ClubDisplayScreen extends React.Component<Props, State> {
icon="account-tie"/>} icon="account-tie"/>}
/> />
<Card.Content> <Card.Content>
{managersListView} {final}
{this.getEmailButton(email, hasManagers)} {this.getEmailButton(email, hasManagers)}
</Card.Content> </Card.Content>
</Card> </Card>
); );
} }
/**
* Gets the email button to contact the club, or the amicale if the club does not have any managers
*
* @param email The club contact email
* @param hasManagers True if the club has managers
* @returns {*}
*/
getEmailButton(email: string | null, hasManagers: boolean) { getEmailButton(email: string | null, hasManagers: boolean) {
const destinationEmail = email != null && hasManagers const destinationEmail = email != null && hasManagers
? email ? email
@ -167,21 +141,13 @@ class ClubDisplayScreen extends React.Component<Props, State> {
); );
} }
/** updateHeaderTitle(data: Object) {
* Updates the header title to match the given club
*
* @param data The club data
*/
updateHeaderTitle(data: club) {
this.props.navigation.setOptions({title: data.name}) this.props.navigation.setOptions({title: data.name})
} }
getScreen = (response: Array<{ [key: string]: any } | null>) => { getScreen = (response: Array<Object>) => {
let data: club | null = null; let data: club = response[0];
if (response[0] != null) { this.updateHeaderTitle(data);
data = response[0];
this.updateHeaderTitle(data);
}
if (data != null) { if (data != null) {
return ( return (
<ScrollView style={{paddingLeft: 5, paddingRight: 5}}> <ScrollView style={{paddingLeft: 5, paddingRight: 5}}>
@ -218,6 +184,7 @@ class ClubDisplayScreen extends React.Component<Props, State> {
); );
} else } else
return null; return null;
}; };
render() { render() {

View file

@ -131,15 +131,6 @@ class ClubListScreen extends React.Component<Props, State> {
onChipSelect = (id: number) => this.updateFilteredData(null, id); onChipSelect = (id: number) => this.updateFilteredData(null, id);
/**
* Updates the search string and category filter, saving them to the State.
*
* If the given category is already in the filter, it removes it.
* Otherwise it adds it to the filter.
*
* @param filterStr The new filter string to use
* @param categoryId The category to add/remove from the filter
*/
updateFilteredData(filterStr: string | null, categoryId: number | null) { updateFilteredData(filterStr: string | null, categoryId: number | null) {
let newCategoriesState = [...this.state.currentlySelectedCategories]; let newCategoriesState = [...this.state.currentlySelectedCategories];
let newStrState = this.state.currentSearchString; let newStrState = this.state.currentSearchString;
@ -159,11 +150,6 @@ class ClubListScreen extends React.Component<Props, State> {
}) })
} }
/**
* Gets the list header, with controls to change the categories filter
*
* @returns {*}
*/
getListHeader() { getListHeader() {
return <ClubListHeader return <ClubListHeader
categories={this.categories} categories={this.categories}
@ -172,12 +158,6 @@ class ClubListScreen extends React.Component<Props, State> {
/>; />;
} }
/**
* Gets the category object of the given ID
*
* @param id The ID of the category to find
* @returns {*}
*/
getCategoryOfId = (id: number) => { getCategoryOfId = (id: number) => {
for (let i = 0; i < this.categories.length; i++) { for (let i = 0; i < this.categories.length; i++) {
if (id === this.categories[i].id) if (id === this.categories[i].id)
@ -185,12 +165,6 @@ class ClubListScreen extends React.Component<Props, State> {
} }
}; };
/**
* Checks if the given item should be rendered according to current name and category filters
*
* @param item The club to check
* @returns {boolean}
*/
shouldRenderItem(item: club) { shouldRenderItem(item: club) {
let shouldRender = this.state.currentlySelectedCategories.length === 0 let shouldRender = this.state.currentlySelectedCategories.length === 0
|| isItemInCategoryFilter(this.state.currentlySelectedCategories, item.category); || isItemInCategoryFilter(this.state.currentlySelectedCategories, item.category);

View file

@ -11,11 +11,10 @@ import {Collapsible} from "react-navigation-collapsible";
import CustomTabBar from "../../components/Tabbar/CustomTabBar"; import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import type {CustomTheme} from "../../managers/ThemeManager"; import type {CustomTheme} from "../../managers/ThemeManager";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {StackNavigationProp} from "@react-navigation/stack";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: Object,
route: { params: { nextScreen: string } }, route: Object,
collapsibleStack: Collapsible, collapsibleStack: Collapsible,
theme: CustomTheme theme: CustomTheme
} }
@ -48,15 +47,14 @@ class LoginScreen extends React.Component<Props, State> {
dialogError: 0, dialogError: 0,
}; };
onEmailChange: (value: string) => null; onEmailChange: Function;
onPasswordChange: (value: string) => null; onPasswordChange: Function;
passwordInputRef: { current: null | TextInput }; passwordInputRef: Object;
nextScreen: string | null; nextScreen: string | null;
constructor(props) { constructor(props) {
super(props); super(props);
this.passwordInputRef = React.createRef();
this.onEmailChange = this.onInputChange.bind(this, true); this.onEmailChange = this.onInputChange.bind(this, true);
this.onPasswordChange = this.onInputChange.bind(this, false); this.onPasswordChange = this.onInputChange.bind(this, false);
this.props.navigation.addListener('focus', this.onScreenFocus); this.props.navigation.addListener('focus', this.onScreenFocus);
@ -66,10 +64,7 @@ class LoginScreen extends React.Component<Props, State> {
this.handleNavigationParams(); this.handleNavigationParams();
}; };
/** handleNavigationParams () {
* Saves the screen to navigate to after a successful login if one was provided in navigation parameters
*/
handleNavigationParams() {
if (this.props.route.params != null) { if (this.props.route.params != null) {
if (this.props.route.params.nextScreen != null) if (this.props.route.params.nextScreen != null)
this.nextScreen = this.props.route.params.nextScreen; this.nextScreen = this.props.route.params.nextScreen;
@ -78,11 +73,6 @@ class LoginScreen extends React.Component<Props, State> {
} }
} }
/**
* Shows an error dialog with the corresponding login error
*
* @param error The error given by the login request
*/
showErrorDialog = (error: number) => showErrorDialog = (error: number) =>
this.setState({ this.setState({
dialogVisible: true, dialogVisible: true,
@ -91,10 +81,6 @@ class LoginScreen extends React.Component<Props, State> {
hideErrorDialog = () => this.setState({dialogVisible: false}); hideErrorDialog = () => this.setState({dialogVisible: false});
/**
* Navigates to the screen specified in navigation parameters or simply go back tha stack.
* Saves in user preferences to not show the login banner again.
*/
handleSuccess = () => { handleSuccess = () => {
// Do not show the login banner again // Do not show the login banner again
AsyncStorageManager.getInstance().savePref( AsyncStorageManager.getInstance().savePref(
@ -107,75 +93,32 @@ class LoginScreen extends React.Component<Props, State> {
this.props.navigation.replace(this.nextScreen); this.props.navigation.replace(this.nextScreen);
}; };
/**
* Navigates to the Amicale website screen with the reset password link as navigation parameters
*/
onResetPasswordClick = () => this.props.navigation.navigate('amicale-website', {path: RESET_PASSWORD_PATH}); onResetPasswordClick = () => this.props.navigation.navigate('amicale-website', {path: RESET_PASSWORD_PATH});
/**
* The user has unfocused the input, his email is ready to be validated
*/
validateEmail = () => this.setState({isEmailValidated: true}); validateEmail = () => this.setState({isEmailValidated: true});
/**
* Checks if the entered email is valid (matches the regex)
*
* @returns {boolean}
*/
isEmailValid() { isEmailValid() {
return emailRegex.test(this.state.email); return emailRegex.test(this.state.email);
} }
/**
* Checks if we should tell the user his email is invalid.
* We should only show this if his email is invalid and has been checked when un-focusing the input
*
* @returns {boolean|boolean}
*/
shouldShowEmailError() { shouldShowEmailError() {
return this.state.isEmailValidated && !this.isEmailValid(); return this.state.isEmailValidated && !this.isEmailValid();
} }
/**
* The user has unfocused the input, his password is ready to be validated
*/
validatePassword = () => this.setState({isPasswordValidated: true}); validatePassword = () => this.setState({isPasswordValidated: true});
/**
* Checks if the user has entered a password
*
* @returns {boolean}
*/
isPasswordValid() { isPasswordValid() {
return this.state.password !== ''; return this.state.password !== '';
} }
/**
* Checks if we should tell the user his password is invalid.
* We should only show this if his password is invalid and has been checked when un-focusing the input
*
* @returns {boolean|boolean}
*/
shouldShowPasswordError() { shouldShowPasswordError() {
return this.state.isPasswordValidated && !this.isPasswordValid(); return this.state.isPasswordValidated && !this.isPasswordValid();
} }
/**
* If the email and password are valid, and we are not loading a request, then the login button can be enabled
*
* @returns {boolean}
*/
shouldEnableLogin() { shouldEnableLogin() {
return this.isEmailValid() && this.isPasswordValid() && !this.state.loading; return this.isEmailValid() && this.isPasswordValid() && !this.state.loading;
} }
/**
* Called when the user input changes in the email or password field.
* This saves the new value in the State and disabled input validation (to prevent errors to show while typing)
*
* @param isEmail True if the field is the email field
* @param value The new field value
*/
onInputChange(isEmail: boolean, value: string) { onInputChange(isEmail: boolean, value: string) {
if (isEmail) { if (isEmail) {
this.setState({ this.setState({
@ -190,23 +133,8 @@ class LoginScreen extends React.Component<Props, State> {
} }
} }
/** onEmailSubmit = () => this.passwordInputRef.focus();
* Focuses the password field when the email field is done
*
* @returns {*}
*/
onEmailSubmit = () => {
if (this.passwordInputRef.current != null)
this.passwordInputRef.current.focus();
}
/**
* Called when the user clicks on login or finishes to type his password.
*
* Checks if we should allow the user to login,
* then makes the login request and enters a loading state until the request finishes
*
*/
onSubmit = () => { onSubmit = () => {
if (this.shouldEnableLogin()) { if (this.shouldEnableLogin()) {
this.setState({loading: true}); this.setState({loading: true});
@ -219,11 +147,6 @@ class LoginScreen extends React.Component<Props, State> {
} }
}; };
/**
* Gets the form input
*
* @returns {*}
*/
getFormInput() { getFormInput() {
return ( return (
<View> <View>
@ -250,7 +173,9 @@ class LoginScreen extends React.Component<Props, State> {
{i18n.t("loginScreen.emailError")} {i18n.t("loginScreen.emailError")}
</HelperText> </HelperText>
<TextInput <TextInput
ref={this.passwordInputRef} ref={(ref) => {
this.passwordInputRef = ref;
}}
label={i18n.t("loginScreen.password")} label={i18n.t("loginScreen.password")}
mode='outlined' mode='outlined'
value={this.state.password} value={this.state.password}
@ -276,10 +201,6 @@ class LoginScreen extends React.Component<Props, State> {
); );
} }
/**
* Gets the card containing the input form
* @returns {*}
*/
getMainCard() { getMainCard() {
return ( return (
<Card style={styles.card}> <Card style={styles.card}>
@ -318,11 +239,6 @@ class LoginScreen extends React.Component<Props, State> {
); );
} }
/**
* Gets the card containing the information about the Amicale account
*
* @returns {*}
*/
getSecondaryCard() { getSecondaryCard() {
return ( return (
<Card style={styles.card}> <Card style={styles.card}>

View file

@ -12,12 +12,10 @@ import {Collapsible} from "react-navigation-collapsible";
import {withCollapsible} from "../../utils/withCollapsible"; import {withCollapsible} from "../../utils/withCollapsible";
import type {cardList} from "../../components/Lists/CardList/CardList"; import type {cardList} from "../../components/Lists/CardList/CardList";
import CardList from "../../components/Lists/CardList/CardList"; import CardList from "../../components/Lists/CardList/CardList";
import {StackNavigationProp} from "@react-navigation/stack";
import type {CustomTheme} from "../../managers/ThemeManager";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: Object,
theme: CustomTheme, theme: Object,
collapsibleStack: Collapsible, collapsibleStack: Collapsible,
} }
@ -25,23 +23,6 @@ type State = {
dialogVisible: boolean, dialogVisible: boolean,
} }
type ProfileData = {
first_name: string,
last_name: string,
email: string,
birthday: string,
phone: string,
branch: string,
link: string,
validity: boolean,
clubs: Array<Club>,
}
type Club = {
id: number,
name: string,
is_manager: boolean,
}
const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png"; const CLUBS_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Clubs.png";
const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png"; const VOTE_IMAGE = "https://etud.insa-toulouse.fr/~amicale_app/images/Vote.png";
@ -53,9 +34,9 @@ class ProfileScreen extends React.Component<Props, State> {
dialogVisible: false, dialogVisible: false,
}; };
data: ProfileData; data: Object;
flatListData: Array<{ id: string }>; flatListData: Array<Object>;
amicaleDataset: cardList; amicaleDataset: cardList;
constructor() { constructor() {
@ -98,25 +79,12 @@ class ProfileScreen extends React.Component<Props, State> {
hideDisconnectDialog = () => this.setState({dialogVisible: false}); hideDisconnectDialog = () => this.setState({dialogVisible: false});
/**
* Gets the logout header button
*
* @returns {*}
*/
getHeaderButton = () => <MaterialHeaderButtons> getHeaderButton = () => <MaterialHeaderButtons>
<Item title="logout" iconName="logout" onPress={this.showDisconnectDialog}/> <Item title="logout" iconName="logout" onPress={this.showDisconnectDialog}/>
</MaterialHeaderButtons>; </MaterialHeaderButtons>;
/** getScreen = (data: Object) => {
* Gets the main screen component with the fetched data this.data = data[0];
*
* @param data The data fetched from the server
* @returns {*}
*/
getScreen = (data: Array<{ [key: string]: any } | null>) => {
if (data[0] != null) {
this.data = data[0];
}
const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack; const {containerPaddingTop, scrollIndicatorInsetTop, onScroll} = this.props.collapsibleStack;
return ( return (
<View style={{flex: 1}}> <View style={{flex: 1}}>
@ -141,7 +109,7 @@ class ProfileScreen extends React.Component<Props, State> {
) )
}; };
getRenderItem = ({item}: { item: { id: string } }) => { getRenderItem = ({item}: Object) => {
switch (item.id) { switch (item.id) {
case '0': case '0':
return this.getWelcomeCard(); return this.getWelcomeCard();
@ -154,11 +122,6 @@ class ProfileScreen extends React.Component<Props, State> {
} }
}; };
/**
* Gets the list of services available with the Amicale account
*
* @returns {*}
*/
getServicesList() { getServicesList() {
return ( return (
<CardList <CardList
@ -168,17 +131,12 @@ class ProfileScreen extends React.Component<Props, State> {
); );
} }
/**
* Gets a card welcoming the user to his account
*
* @returns {*}
*/
getWelcomeCard() { getWelcomeCard() {
return ( return (
<Card style={styles.card}> <Card style={styles.card}>
<Card.Title <Card.Title
title={i18n.t("profileScreen.welcomeTitle", {name: this.data.first_name})} title={i18n.t("profileScreen.welcomeTitle", {name: this.data.first_name})}
left={() => <Avatar.Image left={(props) => <Avatar.Image
size={64} size={64}
source={ICON_AMICALE} source={ICON_AMICALE}
style={{backgroundColor: 'transparent',}} style={{backgroundColor: 'transparent',}}
@ -382,7 +340,7 @@ class ProfileScreen extends React.Component<Props, State> {
* @param item The club to render * @param item The club to render
* @return {*} * @return {*}
*/ */
clubListItem = ({item}: { item: Club }) => { clubListItem = ({item}: Object) => {
const onPress = () => this.openClubDetailsScreen(item.id); const onPress = () => this.openClubDetailsScreen(item.id);
let description = i18n.t("profileScreen.isMember"); let description = i18n.t("profileScreen.isMember");
let icon = (props) => <List.Icon {...props} icon="chevron-right"/>; let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
@ -398,9 +356,9 @@ class ProfileScreen extends React.Component<Props, State> {
/>; />;
}; };
clubKeyExtractor = (item: Club) => item.name; clubKeyExtractor = (item: Object) => item.name;
sortClubList = (a: Club, b: Club) => a.is_manager ? -1 : 1; sortClubList = (a: Object, b: Object) => a.is_manager ? -1 : 1;
/** /**
* Renders the list of clubs the user is part of * Renders the list of clubs the user is part of
@ -408,7 +366,7 @@ class ProfileScreen extends React.Component<Props, State> {
* @param list The club list * @param list The club list
* @return {*} * @return {*}
*/ */
getClubList(list: Array<Club>) { getClubList(list: Array<Object>) {
list.sort(this.sortClubList); list.sort(this.sortClubList);
return ( return (
//$FlowFixMe //$FlowFixMe

View file

@ -9,7 +9,6 @@ import VoteTease from "../../components/Amicale/Vote/VoteTease";
import VoteSelect from "../../components/Amicale/Vote/VoteSelect"; import VoteSelect from "../../components/Amicale/Vote/VoteSelect";
import VoteResults from "../../components/Amicale/Vote/VoteResults"; import VoteResults from "../../components/Amicale/Vote/VoteResults";
import VoteWait from "../../components/Amicale/Vote/VoteWait"; import VoteWait from "../../components/Amicale/Vote/VoteWait";
import {StackNavigationProp} from "@react-navigation/stack";
export type team = { export type team = {
id: number, id: number,
@ -87,16 +86,13 @@ type objectVoteDates = {
const MIN_REFRESH_TIME = 5 * 1000; const MIN_REFRESH_TIME = 5 * 1000;
type Props = { type Props = {
navigation: StackNavigationProp navigation: Object
} }
type State = { type State = {
hasVoted: boolean, hasVoted: boolean,
} }
/**
* Screen displaying vote information and controls
*/
export default class VoteScreen extends React.Component<Props, State> { export default class VoteScreen extends React.Component<Props, State> {
state = { state = {
@ -111,7 +107,7 @@ export default class VoteScreen extends React.Component<Props, State> {
today: Date; today: Date;
mainFlatListData: Array<{ key: string }>; mainFlatListData: Array<{ key: string }>;
lastRefresh: Date | null; lastRefresh: Date;
authRef: { current: null | AuthenticatedScreen }; authRef: { current: null | AuthenticatedScreen };
@ -120,30 +116,22 @@ export default class VoteScreen extends React.Component<Props, State> {
this.hasVoted = false; this.hasVoted = false;
this.today = new Date(); this.today = new Date();
this.authRef = React.createRef(); this.authRef = React.createRef();
this.lastRefresh = null;
this.mainFlatListData = [ this.mainFlatListData = [
{key: 'main'}, {key: 'main'},
{key: 'info'}, {key: 'info'},
] ]
} }
/**
* Reloads vote data if last refresh delta is smaller than the minimum refresh time
*/
reloadData = () => { reloadData = () => {
let canRefresh; let canRefresh;
const lastRefresh = this.lastRefresh; if (this.lastRefresh !== undefined)
if (lastRefresh != null) canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME;
canRefresh = (new Date().getTime() - lastRefresh.getTime()) > MIN_REFRESH_TIME;
else else
canRefresh = true; canRefresh = true;
if (canRefresh && this.authRef.current != null) if (canRefresh && this.authRef.current != null)
this.authRef.current.reload() this.authRef.current.reload()
}; };
/**
* Generates the objects containing string and Date representations of key vote dates
*/
generateDateObject() { generateDateObject() {
const strings = this.datesString; const strings = this.datesString;
if (strings != null) { if (strings != null) {
@ -164,16 +152,6 @@ export default class VoteScreen extends React.Component<Props, State> {
this.dates = null; this.dates = null;
} }
/**
* Gets the string representation of the given date.
*
* If the given date is the same day as today, only return the tile.
* Otherwise, return the full date.
*
* @param date The Date object representation of the wanted date
* @param dateString The string representation of the wanted date
* @returns {string}
*/
getDateString(date: Date, dateString: string): string { getDateString(date: Date, dateString: string): string {
if (this.today.getDate() === date.getDate()) { if (this.today.getDate() === date.getDate()) {
const str = getTimeOnlyString(dateString); const str = getTimeOnlyString(dateString);
@ -198,7 +176,7 @@ export default class VoteScreen extends React.Component<Props, State> {
return this.dates != null && this.today > this.dates.date_result_begin; return this.dates != null && this.today > this.dates.date_result_begin;
} }
mainRenderItem = ({item}: { item: { key: string } }) => { mainRenderItem = ({item}: Object) => {
if (item.key === 'info') if (item.key === 'info')
return <VoteTitle/>; return <VoteTitle/>;
else if (item.key === 'main' && this.dates != null) else if (item.key === 'main' && this.dates != null)
@ -212,8 +190,8 @@ export default class VoteScreen extends React.Component<Props, State> {
// data[1] = FAKE_DATE; // data[1] = FAKE_DATE;
this.lastRefresh = new Date(); this.lastRefresh = new Date();
const teams: teamResponse | null = data[0]; const teams : teamResponse | null = data[0];
const dateStrings: stringVoteDates | null = data[1]; const dateStrings : stringVoteDates | null = data[1];
if (dateStrings != null && dateStrings.date_begin == null) if (dateStrings != null && dateStrings.date_begin == null)
this.datesString = null; this.datesString = null;
@ -304,13 +282,6 @@ export default class VoteScreen extends React.Component<Props, State> {
isVoteRunning={this.isVoteRunning()}/>; isVoteRunning={this.isVoteRunning()}/>;
} }
/**
* Renders the authenticated screen.
*
* Teams and dates are not mandatory to allow showing the information box even if api requests fail
*
* @returns {*}
*/
render() { render() {
return ( return (
<AuthenticatedScreen <AuthenticatedScreen

View file

@ -7,29 +7,29 @@ import ImageModal from 'react-native-image-modal';
import Autolink from "react-native-autolink"; import Autolink from "react-native-autolink";
import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton"; import MaterialHeaderButtons, {Item} from "../../components/Overrides/CustomHeaderButton";
import CustomTabBar from "../../components/Tabbar/CustomTabBar"; import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import {StackNavigationProp} from "@react-navigation/stack";
import type {feedItem} from "./HomeScreen";
type Props = { type Props = {
navigation: StackNavigationProp, navigation: Object,
route: { params: { data: feedItem, date: string } } route: Object
}; };
const ICON_AMICALE = require('../../../assets/amicale.png'); const ICON_AMICALE = require('../../../assets/amicale.png');
const NAME_AMICALE = 'Amicale INSA Toulouse'; const NAME_AMICALE = 'Amicale INSA Toulouse';
/** /**
* Class defining a feed item page. * Class defining a planning event information page.
*/ */
class FeedItemScreen extends React.Component<Props> { class FeedItemScreen extends React.Component<Props> {
displayData: feedItem; displayData: Object;
date: string; date: string;
colors: Object;
constructor(props) { constructor(props) {
super(props); super(props);
this.displayData = props.route.params.data; this.colors = props.theme.colors;
this.date = props.route.params.date; this.displayData = this.props.route.params.data;
this.date = this.props.route.params.date;
} }
componentDidMount() { componentDidMount() {
@ -38,29 +38,16 @@ class FeedItemScreen extends React.Component<Props> {
}); });
} }
/**
* Opens the feed item out link in browser or compatible app
*/
onOutLinkPress = () => { onOutLinkPress = () => {
Linking.openURL(this.displayData.permalink_url); Linking.openURL(this.displayData.permalink_url);
}; };
/**
* Gets the out link header button
*
* @returns {*}
*/
getHeaderButton = () => { getHeaderButton = () => {
return <MaterialHeaderButtons> return <MaterialHeaderButtons>
<Item title="main" iconName={'facebook'} color={"#2e88fe"} onPress={this.onOutLinkPress}/> <Item title="main" iconName={'facebook'} color={"#2e88fe"} onPress={this.onOutLinkPress}/>
</MaterialHeaderButtons>; </MaterialHeaderButtons>;
}; };
/**
* Gets the Amicale INSA avatar
*
* @returns {*}
*/
getAvatar() { getAvatar() {
return ( return (
<Avatar.Image size={48} source={ICON_AMICALE} <Avatar.Image size={48} source={ICON_AMICALE}
@ -68,8 +55,8 @@ class FeedItemScreen extends React.Component<Props> {
); );
} }
render() { getContent() {
const hasImage = this.displayData.full_picture !== '' && this.displayData.full_picture != null; const hasImage = this.displayData.full_picture !== '' && this.displayData.full_picture !== undefined;
return ( return (
<ScrollView style={{margin: 5,}}> <ScrollView style={{margin: 5,}}>
<Card.Title <Card.Title
@ -102,6 +89,10 @@ class FeedItemScreen extends React.Component<Props> {
</ScrollView> </ScrollView>
); );
} }
render() {
return this.getContent();
}
} }
export default withTheme(FeedItemScreen); export default withTheme(FeedItemScreen);

View file

@ -111,6 +111,8 @@ type State = {
*/ */
class HomeScreen extends React.Component<Props, State> { class HomeScreen extends React.Component<Props, State> {
colors: Object;
isLoggedIn: boolean | null; isLoggedIn: boolean | null;
fabRef: { current: null | AnimatedFAB }; fabRef: { current: null | AnimatedFAB };
@ -123,6 +125,7 @@ class HomeScreen extends React.Component<Props, State> {
constructor(props) { constructor(props) {
super(props); super(props);
this.colors = props.theme.colors;
this.fabRef = React.createRef(); this.fabRef = React.createRef();
this.currentNewFeed = []; this.currentNewFeed = [];
this.isLoggedIn = null; this.isLoggedIn = null;
@ -152,9 +155,6 @@ class HomeScreen extends React.Component<Props, State> {
}) })
} }
/**
* Updates login state and navigation parameters on screen focus
*/
onScreenFocus = () => { onScreenFocus = () => {
if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) { if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) {
this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn(); this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn();
@ -169,9 +169,6 @@ class HomeScreen extends React.Component<Props, State> {
this.handleNavigationParams(); this.handleNavigationParams();
}; };
/**
* Navigates to the a new screen if navigation parameters specify one
*/
handleNavigationParams = () => { handleNavigationParams = () => {
if (this.props.route.params != null) { if (this.props.route.params != null) {
if (this.props.route.params.nextScreen != null) { if (this.props.route.params.nextScreen != null) {
@ -182,11 +179,6 @@ class HomeScreen extends React.Component<Props, State> {
} }
}; };
/**
* Gets header buttons based on login state
*
* @returns {*}
*/
getHeaderButton = () => { getHeaderButton = () => {
let onPressLog = () => this.props.navigation.navigate("login", {nextScreen: "profile"}); let onPressLog = () => this.props.navigation.navigate("login", {nextScreen: "profile"});
let logIcon = "login"; let logIcon = "login";
@ -270,7 +262,7 @@ class HomeScreen extends React.Component<Props, State> {
id: 'washers', id: 'washers',
data: dashboardData == null ? 0 : dashboardData.available_machines.washers, data: dashboardData == null ? 0 : dashboardData.available_machines.washers,
icon: 'washing-machine', icon: 'washing-machine',
color: this.props.theme.colors.proxiwashColor, color: this.colors.proxiwashColor,
onPress: this.onProxiwashClick, onPress: this.onProxiwashClick,
isAvailable: dashboardData == null ? false : dashboardData.available_machines.washers > 0 isAvailable: dashboardData == null ? false : dashboardData.available_machines.washers > 0
}, },
@ -278,7 +270,7 @@ class HomeScreen extends React.Component<Props, State> {
id: 'dryers', id: 'dryers',
data: dashboardData == null ? 0 : dashboardData.available_machines.dryers, data: dashboardData == null ? 0 : dashboardData.available_machines.dryers,
icon: 'tumble-dryer', icon: 'tumble-dryer',
color: this.props.theme.colors.proxiwashColor, color: this.colors.proxiwashColor,
onPress: this.onProxiwashClick, onPress: this.onProxiwashClick,
isAvailable: dashboardData == null ? false : dashboardData.available_machines.dryers > 0 isAvailable: dashboardData == null ? false : dashboardData.available_machines.dryers > 0
}, },
@ -286,7 +278,7 @@ class HomeScreen extends React.Component<Props, State> {
id: 'available_tutorials', id: 'available_tutorials',
data: dashboardData == null ? 0 : dashboardData.available_tutorials, data: dashboardData == null ? 0 : dashboardData.available_tutorials,
icon: 'school', icon: 'school',
color: this.props.theme.colors.tutorinsaColor, color: this.colors.tutorinsaColor,
onPress: this.onTutorInsaClick, onPress: this.onTutorInsaClick,
isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0 isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0
}, },
@ -294,7 +286,7 @@ class HomeScreen extends React.Component<Props, State> {
id: 'proximo_articles', id: 'proximo_articles',
data: dashboardData == null ? 0 : dashboardData.proximo_articles, data: dashboardData == null ? 0 : dashboardData.proximo_articles,
icon: 'shopping', icon: 'shopping',
color: this.props.theme.colors.proximoColor, color: this.colors.proximoColor,
onPress: this.onProximoClick, onPress: this.onProximoClick,
isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0 isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0
}, },
@ -302,7 +294,7 @@ class HomeScreen extends React.Component<Props, State> {
id: 'today_menu', id: 'today_menu',
data: dashboardData == null ? [] : dashboardData.today_menu, data: dashboardData == null ? [] : dashboardData.today_menu,
icon: 'silverware-fork-knife', icon: 'silverware-fork-knife',
color: this.props.theme.colors.menuColor, color: this.colors.menuColor,
onPress: this.onMenuClick, onPress: this.onMenuClick,
isAvailable: dashboardData == null ? false : dashboardData.today_menu.length > 0 isAvailable: dashboardData == null ? false : dashboardData.today_menu.length > 0
}, },
@ -332,11 +324,6 @@ class HomeScreen extends React.Component<Props, State> {
return this.getDashboardActions(); return this.getDashboardActions();
} }
/**
* Gets a dashboard item with action buttons
*
* @returns {*}
*/
getDashboardActions() { getDashboardActions() {
return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>; return <ActionsDashBoardItem {...this.props} isLoggedIn={this.isLoggedIn}/>;
} }
@ -459,7 +446,7 @@ class HomeScreen extends React.Component<Props, State> {
onEventContainerClick = () => this.props.navigation.navigate('planning'); onEventContainerClick = () => this.props.navigation.navigate('planning');
/** /**
* Gets the event dashboard render item. * Gets the event render item.
* If a preview is available, it will be rendered inside * If a preview is available, it will be rendered inside
* *
* @param content * @param content
@ -486,12 +473,6 @@ class HomeScreen extends React.Component<Props, State> {
); );
} }
/**
* Gets a dashboard shortcut item
*
* @param item
* @returns {*}
*/
dashboardRowRenderItem = ({item}: { item: dashboardSmallItem }) => { dashboardRowRenderItem = ({item}: { item: dashboardSmallItem }) => {
return ( return (
<SquareDashboardItem <SquareDashboardItem
@ -505,7 +486,7 @@ class HomeScreen extends React.Component<Props, State> {
}; };
/** /**
* Gets a dashboard item with a row of shortcut buttons. * Gets a classic dashboard item.
* *
* @param content * @param content
* @return {*} * @return {*}
@ -572,7 +553,7 @@ class HomeScreen extends React.Component<Props, State> {
/** /**
* Callback used when closing the banner. * Callback used when closing the banner.
* This hides the banner and saves to preferences to prevent it from reopening. * This hides the banner and saves to preferences to prevent it from reopening
*/ */
onHideBanner = () => { onHideBanner = () => {
this.setState({bannerVisible: false}); this.setState({bannerVisible: false});
@ -582,10 +563,6 @@ class HomeScreen extends React.Component<Props, State> {
); );
}; };
/**
* Callback when pressing the login button on the banner.
* This hides the banner and takes the user to the login page.
*/
onLoginBanner = () => { onLoginBanner = () => {
this.onHideBanner(); this.onHideBanner();
this.props.navigation.navigate("login", {nextScreen: "profile"}); this.props.navigation.navigate("login", {nextScreen: "profile"});

View file

@ -41,9 +41,6 @@ class ScannerScreen extends React.Component<Props, State> {
this.requestPermissions(); this.requestPermissions();
} }
/**
* Requests permission to use the camera
*/
requestPermissions = () => { requestPermissions = () => {
if (Platform.OS === 'android') if (Platform.OS === 'android')
request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus) request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus)
@ -51,19 +48,8 @@ class ScannerScreen extends React.Component<Props, State> {
request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus) request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus)
}; };
/**
* Updates the state permission status
*
* @param result
*/
updatePermissionStatus = (result) => this.setState({hasPermission: result === RESULTS.GRANTED}); updatePermissionStatus = (result) => this.setState({hasPermission: result === RESULTS.GRANTED});
/**
* Opens scanned link if it is a valid app link or shows and error dialog
*
* @param type The barcode type
* @param data The scanned value
*/
handleCodeScanned = ({type, data}) => { handleCodeScanned = ({type, data}) => {
if (!URLHandler.isUrlValid(data)) if (!URLHandler.isUrlValid(data))
this.showErrorDialog(); this.showErrorDialog();
@ -73,11 +59,6 @@ class ScannerScreen extends React.Component<Props, State> {
} }
}; };
/**
* Gets a view asking user for permission to use the camera
*
* @returns {*}
*/
getPermissionScreen() { getPermissionScreen() {
return <View style={{marginLeft: 10, marginRight: 10}}> return <View style={{marginLeft: 10, marginRight: 10}}>
<Text>{i18n.t("scannerScreen.errorPermission")}</Text> <Text>{i18n.t("scannerScreen.errorPermission")}</Text>
@ -96,9 +77,6 @@ class ScannerScreen extends React.Component<Props, State> {
</View> </View>
} }
/**
* Shows a dialog indicating how to use the scanner
*/
showHelpDialog = () => { showHelpDialog = () => {
this.setState({ this.setState({
dialogVisible: true, dialogVisible: true,
@ -108,9 +86,6 @@ class ScannerScreen extends React.Component<Props, State> {
}); });
}; };
/**
* Shows a loading dialog
*/
showOpeningDialog = () => { showOpeningDialog = () => {
this.setState({ this.setState({
loading: true, loading: true,
@ -118,9 +93,6 @@ class ScannerScreen extends React.Component<Props, State> {
}); });
}; };
/**
* Shows a dialog indicating the user the scanned code was invalid
*/
showErrorDialog() { showErrorDialog() {
this.setState({ this.setState({
dialogVisible: true, dialogVisible: true,
@ -130,21 +102,11 @@ class ScannerScreen extends React.Component<Props, State> {
}); });
} }
/**
* Hide any dialog
*/
onDialogDismiss = () => this.setState({ onDialogDismiss = () => this.setState({
dialogVisible: false, dialogVisible: false,
scanned: false, scanned: false,
}); });
/**
* Gets a view with the scanner.
* This scanner uses the back camera, can only scan qr codes and has a square mask on the center.
* The mask is only for design purposes as a code is scanned as soon as it enters the camera view
*
* @returns {*}
*/
getScanner() { getScanner() {
return ( return (
<RNCamera <RNCamera

View file

@ -32,10 +32,10 @@ export function stringMatchQuery(str: string, query: string) {
* Checks if the given arrays have an item in common * Checks if the given arrays have an item in common
* *
* @param filter The filter array * @param filter The filter array
* @param categories The item's categories tuple * @param categories The item's categories array
* @returns {boolean} True if at least one entry is in both arrays * @returns {boolean} True if at least one entry is in both arrays
*/ */
export function isItemInCategoryFilter(filter: Array<number>, categories: [number, number]) { export function isItemInCategoryFilter(filter: Array<string>, categories: Array<string>) {
for (const category of categories) { for (const category of categories) {
if (filter.indexOf(category) !== -1) if (filter.indexOf(category) !== -1)
return true; return true;