forked from vergnet/application-amicale
Improved doc and typing and removed unused file
This commit is contained in:
parent
869a8e5ec0
commit
b66e50eaf8
15 changed files with 427 additions and 228 deletions
|
@ -4,6 +4,7 @@ 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,
|
||||||
|
@ -16,7 +17,7 @@ type listItem = {
|
||||||
* @param object The raw json
|
* @param object The raw json
|
||||||
* @return {Array<listItem>}
|
* @return {Array<listItem>}
|
||||||
*/
|
*/
|
||||||
function generateListFromObject(object: { [string]: string }): Array<listItem> {
|
function generateListFromObject(object: { [key: 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);
|
||||||
|
@ -28,8 +29,7 @@ function generateListFromObject(object: { [string]: string }): Array<listItem> {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: StackNavigationProp,
|
||||||
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<Object>;
|
data: Array<listItem>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.data = generateListFromObject(packageJson.dependencies);
|
this.data = generateListFromObject(packageJson.dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyExtractor = (item: Object) => item.name;
|
keyExtractor = (item: listItem) => item.name;
|
||||||
|
|
||||||
renderItem = ({item}: Object) =>
|
renderItem = ({item}: { item: listItem }) =>
|
||||||
<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, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
|
itemLayout = (data: any, index: number) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,6 +5,14 @@ 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',
|
||||||
|
@ -29,7 +37,7 @@ const links = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: StackNavigationProp,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,7 +56,7 @@ class AboutScreen extends React.Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Data to be displayed in the app card
|
* Data to be displayed in the app card
|
||||||
*/
|
*/
|
||||||
appData: Array<Object> = [
|
appData = [
|
||||||
{
|
{
|
||||||
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',
|
||||||
|
@ -83,7 +91,7 @@ class AboutScreen extends React.Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Data to be displayed in the author card
|
* Data to be displayed in the author card
|
||||||
*/
|
*/
|
||||||
authorData: Array<Object> = [
|
authorData = [
|
||||||
{
|
{
|
||||||
onPressCallback: () => openWebLink(links.meme),
|
onPressCallback: () => openWebLink(links.meme),
|
||||||
icon: 'account-circle',
|
icon: 'account-circle',
|
||||||
|
@ -106,7 +114,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: Array<Object> = [
|
additionalDevData = [
|
||||||
{
|
{
|
||||||
onPressCallback: () => console.log('Meme this'),
|
onPressCallback: () => console.log('Meme this'),
|
||||||
icon: 'account',
|
icon: 'account',
|
||||||
|
@ -129,7 +137,7 @@ class AboutScreen extends React.Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Data to be displayed in the technologies card
|
* Data to be displayed in the technologies card
|
||||||
*/
|
*/
|
||||||
technoData: Array<Object> = [
|
technoData = [
|
||||||
{
|
{
|
||||||
onPressCallback: () => openWebLink(links.react),
|
onPressCallback: () => openWebLink(links.react),
|
||||||
icon: 'react',
|
icon: 'react',
|
||||||
|
@ -146,7 +154,7 @@ class AboutScreen extends React.Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Order of information cards
|
* Order of information cards
|
||||||
*/
|
*/
|
||||||
dataOrder: Array<Object> = [
|
dataOrder = [
|
||||||
{
|
{
|
||||||
id: 'app',
|
id: 'app',
|
||||||
},
|
},
|
||||||
|
@ -158,16 +166,9 @@ 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 {*}
|
||||||
*/
|
*/
|
||||||
|
@ -187,7 +188,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: Object): string {
|
keyExtractor(item: ListItem): string {
|
||||||
return item.icon;
|
return item.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +272,7 @@ class AboutScreen extends React.Component<Props> {
|
||||||
* @param props
|
* @param props
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getChevronIcon(props: Object) {
|
getChevronIcon(props) {
|
||||||
return (
|
return (
|
||||||
<List.Icon {...props} icon={'chevron-right'}/>
|
<List.Icon {...props} icon={'chevron-right'}/>
|
||||||
);
|
);
|
||||||
|
@ -284,18 +285,18 @@ class AboutScreen extends React.Component<Props> {
|
||||||
* @param props
|
* @param props
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getItemIcon(item: Object, props: Object) {
|
getItemIcon(item: ListItem, props) {
|
||||||
return (
|
return (
|
||||||
<List.Icon {...props} icon={item.icon}/>
|
<List.Icon {...props} icon={item.icon}/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a clickable card item to be rendered inside a card.
|
* Gets a clickable card item to be rendered inside a card.
|
||||||
*
|
*
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getCardItem = ({item}: Object) => {
|
getCardItem = ({item}: { item: ListItem }) => {
|
||||||
const getItemIcon = this.getItemIcon.bind(this, item);
|
const getItemIcon = this.getItemIcon.bind(this, item);
|
||||||
if (item.showChevron) {
|
if (item.showChevron) {
|
||||||
return (
|
return (
|
||||||
|
@ -323,7 +324,7 @@ class AboutScreen extends React.Component<Props> {
|
||||||
* @param item The item to show
|
* @param item The item to show
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getMainCard = ({item}: Object) => {
|
getMainCard = ({item}: { item: { id: string } }) => {
|
||||||
switch (item.id) {
|
switch (item.id) {
|
||||||
case 'app':
|
case 'app':
|
||||||
return this.getAppCard();
|
return this.getAppCard();
|
||||||
|
|
|
@ -5,14 +5,24 @@ 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: Object,
|
navigation: StackNavigationProp,
|
||||||
|
theme: CustomTheme
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
modalCurrentDisplayItem: Object,
|
modalCurrentDisplayItem: PreferenceItem,
|
||||||
currentPreferences: Array<Object>,
|
currentPreferences: Array<PreferenceItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,20 +31,20 @@ type State = {
|
||||||
*/
|
*/
|
||||||
class DebugScreen extends React.Component<Props, State> {
|
class DebugScreen extends React.Component<Props, State> {
|
||||||
|
|
||||||
modalRef: Object;
|
modalRef: Modalize;
|
||||||
modalInputValue = '';
|
modalInputValue: string;
|
||||||
|
|
||||||
onModalRef: Function;
|
|
||||||
|
|
||||||
colors: Object;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies user preferences to state for easier manipulation
|
||||||
|
*
|
||||||
|
* @param props
|
||||||
|
*/
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.onModalRef = this.onModalRef.bind(this);
|
this.modalInputValue = "";
|
||||||
this.colors = props.theme.colors;
|
|
||||||
let copy = {...AsyncStorageManager.getInstance().preferences};
|
let copy = {...AsyncStorageManager.getInstance().preferences};
|
||||||
let currentPreferences = [];
|
let currentPreferences : Array<PreferenceItem> = [];
|
||||||
Object.values(copy).map((object) => {
|
Object.values(copy).map((object: any) => {
|
||||||
currentPreferences.push(object);
|
currentPreferences.push(object);
|
||||||
});
|
});
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -44,10 +54,11 @@ class DebugScreen extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the edit modal
|
* Shows the edit modal
|
||||||
|
*
|
||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
showEditModal(item: Object) {
|
showEditModal(item: PreferenceItem) {
|
||||||
this.setState({
|
this.setState({
|
||||||
modalCurrentDisplayItem: item
|
modalCurrentDisplayItem: item
|
||||||
});
|
});
|
||||||
|
@ -81,14 +92,14 @@ class DebugScreen extends React.Component<Props, State> {
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
dark={true}
|
dark={true}
|
||||||
color={this.colors.success}
|
color={this.props.theme.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.colors.danger}
|
color={this.props.theme.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>
|
||||||
|
@ -98,6 +109,12 @@ 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++) {
|
||||||
|
@ -130,11 +147,11 @@ class DebugScreen extends React.Component<Props, State> {
|
||||||
*
|
*
|
||||||
* @param ref
|
* @param ref
|
||||||
*/
|
*/
|
||||||
onModalRef(ref: Object) {
|
onModalRef = (ref: Modalize) => {
|
||||||
this.modalRef = ref;
|
this.modalRef = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem = ({item}: Object) => {
|
renderItem = ({item}: {item: PreferenceItem}) => {
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
title={item.key}
|
title={item.key}
|
||||||
|
|
|
@ -12,14 +12,18 @@ type Props = {
|
||||||
collapsibleStack: Collapsible
|
collapsibleStack: Collapsible
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {};
|
type DatasetItem = {
|
||||||
|
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, State> {
|
class AmicaleContactScreen extends React.Component<Props> {
|
||||||
|
|
||||||
|
|
||||||
|
// Dataset containing information about contacts
|
||||||
CONTACT_DATASET = [
|
CONTACT_DATASET = [
|
||||||
{
|
{
|
||||||
name: i18n.t("amicaleAbout.roles.interSchools"),
|
name: i18n.t("amicaleAbout.roles.interSchools"),
|
||||||
|
@ -68,18 +72,11 @@ class AmicaleContactScreen extends React.Component<Props, State> {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
colors: Object;
|
keyExtractor = (item: DatasetItem) => item.email;
|
||||||
|
|
||||||
constructor(props) {
|
getChevronIcon = (props) => <List.Icon {...props} icon={'chevron-right'}/>;
|
||||||
super(props);
|
|
||||||
this.colors = props.theme.colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyExtractor = (item: Object) => item.email;
|
renderItem = ({item}: { item: DatasetItem }) => {
|
||||||
|
|
||||||
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}
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
// @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);
|
|
|
@ -6,25 +6,11 @@ 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 (
|
||||||
|
|
|
@ -65,6 +65,12 @@ 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++) {
|
||||||
|
@ -75,6 +81,12 @@ 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;
|
||||||
|
@ -95,12 +107,19 @@ 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) {
|
/**
|
||||||
let final = [];
|
* Gets the view for rendering club managers if any
|
||||||
for (let i = 0; i < resp.length; i++) {
|
*
|
||||||
final.push(<Paragraph key={i.toString()}>{resp[i]}</Paragraph>)
|
* @param managers The list of manager names
|
||||||
|
* @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 = resp.length > 0;
|
const hasManagers = managers.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
|
||||||
|
@ -113,13 +132,20 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
icon="account-tie"/>}
|
icon="account-tie"/>}
|
||||||
/>
|
/>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
{final}
|
{managersListView}
|
||||||
{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
|
||||||
|
@ -141,13 +167,21 @@ 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<Object>) => {
|
getScreen = (response: Array<{ [key: string]: any } | null>) => {
|
||||||
let data: club = response[0];
|
let data: club | null = null;
|
||||||
this.updateHeaderTitle(data);
|
if (response[0] != null) {
|
||||||
|
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}}>
|
||||||
|
@ -184,7 +218,6 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
);
|
);
|
||||||
} else
|
} else
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -131,6 +131,15 @@ 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;
|
||||||
|
@ -150,6 +159,11 @@ 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}
|
||||||
|
@ -158,6 +172,12 @@ 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)
|
||||||
|
@ -165,6 +185,12 @@ 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);
|
||||||
|
|
|
@ -11,10 +11,11 @@ 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: Object,
|
navigation: StackNavigationProp,
|
||||||
route: Object,
|
route: { params: { nextScreen: string } },
|
||||||
collapsibleStack: Collapsible,
|
collapsibleStack: Collapsible,
|
||||||
theme: CustomTheme
|
theme: CustomTheme
|
||||||
}
|
}
|
||||||
|
@ -47,9 +48,9 @@ class LoginScreen extends React.Component<Props, State> {
|
||||||
dialogError: 0,
|
dialogError: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
onEmailChange: Function;
|
onEmailChange: (value: string) => null;
|
||||||
onPasswordChange: Function;
|
onPasswordChange: (value: string) => null;
|
||||||
passwordInputRef: Object;
|
passwordInputRef: { current: null | TextInput };
|
||||||
|
|
||||||
nextScreen: string | null;
|
nextScreen: string | null;
|
||||||
|
|
||||||
|
@ -64,7 +65,10 @@ 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;
|
||||||
|
@ -73,6 +77,11 @@ 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,
|
||||||
|
@ -81,6 +90,10 @@ 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(
|
||||||
|
@ -93,32 +106,75 @@ 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({
|
||||||
|
@ -133,8 +189,23 @@ 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});
|
||||||
|
@ -147,6 +218,11 @@ class LoginScreen extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the form input
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
getFormInput() {
|
getFormInput() {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
@ -173,9 +249,7 @@ class LoginScreen extends React.Component<Props, State> {
|
||||||
{i18n.t("loginScreen.emailError")}
|
{i18n.t("loginScreen.emailError")}
|
||||||
</HelperText>
|
</HelperText>
|
||||||
<TextInput
|
<TextInput
|
||||||
ref={(ref) => {
|
ref={this.passwordInputRef}
|
||||||
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}
|
||||||
|
@ -201,6 +275,10 @@ 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}>
|
||||||
|
@ -239,6 +317,11 @@ 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}>
|
||||||
|
|
|
@ -12,10 +12,12 @@ 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: Object,
|
navigation: StackNavigationProp,
|
||||||
theme: Object,
|
theme: CustomTheme,
|
||||||
collapsibleStack: Collapsible,
|
collapsibleStack: Collapsible,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +25,23 @@ 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";
|
||||||
|
|
||||||
|
@ -34,9 +53,9 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
data: Object;
|
data: ProfileData;
|
||||||
|
|
||||||
flatListData: Array<Object>;
|
flatListData: Array<{ id: string }>;
|
||||||
amicaleDataset: cardList;
|
amicaleDataset: cardList;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -79,12 +98,25 @@ 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) => {
|
/**
|
||||||
this.data = data[0];
|
* Gets the main screen component with the fetched data
|
||||||
|
*
|
||||||
|
* @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}}>
|
||||||
|
@ -109,7 +141,7 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
getRenderItem = ({item}: Object) => {
|
getRenderItem = ({item}: { item: { id: string } }) => {
|
||||||
switch (item.id) {
|
switch (item.id) {
|
||||||
case '0':
|
case '0':
|
||||||
return this.getWelcomeCard();
|
return this.getWelcomeCard();
|
||||||
|
@ -122,6 +154,11 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of services available with the Amicale account
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
getServicesList() {
|
getServicesList() {
|
||||||
return (
|
return (
|
||||||
<CardList
|
<CardList
|
||||||
|
@ -131,12 +168,17 @@ 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={(props) => <Avatar.Image
|
left={() => <Avatar.Image
|
||||||
size={64}
|
size={64}
|
||||||
source={ICON_AMICALE}
|
source={ICON_AMICALE}
|
||||||
style={{backgroundColor: 'transparent',}}
|
style={{backgroundColor: 'transparent',}}
|
||||||
|
@ -340,7 +382,7 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
* @param item The club to render
|
* @param item The club to render
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
clubListItem = ({item}: Object) => {
|
clubListItem = ({item}: { item: Club }) => {
|
||||||
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"/>;
|
||||||
|
@ -356,9 +398,9 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
/>;
|
/>;
|
||||||
};
|
};
|
||||||
|
|
||||||
clubKeyExtractor = (item: Object) => item.name;
|
clubKeyExtractor = (item: Club) => item.name;
|
||||||
|
|
||||||
sortClubList = (a: Object, b: Object) => a.is_manager ? -1 : 1;
|
sortClubList = (a: Club, b: Club) => 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
|
||||||
|
@ -366,7 +408,7 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
* @param list The club list
|
* @param list The club list
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getClubList(list: Array<Object>) {
|
getClubList(list: Array<Club>) {
|
||||||
list.sort(this.sortClubList);
|
list.sort(this.sortClubList);
|
||||||
return (
|
return (
|
||||||
//$FlowFixMe
|
//$FlowFixMe
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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,
|
||||||
|
@ -86,13 +87,16 @@ type objectVoteDates = {
|
||||||
const MIN_REFRESH_TIME = 5 * 1000;
|
const MIN_REFRESH_TIME = 5 * 1000;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object
|
navigation: StackNavigationProp
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
||||||
|
@ -107,7 +111,7 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
today: Date;
|
today: Date;
|
||||||
|
|
||||||
mainFlatListData: Array<{ key: string }>;
|
mainFlatListData: Array<{ key: string }>;
|
||||||
lastRefresh: Date;
|
lastRefresh: Date | null;
|
||||||
|
|
||||||
authRef: { current: null | AuthenticatedScreen };
|
authRef: { current: null | AuthenticatedScreen };
|
||||||
|
|
||||||
|
@ -116,22 +120,30 @@ 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;
|
||||||
if (this.lastRefresh !== undefined)
|
const lastRefresh = this.lastRefresh;
|
||||||
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME;
|
if (lastRefresh != null)
|
||||||
|
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) {
|
||||||
|
@ -152,6 +164,16 @@ 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);
|
||||||
|
@ -176,7 +198,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}: Object) => {
|
mainRenderItem = ({item}: { item: { key: string } }) => {
|
||||||
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)
|
||||||
|
@ -190,8 +212,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;
|
||||||
|
@ -282,6 +304,13 @@ 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
|
||||||
|
|
|
@ -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: Object,
|
navigation: StackNavigationProp,
|
||||||
route: Object
|
route: { params: { data: feedItem, date: string } }
|
||||||
};
|
};
|
||||||
|
|
||||||
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 planning event information page.
|
* Class defining a feed item page.
|
||||||
*/
|
*/
|
||||||
class FeedItemScreen extends React.Component<Props> {
|
class FeedItemScreen extends React.Component<Props> {
|
||||||
|
|
||||||
displayData: Object;
|
displayData: feedItem;
|
||||||
date: string;
|
date: string;
|
||||||
|
|
||||||
colors: Object;
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.colors = props.theme.colors;
|
this.displayData = props.route.params.data;
|
||||||
this.displayData = this.props.route.params.data;
|
this.date = props.route.params.date;
|
||||||
this.date = this.props.route.params.date;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -38,16 +38,29 @@ 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}
|
||||||
|
@ -55,8 +68,8 @@ class FeedItemScreen extends React.Component<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getContent() {
|
render() {
|
||||||
const hasImage = this.displayData.full_picture !== '' && this.displayData.full_picture !== undefined;
|
const hasImage = this.displayData.full_picture !== '' && this.displayData.full_picture != null;
|
||||||
return (
|
return (
|
||||||
<ScrollView style={{margin: 5,}}>
|
<ScrollView style={{margin: 5,}}>
|
||||||
<Card.Title
|
<Card.Title
|
||||||
|
@ -89,10 +102,6 @@ class FeedItemScreen extends React.Component<Props> {
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
return this.getContent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(FeedItemScreen);
|
export default withTheme(FeedItemScreen);
|
||||||
|
|
|
@ -111,8 +111,6 @@ 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 };
|
||||||
|
@ -125,7 +123,6 @@ 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;
|
||||||
|
@ -155,6 +152,9 @@ 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,6 +169,9 @@ 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) {
|
||||||
|
@ -179,6 +182,11 @@ 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";
|
||||||
|
@ -262,7 +270,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.colors.proxiwashColor,
|
color: this.props.theme.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
|
||||||
},
|
},
|
||||||
|
@ -270,7 +278,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.colors.proxiwashColor,
|
color: this.props.theme.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
|
||||||
},
|
},
|
||||||
|
@ -278,7 +286,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.colors.tutorinsaColor,
|
color: this.props.theme.colors.tutorinsaColor,
|
||||||
onPress: this.onTutorInsaClick,
|
onPress: this.onTutorInsaClick,
|
||||||
isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0
|
isAvailable: dashboardData == null ? false : dashboardData.available_tutorials > 0
|
||||||
},
|
},
|
||||||
|
@ -286,7 +294,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.colors.proximoColor,
|
color: this.props.theme.colors.proximoColor,
|
||||||
onPress: this.onProximoClick,
|
onPress: this.onProximoClick,
|
||||||
isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0
|
isAvailable: dashboardData == null ? false : dashboardData.proximo_articles > 0
|
||||||
},
|
},
|
||||||
|
@ -294,7 +302,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.colors.menuColor,
|
color: this.props.theme.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
|
||||||
},
|
},
|
||||||
|
@ -324,6 +332,11 @@ 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}/>;
|
||||||
}
|
}
|
||||||
|
@ -446,7 +459,7 @@ class HomeScreen extends React.Component<Props, State> {
|
||||||
onEventContainerClick = () => this.props.navigation.navigate('planning');
|
onEventContainerClick = () => this.props.navigation.navigate('planning');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the event render item.
|
* Gets the event dashboard 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
|
||||||
|
@ -473,6 +486,12 @@ 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
|
||||||
|
@ -486,7 +505,7 @@ class HomeScreen extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a classic dashboard item.
|
* Gets a dashboard item with a row of shortcut buttons.
|
||||||
*
|
*
|
||||||
* @param content
|
* @param content
|
||||||
* @return {*}
|
* @return {*}
|
||||||
|
@ -553,7 +572,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});
|
||||||
|
@ -563,6 +582,10 @@ 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"});
|
||||||
|
|
|
@ -41,6 +41,9 @@ 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)
|
||||||
|
@ -48,8 +51,19 @@ 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();
|
||||||
|
@ -59,6 +73,11 @@ 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>
|
||||||
|
@ -77,6 +96,9 @@ 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,
|
||||||
|
@ -86,6 +108,9 @@ class ScannerScreen extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a loading dialog
|
||||||
|
*/
|
||||||
showOpeningDialog = () => {
|
showOpeningDialog = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
|
@ -93,6 +118,9 @@ 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,
|
||||||
|
@ -102,11 +130,21 @@ 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
|
||||||
|
|
|
@ -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 array
|
* @param categories The item's categories tuple
|
||||||
* @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<string>, categories: Array<string>) {
|
export function isItemInCategoryFilter(filter: Array<number>, categories: [number, number]) {
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
if (filter.indexOf(category) !== -1)
|
if (filter.indexOf(category) !== -1)
|
||||||
return true;
|
return true;
|
||||||
|
|
Loading…
Reference in a new issue